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

sqlc 可空查询参数怎么生成指针或 Null 类型

来源:17golang原创

时间:2026-11-05 12:56:17 440浏览 收藏

sqlc 生成可空查询参数时,关键不是把整个参数结构体改成指针,而是先在 SQL 中使用 sqlc.narg() 明确参数可为空。默认配置通常会得到 sql.NullString、sql.NullInt64 这类 Null 类型;在支持的 PostgreSQL pgx/v4、pgx/v5 或 SQLite 场景中,再打开 emit_pointers_for_null_types: true,才可以让可空类型表现为 *string、*int64。

官方地址:https://docs.sqlc.dev/en/stable/

下面用一个“只更新传入字段”的查询做对照,重点看 SQL 标记、生成配置、Params 字段和调用方式四个位置。

先把可空参数写在 SQL 里

sqlc 会根据 SQL 上下文推断参数是否可空。如果希望一个参数无论上下文如何都按可空参数生成,就使用 sqlc.narg。它和 sqlc.arg 一样可以给参数命名,但额外覆盖了可空性推断。

-- name: UpdateAuthor :one
UPDATE authors
SET
    name = COALESCE(sqlc.narg('name'), name),
    bio = COALESCE(sqlc.narg('bio'), bio)
WHERE id = sqlc.arg('id')
RETURNING *;

这里的 name 和 bio 是可空参数,id 仍然是普通的非空参数。COALESCE 只是这个更新场景的示例,文章关注的是生成出来的参数类型,不讨论数据库字段本身的 NULL 设计。

先分清三个容易混淆的设置

生成类型之前,先把三个配置分开。它们作用在不同层级:

配置或写法改变什么不会改变什么
sqlc.narg()把 SQL 参数标为 nullable不会直接决定使用指针还是 Null 包装
emit_pointers_for_null_types让支持的可空类型生成 *T不会把普通非空字段改成指针
emit_params_struct_pointers让调用方法接收 *UpdateAuthorParams不会把其中的字段改成 *string

因此,看到 *UpdateAuthorParams 时不要误以为 Name 一定是 *string。参数结构体的指针和结构体字段的可空类型是两条独立配置链。

原创 sqlc 配置界面说明图,展示 sqlc.narg、pgx/v5 和可空指针设置的关系
图1:可空参数配置的界面说明图,展示 SQL 标记、驱动选择和指针设置的对应关系;这是原创操作示意图,不是实际软件截图。

默认 Null 类型:调用时看 Valid

不打开指针选项时,常见生成结果如下。以 database/sql 的字符串为例,字段通过 Valid 区分“有值”和“应传 NULL”。

type UpdateAuthorParams struct {
    // Valid=false 时由驱动传递 NULL,避免把空字符串误当成缺省值。
    Name sql.NullString
    // 同一个规则适用于第二个可选字段。
    Bio  sql.NullString
    // id 由 sqlc.arg 生成普通的非空参数。
    ID   int64
}

params := UpdateAuthorParams{
    // Valid=true 才会把 String 作为参数值传入。
    Name: sql.NullString{String: "Ada", Valid: true},
    // Valid=false 表示本次不更新 bio。
    Bio:  sql.NullString{Valid: false},
    ID:   7,
}
author, err := queries.UpdateAuthor(ctx, params)
if err != nil {
    // 保留生成方法返回的数据库错误,交给上层处理。
    return err
}
_ = author

默认 Null 类型的优点是语义明确:值和有效性放在同一个字段里。代价是调用方需要构造包装类型,不能直接把普通字符串赋给 Name。

支持的场景下改成指针类型

如果项目使用 PostgreSQL 的 pgx/v4、pgx/v5,或使用 SQLite,可以在 Go 生成配置中打开指针选项。配置重点如下:

version: "2"
sql:
  - engine: postgresql
    schema: db/schema.sql
    queries: db/query.sql
    gen:
      go:
        package: db
        out: internal/db
        sql_package: pgx/v5
        # 让可空字符串等类型生成 *string,而不是 Null 包装类型。
        emit_pointers_for_null_types: true

在这个支持范围内,前面的可空参数可以表现为类似下面的结构:

