@@ -289,23 +289,17 @@ pub fn create_physical_expr(
289289 Ok ( expressions:: case ( expr, when_then_expr, else_expr) ?)
290290 }
291291 Expr :: Cast ( Cast { expr, field } ) => {
292- if !field. metadata ( ) . is_empty ( ) {
293- let ( _, src_field) = expr. to_field ( input_dfschema) ?;
294- return plan_err ! (
295- "Cast from {} to {} is not supported" ,
296- format_type_and_metadata(
297- src_field. data_type( ) ,
298- Some ( src_field. metadata( ) ) ,
299- ) ,
300- format_type_and_metadata( field. data_type( ) , Some ( field. metadata( ) ) )
301- ) ;
302- }
292+ let expr = create_physical_expr ( expr, input_dfschema, execution_props) ?;
303293
304- expressions:: cast (
305- create_physical_expr ( expr, input_dfschema, execution_props) ?,
306- input_schema,
307- field. data_type ( ) . clone ( ) ,
308- )
294+ // Reuse the standard CAST validation path, but preserve the logical
295+ // target field instead of lowering to a type-only physical cast.
296+ expressions:: cast ( Arc :: clone ( & expr) , input_schema, field. data_type ( ) . clone ( ) ) ?;
297+
298+ Ok ( Arc :: new ( expressions:: CastExpr :: new_with_target_field (
299+ expr,
300+ Arc :: clone ( field) ,
301+ None ,
302+ ) ) )
309303 }
310304 Expr :: TryCast ( TryCast { expr, field } ) => {
311305 if !field. metadata ( ) . is_empty ( ) {
@@ -476,7 +470,63 @@ mod tests {
476470 }
477471
478472 #[ test]
479- fn test_cast_to_extension_type ( ) -> Result < ( ) > {
473+ fn test_cast_lowering_preserves_target_field_metadata ( ) -> Result < ( ) > {
474+ let schema = Schema :: new ( vec ! [ Field :: new( "a" , DataType :: Int32 , false ) ] ) ;
475+ let df_schema = DFSchema :: try_from ( schema. clone ( ) ) ?;
476+ let target_field = Arc :: new (
477+ Field :: new ( "cast_target" , DataType :: Int64 , true ) . with_metadata (
478+ [ ( "target_meta" . to_string ( ) , "1" . to_string ( ) ) ] . into ( ) ,
479+ ) ,
480+ ) ;
481+ let cast_expr = Expr :: Cast ( Cast :: new_from_field (
482+ Box :: new ( col ( "a" ) ) ,
483+ Arc :: clone ( & target_field) ,
484+ ) ) ;
485+
486+ let physical =
487+ create_physical_expr ( & cast_expr, & df_schema, & ExecutionProps :: new ( ) ) ?;
488+ let cast = physical
489+ . as_any ( )
490+ . downcast_ref :: < expressions:: CastExpr > ( )
491+ . expect ( "planner should lower logical CAST to CastExpr" ) ;
492+
493+ assert_eq ! ( cast. target_field( ) , & target_field) ;
494+ assert_eq ! ( physical. return_field( & schema) ?, target_field) ;
495+ assert ! ( physical. nullable( & schema) ?) ;
496+
497+ Ok ( ( ) )
498+ }
499+
500+ #[ test]
501+ fn test_cast_lowering_preserves_same_type_field_semantics ( ) -> Result < ( ) > {
502+ let schema = Schema :: new ( vec ! [ Field :: new( "a" , DataType :: Int32 , false ) ] ) ;
503+ let df_schema = DFSchema :: try_from ( schema. clone ( ) ) ?;
504+ let target_field = Arc :: new (
505+ Field :: new ( "same_type_cast" , DataType :: Int32 , true ) . with_metadata (
506+ [ ( "target_meta" . to_string ( ) , "same-type" . to_string ( ) ) ] . into ( ) ,
507+ ) ,
508+ ) ;
509+ let cast_expr = Expr :: Cast ( Cast :: new_from_field (
510+ Box :: new ( col ( "a" ) ) ,
511+ Arc :: clone ( & target_field) ,
512+ ) ) ;
513+
514+ let physical =
515+ create_physical_expr ( & cast_expr, & df_schema, & ExecutionProps :: new ( ) ) ?;
516+ let cast = physical
517+ . as_any ( )
518+ . downcast_ref :: < expressions:: CastExpr > ( )
519+ . expect ( "same-type casts should not be elided when the target field carries semantics" ) ;
520+
521+ assert_eq ! ( cast. target_field( ) , & target_field) ;
522+ assert_eq ! ( physical. return_field( & schema) ?, target_field) ;
523+ assert ! ( physical. nullable( & schema) ?) ;
524+
525+ Ok ( ( ) )
526+ }
527+
528+ #[ test]
529+ fn test_try_cast_to_extension_type_is_rejected ( ) -> Result < ( ) > {
480530 let extension_field_type = Arc :: new (
481531 DataType :: FixedSizeBinary ( 16 )
482532 . into_nullable_field ( )
@@ -486,17 +536,8 @@ mod tests {
486536 ) ,
487537 ) ;
488538 let expr = lit ( "3230e5d4-888e-408b-b09b-831f44aa0c58" ) ;
489- let cast_expr = Expr :: Cast ( Cast :: new_from_field (
490- Box :: new ( expr. clone ( ) ) ,
491- Arc :: clone ( & extension_field_type) ,
492- ) ) ;
493- let err =
494- create_physical_expr ( & cast_expr, & DFSchema :: empty ( ) , & ExecutionProps :: new ( ) )
495- . unwrap_err ( ) ;
496- assert ! ( err. message( ) . contains( "arrow.uuid" ) ) ;
497-
498539 let try_cast_expr = Expr :: TryCast ( TryCast :: new_from_field (
499- Box :: new ( expr. clone ( ) ) ,
540+ Box :: new ( expr) ,
500541 Arc :: clone ( & extension_field_type) ,
501542 ) ) ;
502543 let err = create_physical_expr (
0 commit comments