diff --git a/src/backend/optimizer/util/var.c b/src/backend/optimizer/util/var.c index ce8a2e66fe4..d14d80973e4 100644 --- a/src/backend/optimizer/util/var.c +++ b/src/backend/optimizer/util/var.c @@ -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) { diff --git a/src/test/regress/expected/join.out b/src/test/regress/expected/join.out index 25a9a39cdc1..b0cab6a7823 100644 --- a/src/test/regress/expected/join.out +++ b/src/test/regress/expected/join.out @@ -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 -- diff --git a/src/test/regress/sql/join.sql b/src/test/regress/sql/join.sql index 7a431b926ea..4fc0ded779d 100644 --- a/src/test/regress/sql/join.sql +++ b/src/test/regress/sql/join.sql @@ -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 --