From d51f2f55d4da92fb4f4c6e0880ee49361b48e152 Mon Sep 17 00:00:00 2001
From: Henson Choi <assam258@gmail.com>
Date: Mon, 22 Jun 2026 22:46:26 +0900
Subject: [PATCH v50 23/29] Tidy up the row pattern unbounded-quantifier
 sentinel

The grammar and planner spelled the unbounded quantifier bound as a bare
PG_INT32_MAX, while the executor already used RPR_QUANTITY_INF for the
same value.  Use RPR_QUANTITY_INF on the parser and planner side as well
so every stage shares one sentinel; gram.y keeps a local copy of the
macro to avoid including the planner header.

With the parser emitting RPR_QUANTITY_INF directly, the explicit
PG_INT32_MAX-to-RPR_QUANTITY_INF mapping in fillRPRPattern* becomes an
identity and is removed, and the adjacent quantifier-range Asserts are
simplified accordingly.

Also rewrite splitRPRTrailingAlt's loop with foreach_node() and
foreach_current_index(), and add a comment to makeRPRQuantifier.

Based on a patch by Jian He.
---
 src/backend/optimizer/plan/rpr.c | 24 ++++------
 src/backend/parser/gram.y        | 78 ++++++++++++++++----------------
 2 files changed, 49 insertions(+), 53 deletions(-)

diff --git a/src/backend/optimizer/plan/rpr.c b/src/backend/optimizer/plan/rpr.c
index 62292508aad..3d7752bd5ed 100644
--- a/src/backend/optimizer/plan/rpr.c
+++ b/src/backend/optimizer/plan/rpr.c
@@ -1187,10 +1187,9 @@ fillRPRPatternVar(RPRPatternNode *node, RPRPattern *pat, int *idx, RPRDepth dept
 	elem->varId = getVarIdFromPattern(pat, node->varName);
 	elem->depth = depth;
 	elem->min = node->min;
-	elem->max = (node->max == PG_INT32_MAX) ? RPR_QUANTITY_INF : node->max;
+	elem->max = node->max;
 	Assert(elem->min >= 0 && elem->min < RPR_QUANTITY_INF &&
-		   elem->max >= 1 &&
-		   (elem->max == RPR_QUANTITY_INF || elem->min <= elem->max));
+		   elem->max >= 1 && elem->min <= elem->max);
 	elem->next = RPR_ELEMIDX_INVALID;
 	elem->jump = RPR_ELEMIDX_INVALID;
 	if (node->reluctant)
@@ -1239,10 +1238,9 @@ fillRPRPatternGroup(RPRPatternNode *node, RPRPattern *pat, int *idx, RPRDepth de
 		elem->varId = RPR_VARID_BEGIN;
 		elem->depth = depth;
 		elem->min = node->min;
-		elem->max = (node->max == PG_INT32_MAX) ? RPR_QUANTITY_INF : node->max;
+		elem->max = node->max;
 		Assert(elem->min >= 0 && elem->min < RPR_QUANTITY_INF &&
-			   elem->max >= 1 &&
-			   (elem->max == RPR_QUANTITY_INF || elem->min <= elem->max));
+			   elem->max >= 1 && elem->min <= elem->max);
 		elem->next = RPR_ELEMIDX_INVALID;	/* set by finalize */
 		elem->jump = RPR_ELEMIDX_INVALID;	/* set after END */
 		if (node->reluctant)
