From fcba7e3a68cd327fc2d0bc8fc3418f279e0e2334 Mon Sep 17 00:00:00 2001 From: Tatsuo Ishii Date: Sun, 18 Jan 2026 21:01:37 +0900 Subject: [PATCH v40 3/8] Row pattern recognition patch (rewriter). --- src/backend/utils/adt/ruleutils.c | 172 ++++++++++++++++++++++++++++++ 1 file changed, 172 insertions(+) diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index 033b625f3fc..b8a3ee2915b 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -438,6 +438,12 @@ static void get_rule_groupingset(GroupingSet *gset, List *targetlist, bool omit_parens, deparse_context *context); static void get_rule_orderby(List *orderList, List *targetList, bool force_colno, deparse_context *context); +static void append_pattern_quantifier(StringInfo buf, RPRPatternNode *node); +static void get_rule_pattern_node(RPRPatternNode *node, deparse_context *context); +static void get_rule_pattern(RPRPatternNode *rpPattern, bool force_colno, + deparse_context *context); +static void get_rule_define(List *defineClause, bool force_colno, + deparse_context *context); static void get_rule_windowclause(Query *query, deparse_context *context); static void get_rule_windowspec(WindowClause *wc, List *targetList, deparse_context *context); @@ -6744,6 +6750,127 @@ get_rule_orderby(List *orderList, List *targetList, } } +/* + * Helper function to append quantifier string for pattern node + */ +static void +append_pattern_quantifier(StringInfo buf, RPRPatternNode *node) +{ + bool has_quantifier = true; + + if (node->min == 1 && node->max == 1) + { + /* {1,1} = no quantifier */ + has_quantifier = false; + } + else if (node->min == 0 && node->max == INT_MAX) + appendStringInfoChar(buf, '*'); + else if (node->min == 1 && node->max == INT_MAX) + appendStringInfoChar(buf, '+'); + else if (node->min == 0 && node->max == 1) + appendStringInfoChar(buf, '?'); + else if (node->max == INT_MAX) + appendStringInfo(buf, "{%d,}", node->min); + else if (node->min == node->max) + appendStringInfo(buf, "{%d}", node->min); + else + appendStringInfo(buf, "{%d,%d}", node->min, node->max); + + if (node->reluctant && has_quantifier) + appendStringInfoChar(buf, '?'); +} + +/* + * Recursive helper to display RPRPatternNode tree + */ +static void +get_rule_pattern_node(RPRPatternNode *node, deparse_context *context) +{ + StringInfo buf = context->buf; + ListCell *lc; + const char *sep; + + if (node == NULL) + return; + + switch (node->nodeType) + { + case RPR_PATTERN_VAR: + appendStringInfoString(buf, node->varName); + append_pattern_quantifier(buf, node); + break; + + case RPR_PATTERN_SEQ: + sep = ""; + foreach(lc, node->children) + { + appendStringInfoString(buf, sep); + get_rule_pattern_node((RPRPatternNode *) lfirst(lc), context); + sep = " "; + } + break; + + case RPR_PATTERN_ALT: + sep = ""; + foreach(lc, node->children) + { + appendStringInfoString(buf, sep); + get_rule_pattern_node((RPRPatternNode *) lfirst(lc), context); + sep = " | "; + } + break; + + case RPR_PATTERN_GROUP: + appendStringInfoChar(buf, '('); + sep = ""; + foreach(lc, node->children) + { + appendStringInfoString(buf, sep); + get_rule_pattern_node((RPRPatternNode *) lfirst(lc), context); + sep = " "; + } + appendStringInfoChar(buf, ')'); + append_pattern_quantifier(buf, node); + break; + } +} + +/* + * Display a PATTERN clause. + */ +static void +get_rule_pattern(RPRPatternNode *rpPattern, bool force_colno, + deparse_context *context) +{ + StringInfo buf = context->buf; + + appendStringInfoChar(buf, '('); + get_rule_pattern_node(rpPattern, context); + appendStringInfoChar(buf, ')'); +} + +/* + * Display a DEFINE clause. + */ +static void +get_rule_define(List *defineClause, bool force_colno, deparse_context *context) +{ + StringInfo buf = context->buf; + const char *sep; + ListCell *lc_def; + + sep = " "; + + foreach(lc_def, defineClause) + { + TargetEntry *te = (TargetEntry *) lfirst(lc_def); + + appendStringInfo(buf, "%s%s AS ", sep, te->resname); + get_rule_expr((Node *) te->expr, context, false); + sep = ",\n "; + } +} + /* * Display a WINDOW clause. * @@ -6824,6 +6951,7 @@ get_rule_windowspec(WindowClause *wc, List *targetList, get_rule_orderby(wc->orderClause, targetList, false, context); needspace = true; } + /* framing clause is never inherited, so print unless it's default */ if (wc->frameOptions & FRAMEOPTION_NONDEFAULT) { @@ -6832,7 +6960,51 @@ get_rule_windowspec(WindowClause *wc, List *targetList, get_window_frame_options(wc->frameOptions, wc->startOffset, wc->endOffset, context); + needspace = true; + } + + /* RPR */ + if (wc->rpSkipTo == ST_NEXT_ROW) + { + if (needspace) + appendStringInfoChar(buf, ' '); + appendStringInfoString(buf, + "\n AFTER MATCH SKIP TO NEXT ROW "); + needspace = true; + } + else if (wc->rpSkipTo == ST_PAST_LAST_ROW) + { + if (needspace) + appendStringInfoChar(buf, ' '); + appendStringInfoString(buf, + "\n AFTER MATCH SKIP PAST LAST ROW "); + needspace = true; + } + if (wc->initial) + { + if (needspace) + appendStringInfoChar(buf, ' '); + appendStringInfoString(buf, "\n INITIAL"); + needspace = true; } + if (wc->rpPattern) + { + if (needspace) + appendStringInfoChar(buf, ' '); + appendStringInfoString(buf, "\n PATTERN "); + get_rule_pattern(wc->rpPattern, false, context); + needspace = true; + } + + if (wc->defineClause) + { + if (needspace) + appendStringInfoChar(buf, ' '); + appendStringInfoString(buf, "\n DEFINE\n"); + get_rule_define(wc->defineClause, false, context); + appendStringInfoChar(buf, ' '); + } + appendStringInfoChar(buf, ')'); } -- 2.43.0