use std::sync::Arc;
use arrow::datatypes::{DataType, Schema};
use super::{functions::Signature, PhysicalExpr};
use crate::error::{ExecutionError, Result};
use crate::physical_plan::expressions::cast;
pub fn coerce(
expressions: &Vec<Arc<dyn PhysicalExpr>>,
schema: &Schema,
signature: &Signature,
) -> Result<Vec<Arc<dyn PhysicalExpr>>> {
let current_types = expressions
.iter()
.map(|e| e.data_type(schema))
.collect::<Result<Vec<_>>>()?;
let new_types = data_types(¤t_types, signature)?;
expressions
.iter()
.enumerate()
.map(|(i, expr)| cast(expr.clone(), &schema, new_types[i].clone()))
.collect::<Result<Vec<_>>>()
}
pub fn data_types(
current_types: &Vec<DataType>,
signature: &Signature,
) -> Result<Vec<DataType>> {
let valid_types = match signature {
Signature::Variadic(valid_types) => valid_types
.iter()
.map(|valid_type| current_types.iter().map(|_| valid_type.clone()).collect())
.collect(),
Signature::Uniform(number, valid_types) => valid_types
.iter()
.map(|valid_type| (0..*number).map(|_| valid_type.clone()).collect())
.collect(),
Signature::VariadicEqual => {
vec![current_types
.iter()
.map(|_| current_types[0].clone())
.collect()]
}
Signature::Exact(valid_types) => vec![valid_types.clone()],
Signature::Any(number) => {
if current_types.len() != *number {
return Err(ExecutionError::General(format!(
"The function expected {} arguments but received {}",
number,
current_types.len()
)));
}
vec![(0..*number).map(|i| current_types[i].clone()).collect()]
}
};
if valid_types.contains(current_types) {
return Ok(current_types.clone());
}
for valid_types in valid_types {
if let Some(types) = maybe_data_types(&valid_types, ¤t_types) {
return Ok(types);
}
}
Err(ExecutionError::General(format!(
"Coercion from {:?} to the signature {:?} failed.",
current_types, signature
)))
}
fn maybe_data_types(
valid_types: &Vec<DataType>,
current_types: &Vec<DataType>,
) -> Option<Vec<DataType>> {
if valid_types.len() != current_types.len() {
return None;
}
let mut new_type = Vec::with_capacity(valid_types.len());
for (i, valid_type) in valid_types.iter().enumerate() {
let current_type = ¤t_types[i];
if current_type == valid_type {
new_type.push(current_type.clone())
} else {
if can_coerce_from(valid_type, ¤t_type) {
new_type.push(valid_type.clone())
} else {
return None;
}
}
}
Some(new_type)
}
pub fn can_coerce_from(type_into: &DataType, type_from: &DataType) -> bool {
use self::DataType::*;
match type_into {
Int8 => match type_from {
Int8 => true,
_ => false,
},
Int16 => match type_from {
Int8 | Int16 | UInt8 => true,
_ => false,
},
Int32 => match type_from {
Int8 | Int16 | Int32 | UInt8 | UInt16 => true,
_ => false,
},
Int64 => match type_from {
Int8 | Int16 | Int32 | Int64 | UInt8 | UInt16 | UInt32 => true,
_ => false,
},
UInt8 => match type_from {
UInt8 => true,
_ => false,
},
UInt16 => match type_from {
UInt8 | UInt16 => true,
_ => false,
},
UInt32 => match type_from {
UInt8 | UInt16 | UInt32 => true,
_ => false,
},
UInt64 => match type_from {
UInt8 | UInt16 | UInt32 | UInt64 => true,
_ => false,
},
Float32 => match type_from {
Int8 | Int16 | Int32 | Int64 => true,
UInt8 | UInt16 | UInt32 | UInt64 => true,
Float32 => true,
_ => false,
},
Float64 => match type_from {
Int8 | Int16 | Int32 | Int64 => true,
UInt8 | UInt16 | UInt32 | UInt64 => true,
Float32 | Float64 => true,
_ => false,
},
Utf8 => true,
_ => false,
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::physical_plan::expressions::col;
use arrow::datatypes::{DataType, Field, Schema};
#[test]
fn test_maybe_data_types() -> Result<()> {
let cases = vec![
(
vec![DataType::UInt8, DataType::UInt16],
vec![DataType::UInt8, DataType::UInt16],
Some(vec![DataType::UInt8, DataType::UInt16]),
),
(
vec![DataType::UInt16, DataType::UInt16],
vec![DataType::UInt8, DataType::UInt16],
Some(vec![DataType::UInt16, DataType::UInt16]),
),
(vec![], vec![], Some(vec![])),
(
vec![DataType::Boolean, DataType::UInt16],
vec![DataType::UInt8, DataType::UInt16],
None,
),
(
vec![DataType::Boolean, DataType::UInt32],
vec![DataType::Boolean, DataType::UInt16],
Some(vec![DataType::Boolean, DataType::UInt32]),
),
];
for case in cases {
assert_eq!(maybe_data_types(&case.0, &case.1), case.2)
}
Ok(())
}
#[test]
fn test_coerce() -> Result<()> {
let schema = |t: Vec<DataType>| {
Schema::new(
t.iter()
.enumerate()
.map(|(i, t)| Field::new(&*format!("c{}", i), t.clone(), true))
.collect(),
)
};
let expressions = |t: Vec<DataType>, schema| -> Result<Vec<_>> {
t.iter()
.enumerate()
.map(|(i, t)| cast(col(&format!("c{}", i)), &schema, t.clone()))
.collect::<Result<Vec<_>>>()
};
let case =
|observed: Vec<DataType>, valid, expected: Vec<DataType>| -> Result<_> {
let schema = schema(observed.clone());
let expr = expressions(observed, schema.clone())?;
let expected = expressions(expected, schema.clone())?;
Ok((expr.clone(), schema, valid, expected))
};
let cases = vec![
case(
vec![DataType::UInt16],
Signature::Uniform(1, vec![DataType::UInt32]),
vec![DataType::UInt32],
)?,
case(
vec![DataType::UInt32, DataType::UInt32],
Signature::Uniform(2, vec![DataType::UInt32]),
vec![DataType::UInt32, DataType::UInt32],
)?,
case(
vec![DataType::UInt32],
Signature::Uniform(1, vec![DataType::Float32, DataType::Float64]),
vec![DataType::Float32],
)?,
case(
vec![DataType::UInt32, DataType::UInt32],
Signature::Variadic(vec![DataType::Float32]),
vec![DataType::Float32, DataType::Float32],
)?,
case(
vec![DataType::Float32, DataType::UInt32],
Signature::VariadicEqual,
vec![DataType::Float32, DataType::Float32],
)?,
case(
vec![DataType::UInt32, DataType::UInt64],
Signature::Variadic(vec![DataType::UInt32, DataType::UInt64]),
vec![DataType::UInt64, DataType::UInt64],
)?,
case(
vec![DataType::Float32],
Signature::Any(1),
vec![DataType::Float32],
)?,
];
for case in cases {
let observed = format!("{:?}", coerce(&case.0, &case.1, &case.2)?);
let expected = format!("{:?}", case.3);
assert_eq!(observed, expected);
}
let cases = vec![
case(
vec![DataType::Boolean],
Signature::Uniform(1, vec![DataType::UInt16]),
vec![],
)?,
case(
vec![DataType::UInt32, DataType::Boolean],
Signature::VariadicEqual,
vec![],
)?,
case(
vec![DataType::Boolean, DataType::Boolean],
Signature::Variadic(vec![DataType::UInt32]),
vec![],
)?,
case(vec![DataType::UInt32], Signature::Any(2), vec![])?,
];
for case in cases {
if let Ok(_) = coerce(&case.0, &case.1, &case.2) {
return Err(ExecutionError::General(format!(
"Error was expected in {:?}",
case
)));
}
}
Ok(())
}
}