@@ -1267,19 +1265,18 @@ fillRPRPatternGroup(RPRPatternNode *node, RPRPattern *pat, int *idx, RPRDepth de
 		endElem->varId = RPR_VARID_END;
 		endElem->depth = depth;
 		endElem->min = node->min;
-		endElem->max = (node->max == PG_INT32_MAX) ? RPR_QUANTITY_INF : node->max;
+		endElem->max = node->max;
 		Assert(endElem->min >= 0 && endElem->min < RPR_QUANTITY_INF &&
-			   endElem->max >= 1 &&
-			   (endElem->max == RPR_QUANTITY_INF || endElem->min <= endElem->max));
+			   endElem->max >= 1 && endElem->min <= endElem->max);
 		endElem->next = RPR_ELEMIDX_INVALID;
 		endElem->jump = groupStartIdx;	/* loop to first child */
 		if (node->reluctant)
 			endElem->flags |= RPR_ELEM_RELUCTANT;
 
 		/*
-		 * If the group body is nullable (all paths can match empty), mark the
-		 * END element so that nfa_advance_end can fast-forward the iteration
-		 * count to min when reached via empty-match skip paths.
+		 * If the group body is nullable, mark the END element so that
+		 * nfa_advance_end can fast-forward the iteration count to min when
+		 * reached via empty-match skip paths.
 		 */
 		if (bodyNullable)
 			endElem->flags |= RPR_ELEM_EMPTY_LOOP;
@@ -1455,8 +1452,7 @@ finalizeRPRPattern(RPRPattern *result)
 
 		/* Verify quantifier range is valid */
 		Assert(elem->min >= 0 && elem->min < RPR_QUANTITY_INF &&
-			   elem->max >= 1 &&
-			   (elem->max == RPR_QUANTITY_INF || elem->min <= elem->max));
+			   elem->max >= 1 && elem->min <= elem->max);
 	}
 
 	/* Add FIN marker at the end */
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 6eb01ea7f0b..731b01eb742 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -151,6 +151,9 @@ typedef struct KeyActions
 #define parser_yyerror(msg)  scanner_yyerror(msg, yyscanner)
 #define parser_errposition(pos)  scanner_errposition(pos, yyscanner)
 
+/* unbounded quantifier; must match RPR_QUANTITY_INF in optimizer/rpr.h */
+#define RPR_QUANTITY_INF	PG_INT32_MAX
+
 static void base_yyerror(YYLTYPE *yylloc, core_yyscan_t yyscanner,
 						 const char *msg);
 static RawStmt *makeRawStmt(Node *stmt, int stmt_location);
@@ -17744,11 +17747,11 @@ row_pattern_quantifier_opt:
 				}
 			| '*'
 				{
-					$$ = (Node *) makeRPRQuantifier(0, PG_INT32_MAX, false, @1);
+					$$ = (Node *) makeRPRQuantifier(0, RPR_QUANTITY_INF, false, @1);
 				}
 			| '+'
 				{
-					$$ = (Node *) makeRPRQuantifier(1, PG_INT32_MAX, false, @1);
+					$$ = (Node *) makeRPRQuantifier(1, RPR_QUANTITY_INF, false, @1);
 				}
 			| Op
 				{
@@ -17756,19 +17759,19 @@ row_pattern_quantifier_opt:
 					if (strcmp($1, "?") == 0)
 						$$ = (Node *) makeRPRQuantifier(0, 1, false, @1);
 					else if (strcmp($1, "*?") == 0)
-						$$ = (Node *) makeRPRQuantifier(0, PG_INT32_MAX, true, @1);
+						$$ = (Node *) makeRPRQuantifier(0, RPR_QUANTITY_INF, true, @1);
 					else if (strcmp($1, "+?") == 0)
-						$$ = (Node *) makeRPRQuantifier(1, PG_INT32_MAX, true, @1);
+						$$ = (Node *) makeRPRQuantifier(1, RPR_QUANTITY_INF, true, @1);
 					else if (strcmp($1, "??") == 0)
 						$$ = (Node *) makeRPRQuantifier(0, 1, true, @1);
 					else if (strcmp($1, "*|") == 0)
 					{
-						$$ = (Node *) makeRPRQuantifier(0, PG_INT32_MAX, false, @1);
+						$$ = (Node *) makeRPRQuantifier(0, RPR_QUANTITY_INF, false, @1);
 						((RPRPatternNode *) $$)->trailing_alt = true;
 					}
 					else if (strcmp($1, "+|") == 0)
 					{
-						$$ = (Node *) makeRPRQuantifier(1, PG_INT32_MAX, false, @1);
+						$$ = (Node *) makeRPRQuantifier(1, RPR_QUANTITY_INF, false, @1);
 						((RPRPatternNode *) $$)->trailing_alt = true;
 					}
 					else if (strcmp($1, "?|") == 0)
@@ -17778,12 +17781,12 @@ row_pattern_quantifier_opt:
 					}
 					else if (strcmp($1, "*?|") == 0)
 					{
-						$$ = (Node *) makeRPRQuantifier(0, PG_INT32_MAX, true, @1);
+						$$ = (Node *) makeRPRQuantifier(0, RPR_QUANTITY_INF, true, @1);
 						((RPRPatternNode *) $$)->trailing_alt = true;
 					}
 					else if (strcmp($1, "+?|") == 0)
 					{
-						$$ = (Node *) makeRPRQuantifier(1, PG_INT32_MAX, true, @1);
+						$$ = (Node *) makeRPRQuantifier(1, RPR_QUANTITY_INF, true, @1);
 						((RPRPatternNode *) $$)->trailing_alt = true;
 					}
 					else if (strcmp($1, "??|") == 0)
