Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
73 changes: 47 additions & 26 deletions datafusion/expr/src/udf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -543,11 +543,7 @@ pub trait ScalarUDFImpl: Debug + DynEq + DynHash + Send + Sync + Any {
///
/// See [`Expr::schema_name`] for details
fn schema_name(&self, args: &[Expr]) -> Result<String> {
Ok(format!(
"{}({})",
self.name(),
schema_name_from_exprs_comma_separated_without_space(args)?
))
udf_default_schema_name(self.name(), args)
}

/// Returns a [`Signature`] describing the argument types for which this
Expand Down Expand Up @@ -660,12 +656,7 @@ pub trait ScalarUDFImpl: Debug + DynEq + DynHash + Send + Sync + Any {
/// logical input even if the input is simplified (e.g. it must return the same
/// value for `('foo' | 'bar')` as it does for ('foobar').
fn return_field_from_args(&self, args: ReturnFieldArgs) -> Result<FieldRef> {
let data_types = args
.arg_fields
.iter()
.map(|f| f.data_type())
.cloned()
.collect::<Vec<_>>();
let data_types = arg_fields_to_data_types(args.arg_fields);
let return_type = self.return_type(&data_types)?;
Ok(Arc::new(Field::new(self.name(), return_type, true)))
}
Expand Down Expand Up @@ -941,20 +932,7 @@ pub trait ScalarUDFImpl: Debug + DynEq + DynHash + Send + Sync + Any {
if !self.preserves_lex_ordering(inputs)? {
return Ok(SortProperties::Unordered);
}

let Some(first_order) = inputs.first().map(|p| &p.sort_properties) else {
return Ok(SortProperties::Singleton);
};

if inputs
.iter()
.skip(1)
.all(|input| &input.sort_properties == first_order)
{
Ok(*first_order)
} else {
Ok(SortProperties::Unordered)
}
Ok(common_sort_properties(inputs))
}

/// Returns true if the function preserves lexicographical ordering based on
Expand Down Expand Up @@ -995,7 +973,7 @@ pub trait ScalarUDFImpl: Debug + DynEq + DynHash + Send + Sync + Any {
/// A Vec the same length as `arg_types`. DataFusion will `CAST` the function call
/// arguments to these specific types.
fn coerce_types(&self, _arg_types: &[DataType]) -> Result<Vec<DataType>> {
not_impl_err!("Function {} does not implement coerce_types", self.name())
coerce_types_not_implemented(self.name())
}

/// For struct-producing functions, return how output fields map to input
Expand Down Expand Up @@ -1040,6 +1018,49 @@ pub trait ScalarUDFImpl: Debug + DynEq + DynHash + Send + Sync + Any {
}
}

/// Default implementation of [`ScalarUDFImpl::coerce_types`].

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i think it would be better to have these as inner functions, see:

benefit is its more clear this is only meant to be used within that function, and we dont need to bother with this extensive documentation

/// Extracted to a free-standing function to reduce binary size by avoiding instantiating code per ScalarUDFImpl impl.
fn coerce_types_not_implemented(name: &str) -> Result<Vec<DataType>> {
not_impl_err!("Function {name} does not implement coerce_types")
}

/// Default implementation of [`ScalarUDFImpl::schema_name`]:
/// `name(arg1,arg2,..)`
/// Extracted to a free-standing function to reduce binary size by avoiding instantiating code per ScalarUDFImpl impl.
fn udf_default_schema_name(name: &str, args: &[Expr]) -> Result<String> {
Ok(format!(
"{}({})",
name,
schema_name_from_exprs_comma_separated_without_space(args)?
))
}

/// Collects the [`DataType`] of each field
/// Extracted to a free-standing function to reduce binary size by avoiding instantiating code per ScalarUDFImpl impl.
fn arg_fields_to_data_types(arg_fields: &[FieldRef]) -> Vec<DataType> {
arg_fields.iter().map(|f| f.data_type().clone()).collect()
}

/// Returns the [`SortProperties`] shared by all `inputs`, or
/// [`SortProperties::Unordered`] if they differ. Used by the default
/// implementation of [`ScalarUDFImpl::output_ordering`].
/// Extracted to a free-standing function to reduce binary size by avoiding instantiating code per ScalarUDFImpl impl.
fn common_sort_properties(inputs: &[ExprProperties]) -> SortProperties {
let Some(first_order) = inputs.first().map(|p| &p.sort_properties) else {
return SortProperties::Singleton;
};

if inputs
.iter()
.skip(1)
.all(|input| &input.sort_properties == first_order)
{
*first_order
} else {
SortProperties::Unordered
}
}

impl dyn ScalarUDFImpl {
/// Returns `true` if the implementation is of type `T`.
///
Expand Down