1use std::cmp::Ordering;
66
67use rudb_common::{Error, LogicalType, Result, Value};
68use rudb_vector::{Data, Form, StringColumn, Validity, Vector};
69
70use crate::fallback::{self, Kernel};
71use crate::number::{approximate, integral};
72use crate::shape::{first, identity, nulls_of, single};
73
74#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
76pub enum Comparison {
77 Equal,
79 NotEqual,
81 Less,
83 LessOrEqual,
85 Greater,
87 GreaterOrEqual,
89 DistinctFrom,
91 NotDistinctFrom,
93}
94
95impl Comparison {
96 #[must_use]
98 pub fn is_total(self) -> bool {
99 matches!(self, Self::DistinctFrom | Self::NotDistinctFrom)
100 }
101
102 #[must_use]
109 pub fn swapped(self) -> Self {
110 match self {
111 Self::Less => Self::Greater,
112 Self::LessOrEqual => Self::GreaterOrEqual,
113 Self::Greater => Self::Less,
114 Self::GreaterOrEqual => Self::LessOrEqual,
115 same => same,
116 }
117 }
118}
119
120pub fn compare(op: Comparison, left: &Vector, right: &Vector) -> Result<Vector> {
126 if left.len() != right.len() {
127 return Err(Error::internal(format!(
128 "a comparison of a {} row vector with a {} row one",
129 left.len(),
130 right.len()
131 )));
132 }
133 let len = left.len();
134 if left.form() == Form::Constant && right.form() == Form::Constant && len > 0 {
135 let single = compare_values(op, &left.value_at(0), &right.value_at(0))?;
136 return Ok(Vector::constant(LogicalType::Boolean, single, len));
137 }
138
139 let (left_valid, right_valid) = (nulls_of(left), nulls_of(right));
140 if !op.is_total()
144 && (left_valid == Validity::AllInvalid || right_valid == Validity::AllInvalid)
145 && len > 0
146 {
147 return boolean(vec![false; len], Validity::AllInvalid, len);
148 }
149
150 if let Some(answers) = specialized(op, left, right, &left_valid, &right_valid) {
151 let validity =
152 if op.is_total() { Validity::AllValid } else { left_valid.and(&right_valid, len) };
153 return boolean(blank_the_nulls(answers, &validity), validity, len);
154 }
155
156 fallback::record(Kernel::Compare, left.form(), right.form());
157 let mut values = Vec::with_capacity(len);
158 for index in 0..len {
161 values.push(compare_values(op, &left.value_at(index), &right.value_at(index))?);
162 }
163 Vector::from_values(LogicalType::Boolean, &values)
164}
165
166fn boolean(answers: Vec<bool>, validity: Validity, len: usize) -> Result<Vector> {
168 let validity = if len == 0 { Validity::AllValid } else { validity.normalize(len) };
172 Ok(Vector::flat(LogicalType::Boolean, Data::Bool(answers.into()))?.with_validity(validity))
173}
174
175fn blank_the_nulls(mut answers: Vec<bool>, validity: &Validity) -> Vec<bool> {
183 if let Validity::Mask(mask) = validity {
184 for (index, answer) in answers.iter_mut().enumerate() {
185 if !mask.get(index) {
186 *answer = false;
187 }
188 }
189 }
190 answers
191}
192
193fn specialized(
195 op: Comparison,
196 left: &Vector,
197 right: &Vector,
198 left_valid: &Validity,
199 right_valid: &Validity,
200) -> Option<Vec<bool>> {
201 if left.logical_type() != right.logical_type() {
205 return None;
206 }
207 let len = left.len();
208
209 if let (Some(one), Some(other)) = (left.data(), right.data()) {
210 return dispatch(op, len, one, identity, other, identity, left_valid, right_valid);
211 }
212 if let (Some(one), Some(value)) = (left.data(), right.constant_value()) {
213 let held = single(left.logical_type(), value)?;
214 let other = held.data()?;
215 return dispatch(op, len, one, identity, other, first, left_valid, right_valid);
216 }
217 if let (Some(value), Some(other)) = (left.constant_value(), right.data()) {
218 let held = single(right.logical_type(), value)?;
220 let one = held.data()?;
221 return dispatch(op.swapped(), len, other, identity, one, first, right_valid, left_valid);
222 }
223 if let (Some((codes, values)), Some(value)) = (left.dictionary_parts(), right.constant_value())
224 {
225 let one = values.data()?;
226 let held = single(left.logical_type(), value)?;
227 let other = held.data()?;
228 let at = |index: usize| codes[index] as usize;
229 return dispatch(op, len, one, at, other, first, left_valid, right_valid);
230 }
231 if let (Some(value), Some((codes, values))) = (left.constant_value(), right.dictionary_parts())
232 {
233 let other = values.data()?;
234 let held = single(right.logical_type(), value)?;
235 let one = held.data()?;
236 let at = |index: usize| codes[index] as usize;
237 return dispatch(op.swapped(), len, other, at, one, first, right_valid, left_valid);
238 }
239 if let (Some((codes, values)), Some(other)) = (left.dictionary_parts(), right.data()) {
245 let one = values.data()?;
246 let at = |index: usize| codes[index] as usize;
247 return dispatch(op, len, one, at, other, identity, left_valid, right_valid);
248 }
249 if let (Some(one), Some((codes, values))) = (left.data(), right.dictionary_parts()) {
250 let other = values.data()?;
251 let at = |index: usize| codes[index] as usize;
252 return dispatch(op.swapped(), len, other, at, one, identity, right_valid, left_valid);
253 }
254 None
255}
256
257#[expect(
263 clippy::too_many_arguments,
264 reason = "two sides with an index each, the operator, the length and two validities, all of \
265 which the loop needs and none of which is worth a struct that exists for one call"
266)]
267fn dispatch<L, R>(
268 op: Comparison,
269 len: usize,
270 left: &Data,
271 at_left: L,
272 right: &Data,
273 at_right: R,
274 left_valid: &Validity,
275 right_valid: &Validity,
276) -> Option<Vec<bool>>
277where
278 L: Fn(usize) -> usize,
279 R: Fn(usize) -> usize,
280{
281 macro_rules! layouts {
285 ($(($variant:ident, $native:ty, $zero:expr)),+ $(,)?) => {
286 match (left, right) {
287 $(
288 (Data::$variant(one), Data::$variant(other)) => Some(sweep(
289 op,
290 len,
291 |index| one[at_left(index)].cmp(&other[at_right(index)]),
292 left_valid,
293 right_valid,
294 )),
295 )+
296 (Data::Float32(one), Data::Float32(other)) => Some(sweep(
299 op,
300 len,
301 |index| {
302 float_order(
303 f64::from(one[at_left(index)]),
304 f64::from(other[at_right(index)]),
305 )
306 },
307 left_valid,
308 right_valid,
309 )),
310 (Data::Float64(one), Data::Float64(other)) => Some(sweep(
311 op,
312 len,
313 |index| float_order(one[at_left(index)], other[at_right(index)]),
314 left_valid,
315 right_valid,
316 )),
317 (Data::Varlen(one), Data::Varlen(other)) => Some(sweep(
318 op,
319 len,
320 |index| string_order(one, at_left(index), other, at_right(index)),
321 left_valid,
322 right_valid,
323 )),
324 _ => None,
325 }
326 };
327 }
328 rudb_vector::for_each_layout!(ordered, layouts)
329}
330
331fn string_order(
339 left: &StringColumn,
340 at_left: usize,
341 right: &StringColumn,
342 at_right: usize,
343) -> Ordering {
344 let (Some(one), Some(other)) = (left.views().get(at_left), right.views().get(at_right)) else {
345 return Ordering::Equal;
346 };
347 let (prefix, against) = (one.prefix(), other.prefix());
348 if prefix != against {
349 return prefix.cmp(&against);
350 }
351 let bytes = left.bytes(at_left).unwrap_or_default();
356 let against_bytes = right.bytes(at_right).unwrap_or_default();
357 bytes.cmp(against_bytes)
358}
359
360fn sweep<O>(
366 op: Comparison,
367 len: usize,
368 order_at: O,
369 left_valid: &Validity,
370 right_valid: &Validity,
371) -> Vec<bool>
372where
373 O: Fn(usize) -> Ordering,
374{
375 let mut answers = vec![false; len];
376 match op {
377 Comparison::Equal => fill(&mut answers, order_at, |o| o == Ordering::Equal),
378 Comparison::NotEqual => fill(&mut answers, order_at, |o| o != Ordering::Equal),
379 Comparison::Less => fill(&mut answers, order_at, |o| o == Ordering::Less),
380 Comparison::LessOrEqual => fill(&mut answers, order_at, |o| o != Ordering::Greater),
381 Comparison::Greater => fill(&mut answers, order_at, |o| o == Ordering::Greater),
382 Comparison::GreaterOrEqual => fill(&mut answers, order_at, |o| o != Ordering::Less),
383 Comparison::DistinctFrom => {
384 total(&mut answers, order_at, left_valid, right_valid);
385 for answer in &mut answers {
386 *answer = !*answer;
387 }
388 }
389 Comparison::NotDistinctFrom => total(&mut answers, order_at, left_valid, right_valid),
390 }
391 answers
392}
393
394#[inline]
396fn fill<O, H>(answers: &mut [bool], order_at: O, held: H)
397where
398 O: Fn(usize) -> Ordering,
399 H: Fn(Ordering) -> bool,
400{
401 for (index, answer) in answers.iter_mut().enumerate() {
402 *answer = held(order_at(index));
403 }
404}
405
406fn total<O>(answers: &mut [bool], order_at: O, left_valid: &Validity, right_valid: &Validity)
413where
414 O: Fn(usize) -> Ordering,
415{
416 if *left_valid == Validity::AllValid && *right_valid == Validity::AllValid {
417 fill(answers, order_at, |o| o == Ordering::Equal);
418 return;
419 }
420 for (index, answer) in answers.iter_mut().enumerate() {
421 *answer = match (left_valid.is_valid(index), right_valid.is_valid(index)) {
422 (true, true) => order_at(index) == Ordering::Equal,
423 (false, false) => true,
424 _ => false,
425 };
426 }
427}
428
429pub fn compare_values(op: Comparison, left: &Value, right: &Value) -> Result<Value> {
435 if op.is_total() {
436 let same = match (left.is_null(), right.is_null()) {
437 (true, true) => true,
438 (true, false) | (false, true) => false,
439 (false, false) => order(left, right)? == Ordering::Equal,
440 };
441 return Ok(Value::Boolean(match op {
442 Comparison::NotDistinctFrom => same,
443 _ => !same,
444 }));
445 }
446 if left.is_null() || right.is_null() {
447 return Ok(Value::Null);
448 }
449 let ordering = order(left, right)?;
450 let held = match op {
451 Comparison::Equal => ordering == Ordering::Equal,
452 Comparison::NotEqual => ordering != Ordering::Equal,
453 Comparison::Less => ordering == Ordering::Less,
454 Comparison::LessOrEqual => ordering != Ordering::Greater,
455 Comparison::Greater => ordering == Ordering::Greater,
456 Comparison::GreaterOrEqual => ordering != Ordering::Less,
457 Comparison::DistinctFrom | Comparison::NotDistinctFrom => {
458 return Err(Error::internal("a total comparison reached the ordered path"));
459 }
460 };
461 Ok(Value::Boolean(held))
462}
463
464pub fn order(left: &Value, right: &Value) -> Result<Ordering> {
475 match (left, right) {
476 (Value::Null, _) | (_, Value::Null) => {
477 Err(Error::internal("a null reached the ordering path"))
478 }
479 (Value::Boolean(a), Value::Boolean(b)) => Ok(a.cmp(b)),
480 (Value::Varchar(a), Value::Varchar(b)) => Ok(a.as_bytes().cmp(b.as_bytes())),
481 (Value::Blob(a), Value::Blob(b)) => Ok(a.cmp(b)),
482 (Value::Date(a), Value::Date(b)) => Ok(a.cmp(b)),
483 (Value::Time(a), Value::Time(b)) | (Value::Timestamp(a), Value::Timestamp(b)) => {
484 Ok(a.cmp(b))
485 }
486 (
487 Value::Interval { months: am, days: ad, micros: au },
488 Value::Interval { months: bm, days: bd, micros: bu },
489 ) => Ok((am, ad, au).cmp(&(bm, bd, bu))),
490 _ => numeric_order(left, right),
491 }
492}
493
494fn numeric_order(left: &Value, right: &Value) -> Result<Ordering> {
496 if let (Some(a), Some(b)) = (integral(left), integral(right)) {
497 return Ok(a.cmp(&b));
498 }
499 if let (
500 Value::Decimal { unscaled: a, scale: sa, .. },
501 Value::Decimal { unscaled: b, scale: sb, .. },
502 ) = (left, right)
503 {
504 if sa == sb {
505 return Ok(a.cmp(b));
506 }
507 }
508 match (approximate(left), approximate(right)) {
509 (Some(a), Some(b)) => Ok(float_order(a, b)),
510 _ => Err(Error::not_implemented(format!(
511 "comparing {} with {}",
512 left.logical_type(),
513 right.logical_type()
514 ))),
515 }
516}
517
518fn float_order(left: f64, right: f64) -> Ordering {
520 if left == right {
521 return Ordering::Equal;
522 }
523 match (left.is_nan(), right.is_nan()) {
524 (true, true) => Ordering::Equal,
525 (true, false) => Ordering::Greater,
526 (false, true) => Ordering::Less,
527 (false, false) => left.partial_cmp(&right).unwrap_or(Ordering::Equal),
528 }
529}
530
531pub fn order_with_nulls(left: &Value, right: &Value, nulls_first: bool) -> Result<Ordering> {
540 match (left.is_null(), right.is_null()) {
541 (true, true) => Ok(Ordering::Equal),
542 (true, false) => Ok(if nulls_first { Ordering::Less } else { Ordering::Greater }),
543 (false, true) => Ok(if nulls_first { Ordering::Greater } else { Ordering::Less }),
544 (false, false) => order(left, right),
545 }
546}
547
548#[cfg(test)]
549mod tests {
550 use super::*;
551
552 fn compared(op: Comparison, left: Value, right: Value) -> Value {
553 compare_values(op, &left, &right).expect("these types compare")
554 }
555
556 const EVERY: [Comparison; 8] = [
558 Comparison::Equal,
559 Comparison::NotEqual,
560 Comparison::Less,
561 Comparison::LessOrEqual,
562 Comparison::Greater,
563 Comparison::GreaterOrEqual,
564 Comparison::DistinctFrom,
565 Comparison::NotDistinctFrom,
566 ];
567
568 fn oracle(op: Comparison, left: &Vector, right: &Vector) -> Vector {
574 let values: Vec<Value> = (0..left.len())
575 .map(|index| {
576 compare_values(op, &left.value_at(index), &right.value_at(index))
577 .expect("the oracle is only asked about types that compare")
578 })
579 .collect();
580 Vector::from_values(LogicalType::Boolean, &values).expect("booleans")
581 }
582
583 fn agrees(op: Comparison, left: &Vector, right: &Vector) {
587 let fast = compare(op, left, right).expect("compares");
588 let slow = oracle(op, left, right);
589 assert_eq!(fast, slow, "{op:?} on a {:?} against a {:?}", left.form(), right.form());
590 }
591
592 struct Rng(u64);
595
596 impl Rng {
597 fn next(&mut self) -> u64 {
598 self.0 ^= self.0 << 13;
599 self.0 ^= self.0 >> 7;
600 self.0 ^= self.0 << 17;
601 self.0
602 }
603
604 fn below(&mut self, bound: u64) -> u64 {
605 self.next() % bound
606 }
607 }
608
609 #[test]
610 fn an_ordinary_comparison_is_null_when_either_side_is() {
611 assert_eq!(compared(Comparison::Equal, Value::Integer(1), Value::Null), Value::Null);
612 assert_eq!(compared(Comparison::Less, Value::Null, Value::Integer(1)), Value::Null);
613 }
614
615 #[test]
616 fn a_total_comparison_is_never_null() {
617 assert_eq!(
618 compared(Comparison::NotDistinctFrom, Value::Null, Value::Null),
619 Value::Boolean(true)
620 );
621 assert_eq!(
622 compared(Comparison::NotDistinctFrom, Value::Integer(1), Value::Null),
623 Value::Boolean(false)
624 );
625 assert_eq!(
626 compared(Comparison::DistinctFrom, Value::Integer(1), Value::Null),
627 Value::Boolean(true)
628 );
629 }
630
631 #[test]
632 fn a_string_compares_by_bytes() {
633 assert_eq!(
634 compared(Comparison::Less, Value::Varchar("a".into()), Value::Varchar("b".into())),
635 Value::Boolean(true)
636 );
637 assert_eq!(
638 compared(Comparison::Less, Value::Varchar("Z".into()), Value::Varchar("a".into())),
639 Value::Boolean(true)
640 );
641 }
642
643 #[test]
646 fn two_nans_are_one_value_and_they_sort_above_the_numbers() {
647 assert_eq!(
648 compared(Comparison::Equal, Value::Double(f64::NAN), Value::Double(f64::NAN)),
649 Value::Boolean(true)
650 );
651 assert_eq!(
652 compared(Comparison::Greater, Value::Double(f64::NAN), Value::Double(1e300)),
653 Value::Boolean(true)
654 );
655 }
656
657 #[test]
658 fn zero_has_one_value_however_it_is_signed() {
659 assert_eq!(
660 compared(Comparison::Equal, Value::Double(0.0), Value::Double(-0.0)),
661 Value::Boolean(true)
662 );
663 }
664
665 #[test]
666 fn a_number_compares_the_same_however_it_is_stored() {
667 assert_eq!(
668 compared(Comparison::Equal, Value::Integer(3), Value::BigInt(3)),
669 Value::Boolean(true)
670 );
671 assert_eq!(
672 compared(Comparison::Less, Value::Integer(3), Value::Double(3.5)),
673 Value::Boolean(true)
674 );
675 }
676
677 #[test]
678 fn nulls_go_where_the_query_asked_for_them() {
679 assert_eq!(
680 order_with_nulls(&Value::Null, &Value::Integer(1), true).expect("orders"),
681 Ordering::Less
682 );
683 assert_eq!(
684 order_with_nulls(&Value::Null, &Value::Integer(1), false).expect("orders"),
685 Ordering::Greater
686 );
687 }
688
689 #[test]
690 fn two_constant_vectors_cost_one_comparison() {
691 let left = Vector::constant(LogicalType::Integer, Value::Integer(1), 512);
692 let right = Vector::constant(LogicalType::Integer, Value::Integer(2), 512);
693 let result = compare(Comparison::Less, &left, &right).expect("compares");
694 assert_eq!(result.form(), Form::Constant);
695 assert_eq!(result.value_at(500), Value::Boolean(true));
696 }
697
698 #[test]
699 fn a_comparison_of_two_vectors_is_one_answer_per_row() {
700 let left = Vector::from_values(
701 LogicalType::Integer,
702 &[Value::Integer(1), Value::Integer(5), Value::Null],
703 )
704 .expect("three rows");
705 let right = Vector::constant(LogicalType::Integer, Value::Integer(3), 3);
706 let result = compare(Comparison::Greater, &left, &right).expect("compares");
707 assert_eq!(result.value_at(0), Value::Boolean(false));
708 assert_eq!(result.value_at(1), Value::Boolean(true));
709 assert_eq!(result.value_at(2), Value::Null);
710 }
711
712 #[test]
713 fn two_vectors_of_different_lengths_are_caught() {
714 let left = Vector::constant(LogicalType::Integer, Value::Integer(1), 4);
715 let right = Vector::constant(LogicalType::Integer, Value::Integer(1), 5);
716 let error = compare(Comparison::Equal, &left, &right).expect_err("ragged");
717 assert!(error.message().contains("4 row vector"), "{error}");
718 }
719
720 #[test]
721 fn turning_a_comparison_around_is_what_the_other_side_would_have_said() {
722 for op in EVERY {
723 let left = Value::Integer(3);
724 let right = Value::Integer(7);
725 assert_eq!(
726 compare_values(op, &left, &right).expect("compares"),
727 compare_values(op.swapped(), &right, &left).expect("compares"),
728 "{op:?}"
729 );
730 }
731 }
732
733 #[test]
736 fn every_specialized_path_agrees_with_the_row_at_a_time_path() {
737 let mut rng = Rng(0x5eed_1234_9876_4321);
738 let types: [LogicalType; 10] = [
739 LogicalType::Boolean,
740 LogicalType::TinyInt,
741 LogicalType::SmallInt,
742 LogicalType::Integer,
743 LogicalType::BigInt,
744 LogicalType::HugeInt,
745 LogicalType::UInteger,
746 LogicalType::Float,
747 LogicalType::Double,
748 LogicalType::Varchar,
749 ];
750 for ty in &types {
751 for nulls in [0u64, 1, 3] {
752 let len = 37;
753 let make = |rng: &mut Rng| {
754 let values: Vec<Value> = (0..len)
755 .map(|_| {
756 if nulls > 0 && rng.below(nulls + 1) == 0 {
757 Value::Null
758 } else {
759 sample(ty, rng)
760 }
761 })
762 .collect();
763 Vector::from_values(ty.clone(), &values).expect("a flat vector")
764 };
765 let left = make(&mut rng);
766 let right = make(&mut rng);
767 let literal = sample(ty, &mut rng);
768 let constant = Vector::constant(ty.clone(), literal, len);
769 let null_constant = Vector::constant(ty.clone(), Value::Null, len);
770 let codes: Vec<u32> =
771 (0..len).map(|_| rng.below(left.len() as u64) as u32).collect();
772 let dictionary =
773 Vector::dictionary(codes, left.clone()).expect("codes are in range");
774
775 for op in EVERY {
776 agrees(op, &left, &right);
777 agrees(op, &left, &constant);
778 agrees(op, &constant, &left);
779 agrees(op, &left, &null_constant);
780 agrees(op, &null_constant, &left);
781 agrees(op, &dictionary, &constant);
782 agrees(op, &constant, &dictionary);
783 agrees(op, &dictionary, &right);
787 agrees(op, &right, &dictionary);
788 }
789 }
790 }
791 }
792
793 fn sample(ty: &LogicalType, rng: &mut Rng) -> Value {
795 match ty {
796 LogicalType::Boolean => Value::Boolean(rng.below(2) == 1),
797 LogicalType::TinyInt => Value::TinyInt(rng.below(7) as i8 - 3),
798 LogicalType::SmallInt => Value::SmallInt(rng.below(11) as i16 - 5),
799 LogicalType::Integer => Value::Integer(rng.below(9) as i32 - 4),
800 LogicalType::BigInt => Value::BigInt(rng.below(9) as i64 - 4),
801 LogicalType::HugeInt => Value::HugeInt(i128::from(rng.below(9)) - 4),
802 LogicalType::UInteger => Value::UInteger(rng.below(9) as u32),
803 LogicalType::Float => Value::Float(match rng.below(5) {
806 0 => f32::NAN,
807 1 => -0.0,
808 other => other as f32 - 2.0,
809 }),
810 LogicalType::Double => Value::Double(match rng.below(5) {
811 0 => f64::NAN,
812 1 => -0.0,
813 other => other as f64 - 2.0,
814 }),
815 LogicalType::Varchar => Value::Varchar(
818 match rng.below(6) {
819 0 => "",
820 1 => "ab",
821 2 => "abc",
822 3 => "abcdefghijkl",
823 4 => "abcdefghijklm",
824 _ => "abcdefghijklmnopqrstuvwxyz",
825 }
826 .to_owned(),
827 ),
828 other => panic!("the generator has no values for {other}"),
829 }
830 }
831
832 #[test]
836 fn prefix_order_is_byte_order_whenever_the_prefixes_differ() {
837 let words =
838 ["", "a", "ab", "abc", "abcd", "abcde", "b", "abcdefghijklmnop", "abcdefghijklmnoq"];
839 let mut column = StringColumn::new();
840 for word in words {
841 column.push(word);
842 }
843 for (i, one) in words.iter().enumerate() {
844 for (j, other) in words.iter().enumerate() {
845 assert_eq!(
846 string_order(&column, i, &column, j),
847 one.as_bytes().cmp(other.as_bytes()),
848 "{one:?} against {other:?}"
849 );
850 }
851 }
852 }
853
854 #[test]
857 fn a_dictionary_against_a_constant_reads_its_nulls_from_the_values() {
858 let values = Vector::from_values(
859 LogicalType::Integer,
860 &[Value::Integer(1), Value::Null, Value::Integer(9)],
861 )
862 .expect("three values");
863 let dictionary =
864 Vector::dictionary(vec![0, 1, 2, 1, 0], values).expect("codes are in range");
865 let constant = Vector::constant(LogicalType::Integer, Value::Integer(5), 5);
866 let result = compare(Comparison::Less, &dictionary, &constant).expect("compares");
867 assert_eq!(result.value_at(0), Value::Boolean(true));
868 assert_eq!(result.value_at(1), Value::Null);
869 assert_eq!(result.value_at(2), Value::Boolean(false));
870 assert_eq!(result.value_at(3), Value::Null);
871 assert_eq!(result.value_at(4), Value::Boolean(true));
872 }
873
874 #[test]
877 fn a_form_pair_with_no_loop_is_still_right_and_says_so() {
878 let _turn = fallback::TURN.lock().expect("no test panics while holding this");
881 let before = fallback::count(Kernel::Compare, Form::Sequence, Form::Flat);
882 let sequence = Vector::sequence(10, 1, 4);
883 let flat = Vector::from_values(
884 LogicalType::BigInt,
885 &[Value::BigInt(9), Value::BigInt(11), Value::BigInt(12), Value::Null],
886 )
887 .expect("four rows");
888 let result = compare(Comparison::Less, &sequence, &flat).expect("compares");
889 assert_eq!(result.value_at(0), Value::Boolean(false));
890 assert_eq!(result.value_at(1), Value::Boolean(false));
891 assert_eq!(result.value_at(2), Value::Boolean(false));
892 assert_eq!(result.value_at(3), Value::Null);
893 assert!(fallback::count(Kernel::Compare, Form::Sequence, Form::Flat) > before);
894 }
895
896 #[test]
906 fn a_second_level_of_codes_does_not_turn_the_loops_off() {
907 let _turn = fallback::TURN.lock().expect("no test panics while holding this");
908 let before = fallback::count(Kernel::Compare, Form::Dictionary, Form::Constant);
909 let values = Vector::from_values(
910 LogicalType::Integer,
911 &[Value::Integer(1), Value::Integer(5), Value::Integer(9)],
912 )
913 .expect("three rows");
914 let once = Vector::dictionary(vec![2, 1, 0], values).expect("codes are in range");
915 let twice = Vector::dictionary(vec![1, 2], once).expect("codes are in range");
916 let cut = Vector::constant(LogicalType::Integer, Value::Integer(4), 2);
917 let result = compare(Comparison::Greater, &twice, &cut).expect("compares");
918 assert_eq!(result.value_at(0), Value::Boolean(true));
919 assert_eq!(result.value_at(1), Value::Boolean(false));
920 assert_eq!(fallback::count(Kernel::Compare, Form::Dictionary, Form::Constant), before);
921 }
922
923 #[test]
927 fn a_side_that_is_entirely_null_answers_without_reading_the_other() {
928 let nulls = Vector::constant(LogicalType::Integer, Value::Null, 6);
929 let flat = Vector::from_values(
930 LogicalType::Integer,
931 &[
932 Value::Integer(1),
933 Value::Integer(2),
934 Value::Integer(3),
935 Value::Integer(4),
936 Value::Integer(5),
937 Value::Integer(6),
938 ],
939 )
940 .expect("six rows");
941 agrees(Comparison::Less, &nulls, &flat);
942 agrees(Comparison::Equal, &flat, &nulls);
943 assert_eq!(
944 compare(Comparison::Less, &nulls, &flat).expect("compares").validity(),
945 &Validity::AllInvalid
946 );
947 }
948
949 #[test]
952 fn an_empty_comparison_is_an_empty_answer() {
953 let left = Vector::from_values(LogicalType::Integer, &[]).expect("no rows");
954 let right = Vector::constant(LogicalType::Integer, Value::Integer(1), 0);
955 let result = compare(Comparison::Equal, &left, &right).expect("compares");
956 assert_eq!(result.len(), 0);
957 }
958}