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, interval_micros};
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.positions(), right.constant_value()) {
346        let one = values.data()?;
347        let held = single(left.logical_type(), value)?;
348        let other = held.data()?;
349        let at = |index: usize| codes[map(index)] as usize;
350        return dispatch(op, len, one, at, other, first, left_valid, right_valid, map);
351    }
352    if let (Some(value), Some((codes, values))) = (left.constant_value(), right.positions()) {
353        let other = values.data()?;
354        let held = single(right.logical_type(), value)?;
355        let one = held.data()?;
356        let at = |index: usize| codes[map(index)] as usize;
357        return dispatch(op.swapped(), len, other, at, one, first, right_valid, left_valid, map);
358    }
359    // A dictionary against a flat column. This pair had no loop until the kernel table put a number
360    // on what that cost, which on `server3` was 83 nanoseconds a row against 1.2 for the dictionary
361    // against constant pair beside it, on the same data and the same operator. It is not a rare
362    // shape either: it is what a filtered column compared against an unfiltered one is, which is
363    // every conjunct after the first.
364    if let (Some((codes, values)), Some(other)) = (left.positions(), right.data()) {
365        let one = values.data()?;
366        let at = |index: usize| codes[map(index)] as usize;
367        return dispatch(op, len, one, at, other, map, left_valid, right_valid, map);
368    }
369    if let (Some(one), Some((codes, values))) = (left.data(), right.positions()) {
370        let other = values.data()?;
371        let at = |index: usize| codes[map(index)] as usize;
372        return dispatch(op.swapped(), len, other, at, one, map, right_valid, left_valid, map);
373    }
374    None
375}
376
377/// One loop per physical layout, generated rather than written out.
378///
379/// The two index closures are what let the same body serve flat against flat, a column against a
380/// constant and a dictionary against a constant. `identity` on both sides is the first, `first` on
381/// the right is the second, and the codes on the left are the third.
382#[expect(
383    clippy::too_many_arguments,
384    reason = "two sides with an index each, the operator, the length and two validities, all of \
385              which the loop needs and none of which is worth a struct that exists for one call"
386)]
387fn dispatch<L, R, V>(
388    op: Comparison,
389    len: usize,
390    left: &Data,
391    at_left: L,
392    right: &Data,
393    at_right: R,
394    left_valid: &Validity,
395    right_valid: &Validity,
396    at_valid: V,
397) -> Option<Vec<bool>>
398where
399    L: Fn(usize) -> usize,
400    R: Fn(usize) -> usize,
401    V: Fn(usize) -> usize,
402{
403    macro_rules! layouts {
404        ($(($variant:ident, $native:ty, $zero:expr)),+ $(,)?) => {
405            match (left, right) {
406                $(
407                    (Data::$variant(one), Data::$variant(other)) => Some(sweep(
408                        op,
409                        len,
410                        |index| one[at_left(index)].cmp(&other[at_right(index)]),
411                        left_valid,
412                        right_valid,
413                        &at_valid,
414                    )),
415                )+
416                // Floats have their own order, which is DuckDB's rather than IEEE's, and the
417                // widening on a `f32` is free because the comparison is against another `f32`.
418                (Data::Float32(one), Data::Float32(other)) => Some(sweep(
419                    op,
420                    len,
421                    |index| {
422                        float_order(
423                            f64::from(one[at_left(index)]),
424                            f64::from(other[at_right(index)]),
425                        )
426                    },
427                    left_valid,
428                    right_valid,
429                    &at_valid,
430                )),
431                (Data::Float64(one), Data::Float64(other)) => Some(sweep(
432                    op,
433                    len,
434                    |index| float_order(one[at_left(index)], other[at_right(index)]),
435                    left_valid,
436                    right_valid,
437                    &at_valid,
438                )),
439                // An interval is three counts and the order is over the one length they add up to,
440                // so this is not the derived order of the triple and cannot be generated above.
441                (Data::Interval(one), Data::Interval(other)) => Some(sweep(
442                    op,
443                    len,
444                    |index| {
445                        let (months, days, micros) = one[at_left(index)];
446                        let (bm, bd, bu) = other[at_right(index)];
447                        interval_micros(months, days, micros).cmp(&interval_micros(bm, bd, bu))
448                    },
449                    left_valid,
450                    right_valid,
451                    &at_valid,
452                )),
453                (Data::Varlen(one), Data::Varlen(other)) => Some(sweep(
454                    op,
455                    len,
456                    |index| string_order(one, at_left(index), other, at_right(index)),
457                    left_valid,
458                    right_valid,
459                    &at_valid,
460                )),
461                _ => None,
462            }
463        };
464    }
465    rudb_vector::for_each_layout!(ordered, layouts)
466}
467
468/// Two strings in byte order, resolved from the four byte prefix where it can be.
469///
470/// The lemma this rests on is that prefix order is byte order whenever the two prefixes differ. A
471/// view pads a string shorter than four bytes with zeros, zero is the least byte, and byte order
472/// says a string is less than any string that extends it, so padding compares the same way the
473/// missing bytes would have. When the prefixes are equal the payload settles it, which for an
474/// inline string is the same sixteen bytes already loaded and for a long one is a block read.
475fn string_order(
476    left: &StringColumn,
477    at_left: usize,
478    right: &StringColumn,
479    at_right: usize,
480) -> Ordering {
481    let (Some(one), Some(other)) = (left.views().get(at_left), right.views().get(at_right)) else {
482        return Ordering::Equal;
483    };
484    let (prefix, against) = (one.prefix(), other.prefix());
485    if prefix != against {
486        return prefix.cmp(&against);
487    }
488    // Bytes rather than `StringColumn::get`, which validates UTF-8. Everything in a column was
489    // pushed from a `&str` so the validation cannot fail, and on a URL column, where every row
490    // shares the `http` prefix and the payload therefore decides every comparison, it was the
491    // larger half of the per row cost.
492    let bytes = left.bytes(at_left).unwrap_or_default();
493    let against_bytes = right.bytes(at_right).unwrap_or_default();
494    bytes.cmp(against_bytes)
495}
496
497/// The answers for one ordering, with the operator decided once rather than once per row.
498///
499/// This is where the match on the operator gets hoisted. Each arm calls a generic `fill` with a
500/// different predicate, so the compiler produces eight loops whose bodies are an ordering against a
501/// constant, rather than one loop with a branch table in it.
502fn sweep<O, V>(
503    op: Comparison,
504    len: usize,
505    order_at: O,
506    left_valid: &Validity,
507    right_valid: &Validity,
508    at_valid: V,
509) -> Vec<bool>
510where
511    O: Fn(usize) -> Ordering,
512    V: Fn(usize) -> usize,
513{
514    let mut answers = vec![false; len];
515    match op {
516        Comparison::Equal => fill(&mut answers, order_at, |o| o == Ordering::Equal),
517        Comparison::NotEqual => fill(&mut answers, order_at, |o| o != Ordering::Equal),
518        Comparison::Less => fill(&mut answers, order_at, |o| o == Ordering::Less),
519        Comparison::LessOrEqual => fill(&mut answers, order_at, |o| o != Ordering::Greater),
520        Comparison::Greater => fill(&mut answers, order_at, |o| o == Ordering::Greater),
521        Comparison::GreaterOrEqual => fill(&mut answers, order_at, |o| o != Ordering::Less),
522        Comparison::DistinctFrom => {
523            total(&mut answers, order_at, left_valid, right_valid, at_valid);
524            for answer in &mut answers {
525                *answer = !*answer;
526            }
527        }
528        Comparison::NotDistinctFrom => {
529            total(&mut answers, order_at, left_valid, right_valid, at_valid);
530        }
531    }
532    answers
533}
534
535/// One loop, one predicate, no branch on the operator.
536#[inline]
537fn fill<O, H>(answers: &mut [bool], order_at: O, held: H)
538where
539    O: Fn(usize) -> Ordering,
540    H: Fn(Ordering) -> bool,
541{
542    for (index, answer) in answers.iter_mut().enumerate() {
543        *answer = held(order_at(index));
544    }
545}
546
547/// `IS NOT DISTINCT FROM`, which reads validity as data rather than as an absence.
548///
549/// Two nulls are the same value here and a null against anything else is not, which is the whole
550/// difference between this and `=`. The all valid case is checked once so that the common shape,
551/// which is a total comparison inside a join on columns that happen not to be nullable, does not
552/// pay for two validity lookups per row.
553fn total<O, V>(
554    answers: &mut [bool],
555    order_at: O,
556    left_valid: &Validity,
557    right_valid: &Validity,
558    at_valid: V,
559) where
560    O: Fn(usize) -> Ordering,
561    V: Fn(usize) -> usize,
562{
563    if *left_valid == Validity::AllValid && *right_valid == Validity::AllValid {
564        fill(answers, order_at, |o| o == Ordering::Equal);
565        return;
566    }
567    for (index, answer) in answers.iter_mut().enumerate() {
568        let row = at_valid(index);
569        *answer = match (left_valid.is_valid(row), right_valid.is_valid(row)) {
570            (true, true) => order_at(index) == Ordering::Equal,
571            (false, false) => true,
572            _ => false,
573        };
574    }
575}
576
577/// Compares two values, producing `TRUE`, `FALSE` or `NULL`.
578///
579/// # Errors
580///
581/// If the two types cannot be compared, which after binding means one of them is a nested type.
582pub fn compare_values(op: Comparison, left: &Value, right: &Value) -> Result<Value> {
583    if op.is_total() {
584        let same = match (left.is_null(), right.is_null()) {
585            (true, true) => true,
586            (true, false) | (false, true) => false,
587            (false, false) => order(left, right)? == Ordering::Equal,
588        };
589        return Ok(Value::Boolean(match op {
590            Comparison::NotDistinctFrom => same,
591            _ => !same,
592        }));
593    }
594    if left.is_null() || right.is_null() {
595        return Ok(Value::Null);
596    }
597    let ordering = order(left, right)?;
598    let held = match op {
599        Comparison::Equal => ordering == Ordering::Equal,
600        Comparison::NotEqual => ordering != Ordering::Equal,
601        Comparison::Less => ordering == Ordering::Less,
602        Comparison::LessOrEqual => ordering != Ordering::Greater,
603        Comparison::Greater => ordering == Ordering::Greater,
604        Comparison::GreaterOrEqual => ordering != Ordering::Less,
605        Comparison::DistinctFrom | Comparison::NotDistinctFrom => {
606            return Err(Error::internal("a total comparison reached the ordered path"));
607        }
608    };
609    Ok(Value::Boolean(held))
610}
611
612/// The order of two values, neither of which is null.
613///
614/// This is the one place the sort order of a type is written down. `ORDER BY`, `GROUP BY`, a merge
615/// join and a min or max aggregate all reach it, and a type that ordered differently in two of
616/// those would produce a query whose answer depends on which operator the optimizer picked.
617///
618/// # Errors
619///
620/// If either value is null, which is the caller's mistake rather than a comparison, or if the
621/// types have no order between them.
622pub fn order(left: &Value, right: &Value) -> Result<Ordering> {
623    match (left, right) {
624        (Value::Null, _) | (_, Value::Null) => {
625            Err(Error::internal("a null reached the ordering path"))
626        }
627        (Value::Boolean(a), Value::Boolean(b)) => Ok(a.cmp(b)),
628        (Value::Varchar(a), Value::Varchar(b)) => Ok(a.as_bytes().cmp(b.as_bytes())),
629        (Value::Blob(a), Value::Blob(b)) => Ok(a.cmp(b)),
630        (Value::Date(a), Value::Date(b)) => Ok(a.cmp(b)),
631        (Value::Time(a), Value::Time(b)) | (Value::Timestamp(a), Value::Timestamp(b)) => {
632            Ok(a.cmp(b))
633        }
634        (
635            Value::Interval { months: am, days: ad, micros: au },
636            Value::Interval { months: bm, days: bd, micros: bu },
637        ) => Ok(interval_micros(*am, *ad, *au).cmp(&interval_micros(*bm, *bd, *bu))),
638        _ => numeric_order(left, right),
639    }
640}
641
642/// The order of two numbers, which is the case that has to work across representations.
643fn numeric_order(left: &Value, right: &Value) -> Result<Ordering> {
644    if let (Some(a), Some(b)) = (integral(left), integral(right)) {
645        return Ok(a.cmp(&b));
646    }
647    if let (
648        Value::Decimal { unscaled: a, scale: sa, .. },
649        Value::Decimal { unscaled: b, scale: sb, .. },
650    ) = (left, right)
651    {
652        if sa == sb {
653            return Ok(a.cmp(b));
654        }
655    }
656    match (approximate(left), approximate(right)) {
657        (Some(a), Some(b)) => Ok(float_order(a, b)),
658        _ => Err(Error::not_implemented(format!(
659            "comparing {} with {}",
660            left.logical_type(),
661            right.logical_type()
662        ))),
663    }
664}
665
666/// DuckDB's float order: NaN is equal to itself and above everything else, and zero has one place.
667fn float_order(left: f64, right: f64) -> Ordering {
668    if left == right {
669        return Ordering::Equal;
670    }
671    match (left.is_nan(), right.is_nan()) {
672        (true, true) => Ordering::Equal,
673        (true, false) => Ordering::Greater,
674        (false, true) => Ordering::Less,
675        (false, false) => left.partial_cmp(&right).unwrap_or(Ordering::Equal),
676    }
677}
678
679/// The order of two values with nulls in it, for a sort key.
680///
681/// A sort has to put nulls somewhere and SQL lets the query say where, so this takes the answer
682/// rather than deciding it.
683///
684/// # Errors
685///
686/// If the two types have no order between them.
687pub fn order_with_nulls(left: &Value, right: &Value, nulls_first: bool) -> Result<Ordering> {
688    match (left.is_null(), right.is_null()) {
689        (true, true) => Ok(Ordering::Equal),
690        (true, false) => Ok(if nulls_first { Ordering::Less } else { Ordering::Greater }),
691        (false, true) => Ok(if nulls_first { Ordering::Greater } else { Ordering::Less }),
692        (false, false) => order(left, right),
693    }
694}
695
696#[cfg(test)]
697mod tests {
698    use super::*;
699
700    fn compared(op: Comparison, left: Value, right: Value) -> Value {
701        compare_values(op, &left, &right).expect("these types compare")
702    }
703
704    /// Every comparison, so that a test that sweeps them cannot quietly miss one.
705    const EVERY: [Comparison; 8] = [
706        Comparison::Equal,
707        Comparison::NotEqual,
708        Comparison::Less,
709        Comparison::LessOrEqual,
710        Comparison::Greater,
711        Comparison::GreaterOrEqual,
712        Comparison::DistinctFrom,
713        Comparison::NotDistinctFrom,
714    ];
715
716    /// The row at a time path, kept as the oracle rather than deleted.
717    ///
718    /// `spec/engine/03-data-plane.md` is explicit that the slow path becomes the thing the fast
719    /// path is checked against. This is that, written out here so that a test can call it on a pair
720    /// of vectors whose forms the fast path does specialize.
721    fn oracle(op: Comparison, left: &Vector, right: &Vector) -> Vector {
722        let values: Vec<Value> = (0..left.len())
723            .map(|index| {
724                compare_values(op, &left.value_at(index), &right.value_at(index))
725                    .expect("the oracle is only asked about types that compare")
726            })
727            .collect();
728        Vector::from_values(LogicalType::Boolean, &values).expect("booleans")
729    }
730
731    /// Asserts that the specialized path and the oracle produce the same vector, not merely the
732    /// same answers. Same vector means the same data, the same validity representation and the
733    /// same false in every null position, which is a much stronger statement and is free to check.
734    fn agrees(op: Comparison, left: &Vector, right: &Vector) {
735        let fast = compare(op, left, right).expect("compares");
736        let slow = oracle(op, left, right);
737        assert_eq!(fast, slow, "{op:?} on a {:?} against a {:?}", left.form(), right.form());
738    }
739
740    /// A small deterministic generator, because a property test with no seed is a test that fails
741    /// on somebody else's machine and passes on yours.
742    struct Rng(u64);
743
744    impl Rng {
745        fn next(&mut self) -> u64 {
746            self.0 ^= self.0 << 13;
747            self.0 ^= self.0 >> 7;
748            self.0 ^= self.0 << 17;
749            self.0
750        }
751
752        fn below(&mut self, bound: u64) -> u64 {
753            self.next() % bound
754        }
755    }
756
757    #[test]
758    fn an_ordinary_comparison_is_null_when_either_side_is() {
759        assert_eq!(compared(Comparison::Equal, Value::Integer(1), Value::Null), Value::Null);
760        assert_eq!(compared(Comparison::Less, Value::Null, Value::Integer(1)), Value::Null);
761    }
762
763    #[test]
764    fn a_total_comparison_is_never_null() {
765        assert_eq!(
766            compared(Comparison::NotDistinctFrom, Value::Null, Value::Null),
767            Value::Boolean(true)
768        );
769        assert_eq!(
770            compared(Comparison::NotDistinctFrom, Value::Integer(1), Value::Null),
771            Value::Boolean(false)
772        );
773        assert_eq!(
774            compared(Comparison::DistinctFrom, Value::Integer(1), Value::Null),
775            Value::Boolean(true)
776        );
777    }
778
779    #[test]
780    fn a_string_compares_by_bytes() {
781        assert_eq!(
782            compared(Comparison::Less, Value::Varchar("a".into()), Value::Varchar("b".into())),
783            Value::Boolean(true)
784        );
785        assert_eq!(
786            compared(Comparison::Less, Value::Varchar("Z".into()), Value::Varchar("a".into())),
787            Value::Boolean(true)
788        );
789    }
790
791    /// The reason this crate does not use `f64::partial_cmp` directly. A NaN that compared
792    /// unordered would make a group by produce a group nothing can find again.
793    #[test]
794    fn two_nans_are_one_value_and_they_sort_above_the_numbers() {
795        assert_eq!(
796            compared(Comparison::Equal, Value::Double(f64::NAN), Value::Double(f64::NAN)),
797            Value::Boolean(true)
798        );
799        assert_eq!(
800            compared(Comparison::Greater, Value::Double(f64::NAN), Value::Double(1e300)),
801            Value::Boolean(true)
802        );
803    }
804
805    #[test]
806    fn zero_has_one_value_however_it_is_signed() {
807        assert_eq!(
808            compared(Comparison::Equal, Value::Double(0.0), Value::Double(-0.0)),
809            Value::Boolean(true)
810        );
811    }
812
813    /// An interval is three counts and two of them that are the same length are one value, at
814    /// thirty days to a month and twenty four hours to a day, which is what upstream answers. The
815    /// three counts are still kept apart, because adding a month to a date is not adding thirty
816    /// days to it, so these pairs are equal and print differently.
817    #[test]
818    fn two_intervals_of_the_same_length_are_one_value() {
819        let day = Value::Interval { months: 0, days: 1, micros: 0 };
820        let hours = Value::Interval { months: 0, days: 0, micros: 86_400_000_000 };
821        let month = Value::Interval { months: 1, days: 0, micros: 0 };
822        let thirty = Value::Interval { months: 0, days: 30, micros: 0 };
823        let long_day = Value::Interval { months: 0, days: 0, micros: 90_000_000_000 };
824        assert_eq!(compared(Comparison::Equal, day.clone(), hours), Value::Boolean(true));
825        assert_eq!(compared(Comparison::Equal, month, thirty), Value::Boolean(true));
826        assert_eq!(compared(Comparison::Greater, long_day, day), Value::Boolean(true));
827    }
828
829    #[test]
830    fn a_number_compares_the_same_however_it_is_stored() {
831        assert_eq!(
832            compared(Comparison::Equal, Value::Integer(3), Value::BigInt(3)),
833            Value::Boolean(true)
834        );
835        assert_eq!(
836            compared(Comparison::Less, Value::Integer(3), Value::Double(3.5)),
837            Value::Boolean(true)
838        );
839    }
840
841    #[test]
842    fn nulls_go_where_the_query_asked_for_them() {
843        assert_eq!(
844            order_with_nulls(&Value::Null, &Value::Integer(1), true).expect("orders"),
845            Ordering::Less
846        );
847        assert_eq!(
848            order_with_nulls(&Value::Null, &Value::Integer(1), false).expect("orders"),
849            Ordering::Greater
850        );
851    }
852
853    #[test]
854    fn two_constant_vectors_cost_one_comparison() {
855        let left = Vector::constant(LogicalType::Integer, Value::Integer(1), 512);
856        let right = Vector::constant(LogicalType::Integer, Value::Integer(2), 512);
857        let result = compare(Comparison::Less, &left, &right).expect("compares");
858        assert_eq!(result.form(), Form::Constant);
859        assert_eq!(result.value_at(500), Value::Boolean(true));
860    }
861
862    #[test]
863    fn a_comparison_of_two_vectors_is_one_answer_per_row() {
864        let left = Vector::from_values(
865            LogicalType::Integer,
866            &[Value::Integer(1), Value::Integer(5), Value::Null],
867        )
868        .expect("three rows");
869        let right = Vector::constant(LogicalType::Integer, Value::Integer(3), 3);
870        let result = compare(Comparison::Greater, &left, &right).expect("compares");
871        assert_eq!(result.value_at(0), Value::Boolean(false));
872        assert_eq!(result.value_at(1), Value::Boolean(true));
873        assert_eq!(result.value_at(2), Value::Null);
874    }
875
876    #[test]
877    fn two_vectors_of_different_lengths_are_caught() {
878        let left = Vector::constant(LogicalType::Integer, Value::Integer(1), 4);
879        let right = Vector::constant(LogicalType::Integer, Value::Integer(1), 5);
880        let error = compare(Comparison::Equal, &left, &right).expect_err("ragged");
881        assert!(error.message().contains("4 row vector"), "{error}");
882    }
883
884    #[test]
885    fn turning_a_comparison_around_is_what_the_other_side_would_have_said() {
886        for op in EVERY {
887            let left = Value::Integer(3);
888            let right = Value::Integer(7);
889            assert_eq!(
890                compare_values(op, &left, &right).expect("compares"),
891                compare_values(op.swapped(), &right, &left).expect("compares"),
892                "{op:?}"
893            );
894        }
895    }
896
897    /// The whole point of the rewrite, stated as a property. Every operator, every physical
898    /// layout, every form pair the fast path claims, against the row at a time oracle.
899    #[test]
900    fn every_specialized_path_agrees_with_the_row_at_a_time_path() {
901        let mut rng = Rng(0x5eed_1234_9876_4321);
902        let types: [LogicalType; 11] = [
903            LogicalType::Boolean,
904            LogicalType::TinyInt,
905            LogicalType::SmallInt,
906            LogicalType::Integer,
907            LogicalType::BigInt,
908            LogicalType::HugeInt,
909            LogicalType::UInteger,
910            LogicalType::Float,
911            LogicalType::Double,
912            LogicalType::Varchar,
913            LogicalType::Interval,
914        ];
915        for ty in &types {
916            for nulls in [0u64, 1, 3] {
917                let len = 37;
918                let make = |rng: &mut Rng| {
919                    let values: Vec<Value> = (0..len)
920                        .map(|_| {
921                            if nulls > 0 && rng.below(nulls + 1) == 0 {
922                                Value::Null
923                            } else {
924                                sample(ty, rng)
925                            }
926                        })
927                        .collect();
928                    Vector::from_values(ty.clone(), &values).expect("a flat vector")
929                };
930                let left = make(&mut rng);
931                let right = make(&mut rng);
932                let literal = sample(ty, &mut rng);
933                let constant = Vector::constant(ty.clone(), literal, len);
934                let null_constant = Vector::constant(ty.clone(), Value::Null, len);
935                let codes: Vec<u32> =
936                    (0..len).map(|_| rng.below(left.len() as u64) as u32).collect();
937                let dictionary =
938                    Vector::dictionary(codes, left.clone()).expect("codes are in range");
939                // Runs over the same values, with the last one cut short so that a run boundary
940                // does not land on the end of the vector.
941                let ends: Vec<u32> = (1..=left.len())
942                    .map(|run| ((run * len) / left.len()).max(run) as u32)
943                    .collect();
944                let runs = Vector::runs(ends, left.clone()).expect("one value for each run");
945
946                for op in EVERY {
947                    agrees(op, &left, &right);
948                    agrees(op, &left, &constant);
949                    agrees(op, &constant, &left);
950                    agrees(op, &left, &null_constant);
951                    agrees(op, &null_constant, &left);
952                    agrees(op, &dictionary, &constant);
953                    agrees(op, &constant, &dictionary);
954                    // The dictionary against a flat column, which reads a null from either side and
955                    // from the dictionary's values as well, so it is the pair with the most ways to
956                    // disagree with the oracle and the one that got a loop last.
957                    agrees(op, &dictionary, &right);
958                    agrees(op, &right, &dictionary);
959                    // The same four pairings for run length, which reaches the same loops through
960                    // the same accessor, so what is being checked is that the positions it works
961                    // out are the positions the row at a time path reads.
962                    agrees(op, &runs, &constant);
963                    agrees(op, &constant, &runs);
964                    agrees(op, &runs, &right);
965                    agrees(op, &right, &runs);
966                }
967            }
968        }
969    }
970
971    /// The rows of a selection the row at a time path keeps, which is what [`refine`] has to say.
972    fn refined(op: Comparison, left: &Vector, right: &Vector, kept: &Selection) -> Selection {
973        let mut out = Vec::new();
974        for &row in kept.indices() {
975            let index = row as usize;
976            let answer = compare_values(op, &left.value_at(index), &right.value_at(index))
977                .expect("the oracle is only asked about types that compare");
978            if is_true(&answer) {
979                out.push(row);
980            }
981        }
982        Selection::from_indices(out)
983    }
984
985    fn threads(op: Comparison, left: &Vector, right: &Vector, kept: &Selection) {
986        let fast = refine(op, left, right, kept).expect("compares");
987        assert_eq!(
988            fast,
989            refined(op, left, right, kept),
990            "{op:?} on a {:?} against a {:?} over {} rows",
991            left.form(),
992            right.form(),
993            kept.len()
994        );
995    }
996
997    /// Threading a selection through a comparison is the same rows as comparing everything and
998    /// then keeping the ones that were already kept. Every operator, every form pair that has a
999    /// loop, at four densities of selection, against the row at a time path.
1000    #[test]
1001    fn a_threaded_comparison_keeps_what_the_row_at_a_time_path_keeps() {
1002        let mut rng = Rng(0x5eed_4321_1234_9876);
1003        let types = [LogicalType::Integer, LogicalType::Double, LogicalType::Varchar];
1004        for ty in &types {
1005            for nulls in [0u64, 1, 3] {
1006                let len = 37;
1007                let make = |rng: &mut Rng| {
1008                    let values: Vec<Value> = (0..len)
1009                        .map(|_| {
1010                            if nulls > 0 && rng.below(nulls + 1) == 0 {
1011                                Value::Null
1012                            } else {
1013                                sample(ty, rng)
1014                            }
1015                        })
1016                        .collect();
1017                    Vector::from_values(ty.clone(), &values).expect("a flat vector")
1018                };
1019                let left = make(&mut rng);
1020                let right = make(&mut rng);
1021                let constant = Vector::constant(ty.clone(), sample(ty, &mut rng), len);
1022                let null_constant = Vector::constant(ty.clone(), Value::Null, len);
1023                let codes: Vec<u32> =
1024                    (0..len).map(|_| rng.below(left.len() as u64) as u32).collect();
1025                let dictionary =
1026                    Vector::dictionary(codes, left.clone()).expect("codes are in range");
1027
1028                // Everything, every third row, a handful including the last one, and nothing,
1029                // which is the state a conjunct chain reaches as soon as one conjunct rejects a
1030                // whole chunk and is the case where the loop below must not read anything at all.
1031                let selections = [
1032                    Selection::identity(len),
1033                    Selection::from_indices((0..len as u32).filter(|row| row % 3 == 0).collect()),
1034                    Selection::from_indices(vec![2, 5, 6, 17, 36]),
1035                    Selection::empty(),
1036                ];
1037                for op in EVERY {
1038                    for kept in &selections {
1039                        threads(op, &left, &right, kept);
1040                        threads(op, &left, &constant, kept);
1041                        threads(op, &constant, &left, kept);
1042                        threads(op, &left, &null_constant, kept);
1043                        threads(op, &null_constant, &left, kept);
1044                        threads(op, &constant, &null_constant, kept);
1045                        threads(op, &dictionary, &constant, kept);
1046                        threads(op, &constant, &dictionary, kept);
1047                        threads(op, &dictionary, &right, kept);
1048                        threads(op, &right, &dictionary, kept);
1049                    }
1050                }
1051            }
1052        }
1053    }
1054
1055    /// Two conjuncts threaded one after the other are the rows both of them keep, which is the
1056    /// property the whole filter path rests on. The second comparison sees the rows the first one
1057    /// left and never looks at the others.
1058    #[test]
1059    fn a_second_conjunct_reads_only_what_the_first_one_left() {
1060        let numbers: Vec<Value> = (0..64).map(|row| Value::Integer(row % 10)).collect();
1061        let column = Vector::from_values(LogicalType::Integer, &numbers).expect("a flat vector");
1062        let three = Vector::constant(LogicalType::Integer, Value::Integer(3), 64);
1063        let seven = Vector::constant(LogicalType::Integer, Value::Integer(7), 64);
1064
1065        let first = refine(Comparison::Greater, &column, &three, &Selection::identity(64))
1066            .expect("compares");
1067        let both = refine(Comparison::Less, &column, &seven, &first).expect("compares");
1068
1069        let expected: Vec<u32> = (0..64)
1070            .filter(|row| {
1071                let value = row % 10;
1072                value > 3 && value < 7
1073            })
1074            .collect();
1075        assert_eq!(both.indices(), expected.as_slice());
1076        assert!(both.len() < first.len(), "the second conjunct narrowed the selection");
1077    }
1078
1079    /// A null is not a true, so a threaded comparison drops the row rather than keeping it with an
1080    /// unknown answer. This is the rule that makes `WHERE a < 5` leave out the rows where `a` is
1081    /// null, and it is the one a branchless loop gets wrong if the validity is left out of it.
1082    #[test]
1083    fn a_null_row_is_not_kept_by_an_ordinary_comparison_and_is_by_a_total_one() {
1084        let column = Vector::from_values(
1085            LogicalType::Integer,
1086            &[Value::Integer(1), Value::Null, Value::Integer(3), Value::Null],
1087        )
1088        .expect("four rows");
1089        let cut = Vector::constant(LogicalType::Integer, Value::Integer(2), 4);
1090        let all = Selection::identity(4);
1091        assert_eq!(
1092            refine(Comparison::Less, &column, &cut, &all).expect("compares").indices(),
1093            &[0]
1094        );
1095        // The total comparison has an answer at every row, so the two nulls are kept here.
1096        let nulls = Vector::constant(LogicalType::Integer, Value::Null, 4);
1097        assert_eq!(
1098            refine(Comparison::NotDistinctFrom, &column, &nulls, &all).expect("compares").indices(),
1099            &[1, 3]
1100        );
1101    }
1102
1103    #[test]
1104    fn a_selection_past_the_end_is_caught() {
1105        let column = Vector::constant(LogicalType::Integer, Value::Integer(1), 4);
1106        let past = Selection::from_indices(vec![0, 4]);
1107        let error = refine(Comparison::Equal, &column, &column, &past).expect_err("out of range");
1108        assert!(error.message().contains("4 row vector"), "{error}");
1109    }
1110
1111    /// One value of a type, for the generator above.
1112    fn sample(ty: &LogicalType, rng: &mut Rng) -> Value {
1113        match ty {
1114            LogicalType::Boolean => Value::Boolean(rng.below(2) == 1),
1115            LogicalType::TinyInt => Value::TinyInt(rng.below(7) as i8 - 3),
1116            LogicalType::SmallInt => Value::SmallInt(rng.below(11) as i16 - 5),
1117            LogicalType::Integer => Value::Integer(rng.below(9) as i32 - 4),
1118            LogicalType::BigInt => Value::BigInt(rng.below(9) as i64 - 4),
1119            LogicalType::HugeInt => Value::HugeInt(i128::from(rng.below(9)) - 4),
1120            LogicalType::UInteger => Value::UInteger(rng.below(9) as u32),
1121            // A NaN and a negative zero in the pool on purpose, because DuckDB's float order is
1122            // not IEEE's and the fast path has to reach the same answer the oracle does.
1123            LogicalType::Float => Value::Float(match rng.below(5) {
1124                0 => f32::NAN,
1125                1 => -0.0,
1126                other => other as f32 - 2.0,
1127            }),
1128            LogicalType::Double => Value::Double(match rng.below(5) {
1129                0 => f64::NAN,
1130                1 => -0.0,
1131                other => other as f64 - 2.0,
1132            }),
1133            // The same length written three ways and two lengths that are close to it, because an
1134            // interval that compares as a triple gets every pair here wrong and one that compares
1135            // as a length gets them right.
1136            LogicalType::Interval => match rng.below(6) {
1137                0 => Value::Interval { months: 0, days: 1, micros: 0 },
1138                1 => Value::Interval { months: 0, days: 0, micros: 86_400_000_000 },
1139                2 => Value::Interval { months: 1, days: -29, micros: 86_400_000_000 },
1140                3 => Value::Interval { months: 1, days: 0, micros: 0 },
1141                4 => Value::Interval { months: 0, days: 0, micros: 90_000_000_000 },
1142                _ => Value::Interval { months: -1, days: 0, micros: 0 },
1143            },
1144            // Short, at the inline limit, over it, and sharing a prefix with each other, which is
1145            // where a comparison that trusts the prefix too far goes wrong.
1146            LogicalType::Varchar => Value::Varchar(
1147                match rng.below(6) {
1148                    0 => "",
1149                    1 => "ab",
1150                    2 => "abc",
1151                    3 => "abcdefghijkl",
1152                    4 => "abcdefghijklm",
1153                    _ => "abcdefghijklmnopqrstuvwxyz",
1154                }
1155                .to_owned(),
1156            ),
1157            other => panic!("the generator has no values for {other}"),
1158        }
1159    }
1160
1161    /// The prefix lemma, written as a test because the whole string path rests on it. A view pads
1162    /// a short string with zeros, so prefix order has to agree with byte order on every pair where
1163    /// the prefixes differ, including the pairs where one string is shorter than four bytes.
1164    #[test]
1165    fn prefix_order_is_byte_order_whenever_the_prefixes_differ() {
1166        let words =
1167            ["", "a", "ab", "abc", "abcd", "abcde", "b", "abcdefghijklmnop", "abcdefghijklmnoq"];
1168        let mut column = StringColumn::new();
1169        for word in words {
1170            column.push(word);
1171        }
1172        for (i, one) in words.iter().enumerate() {
1173            for (j, other) in words.iter().enumerate() {
1174                assert_eq!(
1175                    string_order(&column, i, &column, j),
1176                    one.as_bytes().cmp(other.as_bytes()),
1177                    "{one:?} against {other:?}"
1178                );
1179            }
1180        }
1181    }
1182
1183    /// A dictionary is compared once per distinct value, not once per row, and it has to reach the
1184    /// same answer including for the nulls it keeps in the vector it points at.
1185    #[test]
1186    fn a_dictionary_against_a_constant_reads_its_nulls_from_the_values() {
1187        let values = Vector::from_values(
1188            LogicalType::Integer,
1189            &[Value::Integer(1), Value::Null, Value::Integer(9)],
1190        )
1191        .expect("three values");
1192        let dictionary =
1193            Vector::dictionary(vec![0, 1, 2, 1, 0], values).expect("codes are in range");
1194        let constant = Vector::constant(LogicalType::Integer, Value::Integer(5), 5);
1195        let result = compare(Comparison::Less, &dictionary, &constant).expect("compares");
1196        assert_eq!(result.value_at(0), Value::Boolean(true));
1197        assert_eq!(result.value_at(1), Value::Null);
1198        assert_eq!(result.value_at(2), Value::Boolean(false));
1199        assert_eq!(result.value_at(3), Value::Null);
1200        assert_eq!(result.value_at(4), Value::Boolean(true));
1201    }
1202
1203    /// A form pair with no loop is answered correctly and counted, which is the whole contract of
1204    /// the fallback counter. Sequence against a column is the one this file leaves out on purpose.
1205    #[test]
1206    fn a_form_pair_with_no_loop_is_still_right_and_says_so() {
1207        // The counters are per thread in a test build, so this reads its own and nothing else's.
1208        let before = fallback::count(Kernel::Compare, Form::Sequence, Form::Flat);
1209        let sequence = Vector::sequence(10, 1, 4);
1210        let flat = Vector::from_values(
1211            LogicalType::BigInt,
1212            &[Value::BigInt(9), Value::BigInt(11), Value::BigInt(12), Value::Null],
1213        )
1214        .expect("four rows");
1215        let result = compare(Comparison::Less, &sequence, &flat).expect("compares");
1216        assert_eq!(result.value_at(0), Value::Boolean(false));
1217        assert_eq!(result.value_at(1), Value::Boolean(false));
1218        assert_eq!(result.value_at(2), Value::Boolean(false));
1219        assert_eq!(result.value_at(3), Value::Null);
1220        assert!(fallback::count(Kernel::Compare, Form::Sequence, Form::Flat) > before);
1221    }
1222
1223    /// The reason `Vector::dictionary` composes rather than stacks, stated as the thing that breaks
1224    /// if it stops.
1225    ///
1226    /// Every loop in this file reaches for the values behind the codes with `Vector::data`, and a
1227    /// dictionary pointing at a dictionary has no data to hand back, so a second filter over an
1228    /// already filtered chunk used to turn every one of these kernels off and drop the comparison
1229    /// onto the row at a time path. Measured on server3 over a chunk of two numeric columns that was
1230    /// selected twice, that was 3.5 nanoseconds a row becoming 104, and a third and fourth level
1231    /// cost nothing more because the first one had already given up everything there was to give.
1232    #[test]
1233    fn a_second_level_of_codes_does_not_turn_the_loops_off() {
1234        let before = fallback::count(Kernel::Compare, Form::Dictionary, Form::Constant);
1235        let values = Vector::from_values(
1236            LogicalType::Integer,
1237            &[Value::Integer(1), Value::Integer(5), Value::Integer(9)],
1238        )
1239        .expect("three rows");
1240        let once = Vector::dictionary(vec![2, 1, 0], values).expect("codes are in range");
1241        let twice = Vector::dictionary(vec![1, 2], once).expect("codes are in range");
1242        let cut = Vector::constant(LogicalType::Integer, Value::Integer(4), 2);
1243        let result = compare(Comparison::Greater, &twice, &cut).expect("compares");
1244        assert_eq!(result.value_at(0), Value::Boolean(true));
1245        assert_eq!(result.value_at(1), Value::Boolean(false));
1246        assert_eq!(fallback::count(Kernel::Compare, Form::Dictionary, Form::Constant), before);
1247    }
1248
1249    /// Either side all null, on one of the six ordinary comparisons, is every answer null without
1250    /// the data being read. The vector this produces has to be the one the oracle produces, which
1251    /// is a flat run of falses under an all invalid validity rather than a constant.
1252    #[test]
1253    fn a_side_that_is_entirely_null_answers_without_reading_the_other() {
1254        let nulls = Vector::constant(LogicalType::Integer, Value::Null, 6);
1255        let flat = Vector::from_values(
1256            LogicalType::Integer,
1257            &[
1258                Value::Integer(1),
1259                Value::Integer(2),
1260                Value::Integer(3),
1261                Value::Integer(4),
1262                Value::Integer(5),
1263                Value::Integer(6),
1264            ],
1265        )
1266        .expect("six rows");
1267        agrees(Comparison::Less, &nulls, &flat);
1268        agrees(Comparison::Equal, &flat, &nulls);
1269        assert_eq!(
1270            compare(Comparison::Less, &nulls, &flat).expect("compares").validity(),
1271            &Validity::AllInvalid
1272        );
1273    }
1274
1275    /// An empty vector is not a special case anywhere, and the easiest way to keep it that way is
1276    /// to say so in a test rather than to find out from a panic in an operator.
1277    #[test]
1278    fn an_empty_comparison_is_an_empty_answer() {
1279        let left = Vector::from_values(LogicalType::Integer, &[]).expect("no rows");
1280        let right = Vector::constant(LogicalType::Integer, Value::Integer(1), 0);
1281        let result = compare(Comparison::Equal, &left, &right).expect("compares");
1282        assert_eq!(result.len(), 0);
1283    }
1284}