86 lines
2.6 KiB
Go
86 lines
2.6 KiB
Go
// applysql —— 开发辅助工具:把 SQL 文件应用到 z_xk 库
|
||
//
|
||
// 为什么需要它:Windows 开发机没有 mysql 客户端,而 Go 项目本身带着
|
||
// GORM 依赖与 config.Load() 的 DSN 解析逻辑,直接复用即可执行 DDL,
|
||
// 且凭据不经过命令行/控制台(从 manifest/config/config.yaml 读取)。
|
||
//
|
||
// 用法(必须在 nl-tcm-agent 项目根目录执行,保证能读到配置文件):
|
||
// go run ./tools/applysql <sql文件1> [sql文件2 ...]
|
||
//
|
||
// 说明:
|
||
// - 按 ";\n" 粗粒度拆分语句(我们自己的 SQL 文件都是规范的分号+换行结尾)
|
||
// - "--" 开头的注释行会被剥掉
|
||
// - 任一语句失败立即退出(DDL 顺序有依赖,跳过会导致后续更混乱)
|
||
package main
|
||
|
||
import (
|
||
"fmt"
|
||
"log"
|
||
"os"
|
||
"strings"
|
||
|
||
"tcm-agent/internal/config"
|
||
"tcm-agent/internal/dao"
|
||
)
|
||
|
||
func main() {
|
||
if len(os.Args) < 2 {
|
||
log.Fatal("用法: go run ./tools/applysql <sql文件...>")
|
||
}
|
||
cfg := config.Load()
|
||
if cfg.DB.DSN == "" {
|
||
log.Fatal("配置缺少 db.dsn(检查 manifest/config/config.yaml)")
|
||
}
|
||
if err := dao.Init(cfg.DB.DSN); err != nil {
|
||
log.Fatalf("连接数据库失败: %v", err)
|
||
}
|
||
for _, path := range os.Args[1:] {
|
||
raw, err := os.ReadFile(path)
|
||
if err != nil {
|
||
log.Fatalf("读取 %s 失败: %v", path, err)
|
||
}
|
||
stmts := splitStatements(string(raw))
|
||
fmt.Printf("== %s:%d 条语句 ==\n", path, len(stmts))
|
||
for i, stmt := range stmts {
|
||
if err := dao.DB.Exec(stmt).Error; err != nil {
|
||
log.Fatalf("第 %d 条语句执行失败: %v\n语句片段: %.120s", i+1, err, stmt)
|
||
}
|
||
fmt.Printf(" [%d/%d] OK %.60s...\n", i+1, len(stmts), firstLine(stmt))
|
||
}
|
||
}
|
||
fmt.Println("全部执行完成")
|
||
}
|
||
|
||
// splitStatements 把 SQL 文本拆成可执行语句:剥注释行后按分号结尾切分
|
||
func splitStatements(sqlText string) []string {
|
||
// Windows 编辑器写出的文件是 CRLF,先统一成 LF,否则 ";\n" 切分匹配不到
|
||
sqlText = strings.ReplaceAll(sqlText, "\r\n", "\n")
|
||
var sb strings.Builder
|
||
for _, line := range strings.Split(sqlText, "\n") {
|
||
trimmed := strings.TrimSpace(line)
|
||
// 剥掉整行注释(字段内注释在行中不受影响)
|
||
if strings.HasPrefix(trimmed, "--") || trimmed == "" {
|
||
continue
|
||
}
|
||
sb.WriteString(line)
|
||
sb.WriteString("\n")
|
||
}
|
||
parts := strings.Split(sb.String(), ";\n")
|
||
out := make([]string, 0, len(parts))
|
||
for _, p := range parts {
|
||
p = strings.TrimSpace(p)
|
||
if p != "" {
|
||
out = append(out, p)
|
||
}
|
||
}
|
||
return out
|
||
}
|
||
|
||
// firstLine 取语句首行用于进度展示
|
||
func firstLine(stmt string) string {
|
||
if i := strings.Index(stmt, "\n"); i > 0 {
|
||
return stmt[:i]
|
||
}
|
||
return stmt
|
||
}
|