@@ -17802,11 +17805,11 @@ row_pattern_quantifier_opt:
 			| '*' Op
 				{
 					if (strcmp($2, "?") == 0)
-						$$ = (Node *) makeRPRQuantifier(0, PG_INT32_MAX, true, @1);
+						$$ = (Node *) makeRPRQuantifier(0, RPR_QUANTITY_INF, true, @1);
 					else if (strcmp($2, "?|") == 0)
 					{
 						/* "A* ?|B" = reluctant "A*?" plus alternation */
-						$$ = (Node *) makeRPRQuantifier(0, PG_INT32_MAX, true, @1);
+						$$ = (Node *) makeRPRQuantifier(0, RPR_QUANTITY_INF, true, @1);
 						((RPRPatternNode *) $$)->trailing_alt = true;
 					}
 					else
@@ -17819,11 +17822,11 @@ row_pattern_quantifier_opt:
 			| '+' Op
 				{
 					if (strcmp($2, "?") == 0)
-						$$ = (Node *) makeRPRQuantifier(1, PG_INT32_MAX, true, @1);
+						$$ = (Node *) makeRPRQuantifier(1, RPR_QUANTITY_INF, true, @1);
 					else if (strcmp($2, "?|") == 0)
 					{
 						/* "A+ ?|B" = reluctant "A+?" plus alternation */
-						$$ = (Node *) makeRPRQuantifier(1, PG_INT32_MAX, true, @1);
+						$$ = (Node *) makeRPRQuantifier(1, RPR_QUANTITY_INF, true, @1);
 						((RPRPatternNode *) $$)->trailing_alt = true;
 					}
 					else
@@ -17859,37 +17862,37 @@ row_pattern_quantifier_opt:
 			/* {n}, {n,}, {,m}, {n,m} quantifiers */
 			| '{' Iconst '}'
 				{
-					if ($2 <= 0 || $2 >= PG_INT32_MAX)
+					if ($2 <= 0 || $2 >= RPR_QUANTITY_INF)
 						ereport(ERROR,
 								errcode(ERRCODE_SYNTAX_ERROR),
-								errmsg("quantifier bound must be between 1 and %d", PG_INT32_MAX - 1),
+								errmsg("quantifier bound must be between 1 and %d", RPR_QUANTITY_INF - 1),
 								parser_errposition(@2));
 					$$ = (Node *) makeRPRQuantifier($2, $2, false, @1);
 				}
 			| '{' Iconst ',' '}'
 				{
-					if ($2 < 0 || $2 >= PG_INT32_MAX)
+					if ($2 < 0 || $2 >= RPR_QUANTITY_INF)
 						ereport(ERROR,
 								errcode(ERRCODE_SYNTAX_ERROR),
-								errmsg("quantifier bound must be between 0 and %d", PG_INT32_MAX - 1),
+								errmsg("quantifier bound must be between 0 and %d", RPR_QUANTITY_INF - 1),
 								parser_errposition(@2));
-					$$ = (Node *) makeRPRQuantifier($2, PG_INT32_MAX, false, @1);
+					$$ = (Node *) makeRPRQuantifier($2, RPR_QUANTITY_INF, false, @1);
 				}
 			| '{' ',' Iconst '}'
 				{
-					if ($3 <= 0 || $3 >= PG_INT32_MAX)
+					if ($3 <= 0 || $3 >= RPR_QUANTITY_INF)
 						ereport(ERROR,
 								errcode(ERRCODE_SYNTAX_ERROR),
-								errmsg("quantifier bound must be between 1 and %d", PG_INT32_MAX - 1),
+								errmsg("quantifier bound must be between 1 and %d", RPR_QUANTITY_INF - 1),
 								parser_errposition(@3));
 					$$ = (Node *) makeRPRQuantifier(0, $3, false, @1);
 				}
 			| '{' Iconst ',' Iconst '}'
 				{
-					if ($2 < 0 || $4 <= 0 || $2 >= PG_INT32_MAX || $4 >= PG_INT32_MAX)
+					if ($2 < 0 || $4 <= 0 || $2 >= RPR_QUANTITY_INF || $4 >= RPR_QUANTITY_INF)
 						ereport(ERROR,
 								errcode(ERRCODE_SYNTAX_ERROR),
-								errmsg("quantifier bounds must be between 0 and %d with max >= 1", PG_INT32_MAX - 1),
+								errmsg("quantifier bounds must be between 0 and %d with max >= 1", RPR_QUANTITY_INF - 1),
 								parser_errposition(@2));
 					if ($2 > $4)
 						ereport(ERROR,
@@ -17907,10 +17910,10 @@ row_pattern_quantifier_opt:
 								errmsg("invalid token \"%s\" after range quantifier", rpr_invalid_quantifier_token($4)),
 								errhint("Only \"?\" is allowed after {n} to make it reluctant."),
 								parser_errposition(@4));
-					if ($2 <= 0 || $2 >= PG_INT32_MAX)
+					if ($2 <= 0 || $2 >= RPR_QUANTITY_INF)
 						ereport(ERROR,
 								errcode(ERRCODE_SYNTAX_ERROR),
-								errmsg("quantifier bound must be between 1 and %d", PG_INT32_MAX - 1),
+								errmsg("quantifier bound must be between 1 and %d", RPR_QUANTITY_INF - 1),
 								parser_errposition(@2));
 					$$ = (Node *) makeRPRQuantifier($2, $2, true, @1);
 					if (strcmp($4, "?|") == 0)
@@ -17924,12 +17927,12 @@ row_pattern_quantifier_opt:
 								errmsg("invalid token \"%s\" after range quantifier", rpr_invalid_quantifier_token($5)),
 								errhint("Only \"?\" is allowed after {n,} or {,m} to make it reluctant."),
 								parser_errposition(@5));
-					if ($2 < 0 || $2 >= PG_INT32_MAX)
+					if ($2 < 0 || $2 >= RPR_QUANTITY_INF)
 						ereport(ERROR,
 								errcode(ERRCODE_SYNTAX_ERROR),
-								errmsg("quantifier bound must be between 0 and %d", PG_INT32_MAX - 1),
+								errmsg("quantifier bound must be between 0 and %d", RPR_QUANTITY_INF - 1),
 								parser_errposition(@2));
-					$$ = (Node *) makeRPRQuantifier($2, PG_INT32_MAX, true, @1);
+					$$ = (Node *) makeRPRQuantifier($2, RPR_QUANTITY_INF, true, @1);
 					if (strcmp($5, "?|") == 0)
 						((RPRPatternNode *) $$)->trailing_alt = true;
 				}
@@ -17941,10 +17944,10 @@ row_pattern_quantifier_opt:
 								errmsg("invalid token \"%s\" after range quantifier", rpr_invalid_quantifier_token($5)),
 								errhint("Only \"?\" is allowed after {n,} or {,m} to make it reluctant."),
 								parser_errposition(@5));
