fix: allow COMMIT/ROLLBACK in nested PL/iSQL procedure calls#1125
fix: allow COMMIT/ROLLBACK in nested PL/iSQL procedure calls#1125gaoxueyu merged 1 commit intoIvorySQL:masterfrom
Conversation
📝 WalkthroughWalkthroughSets a resource-owner requirement flag when PL/iSQL constructs CALLs from parsed SQL statements and adds extensive nested COMMIT/ROLLBACK tests (including package-based scenarios) exercising multi-level and Oracle-style call patterns; the transaction test block appears inserted twice. Changes
Sequence Diagram(s)(omitted) Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches🧪 Generate unit tests (beta)
No actionable comments were generated in the recent review. 🎉 Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 0
🧹 Nitpick comments (2)
src/pl/plisql/src/pl_gram.y (1)
2720-2721: Marking Oracle‑style procedure calls as requiring a procedure ResourceOwner looks correctSetting
plisql_curr_compile->requires_procedure_resowner = truein the T_WORD and T_CWORD call branches aligns Oracle‑style calls (nested_inner_commit();and qualified variants) with the existing K_DO/K_CALL handling, so they get the same resource‑owner treatment during execution. This is the right place to flip the flag and should address the resource/leak/mismatch symptoms seen with nested COMMIT/ROLLBACK from such calls.If you touch this area again, consider centralizing the flag assignment in a helper that’s invoked whenever a
PLiSQL_stmt_callis constructed, to avoid missing future call sites, but that’s optional given the current limited set of creators.Also applies to: 2750-2751
src/pl/plisql/src/sql/plisql_transaction.sql (1)
652-800: Nested COMMIT/ROLLBACK regression tests are well‑structured and aligned with the bugfixThe new
test_nested_committable and associatednested_*procedures:
- Exercise the key scenarios from Issue #1007: COMMIT/ROLLBACK in inner procedures, deep nesting, Oracle‑style calls (no CALL keyword), and multi‑statement query strings.
- Correctly mark all new procedures as
SECURITY INVOKER, matching the documented limitation that SECURITY DEFINER andSEToptions force atomic mode.- Clean up after themselves by truncating between tests and dropping all created procedures and
test_nested_commitat the end of the script.This provides solid coverage of the newly introduced behavior. If you want to extend coverage later, you could add a couple of nested SECURITY DEFINER /
SETprocedures to assert that transaction commands still fail there, but that’s not necessary for this PR.
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
src/pl/plisql/src/expected/plisql_transaction.outis excluded by!**/*.out
📒 Files selected for processing (4)
src/backend/tcop/utility.c(3 hunks)src/pl/plisql/src/pl_exec.c(2 hunks)src/pl/plisql/src/pl_gram.y(2 hunks)src/pl/plisql/src/sql/plisql_transaction.sql(1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
src/backend/tcop/utility.c (1)
src/backend/commands/functioncmds.c (2)
ExecuteDoStmt(2794-2962)ExecuteCallStmt(2992-3182)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
- GitHub Check: meson_build (ubuntu-latest)
- GitHub Check: pg_regression (ubuntu-latest)
- GitHub Check: oracle_regression (ubuntu-latest)
- GitHub Check: build (ubuntu-latest)
- GitHub Check: contrib_regression
- GitHub Check: oracle_pg_regression (ubuntu-latest)
🔇 Additional comments (4)
src/pl/plisql/src/pl_exec.c (2)
5318-5335: Explicitly resetting simple-expression globals on COMMIT is sound and matches the designClearing
simple_econtext_stack,shared_simple_eval_estate, andshared_simple_eval_resownerafterSPI_commit()is consistent with the transaction/subxact callbacks and with thebefore_lxid/after_lxidhandling inexec_stmt_call(). It prevents use of stale pointers when callbacks are skipped (e.g., implicit transaction blocks in multi‑statement queries), and is idempotent when callbacks do run. The subsequentplisql_create_econtext()correctly rebuilds per‑estate simple‑expression state in the new transaction.
5353-5370: Symmetric cleanup on ROLLBACK correctly mirrors COMMIT behaviorThe added comment and the explicit NULLing of
simple_econtext_stack,shared_simple_eval_estate, andshared_simple_eval_resownerafterSPI_rollback()mirror the COMMIT path and ensure we don’t retain stack entries or shared state from the aborted transaction. Combined with the reinitialization viaplisql_create_econtext(), this avoids stale pointers in both rollback and nested procedure scenarios while remaining safe when xact callbacks already performed the cleanup.src/backend/tcop/utility.c (2)
70-70: Oracle‑compatibility header inclusion is appropriateIncluding
utils/ora_compatible.hhere is necessary for accessingcompatible_db/ORA_PARSERand is scoped appropriately to the new DO/CALL atomicity logic.
716-735: Oracle‑mode DO/CALL atomicity override matches the PR goal but changes behavior inside explicit transactionsThe new
doAtomicContext/callAtomicContextlogic:
- Leaves behavior unchanged for non‑Oracle modes.
- In Oracle mode, forces DO and CALL to be non‑atomic whenever
contextisPROCESS_UTILITY_TOPLEVELorPROCESS_UTILITY_QUERY_NONATOMIC, regardless ofIsTransactionBlock(), so COMMIT/ROLLBACK are allowed inside anonymous blocks and procedures (and nested calls) in those contexts.- Still respects existing safeguards in
ExecuteCallStmt()(proconfig andSECURITY DEFINERforcingatomic=true), so such procedures continue to disallow transaction control as before.This seems correct for fixing nested PL/iSQL COMMIT/ROLLBACK and multi‑statement query crashes, and it’s nicely constrained to Oracle compatibility mode.
One thing to be aware of is that in Oracle mode this also makes top‑level DO/CALL non‑atomic even when the user has explicitly started a transaction block (
BEGIN; CALL ...;), so COMMIT inside those blocks will now terminate the outer transaction as well. If that’s intentional for Oracle parity, you might want to document it; if not, consider additionally gating the override on!IsTransactionBlock()to keep DO/CALL atomic when wrapped in an explicit user transaction.Also applies to: 878-897
Oracle compatibility verificationVerified behavior against Oracle 23.26 Free:
Oracle test script-- Setup
CREATE TABLE test_nested_commit (id NUMBER);
-- Inner procedure with COMMIT
CREATE OR REPLACE PROCEDURE nested_inner_commit AS
BEGIN
INSERT INTO test_nested_commit VALUES (1);
COMMIT;
INSERT INTO test_nested_commit VALUES (2);
END;
/
-- Outer procedure calling inner
CREATE OR REPLACE PROCEDURE nested_outer_commit AS
BEGIN
INSERT INTO test_nested_commit VALUES (0);
nested_inner_commit();
INSERT INTO test_nested_commit VALUES (3);
END;
/
-- Test 1: Basic nested call with COMMIT
TRUNCATE TABLE test_nested_commit;
EXEC nested_outer_commit;
SELECT * FROM test_nested_commit ORDER BY id;
-- Result: 0, 1, 2, 3
-- Test 2: Deeply nested calls (3 levels)
CREATE OR REPLACE PROCEDURE nested_level3 AS
BEGIN
INSERT INTO test_nested_commit VALUES (103);
COMMIT;
END;
/
CREATE OR REPLACE PROCEDURE nested_level2 AS
BEGIN
INSERT INTO test_nested_commit VALUES (102);
nested_level3();
INSERT INTO test_nested_commit VALUES (104);
END;
/
CREATE OR REPLACE PROCEDURE nested_level1 AS
BEGIN
INSERT INTO test_nested_commit VALUES (101);
nested_level2();
INSERT INTO test_nested_commit VALUES (105);
END;
/
TRUNCATE TABLE test_nested_commit;
EXEC nested_level1;
SELECT * FROM test_nested_commit ORDER BY id;
-- Result: 101, 102, 103, 104, 105
-- Test 3: ROLLBACK in nested procedure
CREATE OR REPLACE PROCEDURE nested_inner_rollback AS
BEGIN
INSERT INTO test_nested_commit VALUES (201);
ROLLBACK;
INSERT INTO test_nested_commit VALUES (202);
END;
/
CREATE OR REPLACE PROCEDURE nested_outer_rollback AS
BEGIN
INSERT INTO test_nested_commit VALUES (200);
nested_inner_rollback();
INSERT INTO test_nested_commit VALUES (203);
END;
/
TRUNCATE TABLE test_nested_commit;
EXEC nested_outer_rollback;
SELECT * FROM test_nested_commit ORDER BY id;
-- Result: 202, 203 (200 and 201 were rolled back) |
I tried to understand this statement, and found the followings: commit ee895a6 |
| new->is_call = true; | ||
|
|
||
| /* Remember we may need a procedure resource owner */ | ||
| plisql_curr_compile->requires_procedure_resowner = true; |
There was a problem hiding this comment.
Good catch!requires_procedure_resowner is necessary for stmt_execsql;
| * an atomic context (e.g., called from a function), we must | ||
| * respect that. | ||
| */ | ||
| { |
There was a problem hiding this comment.
Are the changes to utility.c used for correcting the "invalid transaction termination" error reported by 2 existing regression cases in plisql_transaction.sql?
There was a problem hiding this comment.
Yes. These changes make DO and CALL non-atomic in Oracle mode at top level, which fixes the "invalid transaction termination" error in those 2 existing cases. The expected output has been updated accordingly.
Note that the CALL transaction_test1(9, 'error') case (line 47) still errors because that procedure uses SECURITY DEFINER, which forces atomic mode in ExecuteCallStmt().
|
For commit 307b066 (fix: allow COMMIT in nested procedure calls for Oracle compatibility)
DO $$ |
|
For commit 20c3898 (fix: handle econtext stack in multi-statement queries with COMMIT/ROLLBACK), what crash scenario does this fix address? Could you please provide: |
|
|
||
| -- Test 2: Multi-statement query with nested CALL (previously crashed) | ||
| TRUNCATE test_nested_commit; | ||
| SELECT 1 AS setup; CALL nested_outer_commit(); SELECT 2 AS done; |
There was a problem hiding this comment.
psql client would seperate "SELECT 1 AS setup; CALL nested_outer_commit(); SELECT 2 AS done;" into 3 simple query messages, and doesn't create implicit transaction block.
|
Hi @rophy, thanks again for the PR. Since I haven’t heard back for a while, would you mind if I push a small follow-up commit to your branch? I’d like to keep the pl_gram.y change and adjust/drop the parts we discussed (e.g. the utility.c change and/or test tweaks), then help get this merged. |
@yuanyl630 feel free to push things as necessary. Sorry that I'll have to wait till this weekend to continue working on PRs. |
|
Commit 2ad34f3 removed utility.c changes as suggested in #1125 (comment), and tests pass. Still working on #1125 (comment) - honestly I don't understand and remember why I (ok, Calude) changed pl_exec.c. I vaguely remember I ased Claude to add more tests and see if this indeed fixed "COMMIT/ROLLBACK in nested PL/iSQL procedure calls", and I think the changes resulted in Claude trying to fix something in pl_exec.c. |
|
Updated based on review feedback:
Net changes (3 files): Re @OreoYang's SECURITY INVOKER question: The PR description was inaccurate. Test procedures use The original issue's Test 1 (standalone procs without explicit |
|
In reply to #1125 (comment)): Why test procedures use Oracle-syntax if (ORA_PARSER == compatible_db &&
language_item &&
strcmp(*language, "plisql") == 0)
*security_definer = true; /* default is DEFINER in oracle */PostgreSQL blocks COMMIT/ROLLBACK in SECURITY DEFINER procedures (forces However, Oracle allows COMMIT in AUTHID DEFINER procedures. I verified against Oracle 23.26 Free — COMMIT works identically in both AUTHID DEFINER and AUTHID CURRENT_USER. The partial commit behavior is the same. So the real issue is: IvorySQL applies Oracle's AUTHID default but PostgreSQL's atomic restriction, which is incompatible. Oracle-mode should either:
This should probably be discussed in another issue |
|
Final update:
What this PR fixes:
Error 1 is a separate issue: IvorySQL applies Oracle's Net changes (5 files): Ready for squash/merge. @yuanyl630 feel free to adjust. |
fixes plancache resource leak from issue IvorySQL#1007 by setting requires_procedure_resowner for Oracle-style calls in pl_gram.y codes by Rophy Tsai <[email protected]>
Summary
Changes
src/backend/tcop/utility.c
atomic=falsefor DO and CALL statements in Oracle mode at top-levelsrc/pl/plisql/src/pl_gram.y
requires_procedure_resowner = truefor Oracle-style procedure calls (T_WORD/T_CWORD)src/pl/plisql/src/pl_exec.c
simple_econtext_stackinexec_stmt_commit()andexec_stmt_rollback()to handle cases where xact callbacks are not firedTest plan
Known limitations
SECURITY INVOKER(PL/iSQL defaults toSECURITY DEFINERwhich forces atomic mode)SEToptions also force atomic modeFixes #1007
Summary by CodeRabbit
Improvements
Tests