Conversation
Function and procedure bodies could hold SQL statements and RETURN but none of the control flow that PL/pgSQL and PL/SQL bodies are made of. Adds assignment with `:=` or `=`, `NULL`, `IF ... ELSIF ... ELSE ... END IF`, `CASE ... END CASE`, `LOOP`, `WHILE ... LOOP`, `FOR ... IN` over an integer range, a query or a cursor, `FOREACH ... IN ARRAY`, `EXIT` and `CONTINUE` with an optional label and `WHEN`, `<<label>>` on blocks and loops, nested `DECLARE ... BEGIN ... END` blocks and a bare `RETURN`. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Adds the error-handling statements of PL/pgSQL and PL/SQL bodies: `RAISE` with a level, a format string and arguments, a condition name, `SQLSTATE` or `USING` options; `ASSERT`; `EXCEPTION WHEN ... THEN` handlers at the end of a function body, procedure body or block, with `OR`-joined conditions, `SQLSTATE` and `OTHERS`; and `GET [CURRENT | STACKED] DIAGNOSTICS`. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Declarations gain `%TYPE` and `%ROWTYPE` (also in parameters), `CONSTANT`, `NOT NULL`, `=` and `DEFAULT` initialisers, `ALIAS FOR`, and cursor declarations in both the PL/pgSQL (`c [NO SCROLL] CURSOR (args) FOR query`) and PL/SQL (`CURSOR c RETURN type IS query`) forms. Bodies gain `OPEN`, `FETCH`, `MOVE` and `CLOSE`. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Adds the statements a PL/pgSQL or PL/SQL body uses to run SQL: `PERFORM`, `EXECUTE [IMMEDIATE] expr [INTO [STRICT] targets] [USING args]`, `RETURN NEXT expr`, `RETURN QUERY query | EXECUTE ...`, `SELECT ... INTO STRICT`, `RETURNING ... INTO [STRICT] targets`, and `OPEN cursor FOR EXECUTE ...`. Adds the top-level `DO [LANGUAGE x] $$ ... $$` block and `CALL procedure(args)`. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
sebsnyk
marked this pull request as draft
September 10, 2026 17:17
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.
Problem
The last group of statements that real PL/pgSQL bodies use every few lines:
perform,execute ... into ... using,return next/return query,select ... into strict,insert ... returning ... into, and thedo $$ ... $$block that migrations wrap them in. The sample in #236 hasRETURNING * INTO session_recordas its one remaining unparsed construct after #371 to #373; with this PR it parses without anERRORnode. Against real sources:ERRORnodes with #373src/test/regress/sql/plpgsql.sqlsql/functions/check_default.sqlsource/core/ut_utils.pkbpg_partman's remaining six are
@extschema@templating placeholders andSET search_path = ...in the function header (#364, separate PR). utPLSQL still needs Oracle's header shape and package bodies.Solution
A new
grammar/statements/dynamic-sql.js:perform_statement:PERFORM [DISTINCT] select_expression [from ...], reusing the select tail so joins,where,order byandlimitcome for free.execute_statement:EXECUTE [IMMEDIATE] expr [INTO [STRICT] target, ...] [USING [IN | OUT | IN OUT] arg, ...].IMMEDIATEand the argument modes are PL/SQL's spelling of the same statement._function_returnextended withRETURN NEXT exprandRETURN QUERY (query | EXECUTE ...).SELECT ... INTO [STRICT] ...in_select_statement, andRETURNING ... INTO [STRICT] target, ...onreturning, soinsert,updateanddeleteall take it. Both are optional keywords in an existing position; existing trees are unchanged.OPEN cursor [SCROLL] FOR EXECUTE ...on theopen_statementfrom feat(grammar): declaration attributes and cursors in procedural bodies #373.do_statement:DO [LANGUAGE x] $$ [<<label>>] [DECLARE ...] BEGIN ... [EXCEPTION ...] END $$or a string body, instatementso it works at the top level.call_statement:CALL procedure(args), also instatement.Notes
tree-sitter test: 538 corpus tests on feat(grammar): declaration attributes and cursors in procedural bodies #373, 548 with this PR (6 new intest/corpus/execute.txt, 2 indo.txt, 2 incall.txt), plus the 2 highlight tests. NoERROR/MISSING;scripts/test-keywords.shpasses.STATE_COUNT31202 → 31151 (-0.2%), butLARGE_STATE_COUNT5346 → 5512 andparser.c42.7 MB → 43.2 MB (+1.2% on top of feat(grammar): declaration attributes and cursors in procedural bodies #373; +3.8% overmain).DOandCALLsit instatement, so unlike the body-only rules in the earlier PRs they reach every statement context; that is where the large-state growth comes from. If that cost is unwelcome, the two could be limited toprogram.COMMIT/ROLLBACKinside a procedure body.transactionshares the body statement list since feat(grammar): procedural control flow in function and procedure bodies #371, so aCOMMITstatement inside a body would be ambiguous with the one that ends aBEGIN ... COMMITtransaction; that needs a decision about how the two rules should relate.