Skip to content

feat(grammar): RAISE, ASSERT, EXCEPTION handlers and GET DIAGNOSTICS - #372

Draft
sebsnyk wants to merge 2 commits into
DerekStride:mainfrom
sebsnyk:feat/exception-handling
Draft

feat(grammar): RAISE, ASSERT, EXCEPTION handlers and GET DIAGNOSTICS#372
sebsnyk wants to merge 2 commits into
DerekStride:mainfrom
sebsnyk:feat/exception-handling

Conversation

@sebsnyk

@sebsnyk sebsnyk commented Sep 10, 2026

Copy link
Copy Markdown

Stacked PR, converted to draft. This sits second in the order #371 -> #372 -> #373 -> #374 and depends on #371. GitHub cannot base a fork PR on another fork branch, so the diff below includes #371; only the last commit (d621f5e) is this change. It is not ready for review until #371 lands and it is rebased; closing it until then is a reasonable call.

Problem

Once a body can hold control flow (#371), the next thing every real PL/pgSQL or PL/SQL routine trips on is error handling: RAISE NOTICE, RAISE EXCEPTION ... USING, an EXCEPTION WHEN others THEN section, and in tests ASSERT and GET DIAGNOSTICS. The #236 sample stops at its RAISE EXCEPTION. Against real sources:

source ERROR nodes with #371 with this PR
PostgreSQL src/test/regress/sql/plpgsql.sql 636 414
utPLSQL source/core/ut_utils.pkb 233 233
pg_partman sql/functions/check_default.sql 11 11

utPLSQL and pg_partman do not move because their remaining errors are %TYPE, cursors, PERFORM/EXECUTE and Oracle's IS ... END name; headers, which come next.

Solution

A new grammar/statements/error-handling.js:

  • raise_statement: RAISE [DEBUG | LOG | INFO | NOTICE | WARNING | EXCEPTION] [format, args... | condition | SQLSTATE 'x'] [USING option = expr, ...], and a bare RAISE. Each USING option is a raise_option.
  • assert_statement: ASSERT condition [, message].
  • exception_handler: WHEN cond [OR cond]... THEN statements, where a condition is OTHERS, SQLSTATE 'x' or a name. A keyword_exception followed by one or more handlers may close function_body, procedure_body and block.
  • get_diagnostics: GET [CURRENT | STACKED] DIAGNOSTICS var = item, ....

All four are reachable only from bodies and blocks, as in #371.

One shape to check: a condition name in RAISE unique_violation parses as (field (identifier)), because a separate identifier alternative would be ambiguous with a format expression. In an exception_handler the name is a plain (identifier).

Notes

sebsnyk and others added 2 commits September 10, 2026 17:08
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>
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