mirror of
https://github.com/Syngnat/GoNavi.git
synced 2026-05-12 02:59:42 +08:00
- MySQL/Redis/Oracle/PostgreSQL 内置可用,其余数据源改为“安装启用”后可用 - 新建连接对未安装驱动做弹窗内拦截提示,并支持一键跳转驱动管理安装 - 驱动管理展示安装包真实大小(从 Release 资产元数据读取)并优化加载性能 - Release 工作流发布各平台驱动代理资产,主程序构建启用 -s -w 精简
200 lines
5.4 KiB
Go
200 lines
5.4 KiB
Go
//go:build gonavi_full_drivers || gonavi_sphinx_driver
|
||
|
||
package db
|
||
|
||
import (
|
||
"fmt"
|
||
"strings"
|
||
|
||
"GoNavi-Wails/internal/connection"
|
||
"GoNavi-Wails/internal/logger"
|
||
)
|
||
|
||
const sphinxDefaultDatabaseName = "default"
|
||
|
||
// SphinxDB 复用 MySQL 协议实现,并在数据库列表不可用时提供兜底。
|
||
type SphinxDB struct {
|
||
MySQLDB
|
||
fallbackDatabase string
|
||
}
|
||
|
||
func isSphinxUnsupportedFeatureError(err error) bool {
|
||
if err == nil {
|
||
return false
|
||
}
|
||
text := strings.ToLower(strings.TrimSpace(err.Error()))
|
||
if text == "" {
|
||
return false
|
||
}
|
||
keywords := []string{
|
||
"not supported",
|
||
"unsupported",
|
||
"syntax error",
|
||
"unknown table",
|
||
"unknown column",
|
||
"doesn't exist",
|
||
}
|
||
for _, keyword := range keywords {
|
||
if strings.Contains(text, keyword) {
|
||
return true
|
||
}
|
||
}
|
||
return false
|
||
}
|
||
|
||
func (s *SphinxDB) Connect(config connection.ConnectionConfig) error {
|
||
runConfig := applyMySQLURI(config)
|
||
s.fallbackDatabase = strings.TrimSpace(runConfig.Database)
|
||
return s.MySQLDB.Connect(config)
|
||
}
|
||
|
||
func (s *SphinxDB) resolveDatabaseName(dbName string) string {
|
||
name := strings.TrimSpace(dbName)
|
||
if name == "" {
|
||
return s.fallbackDatabase
|
||
}
|
||
if strings.EqualFold(name, sphinxDefaultDatabaseName) && s.fallbackDatabase == "" {
|
||
return ""
|
||
}
|
||
return name
|
||
}
|
||
|
||
func (s *SphinxDB) GetDatabases() ([]string, error) {
|
||
dbs, err := s.MySQLDB.GetDatabases()
|
||
if err == nil && len(dbs) > 0 {
|
||
return dbs, nil
|
||
}
|
||
if s.fallbackDatabase != "" {
|
||
return []string{s.fallbackDatabase}, nil
|
||
}
|
||
return []string{sphinxDefaultDatabaseName}, nil
|
||
}
|
||
|
||
func (s *SphinxDB) GetTables(dbName string) ([]string, error) {
|
||
tables, err := s.MySQLDB.GetTables(s.resolveDatabaseName(dbName))
|
||
if err == nil {
|
||
return tables, nil
|
||
}
|
||
if !isSphinxUnsupportedFeatureError(err) {
|
||
return nil, err
|
||
}
|
||
|
||
// Sphinx/Manticore 常见返回列名为 `Index`,并且不支持 `SHOW TABLES FROM <db>` 语法。
|
||
data, fields, fallbackErr := s.MySQLDB.Query("SHOW TABLES")
|
||
if fallbackErr != nil {
|
||
return nil, fallbackErr
|
||
}
|
||
|
||
fallbackTables := make([]string, 0, len(data))
|
||
for _, row := range data {
|
||
if val, ok := row["Index"]; ok {
|
||
fallbackTables = append(fallbackTables, fmt.Sprintf("%v", val))
|
||
continue
|
||
}
|
||
if val, ok := row["index"]; ok {
|
||
fallbackTables = append(fallbackTables, fmt.Sprintf("%v", val))
|
||
continue
|
||
}
|
||
for _, field := range fields {
|
||
if val, ok := row[field]; ok {
|
||
fallbackTables = append(fallbackTables, fmt.Sprintf("%v", val))
|
||
break
|
||
}
|
||
}
|
||
}
|
||
|
||
return fallbackTables, nil
|
||
}
|
||
|
||
func (s *SphinxDB) GetCreateStatement(dbName, tableName string) (string, error) {
|
||
return s.MySQLDB.GetCreateStatement(s.resolveDatabaseName(dbName), tableName)
|
||
}
|
||
|
||
func (s *SphinxDB) GetColumns(dbName, tableName string) ([]connection.ColumnDefinition, error) {
|
||
// Sphinx 使用 DESCRIBE 语法获取索引结构
|
||
query := fmt.Sprintf("DESCRIBE %s", tableName)
|
||
data, _, err := s.MySQLDB.Query(query)
|
||
if err != nil {
|
||
// 如果 DESCRIBE 失败,尝试使用 MySQL 的方式作为降级
|
||
return s.MySQLDB.GetColumns(s.resolveDatabaseName(dbName), tableName)
|
||
}
|
||
|
||
var columns []connection.ColumnDefinition
|
||
for _, row := range data {
|
||
// Sphinx DESCRIBE 返回的字段:Field, Type, Properties
|
||
fieldName := ""
|
||
if val, ok := row["Field"]; ok {
|
||
fieldName = fmt.Sprintf("%v", val)
|
||
} else if val, ok := row["field"]; ok {
|
||
fieldName = fmt.Sprintf("%v", val)
|
||
}
|
||
|
||
fieldType := ""
|
||
if val, ok := row["Type"]; ok {
|
||
fieldType = fmt.Sprintf("%v", val)
|
||
} else if val, ok := row["type"]; ok {
|
||
fieldType = fmt.Sprintf("%v", val)
|
||
}
|
||
|
||
properties := ""
|
||
if val, ok := row["Properties"]; ok {
|
||
properties = fmt.Sprintf("%v", val)
|
||
} else if val, ok := row["properties"]; ok {
|
||
properties = fmt.Sprintf("%v", val)
|
||
}
|
||
|
||
if fieldName == "" {
|
||
continue
|
||
}
|
||
|
||
col := connection.ColumnDefinition{
|
||
Name: fieldName,
|
||
Type: fieldType,
|
||
Nullable: "YES", // Sphinx 默认字段可为空
|
||
Key: "", // Sphinx 没有主键概念
|
||
Default: nil, // Sphinx DESCRIBE 不返回默认值
|
||
Extra: properties,
|
||
Comment: "",
|
||
}
|
||
|
||
// 根据 properties 判断是否为索引字段
|
||
if strings.Contains(strings.ToLower(properties), "indexed") {
|
||
col.Key = "MUL"
|
||
}
|
||
|
||
columns = append(columns, col)
|
||
}
|
||
|
||
// 如果没有获取到任何列,尝试使用 MySQL 方式
|
||
if len(columns) == 0 {
|
||
logger.Warnf("Sphinx DESCRIBE 未返回任何列,尝试使用 MySQL 方式获取:表=%s", tableName)
|
||
return s.MySQLDB.GetColumns(s.resolveDatabaseName(dbName), tableName)
|
||
}
|
||
|
||
return columns, nil
|
||
}
|
||
|
||
func (s *SphinxDB) GetAllColumns(dbName string) ([]connection.ColumnDefinitionWithTable, error) {
|
||
return s.MySQLDB.GetAllColumns(s.resolveDatabaseName(dbName))
|
||
}
|
||
|
||
func (s *SphinxDB) GetIndexes(dbName, tableName string) ([]connection.IndexDefinition, error) {
|
||
return s.MySQLDB.GetIndexes(s.resolveDatabaseName(dbName), tableName)
|
||
}
|
||
|
||
func (s *SphinxDB) GetForeignKeys(dbName, tableName string) ([]connection.ForeignKeyDefinition, error) {
|
||
fks, err := s.MySQLDB.GetForeignKeys(s.resolveDatabaseName(dbName), tableName)
|
||
if err != nil && isSphinxUnsupportedFeatureError(err) {
|
||
return []connection.ForeignKeyDefinition{}, nil
|
||
}
|
||
return fks, err
|
||
}
|
||
|
||
func (s *SphinxDB) GetTriggers(dbName, tableName string) ([]connection.TriggerDefinition, error) {
|
||
triggers, err := s.MySQLDB.GetTriggers(s.resolveDatabaseName(dbName), tableName)
|
||
if err != nil && isSphinxUnsupportedFeatureError(err) {
|
||
return []connection.TriggerDefinition{}, nil
|
||
}
|
||
return triggers, err
|
||
}
|