129 lines
3.4 KiB
Go
129 lines
3.4 KiB
Go
// dbtool 数据库初始化工具:读取 sql/init.sql 并逐条执行
|
||
// 用法(在 nl-game-api-gin 目录下):go run ./cmd/dbtool -f ../sql/init.sql
|
||
// 连接参数复用 config.yaml 中的 mysql 配置(不指定库名,脚本自带 CREATE DATABASE)
|
||
package main
|
||
|
||
import (
|
||
"database/sql"
|
||
"flag"
|
||
"fmt"
|
||
"log"
|
||
"os"
|
||
"strings"
|
||
|
||
_ "github.com/go-sql-driver/mysql"
|
||
|
||
"nl-game-api-gin/internal/config"
|
||
)
|
||
|
||
func main() {
|
||
file := flag.String("f", "../sql/init.sql", "SQL 脚本路径")
|
||
conf := flag.String("c", "config.yaml", "配置文件路径")
|
||
flag.Parse()
|
||
if err := config.Load(*conf); err != nil {
|
||
log.Fatalln("配置加载失败:", err)
|
||
}
|
||
raw, err := os.ReadFile(*file)
|
||
if err != nil {
|
||
log.Fatalln("读取 SQL 文件失败:", err)
|
||
}
|
||
// 服务器级 DSN(不带库名),multiStatements 关闭、逐条执行便于定位错误
|
||
dsn := fmt.Sprintf("%s:%s@tcp(%s:%d)/?charset=utf8mb4&parseTime=false",
|
||
config.C.MySQL.User, config.C.MySQL.Password, config.C.MySQL.Host, config.C.MySQL.Port)
|
||
db, err := sql.Open("mysql", dsn)
|
||
if err != nil {
|
||
log.Fatalln("连接 MySQL 失败:", err)
|
||
}
|
||
defer db.Close()
|
||
// 限制单连接:保证 USE xiaoyouxi 对后续语句生效
|
||
db.SetMaxOpenConns(1)
|
||
if err := db.Ping(); err != nil {
|
||
log.Fatalln("MySQL 无法连通,请确认服务已启动且账号密码正确:", err)
|
||
}
|
||
stmts := splitSQL(string(raw))
|
||
log.Printf("共解析出 %d 条语句,开始执行…\n", len(stmts))
|
||
for i, s := range stmts {
|
||
if _, err := db.Exec(s); err != nil {
|
||
head := s
|
||
if len(head) > 80 {
|
||
head = head[:80] + "…"
|
||
}
|
||
log.Fatalf("第 %d 条语句执行失败: %v\n语句开头: %s", i+1, err, head)
|
||
}
|
||
}
|
||
log.Println("✅ 数据库初始化完成(库名 xiaoyouxi,含种子数据与超管 liqi)")
|
||
}
|
||
|
||
// splitSQL 把脚本按分号拆成独立语句:跟踪引号与注释状态,避免误切字符串内的分号
|
||
func splitSQL(text string) []string {
|
||
var stmts []string
|
||
var sb strings.Builder
|
||
inSingle, inDouble, inBacktick, inLineComment := false, false, false, false
|
||
runes := []rune(text)
|
||
for i := 0; i < len(runes); i++ {
|
||
ch := runes[i]
|
||
// 行注释:遇到换行结束(注释内容不写入语句)
|
||
if inLineComment {
|
||
if ch == '\n' {
|
||
inLineComment = false
|
||
}
|
||
continue
|
||
}
|
||
// 检测行注释起始(不在任何引号内)
|
||
if !inSingle && !inDouble && !inBacktick && ch == '-' && i+1 < len(runes) && runes[i+1] == '-' {
|
||
inLineComment = true
|
||
i++
|
||
continue
|
||
}
|
||
// 引号状态翻转(MySQL 字符串内以 \' 转义,需要跳过转义符)
|
||
switch {
|
||
case inSingle:
|
||
if ch == '\\' && i+1 < len(runes) {
|
||
sb.WriteRune(ch)
|
||
i++
|
||
sb.WriteRune(runes[i])
|
||
continue
|
||
}
|
||
if ch == '\'' {
|
||
inSingle = false
|
||
}
|
||
case inDouble:
|
||
if ch == '\\' && i+1 < len(runes) {
|
||
sb.WriteRune(ch)
|
||
i++
|
||
sb.WriteRune(runes[i])
|
||
continue
|
||
}
|
||
if ch == '"' {
|
||
inDouble = false
|
||
}
|
||
case inBacktick:
|
||
if ch == '`' {
|
||
inBacktick = false
|
||
}
|
||
default:
|
||
if ch == '\'' {
|
||
inSingle = true
|
||
} else if ch == '"' {
|
||
inDouble = true
|
||
} else if ch == '`' {
|
||
inBacktick = true
|
||
} else if ch == ';' {
|
||
// 语句结束
|
||
stmt := strings.TrimSpace(sb.String())
|
||
if stmt != "" {
|
||
stmts = append(stmts, stmt)
|
||
}
|
||
sb.Reset()
|
||
continue
|
||
}
|
||
}
|
||
sb.WriteRune(ch)
|
||
}
|
||
// 收尾:最后一条可能没有分号
|
||
if tail := strings.TrimSpace(sb.String()); tail != "" {
|
||
stmts = append(stmts, tail)
|
||
}
|
||
return stmts
|
||
}
|