mirror of
https://github.com/Syngnat/GoNavi.git
synced 2026-07-12 07:52:44 +08:00
fix(oceanbase): set current schema for Oracle tenant queries
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
package app
|
||||
|
||||
import (
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
@@ -23,7 +24,9 @@ func normalizeRunConfig(config connection.ConnectionConfig, dbName string) conne
|
||||
case "kafka", "apache-kafka", "apache_kafka":
|
||||
// Kafka 的 Database 字段表示默认 Topic,不能被树上的 synthetic database(topics) 覆盖。
|
||||
case "oceanbase":
|
||||
if !isOceanBaseOracleProtocol(config) {
|
||||
if isOceanBaseOracleProtocol(config) {
|
||||
runConfig = applyOceanBaseOracleCurrentSchemaInit(runConfig, name)
|
||||
} else {
|
||||
runConfig.Database = name
|
||||
}
|
||||
case "mysql", "mariadb", "goldendb", "greatdb", "gdb", "diros", "starrocks", "sphinx", "postgres", "kingbase", "highgo", "vastbase", "opengauss", "gaussdb", "sqlserver", "iris", "intersystems", "intersystemsiris", "inter-systems", "inter-systems-iris", "mongodb", "tdengine", "iotdb", "clickhouse", "trino", "rabbitmq", "rabbit-mq", "rabbit_mq":
|
||||
@@ -46,6 +49,59 @@ func normalizeRunConfig(config connection.ConnectionConfig, dbName string) conne
|
||||
return runConfig
|
||||
}
|
||||
|
||||
func applyOceanBaseOracleCurrentSchemaInit(config connection.ConnectionConfig, schema string) connection.ConnectionConfig {
|
||||
normalizedSchema := strings.TrimSpace(schema)
|
||||
if normalizedSchema == "" {
|
||||
return config
|
||||
}
|
||||
values, err := url.ParseQuery(strings.TrimSpace(config.ConnectionParams))
|
||||
if err != nil {
|
||||
return config
|
||||
}
|
||||
statement := "ALTER SESSION SET CURRENT_SCHEMA = " + quoteOracleCurrentSchemaIdentifier(normalizedSchema)
|
||||
for _, existing := range values["init"] {
|
||||
if strings.EqualFold(strings.TrimSpace(existing), statement) {
|
||||
return config
|
||||
}
|
||||
}
|
||||
values.Add("init", statement)
|
||||
config.ConnectionParams = values.Encode()
|
||||
return config
|
||||
}
|
||||
|
||||
func quoteOracleCurrentSchemaIdentifier(schema string) string {
|
||||
normalized := strings.TrimSpace(schema)
|
||||
if normalized == "" {
|
||||
return normalized
|
||||
}
|
||||
if isSimpleOracleIdentifier(normalized) {
|
||||
return strings.ToUpper(normalized)
|
||||
}
|
||||
return `"` + strings.ReplaceAll(normalized, `"`, `""`) + `"`
|
||||
}
|
||||
|
||||
func isSimpleOracleIdentifier(value string) bool {
|
||||
text := strings.TrimSpace(value)
|
||||
if text == "" {
|
||||
return false
|
||||
}
|
||||
for index, r := range text {
|
||||
isLetter := (r >= 'A' && r <= 'Z') || (r >= 'a' && r <= 'z')
|
||||
isDigit := r >= '0' && r <= '9'
|
||||
isSpecial := r == '_' || r == '$' || r == '#'
|
||||
if index == 0 {
|
||||
if !isLetter && r != '_' {
|
||||
return false
|
||||
}
|
||||
continue
|
||||
}
|
||||
if !isLetter && !isDigit && !isSpecial {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func normalizeSchemaAndTable(config connection.ConnectionConfig, dbName string, tableName string) (string, string) {
|
||||
rawTable := strings.TrimSpace(tableName)
|
||||
rawDB := strings.TrimSpace(dbName)
|
||||
|
||||
85
internal/app/db_context_oceanbase_oracle_test.go
Normal file
85
internal/app/db_context_oceanbase_oracle_test.go
Normal file
@@ -0,0 +1,85 @@
|
||||
package app
|
||||
|
||||
import (
|
||||
"net/url"
|
||||
"testing"
|
||||
|
||||
"GoNavi-Wails/internal/connection"
|
||||
)
|
||||
|
||||
func TestNormalizeRunConfig_OceanBaseOracleAddsCurrentSchemaInit(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
config := connection.ConnectionConfig{
|
||||
Type: "oceanbase",
|
||||
Database: "OBORCL",
|
||||
OceanBaseProtocol: "oracle",
|
||||
}
|
||||
runConfig := normalizeRunConfig(config, "sbdev")
|
||||
|
||||
if runConfig.Database != "OBORCL" {
|
||||
t.Fatalf("expected OceanBase Oracle service name to stay OBORCL, got %q", runConfig.Database)
|
||||
}
|
||||
values, err := url.ParseQuery(runConfig.ConnectionParams)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected connection params parse error: %v", err)
|
||||
}
|
||||
initValues := values["init"]
|
||||
if len(initValues) != 1 || initValues[0] != "ALTER SESSION SET CURRENT_SCHEMA = SBDEV" {
|
||||
t.Fatalf("expected current schema init for selected schema, got %#v", initValues)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNormalizeRunConfig_OceanBaseOraclePreservesExistingInit(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
config := connection.ConnectionConfig{
|
||||
Type: "oceanbase",
|
||||
Database: "OBORCL",
|
||||
OceanBaseProtocol: "oracle",
|
||||
ConnectionParams: "init=ALTER+SESSION+SET+NLS_DATE_FORMAT%3D%27YYYY-MM-DD%27&timeout=10s",
|
||||
}
|
||||
runConfig := normalizeRunConfig(config, "sbdev")
|
||||
|
||||
values, err := url.ParseQuery(runConfig.ConnectionParams)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected connection params parse error: %v", err)
|
||||
}
|
||||
initValues := values["init"]
|
||||
if len(initValues) != 2 {
|
||||
t.Fatalf("expected existing init plus current schema init, got %#v", initValues)
|
||||
}
|
||||
if initValues[0] != "ALTER SESSION SET NLS_DATE_FORMAT='YYYY-MM-DD'" {
|
||||
t.Fatalf("expected existing init to stay first, got %#v", initValues)
|
||||
}
|
||||
if initValues[1] != "ALTER SESSION SET CURRENT_SCHEMA = SBDEV" {
|
||||
t.Fatalf("expected current schema init appended, got %#v", initValues)
|
||||
}
|
||||
if values.Get("timeout") != "10s" {
|
||||
t.Fatalf("expected non-init connection param preserved, got %q", values.Get("timeout"))
|
||||
}
|
||||
}
|
||||
|
||||
func TestQuoteOracleCurrentSchemaIdentifier(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
cases := []struct {
|
||||
name string
|
||||
raw string
|
||||
want string
|
||||
}{
|
||||
{name: "lower simple", raw: "sbdev", want: "SBDEV"},
|
||||
{name: "normal uppercase", raw: "SBDEV", want: "SBDEV"},
|
||||
{name: "quoted mixed case required", raw: "Sb Dev", want: `"Sb Dev"`},
|
||||
{name: "quote escaping", raw: `A"B`, want: `"A""B"`},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
if got := quoteOracleCurrentSchemaIdentifier(tc.raw); got != tc.want {
|
||||
t.Fatalf("expected %q, got %q", tc.want, got)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user