@@ -74,7 +74,7 @@ fn build_get_options<'a>(path: VariantPath<'a>, as_type: &Option<FieldRef>) -> G
7474
7575/// Determines how a string path is converted to a [`VariantPath`].
7676#[ derive( Debug , Clone , Copy , Hash , PartialEq , Eq ) ]
77- enum PathMode {
77+ pub enum PathMode {
7878 /// Splits the path on `.` for dot-notation traversal (e.g., `"a.b.c"` → `["a", "b", "c"]`).
7979 DotNotation ,
8080 /// Treats the entire path string as a single field name (e.g., `"a.b.c"` → `["a.b.c"]`).
@@ -84,7 +84,7 @@ enum PathMode {
8484}
8585
8686impl PathMode {
87- fn try_build_path < ' a > ( & self , path : & ' a str ) -> Result < VariantPath < ' a > > {
87+ pub fn try_build_path < ' a > ( & self , path : & ' a str ) -> Result < VariantPath < ' a > > {
8888 match self {
8989 PathMode :: DotNotation => VariantPath :: try_from ( path) . map_err ( Into :: into) ,
9090 PathMode :: SingleField => Ok ( VariantPath :: new ( vec ! [ VariantPathElement :: field( path) ] ) ) ,
@@ -1556,4 +1556,134 @@ mod tests {
15561556 assert ! ( bool_arr. is_null( 2 ) ) ;
15571557 assert ! ( bool_arr. is_null( 3 ) ) ;
15581558 }
1559+
1560+ #[ test]
1561+ fn test_get_str_with_single_field_mode_dotted_key ( ) {
1562+ // VariantGetStrUdf with SingleField mode should treat dotted keys as a single field
1563+ let variant_input = variant_scalar_from_json ( serde_json:: json!( {
1564+ "http.response.status_code" : 200 ,
1565+ "service.name" : "my-service"
1566+ } ) ) ;
1567+
1568+ let udf = VariantGetStrUdf :: with_path_mode ( PathMode :: SingleField ) ;
1569+ let args = build_variant_get_args (
1570+ ColumnarValue :: Scalar ( variant_input) ,
1571+ ColumnarValue :: Scalar ( ScalarValue :: Utf8 ( Some (
1572+ "http.response.status_code" . to_string ( ) ,
1573+ ) ) ) ,
1574+ DataType :: Utf8View ,
1575+ standard_variant_get_arg_fields ( ) ,
1576+ ) ;
1577+
1578+ let result = udf. invoke_with_args ( args) . unwrap ( ) ;
1579+
1580+ let ColumnarValue :: Scalar ( ScalarValue :: Utf8View ( Some ( s) ) ) = result else {
1581+ panic ! ( "expected Utf8View scalar, got {result:?}" ) ;
1582+ } ;
1583+ // Integer 200 should be JSON-serialized to "200"
1584+ assert_eq ! ( s, "200" ) ;
1585+ }
1586+
1587+ #[ test]
1588+ fn test_get_str_with_single_field_mode_string_value ( ) {
1589+ let variant_input = variant_scalar_from_json ( serde_json:: json!( {
1590+ "service.name" : "my-service"
1591+ } ) ) ;
1592+
1593+ let udf = VariantGetStrUdf :: with_path_mode ( PathMode :: SingleField ) ;
1594+ let args = build_variant_get_args (
1595+ ColumnarValue :: Scalar ( variant_input) ,
1596+ ColumnarValue :: Scalar ( ScalarValue :: Utf8 ( Some ( "service.name" . to_string ( ) ) ) ) ,
1597+ DataType :: Utf8View ,
1598+ standard_variant_get_arg_fields ( ) ,
1599+ ) ;
1600+
1601+ let result = udf. invoke_with_args ( args) . unwrap ( ) ;
1602+
1603+ let ColumnarValue :: Scalar ( ScalarValue :: Utf8View ( Some ( s) ) ) = result else {
1604+ panic ! ( "expected Utf8View scalar, got {result:?}" ) ;
1605+ } ;
1606+ // String values returned as-is (no JSON quotes)
1607+ assert_eq ! ( s, "my-service" ) ;
1608+ }
1609+
1610+ #[ test]
1611+ fn test_get_str_with_single_field_mode_array ( ) {
1612+ // Test array input with SingleField mode
1613+ let json_rows = vec ! [
1614+ serde_json:: json!( { "http.status" : 200 , "http.method" : "GET" } ) ,
1615+ serde_json:: json!( { "http.status" : 404 , "http.method" : "POST" } ) ,
1616+ serde_json:: json!( { "http.status" : 500 } ) ,
1617+ ] ;
1618+
1619+ let variant_array = variant_array_from_json_rows ( & json_rows) ;
1620+
1621+ let udf = VariantGetStrUdf :: with_path_mode ( PathMode :: SingleField ) ;
1622+ let args = build_variant_get_args (
1623+ ColumnarValue :: Array ( variant_array) ,
1624+ ColumnarValue :: Scalar ( ScalarValue :: Utf8 ( Some ( "http.status" . to_string ( ) ) ) ) ,
1625+ DataType :: Utf8View ,
1626+ standard_variant_get_arg_fields ( ) ,
1627+ ) ;
1628+
1629+ let result = udf. invoke_with_args ( args) . unwrap ( ) ;
1630+
1631+ let ColumnarValue :: Array ( arr) = result else {
1632+ panic ! ( "expected array output" ) ;
1633+ } ;
1634+ let str_arr = arr. as_any ( ) . downcast_ref :: < StringViewArray > ( ) . unwrap ( ) ;
1635+ assert_eq ! ( str_arr. len( ) , 3 ) ;
1636+ assert_eq ! ( str_arr. value( 0 ) , "200" ) ;
1637+ assert_eq ! ( str_arr. value( 1 ) , "404" ) ;
1638+ assert_eq ! ( str_arr. value( 2 ) , "500" ) ;
1639+ }
1640+
1641+ #[ test]
1642+ fn test_get_int_with_single_field_mode ( ) {
1643+ let variant_input = variant_scalar_from_json ( serde_json:: json!( {
1644+ "http.status" : 200
1645+ } ) ) ;
1646+
1647+ let udf = VariantGetIntUdf :: with_path_mode ( PathMode :: SingleField ) ;
1648+ let args = build_variant_get_args (
1649+ ColumnarValue :: Scalar ( variant_input) ,
1650+ ColumnarValue :: Scalar ( ScalarValue :: Utf8 ( Some ( "http.status" . to_string ( ) ) ) ) ,
1651+ DataType :: Int64 ,
1652+ standard_variant_get_arg_fields ( ) ,
1653+ ) ;
1654+
1655+ let result = udf. invoke_with_args ( args) . unwrap ( ) ;
1656+
1657+ let ColumnarValue :: Scalar ( ScalarValue :: Int64 ( Some ( v) ) ) = result else {
1658+ panic ! ( "expected Int64 scalar, got {result:?}" ) ;
1659+ } ;
1660+ assert_eq ! ( v, 200 ) ;
1661+ }
1662+
1663+ #[ test]
1664+ fn test_get_str_dot_notation_splits_dotted_key ( ) {
1665+ // With default DotNotation mode, dotted keys are split — should return NULL
1666+ // for a key like "http.response.status_code" that's stored as a single field
1667+ let variant_input = variant_scalar_from_json ( serde_json:: json!( {
1668+ "http.response.status_code" : 200
1669+ } ) ) ;
1670+
1671+ let udf = VariantGetStrUdf :: default ( ) ; // DotNotation
1672+ let args = build_variant_get_args (
1673+ ColumnarValue :: Scalar ( variant_input) ,
1674+ ColumnarValue :: Scalar ( ScalarValue :: Utf8 ( Some (
1675+ "http.response.status_code" . to_string ( ) ,
1676+ ) ) ) ,
1677+ DataType :: Utf8View ,
1678+ standard_variant_get_arg_fields ( ) ,
1679+ ) ;
1680+
1681+ let result = udf. invoke_with_args ( args) . unwrap ( ) ;
1682+
1683+ // DotNotation splits on dots, tries to traverse http -> response -> status_code
1684+ // which doesn't exist, so returns NULL
1685+ let ColumnarValue :: Scalar ( ScalarValue :: Utf8View ( None ) ) = result else {
1686+ panic ! ( "expected NULL (dot notation splits the key), got {result:?}" ) ;
1687+ } ;
1688+ }
15591689}
0 commit comments