This is an automated email from the ASF dual-hosted git repository.

agrove pushed a commit to branch comet-parquet-exec
in repository https://gitbox.apache.org/repos/asf/datafusion-comet.git


The following commit(s) were added to refs/heads/comet-parquet-exec by this 
push:
     new eafda43e [comet-parquet-exec] Pass Spark's partitions to DF's 
ParquetExec (#1081)
eafda43e is described below

commit eafda43e7cd3bf6465e3594ed7d4fd01e622bd5c
Author: Matt Butrovich <[email protected]>
AuthorDate: Wed Nov 13 14:58:32 2024 -0500

    [comet-parquet-exec] Pass Spark's partitions to DF's ParquetExec (#1081)
    
    * I think serde works. Gonna try removing the old stuff.
    
    * Fixes after merging in upstream.
    
    * Remove previous file_config logic. Clippy.
    
    * Temporary assertion for testing.
    
    * Remove old path proto value.
    
    * Selectively generate projection vector.
---
 native/core/src/execution/datafusion/planner.rs    | 75 ++++++++++++----------
 native/proto/src/proto/operator.proto              | 21 ++++--
 .../org/apache/comet/serde/QueryPlanSerde.scala    | 21 +++++-
 3 files changed, 76 insertions(+), 41 deletions(-)

diff --git a/native/core/src/execution/datafusion/planner.rs 
b/native/core/src/execution/datafusion/planner.rs
index c709021f..cd79e8e0 100644
--- a/native/core/src/execution/datafusion/planner.rs
+++ b/native/core/src/execution/datafusion/planner.rs
@@ -949,8 +949,8 @@ impl PhysicalPlanner {
                 ))
             }
             OpStruct::NativeScan(scan) => {
-                let data_schema = 
parse_message_type(&*scan.data_schema).unwrap();
-                let required_schema = 
parse_message_type(&*scan.required_schema).unwrap();
+                let data_schema = 
parse_message_type(&scan.data_schema).unwrap();
+                let required_schema = 
parse_message_type(&scan.required_schema).unwrap();
 
                 let data_schema_descriptor =
                     
parquet::schema::types::SchemaDescriptor::new(Arc::new(data_schema));
@@ -968,16 +968,6 @@ impl PhysicalPlanner {
                     )
                     .unwrap(),
                 );
-                assert!(!required_schema_arrow.fields.is_empty());
-
-                let mut projection_vector: Vec<usize> =
-                    Vec::with_capacity(required_schema_arrow.fields.len());
-                // TODO: could be faster with a hashmap rather than iterating 
over data_schema_arrow with index_of.
-                required_schema_arrow.fields.iter().for_each(|field| {
-                    
projection_vector.push(data_schema_arrow.index_of(field.name()).unwrap());
-                });
-
-                assert_eq!(projection_vector.len(), 
required_schema_arrow.fields.len());
 
                 // Convert the Spark expressions to Physical expressions
                 let data_filters: Result<Vec<Arc<dyn PhysicalExpr>>, 
ExecutionError> = scan
@@ -997,13 +987,6 @@ impl PhysicalPlanner {
                     ))
                 });
 
-                let object_store_url = ObjectStoreUrl::local_filesystem();
-                let paths: Vec<Url> = scan
-                    .path
-                    .iter()
-                    .map(|path| Url::parse(path).unwrap())
-                    .collect();
-
                 let object_store = object_store::local::LocalFileSystem::new();
                 // register the object store with the runtime environment
                 let url = Url::try_from("file://").unwrap();
