package db import ( "fmt" "os" "path/filepath" "strings" "sync" "GoNavi-Wails/internal/appdata" ) // coreBuiltinDrivers 是始终内置可用的核心驱动,无需额外安装即可使用。 var coreBuiltinDrivers = map[string]struct{}{ "mysql": {}, "redis": {}, "oracle": {}, "postgres": {}, } // optionalGoDrivers 表示需要用户“安装启用”后才能使用的纯 Go 驱动。 // 注意:这是一种运行时门控(installed.json 标记),并不减少主二进制体积。 var optionalGoDrivers = map[string]struct{}{ "mariadb": {}, "oceanbase": {}, "diros": {}, "sphinx": {}, "sqlserver": {}, "sqlite": {}, "duckdb": {}, "dameng": {}, "kingbase": {}, "highgo": {}, "vastbase": {}, "opengauss": {}, "mongodb": {}, "tdengine": {}, "clickhouse": {}, } // optionalDriverAgentRevisions 记录 GoNavi 对各可选 driver-agent 包装逻辑的兼容版本。 // 该 map 由 tools/generate-driver-agent-revisions.sh 按 driver-agent 源码依赖自动生成, // 避免人工判断需要 bump 哪个驱动 revision。 var optionalDriverAgentRevisions = map[string]string{} var ( externalDriverDirMu sync.RWMutex externalDriverDir string ) func normalizeRuntimeDriverType(driverType string) string { normalized := strings.ToLower(strings.TrimSpace(driverType)) switch normalized { case "doris": return "diros" case "postgresql": return "postgres" case "opengauss", "open_gauss", "open-gauss": return "opengauss" default: return normalized } } func driverDisplayName(driverType string) string { switch normalizeRuntimeDriverType(driverType) { case "mysql": return "MySQL" case "oracle": return "Oracle" case "redis": return "Redis" case "mariadb": return "MariaDB" case "oceanbase": return "OceanBase" case "diros": return "Doris" case "sphinx": return "Sphinx" case "postgres": return "PostgreSQL" case "sqlserver": return "SQL Server" case "sqlite": return "SQLite" case "duckdb": return "DuckDB" case "dameng": return "Dameng" case "kingbase": return "Kingbase" case "highgo": return "HighGo" case "vastbase": return "Vastbase" case "opengauss": return "OpenGauss" case "mongodb": return "MongoDB" case "tdengine": return "TDengine" case "clickhouse": return "ClickHouse" default: return strings.ToUpper(strings.TrimSpace(driverType)) } } // IsOptionalGoDriver 返回指定驱动类型是否为可选的纯 Go 驱动。 // 可选驱动需要用户在驱动管理界面点击“安装启用”后才能使用。 func IsOptionalGoDriver(driverType string) bool { _, ok := optionalGoDrivers[normalizeRuntimeDriverType(driverType)] return ok } func IsOptionalGoDriverBuildIncluded(driverType string) bool { return optionalGoDriverBuildIncluded(normalizeRuntimeDriverType(driverType)) } func OptionalDriverAgentRevision(driverType string) string { return strings.TrimSpace(optionalDriverAgentRevisions[normalizeRuntimeDriverType(driverType)]) } // IsBuiltinDriver 返回指定驱动类型是否为核心内置驱动(始终可用,无需安装)。 func IsBuiltinDriver(driverType string) bool { _, ok := coreBuiltinDrivers[normalizeRuntimeDriverType(driverType)] return ok } func defaultExternalDriverDownloadDirectory() string { return appdata.DriverRoot("") } func resolveExternalDriverRoot(downloadDir string) (string, error) { root := strings.TrimSpace(downloadDir) if root == "" { root = currentExternalDriverDownloadDirectory() } if root == "" { root = defaultExternalDriverDownloadDirectory() } if !filepath.IsAbs(root) { abs, err := filepath.Abs(root) if err != nil { return "", err } root = abs } if err := os.MkdirAll(root, 0o755); err != nil { return "", fmt.Errorf("创建驱动目录失败:%w", err) } return root, nil } func currentExternalDriverDownloadDirectory() string { externalDriverDirMu.RLock() current := strings.TrimSpace(externalDriverDir) externalDriverDirMu.RUnlock() if current != "" { return current } return defaultExternalDriverDownloadDirectory() } // SetExternalDriverDownloadDirectory 设置可选驱动的下载存储目录。 // 如果路径解析失败,会回退到默认目录(~/.gonavi/drivers)。 func SetExternalDriverDownloadDirectory(downloadDir string) { root, err := resolveExternalDriverRoot(downloadDir) if err != nil { root = defaultExternalDriverDownloadDirectory() } externalDriverDirMu.Lock() externalDriverDir = root externalDriverDirMu.Unlock() } func ResolveExternalDriverRoot(downloadDir string) (string, error) { return resolveExternalDriverRoot(downloadDir) } func ResolveOptionalGoDriverMarkerPath(downloadDir string, driverType string) (string, error) { normalized := normalizeRuntimeDriverType(driverType) if !IsOptionalGoDriver(normalized) { return "", fmt.Errorf("%s 不是可选 Go 驱动", driverDisplayName(normalized)) } root, err := resolveExternalDriverRoot(downloadDir) if err != nil { return "", err } return filepath.Join(root, normalized, "installed.json"), nil } func optionalGoDriverInstalled(driverType string) bool { markerPath, err := ResolveOptionalGoDriverMarkerPath("", driverType) if err != nil { return false } info, statErr := os.Stat(markerPath) return statErr == nil && !info.IsDir() } func optionalGoDriverRuntimeReady(driverType string) (bool, string) { normalized := normalizeRuntimeDriverType(driverType) if !IsOptionalGoDriver(normalized) { return true, "" } executablePath, err := ResolveOptionalDriverAgentExecutablePath("", normalized) if err != nil { return false, fmt.Sprintf("%s 驱动代理路径解析失败,请在驱动管理中重新安装启用", driverDisplayName(normalized)) } info, statErr := os.Stat(executablePath) if statErr != nil || info.IsDir() { return false, fmt.Sprintf("%s 驱动代理缺失,请在驱动管理中重新安装启用", driverDisplayName(normalized)) } if validateErr := ValidateOptionalDriverAgentExecutable(normalized, executablePath); validateErr != nil { return false, fmt.Sprintf("%s;请在驱动管理中重新安装启用", validateErr.Error()) } return true, "" } // DriverRuntimeSupportStatus 返回当前构建下驱动是否可用(可直接用于连接)。 func DriverRuntimeSupportStatus(driverType string) (bool, string) { normalized := normalizeRuntimeDriverType(driverType) if normalized == "" { return false, "未识别的数据源类型" } if normalized == "custom" { return true, "" } if IsBuiltinDriver(normalized) { return true, "" } if IsOptionalGoDriver(normalized) { if !IsOptionalGoDriverBuildIncluded(normalized) { return false, fmt.Sprintf("%s 当前发行包为精简构建,未内置该驱动;如需使用请安装 Full 版", driverDisplayName(normalized)) } if optionalGoDriverInstalled(normalized) { if ready, reason := optionalGoDriverRuntimeReady(normalized); !ready { return false, reason } return true, "" } return false, fmt.Sprintf("%s 纯 Go 驱动未启用,请先在驱动管理中点击“安装启用”", driverDisplayName(normalized)) } return true, "" }