Go中构建动态WHERE条件应使用结构化变参(如...Condition),禁止裸interface{};IN子句须动态生成占位符并白名单校验字段名与操作符,空slice需跳过;ORDER BY/LIMIT必须通过白名单控制字段和数值范围。

变参函数怎么接住任意数量的WHERE条件
Go 的 func(...interface{}) 是构建查询生成器的基础,但直接用 ...interface{} 容易丢类型信息,导致 SQL 拼接时无法区分字段名、值、操作符。推荐用结构化参数替代裸 interface{}:定义一个 Condition 类型,包含 field、op(如 "="、"IN")、value(支持单值或 []interface{}),再让变参接收 ...Condition。
常见错误是把 nil 或空 slice 当作有效条件传入,结果生成 WHERE field = ? 但没绑定值,执行时报 sql: expected 1 arguments, got 0。务必在拼接前过滤掉 Condition{} 或 value == nil 的项。
-
Condition{Field: "status", Op: "=", Value: "active"}→status = ? -
Condition{Field: "id", Op: "IN", Value: []interface{}{1,2,3}}→id IN (?, ?, ?) - 传入
Condition{Field: "deleted", Op: "=", Value: nil}必须跳过,否则占位符不匹配
如何安全地展开 IN 子句的占位符
SQL 的 IN 不支持单个 ? 代表多个值,必须动态生成对应数量的 ?。手动拼字符串容易被注入,正确做法是:对每个 IN 条件的 Value,用 strings.Repeat(", ", len(vals)-1) 补逗号,并用 make([]interface{}, len(vals)) 构建参数切片。
别用 fmt.Sprintf("(%s)", strings.Join(placeholders, ",")) 直接拼进 SQL 字符串——哪怕 Value 是 int,也存在被构造恶意字符串绕过类型检查的风险。所有字段名和操作符必须白名单校验(比如只允许 map[string]bool{"=":true, "!=":true, "IN":true, "LIKE":true}),值一律走 ? 占位符绑定。
立即学习“go语言免费学习笔记(深入)”;
在 Golang 中使用 samber/hot 进行内存缓存,支持 LRU、LFU、TinyLFU、W‑TinyLFU、S3FIFO、ARC、TwoQueue、SIEVE、FIFO 等淘汰算法,提供 TTL、缓存加载器及分片功能。
-
IN的Value是[]string{"a","b"}→ 生成field IN (?, ?),参数列表追加"a", "b" -
Value是空 slice([]int{})→ 整个条件应被忽略,否则IN ()是语法错误 - 字段名如
"user.name"需提前用正则^[a-zA-Z_][a-zA-Z0-9_.]*$校验,禁止含空格或括号
ORDER BY 和 LIMIT 怎么避免 SQL 注入
ORDER BY 和 LIMIT 后面不能用 ? 占位符(驱动不支持),所以必须白名单控制。比如 OrderBy 接收 string,但只允许预设字段:map[string]bool{"id": true, "created_at": true, "score": true};Limit 接收 int,且必须 > 0 并 (防拖库)。
有人尝试用 fmt.Sprintf("ORDER BY %s", field) 然后靠 strings.Contains(field, ";") 过滤,这完全无效——攻击者用注释 id -- 或 Unicode 空格就能绕过。真正安全的方式是查表映射:orderMap := map[string]string{"id": "id", "name_desc": "name DESC"},传入键不存在就 panic 或返回默认排序。
-
OrderBy("name_desc")→ 映射为"name DESC",拼进 SQL -
OrderBy("id; DROP TABLE users")→ 键不在orderMap中,直接报错退出 -
Limit(-1)或Limit(0)应拒绝,最小合法值设为1
为什么不能把整个 WHERE 条件当字符串传进去
有些实现图省事,写成 Where("status = ? AND score > ?", "active", 80),看似灵活,实则破坏了查询生成器的核心价值:类型安全和可组合性。这种写法无法做字段名校验,无法自动处理 IN 展开,更没法和 Join、GroupBy 等后续方法链式调用。
更大的问题是调试困难——当生成的 SQL 出错,你得手动拆解字符串找哪个 ? 对应哪个参数,而结构化 Condition 可以直接打印 fmt.Printf("%+v", cond) 查字段、操作符、值是否符合预期。真正的灵活性来自组合能力,不是字符串拼接自由度。
复杂点在于 OR 分组和嵌套括号,比如 (status = ? OR type = ?) AND deleted = ?。这需要额外引入 Or() 方法返回子条件切片,而不是塞进扁平的 ...Condition。这点容易被忽略,但一旦业务出现多级逻辑,裸变参就撑不住了。

















