| // Licensed to the Apache Software Foundation (ASF) under one |
| // or more contributor license agreements. See the NOTICE file |
| // distributed with this work for additional information |
| // regarding copyright ownership. The ASF licenses this file |
| // to you under the Apache License, Version 2.0 (the |
| // "License"); you may not use this file except in compliance |
| // with the License. You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, |
| // software distributed under the License is distributed on an |
| // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| // KIND, either express or implied. See the License for the |
| // specific language governing permissions and limitations |
| // under the License. |
| |
| use std::fmt; |
| |
| /// Operators applied to expressions |
| #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Hash)] |
| pub enum Operator { |
| /// Expressions are equal |
| Eq, |
| /// Expressions are not equal |
| NotEq, |
| /// Left side is smaller than right side |
| Lt, |
| /// Left side is smaller or equal to right side |
| LtEq, |
| /// Left side is greater than right side |
| Gt, |
| /// Left side is greater or equal to right side |
| GtEq, |
| /// Addition |
| Plus, |
| /// Subtraction |
| Minus, |
| /// Multiplication operator, like `*` |
| Multiply, |
| /// Division operator, like `/` |
| Divide, |
| /// Remainder operator, like `%` |
| Modulo, |
| /// Logical AND, like `&&` |
| And, |
| /// Logical OR, like `||` |
| Or, |
| /// `IS DISTINCT FROM` (see [`distinct`]) |
| /// |
| /// [`distinct`]: arrow::compute::kernels::cmp::distinct |
| IsDistinctFrom, |
| /// `IS NOT DISTINCT FROM` (see [`not_distinct`]) |
| /// |
| /// [`not_distinct`]: arrow::compute::kernels::cmp::not_distinct |
| IsNotDistinctFrom, |
| /// Case sensitive regex match |
| RegexMatch, |
| /// Case insensitive regex match |
| RegexIMatch, |
| /// Case sensitive regex not match |
| RegexNotMatch, |
| /// Case insensitive regex not match |
| RegexNotIMatch, |
| /// Case sensitive pattern match |
| LikeMatch, |
| /// Case insensitive pattern match |
| ILikeMatch, |
| /// Case sensitive pattern not match |
| NotLikeMatch, |
| /// Case insensitive pattern not match |
| NotILikeMatch, |
| /// Bitwise and, like `&` |
| BitwiseAnd, |
| /// Bitwise or, like `|` |
| BitwiseOr, |
| /// Bitwise xor, such as `^` in MySQL or `#` in PostgreSQL |
| BitwiseXor, |
| /// Bitwise right, like `>>` |
| BitwiseShiftRight, |
| /// Bitwise left, like `<<` |
| BitwiseShiftLeft, |
| /// String concat |
| StringConcat, |
| /// At arrow, like `@>`. |
| /// |
| /// Currently only supported to be used with lists: |
| /// ```sql |
| /// select [1,3] <@ [1,2,3] |
| /// ``` |
| AtArrow, |
| /// Arrow at, like `<@`. |
| /// |
| /// Currently only supported to be used with lists: |
| /// ```sql |
| /// select [1,2,3] @> [1,3] |
| /// ``` |
| ArrowAt, |
| /// Arrow, like `->`. |
| /// |
| /// Not implemented in DataFusion yet. |
| Arrow, |
| /// Long arrow, like `->>` |
| /// |
| /// Not implemented in DataFusion yet. |
| LongArrow, |
| /// Hash arrow, like `#>` |
| /// |
| /// Not implemented in DataFusion yet. |
| HashArrow, |
| /// Hash long arrow, like `#>>` |
| /// |
| /// Not implemented in DataFusion yet. |
| HashLongArrow, |
| /// At at, like `@@` |
| /// |
| /// Not implemented in DataFusion yet. |
| AtAt, |
| /// Integer division operator, like `DIV` from MySQL or `//` from DuckDB |
| /// |
| /// Not implemented in DataFusion yet. |
| IntegerDivide, |
| /// Hash Minis, like `#-` |
| /// |
| /// Not implemented in DataFusion yet. |
| HashMinus, |
| /// At question, like `@?` |
| /// |
| /// Not implemented in DataFusion yet. |
| AtQuestion, |
| /// Question, like `?` |
| /// |
| /// Not implemented in DataFusion yet. |
| Question, |
| /// Question and, like `?&` |
| /// |
| /// Not implemented in DataFusion yet. |
| QuestionAnd, |
| /// Question pipe, like `?|` |
| /// |
| /// Not implemented in DataFusion yet. |
| QuestionPipe, |
| } |
| |
| impl Operator { |
| /// If the operator can be negated, return the negated operator |
| /// otherwise return None |
| pub fn negate(&self) -> Option<Operator> { |
| match self { |
| Operator::Eq => Some(Operator::NotEq), |
| Operator::NotEq => Some(Operator::Eq), |
| Operator::Lt => Some(Operator::GtEq), |
| Operator::LtEq => Some(Operator::Gt), |
| Operator::Gt => Some(Operator::LtEq), |
| Operator::GtEq => Some(Operator::Lt), |
| Operator::IsDistinctFrom => Some(Operator::IsNotDistinctFrom), |
| Operator::IsNotDistinctFrom => Some(Operator::IsDistinctFrom), |
| Operator::LikeMatch => Some(Operator::NotLikeMatch), |
| Operator::ILikeMatch => Some(Operator::NotILikeMatch), |
| Operator::NotLikeMatch => Some(Operator::LikeMatch), |
| Operator::NotILikeMatch => Some(Operator::ILikeMatch), |
| Operator::Plus |
| | Operator::Minus |
| | Operator::Multiply |
| | Operator::Divide |
| | Operator::Modulo |
| | Operator::And |
| | Operator::Or |
| | Operator::RegexMatch |
| | Operator::RegexIMatch |
| | Operator::RegexNotMatch |
| | Operator::RegexNotIMatch |
| | Operator::BitwiseAnd |
| | Operator::BitwiseOr |
| | Operator::BitwiseXor |
| | Operator::BitwiseShiftRight |
| | Operator::BitwiseShiftLeft |
| | Operator::StringConcat |
| | Operator::AtArrow |
| | Operator::ArrowAt |
| | Operator::Arrow |
| | Operator::LongArrow |
| | Operator::HashArrow |
| | Operator::HashLongArrow |
| | Operator::AtAt |
| | Operator::IntegerDivide |
| | Operator::HashMinus |
| | Operator::AtQuestion |
| | Operator::Question |
| | Operator::QuestionAnd |
| | Operator::QuestionPipe => None, |
| } |
| } |
| |
| /// Return true if the operator is a numerical operator. |
| /// |
| /// For example, 'Binary(a, +, b)' would be a numerical expression. |
| /// PostgresSQL concept: <https://www.postgresql.org/docs/7.0/operators2198.htm> |
| pub fn is_numerical_operators(&self) -> bool { |
| matches!( |
| self, |
| Operator::Plus |
| | Operator::Minus |
| | Operator::Multiply |
| | Operator::Divide |
| | Operator::Modulo |
| ) |
| } |
| |
| /// Return true if the comparison operator can be used in interval arithmetic and constraint |
| /// propagation |
| /// |
| /// For example, 'Binary(a, >, b)' expression supports propagation. |
| pub fn supports_propagation(&self) -> bool { |
| matches!( |
| self, |
| Operator::Eq |
| | Operator::NotEq |
| | Operator::Lt |
| | Operator::LtEq |
| | Operator::Gt |
| | Operator::GtEq |
| | Operator::IsDistinctFrom |
| | Operator::IsNotDistinctFrom |
| | Operator::RegexMatch |
| | Operator::RegexIMatch |
| | Operator::RegexNotMatch |
| | Operator::RegexNotIMatch |
| ) |
| } |
| |
| /// Return true if the operator is a logic operator. |
| /// |
| /// For example, 'Binary(Binary(a, >, b), AND, Binary(a, <, b + 3))' would |
| /// be a logical expression. |
| pub fn is_logic_operator(&self) -> bool { |
| matches!(self, Operator::And | Operator::Or) |
| } |
| |
| /// Return the operator where swapping lhs and rhs wouldn't change the result. |
| /// |
| /// For example `Binary(50, >=, a)` could also be represented as `Binary(a, <=, 50)`. |
| pub fn swap(&self) -> Option<Operator> { |
| match self { |
| Operator::Eq => Some(Operator::Eq), |
| Operator::NotEq => Some(Operator::NotEq), |
| Operator::Lt => Some(Operator::Gt), |
| Operator::LtEq => Some(Operator::GtEq), |
| Operator::Gt => Some(Operator::Lt), |
| Operator::GtEq => Some(Operator::LtEq), |
| Operator::AtArrow => Some(Operator::ArrowAt), |
| Operator::ArrowAt => Some(Operator::AtArrow), |
| Operator::IsDistinctFrom |
| | Operator::IsNotDistinctFrom |
| | Operator::Plus |
| | Operator::Minus |
| | Operator::Multiply |
| | Operator::Divide |
| | Operator::Modulo |
| | Operator::And |
| | Operator::Or |
| | Operator::RegexMatch |
| | Operator::RegexIMatch |
| | Operator::RegexNotMatch |
| | Operator::RegexNotIMatch |
| | Operator::LikeMatch |
| | Operator::ILikeMatch |
| | Operator::NotLikeMatch |
| | Operator::NotILikeMatch |
| | Operator::BitwiseAnd |
| | Operator::BitwiseOr |
| | Operator::BitwiseXor |
| | Operator::BitwiseShiftRight |
| | Operator::BitwiseShiftLeft |
| | Operator::StringConcat |
| | Operator::Arrow |
| | Operator::LongArrow |
| | Operator::HashArrow |
| | Operator::HashLongArrow |
| | Operator::AtAt |
| | Operator::IntegerDivide |
| | Operator::HashMinus |
| | Operator::AtQuestion |
| | Operator::Question |
| | Operator::QuestionAnd |
| | Operator::QuestionPipe => None, |
| } |
| } |
| |
| /// Get the operator precedence |
| /// use <https://www.postgresql.org/docs/7.2/sql-precedence.html> as a reference |
| pub fn precedence(&self) -> u8 { |
| match self { |
| Operator::Or => 5, |
| Operator::And => 10, |
| Operator::Eq | Operator::NotEq | Operator::LtEq | Operator::GtEq => 15, |
| Operator::Lt | Operator::Gt => 20, |
| Operator::LikeMatch |
| | Operator::NotLikeMatch |
| | Operator::ILikeMatch |
| | Operator::NotILikeMatch => 25, |
| Operator::IsDistinctFrom |
| | Operator::IsNotDistinctFrom |
| | Operator::RegexMatch |
| | Operator::RegexNotMatch |
| | Operator::RegexIMatch |
| | Operator::RegexNotIMatch |
| | Operator::BitwiseAnd |
| | Operator::BitwiseOr |
| | Operator::BitwiseShiftLeft |
| | Operator::BitwiseShiftRight |
| | Operator::BitwiseXor |
| | Operator::StringConcat |
| | Operator::AtArrow |
| | Operator::ArrowAt |
| | Operator::Arrow |
| | Operator::LongArrow |
| | Operator::HashArrow |
| | Operator::HashLongArrow |
| | Operator::AtAt |
| | Operator::IntegerDivide |
| | Operator::HashMinus |
| | Operator::AtQuestion |
| | Operator::Question |
| | Operator::QuestionAnd |
| | Operator::QuestionPipe => 30, |
| Operator::Plus | Operator::Minus => 40, |
| Operator::Multiply | Operator::Divide | Operator::Modulo => 45, |
| } |
| } |
| |
| /// Returns true if the `Expr::BinaryOperator` with this operator |
| /// is guaranteed to return null if either side is null. |
| pub fn returns_null_on_null(&self) -> bool { |
| match self { |
| Operator::Eq |
| | Operator::NotEq |
| | Operator::Lt |
| | Operator::LtEq |
| | Operator::Gt |
| | Operator::GtEq |
| | Operator::Plus |
| | Operator::Minus |
| | Operator::Multiply |
| | Operator::Divide |
| | Operator::Modulo |
| | Operator::RegexMatch |
| | Operator::RegexIMatch |
| | Operator::RegexNotMatch |
| | Operator::RegexNotIMatch |
| | Operator::LikeMatch |
| | Operator::ILikeMatch |
| | Operator::NotLikeMatch |
| | Operator::NotILikeMatch |
| | Operator::BitwiseAnd |
| | Operator::BitwiseOr |
| | Operator::BitwiseXor |
| | Operator::BitwiseShiftRight |
| | Operator::BitwiseShiftLeft |
| | Operator::AtArrow |
| | Operator::ArrowAt |
| | Operator::Arrow |
| | Operator::LongArrow |
| | Operator::HashArrow |
| | Operator::HashLongArrow |
| | Operator::AtAt |
| | Operator::IntegerDivide |
| | Operator::HashMinus |
| | Operator::AtQuestion |
| | Operator::Question |
| | Operator::QuestionAnd |
| | Operator::QuestionPipe => true, |
| |
| // E.g. `TRUE OR NULL` is `TRUE` |
| Operator::Or |
| // E.g. `FALSE AND NULL` is `FALSE` |
| | Operator::And |
| // IS DISTINCT FROM and IS NOT DISTINCT FROM always return a TRUE/FALSE value, never NULL |
| | Operator::IsDistinctFrom |
| | Operator::IsNotDistinctFrom |
| // DataFusion string concatenation operator treats NULL as an empty string |
| | Operator::StringConcat => false, |
| } |
| } |
| } |
| |
| impl fmt::Display for Operator { |
| fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
| let display = match &self { |
| Operator::Eq => "=", |
| Operator::NotEq => "!=", |
| Operator::Lt => "<", |
| Operator::LtEq => "<=", |
| Operator::Gt => ">", |
| Operator::GtEq => ">=", |
| Operator::Plus => "+", |
| Operator::Minus => "-", |
| Operator::Multiply => "*", |
| Operator::Divide => "/", |
| Operator::Modulo => "%", |
| Operator::And => "AND", |
| Operator::Or => "OR", |
| Operator::RegexMatch => "~", |
| Operator::RegexIMatch => "~*", |
| Operator::RegexNotMatch => "!~", |
| Operator::RegexNotIMatch => "!~*", |
| Operator::LikeMatch => "~~", |
| Operator::ILikeMatch => "~~*", |
| Operator::NotLikeMatch => "!~~", |
| Operator::NotILikeMatch => "!~~*", |
| Operator::IsDistinctFrom => "IS DISTINCT FROM", |
| Operator::IsNotDistinctFrom => "IS NOT DISTINCT FROM", |
| Operator::BitwiseAnd => "&", |
| Operator::BitwiseOr => "|", |
| Operator::BitwiseXor => "BIT_XOR", |
| Operator::BitwiseShiftRight => ">>", |
| Operator::BitwiseShiftLeft => "<<", |
| Operator::StringConcat => "||", |
| Operator::AtArrow => "@>", |
| Operator::ArrowAt => "<@", |
| Operator::Arrow => "->", |
| Operator::LongArrow => "->>", |
| Operator::HashArrow => "#>", |
| Operator::HashLongArrow => "#>>", |
| Operator::AtAt => "@@", |
| Operator::IntegerDivide => "DIV", |
| Operator::HashMinus => "#-", |
| Operator::AtQuestion => "@?", |
| Operator::Question => "?", |
| Operator::QuestionAnd => "?&", |
| Operator::QuestionPipe => "?|", |
| }; |
| write!(f, "{display}") |
| } |
| } |