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:
@@ -776,6 +776,9 @@ pull_var_clause_walker(Node *node, pull_var_clause_context *context)
|
||||
* PlaceHolderVar or constructed from those, we can just add the
|
||||
* varnullingrels bits to the existing nullingrels field(s); otherwise
|
||||
* we have to add a PlaceHolderVar wrapper.
|
||||
*
|
||||
* If root is NULL, nulled whole-row JOIN Vars are left unexpanded, so the
|
||||
* result must be flattened again with a root before it can be executed.
|
||||
*/
|
||||
Node *
|
||||
flatten_join_alias_vars(PlannerInfo *root, Query *query, Node *node)
|
||||
@@ -809,7 +812,8 @@ flatten_join_alias_vars(PlannerInfo *root, Query *query, Node *node)
|
||||
* 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.
|
||||
* would make as a join alias expression, we're OK. (Nulled whole-row join
|
||||
* Vars are the exception; those are left unexpanded.)
|
||||
*
|
||||
* The "node" might be part of a sub-query of the Query whose join alias
|
||||
* Vars are to be expanded. "sublevels_up" indicates how far below the
|
||||
@@ -865,6 +869,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)
|
||||
{
|
||||
|
||||
@@ -4422,6 +4422,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
|
||||
--
|
||||
|
||||
@@ -1384,6 +1384,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
|
||||
--
|
||||
|
||||
Reference in New Issue
Block a user