@@ -975,6 +975,20 @@ impl SessionState {
975975 }
976976}
977977
978+ /// Deduplicates function-registry map entries by keeping only entries whose key
979+ /// matches the canonical name. The session stores one hash map entry per alias
980+ /// plus the canonical name; filtering to canonical-name entries yields exactly
981+ /// one [`Arc`] per logical function.
982+ fn dedup_function_registry_by_canonical_name < T > (
983+ map : & HashMap < String , Arc < T > > ,
984+ canonical_name : impl Fn ( & T ) -> & str ,
985+ ) -> Vec < Arc < T > > {
986+ map. iter ( )
987+ . filter ( |( key, udf) | key. as_str ( ) == canonical_name ( udf. as_ref ( ) ) )
988+ . map ( |( _, udf) | Arc :: clone ( udf) )
989+ . collect ( )
990+ }
991+
978992/// A builder to be used for building [`SessionState`]'s. Defaults will
979993/// be used for all values unless explicitly provided.
980994///
@@ -1088,11 +1102,18 @@ impl SessionStateBuilder {
10881102 query_planner : Some ( existing. query_planner ) ,
10891103 catalog_list : Some ( existing. catalog_list ) ,
10901104 table_functions : Some ( existing. table_functions ) ,
1091- scalar_functions : Some ( existing. scalar_functions . into_values ( ) . collect_vec ( ) ) ,
1092- aggregate_functions : Some (
1093- existing. aggregate_functions . into_values ( ) . collect_vec ( ) ,
1094- ) ,
1095- window_functions : Some ( existing. window_functions . into_values ( ) . collect_vec ( ) ) ,
1105+ scalar_functions : Some ( dedup_function_registry_by_canonical_name (
1106+ & existing. scalar_functions ,
1107+ |u| u. name ( ) ,
1108+ ) ) ,
1109+ aggregate_functions : Some ( dedup_function_registry_by_canonical_name (
1110+ & existing. aggregate_functions ,
1111+ |u| u. name ( ) ,
1112+ ) ) ,
1113+ window_functions : Some ( dedup_function_registry_by_canonical_name (
1114+ & existing. window_functions ,
1115+ |u| u. name ( ) ,
1116+ ) ) ,
10961117 extension_types : Some ( existing. extension_types ) ,
10971118 serializer_registry : Some ( existing. serializer_registry ) ,
10981119 file_formats : Some ( existing. file_formats . into_values ( ) . collect_vec ( ) ) ,
@@ -2340,6 +2361,37 @@ mod tests {
23402361 Ok ( ( ) )
23412362 }
23422363
2364+ #[ test]
2365+ fn new_from_existing_preserves_scalar_udf_aliases ( ) -> Result < ( ) > {
2366+ use arrow:: datatypes:: DataType ;
2367+ use datafusion_common:: ScalarValue ;
2368+ use datafusion_expr:: registry:: FunctionRegistry ;
2369+ use datafusion_expr:: { ColumnarValue , Volatility , create_udf} ;
2370+
2371+ let udf = create_udf (
2372+ "postgres_to_char" ,
2373+ vec ! [ DataType :: Utf8 ] ,
2374+ DataType :: Utf8 ,
2375+ Volatility :: Immutable ,
2376+ Arc :: new ( |_args| Ok ( ColumnarValue :: Scalar ( ScalarValue :: Utf8 ( None ) ) ) ) ,
2377+ )
2378+ . with_aliases ( [ "to_char" ] ) ;
2379+
2380+ let mut state = SessionStateBuilder :: new ( ) . build ( ) ;
2381+ state. register_udf ( Arc :: new ( udf) ) ?;
2382+
2383+ assert_eq ! ( state. udf( "postgres_to_char" ) ?. name( ) , "postgres_to_char" ) ;
2384+ assert_eq ! ( state. udf( "to_char" ) ?. name( ) , "postgres_to_char" ) ;
2385+
2386+ let roundtrip = SessionStateBuilder :: new_from_existing ( state) . build ( ) ;
2387+ assert_eq ! ( roundtrip. udf( "to_char" ) ?. name( ) , "postgres_to_char" ) ;
2388+ assert_eq ! (
2389+ roundtrip. udf( "postgres_to_char" ) ?. name( ) ,
2390+ "postgres_to_char"
2391+ ) ;
2392+ Ok ( ( ) )
2393+ }
2394+
23432395 #[ test]
23442396 fn test_session_state_with_optimizer_rules ( ) {
23452397 #[ derive( Default , Debug ) ]
0 commit comments