Add PLiSQL per-package and reset-all API#1375
Conversation
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughAdds package reset support in PL/iSQL, exposes it through DBMS_SESSION and a test extension, and adds regression coverage for single-package, bulk, NULL, and default-restoration behavior. ChangesPackage reset implementation and tests
Estimated code review effort: 4 (Complex) | ~60 minutes Sequence Diagram(s)sequenceDiagram
participant test_package_reset.reset_package_by_oid as SQL
participant ResetPackageContext as Cache API
participant plisql_internal_funcs as PL/iSQL dispatch
participant plisql_package_reset_context as Reset implementation
SQL->>Cache API: ResetPackageContext(pkg_oid)
Cache API->>PL/iSQL dispatch: package_reset_context(pkg_oid)
PL/iSQL dispatch->>Reset implementation: dispatch
Reset implementation->>Reset implementation: clear package datums
Reset implementation-->>PL/iSQL dispatch: bool result
PL/iSQL dispatch-->>Cache API: bool result
Cache API-->>SQL: bool result
Suggested labels: Suggested reviewers: 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 5
🧹 Nitpick comments (2)
src/oracle_test/modules/test_package_reset/expected/test_reset_defaults.out (1)
84-108: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winAdd a dynamic-default or init-block assertion here.
This expected output only proves that literal defaults and
NULLcome back after reset. It would still pass if the implementation restored a saved snapshot instead of re-running default expressions / the init block, which is the behavior this PR is explicitly trying to preserve. Adding a value derived from a changing expression or an init-block side effect would close that gap. As per path instructions, "**/expected/*.out: These are expected test outputs from pg_regress. Review for: ... Edge case coverage ... Consistency with corresponding .sql test files`."🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/oracle_test/modules/test_package_reset/expected/test_reset_defaults.out` around lines 84 - 108, Add an assertion in the pkg_defaults_test reset expectations that exercises a dynamic default or init block, not just literal defaults and NULL, so the pg_regress output verifies re-execution after reset. Update the corresponding expected output for test_reset_defaults to include a value from a changing expression or init-block side effect, using the existing test_package_reset.reset_all_packages and pkg_defaults_test getters as anchors to keep coverage aligned with the .sql test.Source: Path instructions
src/oracle_test/modules/test_package_reset/sql/test_reset_defaults.sql (1)
5-15: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick winAdd a dynamic-default / init-block assertion here.
This only proves literal defaults are restored. The PR contract also says reset must re-evaluate dynamic defaults and rerun the package init block lazily, but neither behavior is exercised here, so a cached-default or skipped-init regression would still pass. As per path instructions,
**/sql/*.sqlshould ensure comprehensive coverage of features.Also applies to: 61-67
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/oracle_test/modules/test_package_reset/sql/test_reset_defaults.sql` around lines 5 - 15, Add coverage in the pkg_defaults_test setup to assert both a dynamic default and the package init block are re-evaluated after reset, not just literal defaults. Update the SQL test around pkg_defaults_test by introducing a dynamic-default-backed symbol and an initialization side effect, then verify via the existing reset flow that the dynamic value changes and the init block runs lazily again on first access after reset. Use the pkg_defaults_test package and its reset-defaults test case to locate the assertions and extend them rather than creating a separate test.Source: Path instructions
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/backend/utils/cache/packagecache.c`:
- Around line 310-312: The package cache entrypoint is checking
plisql_internal_funcs.isload before the rendezvous table has been initialized,
which can leave it reading stale zero-initialized state in a fresh backend.
Update the package-cache path that performs this early return so it first calls
plisql_internel_funcs_init() before accessing plisql_internal_funcs.isload,
mirroring the initialization order used by ResetPackageContext(). Keep the
existing count behavior, but ensure the load-state check only happens after the
rendezvous lookup is ready.
- Around line 265-269: Guard the callback invocation in ResetPackageContext by
checking whether plisql_internal_funcs.package_reset_context is set before
calling it, since PL/iSQL may not be loaded in the same way
ResetAllPackagesContext already handles. If the callback is NULL, return the
“not loaded” path instead of dereferencing it; otherwise, call the existing
package_reset_context handler after plisql_internel_funcs_init().
In `@src/oracle_test/modules/test_package_reset/expected/test_reset_all.out`:
- Around line 58-63: Update the expected output for the first bulk reset in the
test scenario so it matches the actual behavior of ResetAllPackagesContext after
the CREATE PACKAGE and CREATE PACKAGE BODY statements have loaded PL/iSQL. The
reset_all_packages result should be 0 instead of -1, since the “PL/iSQL not
loaded” path is no longer applicable here; adjust the test_package_reset
expected .out fixture accordingly and keep the surrounding SQL output unchanged.
In `@src/oracle_test/modules/test_package_reset/README`:
- Around line 15-18: The documented no-op behavior for reset_package_by_oid is
inconsistent across the package-reset docs: this README says it returns true
when nothing needed resetting, while test_package_reset--1.0.sql and the
bulk-reset wording imply a false/no-op or “actually reset” contract. Update the
README and the related SQL docs for
reset_package_by_oid/reset_package_by_oid_bulk to agree on one return-value
convention, and keep the wording consistent around the status-already-initial
case and what counts toward bulk reset totals.
In `@src/oracle_test/modules/test_package_reset/test_package_reset--1.0.sql`:
- Around line 18-19: The SQL comment for test_package_reset.reset_all_packages()
describes the wrong failure sentinel; update it to match the behavior in
ResetAllPackagesContext(), which returns -1 only when PL/iSQL is not loaded,
while an empty package cache should return 0. Keep the comment aligned with the
function’s actual contract by revising the “-1” wording so it no longer refers
to an uninitialized cache.
---
Nitpick comments:
In `@src/oracle_test/modules/test_package_reset/expected/test_reset_defaults.out`:
- Around line 84-108: Add an assertion in the pkg_defaults_test reset
expectations that exercises a dynamic default or init block, not just literal
defaults and NULL, so the pg_regress output verifies re-execution after reset.
Update the corresponding expected output for test_reset_defaults to include a
value from a changing expression or init-block side effect, using the existing
test_package_reset.reset_all_packages and pkg_defaults_test getters as anchors
to keep coverage aligned with the .sql test.
In `@src/oracle_test/modules/test_package_reset/sql/test_reset_defaults.sql`:
- Around line 5-15: Add coverage in the pkg_defaults_test setup to assert both a
dynamic default and the package init block are re-evaluated after reset, not
just literal defaults. Update the SQL test around pkg_defaults_test by
introducing a dynamic-default-backed symbol and an initialization side effect,
then verify via the existing reset flow that the dynamic value changes and the
init block runs lazily again on first access after reset. Use the
pkg_defaults_test package and its reset-defaults test case to locate the
assertions and extend them rather than creating a separate test.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: e209aae6-3ef9-4ae2-9ff4-0e3633e0e0c2
📒 Files selected for processing (20)
src/backend/utils/cache/packagecache.csrc/include/funcapi.hsrc/include/utils/packagecache.hsrc/oracle_test/modules/test_package_reset/.gitignoresrc/oracle_test/modules/test_package_reset/Makefilesrc/oracle_test/modules/test_package_reset/READMEsrc/oracle_test/modules/test_package_reset/expected/test_package_reset.outsrc/oracle_test/modules/test_package_reset/expected/test_reset_all.outsrc/oracle_test/modules/test_package_reset/expected/test_reset_defaults.outsrc/oracle_test/modules/test_package_reset/expected/test_reset_one_oid.outsrc/oracle_test/modules/test_package_reset/sql/test_package_reset.sqlsrc/oracle_test/modules/test_package_reset/sql/test_reset_all.sqlsrc/oracle_test/modules/test_package_reset/sql/test_reset_defaults.sqlsrc/oracle_test/modules/test_package_reset/sql/test_reset_one_oid.sqlsrc/oracle_test/modules/test_package_reset/test_package_reset--1.0.sqlsrc/oracle_test/modules/test_package_reset/test_package_reset.csrc/oracle_test/modules/test_package_reset/test_package_reset.controlsrc/pl/plisql/src/pl_package.csrc/pl/plisql/src/pl_package.hsrc/pl/plisql/src/pl_subproc_function.c
|
@hs-liuxh will follow up on this. |
1169798 to
76b93b2
Compare
|
Implements #1376 |
|
Hi @db-nikhil, thanks for this work — the backend de-instantiation logic is a solid foundation. I'll review the code as soon as possible. For now, I have two initial suggestions:
|
|
Hi @sixhui
Yeah, that was the intention anyways :-), I have added this implementation in this PR now.
Good idea, I have registered it via meson.build changes in this PR as well now. |
76b93b2 to
afc9414
Compare
There was a problem hiding this comment.
🧹 Nitpick comments (1)
contrib/ivorysql_ora/sql/dbms_session.sql (1)
118-153: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick winAdd coverage that RESET_PACKAGE leaves other session state untouched.
This section validates package-state reset, but doesn't verify
dbms_session.reset_package()is scoped to packages only. Per PR objectives, the reset must not "impact other sessions or non-package session state." Consider setting aDBMS_SESSIONapplication-context value before callingreset_package()and asserting it's still readable afterward.💡 Suggested addition
+-- Verify RESET_PACKAGE does not disturb unrelated session state (application context) +call dbms_session.set_context('rp_isolation', 'k', 'v'); +call dbms_session.reset_package(); +select sys_context('rp_isolation', 'k') as context_survives_reset; + DROP PACKAGE rp_pkg;🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@contrib/ivorysql_ora/sql/dbms_session.sql` around lines 118 - 153, The RESET_PACKAGE smoke test in dbms_session.sql only verifies package state, but it should also prove non-package session state is preserved. Extend the existing reset_package scenario by using dbms_session to set a session/application-context value before the call and asserting it remains readable afterward, while keeping the current rp_pkg package-state checks intact.Source: Path instructions
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Nitpick comments:
In `@contrib/ivorysql_ora/sql/dbms_session.sql`:
- Around line 118-153: The RESET_PACKAGE smoke test in dbms_session.sql only
verifies package state, but it should also prove non-package session state is
preserved. Extend the existing reset_package scenario by using dbms_session to
set a session/application-context value before the call and asserting it remains
readable afterward, while keeping the current rp_pkg package-state checks
intact.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 00ebefd0-471b-4d9f-a7a5-09a9175c1dad
📒 Files selected for processing (27)
contrib/ivorysql_ora/expected/dbms_session.outcontrib/ivorysql_ora/sql/dbms_session.sqlcontrib/ivorysql_ora/src/builtin_packages/dbms_session/dbms_session--1.0.sqlcontrib/ivorysql_ora/src/builtin_packages/dbms_session/dbms_session.csrc/backend/utils/cache/packagecache.csrc/include/funcapi.hsrc/include/utils/packagecache.hsrc/oracle_test/modules/Makefilesrc/oracle_test/modules/meson.buildsrc/oracle_test/modules/test_package_reset/.gitignoresrc/oracle_test/modules/test_package_reset/Makefilesrc/oracle_test/modules/test_package_reset/READMEsrc/oracle_test/modules/test_package_reset/expected/test_package_reset.outsrc/oracle_test/modules/test_package_reset/expected/test_reset_all.outsrc/oracle_test/modules/test_package_reset/expected/test_reset_defaults.outsrc/oracle_test/modules/test_package_reset/expected/test_reset_one_oid.outsrc/oracle_test/modules/test_package_reset/meson.buildsrc/oracle_test/modules/test_package_reset/sql/test_package_reset.sqlsrc/oracle_test/modules/test_package_reset/sql/test_reset_all.sqlsrc/oracle_test/modules/test_package_reset/sql/test_reset_defaults.sqlsrc/oracle_test/modules/test_package_reset/sql/test_reset_one_oid.sqlsrc/oracle_test/modules/test_package_reset/test_package_reset--1.0.sqlsrc/oracle_test/modules/test_package_reset/test_package_reset.csrc/oracle_test/modules/test_package_reset/test_package_reset.controlsrc/pl/plisql/src/pl_package.csrc/pl/plisql/src/pl_package.hsrc/pl/plisql/src/pl_subproc_function.c
✅ Files skipped from review due to trivial changes (6)
- src/oracle_test/modules/test_package_reset/.gitignore
- src/oracle_test/modules/Makefile
- src/oracle_test/modules/test_package_reset/test_package_reset.control
- src/oracle_test/modules/test_package_reset/README
- src/include/utils/packagecache.h
- src/oracle_test/modules/test_package_reset/expected/test_reset_all.out
🚧 Files skipped from review as they are similar to previous changes (15)
- src/oracle_test/modules/test_package_reset/expected/test_reset_defaults.out
- src/oracle_test/modules/test_package_reset/expected/test_reset_one_oid.out
- src/oracle_test/modules/test_package_reset/expected/test_package_reset.out
- src/include/funcapi.h
- src/oracle_test/modules/test_package_reset/test_package_reset--1.0.sql
- src/oracle_test/modules/test_package_reset/sql/test_reset_one_oid.sql
- src/oracle_test/modules/test_package_reset/sql/test_reset_all.sql
- src/oracle_test/modules/test_package_reset/sql/test_package_reset.sql
- src/pl/plisql/src/pl_package.h
- src/oracle_test/modules/test_package_reset/sql/test_reset_defaults.sql
- src/oracle_test/modules/test_package_reset/test_package_reset.c
- src/backend/utils/cache/packagecache.c
- src/oracle_test/modules/test_package_reset/Makefile
- src/pl/plisql/src/pl_subproc_function.c
- src/pl/plisql/src/pl_package.c
afc9414 to
63378be
Compare
|
Handled the nitpick comment from coderabbit as well. |
There was a problem hiding this comment.
🧹 Nitpick comments (1)
src/oracle_test/modules/test_package_reset/sql/test_reset_defaults.sql (1)
104-105: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win
set_elemmutation is never independently verified.
set_elem(2, 99)at line 104 is immediately overwritten byset_arr(ARRAY[10, 20, 30, 40])at line 105 with no intermediate SELECT. Ifset_elemwere a no-op, the test would still pass, leaving the element-update code path untested.Add a SELECT between the two CALL statements to verify the element mutation took effect, and update the expected output accordingly.
♻️ Proposed fix
CALL pkg_arr_defaults.set_elem(2, 99); +SELECT pkg_arr_defaults.get_arr_len AS len, pkg_arr_defaults.get_arr_first AS first; CALL pkg_arr_defaults.set_arr(ARRAY[10, 20, 30, 40]);🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/oracle_test/modules/test_package_reset/sql/test_reset_defaults.sql` around lines 104 - 105, The pkg_arr_defaults.set_elem mutation is overwritten by pkg_arr_defaults.set_arr before it is verified, so the element-update path is not actually tested. Insert a SELECT of the package array state between the set_elem and set_arr calls in test_reset_defaults.sql, using the existing pkg_arr_defaults symbols so the mutation is asserted independently, and update the expected output for that added verification before continuing with the array reset.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Nitpick comments:
In `@src/oracle_test/modules/test_package_reset/sql/test_reset_defaults.sql`:
- Around line 104-105: The pkg_arr_defaults.set_elem mutation is overwritten by
pkg_arr_defaults.set_arr before it is verified, so the element-update path is
not actually tested. Insert a SELECT of the package array state between the
set_elem and set_arr calls in test_reset_defaults.sql, using the existing
pkg_arr_defaults symbols so the mutation is asserted independently, and update
the expected output for that added verification before continuing with the array
reset.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 3f6b3e22-4589-466c-95b4-974c1c6763ab
📒 Files selected for processing (2)
src/oracle_test/modules/test_package_reset/expected/test_reset_defaults.outsrc/oracle_test/modules/test_package_reset/sql/test_reset_defaults.sql
|
Hi @db-nikhil, I've found a memory deallocation bug in the package-reset path (src/pl/plisql/src/pl_package.c, the PLISQL_DTYPE_VAR branch, ~line 3745). Array-typed package globals fall into the PLISQL_DTYPE_VAR case and are freed using pfree. However, since Array-typed package globals become expanded objects once assigned, they should be released via DeleteExpandedObject. The adjacent PLISQL_DTYPE_REC branch already does exactly this (DeleteExpandedObject), so the VAR branch just needs the same treatment. I've also pushed a test case to verify this. Compiling with the Could you please fix this issue? Note that I pushed a commit adding a regression test — please git pull before amending so it isn't overwritten. |
Reset a single PL/iSQL package by OID (Oracle DBMS_SESSION.RESET_PACKAGE
semantics): close refcursors, free variable memory, reset mutable globals,
and mark the package as uninitialized (PLISQL_PACKAGE_NO_INIT) so that
the full init sequence — default expressions re-evaluated and init block
re-executed — runs lazily on next access.
This lazy re-init is spec-compliant: dynamic defaults (SYSDATE,
seq.NEXTVAL, cross-package function calls) are re-evaluated at
re-init time, not frozen at reset time. A snapshot/restore approach
would have captured stale values; NO_INIT delegates everything to the
existing lazy-init path in plisql_check_package_status.
ResetAllPackagesContext uses a two-phase approach (collect via
PackageCacheListItems, then reset each) to avoid hash iterator corruption
during modification.
Both APIs are exposed via the rendezvous mechanism
(plisql_internal_funcs.package_reset_context) so the PL/iSQL layer does
not need to access the PackageCache hash directly. Invalid OIDs trigger
ERROR; NULL OIDs are short-circuited by the STRICT function attribute.
ResetPackageContext returns bool: true if package was reset, false if
nothing to do (status too low).
ResetAllPackagesContext returns int: count of packages actually reset,
or -1 if PL/iSQL is unavailable (rendezvous function pointer is NULL).
Also add DBMS_SESSION.RESET_PACKAGE and register test_package_reset in CI
Test extension test_package_reset provides:
- reset_package_by_oid(oid) -> bool
- reset_all_packages() -> int4
Five test scenarios:
- test_package_reset: extension loads; reset_all returns -1 when PL/iSQL
unavailable; reset_package_by_oid(0) throws ERROR
- test_reset_one_oid: reset each of 3 packages by OID individually,
siblings untouched; loop reset each individually; NULL OID short-
circuits to NULL with no side effects
- test_reset_all: reset all -> init one -> get all -> reset all ->
get all -> init all -> get all -> reset all -> get all
- test_reset_defaults: package with default 42, no-default, and text
default; verify mutation then reset restores defaults (lazy re-init)
Rendezvous field: bool (*)(Oid) in PLiSQL_funcs_call and PLiSQL_plugin.
PackageCacheListItems is static (no external callers).
Spec reference: Oracle 21c DBMS_SESSION.RESET_PACKAGE — session-only
effect, does not affect other sessions. Defaults are re-evaluated
(§2.1: 'Default values for package variables are re-initialized'),
init block re-executed (§2.3: 'The INITIALIZATION section runs once
per session; RESET_PACKAGE forces re-execution'), re-instantiation
is lazy (§4.2: 'Re-instantiation occurs when next referenced').
Authored-by: Cedric Villemain <cedric.villemain@data-bene.io>
Co-authored-by: Nikhil <nikhil@data-bene.io>
f3f937e to
2bd694f
Compare
|
Hi @hs-liuxh
Thanks for the report! I missed using the same logic for VAR. And yes, I always configure with
Ok, thanks for the test. I have |
Implements #1376 (Fixes #1376)
Reset a single PL/iSQL package by OID (Oracle DBMS_SESSION.RESET_PACKAGE semantics): close refcursors, free variable memory, reset mutable globals, and mark the package as uninitialized (PLISQL_PACKAGE_NO_INIT) so that the full init sequence — default expressions re-evaluated and init block re-executed — runs lazily on next access.
This lazy re-init is spec-compliant: dynamic defaults (SYSDATE, seq.NEXTVAL, cross-package function calls) are re-evaluated at re-init time, not frozen at reset time. A snapshot/restore approach would have captured stale values; NO_INIT delegates everything to the existing lazy-init path in plisql_check_package_status.
ResetAllPackagesContext uses a two-phase approach (collect via PackageCacheListItems, then reset each) to avoid hash iterator corruption during modification.
Both APIs are exposed via the rendezvous mechanism (plisql_internal_funcs.package_reset_context) so the PL/iSQL layer does not need to access the PackageCache hash directly. Invalid OIDs trigger ERROR; NULL OIDs are short-circuited by the STRICT function attribute.
ResetPackageContext returns bool: true if package was reset, false if nothing to do (status too low).
ResetAllPackagesContext returns int: count of packages actually reset, or -1 if PL/iSQL is unavailable (rendezvous function pointer is NULL).
Test extension test_package_reset provides:
Five test scenarios:
get all -> init all -> get all -> reset all -> get all
Rendezvous field: bool (*)(Oid) in PLiSQL_funcs_call and PLiSQL_plugin. PackageCacheListItems is static (no external callers).
Spec reference: Oracle 21c DBMS_SESSION.RESET_PACKAGE — session-only effect, does not affect other sessions. Defaults are re-evaluated (§2.1: 'Default values for package variables are re-initialized'), init block re-executed (§2.3: 'The INITIALIZATION section runs once per session; RESET_PACKAGE forces re-execution'), re-instantiation is lazy (§4.2: 'Re-instantiation occurs when next referenced').
Authored-by: Cedric Villemain cedric.villemain@data-bene.io
Co-authored-by: Nikhil nikhil@data-bene.io
Summary by CodeRabbit
Summary
New Features
true/false) or reset all packages (returns a reset count).dbms_session.reset_package()to trigger the same behavior.Bug Fixes
Tests
STRICTNULL semantics.