From aa2351c90ee84dfbf8148a58be3be30a3575d4aa Mon Sep 17 00:00:00 2001 From: Henson Choi Date: Mon, 22 Jun 2026 22:46:26 +0900 Subject: [PATCH] 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. Define RPR_QUANTITY_INF once in nodes/parsenodes.h, next to RPRPatternNode whose max field stores it, and use it on the parser and planner side as well so every stage shares one definition; optimizer/rpr.h and gram.y no longer spell out their own copy. 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 | 75 +++++++++++++++----------------- src/include/nodes/parsenodes.h | 11 ++++- src/include/optimizer/rpr.h | 6 +-- 4 files changed, 59 insertions(+), 57 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 da39a06d601..818db6adce0 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -17833,11 +17833,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 { @@ -17845,19 +17845,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) @@ -17867,12 +17867,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) @@ -17891,11 +17891,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 @@ -17908,11 +17908,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 @@ -17948,37 +17948,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, @@ -17996,10 +17996,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) @@ -18013,12 +18013,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; } @@ -18030,10 +18030,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) @@ -18047,10 +18047,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, @@ -21496,6 +21496,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; } @@ -21531,9 +21533,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) @@ -21547,14 +21546,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; @@ -21579,7 +21577,6 @@ splitRPRTrailingAlt(RPRPatternNode *node, core_yyscan_t yyscanner) altn->location = node->location; return altn; } - i++; } return node; } diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index ae85d09bb6c..fb3496c5476 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -620,6 +620,14 @@ typedef enum RPRPatternNodeType RPR_PATTERN_GROUP, /* group (parentheses) */ } RPRPatternNodeType; +/* + * RPR_QUANTITY_INF is the sentinel stored in RPRPatternNode.max for an + * unbounded quantifier (*, +, or {n,}); later stages treat this max as + * "no upper bound". It lives here, next to the node, so the parser, the + * planner (optimizer/rpr.h), and the executor all share one definition. + */ +#define RPR_QUANTITY_INF PG_INT32_MAX /* unbounded quantifier */ + /* * RPRPatternNode - Row Pattern Recognition pattern parse tree node */ @@ -628,7 +636,8 @@ typedef struct RPRPatternNode NodeTag type; /* T_RPRPatternNode */ RPRPatternNodeType nodeType; /* VAR, SEQ, ALT, GROUP */ int32 min; /* minimum repetitions (0 for *, ?) */ - int32 max; /* maximum repetitions (PG_INT32_MAX for *, +) */ + int32 max; /* maximum repetitions (RPR_QUANTITY_INF for + * *, +) */ bool reluctant; /* true for reluctant (non-greedy) */ ParseLoc location; /* token location, or -1 if unknown */ char *varName; /* VAR: variable name */ diff --git a/src/include/optimizer/rpr.h b/src/include/optimizer/rpr.h index f5462ab2a7c..70991ef2ddb 100644 --- a/src/include/optimizer/rpr.h +++ b/src/include/optimizer/rpr.h @@ -31,10 +31,10 @@ /* * RPR_COUNT_INF is the value a runtime repetition count saturates at to avoid * int32 overflow (see the count++ guard in nfa_match). It is defined as - * RPR_QUANTITY_INF so that a saturated count compares as "unbounded", just - * like an unbounded quantifier's max. + * RPR_QUANTITY_INF (from nodes/parsenodes.h, included above) so that a + * saturated count compares as "unbounded", just like an unbounded + * quantifier's max. */ -#define RPR_QUANTITY_INF PG_INT32_MAX /* unbounded quantifier */ #define RPR_COUNT_INF RPR_QUANTITY_INF #define RPR_ELEMIDX_MAX PG_INT16_MAX /* max pattern elements */ #define RPR_ELEMIDX_INVALID ((RPRElemIdx) -1) /* invalid index */ -- 2.50.1 (Apple Git-155)