fix(postgres): work through connection poolers (disable prepared-stmt cache) - #85
Merged
Merged
Conversation
… cache) Queries through a transaction/statement-pooling PgBouncer (or Supabase pooler, RDS Proxy, …) failed with 'prepared statement "sqlx_s_N" does not exist': sqlx prepares a statement on one pooled backend, then the next query lands on a different backend where it isn't defined. Set statement_cache_capacity(0) so sqlx uses the unnamed/simple protocol that survives poolers. This is a desktop client where queries are user-paced, so the lost caching is negligible — correctness through the poolers many managed Postgres providers put in front is worth more.
oesukam
added a commit
that referenced
this pull request
Aug 4, 2026
## Why #85 wasn't enough #85 set `statement_cache_capacity(0)`, but the user **still** hit `prepared statement "sqlx_s_1" does not exist` through their PgBouncer after building locally. Root cause (from sqlx 0.8 source, `executor.rs`): the cache capacity only disables statement **reuse**. sqlx still **creates a named** statement (`sqlx_s_N`) per query, because `Query::persistent` defaults to `true`. Named statements don't survive a transaction pooler — the next query lands on a different backend where the name isn't defined. ## The real fix Set **`.persistent(false)`** on every query → sqlx uses the **unnamed** prepared statement (per-execution), which works through PgBouncer / Supabase pooler / RDS Proxy. Coverage: - **`lib.rs`**: `bind_params()` (both the SQL editor and **JS `db.query`** route through `stream`/`execute` → here) + the direct `SELECT 1` / `pg_backend_pid` / `pg_cancel_backend` / `SET statement_timeout` calls. - **`introspect.rs`**: all 8 schema queries — these run **during connect**, which is exactly why Connect failed. - **`object_ops.rs`**: both. JS scripting needs no separate change (it goes through the same driver methods). ## Verified - `cargo check` + `fmt` + `clippy` clean. - Audited: every `sqlx::query*` in the crate now uses an unnamed statement.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The bug
Running a query through a transaction/statement-pooling PgBouncer (or Supabase's pooler, RDS Proxy, etc.) failed with:
sqlx caches prepared statements, but a pooler in transaction mode routes consecutive queries to different backends — so a statement prepared on one isn't visible on the next. This is the same managed/pooled server (
postgres.clymist.com) behind the earlier reports, and it made Nembrix unable to query through it at all.Fix
Set
statement_cache_capacity(0)on the Postgres connection options → sqlx uses the unnamed/simple protocol, which survives poolers.Trade-off: no prepared-statement caching. For a desktop client where queries are user-paced (not a high-throughput hot path), the caching win is negligible — and correctness through the poolers that most managed Postgres providers front is worth far more.
Verified
cargo check+fmt+clippyclean ondb-postgres..prepare()/query!calls remain that would still break.