Thread: Wrong results with subquery pullup and grouping sets

Wrong results with subquery pullup and grouping sets

From
Richard Guo
Date:
While working on the expansion of virtual generated columns, Dean
encountered $subject in [1], which can be reproduced using the query
below.

create table t (a int);
insert into t values (1);

# select a, b
  from (select a, a as b from t) ss
  group by grouping sets(a, b)
  having b = 1;
 a | b
---+---
 1 |
(1 row)

Note that the having clause filters out the wrong row.

In 90947674f, we fixed a similar issue by wrapping subquery outputs
that are non-var expressions in PlaceHolderVars.  This prevents
const-simplification from merging them into the surrounding expression
after subquery pullup and resulting in something that won't match the
grouping set expression in setrefs.c.

It seems that we still have loose ends with that fix.

This issue illustrates that if the subquery's target list contains two
or more identical Var expressions, we can also fail to match the Var
expression to the expected grouping set expression.  This is not
related to const-simplification, but rather to how we match
expressions to lower target items in setrefs.c.

For sort/group expressions, we use ressortgroupref matching, which
works well.  For other expressions, we primarily rely on comparing the
expressions to determine if they are the same.  This is why, in the
query above, the Var 'b' in the HAVING clause matches the first target
entry of the subquery, rather than the expected second one.

You might wonder why this issue wasn't fixed by the commit that
introduced a dummy RTE representing the output of the grouping step.
I think that commit prevents one expression that matches the grouping
item from being const-folded or preprocessed, but it doesn't prevent
setrefs.c from matching the expression to some other identical ones.

It seems to me that simple Var expressions in a subquery's target list
also need to retain their separate identity in order to match grouping
set columns after subquery pullup, not just non-var expressions.
Alternatively, is there a way to teach setrefs.c to match an
expression to the expected one when there are multiple identical
expressions?

[1] https://postgr.es/m/CAEZATCWsKqCtZ=ud26-gGV3zHt-hjS4OKG43GCBhSaYUWyfKiw@mail.gmail.com

Thanks
Richard



Re: Wrong results with subquery pullup and grouping sets

From
Richard Guo
Date:
On Wed, Mar 5, 2025 at 11:02 AM Richard Guo <guofenglinux@gmail.com> wrote:
> create table t (a int);
> insert into t values (1);
>
> # select a, b
>   from (select a, a as b from t) ss
>   group by grouping sets(a, b)
>   having b = 1;
>  a | b
> ---+---
>  1 |
> (1 row)
>
> Note that the having clause filters out the wrong row.

BTW, this issue is not limited to HAVING clause; it can also happen to
targetlist.

# select a, b+1
  from (select a, a as b from t) ss
  group by grouping sets(a, b);
 a | ?column?
---+----------
 1 |        2
   |
(2 rows)

Surely this is wrong.

Thanks
Richard



Re: Wrong results with subquery pullup and grouping sets

From
Richard Guo
Date:
On Wed, Mar 5, 2025 at 11:02 AM Richard Guo <guofenglinux@gmail.com> wrote:
> It seems to me that simple Var expressions in a subquery's target list
> also need to retain their separate identity in order to match grouping
> set columns after subquery pullup, not just non-var expressions.

I noticed the adjacent code that sets wrap_non_vars to true if we
are considering an appendrel child subquery, and I doubt this is
necessary.

 /*
  * 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.wrap_non_vars = true;

As explained in e42e31243, the only part of the upper query that could
reference the appendrel child yet is the translated_vars list of the
associated AppendRelInfo that we just made for this child, and we do
not want to force use of PHVs in the AppendRelInfo (see the comment in
perform_pullup_replace_vars).  In fact, perform_pullup_replace_vars
sets wrap_non_vars to false before performing pullup_replace_vars on
the AppendRelInfo.  It seems to me that this makes the code shown
above unnecessary, and we can simply remove it.

Any thoughts?

Thanks
Richard



Re: Wrong results with subquery pullup and grouping sets

From
Dean Rasheed
Date:
On Wed, 5 Mar 2025 at 09:09, Richard Guo <guofenglinux@gmail.com> wrote:
>
> I noticed the adjacent code that sets wrap_non_vars to true if we
> are considering an appendrel child subquery, and I doubt this is
> necessary.
>
>  /*
>   * 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.wrap_non_vars = true;
>
> As explained in e42e31243, the only part of the upper query that could
> reference the appendrel child yet is the translated_vars list of the
> associated AppendRelInfo that we just made for this child, and we do
> not want to force use of PHVs in the AppendRelInfo (see the comment in
> perform_pullup_replace_vars).  In fact, perform_pullup_replace_vars
> sets wrap_non_vars to false before performing pullup_replace_vars on
> the AppendRelInfo.  It seems to me that this makes the code shown
> above unnecessary, and we can simply remove it.
>

Yes, that makes sense, and it seems like a fairly straightforward
simplification, given that perform_pullup_replace_vars() sets it back
to false shortly afterwards.

The same thing happens in pull_up_constant_function().

Regards,
Dean