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: 36
Message: Re: Row pattern recognition
From 36b034a809fc7d39a5d09ffca319bbb95f9152ba 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/38] 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)