Skip to content

feat: support return values in autonomous transaction functions#9

Merged
rophy merged 2 commits intomasterfrom
feat/auto-tx
Nov 28, 2025
Merged

feat: support return values in autonomous transaction functions#9
rophy merged 2 commits intomasterfrom
feat/auto-tx

Conversation

@rophy
Copy link
Copy Markdown
Owner

@rophy rophy commented Nov 27, 2025

Summary

Adds return value support for autonomous transaction functions, achieving full Oracle compatibility for the PRAGMA AUTONOMOUS_TRANSACTION feature.

What changed:

Implementation Details

Function vs Procedure Detection

/* Determine if this is a function (returns value) or procedure (void) */
bool is_function = (procstruct->prorettype != VOIDOID);

if (is_function)
    /* Use SELECT to capture return value */
    sql = "SELECT function_name(args);";
else
    /* Use CALL for procedures (no return value) */
    sql = "CALL procedure_name(args);";

Return Value Capture

  • Functions execute via SELECT * FROM dblink(...) AS t(result rettype)
  • SPI executes the query and extracts the result
  • Proper memory management with datumCopy() before SPI_finish()
  • Handles NULL returns correctly

Test Coverage

Added 5 new test cases:

Test 10: Function with return value

CREATE OR REPLACE FUNCTION test_auto_return(p_value INT) RETURN INT AS
DECLARE
    PRAGMA AUTONOMOUS_TRANSACTION;
    result INT;
BEGIN
    result := p_value * 2;
    RETURN result;
END;

SELECT test_auto_return(50);  -- Returns: 100

Test 11: Function with NULL return

