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 comparison operator can be used in interval arithmetic and constraint
233 /// propagation
234 ///
235 /// For example, 'Binary(a, >, b)' expression supports propagation.
236 #[deprecated(since = "43.0.0", note = "please use `supports_propagation` instead")]
237 pub fn is_comparison_operator(&self) -> bool {
238 self.supports_propagation()
239 }
240
241 /// Return true if the operator is a logic operator.
242 ///
243 /// For example, 'Binary(Binary(a, >, b), AND, Binary(a, <, b + 3))' would
244 /// be a logical expression.
245 pub fn is_logic_operator(&self) -> bool {
246 matches!(self, Operator::And | Operator::Or)
247 }
248
249 /// Return the operator where swapping lhs and rhs wouldn't change the result.
250 ///
251 /// For example `Binary(50, >=, a)` could also be represented as `Binary(a, <=, 50)`.
252 pub fn swap(&self) -> Option<Operator> {
253 match self {
254 Operator::Eq => Some(Operator::Eq),
255 Operator::NotEq => Some(Operator::NotEq),
256 Operator::Lt => Some(Operator::Gt),
257 Operator::LtEq => Some(Operator::GtEq),
258 Operator::Gt => Some(Operator::Lt),
259 Operator::GtEq => Some(Operator::LtEq),
260 Operator::AtArrow => Some(Operator::ArrowAt),
261 Operator::ArrowAt => Some(Operator::AtArrow),
262 Operator::IsDistinctFrom
263 | Operator::IsNotDistinctFrom
264 | Operator::Plus
265 | Operator::Minus
266 | Operator::Multiply
267 | Operator::Divide
268 | Operator::Modulo
269 | Operator::And
270 | Operator::Or
271 | Operator::RegexMatch
272 | Operator::RegexIMatch
273 | Operator::RegexNotMatch
274 | Operator::RegexNotIMatch
275 | Operator::LikeMatch
276 | Operator::ILikeMatch
277 | Operator::NotLikeMatch
278 | Operator::NotILikeMatch
279 | Operator::BitwiseAnd
280 | Operator::BitwiseOr
281 | Operator::BitwiseXor
282 | Operator::BitwiseShiftRight
283 | Operator::BitwiseShiftLeft
284 | Operator::StringConcat
285 | Operator::Arrow
286 | Operator::LongArrow
287 | Operator::HashArrow
288 | Operator::HashLongArrow
289 | Operator::AtAt
290 | Operator::IntegerDivide
291 | Operator::HashMinus
292 | Operator::AtQuestion
293 | Operator::Question
294 | Operator::QuestionAnd
295 | Operator::QuestionPipe => None,
296 }
297 }
298
299 /// Get the operator precedence
300 /// use <https://www.postgresql.org/docs/7.2/sql-precedence.html> as a reference
301 pub fn precedence(&self) -> u8 {
302 match self {
303 Operator::Or => 5,
304 Operator::And => 10,
305 Operator::Eq | Operator::NotEq | Operator::LtEq | Operator::GtEq => 15,
306 Operator::Lt | Operator::Gt => 20,
307 Operator::LikeMatch
308 | Operator::NotLikeMatch
309 | Operator::ILikeMatch
310 | Operator::NotILikeMatch => 25,
311 Operator::IsDistinctFrom
312 | Operator::IsNotDistinctFrom
313 | Operator::RegexMatch
314 | Operator::RegexNotMatch
315 | Operator::RegexIMatch
316 | Operator::RegexNotIMatch
317 | Operator::BitwiseAnd
318 | Operator::BitwiseOr
319 | Operator::BitwiseShiftLeft
320 | Operator::BitwiseShiftRight
321 | Operator::BitwiseXor
322 | Operator::StringConcat
323 | Operator::AtArrow
324 | Operator::ArrowAt
325 | Operator::Arrow
326 | Operator::LongArrow
327 | Operator::HashArrow
328 | Operator::HashLongArrow
329 | Operator::AtAt
330 | Operator::IntegerDivide
331 | Operator::HashMinus
332 | Operator::AtQuestion
333 | Operator::Question
334 | Operator::QuestionAnd
335 | Operator::QuestionPipe => 30,
336 Operator::Plus | Operator::Minus => 40,
337 Operator::Multiply | Operator::Divide | Operator::Modulo => 45,
338 }
339 }
340}
341
342impl fmt::Display for Operator {
343 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
344 let display = match &self {
345 Operator::Eq => "=",
346 Operator::NotEq => "!=",
347 Operator::Lt => "<",
348 Operator::LtEq => "<=",
349 Operator::Gt => ">",
350 Operator::GtEq => ">=",
351 Operator::Plus => "+",
352 Operator::Minus => "-",
353 Operator::Multiply => "*",
354 Operator::Divide => "/",
355 Operator::Modulo => "%",
356 Operator::And => "AND",
357 Operator::Or => "OR",
358 Operator::RegexMatch => "~",
359 Operator::RegexIMatch => "~*",
360 Operator::RegexNotMatch => "!~",
361 Operator::RegexNotIMatch => "!~*",
362 Operator::LikeMatch => "~~",
363 Operator::ILikeMatch => "~~*",
364 Operator::NotLikeMatch => "!~~",
365 Operator::NotILikeMatch => "!~~*",
366 Operator::IsDistinctFrom => "IS DISTINCT FROM",
367 Operator::IsNotDistinctFrom => "IS NOT DISTINCT FROM",
368 Operator::BitwiseAnd => "&",
369 Operator::BitwiseOr => "|",
370 Operator::BitwiseXor => "BIT_XOR",
371 Operator::BitwiseShiftRight => ">>",
372 Operator::BitwiseShiftLeft => "<<",
373 Operator::StringConcat => "||",
374 Operator::AtArrow => "@>",
375 Operator::ArrowAt => "<@",
376 Operator::Arrow => "->",
377 Operator::LongArrow => "->>",
378 Operator::HashArrow => "#>",
379 Operator::HashLongArrow => "#>>",
380 Operator::AtAt => "@@",
381 Operator::IntegerDivide => "DIV",
382 Operator::HashMinus => "#-",
383 Operator::AtQuestion => "@?",
384 Operator::Question => "?",
385 Operator::QuestionAnd => "?&",
386 Operator::QuestionPipe => "?|",
387 };
388 write!(f, "{display}")
389 }
390}