zhuqi-lucas commented on code in PR #24638:
URL: https://github.com/apache/datafusion/pull/24638#discussion_r3848922786


##########
datafusion/core/tests/parquet/filter_pushdown.rs:
##########
@@ -746,3 +752,51 @@ impl PredicateCacheTest {
         Ok(())
     }
 }
+
+/// A predicate that is pushed into the parquet decoder and then fails while it
+/// is being evaluated must report the original error, so that callers can 
still
+/// tell a user error apart from an internal one.
+#[tokio::test]
+async fn pushed_down_predicate_reports_the_original_error() {
+    let tempdir = TempDir::new_in(Path::new(".")).unwrap();
+    let path = tempdir.path().join("cast_error.parquet");
+
+    let batch = RecordBatch::try_from_iter(vec![(
+        "s",
+        Arc::new(StringArray::from(vec!["not_an_int"])) as ArrayRef,

Review Comment:
   Forward-looking nit on test robustness: the file has a single column `s` and 
the predicate is also on `s`, so the projection contains **zero** non-filter 
columns. That's exactly the shape a narrow-projection pushdown heuristic would 
decline, and #24426 is prototyping that kind of gating.
   
   I checked — no such gate exists on `main` today (only unsupported nested 
types make `build()` return `None`), so this passes now. But 
`assert!(!plan.contains("FilterExec"))` would start failing if one lands. Cheap 
insurance: write a second column that the predicate doesn't touch, so the 
projection always has a non-filter column.



##########
datafusion/datasource-parquet/src/row_filter.rs:
##########
@@ -161,11 +161,10 @@ impl ArrowPredicate for DatafusionArrowPredicate {
                 timer.stop();
                 Ok(bool_arr)
             })
-            .map_err(|e| {
-                ArrowError::ComputeError(format!(
-                    "Error evaluating filter predicate: {e:?}"
-                ))
-            })
+            // Convert rather than format: converting leaves the original error
+            // in the source chain, so callers can still recover it (for 
example
+            // with `DataFusionError::find_root`)
+            .map_err(|e| e.context("Error evaluating filter predicate").into())

Review Comment:
   Worth making explicit which trade this is, since `From<DataFusionError> for 
ArrowError` has an arm built for exactly this case:
   
   ```rust
   // common/src/error.rs:370
   match e {
       DataFusionError::ArrowError(e, _) => *e,        // preserves the 
original variant
       DataFusionError::External(e) => ArrowError::ExternalError(e),
       other => ArrowError::ExternalError(Box::new(other)),   // <- .context() 
lands here
   }
   ```
   
   `.context()` wraps into `DataFusionError::Context` first, which is precisely 
what routes past that first arm — so a plain `.map_err(|e| e.into())` would 
hand back a real `ArrowError::CastError` at this boundary, while this version 
puts it in the source chain behind `ExternalError`.
   
   You call this out in the description, so the question is just which consumer 
you're targeting: does the embedder classify by matching `ArrowError` variants 
(then the direct conversion serves them better), or does it go through 
`find_root` (then this is fine)? I lean toward keeping the context — the outer 
error only says "Parquet error", so naming the predicate stage is genuinely 
useful — but it's worth pinning down the intent.



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