Files
MyGoNavi/internal/sync/sql_helpers.go
Syngnat 74a422a5e2 🐛 fix(sync): 修复 Oracle 同步连接与 MySQL 备份导出异常
- 分离 Oracle/OceanBase Oracle 同步连接 Service Name 与选中 schema

- 兼容旧同步请求中 database 被 schema 覆盖的情况

- 规范 MySQL/MariaDB SHOW CREATE TABLE 标识符引用

Refs #549

Refs #518
2026-06-11 10:24:48 +08:00

217 lines
5.5 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package sync
import (
"strings"
"GoNavi-Wails/internal/connection"
"GoNavi-Wails/internal/db"
)
func selectedSyncDatabase(selected string, fallback string) string {
if value := strings.TrimSpace(selected); value != "" {
return value
}
return strings.TrimSpace(fallback)
}
func selectedSyncSourceDatabase(config SyncConfig) string {
return selectedSyncDatabase(config.SourceDatabase, config.SourceConfig.Database)
}
func selectedSyncTargetDatabase(config SyncConfig) string {
return selectedSyncDatabase(config.TargetDatabase, config.TargetConfig.Database)
}
func normalizeSyncConnectionDatabases(config SyncConfig) SyncConfig {
config.SourceConfig = normalizeSyncConnectionDatabase(config.SourceConfig, config.SourceDatabase)
config.TargetConfig = normalizeSyncConnectionDatabase(config.TargetConfig, config.TargetDatabase)
return config
}
func normalizeSyncConnectionDatabase(config connection.ConnectionConfig, selectedDatabase string) connection.ConnectionConfig {
selected := strings.TrimSpace(selectedDatabase)
if selected == "" {
return config
}
switch resolveMigrationDBType(config) {
case "oracle":
// Oracle 的 ConnectionConfig.Database 是 Service Name数据同步选择的是 schema/owner。
return config
case "oceanbase":
if isOceanBaseOracleSyncConnection(config) {
return config
}
default:
config.Database = selected
return config
}
config.Database = selected
return config
}
func isOceanBaseOracleSyncConnection(config connection.ConnectionConfig) bool {
if !strings.EqualFold(strings.TrimSpace(config.Type), "oceanbase") {
return false
}
if strings.EqualFold(strings.TrimSpace(config.OceanBaseProtocol), "oracle") {
return true
}
for _, part := range strings.FieldsFunc(config.ConnectionParams, func(r rune) bool { return r == '&' || r == ';' }) {
key, value, ok := strings.Cut(part, "=")
if !ok {
continue
}
normalizedKey := strings.ToLower(strings.TrimSpace(key))
normalizedValue := strings.ToLower(strings.TrimSpace(value))
if (normalizedKey == "protocol" || normalizedKey == "tenantmode") && normalizedValue == "oracle" {
return true
}
}
return false
}
func normalizeSyncMode(mode string) string {
m := strings.ToLower(strings.TrimSpace(mode))
switch m {
case "", "insert_update":
return "insert_update"
case "insert_only":
return "insert_only"
case "full_overwrite":
return "full_overwrite"
default:
return "insert_update"
}
}
func quoteIdentByType(dbType string, ident string) string {
if ident == "" {
return ident
}
switch normalizeMigrationDBType(dbType) {
case "mysql", "mariadb", "oceanbase", "diros", "starrocks", "sphinx", "clickhouse", "tdengine":
return "`" + strings.ReplaceAll(ident, "`", "``") + "`"
case "kingbase":
return db.QuoteKingbaseIdentifier(ident)
case "sqlserver":
escaped := strings.ReplaceAll(ident, "]", "]]")
return "[" + escaped + "]"
default:
return `"` + strings.ReplaceAll(ident, `"`, `""`) + `"`
}
}
func quoteQualifiedIdentByType(dbType string, ident string) string {
raw := strings.TrimSpace(ident)
if raw == "" {
return raw
}
normalizedType := normalizeMigrationDBType(dbType)
if normalizedType == "kingbase" {
schema, table := db.SplitKingbaseQualifiedName(raw)
if table == "" {
return quoteIdentByType(normalizedType, raw)
}
if schema == "" {
return quoteIdentByType(normalizedType, table)
}
return quoteIdentByType(normalizedType, schema) + "." + quoteIdentByType(normalizedType, table)
}
parts := strings.Split(raw, ".")
if len(parts) <= 1 {
return quoteIdentByType(normalizedType, raw)
}
quotedParts := make([]string, 0, len(parts))
for _, part := range parts {
part = strings.TrimSpace(part)
if part == "" {
continue
}
quotedParts = append(quotedParts, quoteIdentByType(normalizedType, part))
}
if len(quotedParts) == 0 {
return quoteIdentByType(normalizedType, raw)
}
return strings.Join(quotedParts, ".")
}
func normalizeSchemaAndTable(dbType string, dbName string, tableName string) (string, string) {
rawTable := strings.TrimSpace(tableName)
rawDB := strings.TrimSpace(dbName)
if rawTable == "" {
return rawDB, rawTable
}
normalizedType := normalizeMigrationDBType(dbType)
if normalizedType == "kingbase" {
schema, table := db.SplitKingbaseQualifiedName(rawTable)
if schema != "" && table != "" {
return schema, table
}
if table != "" {
return "public", table
}
}
if parts := strings.SplitN(rawTable, ".", 2); len(parts) == 2 {
schema := strings.TrimSpace(parts[0])
table := strings.TrimSpace(parts[1])
if schema != "" && table != "" {
return schema, table
}
}
switch normalizedType {
case "postgres", "kingbase", "highgo", "vastbase", "opengauss":
return "public", rawTable
case "duckdb":
return "main", rawTable
default:
return rawDB, rawTable
}
}
func qualifiedNameForQuery(dbType string, schema string, table string, original string) string {
raw := strings.TrimSpace(original)
if raw == "" {
return raw
}
if strings.Contains(raw, ".") {
return raw
}
switch normalizeMigrationDBType(dbType) {
case "postgres", "kingbase", "highgo", "vastbase", "opengauss":
s := strings.TrimSpace(schema)
if s == "" {
s = "public"
}
if table == "" {
return raw
}
return s + "." + table
case "duckdb":
s := strings.TrimSpace(schema)
if s == "" {
s = "main"
}
if table == "" {
return raw
}
return s + "." + table
case "mysql", "mariadb", "oceanbase", "diros", "starrocks", "sphinx", "clickhouse", "tdengine":
s := strings.TrimSpace(schema)
if s == "" || table == "" {
return table
}
return s + "." + table
default:
return table
}
}