@@ -5,18 +5,18 @@ use std::ffi::c_void;
55use delta_kernel:: expressions:: {
66 ArrayData , BinaryExpression , BinaryExpressionOp , BinaryPredicate , BinaryPredicateOp ,
77 ColumnName , Expression , ExpressionRef , ExpressionStructPatch , JunctionPredicate ,
8- JunctionPredicateOp , MapData , MapToStructExpression , MapToStructOptions , OpaqueExpression ,
9- OpaqueExpressionOpRef , OpaquePredicate , OpaquePredicateOpRef , ParseJsonExpression , Predicate ,
10- Scalar , StructData , UnaryExpression , UnaryExpressionOp , UnaryPredicate , UnaryPredicateOp ,
11- VariadicExpression , VariadicExpressionOp ,
8+ JunctionPredicateOp , MapData , MapToStructExpression , OpaqueExpression , OpaqueExpressionOpRef ,
9+ OpaquePredicate , OpaquePredicateOpRef , ParseJsonExpression , Predicate , Scalar , StructData ,
10+ UnaryExpression , UnaryExpressionOp , UnaryPredicate , UnaryPredicateOp , VariadicExpression ,
11+ VariadicExpressionOp ,
1212} ;
1313
1414use super :: kernel_visitor:: NullTypeTag ;
1515use 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,15 +702,22 @@ fn visit_expression_impl(
696702 schema_handle
697703 ) ;
698704 }
699- Expression :: MapToStruct ( map_to_struct)
700- if map_to_struct. options != MapToStructOptions :: default ( ) =>
701- {
702- visit_unknown ( visitor, sibling_list_id, "map_to_struct" )
703- }
704- Expression :: MapToStruct ( MapToStructExpression { map_expr, .. } ) => {
705+ Expression :: MapToStruct ( MapToStructExpression { map_expr, options } ) => {
705706 let child_list_id = call ! ( visitor, make_field_list, 1 ) ;
706707 visit_expression_impl ( visitor, map_expr, child_list_id) ;
707- 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+ ) ;
708721 }
709722 // TODO(#2975): Add a dedicated visitor callback for cast expressions.
710723 Expression :: Cast ( cast) => visit_unknown (
@@ -800,6 +813,11 @@ mod tests {
800813 sibling_list_id : usize ,
801814 name : String ,
802815 } ,
816+ MapToStruct {
817+ sibling_list_id : usize ,
818+ child_list_id : usize ,
819+ timestamp_timezone : Option < String > ,
820+ } ,
803821 }
804822
805823 #[ derive( Default ) ]
@@ -815,6 +833,22 @@ mod tests {
815833 list_id
816834 }
817835
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+
818852 extern "C" fn visit_literal_interval_year_month (
819853 data : * mut c_void ,
820854 sibling_list_id : usize ,
@@ -928,7 +962,7 @@ mod tests {
928962 visit_is_null : ignore_child_list,
929963 visit_to_json : ignore_child_list,
930964 visit_parse_json : ignore_parse_json,
931- visit_map_to_struct : ignore_child_list ,
965+ visit_map_to_struct,
932966 visit_lt : ignore_child_list,
933967 visit_gt : ignore_child_list,
934968 visit_eq : ignore_child_list,
@@ -1014,12 +1048,15 @@ mod tests {
10141048 assert_eq ! ( builder. events, vec![ expected] ) ;
10151049 }
10161050
1017- #[ test]
1018- fn timezone_aware_map_to_struct_visits_unknown ( ) {
1019- let expression = Expression :: map_to_struct (
1020- Expression :: column ( [ "partitionValues" ] ) ,
1021- MapToStructOptions :: default ( ) . with_timestamp_timezone ( "America/Los_Angeles" ) ,
1022- ) ;
1051+ #[ rstest]
1052+ #[ case:: default( None ) ]
1053+ #[ case:: configured( Some ( "America/Los_Angeles" ) ) ]
1054+ fn map_to_struct_visits_options ( #[ case] timestamp_timezone : Option < & str > ) {
1055+ let options = timestamp_timezone. map_or_else ( MapToStructOptions :: default, |timezone| {
1056+ MapToStructOptions :: default ( ) . with_timestamp_timezone ( timezone)
1057+ } ) ;
1058+ let expression =
1059+ Expression :: map_to_struct ( Expression :: column ( [ "partitionValues" ] ) , options) ;
10231060 let mut builder = TestExpressionBuilder :: default ( ) ;
10241061 let mut visitor = test_visitor ( & mut builder) ;
10251062
@@ -1028,10 +1065,17 @@ mod tests {
10281065 assert_eq ! ( top_level_id, 0 ) ;
10291066 assert_eq ! (
10301067 builder. events,
1031- vec![ LiteralEvent :: Unknown {
1032- sibling_list_id: 0 ,
1033- name: "map_to_struct" . to_string( ) ,
1034- } ]
1068+ vec![
1069+ LiteralEvent :: Column {
1070+ sibling_list_id: 1 ,
1071+ parts: vec![ "partitionValues" . to_string( ) ] ,
1072+ } ,
1073+ LiteralEvent :: MapToStruct {
1074+ sibling_list_id: 0 ,
1075+ child_list_id: 1 ,
1076+ timestamp_timezone: timestamp_timezone. map( str :: to_string) ,
1077+ }
1078+ ]
10351079 ) ;
10361080 }
10371081}
0 commit comments