v48-0001-v48-misc-refactoring.nocfbot
application/octet-stream
Filename: v48-0001-v48-misc-refactoring.nocfbot
Type: application/octet-stream
Part: 0
Message:
Re: Row pattern recognition
From e67ec2fff608820d6818d34ba3e094286f6b69fb Mon Sep 17 00:00:00 2001
From: jian he <jian.universality@gmail.com>
Date: Mon, 15 Jun 2026 14:00:12 +0800
Subject: [PATCH v48 1/1] v48 misc refactoring
RPRNavExpr->resulttype should also marked as pg_node_attr(query_jumble_ignore)
collectPatternVariables is not needed.
The parser already ensures every DEFINE variable appears in PATTERN, so there is nothing to filter.
Also, we don't really do anything special (like make a dummy Const) regarding
PATTERN variables that not appearing in the DEFINE clause.
See nfa_evaluate_row the for loop break.
buildDefineVariableList is trivial. No need to export it as an external function.
Rename WindowAggState.defineClauseList to defineClauseExprs
Minor refactoring of regress test comments.
Flatten a needlessly nested block in show_window_def().
Replace a post-loop ListCell NULL check in remove_unused_subquery_outputs() with a boolean flag.
---
src/backend/commands/explain.c | 94 +++++++++----------
src/backend/executor/README.rpr | 5 +-
src/backend/executor/execRPR.c | 2 +-
src/backend/executor/nodeWindowAgg.c | 39 ++++----
src/backend/optimizer/path/allpaths.c | 13 ++-
src/backend/optimizer/path/costsize.c | 20 +---
src/backend/optimizer/plan/createplan.c | 24 +++--
src/backend/optimizer/plan/rpr.c | 77 ---------------
src/include/nodes/execnodes.h | 2 +-
src/include/nodes/parsenodes.h | 2 +-
src/include/nodes/primnodes.h | 3 +-
src/include/optimizer/rpr.h | 3 -
src/test/regress/expected/rpr_integration.out | 29 ++----
src/test/regress/sql/rpr_integration.sql | 31 ++----
src/tools/pgindent/typedefs.list | 24 +++++
15 files changed, 133 insertions(+), 235 deletions(-)
diff --git a/src/backend/commands/explain.c b/src/backend/commands/explain.c
index 70fd7f386a..696bdb9c8b 100644
--- a/src/backend/commands/explain.c
+++ b/src/backend/commands/explain.c
@@ -3212,77 +3212,73 @@ show_window_def(WindowAggState *planstate, List *ancestors, ExplainState *es)
/* Show Row Pattern Recognition pattern if present */
if (wagg->rpPattern != NULL)
{
+ RPRNavOffsetKind maxKind = wagg->navMaxOffsetKind;
+ int64 maxOffset = wagg->navMaxOffset;
+ RPRNavOffsetKind firstKind = wagg->navFirstOffsetKind;
+ int64 firstOffset = wagg->navFirstOffset;
+
char *patternStr = deparse_rpr_pattern(wagg->rpPattern);
- if (patternStr != NULL)
- {
- ExplainPropertyText("Pattern", patternStr, es);
- pfree(patternStr);
- }
+ ExplainPropertyText("Pattern", patternStr, es);
+
+ pfree(patternStr);
/*
* Show navigation offsets for tuplestore trim. For EXPLAIN ANALYZE,
* use the executor-resolved values (which may differ from the plan
* when NEEDS_EVAL was resolved to FIXED or RETAIN_ALL at init).
*/
+ if (es->analyze)
{
- RPRNavOffsetKind maxKind = wagg->navMaxOffsetKind;
- int64 maxOffset = wagg->navMaxOffset;
- RPRNavOffsetKind firstKind = wagg->navFirstOffsetKind;
- int64 firstOffset = wagg->navFirstOffset;
+ maxKind = planstate->navMaxOffsetKind;
+ maxOffset = planstate->navMaxOffset;
+ firstKind = planstate->navFirstOffsetKind;
+ firstOffset = planstate->navFirstOffset;
+ }
- if (es->analyze)
- {
- maxKind = planstate->navMaxOffsetKind;
- maxOffset = planstate->navMaxOffset;
- firstKind = planstate->navFirstOffsetKind;
- firstOffset = planstate->navFirstOffset;
- }
+ switch (maxKind)
+ {
+ case RPR_NAV_OFFSET_NEEDS_EVAL:
+ ExplainPropertyText("Nav Mark Lookback", "runtime", es);
+ break;
+ case RPR_NAV_OFFSET_RETAIN_ALL:
+ ExplainPropertyText("Nav Mark Lookback", "retain all", es);
+ break;
+ case RPR_NAV_OFFSET_FIXED:
+ ExplainPropertyInteger("Nav Mark Lookback", NULL,
+ maxOffset, es);
+ break;
+ default:
+ elog(ERROR, "unrecognized RPR nav offset kind: %d",
+ maxKind);
+ break;
+ }
- switch (maxKind)
+ if (wagg->hasFirstNav)
+ {
+ switch (firstKind)
{
case RPR_NAV_OFFSET_NEEDS_EVAL:
- ExplainPropertyText("Nav Mark Lookback", "runtime", es);
+ ExplainPropertyText("Nav Mark Lookahead", "runtime",
+ es);
break;
case RPR_NAV_OFFSET_RETAIN_ALL:
- ExplainPropertyText("Nav Mark Lookback", "retain all", es);
+ ExplainPropertyText("Nav Mark Lookahead", "retain all",
+ es);
break;
case RPR_NAV_OFFSET_FIXED:
- ExplainPropertyInteger("Nav Mark Lookback", NULL,
- maxOffset, es);
+ if (firstOffset == PG_INT64_MAX)
+ ExplainPropertyText("Nav Mark Lookahead", "infinite",
+ es);
+ else
+ ExplainPropertyInteger("Nav Mark Lookahead", NULL,
+ firstOffset, es);
break;
default:
elog(ERROR, "unrecognized RPR nav offset kind: %d",
- maxKind);
+ firstKind);
break;
}
-
- if (wagg->hasFirstNav)
- {
- switch (firstKind)
- {
- case RPR_NAV_OFFSET_NEEDS_EVAL:
- ExplainPropertyText("Nav Mark Lookahead", "runtime",
- es);
- break;
- case RPR_NAV_OFFSET_RETAIN_ALL:
- ExplainPropertyText("Nav Mark Lookahead", "retain all",
- es);
- break;
- case RPR_NAV_OFFSET_FIXED:
- if (firstOffset == PG_INT64_MAX)
- ExplainPropertyText("Nav Mark Lookahead", "infinite",
- es);
- else
- ExplainPropertyInteger("Nav Mark Lookahead", NULL,
- firstOffset, es);
- break;
- default:
- elog(ERROR, "unrecognized RPR nav offset kind: %d",
- firstKind);
- break;
- }
- }
}
}
}
diff --git a/src/backend/executor/README.rpr b/src/backend/executor/README.rpr
index 00af86681b..713ad84e1d 100644
--- a/src/backend/executor/README.rpr
+++ b/src/backend/executor/README.rpr
@@ -188,7 +188,6 @@ Chapter IV Compilation Phase
IV-1. Entry Point
create_windowagg_plan() (createplan.c)
- +-- buildDefineVariableList() Build variable name list from DEFINE
+-- buildRPRPattern() NFA compilation (6 phases)
IV-2. The 6 Phases of buildRPRPattern()
@@ -1468,8 +1467,6 @@ Appendix A. Key Function Index
--------------------------------------------------------------------------
transformRPR parse_rpr.c Parser entry point
transformDefineClause parse_rpr.c DEFINE transformation
- collectPatternVariables rpr.c Variable collection
- buildDefineVariableList rpr.c DEFINE variable list
buildRPRPattern rpr.c NFA compilation main
optimizeRPRPattern rpr.c AST optimization
fillRPRPattern rpr.c NFA element generation
@@ -1538,7 +1535,7 @@ Appendix B. Data Structure Relationship Diagram
|--- rpSkipTo: RPSkipTo (AFTER MATCH SKIP mode)
|--- rpPattern: RPRPattern* (copied from plan)
|--- defineVariableList: List<String> (variable names, DEFINE order)
- |--- defineClauseList: List<ExprState>
+ |--- defineClauseExprs: List<ExprState>
|--- nfaVarMatched: bool[] (per-row cache)
|--- defineMatchStartDependent: Bitmapset* (match_start_dependent
| DEFINE vars; see VI-4)
diff --git a/src/backend/executor/execRPR.c b/src/backend/executor/execRPR.c
index b326a58bbf..122a09e66d 100644
--- a/src/backend/executor/execRPR.c
+++ b/src/backend/executor/execRPR.c
@@ -1603,7 +1603,7 @@ nfa_reevaluate_dependent_vars(WindowAggState *winstate, RPRNFAContext *ctx,
/* Invalidate nav_slot cache since match_start changed */
winstate->nav_slot_pos = -1;
- foreach_ptr(ExprState, exprState, winstate->defineClauseList)
+ foreach_ptr(ExprState, exprState, winstate->defineClauseExprs)
{
int varIdx = foreach_current_index(exprState);
diff --git a/src/backend/executor/nodeWindowAgg.c b/src/backend/executor/nodeWindowAgg.c
index cb6a484b7d..25d1ad2143 100644
--- a/src/backend/executor/nodeWindowAgg.c
+++ b/src/backend/executor/nodeWindowAgg.c
@@ -3076,27 +3076,26 @@ ExecInitWindowAgg(WindowAgg *node, EState *estate, int eflags)
/* Set up row pattern recognition DEFINE clause */
winstate->defineVariableList = NIL;
- winstate->defineClauseList = NIL;
- if (node->defineClause != NIL)
+ winstate->defineClauseExprs = NIL;
+
+ /*
+ * Compile DEFINE clause expressions. PREV/NEXT navigation is handled by
+ * EEOP_RPR_NAV_SET/RESTORE opcodes emitted during ExecInitExpr, so no
+ * varno rewriting is needed here.
+ */
+ foreach_node(TargetEntry, te, node->defineClause)
{
- /*
- * Compile DEFINE clause expressions. PREV/NEXT navigation is handled
- * by EEOP_RPR_NAV_SET/RESTORE opcodes emitted during ExecInitExpr, so
- * no varno rewriting is needed here.
- */
- foreach_node(TargetEntry, te, node->defineClause)
- {
- char *name = te->resname;
- Expr *expr = te->expr;
- ExprState *exps;
+ char *name = te->resname;
+ ExprState *exprs;
- winstate->defineVariableList =
- lappend(winstate->defineVariableList,
- makeString(pstrdup(name)));
- exps = ExecInitExpr(expr, (PlanState *) winstate);
- winstate->defineClauseList =
- lappend(winstate->defineClauseList, exps);
- }
+ winstate->defineVariableList =
+ lappend(winstate->defineVariableList,
+ makeString(pstrdup(name)));
+
+ exprs = ExecInitExpr(te->expr, (PlanState *) winstate);
+
+ winstate->defineClauseExprs =
+ lappend(winstate->defineClauseExprs, exprs);
}
/* Initialize NFA free lists for row pattern matching */
@@ -4598,7 +4597,7 @@ nfa_evaluate_row(WindowObject winobj, int64 pos, bool *varMatched)
/* Invalidate nav_slot cache so PREV/NEXT re-fetch for new row */
winstate->nav_slot_pos = -1;
- foreach_ptr(ExprState, exprState, winstate->defineClauseList)
+ foreach_ptr(ExprState, exprState, winstate->defineClauseExprs)
{
Datum result;
bool isnull;
diff --git a/src/backend/optimizer/path/allpaths.c b/src/backend/optimizer/path/allpaths.c
index f3c9f3c0bd..b289e46a95 100644
--- a/src/backend/optimizer/path/allpaths.c
+++ b/src/backend/optimizer/path/allpaths.c
@@ -4807,20 +4807,19 @@ remove_unused_subquery_outputs(Query *subquery, RelOptInfo *rel,
*/
if (IsA(texpr, WindowFunc))
{
+ bool is_rpr = false;
WindowFunc *wfunc = (WindowFunc *) texpr;
- ListCell *wlc;
- foreach(wlc, subquery->windowClause)
+ foreach_node(WindowClause, wc, subquery->windowClause)
{
- WindowClause *wc = lfirst_node(WindowClause, wlc);
-
- if (wc->winref == wfunc->winref &&
- wc->defineClause != NIL)
+ if (wc->winref == wfunc->winref && wc->defineClause != NIL)
{
+ is_rpr = true;
break;
}
}
- if (wlc != NULL)
+
+ if (is_rpr)
continue;
}
diff --git a/src/backend/optimizer/path/costsize.c b/src/backend/optimizer/path/costsize.c
index 82472c3fe9..02b25f6f08 100644
--- a/src/backend/optimizer/path/costsize.c
+++ b/src/backend/optimizer/path/costsize.c
@@ -3235,26 +3235,14 @@ cost_windowagg(Path *path, PlannerInfo *root,
*/
if (winclause->rpPattern)
{
- List *pattern_vars;
QualCost defcosts;
- pattern_vars = collectPatternVariables(winclause->rpPattern);
-
- foreach_node(String, pv, pattern_vars)
+ foreach_node(TargetEntry, def, winclause->defineClause)
{
- char *ptname = strVal(pv);
-
- foreach_node(TargetEntry, def, winclause->defineClause)
- {
- if (!strcmp(ptname, def->resname))
- {
- cost_qual_eval_node(&defcosts, (Node *) def->expr, root);
- startup_cost += defcosts.startup;
- total_cost += defcosts.per_tuple * input_tuples;
- }
- }
+ cost_qual_eval_node(&defcosts, (Node *) def->expr, root);
+ startup_cost += defcosts.startup;
+ total_cost += defcosts.per_tuple * input_tuples;
}
- list_free_deep(pattern_vars);
}
foreach(lc, windowFuncs)
diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c
index cca4126e51..8bf2e89906 100644
--- a/src/backend/optimizer/plan/createplan.c
+++ b/src/backend/optimizer/plan/createplan.c
@@ -290,9 +290,8 @@ static Memoize *make_memoize(Plan *lefttree, Oid *hashoperators,
static WindowAgg *make_windowagg(List *tlist, WindowClause *wc,
int partNumCols, AttrNumber *partColIdx, Oid *partOperators, Oid *partCollations,
int ordNumCols, AttrNumber *ordColIdx, Oid *ordOperators, Oid *ordCollations,
- List *runCondition, RPSkipTo rpSkipTo,
+ List *runCondition,
RPRPattern *compiledPattern,
- List *defineClause,
Bitmapset *defineMatchStartDependent,
RPRNavOffsetKind navMaxOffsetKind, int64 navMaxOffset,
bool hasFirstNav,
@@ -2822,7 +2821,6 @@ create_windowagg_plan(PlannerInfo *root, WindowAggPath *best_path)
Oid *ordCollations;
ListCell *lc;
List *defineVariableList = NIL;
- List *filteredDefineClause = NIL;
RPRPattern *compiledPattern = NULL;
Bitmapset *matchStartDependent = NULL;
RPRNavOffsetKind navMaxOffsetKind = RPR_NAV_OFFSET_FIXED;
@@ -2889,8 +2887,9 @@ create_windowagg_plan(PlannerInfo *root, WindowAggPath *best_path)
* rejects DEFINE variables not used in PATTERN, so no filtering is
* needed.
*/
- buildDefineVariableList(wc->defineClause, &defineVariableList);
- filteredDefineClause = wc->defineClause;
+ foreach_node(TargetEntry, te, wc->defineClause)
+ defineVariableList = lappend(defineVariableList,
+ makeString(pstrdup(te->resname)));
/*
* Walk DEFINE once: collect nav offsets (for tuplestore trim) and the
@@ -2923,13 +2922,13 @@ create_windowagg_plan(PlannerInfo *root, WindowAggPath *best_path)
ordOperators,
ordCollations,
best_path->runCondition,
- wc->rpSkipTo,
compiledPattern,
- filteredDefineClause,
matchStartDependent,
- navMaxOffsetKind, navMaxOffset,
+ navMaxOffsetKind,
+ navMaxOffset,
hasFirstNav,
- navFirstOffsetKind, navFirstOffset,
+ navFirstOffsetKind,
+ navFirstOffset,
best_path->qual,
best_path->topwindow,
subplan);
@@ -7000,9 +6999,8 @@ static WindowAgg *
make_windowagg(List *tlist, WindowClause *wc,
int partNumCols, AttrNumber *partColIdx, Oid *partOperators, Oid *partCollations,
int ordNumCols, AttrNumber *ordColIdx, Oid *ordOperators, Oid *ordCollations,
- List *runCondition, RPSkipTo rpSkipTo,
+ List *runCondition,
RPRPattern *compiledPattern,
- List *defineClause,
Bitmapset *defineMatchStartDependent,
RPRNavOffsetKind navMaxOffsetKind, int64 navMaxOffset,
bool hasFirstNav,
@@ -7034,12 +7032,12 @@ make_windowagg(List *tlist, WindowClause *wc,
node->inRangeAsc = wc->inRangeAsc;
node->inRangeNullsFirst = wc->inRangeNullsFirst;
node->topWindow = topWindow;
- node->rpSkipTo = rpSkipTo;
+ node->rpSkipTo = wc->rpSkipTo;
/* Store compiled pattern for NFA execution */
node->rpPattern = compiledPattern;
- node->defineClause = defineClause;
+ node->defineClause = wc->defineClause;
/* Store pre-computed match_start dependency bitmapset */
node->defineMatchStartDependent = defineMatchStartDependent;
diff --git a/src/backend/optimizer/plan/rpr.c b/src/backend/optimizer/plan/rpr.c
index 175777a8ff..fb5260c786 100644
--- a/src/backend/optimizer/plan/rpr.c
+++ b/src/backend/optimizer/plan/rpr.c
@@ -98,9 +98,6 @@ static void computeAbsorbabilityRecursive(RPRPattern *pattern,
bool *hasAbsorbable);
static void computeAbsorbability(RPRPattern *pattern);
-static void collectPatternVariablesRecursive(RPRPatternNode *node,
- List **varNames);
-
/*
* rprPatternEqual
* Compare two RPRPatternNode trees for equality.
@@ -1826,59 +1823,6 @@ computeAbsorbability(RPRPattern *pattern)
pattern->isAbsorbable = hasAbsorbable;
}
-/*
- * collectPatternVariablesRecursive
- * Recursively collect variable names from pattern AST.
- */
-static void
-collectPatternVariablesRecursive(RPRPatternNode *node, List **varNames)
-{
- Assert(node != NULL);
-
- check_stack_depth();
-
- switch (node->nodeType)
- {
- case RPR_PATTERN_VAR:
- /* Add variable if not already in list */
- foreach_node(String, varname, *varNames)
- {
- if (strcmp(strVal(varname), node->varName) == 0)
- return; /* Already collected */
- }
- *varNames = lappend(*varNames, makeString(pstrdup(node->varName)));
- break;
-
- case RPR_PATTERN_SEQ:
- case RPR_PATTERN_ALT:
- case RPR_PATTERN_GROUP:
- foreach_node(RPRPatternNode, child, node->children)
- {
- collectPatternVariablesRecursive(child, varNames);
- }
- break;
- }
-}
-
-/*
- * collectPatternVariables
- * Collect unique variable names used in PATTERN clause.
- *
- * Returns a list of String nodes containing variable names.
- * Used to filter defineClause before varId assignment.
- */
-List *
-collectPatternVariables(RPRPatternNode *pattern)
-{
- List *varNames = NIL;
-
- /* Caller ensures pattern is not NULL */
- Assert(pattern != NULL);
-
- collectPatternVariablesRecursive(pattern, &varNames);
- return varNames;
-}
-
/*
* rpr_volatile_func_checker
* check_functions_in_node callback: true if funcid is VOLATILE.
@@ -1955,27 +1899,6 @@ validate_rpr_define_volatility(List *defineClause)
}
}
-/*
- * buildDefineVariableList
- * Build defineVariableList from defineClause.
- *
- * The parser already ensures that all DEFINE variables appear in PATTERN,
- * so no filtering is needed here.
- *
- * Sets *defineVariableList to list of variable names (String nodes).
- */
-void
-buildDefineVariableList(List *defineClause, List **defineVariableList)
-{
- *defineVariableList = NIL;
-
- foreach_node(TargetEntry, te, defineClause)
- {
- *defineVariableList = lappend(*defineVariableList,
- makeString(pstrdup(te->resname)));
- }
-}
-
/*
* buildRPRPattern
* Compile pattern AST to flat bytecode array.
diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h
index 792aa3f0d0..d5dff86dd2 100644
--- a/src/include/nodes/execnodes.h
+++ b/src/include/nodes/execnodes.h
@@ -2657,7 +2657,7 @@ typedef struct WindowAggState
struct RPRPattern *rpPattern; /* compiled pattern for NFA execution */
List *defineVariableList; /* list of row pattern definition
* variables (list of String) */
- List *defineClauseList; /* expression for row pattern definition
+ List *defineClauseExprs; /* expression for row pattern definition
* search conditions ExprState list */
RPRNFAContext *nfaContext; /* active matching contexts (head) */
RPRNFAContext *nfaContextTail; /* tail of active contexts (for reverse
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 3266c11a71..72da32ae28 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -619,7 +619,7 @@ typedef enum RPRPatternNodeType
} RPRPatternNodeType;
/*
- * RPRPatternNode - Row Pattern Recognition pattern AST node
+ * RPRPatternNode - Row Pattern Recognition pattern clause
*/
typedef struct RPRPatternNode
{
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index d7138f5141..84caf0733e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -695,7 +695,8 @@ typedef struct RPRNavExpr
Expr *offset_arg; /* offset expression, or NULL for default */
Expr *compound_offset_arg; /* outer offset for compound nav, or
* NULL if simple */
- Oid resulttype; /* result type (same as arg's type) */
+ /* result type (same as arg's type) */
+ Oid resulttype pg_node_attr(query_jumble_ignore);
/* OID of collation of result */
Oid resultcollid pg_node_attr(query_jumble_ignore);
/* token location, or -1 if unknown */
diff --git a/src/include/optimizer/rpr.h b/src/include/optimizer/rpr.h
index 802d2f1dd6..31866245f7 100644
--- a/src/include/optimizer/rpr.h
+++ b/src/include/optimizer/rpr.h
@@ -66,10 +66,7 @@
#define RPRElemIsFin(e) ((e)->varId == RPR_VARID_FIN)
#define RPRElemCanSkip(e) ((e)->min == 0)
-extern List *collectPatternVariables(RPRPatternNode *pattern);
extern void validate_rpr_define_volatility(List *defineClause);
-extern void buildDefineVariableList(List *defineClause,
- List **defineVariableList);
extern RPRPattern *buildRPRPattern(RPRPatternNode *pattern, List *defineVariableList,
RPSkipTo rpSkipTo, int frameOptions,
bool hasMatchStartDependent);
diff --git a/src/test/regress/expected/rpr_integration.out b/src/test/regress/expected/rpr_integration.out
index 80a32cca9a..ef537396b7 100644
--- a/src/test/regress/expected/rpr_integration.out
+++ b/src/test/regress/expected/rpr_integration.out
@@ -240,9 +240,9 @@ ORDER BY id;
-- must therefore yield two separate WindowAgg nodes. Inline specs
-- are used here because dedup only applies to inline windows.
-- Baseline: two inline RPR windows that are structurally identical
--- (same ORDER BY, frame, PATTERN, and DEFINE) are deduped by the
--- parser into a single WindowAgg node, confirming that parser-level
--- dedup is active for RPR windows whose DEFINE matches.
+-- (same PARTITION BY, ORDER BY, frame, PATTERN, and DEFINE) are deduped by the
+-- parser into a single WindowAgg node, confirming that parser-level dedup is
+-- active for RPR windows whose DEFINE matches.
EXPLAIN (COSTS OFF)
SELECT
count(*) OVER (ORDER BY id
@@ -325,15 +325,10 @@ ORDER BY id;
-- A5. Unused window removal prevention
-- ============================================================
-- Verify that remove_unused_subquery_outputs() does not drop an RPR
--- window function even when the outer query does not reference its
--- result. The RPR WindowAgg node is responsible for performing pattern
--- matching, so removing the window function would silently skip the
--- pattern match even though the surrounding query still depends on
--- RPR semantics.
--- The outer query ignores the per-row window result, yet pattern
--- matching must still execute. The plan must still contain a
--- WindowAgg node below the outer Aggregate; if the window were
--- removed, only Aggregate + Seq Scan would appear.
+-- window function when the outer query does not reference its result.
+-- The WindowAgg node performs the pattern match itself; without it,
+-- the match would be silently skipped. The plan must contain a
+-- WindowAgg node beneath the outer Aggregate.
EXPLAIN (COSTS OFF)
SELECT count(*) FROM (
SELECT count(*) OVER w FROM rpr_integ
@@ -587,10 +582,8 @@ WHERE cnt > 0;
-- EXPLAIN VERBOSE is therefore expected to show a clean targetlist on
-- the outer WindowAgg, with no DEFINE-derived expression leaking in.
-- Note: columns referenced by DEFINE (e.g., "val") may appear as
--- resjunk entries in upper WindowAgg targetlists -- that is a
--- harmless byproduct of the column guard's broad scope and does not
--- affect client output. The claim here is limited to the full
--- DEFINE boolean expression.
+-- resjunk entries in upper WindowAgg targetlists -- but that is a harmless.
+-- The claim here is limited to the full DEFINE boolean expression.
EXPLAIN (VERBOSE, COSTS OFF)
SELECT
count(*) OVER w_rpr AS rpr_cnt,
@@ -620,10 +613,6 @@ WINDOW
Output: id, val
(13 rows)
--- Executing the same query shows the client result is limited to
--- the two projected columns; "id" and "val" that appeared in the
--- upper WindowAgg Output line are resjunk-only and do not reach
--- the client.
SELECT
count(*) OVER w_rpr AS rpr_cnt,
count(*) OVER w_normal AS normal_cnt
diff --git a/src/test/regress/sql/rpr_integration.sql b/src/test/regress/sql/rpr_integration.sql
index 1d6075265b..62634d0f01 100644
--- a/src/test/regress/sql/rpr_integration.sql
+++ b/src/test/regress/sql/rpr_integration.sql
@@ -166,9 +166,9 @@ ORDER BY id;
-- are used here because dedup only applies to inline windows.
-- Baseline: two inline RPR windows that are structurally identical
--- (same ORDER BY, frame, PATTERN, and DEFINE) are deduped by the
--- parser into a single WindowAgg node, confirming that parser-level
--- dedup is active for RPR windows whose DEFINE matches.
+-- (same PARTITION BY, ORDER BY, frame, PATTERN, and DEFINE) are deduped by the
+-- parser into a single WindowAgg node, confirming that parser-level dedup is
+-- active for RPR windows whose DEFINE matches.
EXPLAIN (COSTS OFF)
SELECT
count(*) OVER (ORDER BY id
@@ -214,16 +214,10 @@ ORDER BY id;
-- A5. Unused window removal prevention
-- ============================================================
-- Verify that remove_unused_subquery_outputs() does not drop an RPR
--- window function even when the outer query does not reference its
--- result. The RPR WindowAgg node is responsible for performing pattern
--- matching, so removing the window function would silently skip the
--- pattern match even though the surrounding query still depends on
--- RPR semantics.
-
--- The outer query ignores the per-row window result, yet pattern
--- matching must still execute. The plan must still contain a
--- WindowAgg node below the outer Aggregate; if the window were
--- removed, only Aggregate + Seq Scan would appear.
+-- window function when the outer query does not reference its result.
+-- The WindowAgg node performs the pattern match itself; without it,
+-- the match would be silently skipped. The plan must contain a
+-- WindowAgg node beneath the outer Aggregate.
EXPLAIN (COSTS OFF)
SELECT count(*) FROM (
SELECT count(*) OVER w FROM rpr_integ
@@ -399,11 +393,8 @@ WHERE cnt > 0;
-- EXPLAIN VERBOSE is therefore expected to show a clean targetlist on
-- the outer WindowAgg, with no DEFINE-derived expression leaking in.
-- Note: columns referenced by DEFINE (e.g., "val") may appear as
--- resjunk entries in upper WindowAgg targetlists -- that is a
--- harmless byproduct of the column guard's broad scope and does not
--- affect client output. The claim here is limited to the full
--- DEFINE boolean expression.
-
+-- resjunk entries in upper WindowAgg targetlists -- but that is a harmless.
+-- The claim here is limited to the full DEFINE boolean expression.
EXPLAIN (VERBOSE, COSTS OFF)
SELECT
count(*) OVER w_rpr AS rpr_cnt,
@@ -417,10 +408,6 @@ WINDOW
w_normal AS (ORDER BY id
ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING);
--- Executing the same query shows the client result is limited to
--- the two projected columns; "id" and "val" that appeared in the
--- upper WindowAgg Output line are resjunk-only and do not reach
--- the client.
SELECT
count(*) OVER w_rpr AS rpr_cnt,
count(*) OVER w_normal AS normal_cnt
diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list
index f9eb23e52c..b500ae5677 100644
--- a/src/tools/pgindent/typedefs.list
+++ b/src/tools/pgindent/typedefs.list
@@ -533,6 +533,7 @@ Constraint
ConstraintCategory
ConstraintInfo
ConstraintsSetStmt
+ContainRPRContext
ControlData
ControlFileData
ConvInfo
@@ -663,7 +664,10 @@ DecodingWorkerShared
DefElem
DefElemAction
DefaultACLInfo
+DefineMetadataContext
+DefinePhase
DefineStmt
+DefineWalkCtx
DefnDumperPtr
DeleteStmt
DependenciesParseState
@@ -762,6 +766,7 @@ ErrorData
ErrorSaveContext
EstimateDSMForeignScan_function
EstimationInfo
+EvalDefineOffsetsContext
EventTriggerCacheEntry
EventTriggerCacheItem
EventTriggerCacheStateType
@@ -1832,6 +1837,8 @@ NamedLWLockTrancheRequest
NamedTuplestoreScan
NamedTuplestoreScanState
NamespaceInfo
+NavTraversal
+NavVisitFn
NestLoop
NestLoopParam
NestLoopState
@@ -1841,6 +1848,7 @@ NewConstraint
NextSampleBlock_function
NextSampleTuple_function
NextValueExpr
+NFALengthStats
Node
NodeInstrumentation
NodeTag
@@ -2524,6 +2532,22 @@ RI_ConstraintInfo
RI_FastPathEntry
RI_QueryHashEntry
RI_QueryKey
+RPCommonSyntax
+RPRDepth
+RPRElemFlags
+RPRElemIdx
+RPRNFAContext
+RPRNFAState
+RPRNavExpr
+RPRNavKind
+RPRNavOffsetKind
+RPRPattern
+RPRPatternElement
+RPRPatternNode
+RPRPatternNodeType
+RPRQuantity
+RPRVarId
+RPSkipTo
RTEKind
RTEPermissionInfo
RWConflict
--
2.34.1