From a70f1c1e753425adcec6021c07a0f5af6dc0968f Mon Sep 17 00:00:00 2001 From: Henson Choi Date: Mon, 15 Jun 2026 16:54:13 +0900 Subject: [PATCH 04/13] Use a dedicated ExprContext for RPR DEFINE clause evaluation DEFINE clauses were evaluated in the per-output-tuple context. Because EEOP_RPR_NAV_RESTORE copies pass-by-reference navigation results into that context, resetting it per row freed window function results before ExecProject (a use-after-free), while not resetting it leaked across the partition. tmpcontext cannot be reused either, as ExecQualAndReset() resets it mid-expression when NEXT re-enters spool_tuples. Use a third ExprContext, reset once per row and separate from both. --- src/backend/executor/execRPR.c | 5 ++++- src/backend/executor/nodeWindowAgg.c | 25 ++++++++++++++++++++++--- src/include/nodes/execnodes.h | 1 + 3 files changed, 27 insertions(+), 4 deletions(-) diff --git a/src/backend/executor/execRPR.c b/src/backend/executor/execRPR.c index b326a58bbf5..de78b06d277 100644 --- a/src/backend/executor/execRPR.c +++ b/src/backend/executor/execRPR.c @@ -1592,10 +1592,13 @@ static void nfa_reevaluate_dependent_vars(WindowAggState *winstate, RPRNFAContext *ctx, int64 currentPos) { - ExprContext *econtext = winstate->ss.ps.ps_ExprContext; + ExprContext *econtext = winstate->rprContext; int64 saved_match_start = winstate->nav_match_start; int64 saved_pos = winstate->currentpos; + /* Release the previous evaluation's DEFINE expression memory */ + ResetExprContext(econtext); + /* Temporarily set nav_match_start and currentpos for FIRST/LAST */ winstate->nav_match_start = ctx->matchStartRow; winstate->currentpos = currentPos; diff --git a/src/backend/executor/nodeWindowAgg.c b/src/backend/executor/nodeWindowAgg.c index 5b2385f1d8d..90f33bdee40 100644 --- a/src/backend/executor/nodeWindowAgg.c +++ b/src/backend/executor/nodeWindowAgg.c @@ -2742,12 +2742,28 @@ ExecInitWindowAgg(WindowAgg *node, EState *estate, int eflags) /* * Create expression contexts. We need two, one for per-input-tuple - * processing and one for per-output-tuple processing. We cheat a little - * by using ExecAssignExprContext() to build both. + * processing and one for per-output-tuple processing, plus an optional + * third for row pattern recognition DEFINE evaluation (built just below + * when a DEFINE clause is present). We cheat a little by using + * ExecAssignExprContext() to build them all. Each call overwrites + * ps_ExprContext, so the last call must establish the output context. */ ExecAssignExprContext(estate, &winstate->ss.ps); tmpcontext = winstate->ss.ps.ps_ExprContext; winstate->tmpcontext = tmpcontext; + + /* + * Row pattern recognition evaluates DEFINE clauses in a third context, + * reset before each DEFINE evaluation pass. It must be distinct from + * tmpcontext and ps_ExprContext so its reset frees neither input nor + * output tuple memory. + */ + if (node->defineClause != NIL) + { + ExecAssignExprContext(estate, &winstate->ss.ps); + winstate->rprContext = winstate->ss.ps.ps_ExprContext; + } + ExecAssignExprContext(estate, &winstate->ss.ps); /* Create long-lived context for storage of partition-local memory etc */ @@ -4572,12 +4588,15 @@ static bool nfa_evaluate_row(WindowObject winobj, int64 pos, bool *varMatched) { WindowAggState *winstate = winobj->winstate; - ExprContext *econtext = winstate->ss.ps.ps_ExprContext; + ExprContext *econtext = winstate->rprContext; int numDefineVars = list_length(winstate->defineVariableList); int varIdx = 0; TupleTableSlot *slot; int64 saved_pos; + /* Release the previous row's DEFINE evaluation memory */ + ResetExprContext(econtext); + /* Fetch current row into temp_slot_1 */ slot = winstate->temp_slot_1; if (!window_gettupleslot(winobj, pos, slot)) diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h index 8060b018ef8..5b4ac8a6c33 100644 --- a/src/include/nodes/execnodes.h +++ b/src/include/nodes/execnodes.h @@ -2698,6 +2698,7 @@ typedef struct WindowAggState MemoryContext aggcontext; /* shared context for aggregate working data */ MemoryContext curaggcontext; /* current aggregate's working data */ ExprContext *tmpcontext; /* short-term evaluation context */ + ExprContext *rprContext; /* DEFINE clause evaluation context */ bool all_first; /* true if the scan is starting */ bool partition_spooled; /* true if all tuples in current partition -- 2.50.1 (Apple Git-155)