Files
dbx/apps/desktop/src/lib/database/visibleDatabases.ts
T

234 lines
9.3 KiB
TypeScript

import type { ConnectionConfig, DatabaseType } from "@/types/database";
import { connectionUsesConnectionRootSchemaMode, effectiveDatabaseTypeForConnection } from "@/lib/database/jdbcDialect";
type VisibleDatabaseConnection = Partial<ConnectionConfig>;
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"]),
starrocks: new Set(["information_schema", "mysql", "performance_schema", "sys"]),
manticoresearch: new Set(["information_schema", "mysql", "performance_schema", "sys"]),
goldendb: new Set(["information_schema", "mysql", "performance_schema", "sys"]),
gbase: new Set(["information_schema", "mysql", "performance_schema", "sys"]),
postgres: new Set(["template0", "template1"]),
gaussdb: new Set(["template0", "template1"]),
kwdb: new Set(["template0", "template1"]),
opengauss: new Set(["template0", "template1"]),
questdb: new Set(["template0", "template1"]),
kingbase: new Set(["template0", "template1"]),
highgo: new Set(["template0", "template1"]),
uxdb: new Set(["template0", "template1"]),
vastbase: new Set(["template0", "template1"]),
redshift: new Set(["template0", "template1"]),
clickhouse: new Set(["information_schema", "system"]),
tdengine: new Set(["information_schema", "performance_schema"]),
sqlserver: new Set(["master", "model", "msdb", "tempdb"]),
mongodb: new Set(["admin", "config", "local"]),
oracle: 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: new Set(["_sys_statistics", "ctisys", "dba", "sys", "sys_dba", "sys_phm", "sysauditor", "sysdba", "sysdbo", "syssso", "system"]),
saphana: new Set(["_sys_afl", "_sys_bi", "_sys_bic", "_sys_repo", "_sys_statistics", "sys"]),
cassandra: new Set(["system", "system_auth", "system_distributed", "system_schema", "system_traces", "system_views", "system_virtual_schema"]),
neo4j: new Set(["system"]),
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);
}
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 normalizeVisibleDatabaseSelection(selectedNames: string[], databaseNames: string[]): string[] {
const available = new Set(databaseNames);
const seen = new Set<string>();
return selectedNames.filter((name) => {
if (!available.has(name) || seen.has(name)) return false;
seen.add(name);
return true;
});
}
export function isSystemDatabaseName(databaseType: DatabaseType | undefined, databaseName: string): boolean {
if (!databaseType) return false;
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: VisibleDatabaseConnection | undefined): string[] {
const visibleDatabases = connection?.visible_databases;
if (visibleDatabaseFilterIsEnabled(visibleDatabases)) {
return filterVisibleDatabaseNames(databaseNames, visibleDatabases);
}
return filterDatabaseNamesForVisiblePicker(databaseNames, connection);
}
export function filterDatabaseNamesForVisiblePicker(databaseNames: string[], connection: VisibleDatabaseConnection | undefined): string[] {
if (connection?.db_type === "gbase" && connection.driver_profile === "gbase8s") {
return databaseNames;
}
return databaseNames.filter((name) => !isSystemDatabaseName(effectiveDatabaseTypeForConnection(connection), name));
}
export function filterSchemaNamesForVisiblePicker(schemaNames: string[], connection: VisibleDatabaseConnection | undefined, options?: SchemaFilterOptions): string[] {
if (schemaFilterShowSystemSchemas(connection, options)) return schemaNames;
const currentSchema = connection?.username?.trim().toLowerCase();
const databaseType = effectiveDatabaseTypeForConnection(connection);
return schemaNames.filter((name) => name.toLowerCase() === currentSchema || !isSystemSchemaName(databaseType, name));
}
export function connectionUsesVisibleSchemaFilter(connection: VisibleDatabaseConnection | undefined): boolean {
return connectionUsesConnectionRootSchemaMode(connection);
}
export function visibleSchemaFilterIsEnabled(visibleSchemas: Record<string, string[]> | undefined, database: string): boolean {
return Array.isArray(visibleSchemas?.[database]);
}
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);
}
return filterSchemaNamesForVisiblePicker(schemaNames, connection, options);
}
const visible = new Set(visibleSchemas![database]);
return schemaNames.filter((name) => visible.has(name));
}
export function normalizeVisibleSchemaSelection(selectedNames: string[], schemaNames: string[]): string[] {
const available = new Set(schemaNames);
const seen = new Set<string>();
return selectedNames.filter((name) => {
if (!available.has(name) || seen.has(name)) return false;
seen.add(name);
return true;
});
}
const DRAFT_VISIBLE_SCHEMAS_PREFIX = "__visible_schema_draft_";
export function buildDraftVisibleSchemasConnectionId(seed: string): string {
return `${DRAFT_VISIBLE_SCHEMAS_PREFIX}${seed}`;
}