Skip to content

PRAGMA AUTONOMOUS_TRANSACTION Implementation#986

Merged
gaoxueyu merged 4 commits intoIvorySQL:masterfrom
rophy:feat/auto-tx
Dec 18, 2025
Merged

PRAGMA AUTONOMOUS_TRANSACTION Implementation#986
gaoxueyu merged 4 commits intoIvorySQL:masterfrom
rophy:feat/auto-tx

Conversation

@rophy
Copy link
Copy Markdown
Contributor

@rophy rophy commented Nov 28, 2025

Summary

This PR implements Oracle-compatible PRAGMA AUTONOMOUS_TRANSACTION syntax for PL/iSQL, enabling functions and procedures to execute in independent transactions that can commit or rollback separately from the parent transaction.

Implementation Approach

Design Decision: dblink-based Implementation

After evaluating three approaches, we chose a dblink-based implementation for the initial release:

Approach 1: dblink Extension (Chosen)

How it works: Opens new database connection for independent transaction

Pros:

  • Available now in standard PostgreSQL
  • Well-tested, stable solution
  • Simple concept (separate connection)
  • No external dependencies beyond contrib/dblink

Cons:

  • Performance overhead (~3-7ms per call)
  • Requires dblink extension installation

References:

Approach 2: pg_background Extension (Evaluated but not chosen)

How it works: Uses background worker processes for independent transactions

Pros:

  • Better performance than dblink (~5-10ms vs 3-7ms)
  • Uses background workers (no new connections)
  • Cleaner implementation

Cons:

  • Requires external extension (not in PostgreSQL contrib)
  • Asynchronous by default (needs result polling)
  • Less widely deployed than dblink
  • Cross-session visibility issues with PL/iSQL procedures

References:

Approach 3: Native PostgreSQL Patch (Future consideration)

How it works: Port WIP PostgreSQL autonomous transaction patch
Status: Work in Progress (not merged to PostgreSQL upstream as of December 2023)

Pros:

  • Best performance (<1ms overhead)
  • True native implementation
  • Proper isolation and resource management
  • Uses background workers with client-server protocol
  • Lazy pool of autonomous sessions

Cons:

  • Not merged to PostgreSQL upstream
  • Significant engineering effort
  • Maintenance burden across PostgreSQL versions
  • Unknown merge timeline
  • High complexity

References:

Decision Rationale

I chose Approach 1 (dblink) because:

  1. pg_background requires adding another git project as dependency, dblink is included in postgres src.
  2. I actually asked AI to implement pg_background, but failed - AI had trouble making the code to pass simple tests.

Syntax

CREATE OR REPLACE PROCEDURE log_audit(p_action TEXT, p_details TEXT)
AS $$
DECLARE
    PRAGMA AUTONOMOUS_TRANSACTION;
BEGIN
    INSERT INTO audit_log (action, details, ts)
    VALUES (p_action, p_details, CURRENT_TIMESTAMP);
END;
$$ LANGUAGE plisql;

Supported declarations:

  • Functions (with any return type)
  • Procedures
  • Package body functions/procedures
  • Nested subprocedures

Placement: Must appear in DECLARE section (anywhere among variable declarations)

Implementation Details

Recursion Prevention

Autonomous transactions are prevented from recursively calling themselves via a session-local GUC flag:

/* In pl_handler.c */
DefineCustomBoolVariable("plisql.inside_autonomous_transaction",
    gettext_noop("Internal flag to prevent recursive autonomous transaction execution."),
    NULL,
    &plisql_inside_autonomous_transaction,
    false,
    PGC_USERSET,  /* MUST be USERSET for dblink SET command to work */
    GUC_NOT_IN_SAMPLE | GUC_NO_SHOW_ALL | GUC_NO_RESET_ALL,  /* Hidden from users */
    NULL, NULL, NULL);

Why PGC_USERSET instead of PGC_INTERNAL:

  • Autonomous transactions execute via dblink in a separate connection
  • The SQL contains: SET plisql.inside_autonomous_transaction = true;
  • If the GUC context is PGC_INTERNAL, PostgreSQL silently ignores the SET command
  • Without the flag being set, autonomous procedures would call themselves recursively
  • This causes connection exhaustion ("too many clients already" errors)

Security mitigation:

  • Hidden from SHOW ALL via GUC_NO_SHOW_ALL
  • Hidden from sample postgresql.conf via GUC_NOT_IN_SAMPLE
  • Cannot be reset via RESET ALL via GUC_NO_RESET_ALL
  • Users can only affect their own session (bypassing the flag causes self-harm via connection exhaustion)

OID Cache Invalidation

The implementation caches the dblink_exec function OID for performance. A syscache invalidation callback ensures the cache is cleared when the dblink extension is dropped/recreated:

void plisql_autonomous_init(void)
{
    /* Register callback to invalidate cached dblink_exec OID on pg_proc changes */
    CacheRegisterSyscacheCallback(PROCOID, dblink_oid_invalidation_callback, (Datum) 0);
}

Connection String Safety

The implementation uses proper libpq connection string escaping (single-quote escaping) instead of SQL identifier quoting (double-quote escaping):

/* Append dbname with single-quote escaping for libpq */
appendStringInfoString(&connstr_buf, "dbname='");
for (const char *p = dbname; *p; p++)
{
    if (*p == '\'' || *p == '\\')
        appendStringInfoChar(&connstr_buf, '\\');
    appendStringInfoChar(&connstr_buf, *p);
}
appendStringInfoChar(&connstr_buf, '\'');

NULL Safety

Guards against get_namespace_name() returning NULL when a schema is dropped concurrently:

nspname = get_namespace_name(procstruct->pronamespace);
if (nspname == NULL)
{
    /* Schema was dropped concurrently; use pg_catalog as fallback */
    nspname = pstrdup("pg_catalog");
}

Deployment Requirements

Installation

  1. Install dblink extension (one-time per database):

    CREATE EXTENSION dblink;
  2. Usage (no additional configuration needed):

    CREATE OR REPLACE PROCEDURE my_logger(p_msg TEXT) AS $$
    DECLARE
        PRAGMA AUTONOMOUS_TRANSACTION;
    BEGIN
        INSERT INTO log_table VALUES (p_msg, CURRENT_TIMESTAMP);
    END;
    $$ LANGUAGE plisql;

Configuration

No special configuration required. The implementation automatically:

  • Detects current database name from connection info
  • Reads port from GUC settings
  • Reads listen_addresses from GUC settings
  • Builds connection string with localhost and current port

