partiql_value/
comparison.rs

1use crate::{util, Bag, List, Tuple};
2use crate::{Value, Variant};
3
4pub trait Comparable {
5    fn is_comparable_to(&self, rhs: &Self) -> bool;
6}
7
8impl Comparable for Value {
9    /// Returns true if and only if `self` is comparable to `rhs`
10    fn is_comparable_to(&self, rhs: &Self) -> bool {
11        match (self, rhs) {
12            // Null/Missing compare to anything
13            (Value::Missing | Value::Null, _)
14            | (_, Value::Missing | Value::Null)
15            // Everything compares to its own type
16            | (Value::Boolean(_), Value::Boolean(_))
17            | (Value::String(_), Value::String(_))
18            | (Value::Blob(_), Value::Blob(_))
19            | (Value::DateTime(_), Value::DateTime(_))
20            | (Value::List(_), Value::List(_))
21            | (Value::Bag(_), Value::Bag(_))
22            | (Value::Tuple(_), Value::Tuple(_))
23            // Numerics compare to each other
24            | (
25                Value::Integer(_) | Value::Real(_) | Value::Decimal(_),
26                Value::Integer(_) | Value::Real(_) | Value::Decimal(_),
27            )=> true,
28            (Value::Variant(lhs), Value::Variant(rhs)) => {
29                lhs.is_comparable_to(rhs)
30            }
31            (_, _) => false,
32        }
33    }
34}
35
36// `Value` `eq` and `neq` with Missing and Null propagation
37pub trait NullableEq {
38    fn eq(&self, rhs: &Self) -> Value;
39
40    fn neq(&self, rhs: &Self) -> Value {
41        let eq_result = NullableEq::eq(self, rhs);
42        match eq_result {
43            Value::Boolean(_) | Value::Null => !eq_result,
44            _ => Value::Missing,
45        }
46    }
47
48    /// `PartiQL's `eqg` is used to compare the internals of Lists, Bags, and Tuples.
49    ///
50    /// > The eqg, unlike the =, returns true when a NULL is compared to a NULL or a MISSING
51    /// > to a MISSING
52    fn eqg(&self, rhs: &Self) -> Value;
53
54    fn neqg(&self, rhs: &Self) -> Value {
55        let eqg_result = NullableEq::eqg(self, rhs);
56        match eqg_result {
57            Value::Boolean(_) | Value::Null => !eqg_result,
58            _ => Value::Missing,
59        }
60    }
61}
62
63/// A wrapper on [`T`] that specifies equality outcome for missing and null, and `NaN` values.
64#[derive(Eq, PartialEq, Debug)]
65pub struct EqualityValue<'a, const NULLS_EQUAL: bool, const NAN_EQUAL: bool, T>(pub &'a T);
66
67impl<const GROUP_NULLS: bool, const NAN_EQUAL: bool> NullableEq
68    for EqualityValue<'_, GROUP_NULLS, NAN_EQUAL, Value>
69{
70    #[inline(always)]
71    fn eq(&self, rhs: &Self) -> Value {
72        let wrap_list = EqualityValue::<'_, { GROUP_NULLS }, { NAN_EQUAL }, List>;
73        let wrap_bag = EqualityValue::<'_, { GROUP_NULLS }, { NAN_EQUAL }, Bag>;
74        let wrap_tuple = EqualityValue::<'_, { GROUP_NULLS }, { NAN_EQUAL }, Tuple>;
75        let wrap_var = EqualityValue::<'_, { GROUP_NULLS }, { NAN_EQUAL }, Variant>;
76        if GROUP_NULLS {
77            if let (Value::Missing | Value::Null, Value::Missing | Value::Null) = (self.0, rhs.0) {
78                return Value::Boolean(true);
79            }
80        } else if matches!(self.0, Value::Missing) || matches!(rhs.0, Value::Missing) {
81            return Value::Missing;
82        } else if matches!(self.0, Value::Null) || matches!(rhs.0, Value::Null) {
83            return Value::Null;
84        }
85
86        match (self.0, rhs.0) {
87            (Value::Integer(_), Value::Real(_)) => {
88                Value::from(&util::coerce_int_to_real(self.0) == rhs.0)
89            }
90            (Value::Integer(_), Value::Decimal(_)) => {
91                Value::from(&util::coerce_int_or_real_to_decimal(self.0) == rhs.0)
92            }
93            (Value::Real(_), Value::Decimal(_)) => {
94                Value::from(&util::coerce_int_or_real_to_decimal(self.0) == rhs.0)
95            }
96            (Value::Real(_), Value::Integer(_)) => {
97                Value::from(self.0 == &util::coerce_int_to_real(rhs.0))
98            }
99            (Value::Decimal(_), Value::Integer(_)) => {
100                Value::from(self.0 == &util::coerce_int_or_real_to_decimal(rhs.0))
101            }
102            (Value::Decimal(_), Value::Real(_)) => {
103                Value::from(self.0 == &util::coerce_int_or_real_to_decimal(rhs.0))
104            }
105            (Value::Real(l), Value::Real(r)) => {
106                if NAN_EQUAL && l.is_nan() && r.is_nan() {
107                    return Value::Boolean(true);
108                }
109                Value::from(l == r)
110            }
111            (Value::List(l), Value::List(r)) => NullableEq::eq(&wrap_list(l), &wrap_list(r)),
112            (Value::Bag(l), Value::Bag(r)) => NullableEq::eq(&wrap_bag(l), &wrap_bag(r)),
113            (Value::Tuple(l), Value::Tuple(r)) => NullableEq::eq(&wrap_tuple(l), &wrap_tuple(r)),
114            (Value::Variant(l), Value::Variant(r)) => NullableEq::eq(&wrap_var(l), &wrap_var(r)),
115            (_, _) => Value::from(self.0 == rhs.0),
116        }
117    }
118
119    #[inline(always)]
120    fn eqg(&self, rhs: &Self) -> Value {
121        let wrap = EqualityValue::<'_, true, { NAN_EQUAL }, _>;
122        NullableEq::eq(&wrap(self.0), &wrap(rhs.0))
123    }
124}
125
126// `Value` comparison with Missing and Null propagation
127pub trait NullableOrd {
128    type Output;
129
130    fn lt(&self, rhs: &Self) -> Self::Output;
131    fn gt(&self, rhs: &Self) -> Self::Output;
132    fn lteq(&self, rhs: &Self) -> Self::Output;
133    fn gteq(&self, rhs: &Self) -> Self::Output;
134}
135
136impl NullableOrd for Value {
137    type Output = Self;
138
139    fn lt(&self, rhs: &Self) -> Self::Output {
140        match (self, rhs) {
141            (Value::Missing, _) => Value::Missing,
142            (_, Value::Missing) => Value::Missing,
143            (Value::Null, _) => Value::Null,
144            (_, Value::Null) => Value::Null,
145            (_, _) => {
146                if self.is_comparable_to(rhs) {
147                    Value::from(self < rhs)
148                } else {
149                    Value::Missing
150                }
151            }
152        }
153    }
154
155    fn gt(&self, rhs: &Self) -> Self::Output {
156        match (self, rhs) {
157            (Value::Missing, _) => Value::Missing,
158            (_, Value::Missing) => Value::Missing,
159            (Value::Null, _) => Value::Null,
160            (_, Value::Null) => Value::Null,
161            (_, _) => {
162                if self.is_comparable_to(rhs) {
163                    Value::from(self > rhs)
164                } else {
165                    Value::Missing
166                }
167            }
168        }
169    }
170
171    fn lteq(&self, rhs: &Self) -> Self::Output {
172        match (self, rhs) {
173            (Value::Missing, _) => Value::Missing,
174            (_, Value::Missing) => Value::Missing,
175            (Value::Null, _) => Value::Null,
176            (_, Value::Null) => Value::Null,
177            (_, _) => {
178                if self.is_comparable_to(rhs) {
179                    Value::from(self <= rhs)
180                } else {
181                    Value::Missing
182                }
183            }
184        }
185    }
186
187    fn gteq(&self, rhs: &Self) -> Self::Output {
188        match (self, rhs) {
189            (Value::Missing, _) => Value::Missing,
190            (_, Value::Missing) => Value::Missing,
191            (Value::Null, _) => Value::Null,
192            (_, Value::Null) => Value::Null,
193            (_, _) => {
194                if self.is_comparable_to(rhs) {
195                    Value::from(self >= rhs)
196                } else {
197                    Value::Missing
198                }
199            }
200        }
201    }
202}