vortex_expr/exprs/
operators.rs

1// SPDX-License-Identifier: Apache-2.0
2// SPDX-FileCopyrightText: Copyright the Vortex contributors
3
4use core::fmt;
5use std::fmt::{Display, Formatter};
6
7use vortex_array::compute;
8use vortex_error::{VortexError, VortexResult, vortex_bail};
9use vortex_proto::expr::binary_opts::BinaryOp;
10
11/// Equalities, inequalities, and boolean operations over possibly null values.
12///
13/// For most operations, if either side is null, the result is null.
14///
15/// The Boolean operators (And, Or) obey [Kleene (three-valued) logic](https://en.wikipedia.org/wiki/Three-valued_logic#Kleene_and_Priest_logics).
16#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Hash)]
17#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
18pub enum Operator {
19    /// Expressions are equal.
20    Eq,
21    /// Expressions are not equal.
22    NotEq,
23    /// Expression is greater than another
24    Gt,
25    /// Expression is greater or equal to another
26    Gte,
27    /// Expression is less than another
28    Lt,
29    /// Expression is less or equal to another
30    Lte,
31    /// Boolean AND (∧).
32    And,
33    /// Boolean OR (∨).
34    Or,
35    /// The sum of the arguments.
36    ///
37    /// Errs at runtime if the sum would overflow or underflow.
38    Add,
39    /// The difference between the arguments.
40    ///
41    /// Errs at runtime if the sum would overflow or underflow.
42    ///
43    /// The result is null at any index that either input is null.
44    Sub,
45}
46
47impl From<Operator> for i32 {
48    fn from(value: Operator) -> Self {
49        let op: BinaryOp = value.into();
50        op.into()
51    }
52}
53
54impl From<Operator> for BinaryOp {
55    fn from(value: Operator) -> Self {
56        match value {
57            Operator::Eq => BinaryOp::Eq,
58            Operator::NotEq => BinaryOp::NotEq,
59            Operator::Gt => BinaryOp::Gt,
60            Operator::Gte => BinaryOp::Gte,
61            Operator::Lt => BinaryOp::Lt,
62            Operator::Lte => BinaryOp::Lte,
63            Operator::And => BinaryOp::And,
64            Operator::Or => BinaryOp::Or,
65            Operator::Add => BinaryOp::Add,
66            Operator::Sub => BinaryOp::Sub,
67        }
68    }
69}
70
71impl TryFrom<i32> for Operator {
72    type Error = VortexError;
73
74    fn try_from(value: i32) -> Result<Self, Self::Error> {
75        Ok(BinaryOp::try_from(value)?.into())
76    }
77}
78
79impl From<BinaryOp> for Operator {
80    fn from(value: BinaryOp) -> Self {
81        match value {
82            BinaryOp::Eq => Operator::Eq,
83            BinaryOp::NotEq => Operator::NotEq,
84            BinaryOp::Gt => Operator::Gt,
85            BinaryOp::Gte => Operator::Gte,
86            BinaryOp::Lt => Operator::Lt,
87            BinaryOp::Lte => Operator::Lte,
88            BinaryOp::And => Operator::And,
89            BinaryOp::Or => Operator::Or,
90            BinaryOp::Add => Operator::Add,
91            BinaryOp::Sub => Operator::Sub,
92        }
93    }
94}
95
96impl Display for Operator {
97    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
98        let display = match &self {
99            Operator::Eq => "=",
100            Operator::NotEq => "!=",
101            Operator::Gt => ">",
102            Operator::Gte => ">=",
103            Operator::Lt => "<",
104            Operator::Lte => "<=",
105            Operator::And => "and",
106            Operator::Or => "or",
107            Operator::Add => "+",
108            Operator::Sub => "-",
109        };
110        Display::fmt(display, f)
111    }
112}
113
114impl Operator {
115    pub fn inverse(self) -> Option<Self> {
116        match self {
117            Operator::Eq => Some(Operator::NotEq),
118            Operator::NotEq => Some(Operator::Eq),
119            Operator::Gt => Some(Operator::Lte),
120            Operator::Gte => Some(Operator::Lt),
121            Operator::Lt => Some(Operator::Gte),
122            Operator::Lte => Some(Operator::Gt),
123            Operator::And | Operator::Or | Operator::Add | Operator::Sub => None,
124        }
125    }
126
127    pub fn logical_inverse(self) -> Option<Self> {
128        match self {
129            Operator::And => Some(Operator::Or),
130            Operator::Or => Some(Operator::And),
131            _ => None,
132        }
133    }
134
135    /// Change the sides of the operator, where changing lhs and rhs won't change the result of the operation
136    pub fn swap(self) -> Self {
137        match self {
138            Operator::Eq => Operator::Eq,
139            Operator::NotEq => Operator::NotEq,
140            Operator::Gt => Operator::Lt,
141            Operator::Gte => Operator::Lte,
142            Operator::Lt => Operator::Gt,
143            Operator::Lte => Operator::Gte,
144            Operator::And => Operator::And,
145            Operator::Or => Operator::Or,
146            Operator::Add => Operator::Add,
147            Operator::Sub => Operator::Sub,
148        }
149    }
150
151    pub fn maybe_cmp_operator(self) -> Option<compute::Operator> {
152        match self {
153            Operator::Eq => Some(compute::Operator::Eq),
154            Operator::NotEq => Some(compute::Operator::NotEq),
155            Operator::Lt => Some(compute::Operator::Lt),
156            Operator::Lte => Some(compute::Operator::Lte),
157            Operator::Gt => Some(compute::Operator::Gt),
158            Operator::Gte => Some(compute::Operator::Gte),
159            _ => None,
160        }
161    }
162}
163
164impl From<compute::Operator> for Operator {
165    fn from(cmp_operator: compute::Operator) -> Self {
166        match cmp_operator {
167            compute::Operator::Eq => Operator::Eq,
168            compute::Operator::NotEq => Operator::NotEq,
169            compute::Operator::Gt => Operator::Gt,
170            compute::Operator::Gte => Operator::Gte,
171            compute::Operator::Lt => Operator::Lt,
172            compute::Operator::Lte => Operator::Lte,
173        }
174    }
175}
176
177impl TryInto<compute::Operator> for Operator {
178    type Error = VortexError;
179
180    fn try_into(self) -> VortexResult<compute::Operator> {
181        Ok(match self {
182            Operator::Eq => compute::Operator::Eq,
183            Operator::NotEq => compute::Operator::NotEq,
184            Operator::Gt => compute::Operator::Gt,
185            Operator::Gte => compute::Operator::Gte,
186            Operator::Lt => compute::Operator::Lt,
187            Operator::Lte => compute::Operator::Lte,
188            _ => vortex_bail!("Not a compute operator: {}", self),
189        })
190    }
191}