comphead commented on code in PR #5262:
URL: https://github.com/apache/datafusion-comet/pull/5262#discussion_r3935620815


##########
native/core/src/parquet/schema_adapter.rs:
##########
@@ -77,6 +78,91 @@ fn schema_has_field_ids(schema: &SchemaRef) -> bool {
     schema.fields().iter().any(|f| parse_field_id(f).is_some())
 }
 
+/// Returns true when casting `physical_type` to `target_type` is a *pure* 
structural
+/// narrowing (dropping unrequested struct/list fields, no leaf-level value 
reinterpretation)
+/// that DataFusion's own `datafusion_common::nested_struct::cast_column` 
already computes
+/// identically to Comet's `spark_parquet_convert`. `ColumnarValue::cast_to` 
(the function a
+/// plain, un-swapped `CastExpr` runs at execution time; see
+/// datafusion/expr-common/src/columnar_value.rs) routes to that same function 
whenever
+/// `datafusion_common::nested_struct::requires_nested_struct_cast` holds, 
matching arbitrary
+/// struct/list fields by name, null-filling missing target fields, and 
dropping extra source
+/// fields, exactly the shape apache/datafusion-comet#4859 needs pruned. 
Confirmed
+/// byte-identical to `spark_parquet_convert` for the covered shapes by
+/// `test::nested_struct_narrowing_cast_matches_datafusion_generic_cast`.
+///
+/// When this returns `true`, `replace_with_spark_cast` leaves DataFusion's 
`CastExpr` in
+/// place instead of swapping in `CometCastColumnExpr`, so DataFusion's 
leaf-pruning
+/// (`build_projection_read_plan`'s cast-clipping, apache/datafusion#24090) 
can see the cast
+/// and read only the requested Parquet leaves, instead of falling back to a 
full-column read
+/// because it can't recognize `CometCastColumnExpr`.
+///
+/// This is deliberately an allow list, not a deny list: it only recurses 
through the two
+/// container shapes `nested_struct::cast_column` actually implements (Struct, 
List /
+/// LargeList), and requires every leaf it bottoms out at to be an *exact* 
type match. Pruning
+/// (the only case this predicate needs to cover, see 
apache/datafusion-comet#4859) only
+/// changes which struct/list fields are kept, never a leaf's type, so 
exact-match leaves are
+/// sufficient. A deny list here (enumerate every case where Comet's nested 
cast differs from
+/// Arrow's, allow everything else) would fail open: a future addition to
+/// `parquet_convert_array` that this predicate does not know to also exclude 
would silently
+/// start producing wrong results instead of just missing an optimization.
+fn is_pure_structural_narrowing(
+    physical_type: &DataType,
+    target_type: &DataType,
+    parquet_options: &SparkParquetOptions,
+) -> bool {
+    match (physical_type, target_type) {
+        (DataType::Struct(source_fields), DataType::Struct(target_fields)) => {
+            // Comet matches by Parquet field id first when the target carries 
one;
+            // DataFusion's generic cast has no field-id concept, so any 
field-id-bearing
+            // target field is a potential divergence.
+            if parquet_options.use_field_id
+                && target_fields.iter().any(|f| parse_field_id(f).is_some())
+            {
+                return false;
+            }
+            target_fields.iter().all(|target_field| {
+                // Require an *exact* (case-sensitive) name match for every 
target field.
+                // `nested_struct::cast_column` always matches by exact name; 
Comet
+                // additionally matches case-insensitively when 
`case_sensitive` is false,
+                // which could resolve a field DataFusion would instead treat 
as missing (and
+                // null-fill). Requiring an exact match sidesteps that 
divergence regardless
+                // of the `case_sensitive` setting, and also sidesteps the 
missing-field
+                // nullability divergence: DataFusion errors when a 
non-nullable target field
+                // is missing from the source, whereas Comet null-fills 
unconditionally.
+                source_fields
+                    .iter()
+                    .find(|f| f.name() == target_field.name())
+                    .is_some_and(|source_field| {

Review Comment:
   it might be a good follow up ticket



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