Skip to content

chore(db): Swap lib/pq for jackc/pgx/v5#259

Open
Amund211 wants to merge 2 commits into
mainfrom
chore/migrate-pq-to-pgx
Open

chore(db): Swap lib/pq for jackc/pgx/v5#259
Amund211 wants to merge 2 commits into
mainfrom
chore/migrate-pq-to-pgx

Conversation

@Amund211

Copy link
Copy Markdown
Owner

Summary

Two commits:

  1. 9459243 Swap lib/pq for jackc/pgx/v5 as the sql driver. database/sql driver name "postgres""pgx" via _ "github.com/jackc/pgx/v5/stdlib". Every pq.QuoteIdentifier(...) call becomes pgx.Identifier{...}.Sanitize(). CloudSQL DSN keyword database=dbname= (pgx doesn't accept the pq-only alias).
  2. 37abad5 Run migrations via database/pgx/v5 with per-schema DSN. Migrations now run through a one-off *sql.DB built by stdlib.OpenDB(*cfg) where RuntimeParams["search_path"] is set to the target schema. NewDatabaseMigrator takes a connection string instead of an *sqlx.DB. NewCloudsqlPostgresDatabase lost its only caller and was removed; ConnectionString(conf) was extracted.

Why

lib/pq is in maintenance mode — README and issue #1010 explicitly point users at pgx. flashlight's direct usage was limited to pq.QuoteIdentifier, so the swap is minimal-risk.

Caveats / things a reviewer should know

  • pgx.Identifier{name}.Sanitize() is the canonical pgx-native identifier quoter (pgx.Identifier []string with a Sanitize() method). Multi-part identifiers like {"schema","table"} produce "schema"."table". Same semantics as the old pq.QuoteIdentifier for single names.
  • DSN keyword database= is pq-only. pgx's libpq-style parser uses dbname= (matches PostgreSQL's own keyword set). The CloudSQL helper was changed.
  • migrate database/pgx/v5 only exposes WithInstance(*sql.DB) — no WithConnection(*sql.Conn). So per-schema search_path can't ride on a single borrowed conn anymore; it has to be a property of every conn in the pool, which is why the throwaway DB / RuntimeParams approach.
  • search_path must be quoted inside RuntimeParams. First implementation set it to the bare schema name. Postgres parses each comma-separated entry of search_path as a SQL identifier — unquoted names get lower-cased and reject -. Test schemas like find_milestone_Single_milestone_reached and find_milestone_Milestones_skipped_-_final_reached silently resolved to a non-existent schema; unqualified DDL then failed with SQLSTATE 3F000 ("no schema has been selected to create in"), while SHOW search_path happily echoed the raw value back. Wrapping the value in pgx.Identifier{...}.Sanitize() fixes it. There's a comment on openSchemaScopedDB explaining this.
  • migrate driver name passed to migrate.NewWithInstance is "pgx5", not "pgx" or "postgres" — that's what database/pgx/v5 registers itself as.
  • lib/pq is still in go.sum as // indirect, but only because jmoiron/sqlx's own test suite imports it (flashlight → sqlx → sqlx.test → lib/pq). It is never compiled into flashlight's binary or its tests. Fully removing it would require dropping sqlx itself; out of scope here.
  • PgBouncer note (no action needed): pgx defaults to cache_statement which is incompatible with PgBouncer transaction/statement pooling on older PgBouncer (< 1.21). flashlight talks to Cloud SQL directly over Unix socket — no PgBouncer in the path — so the default is fine. If that changes, append ?default_query_exec_mode=exec to the DSN.

Test plan

  • go build ./...
  • go vet ./...
  • go test ./... — all 19 packages pass, including the four Postgres-backed integration suites (accountrepository, database, playerrepository, userrepository) that exercise schema creation, migrations, and per-schema search_path under the new driver.
  • Smoke-test in staging before prod rollout.

🤖 Generated with Claude Code

