@@ -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,11 +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. The FFI does not currently
178- /// represent [`MapToStructOptions`], so expressions with configured options are reported
179- /// through `visit_unknown` without visiting the child expression. The sub-expression is in
180- /// the one-item list identified by `child_list_id`.
181- 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 ,
182187 /// Visits the `LessThan` binary operator belonging to the list identified by
183188 /// `sibling_list_id`. The operands will be in a _two_ item list identified by
184189 /// `child_list_id`
@@ -697,16 +702,22 @@ fn visit_expression_impl(
697702 schema_handle
698703 ) ;
699704 }
700- // TODO: Add a dedicated FFI representation for MapToStructOptions.
701- Expression :: MapToStruct ( map_to_struct)
702- if map_to_struct. options != MapToStructOptions :: default ( ) =>
703- {
704- visit_unknown ( visitor, sibling_list_id, "map_to_struct" )
705- }
706- Expression :: MapToStruct ( MapToStructExpression { map_expr, .. } ) => {
705+ Expression :: MapToStruct ( MapToStructExpression { map_expr, options } ) => {
707706 let child_list_id = call ! ( visitor, make_field_list, 1 ) ;
708707 visit_expression_impl ( visitor, map_expr, child_list_id) ;
709- 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+ ) ;
710721 }
711722 // TODO(#2975): Add a dedicated visitor callback for cast expressions.
712723 Expression :: Cast ( cast) => visit_unknown (
@@ -802,6 +813,11 @@ mod tests {
802813 sibling_list_id : usize ,
803814 name : String ,
804815 } ,
816+ MapToStruct {
817+ sibling_list_id : usize ,
818+ child_list_id : usize ,
819+ timestamp_timezone : Option < String > ,
820+ } ,
805821 }
806822
807823 #[ derive( Default ) ]
@@ -817,6 +833,22 @@ mod tests {
817833 list_id
818834 }
819835
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+
820852 extern "C" fn visit_literal_interval_year_month (
821853 data : * mut c_void ,
822854 sibling_list_id : usize ,
@@ -930,7 +962,7 @@ mod tests {
930962 visit_is_null : ignore_child_list,
931963 visit_to_json : ignore_child_list,
932964 visit_parse_json : ignore_parse_json,
933- visit_map_to_struct : ignore_child_list ,
965+ visit_map_to_struct,
934966 visit_lt : ignore_child_list,
935967 visit_gt : ignore_child_list,
936968 visit_eq : ignore_child_list,
@@ -1016,12 +1048,15 @@ mod tests {
10161048 assert_eq ! ( builder. events, vec![ expected] ) ;
10171049 }
10181050
1019- #[ test]
1020- fn timezone_aware_map_to_struct_visits_unknown ( ) {
1021- let expression = Expression :: map_to_struct (
1022- Expression :: column ( [ "partitionValues" ] ) ,
1023- MapToStructOptions :: default ( ) . with_timestamp_timezone ( "America/Los_Angeles" ) ,
1024- ) ;
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) ;
10251060 let mut builder = TestExpressionBuilder :: default ( ) ;
10261061 let mut visitor = test_visitor ( & mut builder) ;
10271062
@@ -1030,10 +1065,17 @@ mod tests {
10301065 assert_eq ! ( top_level_id, 0 ) ;
10311066 assert_eq ! (
10321067 builder. events,
1033- vec![ LiteralEvent :: Unknown {
1034- sibling_list_id: 0 ,
1035- name: "map_to_struct" . to_string( ) ,
1036- } ]
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+ ]
10371079 ) ;
10381080 }
10391081}
0 commit comments