@@ -174,9 +174,9 @@ pub struct EngineExpressionVisitor {
174174 /// `child_list_id`. The `output_schema` handle specifies the schema to parse the JSON
175175 /// into.
176176 pub visit_parse_json : VisitParseJsonFn ,
177- /// Visits the `MapToStruct` expression belonging to the list identified by `sibling_list_id`.
178- /// The sub-expression (map column) will be in a _one_ item list identified by `child_list_id`.
179- /// The output struct schema is determined by the evaluator's result type .
177+ /// Visits a `MapToStruct` expression with default options. Expressions with configured options
178+ /// are reported through `visit_unknown` without visiting the child expression. The
179+ /// sub-expression is in the one-item list identified by `child_list_id` .
180180 pub visit_map_to_struct : VisitUnaryFn ,
181181 /// Visits the `LessThan` binary operator belonging to the list identified by
182182 /// `sibling_list_id`. The operands will be in a _two_ item list identified by
@@ -696,7 +696,10 @@ fn visit_expression_impl(
696696 schema_handle
697697 ) ;
698698 }
699- Expression :: MapToStruct ( MapToStructExpression { map_expr } ) => {
699+ Expression :: MapToStruct ( map_to_struct) if !can_visit_map_to_struct ( map_to_struct) => {
700+ visit_unknown ( visitor, sibling_list_id, "configured_map_to_struct" )
701+ }
702+ Expression :: MapToStruct ( MapToStructExpression { map_expr, .. } ) => {
700703 let child_list_id = call ! ( visitor, make_field_list, 1 ) ;
701704 visit_expression_impl ( visitor, map_expr, child_list_id) ;
702705 call ! ( visitor, visit_map_to_struct, sibling_list_id, child_list_id) ;
@@ -711,6 +714,10 @@ fn visit_expression_impl(
711714 }
712715}
713716
717+ fn can_visit_map_to_struct ( map_to_struct : & MapToStructExpression ) -> bool {
718+ map_to_struct. options . is_default ( )
719+ }
720+
714721fn visit_predicate_impl (
715722 visitor : & mut EngineExpressionVisitor ,
716723 predicate : & Predicate ,
@@ -771,7 +778,7 @@ fn visit_predicate_internal(predicate: &Predicate, visitor: &mut EngineExpressio
771778
772779#[ cfg( test) ]
773780mod tests {
774- use delta_kernel:: expressions:: { lit, Expression , Scalar } ;
781+ use delta_kernel:: expressions:: { lit, Expression , MapToStructOptions , Scalar } ;
775782 use rstest:: rstest;
776783
777784 use super :: * ;
@@ -791,6 +798,14 @@ mod tests {
791798 sibling_list_id : usize ,
792799 parts : Vec < String > ,
793800 } ,
801+ Unknown {
802+ sibling_list_id : usize ,
803+ name : String ,
804+ } ,
805+ MapToStruct {
806+ sibling_list_id : usize ,
807+ child_list_id : usize ,
808+ } ,
794809 }
795810
796811 #[ derive( Default ) ]
@@ -847,6 +862,30 @@ mod tests {
847862 } ) ;
848863 }
849864
865+ extern "C" fn visit_unknown_name (
866+ data : * mut c_void ,
867+ sibling_list_id : usize ,
868+ name : KernelStringSlice ,
869+ ) {
870+ let builder = unsafe { & mut * ( data as * mut TestExpressionBuilder ) } ;
871+ let name = unsafe { String :: try_from_slice ( & name) } . unwrap ( ) ;
872+ builder. events . push ( LiteralEvent :: Unknown {
873+ sibling_list_id,
874+ name,
875+ } ) ;
876+ }
877+
878+ extern "C" fn visit_map_to_struct (
879+ data : * mut c_void ,
880+ sibling_list_id : usize ,
881+ child_list_id : usize ,
882+ ) {
883+ let builder = unsafe { & mut * ( data as * mut TestExpressionBuilder ) } ;
884+ builder. events . push ( LiteralEvent :: MapToStruct {
885+ sibling_list_id,
886+ child_list_id,
887+ } ) ;
888+ }
850889 macro_rules! ignore_fn {
851890 ( $fn_name: ident $( , $arg_type: ty) * ) => {
852891 extern "C" fn $fn_name(
@@ -907,7 +946,7 @@ mod tests {
907946 visit_is_null : ignore_child_list,
908947 visit_to_json : ignore_child_list,
909948 visit_parse_json : ignore_parse_json,
910- visit_map_to_struct : ignore_child_list ,
949+ visit_map_to_struct,
911950 visit_lt : ignore_child_list,
912951 visit_gt : ignore_child_list,
913952 visit_eq : ignore_child_list,
@@ -925,7 +964,7 @@ mod tests {
925964 visit_field_patch : ignore_field_patch,
926965 visit_opaque_expr : ignore_opaque_expr,
927966 visit_opaque_pred : ignore_opaque_pred,
928- visit_unknown : ignore_string_slice ,
967+ visit_unknown : visit_unknown_name ,
929968 }
930969 }
931970
@@ -992,4 +1031,52 @@ mod tests {
9921031 assert_eq ! ( top_level_id, 0 ) ;
9931032 assert_eq ! ( builder. events, vec![ expected] ) ;
9941033 }
1034+
1035+ #[ test]
1036+ fn timezone_aware_map_to_struct_visits_unknown ( ) {
1037+ let expression = Expression :: map_to_struct (
1038+ Expression :: column ( [ "partitionValues" ] ) ,
1039+ MapToStructOptions :: default ( ) . with_timestamp_timezone ( "America/Los_Angeles" ) ,
1040+ ) ;
1041+ let mut builder = TestExpressionBuilder :: default ( ) ;
1042+ let mut visitor = test_visitor ( & mut builder) ;
1043+
1044+ let top_level_id = visit_expression_internal ( & expression, & mut visitor) ;
1045+
1046+ assert_eq ! ( top_level_id, 0 ) ;
1047+ assert_eq ! (
1048+ builder. events,
1049+ vec![ LiteralEvent :: Unknown {
1050+ sibling_list_id: 0 ,
1051+ name: "configured_map_to_struct" . to_string( ) ,
1052+ } ]
1053+ ) ;
1054+ }
1055+
1056+ #[ test]
1057+ fn default_map_to_struct_visits_child_then_map_to_struct ( ) {
1058+ let expression = Expression :: map_to_struct (
1059+ Expression :: column ( [ "partitionValues" ] ) ,
1060+ MapToStructOptions :: default ( ) ,
1061+ ) ;
1062+ let mut builder = TestExpressionBuilder :: default ( ) ;
1063+ let mut visitor = test_visitor ( & mut builder) ;
1064+
1065+ let top_level_id = visit_expression_internal ( & expression, & mut visitor) ;
1066+
1067+ assert_eq ! ( top_level_id, 0 ) ;
1068+ assert_eq ! (
1069+ builder. events,
1070+ vec![
1071+ LiteralEvent :: Column {
1072+ sibling_list_id: 1 ,
1073+ parts: vec![ "partitionValues" . to_string( ) ] ,
1074+ } ,
1075+ LiteralEvent :: MapToStruct {
1076+ sibling_list_id: 0 ,
1077+ child_list_id: 1 ,
1078+ } ,
1079+ ]
1080+ ) ;
1081+ }
9951082}
0 commit comments