//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 ` 语法。 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 }