-					if ($3 <= 0 || $3 >= PG_INT32_MAX)
+					if ($3 <= 0 || $3 >= RPR_QUANTITY_INF)
 						ereport(ERROR,
 								errcode(ERRCODE_SYNTAX_ERROR),
-								errmsg("quantifier bound must be between 1 and %d", PG_INT32_MAX - 1),
+								errmsg("quantifier bound must be between 1 and %d", RPR_QUANTITY_INF - 1),
 								parser_errposition(@3));
 					$$ = (Node *) makeRPRQuantifier(0, $3, true, @1);
 					if (strcmp($5, "?|") == 0)
@@ -17958,10 +17961,10 @@ row_pattern_quantifier_opt:
 								errmsg("invalid token \"%s\" after range quantifier", rpr_invalid_quantifier_token($6)),
 								errhint("Only \"?\" is allowed after {n,m} to make it reluctant."),
 								parser_errposition(@6));
-					if ($2 < 0 || $4 <= 0 || $2 >= PG_INT32_MAX || $4 >= PG_INT32_MAX)
+					if ($2 < 0 || $4 <= 0 || $2 >= RPR_QUANTITY_INF || $4 >= RPR_QUANTITY_INF)
 						ereport(ERROR,
 								errcode(ERRCODE_SYNTAX_ERROR),
-								errmsg("quantifier bounds must be between 0 and %d with max >= 1", PG_INT32_MAX - 1),
+								errmsg("quantifier bounds must be between 0 and %d with max >= 1", RPR_QUANTITY_INF - 1),
 								parser_errposition(@2));
 					if ($2 > $4)
 						ereport(ERROR,
@@ -21407,6 +21410,8 @@ makeRPRQuantifier(int32 min, int32 max, bool reluctant, int location)
 	n->max = max;
 	n->reluctant = reluctant;
 	n->location = location;
+
+	/* Other fields are irrelevant for a quantifier node */
 	return n;
 }
 