Amund211 and others added 2 commits May 14, 2026 17:00
lib/pq is in maintenance mode and the community has consolidated on pgx.
Switch the database/sql driver name from "postgres" to "pgx" via the
pgx/v5/stdlib side-effect import and replace every pq.QuoteIdentifier
call site with pgx.Identifier{...}.Sanitize(). Also rename the CloudSQL
DSN keyword from "database" to "dbname" since pgx's libpq-style parser
does not accept the pq-only alias.

The golang-migrate database/postgres driver still pulls lib/pq in as an
indirect dep; a follow-up will move migrations onto database/pgx/v5 to
drop it entirely.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Switch the migrate driver from database/postgres to database/pgx/v5 and
inject search_path via a one-off *sql.DB built with
stdlib.OpenDB(*connConfig) where RuntimeParams["search_path"] is set to
the target schema. Every connection in that pool gets search_path as a
startup parameter, so migrate's WithInstance driver finds the right
schema regardless of which conn it borrows.

The schema name is run through pgx.Identifier.Sanitize() before being
stuffed into the runtime param: Postgres parses each entry of
search_path as a SQL identifier, so unquoted names get lower-cased and
reject hyphens — without the quoting, mixed-case test schemas like
find_milestone_Single_milestone_reached silently resolved to a
non-existent schema and unqualified DDL failed with SQLSTATE 3F000.

NewDatabaseMigrator now takes a connection string instead of an
*sqlx.DB, mirroring how it builds its own short-lived pool.
NewCloudsqlPostgresDatabase had only one caller and was redundant once
ConnectionString was extracted; removed.

lib/pq is no longer reachable from flashlight's binary or tests — it
remains in go.sum only because jmoiron/sqlx's own test suite imports
it, which the Go module graph keeps even though we never compile it.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings May 14, 2026 15:18

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR migrates the project’s PostgreSQL integration from lib/pq to jackc/pgx/v5, including updating identifier quoting and switching migrations to use the golang-migrate pgx/v5 database driver with schema-scoped connections.

Changes:

  • Switched database/sql driver from "postgres" (lib/pq) to "pgx" (pgx stdlib) and replaced pq.QuoteIdentifier usages with pgx.Identifier{...}.Sanitize().
  • Updated Cloud SQL DSN keyword database=dbname= for pgx compatibility.
  • Reworked migrations to run via database/pgx/v5 using a per-schema search_path startup parameter on a temporary *sql.DB.

Reviewed changes

Copilot reviewed 13 out of 14 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
main.go Uses a shared connection string for both repository DB creation and migrator initialization.
internal/adapters/userrepository/postgres.go Replaces schema identifier quoting with pgx sanitizer.
internal/adapters/userrepository/postgres_test.go Updates schema DDL/search_path quoting and migrator construction API.
internal/adapters/playerrepository/repository.go Updates SET search_path quoting to pgx sanitizer.
internal/adapters/playerrepository/repository_test.go Updates schema setup/search_path quoting and migrator construction API.
internal/adapters/database/postgres.go Switches sql driver to pgx, introduces ConnectionString, and adds schema-scoped DB helper for migrations.
internal/adapters/database/postgres_test.go Updates tests to connect using the pgx driver name.
internal/adapters/database/migrator.go Switches migrate DB driver from postgres to pgx/v5 and uses schema-scoped DB instances.
internal/adapters/database/migrator_test.go Updates migration test setup to use pgx/v5 migrate driver and schema-scoped DB helper.
internal/adapters/database/cloudsql.go Updates DSN to use dbname= instead of database=.
internal/adapters/accountrepository/postgres.go Replaces schema identifier quoting with pgx sanitizer.
internal/adapters/accountrepository/postgres_test.go Updates schema setup/search_path quoting and migrator construction API.
go.mod Adds pgx/v5 and moves lib/pq to indirect dependency.
go.sum Adds checksums for pgx and related indirect dependencies.

connConfig.RuntimeParams["search_path"] = pgx.Identifier{schemaName}.Sanitize()

return db, nil
return stdlib.OpenDB(*connConfig), nil
@@ -28,32 +28,30 @@ func TestMigrator(t *testing.T) {
db, err := NewPostgresDatabase(LocalConnectionString)
require.NoError(t, err)
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.

2 participants