@@ -32,17 +32,19 @@ use async_trait::async_trait;
3232use datafusion_common:: DataFusionError ;
3333use datafusion_common:: config:: { ConfigEntry , ConfigOptions } ;
3434use datafusion_common:: error:: Result ;
35- use datafusion_common:: types:: NativeType ;
35+ use datafusion_common:: types:: { LogicalType , NativeType } ;
3636use datafusion_execution:: TaskContext ;
3737use datafusion_execution:: runtime_env:: RuntimeEnv ;
3838use datafusion_expr:: function:: WindowUDFFieldArgs ;
39+ use datafusion_expr:: type_coercion:: functions:: fields_with_udf;
3940use datafusion_expr:: {
4041 AggregateUDF , ReturnFieldArgs , ScalarUDF , Signature , TypeSignature , WindowUDF ,
4142} ;
4243use datafusion_expr:: { TableType , Volatility } ;
4344use datafusion_physical_plan:: SendableRecordBatchStream ;
4445use datafusion_physical_plan:: stream:: RecordBatchStreamAdapter ;
4546use datafusion_physical_plan:: streaming:: PartitionStream ;
47+ use itertools:: Itertools ;
4648use std:: collections:: { BTreeSet , HashMap , HashSet } ;
4749use std:: fmt:: Debug ;
4850use std:: sync:: Arc ;
@@ -454,118 +456,99 @@ impl InformationSchemaConfig {
454456 }
455457}
456458
457- /// get the arguments and return types of a UDF
458- /// returns a tuple of (arg_types, return_type)
459+ /// Origins used to enumerate the physical types a native type can take
460+ const RESOLVE_CAST_SOURCES : [ DataType ; 2 ] = [ DataType :: Null , DataType :: LargeUtf8 ] ;
461+
462+ /// Build argument fields for `information_schema` to provide possible return types
463+ fn resolve_informational_fields ( idx : usize , t : & NativeType ) -> Vec < FieldRef > {
464+ // Since native types map to several physical types, resolve it against
465+ // ambiguous types to get canonical `DataType`s for the native type.
466+ // Skip origins the type has no cast from (e.g. `Struct` from `LargeUtf8`)
467+ RESOLVE_CAST_SOURCES
468+ . iter ( )
469+ . filter_map ( |source| t. default_cast_for ( source) . ok ( ) )
470+ . unique ( )
471+ . map ( |dt| Arc :: new ( Field :: new ( format ! ( "arg_{idx}" ) , dt, true ) ) )
472+ . collect ( )
473+ }
474+
475+ /// Function information schema is a set of tuples - argument types and an optional return type
476+ type FunctionInformationSchema = BTreeSet < ( Vec < String > , Option < String > ) > ;
477+
478+ /// Get the arguments and return types of a function from its signature
479+ fn get_args_and_return_types (
480+ signature : & Signature ,
481+ return_field : impl Fn ( & [ FieldRef ] ) -> Result < FieldRef > ,
482+ ) -> Result < FunctionInformationSchema > {
483+ let arg_types = signature. type_signature . get_representative_types ( ) ;
484+ if arg_types. is_empty ( ) {
485+ // Edge case if function doesn't have arguments
486+ return Ok ( BTreeSet :: from ( [ ( vec ! [ ] , None ) ] ) ) ;
487+ }
488+ arg_types
489+ . into_iter ( )
490+ . map ( |arg_types| {
491+ // Get possible types for each input arg
492+ let arg_fields = arg_types
493+ . iter ( )
494+ . enumerate ( )
495+ . map ( |( i, t) | resolve_informational_fields ( i, t) )
496+ . collect :: < Vec < _ > > ( ) ;
497+ // Build combinations of arg types with the return type
498+ let return_types = arg_fields
499+ . into_iter ( )
500+ . multi_cartesian_product ( )
501+ . filter_map ( |arg_fields| return_field ( & arg_fields) . ok ( ) )
502+ . map ( |f| Some ( remove_native_type_prefix ( & f. data_type ( ) . into ( ) ) ) )
503+ . collect :: < BTreeSet < _ > > ( ) ;
504+ let return_types = if return_types. is_empty ( ) {
505+ // Indicate `None` if the return type cannot be represented from a signature,
506+ BTreeSet :: from ( [ None ] )
507+ } else {
508+ return_types
509+ } ;
510+ let arg_types = arg_types
511+ . iter ( )
512+ . map ( remove_native_type_prefix)
513+ . collect :: < Vec < _ > > ( ) ;
514+ let tuples = return_types
515+ . into_iter ( )
516+ . map ( move |return_type| ( arg_types. clone ( ) , return_type) ) ;
517+ Ok ( tuples)
518+ } )
519+ . flatten_ok ( )
520+ . collect ( )
521+ }
522+
459523fn get_udf_args_and_return_types (
460524 udf : & Arc < ScalarUDF > ,
461- ) -> Result < BTreeSet < ( Vec < String > , Option < String > ) > > {
462- let signature = udf. signature ( ) ;
463- let arg_types = signature. type_signature . get_example_types ( ) ;
464- if arg_types. is_empty ( ) {
465- Ok ( vec ! [ ( vec![ ] , None ) ] . into_iter ( ) . collect :: < BTreeSet < _ > > ( ) )
466- } else {
467- Ok ( arg_types
468- . into_iter ( )
469- . map ( |arg_types| {
470- let arg_fields: Vec < FieldRef > = arg_types
471- . iter ( )
472- . enumerate ( )
473- . map ( |( i, t) | {
474- Arc :: new ( Field :: new ( format ! ( "arg_{i}" ) , t. clone ( ) , true ) )
475- } )
476- . collect ( ) ;
477- let scalar_arguments = vec ! [ None ; arg_fields. len( ) ] ;
478- let return_type = udf
479- . return_field_from_args ( ReturnFieldArgs {
480- arg_fields : & arg_fields,
481- scalar_arguments : & scalar_arguments,
482- } )
483- . map ( |f| {
484- remove_native_type_prefix ( & NativeType :: from (
485- f. data_type ( ) . clone ( ) ,
486- ) )
487- } )
488- . ok ( ) ;
489- let arg_types = arg_types
490- . into_iter ( )
491- . map ( |t| remove_native_type_prefix ( & NativeType :: from ( t) ) )
492- . collect :: < Vec < _ > > ( ) ;
493- ( arg_types, return_type)
494- } )
495- . collect :: < BTreeSet < _ > > ( ) )
496- }
525+ ) -> Result < FunctionInformationSchema > {
526+ get_args_and_return_types ( udf. signature ( ) , |arg_fields| {
527+ let arg_fields = & fields_with_udf ( arg_fields, udf. as_ref ( ) ) ?;
528+ let scalar_arguments = & vec ! [ None ; arg_fields. len( ) ] ;
529+ udf. return_field_from_args ( ReturnFieldArgs {
530+ arg_fields,
531+ scalar_arguments,
532+ } )
533+ } )
497534}
498535
499536fn get_udaf_args_and_return_types (
500537 udaf : & Arc < AggregateUDF > ,
501- ) -> Result < BTreeSet < ( Vec < String > , Option < String > ) > > {
502- let signature = udaf. signature ( ) ;
503- let arg_types = signature. type_signature . get_example_types ( ) ;
504- if arg_types. is_empty ( ) {
505- Ok ( vec ! [ ( vec![ ] , None ) ] . into_iter ( ) . collect :: < BTreeSet < _ > > ( ) )
506- } else {
507- Ok ( arg_types
508- . into_iter ( )
509- . map ( |arg_types| {
510- let arg_fields: Vec < FieldRef > = arg_types
511- . iter ( )
512- . enumerate ( )
513- . map ( |( i, t) | {
514- Arc :: new ( Field :: new ( format ! ( "arg_{i}" ) , t. clone ( ) , true ) )
515- } )
516- . collect ( ) ;
517- let return_type = udaf
518- . return_field ( & arg_fields)
519- . map ( |f| {
520- remove_native_type_prefix ( & NativeType :: from (
521- f. data_type ( ) . clone ( ) ,
522- ) )
523- } )
524- . ok ( ) ;
525- let arg_types = arg_types
526- . into_iter ( )
527- . map ( |t| remove_native_type_prefix ( & NativeType :: from ( t) ) )
528- . collect :: < Vec < _ > > ( ) ;
529- ( arg_types, return_type)
530- } )
531- . collect :: < BTreeSet < _ > > ( ) )
532- }
538+ ) -> Result < FunctionInformationSchema > {
539+ get_args_and_return_types ( udaf. signature ( ) , |arg_fields| {
540+ let arg_fields = & fields_with_udf ( arg_fields, udaf. as_ref ( ) ) ?;
541+ udaf. return_field ( arg_fields)
542+ } )
533543}
534544
535545fn get_udwf_args_and_return_types (
536546 udwf : & Arc < WindowUDF > ,
537- ) -> Result < BTreeSet < ( Vec < String > , Option < String > ) > > {
538- let signature = udwf. signature ( ) ;
539- let arg_types = signature. type_signature . get_example_types ( ) ;
540- if arg_types. is_empty ( ) {
541- Ok ( vec ! [ ( vec![ ] , None ) ] . into_iter ( ) . collect :: < BTreeSet < _ > > ( ) )
542- } else {
543- Ok ( arg_types
544- . into_iter ( )
545- . map ( |arg_types| {
546- let arg_fields: Vec < FieldRef > = arg_types
547- . iter ( )
548- . enumerate ( )
549- . map ( |( i, t) | {
550- Arc :: new ( Field :: new ( format ! ( "arg_{i}" ) , t. clone ( ) , true ) )
551- } )
552- . collect ( ) ;
553- let return_type = udwf
554- . field ( WindowUDFFieldArgs :: new ( & arg_fields, udwf. name ( ) ) )
555- . map ( |f| {
556- remove_native_type_prefix ( & NativeType :: from (
557- f. data_type ( ) . clone ( ) ,
558- ) )
559- } )
560- . ok ( ) ;
561- let arg_types = arg_types
562- . into_iter ( )
563- . map ( |t| remove_native_type_prefix ( & NativeType :: from ( t) ) )
564- . collect :: < Vec < _ > > ( ) ;
565- ( arg_types, return_type)
566- } )
567- . collect :: < BTreeSet < _ > > ( ) )
568- }
547+ ) -> Result < FunctionInformationSchema > {
548+ get_args_and_return_types ( udwf. signature ( ) , |arg_fields| {
549+ let arg_fields = & fields_with_udf ( arg_fields, udwf. as_ref ( ) ) ?;
550+ udwf. field ( WindowUDFFieldArgs :: new ( arg_fields, udwf. name ( ) ) )
551+ } )
569552}
570553
571554#[ inline]
@@ -1510,6 +1493,9 @@ mod tests {
15101493 use super :: * ;
15111494 use crate :: CatalogProvider ;
15121495 use arrow:: array:: Array ;
1496+ use arrow:: datatypes:: Fields ;
1497+ use datafusion_common:: ScalarValue ;
1498+ use datafusion_expr:: { ColumnarValue , ScalarFunctionArgs , ScalarUDFImpl } ;
15131499
15141500 #[ test]
15151501 fn schemata_builder_emits_canonical_schema_and_rows ( ) {
@@ -1580,6 +1566,70 @@ mod tests {
15801566 assert_eq ! ( "BASE TABLE" , builder. table_types. finish( ) . value( 0 ) ) ;
15811567 }
15821568
1569+ // UDF
1570+ #[ derive( Debug , PartialEq , Eq , Hash ) ]
1571+ struct TestScalarUDF {
1572+ signature : Signature ,
1573+ }
1574+ impl ScalarUDFImpl for TestScalarUDF {
1575+ fn name ( & self ) -> & str {
1576+ "TestScalarUDF"
1577+ }
1578+
1579+ fn signature ( & self ) -> & Signature {
1580+ & self . signature
1581+ }
1582+
1583+ fn return_type ( & self , arg_types : & [ DataType ] ) -> Result < DataType > {
1584+ Ok ( arg_types. last ( ) . unwrap ( ) . clone ( ) )
1585+ }
1586+
1587+ fn invoke_with_args ( & self , _args : ScalarFunctionArgs ) -> Result < ColumnarValue > {
1588+ Ok ( ColumnarValue :: Scalar ( ScalarValue :: from ( "a" ) ) )
1589+ }
1590+ }
1591+
1592+ #[ test]
1593+ fn test_get_udf_args_and_return_types ( ) -> Result < ( ) > {
1594+ // heterogeneous arguments to test mixed arguments retrieval
1595+ let signature = Signature :: exact (
1596+ [
1597+ vec ! [ DataType :: Int32 ; 6 ] ,
1598+ vec ! [ DataType :: Float32 ; 6 ] ,
1599+ vec ! [ DataType :: Utf8 ; 1 ] ,
1600+ ]
1601+ . concat ( ) ,
1602+ Volatility :: Stable ,
1603+ ) ;
1604+ let udf = Arc :: new ( ScalarUDF :: from ( TestScalarUDF { signature } ) ) ;
1605+ let result = get_udf_args_and_return_types ( & udf) ?;
1606+ assert_eq ! ( result. len( ) , 1 ) ;
1607+ let ( args, ret) = result. iter ( ) . next ( ) . unwrap ( ) ;
1608+ assert_eq ! (
1609+ * args,
1610+ [
1611+ vec![ String :: from( "Int32" ) ; 6 ] ,
1612+ vec![ String :: from( "Float32" ) ; 6 ] ,
1613+ vec![ String :: from( "String" ) ; 1 ]
1614+ ]
1615+ . concat( )
1616+ ) ;
1617+ assert_eq ! ( * ret, Some ( String :: from( "String" ) ) ) ;
1618+
1619+ Ok ( ( ) )
1620+ }
1621+
1622+ #[ test]
1623+ fn test_get_udf_args_and_return_types_nested ( ) -> Result < ( ) > {
1624+ let struct_type =
1625+ DataType :: Struct ( Fields :: from ( vec ! [ Field :: new( "a" , DataType :: Int32 , true ) ] ) ) ;
1626+ let signature = Signature :: exact ( vec ! [ struct_type] , Volatility :: Stable ) ;
1627+ let udf = Arc :: new ( ScalarUDF :: from ( TestScalarUDF { signature } ) ) ;
1628+ let result = get_udf_args_and_return_types ( & udf) ?;
1629+ assert_eq ! ( result. len( ) , 1 ) ;
1630+ Ok ( ( ) )
1631+ }
1632+
15831633 #[ derive( Debug ) ]
15841634 struct Fixture ;
15851635
0 commit comments