From 0c2d95b83ecac958f8aedf17d086aed9394c1146 Mon Sep 17 00:00:00 2001 From: t8y2 <1156263951@qq.com> Date: Sun, 28 Jun 2026 11:05:04 +0800 Subject: [PATCH] fix(schema-diff): qualify MySQL deploy SQL target --- .../src/components/diff/SchemaDiffDialog.vue | 4 +- apps/desktop/src/lib/schemaDiff.ts | 14 ++++ crates/dbx-core/src/schema_diff.rs | 65 +++++++++++++++++++ packages/app-tests/schemaDiff.test.ts | 9 ++- 4 files changed, 89 insertions(+), 3 deletions(-) diff --git a/apps/desktop/src/components/diff/SchemaDiffDialog.vue b/apps/desktop/src/components/diff/SchemaDiffDialog.vue index f8e517e6e5..90bd26e54c 100644 --- a/apps/desktop/src/components/diff/SchemaDiffDialog.vue +++ b/apps/desktop/src/components/diff/SchemaDiffDialog.vue @@ -19,7 +19,7 @@ import { createConcurrencyLimiter, mapWithConcurrency, schemaDiffMetadataConcurr import { normalizeSchemaDiffCompareOptions } from "@/types/schemaDiff"; import type { SchemaDiffCompareOptions, SchemaDiffConfig } from "@/types/schemaDiff"; import type { ObjectSourceKind, TableInfo } from "@/types/database"; -import { buildDeploySqlForObjects, convertToSchemaDiffObjects, groupDiffObjects, type OperationGroup, type SchemaDiffObject, type DiffOperationType, type DiffObjectKind, type SchemaDiffPreparation, type TableSchemaDetail } from "@/lib/schemaDiff"; +import { buildDeploySqlForObjects, convertToSchemaDiffObjects, groupDiffObjects, schemaDiffDeployTargetSchema, type OperationGroup, type SchemaDiffObject, type DiffOperationType, type DiffObjectKind, type SchemaDiffPreparation, type TableSchemaDetail } from "@/lib/schemaDiff"; import { compileSchemaDiffTableFilter, filterSchemaDiffTables } from "@/lib/schemaDiffTableFilter"; import { Splitpanes, Pane } from "splitpanes"; import "splitpanes/dist/splitpanes.css"; @@ -390,7 +390,7 @@ async function handleCompare() { sourceOwners: srcOwners, targetOwners: tgtOwners, databaseType: dbType, - targetSchema: targetSchema.value, + targetSchema: schemaDiffDeployTargetSchema(dbType, targetDatabase.value, targetSchema.value), ignoreComments: ignoreComments.value, cascadeDelete: opts?.cascadeDelete ?? false, compareColumnOrder: opts.compareColumnOrder, diff --git a/apps/desktop/src/lib/schemaDiff.ts b/apps/desktop/src/lib/schemaDiff.ts index 8f3e82e553..2bd7f7b070 100644 --- a/apps/desktop/src/lib/schemaDiff.ts +++ b/apps/desktop/src/lib/schemaDiff.ts @@ -117,6 +117,20 @@ export interface SchemaDiffPreparation { syncSql: string; } +const MYSQL_LIKE_SCHEMA_DIFF_TARGET_TYPES = new Set(["mysql", "doris", "starrocks", "goldendb", "sundb", "databend", "gbase"]); + +export function schemaDiffDeployTargetSchema(databaseType: DatabaseType | undefined, targetDatabase: string, targetSchema?: string): string | undefined { + const schema = targetSchema?.trim(); + if (schema) return schema; + + const database = targetDatabase.trim(); + if (databaseType && MYSQL_LIKE_SCHEMA_DIFF_TARGET_TYPES.has(databaseType) && database) { + return database; + } + + return undefined; +} + // Unified object type for UI display export type DiffOperationType = "modify" | "create" | "delete" | "none"; export type DiffObjectKind = "table" | "view" | "function" | "sequence" | "rule" | "owner" | "index" | "trigger" | "foreignKey"; diff --git a/crates/dbx-core/src/schema_diff.rs b/crates/dbx-core/src/schema_diff.rs index b6e0928db6..16e633fca6 100644 --- a/crates/dbx-core/src/schema_diff.rs +++ b/crates/dbx-core/src/schema_diff.rs @@ -957,6 +957,8 @@ fn column_def(col: &ColumnInfo, db_type: DatabaseType) -> String { fn qualified_name(name: &str, db_type: DatabaseType, schema: Option<&str>) -> String { schema + .map(str::trim) + .filter(|schema| !schema.is_empty()) .map(|schema| format!("{}.{}", quote_id(schema, db_type), quote_id(name, db_type))) .unwrap_or_else(|| quote_id(name, db_type)) } @@ -1660,6 +1662,69 @@ mod tests { ); } + #[test] + fn mysql_schema_sync_sql_qualifies_tables_with_target_database() { + let diffs = vec![TableDiff { + diff_type: "modified".to_string(), + object_type: None, + name: "notify_channel_config".to_string(), + columns: Some(vec![ColumnDiff { + diff_type: "modified".to_string(), + name: "config_json".to_string(), + source: Some(column("config_json", "json", Some("渠道配置"))), + target: Some(column("config_json", "json", Some("Config"))), + changes: vec!["comment: Config → 渠道配置".to_string()], + }]), + indexes: None, + foreign_keys: None, + triggers: None, + ddl: None, + target_ddl: None, + source_table_comment: None, + target_table_comment: None, + sync_sql: None, + }]; + + assert_eq!( + generate_schema_sync_sql(&diffs, &[], &[], &[], &[], DatabaseType::Mysql, Some("target_db"), false), + [ + "-- Alter table: notify_channel_config", + "ALTER TABLE `target_db`.`notify_channel_config`", + " MODIFY COLUMN `config_json` json NOT NULL COMMENT '渠道配置';", + ] + .join("\n") + ); + } + + #[test] + fn blank_target_schema_does_not_generate_empty_qualifier() { + let diffs = vec![TableDiff { + diff_type: "modified".to_string(), + object_type: None, + name: "notify_channel_config".to_string(), + columns: Some(vec![ColumnDiff { + diff_type: "modified".to_string(), + name: "config_json".to_string(), + source: Some(column("config_json", "json", Some("渠道配置"))), + target: Some(column("config_json", "json", Some("Config"))), + changes: vec!["comment: Config → 渠道配置".to_string()], + }]), + indexes: None, + foreign_keys: None, + triggers: None, + ddl: None, + target_ddl: None, + source_table_comment: None, + target_table_comment: None, + sync_sql: None, + }]; + + let sql = generate_schema_sync_sql(&diffs, &[], &[], &[], &[], DatabaseType::Mysql, Some(" "), false); + + assert!(sql.contains("ALTER TABLE `notify_channel_config`")); + assert!(!sql.contains("``.")); + } + #[test] fn ignore_comments_skips_column_and_table_comment_diffs() { let options = SchemaDiffPreparationOptions { diff --git a/packages/app-tests/schemaDiff.test.ts b/packages/app-tests/schemaDiff.test.ts index de037a9f35..2ee358a9b8 100644 --- a/packages/app-tests/schemaDiff.test.ts +++ b/packages/app-tests/schemaDiff.test.ts @@ -1,6 +1,6 @@ import assert from "node:assert/strict"; import { test } from "vitest"; -import { buildDeploySqlForObjects, convertToSchemaDiffObjects, type TableDiff } from "../../apps/desktop/src/lib/schemaDiff.ts"; +import { buildDeploySqlForObjects, convertToSchemaDiffObjects, schemaDiffDeployTargetSchema, type TableDiff } from "../../apps/desktop/src/lib/schemaDiff.ts"; test("uses generated sync SQL for modified table deployment", () => { const tableDiffs: TableDiff[] = [ @@ -44,3 +44,10 @@ test("falls back to source DDL when object sync SQL is unavailable", () => { assert.equal(buildDeploySqlForObjects(objects), "-- Create table: users\nCREATE TABLE `users` (`id` int);\n"); }); + +test("uses mysql target database as schema diff deploy qualifier", () => { + assert.equal(schemaDiffDeployTargetSchema("mysql", "target_db", ""), "target_db"); + assert.equal(schemaDiffDeployTargetSchema("mysql", "target_db", " "), "target_db"); + assert.equal(schemaDiffDeployTargetSchema("mysql", "target_db", "explicit_schema"), "explicit_schema"); + assert.equal(schemaDiffDeployTargetSchema("sqlite", "main", ""), undefined); +});