in datafusion/expr-common/src/type_coercion/binary.rs [126:271]
fn signature(&'a self) -> Result<Signature> {
use arrow::datatypes::DataType::*;
use Operator::*;
let result = match self.op {
Eq |
NotEq |
Lt |
LtEq |
Gt |
GtEq |
IsDistinctFrom |
IsNotDistinctFrom => {
comparison_coercion(self.lhs, self.rhs).map(Signature::comparison).ok_or_else(|| {
plan_datafusion_err!(
"Cannot infer common argument type for comparison operation {} {} {}",
self.lhs,
self.op,
self.rhs
)
})
}
And | Or => if matches!((self.lhs, self.rhs), (Boolean | Null, Boolean | Null)) {
// Logical binary boolean operators can only be evaluated for
// boolean or null arguments.
Ok(Signature::uniform(Boolean))
} else {
plan_err!(
"Cannot infer common argument type for logical boolean operation {} {} {}", self.lhs, self.op, self.rhs
)
}
RegexMatch | RegexIMatch | RegexNotMatch | RegexNotIMatch => {
regex_coercion(self.lhs, self.rhs).map(Signature::comparison).ok_or_else(|| {
plan_datafusion_err!(
"Cannot infer common argument type for regex operation {} {} {}", self.lhs, self.op, self.rhs
)
})
}
LikeMatch | ILikeMatch | NotLikeMatch | NotILikeMatch => {
regex_coercion(self.lhs, self.rhs).map(Signature::comparison).ok_or_else(|| {
plan_datafusion_err!(
"Cannot infer common argument type for regex operation {} {} {}", self.lhs, self.op, self.rhs
)
})
}
BitwiseAnd | BitwiseOr | BitwiseXor | BitwiseShiftRight | BitwiseShiftLeft => {
bitwise_coercion(self.lhs, self.rhs).map(Signature::uniform).ok_or_else(|| {
plan_datafusion_err!(
"Cannot infer common type for bitwise operation {} {} {}", self.lhs, self.op, self.rhs
)
})
}
StringConcat => {
string_concat_coercion(self.lhs, self.rhs).map(Signature::uniform).ok_or_else(|| {
plan_datafusion_err!(
"Cannot infer common string type for string concat operation {} {} {}", self.lhs, self.op, self.rhs
)
})
}
AtArrow | ArrowAt => {
// Array contains or search (similar to LIKE) operation
array_coercion(self.lhs, self.rhs)
.or_else(|| like_coercion(self.lhs, self.rhs)).map(Signature::comparison).ok_or_else(|| {
plan_datafusion_err!(
"Cannot infer common argument type for operation {} {} {}", self.lhs, self.op, self.rhs
)
})
}
AtAt => {
// text search has similar signature to LIKE
like_coercion(self.lhs, self.rhs).map(Signature::comparison).ok_or_else(|| {
plan_datafusion_err!(
"Cannot infer common argument type for AtAt operation {} {} {}", self.lhs, self.op, self.rhs
)
})
}
Plus | Minus | Multiply | Divide | Modulo => {
let get_result = |lhs, rhs| {
use arrow::compute::kernels::numeric::*;
let l = new_empty_array(lhs);
let r = new_empty_array(rhs);
let result = match self.op {
Plus => add_wrapping(&l, &r),
Minus => sub_wrapping(&l, &r),
Multiply => mul_wrapping(&l, &r),
Divide => div(&l, &r),
Modulo => rem(&l, &r),
_ => unreachable!(),
};
result.map(|x| x.data_type().clone())
};
if let Ok(ret) = get_result(self.lhs, self.rhs) {
// Temporal arithmetic, e.g. Date32 + Interval
Ok(Signature{
lhs: self.lhs.clone(),
rhs: self.rhs.clone(),
ret,
})
} else if let Some(coerced) = temporal_coercion_strict_timezone(self.lhs, self.rhs) {
// Temporal arithmetic by first coercing to a common time representation
// e.g. Date32 - Timestamp
let ret = get_result(&coerced, &coerced).map_err(|e| {
plan_datafusion_err!(
"Cannot get result type for temporal operation {coerced} {} {coerced}: {e}", self.op
)
})?;
Ok(Signature{
lhs: coerced.clone(),
rhs: coerced,
ret,
})
} else if let Some((lhs, rhs)) = math_decimal_coercion(self.lhs, self.rhs) {
// Decimal arithmetic, e.g. Decimal(10, 2) + Decimal(10, 0)
let ret = get_result(&lhs, &rhs).map_err(|e| {
plan_datafusion_err!(
"Cannot get result type for decimal operation {} {} {}: {e}", self.lhs, self.op, self.rhs
)
})?;
Ok(Signature{
lhs,
rhs,
ret,
})
} else if let Some(numeric) = mathematics_numerical_coercion(self.lhs, self.rhs) {
// Numeric arithmetic, e.g. Int32 + Int32
Ok(Signature::uniform(numeric))
} else {
plan_err!(
"Cannot coerce arithmetic expression {} {} {} to valid types", self.lhs, self.op, self.rhs
)
}
},
IntegerDivide | Arrow | LongArrow | HashArrow | HashLongArrow
| HashMinus | AtQuestion | Question | QuestionAnd | QuestionPipe => {
not_impl_err!("Operator {} is not yet supported", self.op)
}
};
result.map_err(|err| {
let diagnostic =
Diagnostic::new_error("expressions have incompatible types", self.span())
.with_note(format!("has type {}", self.lhs), self.lhs_spans.first())
.with_note(format!("has type {}", self.rhs), self.rhs_spans.first());
err.with_diagnostic(diagnostic)
})
}