nocfbot-0039-Fix-memory-leak-in-row-pattern-recognition-DEFINE.txt

text/plain

Filename: nocfbot-0039-Fix-memory-leak-in-row-pattern-recognition-DEFINE.txt
Type: text/plain
Part: 39
Message: Re: Row pattern recognition
From 30e1c3dcea5abe8b0909d047550eb589e8e0a248 Mon Sep 17 00:00:00 2001
From: Henson Choi <assam258@gmail.com>
Date: Mon, 1 Jun 2026 22:38:55 +0900
Subject: [PATCH 39/68] Fix memory leak in row pattern recognition DEFINE
 evaluation

The row pattern recognition scan evaluates every DEFINE expression once
per partition row in update_reduced_frame, but did so with the plain
ExecEvalExpr in the long-lived query context and never reset it.  By-ref
scratch -- a NUMERIC DEFINE such as price * 1.1 > 165 is the typical case
-- therefore accumulated for the whole forward scan, growing memory
O(partition size) within a single window.

Evaluate the DEFINE expressions in the per-tuple memory context with
ExecEvalExprSwitchContext, in both nfa_evaluate_row and
nfa_reevaluate_dependent_vars, and reset that context once per processed
row in the scan loop.  The boolean results are consumed immediately, and
the cross-row state -- nfaVarMatched in the node context and the NFA
states in the partition context -- lives elsewhere, so the reset is safe.

Per a report from a static analysis tool.
---
 src/backend/executor/execRPR.c       |  3 ++-
 src/backend/executor/nodeWindowAgg.c | 10 ++++++++--
 2 files changed, 10 insertions(+), 3 deletions(-)

diff --git a/src/backend/executor/execRPR.c b/src/backend/executor/execRPR.c
index eac0c04c38d..a2c304da0d1 100644
--- a/src/backend/executor/execRPR.c
+++ b/src/backend/executor/execRPR.c
@@ -1706,7 +1706,8 @@ nfa_reevaluate_dependent_vars(WindowAggState *winstate, RPRNFAContext *ctx,
 			Datum		result;
 			bool		isnull;
 
-			result = ExecEvalExpr(exprState, econtext, &isnull);
+			/* Per-tuple context; scratch freed by the per-row reset */
+			result = ExecEvalExprSwitchContext(exprState, econtext, &isnull);
 			winstate->nfaVarMatched[varIdx] = (!isnull && DatumGetBool(result));
 		}
 
diff --git a/src/backend/executor/nodeWindowAgg.c b/src/backend/executor/nodeWindowAgg.c
index 0b0196e7e40..408bbc120b7 100644
--- a/src/backend/executor/nodeWindowAgg.c
+++ b/src/backend/executor/nodeWindowAgg.c
@@ -4515,6 +4515,12 @@ update_reduced_frame(WindowObject winobj, int64 pos)
 		 * appropriately as pruned or mismatched.
 		 */
 		ExecRPRCleanupDeadContexts(winstate, targetCtx);
+
+		/*
+		 * Free this row's per-tuple DEFINE-evaluation scratch; cross-row
+		 * state (nfaVarMatched, NFA states) lives in other contexts.
+		 */
+		ResetExprContext(winstate->ss.ps.ps_ExprContext);
 	}
 
 register_result:
@@ -4606,8 +4612,8 @@ nfa_evaluate_row(WindowObject winobj, int64 pos, bool *varMatched)
 		Datum		result;
 		bool		isnull;
 
-		/* Evaluate DEFINE expression */
-		result = ExecEvalExpr(exprState, econtext, &isnull);
+		/* Per-tuple context so by-ref scratch is freed by the per-row reset */
+		result = ExecEvalExprSwitchContext(exprState, econtext, &isnull);
 
 		varMatched[varIdx] = (!isnull && DatumGetBool(result));
 
-- 
2.50.1 (Apple Git-155)