nocfbot-0012-comment-doc-clarity.txt
text/plain
Filename: nocfbot-0012-comment-doc-clarity.txt
Type: text/plain
Part: 12
Message:
Re: Row pattern recognition
From 9c7e5bc9dde1adafcff995370340975661890d3c Mon Sep 17 00:00:00 2001
From: Henson Choi <assam258@gmail.com>
Date: Fri, 19 Jun 2026 13:43:58 +0900
Subject: [PATCH 12/13] Improve comments, documentation, and naming for row
pattern recognition
A batch of clarity cleanups for row pattern recognition, none of which
change behavior or query output:
- Call the parser-built PATTERN structure a "parse tree" rather than an
"AST", which is not PostgreSQL's usual term (parsenodes.h, parse_rpr.c,
plan/rpr.c, and the executor README).
- Trim the transformDefineClause header comment, whose step list merely
duplicated the inline comments.
- Drop a duplicated standard-citation sentence from the contain_rpr_walker
header; transformWithClause already carries it.
- Fix a stale "ALT_START" reference to name the ALT marker, and normalize
an "or -1" ParseLoc comment to the usual "or -1 if unknown".
- Move the EMPTY-alternative explanation in row_pattern_quantifier_opt
into the action block, keeping the /*EMPTY*/ marker.
- Reword the Run Condition pushdown test comment to explain in SQL terms
why count(*) is DECREASING over the required frame.
- Rename RPR_COUNT_MAX to RPR_COUNT_INF, defined from RPR_QUANTITY_INF.
The old "MAX" name suggested a configurable repetition limit -- that is
elem->max, a different thing -- which led to questions about erroring
when a count reaches it. The value is the int32 saturation ceiling, and
a saturated count reads as "unbounded".
---
src/backend/executor/README.rpr | 44 +++++++++++------------
src/backend/executor/execRPR.c | 23 ++++++------
src/backend/optimizer/plan/rpr.c | 23 ++++++------
src/backend/parser/gram.y | 7 ++--
src/backend/parser/parse_cte.c | 4 +--
src/backend/parser/parse_rpr.c | 19 ++--------
src/include/nodes/parsenodes.h | 12 +++----
src/include/optimizer/rpr.h | 9 ++++-
src/test/regress/expected/rpr_explain.out | 12 ++++---
src/test/regress/sql/rpr_explain.sql | 12 ++++---
10 files changed, 84 insertions(+), 81 deletions(-)
diff --git a/src/backend/executor/README.rpr b/src/backend/executor/README.rpr
index 50d1ff87f7e..9275e265d4b 100644
--- a/src/backend/executor/README.rpr
+++ b/src/backend/executor/README.rpr
@@ -96,16 +96,16 @@ Chapter II Overall Processing Pipeline
RPR processing is divided into three phases:
- +------------------------------------------------------------+
- | 1. Parsing (Parser) |
- | SQL text -> PATTERN AST + DEFINE expression tree |
- | |
- | 2. Compilation (Optimizer/Planner) |
- | PATTERN AST -> optimization -> flat NFA element array |
- | |
- | 3. Execution (Executor) |
- | Row-by-row matching via NFA simulation |
- +------------------------------------------------------------+
+ +--------------------------------------------------------------+
+ | 1. Parsing (Parser) |
+ | SQL text -> PATTERN parse tree + DEFINE expression tree |
+ | |
+ | 2. Compilation (Optimizer/Planner) |
+ | PATTERN parse tree -> optimization -> flat NFA elements |
+ | |
+ | 3. Execution (Executor) |
+ | Row-by-row matching via NFA simulation |
+ +--------------------------------------------------------------+
Each phase uses independent data structures, and the interfaces between
phases are well-defined:
@@ -137,7 +137,7 @@ following:
(3) DEFINE clause transformation (transformDefineClause)
-III-2. PATTERN AST (Abstract Syntax Tree)
+III-2. PATTERN parse tree
The parser transforms the PATTERN clause into an RPRPatternNode tree.
Each node has one of the following four types:
@@ -192,16 +192,16 @@ IV-1. Entry Point
IV-2. The 6 Phases of buildRPRPattern()
- Phase 1: AST optimization (optimizeRPRPattern)
+ Phase 1: parse tree optimization (optimizeRPRPattern)
Phase 2: Statistics collection (scanRPRPattern)
Phase 3: Memory allocation (makeRPRPattern)
Phase 4: NFA element fill (fillRPRPattern)
Phase 5: Finalization (finalizeRPRPattern)
Phase 6: Absorbability analysis (computeAbsorbability)
-IV-3. Phase 1: AST Optimization
+IV-3. Phase 1: Parse Tree Optimization
-After copying the parser-generated AST, the following optimizations are
+After copying the parser-generated parse tree, the following optimizations are
applied:
(a) SEQ flattening: Unwrap nested SEQ nodes
@@ -236,7 +236,7 @@ applied:
IV-4. Phase 4: NFA Element Array Generation
-Transforms the optimized AST into a flat array of RPRPatternElement.
+Transforms the optimized parse tree into a flat array of RPRPatternElement.
This is the core data structure used for NFA simulation at runtime.
RPRPatternElement struct (16 bytes):
@@ -298,7 +298,7 @@ Element flags (1 byte, bitmask):
Example: PATTERN (A+ B | C)
- AST: ALT(SEQ(VAR(A,1,INF), VAR(B,1,1)), VAR(C,1,1))
+ Parse tree: ALT(SEQ(VAR(A,1,INF), VAR(B,1,1)), VAR(C,1,1))
Compilation result:
@@ -345,9 +345,9 @@ Example: PATTERN ((A B)+)
IV-4a. Reluctant Flag (RPR_ELEM_RELUCTANT)
-The reluctant flag is set during Phase 4 (fillRPRPattern) when the AST node
-has reluctant == true. It reverses the priority of quantifier expansion at
-runtime:
+The reluctant flag is set during Phase 4 (fillRPRPattern) when the parse
+tree node has reluctant == true. It reverses the priority of quantifier
+expansion at runtime:
Greedy (default): try loop-back first, then exit (prefer longer match)
Reluctant: try exit first, then loop-back (prefer shorter match)
@@ -1363,9 +1363,9 @@ XII-5. Execution Optimization Summary
-- Compile-time --
- (1) AST Optimization (IV-3)
+ (1) Parse Tree Optimization (IV-3)
- Simplifies the AST before converting the pattern to an NFA.
+ Simplifies the parse tree before converting the pattern to an NFA.
Reduces the number of NFA elements through consecutive variable
merging (A A -> A{2}), SEQ flattening, quantifier multiplication,
and other transformations.
@@ -1468,7 +1468,7 @@ Appendix A. Key Function Index
transformRPR parse_rpr.c Parser entry point
transformDefineClause parse_rpr.c DEFINE transformation
buildRPRPattern rpr.c NFA compilation main
- optimizeRPRPattern rpr.c AST optimization
+ optimizeRPRPattern rpr.c parse tree optimization
fillRPRPattern rpr.c NFA element generation
finalizeRPRPattern rpr.c Finalization
computeAbsorbability rpr.c Absorption analysis
diff --git a/src/backend/executor/execRPR.c b/src/backend/executor/execRPR.c
index 90e3a068f04..e5e30d79f01 100644
--- a/src/backend/executor/execRPR.c
+++ b/src/backend/executor/execRPR.c
@@ -821,8 +821,11 @@ nfa_match(WindowAggState *winstate, RPRNFAContext *ctx, bool *varMatched)
if (matched)
{
- /* Increment count */
- if (count < RPR_COUNT_MAX)
+ /*
+ * Increment count, saturating at RPR_COUNT_INF to avoid int32
+ * overflow; a saturated count then compares as "unbounded".
+ */
+ if (count < RPR_COUNT_INF)
count++;
/* Max constraint should not be exceeded */
@@ -857,7 +860,7 @@ nfa_match(WindowAggState *winstate, RPRNFAContext *ctx, bool *varMatched)
int32 endCount = state->counts[endDepth];
/* Increment group count */
- if (endCount < RPR_COUNT_MAX)
+ if (endCount < RPR_COUNT_INF)
endCount++;
Assert(endElem->max == RPR_QUANTITY_INF ||
endCount <= endElem->max);
@@ -900,7 +903,7 @@ nfa_match(WindowAggState *winstate, RPRNFAContext *ctx, bool *varMatched)
state->counts[endDepth] = 0;
/* Increment outer group count */
- if (outerCount < RPR_COUNT_MAX)
+ if (outerCount < RPR_COUNT_INF)
outerCount++;
Assert(outerEnd->max == RPR_QUANTITY_INF ||
outerCount <= outerEnd->max);
@@ -1180,7 +1183,7 @@ nfa_advance_end(WindowAggState *winstate, RPRNFAContext *ctx,
/* END->END: increment outer END's count */
if (RPRElemIsEnd(nextElem) &&
- ffState->counts[nextElem->depth] < RPR_COUNT_MAX)
+ ffState->counts[nextElem->depth] < RPR_COUNT_INF)
ffState->counts[nextElem->depth]++;
}
@@ -1235,7 +1238,7 @@ nfa_advance_end(WindowAggState *winstate, RPRNFAContext *ctx,
RPRElemIsAbsorbableBranch(nextElem);
/* END->END: increment outer END's count */
- if (RPRElemIsEnd(nextElem) && state->counts[nextElem->depth] < RPR_COUNT_MAX)
+ if (RPRElemIsEnd(nextElem) && state->counts[nextElem->depth] < RPR_COUNT_INF)
state->counts[nextElem->depth]++;
nfa_route_to_elem(winstate, ctx, state, nextElem, currentPos);
@@ -1262,7 +1265,7 @@ nfa_advance_end(WindowAggState *winstate, RPRNFAContext *ctx,
nextElem = &elements[exitState->elemIdx];
/* END->END: increment outer END's count */
- if (RPRElemIsEnd(nextElem) && exitState->counts[nextElem->depth] < RPR_COUNT_MAX)
+ if (RPRElemIsEnd(nextElem) && exitState->counts[nextElem->depth] < RPR_COUNT_INF)
exitState->counts[nextElem->depth]++;
/* Prepare loop state */
@@ -1355,7 +1358,7 @@ nfa_advance_var(WindowAggState *winstate, RPRNFAContext *ctx,
/* When exiting directly to an outer END, increment its count */
if (RPRElemIsEnd(nextElem))
{
- if (cloneState->counts[nextElem->depth] < RPR_COUNT_MAX)
+ if (cloneState->counts[nextElem->depth] < RPR_COUNT_INF)
cloneState->counts[nextElem->depth]++;
}
@@ -1404,7 +1407,7 @@ nfa_advance_var(WindowAggState *winstate, RPRNFAContext *ctx,
*/
if (RPRElemIsEnd(nextElem))
{
- if (state->counts[nextElem->depth] < RPR_COUNT_MAX)
+ if (state->counts[nextElem->depth] < RPR_COUNT_INF)
state->counts[nextElem->depth]++;
}
@@ -1438,7 +1441,7 @@ nfa_advance_var(WindowAggState *winstate, RPRNFAContext *ctx,
/* See comment above: increment outer END count for quantified VARs */
if (RPRElemIsEnd(nextElem))
{
- if (state->counts[nextElem->depth] < RPR_COUNT_MAX)
+ if (state->counts[nextElem->depth] < RPR_COUNT_INF)
state->counts[nextElem->depth]++;
}
diff --git a/src/backend/optimizer/plan/rpr.c b/src/backend/optimizer/plan/rpr.c
index ebba8e50b1d..62292508aad 100644
--- a/src/backend/optimizer/plan/rpr.c
+++ b/src/backend/optimizer/plan/rpr.c
@@ -3,13 +3,13 @@
* rpr.c
* Row Pattern Recognition pattern compilation for planner
*
- * This file contains functions for optimizing RPR pattern AST and
+ * This file contains functions for optimizing the RPR pattern parse tree and
* compiling it to a flat element array for NFA execution by WindowAgg.
*
* Key components:
* 1. Pattern Optimization: Simplifies patterns before compilation
* (e.g., flatten nested SEQ/ALT, merge consecutive vars)
- * 2. Pattern Compilation: Converts AST to flat element array for NFA
+ * 2. Pattern Compilation: Converts parse tree to flat element array for NFA
* 3. Absorption Analysis: Computes flags for O(n^2)->O(n) optimization
*
* Context Absorption Optimization:
@@ -995,7 +995,7 @@ collectDefineVariables(List *defineVariableList, char **varNames)
/*
* scanRPRPatternRecursive
- * Recursively scan pattern AST (pass 1 internal).
+ * Recursively scan pattern parse tree (pass 1 internal).
*
* Collects unique variable names and counts elements while tracking depth.
* Variables from DEFINE clause are already in varNames; this adds any
@@ -1092,7 +1092,7 @@ scanRPRPatternRecursive(RPRPatternNode *node, char **varNames, int *numVars,
/*
* scanRPRPattern
- * Scan pattern AST (pass 1 entry point).
+ * Scan pattern parse tree (pass 1 entry point).
*
* Collects unique variable names (appending to those from DEFINE clause),
* counts total elements (including FIN marker), and tracks maximum depth.
@@ -1297,7 +1297,7 @@ fillRPRPatternGroup(RPRPatternNode *node, RPRPattern *pat, int *idx, RPRDepth de
* fillRPRPatternAlt
* Fill an ALT pattern and its alternatives.
*
- * Creates ALT_START marker, fills each alternative at increased depth,
+ * 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
@@ -1383,9 +1383,10 @@ fillRPRPatternAlt(RPRPatternNode *node, RPRPattern *pat, int *idx, RPRDepth dept
/*
* fillRPRPattern
- * Fill pattern elements array from AST (pass 2).
+ * Fill pattern elements array from parse tree (pass 2).
*
- * Recursively traverses AST and populates pre-allocated elements array.
+ * Recursively traverses the parse tree and populates pre-allocated elements
+ * array.
* Dispatches to type-specific fill functions.
*
* Returns true if the pattern is nullable (can match zero rows).
@@ -1767,7 +1768,7 @@ computeAbsorbabilityRecursive(RPRPattern *pattern, RPRElemIdx startIdx,
}
else
{
- /* Should never reach END - structural invariant of pattern AST */
+ /* Should never reach END - structural invariant of pattern parse tree */
Assert(!RPRElemIsEnd(elem));
/* Non-ALT, non-BEGIN: check if unbounded start */
@@ -1894,13 +1895,13 @@ validate_rpr_define_volatility(List *defineClause)
/*
* buildRPRPattern
- * Compile pattern AST to flat bytecode array.
+ * Compile pattern parse tree to flat bytecode array.
*
* Compilation phases:
- * 1. Optimize AST (flatten, merge, deduplicate)
+ * 1. Optimize parse tree (flatten, merge, deduplicate)
* 2. Scan: collect variables, count elements (pass 1)
* 3. Allocate result structure
- * 4. Fill elements from AST (pass 2)
+ * 4. Fill elements from parse tree (pass 2)
* 5. Finalize pattern structure
* 6. Compute context absorption eligibility
*
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4ae951aaeba..6eb01ea7f0b 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -17734,9 +17734,12 @@ row_pattern_primary:
;
row_pattern_quantifier_opt:
- /* EMPTY - no quantifier means exactly once; @$ is unused since
- * min=max=1 never produces an error */
+ /*EMPTY*/
{
+ /*
+ * no quantifier means exactly once; @$ is unused since
+ * min=max=1 never produces an error
+ */
$$ = (Node *) makeRPRQuantifier(1, 1, false, @$);
}
| '*'
diff --git a/src/backend/parser/parse_cte.c b/src/backend/parser/parse_cte.c
index 3e493beba0b..c35feeae6fd 100644
--- a/src/backend/parser/parse_cte.c
+++ b/src/backend/parser/parse_cte.c
@@ -1303,9 +1303,7 @@ checkWellFormedSelectStmt(SelectStmt *stmt, CteState *cstate)
/*
* contain_rpr_walker
* Returns true if the raw parse tree contains any <row pattern common
- * syntax> -- i.e., any WindowDef with PATTERN/DEFINE attached. Used
- * by transformWithClause() to enforce ISO/IEC 9075-2:2016 7.17 SR 3)f)
- * on WITH RECURSIVE elements.
+ * syntax> -- i.e., any WindowDef with PATTERN/DEFINE attached.
*/
static bool
contain_rpr_walker(Node *node, void *context)
diff --git a/src/backend/parser/parse_rpr.c b/src/backend/parser/parse_rpr.c
index 7c201d55164..ed12190cb06 100644
--- a/src/backend/parser/parse_rpr.c
+++ b/src/backend/parser/parse_rpr.c
@@ -8,7 +8,7 @@
* - Validates frame options (must start at CURRENT ROW, no EXCLUDE)
* - Validates PATTERN variable count (max RPR_VARID_MAX + 1)
* - Transforms DEFINE clause
- * - Stores the PATTERN AST and the SKIP TO/INITIAL flags
+ * - Stores the PATTERN parse tree and the SKIP TO/INITIAL flags
*
* Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
@@ -67,7 +67,7 @@ static bool define_walker(Node *node, void *context);
* - Set AFTER MATCH SKIP TO flag
* - Set SEEK/INITIAL flag
* - Transforms DEFINE clause into TargetEntry list
- * - Stores PATTERN AST for deparsing (optimization happens in planner)
+ * - Stores PATTERN parse tree for deparsing (optimization happens in planner)
*
* Returns early if windef has no rpCommonSyntax (non-RPR window).
*/
@@ -180,7 +180,7 @@ transformRPR(ParseState *pstate, WindowClause *wc, WindowDef *windef,
/* Transform DEFINE clause into list of TargetEntry's */
wc->defineClause = transformDefineClause(pstate, windef, targetlist);
- /* Store PATTERN AST for deparsing */
+ /* Store PATTERN parse tree for deparsing */
wc->rpPattern = windef->rpCommonSyntax->rpPattern;
}
@@ -264,19 +264,6 @@ validateRPRPatternVarCount(ParseState *pstate, RPRPatternNode *node,
* transformDefineClause
* Process DEFINE clause and transform ResTarget into list of TargetEntry.
*
- * First:
- * 1. Validates PATTERN variable count and collects RPR variable names
- * 2. Rejects DEFINE variables not used in PATTERN
- * 3. Checks for duplicate variable names in DEFINE clause
- *
- * Then for each DEFINE variable:
- * 4. Transforms expression via transformExpr() and coerces it to boolean
- * 5. Creates defineClause entry with proper resname (pattern variable name)
- * 6. Ensures referenced Var nodes are present in the query targetlist (via
- * pull_var_clause)
- *
- * Finally marks column origins and assigns collation information.
- *
* Note: Variables not in DEFINE are evaluated as TRUE by the executor.
* Variables in DEFINE but not in PATTERN are rejected as an error.
*
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 947e668020e..f28215b8e83 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -621,7 +621,7 @@ typedef enum RPRPatternNodeType
} RPRPatternNodeType;
/*
- * RPRPatternNode - Row Pattern Recognition pattern AST node
+ * RPRPatternNode - Row Pattern Recognition pattern parse tree node
*/
typedef struct RPRPatternNode
{
@@ -630,7 +630,7 @@ typedef struct RPRPatternNode
int32 min; /* minimum repetitions (0 for *, ?) */
int32 max; /* maximum repetitions (PG_INT32_MAX for *, +) */
bool reluctant; /* true for reluctant (non-greedy) */
- ParseLoc location; /* token location, or -1 */
+ ParseLoc location; /* token location, or -1 if unknown */
char *varName; /* VAR: variable name */
List *children; /* SEQ, ALT, GROUP: child nodes */
@@ -652,10 +652,10 @@ typedef struct RPCommonSyntax
RPSkipTo rpSkipTo; /* Row Pattern AFTER MATCH SKIP type */
bool initial; /* true if <row pattern initial or seek> is
* initial */
- RPRPatternNode *rpPattern; /* PATTERN clause AST */
+ RPRPatternNode *rpPattern; /* PATTERN parse tree */
List *rpDefs; /* row pattern definitions clause (list of
* ResTarget) */
- ParseLoc location; /* PATTERN keyword location, or -1 */
+ ParseLoc location; /* PATTERN keyword location, or -1 if unknown */
} RPCommonSyntax;
/*
@@ -1727,7 +1727,7 @@ typedef struct GroupingSet
* options are never copied, per spec.
* "defineClause" is Row Pattern Recognition DEFINE clause (list of
* TargetEntry). TargetEntry.resname represents row pattern definition
- * variable name. "rpPattern" represents PATTERN clause as an AST tree
+ * variable name. "rpPattern" represents the PATTERN clause as a parse tree
* (RPRPatternNode).
*
*/
@@ -1763,7 +1763,7 @@ typedef struct WindowClause
* initial */
/* Row Pattern DEFINE clause (list of TargetEntry) */
List *defineClause;
- /* Row Pattern PATTERN clause AST */
+ /* Row Pattern PATTERN parse tree */
RPRPatternNode *rpPattern;
} WindowClause;
diff --git a/src/include/optimizer/rpr.h b/src/include/optimizer/rpr.h
index 83d3cd29b07..f5462ab2a7c 100644
--- a/src/include/optimizer/rpr.h
+++ b/src/include/optimizer/rpr.h
@@ -27,8 +27,15 @@
* before release.
*/
#define RPR_VARID_MAX 0xEF /* pattern variables are 0 to 0xEF */
+
+/*
+ * 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.
+ */
#define RPR_QUANTITY_INF PG_INT32_MAX /* unbounded quantifier */
-#define RPR_COUNT_MAX PG_INT32_MAX /* max runtime count (NFA state) */
+#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 */
#define RPR_DEPTH_MAX (PG_UINT8_MAX - 1) /* max pattern nesting depth:
diff --git a/src/test/regress/expected/rpr_explain.out b/src/test/regress/expected/rpr_explain.out
index cc86d0aae30..8672b4c3055 100644
--- a/src/test/regress/expected/rpr_explain.out
+++ b/src/test/regress/expected/rpr_explain.out
@@ -5598,13 +5598,15 @@ EXPLAIN (COSTS OFF) SELECT * FROM rpr_ev_opt_mixed;
-- find_window_run_conditions() pushes WHERE filters on monotonic window
-- functions into WindowAgg as Run Conditions for early termination.
-- With RPR's required frame (ROWS BETWEEN CURRENT ROW AND UNBOUNDED
--- FOLLOWING), the monotonic direction determines which operators trigger
--- Run Condition pushdown:
+-- FOLLOWING), the monotonic direction of the window function determines
+-- which comparison operators allow pushdown:
-- INCREASING (<=): row_number, rank, dense_rank, percent_rank,
-- cume_dist, ntile
--- DECREASING (>): count(*) (via int8inc, END_UNBOUNDED_FOLLOWING)
--- RPR window function results are match-dependent, not monotonic.
--- Test with count(*) > 0 as representative case.
+-- DECREASING (>): count(*). As the current row advances, the frame
+-- (which ends at UNBOUNDED FOLLOWING) shrinks, so the
+-- count decreases.
+-- RPR window function results are match-dependent, not monotonic, so this
+-- pushdown does not apply. Test with count(*) > 0 as a representative case.
--
-- Without RPR: count(*) > 0 is pushed down as Run Condition
EXPLAIN (COSTS OFF)
diff --git a/src/test/regress/sql/rpr_explain.sql b/src/test/regress/sql/rpr_explain.sql
index 297ca78d54b..115402e304d 100644
--- a/src/test/regress/sql/rpr_explain.sql
+++ b/src/test/regress/sql/rpr_explain.sql
@@ -3174,13 +3174,15 @@ EXPLAIN (COSTS OFF) SELECT * FROM rpr_ev_opt_mixed;
-- find_window_run_conditions() pushes WHERE filters on monotonic window
-- functions into WindowAgg as Run Conditions for early termination.
-- With RPR's required frame (ROWS BETWEEN CURRENT ROW AND UNBOUNDED
--- FOLLOWING), the monotonic direction determines which operators trigger
--- Run Condition pushdown:
+-- FOLLOWING), the monotonic direction of the window function determines
+-- which comparison operators allow pushdown:
-- INCREASING (<=): row_number, rank, dense_rank, percent_rank,
-- cume_dist, ntile
--- DECREASING (>): count(*) (via int8inc, END_UNBOUNDED_FOLLOWING)
--- RPR window function results are match-dependent, not monotonic.
--- Test with count(*) > 0 as representative case.
+-- DECREASING (>): count(*). As the current row advances, the frame
+-- (which ends at UNBOUNDED FOLLOWING) shrinks, so the
+-- count decreases.
+-- RPR window function results are match-dependent, not monotonic, so this
+-- pushdown does not apply. Test with count(*) > 0 as a representative case.
--
-- Without RPR: count(*) > 0 is pushed down as Run Condition
--
2.50.1 (Apple Git-155)