Skip to main content

rudb_kernels/
compare.rs

1//! Comparing values, which is where SQL's three-valued logic actually lives.
2//!
3//! Six of the eight comparisons return null when either side is null, and the other two never do.
4//! That is not a detail: `WHERE a = b` drops a row where either is null and `WHERE a IS NOT
5//! DISTINCT FROM b` keeps the row where both are, and the binder produces the second one for `IS
6//! NULL` and for a `USING` join under some rewrites. One enum with the null rule attached to the
7//! variant is what stops that difference from being re-decided in every operator.
8//!
9//! The comparison enum here is this crate's own rather than `rudb_plan`'s, because the plan sits
10//! nine ranks above the kernels and a kernel that imports a plan type is a kernel that cannot be
11//! called from anywhere else. The executor maps one to the other, which is four lines it writes
12//! once.
13//!
14//! Float comparison is DuckDB's rather than IEEE's. Two NaNs are equal, NaN sorts above every
15//! number, and negative zero equals zero. IEEE says the first is false and that a NaN comparison is
16//! unordered, which would make `GROUP BY` over a column with a NaN in it produce a group nothing
17//! can ever find again and make a sort's result depend on the order the rows arrived in.
18//!
19//! # How the vectorized path is put together
20//!
21//! `spec/engine/03-data-plane.md` opens with this file as the example of what layer one is for.
22//! What it used to be was a loop from zero to length calling `value_at` on both sides, comparing
23//! two owned `Value`s and pushing into a `Vec<Value>` that a second pass then walked to pack into a
24//! vector. On a varchar column that is a heap allocation and a memcpy per row per side, plus a
25//! match on the operator inside the loop that the compiler has no way to hoist.
26//!
27//! What it is now is three decisions taken once per vector and then a loop that does one thing.
28//!
29//! The first decision is the form pair. Flat against flat, flat against constant and dictionary
30//! against constant each get a hand written path, because those three are what a filter on a scan
31//! actually produces. Constant on the left is the same code with the comparison turned around,
32//! which [`Comparison::swapped`] does, so there is one loop rather than two. Everything else falls
33//! through to the row at a time path, which is still here, is still correct, and now increments a
34//! counter in [`crate::fallback`] on the way past so that a combination worth specializing shows up
35//! as a number rather than as an opinion.
36//!
37//! The second decision is the physical type, which a macro turns into one loop per layout. Fifteen
38//! layouts by three form pairs by eight operators written out by hand is how a wrong answer gets
39//! in, and it is also four thousand lines nobody reads.
40//!
41//! The third decision is the operator, hoisted out of the loop once. The eight operators
42//! become eight monomorphized loops over the same ordering, each with a comparison against a
43//! constant `Ordering` in it, which is what makes the body a compare and a store.
44//!
45//! Validity gets its three cases used rather than collapsed. Two all valid sides skip the mask
46//! entirely and produce an all valid result. Either side all invalid, on one of the six ordinary
47//! comparisons, is every answer null without reading the data at all, which is a real case because
48//! it is what a constant `NULL` in a predicate is.
49//!
50//! A conjunct that is not the first one does not need every row. [`refine`] is the same three
51//! decisions with the output position mapped through the selection the conjuncts before it left, so
52//! every loop in this file serves the threaded path without being written twice. The mapping is a
53//! generic parameter rather than a function in a field, because an index mapping the compiler cannot
54//! see through is an indirect call in a loop that is otherwise three instructions.
55//!
56//! Strings resolve from the four byte prefix in the view. Two views whose prefixes differ are in
57//! that order, which holds because the payload past the end of a short string is zero and zero is
58//! the least byte, so prefix order is byte order whenever the prefixes are not equal. On `hits` the
59//! columns that carry the file are `URL` and `Referer`, and a filter on either of them is now a
60//! four byte compare on almost every row instead of a `String` being built to be thrown away.
61//!
62//! # What is still slow here
63//!
64//! The index into each side goes through a closure so that the same macro serves flat, constant and
65//! dictionary, which means the bounds check on each access survives. That is a known cost and it is
66//! next to nothing beside the allocation it replaced, but it is the reason this file will not hit
67//! the one nanosecond per row target on its own. The way out is a slice narrowed to the vector
68//! length on the identity path, and that wants the benchmark suite to exist first so that the
69//! change is a number rather than a belief.
70
71use std::cmp::Ordering;
72
73use rudb_common::{Error, LogicalType, Result, Value};
74use rudb_vector::{Data, Form, Selection, StringColumn, Validity, Vector};
75
76use crate::fallback::{self, Kernel};
77use crate::logic::is_true;
78use crate::number::{approximate, integral};
79use crate::shape::{first, identity, nulls_of, single};
80
81/// Which comparison.
82#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
83pub enum Comparison {
84    /// `=`, null if either side is null.
85    Equal,
86    /// `<>`, null if either side is null.
87    NotEqual,
88    /// `<`, null if either side is null.
89    Less,
90    /// `<=`, null if either side is null.
91    LessOrEqual,
92    /// `>`, null if either side is null.
93    Greater,
94    /// `>=`, null if either side is null.
95    GreaterOrEqual,
96    /// `IS DISTINCT FROM`, which is total and never null.
97    DistinctFrom,
98    /// `IS NOT DISTINCT FROM`, which is total and never null.
99    NotDistinctFrom,
100}
101
102impl Comparison {
103    /// Whether this comparison treats null as a value rather than as an absence.
104    #[must_use]
105    pub fn is_total(self) -> bool {
106        matches!(self, Self::DistinctFrom | Self::NotDistinctFrom)
107    }
108
109    /// The comparison that means the same thing with the two sides exchanged.
110    ///
111    /// This is what halves the number of specialized loops. A constant on the left against a
112    /// column on the right is the column against the constant with the inequality turned around,
113    /// and writing it that way means the column against constant loop is written once and tested
114    /// once rather than twice with a chance of the second one being subtly wrong.
115    #[must_use]
116    pub fn swapped(self) -> Self {
117        match self {
118            Self::Less => Self::Greater,
119            Self::LessOrEqual => Self::GreaterOrEqual,
120            Self::Greater => Self::Less,
121            Self::GreaterOrEqual => Self::LessOrEqual,
122            same => same,
123        }
124    }
125}
126
127/// Compares two vectors of the same length, producing a `BOOLEAN` vector.
128///
129/// # Errors
130///
131/// If the two sides are not the same length, or if the two types cannot be compared.
132pub fn compare(op: Comparison, left: &Vector, right: &Vector) -> Result<Vector> {
133    if left.len() != right.len() {
134        return Err(Error::internal(format!(
135            "a comparison of a {} row vector with a {} row one",
136            left.len(),
137            right.len()
138        )));
139    }
140    let len = left.len();
141    if left.form() == Form::Constant && right.form() == Form::Constant && len > 0 {
142        let single = compare_values(op, &left.value_at(0), &right.value_at(0))?;
143        return Ok(Vector::constant(LogicalType::Boolean, single, len));
144    }
145
146    let (left_valid, right_valid) = (nulls_of(left), nulls_of(right));
147    // Either side entirely null, on one of the six ordinary comparisons, is every answer null and
148    // the data is never read. This is not a corner case: a `NULL` literal in a predicate is a
149    // constant vector whose validity is exactly this, and so is a column the scan knows is empty.
150    if !op.is_total()
151        && (left_valid == Validity::AllInvalid || right_valid == Validity::AllInvalid)
152        && len > 0
153    {
154        return boolean(vec![false; len], Validity::AllInvalid, len);
155    }
156
157    if let Some(answers) = specialized(op, left, right, &left_valid, &right_valid, len, identity) {
158        let validity =
159            if op.is_total() { Validity::AllValid } else { left_valid.and(&right_valid, len) };
160        return boolean(blank_the_nulls(answers, &validity), validity, len);
161    }
162
163    fallback::record(Kernel::Compare, left.form(), right.form());
164    let mut values = Vec::with_capacity(len);
165    // row at a time: the path recorded on the line above, which exists to be correct for a pair of
166    // forms no specialization covers and counts itself so that pair shows up in the report.
167    for index in 0..len {
168        values.push(compare_values(op, &left.value_at(index), &right.value_at(index))?);
169    }
170    Vector::from_values(LogicalType::Boolean, &values)
171}
172
173/// The rows of `kept` the comparison also keeps.
174///
175/// This is [`compare`] for a conjunct that is not the first one. A filter with four conjuncts
176/// evaluated the obvious way runs all four over every row, so on TPC-H Q6, where each conjunct
177/// passes about a fifth of the rows and the four together pass about two percent, the last conjunct
178/// does fifty times the work it needs to. Handing it the rows the earlier ones kept is the whole
179/// difference, and it is a difference that grows with the number of conjuncts rather than washing
180/// out.
181///
182/// The answer is the rows of `kept`, in the order `kept` has them, for which the comparison is true.
183/// Null is not true, so a row whose either side is null is dropped on the six ordinary comparisons,
184/// which is the same rule [`crate::select::selection`] applies to a flag vector and the reason both
185/// of them are a kernel rather than a line at the call site.
186///
187/// # Errors
188///
189/// If the two sides are not the same length, or if a position in `kept` is past the end of them.
190pub fn refine(
191    op: Comparison,
192    left: &Vector,
193    right: &Vector,
194    kept: &Selection,
195) -> Result<Selection> {
196    if left.len() != right.len() {
197        return Err(Error::internal(format!(
198            "a comparison of a {} row vector with a {} row one",
199            left.len(),
200            right.len()
201        )));
202    }
203    let len = left.len();
204    // One vectorized pass over a run of `u32` before any of the loops below index with them, which
205    // is what turns a caller's mistake into this message rather than into a panic from inside a
206    // macro generated loop eight frames down.
207    if kept.indices().iter().any(|&row| row as usize >= len) {
208        return Err(Error::internal(format!("a selection past the end of a {len} row vector")));
209    }
210    if kept.is_empty() {
211        return Ok(Selection::empty());
212    }
213    if left.form() == Form::Constant && right.form() == Form::Constant {
214        let single = compare_values(op, &left.value_at(0), &right.value_at(0))?;
215        return Ok(if is_true(&single) { kept.clone() } else { Selection::empty() });
216    }
217
218    let (left_valid, right_valid) = (nulls_of(left), nulls_of(right));
219    if !op.is_total() && (left_valid == Validity::AllInvalid || right_valid == Validity::AllInvalid)
220    {
221        return Ok(Selection::empty());
222    }
223
224    let rows = kept.indices();
225    let map = |slot: usize| rows[slot] as usize;
226    if let Some(answers) = specialized(op, left, right, &left_valid, &right_valid, kept.len(), map)
227    {
228        // A total comparison has the nulls in the answer already, and two all valid sides have no
229        // null to drop, so both of those get the loop with nothing in it but the flag.
230        if op.is_total() || (left_valid == Validity::AllValid && right_valid == Validity::AllValid)
231        {
232            return Ok(narrowed(&answers, rows, |_| true));
233        }
234        // A bit at a time rather than a word at a time, which is the one place this path gives up
235        // something `compare` has. The rows are scattered by construction, so the two mask reads for
236        // one row are in different words as often as not and a word oriented loop would reread them.
237        return Ok(narrowed(&answers, rows, |slot| {
238            let row = rows[slot] as usize;
239            left_valid.is_valid(row) && right_valid.is_valid(row)
240        }));
241    }
242
243    fallback::record(Kernel::Compare, left.form(), right.form());
244    let mut out = Vec::with_capacity(kept.len());
245    // row at a time: the path recorded on the line above, for a pair of forms no specialization
246    // covers, reading only the rows the conjuncts before this one kept.
247    for &row in rows {
248        let index = row as usize;
249        if is_true(&compare_values(op, &left.value_at(index), &right.value_at(index))?) {
250            out.push(row);
251        }
252    }
253    Ok(Selection::from_indices(out))
254}
255
256/// The positions of `rows` whose answer is true and whose row is live, without a branch per row.
257///
258/// The same shape as the loop in `crate::select` and for the same reason: which rows a filter keeps is
259/// what the data decides rather than what the code does, so the branch is unpredictable by
260/// construction and a mispredict is worth more than the rest of the loop put together. Every slot
261/// writes its row at the current length and only a slot that is kept moves the length on.
262fn narrowed<L: Fn(usize) -> bool>(answers: &[bool], rows: &[u32], live: L) -> Selection {
263    let mut out = vec![0_u32; answers.len()];
264    let mut count = 0;
265    for (slot, &answer) in answers.iter().enumerate() {
266        out[count] = rows[slot];
267        // A single `&` rather than `&&`, because the short circuit would put back the branch.
268        count += usize::from(answer & live(slot));
269    }
270    out.truncate(count);
271    Selection::from_indices(out)
272}
273
274/// A `BOOLEAN` vector from a run of answers and the validity that says which of them count.
275fn boolean(answers: Vec<bool>, validity: Validity, len: usize) -> Result<Vector> {
276    // An empty vector has no null to record, and `Vector::from_values` normalizes the empty mask it
277    // builds to all valid, so saying the same here is what keeps an empty specialized result the
278    // same vector as the oracle's rather than merely the same length.
279    let validity = if len == 0 { Validity::AllValid } else { validity.normalize(len) };
280    Ok(Vector::flat(LogicalType::Boolean, Data::Bool(answers.into()))?.with_validity(validity))
281}
282
283/// A false in every position the validity says is null.
284///
285/// The comparison at a null position read whatever the zero the null was stored as compared to,
286/// which is a defined value and a meaningless one. Writing false there costs one pass over a run
287/// of bytes, only when there are nulls at all, and it buys the property that a specialized result
288/// is the same vector as the row at a time result rather than merely the same answer. A test that
289/// can compare two vectors with `==` is a much better test than one that has to walk them.
290fn blank_the_nulls(mut answers: Vec<bool>, validity: &Validity) -> Vec<bool> {
291    if let Validity::Mask(mask) = validity {
292        for (index, answer) in answers.iter_mut().enumerate() {
293            if !mask.get(index) {
294                *answer = false;
295            }
296        }
297    }
298    answers
299}
300
301/// The answers for a form pair this file has a loop for, or `None` to say it has not.
302///
303/// `map` turns an output position into the row of `left` and `right` it is the answer for, and
304/// `len` is how many output positions there are. [`compare`] passes [`identity`] and the length of
305/// its operands, which is every row. [`refine`] passes the selection it was handed and the size of
306/// it, which is how a conjunct after the first reads only the rows the conjuncts before it kept.
307///
308/// A generic parameter rather than a `fn(usize) -> usize` in a field, for the reason
309/// `spec/engine/03-data-plane.md` records as the first performance lesson of this layer: an index
310/// mapping the compiler cannot see through is an indirect call per row, and one of those in a loop
311/// that is otherwise three instructions is the whole loop.
312fn specialized<M>(
313    op: Comparison,
314    left: &Vector,
315    right: &Vector,
316    left_valid: &Validity,
317    right_valid: &Validity,
318    len: usize,
319    map: M,
320) -> Option<Vec<bool>>
321where
322    M: Fn(usize) -> usize + Copy,
323{
324    // Across representations is the fallback's job. `INTEGER` against `BIGINT` reaches the same
325    // answer through `numeric_order`, and a specialized loop that assumed the two runs had the same
326    // layout would compare a four byte column against an eight byte one position by position.
327    if left.logical_type() != right.logical_type() {
328        return None;
329    }
330
331    if let (Some(one), Some(other)) = (left.data(), right.data()) {
332        return dispatch(op, len, one, map, other, map, left_valid, right_valid, map);
333    }
334    if let (Some(one), Some(value)) = (left.data(), right.constant_value()) {
335        let held = single(left.logical_type(), value)?;
336        let other = held.data()?;
337        return dispatch(op, len, one, map, other, first, left_valid, right_valid, map);
338    }
339    if let (Some(value), Some(other)) = (left.constant_value(), right.data()) {
340        // The same loop with the comparison turned around, rather than a second loop.
341        let held = single(right.logical_type(), value)?;
342        let one = held.data()?;
343        return dispatch(op.swapped(), len, other, map, one, first, right_valid, left_valid, map);
344    }
345    if let (Some((codes, values)), Some(value)) = (left.dictionary_parts(), right.constant_value())
346    {
347        let one = values.data()?;
348        let held = single(left.logical_type(), value)?;
349        let other = held.data()?;
350        let at = |index: usize| codes[map(index)] as usize;
351        return dispatch(op, len, one, at, other, first, left_valid, right_valid, map);
352    }
353    if let (Some(value), Some((codes, values))) = (left.constant_value(), right.dictionary_parts())
354    {
355        let other = values.data()?;
356        let held = single(right.logical_type(), value)?;
357        let one = held.data()?;
358        let at = |index: usize| codes[map(index)] as usize;
359        return dispatch(op.swapped(), len, other, at, one, first, right_valid, left_valid, map);
360    }
361    // A dictionary against a flat column. This pair had no loop until the kernel table put a number
362    // on what that cost, which on `server3` was 83 nanoseconds a row against 1.2 for the dictionary
363    // against constant pair beside it, on the same data and the same operator. It is not a rare
364    // shape either: it is what a filtered column compared against an unfiltered one is, which is
365    // every conjunct after the first.
366    if let (Some((codes, values)), Some(other)) = (left.dictionary_parts(), right.data()) {
367        let one = values.data()?;
368        let at = |index: usize| codes[map(index)] as usize;
369        return dispatch(op, len, one, at, other, map, left_valid, right_valid, map);
370    }
371    if let (Some(one), Some((codes, values))) = (left.data(), right.dictionary_parts()) {
372        let other = values.data()?;
373        let at = |index: usize| codes[map(index)] as usize;
374        return dispatch(op.swapped(), len, other, at, one, map, right_valid, left_valid, map);
375    }
376    None
377}
378
379/// One loop per physical layout, generated rather than written out.
380///
381/// The two index closures are what let the same body serve flat against flat, a column against a
382/// constant and a dictionary against a constant. `identity` on both sides is the first, `first` on
383/// the right is the second, and the codes on the left are the third.
384#[expect(
385    clippy::too_many_arguments,
386    reason = "two sides with an index each, the operator, the length and two validities, all of \
387              which the loop needs and none of which is worth a struct that exists for one call"
388)]
389fn dispatch<L, R, V>(
390    op: Comparison,
391    len: usize,
392    left: &Data,
393    at_left: L,
394    right: &Data,
395    at_right: R,
396    left_valid: &Validity,
397    right_valid: &Validity,
398    at_valid: V,
399) -> Option<Vec<bool>>
400where
401    L: Fn(usize) -> usize,
402    R: Fn(usize) -> usize,
403    V: Fn(usize) -> usize,
404{
405    // A `Data::Interval` is in the ordered group because it is a tuple of three integers whose
406    // derived order is months, then days, then microseconds, which is exactly what `order` does for
407    // the same value by hand.
408    macro_rules! layouts {
409        ($(($variant:ident, $native:ty, $zero:expr)),+ $(,)?) => {
410            match (left, right) {
411                $(
412                    (Data::$variant(one), Data::$variant(other)) => Some(sweep(
413                        op,
414                        len,
415                        |index| one[at_left(index)].cmp(&other[at_right(index)]),
416                        left_valid,
417                        right_valid,
418                        &at_valid,
419                    )),
420                )+
421                // Floats have their own order, which is DuckDB's rather than IEEE's, and the
422                // widening on a `f32` is free because the comparison is against another `f32`.
423                (Data::Float32(one), Data::Float32(other)) => Some(sweep(
424                    op,
425                    len,
426                    |index| {
427                        float_order(
428                            f64::from(one[at_left(index)]),
429                            f64::from(other[at_right(index)]),
430                        )
431                    },
432                    left_valid,
433                    right_valid,
434                    &at_valid,
435                )),
436                (Data::Float64(one), Data::Float64(other)) => Some(sweep(
437                    op,
438                    len,
439                    |index| float_order(one[at_left(index)], other[at_right(index)]),
440                    left_valid,
441                    right_valid,
442                    &at_valid,
443                )),
444                (Data::Varlen(one), Data::Varlen(other)) => Some(sweep(
445                    op,
446                    len,
447                    |index| string_order(one, at_left(index), other, at_right(index)),
448                    left_valid,
449                    right_valid,
450                    &at_valid,
451                )),
452                _ => None,
453            }
454        };
455    }
456    rudb_vector::for_each_layout!(ordered, layouts)
457}
458
459/// Two strings in byte order, resolved from the four byte prefix where it can be.
460///
461/// The lemma this rests on is that prefix order is byte order whenever the two prefixes differ. A
462/// view pads a string shorter than four bytes with zeros, zero is the least byte, and byte order
463/// says a string is less than any string that extends it, so padding compares the same way the
464/// missing bytes would have. When the prefixes are equal the payload settles it, which for an
465/// inline string is the same sixteen bytes already loaded and for a long one is a block read.
466fn string_order(
467    left: &StringColumn,
468    at_left: usize,
469    right: &StringColumn,
470    at_right: usize,
471) -> Ordering {
472    let (Some(one), Some(other)) = (left.views().get(at_left), right.views().get(at_right)) else {
473        return Ordering::Equal;
474    };
475    let (prefix, against) = (one.prefix(), other.prefix());
476    if prefix != against {
477        return prefix.cmp(&against);
478    }
479    // Bytes rather than `StringColumn::get`, which validates UTF-8. Everything in a column was
480    // pushed from a `&str` so the validation cannot fail, and on a URL column, where every row
481    // shares the `http` prefix and the payload therefore decides every comparison, it was the
482    // larger half of the per row cost.
483    let bytes = left.bytes(at_left).unwrap_or_default();
484    let against_bytes = right.bytes(at_right).unwrap_or_default();
485    bytes.cmp(against_bytes)
486}
487
488/// The answers for one ordering, with the operator decided once rather than once per row.
489///
490/// This is where the match on the operator gets hoisted. Each arm calls a generic `fill` with a
491/// different predicate, so the compiler produces eight loops whose bodies are an ordering against a
492/// constant, rather than one loop with a branch table in it.
493fn sweep<O, V>(
494    op: Comparison,
495    len: usize,
496    order_at: O,
497    left_valid: &Validity,
498    right_valid: &Validity,
499    at_valid: V,
500) -> Vec<bool>
501where
502    O: Fn(usize) -> Ordering,
503    V: Fn(usize) -> usize,
504{
505    let mut answers = vec![false; len];
506    match op {
507        Comparison::Equal => fill(&mut answers, order_at, |o| o == Ordering::Equal),
508        Comparison::NotEqual => fill(&mut answers, order_at, |o| o != Ordering::Equal),
509        Comparison::Less => fill(&mut answers, order_at, |o| o == Ordering::Less),
510        Comparison::LessOrEqual => fill(&mut answers, order_at, |o| o != Ordering::Greater),
511        Comparison::Greater => fill(&mut answers, order_at, |o| o == Ordering::Greater),
512        Comparison::GreaterOrEqual => fill(&mut answers, order_at, |o| o != Ordering::Less),
513        Comparison::DistinctFrom => {
514            total(&mut answers, order_at, left_valid, right_valid, at_valid);
515            for answer in &mut answers {
516                *answer = !*answer;
517            }
518        }
519        Comparison::NotDistinctFrom => {
520            total(&mut answers, order_at, left_valid, right_valid, at_valid);
521        }
522    }
523    answers
524}
525
526/// One loop, one predicate, no branch on the operator.
527#[inline]
528fn fill<O, H>(answers: &mut [bool], order_at: O, held: H)
529where
530    O: Fn(usize) -> Ordering,
531    H: Fn(Ordering) -> bool,
532{
533    for (index, answer) in answers.iter_mut().enumerate() {
534        *answer = held(order_at(index));
535    }
536}
537
538/// `IS NOT DISTINCT FROM`, which reads validity as data rather than as an absence.
539///
540/// Two nulls are the same value here and a null against anything else is not, which is the whole
541/// difference between this and `=`. The all valid case is checked once so that the common shape,
542/// which is a total comparison inside a join on columns that happen not to be nullable, does not
543/// pay for two validity lookups per row.
544fn total<O, V>(
545    answers: &mut [bool],
546    order_at: O,
547    left_valid: &Validity,
548    right_valid: &Validity,
549    at_valid: V,
550) where
551    O: Fn(usize) -> Ordering,
552    V: Fn(usize) -> usize,
553{
554    if *left_valid == Validity::AllValid && *right_valid == Validity::AllValid {
555        fill(answers, order_at, |o| o == Ordering::Equal);
556        return;
557    }
558    for (index, answer) in answers.iter_mut().enumerate() {
559        let row = at_valid(index);
560        *answer = match (left_valid.is_valid(row), right_valid.is_valid(row)) {
561            (true, true) => order_at(index) == Ordering::Equal,
562            (false, false) => true,
563            _ => false,
564        };
565    }
566}
567
568/// Compares two values, producing `TRUE`, `FALSE` or `NULL`.
569///
570/// # Errors
571///
572/// If the two types cannot be compared, which after binding means one of them is a nested type.
573pub fn compare_values(op: Comparison, left: &Value, right: &Value) -> Result<Value> {
574    if op.is_total() {
575        let same = match (left.is_null(), right.is_null()) {
576            (true, true) => true,
577            (true, false) | (false, true) => false,
578            (false, false) => order(left, right)? == Ordering::Equal,
579        };
580        return Ok(Value::Boolean(match op {
581            Comparison::NotDistinctFrom => same,
582            _ => !same,
583        }));
584    }
585    if left.is_null() || right.is_null() {
586        return Ok(Value::Null);
587    }
588    let ordering = order(left, right)?;
589    let held = match op {
590        Comparison::Equal => ordering == Ordering::Equal,
591        Comparison::NotEqual => ordering != Ordering::Equal,
592        Comparison::Less => ordering == Ordering::Less,
593        Comparison::LessOrEqual => ordering != Ordering::Greater,
594        Comparison::Greater => ordering == Ordering::Greater,
595        Comparison::GreaterOrEqual => ordering != Ordering::Less,
596        Comparison::DistinctFrom | Comparison::NotDistinctFrom => {
597            return Err(Error::internal("a total comparison reached the ordered path"));
598        }
599    };
600    Ok(Value::Boolean(held))
601}
602
603/// The order of two values, neither of which is null.
604///
605/// This is the one place the sort order of a type is written down. `ORDER BY`, `GROUP BY`, a merge
606/// join and a min or max aggregate all reach it, and a type that ordered differently in two of
607/// those would produce a query whose answer depends on which operator the optimizer picked.
608///
609/// # Errors
610///
611/// If either value is null, which is the caller's mistake rather than a comparison, or if the
612/// types have no order between them.
613pub fn order(left: &Value, right: &Value) -> Result<Ordering> {
614    match (left, right) {
615        (Value::Null, _) | (_, Value::Null) => {
616            Err(Error::internal("a null reached the ordering path"))
617        }
618        (Value::Boolean(a), Value::Boolean(b)) => Ok(a.cmp(b)),
619        (Value::Varchar(a), Value::Varchar(b)) => Ok(a.as_bytes().cmp(b.as_bytes())),
620        (Value::Blob(a), Value::Blob(b)) => Ok(a.cmp(b)),
621        (Value::Date(a), Value::Date(b)) => Ok(a.cmp(b)),
622        (Value::Time(a), Value::Time(b)) | (Value::Timestamp(a), Value::Timestamp(b)) => {
623            Ok(a.cmp(b))
624        }
625        (
626            Value::Interval { months: am, days: ad, micros: au },
627            Value::Interval { months: bm, days: bd, micros: bu },
628        ) => Ok((am, ad, au).cmp(&(bm, bd, bu))),
629        _ => numeric_order(left, right),
630    }
631}
632
633/// The order of two numbers, which is the case that has to work across representations.
634fn numeric_order(left: &Value, right: &Value) -> Result<Ordering> {
635    if let (Some(a), Some(b)) = (integral(left), integral(right)) {
636        return Ok(a.cmp(&b));
637    }
638    if let (
639        Value::Decimal { unscaled: a, scale: sa, .. },
640        Value::Decimal { unscaled: b, scale: sb, .. },
641    ) = (left, right)
642    {
643        if sa == sb {
644            return Ok(a.cmp(b));
645        }
646    }
647    match (approximate(left), approximate(right)) {
648        (Some(a), Some(b)) => Ok(float_order(a, b)),
649        _ => Err(Error::not_implemented(format!(
650            "comparing {} with {}",
651            left.logical_type(),
652            right.logical_type()
653        ))),
654    }
655}
656
657/// DuckDB's float order: NaN is equal to itself and above everything else, and zero has one place.
658fn float_order(left: f64, right: f64) -> Ordering {
659    if left == right {
660        return Ordering::Equal;
661    }
662    match (left.is_nan(), right.is_nan()) {
663        (true, true) => Ordering::Equal,
664        (true, false) => Ordering::Greater,
665        (false, true) => Ordering::Less,
666        (false, false) => left.partial_cmp(&right).unwrap_or(Ordering::Equal),
667    }
668}
669
670/// The order of two values with nulls in it, for a sort key.
671///
672/// A sort has to put nulls somewhere and SQL lets the query say where, so this takes the answer
673/// rather than deciding it.
674///
675/// # Errors
676///
677/// If the two types have no order between them.
678pub fn order_with_nulls(left: &Value, right: &Value, nulls_first: bool) -> Result<Ordering> {
679    match (left.is_null(), right.is_null()) {
680        (true, true) => Ok(Ordering::Equal),
681        (true, false) => Ok(if nulls_first { Ordering::Less } else { Ordering::Greater }),
682        (false, true) => Ok(if nulls_first { Ordering::Greater } else { Ordering::Less }),
683        (false, false) => order(left, right),
684    }
685}
686
687#[cfg(test)]
688mod tests {
689    use super::*;
690
691    fn compared(op: Comparison, left: Value, right: Value) -> Value {
692        compare_values(op, &left, &right).expect("these types compare")
693    }
694
695    /// Every comparison, so that a test that sweeps them cannot quietly miss one.
696    const EVERY: [Comparison; 8] = [
697        Comparison::Equal,
698        Comparison::NotEqual,
699        Comparison::Less,
700        Comparison::LessOrEqual,
701        Comparison::Greater,
702        Comparison::GreaterOrEqual,
703        Comparison::DistinctFrom,
704        Comparison::NotDistinctFrom,
705    ];
706
707    /// The row at a time path, kept as the oracle rather than deleted.
708    ///
709    /// `spec/engine/03-data-plane.md` is explicit that the slow path becomes the thing the fast
710    /// path is checked against. This is that, written out here so that a test can call it on a pair
711    /// of vectors whose forms the fast path does specialize.
712    fn oracle(op: Comparison, left: &Vector, right: &Vector) -> Vector {
713        let values: Vec<Value> = (0..left.len())
714            .map(|index| {
715                compare_values(op, &left.value_at(index), &right.value_at(index))
716                    .expect("the oracle is only asked about types that compare")
717            })
718            .collect();
719        Vector::from_values(LogicalType::Boolean, &values).expect("booleans")
720    }
721
722    /// Asserts that the specialized path and the oracle produce the same vector, not merely the
723    /// same answers. Same vector means the same data, the same validity representation and the
724    /// same false in every null position, which is a much stronger statement and is free to check.
725    fn agrees(op: Comparison, left: &Vector, right: &Vector) {
726        let fast = compare(op, left, right).expect("compares");
727        let slow = oracle(op, left, right);
728        assert_eq!(fast, slow, "{op:?} on a {:?} against a {:?}", left.form(), right.form());
729    }
730
731    /// A small deterministic generator, because a property test with no seed is a test that fails
732    /// on somebody else's machine and passes on yours.
733    struct Rng(u64);
734
735    impl Rng {
736        fn next(&mut self) -> u64 {
737            self.0 ^= self.0 << 13;
738            self.0 ^= self.0 >> 7;
739            self.0 ^= self.0 << 17;
740            self.0
741        }
742
743        fn below(&mut self, bound: u64) -> u64 {
744            self.next() % bound
745        }
746    }
747
748    #[test]
749    fn an_ordinary_comparison_is_null_when_either_side_is() {
750        assert_eq!(compared(Comparison::Equal, Value::Integer(1), Value::Null), Value::Null);
751        assert_eq!(compared(Comparison::Less, Value::Null, Value::Integer(1)), Value::Null);
752    }
753
754    #[test]
755    fn a_total_comparison_is_never_null() {
756        assert_eq!(
757            compared(Comparison::NotDistinctFrom, Value::Null, Value::Null),
758            Value::Boolean(true)
759        );
760        assert_eq!(
761            compared(Comparison::NotDistinctFrom, Value::Integer(1), Value::Null),
762            Value::Boolean(false)
763        );
764        assert_eq!(
765            compared(Comparison::DistinctFrom, Value::Integer(1), Value::Null),
766            Value::Boolean(true)
767        );
768    }
769
770    #[test]
771    fn a_string_compares_by_bytes() {
772        assert_eq!(
773            compared(Comparison::Less, Value::Varchar("a".into()), Value::Varchar("b".into())),
774            Value::Boolean(true)
775        );
776        assert_eq!(
777            compared(Comparison::Less, Value::Varchar("Z".into()), Value::Varchar("a".into())),
778            Value::Boolean(true)
779        );
780    }
781
782    /// The reason this crate does not use `f64::partial_cmp` directly. A NaN that compared
783    /// unordered would make a group by produce a group nothing can find again.
784    #[test]
785    fn two_nans_are_one_value_and_they_sort_above_the_numbers() {
786        assert_eq!(
787            compared(Comparison::Equal, Value::Double(f64::NAN), Value::Double(f64::NAN)),
788            Value::Boolean(true)
789        );
790        assert_eq!(
791            compared(Comparison::Greater, Value::Double(f64::NAN), Value::Double(1e300)),
792            Value::Boolean(true)
793        );
794    }
795
796    #[test]
797    fn zero_has_one_value_however_it_is_signed() {
798        assert_eq!(
799            compared(Comparison::Equal, Value::Double(0.0), Value::Double(-0.0)),
800            Value::Boolean(true)
801        );
802    }
803
804    #[test]
805    fn a_number_compares_the_same_however_it_is_stored() {
806        assert_eq!(
807            compared(Comparison::Equal, Value::Integer(3), Value::BigInt(3)),
808            Value::Boolean(true)
809        );
810        assert_eq!(
811            compared(Comparison::Less, Value::Integer(3), Value::Double(3.5)),
812            Value::Boolean(true)
813        );
814    }
815
816    #[test]
817    fn nulls_go_where_the_query_asked_for_them() {
818        assert_eq!(
819            order_with_nulls(&Value::Null, &Value::Integer(1), true).expect("orders"),
820            Ordering::Less
821        );
822        assert_eq!(
823            order_with_nulls(&Value::Null, &Value::Integer(1), false).expect("orders"),
824            Ordering::Greater
825        );
826    }
827
828    #[test]
829    fn two_constant_vectors_cost_one_comparison() {
830        let left = Vector::constant(LogicalType::Integer, Value::Integer(1), 512);
831        let right = Vector::constant(LogicalType::Integer, Value::Integer(2), 512);
832        let result = compare(Comparison::Less, &left, &right).expect("compares");
833        assert_eq!(result.form(), Form::Constant);
834        assert_eq!(result.value_at(500), Value::Boolean(true));
835    }
836
837    #[test]
838    fn a_comparison_of_two_vectors_is_one_answer_per_row() {
839        let left = Vector::from_values(
840            LogicalType::Integer,
841            &[Value::Integer(1), Value::Integer(5), Value::Null],
842        )
843        .expect("three rows");
844        let right = Vector::constant(LogicalType::Integer, Value::Integer(3), 3);
845        let result = compare(Comparison::Greater, &left, &right).expect("compares");
846        assert_eq!(result.value_at(0), Value::Boolean(false));
847        assert_eq!(result.value_at(1), Value::Boolean(true));
848        assert_eq!(result.value_at(2), Value::Null);
849    }
850
851    #[test]
852    fn two_vectors_of_different_lengths_are_caught() {
853        let left = Vector::constant(LogicalType::Integer, Value::Integer(1), 4);
854        let right = Vector::constant(LogicalType::Integer, Value::Integer(1), 5);
855        let error = compare(Comparison::Equal, &left, &right).expect_err("ragged");
856        assert!(error.message().contains("4 row vector"), "{error}");
857    }
858
859    #[test]
860    fn turning_a_comparison_around_is_what_the_other_side_would_have_said() {
861        for op in EVERY {
862            let left = Value::Integer(3);
863            let right = Value::Integer(7);
864            assert_eq!(
865                compare_values(op, &left, &right).expect("compares"),
866                compare_values(op.swapped(), &right, &left).expect("compares"),
867                "{op:?}"
868            );
869        }
870    }
871
872    /// The whole point of the rewrite, stated as a property. Every operator, every physical
873    /// layout, every form pair the fast path claims, against the row at a time oracle.
874    #[test]
875    fn every_specialized_path_agrees_with_the_row_at_a_time_path() {
876        let mut rng = Rng(0x5eed_1234_9876_4321);
877        let types: [LogicalType; 10] = [
878            LogicalType::Boolean,
879            LogicalType::TinyInt,
880            LogicalType::SmallInt,
881            LogicalType::Integer,
882            LogicalType::BigInt,
883            LogicalType::HugeInt,
884            LogicalType::UInteger,
885            LogicalType::Float,
886            LogicalType::Double,
887            LogicalType::Varchar,
888        ];
889        for ty in &types {
890            for nulls in [0u64, 1, 3] {
891                let len = 37;
892                let make = |rng: &mut Rng| {
893                    let values: Vec<Value> = (0..len)
894                        .map(|_| {
895                            if nulls > 0 && rng.below(nulls + 1) == 0 {
896                                Value::Null
897                            } else {
898                                sample(ty, rng)
899                            }
900                        })
901                        .collect();
902                    Vector::from_values(ty.clone(), &values).expect("a flat vector")
903                };
904                let left = make(&mut rng);
905                let right = make(&mut rng);
906                let literal = sample(ty, &mut rng);
907                let constant = Vector::constant(ty.clone(), literal, len);
908                let null_constant = Vector::constant(ty.clone(), Value::Null, len);
909                let codes: Vec<u32> =
910                    (0..len).map(|_| rng.below(left.len() as u64) as u32).collect();
911                let dictionary =
912                    Vector::dictionary(codes, left.clone()).expect("codes are in range");
913
914                for op in EVERY {
915                    agrees(op, &left, &right);
916                    agrees(op, &left, &constant);
917                    agrees(op, &constant, &left);
918                    agrees(op, &left, &null_constant);
919                    agrees(op, &null_constant, &left);
920                    agrees(op, &dictionary, &constant);
921                    agrees(op, &constant, &dictionary);
922                    // The dictionary against a flat column, which reads a null from either side and
923                    // from the dictionary's values as well, so it is the pair with the most ways to
924                    // disagree with the oracle and the one that got a loop last.
925                    agrees(op, &dictionary, &right);
926                    agrees(op, &right, &dictionary);
927                }
928            }
929        }
930    }
931
932    /// The rows of a selection the row at a time path keeps, which is what [`refine`] has to say.
933    fn refined(op: Comparison, left: &Vector, right: &Vector, kept: &Selection) -> Selection {
934        let mut out = Vec::new();
935        for &row in kept.indices() {
936            let index = row as usize;
937            let answer = compare_values(op, &left.value_at(index), &right.value_at(index))
938                .expect("the oracle is only asked about types that compare");
939            if is_true(&answer) {
940                out.push(row);
941            }
942        }
943        Selection::from_indices(out)
944    }
945
946    fn threads(op: Comparison, left: &Vector, right: &Vector, kept: &Selection) {
947        let fast = refine(op, left, right, kept).expect("compares");
948        assert_eq!(
949            fast,
950            refined(op, left, right, kept),
951            "{op:?} on a {:?} against a {:?} over {} rows",
952            left.form(),
953            right.form(),
954            kept.len()
955        );
956    }
957
958    /// Threading a selection through a comparison is the same rows as comparing everything and
959    /// then keeping the ones that were already kept. Every operator, every form pair that has a
960    /// loop, at four densities of selection, against the row at a time path.
961    #[test]
962    fn a_threaded_comparison_keeps_what_the_row_at_a_time_path_keeps() {
963        let mut rng = Rng(0x5eed_4321_1234_9876);
964        let types = [LogicalType::Integer, LogicalType::Double, LogicalType::Varchar];
965        for ty in &types {
966            for nulls in [0u64, 1, 3] {
967                let len = 37;
968                let make = |rng: &mut Rng| {
969                    let values: Vec<Value> = (0..len)
970                        .map(|_| {
971                            if nulls > 0 && rng.below(nulls + 1) == 0 {
972                                Value::Null
973                            } else {
974                                sample(ty, rng)
975                            }
976                        })
977                        .collect();
978                    Vector::from_values(ty.clone(), &values).expect("a flat vector")
979                };
980                let left = make(&mut rng);
981                let right = make(&mut rng);
982                let constant = Vector::constant(ty.clone(), sample(ty, &mut rng), len);
983                let null_constant = Vector::constant(ty.clone(), Value::Null, len);
984                let codes: Vec<u32> =
985                    (0..len).map(|_| rng.below(left.len() as u64) as u32).collect();
986                let dictionary =
987                    Vector::dictionary(codes, left.clone()).expect("codes are in range");
988
989                // Everything, every third row, a handful including the last one, and nothing,
990                // which is the state a conjunct chain reaches as soon as one conjunct rejects a
991                // whole chunk and is the case where the loop below must not read anything at all.
992                let selections = [
993                    Selection::identity(len),
994                    Selection::from_indices((0..len as u32).filter(|row| row % 3 == 0).collect()),
995                    Selection::from_indices(vec![2, 5, 6, 17, 36]),
996                    Selection::empty(),
997                ];
998                for op in EVERY {
999                    for kept in &selections {
1000                        threads(op, &left, &right, kept);
1001                        threads(op, &left, &constant, kept);
1002                        threads(op, &constant, &left, kept);
1003                        threads(op, &left, &null_constant, kept);
1004                        threads(op, &null_constant, &left, kept);
1005                        threads(op, &constant, &null_constant, kept);
1006                        threads(op, &dictionary, &constant, kept);
1007                        threads(op, &constant, &dictionary, kept);
1008                        threads(op, &dictionary, &right, kept);
1009                        threads(op, &right, &dictionary, kept);
1010                    }
1011                }
1012            }
1013        }
1014    }
1015
1016    /// Two conjuncts threaded one after the other are the rows both of them keep, which is the
1017    /// property the whole filter path rests on. The second comparison sees the rows the first one
1018    /// left and never looks at the others.
1019    #[test]
1020    fn a_second_conjunct_reads_only_what_the_first_one_left() {
1021        let numbers: Vec<Value> = (0..64).map(|row| Value::Integer(row % 10)).collect();
1022        let column = Vector::from_values(LogicalType::Integer, &numbers).expect("a flat vector");
1023        let three = Vector::constant(LogicalType::Integer, Value::Integer(3), 64);
1024        let seven = Vector::constant(LogicalType::Integer, Value::Integer(7), 64);
1025
1026        let first = refine(Comparison::Greater, &column, &three, &Selection::identity(64))
1027            .expect("compares");
1028        let both = refine(Comparison::Less, &column, &seven, &first).expect("compares");
1029
1030        let expected: Vec<u32> = (0..64)
1031            .filter(|row| {
1032                let value = row % 10;
1033                value > 3 && value < 7
1034            })
1035            .collect();
1036        assert_eq!(both.indices(), expected.as_slice());
1037        assert!(both.len() < first.len(), "the second conjunct narrowed the selection");
1038    }
1039
1040    /// A null is not a true, so a threaded comparison drops the row rather than keeping it with an
1041    /// unknown answer. This is the rule that makes `WHERE a < 5` leave out the rows where `a` is
1042    /// null, and it is the one a branchless loop gets wrong if the validity is left out of it.
1043    #[test]
1044    fn a_null_row_is_not_kept_by_an_ordinary_comparison_and_is_by_a_total_one() {
1045        let column = Vector::from_values(
1046            LogicalType::Integer,
1047            &[Value::Integer(1), Value::Null, Value::Integer(3), Value::Null],
1048        )
1049        .expect("four rows");
1050        let cut = Vector::constant(LogicalType::Integer, Value::Integer(2), 4);
1051        let all = Selection::identity(4);
1052        assert_eq!(
1053            refine(Comparison::Less, &column, &cut, &all).expect("compares").indices(),
1054            &[0]
1055        );
1056        // The total comparison has an answer at every row, so the two nulls are kept here.
1057        let nulls = Vector::constant(LogicalType::Integer, Value::Null, 4);
1058        assert_eq!(
1059            refine(Comparison::NotDistinctFrom, &column, &nulls, &all).expect("compares").indices(),
1060            &[1, 3]
1061        );
1062    }
1063
1064    #[test]
1065    fn a_selection_past_the_end_is_caught() {
1066        let column = Vector::constant(LogicalType::Integer, Value::Integer(1), 4);
1067        let past = Selection::from_indices(vec![0, 4]);
1068        let error = refine(Comparison::Equal, &column, &column, &past).expect_err("out of range");
1069        assert!(error.message().contains("4 row vector"), "{error}");
1070    }
1071
1072    /// One value of a type, for the generator above.
1073    fn sample(ty: &LogicalType, rng: &mut Rng) -> Value {
1074        match ty {
1075            LogicalType::Boolean => Value::Boolean(rng.below(2) == 1),
1076            LogicalType::TinyInt => Value::TinyInt(rng.below(7) as i8 - 3),
1077            LogicalType::SmallInt => Value::SmallInt(rng.below(11) as i16 - 5),
1078            LogicalType::Integer => Value::Integer(rng.below(9) as i32 - 4),
1079            LogicalType::BigInt => Value::BigInt(rng.below(9) as i64 - 4),
1080            LogicalType::HugeInt => Value::HugeInt(i128::from(rng.below(9)) - 4),
1081            LogicalType::UInteger => Value::UInteger(rng.below(9) as u32),
1082            // A NaN and a negative zero in the pool on purpose, because DuckDB's float order is
1083            // not IEEE's and the fast path has to reach the same answer the oracle does.
1084            LogicalType::Float => Value::Float(match rng.below(5) {
1085                0 => f32::NAN,
1086                1 => -0.0,
1087                other => other as f32 - 2.0,
1088            }),
1089            LogicalType::Double => Value::Double(match rng.below(5) {
1090                0 => f64::NAN,
1091                1 => -0.0,
1092                other => other as f64 - 2.0,
1093            }),
1094            // Short, at the inline limit, over it, and sharing a prefix with each other, which is
1095            // where a comparison that trusts the prefix too far goes wrong.
1096            LogicalType::Varchar => Value::Varchar(
1097                match rng.below(6) {
1098                    0 => "",
1099                    1 => "ab",
1100                    2 => "abc",
1101                    3 => "abcdefghijkl",
1102                    4 => "abcdefghijklm",
1103                    _ => "abcdefghijklmnopqrstuvwxyz",
1104                }
1105                .to_owned(),
1106            ),
1107            other => panic!("the generator has no values for {other}"),
1108        }
1109    }
1110
1111    /// The prefix lemma, written as a test because the whole string path rests on it. A view pads
1112    /// a short string with zeros, so prefix order has to agree with byte order on every pair where
1113    /// the prefixes differ, including the pairs where one string is shorter than four bytes.
1114    #[test]
1115    fn prefix_order_is_byte_order_whenever_the_prefixes_differ() {
1116        let words =
1117            ["", "a", "ab", "abc", "abcd", "abcde", "b", "abcdefghijklmnop", "abcdefghijklmnoq"];
1118        let mut column = StringColumn::new();
1119        for word in words {
1120            column.push(word);
1121        }
1122        for (i, one) in words.iter().enumerate() {
1123            for (j, other) in words.iter().enumerate() {
1124                assert_eq!(
1125                    string_order(&column, i, &column, j),
1126                    one.as_bytes().cmp(other.as_bytes()),
1127                    "{one:?} against {other:?}"
1128                );
1129            }
1130        }
1131    }
1132
1133    /// A dictionary is compared once per distinct value, not once per row, and it has to reach the
1134    /// same answer including for the nulls it keeps in the vector it points at.
1135    #[test]
1136    fn a_dictionary_against_a_constant_reads_its_nulls_from_the_values() {
1137        let values = Vector::from_values(
1138            LogicalType::Integer,
1139            &[Value::Integer(1), Value::Null, Value::Integer(9)],
1140        )
1141        .expect("three values");
1142        let dictionary =
1143            Vector::dictionary(vec![0, 1, 2, 1, 0], values).expect("codes are in range");
1144        let constant = Vector::constant(LogicalType::Integer, Value::Integer(5), 5);
1145        let result = compare(Comparison::Less, &dictionary, &constant).expect("compares");
1146        assert_eq!(result.value_at(0), Value::Boolean(true));
1147        assert_eq!(result.value_at(1), Value::Null);
1148        assert_eq!(result.value_at(2), Value::Boolean(false));
1149        assert_eq!(result.value_at(3), Value::Null);
1150        assert_eq!(result.value_at(4), Value::Boolean(true));
1151    }
1152
1153    /// A form pair with no loop is answered correctly and counted, which is the whole contract of
1154    /// the fallback counter. Sequence against a column is the one this file leaves out on purpose.
1155    #[test]
1156    fn a_form_pair_with_no_loop_is_still_right_and_says_so() {
1157        // The counters are process wide and another test in this crate resets them, so the ones
1158        // that read a count take turns.
1159        let _turn = fallback::TURN.lock().expect("no test panics while holding this");
1160        let before = fallback::count(Kernel::Compare, Form::Sequence, Form::Flat);
1161        let sequence = Vector::sequence(10, 1, 4);
1162        let flat = Vector::from_values(
1163            LogicalType::BigInt,
1164            &[Value::BigInt(9), Value::BigInt(11), Value::BigInt(12), Value::Null],
1165        )
1166        .expect("four rows");
1167        let result = compare(Comparison::Less, &sequence, &flat).expect("compares");
1168        assert_eq!(result.value_at(0), Value::Boolean(false));
1169        assert_eq!(result.value_at(1), Value::Boolean(false));
1170        assert_eq!(result.value_at(2), Value::Boolean(false));
1171        assert_eq!(result.value_at(3), Value::Null);
1172        assert!(fallback::count(Kernel::Compare, Form::Sequence, Form::Flat) > before);
1173    }
1174
1175    /// The reason `Vector::dictionary` composes rather than stacks, stated as the thing that breaks
1176    /// if it stops.
1177    ///
1178    /// Every loop in this file reaches for the values behind the codes with `Vector::data`, and a
1179    /// dictionary pointing at a dictionary has no data to hand back, so a second filter over an
1180    /// already filtered chunk used to turn every one of these kernels off and drop the comparison
1181    /// onto the row at a time path. Measured on server3 over a chunk of two numeric columns that was
1182    /// selected twice, that was 3.5 nanoseconds a row becoming 104, and a third and fourth level
1183    /// cost nothing more because the first one had already given up everything there was to give.
1184    #[test]
1185    fn a_second_level_of_codes_does_not_turn_the_loops_off() {
1186        let _turn = fallback::TURN.lock().expect("no test panics while holding this");
1187        let before = fallback::count(Kernel::Compare, Form::Dictionary, Form::Constant);
1188        let values = Vector::from_values(
1189            LogicalType::Integer,
1190            &[Value::Integer(1), Value::Integer(5), Value::Integer(9)],
1191        )
1192        .expect("three rows");
1193        let once = Vector::dictionary(vec![2, 1, 0], values).expect("codes are in range");
1194        let twice = Vector::dictionary(vec![1, 2], once).expect("codes are in range");
1195        let cut = Vector::constant(LogicalType::Integer, Value::Integer(4), 2);
1196        let result = compare(Comparison::Greater, &twice, &cut).expect("compares");
1197        assert_eq!(result.value_at(0), Value::Boolean(true));
1198        assert_eq!(result.value_at(1), Value::Boolean(false));
1199        assert_eq!(fallback::count(Kernel::Compare, Form::Dictionary, Form::Constant), before);
1200    }
1201
1202    /// Either side all null, on one of the six ordinary comparisons, is every answer null without
1203    /// the data being read. The vector this produces has to be the one the oracle produces, which
1204    /// is a flat run of falses under an all invalid validity rather than a constant.
1205    #[test]
1206    fn a_side_that_is_entirely_null_answers_without_reading_the_other() {
1207        let nulls = Vector::constant(LogicalType::Integer, Value::Null, 6);
1208        let flat = Vector::from_values(
1209            LogicalType::Integer,
1210            &[
1211                Value::Integer(1),
1212                Value::Integer(2),
1213                Value::Integer(3),
1214                Value::Integer(4),
1215                Value::Integer(5),
1216                Value::Integer(6),
1217            ],
1218        )
1219        .expect("six rows");
1220        agrees(Comparison::Less, &nulls, &flat);
1221        agrees(Comparison::Equal, &flat, &nulls);
1222        assert_eq!(
1223            compare(Comparison::Less, &nulls, &flat).expect("compares").validity(),
1224            &Validity::AllInvalid
1225        );
1226    }
1227
1228    /// An empty vector is not a special case anywhere, and the easiest way to keep it that way is
1229    /// to say so in a test rather than to find out from a panic in an operator.
1230    #[test]
1231    fn an_empty_comparison_is_an_empty_answer() {
1232        let left = Vector::from_values(LogicalType::Integer, &[]).expect("no rows");
1233        let right = Vector::constant(LogicalType::Integer, Value::Integer(1), 0);
1234        let result = compare(Comparison::Equal, &left, &right).expect("compares");
1235        assert_eq!(result.len(), 0);
1236    }
1237}