Test 12-14: Commented test cases for COMMIT/ROLLBACK (references issue IvorySQL#985)

  • Uses Oracle-style syntax (no PostgreSQL dollar quoting)
  • Documents expected behavior vs current limitation

Oracle Compatibility

Feature Oracle Before After
Function return values ❌ NULL ✅ Correct
Procedure execution
COMMIT in autonomous block ❌ (issue IvorySQL#985)

Testing

All 17 regression tests pass:

docker compose exec dev make check
# Test: plisql_autonomous ... ok

Verified against Oracle Database Free 23.26:

  • Oracle returns 84 for input 42 → IvorySQL now matches ✅
  • Oracle syntax compatibility verified (no $ dollar quoting needed)

Known Limitations

COMMIT/ROLLBACK not supported (tracked separately in issue IvorySQL#985)

  • This is a PL/iSQL limitation affecting both autonomous and regular procedures
  • Not specific to autonomous transactions
  • Test cases added as comments for future implementation

Code Changes

Files modified:

  • src/pl/plisql/src/pl_autonomous.c (+217/-39 lines)

    • Added execute_autonomous_function() helper
    • Function detection and SQL generation
    • SPI integration for return values
  • src/pl/plisql/src/sql/plisql_autonomous.sql (+106 lines)

    • Tests 10-14 added
  • src/pl/plisql/src/expected/plisql_autonomous.out (+118 lines)

    • Updated expected output

Total: 402 insertions, 39 deletions

Related Issues

Summary by CodeRabbit

  • New Features
    • Autonomous calls now distinguish functions vs procedures so functions return values (SELECT-style) while procedures use CALL-style execution.
  • Bug Fixes
    • Corrected return/null semantics and strengthened error handling for autonomous executions.
  • Tests
    • Added comprehensive autonomous-function tests covering multiple return types, NULLs, errors, and nested calls.

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

- Detect function vs procedure (VOID vs non-VOID return type)
- Functions use SELECT via dblink() with SPI to capture return value
- Procedures use CALL via dblink_exec() (no return value)
- Add test cases for function return values (Test 10, 11)
- Document COMMIT/ROLLBACK limitation (issue IvorySQL#985)

All 17 tests pass. Matches Oracle behavior for function returns.
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Nov 27, 2025

Walkthrough

Distinguishes autonomous functions from procedures: adds a dblink OID cache and invalidation, changes call generation to use SELECT for functions and CALL for procedures, and adds execute_autonomous_function to run dblink SELECT via SPI and return a copied Datum with proper null semantics.

Changes

Cohort / File(s) Summary
Core Autonomous Execution Logic
src/pl/plisql/src/pl_autonomous.c
Adds static dblink_oid cache and #include "utils/datum.h"; extends catalog invalidation to reset both dblink_exec_oid and dblink_oid; changes build_autonomous_call signature to output is_function and generate SELECT for functions vs CALL for procedures; updates plisql_exec_autonomous_function to detect return type and route either to new static Datum execute_autonomous_function(char *connstr, char *sql, Oid rettype, FunctionCallInfo fcinfo) (opens SPI, runs dblink SELECT, copies result Datum, preserves fcinfo->isnull with PG_TRY/PG_CATCH) or to existing dblink_exec path for procedures; adjusts memory cleanup and return semantics accordingly.
Autonomous Function Tests
src/pl/plisql/src/sql/plisql_autonomous.sql
Adds tests and function definitions for multiple autonomous-function cases (e.g., test_function_return(INT) RETURNS INT, test_function_null() RETURNS TEXT, test_function_text(TEXT), nested inner_function/outer_function, numeric/date/boolean variants), each using PRAGMA AUTONOMOUS_TRANSACTION, INSERT side-effects, RETURN values, verification SELECTs, and DROP FUNCTION cleanup.

Sequence Diagram

sequenceDiagram
    participant Client
    participant PLiSQL as "PL/pgSQL Runtime"
    participant Cache
    participant dblink
    participant SPI

    Client->>PLiSQL: invoke autonomous call
    PLiSQL->>Cache: check `dblink_oid` / `dblink_exec_oid`
    alt cache miss
        PLiSQL->>dblink: lookup dblink OIDs
        dblink-->>PLiSQL: return OIDs
        PLiSQL->>Cache: update cache
    end

    alt detected as function
        PLiSQL->>SPI: open SPI session
        PLiSQL->>dblink: run dblink SELECT (SQL)
        dblink-->>SPI: return result row
        SPI-->>PLiSQL: return Datum (copied)
        PLiSQL-->>Client: return Datum (fcinfo->isnull = false/true accordingly)
    else detected as procedure
        PLiSQL->>dblink: call dblink_exec with CALL statement
        dblink-->>PLiSQL: execution status
        PLiSQL-->>Client: return NULL (fcinfo->isnull = true)
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

  • Inspect execute_autonomous_function for SPI lifecycle, PG_TRY/PG_CATCH correctness, and Datum memory ownership/copying.
  • Verify build_autonomous_call correctly classifies functions vs procedures across return-type edge cases (void, polymorphic, composite).
  • Check invalidation callback and dblink_oid/dblink_exec_oid cache coherence across catalog changes.
  • Validate tests in plisql_autonomous.sql cover return, NULL, error, and nested-call paths and clean up properly.

Possibly related PRs

Poem

🐰 I nibble at code and hop through the night,
SELECTs for functions, CALLs set aright,
I stash OIDs in burrows, then fetch with a cheer,
Datums copied gently, commits held dear,
A rabbit's small rhyme for autonomous flight!

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 clearly and concisely summarizes the main change: adding support for return values in autonomous transaction functions, which is the core feature implemented across both the C module and test files.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/auto-tx

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

Copy link
Copy Markdown

@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: 2

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

431-440: Consider avoiding duplicate syscache lookup.

The function looks up proctup here to get rettype, but build_autonomous_call() performs the same lookup again internally (lines 222-225). This is a minor inefficiency.

You could refactor to pass rettype to build_autonomous_call() or have it return both is_function and rettype:

-static char *
-build_autonomous_call(PLiSQL_function *func, FunctionCallInfo fcinfo, bool *is_function)
+static char *
+build_autonomous_call(PLiSQL_function *func, FunctionCallInfo fcinfo, bool *is_function, Oid *rettype)

This would eliminate the duplicate syscache lookup in the caller.

📜 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 33bab6f and 3ebc4e8.

⛔ 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_autonomous.c (9 hunks)
  • src/pl/plisql/src/sql/plisql_autonomous.sql (2 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
src/pl/plisql/src/pl_autonomous.c (5)
src/include/postgres.h (1)
  • ObjectIdGetDatum (256-260)
src/include/access/htup_details.h (1)
  • GETSTRUCT (751-755)
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/cache/lsyscache.c (1)
  • get_typlenbyval (2668-2681)
🔇 Additional comments (8)
src/pl/plisql/src/sql/plisql_autonomous.sql (3)

197-207: LGTM! Good test coverage for autonomous function return values.

The test correctly validates that:

  1. The function returns the computed value (doubled input)
  2. The side effect (INSERT) persists in the autonomous transaction

Note: The PRAGMA placement inside DECLARE block differs from procedure tests (where it's directly in the procedure body). Both placements are valid in PL/iSQL, but consistency across tests would improve readability.


220-233: Good coverage for NULL return handling.

This test ensures that NULL returns from autonomous functions are handled correctly, which is an important edge case for the new execute_autonomous_function() implementation.


235-296: Appreciated: Documented known limitations with issue references.

The commented-out tests for COMMIT/ROLLBACK inside autonomous blocks are valuable documentation. They clearly reference issue IvorySQL#985 and explain the expected behavior vs. actual limitation.

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

33-52: LGTM! Cache invalidation properly handles both OIDs.

The invalidation callback correctly resets both dblink_exec_oid and dblink_oid when pg_proc changes, ensuring stale OIDs won't be used after extension drop/recreate.


227-228: LGTM! Correct detection of function vs procedure.

Using prorettype != VOIDOID is the standard PostgreSQL pattern to distinguish functions that return values from procedures.


367-374: LGTM! Correct datum handling before SPI cleanup.

The code correctly:

  1. Retrieves type length/byval info
  2. Copies pass-by-reference datums before SPI_finish() frees SPI memory
  3. Handles NULL returns without unnecessary copying

This ensures the returned datum remains valid after SPI context is destroyed.


498-520: LGTM! Procedure path has proper error handling.

The PG_TRY/PG_CATCH block correctly handles cleanup on error. The allocated datums (connstr_datum, sql_datum) will be freed when the memory context resets, which is acceptable for this short-lived operation.


470-494: LGTM! Function execution path properly handles cleanup.

The PG_TRY/PG_CATCH ensures resources are freed on both success and error paths. The result is already safely copied inside execute_autonomous_function() before SPI cleanup.

Comment thread src/pl/plisql/src/pl_autonomous.c
Comment thread src/pl/plisql/src/pl_autonomous.c Outdated
Copy link
Copy Markdown

@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

📜 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 3ebc4e8 and a591a48.

📒 Files selected for processing (1)
  • src/pl/plisql/src/pl_autonomous.c (10 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
src/pl/plisql/src/pl_autonomous.c (4)
src/common/stringinfo.c (1)
  • appendStringInfo (144-166)
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/cache/lsyscache.c (1)
  • get_typlenbyval (2668-2681)
🔇 Additional comments (8)
src/pl/plisql/src/pl_autonomous.c (8)

25-25: LGTM!

The addition of utils/datum.h for datumCopy support and the new dblink_oid cache variable follow the established pattern for dblink_exec_oid.

Also applies to: 35-35


50-52: LGTM!

Correctly invalidates both dblink_exec_oid and dblink_oid when the catalog changes, ensuring no stale function references.


207-230: LGTM!

The function/procedure detection logic using prorettype != VOIDOID is correct and follows PostgreSQL conventions. The output parameter is_function properly communicates the result to the caller.


280-300: LGTM!

The SQL generation correctly uses SELECT for functions to capture return values and CALL for procedures. The session settings are properly included in both paths.


340-344: LGTM!

The query construction correctly uses quote_literal_cstr for string arguments and format_type_be for the return type. The dblink() call with typed output columns AS t(result rettype) follows the proper syntax for returning SETOF record.


371-382: LGTM!

The result extraction and type handling is correct. datumCopy is appropriately called only for non-byval types before SPI_finish() frees the SPI memory context. Byval types are safely returned as-is since they're values, not pointers.


451-545: LGTM!

The dual execution paths correctly handle functions (using execute_autonomous_function to capture return values) and procedures (using dblink_exec with no return). Both paths have proper error handling with PG_TRY/PG_CATCH blocks and consistent resource cleanup.


328-338: LGTM!

The dblink() function lookup correctly specifies the two-argument signature (text, text) and provides a helpful error message with installation hint if the function is not found.

Comment on lines +347 to +393
PG_TRY();
{
/* Connect to SPI */
ret = SPI_connect();
if (ret < 0)
ereport(ERROR,
(errcode(ERRCODE_INTERNAL_ERROR),
errmsg("could not connect to SPI for autonomous function execution"),
errdetail("SPI_connect returned %d", ret)));

/* Execute the query */
ret = SPI_execute(query, true, 1);
if (ret != SPI_OK_SELECT)
ereport(ERROR,
(errcode(ERRCODE_INTERNAL_ERROR),
errmsg("autonomous function execution failed"),
errdetail("SPI_execute returned %d", ret)));

if (SPI_processed != 1)
ereport(ERROR,
(errcode(ERRCODE_INTERNAL_ERROR),
errmsg("autonomous function returned unexpected number of rows: %lu",
(unsigned long) SPI_processed)));

/* Extract return value from result */
result = SPI_getbinval(SPI_tuptable->vals[0],
SPI_tuptable->tupdesc,
1,
&isnull);

/* Get type information for datumCopy */
get_typlenbyval(rettype, &typlen, &typbyval);

/* Copy result to upper context before SPI_finish frees SPI memory */
if (!isnull && !typbyval)
result = datumCopy(result, typbyval, typlen);

SPI_finish();
}
PG_CATCH();
{
/* Clean up on error */
SPI_finish();
pfree(query);
PG_RE_THROW();
}
PG_END_TRY();
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

SPI_finish called when SPI not connected.

In the PG_CATCH block (line 389), SPI_finish() is called unconditionally, even if SPI_connect() failed at line 350. While SPI_finish() returns an error code rather than crashing when called without an active connection, this is still incorrect practice.

Consider tracking the connection state or restructuring with a flag:

 	/* Execute via SPI with proper error handling */
+	bool spi_connected = false;
+
 	PG_TRY();
 	{
 		/* Connect to SPI */
 		ret = SPI_connect();
 		if (ret < 0)
 			ereport(ERROR,
 					(errcode(ERRCODE_INTERNAL_ERROR),
 					 errmsg("could not connect to SPI for autonomous function execution"),
 					 errdetail("SPI_connect returned %d", ret)));
+		spi_connected = true;
 
 		/* Execute the query */
 		ret = SPI_execute(query, true, 1);
 		if (ret != SPI_OK_SELECT)
 			ereport(ERROR,
 					(errcode(ERRCODE_INTERNAL_ERROR),
 					 errmsg("autonomous function execution failed"),
 					 errdetail("SPI_execute returned %d", ret)));
 
 		if (SPI_processed != 1)
 			ereport(ERROR,
 					(errcode(ERRCODE_INTERNAL_ERROR),
 					 errmsg("autonomous function returned unexpected number of rows: %lu",
 							(unsigned long) SPI_processed)));
 
 		/* Extract return value from result */
 		result = SPI_getbinval(SPI_tuptable->vals[0],
 							  SPI_tuptable->tupdesc,
 							  1,
 							  &isnull);
 
 		/* Get type information for datumCopy */
 		get_typlenbyval(rettype, &typlen, &typbyval);
 
 		/* Copy result to upper context before SPI_finish frees SPI memory */
 		if (!isnull && !typbyval)
 			result = datumCopy(result, typbyval, typlen);
 
 		SPI_finish();
+		spi_connected = false;
 	}
 	PG_CATCH();
 	{
 		/* Clean up on error */
-		SPI_finish();
+		if (spi_connected)
+			SPI_finish();
 		pfree(query);
 		PG_RE_THROW();
 	}
 	PG_END_TRY();
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
PG_TRY();
{
/* Connect to SPI */
ret = SPI_connect();
if (ret < 0)
ereport(ERROR,
(errcode(ERRCODE_INTERNAL_ERROR),
errmsg("could not connect to SPI for autonomous function execution"),
errdetail("SPI_connect returned %d", ret)));
/* Execute the query */
ret = SPI_execute(query, true, 1);
if (ret != SPI_OK_SELECT)
ereport(ERROR,
(errcode(ERRCODE_INTERNAL_ERROR),
errmsg("autonomous function execution failed"),
errdetail("SPI_execute returned %d", ret)));
if (SPI_processed != 1)
ereport(ERROR,
(errcode(ERRCODE_INTERNAL_ERROR),
errmsg("autonomous function returned unexpected number of rows: %lu",
(unsigned long) SPI_processed)));
/* Extract return value from result */
result = SPI_getbinval(SPI_tuptable->vals[0],
SPI_tuptable->tupdesc,
1,
&isnull);
/* Get type information for datumCopy */
get_typlenbyval(rettype, &typlen, &typbyval);
/* Copy result to upper context before SPI_finish frees SPI memory */
if (!isnull && !typbyval)
result = datumCopy(result, typbyval, typlen);
SPI_finish();
}
PG_CATCH();
{
/* Clean up on error */
SPI_finish();
pfree(query);
PG_RE_THROW();
}
PG_END_TRY();
bool spi_connected = false;
PG_TRY();
{
/* Connect to SPI */
ret = SPI_connect();
if (ret < 0)
ereport(ERROR,
(errcode(ERRCODE_INTERNAL_ERROR),
errmsg("could not connect to SPI for autonomous function execution"),
errdetail("SPI_connect returned %d", ret)));
spi_connected = true;
/* Execute the query */
ret = SPI_execute(query, true, 1);
if (ret != SPI_OK_SELECT)
ereport(ERROR,
(errcode(ERRCODE_INTERNAL_ERROR),
errmsg("autonomous function execution failed"),
errdetail("SPI_execute returned %d", ret)));
if (SPI_processed != 1)
ereport(ERROR,
(errcode(ERRCODE_INTERNAL_ERROR),
errmsg("autonomous function returned unexpected number of rows: %lu",
(unsigned long) SPI_processed)));
/* Extract return value from result */
result = SPI_getbinval(SPI_tuptable->vals[0],
SPI_tuptable->tupdesc,
1,
&isnull);
/* Get type information for datumCopy */
get_typlenbyval(rettype, &typlen, &typbyval);
/* Copy result to upper context before SPI_finish frees SPI memory */
if (!isnull && !typbyval)
result = datumCopy(result, typbyval, typlen);
SPI_finish();
spi_connected = false;
}
PG_CATCH();
{
/* Clean up on error */
if (spi_connected)
SPI_finish();
pfree(query);
PG_RE_THROW();
}
PG_END_TRY();
🤖 Prompt for AI Agents
In src/pl/plisql/src/pl_autonomous.c around lines 347 to 393, the PG_CATCH block
unconditionally calls SPI_finish even if SPI_connect failed; add a local boolean
(e.g., bool spi_connected = false) initialized false, set it true immediately
after successful SPI_connect, and replace unconditional SPI_finish() calls with
conditional calls (if (spi_connected) SPI_finish()); ensure both normal and
error paths respect this flag so SPI_finish is only invoked when a connection
was actually established.

- Check SPI_connect() return value before proceeding
- Wrap SPI operations in PG_TRY/PG_CATCH to prevent memory leaks
- Add utils/datum.h include for datumCopy() function
- Fix memory context for pass-by-reference types (TEXT, NUMERIC)
- Switch to function's memory context before datumCopy()

Test coverage additions:
- Test 15: Error handling (PG_CATCH cleanup verification)
- Test 16: TEXT return (pass-by-reference, found and fixed bug)
- Test 17: Nested autonomous calls (multiple SPI contexts)
- Test 18: NUMERIC return (variable-length pass-by-reference)
- Test 19: DATE return (pass-by-value temporal type)
- Test 20: BOOLEAN return (1-byte pass-by-value type)

All 20 tests pass.
Copy link
Copy Markdown

@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

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

395-401: SPI_finish called when SPI not connected.

The PG_CATCH block (line 398) calls SPI_finish() unconditionally, even if SPI_connect() failed at line 351. While SPI_finish() returns an error code rather than crashing when called without an active connection, this is still incorrect practice.

The previous review suggested tracking the connection state with a boolean flag, and this was marked as addressed in commit a591a48, but the current code does not implement the flag.

Consider tracking the connection state:

 	/* Execute via SPI with proper error handling */
+	bool spi_connected = false;
+
 	PG_TRY();
 	{
 		/* Connect to SPI */
 		ret = SPI_connect();
 		if (ret < 0)
 			ereport(ERROR,
 					(errcode(ERRCODE_INTERNAL_ERROR),
 					 errmsg("could not connect to SPI for autonomous function execution"),
 					 errdetail("SPI_connect returned %d", ret)));
+		spi_connected = true;
 
 		/* Execute the query */
 		ret = SPI_execute(query, true, 1);
 		// ... rest of try block ...
 
 		SPI_finish();
+		spi_connected = false;
 	}
 	PG_CATCH();
 	{
 		/* Clean up on error */
-		SPI_finish();
+		if (spi_connected)
+			SPI_finish();
 		pfree(query);
 		PG_RE_THROW();
 	}
 	PG_END_TRY();
🧹 Nitpick comments (1)
src/pl/plisql/src/pl_autonomous.c (1)

460-466: LGTM: Proper function/procedure branching with resource cleanup.

The code correctly routes functions through execute_autonomous_function (returning a value) and procedures through dblink_exec (returning NULL). Both paths have proper PG_TRY/CATCH blocks ensuring cleanup of allocated resources.

Minor optimization opportunity: rettype is looked up twice (here at line 461 and inside build_autonomous_call at line 223). This could be consolidated by extending build_autonomous_call to return rettype, avoiding the second syscache lookup. However, this is a performance optimization, not a correctness issue.

Also applies to: 500-554

📜 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 a591a48 and 7fc9939.

⛔ 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_autonomous.c (10 hunks)
  • src/pl/plisql/src/sql/plisql_autonomous.sql (2 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
src/pl/plisql/src/pl_autonomous.c (4)
src/backend/utils/cache/syscache.c (2)
  • SearchSysCache1 (220-229)
  • ReleaseSysCache (268-272)
src/include/postgres.h (1)
  • ObjectIdGetDatum (256-260)
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)
🔇 Additional comments (7)
src/pl/plisql/src/sql/plisql_autonomous.sql (3)

194-429: Verify test count aligns with PR objectives.

PR objectives state "five new tests added," but the code implements 11 new autonomous-function tests (Tests 10–20 at lines 194–429), including 3 commented-out tests referencing issue IvorySQL#985. Confirm whether this count discrepancy is intentional or a documentation issue.


343-360: Nested autonomous function test is well-structured.

Test 17 correctly validates multiple SPI contexts: outer_function calls inner_function, both insert autonomously, and return values are chained correctly (160 + 10 + 5 = 175). The test includes verification queries for both inserts.


220-227: NULL and typed return values are comprehensive.

Tests 11 and 16–20 properly exercise NULL handling and various return types (TEXT, NUMERIC, DATE, BOOLEAN), covering both pass-by-value and pass-by-reference semantics. Verification queries confirm both the returned values and side effects (inserts).

Also applies to: 323-331, 373-381, 393-401, 414-422

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

25-25: LGTM: Include and global variable for dblink() caching.

The new include and cached OID follow the existing pattern for dblink_exec_oid and are correctly invalidated together.

Also applies to: 35-35


50-52: LGTM: Invalidation callback updated for both cached OIDs.

Correctly resets both dblink_exec_oid and dblink_oid when pg_proc changes.


207-207: LGTM: Function vs procedure detection and SQL generation.

The distinction based on prorettype != VOIDOID correctly identifies functions (SELECT) vs procedures (CALL), enabling return value capture for functions.

Also applies to: 228-229, 280-300


386-391: LGTM: Correct memory context handling for pass-by-reference types.

The code correctly switches to the function's memory context (fn_mcxt) before copying pass-by-reference return values, ensuring the data survives SPI_finish(). Pass-by-value types are correctly skipped.

Comment thread src/pl/plisql/src/sql/plisql_autonomous.sql
@rophy rophy merged commit d8e55f1 into master Nov 28, 2025
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant