adriangb opened a new pull request, #25349:
URL: https://github.com/apache/datafusion/pull/25349

   ## Rationale for this change
   
   `SELECT DISTINCT ... LIMIT n` with no `ORDER BY` fails at execution with
   `Internal error: Ordering direction required for DISTINCT with limit` when 
one of the
   projected columns is a window function:
   
   ```sql
   CREATE TABLE t (env VARCHAR, region VARCHAR, deployment VARCHAR);
   INSERT INTO t VALUES ('prod','a','x'), ('prod','a','y'), ('staging','b','x');
   SELECT DISTINCT env, region, count(*) OVER (PARTITION BY env, deployment) AS 
n FROM t LIMIT 30;
   ```
   
   `LimitedDistinctAggregation` pushes `LimitOptions::new(limit)` (so 
`descending: None`) into the
   `AggregateExec` while `is_unordered_unfiltered_group_by_distinct()` holds. 
That predicate is not
   stable under later rewrites: `EnforceSorting` inserts the sort the window 
function requires below
   the aggregation, the aggregation is rebuilt through `replace_children` with
   `ChildrenPropertiesMode::Recompute`, which re-derives the properties from 
the now-ordered child
   while carrying `limit_options` over, and the rebuilt node ends up with
   `ordering_mode=PartiallySorted(..)` and an output ordering. The predicate is 
now false, so
   `execute_typed` selects `GroupedTopKAggregateStream`, whose constructor 
needs an ordering
   direction that was never supplied, and errors.
   
   The top-k stream is not just missing a direction here: it also reads only 
the first group key
   (`aggr.group_expr().expr()[0]`), so it would be wrong for a multi-column 
`DISTINCT` even with a
   direction. The correct behaviour is to not take that path at all.
   
   ## What changes are included in this PR?
   
   `AggregateExec::execute_typed` now takes the top-k path only when an 
ordering direction is
   actually available, matching what `GroupedTopKAggregateStream::new` 
requires: either
   `limit_options().descending` is set (which is what `TopKAggregation` pushes, 
and it already
   restricts itself to a single group key) or `get_minmax_desc()` supplies one. 
Otherwise execution
   falls back to the regular grouped streams, which is correct because they 
treat the pushed limit
   as a soft limit (or ignore it) and the `LIMIT` above the aggregation still 
truncates the result.
   
   The pushdown itself and the resulting plans are unchanged; this only changes 
which stream is
   selected at execution time.
   
   ## Are these changes tested?
   
   Yes. A new case in `datafusion/sqllogictest/test_files/aggregate.slt` covers 
the multi-column and
   single-column forms of the query and asserts the returned rows (matching 
PostgreSQL and DuckDB).
   It fails on `main` with the reported internal error and passes with this 
change.
   
   `cargo test -p datafusion-physical-plan` (2239 tests) and the 
`aggregate.slt`,
   `aggregates_topk.slt`, `limit.slt`, `window.slt`, `group_by.slt`, 
`explain.slt` and
   `distinct_on.slt` sqllogictest files all pass.
   
   ## Are there any user-facing changes?
   
   Queries that previously failed with `Internal error: Ordering direction 
required for DISTINCT
   with limit` now return the correct rows. There are no API changes and no 
plan changes.


-- 
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