mirror of
https://github.com/Syngnat/GoNavi.git
synced 2026-05-12 02:49:42 +08:00
- 批量导出/备份:表与数据库支持全选/反选/智能上下文 - 右键菜单:单元格菜单支持设置 NULL - 编辑优化:大字段弹窗、仅值变化标记、提交只发送差异字段 - 事务支持:PostgreSQL/SQLite/Oracle/DaMeng/KingBase ApplyChanges - MySQL 修复:提交前归一化 datetime,避免写入失败 - 性能优化:移除 activeCell 重渲染、useRef 存储选中节点、防重加载 - Redis 优化:二进制智能解码与视图模式切换 - 资源更新:替换前端 favicon/logo
293 lines
10 KiB
Go
293 lines
10 KiB
Go
package app
|
||
|
||
import (
|
||
"context"
|
||
"fmt"
|
||
"strings"
|
||
"time"
|
||
|
||
"GoNavi-Wails/internal/connection"
|
||
"GoNavi-Wails/internal/logger"
|
||
"GoNavi-Wails/internal/utils"
|
||
)
|
||
|
||
// Generic DB Methods
|
||
|
||
func (a *App) DBConnect(config connection.ConnectionConfig) connection.QueryResult {
|
||
// 连接测试需要强制 ping,避免缓存命中但连接已失效时误判成功。
|
||
_, err := a.getDatabaseForcePing(config)
|
||
if err != nil {
|
||
logger.Error(err, "DBConnect 连接失败:%s", formatConnSummary(config))
|
||
return connection.QueryResult{Success: false, Message: err.Error()}
|
||
}
|
||
|
||
logger.Infof("DBConnect 连接成功:%s", formatConnSummary(config))
|
||
return connection.QueryResult{Success: true, Message: "连接成功"}
|
||
}
|
||
|
||
func (a *App) TestConnection(config connection.ConnectionConfig) connection.QueryResult {
|
||
_, err := a.getDatabaseForcePing(config)
|
||
if err != nil {
|
||
logger.Error(err, "TestConnection 连接测试失败:%s", formatConnSummary(config))
|
||
return connection.QueryResult{Success: false, Message: err.Error()}
|
||
}
|
||
|
||
logger.Infof("TestConnection 连接测试成功:%s", formatConnSummary(config))
|
||
return connection.QueryResult{Success: true, Message: "连接成功"}
|
||
}
|
||
|
||
func (a *App) CreateDatabase(config connection.ConnectionConfig, dbName string) connection.QueryResult {
|
||
runConfig := config
|
||
runConfig.Database = ""
|
||
|
||
dbInst, err := a.getDatabase(runConfig)
|
||
if err != nil {
|
||
return connection.QueryResult{Success: false, Message: err.Error()}
|
||
}
|
||
|
||
escapedDbName := strings.ReplaceAll(dbName, "`", "``")
|
||
query := fmt.Sprintf("CREATE DATABASE `%s` CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci", escapedDbName)
|
||
if runConfig.Type == "postgres" {
|
||
escapedDbName = strings.ReplaceAll(dbName, `"`, `""`)
|
||
query = fmt.Sprintf("CREATE DATABASE \"%s\"", escapedDbName)
|
||
}
|
||
|
||
_, err = dbInst.Exec(query)
|
||
if err != nil {
|
||
return connection.QueryResult{Success: false, Message: err.Error()}
|
||
}
|
||
|
||
return connection.QueryResult{Success: true, Message: "Database created successfully"}
|
||
}
|
||
|
||
func (a *App) MySQLConnect(config connection.ConnectionConfig) connection.QueryResult {
|
||
config.Type = "mysql"
|
||
return a.DBConnect(config)
|
||
}
|
||
|
||
func (a *App) MySQLQuery(config connection.ConnectionConfig, dbName string, query string) connection.QueryResult {
|
||
config.Type = "mysql"
|
||
return a.DBQuery(config, dbName, query)
|
||
}
|
||
|
||
func (a *App) MySQLGetDatabases(config connection.ConnectionConfig) connection.QueryResult {
|
||
config.Type = "mysql"
|
||
return a.DBGetDatabases(config)
|
||
}
|
||
|
||
func (a *App) MySQLGetTables(config connection.ConnectionConfig, dbName string) connection.QueryResult {
|
||
config.Type = "mysql"
|
||
return a.DBGetTables(config, dbName)
|
||
}
|
||
|
||
func (a *App) MySQLShowCreateTable(config connection.ConnectionConfig, dbName string, tableName string) connection.QueryResult {
|
||
config.Type = "mysql"
|
||
return a.DBShowCreateTable(config, dbName, tableName)
|
||
}
|
||
|
||
func (a *App) DBQuery(config connection.ConnectionConfig, dbName string, query string) connection.QueryResult {
|
||
runConfig := normalizeRunConfig(config, dbName)
|
||
|
||
dbInst, err := a.getDatabase(runConfig)
|
||
if err != nil {
|
||
logger.Error(err, "DBQuery 获取连接失败:%s", formatConnSummary(runConfig))
|
||
return connection.QueryResult{Success: false, Message: err.Error()}
|
||
}
|
||
|
||
query = sanitizeSQLForPgLike(runConfig.Type, query)
|
||
timeoutSeconds := runConfig.Timeout
|
||
if timeoutSeconds <= 0 {
|
||
timeoutSeconds = 30
|
||
}
|
||
ctx, cancel := utils.ContextWithTimeout(time.Duration(timeoutSeconds) * time.Second)
|
||
defer cancel()
|
||
|
||
lowerQuery := strings.TrimSpace(strings.ToLower(query))
|
||
if strings.HasPrefix(lowerQuery, "select") || strings.HasPrefix(lowerQuery, "show") || strings.HasPrefix(lowerQuery, "describe") || strings.HasPrefix(lowerQuery, "explain") {
|
||
var data []map[string]interface{}
|
||
var columns []string
|
||
if q, ok := dbInst.(interface {
|
||
QueryContext(context.Context, string) ([]map[string]interface{}, []string, error)
|
||
}); ok {
|
||
data, columns, err = q.QueryContext(ctx, query)
|
||
} else {
|
||
data, columns, err = dbInst.Query(query)
|
||
}
|
||
if err != nil {
|
||
logger.Error(err, "DBQuery 查询失败:%s SQL片段=%q", formatConnSummary(runConfig), sqlSnippet(query))
|
||
return connection.QueryResult{Success: false, Message: err.Error()}
|
||
}
|
||
return connection.QueryResult{Success: true, Data: data, Fields: columns}
|
||
} else {
|
||
var affected int64
|
||
if e, ok := dbInst.(interface {
|
||
ExecContext(context.Context, string) (int64, error)
|
||
}); ok {
|
||
affected, err = e.ExecContext(ctx, query)
|
||
} else {
|
||
affected, err = dbInst.Exec(query)
|
||
}
|
||
if err != nil {
|
||
logger.Error(err, "DBQuery 执行失败:%s SQL片段=%q", formatConnSummary(runConfig), sqlSnippet(query))
|
||
return connection.QueryResult{Success: false, Message: err.Error()}
|
||
}
|
||
return connection.QueryResult{Success: true, Data: map[string]int64{"affectedRows": affected}}
|
||
}
|
||
}
|
||
|
||
func sqlSnippet(query string) string {
|
||
q := strings.TrimSpace(query)
|
||
const max = 200
|
||
if len(q) <= max {
|
||
return q
|
||
}
|
||
return q[:max] + "..."
|
||
}
|
||
|
||
func (a *App) DBGetDatabases(config connection.ConnectionConfig) connection.QueryResult {
|
||
dbInst, err := a.getDatabase(config)
|
||
if err != nil {
|
||
logger.Error(err, "DBGetDatabases 获取连接失败:%s", formatConnSummary(config))
|
||
return connection.QueryResult{Success: false, Message: err.Error()}
|
||
}
|
||
|
||
dbs, err := dbInst.GetDatabases()
|
||
if err != nil {
|
||
logger.Error(err, "DBGetDatabases 获取数据库列表失败:%s", formatConnSummary(config))
|
||
return connection.QueryResult{Success: false, Message: err.Error()}
|
||
}
|
||
|
||
var resData []map[string]string
|
||
for _, name := range dbs {
|
||
resData = append(resData, map[string]string{"Database": name})
|
||
}
|
||
|
||
return connection.QueryResult{Success: true, Data: resData}
|
||
}
|
||
|
||
func (a *App) DBGetTables(config connection.ConnectionConfig, dbName string) connection.QueryResult {
|
||
runConfig := normalizeRunConfig(config, dbName)
|
||
|
||
dbInst, err := a.getDatabase(runConfig)
|
||
if err != nil {
|
||
logger.Error(err, "DBGetTables 获取连接失败:%s", formatConnSummary(runConfig))
|
||
return connection.QueryResult{Success: false, Message: err.Error()}
|
||
}
|
||
|
||
tables, err := dbInst.GetTables(dbName)
|
||
if err != nil {
|
||
logger.Error(err, "DBGetTables 获取表列表失败:%s", formatConnSummary(runConfig))
|
||
return connection.QueryResult{Success: false, Message: err.Error()}
|
||
}
|
||
|
||
var resData []map[string]string
|
||
for _, name := range tables {
|
||
resData = append(resData, map[string]string{"Table": name})
|
||
}
|
||
|
||
return connection.QueryResult{Success: true, Data: resData}
|
||
}
|
||
|
||
func (a *App) DBShowCreateTable(config connection.ConnectionConfig, dbName string, tableName string) connection.QueryResult {
|
||
runConfig := normalizeRunConfig(config, dbName)
|
||
|
||
dbInst, err := a.getDatabase(runConfig)
|
||
if err != nil {
|
||
logger.Error(err, "DBShowCreateTable 获取连接失败:%s", formatConnSummary(runConfig))
|
||
return connection.QueryResult{Success: false, Message: err.Error()}
|
||
}
|
||
|
||
schemaName, pureTableName := normalizeSchemaAndTable(config, dbName, tableName)
|
||
sqlStr, err := dbInst.GetCreateStatement(schemaName, pureTableName)
|
||
if err != nil {
|
||
logger.Error(err, "DBShowCreateTable 获取建表语句失败:%s 表=%s", formatConnSummary(runConfig), tableName)
|
||
return connection.QueryResult{Success: false, Message: err.Error()}
|
||
}
|
||
|
||
return connection.QueryResult{Success: true, Data: sqlStr}
|
||
}
|
||
|
||
func (a *App) DBGetColumns(config connection.ConnectionConfig, dbName string, tableName string) connection.QueryResult {
|
||
runConfig := normalizeRunConfig(config, dbName)
|
||
|
||
dbInst, err := a.getDatabase(runConfig)
|
||
if err != nil {
|
||
return connection.QueryResult{Success: false, Message: err.Error()}
|
||
}
|
||
|
||
schemaName, pureTableName := normalizeSchemaAndTable(config, dbName, tableName)
|
||
columns, err := dbInst.GetColumns(schemaName, pureTableName)
|
||
if err != nil {
|
||
return connection.QueryResult{Success: false, Message: err.Error()}
|
||
}
|
||
|
||
return connection.QueryResult{Success: true, Data: columns}
|
||
}
|
||
|
||
func (a *App) DBGetIndexes(config connection.ConnectionConfig, dbName string, tableName string) connection.QueryResult {
|
||
runConfig := normalizeRunConfig(config, dbName)
|
||
|
||
dbInst, err := a.getDatabase(runConfig)
|
||
if err != nil {
|
||
return connection.QueryResult{Success: false, Message: err.Error()}
|
||
}
|
||
|
||
schemaName, pureTableName := normalizeSchemaAndTable(config, dbName, tableName)
|
||
indexes, err := dbInst.GetIndexes(schemaName, pureTableName)
|
||
if err != nil {
|
||
return connection.QueryResult{Success: false, Message: err.Error()}
|
||
}
|
||
|
||
return connection.QueryResult{Success: true, Data: indexes}
|
||
}
|
||
|
||
func (a *App) DBGetForeignKeys(config connection.ConnectionConfig, dbName string, tableName string) connection.QueryResult {
|
||
runConfig := normalizeRunConfig(config, dbName)
|
||
|
||
dbInst, err := a.getDatabase(runConfig)
|
||
if err != nil {
|
||
return connection.QueryResult{Success: false, Message: err.Error()}
|
||
}
|
||
|
||
schemaName, pureTableName := normalizeSchemaAndTable(config, dbName, tableName)
|
||
fks, err := dbInst.GetForeignKeys(schemaName, pureTableName)
|
||
if err != nil {
|
||
return connection.QueryResult{Success: false, Message: err.Error()}
|
||
}
|
||
|
||
return connection.QueryResult{Success: true, Data: fks}
|
||
}
|
||
|
||
func (a *App) DBGetTriggers(config connection.ConnectionConfig, dbName string, tableName string) connection.QueryResult {
|
||
runConfig := normalizeRunConfig(config, dbName)
|
||
|
||
dbInst, err := a.getDatabase(runConfig)
|
||
if err != nil {
|
||
return connection.QueryResult{Success: false, Message: err.Error()}
|
||
}
|
||
|
||
schemaName, pureTableName := normalizeSchemaAndTable(config, dbName, tableName)
|
||
triggers, err := dbInst.GetTriggers(schemaName, pureTableName)
|
||
if err != nil {
|
||
return connection.QueryResult{Success: false, Message: err.Error()}
|
||
}
|
||
|
||
return connection.QueryResult{Success: true, Data: triggers}
|
||
}
|
||
|
||
func (a *App) DBGetAllColumns(config connection.ConnectionConfig, dbName string) connection.QueryResult {
|
||
runConfig := normalizeRunConfig(config, dbName)
|
||
|
||
dbInst, err := a.getDatabase(runConfig)
|
||
if err != nil {
|
||
return connection.QueryResult{Success: false, Message: err.Error()}
|
||
}
|
||
|
||
cols, err := dbInst.GetAllColumns(dbName)
|
||
if err != nil {
|
||
return connection.QueryResult{Success: false, Message: err.Error()}
|
||
}
|
||
|
||
return connection.QueryResult{Success: true, Data: cols}
|
||
}
|