Skip to content

feat(golang): add emit_read_only_prepared to generate read-only queries - #4669

Open
jarri-abidi wants to merge 1 commit into
sqlc-dev:mainfrom
jarri-abidi:feat/emit-read-only-prepared
Open

jarri-abidi wants to merge 1 commit into
sqlc-dev:mainfrom
jarri-abidi:feat/emit-read-only-prepared

Conversation

@jarri-abidi

Copy link
Copy Markdown

Closes #3187. Supersedes #3291, which was closed automatically for merge conflicts; this is the same feature rebased onto the current main, with the fixes from running it since.

Problem

With emit_prepared_queries, Prepare prepares every query on one connection. A database user that can only read, such as one connecting to a read replica, can't prepare the INSERT, UPDATE and DELETE statements, so you either prepare everything with a read-write user or generate a second package and duplicate all the models (#3187).

Solution

A new emit_read_only_prepared option (Go, database/sql). When set, sqlc also generates read.go with a ReadQueries type, created with PrepareReadOnly, that prepares only the read-only queries. Prepare is unchanged, and the models are shared.

version: "2"
sql:
  - engine: "postgresql"
    queries: "query.sql"
    schema: "schema.sql"
    gen:
      go:
        package: "db"
        out: "db"
        emit_prepared_queries: true
        emit_read_only_prepared: true
writes, err := db.Prepare(ctx, primary)        // every query
reads, err := db.PrepareReadOnly(ctx, replica) // read-only queries only
user, err := reads.GetUserByID(ctx, id)

A query is read-only if it SELECTs and doesn't INSERT, UPDATE, DELETE or SELECT ... FOR UPDATE, so a DELETE ... WHERE id IN (SELECT ...) stays out of read.go, as asked in the #3291 review. Keywords are matched when followed by any whitespace, so queries written as SELECT\n col1, ... are recognised.

read.go gets its imports from the read-only queries' own argument and result types, including github.com/lib/pq when they bind or scan slices with pq.Array.

pgx isn't supported (no read.go is generated), because emit_prepared_queries doesn't apply to it.

Changes

  • emit_read_only_prepared in the Go options, the v1 config, both config JSON schemas and docs/reference/config.md
  • readQueriesFile template (stdlib/readonlyCode.tmpl) and the read-only query selection in gen.go
  • endtoend fixtures under read_write_segregation: MySQL, PostgreSQL, and PostgreSQL with slice arguments and results

Testing

  • go vet ./... and go test --tags=examples ./... pass locally
  • This feature has been running in production services generating read.go, which is where the pq import and bare-SELECT fixes came from

🤖 Generated with Claude Code

Add an emit_read_only_prepared option for database/sql. When set, sqlc
also generates read.go with a ReadQueries type, created with
PrepareReadOnly, that prepares only the read-only queries: those that
SELECT and do not INSERT, UPDATE, DELETE or SELECT ... FOR UPDATE.

This lets read queries be prepared on a connection whose database user
can only read, such as a read replica, without preparing write
statements that user isn't allowed to prepare. Prepare is unchanged and
keeps every query, and the models are shared, so nothing is duplicated.

read.go gets its imports from the read-only queries' own argument and
result types, including lib/pq when they bind or scan slices with
pq.Array. Keywords are matched when followed by any whitespace, so
queries written as "SELECT\n    col1, ..." are recognised. pgx is not
supported; no read.go is generated for it.

Adds the option to the docs and both config schemas, and endtoend
fixtures for MySQL, PostgreSQL, and PostgreSQL with slice arguments and
results.

Closes sqlc-dev#3187. Supersedes sqlc-dev#3291, which was closed for merge conflicts.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@jarri-abidi
jarri-abidi marked this pull request as ready for review October 29, 2026 17:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Option to Segregate Read and Write queries to have separate db users with each type of query.

1 participant