Skip to content

Commit

Permalink
introduce numeric and numeric string
Browse files Browse the repository at this point in the history
Signed-off-by: jayzhan211 <[email protected]>
  • Loading branch information
jayzhan211 committed Nov 4, 2024
1 parent ec65ca1 commit 1aba4cb
Show file tree
Hide file tree
Showing 4 changed files with 81 additions and 28 deletions.
17 changes: 17 additions & 0 deletions datafusion/expr-common/src/signature.rs
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,13 @@ pub enum TypeSignature {
/// Fixed number of arguments of numeric types.
/// See [`NativeType::is_numeric`] to know which type is considered numeric
Numeric(usize),
/// Fixed number of arguments of numeric types.
/// See [`NativeType::is_numeric`] to know which type is considered numeric
/// This signature accepts numeric string
/// Example of functions In Postgres that support numeric string
/// 1. Mathematical Functions, like `abs`
/// 2. `to_timestamp`
NumericAndNumericString(usize),
/// Fixed number of arguments of all the same string types.
/// The precedence of type from high to low is Utf8View, LargeUtf8 and Utf8.
/// Null is considerd as `Utf8` by default
Expand Down Expand Up @@ -202,6 +209,9 @@ impl TypeSignature {
TypeSignature::Numeric(num) => {
vec![format!("Numeric({num})")]
}
TypeSignature::NumericAndNumericString(num) => {
vec![format!("NumericAndNumericString({num})")]
}
TypeSignature::Coercible(types) => {
vec![Self::join_types(types, ", ")]
}
Expand Down Expand Up @@ -292,6 +302,13 @@ impl Signature {
}
}

pub fn numeric_and_numeric_string(arg_count: usize, volatility: Volatility) -> Self {
Self {
type_signature: TypeSignature::NumericAndNumericString(arg_count),
volatility,
}
}

/// A specified number of numeric arguments
pub fn string(arg_count: usize, volatility: Volatility) -> Self {
Self {
Expand Down
85 changes: 58 additions & 27 deletions datafusion/expr/src/type_coercion/functions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,7 @@ fn is_well_supported_signature(type_signature: &TypeSignature) -> bool {
type_signature,
TypeSignature::UserDefined
| TypeSignature::Numeric(_)
| TypeSignature::NumericAndNumericString(_)
| TypeSignature::String(_)
| TypeSignature::Coercible(_)
| TypeSignature::Any(_)
Expand Down Expand Up @@ -396,25 +397,29 @@ fn get_valid_types(
}
}

fn function_length_check(length: usize, expected_length: usize) -> Result<()> {
if length < 1 {
return plan_err!(
"The signature expected at least one argument but received {expected_length}"
);
}

if length != expected_length {
return plan_err!(
"The signature expected {length} arguments but received {expected_length}"
);
}

Ok(())
}

let valid_types = match signature {
TypeSignature::Variadic(valid_types) => valid_types
.iter()
.map(|valid_type| current_types.iter().map(|_| valid_type.clone()).collect())
.collect(),
TypeSignature::String(number) => {
if *number < 1 {
return plan_err!(
"The signature expected at least one argument but received {}",
current_types.len()
);
}
if *number != current_types.len() {
return plan_err!(
"The signature expected {} arguments but received {}",
number,
current_types.len()
);
}
function_length_check(current_types.len(), *number)?;

let mut new_types = Vec::with_capacity(current_types.len());
for data_type in current_types.iter() {
Expand Down Expand Up @@ -474,20 +479,8 @@ fn get_valid_types(

vec![vec![base_type_or_default_type(&coerced_type); *number]]
}
TypeSignature::Numeric(number) => {
if *number < 1 {
return plan_err!(
"The signature expected at least one argument but received {}",
current_types.len()
);
}
if *number != current_types.len() {
return plan_err!(
"The signature expected {} arguments but received {}",
number,
current_types.len()
);
}
TypeSignature::NumericAndNumericString(number) => {
function_length_check(current_types.len(), *number)?;

// Find common numeric type amongs given types except string
let mut valid_type = current_types.first().unwrap().to_owned();
Expand Down Expand Up @@ -529,6 +522,44 @@ fn get_valid_types(

vec![vec![valid_type; *number]]
}
TypeSignature::Numeric(number) => {
function_length_check(current_types.len(), *number)?;

// Find common numeric type amongs given types except string
let mut valid_type = current_types.first().unwrap().to_owned();
for t in current_types.iter().skip(1) {
let logical_data_type: NativeType = t.into();
if logical_data_type == NativeType::Null {
continue;
}

if !logical_data_type.is_numeric() {
return plan_err!(
"The signature expected NativeType::Numeric but received {t}"
);
}

if let Some(coerced_type) = binary_numeric_coercion(&valid_type, t) {
valid_type = coerced_type;
} else {
return plan_err!(
"{} and {} are not coercible to a common numeric type",
valid_type,
t
);
}
}

let logical_data_type: NativeType = valid_type.clone().into();
// Fallback to default type if we don't know which type to coerced to
// f64 is choosen since most of the math function utilize Signature::numeric,
// and their default type is double precision
if logical_data_type == NativeType::Null {
valid_type = DataType::Float64;
}

vec![vec![valid_type; *number]]
}
TypeSignature::Coercible(_target_types) => {
return plan_err!("Deprecated, use String, Numeric directly");
}
Expand Down
2 changes: 1 addition & 1 deletion datafusion/functions/src/math/abs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ impl Default for AbsFunc {
impl AbsFunc {
pub fn new() -> Self {
Self {
signature: Signature::numeric(1, Volatility::Immutable),
signature: Signature::numeric_and_numeric_string(1, Volatility::Immutable),
}
}
}
Expand Down
5 changes: 5 additions & 0 deletions datafusion/sqllogictest/test_files/math.slt
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,11 @@ SELECT abs(1, 2);
query error DataFusion error: Arrow error: Cast error: Cannot cast string 'foo' to value of Float64 type
SELECT abs('foo');

# abs: numeric string
query R
select abs('-1.2');
----
1.2

statement ok
CREATE TABLE test_nullable_integer(
Expand Down

0 comments on commit 1aba4cb

Please sign in to comment.