summaryrefslogtreecommitdiff
path: root/src/backend/parser
diff options
context:
space:
mode:
authorTom Lane <tgl@sss.pgh.pa.us>2025-09-17 16:32:57 -0400
committerTom Lane <tgl@sss.pgh.pa.us>2025-09-17 16:32:57 -0400
commitafd18f27615b0e452373acbc8de2e5282dfeb31b (patch)
tree1edead6ed8215cbb065460315e3de237c3ea4a8d /src/backend/parser
parentf00ad440a5b6ce430dda31b03788e4d4b6659f60 (diff)
Calculate agglevelsup correctly when Aggref contains a CTE.
If an aggregate function call contains a sub-select that has an RTE referencing a CTE outside the aggregate, we must treat that reference like a Var referencing the CTE's query level for purposes of determining the aggregate's level. Otherwise we might reach the nonsensical conclusion that the aggregate should be evaluated at some query level higher than the CTE, ending in a planner error or a broken plan tree that causes executor failures. Bug: #19055 Reported-by: BugForge <dllggyx@outlook.com> Author: Tom Lane <tgl@sss.pgh.pa.us> Discussion: https://postgr.es/m/19055-6970cfa8556a394d@postgresql.org Backpatch-through: 13
Diffstat (limited to 'src/backend/parser')
-rw-r--r--src/backend/parser/parse_agg.c28
1 files changed, 27 insertions, 1 deletions
diff --git a/src/backend/parser/parse_agg.c b/src/backend/parser/parse_agg.c
index 3115296f330..216c4880f19 100644
--- a/src/backend/parser/parse_agg.c
+++ b/src/backend/parser/parse_agg.c
@@ -783,6 +783,32 @@ check_agg_arguments_walker(Node *node,
parser_errposition(context->pstate,
((WindowFunc *) node)->location)));
}
+
+ if (IsA(node, RangeTblEntry))
+ {
+ /*
+ * CTE references act similarly to Vars of the CTE's level. Without
+ * this we might conclude that the Agg can be evaluated above the CTE,
+ * leading to trouble.
+ */
+ RangeTblEntry *rte = (RangeTblEntry *) node;
+
+ if (rte->rtekind == RTE_CTE)
+ {
+ int ctelevelsup = rte->ctelevelsup;
+
+ /* convert levelsup to frame of reference of original query */
+ ctelevelsup -= context->sublevels_up;
+ /* ignore local CTEs of subqueries */
+ if (ctelevelsup >= 0)
+ {
+ if (context->min_varlevel < 0 ||
+ context->min_varlevel > ctelevelsup)
+ context->min_varlevel = ctelevelsup;
+ }
+ }
+ return false; /* allow range_table_walker to continue */
+ }
if (IsA(node, Query))
{
/* Recurse into subselects */
@@ -792,7 +818,7 @@ check_agg_arguments_walker(Node *node,
result = query_tree_walker((Query *) node,
check_agg_arguments_walker,
(void *) context,
- 0);
+ QTW_EXAMINE_RTES_BEFORE);
context->sublevels_up--;
return result;
}