v48-0001-misc-refactor-mostly-related-to-RPR_QUANTITY_INF.nocfbot
application/octet-stream
Filename: v48-0001-misc-refactor-mostly-related-to-RPR_QUANTITY_INF.nocfbot
Type: application/octet-stream
Part: 1
Message:
Re: Row pattern recognition
From cc0e870b7fd54176f7808e8ed7f1ae4f9c242307 Mon Sep 17 00:00:00 2001
From: jian he <jian.universality@gmail.com>
Date: Fri, 19 Jun 2026 14:10:07 +0800
Subject: [PATCH v48 1/2] misc refactor mostly related to RPR_QUANTITY_INF
Replace PG_INT32_MAX with RPR_QUANTITY_INF in both gram.y and rpr.c. To avoid
include src/include/optimizer/rpr.h in gram.y, the RPR_QUANTITY_INF macro is
explicitly defined within gram.y itself.
Abstracting this value into a dedicated macro is future proof: the parsing logic
remains correct even if the quantifier upper bound change from PG_INT32_MAX to
any random value.
Rename the bodyNullable and allNullable variables to match_empty in
fillRPRPattern and its related functions. "nullable" typically implies whether a
value can be SQL NULL, whereas this is more about if a pattern can match zero
rows. Variable "match_empty" more accurately reflects this behavior. All
associated comments have also been updated from "nullable" to "can match zero
rows" or "match empty" for improved clarity.
Refactors splitRPRTrailingAlt: to utilize the foreach_node macro and
foreach_current_index function, removing the need to manually manage ListCell
iteration and manual index counters.
Adds a comment inside makeRPRQuantifier explicitly stating that other fields are
irrelevant for a quantifier node.
---
src/backend/optimizer/plan/rpr.c | 42 ++++++++---------
src/backend/parser/gram.y | 78 ++++++++++++++++++--------------
2 files changed, 64 insertions(+), 56 deletions(-)
diff --git a/src/backend/optimizer/plan/rpr.c b/src/backend/optimizer/plan/rpr.c
index 62292508aa..c4a8e5d6d3 100644
--- a/src/backend/optimizer/plan/rpr.c
+++ b/src/backend/optimizer/plan/rpr.c
@@ -1176,7 +1176,7 @@ getVarIdFromPattern(RPRPattern *pat, const char *varName)
* fillRPRPatternVar
* Fill a VAR pattern element.
*
- * Returns true if this VAR is nullable (can match zero rows).
+ * Returns true if this VAR can match zero rows.
*/
static bool
fillRPRPatternVar(RPRPatternNode *node, RPRPattern *pat, int *idx, RPRDepth depth)
@@ -1187,7 +1187,7 @@ 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 >= RPR_QUANTITY_INF) ? RPR_QUANTITY_INF : node->max;
Assert(elem->min >= 0 && elem->min < RPR_QUANTITY_INF &&
elem->max >= 1 &&
(elem->max == RPR_QUANTITY_INF || elem->min <= elem->max));
@@ -1218,8 +1218,8 @@ fillRPRPatternVar(RPRPatternNode *node, RPRPattern *pat, int *idx, RPRDepth dept
* END.jump points to the first child (loop-back path).
* BEGIN.next and END.next are set later by finalizeRPRPattern().
*
- * Returns true if this group is nullable. A group is nullable when its
- * min is 0 (can be skipped entirely) or its body is nullable (every path
+ * Returns true if this group can match zero row. A group can match empty when
+ * its min is 0 (can be skipped entirely) or its body is match empty (every path
* through the body can match zero rows).
*/
static bool
@@ -1227,7 +1227,7 @@ fillRPRPatternGroup(RPRPatternNode *node, RPRPattern *pat, int *idx, RPRDepth de
{
int groupStartIdx = *idx;
int beginIdx = -1;
- bool bodyNullable = true;
+ bool match_empty = true;
/* Add BEGIN marker if group has non-trivial quantifier (not {1,1}) */
if (node->min != 1 || node->max != 1)
@@ -1239,7 +1239,7 @@ 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 >= RPR_QUANTITY_INF) ? RPR_QUANTITY_INF : node->max;
Assert(elem->min >= 0 && elem->min < RPR_QUANTITY_INF &&
elem->max >= 1 &&
(elem->max == RPR_QUANTITY_INF || elem->min <= elem->max));
@@ -1254,7 +1254,7 @@ fillRPRPatternGroup(RPRPatternNode *node, RPRPattern *pat, int *idx, RPRDepth de
foreach_node(RPRPatternNode, child, node->children)
{
if (!fillRPRPattern(child, pat, idx, depth + 1))
- bodyNullable = false;
+ match_empty = false;
}
/* Add group end marker if group has non-trivial quantifier (not {1,1}) */
@@ -1267,7 +1267,7 @@ 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 >= RPR_QUANTITY_INF) ? RPR_QUANTITY_INF : node->max;
Assert(endElem->min >= 0 && endElem->min < RPR_QUANTITY_INF &&
endElem->max >= 1 &&
(endElem->max == RPR_QUANTITY_INF || endElem->min <= endElem->max));
@@ -1277,11 +1277,11 @@ fillRPRPatternGroup(RPRPatternNode *node, RPRPattern *pat, int *idx, RPRDepth de
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 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 (bodyNullable)
+ if (match_empty)
endElem->flags |= RPR_ELEM_EMPTY_LOOP;
(*idx)++;
@@ -1290,7 +1290,7 @@ fillRPRPatternGroup(RPRPatternNode *node, RPRPattern *pat, int *idx, RPRDepth de
beginElem->jump = *idx; /* skip: go to after END */
}
- return (node->min == 0 || bodyNullable);
+ return (node->min == 0 || match_empty);
}
/*
@@ -1300,8 +1300,8 @@ fillRPRPatternGroup(RPRPatternNode *node, RPRPattern *pat, int *idx, RPRDepth de
* Creates the ALT marker, fills each alternative at increased depth,
* sets jump pointers for backtracking, and next pointers for successful paths.
*
- * Returns true if any branch is nullable (OR semantics: one nullable
- * branch suffices for the alternation to produce an empty match).
+ * Returns true if any branch can match zero rows (OR semantics: it suffices for
+ * one branch to accept an empty match).
*/
static bool
fillRPRPatternAlt(RPRPatternNode *node, RPRPattern *pat, int *idx, RPRDepth depth)
@@ -1312,7 +1312,7 @@ fillRPRPatternAlt(RPRPatternNode *node, RPRPattern *pat, int *idx, RPRDepth dept
List *altBranchStarts = NIL;
List *altEndPositions = NIL;
int afterAltIdx;
- bool anyNullable = false;
+ bool match_empty = false;
/* Add alternation start marker */
elem = &pat->elements[*idx];
@@ -1332,7 +1332,7 @@ fillRPRPatternAlt(RPRPatternNode *node, RPRPattern *pat, int *idx, RPRDepth dept
altBranchStarts = lappend_int(altBranchStarts, branchStart);
if (fillRPRPattern(alt, pat, idx, depth + 1))
- anyNullable = true;
+ match_empty = true;
altEndPositions = lappend_int(altEndPositions, *idx - 1);
}
@@ -1378,7 +1378,7 @@ fillRPRPatternAlt(RPRPatternNode *node, RPRPattern *pat, int *idx, RPRDepth dept
list_free(altBranchStarts);
list_free(altEndPositions);
- return anyNullable;
+ return match_empty;
}
/*
@@ -1395,7 +1395,7 @@ fillRPRPatternAlt(RPRPatternNode *node, RPRPattern *pat, int *idx, RPRDepth dept
static bool
fillRPRPattern(RPRPatternNode *node, RPRPattern *pat, int *idx, RPRDepth depth)
{
- bool allNullable = true;
+ bool match_empty = true;
/* Pattern nodes from parser are never NULL */
Assert(node != NULL);
@@ -1408,9 +1408,9 @@ fillRPRPattern(RPRPatternNode *node, RPRPattern *pat, int *idx, RPRDepth depth)
foreach_node(RPRPatternNode, child, node->children)
{
if (!fillRPRPattern(child, pat, idx, depth))
- allNullable = false;
+ match_empty = false;
}
- return allNullable;
+ return match_empty;
case RPR_PATTERN_VAR:
return fillRPRPatternVar(node, pat, idx, depth);
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 6eb01ea7f0..c76a5ea929 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -151,6 +151,14 @@ typedef struct KeyActions
#define parser_yyerror(msg) scanner_yyerror(msg, yyscanner)
#define parser_errposition(pos) scanner_errposition(pos, yyscanner)
+/*
+ * unbounded quantifier for row pattern recognation.
+ *
+ * If you change this code, see also RPR_QUANTITY_INF reference in
+ * src/include/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 +17752,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 +17764,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 +17786,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 +17810,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 +17827,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 +17867,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 +17915,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 +17932,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 +17949,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 +17966,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 +21415,8 @@ makeRPRQuantifier(int32 min, int32 max, bool reluctant, int location)
n->max = max;
n->reluctant = reluctant;
n->location = location;
+
+ /* Other fields are irrelevant for quantifier node */
return n;
}
@@ -21442,8 +21452,7 @@ makeRPRSeqOrSingle(List *children, int location)
static RPRPatternNode *
splitRPRTrailingAlt(RPRPatternNode *node, core_yyscan_t yyscanner)
{
- ListCell *lc;
- int i = 0;
+ int i;
if (node->nodeType != RPR_PATTERN_SEQ)
{
@@ -21458,9 +21467,9 @@ splitRPRTrailingAlt(RPRPatternNode *node, core_yyscan_t yyscanner)
return node;
}
- foreach(lc, node->children)
+ foreach_node(RPRPatternNode, child, node->children)
{
- RPRPatternNode *child = (RPRPatternNode *) lfirst(lc);
+ i = foreach_current_index(child);
if (child->trailing_alt)
{
@@ -21490,7 +21499,6 @@ splitRPRTrailingAlt(RPRPatternNode *node, core_yyscan_t yyscanner)
altn->location = node->location;
return altn;
}
- i++;
}
return node;
}
--
2.34.1