Go MySQL 升级后,带引号的问号报 expected 0 arguments
interpolateParams 开启后,旧 SQL 里的引号问号不再作为参数。使用真正的值占位符,并为 JSON_CONTAINS 绑定有效 JSON 文本。
升级 go-sql-driver/mysql1.10 后,interpolateParams=true 的查询报 sql: expected 0 arguments, got 1,而旧版本可以执行。若 SQL 写了 '?',先把它当作字符串字面量检查。
从 1.10 的相关解析路径开始,驱动区分普通 SQL、引号字符串、反引号和注释。正式 1.10.1 仍保留这套状态机,字符串里的问号不会被当作值占位符。旧写法碰巧被替换,不代表它是应当保留的 SQL 契约。
值占位符不带 SQL 引号
例如原来写成:
SELECT id FROM items WHERE JSON_CONTAINS(metadata, '?')
应当让参数绑定负责值的编码和转义:
SELECT id FROM items WHERE JSON_CONTAINS(metadata, ?)
不要把修复写成先把用户输入拼进引号,也不要自行统计 SQL 中所有问号的个数;注释、字符串及其他语法位置并非参数。
JSON 字符串还需要 JSON 编码
JSON_CONTAINS 的候选值要求有效 JSON 文本。Go 字符串 "blue" 作为普通参数直接传入时,数据库得到的可以是文本 blue,而不是带 JSON 双引号的字符串值。可以先按业务值编码:
candidate, err := json.Marshal("blue")
if err != nil {
return err
}
rows, err := db.QueryContext(ctx,
"SELECT id FROM items WHERE JSON_CONTAINS(metadata, ?)",
string(candidate),
)
这是已有函数里的调用片段,需导入 encoding/json,db 与 ctx 沿用项目对象。成功取得 rows 后仍需按原流程读取、检查 rows.Err 并关闭,不能只修参数后漏掉资源释放。
数值候选、字符串候选和 JSON null 有不同含义,应编码真实的业务类型。不要把所有参数都先转成字符串再 Marshal:数值 1 与字符串"1"在 JSON 比较中未必相同。
同一错误也可能来自参数确实多传、SQL 由另一层改写,或实际查询根本没有占位符。先保存脱敏后的 SQL 形状、参数个数与实际驱动版本,再确认是不是引号识别分支。
回查普通数字、JSON 字符串、包含引号和问号的内容,以及无参数查询。保留 interpolateParams 之外的连接配置与安全要求;不需要为一个占位符错误关闭 TLS 或放宽数据库权限。