@@ -21442,9 +21447,6 @@ makeRPRSeqOrSingle(List *children, int location)
 static RPRPatternNode *
 splitRPRTrailingAlt(RPRPatternNode *node, core_yyscan_t yyscanner)
 {
-	ListCell   *lc;
-	int			i = 0;
-
 	if (node->nodeType != RPR_PATTERN_SEQ)
 	{
 		if (node->trailing_alt)
@@ -21458,14 +21460,13 @@ splitRPRTrailingAlt(RPRPatternNode *node, core_yyscan_t yyscanner)
 		return node;
 	}
 
-	foreach(lc, node->children)
+	foreach_node(RPRPatternNode, child, node->children)
 	{
-		RPRPatternNode *child = (RPRPatternNode *) lfirst(lc);
-
 		if (child->trailing_alt)
 		{
-			List	   *lefthalf = list_copy_head(node->children, i + 1);
-			List	   *righthalf = list_copy_tail(node->children, i + 1);
+			int			splitIdx = foreach_current_index(child);
+			List	   *lefthalf = list_copy_head(node->children, splitIdx + 1);
+			List	   *righthalf = list_copy_tail(node->children, splitIdx + 1);
 			RPRPatternNode *altn;
 			RPRPatternNode *rightnode;
 
@@ -21490,7 +21491,6 @@ splitRPRTrailingAlt(RPRPatternNode *node, core_yyscan_t yyscanner)
 			altn->location = node->location;
 			return altn;
 		}
-		i++;
 	}
 	return node;
 }
-- 
2.50.1 (Apple Git-155)

