diff --git a/datafusion/functions/src/core/mod.rs b/datafusion/functions/src/core/mod.rs index 731a45c053ca..842a1db3e0d0 100644 --- a/datafusion/functions/src/core/mod.rs +++ b/datafusion/functions/src/core/mod.rs @@ -32,4 +32,3 @@ export_functions!( (nvl, arg_1 arg_2, "returns value2 if value1 is NULL; otherwise it returns value1"), (nvl2, arg_1 arg_2 arg_3, "Returns value2 if value1 is not NULL; otherwise, it returns value3.") ); - diff --git a/datafusion/functions/src/core/nullif.rs b/datafusion/functions/src/core/nullif.rs index afb308e441f5..3ff8dbd942ff 100644 --- a/datafusion/functions/src/core/nullif.rs +++ b/datafusion/functions/src/core/nullif.rs @@ -52,14 +52,14 @@ static SUPPORTED_NULLIF_TYPES: &[DataType] = &[ DataType::LargeUtf8, ]; - impl NullIfFunc { pub fn new() -> Self { Self { - signature: - Signature::uniform(2, SUPPORTED_NULLIF_TYPES.to_vec(), - Volatility::Immutable, - ) + signature: Signature::uniform( + 2, + SUPPORTED_NULLIF_TYPES.to_vec(), + Volatility::Immutable, + ), } } } @@ -78,10 +78,13 @@ impl ScalarUDFImpl for NullIfFunc { fn return_type(&self, arg_types: &[DataType]) -> Result { // NULLIF has two args and they might get coerced, get a preview of this - let coerced_types = datafusion_expr::type_coercion::functions::data_types(arg_types, &self.signature); - coerced_types.map(|typs| typs[0].clone()) - .map_err(|e| e.context("Failed to coerce arguments for NULLIF") - ) + let coerced_types = datafusion_expr::type_coercion::functions::data_types( + arg_types, + &self.signature, + ); + coerced_types + .map(|typs| typs[0].clone()) + .map_err(|e| e.context("Failed to coerce arguments for NULLIF")) } fn invoke(&self, args: &[ColumnarValue]) -> Result { @@ -89,7 +92,6 @@ impl ScalarUDFImpl for NullIfFunc { } } - /// Implements NULLIF(expr1, expr2) /// Args: 0 - left expr is any array /// 1 - if the left is equal to this expr2, then the result is NULL, otherwise left value is passed. diff --git a/datafusion/functions/src/core/nvl.rs b/datafusion/functions/src/core/nvl.rs index 578aaeda2e89..76b037eb81ba 100644 --- a/datafusion/functions/src/core/nvl.rs +++ b/datafusion/functions/src/core/nvl.rs @@ -15,12 +15,12 @@ // specific language governing permissions and limitations // under the License. +use arrow::array::Array; +use arrow::compute::is_not_null; +use arrow::compute::kernels::zip::zip; use arrow::datatypes::DataType; use datafusion_common::{internal_err, Result}; use datafusion_expr::{ColumnarValue, ScalarUDFImpl, Signature, Volatility}; -use arrow::compute::kernels::zip::zip; -use arrow::compute::is_not_null; -use arrow::array::Array; #[derive(Debug)] pub(super) struct NVLFunc { @@ -50,8 +50,9 @@ static SUPPORTED_NVL_TYPES: &[DataType] = &[ impl NVLFunc { pub fn new() -> Self { Self { - signature: - Signature::uniform(2, SUPPORTED_NVL_TYPES.to_vec(), + signature: Signature::uniform( + 2, + SUPPORTED_NVL_TYPES.to_vec(), Volatility::Immutable, ), aliases: vec![String::from("ifnull")], @@ -195,8 +196,11 @@ mod tests { let result = nvl_func(&[a, lit_array])?; let result = result.into_array(0).expect("Failed to convert to array"); - let expected = - Arc::new(BooleanArray::from(vec![Some(true), Some(false), Some(false)])) as ArrayRef; + let expected = Arc::new(BooleanArray::from(vec![ + Some(true), + Some(false), + Some(false), + ])) as ArrayRef; assert_eq!(expected.as_ref(), result.as_ref()); Ok(()) @@ -251,7 +255,9 @@ mod tests { let b_null = ColumnarValue::Scalar(ScalarValue::Int32(Some(2i32))); let result_null = nvl_func(&[a_null, b_null])?; - let result_null = result_null.into_array(1).expect("Failed to convert to array"); + let result_null = result_null + .into_array(1) + .expect("Failed to convert to array"); let expected_null = Arc::new(Int32Array::from(vec![Some(2i32)])) as ArrayRef; diff --git a/datafusion/functions/src/core/nvl2.rs b/datafusion/functions/src/core/nvl2.rs index 6e2fcd061a06..a65657eaeafd 100644 --- a/datafusion/functions/src/core/nvl2.rs +++ b/datafusion/functions/src/core/nvl2.rs @@ -15,12 +15,12 @@ // specific language governing permissions and limitations // under the License. +use arrow::array::Array; +use arrow::compute::is_not_null; +use arrow::compute::kernels::zip::zip; use arrow::datatypes::DataType; use datafusion_common::{internal_err, plan_datafusion_err, Result}; use datafusion_expr::{utils, ColumnarValue, ScalarUDFImpl, Signature, Volatility}; -use arrow::compute::kernels::zip::zip; -use arrow::compute::is_not_null; -use arrow::array::Array; #[derive(Debug)] pub(super) struct NVL2Func { @@ -30,10 +30,7 @@ pub(super) struct NVL2Func { impl NVL2Func { pub fn new() -> Self { Self { - signature: - Signature::variadic_equal( - Volatility::Immutable, - ), + signature: Signature::variadic_equal(Volatility::Immutable), } } } @@ -87,14 +84,13 @@ fn nvl2_func(args: &[ColumnarValue]) -> Result { } } if is_array { - let args = args.iter().map(|arg| match arg { - ColumnarValue::Scalar(scalar) => { - scalar.to_array_of_size(len) - } - ColumnarValue::Array(array) => { - Ok(array.clone()) - } - }).collect::>>()?; + let args = args + .iter() + .map(|arg| match arg { + ColumnarValue::Scalar(scalar) => scalar.to_array_of_size(len), + ColumnarValue::Array(array) => Ok(array.clone()), + }) + .collect::>>()?; let to_apply = is_not_null(&args[0])?; let value = zip(&to_apply, &args[1], &args[2])?; Ok(ColumnarValue::Array(value)) diff --git a/datafusion/functions/src/lib.rs b/datafusion/functions/src/lib.rs index d2f0270959ee..746b1d93d6c2 100644 --- a/datafusion/functions/src/lib.rs +++ b/datafusion/functions/src/lib.rs @@ -84,7 +84,11 @@ use log::debug; #[macro_use] pub mod macros; -make_package!(core, "core_expressions", "Core datafusion expressions"); +/// Core datafusion expressions +/// Enabled via feature flag `core_expressions` +#[cfg(feature = "core_expressions")] +pub mod core; +make_stub_package!(core, "core_expressions"); make_package!( encoding, diff --git a/datafusion/functions/src/macros.rs b/datafusion/functions/src/macros.rs index 5debcbda30cc..fd32e1ed6fc0 100644 --- a/datafusion/functions/src/macros.rs +++ b/datafusion/functions/src/macros.rs @@ -122,6 +122,30 @@ macro_rules! make_package { }; } +/// Macro creates a sub module if the feature is not enabled +/// +/// The rationale for providing stub functions is to help users to configure datafusion +/// properly (so they get an error telling them why a function is not available) +/// instead of getting a cryptic "no function found" message at runtime. + +macro_rules! make_stub_package { + ($name:ident, $feature:literal) => { + #[cfg(not(feature = $feature))] + #[doc = concat!("Disabled. Enable via feature flag `", $feature, "`")] + pub mod $name { + use datafusion_expr::ScalarUDF; + use log::debug; + use std::sync::Arc; + + /// Returns an empty list of functions when the feature is not enabled + pub fn functions() -> Vec> { + debug!("{} functions disabled", stringify!($name)); + vec![] + } + } + }; +} + /// Invokes a function on each element of an array and returns the result as a new array /// /// $ARG: ArrayRef