datafusion_sql/expr/unary_op.rs
1// Licensed to the Apache Software Foundation (ASF) under one
2// or more contributor license agreements. See the NOTICE file
3// distributed with this work for additional information
4// regarding copyright ownership. The ASF licenses this file
5// to you under the Apache License, Version 2.0 (the
6// "License"); you may not use this file except in compliance
7// with the License. You may obtain a copy of the License at
8//
9// http://www.apache.org/licenses/LICENSE-2.0
10//
11// Unless required by applicable law or agreed to in writing,
12// software distributed under the License is distributed on an
13// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14// KIND, either express or implied. See the License for the
15// specific language governing permissions and limitations
16// under the License.
17
18use crate::planner::{ContextProvider, PlannerContext, SqlToRel};
19use datafusion_common::{not_impl_err, plan_err, DFSchema, Diagnostic, Result};
20use datafusion_expr::{
21 type_coercion::{is_interval, is_timestamp},
22 Expr, ExprSchemable,
23};
24use sqlparser::ast::{Expr as SQLExpr, UnaryOperator, Value, ValueWithSpan};
25
26impl<S: ContextProvider> SqlToRel<'_, S> {
27 pub(crate) fn parse_sql_unary_op(
28 &self,
29 op: UnaryOperator,
30 expr: SQLExpr,
31 schema: &DFSchema,
32 planner_context: &mut PlannerContext,
33 ) -> Result<Expr> {
34 match op {
35 UnaryOperator::Not => Ok(Expr::Not(Box::new(
36 self.sql_expr_to_logical_expr(expr, schema, planner_context)?,
37 ))),
38 UnaryOperator::Plus => {
39 let operand =
40 self.sql_expr_to_logical_expr(expr, schema, planner_context)?;
41 let (data_type, _) = operand.data_type_and_nullable(schema)?;
42 if data_type.is_numeric()
43 || is_interval(&data_type)
44 || is_timestamp(&data_type)
45 {
46 Ok(operand)
47 } else {
48 let span = operand.spans().and_then(|s| s.first());
49 let mut diagnostic = Diagnostic::new_error(
50 format!("+ cannot be used with {data_type}"),
51 span,
52 );
53 diagnostic.add_note(
54 "+ can only be used with numbers, intervals, and timestamps",
55 None,
56 );
57 diagnostic
58 .add_help(format!("perhaps you need to cast {operand}"), None);
59 plan_err!("Unary operator '+' only supports numeric, interval and timestamp types"; diagnostic=diagnostic)
60 }
61 }
62 UnaryOperator::Minus => {
63 match expr {
64 // Optimization: if it's a number literal, we apply the negative operator
65 // here directly to calculate the new literal.
66 SQLExpr::Value(ValueWithSpan {
67 value: Value::Number(n, _),
68 span: _,
69 }) => self.parse_sql_number(&n, true),
70 SQLExpr::Interval(interval) => {
71 self.sql_interval_to_expr(true, interval)
72 }
73 // Not a literal, apply negative operator on expression
74 _ => Ok(Expr::Negative(Box::new(self.sql_expr_to_logical_expr(
75 expr,
76 schema,
77 planner_context,
78 )?))),
79 }
80 }
81 _ => not_impl_err!("Unsupported SQL unary operator {op:?}"),
82 }
83 }
84}