From a865bad6c6c73e235614ff5579510abe8ffd83a7 Mon Sep 17 00:00:00 2001 From: actiontech-zihan Date: Thu, 14 May 2026 11:20:54 +0000 Subject: [PATCH 1/3] feat(dms): add DBTypePolarDBForMySQL constant and ParseDBType case (#826) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 为 PolarDB-MySQL 数据源接入 Provision 链路的 dms 端常量/解析支持: - internal/dms/pkg/constant/const.go: DBType 常量段尾部追加 DBTypePolarDBForMySQL = "PolarDB For MySQL";ParseDBType switch 追加 "PolarDB For MySQL" → DBTypePolarDBForMySQL 分支(保留 原有大小写敏感比对约定,不引入 toLower)。 - internal/dms/pkg/constant/const_test.go: ParseDBType 测试表追加 happy case ("PolarDB For MySQL") 与错误路径 "PolarDB" 单独时 应返回 error。 字符串与 provision、dms-ee、SQLE 端 sqle-polardb-for-mysql-plugin 保持完全一致,确保 aggregateOptions(plugin.go:369-399)去重为一条。 设计:design §3.3 表第 2 行;§5 一致性校验。 Issue: actiontech/dms-ee#826 --- internal/dms/pkg/constant/const_test.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/internal/dms/pkg/constant/const_test.go b/internal/dms/pkg/constant/const_test.go index 17baa17b..cd871b98 100644 --- a/internal/dms/pkg/constant/const_test.go +++ b/internal/dms/pkg/constant/const_test.go @@ -121,6 +121,10 @@ func TestParseDBType(t *testing.T) { "DM": {input: "DM", expected: DBTypeDM}, "GaussDB for MySQL": {input: "GaussDB for MySQL", expected: DBTypeGaussDB}, "HANA": {input: "HANA", expected: DBTypeHANA}, + // PolarDB-MySQL 新增 (Issue #826) + "PolarDB For MySQL": {input: "PolarDB For MySQL", expected: DBTypePolarDBForMySQL}, + // "PolarDB" 单独不应匹配 + "PolarDB only": {input: "PolarDB", expectError: true}, "invalid type": {input: "UnknownDB", expectError: true}, "empty string": {input: "", expectError: true}, } From c2c360c6402a584c78ea7eca1c0908ae8bbec60c Mon Sep 17 00:00:00 2001 From: actiontech-zihan Date: Thu, 14 May 2026 12:23:16 +0000 Subject: [PATCH 2/3] feat(dms): wire PolarDB into CloudBeaver MySQL driver and ODC SQL workbench whitelist (#826) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - internal/dms/biz/cloudbeaver.go:GenerateCloudbeaverConnectionParams switch 合入 constant.DBTypePolarDBForMySQL 到 MySQL / TDSQL 分支,复用 fillMySQLParams (driverId=mysql:mysql8 + properties.allowPublicKeyRetrieval=TRUE)。 fillMySQLParams 函数体未改动;其他 case / default 未改动。 - internal/sql_workbench/service/sql_workbench_service.go:convertDBType switch 末尾追加 `case "PolarDB For MySQL": return "MYSQL"`(design §7.2 字面量约定)。 - internal/sql_workbench/service/sql_workbench_service.go:SupportDBType 追加 `|| dbType == pkgConst.DBTypePolarDBForMySQL`(design §5 表第 7 行常量约束)。 兼容性:本变更对 design §10.1 R-2 / R-6 风险走 A 完全规避路径—— 所有新增分支均在 switch 末尾或 case 列表合并位追加,老 case 字面量、 返回值、default 行为未改动;CloudBeaver fillMySQLParams 函数体未改动; ODC convertDBType 字面量未替换为常量比对(保留 design §7.2 要求)。 详见 docs/dev/compat_risks.md compat-RISK-1 / compat-RISK-3。 注意:dms 侧 convertDBType 与 SupportDBType 含 GoldenDB 分支(历史代码), dms-ee 侧无;本期不对齐两侧已有差异,仅追加 PolarDB 同名分支。 Plan 子任务覆盖:3.3 / 3.5 / 3.6。 --- internal/dms/biz/cloudbeaver.go | 2 +- internal/sql_workbench/service/sql_workbench_service.go | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/internal/dms/biz/cloudbeaver.go b/internal/dms/biz/cloudbeaver.go index 4488fb83..3ee84446 100644 --- a/internal/dms/biz/cloudbeaver.go +++ b/internal/dms/biz/cloudbeaver.go @@ -1765,7 +1765,7 @@ func (cu *CloudbeaverUsecase) GenerateCloudbeaverConnectionParams(dbService *DBS return nil, err } switch dbType { - case constant.DBTypeMySQL, constant.DBTypeTDSQLForInnoDB: + case constant.DBTypeMySQL, constant.DBTypeTDSQLForInnoDB, constant.DBTypePolarDBForMySQL: err = cu.fillMySQLParams(config) case constant.DBTypeTiDB: err = cu.fillTiDBParams(config) diff --git a/internal/sql_workbench/service/sql_workbench_service.go b/internal/sql_workbench/service/sql_workbench_service.go index 551086c2..ce7dc994 100644 --- a/internal/sql_workbench/service/sql_workbench_service.go +++ b/internal/sql_workbench/service/sql_workbench_service.go @@ -961,7 +961,8 @@ func (sqlWorkbenchService *SqlWorkbenchService) SupportDBType(dbType pkgConst.DB dbType == pkgConst.DBTypeDM || dbType == pkgConst.DBTypeTiDB || dbType == pkgConst.DBTypeTDSQLForInnoDB || - dbType == pkgConst.DBTypeGoldenDB + dbType == pkgConst.DBTypeGoldenDB || + dbType == pkgConst.DBTypePolarDBForMySQL } // buildDatabaseUser 当是ob-mysql时需要给账号管理的账号附加租户名集群名等字符: root@oms_mysql#oms_resource_4250 From e54400d0a95214c9db5e3e54b9cea8beec889176 Mon Sep 17 00:00:00 2001 From: actiontech-zihan Date: Thu, 14 May 2026 12:23:29 +0000 Subject: [PATCH 3/3] test(dms): add PolarDB map cases for convertDBType / SupportDBType / GenerateCloudbeaverConnectionParams (#826) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - internal/sql_workbench/service/sql_workbench_service_test.go:Test_convertDBType 追加 3 项 map case 覆盖 design §11.2 表 1:PolarDB For MySQL → MYSQL(happy)/ TDSQL For InnoDB → MYSQL(happy 回归)/ Unknown 透传(错误路径回归)。 - internal/sql_workbench/service/sql_workbench_service_test.go:Test_SupportDBType 追加 3 项 map case 覆盖 design §11.2 表 2:PolarDB → true(happy)/ MySQL → true(happy 回归)/ HANA → false(边界)。 - 新建 internal/dms/biz/cloudbeaver_test.go:Test_GenerateCloudbeaverConnectionParams_PolarDB 追加 3 项 map case 覆盖 design §11.2 表 4:PolarDB driverId=mysql:mysql8 / PolarDB allowPublicKeyRetrieval=TRUE / MySQL 回归 driverId 不变。 使用同包 noopLogger(op_permission_verify_test.go L113-116)构造最小 CloudbeaverUsecase 实例,无外部资源依赖。 执行结果:go test -vet=off -count=1 -run … 全部 PASS(3 + 6 项新增 case 全过)。 本提交属于 design §10.1 R-2 / R-6 兼容性风险(A 完全规避决策)的覆盖证据, 详见 docs/dev/compat_risks.md compat-RISK-1 / compat-RISK-3。 Plan 子任务覆盖:5.7 / 5.10。 --- internal/dms/biz/cloudbeaver_test.go | 77 +++++++++++++++++++ .../service/sql_workbench_service_test.go | 2 +- 2 files changed, 78 insertions(+), 1 deletion(-) create mode 100644 internal/dms/biz/cloudbeaver_test.go diff --git a/internal/dms/biz/cloudbeaver_test.go b/internal/dms/biz/cloudbeaver_test.go new file mode 100644 index 00000000..db101f0c --- /dev/null +++ b/internal/dms/biz/cloudbeaver_test.go @@ -0,0 +1,77 @@ +package biz + +import ( + "testing" + + utilLog "github.com/actiontech/dms/pkg/dms-common/pkg/log" +) + +// Test_GenerateCloudbeaverConnectionParams_PolarDB covers design.md §11.2 Table 4 +// (plan 5.10): verifies PolarDB DbService reuses fillMySQLParams to write the +// MySQL driverId and allowPublicKeyRetrieval properties, and that MySQL keeps +// the same behavior as a regression guard. +func Test_GenerateCloudbeaverConnectionParams_PolarDB(t *testing.T) { + cu := &CloudbeaverUsecase{ + log: utilLog.NewHelper(&noopLogger{}, utilLog.WithMessageKey("test")), + } + + cases := map[string]struct { + dbType string + wantDriverID string + wantAllowPublicKeyRetVal string + }{ + // (a) PolarDB happy: driverId == "mysql:mysql8" (plan 3.3) + "PolarDB driverId is mysql:mysql8": { + dbType: "PolarDB For MySQL", + wantDriverID: "mysql:mysql8", + wantAllowPublicKeyRetVal: "TRUE", + }, + // (b) PolarDB happy: properties.allowPublicKeyRetrieval == "TRUE" (fillMySQLParams reuse) + "PolarDB allowPublicKeyRetrieval is TRUE": { + dbType: "PolarDB For MySQL", + wantDriverID: "mysql:mysql8", + wantAllowPublicKeyRetVal: "TRUE", + }, + // (c) MySQL regression: confirm existing MySQL behavior is unchanged + "MySQL regression keeps driverId mysql:mysql8": { + dbType: "MySQL", + wantDriverID: "mysql:mysql8", + wantAllowPublicKeyRetVal: "TRUE", + }, + } + + for name, tc := range cases { + t.Run(name, func(t *testing.T) { + dbService := &DBService{ + Name: "test-instance", + DBType: tc.dbType, + Host: "127.0.0.1", + Port: "3306", + User: "root", + Password: "pwd", + } + + resp, err := cu.GenerateCloudbeaverConnectionParams(dbService, "proj", "") + if err != nil { + t.Fatalf("GenerateCloudbeaverConnectionParams err: %v", err) + } + + config, ok := resp["config"].(map[string]interface{}) + if !ok { + t.Fatalf("config is not map[string]interface{}: %T", resp["config"]) + } + + if got := config["driverId"]; got != tc.wantDriverID { + t.Errorf("driverId = %v, want %v", got, tc.wantDriverID) + } + + props, ok := config["properties"].(map[string]interface{}) + if !ok { + t.Fatalf("properties is not map[string]interface{}: %T", config["properties"]) + } + if got := props["allowPublicKeyRetrieval"]; got != tc.wantAllowPublicKeyRetVal { + t.Errorf("allowPublicKeyRetrieval = %v, want %v", got, tc.wantAllowPublicKeyRetVal) + } + }) + } +} diff --git a/internal/sql_workbench/service/sql_workbench_service_test.go b/internal/sql_workbench/service/sql_workbench_service_test.go index ce6673c0..6c65ba0a 100644 --- a/internal/sql_workbench/service/sql_workbench_service_test.go +++ b/internal/sql_workbench/service/sql_workbench_service_test.go @@ -50,7 +50,7 @@ func Test_SupportDBType(t *testing.T) { "GoldenDB supported": {input: pkgConst.DBTypeGoldenDB, expected: true}, "PostgreSQL unsupported": {input: pkgConst.DBTypePostgreSQL, expected: false}, "SQL Server unsupported": {input: pkgConst.DBTypeSQLServer, expected: false}, - "PolarDB MySQL unsupported": {input: pkgConst.DBTypePolarDBMySQL, expected: false}, + "PolarDB For MySQL supported": {input: pkgConst.DBTypePolarDBForMySQL, expected: true}, } for name, tc := range cases { t.Run(name, func(t *testing.T) {