feat(metadata): honor system schema visibility

This commit is contained in:
amwps290 authored and GitHub committed 2026-07-29 03:23:04 +08:00
1 parent a07def6042
commit 820b8cd26f
44 files changed
+422 -47

No files matched your search

@@ -0,0 +1,52 @@
import { describe, expect, it } from "vitest";
import { filterSchemaNamesForConnection, filterSchemaNamesForVisiblePicker, isSystemSchemaName } from "@/lib/database/visibleDatabases";
describe("visibleDatabases schema filtering", () => {
it("hides common Kingbase system schemas by default", () => {
expect(filterSchemaNamesForVisiblePicker(["anon", "dbms_job", "information_schema", "pg_catalog", "public", "sys", "sys_catalog", "wmsys", "xlog_record_read"], { db_type: "kingbase", username: "test" })).toEqual(["public"]);
});
it("keeps the current schema visible even when it matches a system schema name", () => {
expect(
filterSchemaNamesForVisiblePicker(["public", "sys", "sys_catalog"], {
db_type: "kingbase",
username: "sys",
}),
).toEqual(["public", "sys"]);
});
it("keeps Oracle DIP visible while hiding default system schemas", () => {
expect(filterSchemaNamesForConnection(["DBX_TEST", "DIP", "SYSTEM"], { db_type: "oracle", database: "XE" }, "XE")).toEqual(["DBX_TEST", "DIP"]);
});
it("keeps the Dameng login schema visible while hiding default system schemas", () => {
expect(filterSchemaNamesForConnection(["APP", "SYS", "SYSDBA", "SYSDBO", "SYSAUDITOR"], { db_type: "dameng", username: "SYSDBA" }, "")).toEqual(["APP", "SYSDBA"]);
});
it("hides openGauss system schemas and prefixes while keeping user schemas", () => {
expect(filterSchemaNamesForVisiblePicker(["blockchain", "cstore", "db4ai", "dbe_perf", "dbe_pldeveloper", "dbe_sql_util", "information_schema", "pg_catalog", "public", "snapshot", "sqladvisor", "xmltype"], { db_type: "opengauss", username: "app_user" })).toEqual(["public"]);
});
it("keeps all schemas visible when show-system-schemas is enabled", () => {
expect(filterSchemaNamesForConnection(["blockchain", "db4ai", "public", "test2", "xmltype"], { db_type: "opengauss", show_system_schemas: true }, "postgres")).toEqual(["blockchain", "db4ai", "public", "test2", "xmltype"]);
});
it("respects explicit visible schema configuration after default filtering", () => {
expect(
filterSchemaNamesForConnection(
["public", "sys_catalog", "reporting"],
{
db_type: "kingbase",
visible_schemas: { test: ["sys_catalog"] },
},
"test",
),
).toEqual(["sys_catalog"]);
});
it("matches prefix-based system schema rules", () => {
expect(isSystemSchemaName("kingbase", "xlog_record_read")).toBe(true);
expect(isSystemSchemaName("opengauss", "dbe_pldeveloper")).toBe(true);
expect(isSystemSchemaName("kingbase", "public")).toBe(false);
});
});
@@ -1,5 +1,18 @@
import type { ConnectionConfig, DatabaseType } from "@/types/database";
type SystemNameRules = {
exact?: ReadonlySet<string>;
prefixes?: readonly string[];
};
type SchemaFilterOptions = {
showSystemSchemas?: boolean;
};
function schemaFilterShowSystemSchemas(connection: Partial<Pick<ConnectionConfig, "show_system_schemas">> | undefined, options?: SchemaFilterOptions): boolean {
return options?.showSystemSchemas ?? connection?.show_system_schemas === true;
}
const SYSTEM_DATABASE_RULES: Partial<Record<DatabaseType, ReadonlySet<string>>> = {
mysql: new Set(["information_schema", "mysql", "performance_schema", "sys"]),
doris: new Set(["information_schema", "mysql", "performance_schema", "sys"]),
@@ -59,6 +72,67 @@ const SYSTEM_DATABASE_RULES: Partial<Record<DatabaseType, ReadonlySet<string>>>
snowflake: new Set(["snowflake", "snowflake_sample_data"]),
};
const POSTGRES_LIKE_SYSTEM_SCHEMA_RULES: SystemNameRules = {
exact: new Set(["information_schema", "pg_catalog", "pg_toast"]),
prefixes: ["pg_temp_", "pg_toast_temp_"],
};
const SYSTEM_SCHEMA_RULES: Partial<Record<DatabaseType, SystemNameRules>> = {
oracle: {
exact: new Set([
"anonymous",
"appqossys",
"audsys",
"ctxsys",
"dbsnmp",
"dvf",
"dvsys",
"exfsys",
"flows_files",
"gsmadmin_internal",
"mddata",
"mdsys",
"mgmt_view",
"olapsys",
"orddata",
"ordplugins",
"ordsys",
"outln",
"owbsys",
"remote_scheduler_agent",
"si_informtn_schema",
"sys",
"sysback",
"sysdg",
"syskm",
"system",
"wmsys",
"xdb",
"xs$null",
]),
},
dameng: {
exact: new Set(["_sys_statistics", "ctisys", "dba", "sys", "sys_dba", "sys_phm", "sysauditor", "sysdba", "sysdbo", "syssso", "system"]),
},
postgres: POSTGRES_LIKE_SYSTEM_SCHEMA_RULES,
gaussdb: {
exact: new Set(["blockchain", "coverage", "cstore", "db4ai", "dbe_perf", "dbe_pldebugger", "dbe_pldeveloper", "dbe_sql_util", "information_schema", "pg_catalog", "pg_toast", "pkg_service", "snapshot", "sqladvisor", "xmltype"]),
prefixes: ["pg_temp_", "pg_toast_temp_", "dbe_"],
},
kwdb: POSTGRES_LIKE_SYSTEM_SCHEMA_RULES,
opengauss: {
exact: new Set(["blockchain", "coverage", "cstore", "db4ai", "dbe_perf", "dbe_pldebugger", "dbe_pldeveloper", "dbe_sql_util", "information_schema", "pg_catalog", "pg_toast", "pkg_service", "snapshot", "sqladvisor", "xmltype"]),
prefixes: ["pg_temp_", "pg_toast_temp_", "dbe_"],
},
questdb: POSTGRES_LIKE_SYSTEM_SCHEMA_RULES,
kingbase: {
exact: new Set(["anon", "dbms_job", "dbms_scheduler", "dbms_sql", "information_schema", "kdb_schedule", "perf", "pg_bitmapindex", "pg_catalog", "pg_toast", "src_restrict", "sys", "sys_catalog", "sys_hm", "sysaudit", "sysmac", "wmsys"]),
prefixes: ["dbms_", "pg_temp_", "pg_toast_temp_", "sys_temp_", "sys_toast_temp_", "xlog_"],
},
highgo: POSTGRES_LIKE_SYSTEM_SCHEMA_RULES,
vastbase: POSTGRES_LIKE_SYSTEM_SCHEMA_RULES,
};
export function visibleDatabaseFilterIsEnabled(visibleDatabases: string[] | undefined): boolean {
return Array.isArray(visibleDatabases);
}
@@ -88,6 +162,15 @@ export function isSystemDatabaseName(databaseType: DatabaseType | undefined, dat
return SYSTEM_DATABASE_RULES[databaseType]?.has(databaseName.toLowerCase()) ?? false;
}
export function isSystemSchemaName(databaseType: DatabaseType | undefined, schemaName: string): boolean {
if (!databaseType) return false;
const normalized = schemaName.toLowerCase();
const rules = SYSTEM_SCHEMA_RULES[databaseType];
if (!rules) return false;
if (rules.exact?.has(normalized)) return true;
return rules.prefixes?.some((prefix) => normalized.startsWith(prefix)) ?? false;
}
export function filterDatabaseNamesForConnection(databaseNames: string[], connection: Pick<ConnectionConfig, "db_type" | "driver_profile" | "visible_databases"> | undefined): string[] {
const visibleDatabases = connection?.visible_databases;
if (visibleDatabaseFilterIsEnabled(visibleDatabases)) {
@@ -103,9 +186,10 @@ export function filterDatabaseNamesForVisiblePicker(databaseNames: string[], con
return databaseNames.filter((name) => !isSystemDatabaseName(connection?.db_type, name));
}
export function filterSchemaNamesForVisiblePicker(schemaNames: string[], connection: Partial<Pick<ConnectionConfig, "db_type" | "username">> | undefined): string[] {
export function filterSchemaNamesForVisiblePicker(schemaNames: string[], connection: Partial<Pick<ConnectionConfig, "db_type" | "username" | "show_system_schemas">> | undefined, options?: SchemaFilterOptions): string[] {
if (schemaFilterShowSystemSchemas(connection, options)) return schemaNames;
const currentSchema = connection?.username?.trim().toLowerCase();
return schemaNames.filter((name) => name.toLowerCase() === currentSchema || !isSystemDatabaseName(connection?.db_type, name));
return schemaNames.filter((name) => name.toLowerCase() === currentSchema || !isSystemSchemaName(connection?.db_type, name));
}
export function connectionUsesVisibleSchemaFilter(connection: Pick<ConnectionConfig, "db_type"> | undefined): boolean {
@@ -116,13 +200,18 @@ export function visibleSchemaFilterIsEnabled(visibleSchemas: Record<string, stri
return Array.isArray(visibleSchemas?.[database]);
}
export function filterSchemaNamesForConnection(schemaNames: string[], connection: (Pick<ConnectionConfig, "db_type" | "visible_schemas" | "visible_databases"> & Partial<Pick<ConnectionConfig, "username">>) | undefined, database: string): string[] {
export function filterSchemaNamesForConnection(
schemaNames: string[],
connection: (Pick<ConnectionConfig, "db_type" | "visible_schemas" | "visible_databases" | "show_system_schemas"> & Partial<Pick<ConnectionConfig, "username">>) | 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);
}
return filterSchemaNamesForVisiblePicker(schemaNames, connection);
return filterSchemaNamesForVisiblePicker(schemaNames, connection, options);
}
const visible = new Set(visibleSchemas![database]);
return schemaNames.filter((name) => visible.has(name));