mirror of
https://github.com/Syngnat/GoNavi.git
synced 2026-07-18 19:32:47 +08:00
✨ feat(query): 支持多条SQL语句执行返回多结果集
- 新增 ResultSetData 结构体承载单个结果集数据 - 新增 MultiResultQuerier/MultiResultQuerierContext 可选接口 - 新增 scanMultiRows 函数利用 NextResultSet() 遍历所有结果集 - MySQL 驱动 DSN 开启 multiStatements=true 并实现多结果集接口 - 新增 DBQueryMulti Wails 方法,支持驱动原生多结果集及自动回退逐条执行 - 新增 Go 版 SQL 拆分函数 splitSQLStatements 及 10 个单元测试 - 前端 QueryEditor handleRun 改为一次性调用 DBQueryMulti - MongoDB 保持独立的逐条执行路径不受影响 - refs #235
This commit is contained in:
167
internal/app/sql_split.go
Normal file
167
internal/app/sql_split.go
Normal file
@@ -0,0 +1,167 @@
|
||||
package app
|
||||
|
||||
import "strings"
|
||||
|
||||
// splitSQLStatements 按分号拆分 SQL 文本为独立语句。
|
||||
// 正确处理单引号/双引号/反引号字符串、行注释(-- / #)、块注释(/* */)和
|
||||
// PostgreSQL/Kingbase 的 $$...$$ dollar-quoting,避免在这些上下文中错误拆分。
|
||||
func splitSQLStatements(sql string) []string {
|
||||
text := strings.ReplaceAll(sql, "\r\n", "\n")
|
||||
var statements []string
|
||||
|
||||
cur := ""
|
||||
inSingle := false
|
||||
inDouble := false
|
||||
inBacktick := false
|
||||
escaped := false
|
||||
inLineComment := false
|
||||
inBlockComment := false
|
||||
var dollarTag string // postgres/kingbase: $$...$$ or $tag$...$tag$
|
||||
|
||||
push := func() {
|
||||
s := strings.TrimSpace(cur)
|
||||
if s != "" {
|
||||
statements = append(statements, s)
|
||||
}
|
||||
cur = ""
|
||||
}
|
||||
|
||||
for i := 0; i < len(text); i++ {
|
||||
ch := text[i]
|
||||
next := byte(0)
|
||||
if i+1 < len(text) {
|
||||
next = text[i+1]
|
||||
}
|
||||
|
||||
// 行注释
|
||||
if inLineComment {
|
||||
if ch == '\n' {
|
||||
inLineComment = false
|
||||
}
|
||||
cur += string(ch)
|
||||
continue
|
||||
}
|
||||
|
||||
// 块注释
|
||||
if inBlockComment {
|
||||
cur += string(ch)
|
||||
if ch == '*' && next == '/' {
|
||||
cur += "/"
|
||||
i++
|
||||
inBlockComment = false
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
// Dollar-quoting
|
||||
if dollarTag != "" {
|
||||
if strings.HasPrefix(text[i:], dollarTag) {
|
||||
cur += dollarTag
|
||||
i += len(dollarTag) - 1
|
||||
dollarTag = ""
|
||||
} else {
|
||||
cur += string(ch)
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
// 转义字符
|
||||
if escaped {
|
||||
escaped = false
|
||||
cur += string(ch)
|
||||
continue
|
||||
}
|
||||
if (inSingle || inDouble) && ch == '\\' {
|
||||
escaped = true
|
||||
cur += string(ch)
|
||||
continue
|
||||
}
|
||||
|
||||
// 字符串开闭
|
||||
if !inDouble && !inBacktick && ch == '\'' {
|
||||
inSingle = !inSingle
|
||||
cur += string(ch)
|
||||
continue
|
||||
}
|
||||
if !inSingle && !inBacktick && ch == '"' {
|
||||
inDouble = !inDouble
|
||||
cur += string(ch)
|
||||
continue
|
||||
}
|
||||
if !inSingle && !inDouble && ch == '`' {
|
||||
inBacktick = !inBacktick
|
||||
cur += string(ch)
|
||||
continue
|
||||
}
|
||||
|
||||
// 在引号/反引号内部不做任何判断
|
||||
if inSingle || inDouble || inBacktick {
|
||||
cur += string(ch)
|
||||
continue
|
||||
}
|
||||
|
||||
// 行注释开始
|
||||
if ch == '-' && next == '-' {
|
||||
inLineComment = true
|
||||
cur += string(ch)
|
||||
continue
|
||||
}
|
||||
if ch == '#' {
|
||||
inLineComment = true
|
||||
cur += string(ch)
|
||||
continue
|
||||
}
|
||||
|
||||
// 块注释开始
|
||||
if ch == '/' && next == '*' {
|
||||
inBlockComment = true
|
||||
cur += "/*"
|
||||
i++
|
||||
continue
|
||||
}
|
||||
|
||||
// Dollar-quoting 开始
|
||||
if ch == '$' {
|
||||
if tag := parseSQLDollarTag(text[i:]); tag != "" {
|
||||
dollarTag = tag
|
||||
cur += tag
|
||||
i += len(tag) - 1
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
// 分号分隔(支持全角分号";")
|
||||
if ch == ';' {
|
||||
push()
|
||||
continue
|
||||
}
|
||||
// 全角分号 UTF-8 序列: 0xEF 0xBC 0x9B
|
||||
if ch == 0xEF && i+2 < len(text) && text[i+1] == 0xBC && text[i+2] == 0x9B {
|
||||
push()
|
||||
i += 2
|
||||
continue
|
||||
}
|
||||
|
||||
cur += string(ch)
|
||||
}
|
||||
|
||||
push()
|
||||
return statements
|
||||
}
|
||||
|
||||
// parseSQLDollarTag 解析 PostgreSQL/Kingbase 的 dollar-quoting 标签。
|
||||
func parseSQLDollarTag(s string) string {
|
||||
if len(s) < 2 || s[0] != '$' {
|
||||
return ""
|
||||
}
|
||||
for i := 1; i < len(s); i++ {
|
||||
c := s[i]
|
||||
if c == '$' {
|
||||
return s[:i+1]
|
||||
}
|
||||
if !((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '_') {
|
||||
return ""
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
Reference in New Issue
Block a user