Skip to main content

Regenerating sqlc bindings

PatchBase uses sqlc to generate type-safe Go code from SQL queries. The configuration lives at db/sqlc.yaml, queries are in db/queries/, and generated code goes to internal/sql/.

The workflow

  1. Write or modify SQL queries in db/queries/*.sql

  2. Regenerate schema — after migration changes, run:

    bazel run //db:regen_schema

    This spins up a temporary PostgreSQL container, applies migrations, updates db/schema.sql, and runs sqlc to regenerate Go bindings.

  3. Update BUILD.bazel — if sqlc generated new files, run Gazelle:

    bazel run //:gazelle

Writing queries

sqlc queries live in db/queries/. Example:

-- name: GetHostByID :one
SELECT * FROM hosts
WHERE id = $1;

-- name: ListHosts :many
SELECT * FROM hosts
ORDER BY created_at DESC;

-- name: InsertHost :exec
INSERT INTO hosts (id, display_name, hostname)
VALUES ($1, $2, $3);

Each query has a name and a command type:

TypeDescription
:oneReturns a single row (or error)
:manyReturns multiple rows
:execExecutes without returning rows
:execrowsReturns affected row count
:copyfromBulk insert

Generated code

After regeneration, internal/sql/ contains:

  • queries.go — Go functions for each query
  • models.go — Go structs for each table
  • db.go — Querier interface and Queries struct

The generated code uses pgx types (pgtype.Text, pgtype.Timestamptz, etc.) and the utils.Option[T] type for nullable columns.

Using generated queries

queries := sql.New(pool)
host, err := queries.GetHostByID(ctx, "h_xxx")

For optional results (return ErrNotFound if no row):

host, err := sql.Required(queries.GetHostByID(ctx, hostID))(apperr.ErrHostNotFound)