Skip to main content

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
40    Multiply,
41    /// Division
42    Divide,
43    /// Remainder
44    Modulo,
45    /// Logical AND
46    And,
47    /// Logical OR
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 concatenation, like `||`
84    StringConcat,
85    /// At arrow, like `@>`.
86    ///
87    /// Currently only supported to be used with lists:
88    /// ```sql
89    /// select [1,2,3] @> [1,3]
90    /// ```
91    AtArrow,
92    /// Arrow at, like `<@`.
93    ///
94    /// Currently only supported to be used with lists:
95    /// ```sql
96    /// select [1,3] <@ [1,2,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 Minus, 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    /// Colon operator, like `:`
144    ///
145    /// Not implemented in DataFusion yet.
146    Colon,
147}
148
149impl Operator {
150    /// If the operator can be negated, return the negated operator
151    /// otherwise return None
152    pub fn negate(&self) -> Option<Operator> {
153        match self {
154            Operator::Eq => Some(Operator::NotEq),
155            Operator::NotEq => Some(Operator::Eq),
156            Operator::Lt => Some(Operator::GtEq),
157            Operator::LtEq => Some(Operator::Gt),
158            Operator::Gt => Some(Operator::LtEq),
159            Operator::GtEq => Some(Operator::Lt),
160            Operator::IsDistinctFrom => Some(Operator::IsNotDistinctFrom),
161            Operator::IsNotDistinctFrom => Some(Operator::IsDistinctFrom),
162            Operator::LikeMatch => Some(Operator::NotLikeMatch),
163            Operator::ILikeMatch => Some(Operator::NotILikeMatch),
164            Operator::NotLikeMatch => Some(Operator::LikeMatch),
165            Operator::NotILikeMatch => Some(Operator::ILikeMatch),
166            Operator::RegexMatch => Some(Operator::RegexNotMatch),
167            Operator::RegexIMatch => Some(Operator::RegexNotIMatch),
168            Operator::RegexNotMatch => Some(Operator::RegexMatch),
169            Operator::RegexNotIMatch => Some(Operator::RegexIMatch),
170            Operator::Plus
171            | Operator::Minus
172            | Operator::Multiply
173            | Operator::Divide
174            | Operator::Modulo
175            | Operator::And
176            | Operator::Or
177            | Operator::BitwiseAnd
178            | Operator::BitwiseOr
179            | Operator::BitwiseXor
180            | Operator::BitwiseShiftRight
181            | Operator::BitwiseShiftLeft
182            | Operator::StringConcat
183            | Operator::AtArrow
184            | Operator::ArrowAt
185            | Operator::Arrow
186            | Operator::LongArrow
187            | Operator::HashArrow
188            | Operator::HashLongArrow
189            | Operator::AtAt
190            | Operator::IntegerDivide
191            | Operator::HashMinus
192            | Operator::AtQuestion
193            | Operator::Question
194            | Operator::QuestionAnd
195            | Operator::QuestionPipe
196            | Operator::Colon => None,
197        }
198    }
199
200    /// Return true if the operator is a numerical operator.
201    ///
202    /// For example, 'Binary(a, +, b)' would be a numerical expression.
203    /// PostgresSQL concept: <https://www.postgresql.org/docs/7.0/operators2198.htm>
204    pub fn is_numerical_operators(&self) -> bool {
205        matches!(
206            self,
207            Operator::Plus
208                | Operator::Minus
209                | Operator::Multiply
210                | Operator::Divide
211                | Operator::Modulo
212        )
213    }
214
215    /// Return true if the comparison operator can be used in interval arithmetic and constraint
216    /// propagation
217    ///
218    /// For example, 'Binary(a, >, b)' expression supports propagation.
219    pub fn supports_propagation(&self) -> bool {
220        matches!(
221            self,
222            Operator::Eq
223                | Operator::NotEq
224                | Operator::Lt
225                | Operator::LtEq
226                | Operator::Gt
227                | Operator::GtEq
228                | Operator::IsDistinctFrom
229                | Operator::IsNotDistinctFrom
230                | Operator::RegexMatch
231                | Operator::RegexIMatch
232                | Operator::RegexNotMatch
233                | Operator::RegexNotIMatch
234        )
235    }
236
237    /// Return true if the operator is a logic operator.
238    ///
239    /// For example, 'Binary(Binary(a, >, b), AND, Binary(a, <, b + 3))' would
240    /// be a logical expression.
241    pub fn is_logic_operator(&self) -> bool {
242        matches!(self, Operator::And | Operator::Or)
243    }
244
245    /// Return the operator where swapping lhs and rhs wouldn't change the result.
246    ///
247    /// For example `Binary(50, >=, a)` could also be represented as `Binary(a, <=, 50)`.
248    pub fn swap(&self) -> Option<Operator> {
249        match self {
250            Operator::Eq => Some(Operator::Eq),
251            Operator::NotEq => Some(Operator::NotEq),
252            Operator::Lt => Some(Operator::Gt),
253            Operator::LtEq => Some(Operator::GtEq),
254            Operator::Gt => Some(Operator::Lt),
255            Operator::GtEq => Some(Operator::LtEq),
256            Operator::AtArrow => Some(Operator::ArrowAt),
257            Operator::ArrowAt => Some(Operator::AtArrow),
258            Operator::IsDistinctFrom => Some(Operator::IsDistinctFrom),
259            Operator::IsNotDistinctFrom => Some(Operator::IsNotDistinctFrom),
260            Operator::Plus
261            | Operator::Minus
262            | Operator::Multiply
263            | Operator::Divide
264            | Operator::Modulo
265            | Operator::And
266            | Operator::Or
267            | Operator::RegexMatch
268            | Operator::RegexIMatch
269            | Operator::RegexNotMatch
270            | Operator::RegexNotIMatch
271            | Operator::LikeMatch
272            | Operator::ILikeMatch
273            | Operator::NotLikeMatch
274            | Operator::NotILikeMatch
275            | Operator::BitwiseAnd
276            | Operator::BitwiseOr
277            | Operator::BitwiseXor
278            | Operator::BitwiseShiftRight
279            | Operator::BitwiseShiftLeft
280            | Operator::StringConcat
281            | Operator::Arrow
282            | Operator::LongArrow
283            | Operator::HashArrow
284            | Operator::HashLongArrow
285            | Operator::AtAt
286            | Operator::IntegerDivide
287            | Operator::HashMinus
288            | Operator::AtQuestion
289            | Operator::Question
290            | Operator::QuestionAnd
291            | Operator::QuestionPipe
292            | Operator::Colon => None,
293        }
294    }
295
296    /// Get the operator precedence
297    /// use <https://www.postgresql.org/docs/7.2/sql-precedence.html> as a reference
298    pub fn precedence(&self) -> u8 {
299        match self {
300            Operator::Or => 5,
301            Operator::And => 10,
302            Operator::Eq | Operator::NotEq | Operator::LtEq | Operator::GtEq => 15,
303            Operator::Lt | Operator::Gt => 20,
304            Operator::LikeMatch
305            | Operator::NotLikeMatch
306            | Operator::ILikeMatch
307            | Operator::NotILikeMatch => 25,
308            Operator::IsDistinctFrom
309            | Operator::IsNotDistinctFrom
310            | Operator::RegexMatch
311            | Operator::RegexNotMatch
312            | Operator::RegexIMatch
313            | Operator::RegexNotIMatch
314            | Operator::BitwiseAnd
315            | Operator::BitwiseOr
316            | Operator::BitwiseShiftLeft
317            | Operator::BitwiseShiftRight
318            | Operator::BitwiseXor
319            | Operator::StringConcat
320            | Operator::AtArrow
321            | Operator::ArrowAt
322            | Operator::Arrow
323            | Operator::LongArrow
324            | Operator::HashArrow
325            | Operator::HashLongArrow
326            | Operator::AtAt
327            | Operator::IntegerDivide
328            | Operator::HashMinus
329            | Operator::AtQuestion
330            | Operator::Question
331            | Operator::QuestionAnd
332            | Operator::QuestionPipe
333            | Operator::Colon => 30,
334            Operator::Plus | Operator::Minus => 40,
335            Operator::Multiply | Operator::Divide | Operator::Modulo => 45,
336        }
337    }
338
339    /// Returns true if the `Expr::BinaryOperator` with this operator
340    /// is guaranteed to return null if either side is null.
341    pub fn returns_null_on_null(&self) -> bool {
342        match self {
343            Operator::Eq
344            | Operator::NotEq
345            | Operator::Lt
346            | Operator::LtEq
347            | Operator::Gt
348            | Operator::GtEq
349            | Operator::Plus
350            | Operator::Minus
351            | Operator::Multiply
352            | Operator::Divide
353            | Operator::Modulo
354            | Operator::RegexMatch
355            | Operator::RegexIMatch
356            | Operator::RegexNotMatch
357            | Operator::RegexNotIMatch
358            | Operator::LikeMatch
359            | Operator::ILikeMatch
360            | Operator::NotLikeMatch
361            | Operator::NotILikeMatch
362            | Operator::BitwiseAnd
363            | Operator::BitwiseOr
364            | Operator::BitwiseXor
365            | Operator::BitwiseShiftRight
366            | Operator::BitwiseShiftLeft
367            | Operator::AtArrow
368            | Operator::ArrowAt
369            | Operator::Arrow
370            | Operator::LongArrow
371            | Operator::HashArrow
372            | Operator::HashLongArrow
373            | Operator::AtAt
374            | Operator::IntegerDivide
375            | Operator::HashMinus
376            | Operator::AtQuestion
377            | Operator::Question
378            | Operator::QuestionAnd
379            | Operator::QuestionPipe
380            | Operator::Colon
381            | Operator::StringConcat => true,
382
383            // E.g. `TRUE OR NULL` is `TRUE`
384            Operator::Or
385            // E.g. `FALSE AND NULL` is `FALSE`
386            | Operator::And
387            // IS DISTINCT FROM and IS NOT DISTINCT FROM always return a TRUE/FALSE value, never NULL
388            | Operator::IsDistinctFrom
389            | Operator::IsNotDistinctFrom => false,
390        }
391    }
392
393    /// Parse an `Operator` from the string name `datafusion-proto` uses on the
394    /// wire (the `Debug` name of the variant, e.g. `"Eq"`).
395    ///
396    /// Returns `None` for names with no binary-operator counterpart. This is
397    /// the canonical proto-string mapping, shared by `datafusion-proto`
398    /// (logical plans) and `PhysicalExpr` decoders such as `BinaryExpr`, so the
399    /// mapping is not duplicated across crates.
400    pub fn from_proto_name(name: &str) -> Option<Operator> {
401        Some(match name {
402            "And" => Operator::And,
403            "Or" => Operator::Or,
404            "Eq" => Operator::Eq,
405            "NotEq" => Operator::NotEq,
406            "LtEq" => Operator::LtEq,
407            "Lt" => Operator::Lt,
408            "Gt" => Operator::Gt,
409            "GtEq" => Operator::GtEq,
410            "Plus" => Operator::Plus,
411            "Minus" => Operator::Minus,
412            "Multiply" => Operator::Multiply,
413            "Divide" => Operator::Divide,
414            "Modulo" => Operator::Modulo,
415            "IsDistinctFrom" => Operator::IsDistinctFrom,
416            "IsNotDistinctFrom" => Operator::IsNotDistinctFrom,
417            "BitwiseAnd" => Operator::BitwiseAnd,
418            "BitwiseOr" => Operator::BitwiseOr,
419            "BitwiseXor" => Operator::BitwiseXor,
420            "BitwiseShiftLeft" => Operator::BitwiseShiftLeft,
421            "BitwiseShiftRight" => Operator::BitwiseShiftRight,
422            "RegexIMatch" => Operator::RegexIMatch,
423            "RegexMatch" => Operator::RegexMatch,
424            "RegexNotIMatch" => Operator::RegexNotIMatch,
425            "RegexNotMatch" => Operator::RegexNotMatch,
426            "LikeMatch" => Operator::LikeMatch,
427            "ILikeMatch" => Operator::ILikeMatch,
428            "NotLikeMatch" => Operator::NotLikeMatch,
429            "NotILikeMatch" => Operator::NotILikeMatch,
430            "StringConcat" => Operator::StringConcat,
431            "AtArrow" => Operator::AtArrow,
432            "ArrowAt" => Operator::ArrowAt,
433            _ => return None,
434        })
435    }
436}
437
438impl fmt::Display for Operator {
439    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
440        let display = match &self {
441            Operator::Eq => "=",
442            Operator::NotEq => "!=",
443            Operator::Lt => "<",
444            Operator::LtEq => "<=",
445            Operator::Gt => ">",
446            Operator::GtEq => ">=",
447            Operator::Plus => "+",
448            Operator::Minus => "-",
449            Operator::Multiply => "*",
450            Operator::Divide => "/",
451            Operator::Modulo => "%",
452            Operator::And => "AND",
453            Operator::Or => "OR",
454            Operator::RegexMatch => "~",
455            Operator::RegexIMatch => "~*",
456            Operator::RegexNotMatch => "!~",
457            Operator::RegexNotIMatch => "!~*",
458            Operator::LikeMatch => "~~",
459            Operator::ILikeMatch => "~~*",
460            Operator::NotLikeMatch => "!~~",
461            Operator::NotILikeMatch => "!~~*",
462            Operator::IsDistinctFrom => "IS DISTINCT FROM",
463            Operator::IsNotDistinctFrom => "IS NOT DISTINCT FROM",
464            Operator::BitwiseAnd => "&",
465            Operator::BitwiseOr => "|",
466            Operator::BitwiseXor => "BIT_XOR",
467            Operator::BitwiseShiftRight => ">>",
468            Operator::BitwiseShiftLeft => "<<",
469            Operator::StringConcat => "||",
470            Operator::AtArrow => "@>",
471            Operator::ArrowAt => "<@",
472            Operator::Arrow => "->",
473            Operator::LongArrow => "->>",
474            Operator::HashArrow => "#>",
475            Operator::HashLongArrow => "#>>",
476            Operator::AtAt => "@@",
477            Operator::IntegerDivide => "DIV",
478            Operator::HashMinus => "#-",
479            Operator::AtQuestion => "@?",
480            Operator::Question => "?",
481            Operator::QuestionAnd => "?&",
482            Operator::QuestionPipe => "?|",
483            Operator::Colon => ":",
484        };
485        write!(f, "{display}")
486    }
487}