jayzhan211 commented on PR #24648:
URL: https://github.com/apache/datafusion/pull/24648#issuecomment-5441235067

   Thanks for this — a 0-row file shouldn't invalidate the ordering of the 
other files, and the mixed case works nicely.
   
   One thing to fix before merge. `new_from_files` is used for two things: the 
ordering reasoning, *and* rebuilding the list of files to scan (the callers do 
`flattened_files[statistics.file_index(idx)]`). Skipping empty files is right 
for the first, but it means they no longer have a statistics row — so they 
never make it back into the rebuilt list and silently disappear from the plan.
   
   That's harmless when they'd contribute no rows anyway, but if *all* the 
files reaching the split are empty, the rebuilt list is empty too and we get 
`file_groups={0 groups: []}` → `UnknownPartitioning(0)`. Plain scans still 
return the right answer, but anything with a distribution requirement then 
fails the plan sanity check.
   
   It doesn't need a fully empty table — partition filters are applied before 
the split, so one empty partition is enough:
   
   ```sql
   -- t partitioned by p:  p=A -> 3 rows,  p=B -> a single 0-row parquet file
   SELECT int_col, count(*) FROM t GROUP BY int_col;                -- ok: 1, 
2, 3
   SELECT int_col, count(*) FROM t WHERE p = 'B' GROUP BY int_col;  -- fails on 
this PR
   ```
   
   ```
   SanityCheckPlan: ... does not satisfy distribution requirements:
     KeyPartitioned[[int_col@0]]). Child-0 output partitioning: 
UnknownPartitioning(0)
   ```
   
   Same for a hash join or a window function over that filter; all of them pass 
on the base commit. It only shows up with `split_file_groups_by_statistics = 
true`, so it isn't a default-path break, but 0-row parquet files are common 
enough in practice (DataFusion's own `COPY (SELECT ... WHERE FALSE)` writes 
one, and Spark/Flink jobs emit them for empty batches) that I think it's worth 
handling.
   
   Could the empty files stay in the file list, and be excluded only from the 
min/max reasoning? Appending them at the end of a group is safe since they 
contribute no rows. One option that keeps `MinMaxStatistics` 1:1 with its input:
   
   ```rust
   let (files, empty_files): (Vec<_>, Vec<_>) =
       group.iter().partition(|f| !is_known_empty(f));
   
   let statistics = MinMaxStatistics::new_from_files(..., 
files.iter().copied())?;
   
   sorted_indices
       .iter()
       .map(|(idx, _)| files[*idx].clone())
       .chain(empty_files.iter().map(|f| (*f).clone()))
       .collect()
   ```
   
   That would also let `file_index()` / `file_count()` go away. Three call 
sites rebuild groups this way: `split_groups_by_statistics`, 
`split_groups_by_statistics_with_target_partitions`, and 
`sort_files_within_groups_by_statistics` — the last one can currently also 
produce a group with zero files (two empty files in one group), and its doc 
comment still says group composition is unchanged.
   
   A couple of smaller things:
   
   - Worth a unit test on the rebuild path — nothing in 
`test_split_groups_by_statistics*` passes an empty file today, so a regression 
there wouldn't be caught by anything except the slt.
   - `num_rows == Precision::Exact(0)` now appears in three files; a shared 
`is_known_empty(&Statistics) -> bool` would keep the definition in one place.
   - `new_from_files` on an empty input used to return a `plan_err`; it now 
returns `Ok` with `is_sorted() == true`. No caller hits it today, but it's a 
silent contract change.
   
   Everything else checks out on my end: `parquet_sorted_statistics.slt` and 
`parquet_statistics.slt` pass, both crates' lib tests pass, and fmt/clippy are 
clean.
   


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