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