From 28202cab3c24eeec4793f26d58ce9c59e98a4c2d Mon Sep 17 00:00:00 2001 From: jian he Date: Mon, 10 Aug 2026 23:57:43 +0900 Subject: [PATCH] Drop the always-true RPR initial flag The clause is effectively always INITIAL: the grammar accepts INITIAL or an empty clause and rejects SEEK, so the initial field of RPCommonSyntax and WindowClause was a constant true. The deparser no longer reads it. Drop the field along with its grammar action and %type, its copy in transformRPR, and the redundant term it contributed to the window-dedup equality in optimize_window_clauses. The field carried no query_jumble_ignore, so dropping it shifts the query id of every window clause. The base series added the field, and this submission is not released, so no id anyone has recorded changes. The comments that described the field go with it: the parse_rpr.c file header and the transformRPR summary no longer claim an INITIAL flag is stored or set, and README.rpr drops it from the transcription step and from the structure diagram. While there, the diagram gains the two fields it never listed, RPCommonSyntax.rpDefs and RPRPatternNode.reluctant. The rpr_explain test that compared a window written with INITIAL against one written without it could not fail: the implementation normalizes a bare RPR window to INITIAL -- which is why the field is going away -- and the test looked only at the PATTERN line, where the keyword does not appear. One view without the keyword, printed whole, pins what is actually observable, namely that deparse puts INITIAL back. Also move WindowDef.rpCommonSyntax below the frame fields, where the base series' raw parser patch put the other window fields and where the grammar fills it in: window_specification puts opt_row_pattern_common_syntax after opt_frame_clause. While in parse_rpr.c, complete the define_walker() header. It lists the rules the outer-navigation branch enforces but omits the first check that branch makes: an inner navigation that is not nav.arg itself is reported as not being a direct argument, before the nesting-depth rule is considered. --- src/backend/executor/README.rpr | 5 +- src/backend/optimizer/plan/planner.c | 1 - src/backend/parser/gram.y | 6 +- src/backend/parser/parse_rpr.c | 8 +-- src/include/nodes/parsenodes.h | 6 +- src/test/regress/expected/rpr_explain.out | 85 +++++------------------ src/test/regress/sql/rpr_explain.sql | 42 ++--------- 7 files changed, 30 insertions(+), 123 deletions(-) diff --git a/src/backend/executor/README.rpr b/src/backend/executor/README.rpr index e223a39a309..05bbcd76240 100644 --- a/src/backend/executor/README.rpr +++ b/src/backend/executor/README.rpr @@ -139,7 +139,7 @@ following: positive offset FOLLOWING only) (2) Transcription to WindowClause - - Copies rpPattern, rpSkipTo, initial fields + - Copies the rpPattern and rpSkipTo fields (3) DEFINE clause transformation (transformDefineClause) @@ -1708,10 +1708,11 @@ Appendix A. Data Structure Relationship Diagram -------- RPCommonSyntax |--- rpSkipTo: RPSkipTo - |--- initial: bool + |--- rpDefs: List* (ResTarget) +--- rpPattern: RPRPatternNode* (tree) |--- nodeType: VAR | SEQ | ALT | GROUP |--- min, max: quantifier + |--- reluctant: bool |--- varName: variable name (VAR only) +--- children: List* (SEQ/ALT/GROUP only) diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c index 6993d26488a..c8257faf615 100644 --- a/src/backend/optimizer/plan/planner.c +++ b/src/backend/optimizer/plan/planner.c @@ -6208,7 +6208,6 @@ optimize_window_clauses(PlannerInfo *root, WindowFuncLists *wflists) equal(wc->startOffset, existing_wc->startOffset) && equal(wc->endOffset, existing_wc->endOffset) && wc->rpSkipTo == existing_wc->rpSkipTo && - wc->initial == existing_wc->initial && equal(wc->defineClause, existing_wc->defineClause) && equal(wc->rpPattern, existing_wc->rpPattern)) { diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index c6878e7850f..1031d7ef167 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -741,7 +741,6 @@ static bool rpr_is_quantifier_token(const char *tok); row_pattern_quantifier_opt %type row_pattern_definition_list row_pattern_permute_list %type opt_row_pattern_skip_to -%type opt_row_pattern_initial_or_seek /* * Non-keyword token types. These are hard-wired into the "flex" lexer. @@ -17673,7 +17672,6 @@ opt_row_pattern_skip_to opt_row_pattern_initial_or_seek { RPCommonSyntax *n = makeNode(RPCommonSyntax); n->rpSkipTo = $1; - n->initial = $2; n->rpPattern = (RPRPatternNode *) $5; n->rpDefs = $8; n->location = @3; @@ -17698,7 +17696,7 @@ opt_row_pattern_skip_to: ; opt_row_pattern_initial_or_seek: - INITIAL_P { $$ = true; } + INITIAL_P | SEEK { ereport(ERROR, @@ -17707,7 +17705,7 @@ opt_row_pattern_initial_or_seek: errhint("Use INITIAL instead."), parser_errposition(@1)); } - | /*EMPTY*/ { $$ = true; } + | /*EMPTY*/ ; row_pattern: diff --git a/src/backend/parser/parse_rpr.c b/src/backend/parser/parse_rpr.c index 1779377cf86..6292cd0547f 100644 --- a/src/backend/parser/parse_rpr.c +++ b/src/backend/parser/parse_rpr.c @@ -9,7 +9,7 @@ * EXCLUDE, and CURRENT ROW is not accepted as the frame end) * - Validates PATTERN variable count (max RPR_VARID_MAX + 1) * - Transforms DEFINE clause - * - Stores the PATTERN parse tree and the SKIP TO/INITIAL flags + * - Stores the PATTERN parse tree and the AFTER MATCH SKIP TO flag * * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California @@ -67,7 +67,6 @@ static bool define_walker(Node *node, void *context); * - Validates frame options (ROWS only, must start at CURRENT ROW, no * EXCLUDE, and CURRENT ROW is not accepted as the frame end) * - Set AFTER MATCH SKIP TO flag - * - Set SEEK/INITIAL flag * - Transforms DEFINE clause into TargetEntry list * - Stores PATTERN parse tree for deparsing (optimization happens in planner) * @@ -176,9 +175,6 @@ transformRPR(ParseState *pstate, WindowClause *wc, WindowDef *windef, /* Assign AFTER MATCH SKIP TO flag */ wc->rpSkipTo = windef->rpCommonSyntax->rpSkipTo; - /* Assign INITIAL flag */ - wc->initial = windef->rpCommonSyntax->initial; - /* Transform DEFINE clause into list of TargetEntry's */ wc->defineClause = transformDefineClause(pstate, windef, targetlist); @@ -436,6 +432,8 @@ transformDefineClause(ParseState *pstate, WindowDef *windef, * - PREV/NEXT wrapping FIRST/LAST is flattened in place * to a compound kind (PREV_FIRST, PREV_LAST, NEXT_FIRST, * NEXT_LAST) + * - an inner navigation that is not nav.arg itself is + * rejected as not being a direct argument * - any other nesting is rejected (FIRST(PREV()), * PREV(PREV()), FIRST(FIRST()), three-or-more deep) * [2] for each nav offset (PHASE_NAV_OFFSET): diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index 84ecc4ddc02..df7bd7b6d58 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -643,8 +643,6 @@ typedef struct RPCommonSyntax { NodeTag type; RPSkipTo rpSkipTo; /* Row Pattern AFTER MATCH SKIP type */ - bool initial; /* true if is - * initial */ RPRPatternNode *rpPattern; /* PATTERN parse tree */ List *rpDefs; /* row pattern definitions clause (list of * ResTarget) */ @@ -666,10 +664,10 @@ typedef struct WindowDef char *refname; /* referenced window name, if any */ List *partitionClause; /* PARTITION BY expression list */ List *orderClause; /* ORDER BY (list of SortBy) */ - RPCommonSyntax *rpCommonSyntax; /* row pattern common syntax */ int frameOptions; /* frame_clause options, see below */ Node *startOffset; /* expression for starting bound, if any */ Node *endOffset; /* expression for ending bound, if any */ + RPCommonSyntax *rpCommonSyntax; /* row pattern common syntax */ ParseLoc location; /* parse location, or -1 if none/unknown */ ParseLoc frameLocation; /* ROWS/RANGE/GROUPS location, or -1 */ ParseLoc excludeLocation; /* EXCLUDE location, or -1 */ @@ -1753,8 +1751,6 @@ typedef struct WindowClause bool copiedOrder pg_node_attr(query_jumble_ignore); /* Row Pattern AFTER MATCH SKIP clause */ RPSkipTo rpSkipTo; /* Row Pattern Skip To type */ - bool initial; /* true if is - * initial */ /* Row Pattern DEFINE clause (list of TargetEntry) */ List *defineClause pg_node_attr(custom_query_jumble); /* Row Pattern PATTERN parse tree */ diff --git a/src/test/regress/expected/rpr_explain.out b/src/test/regress/expected/rpr_explain.out index 8b2e8e5b493..ecfe109547a 100644 --- a/src/test/regress/expected/rpr_explain.out +++ b/src/test/regress/expected/rpr_explain.out @@ -3557,48 +3557,10 @@ WINDOW w AS ( -- ============================================================ -- INITIAL vs no INITIAL comparison -- ============================================================ --- With INITIAL keyword -CREATE VIEW rpr_ev_initial_with AS -SELECT count(*) OVER w -FROM generate_series(1, 50) AS s(v) -WINDOW w AS ( - ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING - AFTER MATCH SKIP PAST LAST ROW - INITIAL - PATTERN (A+ B) - DEFINE A AS v % 5 <> 0, B AS v % 5 = 0 -); -SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_ev_initial_with'), E'\n')) AS line WHERE line ~ 'PATTERN'; - line ------------------- - PATTERN (a+ b) -(1 row) - -SELECT rpr_explain_filter(' -EXPLAIN (ANALYZE, BUFFERS OFF, COSTS OFF, TIMING OFF, SUMMARY OFF) -SELECT count(*) OVER w -FROM generate_series(1, 50) AS s(v) -WINDOW w AS ( - ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING - AFTER MATCH SKIP PAST LAST ROW - INITIAL - PATTERN (A+ B) - DEFINE A AS v % 5 <> 0, B AS v % 5 = 0 -);'); - rpr_explain_filter ----------------------------------------------------------------------- - WindowAgg (actual rows=50.00 loops=1) - Window: w AS (ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING) - Pattern: a+# b - Storage: Memory Maximum Storage: NkB - NFA States: 3 peak, 91 total, 0 merged - NFA Contexts: 2 peak, 51 total, 0 pruned - NFA: 10 matched (len 5/5/5.0), 0 mismatched - NFA: 30 absorbed (len 1/1/1.0), 10 skipped (len 1/1/1.0) - -> Function Scan on generate_series s (actual rows=50.00 loops=1) -(9 rows) - --- Without INITIAL keyword (same behavior currently) +-- INITIAL is the only supported mode (SEEK is unimplemented), so omitting the +-- keyword changes nothing at run time and deparse adds it back. Print the +-- whole viewdef: filtering to the PATTERN line would hide the INITIAL literal, +-- which is the only thing this test can pin. CREATE VIEW rpr_ev_initial_without AS SELECT count(*) OVER w FROM generate_series(1, 50) AS s(v) @@ -3608,35 +3570,20 @@ WINDOW w AS ( PATTERN (A+ B) DEFINE A AS v % 5 <> 0, B AS v % 5 = 0 ); -SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_ev_initial_without'), E'\n')) AS line WHERE line ~ 'PATTERN'; - line ------------------- - PATTERN (a+ b) +SELECT pg_get_viewdef('rpr_ev_initial_without'::regclass); + pg_get_viewdef +----------------------------------------------------------------- + SELECT count(*) OVER w AS count + + FROM generate_series(1, 50) s(v) + + WINDOW w AS (ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING+ + AFTER MATCH SKIP PAST LAST ROW + + INITIAL + + PATTERN (a+ b) + + DEFINE + + a AS ((v % 5) <> 0), + + b AS ((v % 5) = 0)); (1 row) -SELECT rpr_explain_filter(' -EXPLAIN (ANALYZE, BUFFERS OFF, COSTS OFF, TIMING OFF, SUMMARY OFF) -SELECT count(*) OVER w -FROM generate_series(1, 50) AS s(v) -WINDOW w AS ( - ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING - AFTER MATCH SKIP PAST LAST ROW - PATTERN (A+ B) - DEFINE A AS v % 5 <> 0, B AS v % 5 = 0 -);'); - rpr_explain_filter ----------------------------------------------------------------------- - WindowAgg (actual rows=50.00 loops=1) - Window: w AS (ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING) - Pattern: a+# b - Storage: Memory Maximum Storage: NkB - NFA States: 3 peak, 91 total, 0 merged - NFA Contexts: 2 peak, 51 total, 0 pruned - NFA: 10 matched (len 5/5/5.0), 0 mismatched - NFA: 30 absorbed (len 1/1/1.0), 10 skipped (len 1/1/1.0) - -> Function Scan on generate_series s (actual rows=50.00 loops=1) -(9 rows) - -- ============================================================ -- Quantifier Variations -- ============================================================ diff --git a/src/test/regress/sql/rpr_explain.sql b/src/test/regress/sql/rpr_explain.sql index ccee047adc9..dd9c74f5474 100644 --- a/src/test/regress/sql/rpr_explain.sql +++ b/src/test/regress/sql/rpr_explain.sql @@ -2002,32 +2002,10 @@ WINDOW w AS ( -- ============================================================ -- INITIAL vs no INITIAL comparison -- ============================================================ - --- With INITIAL keyword -CREATE VIEW rpr_ev_initial_with AS -SELECT count(*) OVER w -FROM generate_series(1, 50) AS s(v) -WINDOW w AS ( - ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING - AFTER MATCH SKIP PAST LAST ROW - INITIAL - PATTERN (A+ B) - DEFINE A AS v % 5 <> 0, B AS v % 5 = 0 -); -SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_ev_initial_with'), E'\n')) AS line WHERE line ~ 'PATTERN'; -SELECT rpr_explain_filter(' -EXPLAIN (ANALYZE, BUFFERS OFF, COSTS OFF, TIMING OFF, SUMMARY OFF) -SELECT count(*) OVER w -FROM generate_series(1, 50) AS s(v) -WINDOW w AS ( - ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING - AFTER MATCH SKIP PAST LAST ROW - INITIAL - PATTERN (A+ B) - DEFINE A AS v % 5 <> 0, B AS v % 5 = 0 -);'); - --- Without INITIAL keyword (same behavior currently) +-- INITIAL is the only supported mode (SEEK is unimplemented), so omitting the +-- keyword changes nothing at run time and deparse adds it back. Print the +-- whole viewdef: filtering to the PATTERN line would hide the INITIAL literal, +-- which is the only thing this test can pin. CREATE VIEW rpr_ev_initial_without AS SELECT count(*) OVER w FROM generate_series(1, 50) AS s(v) @@ -2037,17 +2015,7 @@ WINDOW w AS ( PATTERN (A+ B) DEFINE A AS v % 5 <> 0, B AS v % 5 = 0 ); -SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_ev_initial_without'), E'\n')) AS line WHERE line ~ 'PATTERN'; -SELECT rpr_explain_filter(' -EXPLAIN (ANALYZE, BUFFERS OFF, COSTS OFF, TIMING OFF, SUMMARY OFF) -SELECT count(*) OVER w -FROM generate_series(1, 50) AS s(v) -WINDOW w AS ( - ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING - AFTER MATCH SKIP PAST LAST ROW - PATTERN (A+ B) - DEFINE A AS v % 5 <> 0, B AS v % 5 = 0 -);'); +SELECT pg_get_viewdef('rpr_ev_initial_without'::regclass); -- ============================================================ -- Quantifier Variations