@@ -1011,25 +994,49 @@ impl PhysicalPlanner {
                     .runtime_env()
                     .register_object_store(&url, Arc::new(object_store));
 
-                let files: Vec<PartitionedFile> = paths
-                    .iter()
-                    .map(|path| 
PartitionedFile::from_path(path.path().to_string()).unwrap())
-                    .collect();
-
-                // partition the files
-                // TODO really should partition the row groups
-
-                let mut file_groups = vec![vec![]; partition_count];
-                files.iter().enumerate().for_each(|(idx, file)| {
-                    file_groups[idx % partition_count].push(file.clone());
+                // Generate file groups
+                let mut file_groups: Vec<Vec<PartitionedFile>> =
+                    Vec::with_capacity(partition_count);
+                scan.file_partitions.iter().for_each(|partition| {
+                    let mut files = 
Vec::with_capacity(partition.partitioned_file.len());
+                    partition.partitioned_file.iter().for_each(|file| {
+                        assert!(file.start + file.length <= file.file_size);
+                        files.push(PartitionedFile::new_with_range(
+                            Url::parse(file.file_path.as_ref())
+                                .unwrap()
+                                .path()
+                                .to_string(),
+                            file.file_size as u64,
+                            file.start,
+                            file.start + file.length,
+                        ));
+                    });
+                    file_groups.push(files);
                 });
 
-                let file_scan_config =
+                // TODO: I think we can remove partition_count in the future, 
but leave for testing.
+                assert_eq!(file_groups.len(), partition_count);
+
+                let object_store_url = ObjectStoreUrl::local_filesystem();
+                let mut file_scan_config =
                     FileScanConfig::new(object_store_url, 
Arc::clone(&data_schema_arrow))
-                        .with_file_groups(file_groups)
-                        .with_projection(Some(projection_vector));
+                        .with_file_groups(file_groups);
+
+                // Check for projection, if so generate the vector and add to 
FileScanConfig.
+                if !required_schema_arrow.fields.is_empty() {
+                    let mut projection_vector: Vec<usize> =
+                        Vec::with_capacity(required_schema_arrow.fields.len());
+                    // TODO: could be faster with a hashmap rather than 
iterating over data_schema_arrow with index_of.
+                    required_schema_arrow.fields.iter().for_each(|field| {
+                        
projection_vector.push(data_schema_arrow.index_of(field.name()).unwrap());
+                    });
+
+                    assert_eq!(projection_vector.len(), 
required_schema_arrow.fields.len());
+                    file_scan_config = 
file_scan_config.with_projection(Some(projection_vector));
+                }
 
                 let mut table_parquet_options = TableParquetOptions::new();
+                // TODO: Maybe these are configs?
                 table_parquet_options.global.pushdown_filters = true;
                 table_parquet_options.global.reorder_filters = true;
 
@@ -1041,7 +1048,7 @@ impl PhysicalPlanner {
                 }
 
                 let scan = builder.build();
-                return Ok((vec![], Arc::new(scan)));
+                Ok((vec![], Arc::new(scan)))
             }
             OpStruct::Scan(scan) => {
                 let data_types = 
scan.fields.iter().map(to_arrow_datatype).collect_vec();
diff --git a/native/proto/src/proto/operator.proto 
b/native/proto/src/proto/operator.proto
index fbcea872..f50389db 100644
--- a/native/proto/src/proto/operator.proto
+++ b/native/proto/src/proto/operator.proto
@@ -47,6 +47,19 @@ message Operator {
   }
 }
 
+message SparkPartitionedFile {
+  string file_path = 1;
+  int64 start = 2;
+  int64 length = 3;
+  int64 file_size = 4;
+}
+
+// This name and the one above are not great, but they correspond to the 
(unfortunate) Spark names.
+// I prepended "Spark" since I think there's a name collision on the native 
side, but we can revisit.
+message SparkFilePartition {
+  repeated SparkPartitionedFile partitioned_file = 1;
+}
+
 message Scan {
   repeated spark.spark_expression.DataType fields = 1;
   // The source of the scan (e.g. file scan, broadcast exchange, shuffle, 
etc). This
@@ -61,10 +74,10 @@ message NativeScan {
   // is purely for informational purposes when viewing native query plans in
   // debug mode.
   string source = 2;
-  repeated string path = 3;
-  string required_schema = 4;
-  string data_schema = 5;
-  repeated spark.spark_expression.Expr data_filters = 6;
+  string required_schema = 3;
+  string data_schema = 4;
+  repeated spark.spark_expression.Expr data_filters = 5;
+  repeated SparkFilePartition file_partitions = 6;
 }
 
 message Projection {
diff --git a/spark/src/main/scala/org/apache/comet/serde/QueryPlanSerde.scala 
b/spark/src/main/scala/org/apache/comet/serde/QueryPlanSerde.scala
index 348f9c54..cce48204 100644
--- a/spark/src/main/scala/org/apache/comet/serde/QueryPlanSerde.scala
+++ b/spark/src/main/scala/org/apache/comet/serde/QueryPlanSerde.scala
@@ -35,6 +35,7 @@ import org.apache.spark.sql.execution
 import org.apache.spark.sql.execution._
 import org.apache.spark.sql.execution.adaptive.{BroadcastQueryStageExec, 
ShuffleQueryStageExec}
 import org.apache.spark.sql.execution.aggregate.{BaseAggregateExec, 
HashAggregateExec, ObjectHashAggregateExec}
+import org.apache.spark.sql.execution.datasources.FileScanRDD
 import 
org.apache.spark.sql.execution.datasources.parquet.SparkToParquetSchemaConverter
 import org.apache.spark.sql.execution.exchange.{BroadcastExchangeExec, 
ReusedExchangeExec, ShuffleExchangeExec}
 import org.apache.spark.sql.execution.joins.{BroadcastHashJoinExec, HashJoin, 
ShuffledHashJoinExec, SortMergeJoinExec}
@@ -2496,6 +2497,23 @@ object QueryPlanSerde extends Logging with 
ShimQueryPlanSerde with CometExprShim
           val dataFilters = scan.dataFilters.map(exprToProto(_, scan.output))
           nativeScanBuilder.addAllDataFilters(dataFilters.map(_.get).asJava)
 
+          // Eventually we'll want to modify CometNativeScan to generate the 
file partitions
+          // for us without instantiating the RDD.
+          val file_partitions = 
scan.inputRDD.asInstanceOf[FileScanRDD].filePartitions;
+          file_partitions.foreach(partition => {
+            val partitionBuilder = 
OperatorOuterClass.SparkFilePartition.newBuilder()
+            partition.files.foreach(file => {
+              val fileBuilder = 
OperatorOuterClass.SparkPartitionedFile.newBuilder()
+              fileBuilder
+                .setFilePath(file.pathUri.toString)
+                .setStart(file.start)
+                .setLength(file.length)
+                .setFileSize(file.fileSize)
+              partitionBuilder.addPartitionedFile(fileBuilder.build())
+            })
+            nativeScanBuilder.addFilePartitions(partitionBuilder.build())
+          })
+
           val requiredSchemaParquet =
             new 
SparkToParquetSchemaConverter(conf).convert(scan.requiredSchema)
           val dataSchemaParquet =
@@ -2503,9 +2521,6 @@ object QueryPlanSerde extends Logging with 
ShimQueryPlanSerde with CometExprShim
 
           nativeScanBuilder.setRequiredSchema(requiredSchemaParquet.toString)
           nativeScanBuilder.setDataSchema(dataSchemaParquet.toString)
-          scan.relation.location.inputFiles.foreach { f =>
-            nativeScanBuilder.addPath(f)
-          }
 
           Some(result.setNativeScan(nativeScanBuilder).build())
 


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to