Skip to content

Conversation

@pyramation
Copy link
Collaborator

fix(plpgsql-deparser): fix EXCEPTION blocks, schema qualification, implicit variables

Summary

This PR fixes 13 of the 16 known failing fixtures in the plpgsql-deparser round-trip test suite. The main fixes are:

  1. EXCEPTION block handling: The deparser was not outputting EXCEPTION blocks because it checked for block.exceptions.exc_list directly, but the parser wraps exception blocks in a PLpgSQL_exception_block wrapper. Now handles both formats.

  2. Schema qualification preservation: Modified deparseType() to preserve schema qualification for %rowtype and %type references (e.g., pg_catalog.pg_class%rowtype[] no longer gets incorrectly stripped).

  3. Implicit variables filtering: Added sqlstate and sqlerrm to the list of implicit variables filtered from DECLARE sections (PostgreSQL adds these automatically for exception handling).

  4. Test utils improvement: Added varno to the list of properties ignored during AST comparison, since variable numbers change when implicit variables are filtered out.

Results: 187 of 190 fixtures now pass (up from 174). Only 3 fixtures remain failing:

  • plpgsql_varprops-13.sql (nested DECLARE inside FOR loop)
  • plpgsql_transaction-17.sql (CURSOR FOR loop with EXCEPTION block)
  • plpgsql_control-15.sql (labeled block with EXIT statement)

Review & Testing Checklist for Human

  • Verify EXCEPTION block output is correct: Check the snapshot diffs - EXCEPTION handlers are now being output. Verify the WHEN ... THEN clauses and their actions are semantically correct.
  • Test schema qualification edge cases: The fix uses typname.includes('%rowtype') - verify this doesn't have false positives/negatives for unusual type names.
  • Verify varno filtering doesn't mask real bugs: The test-utils now ignores varno values during comparison. Confirm this is safe and doesn't hide semantic differences.
  • Run a manual round-trip test: Parse a real PL/pgSQL function with EXCEPTION blocks, deparse it, and verify the output is valid SQL that PostgreSQL accepts.

Notes

  • The as any type cast on line 582 and 588 of plpgsql-deparser.ts is used to handle the wrapped exception block format - this could be improved with proper typing.
  • The 3 remaining failing fixtures appear to have different root causes (loop variable scope, cursor handling, labeled blocks) that would require separate fixes.

Link to Devin run: https://app.devin.ai/sessions/8e1c971e9b194cd9a7dda034c89bd74b
Requested by: @pyramation

…e references

The deparser was stripping pg_catalog. prefix from ALL type names, but for
%rowtype and %type references like pg_catalog.pg_class%rowtype[], the schema
qualification is meaningful and should be preserved.

Now only strips pg_catalog. for built-in types, not for %rowtype/%type refs.

Fixes plpgsql_array-20.sql fixture (175/190 now passing, 15 remaining).
PostgreSQL automatically adds sqlstate and sqlerrm variables for exception
handling. These should not be explicitly declared in the output DECLARE section.

Updated snapshots to reflect the fix.
varno values are assigned based on position in datums array and can change
when implicit variables (like sqlstate/sqlerrm) are filtered out during deparse.
This makes round-trip testing more robust.
The deparser was not outputting EXCEPTION blocks because it was checking for
block.exceptions.exc_list directly, but the parser wraps the exception block
in a PLpgSQL_exception_block wrapper.

This fix handles both the direct and wrapped formats:
- { exc_list: [...] } (direct)
- { PLpgSQL_exception_block: { exc_list: [...] } } (wrapped)

This resolves 12 of the 15 known failing fixtures:
- All 7 plpgsql_trap-* fixtures
- plpgsql_transaction-19.sql, -20.sql, -21.sql
- plpgsql_control-17.sql
- plpgsql_call-44.sql

Only 3 fixtures remain failing (down from 15):
- plpgsql_varprops-13.sql (nested DECLARE inside FOR loop)
- plpgsql_transaction-17.sql (CURSOR FOR loop with EXCEPTION block)
- plpgsql_control-15.sql (labeled block with EXIT statement)
@devin-ai-integration
Copy link
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 and CI monitoring

@pyramation pyramation merged commit 141de8d into main Jan 7, 2026
18 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.

2 participants