mirror of
https://github.com/Syngnat/GoNavi.git
synced 2026-05-06 20:03:05 +08:00
@@ -20,7 +20,10 @@
|
||||
| #315 | 窗体内缩放异常 | Fixed | `e19dd82` |
|
||||
| #316 | 人大金仓数据库驱动版本过低 | Fixed | `2500183` |
|
||||
| #317 | 驱动管理增加导入 jar 功能 | Blocked | - |
|
||||
| #318 | mysql,bit 列,修改成 1 失败 | Fixed | Pending |
|
||||
| #318 | mysql,bit 列,修改成 1 失败 | Fixed | `bee78be` |
|
||||
| #319 | 关于运行外部 sql 文件的一些建议 | Deferred | - |
|
||||
| #320 | 无法连接达梦数据库 | Investigating | - |
|
||||
| #327 | SHOW DATABASES 报错 | Fixed | Pending |
|
||||
|
||||
## Notes
|
||||
|
||||
@@ -36,6 +39,22 @@
|
||||
- 处理:为 MySQL `bit` 列补充写入值归一化,将常见文本/布尔/数值输入转换为驱动可接受的 `[]byte`。
|
||||
- 验证:补充 `internal/db/mysql_value_test.go` 回归测试,覆盖 `bit(1)` 的 insert/update 写入路径。
|
||||
|
||||
### #319
|
||||
|
||||
- 现有应用已支持“运行外部 SQL 文件”,但 issue 诉求包含目录树、目录加载、双击文件打开等整组工作区能力。
|
||||
- 该项已超出单点缺陷修复范围,暂按功能增强项顺延,避免在逐条修 bug 流程中引入大范围 UI/状态管理重构。
|
||||
|
||||
### #320
|
||||
|
||||
- 达梦当前走可选 Go 驱动代理安装链路,不支持 JAR 导入属于既有架构边界。
|
||||
- “卡在 20%” 初步定位在驱动下载阶段前的占位进度,后续需要补充下载链路诊断与失败可观测性后再落地修复。
|
||||
|
||||
### #327
|
||||
|
||||
- 根因:低权限 MySQL 账号执行 `SHOW DATABASES` 会直接报错,当前实现没有回退路径。
|
||||
- 处理:为数据库列表查询增加 `SELECT DATABASE()` 回退,仅保留当前连接库时也能正常展示。
|
||||
- 验证:补充 `internal/db/mysql_metadata_test.go` 回归测试,覆盖有权限、多库和低权限回退场景。
|
||||
|
||||
## Next
|
||||
|
||||
- 继续处理下一个最早且可直接落地的开放 issue。
|
||||
|
||||
@@ -74,6 +74,62 @@ func normalizeMySQLAddress(host string, port int) string {
|
||||
return fmt.Sprintf("%s:%d", h, p)
|
||||
}
|
||||
|
||||
var mysqlDatabaseQueries = []string{
|
||||
"SHOW DATABASES",
|
||||
"SELECT DATABASE() AS `Database`",
|
||||
}
|
||||
|
||||
func collectMySQLDatabaseNames(queryFn func(string) ([]map[string]interface{}, []string, error)) ([]string, error) {
|
||||
if queryFn == nil {
|
||||
return nil, fmt.Errorf("查询函数为空")
|
||||
}
|
||||
|
||||
names := make([]string, 0, 8)
|
||||
seen := make(map[string]struct{}, 8)
|
||||
var lastErr error
|
||||
|
||||
appendNames := func(rows []map[string]interface{}) {
|
||||
for _, row := range rows {
|
||||
for _, key := range []string{"Database", "database"} {
|
||||
val, ok := row[key]
|
||||
if !ok || val == nil {
|
||||
continue
|
||||
}
|
||||
name := strings.TrimSpace(fmt.Sprintf("%v", val))
|
||||
if name == "" || strings.EqualFold(name, "<nil>") {
|
||||
continue
|
||||
}
|
||||
if _, exists := seen[name]; exists {
|
||||
continue
|
||||
}
|
||||
seen[name] = struct{}{}
|
||||
names = append(names, name)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, sqlText := range mysqlDatabaseQueries {
|
||||
rows, _, err := queryFn(sqlText)
|
||||
if err != nil {
|
||||
lastErr = err
|
||||
continue
|
||||
}
|
||||
appendNames(rows)
|
||||
if len(names) > 0 {
|
||||
return names, nil
|
||||
}
|
||||
}
|
||||
|
||||
if len(names) > 0 {
|
||||
return names, nil
|
||||
}
|
||||
if lastErr != nil {
|
||||
return nil, lastErr
|
||||
}
|
||||
return nil, fmt.Errorf("未获取到可用数据库")
|
||||
}
|
||||
|
||||
func applyMySQLURI(config connection.ConnectionConfig) connection.ConnectionConfig {
|
||||
uriText := strings.TrimSpace(config.URI)
|
||||
if uriText == "" {
|
||||
@@ -364,19 +420,7 @@ func (m *MySQLDB) Exec(query string) (int64, error) {
|
||||
}
|
||||
|
||||
func (m *MySQLDB) GetDatabases() ([]string, error) {
|
||||
data, _, err := m.Query("SHOW DATABASES")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var dbs []string
|
||||
for _, row := range data {
|
||||
if val, ok := row["Database"]; ok {
|
||||
dbs = append(dbs, fmt.Sprintf("%v", val))
|
||||
} else if val, ok := row["database"]; ok {
|
||||
dbs = append(dbs, fmt.Sprintf("%v", val))
|
||||
}
|
||||
}
|
||||
return dbs, nil
|
||||
return collectMySQLDatabaseNames(m.Query)
|
||||
}
|
||||
|
||||
func (m *MySQLDB) GetTables(dbName string) ([]string, error) {
|
||||
|
||||
84
internal/db/mysql_metadata_test.go
Normal file
84
internal/db/mysql_metadata_test.go
Normal file
@@ -0,0 +1,84 @@
|
||||
package db
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestCollectMySQLDatabaseNames_FallsBackToCurrentDatabase(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
got, err := collectMySQLDatabaseNames(func(query string) ([]map[string]interface{}, []string, error) {
|
||||
switch query {
|
||||
case mysqlDatabaseQueries[0]:
|
||||
return nil, nil, errors.New("Error 1227 (42000): Access denied; you need (at least one of) the SHOW DATABASES privilege(s) for this operation")
|
||||
case mysqlDatabaseQueries[1]:
|
||||
return []map[string]interface{}{
|
||||
{"Database": "biz_app"},
|
||||
}, nil, nil
|
||||
default:
|
||||
return nil, nil, errors.New("unexpected query")
|
||||
}
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("collectMySQLDatabaseNames 返回错误: %v", err)
|
||||
}
|
||||
|
||||
want := []string{"biz_app"}
|
||||
if !reflect.DeepEqual(got, want) {
|
||||
t.Fatalf("unexpected database names, got=%v want=%v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCollectMySQLDatabaseNames_PrefersShowDatabasesWhenAvailable(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
got, err := collectMySQLDatabaseNames(func(query string) ([]map[string]interface{}, []string, error) {
|
||||
switch query {
|
||||
case mysqlDatabaseQueries[0]:
|
||||
return []map[string]interface{}{
|
||||
{"Database": "analytics"},
|
||||
{"database": "audit"},
|
||||
}, nil, nil
|
||||
case mysqlDatabaseQueries[1]:
|
||||
return []map[string]interface{}{
|
||||
{"Database": "should_not_be_used"},
|
||||
}, nil, nil
|
||||
default:
|
||||
return nil, nil, errors.New("unexpected query")
|
||||
}
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("collectMySQLDatabaseNames 返回错误: %v", err)
|
||||
}
|
||||
|
||||
want := []string{"analytics", "audit"}
|
||||
if !reflect.DeepEqual(got, want) {
|
||||
t.Fatalf("unexpected database names, got=%v want=%v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCollectMySQLDatabaseNames_ReturnsOriginalErrorWhenNoDatabaseResolved(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
expectErr := errors.New("show databases denied")
|
||||
got, err := collectMySQLDatabaseNames(func(query string) ([]map[string]interface{}, []string, error) {
|
||||
switch query {
|
||||
case mysqlDatabaseQueries[0]:
|
||||
return nil, nil, expectErr
|
||||
case mysqlDatabaseQueries[1]:
|
||||
return []map[string]interface{}{
|
||||
{"Database": nil},
|
||||
}, nil, nil
|
||||
default:
|
||||
return nil, nil, errors.New("unexpected query")
|
||||
}
|
||||
})
|
||||
if err == nil {
|
||||
t.Fatalf("期望返回错误,实际 got=%v", got)
|
||||
}
|
||||
if !errors.Is(err, expectErr) {
|
||||
t.Fatalf("错误不符合预期: %v", err)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user