v47-0006-refactor-transformDefineClause.nocfbot
application/octet-stream
Filename: v47-0006-refactor-transformDefineClause.nocfbot
Type: application/octet-stream
Part: 2
Message:
Re: Row pattern recognition
From 86e97a060e4d4182abc026540f667fbca7b18d95 Mon Sep 17 00:00:00 2001
From: jian he <jian.universality@gmail.com>
Date: Fri, 12 Jun 2026 18:49:17 +0800
Subject: [PATCH v47 6/6] refactor transformDefineClause
The allowed nav-expression combination is limited, therefore, has_column_ref is
not necessary. Column reference checks can use contain_var_clause, and it's
cheap.
Also, I made the following error message change:
-ERROR: row pattern navigation offset must be a run-time constant
+ERROR: row pattern navigation offset expression must not contain column references
---
src/backend/parser/parse_rpr.c | 102 +++++++-----------------------
src/test/regress/expected/rpr.out | 6 +-
2 files changed, 26 insertions(+), 82 deletions(-)
diff --git a/src/backend/parser/parse_rpr.c b/src/backend/parser/parse_rpr.c
index 59624c7a46..c4d5449ea6 100644
--- a/src/backend/parser/parse_rpr.c
+++ b/src/backend/parser/parse_rpr.c
@@ -38,8 +38,6 @@ typedef enum
{
DEFINE_PHASE_BODY, /* top-level DEFINE expression */
DEFINE_PHASE_NAV_ARG, /* inside an outer nav's arg subtree */
- DEFINE_PHASE_NAV_OFFSET, /* inside an outer nav's offset_arg /
- * compound_offset_arg */
} DefinePhase;
typedef struct
@@ -47,7 +45,6 @@ typedef struct
ParseState *pstate;
DefinePhase phase;
int nav_count; /* RPRNavExpr nodes seen in current nav.arg */
- bool has_column_ref; /* Var seen in current nav scope */
RPRNavKind inner_kind; /* kind of first nested nav in current arg */
} DefineWalkCtx;
@@ -427,7 +424,6 @@ transformDefineClause(ParseState *pstate, WindowClause *wc, WindowDef *windef,
ctx.pstate = pstate;
ctx.phase = DEFINE_PHASE_BODY;
ctx.nav_count = 0;
- ctx.has_column_ref = false;
ctx.inner_kind = 0;
(void) define_walker((Node *) te->expr, &ctx);
}
@@ -491,12 +487,6 @@ define_walker(Node *node, void *context)
if (node == NULL)
return false;
- /* Var sighting feeds the column-ref rule for the enclosing nav scope. */
- if (IsA(node, Var) &&
- (ctx->phase == DEFINE_PHASE_NAV_ARG ||
- ctx->phase == DEFINE_PHASE_NAV_OFFSET))
- ctx->has_column_ref = true;
-
if (IsA(node, RPRNavExpr))
{
RPRNavExpr *nav = (RPRNavExpr *) node;
@@ -511,33 +501,39 @@ define_walker(Node *node, void *context)
if (ctx->nav_count == 0)
ctx->inner_kind = nav->kind;
ctx->nav_count++;
+
+ if (contain_var_clause((Node *) nav->offset_arg))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("row pattern navigation offset expression must not contain column references"),
+ parser_errposition(ctx->pstate, exprLocation((Node *) nav->offset_arg)));
+
return expression_tree_walker(node, define_walker, ctx);
}
-
- if (ctx->phase == DEFINE_PHASE_NAV_OFFSET)
+ else
{
/*
- * Navs inside offset_arg are unusual but not directly banned; the
- * constant-offset rule will catch any Var or volatile they
- * contain.
+ * PHASE_BODY: this is an outer nav at top level. Walk arg first
+ * to collect nesting / column-ref state, then validate and (for
+ * compound forms) flatten, then walk offset(s).
*/
- return expression_tree_walker(node, define_walker, ctx);
- }
-
- /*
- * PHASE_BODY: this is an outer nav at top level. Walk arg first to
- * collect nesting / column-ref state, then validate and (for compound
- * forms) flatten, then walk offset(s).
- */
- {
- DefineWalkCtx saved = *ctx;
bool outer_phys = (nav->kind == RPR_NAV_PREV ||
nav->kind == RPR_NAV_NEXT);
- bool flattened = false;
+
+ if (!contain_var_clause((Node *) nav->arg))
+ 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));
+
+ if (contain_var_clause((Node *) nav->offset_arg))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("row pattern navigation offset expression must not contain column references"),
+ parser_errposition(ctx->pstate, exprLocation((Node *) nav->offset_arg)));
ctx->phase = DEFINE_PHASE_NAV_ARG;
ctx->nav_count = 0;
- ctx->has_column_ref = false;
ctx->inner_kind = 0;
(void) define_walker((Node *) nav->arg, ctx);
@@ -579,17 +575,6 @@ define_walker(Node *node, void *context)
nav->offset_arg = inner->offset_arg;
nav->compound_offset_arg = nav->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,
@@ -610,48 +595,7 @@ define_walker(Node *node, void *context)
errhint("Only PREV(FIRST()), PREV(LAST()), NEXT(FIRST()), and NEXT(LAST()) compound forms are allowed."),
parser_errposition(ctx->pstate, nav->location));
}
- else 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));
- }
- /*
- * Walk offset arg(s) in PHASE_NAV_OFFSET to enforce the
- * constant-offset rule. For compound forms, both the inner
- * (post-flatten nav->offset_arg) and outer (compound_offset_arg)
- * offsets must be constants; the inner's column-ref status was
- * not separately tracked during the PHASE_NAV_ARG walk (which
- * only checks that nav.arg as a whole has at least one Var), so
- * it is re-walked here to catch column references the inner
- * offset would have leaked.
- */
- ctx->phase = DEFINE_PHASE_NAV_OFFSET;
-
- if (nav->offset_arg != NULL)
- {
- ctx->has_column_ref = false;
- (void) define_walker((Node *) nav->offset_arg, ctx);
- if (ctx->has_column_ref)
- ereport(ERROR,
- errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("row pattern navigation offset must be a run-time constant"),
- parser_errposition(ctx->pstate, exprLocation((Node *) nav->offset_arg)));
- }
- if (flattened && nav->compound_offset_arg != NULL)
- {
- ctx->has_column_ref = false;
- (void) define_walker((Node *) nav->compound_offset_arg, ctx);
- if (ctx->has_column_ref)
- ereport(ERROR,
- errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("row pattern navigation offset must be a run-time constant"),
- parser_errposition(ctx->pstate, exprLocation((Node *) nav->compound_offset_arg)));
- }
-
- *ctx = saved;
return false;
}
}
diff --git a/src/test/regress/expected/rpr.out b/src/test/regress/expected/rpr.out
index a07104153f..e3fb41459a 100644
--- a/src/test/regress/expected/rpr.out
+++ b/src/test/regress/expected/rpr.out
@@ -1204,7 +1204,7 @@ WINDOW w AS (
PATTERN (A)
DEFINE A AS PREV(price, price) > 0
);
-ERROR: row pattern navigation offset must be a run-time constant
+ERROR: row pattern navigation offset expression must not contain column references
LINE 7: DEFINE A AS PREV(price, price) > 0
^
-- Non-constant offset: column reference in compound inner offset
@@ -1216,7 +1216,7 @@ WINDOW w AS (
PATTERN (A)
DEFINE A AS PREV(LAST(price, price), 2) > 0
);
-ERROR: row pattern navigation offset must be a run-time constant
+ERROR: row pattern navigation offset expression must not contain column references
LINE 7: DEFINE A AS PREV(LAST(price, price), 2) > 0
^
-- Non-constant offset: column reference in compound outer offset
@@ -1228,7 +1228,7 @@ WINDOW w AS (
PATTERN (A)
DEFINE A AS PREV(LAST(price, 1), price) > 0
);
-ERROR: row pattern navigation offset must be a run-time constant
+ERROR: row pattern navigation offset expression must not contain column references
LINE 7: DEFINE A AS PREV(LAST(price, 1), price) > 0
^
-- Non-constant offset: volatile function as offset
--
2.34.1