vortex_expr/exprs/
operators.rs

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