From 8112fe3f2c1bee4fc330df0b12d737abd4d27903 Mon Sep 17 00:00:00 2001 From: Henson Choi Date: Tue, 2 Jun 2026 00:05:05 +0900 Subject: [PATCH 41/68] Reject column-less compound navigation in row pattern recognition A row pattern navigation argument must contain at least one column reference. The check was applied only to simple navigation; a compound form such as PREV(FIRST(1)) took the nesting-validation/flatten branch in parse_rpr.c's define_walker, which never re-checked has_column_ref. So column-less compound navigation -- PREV(FIRST(1)), NEXT(LAST(1 + 2)), PREV(FIRST(1), 2), and so on -- was silently accepted, while the simple PREV(1) was correctly rejected. Apply the same has_column_ref check after flattening a compound navigation, so both forms are rejected consistently. A compound form whose argument references a column, such as PREV(FIRST(price)), is unaffected. Add regression tests covering column-less compound navigation with the offset on the inner nav, the outer nav, both, or neither. Per a report from a static analysis tool. --- src/backend/parser/parse_rpr.c | 10 +++++ src/test/regress/expected/rpr.out | 62 +++++++++++++++++++++++++++++++ src/test/regress/sql/rpr.sql | 52 ++++++++++++++++++++++++++ 3 files changed, 124 insertions(+) diff --git a/src/backend/parser/parse_rpr.c b/src/backend/parser/parse_rpr.c index fa8c375f48b..6f44edfc50b 100644 --- a/src/backend/parser/parse_rpr.c +++ b/src/backend/parser/parse_rpr.c @@ -641,6 +641,16 @@ define_walker(Node *node, void *context) nav->offset_arg = inner->offset_arg; nav->arg = inner->arg; flattened = true; + + /* + * The flattened argument must include a column reference, + * just like the simple-nav case below. + */ + if (!ctx->has_column_ref) + ereport(ERROR, + errcode(ERRCODE_SYNTAX_ERROR), + errmsg("argument of row pattern navigation operation must include at least one column reference"), + parser_errposition(ctx->pstate, nav->location)); } else if (!outer_phys && inner_phys) ereport(ERROR, diff --git a/src/test/regress/expected/rpr.out b/src/test/regress/expected/rpr.out index 8793dda3cc3..550113700a9 100644 --- a/src/test/regress/expected/rpr.out +++ b/src/test/regress/expected/rpr.out @@ -1133,6 +1133,68 @@ WINDOW w AS ( ERROR: argument of row pattern navigation operation must include at least one column reference LINE 7: DEFINE A AS PREV(1, 1) > 0 ^ +-- Compound navigation without a column reference must be rejected too, +-- consistent with the simple forms above. +-- PREV(FIRST(1)): compound, constant only, no column reference +SELECT price FROM stock +WINDOW w AS ( + PARTITION BY company + ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING + INITIAL + PATTERN (A) + DEFINE A AS PREV(FIRST(1)) > 0 +); +ERROR: argument of row pattern navigation operation must include at least one column reference +LINE 7: DEFINE A AS PREV(FIRST(1)) > 0 + ^ +-- NEXT(LAST(1 + 2)): compound, constant expression, no column reference +SELECT price FROM stock +WINDOW w AS ( + PARTITION BY company + ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING + INITIAL + PATTERN (A) + DEFINE A AS NEXT(LAST(1 + 2)) > 0 +); +ERROR: argument of row pattern navigation operation must include at least one column reference +LINE 7: DEFINE A AS NEXT(LAST(1 + 2)) > 0 + ^ +-- PREV(FIRST(1, 2)): compound, two-arg inner, no column reference +SELECT price FROM stock +WINDOW w AS ( + PARTITION BY company + ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING + INITIAL + PATTERN (A) + DEFINE A AS PREV(FIRST(1, 2)) > 0 +); +ERROR: argument of row pattern navigation operation must include at least one column reference +LINE 7: DEFINE A AS PREV(FIRST(1, 2)) > 0 + ^ +-- PREV(FIRST(1), 2): compound, outer offset only, no column reference +SELECT price FROM stock +WINDOW w AS ( + PARTITION BY company + ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING + INITIAL + PATTERN (A) + DEFINE A AS PREV(FIRST(1), 2) > 0 +); +ERROR: argument of row pattern navigation operation must include at least one column reference +LINE 7: DEFINE A AS PREV(FIRST(1), 2) > 0 + ^ +-- PREV(FIRST(1, 2), 3): compound, inner and outer offsets, no column reference +SELECT price FROM stock +WINDOW w AS ( + PARTITION BY company + ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING + INITIAL + PATTERN (A) + DEFINE A AS PREV(FIRST(1, 2), 3) > 0 +); +ERROR: argument of row pattern navigation operation must include at least one column reference +LINE 7: DEFINE A AS PREV(FIRST(1, 2), 3) > 0 + ^ -- Non-constant offset: column reference as offset SELECT price FROM stock WINDOW w AS ( diff --git a/src/test/regress/sql/rpr.sql b/src/test/regress/sql/rpr.sql index e4790f75b0a..0aa17f01e84 100644 --- a/src/test/regress/sql/rpr.sql +++ b/src/test/regress/sql/rpr.sql @@ -531,6 +531,58 @@ WINDOW w AS ( DEFINE A AS PREV(1, 1) > 0 ); +-- Compound navigation without a column reference must be rejected too, +-- consistent with the simple forms above. +-- PREV(FIRST(1)): compound, constant only, no column reference +SELECT price FROM stock +WINDOW w AS ( + PARTITION BY company + ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING + INITIAL + PATTERN (A) + DEFINE A AS PREV(FIRST(1)) > 0 +); + +-- NEXT(LAST(1 + 2)): compound, constant expression, no column reference +SELECT price FROM stock +WINDOW w AS ( + PARTITION BY company + ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING + INITIAL + PATTERN (A) + DEFINE A AS NEXT(LAST(1 + 2)) > 0 +); + +-- PREV(FIRST(1, 2)): compound, two-arg inner, no column reference +SELECT price FROM stock +WINDOW w AS ( + PARTITION BY company + ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING + INITIAL + PATTERN (A) + DEFINE A AS PREV(FIRST(1, 2)) > 0 +); + +-- PREV(FIRST(1), 2): compound, outer offset only, no column reference +SELECT price FROM stock +WINDOW w AS ( + PARTITION BY company + ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING + INITIAL + PATTERN (A) + DEFINE A AS PREV(FIRST(1), 2) > 0 +); + +-- PREV(FIRST(1, 2), 3): compound, inner and outer offsets, no column reference +SELECT price FROM stock +WINDOW w AS ( + PARTITION BY company + ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING + INITIAL + PATTERN (A) + DEFINE A AS PREV(FIRST(1, 2), 3) > 0 +); + -- Non-constant offset: column reference as offset SELECT price FROM stock WINDOW w AS ( -- 2.50.1 (Apple Git-155)