feat(goldendb): 新增 GoldenDB 数据库连接支持

Refs #477
This commit is contained in:
Syngnat
2026-06-13 21:42:18 +08:00
parent 0ff17dc27c
commit 12fbc7ecf4
39 changed files with 324 additions and 34 deletions

View File

@@ -471,6 +471,9 @@ var databaseFactories = map[string]databaseFactory{
"mysql": func() Database {
return &MySQLDB{}
},
"goldendb": func() Database {
return &MySQLDB{}
},
"postgres": func() Database {
return &PostgresDB{}
},
@@ -521,6 +524,8 @@ func normalizeDatabaseType(dbType string) string {
return "opengauss"
case "gaussdb", "gauss_db", "gauss-db":
return "gaussdb"
case "goldendb", "greatdb", "gdb":
return "goldendb"
case "intersystems", "intersystemsiris", "inter-systems-iris", "inter-systems":
return "iris"
case "chromadb", "chroma-db":

View File

@@ -13,6 +13,7 @@ import (
// coreBuiltinDrivers 是始终内置可用的核心驱动,无需额外安装即可使用。
var coreBuiltinDrivers = map[string]struct{}{
"mysql": {},
"goldendb": {},
"redis": {},
"oracle": {},
"postgres": {},
@@ -69,6 +70,8 @@ func normalizeRuntimeDriverType(driverType string) string {
return "opengauss"
case "gaussdb", "gauss_db", "gauss-db":
return "gaussdb"
case "goldendb", "greatdb", "gdb":
return "goldendb"
case "intersystems", "intersystemsiris", "inter-systems-iris", "inter-systems":
return "iris"
case "elastic":
@@ -90,6 +93,8 @@ func driverDisplayName(driverType string) string {
switch normalizeRuntimeDriverType(driverType) {
case "mysql":
return "MySQL"
case "goldendb":
return "GoldenDB"
case "oracle":
return "Oracle"
case "redis":

View File

@@ -34,6 +34,11 @@ func TestBuiltinLikeDriversRemainAvailable(t *testing.T) {
if !supported {
t.Fatalf("kafka 应始终可用reason=%s", reason)
}
supported, reason = DriverRuntimeSupportStatus("goldendb")
if !supported {
t.Fatalf("goldendb 应始终可用reason=%s", reason)
}
}
func TestOptionalDriverAgentRevisionsGeneratedForOptionalDrivers(t *testing.T) {
@@ -58,6 +63,12 @@ func TestKingbaseRuntimeAliasesNormalizeToKingbase(t *testing.T) {
if got := normalizeDatabaseType("kingbasees"); got != "kingbase" {
t.Fatalf("expected kingbasees database alias to normalize to kingbase, got %q", got)
}
if got := normalizeRuntimeDriverType("greatdb"); got != "goldendb" {
t.Fatalf("expected greatdb runtime alias to normalize to goldendb, got %q", got)
}
if got := normalizeDatabaseType("gdb"); got != "goldendb" {
t.Fatalf("expected gdb database alias to normalize to goldendb, got %q", got)
}
}
func TestManagedDriverRequiresInstallMarker(t *testing.T) {
@@ -145,3 +156,13 @@ func TestMySQLBuiltinRuntimeSupportAvailable(t *testing.T) {
t.Fatalf("mysql 属于免安装内置驱动应可用reason=%s", reason)
}
}
func TestGoldenDBBuiltinDatabaseFactoryUsesMySQLImplementation(t *testing.T) {
dbInst, err := NewDatabase("goldendb")
if err != nil {
t.Fatalf("expected goldendb database factory, got err=%v", err)
}
if _, ok := dbInst.(*MySQLDB); !ok {
t.Fatalf("expected goldendb to reuse MySQLDB implementation, got %T", dbInst)
}
}

View File

@@ -472,3 +472,54 @@ func TestMySQLDSN_URIParamsAndExplicitParamsPrecedence(t *testing.T) {
t.Fatalf("internal topology parameter should not be passed to driver: %v", query)
}
}
func TestApplyMySQLURI_GoldenDBSchemeUsesDefaultPort(t *testing.T) {
t.Parallel()
config := applyMySQLURI(connection.ConnectionConfig{
Type: "goldendb",
URI: "goldendb://glzc:secret@gdb.local/core_ledger?characterEncoding=utf8",
})
if config.Host != "gdb.local" {
t.Fatalf("expected goldendb host from URI, got %q", config.Host)
}
if config.Port != 1523 {
t.Fatalf("expected goldendb default port 1523, got %d", config.Port)
}
if config.Database != "core_ledger" {
t.Fatalf("expected goldendb database from URI, got %q", config.Database)
}
if config.User != "glzc" {
t.Fatalf("expected goldendb user from URI, got %q", config.User)
}
if config.Password != "secret" {
t.Fatalf("expected goldendb password from URI, got %q", config.Password)
}
}
func TestMySQLDSN_AcceptsGoldenDBURICompatibilityParams(t *testing.T) {
t.Parallel()
m := &MySQLDB{}
dsn, err := m.getDSN(connection.ConnectionConfig{
Type: "goldendb",
Host: "gdb.local",
Port: 1523,
User: "glzc",
Password: "secret",
Database: "core_ledger",
URI: "goldendb://glzc:secret@gdb.local/core_ledger?characterEncoding=utf8&useSSL=false",
})
if err != nil {
t.Fatalf("getDSN failed: %v", err)
}
query := parseMySQLDSNQueryForTest(t, dsn)
if got := query.Get("charset"); got != "utf8" {
t.Fatalf("goldendb URI characterEncoding should map to charset=utf8, got=%q", got)
}
if got := query.Get("tls"); got != "false" {
t.Fatalf("goldendb URI useSSL=false should map to tls=false, got=%q", got)
}
}

View File

@@ -27,14 +27,39 @@ type MySQLDB struct {
const (
defaultMySQLPort = 3306
defaultGoldenDBPort = 1523
defaultMySQLInsertBatchSize = 1000
maxMySQLInsertBatchArgs = 60000
)
var mysqlCompatibleURISchemes = []string{
"mysql",
"mariadb",
"doris",
"diros",
"oceanbase",
"starrocks",
"goldendb",
"greatdb",
"gdb",
}
func parseMySQLCompatibleURI(raw string, allowedSchemes ...string) (*url.URL, bool) {
return parseConnectionURI(raw, allowedSchemes...)
}
func resolveMySQLCompatibleDefaultPort(config connection.ConnectionConfig) int {
if config.Port > 0 {
return config.Port
}
switch strings.ToLower(strings.TrimSpace(config.Type)) {
case "goldendb", "greatdb", "gdb":
return defaultGoldenDBPort
default:
return defaultMySQLPort
}
}
func mysqlConnectionParamsFromText(raw string) url.Values {
return connectionParamsFromText(raw)
}
@@ -319,7 +344,7 @@ func hasMySQLConnectionParam(config connection.ConnectionConfig, names ...string
return false
}
if parsed, ok := parseMySQLCompatibleURI(config.URI, "mysql", "mariadb", "doris", "diros", "oceanbase", "starrocks"); ok && hasMatchingKey(parsed.Query()) {
if parsed, ok := parseMySQLCompatibleURI(config.URI, mysqlCompatibleURISchemes...); ok && hasMatchingKey(parsed.Query()) {
return true
}
return hasMatchingKey(mysqlConnectionParamsFromText(config.ConnectionParams))
@@ -368,7 +393,7 @@ func buildMySQLCompatibleDSNWithOptions(config connection.ConnectionConfig, prot
defaultMultiStatements = *options.defaultMultiStatements
}
params.Set("multiStatements", strconv.FormatBool(defaultMultiStatements))
if parsed, ok := parseMySQLCompatibleURI(config.URI, "mysql", "doris", "diros", "oceanbase"); ok {
if parsed, ok := parseMySQLCompatibleURI(config.URI, mysqlCompatibleURISchemes...); ok {
mergeMySQLConnectionParams(params, parsed.Query())
}
mergeMySQLConnectionParams(params, mysqlConnectionParamsFromText(config.ConnectionParams))
@@ -625,7 +650,7 @@ func applyMySQLURI(config connection.ConnectionConfig) connection.ConnectionConf
if uriText == "" {
return config
}
parsed, ok := parseMySQLCompatibleURI(uriText, "mysql")
parsed, ok := parseMySQLCompatibleURI(uriText, mysqlCompatibleURISchemes...)
if !ok {
return config
}
@@ -643,10 +668,7 @@ func applyMySQLURI(config connection.ConnectionConfig) connection.ConnectionConf
config.Database = dbName
}
defaultPort := config.Port
if defaultPort <= 0 {
defaultPort = defaultMySQLPort
}
defaultPort := resolveMySQLCompatibleDefaultPort(config)
hostsFromURI := make([]string, 0, 4)
hostText := strings.TrimSpace(parsed.Host)
@@ -682,10 +704,7 @@ func applyMySQLURI(config connection.ConnectionConfig) connection.ConnectionConf
}
func collectMySQLAddresses(config connection.ConnectionConfig) []string {
defaultPort := config.Port
if defaultPort <= 0 {
defaultPort = defaultMySQLPort
}
defaultPort := resolveMySQLCompatibleDefaultPort(config)
candidates := make([]string, 0, len(config.Hosts)+1)
if len(config.Hosts) > 0 {
@@ -757,11 +776,12 @@ func (m *MySQLDB) Connect(config connection.ConnectionConfig) error {
if len(addresses) == 0 {
return fmt.Errorf("连接建立后验证失败:未找到可用的 MySQL 地址")
}
defaultPort := resolveMySQLCompatibleDefaultPort(runConfig)
var errorDetails []string
for index, address := range addresses {
candidateConfig := runConfig
host, port, ok := parseHostPortWithDefault(address, defaultMySQLPort)
host, port, ok := parseHostPortWithDefault(address, defaultPort)
if !ok {
continue
}