fix: recover RETURN NEXT variable lost by libpg-query + CoalesceExpr/RangeFunction test fixtures#296
Closed
pyramation wants to merge 3 commits into
Closed
fix: recover RETURN NEXT variable lost by libpg-query + CoalesceExpr/RangeFunction test fixtures#296pyramation wants to merge 3 commits into
pyramation wants to merge 3 commits into
Conversation
Add 9 new PL/pgSQL test fixtures exercising expression patterns that were causing UNSUPPORTED_EXPRESSION errors in constructive-db's deparser: - Test 38: COALESCE inside FuncCall arguments (RETURN QUERY) - Test 39: FuncCall in FROM clause (RangeFunction) - Test 40: COALESCE + FuncCall in FROM clause (combined) - Test 41: COALESCE + SRF in SELECT INTO - Test 42: Multiple SRFs in FROM clause - Test 43: COALESCE with function call in assignment - Test 44: Nested COALESCE expressions - Test 45: SELECT INTO from SRF in FROM clause - Test 46: FOR loop with SRF and WHERE filter All 9 patterns round-trip correctly through parse → deparse → reparse, confirming pgsql-parser handles these AST node combinations. The issue in constructive-db is in the SQL-level deparser, not upstream. 8 new snapshot tests added to deparser-fixes.test.ts with explicit assertions for COALESCE, FROM, and function name preservation. Ref: constructive-io/constructive-planning#896
Contributor
🤖 Devin AI EngineerI'll be helping with this pull request! Here's what you should know: ✅ I will automatically:
Note: I can only respond to comments from users who have write access to this repository. ⚙️ Control Options:
|
libpg-query does not serialize the retvarno field of PLpgSQL_stmt_return_next. This causes RETURN NEXT v_entry to deparse as bare RETURN NEXT; — the variable reference is silently dropped. Adds: - Test fixtures 47-48 in plpgsql_deparser_fixes.sql - Two snapshot tests asserting the broken output (RETURN NEXT; instead of RETURN NEXT v_entry; and RETURN NEXT v_val;) - Fixtures 47-48 added to KNOWN_FAILING_FIXTURES in round-trip test These tests document the current broken behaviour. The next commit fixes the deparser to recover the variable.
Thread enclosingForVarName through FOR loop contexts and add inferReturnNextVar() to recover the variable from: 1. Enclosing FOR loop variable (most common pattern) 2. Single user-declared variable (SETOF scalar, no loop) Only infers when returnInfo is provided (kind='setof'), preventing false inference on OUT-param functions where bare RETURN NEXT is correct. Also adds extractReturnInfo() to the test infrastructure so round-trip tests can extract the function's return type from the CREATE FUNCTION SQL and pass it to the deparser. Removes fixtures 47-48 from KNOWN_FAILING — they now pass. Updates snapshot tests from bug-demonstration to fix-verification.
ebe7ea0 to
7f0336e
Compare
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.
UPDATE: don't do this,
inferReturnNextVaris attempt, not actually correct in all cases.we need to wait for an upstream PR in libpg-query
Summary
Three commits, telling the story: broken test → fix → pass.
Commit 1:
test: add CoalesceExpr and RangeFunction round-trip fixtures9 test fixtures (Tests 38–46) confirming pgsql-parser correctly handles CoalesceExpr in FuncCall args and FuncCall in RangeFunction/FROM clauses. All round-trip correctly.
Commit 2:
test: demonstrate RETURN NEXT retvarno loss (proves the bug)Adds fixtures 47-48 and snapshot tests that prove the bug exists: libpg-query does not serialize
retvarnoforPLpgSQL_stmt_return_next, soRETURN NEXT v_entrydeparses as bareRETURN NEXT;. The snapshots capture the broken output:Fixtures are added to
KNOWN_FAILING_FIXTURESin this commit.Commit 3:
fix: recover RETURN NEXT variable when retvarno is lostFixes the deparser to recover the variable:
enclosingForVarNamethrough all FOR loop contextsinferReturnNextVar()recovers from: (1) enclosing FOR loop variable, (2) single user-declared variablereturnInfois provided withkind='setof'— OUT-param functions keep bareRETURN NEXT;extractReturnInfo()in test-utils extracts return type from CREATE FUNCTION SQL for round-trip testsKNOWN_FAILING_FIXTURES— they now passRETURN NEXT v_entry;,RETURN NEXT v_val;All 236 fixtures round-trip. 95 tests pass. 65 snapshots match.
Review & Testing Checklist for Human
RETURN NEXT;without variable). This proves the bug.RETURN NEXT v_entry). Snapshots updated.RETURN NEXT;stays bare for OUT-param functions (test: "should leave RETURN NEXT bare for OUT-param functions")returnInfois not provided (test: "should leave RETURN NEXT bare when returnInfo is not provided")Notes
The
extractReturnInfo()helper only returns returnInfo for SETOF and OUT-param functions. Other return types (scalar, void, trigger) intentionally returnundefinedto avoid changing existing RETURN statement behaviour.Link to Devin session: https://app.devin.ai/sessions/c0494871633d4beb91f1e16e53c776d1
Requested by: @pyramation