229 lines
11 KiB
TypeScript
229 lines
11 KiB
TypeScript
import type { DatabaseType } from "@/types/database";
|
|
|
|
export type SidebarObjectKind = "TABLE" | "VIEW" | "MATERIALIZED_VIEW" | "PROCEDURE" | "FUNCTION" | "TRIGGER" | "EVENT" | "SEQUENCE" | "SYNONYM" | "JOB" | "PACKAGE" | "PACKAGE_BODY" | "TYPE" | "TYPE_BODY";
|
|
|
|
export interface DatabaseObjectCapabilities {
|
|
sidebarObjects: SidebarObjectKind[];
|
|
sourceReadable: SidebarObjectKind[];
|
|
executable: SidebarObjectKind[];
|
|
}
|
|
|
|
const TABLE_VIEW_OBJECTS: SidebarObjectKind[] = ["TABLE", "VIEW"];
|
|
const TABLE_VIEW_MV_OBJECTS: SidebarObjectKind[] = ["TABLE", "VIEW", "MATERIALIZED_VIEW"];
|
|
|
|
const ROUTINE_OBJECTS: SidebarObjectKind[] = ["TABLE", "VIEW", "PROCEDURE", "FUNCTION"];
|
|
const MYSQL_OBJECTS: SidebarObjectKind[] = ["TABLE", "VIEW", "PROCEDURE", "FUNCTION", "TRIGGER", "EVENT"];
|
|
|
|
// PostgreSQL-family databases with a verified pg_type listing path. TYPE only
|
|
// covers user-created types (enum/domain/composite/range/multirange/base);
|
|
// relation auto-generated row types stay hidden.
|
|
const POSTGRES_OBJECTS: SidebarObjectKind[] = ["TABLE", "VIEW", "MATERIALIZED_VIEW", "PROCEDURE", "FUNCTION", "SEQUENCE", "TYPE"];
|
|
const OPENGAUSS_A_OBJECTS: SidebarObjectKind[] = [...POSTGRES_OBJECTS, "PACKAGE", "PACKAGE_BODY"];
|
|
|
|
// KWDB is routed through the PostgreSQL pool but its pg_type catalog
|
|
// compatibility is not verified yet, so it stays on the pre-TYPE object set.
|
|
const POSTGRES_NO_TYPE_OBJECTS: SidebarObjectKind[] = ["TABLE", "VIEW", "MATERIALIZED_VIEW", "PROCEDURE", "FUNCTION", "SEQUENCE"];
|
|
|
|
// Kingbase and Vastbase agents support the same user-defined type listing via
|
|
// their own metadata query, but do not expose sequences. Kingbase additionally
|
|
// has a verified schema-trigger path; keep Vastbase separate until its Agent
|
|
// exposes the same parent-table-aware metadata contract.
|
|
const KINGBASE_OBJECTS: SidebarObjectKind[] = ["TABLE", "VIEW", "MATERIALIZED_VIEW", "PROCEDURE", "FUNCTION", "TRIGGER", "TYPE"];
|
|
const VASTBASE_OBJECTS: SidebarObjectKind[] = ["TABLE", "VIEW", "MATERIALIZED_VIEW", "PROCEDURE", "FUNCTION", "TYPE"];
|
|
|
|
const POSTGRES_LIKE_OBJECTS: SidebarObjectKind[] = ["TABLE", "VIEW", "MATERIALIZED_VIEW", "PROCEDURE", "FUNCTION"];
|
|
const ORACLE_OBJECTS: SidebarObjectKind[] = ["TABLE", "VIEW", "MATERIALIZED_VIEW", "PROCEDURE", "FUNCTION", "SEQUENCE", "SYNONYM", "PACKAGE", "PACKAGE_BODY"];
|
|
const OCEANBASE_ORACLE_OBJECTS: SidebarObjectKind[] = ["TABLE", "VIEW", "MATERIALIZED_VIEW", "PROCEDURE", "FUNCTION", "SEQUENCE", "SYNONYM", "PACKAGE", "PACKAGE_BODY"];
|
|
const DAMENG_OBJECTS: SidebarObjectKind[] = ["TABLE", "VIEW", "MATERIALIZED_VIEW", "PROCEDURE", "FUNCTION", "SEQUENCE", "PACKAGE", "PACKAGE_BODY"];
|
|
const XUGU_OBJECTS: SidebarObjectKind[] = ["TABLE", "VIEW", "PROCEDURE", "FUNCTION", "TRIGGER", "SEQUENCE", "SYNONYM", "PACKAGE", "PACKAGE_BODY", "TYPE", "TYPE_BODY"];
|
|
const PACKAGE_MEMBER_EXPANSION_DATABASES = new Set<DatabaseType>(["oracle", "xugu"]);
|
|
|
|
const DATABASE_TYPE_OBJECTS = new Map<DatabaseType, SidebarObjectKind[]>([
|
|
// postgres
|
|
["postgres", POSTGRES_OBJECTS],
|
|
["gaussdb", POSTGRES_OBJECTS],
|
|
["kwdb", POSTGRES_NO_TYPE_OBJECTS],
|
|
["opengauss", POSTGRES_OBJECTS],
|
|
// postgres like
|
|
["kingbase", KINGBASE_OBJECTS],
|
|
["highgo", POSTGRES_LIKE_OBJECTS],
|
|
["uxdb", POSTGRES_LIKE_OBJECTS],
|
|
["vastbase", VASTBASE_OBJECTS],
|
|
["redshift", POSTGRES_LIKE_OBJECTS],
|
|
// oracle
|
|
["oracle", ORACLE_OBJECTS],
|
|
["dameng", DAMENG_OBJECTS],
|
|
["oceanbase-oracle", OCEANBASE_ORACLE_OBJECTS],
|
|
["xugu", XUGU_OBJECTS],
|
|
["mysql", MYSQL_OBJECTS],
|
|
// Explicit entry so schema-diff routine gating can opt in without relying on the
|
|
// unknown-type ROUTINE_OBJECTS fallback. Keep the same object set the fallback
|
|
// already used (no TRIGGER/SEQUENCE expansion in this change).
|
|
["sqlserver", ROUTINE_OBJECTS],
|
|
// table and view
|
|
["sqlite", TABLE_VIEW_OBJECTS],
|
|
["rqlite", TABLE_VIEW_OBJECTS],
|
|
["turso", TABLE_VIEW_OBJECTS],
|
|
["cloudflare-d1", TABLE_VIEW_OBJECTS],
|
|
["duckdb", TABLE_VIEW_OBJECTS],
|
|
["clickhouse", TABLE_VIEW_OBJECTS],
|
|
// Doris: backend listing path still uses the generic SHOW TABLES path (see
|
|
// `list_tables_once` for `PoolKind::Mysql` in crates/dbx-core/src/schema/mod.rs)
|
|
// and lacks a MV classifier. Keep Doris on TABLE_VIEW_OBJECTS until a
|
|
// Doris-specific MV listing/classification lands, otherwise the UI advertises
|
|
// MV support that the backend cannot route.
|
|
["doris", TABLE_VIEW_OBJECTS],
|
|
["starrocks", TABLE_VIEW_MV_OBJECTS],
|
|
// Inceptor/Hive routines can be listed via JDBC plugin fallbacks (system.procedures_v/functions_v).
|
|
["hive", ROUTINE_OBJECTS],
|
|
// ArgoDB (Transwarp) shares the Hive agent; its catalog views
|
|
// (system.procedures_v / system.functions_v) expose routines natively.
|
|
["argo", ROUTINE_OBJECTS],
|
|
["kyuubi", TABLE_VIEW_OBJECTS],
|
|
["impala", TABLE_VIEW_OBJECTS],
|
|
["spark", TABLE_VIEW_OBJECTS],
|
|
["trino", TABLE_VIEW_OBJECTS],
|
|
["prestosql", TABLE_VIEW_OBJECTS],
|
|
["cassandra", TABLE_VIEW_OBJECTS],
|
|
["bigquery", TABLE_VIEW_OBJECTS],
|
|
// Cloud Spanner has no triggers, routines, sequences, or synonyms; without an
|
|
// explicit entry the sidebar would fall back to ROUTINE_OBJECTS.
|
|
["spanner", TABLE_VIEW_OBJECTS],
|
|
["kylin", TABLE_VIEW_OBJECTS],
|
|
["ignite", TABLE_VIEW_OBJECTS],
|
|
["ignite3", TABLE_VIEW_OBJECTS],
|
|
["tdengine", TABLE_VIEW_OBJECTS],
|
|
["iotdb", TABLE_VIEW_OBJECTS],
|
|
["neo4j", TABLE_VIEW_OBJECTS],
|
|
// others
|
|
["influxdb", ["TABLE"]],
|
|
["influxdb3", ["TABLE"]],
|
|
["victoriametrics", ["TABLE"]],
|
|
["hbase", ["TABLE"]],
|
|
["questdb", ["TABLE", "VIEW", "MATERIALIZED_VIEW"]],
|
|
["manticoresearch", ["TABLE", "FUNCTION"]],
|
|
["databend", ["TABLE", "VIEW", "PROCEDURE"]],
|
|
]);
|
|
/**
|
|
* Whether a kind is readable as object source for the given connection type.
|
|
* TYPE/TYPE_BODY only have a real source implementation on Xugu; PostgreSQL-
|
|
* family databases list types without a CREATE TYPE getter this cycle.
|
|
*/
|
|
function isSourceReadableObjectKind(kind: SidebarObjectKind, dbType?: DatabaseType): boolean {
|
|
if (kind === "TABLE") return false;
|
|
if (kind === "TYPE" || kind === "TYPE_BODY") return supportsTypeObjectSource(dbType);
|
|
return true;
|
|
}
|
|
|
|
export function databaseObjectCapabilities(dbType?: DatabaseType, compatibilityMode?: string): DatabaseObjectCapabilities {
|
|
const sidebarObjects = sidebarObjectKindsForDatabase(dbType, compatibilityMode);
|
|
return {
|
|
sidebarObjects,
|
|
sourceReadable: sidebarObjects.filter((kind) => isSourceReadableObjectKind(kind, dbType)),
|
|
executable: sidebarObjects.filter((kind) => kind === "PROCEDURE"),
|
|
};
|
|
}
|
|
|
|
const SCHEMA_DIFF_ROUTINE_KINDS = ["PROCEDURE", "FUNCTION"] as const;
|
|
|
|
/** Same-dialect families with verified list_objects + get_object_source (or PG catalog) paths. */
|
|
const SCHEMA_DIFF_ROUTINE_FAMILY = new Map<DatabaseType, "postgres" | "mysql" | "sqlserver">([
|
|
["postgres", "postgres"],
|
|
["opengauss", "postgres"],
|
|
["gaussdb", "postgres"],
|
|
["kwdb", "postgres"],
|
|
["kingbase", "postgres"],
|
|
["highgo", "postgres"],
|
|
["uxdb", "postgres"],
|
|
["vastbase", "postgres"],
|
|
["redshift", "postgres"],
|
|
["mysql", "mysql"],
|
|
["sqlserver", "sqlserver"],
|
|
]);
|
|
|
|
/**
|
|
* Schema Diff routine compare is limited to an allowlist of same-dialect families.
|
|
* Sidebar may list routines for more engines (oracle/hive/…); those stay out of
|
|
* schema-diff until a verified compare path lands. Cross-family pairs never match.
|
|
*/
|
|
export function supportsSchemaDiffRoutines(dbType?: DatabaseType): boolean {
|
|
if (!dbType || !SCHEMA_DIFF_ROUTINE_FAMILY.has(dbType)) return false;
|
|
const { sidebarObjects, sourceReadable } = databaseObjectCapabilities(dbType);
|
|
return SCHEMA_DIFF_ROUTINE_KINDS.some((kind) => sidebarObjects.includes(kind) && sourceReadable.includes(kind));
|
|
}
|
|
|
|
export function schemaDiffRoutineObjectTypes(dbType?: DatabaseType): Array<"PROCEDURE" | "FUNCTION"> {
|
|
if (!supportsSchemaDiffRoutines(dbType)) return [];
|
|
const { sidebarObjects, sourceReadable } = databaseObjectCapabilities(dbType);
|
|
return SCHEMA_DIFF_ROUTINE_KINDS.filter((kind) => sidebarObjects.includes(kind) && sourceReadable.includes(kind));
|
|
}
|
|
|
|
export function schemaDiffRoutineObjectTypesIntersection(sourceDbType?: DatabaseType, targetDbType?: DatabaseType): Array<"PROCEDURE" | "FUNCTION"> {
|
|
if (!sourceDbType || !targetDbType) return [];
|
|
const sourceFamily = SCHEMA_DIFF_ROUTINE_FAMILY.get(sourceDbType);
|
|
const targetFamily = SCHEMA_DIFF_ROUTINE_FAMILY.get(targetDbType);
|
|
if (!sourceFamily || sourceFamily !== targetFamily) return [];
|
|
const sourceTypes = new Set(schemaDiffRoutineObjectTypes(sourceDbType));
|
|
return schemaDiffRoutineObjectTypes(targetDbType).filter((kind) => sourceTypes.has(kind));
|
|
}
|
|
|
|
export function sidebarObjectKindsForDatabase(dbType?: DatabaseType, compatibilityMode?: string): SidebarObjectKind[] {
|
|
if (!dbType) return [...TABLE_VIEW_OBJECTS];
|
|
if (dbType === "opengauss" && compatibilityMode?.trim().toUpperCase() === "A") return [...OPENGAUSS_A_OBJECTS];
|
|
return DATABASE_TYPE_OBJECTS.get(dbType) ?? [...ROUTINE_OBJECTS];
|
|
}
|
|
|
|
/**
|
|
* Whether a connection's TYPE tree nodes may be opened as object source.
|
|
*
|
|
* Xugu has a real TYPE/TYPE_BODY source implementation. PostgreSQL-family
|
|
* databases only list user-defined types this cycle; their CREATE TYPE DDL has
|
|
* no unified catalog getter, so opening source would error. Callers must gate
|
|
* the source action (single/double click, context menu, shortcuts) on this
|
|
* before dispatching getObjectSource.
|
|
*/
|
|
export function supportsTypeObjectSource(dbType?: DatabaseType): boolean {
|
|
return dbType === "xugu";
|
|
}
|
|
|
|
export type CustomTypeCapabilities = {
|
|
details: boolean;
|
|
members: boolean;
|
|
ddl: boolean;
|
|
};
|
|
|
|
const VERIFIED_CUSTOM_TYPE_DATABASES = new Set<DatabaseType>(["postgres", "opengauss", "gaussdb", "kingbase", "vastbase"]);
|
|
|
|
/**
|
|
* Whether a connection may open read-only custom type details (phase 2).
|
|
* Kept separate from the listing capability so a future per-kind DDL toggle
|
|
* can be introduced without touching the object-list sets.
|
|
*/
|
|
export function customTypeCapabilities(dbType?: DatabaseType): CustomTypeCapabilities {
|
|
const supported = !!dbType && VERIFIED_CUSTOM_TYPE_DATABASES.has(dbType);
|
|
return { details: supported, members: supported, ddl: supported };
|
|
}
|
|
|
|
export function supportsPackageMemberExpansion(dbType?: DatabaseType, compatibilityMode?: string): boolean {
|
|
return !!dbType && (PACKAGE_MEMBER_EXPANSION_DATABASES.has(dbType) || (dbType === "opengauss" && compatibilityMode?.trim().toUpperCase() === "A"));
|
|
}
|
|
|
|
export function normalizeSidebarObjectKind(type: string): SidebarObjectKind {
|
|
const value = type.toUpperCase();
|
|
const normalized = value.replace(/[\s-]+/g, "_");
|
|
if (normalized.includes("PACKAGE_BODY")) return "PACKAGE_BODY";
|
|
if (normalized.includes("TYPE_BODY")) return "TYPE_BODY";
|
|
if (normalized.includes("PACKAGE")) return "PACKAGE";
|
|
if (normalized.includes("TRIGGER")) return "TRIGGER";
|
|
if (normalized.includes("EVENT")) return "EVENT";
|
|
if (normalized.includes("TYPE")) return "TYPE";
|
|
if (normalized.includes("MATERIALIZED_VIEW")) return "MATERIALIZED_VIEW";
|
|
if (value.includes("VIEW")) return "VIEW";
|
|
if (value.includes("SEQ")) return "SEQUENCE";
|
|
if (value.includes("SYNONYM")) return "SYNONYM";
|
|
if (value.includes("JOB")) return "JOB";
|
|
if (value.includes("PROC")) return "PROCEDURE";
|
|
if (value.includes("FUNC")) return "FUNCTION";
|
|
return "TABLE";
|
|
}
|