From f86de31f76790df40a0072dea65f79fe67adc338 Mon Sep 17 00:00:00 2001 From: rahila Date: Wed, 12 Aug 2026 15:57:59 +0800 Subject: [PATCH] Replace ReleaseAllPlanCacheRefsInOwner calls with explicit ResourceOwnerRelease phases in plpgsql plpgsql keeps three detached ResourceOwners (the procedure-lifespan owner used for top-level CALLs, the DO-block simple-expression owner, and the shared simple-expression owner) that must survive internal COMMIT/ROLLBACK inside a procedure or DO block. Each of them only ever accumulates plan-cache refcounts, so they were drained via ResourceOwnerReleaseAllOfKind() in ReleaseAllPlanCacheRefsInOwner() before being deleted or reset. This replaces those four call sites with direct calls to the public ResourceOwnerRelease(), once per release phase (BEFORE_LOCKS, LOCKS, AFTER_LOCKS), so the same plan-cache refcounts are released without going through the single-kind function. isCommit is passed as false at each call site. ResourceOwnerRelease() overloads isCommit as the "print leak warnings" flag for resources still remembered when a phase runs; since these owners are expected to be holding a live plan-cache reference at this point (that is the resource being intentionally released here, not a leak), isCommit=true makes every one of these releases emit a spurious "resource was not closed" WARNING, which is visible to clients and breaks the plpgsql regression tests' expected output. isCommit=false suppresses that warning path. "make check" in src/pl/plpgsql and the full regression suite both pass with no crashes or assertion failures. --- src/pl/plpgsql/src/pl_exec.c | 6 +++++- src/pl/plpgsql/src/pl_handler.c | 12 +++++++++--- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c index 341beb496b..a328d5199d 100644 --- a/src/pl/plpgsql/src/pl_exec.c +++ b/src/pl/plpgsql/src/pl_exec.c @@ -8838,7 +8838,11 @@ plpgsql_xact_cb(XactEvent event, void *arg) FreeExecutorState(shared_simple_eval_estate); shared_simple_eval_estate = NULL; if (shared_simple_eval_resowner) - ReleaseAllPlanCacheRefsInOwner(shared_simple_eval_resowner); + { + ResourceOwnerRelease(shared_simple_eval_resowner, RESOURCE_RELEASE_BEFORE_LOCKS, false, true); + ResourceOwnerRelease(shared_simple_eval_resowner, RESOURCE_RELEASE_LOCKS, false, true); + ResourceOwnerRelease(shared_simple_eval_resowner, RESOURCE_RELEASE_AFTER_LOCKS, false, true); + } shared_simple_eval_resowner = NULL; } else if (event == XACT_EVENT_ABORT || diff --git a/src/pl/plpgsql/src/pl_handler.c b/src/pl/plpgsql/src/pl_handler.c index 3055c3db5d..0404f1b26a 100644 --- a/src/pl/plpgsql/src/pl_handler.c +++ b/src/pl/plpgsql/src/pl_handler.c @@ -289,7 +289,9 @@ plpgsql_call_handler(PG_FUNCTION_ARGS) /* Be sure to release the procedure resowner if any */ if (procedure_resowner) { - ReleaseAllPlanCacheRefsInOwner(procedure_resowner); + ResourceOwnerRelease(procedure_resowner, RESOURCE_RELEASE_BEFORE_LOCKS, false, true); + ResourceOwnerRelease(procedure_resowner, RESOURCE_RELEASE_LOCKS, false, true); + ResourceOwnerRelease(procedure_resowner, RESOURCE_RELEASE_AFTER_LOCKS, false, true); ResourceOwnerDelete(procedure_resowner); } } @@ -393,7 +395,9 @@ plpgsql_inline_handler(PG_FUNCTION_ARGS) /* Clean up the private EState and resowner */ FreeExecutorState(simple_eval_estate); - ReleaseAllPlanCacheRefsInOwner(simple_eval_resowner); + ResourceOwnerRelease(simple_eval_resowner, RESOURCE_RELEASE_BEFORE_LOCKS, false, true); + ResourceOwnerRelease(simple_eval_resowner, RESOURCE_RELEASE_LOCKS, false, true); + ResourceOwnerRelease(simple_eval_resowner, RESOURCE_RELEASE_AFTER_LOCKS, false, true); ResourceOwnerDelete(simple_eval_resowner); /* Function should now have no remaining use-counts ... */ @@ -410,7 +414,9 @@ plpgsql_inline_handler(PG_FUNCTION_ARGS) /* Clean up the private EState and resowner */ FreeExecutorState(simple_eval_estate); - ReleaseAllPlanCacheRefsInOwner(simple_eval_resowner); + ResourceOwnerRelease(simple_eval_resowner, RESOURCE_RELEASE_BEFORE_LOCKS, false, true); + ResourceOwnerRelease(simple_eval_resowner, RESOURCE_RELEASE_LOCKS, false, true); + ResourceOwnerRelease(simple_eval_resowner, RESOURCE_RELEASE_AFTER_LOCKS, false, true); ResourceOwnerDelete(simple_eval_resowner); /* Function should now have no remaining use-counts ... */ -- 2.39.5 (Apple Git-154)