@@ -16,7 +16,7 @@ use crate::expressions::{
1616 SharedExpression , SharedOpaqueExpressionOp , SharedOpaquePredicateOp , SharedPredicate ,
1717} ;
1818use crate :: handle:: Handle ;
19- use crate :: { kernel_string_slice, KernelStringSlice , SharedSchema } ;
19+ use crate :: { kernel_string_slice, KernelStringSlice , OptionalValue , SharedSchema } ;
2020
2121type VisitLiteralFn < T > = extern "C" fn ( data : * mut c_void , sibling_list_id : usize , value : T ) ;
2222type VisitUnaryFn = extern "C" fn ( data : * mut c_void , sibling_list_id : usize , child_list_id : usize ) ;
@@ -31,6 +31,12 @@ type VisitParseJsonFn = extern "C" fn(
3131 child_list_id : usize ,
3232 output_schema : Handle < SharedSchema > ,
3333) ;
34+ type VisitMapToStructFn = extern "C" fn (
35+ data : * mut c_void ,
36+ sibling_list_id : usize ,
37+ child_list_id : usize ,
38+ timestamp_timezone : OptionalValue < KernelStringSlice > ,
39+ ) ;
3440type VisitColumnFn = extern "C" fn (
3541 data : * mut c_void ,
3642 sibling_list_id : usize ,
@@ -174,10 +180,10 @@ pub struct EngineExpressionVisitor {
174180 /// `child_list_id`. The `output_schema` handle specifies the schema to parse the JSON
175181 /// into.
176182 pub visit_parse_json : VisitParseJsonFn ,
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` .
180- pub visit_map_to_struct : VisitUnaryFn ,
183+ /// Visits a `MapToStruct` expression. The sub-expression is in the one-item list identified by
184+ /// `child_list_id`. `timestamp_timezone` carries the configured reader timezone, or is `None`
185+ /// when the expression uses the default UTC interpretation .
186+ pub visit_map_to_struct : VisitMapToStructFn ,
181187 /// Visits the `LessThan` binary operator belonging to the list identified by
182188 /// `sibling_list_id`. The operands will be in a _two_ item list identified by
183189 /// `child_list_id`
@@ -696,13 +702,22 @@ fn visit_expression_impl(
696702 schema_handle
697703 ) ;
698704 }
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, .. } ) => {
705+ Expression :: MapToStruct ( MapToStructExpression { map_expr, options } ) => {
703706 let child_list_id = call ! ( visitor, make_field_list, 1 ) ;
704707 visit_expression_impl ( visitor, map_expr, child_list_id) ;
705- call ! ( visitor, visit_map_to_struct, sibling_list_id, child_list_id) ;
708+ let timestamp_timezone = match options. timestamp_timezone ( ) {
709+ Some ( timestamp_timezone) => {
710+ OptionalValue :: Some ( kernel_string_slice ! ( timestamp_timezone) )
711+ }
712+ None => OptionalValue :: None ,
713+ } ;
714+ call ! (
715+ visitor,
716+ visit_map_to_struct,
717+ sibling_list_id,
718+ child_list_id,
719+ timestamp_timezone
720+ ) ;
706721 }
707722 // TODO(#2975): Add a dedicated visitor callback for cast expressions.
708723 Expression :: Cast ( cast) => visit_unknown (
@@ -714,10 +729,6 @@ fn visit_expression_impl(
714729 }
715730}
716731
717- fn can_visit_map_to_struct ( map_to_struct : & MapToStructExpression ) -> bool {
718- map_to_struct. options . timestamp_timezone ( ) . is_none ( )
719- }
720-
721732fn visit_predicate_impl (
722733 visitor : & mut EngineExpressionVisitor ,
723734 predicate : & Predicate ,
@@ -805,6 +816,7 @@ mod tests {
805816 MapToStruct {
806817 sibling_list_id : usize ,
807818 child_list_id : usize ,
819+ timestamp_timezone : Option < String > ,
808820 } ,
809821 }
810822
@@ -821,6 +833,22 @@ mod tests {
821833 list_id
822834 }
823835
836+ extern "C" fn visit_map_to_struct (
837+ data : * mut c_void ,
838+ sibling_list_id : usize ,
839+ child_list_id : usize ,
840+ timestamp_timezone : OptionalValue < KernelStringSlice > ,
841+ ) {
842+ let builder = unsafe { & mut * ( data as * mut TestExpressionBuilder ) } ;
843+ let timestamp_timezone = Option :: from ( timestamp_timezone)
844+ . map ( |timezone| unsafe { String :: try_from_slice ( & timezone) . unwrap ( ) } ) ;
845+ builder. events . push ( LiteralEvent :: MapToStruct {
846+ sibling_list_id,
847+ child_list_id,
848+ timestamp_timezone,
849+ } ) ;
850+ }
851+
824852 extern "C" fn visit_literal_interval_year_month (
825853 data : * mut c_void ,
826854 sibling_list_id : usize ,
@@ -875,17 +903,6 @@ mod tests {
875903 } ) ;
876904 }
877905
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- }
889906 macro_rules! ignore_fn {
890907 ( $fn_name: ident $( , $arg_type: ty) * ) => {
891908 extern "C" fn $fn_name(
@@ -1032,33 +1049,15 @@ mod tests {
10321049 assert_eq ! ( builder. events, vec![ expected] ) ;
10331050 }
10341051
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- ) ;
1052+ #[ rstest]
1053+ #[ case:: default( None ) ]
1054+ #[ case:: configured( Some ( "America/Los_Angeles" ) ) ]
1055+ fn map_to_struct_visits_options ( #[ case] timestamp_timezone : Option < & str > ) {
1056+ let options = timestamp_timezone. map_or_else ( MapToStructOptions :: default, |timezone| {
1057+ MapToStructOptions :: default ( ) . with_timestamp_timezone ( timezone)
1058+ } ) ;
1059+ let expression =
1060+ Expression :: map_to_struct ( Expression :: column ( [ "partitionValues" ] ) , options) ;
10621061 let mut builder = TestExpressionBuilder :: default ( ) ;
10631062 let mut visitor = test_visitor ( & mut builder) ;
10641063
@@ -1075,7 +1074,8 @@ mod tests {
10751074 LiteralEvent :: MapToStruct {
10761075 sibling_list_id: 0 ,
10771076 child_list_id: 1 ,
1078- } ,
1077+ timestamp_timezone: timestamp_timezone. map( str :: to_string) ,
1078+ }
10791079 ]
10801080 ) ;
10811081 }
0 commit comments