Skip to content

Support PostgreSQL 18 RETURNING OLD/NEW syntax#4355

Open
stepan-romankov wants to merge 1 commit intosqlc-dev:mainfrom
stepan-romankov:fix/pg17-returning-old-new
Open

Support PostgreSQL 18 RETURNING OLD/NEW syntax#4355
stepan-romankov wants to merge 1 commit intosqlc-dev:mainfrom
stepan-romankov:fix/pg17-returning-old-new

Conversation

@stepan-romankov
Copy link

@stepan-romankov stepan-romankov commented Mar 25, 2026

Fixes #3600

PostgreSQL 18 added support for RETURNING OLD.* and RETURNING NEW.* in INSERT/UPDATE/DELETE statements. The underlying parser (pg_query_go v6.2.2) already handles this syntax, but sqlc's compiler rejects these queries.

What was broken

The compiler has three places where it matches a column scope or alias against known table names. When the scope is "old" or "new", it doesn't match any real table name, so:

  • Star expansion (RETURNING OLD.*) produces an empty column list, rewriting the query to something like RETURNING ;, which fails to re-parse
  • Column refs (RETURNING OLD.bar) fail with "column does not exist"

What this fixes

Three small changes in internal/compiler/expand.go and internal/compiler/output_columns.go to recognize "old" and "new" as special scope values that should match the target table's columns rather than being filtered out.

The expanded queries correctly preserve the OLD/NEW prefix, e.g. RETURNING OLD.* becomes RETURNING old.id, old.bar, old.baz.

Test plan

  • Added end-to-end test (returning_old_new/postgresql/stdlib) covering:
    • RETURNING OLD.* on UPDATE
    • RETURNING NEW.* on UPDATE
    • RETURNING OLD.col, NEW.col on UPDATE
    • RETURNING OLD.* on DELETE
  • All existing TestReplay/base tests pass
  • go build ./... passes

@dosubot dosubot bot added the size:L This PR changes 100-499 lines, ignoring generated files. label Mar 25, 2026
@stepan-romankov
Copy link
Author

@kyleconroy would you mind taking a look when you get a chance?

@stepan-romankov stepan-romankov changed the title Support PostgreSQL 17 RETURNING OLD/NEW syntax Support PostgreSQL 18 RETURNING OLD/NEW syntax Mar 25, 2026
The pg_query_go parser already parses RETURNING OLD.* and RETURNING
NEW.* correctly, but sqlc's compiler rejected these queries because
the star expansion and column resolution logic tried to match
"old"/"new" against actual table names and found no match.

This caused star expansion to produce empty column lists (resulting in
invalid SQL like "RETURNING ;") and column refs like OLD.bar to fail
with "column does not exist".

The fix recognizes "old" and "new" as special scope/alias values in
three places and lets them match the target table's columns instead
of being filtered out.

Fixes sqlc-dev#3600

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@stepan-romankov stepan-romankov force-pushed the fix/pg17-returning-old-new branch from 14a4f33 to 027ca2c Compare March 25, 2026 13:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:L This PR changes 100-499 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

SQLite UPDATE with RETURNING and ORDER BY / LIMIT clauses fail to parse

1 participant