jayzhan211 opened a new issue, #26745:
URL: https://github.com/apache/datafusion/issues/26745

   ### Describe the bug
   
   An order-preserving `RepartitionExec` (`preserve_order=true`) whose input 
has more than one ordering merges its input partitions on all of those 
orderings joined into one key. Its output properties, however, still claim each 
input ordering on its own, and those no longer hold after the merge. The 
planner then removes a sort that is needed, so `ORDER BY` returns rows in the 
wrong order.
   
   This is the `RepartitionExec` form of #26646, which #26654 fixed for 
`SortPreservingMergeExec`.
   
   ### To Reproduce
   
   In `datafusion-cli`:
   
   ```sql
   COPY (VALUES (1, 10), (2, 20)) TO 'w/f1.csv' STORED AS CSV OPTIONS 
('format.has_header' 'true');
   COPY (VALUES (1, 1), (3, 2)) TO 'w/f2.csv' STORED AS CSV OPTIONS 
('format.has_header' 'true');
   
   SET datafusion.execution.target_partitions = 2;
   SET datafusion.optimizer.prefer_existing_sort = true;
   SET datafusion.optimizer.repartition_file_scans = false;
   
   CREATE EXTERNAL TABLE w (a INT, b INT) STORED AS CSV
   WITH ORDER (a) WITH ORDER (b)
   LOCATION 'w/' OPTIONS ('format.has_header' 'true');
   
   SELECT a, b, count(*) FROM w GROUP BY a, b ORDER BY b;
   ```
   
   Each file is sorted by `a` and also by `b`, as the table declares. 
`prefer_existing_sort` makes the planner keep the order through the repartition 
instead of sorting, and `repartition_file_scans = false` keeps one file per 
partition.
   
   ### Expected behavior
   
   | Query | DataFusion returns `(a, b)` | Expected |
   |---|---|---|
   | `SELECT a, b, count(*) FROM w GROUP BY a, b ORDER BY b` | `(1, 1), (1, 
10), (2, 20), (3, 2)` | `(1, 1), (3, 2), (1, 10), (2, 20)` |
   
   `count(*)` is 1 on every row. DuckDB returns the expected order.
   
   ### Additional context
   
   The plan has no sort for `ORDER BY b`:
   
   ```
   SortPreservingMergeExec: [b@1 ASC NULLS LAST]
     ProjectionExec: expr=[a@0 as a, b@1 as b, count(Int64(1))@2 as count(*)]
       AggregateExec: mode=FinalPartitioned, gby=[a@0 as a, b@1 as b], 
aggr=[count(Int64(1))], group_clustering_mode=Full
         RepartitionExec: partitioning=Hash([a@0, b@1], 2), input_partitions=2, 
preserve_order=true, sort_exprs=a@0 ASC NULLS LAST, b@1 ASC NULLS LAST
           AggregateExec: mode=Partial, gby=[a@0 as a, b@1 as b], 
aggr=[count(Int64(1))], group_clustering_mode=Full
             DataSourceExec: file_groups={2 groups: [[.../w/f1.csv], 
[.../w/f2.csv]]}, projection=[a, b], output_orderings=[[a@0 ASC NULLS LAST], 
[b@1 ASC NULLS LAST]], file_type=csv, has_header=true
   ```
   
   - `RepartitionExec` merges on `sort_exprs`, which is 
`self.input.output_ordering()` 
(`datafusion/physical-plan/src/repartition/mod.rs`). 
`EquivalenceProperties::output_ordering` joins all the input's orderings into 
one, so here the merge key is `[a, b]`, and each output partition is sorted by 
`[a, b]`, not by `b`.
   - `RepartitionExec::eq_properties_helper` keeps every input ordering when 
`preserve_order` is set, so the output still claims `[b]`. The sort for `ORDER 
BY b` is removed, and the final `SortPreservingMergeExec` merges partitions 
that are not sorted by `b`.
   
   A fix direction: when `preserve_order` is set and there is more than one 
input partition, keep only the merge key's ordering (clear the orderings, then 
add `sort_exprs`), as #26654 did for `SortPreservingMergeExec`. I have not 
tried it yet. A fix should add a sqllogictest for the query above.
   
   ### Willingness to contribute
   
   Anyone is welcome to pick this up. Feel free to ping me with questions about 
the cause or when you'd like a review.
   


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