1
0
mirror of https://github.com/postgres/postgres.git synced 2026-10-23 13:29:09 +03:00

Fix parser failure with whole-row join alias Vars

When a query has aggregates or GROUP BY, parseCheckAggregates()
flattens join alias Vars in the grouping expressions, target list and
HAVING clause so that aliased and unaliased references compare equal.
The parser has no PlannerInfo, so this relies on every join alias
expression being able to carry varnullingrels without a PHV.  That
doesn't hold for a whole-row Var of a join on the nullable side of an
outer join: it expands to a RowExpr, and pushing the nulling bits down
into its fields would turn a NULL row into ROW(NULL, ...).  Such
queries therefore failed with "unsupported join alias expression".

To fix, leave a nulled whole-row join Var unexpanded when no root is
available.  The planner expands it later and wraps it in a
PlaceHolderVar as before.  Nothing is lost for GROUP BY matching: a
nulled whole-row Var can only be equal to the same Var, never to an
expression over the join's columns.

This has been broken since the introduction of varnullingrels in v16.

Author: Richard Guo <guofenglinux@gmail.com>
Discussion: https://postgr.es/m/CAMbWs49q_gvoeOoF0+KZ-DpQaDb=nGFfSgrz2V_n1_64L4ZNVQ@mail.gmail.com
Backpatch-through: 16
This commit is contained in:
Richard Guo
2026-10-07 14:38:42 +09:00
parent 869c53ff8f
commit 0c42350131
3 changed files with 52 additions and 1 deletions

View File

@@ -738,7 +738,9 @@ pull_var_clause_walker(Node *node, pull_var_clause_context *context)
* for making PlaceHolderVars. We can avoid making PlaceHolderVars in the
* parser's usage because it won't be dealing with arbitrary expressions:
* so long as adjust_standard_join_alias_expression can handle everything
* the parser would make as a join alias expression, we're OK.
* the parser would make as a join alias expression, we're OK. The one
* exception is a nulled whole-row join Var, which is left unexpanded when
* root is NULL; the planner expands it later.
*/
Node *
flatten_join_alias_vars(PlannerInfo *root, Query *query, Node *node)
@@ -790,6 +792,13 @@ flatten_join_alias_vars_mutator(Node *node,
ListCell *lv;
ListCell *ln;
/*
* A nulled whole-row expansion needs a PlaceHolderVar, which we
* can't make without a root; leave the Var unexpanded.
*/
if (context->root == NULL && var->varnullingrels != NULL)
return node;
Assert(list_length(rte->joinaliasvars) == list_length(rte->eref->colnames));
forboth(lv, rte->joinaliasvars, ln, rte->eref->colnames)
{

View File

@@ -3403,6 +3403,35 @@ group by t1.q2 order by 1;
4567890123456789 | 6
(4 rows)
-- nulled whole-row Var of a join alias in aggregate queries
select t1.q2, count(t23)
from int8_tbl t1 left join
(int8_tbl t2 join int4_tbl t3 on t3.f1 = 0) t23
on (t1.q2 = t23.q1)
group by t1.q2 order by 1;
q2 | count
-------------------+-------
-4567890123456789 | 0
123 | 2
456 | 0
4567890123456789 | 6
(4 rows)
select t23, count(*)
from int8_tbl t1 left join
(int8_tbl t2 join int4_tbl t3 on t3.f1 = 0) t23
on (t1.q2 = t23.q1)
group by t23 order by 1;
t23 | count
----------------------------------------+-------
(123,456,0) | 1
(123,4567890123456789,0) | 1
(4567890123456789,-4567890123456789,0) | 2
(4567890123456789,123,0) | 2
(4567890123456789,4567890123456789,0) | 2
| 2
(6 rows)
--
-- test incorrect failure to NULL pulled-up subexpressions
--

View File

@@ -979,6 +979,19 @@ from int8_tbl t1 left join
on (t1.q2 = t2.q1)
group by t1.q2 order by 1;
-- nulled whole-row Var of a join alias in aggregate queries
select t1.q2, count(t23)
from int8_tbl t1 left join
(int8_tbl t2 join int4_tbl t3 on t3.f1 = 0) t23
on (t1.q2 = t23.q1)
group by t1.q2 order by 1;
select t23, count(*)
from int8_tbl t1 left join
(int8_tbl t2 join int4_tbl t3 on t3.f1 = 0) t23
on (t1.q2 = t23.q1)
group by t23 order by 1;
--
-- test incorrect failure to NULL pulled-up subexpressions
--