Push sort requirements into file-based data sources. # Sort Pushdown Architecture When a partition (file group) contains multiple files in wrong order, `validated_output_ordering()` strips the ordering and `EnforceSorting` inserts a `SortExec`. This optimizer fixes the file order by sorting files within each group by min/max statistics, enabling sort elimination. This applies to both single-par
(
&self,
order: &[PhysicalSortExpr],
)
| 954 | /// NO → Inexact (files reordered, Sort stays) |
| 955 | /// ``` |
| 956 | fn try_pushdown_sort( |
| 957 | &self, |
| 958 | order: &[PhysicalSortExpr], |
| 959 | ) -> Result<SortOrderPushdownResult<Arc<dyn DataSource>>> { |
| 960 | let pushdown_result = self |
| 961 | .file_source |
| 962 | .try_pushdown_sort(order, &self.eq_properties())?; |
| 963 | |
| 964 | match pushdown_result { |
| 965 | SortOrderPushdownResult::Exact { inner } => { |
| 966 | let config = self.rebuild_with_source(inner, true, order)?; |
| 967 | // rebuild_with_source keeps output_ordering only when all groups |
| 968 | // are non-overlapping. If output_ordering was cleared, files |
| 969 | // overlap despite within-file ordering → downgrade to Inexact. |
| 970 | if config.output_ordering.is_empty() { |
| 971 | Ok(SortOrderPushdownResult::Inexact { |
| 972 | inner: Arc::new(config), |
| 973 | }) |
| 974 | } else { |
| 975 | Ok(SortOrderPushdownResult::Exact { |
| 976 | inner: Arc::new(config), |
| 977 | }) |
| 978 | } |
| 979 | } |
| 980 | SortOrderPushdownResult::Inexact { inner } => { |
| 981 | let mut config = self.rebuild_with_source(inner, false, order)?; |
| 982 | // `rebuild_with_source` reorders files by stats; if the |
| 983 | // post-sort files are non-overlapping AND the request now |
| 984 | // validates against the new file groups, `output_ordering` |
| 985 | // is preserved and we can upgrade back to Exact. This |
| 986 | // restores the sort-elimination behaviour that lived in |
| 987 | // the `Unsupported` → `try_sort_file_groups_by_statistics` |
| 988 | // path before #21956 routed `column_in_file_schema` cases |
| 989 | // here. |
| 990 | if config.output_ordering.is_empty() { |
| 991 | return Ok(SortOrderPushdownResult::Inexact { |
| 992 | inner: Arc::new(config), |
| 993 | }); |
| 994 | } |
| 995 | // Upgrading to Exact: the post-sort file groups are |
| 996 | // non-overlapping and each file's declared ordering |
| 997 | // re-validates, so reading the files in their natural |
| 998 | // (declared-sorted) order already yields the requested |
| 999 | // ordering — exactly like the `Unsupported` → Exact path, |
| 1000 | // which reads files in natural order too. |
| 1001 | // |
| 1002 | // Drop the runtime row-group reorder hints the Inexact |
| 1003 | // source carried (`sort_order_for_reorder` / |
| 1004 | // `reverse_row_groups`) by restoring the original, |
| 1005 | // hint-free source. With the `SortExec` removed those |
| 1006 | // hints are not just redundant but unsafe: for a DESC |
| 1007 | // request the opener sorts row groups ASC-by-min and then |
| 1008 | // reverses them, which reorders two row groups within a |
| 1009 | // single file that share the same `min` incorrectly |
| 1010 | // (e.g. a file `[10,8,8,8]` whose row groups are |
| 1011 | // `[10,8]` and `[8,8]` would stream as `8,8,10,8`). |
| 1012 | // The `SortExec` used to mask this; once it is gone the |
| 1013 | // reordered stream is the final, wrong answer. |