登录
推荐 文章 Go 技术 课程 下载 专题 AI
首页 >  Golang >  Go教程

Go Squirrel 怎样用 PlaceholderFormat 转换数据库占位符

来源:17golang原创

时间:2026-10-29 12:24:53 339浏览 收藏

Go Squirrel 的占位符转换,核心就是在构造器上设置 PlaceholderFormat:默认的 Question 保留 ?,PostgreSQL 常用 Dollar 生成 $1、$2,还可以选择 Colon 或 AtP。转换只改 SQL 文本,不会把参数值拼进 SQL;args 仍按出现顺序传给 database/sql。

官方资料:https://github.com/Masterminds/squirrel

要点速览
  • 统一配置优先放在 StatementBuilder,单个查询也可单独覆盖。
  • ToSql() 同时返回 SQL、args 和 error,先看三者是否匹配。
  • 子查询最终由父查询统一转换;PostgreSQL 字面问号写成 ??。

先选对 PlaceholderFormat,再生成 SQL

Squirrel 的默认格式是 Question,适合继续使用问号占位符的驱动。若目标驱动使用 PostgreSQL 风格的位置参数,可把构造器改成 sq.Dollar。四种内置格式可以这样理解:

格式SQL 中的结果常见场景
Question?问号占位符驱动
Dollar$1、$2PostgreSQL
Colon:1、:2冒号位置参数
AtP@p1、@p2SQL Server 风格
Go Squirrel PlaceholderFormat 将问号占位符映射到 Dollar、Colon 和 AtP 的说明图
图1:PlaceholderFormat 的占位符映射说明图,不是运行截图。

最小用法是先用问号写条件,再在构造器上选择格式:

package main

import (
    "fmt"

    sq "github.com/Masterminds/squirrel"
)

func main() {
    // 先用通用的 ? 表达条件,最终由 PlaceholderFormat 负责转换。
    builder := sq.StatementBuilder.PlaceholderFormat(sq.Dollar)
    query := builder.Select("id", "name").
        From("users").
        Where("status = ? AND age > ?", "active", 18)

    // ToSql 同时返回 SQL 文本、参数顺序和构造错误。
    sqlText, args, err := query.ToSql()
    if err != nil {
        panic(err) // 示例中直接终止;生产代码应向上返回错误。
    }
    fmt.Println(sqlText) // SELECT id, name FROM users WHERE status = $1 AND age > $2
    fmt.Printf("%v\n", args) // [active 18]
}

实际字段条件的排序由构造器处理,但使用时不要把 $1、$2 手工写入普通条件。让 Squirrel 统一编号,args 才不容易错位。

全局配置和单个查询覆盖怎么选

同一个数据库连接池通常只对应一种占位符风格,可以从 StatementBuilder 统一设置;如果一个服务同时连接不同数据库,则让每个查询或每组 builder 明确声明格式。

// PostgreSQL 查询统一生成 $1、$2;args 仍然单独传递。
postgres := sq.StatementBuilder.PlaceholderFormat(sq.Dollar)
sqlText, args, err := postgres.
    Select("id").
    From("orders").
    Where("user_id = ? AND state = ?", 42, "paid").
    ToSql()
if err != nil {
    return err // 生产代码把构造失败交给调用方处理。
}
// sqlText: ... user_id = $1 AND state = $2
// args: [42 paid]
_ = sqlText
_ = args

也可以只在一个 builder 上调用 .PlaceholderFormat(sq.Question) 或其他格式。这个覆盖只影响当前 builder,不会把同一组 args 复制或重排。执行时仍应把返回值整体交给驱动:

// SQL 和 args 必须成对传入,不能把参数拼接进字符串。
rows, err := db.QueryContext(ctx, sqlText, args...)
if err != nil {
    return err // 查询失败时立即返回,避免继续读取无效 rows。
}
defer rows.Close() // 释放数据库结果集资源。

嵌套查询和字面问号怎么处理

嵌套查询最容易出现“子查询已经编号,父查询又从 1 开始编号”的问题。稳妥做法是:子查询和拼接片段先保持 ?,父查询完成组合后,再在最终 builder 上设置 Dollar 并调用 ToSql()。这样转换器只处理最终 SQL 文本,参数顺序由组合过程保持。

// 子查询先保留 ?,父查询完成组合后统一转换为 $1、$2。
sub := sq.Select("id").
    From("users").
    Where("tenant_id = ?", tenantID)

root := sq.Select("id", "name").
    FromSelect(sub, "u").
    Where("state = ?", "active").
    PlaceholderFormat(sq.Dollar)

sqlText, args, err := root.ToSql()
if err != nil {
    return err // 先处理构造错误,再交给数据库执行。
}
// 最终占位符应按 args 的顺序编号。
_, _ = sqlText, args

如果 SQL 本身需要 PostgreSQL 的 JSON 运算符 ?,Squirrel 会把问号当作占位符入口。用两个问号 ?? 表示字面问号,后面的真正参数占位符再继续编号,例如 meta->'format' ??| array[?,?] 会保留运算符问号,并把数组参数转换成连续的位置参数。

Go Squirrel 子查询统一转换占位符并处理 PostgreSQL 字面问号的结构图
图2:嵌套查询与字面问号的边界结构图,不是运行截图。

生成后用这张清单排查错位

  • 驱动需要什么格式:确认使用的是 Question、Dollar、Colon 还是 AtP。
  • SQL 里有几个真实参数:逐个对照 args 的长度和顺序。
  • 是否混用了已编号参数:普通片段不要手写 $1,交给最终 builder 转换。
  • 是否存在字面问号:PostgreSQL 运算符中的问号写成 ??。

相关问题

PlaceholderFormat 会修改 args 吗?

不会。它主要替换 SQL 中的占位符文本,参数仍以 Squirrel 组合时的顺序返回。

单个查询可以覆盖全局格式吗?

可以,在具体的 Select、Insert、Update 或 Delete builder 上再次调用 PlaceholderFormat 即可。

为什么不直接把值拼进 SQL?

拼接会破坏参数化查询和转义边界。保留占位符并传入 args,才能让驱动负责参数绑定。

声明:本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
相关阅读
更多>
最新阅读
更多>
课程推荐
更多>