Skip to content

fix(any): rewrite ? placeholders to $N for Postgres backend (#3000) - #4351

Open
allocsys wants to merge 1 commit into
transact-rs:mainfrom
allocsys:fix/any-postgres-placeholder-3000-squashed
Open

fix(any): rewrite ? placeholders to $N for Postgres backend (#3000)#4351
allocsys wants to merge 1 commit into
transact-rs:mainfrom
allocsys:fix/any-postgres-placeholder-3000-squashed

Conversation

@allocsys

Copy link
Copy Markdown

Fixes #3000.

The Any driver always wrote ?-style placeholders, which is correct for MySQL/SQLite but not Postgres ($1, $2, ...). This caused a syntax error on any query issued through Any + Postgres that had more than a trivial shape (e.g. bound params combined with LIMIT/OFFSET).

The originally proposed fix (adding an AnyKind field to AnyArguments plus a format_placeholder override) doesn't work: QueryBuilder<Any>::push_bind() writes placeholders into the SQL string before any backend is known, since AnyArguments is built via Default::default(). The backend is only resolved later, in AnyConnectionBackend::fetch_many().

This PR instead rewrites ? -> $1, $2, ... inside sqlx-postgres/src/any.rs, right before the query is dispatched to PgConnection (covers fetch_many, fetch_optional, prepare_with, and describe). Only the Postgres leg needs this, since MySQL/SQLite already use ? natively.

The rewrite is SQL-aware (not a blind str::replace) so it skips ? that appears inside:

  • single-quoted string literals (with '' escaping)
  • double-quoted identifiers (with "" escaping)
  • dollar-quoted blocks ($$...$$ and $tag$...$tag$)
  • -- line comments and /* */ block comments

Added unit tests covering the placeholder-numbering happy path and each of the above edge cases.

CI (Examples, SQLx, SQLx CLI) passes clean on the fork: allocsys#1.

@abonander

Copy link
Copy Markdown
Collaborator

I was thinking that instead of trying to parse the query, QueryBuilder could just remember the positions in the string where the placeholders were inserted (because it would know) and pass that to the driver somehow.

I suppose this has the advantage of being useful even when not using QueryBuilder, but the original intent was to create our own generic placeholder (#875) which I started working on but never finished (#1076).

@allocsys
allocsys force-pushed the fix/any-postgres-placeholder-3000-squashed branch from 0000248 to 6a68d24 Compare July 30, 2026 10:48
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.

Bad query argument format for postgres with Any driver

2 participants