Known Limitations

  1. COMMIT/ROLLBACK not supported in autonomous blocks (Issue PL/iSQL does not support COMMIT/ROLLBACK in procedures (Oracle compatibility gap) #985)

  2. Requires dblink extension

    • Must be installed with CREATE EXTENSION dblink
    • Clear error message guides users to install
    • Standard PostgreSQL contrib extension (widely available)
  3. Connection overhead

    • ~3-7ms overhead per autonomous call
    • Acceptable for typical use cases (logging, auditing)
    • For high-frequency calls, consider batching
  4. No nested savepoint support

    • Autonomous transaction is completely independent
    • Cannot access parent transaction's uncommitted data
    • Matches Oracle behavior
  5. Performance overhead for pass-by-reference types

    • Return values for complex types (TEXT, NUMERIC, etc.) require memory context switching
    • Minimal overhead (<1ms) but worth noting for high-frequency calls

Security Considerations

  1. GUC flag (plisql.inside_autonomous_transaction)

    • Hidden from normal user discovery via GUC flags
    • Users can only affect their own session
    • Bypassing the flag only causes self-harm (connection exhaustion)
  2. dblink permissions

    • Autonomous transactions use dblink, which respects PostgreSQL permissions
    • Users can only execute what they have permission to execute
    • No privilege escalation risk
  3. SQL injection protection

    • All arguments properly quoted via quote_literal_cstr()
    • Numeric types validated before outputting
    • Boolean types converted to SQL keywords (true/false)

Oracle Compatibility

Matches Oracle Behavior

✅ Syntax: PRAGMA AUTONOMOUS_TRANSACTION in DECLARE section
✅ Transaction isolation: Autonomous commits/rollbacks are independent
✅ Use cases: Logging, auditing, error tracking work as expected
✅ Recursion prevention: Autonomous routines cannot call themselves
✅ Exception handling: Exceptions in autonomous blocks don't affect parent
Function return values: Full support for all PostgreSQL types (INT, TEXT, NUMERIC, DATE, BOOLEAN, etc.)
✅ Nested autonomous calls: Autonomous functions can call other autonomous functions
✅ Error handling: Proper cleanup via PG_TRY/PG_CATCH ensures no memory leaks

Known Differences from Oracle

  1. COMMIT/ROLLBACK in autonomous blocks: Not supported (PL/iSQL limitation, see Issue PL/iSQL does not support COMMIT/ROLLBACK in procedures (Oracle compatibility gap) #985)
  2. Implementation: Uses dblink (separate connection) vs Oracle's internal mechanism

Test Coverage (20 tests)

  1. Basic autonomous procedure
  2. Parameters (IN/OUT/INOUT)
  3. Transaction isolation
  4. Multiple parameter types
  5. NULL handling
  6. Sequential calls
  7. Extension drop/recreate (OID invalidation)
  8. Transaction persistence
  9. Return values (INT)
  10. NULL returns
  11. Error handling (nonexistent table)
  12. TEXT return values (pass-by-reference)
  13. Nested autonomous calls
  14. NUMERIC return values
  15. DATE return values
  16. BOOLEAN return values
    17-20. COMMIT/ROLLBACK tests (commented, ref Issue PL/iSQL does not support COMMIT/ROLLBACK in procedures (Oracle compatibility gap) #985)

Summary by CodeRabbit

  • New Features

    • Adds AUTONOMOUS_TRANSACTION pragma to mark functions/procedures for independent commit/rollback, including support for returning values, procedures, and cross-database dispatch when available.
    • Adds safeguards to prevent recursive/autonomous reentry.
  • Tests

    • Adds an extensive autonomous-transaction test suite covering isolation, parameters/NULLs, many return types, nested/sequential calls, persistence across rollbacks, and extension-recreate scenarios.
  • Chores

    • Ensures required extension is installed for autonomous execution.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Nov 28, 2025

Walkthrough

Adds PL/iSQL autonomous-transaction support: new pl_autonomous module + header, PRAGMA AUTONOMOUS_TRANSACTION grammar, execution delegation (via dblink/dblink_exec) for autonomous calls, build/install/test updates, and a runtime flag/GUC to prevent recursive autonomous execution.

Changes

Cohort / File(s) Summary
Build & Install
src/pl/plisql/src/Makefile
Add pl_autonomous.o to object lists and core build, export pl_autonomous.h via install-headers/uninstall-headers, set EXTRA_INSTALL = contrib/dblink, and add plisql_autonomous to REGRESS.
Autonomous Implementation
src/pl/plisql/src/pl_autonomous.c, src/pl/plisql/src/pl_autonomous.h
New module + public header implementing autonomous transactions: dblink OID caching/invalidation, DB-name retrieval, proc-name quoting, connection-string and SQL builder (SELECT/CALL), execution via dblink/dblink_exec with SPI and memory-context/PG_TRY handling, and public APIs (plisql_autonomous_init, plisql_mark_autonomous_transaction, plisql_check_dblink_available, plisql_exec_autonomous_function).
Executor Integration
src/pl/plisql/src/pl_exec.c
Include pl_autonomous.h; update plisql_exec_function signature to accept eval estates/resowners and atomic flag; add branch to delegate execution to plisql_exec_autonomous_function when fn_is_autonomous and not already inside an autonomous transaction; ensure instrumentation and cleanup.
Parser / Grammar
src/pl/plisql/src/pl_gram.y, src/pl/plisql/src/pl_unreserved_kwlist.h
Add token K_AUTONOMOUS_TRANSACTION, register autonomous_transaction as unreserved keyword, and extend grammar with PRAGMA AUTONOMOUS_TRANSACTION; calling plisql_mark_autonomous_transaction at parse time.
Handler & State
src/pl/plisql/src/pl_handler.c, src/pl/plisql/src/plisql.h
Add global plisql_inside_autonomous_transaction flag and an internal GUC in _PG_init, call plisql_autonomous_init() during load, and add fn_is_autonomous field to PLiSQL_function.
Tests
src/pl/plisql/src/sql/plisql_autonomous.sql
New regression test suite that enables dblink, creates autonomous_test table, and includes tests for inserts, parameters, isolation vs outer rollbacks, return values, nested calls, OID invalidation scenarios, package procedures, and cleanup.
Build system (meson)
src/pl/plisql/src/meson.build
Add pl_autonomous.c to plisql_sources so it is compiled into the module.

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant PLiSQL_Executor as "PL/iSQL Executor"
    participant pl_autonomous as "pl_autonomous"
    participant SPI
    participant dblink
    participant RemoteDB as "Remote DB"

    Client->>PLiSQL_Executor: invoke function/procedure
    PLiSQL_Executor->>PLiSQL_Executor: check fn_is_autonomous & plisql_inside_autonomous_transaction
    alt autonomous and not inside autonomous context
        PLiSQL_Executor->>pl_autonomous: plisql_exec_autonomous_function(...)
        pl_autonomous->>pl_autonomous: resolve proc name, build connstr & SQL
        pl_autonomous->>SPI: use SPI for type conversion/prep (if needed)
        pl_autonomous->>dblink: call dblink(connstr, SQL) or dblink_exec(connstr, SQL)
        dblink->>RemoteDB: open connection & execute SQL
        RemoteDB-->>dblink: return result/status
        dblink-->>pl_autonomous: return tuples/status
        pl_autonomous->>pl_autonomous: extract/convert return value
        pl_autonomous-->>PLiSQL_Executor: return Datum / NULL
    end
    PLiSQL_Executor->>PLiSQL_Executor: func_end instrumentation & cleanup
    PLiSQL_Executor-->>Client: return result
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

  • Focus areas:
    • src/pl/plisql/src/pl_autonomous.c — connection-string/SQL construction, OID caching/invalidation, memory-context handling for pass-by-reference types, and PG_TRY/PG_CATCH correctness.
    • src/pl/plisql/src/pl_exec.c — signature change, delegation integration, and proper propagation of resource owners/estates and instrumentation hooks.
    • src/pl/plisql/src/Makefile / meson.build — build/install/test wiring for header/object/extras.
    • src/pl/plisql/src/sql/plisql_autonomous.sql — test assumptions and environment (dblink availability).

Possibly related PRs

  • Callinto 5stable #937 — touches the same PL/iSQL core files (pl_exec.c, pl_handler.c, plisql.h) and may overlap in function/state/ABI changes.

Suggested reviewers

  • OreoYang
  • jiaoshuntian
  • bigplaice

Poem

🐰 I hopped from parse to a distant host afar,

PRAGMA set my paws to run where separate ledgers are.
dblink ferried whispers while the outer rolls could fall,
My little rows stayed steady, safe beyond the call.
A rabbit nods — autonomous hops for all.

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'PRAGMA AUTONOMOUS_TRANSACTION Implementation' clearly and concisely summarizes the main change—adding support for the PRAGMA AUTONOMOUS_TRANSACTION syntax to PL/iSQL, which aligns directly with the comprehensive autonomous transaction feature introduced across the codebase.
Docstring Coverage ✅ Passed Docstring coverage is 84.62% which is sufficient. The required threshold is 80.00%.
✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@rophy
Copy link
Copy Markdown
Contributor Author

rophy commented Nov 28, 2025

Hi, this autonomous transaction looks complicated. I must admit I don't really understand all the details, especially on the pros & cons of design decision details. So no pressure to merge the PR, just wanted to know if this is actually a workable design.

The CodeRabbit agent that your team uses is really nice. I actually tried to let Claude work with CodeRabbit to work out some details in my fork: rophy#7 and rophy#9. I hope the results are OK.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/pl/plisql/src/pl_gram.y (1)

3224-3323: Keep unreserved_keyword production in sync with new “unreserved” tokens

autonomous_transaction and pragma were added to pl_unreserved_kwlist.h as unreserved keywords, but unreserved_keyword here does not include K_AUTONOMOUS_TRANSACTION or K_PRAGMA.

That has two concrete effects:

  • The scanner will now return K_AUTONOMOUS_TRANSACTION / K_PRAGMA instead of T_WORD for those lexemes, but grammar productions that rely on unreserved_keyword (e.g. decl_varname, ora_function_name, any_identifier, decl_aliasitem) will not accept them, so they can no longer be used as identifiers in those positions.
  • It contradicts the comment in pl_unreserved_kwlist.h asking to keep this production consistent with the unreserved kw list.

If the intent is for these to behave like other unreserved words (keyword in some contexts, identifier in others), they should be added here (and to unit_name_keyword if relevant), preserving the existing ordering. For example:

 unreserved_keyword :
 				K_ABSOLUTE
 				| K_ACCESSIBLE
 				| K_ALIAS
 				| K_AND
 				| K_ARRAY
 				| K_AS
 				| K_ASSERT
 				| K_AUTHID
+				| K_AUTONOMOUS_TRANSACTION
 				| K_BACKWARD
@@
 				| K_PIPELINED
-				| K_PG_ROUTINE_OID
-				| K_PRINT_STRICT_PARAMS
+				| K_PG_ROUTINE_OID
+				| K_PRAGMA
+				| K_PRINT_STRICT_PARAMS

If instead the goal is to treat them as fully reserved (never valid as identifiers), they should probably live in the reserved kw list rather than the unreserved one, and remain absent from this production. Either way, making the choice explicit here avoids surprising parsing differences and keeps the grammar consistent with the kwlist.

🧹 Nitpick comments (3)
src/pl/plisql/src/pl_exec.c (1)

684-710: Autonomous delegation path is sound; consider minor robustness/maintainability tweaks

The autonomous path is wired correctly overall: it’s guarded by fn_is_autonomous and plisql_inside_autonomous_transaction, avoids firing during validation via the fcinfo->flinfo->fn_oid == func->fn_oid check, calls plisql_exec_autonomous_function once, then mirrors the normal epilogue (func_end, plisql_destroy_econtext, exec_eval_cleanup, error_context_stack pop) before returning the Datum. That should prevent recursion, preserve error context, and avoid leaks.

Two minor follow‑ups you might consider:

  • Guard rationale / validation paths: The fcinfo != NULL && fcinfo->flinfo != NULL && fcinfo->flinfo->fn_oid == func->fn_oid condition is a subtle contract with call sites to distinguish “real” calls from validation/compilation. It would be good to double‑check all current callers of plisql_exec_function obey that invariant and, if so, maybe add a brief comment pointing to the specific validator path this is protecting against (or even an Assert(fcinfo == NULL || fcinfo->flinfo != NULL) above).
  • Avoid duplicated epilogue logic: The autonomous branch duplicates the normal function epilogue’s func_end + cleanup sequence. If more logic ever gets added to the common exit path, this branch could drift. A tiny helper that encapsulates the common “function end + cleanup + error_context_stack pop” would keep the two paths in lockstep.

These are optional refinements; the current implementation is functionally correct.

src/pl/plisql/src/pl_autonomous.c (2)

130-143: Potential use-after-free: nspname freed after use in psprintf.

The nspname is passed to quote_identifier() within psprintf, and then pfree(nspname) is called. However, get_namespace_name() returns a palloc'd string, but the fallback pstrdup("pg_catalog") also allocates. The issue is that nspname is freed unconditionally, but the condition if (nspname) is always true at that point (either from get_namespace_name returning non-NULL or from pstrdup).

The logic is actually correct, but the if (nspname) check is misleading since after the fallback assignment, nspname is never NULL at line 141. Consider removing the redundant check for clarity:

 	ReleaseSysCache(proctup);
-	if (nspname)
-		pfree(nspname);
+	pfree(nspname);
 	return result;

486-495: Consider quoting host in connection string for robustness.

The host=localhost is hardcoded and safe, but the port is appended directly without escaping. While port comes from the PostgreSQL GUC and should always be a numeric string, defensive escaping would be more robust:

 	/* Add port if configured */
 	if (port_str)
-		appendStringInfo(&connstr_buf, " port=%s", port_str);
+	{
+		appendStringInfoString(&connstr_buf, " port='");
+		for (const char *p = port_str; *p; p++)
+		{
+			if (*p == '\'' || *p == '\\')
+				appendStringInfoChar(&connstr_buf, '\\');
+			appendStringInfoChar(&connstr_buf, *p);
+		}
+		appendStringInfoChar(&connstr_buf, '\'');
+	}

However, since port is a validated GUC that only accepts integer values, the current implementation is safe in practice.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c51d958 and c58c5ec.

⛔ Files ignored due to path filters (1)
  • src/pl/plisql/src/expected/plisql_autonomous.out is excluded by !**/*.out
📒 Files selected for processing (9)
  • src/pl/plisql/src/Makefile (4 hunks)
  • src/pl/plisql/src/pl_autonomous.c (1 hunks)
  • src/pl/plisql/src/pl_autonomous.h (1 hunks)
  • src/pl/plisql/src/pl_exec.c (4 hunks)
  • src/pl/plisql/src/pl_gram.y (3 hunks)
  • src/pl/plisql/src/pl_handler.c (6 hunks)
  • src/pl/plisql/src/pl_unreserved_kwlist.h (2 hunks)
  • src/pl/plisql/src/plisql.h (2 hunks)
  • src/pl/plisql/src/sql/plisql_autonomous.sql (1 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-09-02T10:43:25.257Z
Learnt from: syasirs
Repo: IvorySQL/IvorySQL PR: 858
File: src/Makefile.global.in:513-514
Timestamp: 2025-09-02T10:43:25.257Z
Learning: In IvorySQL Makefile.global.in, $(wildcard t/oracle/*.pl) is used instead of t/oracle/*.pl in Oracle TAP test discovery because the t/oracle directory is optional and may not exist in most builds. The wildcard function safely returns empty string when the directory is missing, preventing shell expansion errors.

Applied to files:

  • src/pl/plisql/src/Makefile
🧬 Code graph analysis (3)
src/pl/plisql/src/pl_autonomous.h (1)
src/pl/plisql/src/pl_autonomous.c (4)
  • plisql_autonomous_init (61-66)
  • plisql_mark_autonomous_transaction (157-179)
  • plisql_exec_autonomous_function (423-555)
  • plisql_check_dblink_available (186-190)
src/pl/plisql/src/pl_exec.c (1)
src/pl/plisql/src/pl_autonomous.c (1)
  • plisql_exec_autonomous_function (423-555)
src/pl/plisql/src/pl_handler.c (2)
src/backend/utils/misc/guc.c (1)
  • DefineCustomBoolVariable (5137-5161)
src/pl/plisql/src/pl_autonomous.c (1)
  • plisql_autonomous_init (61-66)
🔇 Additional comments (18)
src/pl/plisql/src/pl_unreserved_kwlist.h (1)

40-40: New unreserved keywords look correctly wired here

autonomous_transaction and pragma are added in ASCII order and use distinct token codes (K_AUTONOMOUS_TRANSACTION, K_PRAGMA), which is consistent with the surrounding list. Any follow‑up alignment between this list and unreserved_keyword in pl_gram.y is handled in that file’s review.

Also applies to: 105-105

src/pl/plisql/src/plisql.h (1)

1065-1076: Autonomous-transaction flags are cleanly integrated into function and global state

Adding fn_is_autonomous to PLiSQL_function alongside the other per-function flags, and exposing plisql_inside_autonomous_transaction as a global, is a straightforward way to surface autonomous-transaction state to both the compiler and executor paths. Struct layout remains coherent, and the new fields are clearly documented by their naming and comment.

Assuming the corresponding definitions, GUC wiring, and initialization are implemented in the C modules, this header change looks good.

Also applies to: 1278-1279

src/pl/plisql/src/pl_exec.c (3)

45-45: pl_autonomous.h include placement looks correct

Including pl_autonomous.h alongside other PL/iSQL headers is appropriate and keeps the dependency localized to the executor. No issues here.


490-513: Function-level comment matches current plisql_exec_function behavior

The expanded comment accurately describes the responsibilities and parameters of plisql_exec_function, including autonomous-transaction support and the role of procedure_resowner/atomic. This improves readability without affecting behavior.


9884-9931: No functional change in plisql_anonymous_return_out_parameter

Only the closing brace line is touched here; the implementation of plisql_anonymous_return_out_parameter itself is unchanged and remains consistent with how anonymous blocks with OUT parameters are handled elsewhere.

src/pl/plisql/src/pl_handler.c (3)

62-63: LGTM - Global flag for autonomous transaction detection.

The global boolean plisql_inside_autonomous_transaction is correctly declared and will be controlled by the GUC defined later in _PG_init. This enables detection of recursive autonomous transaction attempts.


202-215: LGTM - Well-designed hidden GUC for recursion prevention.

The GUC design is sound:

  • PGC_USERSET is required since the dblink session must be able to SET this flag
  • Hidden flags (GUC_NOT_IN_SAMPLE | GUC_NO_SHOW_ALL | GUC_NO_RESET_ALL) appropriately prevent user discovery while allowing the autonomous session to modify it
  • The comment clearly documents the design rationale

248-250: LGTM - Initialization hook for autonomous transaction support.

The call to plisql_autonomous_init() correctly registers the syscache invalidation callback for dblink OID caching, placed appropriately near the end of _PG_init after other module initialization.

src/pl/plisql/src/pl_autonomous.h (1)

1-45: LGTM - Clean public header for autonomous transaction support.

The header is well-structured with:

  • Proper include guard
  • Minimal dependency on plisql.h for required types
  • Clear documentation comments for each API function
  • Declarations correctly match the implementations in pl_autonomous.c
src/pl/plisql/src/Makefile (2)

55-57: LGTM - Correct EXTRA_INSTALL for dblink dependency.

EXTRA_INSTALL = contrib/dblink ensures the dblink extension is built and installed in the test instance before running the autonomous transaction tests.


109-109: LGTM - Build dependencies correctly updated.

The dependency line properly includes pl_autonomous.o in the list and adds pl_autonomous.h as a prerequisite, ensuring correct rebuild ordering when the autonomous transaction module changes.

src/pl/plisql/src/pl_autonomous.c (4)

251-275: Consider handling BYTEA and other binary types explicitly.

The switch statement handles common scalar types but falls through to quote_literal_cstr for everything else. For BYTEA (binary data), OidOutputFunctionCall returns a hex-encoded string, which quote_literal_cstr will quote correctly. However, depending on the bytea_output setting, this could produce \x... or escape format, both of which should work but may warrant a note.

The current fallback to quote_literal_cstr is safe and handles SQL injection correctly. This is acceptable for initial implementation.


395-404: Double-free of query on error path.

In the PG_CATCH block, pfree(query) is called at line 399, then PG_RE_THROW() is called. However, if an error occurs, execution never reaches line 404. But wait - PG_RE_THROW() doesn't return, so line 404 is only reached on success. Let me re-analyze...

Actually, on closer inspection: PG_RE_THROW() does not return, so line 404 is only reached on the success path. The cleanup is correct. However, for clarity and consistency with other cleanup patterns in this file, consider restructuring:

The error handling is correct - PG_RE_THROW() never returns, so line 404 is only executed on success.


1-33: LGTM - File structure and includes are appropriate.

The includes cover all necessary headers for the functionality:

  • Catalog access for procedure/type lookups
  • SPI for query execution
  • libpq-be for MyProcPort access
  • Syscache invalidation infrastructure

423-426: I'm unable to access the IvorySQL repository directly due to cloning issues, and web search yields no specific documentation about the internal GUC plisql_inside_autonomous_transaction or the recursion prevention mechanism described in your review comment.

Manual verification required.

The review comment makes specific claims about:

  1. The GUC being set in SQL at lines 286 and 296
  2. Missing recursion checks at the function entry (lines 423-426)
  3. A potential workaround via pl_exec.c

Since I cannot access the source code to verify these details, and there is no public documentation available for this internal mechanism, you will need to manually verify:

  • Whether plisql_inside_autonomous_transaction is actually checked before plisql_exec_autonomous_function executes
  • Whether recursion guards exist upstream in the calling code (likely in pl_exec.c)
  • Whether the dblink-based approach with the GUC setting actually provides adequate recursion prevention

If such checks are confirmed to exist elsewhere in the codebase, the review comment should be updated to reference their location or be resolved. If no checks exist, the concern is valid and warrants correction.

src/pl/plisql/src/sql/plisql_autonomous.sql (3)

301-317: Test 15 relies on psql error behavior - verify expected output file handles error.

Test 15 intentionally triggers an error by inserting into nonexistent_table. The test has commented-out \set ON_ERROR_STOP directives. Ensure the expected output file (plisql_autonomous.out) correctly captures this error, or the test may cause the entire regression suite to fail depending on psql settings.


1-454: LGTM - Comprehensive test coverage for autonomous transactions.

The test suite provides good coverage including:

  • Basic procedure/function execution
  • Transaction isolation (autonomous commits survive outer rollbacks)
  • Multiple parameter types and NULL handling
  • OID invalidation after extension recreation
  • Pass-by-reference return types (TEXT, NUMERIC)
  • Error handling paths
  • Appropriate cleanup at the end

The commented tests for explicit COMMIT/ROLLBACK clearly document the known limitation (Issue #985).


343-367: I'll help you verify the nested autonomous function behavior. Let me search the codebase for the recursion prevention logic and how it handles nested autonomous calls.
<function_calls>


#!/bin/bash

Search for the recursion prevention logic and nested autonomous handling

rg -n "plisql_inside_autonomous" --type c -B 2 -A 5


</function_calls>

This will help me understand how the recursion check works. Let me also search for where this variable is defined and used:
<function_calls>


#!/bin/bash

Find definition and all usages of the recursion check

rg -n "inside_autonomous_transaction" --type c -B 1 -A 2 | head -100


</function_calls>

Let me also look at the actual test file to understand the full context around Test 17:
<function_calls>


#!/bin/bash

Read the test file to see the full Test 17 and surrounding context

cat -n src/pl/plisql/src/sql/plisql_autonomous.sql | sed -n '330,380p'


</function_calls>

Let me also search for dblink connection handling and how autonomous transactions interact with dblink:
<function_calls>


#!/bin/bash

Search for dblink and autonomous transaction interaction

rg -n "dblink|autonomous" src/pl/plisql/src/ --type c -B 1 -A 3 | head -150


</function_calls>

Comment thread src/pl/plisql/src/pl_gram.y
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Nitpick comments (2)
src/pl/plisql/src/pl_autonomous.c (2)

141-142: Unnecessary NULL check.

At this point, nspname is guaranteed non-NULL (either from get_namespace_name or the fallback assignment). The conditional is dead code.

 	ReleaseSysCache(proctup);
-	if (nspname)
-		pfree(nspname);
+	pfree(nspname);

419-425: Unused parameters simple_eval_estate and simple_eval_resowner.

The function signature includes EState *simple_eval_estate and ResourceOwner simple_eval_resowner, but neither is used in the function body. If these are reserved for future use, consider documenting that. Otherwise, they add unnecessary API surface.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1cab728 and 160e8bb.

⛔ Files ignored due to path filters (1)
  • src/pl/plisql/src/expected/plisql_autonomous.out is excluded by !**/*.out
📒 Files selected for processing (9)
  • src/pl/plisql/src/Makefile (4 hunks)
  • src/pl/plisql/src/pl_autonomous.c (1 hunks)
  • src/pl/plisql/src/pl_autonomous.h (1 hunks)
  • src/pl/plisql/src/pl_exec.c (3 hunks)
  • src/pl/plisql/src/pl_gram.y (3 hunks)
  • src/pl/plisql/src/pl_handler.c (6 hunks)
  • src/pl/plisql/src/pl_unreserved_kwlist.h (1 hunks)
  • src/pl/plisql/src/plisql.h (2 hunks)
  • src/pl/plisql/src/sql/plisql_autonomous.sql (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
  • src/pl/plisql/src/pl_gram.y
  • src/pl/plisql/src/pl_autonomous.h
  • src/pl/plisql/src/pl_unreserved_kwlist.h
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-09-02T10:43:25.257Z
Learnt from: syasirs
Repo: IvorySQL/IvorySQL PR: 858
File: src/Makefile.global.in:513-514
Timestamp: 2025-09-02T10:43:25.257Z
Learning: In IvorySQL Makefile.global.in, $(wildcard t/oracle/*.pl) is used instead of t/oracle/*.pl in Oracle TAP test discovery because the t/oracle directory is optional and may not exist in most builds. The wildcard function safely returns empty string when the directory is missing, preventing shell expansion errors.

Applied to files:

  • src/pl/plisql/src/Makefile
🧬 Code graph analysis (3)
src/pl/plisql/src/pl_autonomous.c (8)
src/backend/utils/cache/inval.c (1)
  • CacheRegisterSyscacheCallback (1811-1842)
src/backend/commands/extension.c (1)
  • get_extension_oid (166-181)
src/common/stringinfo.c (3)
  • initStringInfo (96-100)
  • appendStringInfoString (229-233)
  • appendStringInfo (144-166)
src/backend/utils/fmgr/fmgr.c (1)
  • OidOutputFunctionCall (1819-1826)
src/backend/parser/parse_func.c (1)
  • LookupFuncName (2414-2459)
src/backend/executor/spi.c (4)
  • SPI_connect (96-100)
  • SPI_execute (605-636)
  • SPI_getbinval (1278-1292)
  • SPI_finish (186-220)
src/include/utils/palloc.h (1)
  • MemoryContextSwitchTo (123-130)
src/backend/utils/misc/guc.c (1)
  • GetConfigOption (4359-4400)
src/pl/plisql/src/pl_handler.c (2)
src/backend/utils/misc/guc.c (1)
  • DefineCustomBoolVariable (5137-5161)
src/pl/plisql/src/pl_autonomous.c (1)
  • plisql_autonomous_init (61-66)
src/pl/plisql/src/pl_exec.c (1)
src/pl/plisql/src/pl_autonomous.c (1)
  • plisql_exec_autonomous_function (423-555)
⏰ 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: oracle_regression (ubuntu-latest)
  • GitHub Check: meson_build (ubuntu-latest)
  • GitHub Check: oracle_pg_regression (ubuntu-latest)
  • GitHub Check: build (ubuntu-latest)
  • GitHub Check: contrib_regression
  • GitHub Check: pg_regression (ubuntu-latest)
🔇 Additional comments (32)
src/pl/plisql/src/sql/plisql_autonomous.sql (9)

1-15: Setup is clear and complete.

Test table and dblink extension correctly provisioned. The schema (id, msg, tx_state) is well-suited for verifying autonomous transaction semantics (commit/rollback isolation).


20-96: Test 1–4: Basic functionality and parameter handling look solid.

Each test follows a consistent pattern: define, commit, call, verify with SELECT. Tests progressively exercise no parameters → scalar parameters → multiple types (INT, TEXT, BOOLEAN). The isolation test (lines 53–71) is particularly good: it confirms autonomous commits survive outer ROLLBACK, which is the core semantic.


100-130: Tests 5–6: NULL handling and sequential calls well covered.

Test 5 (lines 100–111) validates that NULL parameters are properly passed and handled with COALESCE. Test 6 (lines 116–130) confirms multiple back-to-back autonomous calls don't interfere with each other. Both are essential for real-world use.


143-166: Test 8: Persistence semantics verified correctly.

The mid-suite TRUNCATE at line 143 is intentional for test isolation, not a red flag. The test cleanly demonstrates that autonomous inserts persist even when the outer transaction rolls back. The inline SELECT COUNT(*) at line 162 within the transaction is a nice touch to show visibility.


171-192: Test 9: OID invalidation testing is thorough but operationally risky.

Dropping and recreating the dblink extension (lines 185–186) tests the syscache invalidation callback. This is essential for confirming cached OID correctness. However, if this test fails mid-execution, subsequent tests depend on dblink being present. In a production test harness, this test should be isolated or ordered last. For a single-file test, this is acceptable since cleanup happens afterward.

Confirm whether this test file runs as a single atomic unit or if individual tests can be reordered/skipped. If it's meant to be run with psql -f, consider whether dropping dblink mid-suite could cause issues if an earlier test fails.


197-429: Tests 10–20: Comprehensive type coverage and nested/error cases.

Excellent progression through return types (INT, TEXT, NULL, NUMERIC, DATE, BOOLEAN), nested autonomous calls (Test 17, lines 343–367), and pass-by-reference handling. Test 18–20 specifically exercise NUMERIC (variable-length), DATE, and BOOLEAN to stress datumCopy() code paths. Test 15 (error handling) inserts into a nonexistent table to test exception cleanup (SPI_finish, pfree).


240-296: Commented COMMIT/ROLLBACK tests appropriately documented.

Four tests (Tests 12–14) exercising explicit COMMIT/ROLLBACK and conditional transaction control are commented with Issue #985 references, making it clear these are known limitations rather than oversights. This is good practice for documenting intent.


437-454: Cleanup is comprehensive and matches all defined procedures/functions.

All 17 procedures/functions plus the test table are dropped. The cleanup will succeed even if earlier tests failed (assuming CASCADE is handled gracefully). No dangling objects.


301-318: Verify test execution and error-handling behavior for intentional errors in test_function_error().

Lines 315–317 appear to have \set ON_ERROR_STOP off/on directives commented out around an expected error test. Confirm: (1) whether the test harness or runner catches errors automatically, (2) the intended execution mode (manual interactive testing vs. automated suite), and (3) whether execution should halt or continue past this intentional error. Update the code or documentation accordingly.

src/pl/plisql/src/plisql.h (2)

1067-1067: LGTM!

The fn_is_autonomous field is well-placed alongside other function metadata flags like has_exception_block. This correctly extends the function struct to track autonomous transaction capability at compile time.


1280-1281: LGTM!

The extern declaration for plisql_inside_autonomous_transaction follows the established pattern for other PL/iSQL global flags (e.g., plisql_check_asserts, plisql_print_strict_params). The blank line after the declaration maintains consistent formatting.

src/pl/plisql/src/pl_handler.c (5)

27-27: LGTM!

Header include is correctly placed with other module-specific headers.


62-63: LGTM!

Clear initialization of the recursion-prevention flag with a descriptive comment.


154-165: LGTM!

Good documentation update describing the initialization flow, including the new autonomous-transaction support. The warning about not making the function static is helpful for maintainability.


202-215: Verify security implications of PGC_USERSET for internal flag.

The comment correctly explains why PGC_USERSET is needed (dblink sessions must SET it). However, this means any user can manually set plisql.inside_autonomous_transaction = true to bypass autonomous transaction execution (causing an error), or set it to false to potentially bypass the recursion guard.

The hiding flags (GUC_NOT_IN_SAMPLE | GUC_NO_SHOW_ALL | GUC_NO_RESET_ALL) provide obscurity but not security. Consider documenting this trade-off or adding a check in the autonomous execution path that the flag is only being set from a trusted context (though this may be impractical with dblink).


248-250: LGTM!

Initialization call is correctly placed after other module setup and before marking inited = true. The comment accurately describes the purpose.

src/pl/plisql/src/Makefile (6)

42-42: LGTM!

New object file correctly added to the build in alphabetical order.


56-58: LGTM!

Good addition of EXTRA_INSTALL = contrib/dblink with a clear comment explaining the dependency. This ensures the dblink extension is built and installed for the regression tests.


66-66: LGTM!

Test file added to the regression suite.


95-95: LGTM!

Header installation follows the existing pattern for other public headers (plisql.h, pl_subproc_function.h, pl_package.h).


104-104: LGTM!

Uninstall rule correctly mirrors the install rule.


110-110: LGTM!

Dependency rule correctly updated to include both pl_autonomous.o and pl_autonomous.h, ensuring proper rebuild when the autonomous transaction module changes.

src/pl/plisql/src/pl_exec.c (3)

46-46: Include of pl_autonomous.h is appropriate and scoped correctly

The new include cleanly introduces the autonomous-transaction API (plisql_exec_autonomous_function, plisql_inside_autonomous_transaction) into pl_exec.c without impacting existing dependencies. No issues here.


487-515: Updated plisql_exec_function documentation and prototype look consistent

The expanded comment accurately reflects the current behavior and signature (simple-eval estate, both resource owners, and atomic flag), including autonomous transactions and set-returning results. The added forward declaration for plisql_anonymous_return_out_parameter matches its definition below and is correctly typed.


517-538: Verify all call sites of plisql_exec_function populate procedure_resowner and atomic correctly

The concern about caller consistency is valid. Ensure that every invocation of plisql_exec_function passes appropriate values:

  • Expression/SELECT paths: procedure_resowner = NULL, atomic = true
  • CALL/DO paths: appropriate long-lived procedure_resowner, atomic = false

Inconsistent parameter passing across call sites could lead to subtle lifecycle or atomicity bugs.

src/pl/plisql/src/pl_autonomous.c (7)

34-66: LGTM!

The syscache invalidation callback pattern for caching dblink function OIDs is correctly implemented. The callback properly resets both cached OIDs when pg_proc changes, ensuring stale OIDs are never used.


78-103: LGTM!

Defensive checks for MyProcPort and database_name are appropriate. The function correctly handles edge cases like background workers and standalone mode with clear error messages.


157-179: LGTM!

The validation logic is correct, and deferring the dblink availability check to execution time (rather than procedure creation time) is a sensible design choice that avoids dependency issues during DDL.


186-190: LGTM!

Clean implementation using get_extension_oid with missing_ok=true.


280-300: LGTM on SQL construction with proper quoting.

The use of quote_literal_cstr for non-numeric types and direct formatting for validated numeric types provides adequate SQL injection protection. The session variable settings (ivorysql.compatible_mode, plisql.inside_autonomous_transaction) are correctly embedded.


386-391: Good memory context handling for pass-by-reference return values.

Correctly switches to fn_mcxt before copying the result to ensure it survives after SPI_finish() releases SPI memory.


503-515: Consistent error handling with cleanup across both function and procedure paths.

Both paths correctly use PG_TRY/PG_CATCH to ensure memory cleanup (connstr_buf.data, sql, dbname) before re-throwing. Good defensive programming.

Also applies to: 530-544

Comment thread src/pl/plisql/src/pl_autonomous.c
Comment thread src/pl/plisql/src/pl_autonomous.c
Comment thread src/pl/plisql/src/pl_autonomous.c
Comment on lines +485 to +495
/* Add host if configured */
host_str = GetConfigOption("listen_addresses", false, false);
if (host_str && strcmp(host_str, "*") != 0 && strcmp(host_str, "") != 0)
{
/* Use localhost for local connections */
appendStringInfoString(&connstr_buf, " host=localhost");
}

/* Add port if configured */
if (port_str)
appendStringInfo(&connstr_buf, " port=%s", port_str);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Clarify the listen_addresses check logic—the current analysis contains an error.

The condition if (host_str && strcmp(host_str, "*") != 0 && strcmp(host_str, "") != 0) actually adds host=localhost when listen_addresses is 0.0.0.0, not skips it. However, there is a legitimate concern: if listen_addresses is set to a specific interface (e.g., 192.168.1.1), then localhost (which resolves to 127.0.0.1) may not be reachable over TCP, requiring a Unix socket connection instead.

The current strategy—adding host=localhost only when listen_addresses is explicitly configured—is reasonable for local connections, since Unix sockets are used as a fallback. However, consider documenting this assumption or validating that the heuristic works for your deployment scenarios.

Port validation is unnecessary; PostgreSQL's port GUC is already validated as an integer in the range 1–65535.

@OreoYang
Copy link
Copy Markdown
Collaborator

OreoYang commented Dec 2, 2025

Hi, this autonomous transaction looks complicated. I must admit I don't really understand all the details, especially on the pros & cons of design decision details. So no pressure to merge the PR, just wanted to know if this is actually a workable design.

Hi rophy,

Thanks for your effort and output.
Yes, autonomous transaction is an extremely complex and critical feature.
It's impossible to design and perfect it in a single attempt – consistent discussions and version iterations are always required to refine it over time.

It will take several days to thoroughly study the design and implementation specifics. Please appreciate your patience.

The CodeRabbit agent that your team uses is really nice. I actually tried to let Claude work with CodeRabbit to work out some details in my fork: rophy#7 and rophy#9. I hope the results are OK.

Sounds interesting.
Let's thanks CodeRabbit and Claude first, they help us a lot 😊

@OreoYang
Copy link
Copy Markdown
Collaborator

OreoYang commented Dec 2, 2025

The CodeRabbit agent that your team uses is really nice.

@coderabbitai See here, let's have a poem from you

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Dec 2, 2025

Tip

For best results, initiate chat on the files or code changes.

@OreoYang, here's a poem for you! 🎭

Ode to Autonomous Transactions

Through layers of code, connections unfold,
Where PRAGMA marks transactions bold.
A dblink bridge to sessions new,
Independent commits, clean and true.

From parser's grasp to handler's care,
With cached OIDs and contexts fair.
Each autonomous call takes flight alone,
Its fate sealed in a world its own.

Through rophy's work and thoughtful review,
Where complexity meets purpose true.
With patience, iteration, and time to refine,
Great features emerge, line by line.

So here's to code that dares to be,
Autonomous, yet in harmony.
To teams that build with skill and grace,
Making IvorySQL a better place! 🚀


May your transactions always commit successfully! 😊

@OreoYang OreoYang self-assigned this Dec 2, 2025
rophy added 2 commits December 3, 2025 15:36
Add Oracle-compatible autonomous transactions using dblink to execute
functions/procedures in separate database sessions with independent
transaction control.
- Raise error on concurrent schema drop instead of pg_catalog fallback
- Add spi_connected flag to guard SPI_finish() in catch block
- Add argument count bounds check before accessing proargtypes
- Update expected test output to match actual format
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (2)
src/pl/plisql/src/pl_gram.y (1)

35-35: Grammar hook for PRAGMA AUTONOMOUS_TRANSACTION looks correct

Including pl_autonomous.h, adding K_AUTONOMOUS_TRANSACTION, and routing PRAGMA AUTONOMOUS_TRANSACTION; through plisql_mark_autonomous_transaction(plisql_curr_compile, @1, yyscanner) cleanly enforces that the pragma appears in the declarative section and avoids implicit declarations.

One behavioral detail: K_AUTONOMOUS_TRANSACTION is not part of unreserved_keyword, so autonomous_transaction cannot be used as an identifier (unlike many other pragma-related tokens such as K_EXCEPTION_INIT). If you want it to behave as an unreserved keyword (closer to how other pragma keywords are treated), consider adding it to unreserved_keyword (and unit_name_keyword if appropriate); otherwise this is fine as a deliberate choice.

Also applies to: 315-315, 842-846

src/pl/plisql/src/pl_exec.c (1)

685-711: Autonomous delegation branch: verify call-site assumptions and consider centralizing cleanup

The delegation logic looks sound overall (good use of fn_is_autonomous, plisql_inside_autonomous_transaction, OID match, and symmetric func_end + cleanup), but a few points are worth double‑checking and possibly tightening:

  1. Call-site / OID matching assumption

    The guard:

    fcinfo != NULL &&
    fcinfo->flinfo != NULL &&
    fcinfo->flinfo->fn_oid == func->fn_oid

    assumes that for all execution contexts where fn_is_autonomous should trigger (top-level functions, procedures, package-body routines, nested subprocedures), fcinfo->flinfo->fn_oid is the OID of func->fn_oid. If any path (e.g., some nested subprocedure or package call) uses a different FmgrInfo/OID plumbing, autonomous routines in that context will silently execute non-autonomously.

    Please verify that all intended autonomous entry points reach plisql_exec_function with this equality satisfied, and that internal validation/compilation paths either pass fcinfo == NULL or have a fn_oid mismatch so they correctly skip delegation.

  2. Autonomous recursion guard lifecycle

    The recursion prevention here relies entirely on !plisql_inside_autonomous_transaction. That means the dblink-launched session must set this flag (via the GUC) before its first call into plisql_exec_function for the autonomous routine, or you’ll recursively recurse back into plisql_exec_autonomous_function. Please ensure plisql_autonomous_init() (or equivalent) is invoked early enough in the autonomous backend lifecycle.

  3. Shared cleanup duplication

    The autonomous branch manually duplicates the normal-function exit cleanup:

    if (*plisql_plugin_ptr && (*plisql_plugin_ptr)->func_end)
        ((*plisql_plugin_ptr)->func_end) (&estate, func);
    
    plisql_destroy_econtext(&estate);
    exec_eval_cleanup(&estate);
    error_context_stack = plerrcontext.previous;

    The same sequence (plus additional logic) exists in the non-autonomous path at the end of plisql_exec_function. To avoid drift if more steps are added later (e.g., extra hooks or state), consider factoring this into a small helper (e.g., plisql_function_finish(&estate, func, plerrcontext)) and reusing it from both branches.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 160e8bb and f85ece9.

⛔ Files ignored due to path filters (1)
  • src/pl/plisql/src/expected/plisql_autonomous.out is excluded by !**/*.out
📒 Files selected for processing (9)
  • src/pl/plisql/src/Makefile (4 hunks)
  • src/pl/plisql/src/pl_autonomous.c (1 hunks)
  • src/pl/plisql/src/pl_autonomous.h (1 hunks)
  • src/pl/plisql/src/pl_exec.c (3 hunks)
  • src/pl/plisql/src/pl_gram.y (3 hunks)
  • src/pl/plisql/src/pl_handler.c (6 hunks)
  • src/pl/plisql/src/pl_unreserved_kwlist.h (1 hunks)
  • src/pl/plisql/src/plisql.h (2 hunks)
  • src/pl/plisql/src/sql/plisql_autonomous.sql (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
  • src/pl/plisql/src/pl_handler.c
  • src/pl/plisql/src/pl_autonomous.h
  • src/pl/plisql/src/pl_unreserved_kwlist.h
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-09-02T10:43:25.257Z
Learnt from: syasirs
Repo: IvorySQL/IvorySQL PR: 858
File: src/Makefile.global.in:513-514
Timestamp: 2025-09-02T10:43:25.257Z
Learning: In IvorySQL Makefile.global.in, $(wildcard t/oracle/*.pl) is used instead of t/oracle/*.pl in Oracle TAP test discovery because the t/oracle directory is optional and may not exist in most builds. The wildcard function safely returns empty string when the directory is missing, preventing shell expansion errors.

Applied to files:

  • src/pl/plisql/src/Makefile
🧬 Code graph analysis (2)
src/pl/plisql/src/pl_autonomous.c (8)
src/backend/utils/cache/inval.c (1)
  • CacheRegisterSyscacheCallback (1811-1842)
src/backend/utils/error/elog.c (4)
  • errcode (853-864)
  • errmsg (1070-1086)
  • errdetail (1203-1218)
  • errhint (1317-1332)
src/backend/utils/cache/lsyscache.c (1)
  • get_namespace_name (3848-3865)
src/pl/plisql/src/pl_scanner.c (1)
  • plisql_scanner_errposition (613-627)
src/common/stringinfo.c (4)
  • initStringInfo (96-100)
  • appendStringInfoString (229-233)
  • appendStringInfo (144-166)
  • appendStringInfoChar (241-252)
src/backend/parser/parse_func.c (1)
  • LookupFuncName (2414-2459)
src/backend/executor/spi.c (4)
  • SPI_connect (96-100)
  • SPI_execute (605-636)
  • SPI_getbinval (1278-1292)
  • SPI_finish (186-220)
src/backend/utils/misc/guc.c (1)
  • GetConfigOption (4359-4400)
src/pl/plisql/src/pl_exec.c (1)
src/pl/plisql/src/pl_autonomous.c (1)
  • plisql_exec_autonomous_function (435-567)
⏰ 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: contrib_regression
  • GitHub Check: pg_regression (ubuntu-latest)
  • GitHub Check: build (ubuntu-latest)
  • GitHub Check: oracle_regression (ubuntu-latest)
  • GitHub Check: meson_build (ubuntu-latest)
  • GitHub Check: oracle_pg_regression (ubuntu-latest)
🔇 Additional comments (16)
src/pl/plisql/src/plisql.h (1)

1067-1068: Autonomous flags are well-integrated into core PL/iSQL structures

Adding fn_is_autonomous to PLiSQL_function alongside other per-function flags, and exposing plisql_inside_autonomous_transaction for the GUC-backed execution-context flag, is consistent with the existing design and should default correctly with zeroed allocations. Note that plugins that depend on the exact PLiSQL_function layout will need a rebuild against this updated header, which is expected when introducing a feature of this scope.

If you ship out-of-tree instrumentation plugins, please recompile them against this new header to avoid layout mismatches.

Also applies to: 1280-1281

src/pl/plisql/src/Makefile (1)

42-42: Build/test wiring for autonomous support is consistent; mind the new dblink dependency

  • Adding pl_autonomous.o to OBJS and to the explicit dependency line, and exporting pl_autonomous.h via install/uninstall, correctly integrates the new module into the core PL/iSQL shared library and its public headers.
  • Extending REGRESS with plisql_autonomous ensures the autonomous-transaction test suite runs under oracle-(install)check.
  • EXTRA_INSTALL = contrib/dblink makes dblink a required installed extension for this PL’s tests (and possibly for installs, depending on your top-level rules), which matches the implementation choice but tightens the build-time dependency on contrib. This is fine if dblink is always built in your supported configurations; otherwise, consider documenting this requirement or guarding it with the same conditions under which contrib/dblink is available.

Please verify in your CI/build matrix that environments which skip building contrib still either (a) build/install contrib/dblink or (b) are adjusted to skip autonomous PL/iSQL tests accordingly.

Also applies to: 56-59, 66-67, 95-96, 104-105, 110-111

src/pl/plisql/src/pl_exec.c (1)

46-46: Autonomous header inclusion is appropriate

Including pl_autonomous.h here is the right integration point for the new autonomous-transaction delegation path; no issues spotted.

src/pl/plisql/src/sql/plisql_autonomous.sql (5)

7-15: LGTM: Clean test setup.

The extension installation and table creation are appropriate for the test suite. The tx_state column with a default value helps document the expected state of each test row.


17-131: LGTM: Comprehensive basic test coverage.

Tests 1-6 thoroughly verify core autonomous transaction functionality including parameter handling, transaction isolation, multiple data types, NULL handling, and sequential calls. The isolation test (Test 3) correctly demonstrates that autonomous commits survive outer rollbacks.


236-296: Known limitation properly documented.

The commented tests for explicit COMMIT/ROLLBACK within autonomous blocks correctly document the known limitation referenced in Issue #985. The comments clearly explain the expected failures and the current behavior, which aligns with the PR summary.


314-317: Verify error test execution behavior.

The \set ON_ERROR_STOP off/on directives are commented out, meaning Test 15 will cause the test script to fail and stop execution at the intentional error. If this test is meant to verify graceful error handling without stopping the test suite, uncomment these directives.

Do you want the error handling test to continue execution after the error, or should it stop the test suite? If it should continue, uncomment lines 315 and 317.


437-454: LGTM: Complete cleanup.

All created test objects (8 procedures, 9 functions, and 1 table) are properly dropped in the cleanup section.

src/pl/plisql/src/pl_autonomous.c (8)

34-66: LGTM: Correct OID caching with invalidation.

The OID caching strategy with syscache invalidation callback is correctly implemented. Both dblink_exec_oid and dblink_oid are properly reset when pg_proc changes, ensuring stale function OIDs are not used after extension drop/recreate.


78-103: LGTM: Robust database name retrieval.

The function correctly validates that both MyProcPort and database_name are available before use, with clear error messages explaining the context (background processes vs. missing connection info). The use of pstrdup ensures the returned string is allocated in the caller's memory context.


113-148: LGTM: Secure name construction with proper error handling.

The function correctly constructs a schema-qualified, SQL-injection-safe function name using quote_identifier for both components. The concurrent schema drop scenario is properly handled with a clear error message, as noted in the addressed past review comments.


161-183: LGTM: Proper pragma validation.

The function correctly validates placement and prevents duplicate pragmas. Deferring the dblink availability check to execution time (lines 176-180) is a sound design choice that avoids issues during procedure creation.


190-194: LGTM: Clean extension availability check.

Uses the standard PostgreSQL API appropriately with missing_ok=true to avoid errors when the extension is not installed.


210-316: LGTM: Comprehensive SQL construction with proper quoting.

The function correctly:

  • Validates argument count (addresses past review comment)
  • Handles NULL arguments explicitly
  • Applies type-appropriate formatting (unquoted numerics, boolean conversion, quoted literals)
  • Uses quote_literal_cstr for string safety
  • Sets required session flags (ivorysql.compatible_mode and plisql.inside_autonomous_transaction)
  • Manages memory correctly (returns sql.data for caller to free)

327-420: LGTM: Robust execution with proper error handling.

The function correctly implements autonomous function execution with:

  • OID caching for dblink()
  • Proper SPI connection tracking with spi_connected flag (addresses past review comment)
  • Correct result extraction and NULL handling
  • Memory context switching for pass-by-reference types to ensure data survives SPI_finish()
  • Comprehensive error cleanup in the catch block

435-567: LGTM: Complete autonomous execution orchestration.

The function correctly:

  • Builds libpq-safe connection strings with proper escaping for single quotes and backslashes
  • Branches between function (SELECT via dblink + SPI) and procedure (CALL via dblink_exec) paths
  • Includes comprehensive error handling with cleanup in both paths
  • Returns appropriate values (actual Datum for functions, NULL with isnull=true for procedures)

The listen_addresses logic (lines 497-503) was discussed in past review comments and is reasonable for local connections with Unix socket fallback.

Comment thread src/pl/plisql/src/pl_exec.c
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (1)
src/pl/plisql/src/pl_exec.c (1)

491-515: fcinfo contract is now documented as non-NULL but still not enforced in code

The updated comment correctly reflects the implementation’s assumption that fcinfo “must be non-NULL for executable calls,” but plisql_exec_function still unconditionally dereferences fcinfo (e.g., argument loading and fcinfo->resultinfo/fcinfo->isnull) with no defensive check.

Consider adding an early assertion or explicit error to make the contract fail-fast and self-documenting, e.g.:

Assert(fcinfo != NULL);
/* or */
if (fcinfo == NULL)
    elog(ERROR, "plisql_exec_function called with NULL fcinfo");

This also guards against any future internal caller accidentally passing NULL.

🧹 Nitpick comments (1)
src/pl/plisql/src/sql/plisql_autonomous.sql (1)

298-317: Error-handling test intentionally expects failure. Test 15 (lines 301–317) inserts into a nonexistent table to trigger PG_CATCH cleanup paths (pfree, SPI_finish). The commented-out \set ON_ERROR_STOP off/on (lines 315, 317) is noted but doesn't impede test execution—the error is expected and the test validates graceful failure. For clarity in CI/automation, consider uncommenting these directives so error handling doesn't halt the full suite unexpectedly.

- -- \set ON_ERROR_STOP off
- SELECT test_function_error();
- -- \set ON_ERROR_STOP on
+ \set ON_ERROR_STOP off
+ SELECT test_function_error();
+ \set ON_ERROR_STOP on
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f85ece9 and 72d0173.

⛔ Files ignored due to path filters (1)
  • src/pl/plisql/src/expected/plisql_autonomous.out is excluded by !**/*.out
📒 Files selected for processing (2)
  • src/pl/plisql/src/pl_exec.c (3 hunks)
  • src/pl/plisql/src/sql/plisql_autonomous.sql (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
src/pl/plisql/src/pl_exec.c (1)
src/pl/plisql/src/pl_autonomous.c (1)
  • plisql_exec_autonomous_function (435-567)
⏰ 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: contrib_regression
  • GitHub Check: oracle_pg_regression (ubuntu-latest)
  • GitHub Check: pg_regression (ubuntu-latest)
  • GitHub Check: build (ubuntu-latest)
  • GitHub Check: oracle_regression (ubuntu-latest)
🔇 Additional comments (7)
src/pl/plisql/src/pl_exec.c (2)

46-46: Include of pl_autonomous.h correctly wires in autonomous-transaction support

The new include looks appropriate and localized; no issues from this file’s perspective.


685-711: Autonomous delegation path may bypass local SRF/OUT handling; verify full parity with normal execution

The new autonomous branch delegates cleanly to plisql_exec_autonomous_function and mirrors plugin func_end and econtext cleanup. However, verify the following behavioral guarantees:

  1. Set-returning functions:
    This early return result; bypasses the later estate.retisset / ReturnSetInfo handling in plisql_exec_function. Confirm that plisql_exec_autonomous_function:

    • Fully handles fcinfo->resultinfo when func->fn_retset is true (materializes a tuplestore and fills ReturnSetInfo identically to the normal path), and
    • Returns a Datum/fcinfo->isnull pair in final form for both scalar and SRF use-cases.

    Add tests for RETURNS SETOF autonomous functions to ensure callers see identical behavior to the non-autonomous path.

  2. Anonymous blocks and OUT parameters:
    This path skips the anonymous-block OUT propagation and anonymous_have_outparam logic. If PRAGMA AUTONOMOUS_TRANSACTION is forbidden for anonymous DO blocks, document that restriction; otherwise, implement corresponding OUT-parameter handling in the autonomous path or explicitly reject that combination.

  3. Validation / internal calls:
    The fcinfo->flinfo->fn_oid == func->fn_oid guard prevents delegating validation contexts. Verify that:

    • All real, user-visible autonomous calls satisfy this predicate, including nested subprocedures and package-body routines.
    • All internal plumbing/validator paths never call this with matching OIDs, preventing accidental dblink recursion.

Add a comment in pl_exec.c referencing where these guarantees are upheld (e.g., in pl_autonomous.c or test suite).

src/pl/plisql/src/sql/plisql_autonomous.sql (5)

1-15: Comprehensive autonomous-transaction test suite with clear documentation. Setup is well-structured: dblink installed, test table created with appropriate schema, cleanup deferred to end. Inline comments clearly explain test purpose and expected behavior. Good coverage of parameter types, return types, and error paths.


168-192: Test 9 (OID invalidation) is critical for cache correctness. The test validates that the GUC-backed recursion-prevention mechanism survives extension drop/recreate. The DROP EXTENSION dblink CASCADE (line 185) is intentional here to force OID invalidation and test the syscache invalidation callback. This is a key verification for the feature's robustness, and the test design is sound.


235-296: Commented tests (12–14) appropriately document Issue #985 limitation. Tests for explicit COMMIT/ROLLBACK within autonomous blocks are correctly flagged as unsupported and deferred to future work. Comments clearly explain why (PL/iSQL lacks transaction-control statements), cite the issue, and provide example code for when the limitation is lifted. This is good documentation for future maintainers.


340-367: Test 17 (nested autonomous calls) validates multi-SPI-context correctness. Both inner_function and outer_function declare PRAGMA AUTONOMOUS_TRANSACTION, confirming that nested autonomous execution paths don't corrupt stack or SPI state. The expected return value comment (line 365) correctly traces the calculation. This is an important integration test for the feature.


436-453: Cleanup is complete and correct. All procedures, functions, and the test table are dropped in reverse order of creation. No dangling artifacts left in the test database. Proper tear-down for repeatable test runs.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (3)
src/pl/plisql/src/sql/plisql_autonomous.sql (1)

1-454: Comprehensive test suite with good coverage; verify CI compatibility for error handling.

The test suite covers 20+ scenarios including basic calls, isolation, return types, nested calls, OID invalidation, and error handling. Structure is consistent and intentional design decisions (e.g., Issue #985 references) are well-documented.

Recommendations for robustness:

  1. Test isolation: Confirm the OID invalidation test (line 185–186) runs isolated from other tests and does not cause unexpected cascading drops.
  2. Error handling test (lines 301–317): Verify this pattern works in your automated test harness; if not, consider restructuring to use explicit error-catching logic or document as manual-only.
  3. Consider adding a negative test (e.g., recursive autonomous call without GUC guard, malformed PRAGMA) if the C-level guard is not visible from SQL tests.
src/pl/plisql/src/pl_exec.c (2)

528-535: fcinfo contract + function_from initialization look sound

Asserting fcinfo != NULL up front is consistent with the new API contract and with the function’s pervasive use of fcinfo (e.g., argument loading, resultinfo handling, plisql_function_from(fcinfo)). Computing function_from immediately after is straightforward and keeps the later logic unchanged.

If you ever intend to call plisql_exec_function in non-executable/validation contexts with a NULL fcinfo, consider tightening the contract by emitting a clear ERROR instead of just an Assert so production builds fail fast with a message rather than a null dereference, but this is optional given the current design.


690-716: Autonomous delegation path is well‑guarded; consider minor observability tweak and verify return-type parity

The new delegation to plisql_exec_autonomous_function is carefully gated:

  • Only fires for func->fn_is_autonomous and when not already inside an autonomous transaction (!plisql_inside_autonomous_transaction).
  • Skips internal/validation contexts by requiring a real fcinfo->flinfo and matching fn_oid.
  • Leaves non-autonomous and nested/validation paths on the existing top-level-block execution.

Cleanup and hooks mirror the normal path: func_beg runs before the branch, func_end is called in the autonomous branch, the eval econtext is destroyed, exec_eval_cleanup is called, and error_context_stack is restored before returning.

Two small follow-ups to consider:

  1. Instrumentation visibility: before calling func_end, you might want to populate estate.retval, estate.retisnull, and estate.rettype from result/fcinfo->isnull for scalar-returning functions so instrumentation plugins that inspect estate see a result consistent with non-autonomous execution. This is optional but would make the plugin view more uniform.

  2. Behavioral parity check: because this branch bypasses the local return-casting path (tuple coercion, domain checks, SRF materialization), all of that now relies on plisql_exec_autonomous_function and helpers. Please ensure you have tests confirming that autonomous functions and procedures behave identically to non-autonomous ones across:

    • scalar, composite, and domain return types,
    • set-returning functions (including ReturnSetInfo behavior),
    • procedures with IN/OUT/INOUT parameters.

From this file’s perspective the wiring is correct, but those behaviors are enforced in pl_autonomous.c.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 72d0173 and a580f91.

⛔ Files ignored due to path filters (1)
  • src/pl/plisql/src/expected/plisql_autonomous.out is excluded by !**/*.out
📒 Files selected for processing (2)
  • src/pl/plisql/src/pl_exec.c (4 hunks)
  • src/pl/plisql/src/sql/plisql_autonomous.sql (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
src/pl/plisql/src/pl_exec.c (2)
src/pl/plisql/src/pl_subproc_function.c (1)
  • plisql_function_from (1121-1135)
src/pl/plisql/src/pl_autonomous.c (1)
  • plisql_exec_autonomous_function (435-567)
⏰ 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: oracle_regression (ubuntu-latest)
  • GitHub Check: oracle_pg_regression (ubuntu-latest)
  • GitHub Check: contrib_regression
  • GitHub Check: build (ubuntu-latest)
  • GitHub Check: meson_build (ubuntu-latest)
  • GitHub Check: pg_regression (ubuntu-latest)
🔇 Additional comments (6)
src/pl/plisql/src/sql/plisql_autonomous.sql (4)

1-95: Setup and basic tests look sound.

Extension creation, table setup, and Tests 1–4 follow expected patterns. PRAGMA placement in the DECLARE section aligns with documented syntax.


171-192: OID invalidation test: verify CASCADE scope.

Line 185 uses DROP EXTENSION dblink CASCADE; to test OID invalidation and revalidation. While the extension is recreated at line 186, CASCADE could drop dependent functions or procedures if the test suite runs in a persistent environment. Ensure this test runs in isolation or document expected side effects.


240-296: Commented-out tests appropriately documented.

Tests 12–14 are correctly disabled with clear references to Issue #985 (lack of COMMIT/ROLLBACK support in autonomous blocks). The comments explain the limitation and expected failures well, making it easy to re-enable these tests once the issue is resolved.


437-454: Cleanup is thorough and well-ordered.

All test procedures, functions, and the test table are explicitly dropped. The order (procedures before functions, then table) respects dependency constraints.

src/pl/plisql/src/pl_exec.c (2)

46-46: Include of pl_autonomous.h is appropriate

The new header include is placed consistently with other PL/iSQL-specific headers and is the right way to expose plisql_exec_autonomous_function here. No issues.


491-515: Updated plisql_exec_function API comment now matches implementation

The expanded doc block clearly describes the function’s responsibilities, including autonomous transactions and SRF behavior, and correctly states that fcinfo must be non-NULL for executable calls, which matches all current uses and the unconditional dereferences in the body.

Comment on lines +301 to +317
CREATE OR REPLACE FUNCTION test_function_error() RETURN INT AS
DECLARE
PRAGMA AUTONOMOUS_TRANSACTION;
BEGIN
-- This will fail because nonexistent_table doesn't exist
-- Tests PG_CATCH cleanup (pfree and SPI_finish)
INSERT INTO nonexistent_table VALUES (999);
RETURN 1;
END;
/

COMMIT;

-- This should fail gracefully with proper error message
-- \set ON_ERROR_STOP off
SELECT test_function_error();
-- \set ON_ERROR_STOP on
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

# First, check if the file exists and read the specific lines in question
wc -l src/pl/plisql/src/sql/plisql_autonomous.sql

Repository: IvorySQL/IvorySQL

Length of output: 178


🏁 Script executed:

# Read lines 301-317 to see the actual code
sed -n '301,317p' src/pl/plisql/src/sql/plisql_autonomous.sql

Repository: IvorySQL/IvorySQL

Length of output: 178


🏁 Script executed:

# Get broader context around these lines to understand the test structure
sed -n '295,325p' src/pl/plisql/src/sql/plisql_autonomous.sql

Repository: IvorySQL/IvorySQL

Length of output: 178


🏁 Script executed:

# Search for other tests in the file to understand the test pattern
grep -n "CREATE OR REPLACE FUNCTION test_" src/pl/plisql/src/sql/plisql_autonomous.sql | head -20

Repository: IvorySQL/IvorySQL

Length of output: 178


🏁 Script executed:

# Look for test harness or execution setup files
find . -type f \( -name "*.sh" -o -name "*.py" -o -name "Makefile" -o -name "*.mk" \) | grep -E "(test|harness|run)" | head -20

Repository: IvorySQL/IvorySQL

Length of output: 178


🏁 Script executed:

# Search for \set commands in the sql directory to see if this pattern is used elsewhere
rg "\\\\set" src/pl/plisql/src/sql/ -A 2 -B 2

Repository: IvorySQL/IvorySQL

Length of output: 178


🌐 Web query:

IvorySQL test harness plisql testing automated CI error handling

💡 Result:

Short guide — testing PL/SQL/PL/pgSQL on IvorySQL with an automated CI test harness and robust error handling.

Key tools

  • pgTAP for unit tests of PL/pgSQL / PL/SQL routines (assertions, throws_ok/lives_ok, runtests()). [1]
  • PostgreSQL regression harness (pg_regress / make installcheck) for integration/regression tests shipped with the server. Works for IvorySQL (IvorySQL is a PostgreSQL fork so the same test flow applies). [2][3]
  • Standard CI runners (GitHub Actions / GitLab CI / CircleCI) + Docker to run IvorySQL builds and tests in containers.

Minimal workflow (CI-friendly)

  1. Build or pull an IvorySQL Docker image in CI; start a server (initdb, pg_ctl start). Example: initdb -D /tmp/data && pg_ctl -D /tmp/data -o "-k /tmp" -w start. [2][3]
  2. Install test helpers:
    • Install pgTAP extension into a test DB (CREATE EXTENSION pgtap), or add pgTAP SQL files to your repo. Use pg_prove to run TAP tests. [1]
    • For server-level regression tests, run make installcheck or make installcheck-parallel after installing IvorySQL into the CI image. [2]
  3. Organize tests:
    • Unit tests: write pgTAP tests that call your PL/SQL/PL/pgSQL functions. Use throws_ok() and lives_ok() to assert expected exceptions or successful execution. Use runtests() for xUnit-style test functions. [1]
    • Integration tests: use SQL files and expected output by pg_regress (installcheck) for end-to-end behavior. [2]
  4. CI failure/error handling:
    • Make CI fail on any non‑zero exit from pg_prove, make installcheck, or psql commands. Capture stdout/stderr and test TAP output as artifacts.
    • Use pgTAP assertion functions to trap and assert error conditions (throws_ok for expected errors; lives_ok for code that must not raise). This gives clear TAP diagnostics for exception details. [1]
    • If you run server-level regression tests, collect regression.diffs and server logs (pg_log) on failure. [2]
  5. Recommended CI steps (concise example)
    • Checkout code
    • Build IvorySQL or pull image
    • initdb; pg_ctl start
    • createdb test && psql -d test -c "CREATE EXTENSION pgtap;"
    • pg_prove --dbname=test tests/*.sql || exit 1
    • make installcheck || (cat log/*.log && exit 1)
    • pg_ctl stop
      (Collect TAP output and server logs as CI artifacts.) [1][2][3]

Notes and pointers

  • Use pgTAP’s throws_ok/lives_ok to assert and report exception content and context instead of parsing psql exit codes. That yields better, structured error info in CI. [1]
  • Use installcheck / pg_regress for compatibility/regression tests that exercise server internals; run these in an isolated CI runner because they can create cluster-wide temporary objects. [2]
  • IvorySQL-specific packaging or extensions (e.g., DocumentDB support) should be installed into the test cluster before running tests; treat IvorySQL as PostgreSQL-compatible for testing flows. [3]

Sources

  1. pgTAP documentation (unit testing and throws_ok/lives_ok). [1]
  2. PostgreSQL regression test docs (make installcheck, pg_regress behavior). [2]
  3. IvorySQL docs/articles showing IvorySQL as PostgreSQL-compatible (installation / extensions). [3]

If you want, I can:

  • produce a ready-to-run GitHub Actions workflow (YAML) that builds IvorySQL, runs pgTAP and installcheck, and collects logs; or
  • draft a sample pgTAP test showing throws_ok usage for a PL/SQL function.

🌐 Web query:

IvorySQL plisql autonomous transaction tests

💡 Result:

Here are resources and example test approaches for PL/iSQL / autonomous-transaction behaviour (IvorySQL docs + canonical Oracle semantics + examples from other PostgreSQL-compatible projects that implement autonomous transactions):

  • IvorySQL PL/iSQL docs (PL/iSQL language reference). Use this as your IvorySQL feature/reference starting point. [1]
  • Oracle AUTONOMOUS_TRANSACTION pragma (semantics you should test for: independent transaction, can COMMIT/ROLLBACK inside, parent suspended). [2]
  • Example implementations / test patterns used by other PG-compatible projects:
    • MogDB: example CREATE FUNCTION showing PRAGMA AUTONOMOUS_TRANSACTION and START TRANSACTION / COMMIT usage — good test template. [3]
    • Tantor docs: autonomous-transaction examples (logging, commit inside autonomous block, visibility on rollback). [4]
    • Liberatii Gateway: wrapper/dblink-based emulation and caveats (useful if IvorySQL does not natively support autonomous tx). [5]

Suggested minimal tests (adapt these to IvorySQL PL/iSQL syntax):

  1. Logging commit survives outer rollback

    • Create log table.
    • Create a PL/iSQL function/procedure declared AUTONOMOUS (PRAGMA AUTONOMOUS_TRANSACTION or equivalent) that INSERTs into log and COMMITs.
    • In a separate function start a transaction, call the autonomous logger, then RAISE an error / ROLLBACK the outer transaction. Assert log row persists.
  2. Autonomous COMMIT/ROLLBACK control

    • Autonomous routine performs INSERT then conditionally COMMIT or ROLLBACK; assert visibility according to choice.
  3. Visibility and isolation

    • Ensure autonomous tx does not see uncommitted outer changes; ensure outer does/doesn’t see autonomous commits depending on isolation semantics.
  4. Trigger/call-stack interactions and errors

    • Call autonomous routine from trigger / nested procedures; test deadlock/visibility/error behavior.

Useful links:

  • IvorySQL PL/iSQL docs. [1]
  • Oracle AUTONOMOUS_TRANSACTION reference. [2]
  • MogDB autonomous-transaction example. [3]
  • Tantor autonomous-transaction examples. [4]
  • Liberatii autonomous-transaction emulation notes. [5]

If you want, I can:

  • produce ready-to-run IvorySQL test SQL/PLiSQL scripts for the 4 tests above, or
  • search the IvorySQL GitHub for any existing autonomous-transaction tests and link to them. Which would you prefer?

Error handling test uses commented psql metacommands incompatible with automated regression testing.

Lines 315–317 show \set ON_ERROR_STOP off/on (psql metacommands) commented out above a raw SELECT test_function_error(); call. PostgreSQL regression tests (pg_regress, used by IvorySQL) capture expected output and compare it deterministically; commented metacommands do not suppress errors, so this test will produce uncontrolled error output and cause the regression suite to fail. IvorySQL's recommended approach for error testing is pgTAP's throws_ok() function, which asserts and validates exceptions within the test harness.

Restructure this test to use pgTAP throws_ok() to explicitly validate the error, or if this is a standalone regression test, ensure the error output is captured in an .expected file for comparison.

@OreoYang
Copy link
Copy Markdown
Collaborator

OreoYang commented Dec 5, 2025

I completely agree with your decision to go with Approach 1 (the dblink-based implementation). It requires less effort and is easier to maintain. Although the performance is somewhat lower.

proc_name,
args.data);
}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Given that we consistently set plisql.inside_autonomous_transaction = true, is this GUC truly necessary?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, the GUC is necessary to prevent infinite recursion. When an autonomous function executes via dblink, the GUC is set to true in that session. If the function then calls another autonomous function (or itself), the check at pl_exec.c:696:
if (func->fn_is_autonomous &&
!plisql_inside_autonomous_transaction && // skips dblink if already inside
...)
prevents spawning another dblink connection. Instead, the nested call executes directly within the existing autonomous session.

Without this GUC, nested autonomous calls would create recursive dblink connections.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Clear, thank you for the clarification.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@rophy
I'm trying to test it manually, and see that it requires feature allow COMMIT/ROLLBACK in nested PL/iSQL procedure calls , correct?

 pl_exec.c:696:
if (func->fn_is_autonomous &&
!plisql_inside_autonomous_transaction && // skips dblink if already inside
...)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hmm, I think they're relevant, but different issues?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sure, it's absolutely another feature

Comment thread src/pl/plisql/src/sql/plisql_autonomous.sql
@OreoYang
Copy link
Copy Markdown
Collaborator

OreoYang commented Dec 5, 2025

Considering that both Meson and Make build systems are supported, could you please add 'pl_autonomous.c'to the meson.build file?
And meson test not support now, so following is fine.

--- a/src/pl/plisql/src/meson.build
+++ b/src/pl/plisql/src/meson.build
@@ -10,6 +10,7 @@ plisql_sources = files(
   'pl_subproc_function.c',
   'pl_package.c',
   'pl_exception_type.c',
+  'pl_autonomous.c',
 )

- Add Test 21: autonomous transaction in package procedure
- Add pl_autonomous.c to meson.build for Meson build support
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (1)
src/pl/plisql/src/sql/plisql_autonomous.sql (1)

301-317: Error handling test produces uncontrolled error output; incompatible with regression testing.

Lines 315–317 show \set ON_ERROR_STOP off/on (psql metacommands) commented out above a SELECT test_function_error(); call. In PostgreSQL regression tests (pg_regress, used by IvorySQL), error output must be deterministic and captured in a corresponding .expected file. The commented metacommands do not suppress errors, so this test will produce unhandled failure output and cause the regression suite to fail.

Options:

  1. Create a separate plisql_autonomous.expected file with the expected error output for this test, or
  2. Use pgTAP's throws_ok() function to explicitly validate the exception within a proper test harness, or
  3. Move error handling tests to a separate test file with dedicated error-output handling.

Verify which approach is used by other PL/iSQL tests in the repository (e.g., check src/pl/plisql/src/sql/ for .expected files or pgTAP usage patterns).

🧹 Nitpick comments (2)
src/pl/plisql/src/sql/plisql_autonomous.sql (2)

29-29: Remove unnecessary COMMIT statements after DDL blocks.

Multiple COMMIT statements appear immediately after procedure/function creation and before the next test block (e.g., lines 29, 45, 61). These are not required in SQL scripts; DDL auto-commits and explicit COMMITs between statements are unnecessary. They add noise and clutter the test file.

Remove these standalone COMMIT statements throughout the file. PostgreSQL/IvorySQL will auto-commit DDL without explicit COMMIT directives in batch SQL mode.

Also applies to: 45-45, 61-61, 92-92, 108-108, 124-124, 153-153, 179-179, 209-209, 230-230, 312-312, 334-334, 363-363, 384-384, 404-404, 425-425, 449-449


185-189: Destructive test: dropping and recreating dblink during test execution.

This test (lines 185–189) deliberately drops the dblink extension to verify OID invalidation, then immediately recreates it. While the feature is important to test, dropping a required extension mid-test could corrupt the test environment or cause subsequent tests to fail if execution order changes or if cleanup is interrupted. Consider:

  • Running this test in an isolated transaction or separate test session, or
  • Using a mock/synthetic method to test OID invalidation without destructively dropping the extension, or
  • Documenting this as a known risk and ensuring it runs last in the test sequence.

Document the destructive nature of this test, or isolate it in a separate test file to prevent unintended side effects.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a580f91 and 48c46fc.

⛔ Files ignored due to path filters (1)
  • src/pl/plisql/src/expected/plisql_autonomous.out is excluded by !**/*.out
📒 Files selected for processing (2)
  • src/pl/plisql/src/meson.build (1 hunks)
  • src/pl/plisql/src/sql/plisql_autonomous.sql (1 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-12-03T06:50:27.755Z
Learnt from: rophy
Repo: IvorySQL/IvorySQL PR: 998
File: contrib/ivorysql_ora/src/builtin_packages/dbms_output/dbms_output.c:82-146
Timestamp: 2025-12-03T06:50:27.755Z
Learning: In contrib/ivorysql_ora/src/builtin_packages/dbms_output/dbms_output.c, the behavior where ENABLE() clears the existing buffer (rather than preserving it like Oracle does) is an intentional known difference tracked in issue #26 at https://github.com/rophy/IvorySQL/issues/26 (medium impact, open).

Applied to files:

  • src/pl/plisql/src/sql/plisql_autonomous.sql
⏰ 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: oracle_regression (ubuntu-latest)
  • GitHub Check: pg_regression (ubuntu-latest)
  • GitHub Check: meson_build (ubuntu-latest)
  • GitHub Check: oracle_pg_regression (ubuntu-latest)
  • GitHub Check: contrib_regression
  • GitHub Check: build (ubuntu-latest)
🔇 Additional comments (6)
src/pl/plisql/src/meson.build (1)

13-13: pl_autonomous.c correctly wired into build

Adding 'pl_autonomous.c' to plisql_sources ensures the autonomous-transaction support is compiled into the plisql shared module and aligns with the prior review request to hook this file into Meson. No further Meson changes seem necessary here.

src/pl/plisql/src/sql/plisql_autonomous.sql (5)

435-452: Package procedure test case correctly added.

The package procedure test (lines 435–452) addresses OreoYang's request and properly validates autonomous transaction behavior within package-body routines. The test follows the same pattern as standalone procedures and verifies both the autonomous execution and data persistence.


236-296: Commented-out COMMIT/ROLLBACK tests provide good documentation of known limitations.

Tests 12–14 (lines 236–296) are correctly commented out and reference Issue #985, clearly documenting that explicit COMMIT/ROLLBACK within autonomous blocks is not yet supported in PL/iSQL. The comments explain the expected failure mode and set appropriate expectations for future enhancements.


1-15: Test setup and schema are well structured.

The setup correctly installs dblink, creates a focused test table (autonomous_test) with three columns (id, msg, tx_state), and uses clear comments to separate test sections. This design facilitates debugging and future expansion.


340-367: Nested autonomous function test effectively validates SPI context handling.

The nested autonomous test (inner_function and outer_function, lines 340–367) correctly exercises multiple SPI contexts and verifies that both outer and inner autonomous calls execute and commit independently. The expected result (id=60 outer, id=160 inner, return value 175) demonstrates proper isolation and value propagation across nested calls.


197-197: The syntax is correct for PL/iSQL. IvorySQL's PL/iSQL dialect uses RETURN type (not RETURNS type) to match Oracle PL/SQL compatibility. This is intentional and consistent with the Oracle-compatible design of the test suite.

@rophy
Copy link
Copy Markdown
Contributor Author

rophy commented Dec 5, 2025

Considering that both Meson and Make build systems are supported, could you please add 'pl_autonomous.c'to the meson.build file? And meson test not support now, so following is fine.

--- a/src/pl/plisql/src/meson.build
+++ b/src/pl/plisql/src/meson.build
@@ -10,6 +10,7 @@ plisql_sources = files(
   'pl_subproc_function.c',
   'pl_package.c',
   'pl_exception_type.c',
+  'pl_autonomous.c',
 )

added

@rophy
Copy link
Copy Markdown
Contributor Author

rophy commented Dec 10, 2025

Autonomous Transaction Limitations (dblink-based implementation)

IvorySQL implements PRAGMA AUTONOMOUS_TRANSACTION using dblink, which opens a separate database connection. This causes fundamental limitations.

Limitations

1. Cannot call private package procedures

File: bug_autonomous_tx_private_proc.md

CREATE OR REPLACE PACKAGE BODY my_pkg AS
  PROCEDURE private_helper IS ... END;  --
Not in package spec
  PROCEDURE autonomous_proc IS
    PRAGMA AUTONOMOUS_TRANSACTION;
  BEGIN
    private_helper;  -- ERROR: doesn't exist in package
  END;
END;

Error: "private_helper" doesn't exist in package "my_pkg"
Workaround: Make the procedure public (add to package spec)

2. Cannot access package state

File: bug_autonomous_tx_package_state.md

CREATE OR REPLACE PACKAGE BODY my_pkg AS
  g_counter NUMBER := 0;  -- Package variable
  PROCEDURE log_msg IS
    PRAGMA AUTONOMOUS_TRANSACTION;
  BEGIN
    g_counter := g_counter + 1;  -- Sees 0, not caller's value
    other_pkg_proc();            -- May fail with subtransaction error
  END;
END;

Issues:

  • Package variables reset to defaults (not
    shared)
  • Calling other package procedures may fail
    Workaround: Make autonomous procedures
    self-contained (no package state, no other
    procedure calls)

Root Cause

dblink opens a new database session:

  • New session = fresh package initialization
  • Only public interfaces visible (catalog lookup)
  • No shared memory with caller

What Works

Scenario Status
Standalone autonomous procedures
Public package procedures (self-contained)
Transaction isolation (commit survives rollback)
Private package procedures
Package variables
Calling other package procedures

Note

EDB Postgres Advanced Server has the same limitations despite using background workers instead of dblink - the fundamental issue
is separate sessions.

@rophy
Copy link
Copy Markdown
Contributor Author

rophy commented Dec 10, 2025

As a matter of fact, this dblink-based implementation is somewhat limited in terms of enabling oracle packages to run autonomous transactions.

While it works for simple cases, it failed in enabling my sample oracle package to work, at least not without refactoring.

@OreoYang
Copy link
Copy Markdown
Collaborator

OreoYang commented Dec 12, 2025

As a matter of fact, this dblink-based implementation is somewhat limited in terms of enabling oracle packages to run autonomous transactions.

While it works for simple cases, it failed in enabling my sample oracle package to work, at least not without refactoring.

Thanks for your update, I'll share your design implementation internally next week.
Since teams focused on v5.1 release ....
After shared your design will merge this PR.

@OreoYang
Copy link
Copy Markdown
Collaborator

EDB Postgres Advanced Server has the same limitations despite using background workers instead of dblink - the fundamental issue
is separate sessions.

Yes, I see it in your posted references, aws and ora2pg select the same design.

@gaoxueyu gaoxueyu merged commit 892d5e8 into IvorySQL:master Dec 18, 2025
6 checks passed
@coderabbitai coderabbitai bot mentioned this pull request Dec 29, 2025
4 tasks
@OreoYang
Copy link
Copy Markdown
Collaborator

OreoYang commented Jan 6, 2026

For the record, Tom Lane also recommends using dblink to implement autonomous transactions.

Yeah.  Postgres doesn't have autonomous transactions (not yet anyway),
and you can't fake them like that.



A way that does work, I believe, is to set up a second session with
dblink[1] and use that to issue the autonomous transaction.  Ugly
and inefficient for sure, but if you've gotta have it...



			regards, tom lane

https://www.postgresql.org/message-id/1830668.1742655550%40sss.pgh.pa.us

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.

3 participants