diff options
author | Tom Lane <tgl@sss.pgh.pa.us> | 2025-09-17 16:32:57 -0400 |
---|---|---|
committer | Tom Lane <tgl@sss.pgh.pa.us> | 2025-09-17 16:32:57 -0400 |
commit | b0cc0a71e0a0a760f54c72edb8cd000e4555442b (patch) | |
tree | aba85c1d17a67facbf0e76e16ed2bdc954fbda4c /src/backend/parser/parse_agg.c | |
parent | 0951942bba25f85ad29a4f096ed51a356652b5a2 (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/parse_agg.c')
-rw-r--r-- | src/backend/parser/parse_agg.c | 28 |
1 files changed, 27 insertions, 1 deletions
diff --git a/src/backend/parser/parse_agg.c b/src/backend/parser/parse_agg.c index 0ac8966e30f..3254c83cc6c 100644 --- a/src/backend/parser/parse_agg.c +++ b/src/backend/parser/parse_agg.c @@ -791,6 +791,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 */ @@ -800,7 +826,7 @@ check_agg_arguments_walker(Node *node, result = query_tree_walker((Query *) node, check_agg_arguments_walker, context, - 0); + QTW_EXAMINE_RTES_BEFORE); context->sublevels_up--; return result; } |