diff --git a/src/backend/optimizer/prep/prepjointree.c b/src/backend/optimizer/prep/prepjointree.c index 0e2a220..45d82da 100644 *** a/src/backend/optimizer/prep/prepjointree.c --- b/src/backend/optimizer/prep/prepjointree.c *************** pull_up_simple_subquery(PlannerInfo *roo *** 1003,1013 **** /* * The subquery's targetlist items are now in the appropriate form to ! * insert into the top query, but if we are under an outer join then ! * non-nullable items and lateral references may have to be turned into ! * PlaceHolderVars. If we are dealing with an appendrel member then ! * anything that's not a simple Var has to be turned into a ! * PlaceHolderVar. Set up required context data for pullup_replace_vars. */ rvcontext.root = root; rvcontext.targetlist = subquery->targetList; --- 1003,1010 ---- /* * The subquery's targetlist items are now in the appropriate form to ! * insert into the top query, except that we may need to wrap them in ! * PlaceHolderVars. Set up required context data for pullup_replace_vars. */ rvcontext.root = root; rvcontext.targetlist = subquery->targetList; *************** pull_up_simple_subquery(PlannerInfo *roo *** 1019,1032 **** rvcontext.relids = NULL; rvcontext.outer_hasSubLinks = &parse->hasSubLinks; rvcontext.varno = varno; ! rvcontext.need_phvs = (lowest_nulling_outer_join != NULL || ! containing_appendrel != NULL); ! rvcontext.wrap_non_vars = (containing_appendrel != NULL); /* initialize cache array with indexes 0 .. length(tlist) */ rvcontext.rv_cache = palloc0((list_length(subquery->targetList) + 1) * sizeof(Node *)); /* * Replace all of the top query's references to the subquery's outputs * with copies of the adjusted subtlist items, being careful not to * replace any of the jointree structure. (This'd be a lot cleaner if we --- 1016,1064 ---- rvcontext.relids = NULL; rvcontext.outer_hasSubLinks = &parse->hasSubLinks; rvcontext.varno = varno; ! /* these flags will be set below, if needed */ ! rvcontext.need_phvs = false; ! rvcontext.wrap_non_vars = false; /* initialize cache array with indexes 0 .. length(tlist) */ rvcontext.rv_cache = palloc0((list_length(subquery->targetList) + 1) * sizeof(Node *)); /* + * If we are under an outer join then non-nullable items and lateral + * references may have to be turned into PlaceHolderVars. + */ + if (lowest_nulling_outer_join != NULL) + rvcontext.need_phvs = true; + + /* + * If we are dealing with an appendrel member then anything that's not a + * simple Var has to be turned into a PlaceHolderVar. We force this to + * ensure that what we pull up doesn't get merged into a surrounding + * expression during later processing and then fail to match the + * expression actually available from the appendrel. + */ + if (containing_appendrel != NULL) + { + rvcontext.need_phvs = true; + rvcontext.wrap_non_vars = true; + } + + /* + * If the parent query uses grouping sets, we need a PlaceHolderVar for + * anything that's not a simple Var. Again, this ensures that expressions + * retain their separate identity so that they will match grouping set + * columns when appropriate. (It'd be sufficient to wrap values used in + * grouping set columns, and do so only in non-aggregated portions of the + * tlist and havingQual, but that would require a lot of infrastructure + * that pullup_replace_vars hasn't currently got.) + */ + if (parse->groupingSets) + { + rvcontext.need_phvs = true; + rvcontext.wrap_non_vars = true; + } + + /* * Replace all of the top query's references to the subquery's outputs * with copies of the adjusted subtlist items, being careful not to * replace any of the jointree structure. (This'd be a lot cleaner if we diff --git a/src/test/regress/expected/groupingsets.out b/src/test/regress/expected/groupingsets.out index 833d515..cbfdbfd 100644 *** a/src/test/regress/expected/groupingsets.out --- b/src/test/regress/expected/groupingsets.out *************** select g as alias1, g as alias2 *** 389,394 **** --- 389,439 ---- 3 | (6 rows) + -- check that pulled-up subquery outputs still go to null when appropriate + select four, x + from (select four, ten, 'foo'::text as x from tenk1) as t + group by grouping sets (four, x) + having x = 'foo'; + four | x + ------+----- + | foo + (1 row) + + select four, x || 'x' + from (select four, ten, 'foo'::text as x from tenk1) as t + group by grouping sets (four, x) + order by four; + four | ?column? + ------+---------- + 0 | + 1 | + 2 | + 3 | + | foox + (5 rows) + + select (x+y)*1, sum(z) + from (select 1 as x, 2 as y, 3 as z) s + group by grouping sets (x+y, x); + ?column? | sum + ----------+----- + 3 | 3 + | 3 + (2 rows) + + select x, not x as not_x, q2 from + (select *, q1 = 1 as x from int8_tbl i1) as t + group by grouping sets(x, q2) + order by x, q2; + x | not_x | q2 + ---+-------+------------------- + f | t | + | | -4567890123456789 + | | 123 + | | 456 + | | 4567890123456789 + (5 rows) + -- simple rescan tests select a, b, sum(v.x) from (values (1),(2)) v(x), gstest_data(v.x) diff --git a/src/test/regress/sql/groupingsets.sql b/src/test/regress/sql/groupingsets.sql index 2b4ab69..b28d821 100644 *** a/src/test/regress/sql/groupingsets.sql --- b/src/test/regress/sql/groupingsets.sql *************** select g as alias1, g as alias2 *** 152,157 **** --- 152,177 ---- from generate_series(1,3) g group by alias1, rollup(alias2); + -- check that pulled-up subquery outputs still go to null when appropriate + select four, x + from (select four, ten, 'foo'::text as x from tenk1) as t + group by grouping sets (four, x) + having x = 'foo'; + + select four, x || 'x' + from (select four, ten, 'foo'::text as x from tenk1) as t + group by grouping sets (four, x) + order by four; + + select (x+y)*1, sum(z) + from (select 1 as x, 2 as y, 3 as z) s + group by grouping sets (x+y, x); + + select x, not x as not_x, q2 from + (select *, q1 = 1 as x from int8_tbl i1) as t + group by grouping sets(x, q2) + order by x, q2; + -- simple rescan tests select a, b, sum(v.x)