diff --git a/src/backend/utils/adt/ri_triggers.c b/src/backend/utils/adt/ri_triggers.c index 540febf21f7..78da6383ec3 100644 --- a/src/backend/utils/adt/ri_triggers.c +++ b/src/backend/utils/adt/ri_triggers.c @@ -240,6 +240,44 @@ static FastPathMeta *ri_fpmeta_dead_list = NULL; */ static List *ri_plan_dead_list = NIL; +/* + * Plans being executed by ri_PerformCheck(), innermost last. An error can + * leave stale entries behind; that only makes us keep a plan longer. + */ +static SPIPlanPtr *ri_exec_stack = NULL; +static int ri_exec_depth = 0; +static int ri_exec_size = 0; + +static bool +ri_PlanIsExecuting(SPIPlanPtr plan) +{ + for (int i = 0; i < ri_exec_depth; i++) + if (ri_exec_stack[i] == plan) + return true; + return false; +} + +static void +ri_FreeDeadPlans(void) +{ + List *keep = NIL; + ListCell *lc; + MemoryContext oldcxt = MemoryContextSwitchTo(TopMemoryContext); + + foreach(lc, ri_plan_dead_list) + { + SPIPlanPtr plan = (SPIPlanPtr) lfirst(lc); + + if (ri_PlanIsExecuting(plan)) + keep = lappend(keep, plan); + else + SPI_freeplan(plan); + } + list_free(ri_plan_dead_list); + ri_plan_dead_list = keep; + MemoryContextSwitchTo(oldcxt); +} + /* * Local function prototypes */ @@ -2729,10 +2767,24 @@ ri_PerformCheck(const RI_ConstraintInfo *riinfo, * the outer query's after-trigger context and fire after all RI updates * on the same row are complete, rather than immediately. */ + if (ri_exec_depth >= ri_exec_size) + { + ri_exec_size = Max(8, ri_exec_size * 2); + if (ri_exec_stack == NULL) + ri_exec_stack = MemoryContextAlloc(TopMemoryContext, + ri_exec_size * sizeof(SPIPlanPtr)); + else + ri_exec_stack = repalloc(ri_exec_stack, + ri_exec_size * sizeof(SPIPlanPtr)); + } + ri_exec_stack[ri_exec_depth++] = qplan; spi_result = SPI_execute_snapshot(qplan, vals, nulls, test_snapshot, crosscheck_snapshot, false, false, limit); + ri_exec_depth--; + if (ri_plan_dead_list != NIL) + ri_FreeDeadPlans(); /* Restore UID and security context */ SetUserIdAndSecContext(save_userid, save_sec_context); @@ -3712,7 +3764,9 @@ ri_FetchPreparedPlan(RI_QueryKey *key) * progress. */ entry->plan = NULL; - if (plan) + if (plan && !ri_PlanIsExecuting(plan)) + SPI_freeplan(plan); + else if (plan) { MemoryContext oldcxt = MemoryContextSwitchTo(TopMemoryContext); @@ -4051,6 +4105,7 @@ AtEOXact_RI(bool isCommit) pfree(dead); } + ri_exec_depth = 0; if (ri_plan_dead_list != NIL) { List *plans = ri_plan_dead_list;