Skip to content

fix(plpgsql-deparser): cursor SCROLL options, FETCH/MOVE directions, SQLSTATE conditions, bare RAISE, subscripted assignment targets#303

Merged
pyramation merged 1 commit into
mainfrom
feat/plpgsql-deparser-cursor-raise-sqlstate-fixes
Jul 20, 2026
Merged

fix(plpgsql-deparser): cursor SCROLL options, FETCH/MOVE directions, SQLSTATE conditions, bare RAISE, subscripted assignment targets#303
pyramation merged 1 commit into
mainfrom
feat/plpgsql-deparser-cursor-raise-sqlstate-fixes

Conversation

@pyramation

Copy link
Copy Markdown
Collaborator

Summary

Five round-trip corruption bugs found by fuzzing the deparser with esoteric PL/pgSQL (all outputs now reparse AND deploy against a real PG 18):

  1. Wrong SCROLL bitmasks + wrong placementdeparseOpen tested cursor_options & 256/512 (those are CURSOR_OPT_FAST_PLAN/GENERIC_PLAN, which plpgsql always sets), so every OPEN c; became invalid OPEN SCROLL c;. Correct masks are 0x2/0x4, SCROLL goes after the cursor name, and only for unbound OPEN ... FOR — a bound cursor's option belongs on its declaration.
  2. SCROLL dropped from cursor declarationsdeparseVar never emitted it; now c SCROLL CURSOR FOR ... / NO SCROLL round-trip.
  3. MOVE/FETCH directions lost or mangledFORWARD n/BACKWARD n counts are stored as an expr the direction printer ignored (MOVE FORWARD 3MOVE); ALL is how_many = INT_MAX, not 0; and FIRST/LAST (ABSOLUTE ±1 with no expr) now print as FIRST/LAST so the reparse AST matches.
  4. WHEN SQLSTATE '23503' deparsed as invalid WHEN 23503 — condnames matching /^[0-9A-Z]{5}$/ (named conditions are lowercase identifiers) are re-emitted as SQLSTATE 'xxxxx'.
  5. Bare re-throw RAISE; deparsed as invalid RAISE EXCEPTION; — a raise with no condname/message/options is now emitted bare.
  6. a[2] := 20 deparsed as invalid (a)[2] := 20 — the SQL deparser parenthesizes A_Indirection targets, but the plpgsql assignment grammar requires a bare identifier; deparseAssign unwraps a leading (ident) before [/..

Fixtures: Tests 47–53 in __fixtures__/plpgsql/plpgsql_deparser_fixes.sql (regenerated generated.json via pnpm fixtures), plus explicit snapshot tests in deparser-fixes.test.ts. Existing snapshot changes are all RAISE EXCEPTION;RAISE; (fix #5). Full workspace suite passes.

Link to Devin session: https://app.devin.ai/sessions/eeb8deba0c04475091df703c88877302
Requested by: @pyramation

…SQLSTATE conditions, bare RAISE, subscripted assignment targets
@pyramation pyramation self-assigned this Jul 20, 2026
@devin-ai-integration

Copy link
Copy Markdown
Contributor

🤖 Devin AI Engineer

I'll be helping with this pull request! Here's what you should know:

✅ I will automatically:

  • Address comments on this PR. Add '(aside)' to your comment to have me ignore it.
  • Look at CI failures and help fix them

Note: I can only respond to comments from users who have write access to this repository.

⚙️ Control Options:

  • Disable automatic comment, CI, and merge conflict monitoring

@pyramation
pyramation merged commit cb1c19d into main Jul 20, 2026
9 checks passed
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.

typedef

1 participant