use crate::planner::{ContextProvider, PlannerContext, SqlToRel};
use datafusion_common::{not_impl_err, plan_err, DFSchema, Diagnostic, Result};
use datafusion_expr::{
type_coercion::{is_interval, is_timestamp},
Expr, ExprSchemable,
};
use sqlparser::ast::{Expr as SQLExpr, UnaryOperator, Value, ValueWithSpan};
impl<S: ContextProvider> SqlToRel<'_, S> {
pub(crate) fn parse_sql_unary_op(
&self,
op: UnaryOperator,
expr: SQLExpr,
schema: &DFSchema,
planner_context: &mut PlannerContext,
) -> Result<Expr> {
match op {
UnaryOperator::Not => Ok(Expr::Not(Box::new(
self.sql_expr_to_logical_expr(expr, schema, planner_context)?,
))),
UnaryOperator::Plus => {
let operand =
self.sql_expr_to_logical_expr(expr, schema, planner_context)?;
let (data_type, _) = operand.data_type_and_nullable(schema)?;
if data_type.is_numeric()
|| is_interval(&data_type)
|| is_timestamp(&data_type)
{
Ok(operand)
} else {
let span = operand.spans().and_then(|s| s.first());
let mut diagnostic = Diagnostic::new_error(
format!("+ cannot be used with {data_type}"),
span,
);
diagnostic.add_note(
"+ can only be used with numbers, intervals, and timestamps",
None,
);
diagnostic
.add_help(format!("perhaps you need to cast {operand}"), None);
plan_err!("Unary operator '+' only supports numeric, interval and timestamp types"; diagnostic=diagnostic)
}
}
UnaryOperator::Minus => {
match expr {
SQLExpr::Value(ValueWithSpan {
value: Value::Number(n, _),
span: _,
}) => self.parse_sql_number(&n, true),
SQLExpr::Interval(interval) => {
self.sql_interval_to_expr(true, interval)
}
_ => Ok(Expr::Negative(Box::new(self.sql_expr_to_logical_expr(
expr,
schema,
planner_context,
)?))),
}
}
_ => not_impl_err!("Unsupported SQL unary operator {op:?}"),
}
}
}