From ff017d07f33b4e6282b7e5a59991e3f9e4e20298 Mon Sep 17 00:00:00 2001 From: Michael Kleen Date: Sun, 5 Jul 2026 07:15:27 +0200 Subject: [PATCH] fix: Avoid panicing when stats are not available for a file group split (#23277) ## Which issue does this PR close? - Closes https://github.com/apache/datafusion/issues/23219. ## Rationale for this change The query from the issue: ```sql SELECT (((Cast(id AS BIGINT) % 1024) + 1024) % 1024) AS computed_bucket FROM profile ORDER BY computed_bucket, Cast(id AS BIGINT) limit 10; ``` panics: ``` thread 'main' panicked at .../datafusion-datasource-54.0.0/src/statistics.rs:100:48: index out of bounds: the len is 0 but the index is 0 ``` The underlying issue is that the current code panics when files are split by statistics and there are no statistics available for the column where the sort order is defined in this case `computed_bucket`. ## What changes are included in this PR? - Fix in `MinMaxStatistics` to check if there are stats available for a given column - Test ## Are these changes tested? Yes ## Are there any user-facing changes? No --- datafusion/datasource/src/statistics.rs | 39 +++++++++++++++++++++++-- 1 file changed, 37 insertions(+), 2 deletions(-) diff --git a/datafusion/datasource/src/statistics.rs b/datafusion/datasource/src/statistics.rs index 6abfafe9d39d4..5063bf7785816 100644 --- a/datafusion/datasource/src/statistics.rs +++ b/datafusion/datasource/src/statistics.rs @@ -97,8 +97,16 @@ impl MinMaxStatistics { .zip(s.column_statistics[i].max_value.get_value().cloned()) .ok_or_else(|| plan_datafusion_err!("statistics not found")) } else { - let partition_value = &pv[i - s.column_statistics.len()]; - Ok((partition_value.clone(), partition_value.clone())) + if let Some(partition_value) = + pv.get(i - s.column_statistics.len()) + { + Ok((partition_value.clone(), partition_value.clone())) + } else { + Err(plan_datafusion_err!( + "statistics not found for partition, expected at most {}", + s.column_statistics.len() + )) + } } }) .collect::>>()? @@ -890,4 +898,31 @@ mod tests { Ok(()) } + + #[test] + fn min_max_statistics_missing_column_stats_returns_error() { + let schema = test_schema(); + let sort_order = + [PhysicalSortExpr::new_default(Arc::new(Column::new("a", 0)))].into(); + let files = [ + file_with_stats("f1.parquet", Statistics::default()), + file_with_stats("f2.parquet", Statistics::default()), + ]; + + let err = match MinMaxStatistics::new_from_files( + &sort_order, + &schema, + None, + files.iter(), + ) { + Ok(_) => panic!("expected missing statistics error"), + Err(err) => err, + }; + + assert!( + err.to_string() + .contains("statistics not found for partition"), + "unexpected error: {err:?}" + ); + } }