Skip to content

Conversation

@pyramation
Copy link
Collaborator

@pyramation pyramation commented Jan 6, 2026

feat(plpgsql-deparser): add context-based RETURN statement handling

Summary

This PR adds context-based RETURN statement handling to the PL/pgSQL deparser. PostgreSQL requires different RETURN syntax based on function return type:

  • void/setof/trigger/out_params: bare RETURN is valid
  • scalar: RETURN NULL is required for empty returns

Previously, the deparser always output bare RETURN for empty return statements. Now, callers can pass ReturnInfo context to get correct output based on the function's return type.

Key changes:

  • Added ReturnInfo type with kinds: void, setof, trigger, scalar, out_params
  • Modified deparseReturn() to use context when available, defaulting to bare RETURN for backward compatibility
  • Added getReturnInfo() helper in plpgsql-parser to extract return type from CreateFunctionStmt AST
  • Added tests covering all return type scenarios

The snapshot changes reflect previous PERFORM/INTO fixes from PR #266 that were merged to main.

Updates since last revision

Latest: Added plpgsql-parser and plpgsql-deparser to CI workflow

  • Both packages now run tests in the GitHub Actions matrix
  • Previously these packages had tests but weren't being run in CI

Previous: Added 14 regression fixtures from constructive-db PR #229

  • New fixture file __fixtures__/plpgsql/plpgsql_deparser_fixes.sql with real-world-inspired test cases
  • Exercises: PERFORM, INTO clause placement, record field qualification, RETURN handling for all function types
  • Regenerated generated.json: now 190 valid statements (up from 176)
  • 174 fixtures pass round-trip testing (14 new fixtures all pass)

Previous: Comprehensive round-trip testing for ALL generated fixtures

  • Test now runs all 190 fixtures from plpgsql-generated/generated.json
  • 16 fixtures are in a documented allowlist due to pre-existing deparser issues (not introduced by this PR):
    • Schema qualification loss (pg_catalog.pg_class%rowtype[]pg_class%rowtype[])
    • Tagged dollar quote reconstruction ($tag$...$tag$ not supported)
    • Exception block handling issues
  • Test will fail if any NEW fixtures start failing (regression detection)

Review & Testing Checklist for Human

  • Verify getReturnInfo() logic in packages/plpgsql-parser/src/return-info.ts - ensure the AST parsing correctly identifies all return type scenarios (especially edge cases like RETURNS TABLE vs OUT params)
  • Confirm the 16 known-failing fixtures are pre-existing issues - spot-check a few to verify they fail on main branch too, not just this PR
  • Verify CI runs both new packages - check that plpgsql-parser and plpgsql-deparser jobs appear in the workflow run
  • Review new fixtures in __fixtures__/plpgsql/plpgsql_deparser_fixes.sql - verify they cover the patterns that required fixes in constructive-db

Suggested test plan:

  1. Run the test suite: pnpm test in packages/plpgsql-deparser and packages/plpgsql-parser
  2. Try parsing a scalar function with an empty RETURN and verify it outputs RETURN NULL when context is provided
  3. Try parsing a SETOF function and verify it outputs bare RETURN
  4. Check the CI workflow run to confirm 9 jobs now run (was 7)

Notes

This is patch 4 from constructive-db PR #229, implementing a context-passing approach instead of heuristics for RETURN handling.

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

Add ReturnInfo type and context to PLpgSQLDeparserContext for correct
RETURN statement syntax based on function return type:
- void/setof/trigger/out_params: bare RETURN is valid
- scalar: RETURN NULL is required for empty returns

Add getReturnInfo helper to plpgsql-parser to extract return type info
from CreateFunctionStmt AST node.

When returnInfo is provided in context, the deparser uses it to determine
the correct RETURN syntax. When not provided, it falls back to bare RETURN
for backward compatibility.

Includes tests for all return type scenarios (void, setof, trigger,
scalar, OUT params, RETURNS TABLE).
@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

…erated fixtures

Add test that runs round-trip assertions on all 176 fixtures from
plpgsql-generated/generated.json. This ensures the deparser produces
semantically equivalent output for all canonical PL/pgSQL test cases.

16 fixtures are in a known-failing allowlist due to pre-existing issues:
- Schema qualification loss (pg_catalog.pg_class%rowtype[] -> pg_class%rowtype[])
- Tagged dollar quote reconstruction ($tag$...$tag$ not supported)
- Exception block handling issues

The test will fail if any NEW fixtures start failing (regression detection)
while allowing the known issues to be tracked and fixed incrementally.

Also improved cleanPlpgsqlTree to normalize 'undefined vs missing' properties
for more accurate AST comparison.
… PR #229

Add 14 new PL/pgSQL fixtures that exercise the deparser fixes:
- PERFORM statement (parser stores as SELECT, deparser strips SELECT)
- INTO clause with record field target (recfield qualification)
- INTO clause with subquery (depth-aware scanner)
- SETOF function with RETURN QUERY and bare RETURN
- SETOF function with RETURN NEXT
- Void function with bare RETURN
- Scalar function with RETURN NULL
- OUT parameter function with bare RETURN
- RETURNS TABLE function with RETURN QUERY
- Trigger functions with complex logic
- Procedure (implicit void return)

These fixtures are inspired by real-world functions from constructive-db
that required the deparser fixes in PR #266.

Regenerated generated.json: 190 valid statements (up from 176)
Both packages have tests that should be run in CI:
- plpgsql-deparser: comprehensive round-trip tests for 190 fixtures
- plpgsql-parser: return-info extraction tests
@pyramation pyramation merged commit 1f181c5 into main Jan 6, 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