in datafusion/optimizer/src/analyzer/type_coercion.rs [135:395]
fn mutate(&mut self, expr: Expr) -> Result<Expr> {
match expr {
Expr::ScalarSubquery(Subquery {
subquery,
outer_ref_columns,
}) => {
let new_plan = analyze_internal(&self.schema, &subquery)?;
Ok(Expr::ScalarSubquery(Subquery {
subquery: Arc::new(new_plan),
outer_ref_columns,
}))
}
Expr::Exists(Exists { subquery, negated }) => {
let new_plan = analyze_internal(&self.schema, &subquery.subquery)?;
Ok(Expr::Exists(Exists {
subquery: Subquery {
subquery: Arc::new(new_plan),
outer_ref_columns: subquery.outer_ref_columns,
},
negated,
}))
}
Expr::InSubquery(InSubquery {
expr,
subquery,
negated,
}) => {
let new_plan = analyze_internal(&self.schema, &subquery.subquery)?;
let expr_type = expr.get_type(&self.schema)?;
let subquery_type = new_plan.schema().field(0).data_type();
let common_type = comparison_coercion(&expr_type, subquery_type).ok_or(DataFusionError::Plan(
format!(
"expr type {expr_type:?} can't cast to {subquery_type:?} in InSubquery"
),
))?;
let new_subquery = Subquery {
subquery: Arc::new(new_plan),
outer_ref_columns: subquery.outer_ref_columns,
};
Ok(Expr::InSubquery(InSubquery::new(
Box::new(expr.cast_to(&common_type, &self.schema)?),
cast_subquery(new_subquery, &common_type)?,
negated,
)))
}
Expr::IsTrue(expr) => {
let expr = is_true(get_casted_expr_for_bool_op(&expr, &self.schema)?);
Ok(expr)
}
Expr::IsNotTrue(expr) => {
let expr = is_not_true(get_casted_expr_for_bool_op(&expr, &self.schema)?);
Ok(expr)
}
Expr::IsFalse(expr) => {
let expr = is_false(get_casted_expr_for_bool_op(&expr, &self.schema)?);
Ok(expr)
}
Expr::IsNotFalse(expr) => {
let expr =
is_not_false(get_casted_expr_for_bool_op(&expr, &self.schema)?);
Ok(expr)
}
Expr::IsUnknown(expr) => {
let expr = is_unknown(get_casted_expr_for_bool_op(&expr, &self.schema)?);
Ok(expr)
}
Expr::IsNotUnknown(expr) => {
let expr =
is_not_unknown(get_casted_expr_for_bool_op(&expr, &self.schema)?);
Ok(expr)
}
Expr::Like(Like {
negated,
expr,
pattern,
escape_char,
case_insensitive,
}) => {
let left_type = expr.get_type(&self.schema)?;
let right_type = pattern.get_type(&self.schema)?;
let coerced_type = like_coercion(&left_type, &right_type).ok_or_else(|| {
let op_name = if case_insensitive {
"ILIKE"
} else {
"LIKE"
};
DataFusionError::Plan(format!(
"There isn't a common type to coerce {left_type} and {right_type} in {op_name} expression"
))
})?;
let expr = Box::new(expr.cast_to(&coerced_type, &self.schema)?);
let pattern = Box::new(pattern.cast_to(&coerced_type, &self.schema)?);
let expr = Expr::Like(Like::new(
negated,
expr,
pattern,
escape_char,
case_insensitive,
));
Ok(expr)
}
Expr::BinaryExpr(BinaryExpr { left, op, right }) => {
let (left_type, right_type) = get_input_types(
&left.get_type(&self.schema)?,
&op,
&right.get_type(&self.schema)?,
)?;
Ok(Expr::BinaryExpr(BinaryExpr::new(
Box::new(left.cast_to(&left_type, &self.schema)?),
op,
Box::new(right.cast_to(&right_type, &self.schema)?),
)))
}
Expr::Between(Between {
expr,
negated,
low,
high,
}) => {
let expr_type = expr.get_type(&self.schema)?;
let low_type = low.get_type(&self.schema)?;
let low_coerced_type = comparison_coercion(&expr_type, &low_type)
.ok_or_else(|| {
DataFusionError::Internal(format!(
"Failed to coerce types {expr_type} and {low_type} in BETWEEN expression"
))
})?;
let high_type = high.get_type(&self.schema)?;
let high_coerced_type = comparison_coercion(&expr_type, &low_type)
.ok_or_else(|| {
DataFusionError::Internal(format!(
"Failed to coerce types {expr_type} and {high_type} in BETWEEN expression"
))
})?;
let coercion_type =
comparison_coercion(&low_coerced_type, &high_coerced_type)
.ok_or_else(|| {
DataFusionError::Internal(format!(
"Failed to coerce types {expr_type} and {high_type} in BETWEEN expression"
))
})?;
let expr = Expr::Between(Between::new(
Box::new(expr.cast_to(&coercion_type, &self.schema)?),
negated,
Box::new(low.cast_to(&coercion_type, &self.schema)?),
Box::new(high.cast_to(&coercion_type, &self.schema)?),
));
Ok(expr)
}
Expr::InList(InList {
expr,
list,
negated,
}) => {
let expr_data_type = expr.get_type(&self.schema)?;
let list_data_types = list
.iter()
.map(|list_expr| list_expr.get_type(&self.schema))
.collect::<Result<Vec<_>>>()?;
let result_type =
get_coerce_type_for_list(&expr_data_type, &list_data_types);
match result_type {
None => plan_err!(
"Can not find compatible types to compare {expr_data_type:?} with {list_data_types:?}"
),
Some(coerced_type) => {
// find the coerced type
let cast_expr = expr.cast_to(&coerced_type, &self.schema)?;
let cast_list_expr = list
.into_iter()
.map(|list_expr| {
list_expr.cast_to(&coerced_type, &self.schema)
})
.collect::<Result<Vec<_>>>()?;
let expr = Expr::InList(InList ::new(
Box::new(cast_expr),
cast_list_expr,
negated,
));
Ok(expr)
}
}
}
Expr::Case(case) => {
let case = coerce_case_expression(case, &self.schema)?;
Ok(Expr::Case(case))
}
Expr::ScalarUDF(ScalarUDF { fun, args }) => {
let new_expr = coerce_arguments_for_signature(
args.as_slice(),
&self.schema,
&fun.signature,
)?;
Ok(Expr::ScalarUDF(ScalarUDF::new(fun, new_expr)))
}
Expr::ScalarFunction(ScalarFunction { fun, args }) => {
let new_args = coerce_arguments_for_signature(
args.as_slice(),
&self.schema,
&fun.signature(),
)?;
let new_args =
coerce_arguments_for_fun(new_args.as_slice(), &self.schema, &fun)?;
Ok(Expr::ScalarFunction(ScalarFunction::new(fun, new_args)))
}
Expr::AggregateFunction(expr::AggregateFunction {
fun,
args,
distinct,
filter,
order_by,
}) => {
let new_expr = coerce_agg_exprs_for_signature(
&fun,
&args,
&self.schema,
&fun.signature(),
)?;
let expr = Expr::AggregateFunction(expr::AggregateFunction::new(
fun, new_expr, distinct, filter, order_by,
));
Ok(expr)
}
Expr::AggregateUDF(expr::AggregateUDF {
fun,
args,
filter,
order_by,
}) => {
let new_expr = coerce_arguments_for_signature(
args.as_slice(),
&self.schema,
&fun.signature,
)?;
let expr = Expr::AggregateUDF(expr::AggregateUDF::new(
fun, new_expr, filter, order_by,
));
Ok(expr)
}
Expr::WindowFunction(WindowFunction {
fun,
args,
partition_by,
order_by,
window_frame,
}) => {
let window_frame =
coerce_window_frame(window_frame, &self.schema, &order_by)?;
let expr = Expr::WindowFunction(WindowFunction::new(
fun,
args,
partition_by,
order_by,
window_frame,
));
Ok(expr)
}
expr => Ok(expr),
}
}