Go Squirrel 怎样用 Eq 安全构造可选查询条件
用 sq.Eq{"status": value} 为固定列名构造参数化条件,再根据参数是否提供,决定要不要调用 Where。关键是让列名由程序控制、用户值进入参数列表;不要把筛选值拼进 SQL 字符串,也不要把“没提供”误当成 false、0 或 SQL NULL。
官方地址:https://github.com/Masterminds/squirrel
Squirrel 的项目说明将其定位为已完成功能、仍接受修复的 SQL 构造器,本文解读这项已有能力,不把它包装成刚发布的新功能。适用场景是列表接口的可选筛选:状态、是否归档、负责人等参数可以分别省略。
先把可选值和SQL结构分开
传统拼接经常混合三个问题:决定条件是否出现、选择列名、表达参数值。Eq 负责后两者中的值表达,但 map 的键仍是 SQL 结构的一部分。列名必须来自代码或允许列表,不能直接把请求中的任意字段名塞进去。
多个 Where 条件通常以 AND 组合;Eq map 中多个键也构成 AND。构造器返回更新后的值,因此追加时写 query = query.Where(...),而不是调用后丢弃返回结果。官方用法见 Squirrel 项目说明。
图中的 筛选参数、提供状态 属于请求信息;固定列名、Eq、Where、SelectBuilder 属于构造职责。是否提供决定追加与否,Eq 关联固定列名和筛选值,Where 将条件加入构造器。

用指针区分未提供与零值
下面是完整的构造函数文件。它只返回 SQL 和 args,不执行数据库查询。Status=nil 表示不按状态筛选;指向空字符串则表示明确匹配空字符串。Archived 指向 false 时仍应产生条件。
package query
import (
"fmt"
sq "github.com/Masterminds/squirrel"
)
type TicketFilter struct {
Status *string
Archived *bool
OwnerID *int64
OnlyUnassigned bool
}
func BuildTicketQuery(filter TicketFilter) (string, []interface{}, error) {
// 互相矛盾的条件在应用层处理,不把组合解释留给数据库。
if filter.OnlyUnassigned && filter.OwnerID != nil {
return "", nil, fmt.Errorf("负责人和未分配条件不能同时指定")
}
query := sq.Select("id", "status", "archived", "owner_id").
From("support_tickets").
OrderBy("id DESC").
Limit(100).
PlaceholderFormat(sq.Dollar) // 本例使用 PostgreSQL 占位符。
if filter.Status != nil {
query = query.Where(sq.Eq{"status": *filter.Status})
}
if filter.Archived != nil {
// false 也是有效筛选值,不能用 if *filter.Archived 代替。
query = query.Where(sq.Eq{"archived": *filter.Archived})
}
if filter.OwnerID != nil {
query = query.Where(sq.Eq{"owner_id": *filter.OwnerID})
}
if filter.OnlyUnassigned {
// nil 的SQL语义是 IS NULL,不是忽略这个条件。
query = query.Where(sq.Eq{"owner_id": nil})
}
sqlText, args, err := query.ToSql()
if err != nil {
return "", nil, fmt.Errorf("构造工单查询失败: %w", err)
}
return sqlText, args, nil
}项目需在 go.mod 中声明并固定 github.com/Masterminds/squirrel 版本。调用方把接口参数解析成这个模型,再调用 BuildTicketQuery。指针只能保留本模型中的“提供或省略”;请求反序列化是否也保留显式 null,需要在传输模型中另外设计。
该函数允许所有可选字段都省略,结果是取前一百条工单。若产品不允许无筛选查询,应在入口额外拒绝,而不是期待 Eq 自动限制结果范围。Limit 也不替代租户权限条件。
让 Eq 处理 NULL 和列表语义
| 条件 | SQL含义 | 应用注意点 |
|---|---|---|
| 不调用 Where | 该条件省略 | 这才是不按该字段筛选 |
| Eq{"owner_id": nil} | owner_id IS NULL | 明确匹配无负责人 |
| Eq{"owner_id": int64(0)} | 等于占位参数0 | 0不是省略或NULL |
| Eq{"id": []int64{7, 11}} | id IN 两个占位参数 | 列表元素成为参数 |
| Eq{"id": []int64{}} | 恒假条件 | 不是取消id筛选 |
nil 切片与未提供切片应在接口层明确约定:如果你把切片作为 Eq 的值传进去,它按列表语义处理;如果需要空列表表示“不筛选”,就根据长度决定是否追加 Where。某些业务需要空集合表示“无任何可访问记录”,此时保留恒假条件反而符合要求。
另外,[]byte 通常作为数据库标量字节数据处理,不按普通切片生成 IN;[]uint8 与它是同一类型,不能通过改名绕过。列表与 nil 的构造逻辑见 Eq 实现。IN 列表中混入 nil 也不能代替显式的 IS NULL 分支。
ToSql 之后保持 SQL 与 args 配对
ToSql 返回两项需要一起使用的数据:带占位符的 SQL 和按相应次序排列的参数。数据库调用形态应保持为 db.QueryContext(ctx, sqlText, args...)。不能把 args 再格式化、替换进 SQL 文本后发送。
第二张图的 SelectBuilder、ToSql、SQL文本、args、占位符格式、QueryContext 表示构造结果及使用关系:构造器配置占位符,ToSql 产生文本与参数,QueryContext 同时依赖二者。

