Skip to content

Fix numbering of PostgreSQL params across reusable queries - #292

Open
Niols wants to merge 2 commits into
ygrek:masterfrom
Niols:fix-reuse-param-numbering
Open

Fix numbering of PostgreSQL params across reusable queries#292
Niols wants to merge 2 commits into
ygrek:masterfrom
Niols:fix-reuse-param-numbering

Conversation

@Niols

@Niols Niols commented Jul 4, 2026

Copy link
Copy Markdown
Contributor

When a reusable query (-- @foo | include: reuse) is spliced into an outer query as a CTE via WITH x AS &ref, the reused query's parameters and the outer query's parameters end up in a single generated statement, bound positionally by set_params.

With -params postgresql, the positional counter was reset to 0 for the spliced fragment and left unchanged when the outer query resumed, so both fragments numbered their $N placeholders starting from $1. The outer parameters therefore collided with the reused ones. Example:

-- @get_persons | include: reuse
SELECT * FROM person WHERE name LIKE @name;

-- @get_users 
WITH person AS &get_persons
SELECT * FROM "user" JOIN person ON person.user_id = "user".id
WHERE "user".id > @min_id;

On such an input, -params postgresql generated:

WITH person AS (... WHERE name LIKE $1)
SELECT ... WHERE "user".id > $1

Note the $1 used twice.

(This was actually caught by PostgreSQL because set_params still supplies a value for every position, so I got “bind message supplies N parameters, but prepared statement requires M”.)

I checked this only affects the postgresql parameter style; others use names (named and oracle) or use positional ? (unnamed).

A first commit adds tests exhibiting this behaviour; a second threads the positional counter through the shared-query splice in substitute_vars.

Niols added 2 commits July 4, 2026 12:33
Add tests covering `include: reuse` queries spliced as CTEs via `WITH
x AS &ref`, compiled with `-params postgresql`. The expected output
captures the current behaviour, where each fragment restarts `$N`
numbering at `$1` so the outer query's placeholders collide with the
reused query's.
When a reusable query (`include: reuse`) is spliced into an outer query
as a CTE via `WITH x AS &ref`, its parameters and the outer query's
parameters are emitted into a single statement and bound positionally by
`set_params`. However, `substitute_vars` restarted the positional counter
(`parami`) at 0 for the spliced fragment and then continued the outer
query with the unchanged counter, so both fragments numbered their
PostgreSQL `$N` placeholders from `$1`.

Thread `parami` through the shared-query splice: `loop_and_squash` now
takes the current counter and returns the advanced one, so numbering runs
continuously across the whole statement. Only the PostgreSQL params mode
was affected; named/oracle key by parameter name and unnamed uses `?`.
Niols added a commit to Niols/sqlgg that referenced this pull request Jul 4, 2026
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.

1 participant