From 0b7a74cc9b9ab4367843bf6022ef4240aadef126 Mon Sep 17 00:00:00 2001
From: Henson Choi <assam258@gmail.com>
Date: Mon, 15 Jun 2026 16:54:13 +0900
Subject: [PATCH v50 13/29] 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)

