You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@datafusion.apache.org by al...@apache.org on 2025/03/02 13:05:31 UTC
(datafusion) branch main updated: Do not swap with projection when file is partitioned (#14956)
This is an automated email from the ASF dual-hosted git repository.
alamb pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/datafusion.git
The following commit(s) were added to refs/heads/main by this push:
new 5e27008dd5 Do not swap with projection when file is partitioned (#14956)
5e27008dd5 is described below
commit 5e27008dd5d4ab3cc91ee7d1ea7b293ed1f917ca
Author: Dmitrii Blaginin <dm...@blaginin.me>
AuthorDate: Sun Mar 2 13:05:26 2025 +0000
Do not swap with projection when file is partitioned (#14956)
* Do not swap with projection when file is partitioned
* Narrow the case when not swapping
* Add test
---
.../physical_optimizer/projection_pushdown.rs | 51 ++++++++++++++++++++++
datafusion/datasource/src/file_scan_config.rs | 43 +++++++++++-------
2 files changed, 78 insertions(+), 16 deletions(-)
diff --git a/datafusion/core/tests/physical_optimizer/projection_pushdown.rs b/datafusion/core/tests/physical_optimizer/projection_pushdown.rs
index 836758b213..77c32f5623 100644
--- a/datafusion/core/tests/physical_optimizer/projection_pushdown.rs
+++ b/datafusion/core/tests/physical_optimizer/projection_pushdown.rs
@@ -1382,3 +1382,54 @@ fn test_union_after_projection() -> Result<()> {
Ok(())
}
+
+#[test]
+fn test_partition_col_projection_pushdown() -> Result<()> {
+ let file_schema = Arc::new(Schema::new(vec![
+ Field::new("int_col", DataType::Int32, true),
+ Field::new("string_col", DataType::Utf8, true),
+ ]));
+
+ let partitioned_schema = Arc::new(Schema::new(vec![
+ Field::new("int_col", DataType::Int32, true),
+ Field::new("string_col", DataType::Utf8, true),
+ Field::new("partition_col", DataType::Utf8, true),
+ ]));
+
+ let source = FileScanConfig::new(
+ ObjectStoreUrl::parse("test:///").unwrap(),
+ file_schema.clone(),
+ Arc::new(CsvSource::default()),
+ )
+ .with_file(PartitionedFile::new("x".to_string(), 100))
+ .with_table_partition_cols(vec![Field::new("partition_col", DataType::Utf8, true)])
+ .with_projection(Some(vec![0, 1, 2]))
+ .build();
+
+ let projection = Arc::new(ProjectionExec::try_new(
+ vec![
+ (
+ col("string_col", partitioned_schema.as_ref())?,
+ "string_col".to_string(),
+ ),
+ (
+ col("partition_col", partitioned_schema.as_ref())?,
+ "partition_col".to_string(),
+ ),
+ (
+ col("int_col", partitioned_schema.as_ref())?,
+ "int_col".to_string(),
+ ),
+ ],
+ source,
+ )?);
+
+ let after_optimize =
+ ProjectionPushdown::new().optimize(projection, &ConfigOptions::new())?;
+
+ let expected = ["ProjectionExec: expr=[string_col@1 as string_col, partition_col@2 as partition_col, int_col@0 as int_col]",
+ " DataSourceExec: file_groups={1 group: [[x]]}, projection=[int_col, string_col, partition_col], file_type=csv, has_header=false"];
+ assert_eq!(get_plan_string(&after_optimize), expected);
+
+ Ok(())
+}
diff --git a/datafusion/datasource/src/file_scan_config.rs b/datafusion/datasource/src/file_scan_config.rs
index 79279b5c82..bee74e042f 100644
--- a/datafusion/datasource/src/file_scan_config.rs
+++ b/datafusion/datasource/src/file_scan_config.rs
@@ -266,22 +266,33 @@ impl DataSource for FileScanConfig {
) -> Result<Option<Arc<dyn ExecutionPlan>>> {
// If there is any non-column or alias-carrier expression, Projection should not be removed.
// This process can be moved into CsvExec, but it would be an overlap of their responsibility.
- Ok(all_alias_free_columns(projection.expr()).then(|| {
- let file_scan = self.clone();
- let source = Arc::clone(&file_scan.file_source);
- let new_projections = new_projections_for_columns(
- projection,
- &file_scan
- .projection
- .clone()
- .unwrap_or((0..self.file_schema.fields().len()).collect()),
- );
- file_scan
- // Assign projected statistics to source
- .with_projection(Some(new_projections))
- .with_source(source)
- .build() as _
- }))
+
+ let partitioned_columns_in_proj = projection.expr().iter().any(|(expr, _)| {
+ expr.as_any()
+ .downcast_ref::<Column>()
+ .map(|expr| expr.index() >= self.file_schema.fields().len())
+ .unwrap_or(false)
+ });
+
+ Ok(
+ (all_alias_free_columns(projection.expr()) && !partitioned_columns_in_proj)
+ .then(|| {
+ let file_scan = self.clone();
+ let source = Arc::clone(&file_scan.file_source);
+ let new_projections = new_projections_for_columns(
+ projection,
+ &file_scan
+ .projection
+ .clone()
+ .unwrap_or((0..self.file_schema.fields().len()).collect()),
+ );
+ file_scan
+ // Assign projected statistics to source
+ .with_projection(Some(new_projections))
+ .with_source(source)
+ .build() as _
+ }),
+ )
}
}
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@datafusion.apache.org
For additional commands, e-mail: commits-help@datafusion.apache.org