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


##########
native/core/src/parquet/schema_adapter.rs:
##########
@@ -429,6 +429,23 @@ fn is_string_or_binary(dt: &DataType) -> bool {
     )
 }
 
+/// Whether `cast` reconciles a column's Parquet (file) type with its 
table-schema type, as opposed
+/// to being part of the pushed-down expression itself.
+///
+/// DataFusion's `DefaultPhysicalExprAdapter` builds the former with
+/// `CastExpr::new_with_target_field`, passing the table schema's field, so 
the target is named
+/// after the column (or nested field). Comet's planner builds the latter with 
`CastExpr::new`, for
+/// example to coerce a scalar function's arguments to the types the function 
requires:
+/// `substring(s, 1, 2)` casts its Int32 literals to Int64. `CastExpr::new` 
produces an unnamed
+/// target field.
+///
+/// Only the former are Parquet read conversions that Spark's vectorized 
reader validates. Applying
+/// those rules to the latter rejects valid queries, for example as a 
disallowed `INT32 -> bigint`
+/// promotion on Spark 3.x when row-level filter pushdown evaluates the 
predicate.
+fn is_schema_adaptation_cast(cast: 
&datafusion::physical_expr::expressions::CastExpr) -> bool {
+    !cast.target_field().name().is_empty()
+}

Review Comment:
   A concrete fix for the empty-name case sunchao raised. 
`CastExpr::has_explicit_metadata()` exists in the pinned DataFusion 55.2.0 
(`physical-expr/src/expressions/cast.rs`, it returns the `explicit_target` flag 
that only `new_with_target_field` sets, and `with_new_children` preserves it). 
The default adapter builds its schema casts with `new_with_target_field` and 
Comet's planner uses `CastExpr::new`, so `!cast.has_explicit_metadata()` at the 
call site would separate the two without relying on the field name, and 
`is_schema_adaptation_cast` could go. I read this from the sources and did not 
run it.



##########
native/core/src/parquet/schema_adapter.rs:
##########
@@ -2350,6 +2375,220 @@ pub(crate) mod test {
         Ok(())
     }
 
+    /// `is_schema_adaptation_cast` tells the casts DataFusion's default 
adapter inserts apart from
+    /// the casts Comet's planner puts in a predicate by whether the target 
field is named. If a
+    /// DataFusion upgrade changes how `CastExpr::new` names its target, this 
fails and the check
+    /// has to be revisited (newer DataFusion versions also expose an 
explicit-target marker).

Review Comment:
   This comment says newer DataFusion versions expose an explicit-target 
marker, but 55.2.0 already has `has_explicit_metadata()`. The PR description 
makes the same claim. The test 
`schema_adaptation_cast_is_told_apart_by_target_field_name` also pins a naming 
convention that DataFusion pins itself. If the helper is replaced, a regression 
test with a `Field::new("", ..)` target built through `new_with_target_field` 
would be more useful here, together with a scan-level case where a file `Int64` 
column named `""` read as `Int32` must still raise `ParquetSchemaConvert`.



##########
native/core/src/parquet/schema_adapter.rs:
##########
@@ -2350,6 +2375,220 @@ pub(crate) mod test {
         Ok(())
     }
 
+    /// `is_schema_adaptation_cast` tells the casts DataFusion's default 
adapter inserts apart from
+    /// the casts Comet's planner puts in a predicate by whether the target 
field is named. If a
+    /// DataFusion upgrade changes how `CastExpr::new` names its target, this 
fails and the check
+    /// has to be revisited (newer DataFusion versions also expose an 
explicit-target marker).
+    #[test]
+    fn schema_adaptation_cast_is_told_apart_by_target_field_name() {
+        use super::is_schema_adaptation_cast;
+        use datafusion::physical_expr::expressions::CastExpr;
+
+        // Built by the planner, from a bare type.
+        let planner_cast = CastExpr::new(
+            Arc::new(Literal::new(ScalarValue::Int32(Some(1)))),
+            DataType::Int64,
+            None,
+        );
+        assert!(!is_schema_adaptation_cast(&planner_cast));
+
+        // Built by the default adapter, from the table schema's field.
+        let adapter_cast = CastExpr::new_with_target_field(
+            Arc::new(Column::new("a", 0)),
+            Arc::new(Field::new("a", DataType::Int64, true)),
+            None,
+        );
+        assert!(is_schema_adaptation_cast(&adapter_cast));
+    }
+
+    /// Scans a Parquet file with a pushed-down `substring(s, 1, 2) = 'ab'` 
data filter, the way
+    /// `CometNativeScan` sends it, and returns the values the scan produced.
+    ///
+    /// The file schema is identical to the requested schema, so there is no 
schema evolution of any
+    /// kind. The predicate comes from the real planner, fed the proto 
`CometSubstring` emits (Int32
+    /// literals for the position and length). `SparkSubstring` takes Int64 
positions, so the planner
+    /// coerces those literals with `CastExpr::new`.
+    async fn scan_with_substring_filter(

Review Comment:
   `scan_with_substring_filter` rebuilds the Parquet writer, `ParquetSource`, 
`FileScanConfigBuilder`, adapter factory and `DataSourceExec` setup that 
`scan_parquet_file` (line 2634) already provides, and that helper is already 
used with `row_filters = true` and a predicate in the tests around lines 3559 
to 3694. The three `pushed_substring_filter_*` tests also differ only in 
`(row_filter_pushdown, allow_type_promotion)` and the expected values, and only 
the middle one fails without the fix, so one loop with a comment on the 
controls would do. The `try_pushdown_filters` route matches `parquet_exec.rs`, 
so that part may need to stay. I have not run `scan_parquet_file` with a 
`CAST(id AS Int64) = lit` predicate to see whether it reproduces the failure.



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