diff --git a/internal/dms/pkg/constant/const.go b/internal/dms/pkg/constant/const.go index 7377ed0e..c7c5c5c7 100644 --- a/internal/dms/pkg/constant/const.go +++ b/internal/dms/pkg/constant/const.go @@ -262,6 +262,8 @@ func ParseDBType(s string) (DBType, error) { return DBTypeRedis, nil case "OceanBase For Oracle": return DBTypeOceanBaseOracle, nil + case "KingBase": + return DBTypeKingBase, nil default: return "", fmt.Errorf("invalid db type: %s", s) @@ -288,6 +290,7 @@ const ( DBTypeMongoDB DBType = "MongoDB" DBTypeRedis DBType = "Redis" DBTypeOceanBaseOracle DBType = "OceanBase For Oracle" + DBTypeKingBase DBType = "KingBase" ) var supportedDataExportDBTypes = map[DBType]struct{}{ diff --git a/internal/dms/pkg/constant/const_test.go b/internal/dms/pkg/constant/const_test.go index 1958f259..bdd5dc14 100644 --- a/internal/dms/pkg/constant/const_test.go +++ b/internal/dms/pkg/constant/const_test.go @@ -129,6 +129,7 @@ func TestParseDBType(t *testing.T) { "PolarDB For MySQL": {input: "PolarDB For MySQL", expected: DBTypePolarDBForMySQL}, "MongoDB": {input: "MongoDB", expected: DBTypeMongoDB}, "Redis": {input: "Redis", expected: DBTypeRedis}, + "KingBase": {input: "KingBase", expected: DBTypeKingBase}, // "PolarDB" 单独不应匹配 "PolarDB only": {input: "PolarDB", expectError: true}, "invalid type": {input: "UnknownDB", expectError: true}, diff --git a/internal/sql_workbench/service/sql_workbench_service.go b/internal/sql_workbench/service/sql_workbench_service.go index ee177152..a6c20793 100644 --- a/internal/sql_workbench/service/sql_workbench_service.go +++ b/internal/sql_workbench/service/sql_workbench_service.go @@ -1096,6 +1096,16 @@ func (sqlWorkbenchService *SqlWorkbenchService) fillDatasourceBaseInfo(datasourc baseInfo.DefaultSchema = &databaseName } + // KingBase:database_name → ODC defaultSchema;缺失则失败,禁止静默空默认库 + if dbService.DBType == string(pkgConst.DBTypeKingBase) { + databaseNameParam := dbService.AdditionalParams.GetParam("database_name") + if databaseNameParam == nil || databaseNameParam.Value == "" { + return nil, fmt.Errorf("KingBase 数据源 %s 缺少 AdditionalParam database_name,请在数据源 AdditionalParams 中补充", dbService.Name) + } + databaseName := databaseNameParam.Value + baseInfo.DefaultSchema = &databaseName + } + return baseInfo, nil } @@ -1187,6 +1197,8 @@ func (sqlWorkbenchService *SqlWorkbenchService) convertDBType(dmsDBType string) return "REDIS" case "DB2": return "DB2" + case "KingBase": + return "KINGBASE" default: return dmsDBType } @@ -1203,7 +1215,8 @@ func (sqlWorkbenchService *SqlWorkbenchService) SupportDBType(dbType pkgConst.DB dbType == pkgConst.DBTypePolarDBForMySQL || dbType == pkgConst.DBTypeGaussDB || dbType == pkgConst.DBTypePostgreSQL || - dbType == pkgConst.DBTypeRedis + dbType == pkgConst.DBTypeRedis || + dbType == pkgConst.DBTypeKingBase } func buildMongoDatasourceOptions(dbService *biz.DBService) (*string, interface{}, map[string]interface{}) { diff --git a/internal/sql_workbench/service/sql_workbench_service_test.go b/internal/sql_workbench/service/sql_workbench_service_test.go index af0d8408..9fe1c308 100644 --- a/internal/sql_workbench/service/sql_workbench_service_test.go +++ b/internal/sql_workbench/service/sql_workbench_service_test.go @@ -150,6 +150,7 @@ func Test_convertDBType(t *testing.T) { "MongoDB": {input: "MongoDB", expected: "MONGODB"}, "Redis": {input: "Redis", expected: "REDIS"}, "DB2": {input: "DB2", expected: "DB2"}, + "KingBase": {input: "KingBase", expected: "KINGBASE"}, "Unknown passthrough": {input: "UnknownDB", expected: "UnknownDB"}, } for name, tc := range cases { @@ -183,6 +184,7 @@ func Test_SupportDBType(t *testing.T) { "GaussDB supported": {input: pkgConst.DBTypeGaussDB, expected: true}, "GaussDBForMySQL unsupported": {input: pkgConst.DBTypeGaussDBForMySQL, expected: false}, "DB2 unsupported": {input: pkgConst.DBTypeDB2, expected: false}, + "KingBase supported": {input: pkgConst.DBTypeKingBase, expected: true}, "empty string unsupported": {input: pkgConst.DBType(""), expected: false}, "unknown type unsupported": {input: pkgConst.DBType("UnknownDBType"), expected: false}, } @@ -390,6 +392,89 @@ func Test_buildDatasourceBaseInfo_DB2(t *testing.T) { } } +// Test_buildDatasourceBaseInfo_KingBase 覆盖 KingBase → defaultSchema 契约(S1 / AC-1): +// +// (a) 正例:database_name=test → DefaultSchema=="test" 且 Type 经 convert 为 KINGBASE +// (b) 负例:缺 database_name → err 含 "database_name" +// (c) MySQL 回归:DefaultSchema == nil +func Test_buildDatasourceBaseInfo_KingBase(t *testing.T) { + svc := &SqlWorkbenchService{} + const envID = int64(1) + const datasourceName = "proj:kingbase_odc_test" + + cases := map[string]struct { + dbService *biz.DBService + expectErr bool + expectErrSubstr string + expectDefaultSchema *string + expectType string + }{ + "KingBase happy path": { + dbService: &biz.DBService{ + Name: "kingbase_odc_test", + DBType: string(pkgConst.DBTypeKingBase), + Host: "10.186.16.126", + Port: "1522", + User: "kb_dev", + AdditionalParams: pkgParams.Params{ + {Key: "database_name", Value: "test"}, + }, + }, + expectErr: false, + expectDefaultSchema: strPtr("test"), + expectType: "KINGBASE", + }, + "KingBase missing database_name": { + dbService: &biz.DBService{ + Name: "kingbase-missing-db", + DBType: string(pkgConst.DBTypeKingBase), + AdditionalParams: pkgParams.Params{}, + }, + expectErr: true, + expectErrSubstr: "database_name", + }, + "MySQL regression still no DefaultSchema": { + dbService: &biz.DBService{ + Name: "mysql-1", + DBType: "MySQL", + AdditionalParams: pkgParams.Params{}, + }, + expectErr: false, + expectDefaultSchema: nil, + expectType: "MYSQL", + }, + } + + for name, tc := range cases { + t.Run(name, func(t *testing.T) { + got, err := svc.fillDatasourceBaseInfo(datasourceName, tc.dbService, envID) + if tc.expectErr { + if err == nil { + t.Fatalf("expected error, got nil; baseInfo=%+v", got) + } + if tc.expectErrSubstr != "" && !strings.Contains(err.Error(), tc.expectErrSubstr) { + t.Errorf("error %q does not contain %q", err.Error(), tc.expectErrSubstr) + } + return + } + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + if got == nil { + t.Fatalf("expected non-nil baseInfo") + } + if tc.expectType != "" && got.Type != tc.expectType { + t.Errorf("Type = %q, want %q", got.Type, tc.expectType) + } + if (got.DefaultSchema == nil) != (tc.expectDefaultSchema == nil) { + t.Errorf("DefaultSchema nil mismatch: got=%v, want=%v", got.DefaultSchema, tc.expectDefaultSchema) + } else if got.DefaultSchema != nil && tc.expectDefaultSchema != nil && *got.DefaultSchema != *tc.expectDefaultSchema { + t.Errorf("DefaultSchema = %q, want %q", *got.DefaultSchema, *tc.expectDefaultSchema) + } + }) + } +} + func strPtr(s string) *string { return &s }