nocfbot-0041-Reject-column-less-compound-navigation-in-row-pat.txt
text/plain
Filename: nocfbot-0041-Reject-column-less-compound-navigation-in-row-pat.txt
Type: text/plain
Part: 40
Message:
Re: Row pattern recognition
From 8112fe3f2c1bee4fc330df0b12d737abd4d27903 Mon Sep 17 00:00:00 2001
From: Henson Choi <assam258@gmail.com>
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)