NamedQuery本身不自动防SQL注入,关键在参数绑定方式:查询模板必须静态,所有动态值须通过struct或map参数传入,表名/列名等无法参数化部分需白名单校验。

NamedQuery 本身不自动防 SQL 注入,关键在参数绑定方式
sqlx 的 NamedQuery 不是“开箱防注入”的魔法函数——它只是把命名参数(如 :name)替换成问号占位符,再交给 database/sql 去执行。真正起防护作用的是底层驱动的参数绑定机制,而非名字里带“Named”。如果你用字符串拼接构造 query 字符串,哪怕用了 NamedQuery,照样中招。
- ✅ 正确做法:查询模板写死,所有动态值全走
struct或map参数传入 - ❌ 错误做法:
sql := "SELECT * FROM users WHERE name = '" + name + "'";sqlx.NamedQuery(sql, ...) - ⚠️ 特别注意:SQL 模板里的表名、列名、ORDER BY 字段不能参数化——
database/sql不支持这些位置的绑定,必须靠白名单校验或正则过滤
struct 绑定比 map 更安全,且字段名必须匹配命名参数
用 struct 传参时,sqlx 默认按字段名(首字母大写)映射到 SQL 中的 :field_name,大小写和下划线规则要对齐。这比 map 少一层运行时反射歧义,也更利于编译期检查字段是否存在。
- struct 字段需导出(首字母大写),且建议加
dbtag 显式声明,比如Name string `db:"name"` - 如果 SQL 里写的是
:user_name,但 struct 字段叫UserName且没写db:"user_name"tag,sqlx 会静默忽略该参数,查不到数据但不报错 - map[string]interface{} 虽灵活,但键名拼错(比如
"user_name"写成"username")会导致参数缺失,且无类型约束,容易埋 runtime bug
警惕 QueryRowx + NamedQuery 组合导致的扫描失败
NamedQuery 返回的是 *sqlx.Rows,而 QueryRowx 是为单行设计的。直接对 NamedQuery 结果调 Scan 会 panic,常见错误信息是 sql: expected 1 destination argument, got 2 或 Rows.Scan called without calling Next。
- 查单行请用
Get或Select:比如err := db.Get(&u, query, arg),它内部会自动处理NamedQuery+ 单行扫描 - 若坚持用
QueryRowx,得先用Rebind把命名参数转成驱动原生格式(如 PostgreSQL 用$1),再传给QueryRowx——但这绕过了 sqlx 的命名语义,失去可读性,不推荐 - 所有
Scan操作前,必须确保Next()返回 true,NamedQuery不会自动帮你做这步
PostgreSQL 和 MySQL 的命名参数语法不兼容,别混用
sqlx 的 NamedQuery 支持多种方言,但底层驱动只认自己那一套占位符。比如 PostgreSQL 驱动期望 $1,MySQL 驱动期望 ?。sqlx 通过 Rebind 转换,但前提是你要告诉它目标方言。
- 初始化
*sqlx.DB时必须指定 driverName,比如sqlx.Connect("postgres", ...)或sqlx.Connect("mysql", ...) - 如果误用
sqlx.Connect("sqlite3", ...)连 PostgreSQL,NamedQuery生成的$1会被当成字面量,查询永远返回空或报错ERROR: syntax error at or near "$" - 跨数据库迁移时,别依赖 SQL 字符串硬编码——
:id在所有方言下都安全,但手写的$1或?会立刻失效
实际防注入最脆弱的一环,往往不在参数值本身,而在你把用户输入塞进 SQL 模板字符串的那一刻。只要守住“模板静态、参数动态”这条线,NamedQuery 就是可靠的。

















