nocfbot-0038-Demote-dead-runtime-checks-in-the-RPR-executor-to.txt
text/plain
Filename: nocfbot-0038-Demote-dead-runtime-checks-in-the-RPR-executor-to.txt
Type: text/plain
Part: 37
Message:
Re: Row pattern recognition
From cd08e4296f499b9c3db3b7926a625f63455a815e Mon Sep 17 00:00:00 2001
From: Henson Choi <assam258@gmail.com>
Date: Mon, 1 Jun 2026 22:15:04 +0900
Subject: [PATCH 38/68] Demote dead runtime checks in the RPR executor to
assertions
Three checks in the row pattern recognition NFA executor can never fail,
given how the planner builds the pattern:
- The final arm of nfa_advance_var's loop/exit cascade tested canExit,
but reaching it implies !canLoop, which with min <= max forces canExit
true.
- nfa_state_clone guarded the counts memcpy with counts != NULL &&
maxDepth > 0, but every caller passes a live state's counts and the
stored maxDepth is always >= 1.
- nfa_advance_alt bounded the branch walk with altIdx < numElements, but
every jump/next link is -1 or a valid index, and the altIdx >= 0 test
and the depth break already terminate the walk.
Replace each with an assertion that documents the invariant and drop the
runtime branch. Also document why the nullable fast-forward path in
nfa_advance_end intentionally omits the isAbsorbable update: it runs only
for EMPTY_LOOP groups, which are never inside an absorbable region.
Per a report from a static analysis tool.
---
src/backend/executor/execRPR.c | 25 +++++++++++++++++++------
1 file changed, 19 insertions(+), 6 deletions(-)
diff --git a/src/backend/executor/execRPR.c b/src/backend/executor/execRPR.c
index 9e45920de9c..eac0c04c38d 100644
--- a/src/backend/executor/execRPR.c
+++ b/src/backend/executor/execRPR.c
@@ -279,8 +279,9 @@ nfa_state_clone(WindowAggState *winstate, int16 elemIdx,
RPRPatternElement *elem = &pattern->elements[elemIdx];
state->elemIdx = elemIdx;
- if (counts != NULL && maxDepth > 0)
- memcpy(state->counts, counts, sizeof(int32) * maxDepth);
+ /* Every reachable caller passes a live state's counts; maxDepth >= 1. */
+ Assert(counts != NULL && maxDepth > 0);
+ memcpy(state->counts, counts, sizeof(int32) * maxDepth);
/*
* Compute isAbsorbable immediately at transition time. isAbsorbable =
@@ -981,11 +982,15 @@ nfa_advance_alt(WindowAggState *winstate, RPRNFAContext *ctx,
RPRPatternElement *elements = pattern->elements;
RPRElemIdx altIdx = elem->next;
- while (altIdx >= 0 && altIdx < pattern->numElements)
+ while (altIdx >= 0)
{
- RPRPatternElement *altElem = &elements[altIdx];
+ RPRPatternElement *altElem;
RPRNFAState *newState;
+ /* Branch jump/next links are always -1 or a valid index */
+ Assert(altIdx < pattern->numElements);
+ altElem = &elements[altIdx];
+
/*
* Stop if element is outside ALT scope (not a branch). The check
* fires when the last branch is a quantified group whose BEGIN.jump
@@ -1147,6 +1152,13 @@ nfa_advance_end(WindowAggState *winstate, RPRNFAContext *ctx,
ffState->elemIdx = elem->next;
nextElem = &elements[ffState->elemIdx];
+ /*
+ * Unlike the must-exit path, no isAbsorbable update is needed:
+ * the fast-forward path runs only for EMPTY_LOOP (nullable)
+ * groups, which are never inside an absorbable region, so
+ * isAbsorbable is already false here.
+ */
+
/* END->END: increment outer END's count */
if (RPRElemIsEnd(nextElem) &&
ffState->counts[nextElem->depth] < RPR_COUNT_MAX)
@@ -1353,11 +1365,12 @@ nfa_advance_var(WindowAggState *winstate, RPRNFAContext *ctx,
/* Loop only: keep state as-is */
nfa_add_state_unique(winstate, ctx, state);
}
- else if (canExit)
+ else
{
- /* Exit only: advance to next element */
+ /* Exit only: advance to next element (canExit necessarily true) */
RPRPatternElement *nextElem;
+ Assert(canExit);
/* Exit: clear the VAR's own count (count-clear policy) */
state->counts[depth] = 0;
state->elemIdx = elem->next;
--
2.50.1 (Apple Git-155)