Skip to content

Commit 64c900c

Browse files
committed
fix(plpgsql-deparser): fix EXCEPTION block handling in deparser
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)
1 parent 9d79484 commit 64c900c

File tree

4 files changed

+39
-15
lines changed

4 files changed

+39
-15
lines changed

packages/plpgsql-deparser/__tests__/__snapshots__/hydrate-demo.test.ts.snap

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,15 @@ BEGIN
190190
message := format('rollup ok: gross=%s discount=%s tax=%s net=%s (discount_rate=%s tax_rate=%s)', v_gross, v_discount, v_tax, v_net, v_discount_rate, v_tax_rate);
191191
RETURN NEXT;
192192
RETURN;
193+
EXCEPTION
194+
WHEN unique_violation THEN
195+
RAISE NOTICE 'unique_violation: %', sqlerrm;
196+
RAISE EXCEPTION;
197+
WHEN others THEN
198+
IF p_debug THEN
199+
RAISE NOTICE 'error: % (%:%)', sqlerrm, sqlstate, sqlerrm;
200+
END IF;
201+
RAISE EXCEPTION;
193202
END;
194203
RETURN;
195204
END$$"

packages/plpgsql-deparser/__tests__/plpgsql-deparser.test.ts

Lines changed: 4 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -37,22 +37,14 @@ describe('PLpgSQLDeparser', () => {
3737
// - Tagged dollar quote reconstruction ($tag$...$tag$ not supported)
3838
// - Exception block handling issues
3939
// TODO: Fix these underlying issues and remove from allowlist
40+
// Remaining known failing fixtures:
41+
// - plpgsql_varprops-13.sql: nested DECLARE inside FOR loop (loop variable scope issue)
42+
// - plpgsql_transaction-17.sql: CURSOR FOR loop with EXCEPTION block
43+
// - plpgsql_control-15.sql: labeled block with EXIT statement
4044
const KNOWN_FAILING_FIXTURES = new Set([
4145
'plpgsql_varprops-13.sql',
42-
'plpgsql_trap-1.sql',
43-
'plpgsql_trap-2.sql',
44-
'plpgsql_trap-3.sql',
45-
'plpgsql_trap-4.sql',
46-
'plpgsql_trap-5.sql',
47-
'plpgsql_trap-6.sql',
48-
'plpgsql_trap-7.sql',
4946
'plpgsql_transaction-17.sql',
50-
'plpgsql_transaction-19.sql',
51-
'plpgsql_transaction-20.sql',
52-
'plpgsql_transaction-21.sql',
5347
'plpgsql_control-15.sql',
54-
'plpgsql_control-17.sql',
55-
'plpgsql_call-44.sql',
5648
]);
5749

5850
it('should round-trip ALL generated fixtures (excluding known failures)', async () => {

packages/plpgsql-deparser/__tests__/pretty/__snapshots__/plpgsql-pretty.test.ts.snap

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,15 @@ begin
163163
);
164164
return next;
165165
return;
166+
exception
167+
when unique_violation then
168+
raise notice 'unique_violation: %', SQLERRM;
169+
raise exception;
170+
when others then
171+
if p_debug then
172+
raise notice 'error: % (%:%)', SQLERRM, SQLSTATE, SQLERRM;
173+
end if;
174+
raise exception;
166175
end;
167176
return;
168177
end"
@@ -362,6 +371,15 @@ BEGIN
362371
);
363372
RETURN NEXT;
364373
RETURN;
374+
EXCEPTION
375+
WHEN unique_violation THEN
376+
RAISE NOTICE 'unique_violation: %', SQLERRM;
377+
RAISE EXCEPTION;
378+
WHEN others THEN
379+
IF p_debug THEN
380+
RAISE NOTICE 'error: % (%:%)', SQLERRM, SQLSTATE, SQLERRM;
381+
END IF;
382+
RAISE EXCEPTION;
365383
END;
366384
RETURN;
367385
END"

packages/plpgsql-deparser/src/plpgsql-deparser.ts

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -575,12 +575,17 @@ export class PLpgSQLDeparser {
575575
}
576576

577577
// Exception handlers
578-
if (block.exceptions?.exc_list) {
578+
// The exceptions property can be either:
579+
// - { exc_list: [...] } (direct)
580+
// - { PLpgSQL_exception_block: { exc_list: [...] } } (wrapped)
581+
const excList = block.exceptions?.exc_list ||
582+
(block.exceptions as any)?.PLpgSQL_exception_block?.exc_list;
583+
if (excList) {
579584
parts.push(kw('EXCEPTION'));
580-
for (const exc of block.exceptions.exc_list) {
585+
for (const exc of excList) {
581586
if ('PLpgSQL_exception' in exc) {
582587
const excData = exc.PLpgSQL_exception;
583-
const conditions = excData.conditions?.map(c => {
588+
const conditions = excData.conditions?.map((c: any) => {
584589
if ('PLpgSQL_condition' in c) {
585590
return c.PLpgSQL_condition.condname || c.PLpgSQL_condition.sqlerrstate || 'OTHERS';
586591
}

0 commit comments

Comments
 (0)