Files

2.6 KiB
Raw Permalink Blame History

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