不能用正则直接提取sql表名,因正则无法处理子查询、cte、引号包裹、换行注释等复杂结构;应使用xwb1989/sqlparser解析ast,递归遍历aliasedtableexpr和subquery等节点准确提取。

为什么不能直接用正则匹配 SQL 的 WHERE 条件
因为 SQL 的嵌套括号、字符串字面量(如 'a(b)c')、转义引号('O''Reilly')、注释(-- 和 /* */)会让正则迅速失控。你写出来的 WHERE 提取正则,大概率会在遇到 WHERE name = 'foo''bar' AND id IN (SELECT x FROM t) 时漏掉半个条件或提前截断。
真正可行的路径是:先做轻量词法扫描,识别出括号层级、字符串边界、注释范围,再基于这些上下文定位逻辑块。Golang 标准库没有现成 SQL 解析器,但你可以用 strings.Reader 配合状态机推进,比硬啃正则干净得多。
- 跳过所有
/* */和--后到行尾的内容,需单独维护“是否在注释中”状态 - 单引号字符串内两个连续单引号
''是转义,不是结束符;双引号字符串同理(取决于 SQL 方言) - 括号计数必须区分字面量内的括号——进入字符串后,括号不计入层级
如何用 RuleSet 动态控制字段重写与条件过滤
所谓“动态规则集”,本质是一组可配置的 func(string) string 或结构体规则,而不是把逻辑硬编码进解析流程。比如你想把所有 user_id 替换为 tenant_user_id,同时把 status = 'active' 改成 status IN ('active', 'pending'),这些不该耦合在词法扫描里。
建议定义规则类型:
type Rule struct {
TargetColumn string // 如 "user_id"
Matcher string // SQL LIKE 风格通配,或正则 pattern
Rewriter func(string) string
Scope string // "select", "where", "order" —— 限定生效位置
}
关键点在于:规则应用必须发生在语法结构已明确划分之后。例如,在解析出完整 WHERE 子句字符串后,再逐条匹配 Rule 并调用 Rewriter;而不是边扫描边替换,否则会破坏括号/引号平衡。
- 多个规则可能冲突(如两个规则都匹配
id),需约定优先级:按切片顺序执行,或加Priority int字段 -
Scope判断不能只靠关键词搜索——得依赖前面词法扫描标记的 AST-like 区域(如inWhere布尔标志) - 重写后的字符串必须重新校验括号和引号闭合,避免注入语法错误
重构时如何安全插入新条件而不破坏原有逻辑
直接拼接 AND (...) 很危险:原语句可能以分号结尾、可能已有 AND 或 OR 结尾、可能根本没 WHERE 子句。正确做法是提取并复用原始 WHERE 的“根逻辑单元”结构。
Go 配置库,使用 spf13/viper — 分层优先级(flag > env >file > KV > default),提供 BindPFlag/BindPFlags、SetEnvPrefix + SetEnvKeyReplace 等功能。
扫描完后,你应该得到一个类似这样的结构:
type WhereClause struct {
HasWhere bool
RawBody string // 不含 WHERE 关键字本身
RootExpr *LogicExpr // 递归结构:AndExpr / OrExpr / ParenExpr / AtomExpr
}
然后通过遍历 RootExpr,在合适位置插入新节点(比如所有顶层 AndExpr 的末尾),最后再线性展开成字符串。这样能保留原有括号层级和运算符优先级。
- 不要试图用
strings.Replace在字符串末尾加条件——WHERE a=1 OR b=2加AND c=3会变成逻辑错误 - 若原 SQL 没有
WHERE,需补全WHERE (...),注意空格和换行风格一致性 - 插入的条件本身也应经过相同词法扫描,确保引号、括号合法,避免带入非法字符
为什么 ParseSQL 必须返回带位置信息的 Token 流
因为重构不是纯文本替换,而是结构化编辑。如果你只返回 []string 或 map[string]string,就无法回答“这个 user_id 是在 SELECT 列表里,还是在 JOIN ON 条件里?”——而这两处的重写策略往往不同。
最小可用 Token 定义示例:
type Token struct {
Kind TokenType // IDENT, STRING, LPAREN, COMMENT, etc.
Value string
Position int // 字节偏移,用于后续 substring 替换
Line int
Col int
}
有了位置信息,你才能精准地在原始 SQL 字节数组上做 slice 替换,而不是反复 strings.Replace 导致偏移错乱。尤其当多条规则叠加时,位置信息是唯一可靠锚点。
- Token 化阶段就要识别方言差异:MySQL 允许
`col`,PostgreSQL 用"col",SQLite 两者都认——Kind应区分BacktickIdent和DoubleQuoteIdent - 不要省略空白符 Token(
Whitespace),它们影响格式还原;但可设IsSignificant: false方便跳过 - 错误恢复很重要:遇到无法识别的字符(如
\uFFFD),应生成InvalidToken并继续扫描,而不是 panic
最麻烦的从来不是怎么写规则,而是怎么让规则跑在真实 SQL 上不崩——括号、引号、注释、方言、空格,每个细节都在等你漏掉一个判断。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










