nocfbot-0056-Reject-invalid-column-references-in-row-pattern-D.txt
text/plain
Filename: nocfbot-0056-Reject-invalid-column-references-in-row-pattern-D.txt
Type: text/plain
Part: 55
Message:
Re: Row pattern recognition
From 37ae9552adb03842984f4305b9b72bb26af3591d Mon Sep 17 00:00:00 2001
From: Henson Choi <assam258@gmail.com>
Date: Sun, 7 Jun 2026 18:27:16 +0900
Subject: [PATCH 56/68] Reject invalid column references in row pattern DEFINE
clauses
A DEFINE expression could reference an outer query's column (a correlated
reference), which surfaced as the internal "Upper-level Var found where not
expected" error (XX000) raised by pull_var_clause. Separately, a schema- or
catalog-qualified column reference (three or more name parts) slipped past
the existing two-part classifier and was wrongly accepted.
Reject both in transformColumnRef, right after the reference is resolved: an
outer-level Var (varlevelsup > 0) with ERRCODE_FEATURE_NOT_SUPPORTED, and a
reference with three or more name parts with ERRCODE_SYNTAX_ERROR. The latter
uses "qualified expression" wording because a composite-typed qualifier
(e.g. "a.items" in (a.items).amount) is not a column name.
---
src/backend/parser/parse_expr.c | 24 ++++++++++
src/test/regress/expected/rpr.out | 74 +++++++++++++++++++++++++++++++
src/test/regress/sql/rpr.sql | 61 +++++++++++++++++++++++++
3 files changed, 159 insertions(+)
diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c
index 2344aaef9ae..f65a270d20e 100644
--- a/src/backend/parser/parse_expr.c
+++ b/src/backend/parser/parse_expr.c
@@ -947,6 +947,30 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref)
}
}
+ /*
+ * Restrict column references in a row pattern DEFINE clause. node is now
+ * a successfully resolved reference, so reject the two forms RPR does not
+ * allow: a correlated reference to an outer query's column, and a
+ * schema/catalog-qualified reference (three or more name parts). Simple
+ * two-part qualifiers (pattern or range variable) are handled earlier,
+ * before resolution.
+ */
+ if (pstate->p_expr_kind == EXPR_KIND_RPR_DEFINE)
+ {
+ if (IsA(node, Var) && ((Var *) node)->varlevelsup > 0)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot use outer query column in DEFINE clause"),
+ parser_errposition(pstate, cref->location));
+
+ if (list_length(cref->fields) >= 3)
+ ereport(ERROR,
+ errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("qualified expression \"%s\" is not allowed in DEFINE clause",
+ NameListToString(cref->fields)),
+ parser_errposition(pstate, cref->location));
+ }
+
return node;
}
diff --git a/src/test/regress/expected/rpr.out b/src/test/regress/expected/rpr.out
index 550113700a9..1b409b923dd 100644
--- a/src/test/regress/expected/rpr.out
+++ b/src/test/regress/expected/rpr.out
@@ -1295,6 +1295,80 @@ ERROR: volatile functions are not allowed in DEFINE clause
LINE 7: DEFINE A AS price > nextval('rpr_seq')
^
DROP SEQUENCE rpr_seq;
+-- DEFINE cannot reference an outer query's column. A correlated outer
+-- reference must produce a clean error, not the internal "Upper-level Var"
+-- elog that pull_var_clause would otherwise raise.
+-- Qualified outer reference (o.threshold):
+SELECT * FROM (VALUES (95)) AS o(threshold),
+LATERAL (
+ SELECT price FROM stock
+ WINDOW w AS (
+ PARTITION BY company
+ ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
+ INITIAL
+ PATTERN (A)
+ DEFINE A AS price > o.threshold
+ )
+) s;
+ERROR: cannot use outer query column in DEFINE clause
+LINE 9: DEFINE A AS price > o.threshold
+ ^
+-- Unqualified name resolving to the outer column (threshold):
+SELECT * FROM (VALUES (95)) AS o(threshold),
+LATERAL (
+ SELECT price FROM stock
+ WINDOW w AS (
+ PARTITION BY company
+ ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
+ INITIAL
+ PATTERN (A)
+ DEFINE A AS price > threshold
+ )
+) s;
+ERROR: cannot use outer query column in DEFINE clause
+LINE 9: DEFINE A AS price > threshold
+ ^
+-- Outer reference inside a navigation argument is rejected too:
+SELECT * FROM (VALUES (95)) AS o(threshold),
+LATERAL (
+ SELECT price FROM stock
+ WINDOW w AS (
+ PARTITION BY company ORDER BY tdate
+ ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
+ PATTERN (A+)
+ DEFINE A AS PREV(o.threshold, 1) > 0
+ )
+) s;
+ERROR: cannot use outer query column in DEFINE clause
+LINE 8: DEFINE A AS PREV(o.threshold, 1) > 0
+ ^
+-- DEFINE rejects a schema-qualified column reference (three or more name
+-- parts) once it resolves; the qualified form itself is not allowed. (stock
+-- is a temp table, so it is qualified with pg_temp here.)
+-- 3-part (schema.table.column):
+SELECT price FROM stock
+WINDOW w AS (
+ PARTITION BY company
+ ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
+ INITIAL
+ PATTERN (A)
+ DEFINE A AS pg_temp.stock.price > 0
+);
+ERROR: qualified expression "pg_temp.stock.price" is not allowed in DEFINE clause
+LINE 7: DEFINE A AS pg_temp.stock.price > 0
+ ^
+-- whole-row variant (schema.table.*):
+SELECT price FROM stock
+WINDOW w AS (
+ PARTITION BY company
+ ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
+ INITIAL
+ PATTERN (A)
+ DEFINE A AS (pg_temp.stock.*) IS NOT NULL
+);
+ERROR: qualified expression "pg_temp.stock.*" is not allowed in DEFINE clause
+LINE 7: DEFINE A AS (pg_temp.stock.*) IS NOT NULL
+ ^
--
-- 2-arg PREV/NEXT: functional tests
--
diff --git a/src/test/regress/sql/rpr.sql b/src/test/regress/sql/rpr.sql
index 0aa17f01e84..56dff9b6725 100644
--- a/src/test/regress/sql/rpr.sql
+++ b/src/test/regress/sql/rpr.sql
@@ -667,6 +667,67 @@ WINDOW w AS (
);
DROP SEQUENCE rpr_seq;
+-- DEFINE cannot reference an outer query's column. A correlated outer
+-- reference must produce a clean error, not the internal "Upper-level Var"
+-- elog that pull_var_clause would otherwise raise.
+-- Qualified outer reference (o.threshold):
+SELECT * FROM (VALUES (95)) AS o(threshold),
+LATERAL (
+ SELECT price FROM stock
+ WINDOW w AS (
+ PARTITION BY company
+ ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
+ INITIAL
+ PATTERN (A)
+ DEFINE A AS price > o.threshold
+ )
+) s;
+-- Unqualified name resolving to the outer column (threshold):
+SELECT * FROM (VALUES (95)) AS o(threshold),
+LATERAL (
+ SELECT price FROM stock
+ WINDOW w AS (
+ PARTITION BY company
+ ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
+ INITIAL
+ PATTERN (A)
+ DEFINE A AS price > threshold
+ )
+) s;
+-- Outer reference inside a navigation argument is rejected too:
+SELECT * FROM (VALUES (95)) AS o(threshold),
+LATERAL (
+ SELECT price FROM stock
+ WINDOW w AS (
+ PARTITION BY company ORDER BY tdate
+ ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
+ PATTERN (A+)
+ DEFINE A AS PREV(o.threshold, 1) > 0
+ )
+) s;
+
+-- DEFINE rejects a schema-qualified column reference (three or more name
+-- parts) once it resolves; the qualified form itself is not allowed. (stock
+-- is a temp table, so it is qualified with pg_temp here.)
+-- 3-part (schema.table.column):
+SELECT price FROM stock
+WINDOW w AS (
+ PARTITION BY company
+ ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
+ INITIAL
+ PATTERN (A)
+ DEFINE A AS pg_temp.stock.price > 0
+);
+-- whole-row variant (schema.table.*):
+SELECT price FROM stock
+WINDOW w AS (
+ PARTITION BY company
+ ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
+ INITIAL
+ PATTERN (A)
+ DEFINE A AS (pg_temp.stock.*) IS NOT NULL
+);
+
--
-- 2-arg PREV/NEXT: functional tests
--
--
2.50.1 (Apple Git-155)