datafusion_expr_common/
operator.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 std::fmt;
19
20/// Operators applied to expressions
21#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Hash)]
22pub enum Operator {
23    /// Expressions are equal
24    Eq,
25    /// Expressions are not equal
26    NotEq,
27    /// Left side is smaller than right side
28    Lt,
29    /// Left side is smaller or equal to right side
30    LtEq,
31    /// Left side is greater than right side
32    Gt,
33    /// Left side is greater or equal to right side
34    GtEq,
35    /// Addition
36    Plus,
37    /// Subtraction
38    Minus,
39    /// Multiplication operator, like `*`
40    Multiply,
41    /// Division operator, like `/`
42    Divide,
43    /// Remainder operator, like `%`
44    Modulo,
45    /// Logical AND, like `&&`
46    And,
47    /// Logical OR, like `||`
48    Or,
49    /// `IS DISTINCT FROM` (see [`distinct`])
50    ///
51    /// [`distinct`]: arrow::compute::kernels::cmp::distinct
52    IsDistinctFrom,
53    /// `IS NOT DISTINCT FROM` (see [`not_distinct`])
54    ///
55    /// [`not_distinct`]: arrow::compute::kernels::cmp::not_distinct
56    IsNotDistinctFrom,
57    /// Case sensitive regex match
58    RegexMatch,
59    /// Case insensitive regex match
60    RegexIMatch,
61    /// Case sensitive regex not match
62    RegexNotMatch,
63    /// Case insensitive regex not match
64    RegexNotIMatch,
65    /// Case sensitive pattern match
66    LikeMatch,
67    /// Case insensitive pattern match
68    ILikeMatch,
69    /// Case sensitive pattern not match
70    NotLikeMatch,
71    /// Case insensitive pattern not match
72    NotILikeMatch,
73    /// Bitwise and, like `&`
74    BitwiseAnd,
75    /// Bitwise or, like `|`
76    BitwiseOr,
77    /// Bitwise xor, such as `^` in MySQL or `#` in PostgreSQL
78    BitwiseXor,
79    /// Bitwise right, like `>>`
80    BitwiseShiftRight,
81    /// Bitwise left, like `<<`
82    BitwiseShiftLeft,
83    /// String concat
84    StringConcat,
85    /// At arrow, like `@>`.
86    ///
87    /// Currently only supported to be used with lists:
88    /// ```sql
89    /// select [1,3] <@ [1,2,3]
90    /// ```
91    AtArrow,
92    /// Arrow at, like `<@`.
93    ///
94    /// Currently only supported to be used with lists:
95    /// ```sql
96    /// select [1,2,3] @> [1,3]
97    /// ```
98    ArrowAt,
99    /// Arrow, like `->`.
100    ///
101    /// Not implemented in DataFusion yet.
102    Arrow,
103    /// Long arrow, like `->>`
104    ///
105    /// Not implemented in DataFusion yet.
106    LongArrow,
107    /// Hash arrow, like `#>`
108    ///
109    /// Not implemented in DataFusion yet.
110    HashArrow,
111    /// Hash long arrow, like `#>>`
112    ///
113    /// Not implemented in DataFusion yet.
114    HashLongArrow,
115    /// At at, like `@@`
116    ///
117    /// Not implemented in DataFusion yet.
118    AtAt,
119    /// Integer division operator, like `DIV` from MySQL or `//` from DuckDB
120    ///
121    /// Not implemented in DataFusion yet.
122    IntegerDivide,
123    /// Hash Minis, like `#-`
124    ///
125    /// Not implemented in DataFusion yet.
126    HashMinus,
127    /// At question, like `@?`
128    ///
129    /// Not implemented in DataFusion yet.
130    AtQuestion,
131    /// Question, like `?`
132    ///
133    /// Not implemented in DataFusion yet.
134    Question,
135    /// Question and, like `?&`
136    ///
137    /// Not implemented in DataFusion yet.
138    QuestionAnd,
139    /// Question pipe, like `?|`
140    ///
141    /// Not implemented in DataFusion yet.
142    QuestionPipe,
143}
144
145impl Operator {
146    /// If the operator can be negated, return the negated operator
147    /// otherwise return None
148    pub fn negate(&self) -> Option<Operator> {
149        match self {
150            Operator::Eq => Some(Operator::NotEq),
151            Operator::NotEq => Some(Operator::Eq),
152            Operator::Lt => Some(Operator::GtEq),
153            Operator::LtEq => Some(Operator::Gt),
154            Operator::Gt => Some(Operator::LtEq),
155            Operator::GtEq => Some(Operator::Lt),
156            Operator::IsDistinctFrom => Some(Operator::IsNotDistinctFrom),
157            Operator::IsNotDistinctFrom => Some(Operator::IsDistinctFrom),
158            Operator::LikeMatch => Some(Operator::NotLikeMatch),
159            Operator::ILikeMatch => Some(Operator::NotILikeMatch),
160            Operator::NotLikeMatch => Some(Operator::LikeMatch),
161            Operator::NotILikeMatch => Some(Operator::ILikeMatch),
162            Operator::Plus
163            | Operator::Minus
164            | Operator::Multiply
165            | Operator::Divide
166            | Operator::Modulo
167            | Operator::And
168            | Operator::Or
169            | Operator::RegexMatch
170            | Operator::RegexIMatch
171            | Operator::RegexNotMatch
172            | Operator::RegexNotIMatch
173            | Operator::BitwiseAnd
174            | Operator::BitwiseOr
175            | Operator::BitwiseXor
176            | Operator::BitwiseShiftRight
177            | Operator::BitwiseShiftLeft
178            | Operator::StringConcat
179            | Operator::AtArrow
180            | Operator::ArrowAt
181            | Operator::Arrow
182            | Operator::LongArrow
183            | Operator::HashArrow
184            | Operator::HashLongArrow
185            | Operator::AtAt
186            | Operator::IntegerDivide
187            | Operator::HashMinus
188            | Operator::AtQuestion
189            | Operator::Question
190            | Operator::QuestionAnd
191            | Operator::QuestionPipe => None,
192        }
193    }
194
195    /// Return true if the operator is a numerical operator.
196    ///
197    /// For example, 'Binary(a, +, b)' would be a numerical expression.
198    /// PostgresSQL concept: <https://www.postgresql.org/docs/7.0/operators2198.htm>
199    pub fn is_numerical_operators(&self) -> bool {
200        matches!(
201            self,
202            Operator::Plus
203                | Operator::Minus
204                | Operator::Multiply
205                | Operator::Divide
206                | Operator::Modulo
207        )
208    }
209
210    /// Return true if the comparison operator can be used in interval arithmetic and constraint
211    /// propagation
212    ///
213    /// For example, 'Binary(a, >, b)' expression supports propagation.
214    pub fn supports_propagation(&self) -> bool {
215        matches!(
216            self,
217            Operator::Eq
218                | Operator::NotEq
219                | Operator::Lt
220                | Operator::LtEq
221                | Operator::Gt
222                | Operator::GtEq
223                | Operator::IsDistinctFrom
224                | Operator::IsNotDistinctFrom
225                | Operator::RegexMatch
226                | Operator::RegexIMatch
227                | Operator::RegexNotMatch
228                | Operator::RegexNotIMatch
229        )
230    }
231
232    /// Return true if the operator is a logic operator.
233    ///
234    /// For example, 'Binary(Binary(a, >, b), AND, Binary(a, <, b + 3))' would
235    /// be a logical expression.
236    pub fn is_logic_operator(&self) -> bool {
237        matches!(self, Operator::And | Operator::Or)
238    }
239
240    /// Return the operator where swapping lhs and rhs wouldn't change the result.
241    ///
242    /// For example `Binary(50, >=, a)` could also be represented as `Binary(a, <=, 50)`.
243    pub fn swap(&self) -> Option<Operator> {
244        match self {
245            Operator::Eq => Some(Operator::Eq),
246            Operator::NotEq => Some(Operator::NotEq),
247            Operator::Lt => Some(Operator::Gt),
248            Operator::LtEq => Some(Operator::GtEq),
249            Operator::Gt => Some(Operator::Lt),
250            Operator::GtEq => Some(Operator::LtEq),
251            Operator::AtArrow => Some(Operator::ArrowAt),
252            Operator::ArrowAt => Some(Operator::AtArrow),
253            Operator::IsDistinctFrom
254            | Operator::IsNotDistinctFrom
255            | Operator::Plus
256            | Operator::Minus
257            | Operator::Multiply
258            | Operator::Divide
259            | Operator::Modulo
260            | Operator::And
261            | Operator::Or
262            | Operator::RegexMatch
263            | Operator::RegexIMatch
264            | Operator::RegexNotMatch
265            | Operator::RegexNotIMatch
266            | Operator::LikeMatch
267            | Operator::ILikeMatch
268            | Operator::NotLikeMatch
269            | Operator::NotILikeMatch
270            | Operator::BitwiseAnd
271            | Operator::BitwiseOr
272            | Operator::BitwiseXor
273            | Operator::BitwiseShiftRight
274            | Operator::BitwiseShiftLeft
275            | Operator::StringConcat
276            | Operator::Arrow
277            | Operator::LongArrow
278            | Operator::HashArrow
279            | Operator::HashLongArrow
280            | Operator::AtAt
281            | Operator::IntegerDivide
282            | Operator::HashMinus
283            | Operator::AtQuestion
284            | Operator::Question
285            | Operator::QuestionAnd
286            | Operator::QuestionPipe => None,
287        }
288    }
289
290    /// Get the operator precedence
291    /// use <https://www.postgresql.org/docs/7.2/sql-precedence.html> as a reference
292    pub fn precedence(&self) -> u8 {
293        match self {
294            Operator::Or => 5,
295            Operator::And => 10,
296            Operator::Eq | Operator::NotEq | Operator::LtEq | Operator::GtEq => 15,
297            Operator::Lt | Operator::Gt => 20,
298            Operator::LikeMatch
299            | Operator::NotLikeMatch
300            | Operator::ILikeMatch
301            | Operator::NotILikeMatch => 25,
302            Operator::IsDistinctFrom
303            | Operator::IsNotDistinctFrom
304            | Operator::RegexMatch
305            | Operator::RegexNotMatch
306            | Operator::RegexIMatch
307            | Operator::RegexNotIMatch
308            | Operator::BitwiseAnd
309            | Operator::BitwiseOr
310            | Operator::BitwiseShiftLeft
311            | Operator::BitwiseShiftRight
312            | Operator::BitwiseXor
313            | Operator::StringConcat
314            | Operator::AtArrow
315            | Operator::ArrowAt
316            | Operator::Arrow
317            | Operator::LongArrow
318            | Operator::HashArrow
319            | Operator::HashLongArrow
320            | Operator::AtAt
321            | Operator::IntegerDivide
322            | Operator::HashMinus
323            | Operator::AtQuestion
324            | Operator::Question
325            | Operator::QuestionAnd
326            | Operator::QuestionPipe => 30,
327            Operator::Plus | Operator::Minus => 40,
328            Operator::Multiply | Operator::Divide | Operator::Modulo => 45,
329        }
330    }
331
332    /// Returns true if the `Expr::BinaryOperator` with this operator
333    /// is guaranteed to return null if either side is null.
334    pub fn returns_null_on_null(&self) -> bool {
335        match self {
336            Operator::Eq
337            | Operator::NotEq
338            | Operator::Lt
339            | Operator::LtEq
340            | Operator::Gt
341            | Operator::GtEq
342            | Operator::Plus
343            | Operator::Minus
344            | Operator::Multiply
345            | Operator::Divide
346            | Operator::Modulo
347            | Operator::RegexMatch
348            | Operator::RegexIMatch
349            | Operator::RegexNotMatch
350            | Operator::RegexNotIMatch
351            | Operator::LikeMatch
352            | Operator::ILikeMatch
353            | Operator::NotLikeMatch
354            | Operator::NotILikeMatch
355            | Operator::BitwiseAnd
356            | Operator::BitwiseOr
357            | Operator::BitwiseXor
358            | Operator::BitwiseShiftRight
359            | Operator::BitwiseShiftLeft
360            | Operator::AtArrow
361            | Operator::ArrowAt
362            | Operator::Arrow
363            | Operator::LongArrow
364            | Operator::HashArrow
365            | Operator::HashLongArrow
366            | Operator::AtAt
367            | Operator::IntegerDivide
368            | Operator::HashMinus
369            | Operator::AtQuestion
370            | Operator::Question
371            | Operator::QuestionAnd
372            | Operator::QuestionPipe => true,
373
374            // E.g. `TRUE OR NULL` is `TRUE`
375            Operator::Or
376            // E.g. `FALSE AND NULL` is `FALSE`
377            | Operator::And
378            // IS DISTINCT FROM and IS NOT DISTINCT FROM always return a TRUE/FALSE value, never NULL
379            | Operator::IsDistinctFrom
380            | Operator::IsNotDistinctFrom
381            // DataFusion string concatenation operator treats NULL as an empty string
382            | Operator::StringConcat => false,
383        }
384    }
385}
386
387impl fmt::Display for Operator {
388    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
389        let display = match &self {
390            Operator::Eq => "=",
391            Operator::NotEq => "!=",
392            Operator::Lt => "<",
393            Operator::LtEq => "<=",
394            Operator::Gt => ">",
395            Operator::GtEq => ">=",
396            Operator::Plus => "+",
397            Operator::Minus => "-",
398            Operator::Multiply => "*",
399            Operator::Divide => "/",
400            Operator::Modulo => "%",
401            Operator::And => "AND",
402            Operator::Or => "OR",
403            Operator::RegexMatch => "~",
404            Operator::RegexIMatch => "~*",
405            Operator::RegexNotMatch => "!~",
406            Operator::RegexNotIMatch => "!~*",
407            Operator::LikeMatch => "~~",
408            Operator::ILikeMatch => "~~*",
409            Operator::NotLikeMatch => "!~~",
410            Operator::NotILikeMatch => "!~~*",
411            Operator::IsDistinctFrom => "IS DISTINCT FROM",
412            Operator::IsNotDistinctFrom => "IS NOT DISTINCT FROM",
413            Operator::BitwiseAnd => "&",
414            Operator::BitwiseOr => "|",
415            Operator::BitwiseXor => "BIT_XOR",
416            Operator::BitwiseShiftRight => ">>",
417            Operator::BitwiseShiftLeft => "<<",
418            Operator::StringConcat => "||",
419            Operator::AtArrow => "@>",
420            Operator::ArrowAt => "<@",
421            Operator::Arrow => "->",
422            Operator::LongArrow => "->>",
423            Operator::HashArrow => "#>",
424            Operator::HashLongArrow => "#>>",
425            Operator::AtAt => "@@",
426            Operator::IntegerDivide => "DIV",
427            Operator::HashMinus => "#-",
428            Operator::AtQuestion => "@?",
429            Operator::Question => "?",
430            Operator::QuestionAnd => "?&",
431            Operator::QuestionPipe => "?|",
432        };
433        write!(f, "{display}")
434    }
435}