v47-0005-refactor-transformDefineClause.nocfbot
application/octet-stream
Filename: v47-0005-refactor-transformDefineClause.nocfbot
Type: application/octet-stream
Part: 4
Message:
Re: Row pattern recognition
From b32fc75387f499f42d8ab7c5c2491f3cb3a3c90c Mon Sep 17 00:00:00 2001
From: jian he <jian.universality@gmail.com>
Date: Fri, 12 Jun 2026 18:33:37 +0800
Subject: [PATCH v47 5/6] refactor transformDefineClause
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
coerce_to_boolean() was deferred to a second pass over the completed
defineClause list, meaning pull_var_clause() ran on an expression that had not
yet been fully coerced/transformed. The more intuitively order should be:
transformExpr → coerce_to_boolean → pull_var_clause, so pull_var_clause always
sees the final expression form.
---
src/backend/parser/parse_rpr.c | 142 ++++++++++++-------------
src/test/regress/expected/rpr_base.out | 2 +-
2 files changed, 67 insertions(+), 77 deletions(-)
diff --git a/src/backend/parser/parse_rpr.c b/src/backend/parser/parse_rpr.c
index 8521c8ee9f..59624c7a46 100644
--- a/src/backend/parser/parse_rpr.c
+++ b/src/backend/parser/parse_rpr.c
@@ -284,9 +284,7 @@ static List *
transformDefineClause(ParseState *pstate, WindowClause *wc, WindowDef *windef,
List **targetlist)
{
- List *restargets;
List *defineClause = NIL;
- char *name;
List *patternVarNames = NIL;
/*
@@ -326,32 +324,36 @@ transformDefineClause(ParseState *pstate, WindowClause *wc, WindowDef *windef,
pstate->p_rpr_pattern_vars = patternVarNames;
/*
- * Check for duplicate row pattern definition variables. The standard
+ * Check for duplicate row pattern definition variables. The SQL standard
* requires that no two row pattern definition variable names shall be
* equivalent.
*/
- restargets = NIL;
+ for (int outerpos = 0; outerpos < list_length(windef->rpCommonSyntax->rpDefs); outerpos++)
+ {
+ ResTarget *restarget = list_nth_node(ResTarget,
+ windef->rpCommonSyntax->rpDefs,
+ outerpos);
+
+ for (int restpos = outerpos + 1; restpos < list_length(windef->rpCommonSyntax->rpDefs); restpos++)
+ {
+ ResTarget *inner_res = list_nth_node(ResTarget,
+ windef->rpCommonSyntax->rpDefs,
+ restpos);
+
+ if (!strcmp(restarget->name, inner_res->name))
+ ereport(ERROR,
+ errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("DEFINE variable \"%s\" appears more than once",
+ restarget->name),
+ parser_errposition(pstate, exprLocation((Node *) inner_res)));
+ }
+ }
+
foreach_node(ResTarget, restarget, windef->rpCommonSyntax->rpDefs)
{
TargetEntry *teDefine;
-
- name = restarget->name;
-
- foreach_node(ResTarget, r, restargets)
- {
- char *n;
-
- n = r->name;
-
- if (!strcmp(n, name))
- ereport(ERROR,
- errcode(ERRCODE_SYNTAX_ERROR),
- errmsg("DEFINE variable \"%s\" appears more than once",
- name),
- parser_errposition(pstate, exprLocation((Node *) r)));
- }
-
- restargets = lappend(restargets, restarget);
+ Node *expr;
+ List *vars;
/*
* Transform the DEFINE expression. We must NOT add the whole
@@ -363,68 +365,56 @@ transformDefineClause(ParseState *pstate, WindowClause *wc, WindowDef *windef,
* the individual Var nodes it references are present in the
* targetlist, so the planner can propagate the referenced columns.
*/
- {
- Node *expr;
- List *vars;
+ expr = transformExpr(pstate, restarget->val, EXPR_KIND_RPR_DEFINE);
- expr = transformExpr(pstate, restarget->val,
- EXPR_KIND_RPR_DEFINE);
+ expr = coerce_to_boolean(pstate, expr, "DEFINE");
- /*
- * Pull out Var nodes from the transformed expression and ensure
- * each one is present in the targetlist. This is needed so the
- * planner propagates the referenced columns through the plan
- * tree, making them available to the WindowAgg's DEFINE
- * evaluation.
- */
- vars = pull_var_clause(expr, 0);
- foreach_node(Var, var, vars)
- {
- bool found = false;
-
- foreach_node(TargetEntry, tle, *targetlist)
- {
- if (IsA(tle->expr, Var) &&
- ((Var *) tle->expr)->varno == var->varno &&
- ((Var *) tle->expr)->varattno == var->varattno)
- {
- found = true;
- break;
- }
- }
- if (!found)
- {
- TargetEntry *newtle;
-
- newtle = makeTargetEntry((Expr *) copyObject(var),
- list_length(*targetlist) + 1,
- NULL,
- true);
- *targetlist = lappend(*targetlist, newtle);
- }
- }
- list_free(vars);
-
- /* Build the defineClause entry directly from the transformed expr */
- teDefine = makeTargetEntry((Expr *) expr,
- list_length(defineClause) + 1,
- pstrdup(name),
- true);
- }
+ /* Build the defineClause entry directly from the transformed expr */
+ teDefine = makeTargetEntry((Expr *) expr,
+ list_length(defineClause) + 1,
+ pstrdup(restarget->name),
+ true);
/* build transformed DEFINE clause (list of TargetEntry) */
defineClause = lappend(defineClause, teDefine);
+
+ /*
+ * Pull out Var nodes from the transformed expression and ensure each
+ * one is present in the targetlist. This is needed so the planner
+ * propagates the referenced columns through the plan tree, making
+ * them available to the WindowAgg's DEFINE evaluation.
+ */
+ vars = pull_var_clause(expr, 0);
+
+ foreach_node(Var, var, vars)
+ {
+ bool found = false;
+
+ foreach_node(TargetEntry, tle, *targetlist)
+ {
+ if (equal(tle->expr, var))
+ {
+ found = true;
+ break;
+ }
+ }
+
+ if (!found)
+ {
+ TargetEntry *newtle;
+
+ newtle = makeTargetEntry((Expr *) copyObject(var),
+ list_length(*targetlist) + 1,
+ NULL,
+ true);
+ *targetlist = lappend(*targetlist, newtle);
+ }
+ }
+ list_free(vars);
}
- list_free(restargets);
+
pstate->p_rpr_pattern_vars = NIL;
- /*
- * Make sure that the row pattern definition search condition is a boolean
- * expression.
- */
- foreach_ptr(TargetEntry, te, defineClause)
- te->expr = (Expr *) coerce_to_boolean(pstate, (Node *) te->expr, "DEFINE");
-
/*
* Validate DEFINE expressions: nested PREV/NEXT, column references,
* compound flatten, volatile callees -- all in a single walk per
diff --git a/src/test/regress/expected/rpr_base.out b/src/test/regress/expected/rpr_base.out
index 41541898f5..cae5cab0d8 100644
--- a/src/test/regress/expected/rpr_base.out
+++ b/src/test/regress/expected/rpr_base.out
@@ -236,7 +236,7 @@ WINDOW w AS (
);
ERROR: DEFINE variable "a" appears more than once
LINE 7: DEFINE A AS id > 0, A AS id < 10
- ^
+ ^
DROP TABLE rpr_dup;
-- Boolean coercion
CREATE TABLE rpr_bool (id INT, flag BOOLEAN);
--
2.34.1