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
|
|||
|
|
}
|