diff --git a/crates/dbx-core/src/db/oracle_driver.rs b/crates/dbx-core/src/db/oracle_driver.rs index 1ff2021da..d4006f898 100644 --- a/crates/dbx-core/src/db/oracle_driver.rs +++ b/crates/dbx-core/src/db/oracle_driver.rs @@ -62,9 +62,9 @@ pub async fn list_databases(conn: &OracleClient) -> Result, St "WITH schema_names AS ( \ SELECT SYS_CONTEXT('USERENV', 'CURRENT_SCHEMA') AS owner FROM DUAL \ UNION \ - SELECT DISTINCT owner \ - FROM all_objects \ - WHERE object_type IN ('TABLE', 'VIEW') \ + SELECT DISTINCT owner FROM all_tables \ + UNION \ + SELECT DISTINCT owner FROM all_views \ ) \ SELECT owner \ FROM schema_names \ diff --git a/src/components/diff/SchemaDiffDialog.vue b/src/components/diff/SchemaDiffDialog.vue index 9e94d4354..82c08224b 100644 --- a/src/components/diff/SchemaDiffDialog.vue +++ b/src/components/diff/SchemaDiffDialog.vue @@ -48,6 +48,7 @@ const targetSchemas = ref([]); const step = ref<"select" | "comparing" | "result">("select"); const diffs = ref([]); const syncSql = ref(""); +const loadingMeta = ref(false); const executing = ref(false); const sqlConnections = computed(() => @@ -71,6 +72,7 @@ function connectionIconType(connectionId: string) { async function loadDatabases(connectionId: string, side: "source" | "target") { if (!connectionId) return; + loadingMeta.value = true; try { await store.ensureConnected(connectionId); const dbs = await api.listDatabases(connectionId); @@ -89,6 +91,8 @@ async function loadDatabases(connectionId: string, side: "source" | "target") { } catch { if (side === "source") sourceDatabases.value = []; else targetDatabases.value = []; + } finally { + loadingMeta.value = false; } } @@ -141,6 +145,8 @@ async function startCompare() { const srcTableNames = srcTables.filter((t) => t.table_type !== "VIEW").map((t) => t.name); const tgtTableNames = tgtTables.filter((t) => t.table_type !== "VIEW").map((t) => t.name); + const srcTableComments = new Map(srcTables.map((t) => [t.name, t.comment ?? null])); + const tgtTableComments = new Map(tgtTables.map((t) => [t.name, t.comment ?? null])); const srcViewNames = srcTables.filter((t) => t.table_type === "VIEW").map((t) => t.name); const tgtViewNames = tgtTables.filter((t) => t.table_type === "VIEW").map((t) => t.name); const { added, removed, common } = diffTables(srcTableNames, tgtTableNames); @@ -181,8 +187,17 @@ async function startCompare() { const idxDiffs = diffIndexes(srcIdx, tgtIdx); const fkDiffs = diffForeignKeys(srcFks, tgtFks); const triggerDiffs = diffTriggers(srcTriggers, tgtTriggers); + const srcComment = srcTableComments.get(name) ?? null; + const tgtComment = tgtTableComments.get(name) ?? null; + const commentChanged = (srcComment ?? "") !== (tgtComment ?? ""); - if (colDiffs.length > 0 || idxDiffs.length > 0 || fkDiffs.length > 0 || triggerDiffs.length > 0) { + if ( + colDiffs.length > 0 || + idxDiffs.length > 0 || + fkDiffs.length > 0 || + triggerDiffs.length > 0 || + commentChanged + ) { result.push({ type: "modified", objectType: "table", @@ -191,6 +206,8 @@ async function startCompare() { indexes: idxDiffs.length > 0 ? idxDiffs : undefined, foreignKeys: fkDiffs.length > 0 ? fkDiffs : undefined, triggers: triggerDiffs.length > 0 ? triggerDiffs : undefined, + sourceTableComment: commentChanged ? srcComment : undefined, + targetTableComment: commentChanged ? tgtComment : undefined, }); } } @@ -403,9 +420,10 @@ watch(open, async (val) => { - diff --git a/src/lib/schemaDiff.ts b/src/lib/schemaDiff.ts index 6351c210d..bcd8528ce 100644 --- a/src/lib/schemaDiff.ts +++ b/src/lib/schemaDiff.ts @@ -41,6 +41,8 @@ export interface TableDiff { foreignKeys?: ForeignKeyDiff[]; triggers?: TriggerDiff[]; ddl?: string; + sourceTableComment?: string | null; + targetTableComment?: string | null; } export function diffColumns(source: ColumnInfo[], target: ColumnInfo[]): ColumnDiff[] { @@ -63,6 +65,9 @@ export function diffColumns(source: ColumnInfo[], target: ColumnInfo[]): ColumnD if ((sc.column_default ?? "") !== (tc.column_default ?? "")) { changes.push(`default: ${tc.column_default ?? "NULL"} → ${sc.column_default ?? "NULL"}`); } + if ((sc.comment ?? "") !== (tc.comment ?? "")) { + changes.push(`comment: ${tc.comment ?? ""} → ${sc.comment ?? ""}`); + } if (changes.length > 0) { diffs.push({ type: "modified", name: sc.name, source: sc, target: tc, changes }); } @@ -268,6 +273,34 @@ function dropObjectSql(diff: TableDiff, dbType: DatabaseType, schema?: string): return `DROP ${objectType} IF EXISTS ${qualifiedName(diff.name, dbType, schema)};`; } +function commentLiteral(comment: string): string { + return `'${comment.replace(/'/g, "''")}'`; +} + +function columnCommentSql( + tableName: string, + colName: string, + comment: string, + dbType: DatabaseType, + schema?: string, +): string { + const isMySQL = dbType === "mysql" || dbType === "doris" || dbType === "starrocks"; + if (isMySQL) { + return `-- Column comment for ${colName}: use ALTER TABLE ... MODIFY COLUMN to set comment in MySQL`; + } + const qt = qualifiedName(tableName, dbType, schema); + return `COMMENT ON COLUMN ${qt}.${quoteId(colName, dbType)} IS ${commentLiteral(comment)};`; +} + +function tableCommentSql(tableName: string, comment: string, dbType: DatabaseType, schema?: string): string { + const isMySQL = dbType === "mysql" || dbType === "doris" || dbType === "starrocks"; + const qt = qualifiedName(tableName, dbType, schema); + if (isMySQL) { + return `ALTER TABLE ${qt} COMMENT = ${commentLiteral(comment)};`; + } + return `COMMENT ON TABLE ${qt} IS ${commentLiteral(comment)};`; +} + export function generateSyncSql(diffs: TableDiff[], dbType: DatabaseType, schema?: string): string { const lines: string[] = []; const isMySQL = dbType === "mysql" || dbType === "doris" || dbType === "starrocks"; @@ -351,6 +384,21 @@ export function generateSyncSql(diffs: TableDiff[], dbType: DatabaseType, schema lines.push(""); } + if (diff.columns) { + for (const col of diff.columns) { + if (col.source && col.changes?.some((c) => c.startsWith("comment:"))) { + lines.push(columnCommentSql(diff.name, col.name, col.source.comment ?? "", dbType, schema)); + } + if (col.type === "added" && col.source?.comment) { + lines.push(columnCommentSql(diff.name, col.name, col.source.comment, dbType, schema)); + } + } + } + + if (diff.sourceTableComment !== undefined && diff.sourceTableComment !== diff.targetTableComment) { + lines.push(tableCommentSql(diff.name, diff.sourceTableComment ?? "", dbType, schema)); + } + if (diff.indexes) { for (const idx of diff.indexes) { if (idx.type === "added" && idx.source) {