From 30e1c3dcea5abe8b0909d047550eb589e8e0a248 Mon Sep 17 00:00:00 2001 From: Henson Choi 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)