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}