Etienne Pelissier created CALCITE-7779:
------------------------------------------

             Summary: Optimization rule FilterAggregateTransposeRule rewrites 
queries to semantically non-equivalent ones
                 Key: CALCITE-7779
                 URL: https://issues.apache.org/jira/browse/CALCITE-7779
             Project: Calcite
          Issue Type: Bug
          Components: core
            Reporter: Etienne Pelissier
            Assignee: Etienne Pelissier


This only concerns non-simple aggregates (aggregates with multiple grouping 
sets).

In the simple aggregate case, FilterAggregateTransposeRule pushes a predicate 
below an aggregate when the predicate's referenced columns all belong to the 
aggregate’s grouping set, because aggregate-function results are unavailable 
before aggregation.

In the non-simple aggregate case, FilterAggregateTransposeRule pushes a 
predicate below an aggregate when the predicate's referenced columns all belong 
to every grouping set, because a referenced column is replaced with NULL in 
rows produced by a grouping set that omits it, and evaluating the predicate 
before aggregation can change the result.

However, the pushability check in the non-simple case currently compares 
filter-input column positions (= aggregate-output column positions) with 
aggregate-input column positions without remapping. The same position can 
identify different columns in these two row layouts, so this check does not 
establish that the predicate’s referenced columns belong to every grouping set. 
Consequently, the rule can incorrectly push a predicate and change the query 
result, or fail to push a predicate when doing so would be valid.

The existing dedicated grouping-set tests use aggregates whose grouping-column 
input and output positions coincide. They do not cover grouping sets where 
those positions differ, as in the following reproducer.
{code:java}
SELECT a, b
FROM (VALUES (0, 1, 2)) AS t(unused, a, b)
GROUP BY GROUPING SETS ((a), (a, b))
HAVING b IS NULL
{code}
After applying AGGREGATE_PROJECT_MERGE followed by FILTER_AGGREGATE_TRANSPOSE, 
the resulting plan is equivalent to:
{code:java}
SELECT a, b
FROM (VALUES (0, 1, 2)) AS t(unused, a, b)
WHERE b IS NULL
GROUP BY GROUPING SETS ((a), (a, b))
{code}
which isn't semantically equivalent to the first query.

The original query returns:
{code:java}
A | B
--+-----
1 | NULL

(1 row)
{code}
The rewritten query returns no rows.

The fix is straightforward: remap each grouping set from aggregate-input column 
positions to aggregate-output column positions before comparing it with the 
predicate’s referenced columns (around 10 lines of code change).



--
This message was sent by Atlassian Jira
(v8.20.10#820010)

Reply via email to