2.6 KiB
2.6 KiB
squirrel 的坑
github.com/Masterminds/squirrel,纯 SQL 查询构建器(query builder),2026-09 查证。
坑 1:Like 家族多 key 不排序,SQL 输出不确定(真雷)
Eq / NotEq / Lt / Gt 等 map 条件生成 SQL 前会对 key 做 sort.Strings 字典序排序(v1.1.0 起,expr.go 的 getSortedKeys),输出确定。
但 Like / NotLike / ILike / NotILike 的 toSql 是直接 for key, val := range lk——跟随 Go map 随机遍历序,多 key 时每次生成的 SQL 条件顺序随机:
第1次: WHERE nickname LIKE ? AND mobile LIKE ? | args=[%张% %138%]
第3次: WHERE mobile LIKE ? AND nickname LIKE ? | args=[%138% %张%] ← 顺序翻转
后果
- 查询结果没错(AND 交换律),但 SQL 字符串 + args 顺序在变
- 如果用「SQL 字符串 + 参数」生成缓存 key(hash 后当 Redis key 之类),同一查询会打出多个 key:
- 命中率暴跌:n 个 Like 条件最多 n! 种排列 → n! 个缓存 key
- 同一份数据存多份副本,TTL 内占内存
- 依赖 SQL diff 的单测会随机失败
规避
Like只放单个 key- 多模糊条件拆成多次
.Where(...)(内部是 append 切片,天然保序)或用sq.And{...}显式控制顺序
上游状态
- 无任何 issue 报告过此事(全量搜索过,含 closed),作者从未回应
- 当年修 Eq 排序的 PR #141 只改了 Eq/Lt 两处,紧邻的 Like 一行未动,讨论里也没人发现——纯遗漏,非有意设计(作者在 #135 明确说过支持排序)
- 库近乎停滞:最新 release v1.5.4(2023-03),master 最后 commit 2024-02,别指望上游修
坑 2:Eq 排序按字典序,与联合索引列序无关(知道即可)
Eq{"b":2, "a":1} 永远生成 WHERE a = ? AND b = ?,与你书写的顺序无关(issue #321)。
对主流数据库基本无影响:MySQL / PostgreSQL / SQL Server / Oracle 的 CBO 都会重排谓词,索引选择看的是「条件覆盖了哪些列(最左前缀)、选择度、类型是否隐式转换」,不是 WHERE 里的书写顺序。只有对谓词顺序敏感的引擎/老优化器才会踩到。
顺带:这个库的亮点
- builder 是值语义不可变的(基于 lann/builder 持久化数据结构),每次链式调用返回新副本,可安全分叉复用,不像 GORM 那样共享 Statement、分叉前要
Session()克隆 Eq家族的确定性输出有源码保证(expr.go排序逻辑),可放心用于缓存 key 场景- 子查询:
FromSelect(builder, alias),或任何 builder 作为Sqlizer嵌进Where/Having/Join