jayzhan211 commented on code in PR #25529:
URL: https://github.com/apache/datafusion/pull/25529#discussion_r4056658177


##########
datafusion/sqllogictest/test_files/subquery.slt:
##########
@@ -2744,3 +2744,93 @@ b 400
 
 statement ok
 DROP TABLE metrics;
+
+# Regression test for #25519: a correlated filter that sits below an aggregate
+# with a grouping set must not be pulled above it. The pull up adds the
+# correlated column to every set, so `ROLLUP(k)`, which is
+# `GROUPING SETS ((k), ())`, turns into `GROUPING SETS ((k), (k, k))`. The 
empty
+# set is gone, and with it the grand total row the subquery returns for every
+# outer row, including the rows whose filter matches nothing.
+statement ok
+CREATE TABLE gs_outer(k INT) AS VALUES (1), (2), (NULL), (4), (5);
+
+statement ok
+CREATE TABLE gs_inner(k INT, j INT) AS VALUES (1, 10), (NULL, 20), (5, 30), 
(2, 40);
+
+# ROLLUP holds the empty set, so the subquery stays correlated.
+statement error DataFusion error: This feature is not implemented: Physical 
plan does not support logical expression Exists
+SELECT gs_outer.k, EXISTS (SELECT 1 FROM gs_inner WHERE gs_inner.k = 
gs_outer.k GROUP BY ROLLUP(gs_inner.k)) FROM gs_outer;
+
+# So does CUBE.
+statement error DataFusion error: This feature is not implemented: Physical 
plan does not support logical expression Exists
+SELECT gs_outer.k, EXISTS (SELECT 1 FROM gs_inner WHERE gs_inner.k = 
gs_outer.k GROUP BY CUBE(gs_inner.k)) FROM gs_outer;
+
+# And an explicit grouping set that lists the empty set.
+statement error DataFusion error: This feature is not implemented: Physical 
plan does not support logical expression Exists
+SELECT gs_outer.k, EXISTS (SELECT 1 FROM gs_inner WHERE gs_inner.k = 
gs_outer.k GROUP BY GROUPING SETS ((gs_inner.k), ())) FROM gs_outer;
+
+# A set that groups by another column does not carry the correlated column 
either.

Review Comment:
   Comment gives the empty-set rationale, which doesn't apply: `((k), (j))` has 
no empty set. The reason is the NULL fill — the pull up turns `(j)` into `(j, 
k)`, so `gs_inner.k` is non-NULL where the original set fills it with NULL. 
Suggested wording plus a case that shows it (main returns `false` for every 
row; should be `true` for 1, 2, 5):
   
   ```diff
   -# A set that groups by another column does not carry the correlated column 
either.
   +# A set that leaves out the correlated column fills it with NULL. The pull 
up
   +# would turn `(j)` into `(j, k)`, and `k` would then carry a value in the 
rows
   +# where the subquery returns NULL. Anything above the aggregate that reads 
`k`
   +# sees the difference, so the subquery stays correlated.
   +#
   +# Known limitation: when nothing reads `k`, as here, the pull up was correct
   +# before this guard and the query now fails to plan. Telling the two cases 
apart
   +# needs the correlated column added to each set under an alias.
    statement error DataFusion error: This feature is not implemented: Physical 
plan does not support logical expression Exists
    SELECT gs_outer.k, EXISTS (SELECT 1 FROM gs_inner WHERE gs_inner.k = 
gs_outer.k GROUP BY GROUPING SETS ((gs_inner.k), (gs_inner.j))) FROM gs_outer;
   +
   +# The same sets with a HAVING that reads the NULL filled column. For k = 1 
the
   +# `(j)` set yields the row `(NULL, 10)`, which passes the HAVING, so EXISTS 
is
   +# true. With `(j, k)` that row has `k = 1` and is filtered out.
   +statement error DataFusion error: This feature is not implemented: Physical 
plan does not support logical expression Exists
   +SELECT gs_outer.k, EXISTS (SELECT 1 FROM gs_inner WHERE gs_inner.k = 
gs_outer.k GROUP BY GROUPING SETS ((gs_inner.k), (gs_inner.j)) HAVING 
gs_inner.k IS NULL) FROM gs_outer;
   ```
   
   The doc on `grouping_sets_cover_pull_up_cols` has the same gap, it only 
explains the empty set:
   
   ```diff
        /// `ROLLUP` and `CUBE` always contain the empty set, which yields a 
row for
        /// outer rows the correlated filter matches nothing for, so they are 
only safe
        /// when there is nothing to add.
   +    ///
   +    /// A non-empty set that leaves a column out fills it with NULL. Adding 
the
   +    /// column would give it a value that a HAVING or a projection above the
   +    /// aggregate can read, so such a set is rejected as well.
   ```
   
   The alias-based fix is fine as a follow-up.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to