36 files changed
+232
-19
No files matched your search
@@ -1,12 +1,12 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, ref, watch } from "vue";
|
||||
import { useI18n } from "vue-i18n";
|
||||
import { CheckSquare, Loader2, Search, Square } from "@lucide/vue";
|
||||
import { Asterisk, CheckSquare, Loader2, Search, Square } from "@lucide/vue";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Dialog, DialogContent, DialogFooter, DialogHeader, DialogTitle } from "@/components/ui/dialog";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { useConnectionStore } from "@/stores/connectionStore";
|
||||
import { canSaveVisibleDatabaseSelection, connectionUsesVisibleSchemaFilter, filterDatabaseNamesForVisiblePicker, filterSchemaNamesForVisiblePicker, normalizeVisibleDatabaseSelection } from "@/lib/database/visibleDatabases";
|
||||
import { canSaveVisibleDatabaseSelection, connectionUsesVisibleSchemaFilter, databaseNameMatchesVisiblePatterns, filterDatabaseNamesForVisiblePicker, filterSchemaNamesForVisiblePicker, normalizeVisibleDatabaseSelection, parseVisibleDatabasePatternsInput } from "@/lib/database/visibleDatabases";
|
||||
import * as api from "@/lib/backend/api";
|
||||
|
||||
const props = defineProps<{
|
||||
@@ -26,6 +26,7 @@ type FilterMode = "database" | "schema";
|
||||
|
||||
const objectNames = ref<string[]>([]);
|
||||
const selectedNames = ref<Set<string>>(new Set());
|
||||
const patternInput = ref("");
|
||||
const searchText = ref("");
|
||||
const showSystemDatabases = ref(false);
|
||||
const isLoading = ref(false);
|
||||
@@ -51,6 +52,11 @@ const filteredObjectNames = computed(() => {
|
||||
if (!query) return listedObjectNames.value;
|
||||
return listedObjectNames.value.filter((name) => name.toLowerCase().includes(query));
|
||||
});
|
||||
const parsedPatterns = computed(() => parseVisibleDatabasePatternsInput(patternInput.value));
|
||||
const patternMatchCount = computed(() => {
|
||||
if (parsedPatterns.value.length === 0) return 0;
|
||||
return objectNames.value.filter((name) => databaseNameMatchesVisiblePatterns(name, parsedPatterns.value)).length;
|
||||
});
|
||||
const selectedCount = computed(() => selectedNames.value.size);
|
||||
const totalCount = computed(() => listedObjectNames.value.length);
|
||||
const canSaveSelection = computed(() => canSaveVisibleDatabaseSelection([...selectedNames.value]));
|
||||
@@ -59,7 +65,7 @@ const showAllDisabled = computed(() => {
|
||||
if (isSchemaFilterMode.value) {
|
||||
return !connection.value?.visible_schemas?.[databaseKey.value];
|
||||
}
|
||||
return !Array.isArray(connection.value?.visible_databases);
|
||||
return !Array.isArray(connection.value?.visible_databases) && parsedPatterns.value.length === 0;
|
||||
});
|
||||
|
||||
watch(
|
||||
@@ -88,6 +94,7 @@ async function loadDatabases() {
|
||||
objectNames.value = names;
|
||||
showSystemDatabases.value = false;
|
||||
const configured = isSchemaFilterMode.value ? connection.value?.visible_schemas?.[databaseKey.value] : connection.value?.visible_databases;
|
||||
patternInput.value = isSchemaFilterMode.value ? "" : (connection.value?.visible_database_patterns?.join(", ") ?? "");
|
||||
const initialSelection = Array.isArray(configured) ? normalizeVisibleDatabaseSelection(configured, names) : listedObjectNames.value;
|
||||
selectedNames.value = new Set(initialSelection);
|
||||
const defaultVisible = new Set(defaultObjectNames.value);
|
||||
@@ -153,7 +160,7 @@ async function saveSelection() {
|
||||
if (isSchemaFilterMode.value) {
|
||||
await connectionStore.setVisibleSchemas(props.connectionId, databaseKey.value, normalizeVisibleDatabaseSelection([...selectedNames.value], objectNames.value));
|
||||
} else {
|
||||
await connectionStore.setVisibleDatabases(props.connectionId, [...selectedNames.value]);
|
||||
await connectionStore.setVisibleDatabaseFilter(props.connectionId, [...selectedNames.value], parsedPatterns.value);
|
||||
}
|
||||
emit("update:open", false);
|
||||
}
|
||||
@@ -174,6 +181,17 @@ async function saveSelection() {
|
||||
<Input v-model="searchText" :placeholder="t(searchPlaceholderKey)" class="h-8 border-0 px-0 shadow-none focus-visible:ring-0" :disabled="isLoading || !!errorMessage" />
|
||||
</div>
|
||||
|
||||
<!-- 通配符模式:与勾选取并集,命中的库(含之后新建的)始终显示(#7164) -->
|
||||
<div v-if="!isSchemaFilterMode" class="space-y-1">
|
||||
<div class="flex items-center gap-2 rounded-md border bg-background px-2" :title="t('visibleDatabases.patternsHint')">
|
||||
<Asterisk class="h-4 w-4 shrink-0 text-muted-foreground" />
|
||||
<Input v-model="patternInput" data-visible-database-patterns :placeholder="t('visibleDatabases.patternsPlaceholder')" class="h-8 border-0 px-0 shadow-none focus-visible:ring-0" :disabled="isLoading || !!errorMessage" />
|
||||
</div>
|
||||
<p class="px-1 text-xs text-muted-foreground">
|
||||
{{ t("visibleDatabases.patternsHint") }}<template v-if="parsedPatterns.length > 0"> · {{ t("visibleDatabases.patternsMatchCount", { count: patternMatchCount }) }}</template>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center justify-between text-xs text-muted-foreground">
|
||||
<span>{{ t("visibleDatabases.selectedCount", { selected: selectedCount, total: totalCount }) }}</span>
|
||||
<div class="flex items-center gap-2">
|
||||
|
||||
@@ -3264,6 +3264,9 @@ export default {
|
||||
description: 'Choose which databases are shown under "{connection}".',
|
||||
searchPlaceholder: "Search databases...",
|
||||
selectedCount: "{selected}/{total} selected",
|
||||
patternsPlaceholder: "Wildcards, e.g. %n1%, comma separated",
|
||||
patternsHint: "Databases matching these patterns are always shown (% = any characters, _ = single character)",
|
||||
patternsMatchCount: "{count} matching",
|
||||
selectAll: "Select all",
|
||||
selectFiltered: "Select search results",
|
||||
clear: "Clear",
|
||||
|
||||
@@ -3172,6 +3172,9 @@ export default withEnglishFallback({
|
||||
description: 'Elige qué bases de datos se muestran bajo "{connection}".',
|
||||
searchPlaceholder: "Buscar bases de datos...",
|
||||
selectedCount: "{selected}/{total} seleccionadas",
|
||||
patternsPlaceholder: "Comodines, p. ej. %n1%, separados por comas",
|
||||
patternsHint: "Las bases de datos que coincidan con estos patrones se muestran siempre (% = cualquier caracteres, _ = un carácter)",
|
||||
patternsMatchCount: "{count} coinciden",
|
||||
selectAll: "Seleccionar todo",
|
||||
selectFiltered: "Seleccionar resultados de búsqueda",
|
||||
clear: "Limpiar",
|
||||
|
||||
@@ -3170,6 +3170,9 @@ export default withEnglishFallback({
|
||||
description: 'Scegli quali database mostrare sotto "{connection}".',
|
||||
searchPlaceholder: "Cerca database...",
|
||||
selectedCount: "{selected}/{total} selezionati",
|
||||
patternsPlaceholder: "Wildcard, es. %n1%, separate da virgole",
|
||||
patternsHint: "I database che corrispondono a questi pattern vengono sempre mostrati (% = qualsiasi caratteri, _ = un singolo carattere)",
|
||||
patternsMatchCount: "{count} corrispondono",
|
||||
selectAll: "Seleziona tutto",
|
||||
selectFiltered: "Seleziona risultati ricerca",
|
||||
clear: "Cancella",
|
||||
|
||||
@@ -3196,6 +3196,9 @@ export default withEnglishFallback({
|
||||
description: "「{connection}」の下に表示するデータベースを選択してください。",
|
||||
searchPlaceholder: "データベースを検索...",
|
||||
selectedCount: "{selected}/{total}件選択中",
|
||||
patternsPlaceholder: "ワイルドカード(例: %n1%)カンマ区切り",
|
||||
patternsHint: "これらのパターンに一致するデータベースは常に表示されます(% は任意の文字列、_ は任意の 1 文字)",
|
||||
patternsMatchCount: "{count} 件が一致",
|
||||
selectAll: "すべて選択",
|
||||
selectFiltered: "検索結果を選択",
|
||||
clear: "クリア",
|
||||
|
||||
@@ -3048,6 +3048,9 @@ export default withEnglishFallback({
|
||||
description: '"{connection}" 아래에 표시할 데이터베이스를 선택하세요.',
|
||||
searchPlaceholder: "데이터베이스 검색...",
|
||||
selectedCount: "{selected}/{total} 선택됨",
|
||||
patternsPlaceholder: "와일드카드(예: %n1%), 쉼표로 구분",
|
||||
patternsHint: "이 패턴과 일치하는 데이터베이스는 항상 표시됩니다(% = 임의의 문자, _ = 단일 문자)",
|
||||
patternsMatchCount: "{count}개 일치",
|
||||
selectAll: "모두 선택",
|
||||
selectFiltered: "검색 결과 선택",
|
||||
clear: "지우기",
|
||||
|
||||
@@ -3172,6 +3172,9 @@ export default withEnglishFallback({
|
||||
description: 'Escolha quais bancos de dados são exibidos em "{connection}".',
|
||||
searchPlaceholder: "Pesquisar bancos de dados...",
|
||||
selectedCount: "{selected}/{total} selecionados",
|
||||
patternsPlaceholder: "Curingas, ex.: %n1%, separados por vírgula",
|
||||
patternsHint: "Bancos de dados que correspondem a estes padrões são sempre exibidos (% = quaisquer caracteres, _ = um caractere)",
|
||||
patternsMatchCount: "{count} correspondem",
|
||||
selectAll: "Selecionar todos",
|
||||
selectFiltered: "Selecionar resultados da pesquisa",
|
||||
clear: "Limpar",
|
||||
|
||||
@@ -3186,6 +3186,9 @@ export default withEnglishFallback({
|
||||
description: "选择「{connection}」下要在侧边栏显示的数据库。",
|
||||
searchPlaceholder: "搜索数据库...",
|
||||
selectedCount: "已选择 {selected}/{total}",
|
||||
patternsPlaceholder: "通配符,如 %n1%,逗号分隔",
|
||||
patternsHint: "匹配这些模式的数据库始终显示(% 任意字符,_ 单字符)",
|
||||
patternsMatchCount: "匹配 {count} 个",
|
||||
selectAll: "全选",
|
||||
selectFiltered: "选中搜索结果",
|
||||
clear: "清空",
|
||||
|
||||
@@ -3170,6 +3170,9 @@ export default withEnglishFallback({
|
||||
description: "選擇「{connection}」下要在側邊欄顯示的資料庫。",
|
||||
searchPlaceholder: "搜尋資料庫……",
|
||||
selectedCount: "已選擇 {selected}/{total}",
|
||||
patternsPlaceholder: "萬用字元,如 %n1%,逗號分隔",
|
||||
patternsHint: "符合這些模式的資料庫一律顯示(% 任意字元,_ 單一字元)",
|
||||
patternsMatchCount: "符合 {count} 個",
|
||||
selectAll: "全選",
|
||||
selectFiltered: "選取搜尋結果",
|
||||
clear: "清空",
|
||||
|
||||
@@ -61,3 +61,53 @@ describe("visibleDatabases schema filtering", () => {
|
||||
expect(isSystemSchemaName("kingbase", "public")).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe("visible database wildcard patterns (issue #7164)", () => {
|
||||
it("matches names with % and _ wildcards", async () => {
|
||||
const { databaseNameMatchesVisiblePatterns } = await import("@/lib/database/visibleDatabases");
|
||||
expect(databaseNameMatchesVisiblePatterns("n1_order", ["%n1%"])).toBe(true);
|
||||
expect(databaseNameMatchesVisiblePatterns("main", ["%n1%"])).toBe(false);
|
||||
expect(databaseNameMatchesVisiblePatterns("db1", ["db_"])).toBe(true);
|
||||
expect(databaseNameMatchesVisiblePatterns("db12", ["db_"])).toBe(false);
|
||||
expect(databaseNameMatchesVisiblePatterns("a.b", ["a.b"])).toBe(true);
|
||||
expect(databaseNameMatchesVisiblePatterns("axb", ["a.b"])).toBe(false);
|
||||
expect(databaseNameMatchesVisiblePatterns("N1_ORDER", ["%n1%"])).toBe(false);
|
||||
expect(databaseNameMatchesVisiblePatterns("any", [])).toBe(false);
|
||||
expect(databaseNameMatchesVisiblePatterns("any", undefined)).toBe(false);
|
||||
});
|
||||
|
||||
it("unions exact selection with pattern matches in filterVisibleDatabaseNames", async () => {
|
||||
const { filterVisibleDatabaseNames } = await import("@/lib/database/visibleDatabases");
|
||||
const names = ["n1_order", "n2_user", "main", "report"];
|
||||
expect(filterVisibleDatabaseNames(names, ["main"], ["%n1%"])).toEqual(["n1_order", "main"]);
|
||||
expect(filterVisibleDatabaseNames(names, undefined, ["n%"])).toEqual(["n1_order", "n2_user"]);
|
||||
expect(filterVisibleDatabaseNames(names, ["main"], undefined)).toEqual(["main"]);
|
||||
expect(filterVisibleDatabaseNames(names, undefined, [])).toEqual(names);
|
||||
});
|
||||
|
||||
it("applies patterns for connections in filterDatabaseNamesForConnection", async () => {
|
||||
const { filterDatabaseNamesForConnection } = await import("@/lib/database/visibleDatabases");
|
||||
const connection = { db_type: "mysql" as const, visible_database_patterns: ["%n1%"] };
|
||||
expect(filterDatabaseNamesForConnection(["n1_order", "main", "mysql", "sys"], connection)).toEqual(["n1_order"]);
|
||||
});
|
||||
|
||||
it("parses user pattern input with separators, trimming and dedupe", async () => {
|
||||
const { parseVisibleDatabasePatternsInput } = await import("@/lib/database/visibleDatabases");
|
||||
expect(parseVisibleDatabasePatternsInput(" %n1%, n2_; \n%n1% ,,")).toEqual(["%n1%", "n2_"]);
|
||||
expect(parseVisibleDatabasePatternsInput(" ")).toEqual([]);
|
||||
});
|
||||
|
||||
it("treats pattern-only configuration as a configured sidebar filter", async () => {
|
||||
const { connectionHasConfiguredSidebarVisibleFilter } = await import("@/lib/sidebar/sidebarVisibleFilterSummary");
|
||||
expect(connectionHasConfiguredSidebarVisibleFilter({ db_type: "mysql", visible_databases: undefined, visible_database_patterns: ["%n1%"] })).toBe(true);
|
||||
expect(connectionHasConfiguredSidebarVisibleFilter({ db_type: "mysql", visible_databases: undefined, visible_database_patterns: [] })).toBe(false);
|
||||
});
|
||||
|
||||
it("counts pattern-matched databases in the sidebar filter summary", async () => {
|
||||
const { sidebarVisibleFilterSummary } = await import("@/lib/sidebar/sidebarVisibleFilterSummary");
|
||||
const summary = sidebarVisibleFilterSummary({ db_type: "mysql", visible_databases: ["main"], visible_database_patterns: ["%n1%"] }, ["main", "n1_order", "n2_user", "mysql"]);
|
||||
expect(summary.isActive).toBe(true);
|
||||
expect(summary.selected).toBe(2);
|
||||
expect(summary.total).toBe(3);
|
||||
});
|
||||
});
|
||||
@@ -150,14 +150,57 @@ export function visibleDatabaseFilterIsEnabled(visibleDatabases: string[] | unde
|
||||
return Array.isArray(visibleDatabases);
|
||||
}
|
||||
|
||||
const visibleDatabasePatternRegExpCache = new Map<string, RegExp>();
|
||||
|
||||
/** SQL LIKE 语义:`%` 匹配任意字符序列、`_` 匹配单个字符,其余按字面匹配(区分大小写)。 */
|
||||
export function visibleDatabasePatternRegExp(pattern: string): RegExp {
|
||||
const trimmed = pattern.trim();
|
||||
const cached = visibleDatabasePatternRegExpCache.get(trimmed);
|
||||
if (cached) return cached;
|
||||
let source = "";
|
||||
for (const ch of trimmed) {
|
||||
if (ch === "%") source += "[\\s\\S]*";
|
||||
else if (ch === "_") source += "[\\s\\S]";
|
||||
else source += ch.replace(/[\\^$.*+?()[\]{}|]/g, "\\$&");
|
||||
}
|
||||
const regExp = new RegExp(`^${source}$`);
|
||||
if (visibleDatabasePatternRegExpCache.size > 256) visibleDatabasePatternRegExpCache.clear();
|
||||
visibleDatabasePatternRegExpCache.set(trimmed, regExp);
|
||||
return regExp;
|
||||
}
|
||||
|
||||
export function visibleDatabasePatternsAreEnabled(patterns: string[] | undefined): boolean {
|
||||
return Array.isArray(patterns) && patterns.some((pattern) => pattern.trim() !== "");
|
||||
}
|
||||
|
||||
/** 解析用户输入的模式列表:逗号/分号/换行分隔,去空白、去重。 */
|
||||
export function parseVisibleDatabasePatternsInput(value: string): string[] {
|
||||
const seen = new Set<string>();
|
||||
const patterns: string[] = [];
|
||||
for (const part of value.split(/[\n,,;;]+/)) {
|
||||
const pattern = part.trim();
|
||||
if (!pattern || seen.has(pattern)) continue;
|
||||
seen.add(pattern);
|
||||
patterns.push(pattern);
|
||||
}
|
||||
return patterns;
|
||||
}
|
||||
|
||||
export function databaseNameMatchesVisiblePatterns(name: string, patterns: string[] | undefined): boolean {
|
||||
if (!visibleDatabasePatternsAreEnabled(patterns)) return false;
|
||||
return patterns!.some((pattern) => visibleDatabasePatternRegExp(pattern).test(name));
|
||||
}
|
||||
|
||||
export function canSaveVisibleDatabaseSelection(selectedNames: string[]): boolean {
|
||||
return selectedNames.length > 0;
|
||||
}
|
||||
|
||||
export function filterVisibleDatabaseNames(databaseNames: string[], visibleDatabases: string[] | undefined): string[] {
|
||||
if (!visibleDatabaseFilterIsEnabled(visibleDatabases)) return databaseNames;
|
||||
const visible = new Set(visibleDatabases);
|
||||
return databaseNames.filter((name) => visible.has(name));
|
||||
export function filterVisibleDatabaseNames(databaseNames: string[], visibleDatabases: string[] | undefined, visibleDatabasePatterns?: string[]): string[] {
|
||||
const exactEnabled = visibleDatabaseFilterIsEnabled(visibleDatabases);
|
||||
if (!exactEnabled && !visibleDatabasePatternsAreEnabled(visibleDatabasePatterns)) return databaseNames;
|
||||
// 显式勾选与模式取并集:模式对新建的数据库持续生效(#7164)
|
||||
const visible = exactEnabled ? new Set(visibleDatabases) : undefined;
|
||||
return databaseNames.filter((name) => visible?.has(name) || databaseNameMatchesVisiblePatterns(name, visibleDatabasePatterns));
|
||||
}
|
||||
|
||||
export function normalizeVisibleDatabaseSelection(selectedNames: string[], databaseNames: string[]): string[] {
|
||||
@@ -187,8 +230,9 @@ export function isSystemSchemaName(databaseType: DatabaseType | undefined, schem
|
||||
|
||||
export function filterDatabaseNamesForConnection(databaseNames: string[], connection: VisibleDatabaseConnection | undefined): string[] {
|
||||
const visibleDatabases = connection?.visible_databases;
|
||||
if (visibleDatabaseFilterIsEnabled(visibleDatabases)) {
|
||||
return filterVisibleDatabaseNames(databaseNames, visibleDatabases);
|
||||
const visibleDatabasePatterns = connection?.visible_database_patterns;
|
||||
if (visibleDatabaseFilterIsEnabled(visibleDatabases) || visibleDatabasePatternsAreEnabled(visibleDatabasePatterns)) {
|
||||
return filterVisibleDatabaseNames(databaseNames, visibleDatabases, visibleDatabasePatterns);
|
||||
}
|
||||
return filterDatabaseNamesForVisiblePicker(databaseNames, connection);
|
||||
}
|
||||
@@ -218,8 +262,10 @@ export function visibleSchemaFilterIsEnabled(visibleSchemas: Record<string, stri
|
||||
export function filterSchemaNamesForConnection(schemaNames: string[], connection: VisibleDatabaseConnection | undefined, database: string, options?: SchemaFilterOptions): string[] {
|
||||
const visibleSchemas = connection?.visible_schemas;
|
||||
if (!visibleSchemaFilterIsEnabled(visibleSchemas, database)) {
|
||||
if (connectionUsesVisibleSchemaFilter(connection) && visibleDatabaseFilterIsEnabled(connection?.visible_databases)) {
|
||||
return filterVisibleDatabaseNames(schemaNames, connection?.visible_databases);
|
||||
const visibleDatabases = connection?.visible_databases;
|
||||
const visibleDatabasePatterns = connection?.visible_database_patterns;
|
||||
if (connectionUsesVisibleSchemaFilter(connection) && (visibleDatabaseFilterIsEnabled(visibleDatabases) || visibleDatabasePatternsAreEnabled(visibleDatabasePatterns))) {
|
||||
return filterVisibleDatabaseNames(schemaNames, visibleDatabases, visibleDatabasePatterns);
|
||||
}
|
||||
return filterSchemaNamesForVisiblePicker(schemaNames, connection, options);
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import type { ConnectionConfig } from "@/types/database";
|
||||
import { connectionUsesVisibleSchemaFilter, filterDatabaseNamesForVisiblePicker, filterSchemaNamesForVisiblePicker, normalizeVisibleDatabaseSelection } from "@/lib/database/visibleDatabases";
|
||||
import { connectionUsesVisibleSchemaFilter, databaseNameMatchesVisiblePatterns, filterDatabaseNamesForVisiblePicker, filterSchemaNamesForVisiblePicker, normalizeVisibleDatabaseSelection, visibleDatabasePatternsAreEnabled } from "@/lib/database/visibleDatabases";
|
||||
import { nacosNamespaceIdentity } from "@/lib/nacos/nacosNamespaceVisibility";
|
||||
|
||||
type SidebarVisibleFilterConnection = Pick<ConnectionConfig, "database" | "db_type" | "driver_profile" | "show_system_schemas" | "username" | "visible_databases" | "visible_schemas">;
|
||||
type SidebarVisibleFilterConnection = Pick<ConnectionConfig, "database" | "db_type" | "driver_profile" | "show_system_schemas" | "username" | "visible_databases" | "visible_database_patterns" | "visible_schemas">;
|
||||
|
||||
export type SidebarVisibleFilterSummary = {
|
||||
mode: "database" | "schema" | "namespace";
|
||||
@@ -15,13 +15,15 @@ export function connectionHasConfiguredSidebarVisibleFilter(connection: SidebarV
|
||||
if (connectionUsesVisibleSchemaFilter(connection)) {
|
||||
return Array.isArray(connection.visible_schemas?.[connection.database || ""]);
|
||||
}
|
||||
return Array.isArray(connection.visible_databases);
|
||||
return Array.isArray(connection.visible_databases) || (connection.db_type !== "nacos" && visibleDatabasePatternsAreEnabled(connection.visible_database_patterns));
|
||||
}
|
||||
|
||||
export function sidebarVisibleFilterSummary(connection: SidebarVisibleFilterConnection, objectNames?: readonly string[]): SidebarVisibleFilterSummary {
|
||||
const mode = connectionUsesVisibleSchemaFilter(connection) ? "schema" : "database";
|
||||
const configured = mode === "schema" ? connection.visible_schemas?.[connection.database || ""] : connection.visible_databases;
|
||||
const isExplicit = Array.isArray(configured);
|
||||
// 通配符模式只在数据库模式生效;命中的库名与显式勾选取并集(#7164)
|
||||
const patterns = mode === "database" ? connection.visible_database_patterns : undefined;
|
||||
const isExplicit = Array.isArray(configured) || visibleDatabasePatternsAreEnabled(patterns);
|
||||
if (!objectNames) return { mode, isActive: false, selected: null, total: null };
|
||||
|
||||
const names = [...objectNames];
|
||||
@@ -30,7 +32,13 @@ export function sidebarVisibleFilterSummary(connection: SidebarVisibleFilterConn
|
||||
return { mode, isActive: false, selected: defaultNames.length, total: defaultNames.length };
|
||||
}
|
||||
|
||||
const selectedNames = normalizeVisibleDatabaseSelection(configured, names);
|
||||
const selectedSet = new Set(Array.isArray(configured) ? normalizeVisibleDatabaseSelection(configured, names) : []);
|
||||
if (visibleDatabasePatternsAreEnabled(patterns)) {
|
||||
for (const name of names) {
|
||||
if (databaseNameMatchesVisiblePatterns(name, patterns)) selectedSet.add(name);
|
||||
}
|
||||
}
|
||||
const selectedNames = [...selectedSet];
|
||||
const defaultNameSet = new Set(defaultNames);
|
||||
const includesSystemObject = selectedNames.some((name) => !defaultNameSet.has(name));
|
||||
const total = includesSystemObject ? names.length : defaultNames.length;
|
||||
|
||||
@@ -104,7 +104,7 @@ import { migrateSqlServerLegacyCompatibilityConfig, requiresSqlServerLegacyCompa
|
||||
import { gaussdbMTypeDisplayName } from "@/lib/table/postgresDataTypeHelp";
|
||||
import { deleteTabResultSnapshotsForOwner } from "@/lib/tabs/tabResultCache";
|
||||
import { disposeSqlServerActivityTracesForConnection, hasSqlServerActivityTraceForConnection } from "@/lib/sqlserver/sqlServerActivityTraceRuntime";
|
||||
import { connectionUsesVisibleSchemaFilter, filterDatabaseNamesForConnection, filterSchemaNamesForConnection, filterVisibleDatabaseNames, normalizeVisibleDatabaseSelection } from "@/lib/database/visibleDatabases";
|
||||
import { connectionUsesVisibleSchemaFilter, filterDatabaseNamesForConnection, filterSchemaNamesForConnection, filterVisibleDatabaseNames, normalizeVisibleDatabaseSelection, visibleDatabasePatternsAreEnabled } from "@/lib/database/visibleDatabases";
|
||||
import {
|
||||
buildObjectGroupPlaceholderNodes,
|
||||
buildGroupedObjectTreeNodes,
|
||||
@@ -3442,6 +3442,26 @@ export const useConnectionStore = defineStore("connection", () => {
|
||||
await reloadConnectionDatabaseChildren(connectionId);
|
||||
}
|
||||
|
||||
// 显式勾选 + 通配符模式一并保存(#7164):模式对之后新建的库持续生效
|
||||
async function setVisibleDatabaseFilter(connectionId: string, databaseNames: string[], patterns: string[]) {
|
||||
const config = getConfig(connectionId);
|
||||
if (!config) return;
|
||||
const normalizedPatterns = patterns.map((pattern) => pattern.trim()).filter((pattern) => pattern !== "");
|
||||
const idx = connections.value.findIndex((connection) => connection.id === connectionId);
|
||||
if (idx < 0) return;
|
||||
const nextConnections = [...connections.value];
|
||||
nextConnections[idx] = {
|
||||
...nextConnections[idx],
|
||||
visible_databases: normalizeVisibleDatabaseSelection(databaseNames, databaseNames),
|
||||
visible_database_patterns: normalizedPatterns.length > 0 ? normalizedPatterns : undefined,
|
||||
};
|
||||
await persistConnections(nextConnections);
|
||||
connections.value = nextConnections;
|
||||
invalidateCompletionCache(connectionId);
|
||||
rebuildTreeNodes();
|
||||
await reloadConnectionDatabaseChildren(connectionId);
|
||||
}
|
||||
|
||||
function recordPrimaryVisibleObjectNames(connectionId: string, objectNames: readonly string[]) {
|
||||
const names = [...objectNames];
|
||||
const existing = primaryVisibleObjectNames.value[connectionId];
|
||||
@@ -3473,7 +3493,20 @@ export const useConnectionStore = defineStore("connection", () => {
|
||||
|
||||
async function clearVisibleDatabases(connectionId: string) {
|
||||
const config = getConfig(connectionId);
|
||||
if (!config || !Array.isArray(config.visible_databases)) return;
|
||||
if (!config || (!Array.isArray(config.visible_databases) && !visibleDatabasePatternsAreEnabled(config.visible_database_patterns))) return;
|
||||
if (visibleDatabasePatternsAreEnabled(config.visible_database_patterns)) {
|
||||
const idx = connections.value.findIndex((connection) => connection.id === connectionId);
|
||||
if (idx >= 0) {
|
||||
const nextConnections = [...connections.value];
|
||||
nextConnections[idx] = { ...nextConnections[idx], visible_databases: undefined, visible_database_patterns: undefined };
|
||||
await persistConnections(nextConnections);
|
||||
connections.value = nextConnections;
|
||||
invalidateCompletionCache(connectionId);
|
||||
rebuildTreeNodes();
|
||||
await reloadConnectionDatabaseChildren(connectionId);
|
||||
return;
|
||||
}
|
||||
}
|
||||
await updateVisibleDatabasesConfig(connectionId, undefined);
|
||||
await reloadConnectionDatabaseChildren(connectionId);
|
||||
}
|
||||
@@ -8743,6 +8776,7 @@ export const useConnectionStore = defineStore("connection", () => {
|
||||
getRedisDatabaseAlias,
|
||||
setRedisDatabaseAlias,
|
||||
setVisibleDatabases,
|
||||
setVisibleDatabaseFilter,
|
||||
clearVisibleDatabases,
|
||||
ensureVisibleDatabase,
|
||||
setVisibleSchemas,
|
||||
|
||||
@@ -78,6 +78,7 @@ export interface ConnectionConfig {
|
||||
database?: string;
|
||||
default_schema?: string;
|
||||
visible_databases?: string[];
|
||||
visible_database_patterns?: string[];
|
||||
visible_schemas?: Record<string, string[]>;
|
||||
show_system_schemas?: boolean;
|
||||
attached_databases?: AttachedDatabaseConfig[];
|
||||
|
||||
@@ -711,6 +711,7 @@ mod tests {
|
||||
database: database.map(str::to_string),
|
||||
default_schema: None,
|
||||
visible_databases: None,
|
||||
visible_database_patterns: None,
|
||||
visible_schemas: None,
|
||||
show_system_schemas: false,
|
||||
attached_databases: Vec::new(),
|
||||
|
||||
@@ -1367,6 +1367,7 @@ for line in sys.stdin:
|
||||
database: Some(database.to_string()),
|
||||
default_schema: None,
|
||||
visible_databases: None,
|
||||
visible_database_patterns: None,
|
||||
visible_schemas: None,
|
||||
show_system_schemas: false,
|
||||
attached_databases: Vec::new(),
|
||||
|
||||
@@ -1770,6 +1770,7 @@ mod tests {
|
||||
database: Some("app_db".to_string()),
|
||||
default_schema: None,
|
||||
visible_databases: None,
|
||||
visible_database_patterns: None,
|
||||
visible_schemas: None,
|
||||
show_system_schemas: false,
|
||||
attached_databases: Vec::new(),
|
||||
@@ -1831,6 +1832,7 @@ mod tests {
|
||||
database: None,
|
||||
default_schema: None,
|
||||
visible_databases: None,
|
||||
visible_database_patterns: None,
|
||||
visible_schemas: None,
|
||||
show_system_schemas: false,
|
||||
attached_databases: Vec::new(),
|
||||
@@ -2062,6 +2064,7 @@ mod tests {
|
||||
database: None,
|
||||
default_schema: None,
|
||||
visible_databases: None,
|
||||
visible_database_patterns: None,
|
||||
visible_schemas: None,
|
||||
show_system_schemas: false,
|
||||
attached_databases: Vec::new(),
|
||||
|
||||
@@ -5891,6 +5891,7 @@ mod tests {
|
||||
database: database.map(str::to_string),
|
||||
default_schema: None,
|
||||
visible_databases: None,
|
||||
visible_database_patterns: None,
|
||||
visible_schemas: None,
|
||||
show_system_schemas: false,
|
||||
attached_databases: Vec::new(),
|
||||
|
||||
@@ -840,6 +840,7 @@ mod tests {
|
||||
database: Some("postgres".to_string()),
|
||||
default_schema: None,
|
||||
visible_databases: None,
|
||||
visible_database_patterns: None,
|
||||
visible_schemas: None,
|
||||
show_system_schemas: false,
|
||||
attached_databases: Vec::new(),
|
||||
|
||||
@@ -6079,6 +6079,7 @@ mod tests {
|
||||
database: None,
|
||||
default_schema: None,
|
||||
visible_databases: None,
|
||||
visible_database_patterns: None,
|
||||
visible_schemas: None,
|
||||
show_system_schemas: false,
|
||||
attached_databases: Vec::new(),
|
||||
|
||||
@@ -94,6 +94,8 @@ pub struct ConnectionConfig {
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub visible_databases: Option<Vec<String>>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub visible_database_patterns: Option<Vec<String>>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub visible_schemas: Option<HashMap<String, Vec<String>>>,
|
||||
#[serde(default, skip_serializing_if = "is_false")]
|
||||
pub show_system_schemas: bool,
|
||||
@@ -525,6 +527,8 @@ struct ConnectionConfigData {
|
||||
#[serde(default)]
|
||||
pub visible_databases: Option<Vec<String>>,
|
||||
#[serde(default)]
|
||||
pub visible_database_patterns: Option<Vec<String>>,
|
||||
#[serde(default)]
|
||||
pub visible_schemas: Option<HashMap<String, Vec<String>>>,
|
||||
#[serde(default)]
|
||||
pub show_system_schemas: bool,
|
||||
@@ -626,6 +630,7 @@ impl From<ConnectionConfigData> for ConnectionConfig {
|
||||
database: data.database,
|
||||
default_schema: data.default_schema,
|
||||
visible_databases: data.visible_databases,
|
||||
visible_database_patterns: data.visible_database_patterns,
|
||||
visible_schemas: data.visible_schemas,
|
||||
show_system_schemas: data.show_system_schemas,
|
||||
attached_databases: data.attached_databases,
|
||||
@@ -2642,6 +2647,7 @@ mod tests {
|
||||
database: database.map(str::to_string),
|
||||
default_schema: None,
|
||||
visible_databases: None,
|
||||
visible_database_patterns: None,
|
||||
visible_schemas: None,
|
||||
show_system_schemas: false,
|
||||
attached_databases: Vec::new(),
|
||||
|
||||
@@ -382,6 +382,7 @@ mod tests {
|
||||
database: None,
|
||||
default_schema: None,
|
||||
visible_databases: None,
|
||||
visible_database_patterns: None,
|
||||
visible_schemas: None,
|
||||
show_system_schemas: false,
|
||||
attached_databases: Vec::new(),
|
||||
|
||||
@@ -658,6 +658,7 @@ mod tests {
|
||||
database: None,
|
||||
default_schema: None,
|
||||
visible_databases: None,
|
||||
visible_database_patterns: None,
|
||||
visible_schemas: None,
|
||||
show_system_schemas: false,
|
||||
attached_databases: Vec::new(),
|
||||
@@ -734,6 +735,7 @@ mod tests {
|
||||
database: None,
|
||||
default_schema: None,
|
||||
visible_databases: None,
|
||||
visible_database_patterns: None,
|
||||
visible_schemas: None,
|
||||
show_system_schemas: false,
|
||||
attached_databases: Vec::new(),
|
||||
|
||||
@@ -657,6 +657,7 @@ mod tests {
|
||||
database: None,
|
||||
default_schema: None,
|
||||
visible_databases: None,
|
||||
visible_database_patterns: None,
|
||||
visible_schemas: None,
|
||||
show_system_schemas: false,
|
||||
attached_databases: vec![],
|
||||
|
||||
@@ -5633,6 +5633,7 @@ for line in sys.stdin:
|
||||
database: None,
|
||||
default_schema: None,
|
||||
visible_databases: None,
|
||||
visible_database_patterns: None,
|
||||
visible_schemas: None,
|
||||
show_system_schemas: false,
|
||||
attached_databases: Vec::new(),
|
||||
@@ -7665,6 +7666,7 @@ for line in sys.stdin:
|
||||
database: None,
|
||||
default_schema: None,
|
||||
visible_databases: None,
|
||||
visible_database_patterns: None,
|
||||
visible_schemas: None,
|
||||
show_system_schemas: false,
|
||||
attached_databases: Vec::new(),
|
||||
|
||||
@@ -3232,6 +3232,7 @@ mod tests {
|
||||
database: Some("demo".to_string()),
|
||||
default_schema: None,
|
||||
visible_databases: None,
|
||||
visible_database_patterns: None,
|
||||
visible_schemas: None,
|
||||
show_system_schemas: false,
|
||||
attached_databases: Vec::new(),
|
||||
|
||||
@@ -5296,6 +5296,7 @@ mod tests {
|
||||
database: None,
|
||||
default_schema: None,
|
||||
visible_databases: None,
|
||||
visible_database_patterns: None,
|
||||
visible_schemas: None,
|
||||
show_system_schemas: false,
|
||||
attached_databases: Vec::new(),
|
||||
@@ -5364,6 +5365,7 @@ mod tests {
|
||||
database: None,
|
||||
default_schema: None,
|
||||
visible_databases: None,
|
||||
visible_database_patterns: None,
|
||||
visible_schemas: None,
|
||||
show_system_schemas: false,
|
||||
attached_databases: Vec::new(),
|
||||
|
||||
@@ -11848,6 +11848,7 @@ SELECT 1 FROM dual"#
|
||||
database: None,
|
||||
default_schema: None,
|
||||
visible_databases: None,
|
||||
visible_database_patterns: None,
|
||||
visible_schemas: None,
|
||||
show_system_schemas: false,
|
||||
attached_databases: Vec::new(),
|
||||
|
||||
@@ -38,6 +38,7 @@ fn live_postgres_config(
|
||||
database: Some(database.to_string()),
|
||||
default_schema: None,
|
||||
visible_databases: None,
|
||||
visible_database_patterns: None,
|
||||
visible_schemas: None,
|
||||
attached_databases: Vec::new(),
|
||||
init_script: None,
|
||||
|
||||
@@ -34,6 +34,7 @@ fn live_postgres_config(
|
||||
database: Some(database.to_string()),
|
||||
default_schema: None,
|
||||
visible_databases: None,
|
||||
visible_database_patterns: None,
|
||||
visible_schemas: None,
|
||||
attached_databases: Vec::new(),
|
||||
init_script: None,
|
||||
|
||||
@@ -29,6 +29,7 @@ fn live_postgres_config(
|
||||
database: Some(database.to_string()),
|
||||
default_schema: None,
|
||||
visible_databases: None,
|
||||
visible_database_patterns: None,
|
||||
visible_schemas: None,
|
||||
attached_databases: Vec::new(),
|
||||
init_script: None,
|
||||
|
||||
@@ -34,6 +34,7 @@ fn live_postgres_config(
|
||||
database: Some(database.to_string()),
|
||||
default_schema: None,
|
||||
visible_databases: None,
|
||||
visible_database_patterns: None,
|
||||
visible_schemas: None,
|
||||
attached_databases: Vec::new(),
|
||||
init_script: None,
|
||||
|
||||
@@ -26,6 +26,7 @@ fn postgres_test_config(id: &str, database: &str) -> ConnectionConfig {
|
||||
database: Some(database.to_string()),
|
||||
default_schema: None,
|
||||
visible_databases: None,
|
||||
visible_database_patterns: None,
|
||||
visible_schemas: None,
|
||||
attached_databases: Vec::new(),
|
||||
init_script: None,
|
||||
|
||||
@@ -35,6 +35,7 @@ fn live_sqlserver_config(id: &str, database: &str) -> dbx_core::models::connecti
|
||||
database: Some(database.to_string()),
|
||||
default_schema: None,
|
||||
visible_databases: None,
|
||||
visible_database_patterns: None,
|
||||
visible_schemas: None,
|
||||
attached_databases: Vec::new(),
|
||||
init_script: None,
|
||||
|
||||
@@ -24,6 +24,7 @@ fn live_sqlserver_config(id: &str, database: &str) -> dbx_core::models::connecti
|
||||
database: Some(database.to_string()),
|
||||
default_schema: None,
|
||||
visible_databases: None,
|
||||
visible_database_patterns: None,
|
||||
visible_schemas: None,
|
||||
attached_databases: Vec::new(),
|
||||
init_script: None,
|
||||
|
||||
@@ -148,6 +148,7 @@ pub fn postgres_test_config(id: &str, port: u16) -> ConnectionConfig {
|
||||
database: Some("postgres".to_string()),
|
||||
default_schema: None,
|
||||
visible_databases: None,
|
||||
visible_database_patterns: None,
|
||||
visible_schemas: None,
|
||||
attached_databases: Vec::new(),
|
||||
init_script: None,
|
||||
|
||||
Reference in new issue
Block a user