feat(database): add database creation and property editing
This commit is contained in:
1 parent
3fdcc2d454
commit
2bfd03cca4
23 files changed
+1101
-57
No files matched your search
@@ -7,6 +7,8 @@ const MYSQL_COMPATIBLE_TYPES = new Set<DatabaseType>(["mysql", "doris", "starroc
|
||||
export interface CreateDatabaseSqlOptions {
|
||||
databaseType?: DatabaseType;
|
||||
driverProfile?: string | null;
|
||||
target?: "database" | "schema" | "catalog" | "namespace";
|
||||
parent?: string | null;
|
||||
name: string;
|
||||
charset?: string;
|
||||
collation?: string;
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
export * from "@/lib/database/databaseCapabilitySets";
|
||||
export * from "@/lib/database/databaseDriverManifest";
|
||||
export * from "@/lib/database/databaseFeatureSupport";
|
||||
export * from "@/lib/database/databaseNamespaceCreation";
|
||||
export * from "@/lib/database/databasePropertyEditing";
|
||||
export * from "@/lib/database/databaseObjectCapabilities";
|
||||
export * from "@/lib/database/databaseTableDataCapabilities";
|
||||
@@ -0,0 +1,102 @@
|
||||
import type { ConnectionConfig, DatabaseType, TreeNodeType } from "@/types/database";
|
||||
|
||||
export type DatabaseNamespaceCreationTarget = "database" | "schema" | "attach" | "special";
|
||||
|
||||
type ConnectionCreationTarget = Extract<DatabaseNamespaceCreationTarget, "database" | "attach" | "special">;
|
||||
type DatabaseNodeCreationTarget = Extract<DatabaseNamespaceCreationTarget, "schema">;
|
||||
|
||||
export interface DatabaseNamespaceCreationMatrixEntry {
|
||||
connection?: ConnectionCreationTarget;
|
||||
database?: DatabaseNodeCreationTarget;
|
||||
deferred?: string;
|
||||
}
|
||||
|
||||
type CreationConnection = Pick<ConnectionConfig, "db_type" | "driver_profile" | "read_only"> | undefined;
|
||||
|
||||
// Keep creation target-specific: many products expose schemas, files, or provider-managed namespaces instead of a top-level database.
|
||||
export const DATABASE_NAMESPACE_CREATION_MATRIX = {
|
||||
mysql: { connection: "database" },
|
||||
postgres: { connection: "database", database: "schema" },
|
||||
sqlite: { deferred: "file-backed; create a new connection/file instead" },
|
||||
rqlite: { deferred: "single SQLite-compatible database per node" },
|
||||
turso: { deferred: "remote libSQL database lifecycle is provider-managed" },
|
||||
redis: { deferred: "numbered logical databases are server-configured" },
|
||||
duckdb: { connection: "attach" },
|
||||
clickhouse: { connection: "database" },
|
||||
sqlserver: { connection: "database", database: "schema" },
|
||||
mongodb: { connection: "special" },
|
||||
oracle: { deferred: "Oracle schemas are users; database creation is not a normal connected DDL action" },
|
||||
elasticsearch: { deferred: "index creation is not modeled as database creation" },
|
||||
qdrant: { deferred: "collection creation is separate from database creation" },
|
||||
milvus: { deferred: "collection/database lifecycle needs a dedicated vector workflow" },
|
||||
weaviate: { deferred: "collection creation is separate from database creation" },
|
||||
chromadb: { deferred: "collection creation is separate from database creation" },
|
||||
doris: { connection: "database" },
|
||||
starrocks: { connection: "database" },
|
||||
manticoresearch: { deferred: "index/table creation is not database creation" },
|
||||
databend: { connection: "database" },
|
||||
redshift: { connection: "database", database: "schema" },
|
||||
dameng: { deferred: "schema/user creation requires a dedicated user workflow" },
|
||||
gaussdb: { connection: "database", database: "schema" },
|
||||
kingbase: { connection: "database", database: "schema" },
|
||||
highgo: { connection: "database", database: "schema" },
|
||||
vastbase: { connection: "database", database: "schema" },
|
||||
goldendb: { connection: "database" },
|
||||
kwdb: { connection: "database", database: "schema" },
|
||||
yashandb: { connection: "database", database: "schema" },
|
||||
databricks: { database: "schema" },
|
||||
saphana: { database: "schema" },
|
||||
teradata: { database: "schema" },
|
||||
vertica: { database: "schema" },
|
||||
firebird: { deferred: "database files are created through connection provisioning" },
|
||||
exasol: { database: "schema" },
|
||||
opengauss: { connection: "database", database: "schema" },
|
||||
"oceanbase-oracle": { deferred: "Oracle-mode schemas are users; use a dedicated user workflow" },
|
||||
questdb: { deferred: "single database model in DBX" },
|
||||
gbase: { database: "schema" },
|
||||
access: { deferred: "file-backed; create a new connection/file instead" },
|
||||
h2: { database: "schema" },
|
||||
snowflake: { connection: "database", database: "schema" },
|
||||
trino: { database: "schema" },
|
||||
prestosql: { database: "schema" },
|
||||
hive: { deferred: "Hive database creation needs agent metadata validation first" },
|
||||
db2: { database: "schema" },
|
||||
informix: { database: "schema" },
|
||||
neo4j: { deferred: "database creation depends on edition/admin privileges" },
|
||||
cassandra: { deferred: "keyspace creation requires replication options" },
|
||||
bigquery: { deferred: "dataset creation needs project/location options" },
|
||||
kylin: { deferred: "project/model lifecycle is not SQL database creation" },
|
||||
sundb: { deferred: "creation semantics not verified for first pass" },
|
||||
oscar: { database: "schema" },
|
||||
tdengine: { connection: "database" },
|
||||
xugu: { database: "schema" },
|
||||
iotdb: { deferred: "storage group/database semantics need dedicated IoTDB handling" },
|
||||
etcd: { deferred: "key-value namespaces are not databases" },
|
||||
zookeeper: { deferred: "key-value namespaces are not databases" },
|
||||
iris: { database: "schema" },
|
||||
influxdb: { connection: "database" },
|
||||
jdbc: { deferred: "generic JDBC does not expose a reliable dialect-specific create target" },
|
||||
mq: { deferred: "message queue namespaces are handled by MQ admin panels" },
|
||||
nacos: { deferred: "Nacos namespace creation already uses the Nacos admin flow" },
|
||||
} satisfies Record<DatabaseType, DatabaseNamespaceCreationMatrixEntry>;
|
||||
|
||||
export function connectionNamespaceCreationTarget(connection: CreationConnection): ConnectionCreationTarget | null {
|
||||
if (!connection || connection.read_only) return null;
|
||||
if (connection.db_type === "mongodb" && connection.driver_profile === "mongodb-legacy") return null;
|
||||
const entry: DatabaseNamespaceCreationMatrixEntry = DATABASE_NAMESPACE_CREATION_MATRIX[connection.db_type];
|
||||
return entry.connection ?? null;
|
||||
}
|
||||
|
||||
export function databaseNodeNamespaceCreationTarget(connection: CreationConnection, node: Pick<{ type: TreeNodeType; database?: string | null }, "type" | "database">): DatabaseNodeCreationTarget | null {
|
||||
if (!connection || connection.read_only || node.type !== "database" || !node.database) return null;
|
||||
const entry: DatabaseNamespaceCreationMatrixEntry = DATABASE_NAMESPACE_CREATION_MATRIX[connection.db_type];
|
||||
return entry.database ?? null;
|
||||
}
|
||||
|
||||
export function canCreateConnectionNamespace(connection: CreationConnection): boolean {
|
||||
return connectionNamespaceCreationTarget(connection) !== null;
|
||||
}
|
||||
|
||||
export function canCreateDatabaseNodeNamespace(connection: CreationConnection, node: Pick<{ type: TreeNodeType; database?: string | null }, "type" | "database">): boolean {
|
||||
return databaseNodeNamespaceCreationTarget(connection, node) !== null;
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
import type { ConnectionConfig, DatabaseType, TreeNodeType } from "@/types/database";
|
||||
import { supportsCreateDatabaseCharset } from "@/lib/database/createDatabaseSql";
|
||||
|
||||
export type DatabasePropertyEditGroup = "charsetCollation" | "databaseComment" | "schemaComment";
|
||||
|
||||
export interface DatabasePropertyEditingEntry {
|
||||
database?: DatabasePropertyEditGroup[];
|
||||
schema?: DatabasePropertyEditGroup[];
|
||||
deferred?: string;
|
||||
}
|
||||
|
||||
type PropertyEditConnection = Pick<ConnectionConfig, "db_type" | "driver_profile" | "read_only"> | undefined;
|
||||
type DatabaseNode = Pick<{ type: TreeNodeType; database?: string | null }, "type" | "database">;
|
||||
type SchemaNode = Pick<{ type: TreeNodeType; database?: string | null; schema?: string | null }, "type" | "database" | "schema">;
|
||||
|
||||
const POSTGRES_COMMENT_TYPES = new Set<DatabaseType>(["postgres", "gaussdb", "kwdb", "kingbase", "highgo", "vastbase", "opengauss", "yashandb"]);
|
||||
|
||||
export const DATABASE_PROPERTY_EDITING_MATRIX = {
|
||||
mysql: { database: ["charsetCollation"] },
|
||||
postgres: { database: ["databaseComment"], schema: ["schemaComment"] },
|
||||
sqlite: { deferred: "file-backed database properties are not edited in-place" },
|
||||
rqlite: { deferred: "single SQLite-compatible database per node" },
|
||||
turso: { deferred: "remote libSQL database lifecycle is provider-managed" },
|
||||
redis: { deferred: "numbered logical databases are server-configured" },
|
||||
duckdb: { deferred: "attached database file properties need a dedicated DuckDB workflow" },
|
||||
clickhouse: { deferred: "database property editing not verified for first pass" },
|
||||
sqlserver: { deferred: "database options are broad administrative settings and need a dedicated editor" },
|
||||
mongodb: { deferred: "database options are not modeled as SQL database properties" },
|
||||
oracle: { deferred: "Oracle database properties are instance/user/tablespace administration" },
|
||||
elasticsearch: { deferred: "index settings are not database properties" },
|
||||
qdrant: { deferred: "collection settings are not database properties" },
|
||||
milvus: { deferred: "collection/database settings need a dedicated vector workflow" },
|
||||
weaviate: { deferred: "collection settings are not database properties" },
|
||||
chromadb: { deferred: "collection settings are not database properties" },
|
||||
doris: { deferred: "database collation metadata is MySQL-compatible only and not an editable database default" },
|
||||
starrocks: { deferred: "ALTER DATABASE edits quota/rename/storage volume, not charset/collation defaults" },
|
||||
manticoresearch: { deferred: "index/table settings are not database properties" },
|
||||
databend: { deferred: "database property editing not verified for first pass" },
|
||||
redshift: { deferred: "comment catalog compatibility not verified for first pass" },
|
||||
dameng: { deferred: "schema/user settings need a dedicated workflow" },
|
||||
gaussdb: { database: ["databaseComment"], schema: ["schemaComment"] },
|
||||
kingbase: { database: ["databaseComment"], schema: ["schemaComment"] },
|
||||
highgo: { database: ["databaseComment"], schema: ["schemaComment"] },
|
||||
vastbase: { database: ["databaseComment"], schema: ["schemaComment"] },
|
||||
goldendb: { database: ["charsetCollation"] },
|
||||
kwdb: { database: ["databaseComment"], schema: ["schemaComment"] },
|
||||
yashandb: { database: ["databaseComment"], schema: ["schemaComment"] },
|
||||
databricks: { deferred: "catalog/schema properties need warehouse-specific handling" },
|
||||
saphana: { deferred: "schema properties need product-specific handling" },
|
||||
teradata: { deferred: "database properties need product-specific handling" },
|
||||
vertica: { deferred: "schema properties need product-specific handling" },
|
||||
firebird: { deferred: "database files are managed through connection provisioning" },
|
||||
exasol: { deferred: "schema properties need product-specific handling" },
|
||||
opengauss: { database: ["databaseComment"], schema: ["schemaComment"] },
|
||||
"oceanbase-oracle": { deferred: "Oracle-mode schemas are users; use a dedicated user workflow" },
|
||||
questdb: { deferred: "single database model in DBX" },
|
||||
gbase: { deferred: "schema properties need product-specific handling" },
|
||||
access: { deferred: "file-backed database properties are not edited in-place" },
|
||||
h2: { deferred: "schema properties need product-specific handling" },
|
||||
snowflake: { deferred: "database/schema properties need Snowflake-specific options" },
|
||||
trino: { deferred: "catalog properties are connector-managed" },
|
||||
prestosql: { deferred: "catalog properties are connector-managed" },
|
||||
hive: { deferred: "database properties need agent metadata validation first" },
|
||||
db2: { deferred: "schema properties need product-specific handling" },
|
||||
informix: { deferred: "schema properties need product-specific handling" },
|
||||
neo4j: { deferred: "database properties depend on edition/admin privileges" },
|
||||
cassandra: { deferred: "keyspace properties require replication option handling" },
|
||||
bigquery: { deferred: "dataset properties need project/location-specific handling" },
|
||||
kylin: { deferred: "project/model lifecycle is not SQL database property editing" },
|
||||
sundb: { deferred: "property semantics not verified for first pass" },
|
||||
oscar: { deferred: "schema properties need product-specific handling" },
|
||||
tdengine: { deferred: "database property editing not verified for first pass" },
|
||||
xugu: { deferred: "schema properties need product-specific handling" },
|
||||
iotdb: { deferred: "storage group/database semantics need dedicated IoTDB handling" },
|
||||
etcd: { deferred: "key-value namespaces are not databases" },
|
||||
zookeeper: { deferred: "key-value namespaces are not databases" },
|
||||
iris: { deferred: "schema properties need product-specific handling" },
|
||||
influxdb: { deferred: "database retention policies need a dedicated workflow" },
|
||||
jdbc: { deferred: "generic JDBC does not expose reliable dialect-specific properties" },
|
||||
mq: { deferred: "message queue namespaces are handled by MQ admin panels" },
|
||||
nacos: { deferred: "Nacos namespace editing already uses the Nacos admin flow" },
|
||||
} satisfies Record<DatabaseType, DatabasePropertyEditingEntry>;
|
||||
|
||||
function entryFor(connection: PropertyEditConnection): DatabasePropertyEditingEntry | null {
|
||||
if (!connection || connection.read_only) return null;
|
||||
return DATABASE_PROPERTY_EDITING_MATRIX[connection.db_type] ?? null;
|
||||
}
|
||||
|
||||
export function editableDatabasePropertyGroups(connection: PropertyEditConnection, node: DatabaseNode): DatabasePropertyEditGroup[] {
|
||||
if (node.type !== "database" || !node.database) return [];
|
||||
const groups = entryFor(connection)?.database ?? [];
|
||||
return groups.filter((group) => group !== "charsetCollation" || supportsCreateDatabaseCharset(connection?.db_type, connection?.driver_profile));
|
||||
}
|
||||
|
||||
export function editableSchemaPropertyGroups(connection: PropertyEditConnection, node: SchemaNode): DatabasePropertyEditGroup[] {
|
||||
if (node.type !== "schema" || !node.database) return [];
|
||||
return entryFor(connection)?.schema ?? [];
|
||||
}
|
||||
|
||||
export function canEditDatabaseProperties(connection: PropertyEditConnection, node: DatabaseNode): boolean {
|
||||
return editableDatabasePropertyGroups(connection, node).length > 0;
|
||||
}
|
||||
|
||||
export function canEditSchemaProperties(connection: PropertyEditConnection, node: SchemaNode): boolean {
|
||||
return editableSchemaPropertyGroups(connection, node).length > 0;
|
||||
}
|
||||
|
||||
export function supportsPostgresStyleComments(databaseType?: DatabaseType): boolean {
|
||||
return !!databaseType && POSTGRES_COMMENT_TYPES.has(databaseType);
|
||||
}
|
||||
@@ -38,6 +38,16 @@ export interface SchemaCommentSqlOptions extends SchemaNameSqlOptions {
|
||||
comment: string;
|
||||
}
|
||||
|
||||
export interface DatabasePropertyEditSqlOptions {
|
||||
databaseType?: DatabaseType;
|
||||
driverProfile?: string | null;
|
||||
target: "database" | "schema";
|
||||
name: string;
|
||||
charset?: string;
|
||||
collation?: string;
|
||||
comment?: string;
|
||||
}
|
||||
|
||||
export interface DuplicateTableStructureSqlOptions {
|
||||
databaseType?: DatabaseType;
|
||||
schema?: string | null;
|
||||
@@ -88,7 +98,18 @@ export function buildDropSchemaSql(options: SchemaNameSqlOptions): Promise<strin
|
||||
}
|
||||
|
||||
export function supportsSchemaComment(databaseType?: DatabaseType): boolean {
|
||||
return databaseType === "postgres";
|
||||
return ["postgres", "gaussdb", "kwdb", "kingbase", "highgo", "vastbase", "opengauss", "yashandb"].includes(databaseType || "");
|
||||
}
|
||||
|
||||
export function buildUpdateDatabasePropertiesSql(options: DatabasePropertyEditSqlOptions): Promise<string> {
|
||||
return api.buildUpdateDatabasePropertiesSql(options);
|
||||
}
|
||||
|
||||
export function buildGetDatabaseCommentSql(options: DatabaseNameSqlOptions): string {
|
||||
if (!supportsSchemaComment(options.databaseType)) {
|
||||
throw new Error("Database comments are not supported by this database");
|
||||
}
|
||||
return ["SELECT pg_catalog.shobj_description(db.oid, 'pg_database') AS comment", "FROM pg_catalog.pg_database db", `WHERE db.datname = ${quoteSqlLiteral(options.name)};`].join("\n");
|
||||
}
|
||||
|
||||
export function buildGetSchemaCommentSql(options: SchemaNameSqlOptions): string {
|
||||
|
||||
Reference in new issue
Block a user