Skip to content

Comments

fix JSON params in pgx text query modes#1155

Open
bgentry wants to merge 1 commit intomasterfrom
bg/simple-protocol-jsonb-mode
Open

fix JSON params in pgx text query modes#1155
bgentry wants to merge 1 commit intomasterfrom
bg/simple-protocol-jsonb-mode

Conversation

@bgentry
Copy link
Contributor

@bgentry bgentry commented Feb 22, 2026

River passes marshaled JSON query inputs as []byte into sqlc-generated pgx calls. In QueryExecModeSimpleProtocol and QueryExecModeExec (including PgBouncer transaction-pooling setups), pgx treats []byte bind args as bytea, so json/jsonb inputs can fail with invalid JSON syntax.

This change adapts JSON bind arguments from []byte to string at wrapper Exec/Query/QueryRow boundaries, but only for pgx text execution modes. Query option parsing now mirrors pgx option semantics so per-query QueryExecMode overrides are honored. When a pgx.QueryRewriter is present, the driver wraps it so adaptation runs after rewrite against final SQL and bind args.

Explicit binary placeholders cast as $n::bytea or CAST($n AS bytea) are excluded from adaptation so intentional bytea inputs keep working in both extended and simple protocol paths. defaultQueryExecMode stays alongside templateReplaceWrapper, and the test-only SharedTx.Conn() now returns nil for capability probing instead of forcing panic matching.

Coverage includes driver tests for per-query mode overrides, QueryRewriter post-rewrite adaptation, Exec-path behavior, explicit bytea cast protection, and nil-conn fallback. riverdrivertest now exercises pgx endpoints in default, simple-protocol, and exec-mode configurations.

Fixes #1153.

River passes marshaled JSON query inputs as `[]byte` into sqlc-generated
`pgx` calls. In `QueryExecModeSimpleProtocol` and
`QueryExecModeExec` (including PgBouncer transaction-pooling setups),
pgx treats `[]byte` bind args as `bytea`, so `json`/`jsonb` inputs can
fail with invalid JSON syntax.

This commit adapts JSON bind arguments from `[]byte` to `string` at
wrapper `Exec`/`Query`/`QueryRow` boundaries, but only for pgx text
execution modes. Query option parsing now mirrors pgx option semantics
so per-query `QueryExecMode` overrides are honored. When a
`pgx.QueryRewriter` is present, the driver wraps it so adaptation runs
after rewrite against final SQL and bind args.

Explicit binary placeholders cast as `$n::bytea` or `CAST($n AS bytea)`
are excluded from adaptation so intentional `bytea` inputs keep working
in both extended and simple protocol paths. `defaultQueryExecMode`
stays alongside `templateReplaceWrapper`, and the test-only
`SharedTx.Conn()` now returns `nil` for capability probing instead of
forcing panic matching.

Coverage includes driver tests for per-query mode overrides,
`QueryRewriter` post-rewrite adaptation, `Exec`-path behavior, explicit
`bytea` cast protection, and nil-conn fallback. `riverdrivertest` now
exercises pgx endpoints in default, simple-protocol, and exec-mode
configurations.

Fixes #1153.
@bgentry bgentry force-pushed the bg/simple-protocol-jsonb-mode branch from 199a4dc to 61c06f6 Compare February 22, 2026 20:17
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.

Can't use pgx5's QueryExecModeSimpleProtocol to avoid prepared statements when using connection pooling

1 participant