jayzhan211 commented on PR #25013:
URL: https://github.com/apache/datafusion/pull/25013#issuecomment-5642410403

   @peterxcli , a suggestion:
   
   **`try_narrow_struct_cast` can now fail the query instead of skipping the 
rewrite**
   
   `schema_rewriter.rs:565` rebuilds the accessor against the physical file 
schema:
   
   ```rust
   let extracted = Arc::new(ScalarFunctionExpr::try_new(
       Arc::new(get_field_expr.fun().clone()),
       args,
       &self.physical_file_schema,
       Arc::new(get_field_expr.config_options().clone()),
   )?) as Arc<dyn PhysicalExpr>;
   ```
   
   `try_new` runs `fields_with_udf` and `return_field_from_args` on the 
**physical** types — which differ from the logical ones by construction, since 
that difference is why the adapter inserted the cast you're about to drop. With 
the old `try_downcast_func::<GetFieldFunc>` gate the `?` was safe 
(`Signature::user_defined`, permissive). It isn't safe for arbitrary UDFs: a 
function that declares `struct_field_access` but doesn't accept the physical 
types now aborts the whole query, where every other path in this rewriter 
degrades to `Ok(None)`.
   
   Reproduced on this branch using your own `FieldAt` helper with an `Exact` 
signature pinned to the logical struct type. File `s: Struct<value: Int32>`, 
registered schema `s: Struct<value: Int64>`, query `SELECT id FROM t WHERE 
field_at('value', s) > 5`:
   
   ```
   declare_access=false: Ok([...])            // 1 row, as expected
   declare_access=true:  Err(Plan("Failed to coerce arguments to satisfy a call 
to
     'field_at' function: coercion from Utf8, Struct(\"value\": Int32) to the
     signature Exact(Utf8, Struct(\"value\": Int64)) failed"))
   ```
   
   Declaring the capability should never make a working query fail. Make the 
rebuild best-effort:
   
   ```diff
   -        let extracted = Arc::new(ScalarFunctionExpr::try_new(
   -            Arc::new(get_field_expr.fun().clone()),
   -            args,
   -            &self.physical_file_schema,
   -            Arc::new(get_field_expr.config_options().clone()),
   -        )?) as Arc<dyn PhysicalExpr>;
   +        // A third-party accessor may not accept the physical field types. 
That
   +        // is a missed rewrite, not a query error: keep the cast and bail 
out.
   +        let Ok(extracted) = ScalarFunctionExpr::try_new(
   +            Arc::new(get_field_expr.fun().clone()),
   +            args,
   +            &self.physical_file_schema,
   +            Arc::new(get_field_expr.config_options().clone()),
   +        ) else {
   +            return Ok(None);
   +        };
   +        let extracted = Arc::new(extracted) as Arc<dyn PhysicalExpr>;
   ```
   
   Two follow-ups worth folding in:
   
   1. `FieldAt::signature` is currently `Signature::any(2, 
Volatility::Immutable)` at all three construction sites, i.e. the one shape 
that can't hit this. Point one of them at an `Exact` signature over the logical 
struct type and assert the evolved query returns the right rows (unpruned) 
rather than erroring.
   2. The `struct_field_access` doc promises implementors only "reordered or 
narrowed source structs". This rewrite does more — it drops the type-adapting 
cast and re-applies the conversion to the output, so the UDF sees the file's 
physical field types. Please say so in the contract, since that is the 
assumption this code actually relies on.


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