type UpdateAuthorParams struct {
    // nil 表示本次参数为 NULL;非 nil 指向要传入的字符串。
    Name *string
    // nil 让调用方不必额外构造 NullString。
    Bio  *string
    // 非空参数仍然是普通值类型。
    ID   int64
}

name := "Ada"
params := UpdateAuthorParams{
    // 传地址表示有值,传 nil 表示 NULL。
    Name: &name,
    Bio:  nil,
    ID:   7,
}
_, err := queries.UpdateAuthor(ctx, params)
if err != nil {
    // 生成方法的错误仍按普通 Go 错误处理。
    return err
}

不要把这个选项当成所有数据库和驱动的通用开关。sqlc 当前文档对支持范围有明确限制;如果驱动不在支持范围内,优先接受默认 Null 类型,或改用明确的类型覆盖。

需要业务类型时用 nullable override

如果你想用自己的类型表达可空值,配置 overrides 时要明确写 nullable: true。如果目标类型需要指针,再在 go_type 中写 pointer: true。只写非空覆盖不会自动覆盖可空参数。

version: "2"
sql:
  - engine: postgresql
    schema: db/schema.sql
    queries: db/query.sql
    gen:
      go:
        package: db
        out: internal/db
        sql_package: pgx/v5
        overrides:
          # 非空 uuid 和可空 uuid 要分别声明覆盖规则。
          - db_type: uuid
            nullable: true
            go_type:
              import: github.com/google/uuid
              type: UUID
              pointer: true

这个方案适合团队已经统一使用某个值对象的项目。它的注意点是:db_type 覆盖默认只匹配一种可空性;同一数据库类型如果既有非空字段又有可空字段,通常需要分别写两条覆盖规则。

调用前按生成结果做一次核对

配置保存后,重点不是盯着配置面板,而是打开生成文件确认 Params 结构体。可以按下面顺序核对:

  1. SQL 中可选参数是否确实使用了 sqlc.narg。
  2. 指针模式下字段是否为 *T,默认模式下是否为对应的 sql.NullT。
  3. 只有 emit_params_struct_pointers 时,方法参数是否是 *UpdateAuthorParams,字段类型是否保持原配置。
  4. 调用代码是否把 nil 或 Valid=false 当成同一个“传 NULL”语义,而不是把空字符串当成缺省值。
原创 sqlc 生成代码核对界面说明图,展示 UpdateAuthorParams 的指针字段与 Null 类型对照
图2:生成结果核对的界面说明图,重点看 Params 字段类型和调用语义;这是原创结果示意图,不是实际软件截图。

几个常见误区

把 emit_params_struct_pointers 当成字段指针开关。它只影响参数结构体的传递方式,不能替代 emit_pointers_for_null_types 或 nullable override。

只写 sqlc.arg 期待得到可空参数。sqlc.arg 主要负责命名和参数标记;需要强制 nullable 时用 sqlc.narg。

在不支持的驱动上强行期待 *T。先看 sqlc 配置文档对引擎和 sql_package 的限制;不满足时,sql.NullString 等默认类型往往是更可靠的生成结果。

override 漏写 nullable。同一个数据库类型的非空和可空映射不是一条规则自动覆盖,必须按可空性分别配置。

结论

想让 sqlc 生成可空查询参数,先用 sqlc.narg 表达参数语义,再根据驱动选择类型表达:默认使用 sql.NullT,支持的 PostgreSQL pgx 或 SQLite 可用 emit_pointers_for_null_types 生成 *T,特殊业务类型则用带 nullable: true 的 override。最后只检查生成的 Params 字段和调用代码,就能避免把结构体指针、字段指针和 Null 包装混为一谈。

相关问题

sqlc.narg 和 sqlc.arg 有什么区别?前者会强制把参数标记为 nullable,后者主要用于命名和普通参数标记。

为什么打开 emit_params_struct_pointers 后仍然是 sql.NullString?因为它只改变 Params 结构体本身的传参形式,不改变字段的可空类型。

指针模式下如何表达“不更新这个字段”?对支持的生成类型传入 nil;默认 Null 模式则让对应字段的 Valid 为 false。

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