1use std::cmp::Ordering;
20
21use rudb_common::{Error, LogicalType, Result, Value};
22use rudb_vector::{Form, Vector};
23
24use crate::number::{approximate, integral};
25
26#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
28pub enum Comparison {
29 Equal,
31 NotEqual,
33 Less,
35 LessOrEqual,
37 Greater,
39 GreaterOrEqual,
41 DistinctFrom,
43 NotDistinctFrom,
45}
46
47impl Comparison {
48 #[must_use]
50 pub fn is_total(self) -> bool {
51 matches!(self, Self::DistinctFrom | Self::NotDistinctFrom)
52 }
53}
54
55pub fn compare(op: Comparison, left: &Vector, right: &Vector) -> Result<Vector> {
61 if left.len() != right.len() {
62 return Err(Error::internal(format!(
63 "a comparison of a {} row vector with a {} row one",
64 left.len(),
65 right.len()
66 )));
67 }
68 if left.form() == Form::Constant && right.form() == Form::Constant && !left.is_empty() {
69 let single = compare_values(op, &left.value_at(0), &right.value_at(0))?;
70 return Ok(Vector::constant(LogicalType::Boolean, single, left.len()));
71 }
72 let mut values = Vec::with_capacity(left.len());
73 for index in 0..left.len() {
74 values.push(compare_values(op, &left.value_at(index), &right.value_at(index))?);
75 }
76 Vector::from_values(LogicalType::Boolean, &values)
77}
78
79pub fn compare_values(op: Comparison, left: &Value, right: &Value) -> Result<Value> {
85 if op.is_total() {
86 let same = match (left.is_null(), right.is_null()) {
87 (true, true) => true,
88 (true, false) | (false, true) => false,
89 (false, false) => order(left, right)? == Ordering::Equal,
90 };
91 return Ok(Value::Boolean(match op {
92 Comparison::NotDistinctFrom => same,
93 _ => !same,
94 }));
95 }
96 if left.is_null() || right.is_null() {
97 return Ok(Value::Null);
98 }
99 let ordering = order(left, right)?;
100 let held = match op {
101 Comparison::Equal => ordering == Ordering::Equal,
102 Comparison::NotEqual => ordering != Ordering::Equal,
103 Comparison::Less => ordering == Ordering::Less,
104 Comparison::LessOrEqual => ordering != Ordering::Greater,
105 Comparison::Greater => ordering == Ordering::Greater,
106 Comparison::GreaterOrEqual => ordering != Ordering::Less,
107 Comparison::DistinctFrom | Comparison::NotDistinctFrom => {
108 return Err(Error::internal("a total comparison reached the ordered path"));
109 }
110 };
111 Ok(Value::Boolean(held))
112}
113
114pub fn order(left: &Value, right: &Value) -> Result<Ordering> {
125 match (left, right) {
126 (Value::Null, _) | (_, Value::Null) => {
127 Err(Error::internal("a null reached the ordering path"))
128 }
129 (Value::Boolean(a), Value::Boolean(b)) => Ok(a.cmp(b)),
130 (Value::Varchar(a), Value::Varchar(b)) => Ok(a.as_bytes().cmp(b.as_bytes())),
131 (Value::Blob(a), Value::Blob(b)) => Ok(a.cmp(b)),
132 (Value::Date(a), Value::Date(b)) => Ok(a.cmp(b)),
133 (Value::Time(a), Value::Time(b)) | (Value::Timestamp(a), Value::Timestamp(b)) => {
134 Ok(a.cmp(b))
135 }
136 (
137 Value::Interval { months: am, days: ad, micros: au },
138 Value::Interval { months: bm, days: bd, micros: bu },
139 ) => Ok((am, ad, au).cmp(&(bm, bd, bu))),
140 _ => numeric_order(left, right),
141 }
142}
143
144fn numeric_order(left: &Value, right: &Value) -> Result<Ordering> {
146 if let (Some(a), Some(b)) = (integral(left), integral(right)) {
147 return Ok(a.cmp(&b));
148 }
149 if let (
150 Value::Decimal { unscaled: a, scale: sa, .. },
151 Value::Decimal { unscaled: b, scale: sb, .. },
152 ) = (left, right)
153 {
154 if sa == sb {
155 return Ok(a.cmp(b));
156 }
157 }
158 match (approximate(left), approximate(right)) {
159 (Some(a), Some(b)) => Ok(float_order(a, b)),
160 _ => Err(Error::not_implemented(format!(
161 "comparing {} with {}",
162 left.logical_type(),
163 right.logical_type()
164 ))),
165 }
166}
167
168fn float_order(left: f64, right: f64) -> Ordering {
170 if left == right {
171 return Ordering::Equal;
172 }
173 match (left.is_nan(), right.is_nan()) {
174 (true, true) => Ordering::Equal,
175 (true, false) => Ordering::Greater,
176 (false, true) => Ordering::Less,
177 (false, false) => left.partial_cmp(&right).unwrap_or(Ordering::Equal),
178 }
179}
180
181pub fn order_with_nulls(left: &Value, right: &Value, nulls_first: bool) -> Result<Ordering> {
190 match (left.is_null(), right.is_null()) {
191 (true, true) => Ok(Ordering::Equal),
192 (true, false) => Ok(if nulls_first { Ordering::Less } else { Ordering::Greater }),
193 (false, true) => Ok(if nulls_first { Ordering::Greater } else { Ordering::Less }),
194 (false, false) => order(left, right),
195 }
196}
197
198#[cfg(test)]
199mod tests {
200 use super::*;
201
202 fn compared(op: Comparison, left: Value, right: Value) -> Value {
203 compare_values(op, &left, &right).expect("these types compare")
204 }
205
206 #[test]
207 fn an_ordinary_comparison_is_null_when_either_side_is() {
208 assert_eq!(compared(Comparison::Equal, Value::Integer(1), Value::Null), Value::Null);
209 assert_eq!(compared(Comparison::Less, Value::Null, Value::Integer(1)), Value::Null);
210 }
211
212 #[test]
213 fn a_total_comparison_is_never_null() {
214 assert_eq!(
215 compared(Comparison::NotDistinctFrom, Value::Null, Value::Null),
216 Value::Boolean(true)
217 );
218 assert_eq!(
219 compared(Comparison::NotDistinctFrom, Value::Integer(1), Value::Null),
220 Value::Boolean(false)
221 );
222 assert_eq!(
223 compared(Comparison::DistinctFrom, Value::Integer(1), Value::Null),
224 Value::Boolean(true)
225 );
226 }
227
228 #[test]
229 fn a_string_compares_by_bytes() {
230 assert_eq!(
231 compared(Comparison::Less, Value::Varchar("a".into()), Value::Varchar("b".into())),
232 Value::Boolean(true)
233 );
234 assert_eq!(
235 compared(Comparison::Less, Value::Varchar("Z".into()), Value::Varchar("a".into())),
236 Value::Boolean(true)
237 );
238 }
239
240 #[test]
243 fn two_nans_are_one_value_and_they_sort_above_the_numbers() {
244 assert_eq!(
245 compared(Comparison::Equal, Value::Double(f64::NAN), Value::Double(f64::NAN)),
246 Value::Boolean(true)
247 );
248 assert_eq!(
249 compared(Comparison::Greater, Value::Double(f64::NAN), Value::Double(1e300)),
250 Value::Boolean(true)
251 );
252 }
253
254 #[test]
255 fn zero_has_one_value_however_it_is_signed() {
256 assert_eq!(
257 compared(Comparison::Equal, Value::Double(0.0), Value::Double(-0.0)),
258 Value::Boolean(true)
259 );
260 }
261
262 #[test]
263 fn a_number_compares_the_same_however_it_is_stored() {
264 assert_eq!(
265 compared(Comparison::Equal, Value::Integer(3), Value::BigInt(3)),
266 Value::Boolean(true)
267 );
268 assert_eq!(
269 compared(Comparison::Less, Value::Integer(3), Value::Double(3.5)),
270 Value::Boolean(true)
271 );
272 }
273
274 #[test]
275 fn nulls_go_where_the_query_asked_for_them() {
276 assert_eq!(
277 order_with_nulls(&Value::Null, &Value::Integer(1), true).expect("orders"),
278 Ordering::Less
279 );
280 assert_eq!(
281 order_with_nulls(&Value::Null, &Value::Integer(1), false).expect("orders"),
282 Ordering::Greater
283 );
284 }
285
286 #[test]
287 fn two_constant_vectors_cost_one_comparison() {
288 let left = Vector::constant(LogicalType::Integer, Value::Integer(1), 512);
289 let right = Vector::constant(LogicalType::Integer, Value::Integer(2), 512);
290 let result = compare(Comparison::Less, &left, &right).expect("compares");
291 assert_eq!(result.form(), Form::Constant);
292 assert_eq!(result.value_at(500), Value::Boolean(true));
293 }
294
295 #[test]
296 fn a_comparison_of_two_vectors_is_one_answer_per_row() {
297 let left = Vector::from_values(
298 LogicalType::Integer,
299 &[Value::Integer(1), Value::Integer(5), Value::Null],
300 )
301 .expect("three rows");
302 let right = Vector::constant(LogicalType::Integer, Value::Integer(3), 3);
303 let result = compare(Comparison::Greater, &left, &right).expect("compares");
304 assert_eq!(result.value_at(0), Value::Boolean(false));
305 assert_eq!(result.value_at(1), Value::Boolean(true));
306 assert_eq!(result.value_at(2), Value::Null);
307 }
308
309 #[test]
310 fn two_vectors_of_different_lengths_are_caught() {
311 let left = Vector::constant(LogicalType::Integer, Value::Integer(1), 4);
312 let right = Vector::constant(LogicalType::Integer, Value::Integer(1), 5);
313 let error = compare(Comparison::Equal, &left, &right).expect_err("ragged");
314 assert!(error.message().contains("4 row vector"), "{error}");
315 }
316}