mirror of
https://github.com/Syngnat/GoNavi.git
synced 2026-05-06 20:03:05 +08:00
- 数据源支持:新增 OceanBase 与 OpenGauss optional driver-agent 实现 - 连接适配:复用 MySQL/PostgreSQL 兼容链路并补齐查询、DDL、同步能力 - 前端入口:补充连接表单、侧边栏、图标、SQL 方言和危险操作识别 - 驱动管理:更新 driver manifest、安装提示和 revision 自动生成链路 - 构建发布:支持多平台 driver-agent 打包并优化 release 构建失败提示
248 lines
7.0 KiB
Go
248 lines
7.0 KiB
Go
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, ""
|
||
}
|