本例使用 Dollar 格式,面向接受 $1、$2 形式的驱动;使用问号占位符的驱动应选择对应格式。参数顺序由构造器生成,不要按请求参数的到达顺序自行重排 args,也不要从 map 遍历顺序猜测绑定位置。
哪些输入仍需要由应用控制
Eq 不是完整的查询权限系统。租户条件应来自认证上下文并由程序固定追加;排序字段、表名、连接片段和任意表达式仍属于结构,必须单独控制。参数化处理的是值,不会让一个恶意列名自动变安全。
和旧的字符串拼接方式相比,Eq 让普通等值、NULL 和 IN 条件更清楚,但也引入了需要记住的约定:nil 不是省略、空列表不是不限范围、切片类型影响 SQL 形态。采用时把这些约定放在统一查询函数中,避免每个接口各自解释。
状态是空字符串,Eq 会自动忽略吗?
不会。它仍然是一个值。若接口规定空字符串代表不筛选,应先在参数解析层规范化为未提供状态,不能依赖 Eq 猜测业务意图。
可以把“status =”作为 Eq 的键吗?
Eq 的键应是受控的列名或预先设计的 SQL 标识表达,不应拼上等号等运算片段。本例仅使用硬编码列名;其他比较关系选择对应构造类型。
ToSql 成功是否证明查询一定能执行?
不能。它证明构造器成功生成了文本与参数,不会访问数据库来确认表结构、字段类型、索引或权限。数据库执行错误仍由调用层处理,本文没有运行查询或声称线上执行成功。
Go Cron 怎样在关闭调度器时等待正在执行的任务
- 上一篇
- Go Cron 怎样在关闭调度器时等待正在执行的任务
- 下一篇
- Go Squirrel 怎样用 Expr 把表达式与绑定参数组合起来
-
- Golang · Go教程 | 1小时前 |
- Go LDAP 怎样在修改请求中只替换一个指定属性
- 302浏览 收藏
-
- Golang · Go教程 | 1小时前 | go · 分页查询 ·
- Go LDAP 怎样用分页查询读取大型目录结果
- 334浏览 收藏
-
- Golang · Go教程 | 1小时前 |
- Go LDAP 怎样对查询过滤器中的用户输入进行转义
- 135浏览 收藏
-
- Golang · Go教程 | 1小时前 |
- Go SFTP 怎样限制同一文件传输的并发请求数量
- 232浏览 收藏
-
- Golang · Go教程 | 1小时前 |
- Go SFTP 怎样用临时文件与重命名完成上传交接
- 355浏览 收藏
-
- Golang · Go教程 | 1小时前 |
- Go SFTP 怎样流式下载远端文件并保留错误
- 360浏览 收藏
-
- Golang · Go教程 | 1小时前 |
- Go INI 怎样保留重复键的多个值
- 446浏览 收藏
-
- Golang · Go教程 | 1小时前 |
- Go INI 怎样把多个配置源按优先级加载
- 210浏览 收藏
-
- Golang · Go教程 | 1小时前 |
- Go JSONPatch 怎样用 JSON Pointer 表示包含斜杠的键
- 187浏览 收藏
-
- Golang · Go教程 | 1小时前 |
- Go JSONPatch 怎样合并两份 JSON Merge Patch
- 208浏览 收藏
-
- Golang · Go教程 | 1小时前 |
- Go JSONPatch 怎样把 JSON Patch 操作应用到文档
- 164浏览 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 485次学习
-
- PubMedQA
- 深入了解PubMedQA生物医学问答数据集,涵盖其核心功能、使用方法及在临床决策、药物研发等场景的应用,助力提升NLP模型性能。
- 774次使用
-
- H2O EvalGPT
- H2O EvalGPT是H2O.ai推出的开源LLM评估平台,提供详细的大模型性能排行榜、行业特定基准测试及A/B测试功能,助您快速选择最适合项目的高性能大语言模型。
- 770次使用
-
- LMArena
- LMArena是加州大学伯克利分校推出的AI模型匿名评测平台。通过盲测投票机制,用户可对比不同大模型回答并生成实时排行榜,助力开发者优化模型及用户选择最佳AI工具。
- 856次使用
-
- HELM
- 深入了解斯坦福推出的HELM(Holistic Evaluation of Language Models)大模型评测体系。本文解析其核心功能、安装配置步骤及应用场景,涵盖准确性、公平性、鲁棒性等多维度指标,助力开发者全面优化语言模型性能。
- 821次使用
-
- MMBench
- MMBench是由上海人工智能实验室等机构联合推出的多模态基准测试平台,提供细粒度能力评估、大规模数据集及VLMEvalKit工具。本文详细介绍其核心功能、安装使用方法及应用场景,助力开发者全面评估多模态模型性能。
- 651次使用
-
- Java 性能优化上线清单:从定位、改造到灰度发布
- 2026-06-11 860浏览
-
- Spring Boot 压测验证:Gatling、JMeter 与性能回归门禁
- 2026-06-11 843浏览
-
- Java NMT 非堆内存排查:Direct Buffer、线程栈与 Metaspace 分析
- 2026-06-11 826浏览
-
- Spring Boot 容器内存优化:JVM 堆、非堆与 MaxRAMPercentage
- 2026-06-11 809浏览
-
- Tomcat 连接与线程参数调优:maxThreads、acceptCount 与 KeepAlive
- 2026-06-11 792浏览
