🐛 fix(oceanbase): 修复 OceanBase 协议模式识别与缓存隔离

- 支持 MySQL/Oracle 租户协议在前后端统一解析
- 拒绝 Native 协议并避免误回退为 MySQL
- 修复 Oracle 模式下元数据、DDL、SQL 方言识别
- 修复连接缓存键与实际协议解析优先级不一致问题
- 补充前后端协议解析与缓存隔离回归测试
This commit is contained in:
Syngnat
2026-05-13 22:51:01 +08:00
parent 01eb2c25e0
commit f8abe60dc2
22 changed files with 454 additions and 192 deletions

View File

@@ -151,36 +151,62 @@ func normalizeOceanBaseProtocol(raw string) string {
switch strings.ToLower(strings.TrimSpace(raw)) {
case oceanBaseProtocolOracle, "oracle-mode", "oracle_mode", "oboracle":
return oceanBaseProtocolOracle
case oceanBaseProtocolMySQL, "mysql-compatible", "mysql_compatible", "mysql-mode", "mysql_mode", "":
case oceanBaseProtocolMySQL, "mysql-compatible", "mysql_compatible", "mysql-mode", "mysql_mode", "obmysql", "":
return oceanBaseProtocolMySQL
default:
return oceanBaseProtocolMySQL
return ""
}
}
func resolveOceanBaseProtocolFromValues(values url.Values) string {
func unsupportedOceanBaseProtocolError(raw string) error {
return fmt.Errorf("OceanBase 当前仅支持 MySQL/Oracle 租户协议,不支持 %q请改为 MySQL 或 Oracle", strings.TrimSpace(raw))
}
func resolveOceanBaseProtocolFromValues(values url.Values) (string, error) {
if len(values) == 0 {
return ""
return "", nil
}
for _, key := range []string{"protocol", "oceanBaseProtocol", "oceanbaseProtocol", "tenantMode", "compatMode", "mode"} {
if value := strings.TrimSpace(values.Get(key)); value != "" {
return normalizeOceanBaseProtocol(value)
protocol := normalizeOceanBaseProtocol(value)
if protocol == "" {
return "", unsupportedOceanBaseProtocolError(value)
}
return protocol, nil
}
}
return ""
return "", nil
}
func resolveOceanBaseProtocol(config connection.ConnectionConfig) string {
func resolveOceanBaseProtocol(config connection.ConnectionConfig) (string, error) {
explicitProtocol := ""
if explicit := strings.TrimSpace(config.OceanBaseProtocol); explicit != "" {
return normalizeOceanBaseProtocol(explicit)
protocol := normalizeOceanBaseProtocol(explicit)
if protocol == "" {
return "", unsupportedOceanBaseProtocolError(explicit)
}
explicitProtocol = protocol
}
if protocol := resolveOceanBaseProtocolFromValues(connectionParamsFromText(config.ConnectionParams)); protocol != "" {
return protocol
if protocol, err := resolveOceanBaseProtocolFromValues(connectionParamsFromText(config.ConnectionParams)); err != nil {
return "", err
} else if protocol != "" {
if explicitProtocol != "" {
return explicitProtocol, nil
}
return protocol, nil
}
if protocol := resolveOceanBaseProtocolFromValues(connectionParamsFromURI(config.URI, "oceanbase", "mysql")); protocol != "" {
return protocol
if protocol, err := resolveOceanBaseProtocolFromValues(connectionParamsFromURI(config.URI, "oceanbase", "mysql")); err != nil {
return "", err
} else if protocol != "" {
if explicitProtocol != "" {
return explicitProtocol, nil
}
return protocol, nil
}
return oceanBaseProtocolMySQL
if explicitProtocol != "" {
return explicitProtocol, nil
}
return oceanBaseProtocolMySQL, nil
}
func stripOceanBaseProtocolParams(raw string) string {
@@ -256,7 +282,10 @@ func (o *OceanBaseDB) Connect(config connection.ConnectionConfig) error {
o.oracle = nil
o.protocol = oceanBaseProtocolMySQL
appliedConfig := applyOceanBaseURI(config)
protocol := resolveOceanBaseProtocol(appliedConfig)
protocol, err := resolveOceanBaseProtocol(appliedConfig)
if err != nil {
return err
}
runConfig := withoutOceanBaseProtocolParams(appliedConfig)
if protocol == oceanBaseProtocolOracle {
logger.Infof("OceanBase 使用 Oracle 协议连接:地址=%s:%d 用户=%s", runConfig.Host, runConfig.Port, runConfig.User)

View File

@@ -79,13 +79,52 @@ func TestResolveOceanBaseProtocol(t *testing.T) {
tt := tt
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
if got := resolveOceanBaseProtocol(tt.config); got != tt.want {
got, err := resolveOceanBaseProtocol(tt.config)
if err != nil {
t.Fatalf("resolveOceanBaseProtocol() unexpected error: %v", err)
}
if got != tt.want {
t.Fatalf("resolveOceanBaseProtocol() = %q, want %q", got, tt.want)
}
})
}
}
func TestResolveOceanBaseProtocolRejectsUnsupportedNative(t *testing.T) {
t.Parallel()
tests := []struct {
name string
config connection.ConnectionConfig
}{
{
name: "params native",
config: connection.ConnectionConfig{
Type: "oceanbase",
ConnectionParams: "protocol=native",
},
},
{
name: "explicit mysql does not mask params native",
config: connection.ConnectionConfig{
Type: "oceanbase",
OceanBaseProtocol: "mysql",
ConnectionParams: "protocol=native",
},
},
}
for _, tt := range tests {
tt := tt
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
_, err := resolveOceanBaseProtocol(tt.config)
if err == nil || !strings.Contains(err.Error(), "不支持") {
t.Fatalf("expected unsupported protocol error, got %v", err)
}
})
}
}
func TestWithoutOceanBaseProtocolParamsStripsDriverMeta(t *testing.T) {
t.Parallel()