From 66fb455d42fb333ed70c3ec1bd204bd2df07a159 Mon Sep 17 00:00:00 2001 From: jian he Date: Mon, 10 Aug 2026 15:50:15 +0900 Subject: [PATCH] Report a sibling navigation as not being a direct argument define_walker() tested nesting depth before testing whether the inner navigation is the whole argument, so a sibling navigation such as PREV(FIRST(v) + LAST(v)) was reported as nesting more than two levels deep. It nests only two levels; what disqualifies it is that the inner navigation is not the outer one's direct argument. Swap the tests. Nesting depth is then reported only when the inner navigation is the whole argument, so PREV(FIRST(PREV(v))) keeps its own message while PREV(FIRST(PREV(v)) + 1) now reports the direct-argument one; the tests pin both. Whole-row spellings are out of scope here. ROW(B.*) in a DEFINE never reaches define_walker(): transformExpressionList() sends a trailing star to ExpandColumnRefStar(), which does not classify the qualifier as a pattern variable, so the reference fails in ordinary column resolution instead of with a row pattern diagnostic. Closing that gap belongs with the whole-row gate. --- src/backend/parser/parse_rpr.c | 11 ++++++----- src/test/regress/expected/rpr_base.out | 27 ++++++++++++++++++++++++++ src/test/regress/sql/rpr_base.sql | 21 ++++++++++++++++++++ 3 files changed, 54 insertions(+), 5 deletions(-) diff --git a/src/backend/parser/parse_rpr.c b/src/backend/parser/parse_rpr.c index 005d25b4b60..b0de0e2ab93 100644 --- a/src/backend/parser/parse_rpr.c +++ b/src/backend/parser/parse_rpr.c @@ -535,18 +535,19 @@ define_walker(Node *node, void *context) { RPRNavExpr *inner; - /* Reject triple-or-deeper nesting */ - if (ctx->nav_count > 1) + /* Reject an inner nav that is not the whole argument */ + if (!IsA(nav->arg, RPRNavExpr)) ereport(ERROR, errcode(ERRCODE_SYNTAX_ERROR), - errmsg("cannot nest row pattern navigation more than two levels deep"), + errmsg("row pattern navigation operation must be a direct argument of the outer navigation"), errhint("Only PREV(FIRST()), PREV(LAST()), NEXT(FIRST()), and NEXT(LAST()) compound forms are allowed."), parser_errposition(ctx->pstate, nav->location)); - if (!IsA(nav->arg, RPRNavExpr)) + /* Reject triple-or-deeper nesting; siblings caught above */ + if (ctx->nav_count > 1) ereport(ERROR, errcode(ERRCODE_SYNTAX_ERROR), - errmsg("row pattern navigation operation must be a direct argument of the outer navigation"), + errmsg("cannot nest row pattern navigation more than two levels deep"), errhint("Only PREV(FIRST()), PREV(LAST()), NEXT(FIRST()), and NEXT(LAST()) compound forms are allowed."), parser_errposition(ctx->pstate, nav->location)); diff --git a/src/test/regress/expected/rpr_base.out b/src/test/regress/expected/rpr_base.out index 95e9cdbf363..08e16486d98 100644 --- a/src/test/regress/expected/rpr_base.out +++ b/src/test/regress/expected/rpr_base.out @@ -4523,6 +4523,33 @@ ERROR: cannot nest row pattern navigation more than two levels deep LINE 6: DEFINE A AS PREV(FIRST(PREV(v))) > 0 ^ HINT: Only PREV(FIRST()), PREV(LAST()), NEXT(FIRST()), and NEXT(LAST()) compound forms are allowed. +-- Sibling navigations: prohibited, but they are not a deeper nesting, +-- so the inner navigation must be reported as not being the direct +-- argument rather than as a third level. +SELECT count(*) OVER w +FROM generate_series(1,10) s(v) +WINDOW w AS ( + ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING + PATTERN (A+) + DEFINE A AS PREV(FIRST(v) + LAST(v)) > 0 +); +ERROR: row pattern navigation operation must be a direct argument of the outer navigation +LINE 6: DEFINE A AS PREV(FIRST(v) + LAST(v)) > 0 + ^ +HINT: Only PREV(FIRST()), PREV(LAST()), NEXT(FIRST()), and NEXT(LAST()) compound forms are allowed. +-- Three navigations, but the inner one is again not the whole argument, so +-- that is what gets reported and the depth is not reached +SELECT count(*) OVER w +FROM generate_series(1,10) s(v) +WINDOW w AS ( + ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING + PATTERN (A+) + DEFINE A AS PREV(FIRST(PREV(v)) + 1) > 0 +); +ERROR: row pattern navigation operation must be a direct argument of the outer navigation +LINE 6: DEFINE A AS PREV(FIRST(PREV(v)) + 1) > 0 + ^ +HINT: Only PREV(FIRST()), PREV(LAST()), NEXT(FIRST()), and NEXT(LAST()) compound forms are allowed. -- A navigation offset must be a run-time constant, not a navigation operation SELECT count(*) OVER w FROM generate_series(1,10) s(v) diff --git a/src/test/regress/sql/rpr_base.sql b/src/test/regress/sql/rpr_base.sql index 5cb43d8cabd..cc9e8be89a4 100644 --- a/src/test/regress/sql/rpr_base.sql +++ b/src/test/regress/sql/rpr_base.sql @@ -2941,6 +2941,27 @@ WINDOW w AS ( DEFINE A AS PREV(FIRST(PREV(v))) > 0 ); +-- Sibling navigations: prohibited, but they are not a deeper nesting, +-- so the inner navigation must be reported as not being the direct +-- argument rather than as a third level. +SELECT count(*) OVER w +FROM generate_series(1,10) s(v) +WINDOW w AS ( + ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING + PATTERN (A+) + DEFINE A AS PREV(FIRST(v) + LAST(v)) > 0 +); + +-- Three navigations, but the inner one is again not the whole argument, so +-- that is what gets reported and the depth is not reached +SELECT count(*) OVER w +FROM generate_series(1,10) s(v) +WINDOW w AS ( + ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING + PATTERN (A+) + DEFINE A AS PREV(FIRST(PREV(v)) + 1) > 0 +); + -- A navigation offset must be a run-time constant, not a navigation operation SELECT count(*) OVER w FROM generate_series(1,10) s(v)