mirror of
https://github.com/Syngnat/GoNavi.git
synced 2026-07-07 02:51:35 +08:00
🐛 fix(external-sql): 修复外部 SQL 文件保存不写回源文件
- 保存逻辑:外部 SQL 文件标签页携带 filePath,保存时写回原始磁盘文件 - 后端接口:新增 WriteSQLFile 能力,支持覆盖已有 SQL 文件并保留原文件权限 - 状态隔离:外部文件保存失败时不创建 savedQuery,避免写入 localStorage 副本 - 兼容行为:非文件标签页继续沿用原有 savedQuery 快速保存逻辑 - 文案优化:将数据库下入口改为“外部 SQL 目录”,减少与单文件打开入口的歧义 - 测试覆盖:补充前端保存分支、后端写文件边界和外部 SQL 目录文案测试 Refs #422
This commit is contained in:
@@ -96,6 +96,29 @@ func readSQLFileByPath(filePath string) connection.QueryResult {
|
||||
return connection.QueryResult{Success: true, Data: string(content)}
|
||||
}
|
||||
|
||||
func writeSQLFileByPath(filePath string, content string) connection.QueryResult {
|
||||
target := strings.TrimSpace(filePath)
|
||||
if target == "" {
|
||||
return connection.QueryResult{Success: false, Message: "文件路径不能为空"}
|
||||
}
|
||||
if abs, err := filepath.Abs(target); err == nil {
|
||||
target = abs
|
||||
}
|
||||
|
||||
info, err := os.Stat(target)
|
||||
if err != nil {
|
||||
return connection.QueryResult{Success: false, Message: fmt.Sprintf("无法读取文件信息: %v", err)}
|
||||
}
|
||||
if info.IsDir() {
|
||||
return connection.QueryResult{Success: false, Message: "所选路径不是 SQL 文件"}
|
||||
}
|
||||
|
||||
if err := os.WriteFile(target, []byte(content), info.Mode().Perm()); err != nil {
|
||||
return connection.QueryResult{Success: false, Message: fmt.Sprintf("无法写入 SQL 文件: %v", err)}
|
||||
}
|
||||
return connection.QueryResult{Success: true, Data: map[string]interface{}{"filePath": target}}
|
||||
}
|
||||
|
||||
func buildSQLDirectoryEntries(directory string) ([]SQLDirectoryEntry, error) {
|
||||
entries, err := os.ReadDir(directory)
|
||||
if err != nil {
|
||||
@@ -215,6 +238,10 @@ func (a *App) ReadSQLFile(filePath string) connection.QueryResult {
|
||||
return readSQLFileByPath(filePath)
|
||||
}
|
||||
|
||||
func (a *App) WriteSQLFile(filePath string, content string) connection.QueryResult {
|
||||
return writeSQLFileByPath(filePath, content)
|
||||
}
|
||||
|
||||
// ExecuteSQLFile 在后端流式读取并执行大 SQL 文件,通过事件推送进度。
|
||||
// 前端通过 EventsOn("sqlfile:progress", ...) 监听进度。
|
||||
func (a *App) ExecuteSQLFile(config connection.ConnectionConfig, dbName string, filePath string, jobID string) connection.QueryResult {
|
||||
|
||||
@@ -41,6 +41,40 @@ func TestBuildSQLDirectoryEntriesKeepsOnlySQLFilesAndNestedFolders(t *testing.T)
|
||||
}
|
||||
}
|
||||
|
||||
func TestWriteSQLFileByPathOverwritesExistingSQLFile(t *testing.T) {
|
||||
filePath := filepath.Join(t.TempDir(), "report.sql")
|
||||
if err := os.WriteFile(filePath, []byte("select 1;"), 0o644); err != nil {
|
||||
t.Fatalf("WriteFile returned error: %v", err)
|
||||
}
|
||||
|
||||
result := writeSQLFileByPath(filePath, "select 2;\n")
|
||||
if !result.Success {
|
||||
t.Fatalf("expected sql file write to succeed, got %#v", result)
|
||||
}
|
||||
|
||||
content, err := os.ReadFile(filePath)
|
||||
if err != nil {
|
||||
t.Fatalf("ReadFile returned error: %v", err)
|
||||
}
|
||||
if string(content) != "select 2;\n" {
|
||||
t.Fatalf("expected file content to be overwritten, got %q", string(content))
|
||||
}
|
||||
}
|
||||
|
||||
func TestWriteSQLFileByPathRejectsDirectories(t *testing.T) {
|
||||
result := writeSQLFileByPath(t.TempDir(), "select 1;")
|
||||
if result.Success {
|
||||
t.Fatalf("expected directory write to fail, got %#v", result)
|
||||
}
|
||||
}
|
||||
|
||||
func TestWriteSQLFileByPathRejectsEmptyPath(t *testing.T) {
|
||||
result := writeSQLFileByPath(" ", "select 1;")
|
||||
if result.Success {
|
||||
t.Fatalf("expected empty path write to fail, got %#v", result)
|
||||
}
|
||||
}
|
||||
|
||||
func TestReadSQLFileByPathReturnsLargeFileMetadata(t *testing.T) {
|
||||
filePath := filepath.Join(t.TempDir(), "big.sql")
|
||||
file, err := os.Create(filePath)
|
||||
|
||||
Reference in New Issue
Block a user