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, Packed, 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    // A bit packed column against a literal, which is the pair the form was added for. The literal
383    // is turned into a code once and then the loop compares codes, so nothing is unpacked at all,
384    // and a literal outside what the width can hold answers the whole vector without a bit of it
385    // being read. Only the six comparisons that go null on a null side come here, because the other
386    // two want the null rule inside the loop and this loop does not have it.
387    if !op.is_total() {
388        if let (Some(packed), Some(value)) = (left.packed_parts(), right.constant_value()) {
389            let wanted = exact(held, left.logical_type(), value)?;
390            return Some(packed_against(op, &packed, wanted, len, map));
391        }
392        if let (Some(value), Some(packed)) = (left.constant_value(), right.packed_parts()) {
393            let wanted = exact(held, right.logical_type(), value)?;
394            return Some(packed_against(op.swapped(), &packed, wanted, len, map));
395        }
396    }
397    if let (Some(one), Some(value)) = (left.data(), right.constant_value()) {
398        let column = readied(held, left.logical_type(), value)?;
399        let other = column.data()?;
400        return dispatch(op, len, one, map, other, first, left_valid, right_valid, map);
401    }
402    if let (Some(value), Some(other)) = (left.constant_value(), right.data()) {
403        // The same loop with the comparison turned around, rather than a second loop.
404        let column = readied(held, right.logical_type(), value)?;
405        let one = column.data()?;
406        return dispatch(op.swapped(), len, other, map, one, first, right_valid, left_valid, map);
407    }
408    if let (Some((codes, values)), Some(value)) = (left.positions(), right.constant_value()) {
409        let one = values.data()?;
410        let column = readied(held, left.logical_type(), value)?;
411        let other = column.data()?;
412        let at = |index: usize| codes[map(index)] as usize;
413        return dispatch(op, len, one, at, other, first, left_valid, right_valid, map);
414    }
415    if let (Some(value), Some((codes, values))) = (left.constant_value(), right.positions()) {
416        let other = values.data()?;
417        let column = readied(held, right.logical_type(), value)?;
418        let one = column.data()?;
419        let at = |index: usize| codes[map(index)] as usize;
420        return dispatch(op.swapped(), len, other, at, one, first, right_valid, left_valid, map);
421    }
422    // A dictionary against a flat column. This pair had no loop until the kernel table put a number
423    // on what that cost, which on `server3` was 83 nanoseconds a row against 1.2 for the dictionary
424    // against constant pair beside it, on the same data and the same operator. It is not a rare
425    // shape either: it is what a filtered column compared against an unfiltered one is, which is
426    // every conjunct after the first.
427    if let (Some((codes, values)), Some(other)) = (left.positions(), right.data()) {
428        let one = values.data()?;
429        let at = |index: usize| codes[map(index)] as usize;
430        return dispatch(op, len, one, at, other, map, left_valid, right_valid, map);
431    }
432    if let (Some(one), Some((codes, values))) = (left.data(), right.positions()) {
433        let other = values.data()?;
434        let at = |index: usize| codes[map(index)] as usize;
435        return dispatch(op.swapped(), len, other, at, one, map, right_valid, left_valid, map);
436    }
437    None
438}
439
440/// A literal as the whole number it is, and `None` for one that is not a whole number.
441///
442/// It goes through [`readied`] rather than reading the [`Value`] apart, so that a literal written
443/// as `900` against a `SMALLINT` column is narrowed by the same cast path every other comparison
444/// narrows it with. Reading the value apart here would be a second cast path with its own rounding
445/// and its own overflow rule, which is how two comparisons of the same literal end up disagreeing.
446fn exact(held: Option<&Held>, ty: &LogicalType, value: &Value) -> Option<i128> {
447    let column = readied(held, ty, value)?;
448    let data = column.data()?;
449    data.signed_at(0).or_else(|| data.unsigned_at(0).and_then(|value| i128::try_from(value).ok()))
450}
451
452/// A bit packed column against a literal, compared in the code space the column is already in.
453///
454/// The translation is one subtraction done once. After it the loop is a shift, a mask and a compare
455/// of two `u64`, which is what the flat loop would have been doing anyway minus the unpacking, so
456/// the form costs nothing on the operation a filter spends most of its time in.
457fn packed_against<M>(
458    op: Comparison,
459    packed: &Packed<'_>,
460    wanted: i128,
461    len: usize,
462    map: M,
463) -> Vec<bool>
464where
465    M: Fn(usize) -> usize + Copy,
466{
467    let Some(code) = packed.code_of(wanted) else {
468        // The literal is outside the range the width can hold, so every row answers the same way
469        // and the answer is arithmetic on two numbers rather than a pass over the column.
470        let above = wanted > packed.ceiling();
471        let same = match op {
472            Comparison::Equal | Comparison::NotDistinctFrom => false,
473            Comparison::NotEqual | Comparison::DistinctFrom => true,
474            Comparison::Less | Comparison::LessOrEqual => above,
475            Comparison::Greater | Comparison::GreaterOrEqual => !above,
476        };
477        return vec![same; len];
478    };
479    // The operator is decided before the loop rather than inside it, which is the same reason the
480    // generated loops take it as a function rather than matching per row.
481    let test: fn(u64, u64) -> bool = match op {
482        Comparison::Equal | Comparison::NotDistinctFrom => |found, want| found == want,
483        Comparison::NotEqual | Comparison::DistinctFrom => |found, want| found != want,
484        Comparison::Less => |found, want| found < want,
485        Comparison::LessOrEqual => |found, want| found <= want,
486        Comparison::Greater => |found, want| found > want,
487        Comparison::GreaterOrEqual => |found, want| found >= want,
488    };
489    let mut answers = Vec::with_capacity(len);
490    for row in 0..len {
491        answers.push(test(packed.code(map(row)), code));
492    }
493    answers
494}
495
496/// One loop per physical layout, generated rather than written out.
497///
498/// The two index closures are what let the same body serve flat against flat, a column against a
499/// constant and a dictionary against a constant. `identity` on both sides is the first, `first` on
500/// the right is the second, and the codes on the left are the third.
501#[expect(
502    clippy::too_many_arguments,
503    reason = "two sides with an index each, the operator, the length and two validities, all of \
504              which the loop needs and none of which is worth a struct that exists for one call"
505)]
506fn dispatch<L, R, V>(
507    op: Comparison,
508    len: usize,
509    left: &Data,
510    at_left: L,
511    right: &Data,
512    at_right: R,
513    left_valid: &Validity,
514    right_valid: &Validity,
515    at_valid: V,
516) -> Option<Vec<bool>>
517where
518    L: Fn(usize) -> usize,
519    R: Fn(usize) -> usize,
520    V: Fn(usize) -> usize,
521{
522    macro_rules! layouts {
523        ($(($variant:ident, $native:ty, $zero:expr)),+ $(,)?) => {
524            match (left, right) {
525                $(
526                    (Data::$variant(one), Data::$variant(other)) => Some(sweep(
527                        op,
528                        len,
529                        |index| one[at_left(index)].cmp(&other[at_right(index)]),
530                        left_valid,
531                        right_valid,
532                        &at_valid,
533                    )),
534                )+
535                // Floats have their own order, which is DuckDB's rather than IEEE's, and the
536                // widening on a `f32` is free because the comparison is against another `f32`.
537                (Data::Float32(one), Data::Float32(other)) => Some(sweep(
538                    op,
539                    len,
540                    |index| {
541                        float_order(
542                            f64::from(one[at_left(index)]),
543                            f64::from(other[at_right(index)]),
544                        )
545                    },
546                    left_valid,
547                    right_valid,
548                    &at_valid,
549                )),
550                (Data::Float64(one), Data::Float64(other)) => Some(sweep(
551                    op,
552                    len,
553                    |index| float_order(one[at_left(index)], other[at_right(index)]),
554                    left_valid,
555                    right_valid,
556                    &at_valid,
557                )),
558                // An interval is three counts and the order is over the one length they add up to,
559                // so this is not the derived order of the triple and cannot be generated above.
560                (Data::Interval(one), Data::Interval(other)) => Some(sweep(
561                    op,
562                    len,
563                    |index| {
564                        let (months, days, micros) = one[at_left(index)];
565                        let (bm, bd, bu) = other[at_right(index)];
566                        interval_micros(months, days, micros).cmp(&interval_micros(bm, bd, bu))
567                    },
568                    left_valid,
569                    right_valid,
570                    &at_valid,
571                )),
572                (Data::Varlen(one), Data::Varlen(other)) => Some(sweep(
573                    op,
574                    len,
575                    |index| string_order(one, at_left(index), other, at_right(index)),
576                    left_valid,
577                    right_valid,
578                    &at_valid,
579                )),
580                _ => None,
581            }
582        };
583    }
584    rudb_vector::for_each_layout!(ordered, layouts)
585}
586
587/// The one row column for a constant, either the one that was built early or one built here.
588///
589/// Borrowed when a caller handed one over for this side and this value, owned when it did not, and
590/// the loop below cannot tell the two apart. `None` is a type with no column layout, which is what
591/// sends the whole comparison to the row at a time path.
592fn readied<'a>(held: Option<&'a Held>, ty: &LogicalType, value: &Value) -> Option<Cow<'a, Vector>> {
593    match held {
594        Some(held) if held.matches(ty, value) => Some(Cow::Borrowed(held.single())),
595        _ => Some(Cow::Owned(single(ty, value)?)),
596    }
597}
598
599/// Two strings in byte order, resolved from the four byte prefix where it can be.
600///
601/// The lemma this rests on is that prefix order is byte order whenever the two prefixes differ. A
602/// view pads a string shorter than four bytes with zeros, zero is the least byte, and byte order
603/// says a string is less than any string that extends it, so padding compares the same way the
604/// missing bytes would have. When the prefixes are equal the payload settles it, which for an
605/// inline string is the same sixteen bytes already loaded and for a long one is a block read.
606fn string_order(
607    left: &StringColumn,
608    at_left: usize,
609    right: &StringColumn,
610    at_right: usize,
611) -> Ordering {
612    let (Some(one), Some(other)) = (left.views().get(at_left), right.views().get(at_right)) else {
613        return Ordering::Equal;
614    };
615    let (prefix, against) = (one.prefix(), other.prefix());
616    if prefix != against {
617        return prefix.cmp(&against);
618    }
619    // Bytes rather than `StringColumn::get`, which validates UTF-8. Everything in a column was
620    // pushed from a `&str` so the validation cannot fail, and on a URL column, where every row
621    // shares the `http` prefix and the payload therefore decides every comparison, it was the
622    // larger half of the per row cost.
623    let bytes = left.bytes(at_left).unwrap_or_default();
624    let against_bytes = right.bytes(at_right).unwrap_or_default();
625    bytes.cmp(against_bytes)
626}
627
628/// The answers for one ordering, with the operator decided once rather than once per row.
629///
630/// This is where the match on the operator gets hoisted. Each arm calls a generic `fill` with a
631/// different predicate, so the compiler produces eight loops whose bodies are an ordering against a
632/// constant, rather than one loop with a branch table in it.
633fn sweep<O, V>(
634    op: Comparison,
635    len: usize,
636    order_at: O,
637    left_valid: &Validity,
638    right_valid: &Validity,
639    at_valid: V,
640) -> Vec<bool>
641where
642    O: Fn(usize) -> Ordering,
643    V: Fn(usize) -> usize,
644{
645    let mut answers = vec![false; len];
646    match op {
647        Comparison::Equal => fill(&mut answers, order_at, |o| o == Ordering::Equal),
648        Comparison::NotEqual => fill(&mut answers, order_at, |o| o != Ordering::Equal),
649        Comparison::Less => fill(&mut answers, order_at, |o| o == Ordering::Less),
650        Comparison::LessOrEqual => fill(&mut answers, order_at, |o| o != Ordering::Greater),
651        Comparison::Greater => fill(&mut answers, order_at, |o| o == Ordering::Greater),
652        Comparison::GreaterOrEqual => fill(&mut answers, order_at, |o| o != Ordering::Less),
653        Comparison::DistinctFrom => {
654            total(&mut answers, order_at, left_valid, right_valid, at_valid);
655            for answer in &mut answers {
656                *answer = !*answer;
657            }
658        }
659        Comparison::NotDistinctFrom => {
660            total(&mut answers, order_at, left_valid, right_valid, at_valid);
661        }
662    }
663    answers
664}
665
666/// One loop, one predicate, no branch on the operator.
667#[inline]
668fn fill<O, H>(answers: &mut [bool], order_at: O, held: H)
669where
670    O: Fn(usize) -> Ordering,
671    H: Fn(Ordering) -> bool,
672{
673    for (index, answer) in answers.iter_mut().enumerate() {
674        *answer = held(order_at(index));
675    }
676}
677
678/// `IS NOT DISTINCT FROM`, which reads validity as data rather than as an absence.
679///
680/// Two nulls are the same value here and a null against anything else is not, which is the whole
681/// difference between this and `=`. The all valid case is checked once so that the common shape,
682/// which is a total comparison inside a join on columns that happen not to be nullable, does not
683/// pay for two validity lookups per row.
684fn total<O, V>(
685    answers: &mut [bool],
686    order_at: O,
687    left_valid: &Validity,
688    right_valid: &Validity,
689    at_valid: V,
690) where
691    O: Fn(usize) -> Ordering,
692    V: Fn(usize) -> usize,
693{
694    if *left_valid == Validity::AllValid && *right_valid == Validity::AllValid {
695        fill(answers, order_at, |o| o == Ordering::Equal);
696        return;
697    }
698    for (index, answer) in answers.iter_mut().enumerate() {
699        let row = at_valid(index);
700        *answer = match (left_valid.is_valid(row), right_valid.is_valid(row)) {
701            (true, true) => order_at(index) == Ordering::Equal,
702            (false, false) => true,
703            _ => false,
704        };
705    }
706}
707
708/// Compares two values, producing `TRUE`, `FALSE` or `NULL`.
709///
710/// # Errors
711///
712/// If the two types cannot be compared, which after binding means one of them is a nested type.
713pub fn compare_values(op: Comparison, left: &Value, right: &Value) -> Result<Value> {
714    if op.is_total() {
715        let same = match (left.is_null(), right.is_null()) {
716            (true, true) => true,
717            (true, false) | (false, true) => false,
718            (false, false) => order(left, right)? == Ordering::Equal,
719        };
720        return Ok(Value::Boolean(match op {
721            Comparison::NotDistinctFrom => same,
722            _ => !same,
723        }));
724    }
725    if left.is_null() || right.is_null() {
726        return Ok(Value::Null);
727    }
728    let ordering = order(left, right)?;
729    let held = match op {
730        Comparison::Equal => ordering == Ordering::Equal,
731        Comparison::NotEqual => ordering != Ordering::Equal,
732        Comparison::Less => ordering == Ordering::Less,
733        Comparison::LessOrEqual => ordering != Ordering::Greater,
734        Comparison::Greater => ordering == Ordering::Greater,
735        Comparison::GreaterOrEqual => ordering != Ordering::Less,
736        Comparison::DistinctFrom | Comparison::NotDistinctFrom => {
737            return Err(Error::internal("a total comparison reached the ordered path"));
738        }
739    };
740    Ok(Value::Boolean(held))
741}
742
743/// The order of two values, neither of which is null.
744///
745/// This is the one place the sort order of a type is written down. `ORDER BY`, `GROUP BY`, a merge
746/// join and a min or max aggregate all reach it, and a type that ordered differently in two of
747/// those would produce a query whose answer depends on which operator the optimizer picked.
748///
749/// # Errors
750///
751/// If either value is null, which is the caller's mistake rather than a comparison, or if the
752/// types have no order between them.
753pub fn order(left: &Value, right: &Value) -> Result<Ordering> {
754    match (left, right) {
755        (Value::Null, _) | (_, Value::Null) => {
756            Err(Error::internal("a null reached the ordering path"))
757        }
758        (Value::Boolean(a), Value::Boolean(b)) => Ok(a.cmp(b)),
759        (Value::Varchar(a), Value::Varchar(b)) => Ok(a.as_bytes().cmp(b.as_bytes())),
760        (Value::Blob(a), Value::Blob(b)) => Ok(a.cmp(b)),
761        (Value::Date(a), Value::Date(b)) => Ok(a.cmp(b)),
762        (Value::Time(a), Value::Time(b)) | (Value::Timestamp(a), Value::Timestamp(b)) => {
763            Ok(a.cmp(b))
764        }
765        (
766            Value::Interval { months: am, days: ad, micros: au },
767            Value::Interval { months: bm, days: bd, micros: bu },
768        ) => Ok(interval_micros(*am, *ad, *au).cmp(&interval_micros(*bm, *bd, *bu))),
769        _ => numeric_order(left, right),
770    }
771}
772
773/// The order of two numbers, which is the case that has to work across representations.
774fn numeric_order(left: &Value, right: &Value) -> Result<Ordering> {
775    if let (Some(a), Some(b)) = (integral(left), integral(right)) {
776        return Ok(a.cmp(&b));
777    }
778    if let (
779        Value::Decimal { unscaled: a, scale: sa, .. },
780        Value::Decimal { unscaled: b, scale: sb, .. },
781    ) = (left, right)
782    {
783        if sa == sb {
784            return Ok(a.cmp(b));
785        }
786    }
787    match (approximate(left), approximate(right)) {
788        (Some(a), Some(b)) => Ok(float_order(a, b)),
789        _ => Err(Error::not_implemented(format!(
790            "comparing {} with {}",
791            left.logical_type(),
792            right.logical_type()
793        ))),
794    }
795}
796
797/// DuckDB's float order: NaN is equal to itself and above everything else, and zero has one place.
798fn float_order(left: f64, right: f64) -> Ordering {
799    if left == right {
800        return Ordering::Equal;
801    }
802    match (left.is_nan(), right.is_nan()) {
803        (true, true) => Ordering::Equal,
804        (true, false) => Ordering::Greater,
805        (false, true) => Ordering::Less,
806        (false, false) => left.partial_cmp(&right).unwrap_or(Ordering::Equal),
807    }
808}
809
810/// The order of two values with nulls in it, for a sort key.
811///
812/// A sort has to put nulls somewhere and SQL lets the query say where, so this takes the answer
813/// rather than deciding it.
814///
815/// # Errors
816///
817/// If the two types have no order between them.
818pub fn order_with_nulls(left: &Value, right: &Value, nulls_first: bool) -> Result<Ordering> {
819    match (left.is_null(), right.is_null()) {
820        (true, true) => Ok(Ordering::Equal),
821        (true, false) => Ok(if nulls_first { Ordering::Less } else { Ordering::Greater }),
822        (false, true) => Ok(if nulls_first { Ordering::Greater } else { Ordering::Less }),
823        (false, false) => order(left, right),
824    }
825}
826
827#[cfg(test)]
828mod tests {
829    use super::*;
830
831    fn compared(op: Comparison, left: Value, right: Value) -> Value {
832        compare_values(op, &left, &right).expect("these types compare")
833    }
834
835    /// Every comparison, so that a test that sweeps them cannot quietly miss one.
836    const EVERY: [Comparison; 8] = [
837        Comparison::Equal,
838        Comparison::NotEqual,
839        Comparison::Less,
840        Comparison::LessOrEqual,
841        Comparison::Greater,
842        Comparison::GreaterOrEqual,
843        Comparison::DistinctFrom,
844        Comparison::NotDistinctFrom,
845    ];
846
847    /// The row at a time path, kept as the oracle rather than deleted.
848    ///
849    /// `spec/engine/03-data-plane.md` is explicit that the slow path becomes the thing the fast
850    /// path is checked against. This is that, written out here so that a test can call it on a pair
851    /// of vectors whose forms the fast path does specialize.
852    fn oracle(op: Comparison, left: &Vector, right: &Vector) -> Vector {
853        let values: Vec<Value> = (0..left.len())
854            .map(|index| {
855                compare_values(op, &left.value_at(index), &right.value_at(index))
856                    .expect("the oracle is only asked about types that compare")
857            })
858            .collect();
859        Vector::from_values(LogicalType::Boolean, &values).expect("booleans")
860    }
861
862    /// Asserts that the specialized path and the oracle produce the same vector, not merely the
863    /// same answers. Same vector means the same data, the same validity representation and the
864    /// same false in every null position, which is a much stronger statement and is free to check.
865    fn agrees(op: Comparison, left: &Vector, right: &Vector) {
866        let fast = compare(op, left, right).expect("compares");
867        let slow = oracle(op, left, right);
868        assert_eq!(fast, slow, "{op:?} on a {:?} against a {:?}", left.form(), right.form());
869    }
870
871    /// A small deterministic generator, because a property test with no seed is a test that fails
872    /// on somebody else's machine and passes on yours.
873    struct Rng(u64);
874
875    impl Rng {
876        fn next(&mut self) -> u64 {
877            self.0 ^= self.0 << 13;
878            self.0 ^= self.0 >> 7;
879            self.0 ^= self.0 << 17;
880            self.0
881        }
882
883        fn below(&mut self, bound: u64) -> u64 {
884            self.next() % bound
885        }
886    }
887
888    #[test]
889    fn an_ordinary_comparison_is_null_when_either_side_is() {
890        assert_eq!(compared(Comparison::Equal, Value::Integer(1), Value::Null), Value::Null);
891        assert_eq!(compared(Comparison::Less, Value::Null, Value::Integer(1)), Value::Null);
892    }
893
894    #[test]
895    fn a_total_comparison_is_never_null() {
896        assert_eq!(
897            compared(Comparison::NotDistinctFrom, Value::Null, Value::Null),
898            Value::Boolean(true)
899        );
900        assert_eq!(
901            compared(Comparison::NotDistinctFrom, Value::Integer(1), Value::Null),
902            Value::Boolean(false)
903        );
904        assert_eq!(
905            compared(Comparison::DistinctFrom, Value::Integer(1), Value::Null),
906            Value::Boolean(true)
907        );
908    }
909
910    #[test]
911    fn a_string_compares_by_bytes() {
912        assert_eq!(
913            compared(Comparison::Less, Value::Varchar("a".into()), Value::Varchar("b".into())),
914            Value::Boolean(true)
915        );
916        assert_eq!(
917            compared(Comparison::Less, Value::Varchar("Z".into()), Value::Varchar("a".into())),
918            Value::Boolean(true)
919        );
920    }
921
922    /// The reason this crate does not use `f64::partial_cmp` directly. A NaN that compared
923    /// unordered would make a group by produce a group nothing can find again.
924    #[test]
925    fn two_nans_are_one_value_and_they_sort_above_the_numbers() {
926        assert_eq!(
927            compared(Comparison::Equal, Value::Double(f64::NAN), Value::Double(f64::NAN)),
928            Value::Boolean(true)
929        );
930        assert_eq!(
931            compared(Comparison::Greater, Value::Double(f64::NAN), Value::Double(1e300)),
932            Value::Boolean(true)
933        );
934    }
935
936    #[test]
937    fn zero_has_one_value_however_it_is_signed() {
938        assert_eq!(
939            compared(Comparison::Equal, Value::Double(0.0), Value::Double(-0.0)),
940            Value::Boolean(true)
941        );
942    }
943
944    /// An interval is three counts and two of them that are the same length are one value, at
945    /// thirty days to a month and twenty four hours to a day, which is what upstream answers. The
946    /// three counts are still kept apart, because adding a month to a date is not adding thirty
947    /// days to it, so these pairs are equal and print differently.
948    #[test]
949    fn two_intervals_of_the_same_length_are_one_value() {
950        let day = Value::Interval { months: 0, days: 1, micros: 0 };
951        let hours = Value::Interval { months: 0, days: 0, micros: 86_400_000_000 };
952        let month = Value::Interval { months: 1, days: 0, micros: 0 };
953        let thirty = Value::Interval { months: 0, days: 30, micros: 0 };
954        let long_day = Value::Interval { months: 0, days: 0, micros: 90_000_000_000 };
955        assert_eq!(compared(Comparison::Equal, day.clone(), hours), Value::Boolean(true));
956        assert_eq!(compared(Comparison::Equal, month, thirty), Value::Boolean(true));
957        assert_eq!(compared(Comparison::Greater, long_day, day), Value::Boolean(true));
958    }
959
960    #[test]
961    fn a_number_compares_the_same_however_it_is_stored() {
962        assert_eq!(
963            compared(Comparison::Equal, Value::Integer(3), Value::BigInt(3)),
964            Value::Boolean(true)
965        );
966        assert_eq!(
967            compared(Comparison::Less, Value::Integer(3), Value::Double(3.5)),
968            Value::Boolean(true)
969        );
970    }
971
972    #[test]
973    fn nulls_go_where_the_query_asked_for_them() {
974        assert_eq!(
975            order_with_nulls(&Value::Null, &Value::Integer(1), true).expect("orders"),
976            Ordering::Less
977        );
978        assert_eq!(
979            order_with_nulls(&Value::Null, &Value::Integer(1), false).expect("orders"),
980            Ordering::Greater
981        );
982    }
983
984    #[test]
985    fn two_constant_vectors_cost_one_comparison() {
986        let left = Vector::constant(LogicalType::Integer, Value::Integer(1), 512);
987        let right = Vector::constant(LogicalType::Integer, Value::Integer(2), 512);
988        let result = compare(Comparison::Less, &left, &right).expect("compares");
989        assert_eq!(result.form(), Form::Constant);
990        assert_eq!(result.value_at(500), Value::Boolean(true));
991    }
992
993    #[test]
994    fn a_comparison_of_two_vectors_is_one_answer_per_row() {
995        let left = Vector::from_values(
996            LogicalType::Integer,
997            &[Value::Integer(1), Value::Integer(5), Value::Null],
998        )
999        .expect("three rows");
1000        let right = Vector::constant(LogicalType::Integer, Value::Integer(3), 3);
1001        let result = compare(Comparison::Greater, &left, &right).expect("compares");
1002        assert_eq!(result.value_at(0), Value::Boolean(false));
1003        assert_eq!(result.value_at(1), Value::Boolean(true));
1004        assert_eq!(result.value_at(2), Value::Null);
1005    }
1006
1007    #[test]
1008    fn two_vectors_of_different_lengths_are_caught() {
1009        let left = Vector::constant(LogicalType::Integer, Value::Integer(1), 4);
1010        let right = Vector::constant(LogicalType::Integer, Value::Integer(1), 5);
1011        let error = compare(Comparison::Equal, &left, &right).expect_err("ragged");
1012        assert!(error.message().contains("4 row vector"), "{error}");
1013    }
1014
1015    #[test]
1016    fn turning_a_comparison_around_is_what_the_other_side_would_have_said() {
1017        for op in EVERY {
1018            let left = Value::Integer(3);
1019            let right = Value::Integer(7);
1020            assert_eq!(
1021                compare_values(op, &left, &right).expect("compares"),
1022                compare_values(op.swapped(), &right, &left).expect("compares"),
1023                "{op:?}"
1024            );
1025        }
1026    }
1027
1028    /// The whole point of the rewrite, stated as a property. Every operator, every physical
1029    /// layout, every form pair the fast path claims, against the row at a time oracle.
1030    #[test]
1031    fn every_specialized_path_agrees_with_the_row_at_a_time_path() {
1032        let mut rng = Rng(0x5eed_1234_9876_4321);
1033        let types: [LogicalType; 11] = [
1034            LogicalType::Boolean,
1035            LogicalType::TinyInt,
1036            LogicalType::SmallInt,
1037            LogicalType::Integer,
1038            LogicalType::BigInt,
1039            LogicalType::HugeInt,
1040            LogicalType::UInteger,
1041            LogicalType::Float,
1042            LogicalType::Double,
1043            LogicalType::Varchar,
1044            LogicalType::Interval,
1045        ];
1046        for ty in &types {
1047            for nulls in [0u64, 1, 3] {
1048                let len = 37;
1049                let make = |rng: &mut Rng| {
1050                    let values: Vec<Value> = (0..len)
1051                        .map(|_| {
1052                            if nulls > 0 && rng.below(nulls + 1) == 0 {
1053                                Value::Null
1054                            } else {
1055                                sample(ty, rng)
1056                            }
1057                        })
1058                        .collect();
1059                    Vector::from_values(ty.clone(), &values).expect("a flat vector")
1060                };
1061                let left = make(&mut rng);
1062                let right = make(&mut rng);
1063                let literal = sample(ty, &mut rng);
1064                let constant = Vector::constant(ty.clone(), literal, len);
1065                let null_constant = Vector::constant(ty.clone(), Value::Null, len);
1066                let codes: Vec<u32> =
1067                    (0..len).map(|_| rng.below(left.len() as u64) as u32).collect();
1068                let dictionary =
1069                    Vector::dictionary(codes, left.clone()).expect("codes are in range");
1070                // Runs over the same values, with the last one cut short so that a run boundary
1071                // does not land on the end of the vector.
1072                let ends: Vec<u32> = (1..=left.len())
1073                    .map(|run| ((run * len) / left.len()).max(run) as u32)
1074                    .collect();
1075                let runs = Vector::runs(ends, left.clone()).expect("one value for each run");
1076
1077                for op in EVERY {
1078                    agrees(op, &left, &right);
1079                    agrees(op, &left, &constant);
1080                    agrees(op, &constant, &left);
1081                    agrees(op, &left, &null_constant);
1082                    agrees(op, &null_constant, &left);
1083                    agrees(op, &dictionary, &constant);
1084                    agrees(op, &constant, &dictionary);
1085                    // The dictionary against a flat column, which reads a null from either side and
1086                    // from the dictionary's values as well, so it is the pair with the most ways to
1087                    // disagree with the oracle and the one that got a loop last.
1088                    agrees(op, &dictionary, &right);
1089                    agrees(op, &right, &dictionary);
1090                    // The same four pairings for run length, which reaches the same loops through
1091                    // the same accessor, so what is being checked is that the positions it works
1092                    // out are the positions the row at a time path reads.
1093                    agrees(op, &runs, &constant);
1094                    agrees(op, &constant, &runs);
1095                    agrees(op, &runs, &right);
1096                    agrees(op, &right, &runs);
1097                }
1098            }
1099        }
1100    }
1101
1102    /// The rows of a selection the row at a time path keeps, which is what [`refine`] has to say.
1103    fn refined(op: Comparison, left: &Vector, right: &Vector, kept: &Selection) -> Selection {
1104        let mut out = Vec::new();
1105        for &row in kept.indices() {
1106            let index = row as usize;
1107            let answer = compare_values(op, &left.value_at(index), &right.value_at(index))
1108                .expect("the oracle is only asked about types that compare");
1109            if is_true(&answer) {
1110                out.push(row);
1111            }
1112        }
1113        Selection::from_indices(out)
1114    }
1115
1116    fn threads(op: Comparison, left: &Vector, right: &Vector, kept: &Selection) {
1117        let fast = refine(op, left, right, kept).expect("compares");
1118        assert_eq!(
1119            fast,
1120            refined(op, left, right, kept),
1121            "{op:?} on a {:?} against a {:?} over {} rows",
1122            left.form(),
1123            right.form(),
1124            kept.len()
1125        );
1126    }
1127
1128    /// Threading a selection through a comparison is the same rows as comparing everything and
1129    /// then keeping the ones that were already kept. Every operator, every form pair that has a
1130    /// loop, at four densities of selection, against the row at a time path.
1131    #[test]
1132    fn a_threaded_comparison_keeps_what_the_row_at_a_time_path_keeps() {
1133        let mut rng = Rng(0x5eed_4321_1234_9876);
1134        let types = [LogicalType::Integer, LogicalType::Double, LogicalType::Varchar];
1135        for ty in &types {
1136            for nulls in [0u64, 1, 3] {
1137                let len = 37;
1138                let make = |rng: &mut Rng| {
1139                    let values: Vec<Value> = (0..len)
1140                        .map(|_| {
1141                            if nulls > 0 && rng.below(nulls + 1) == 0 {
1142                                Value::Null
1143                            } else {
1144                                sample(ty, rng)
1145                            }
1146                        })
1147                        .collect();
1148                    Vector::from_values(ty.clone(), &values).expect("a flat vector")
1149                };
1150                let left = make(&mut rng);
1151                let right = make(&mut rng);
1152                let constant = Vector::constant(ty.clone(), sample(ty, &mut rng), len);
1153                let null_constant = Vector::constant(ty.clone(), Value::Null, len);
1154                let codes: Vec<u32> =
1155                    (0..len).map(|_| rng.below(left.len() as u64) as u32).collect();
1156                let dictionary =
1157                    Vector::dictionary(codes, left.clone()).expect("codes are in range");
1158
1159                // Everything, every third row, a handful including the last one, and nothing,
1160                // which is the state a conjunct chain reaches as soon as one conjunct rejects a
1161                // whole chunk and is the case where the loop below must not read anything at all.
1162                let selections = [
1163                    Selection::identity(len),
1164                    Selection::from_indices((0..len as u32).filter(|row| row % 3 == 0).collect()),
1165                    Selection::from_indices(vec![2, 5, 6, 17, 36]),
1166                    Selection::empty(),
1167                ];
1168                for op in EVERY {
1169                    for kept in &selections {
1170                        threads(op, &left, &right, kept);
1171                        threads(op, &left, &constant, kept);
1172                        threads(op, &constant, &left, kept);
1173                        threads(op, &left, &null_constant, kept);
1174                        threads(op, &null_constant, &left, kept);
1175                        threads(op, &constant, &null_constant, kept);
1176                        threads(op, &dictionary, &constant, kept);
1177                        threads(op, &constant, &dictionary, kept);
1178                        threads(op, &dictionary, &right, kept);
1179                        threads(op, &right, &dictionary, kept);
1180                    }
1181                }
1182            }
1183        }
1184    }
1185
1186    /// Two conjuncts threaded one after the other are the rows both of them keep, which is the
1187    /// property the whole filter path rests on. The second comparison sees the rows the first one
1188    /// left and never looks at the others.
1189    #[test]
1190    fn a_second_conjunct_reads_only_what_the_first_one_left() {
1191        let numbers: Vec<Value> = (0..64).map(|row| Value::Integer(row % 10)).collect();
1192        let column = Vector::from_values(LogicalType::Integer, &numbers).expect("a flat vector");
1193        let three = Vector::constant(LogicalType::Integer, Value::Integer(3), 64);
1194        let seven = Vector::constant(LogicalType::Integer, Value::Integer(7), 64);
1195
1196        let first = refine(Comparison::Greater, &column, &three, &Selection::identity(64))
1197            .expect("compares");
1198        let both = refine(Comparison::Less, &column, &seven, &first).expect("compares");
1199
1200        let expected: Vec<u32> = (0..64)
1201            .filter(|row| {
1202                let value = row % 10;
1203                value > 3 && value < 7
1204            })
1205            .collect();
1206        assert_eq!(both.indices(), expected.as_slice());
1207        assert!(both.len() < first.len(), "the second conjunct narrowed the selection");
1208    }
1209
1210    /// A null is not a true, so a threaded comparison drops the row rather than keeping it with an
1211    /// unknown answer. This is the rule that makes `WHERE a < 5` leave out the rows where `a` is
1212    /// null, and it is the one a branchless loop gets wrong if the validity is left out of it.
1213    #[test]
1214    fn a_null_row_is_not_kept_by_an_ordinary_comparison_and_is_by_a_total_one() {
1215        let column = Vector::from_values(
1216            LogicalType::Integer,
1217            &[Value::Integer(1), Value::Null, Value::Integer(3), Value::Null],
1218        )
1219        .expect("four rows");
1220        let cut = Vector::constant(LogicalType::Integer, Value::Integer(2), 4);
1221        let all = Selection::identity(4);
1222        assert_eq!(
1223            refine(Comparison::Less, &column, &cut, &all).expect("compares").indices(),
1224            &[0]
1225        );
1226        // The total comparison has an answer at every row, so the two nulls are kept here.
1227        let nulls = Vector::constant(LogicalType::Integer, Value::Null, 4);
1228        assert_eq!(
1229            refine(Comparison::NotDistinctFrom, &column, &nulls, &all).expect("compares").indices(),
1230            &[1, 3]
1231        );
1232    }
1233
1234    #[test]
1235    fn a_selection_past_the_end_is_caught() {
1236        let column = Vector::constant(LogicalType::Integer, Value::Integer(1), 4);
1237        let past = Selection::from_indices(vec![0, 4]);
1238        let error = refine(Comparison::Equal, &column, &column, &past).expect_err("out of range");
1239        assert!(error.message().contains("4 row vector"), "{error}");
1240    }
1241
1242    /// One value of a type, for the generator above.
1243    fn sample(ty: &LogicalType, rng: &mut Rng) -> Value {
1244        match ty {
1245            LogicalType::Boolean => Value::Boolean(rng.below(2) == 1),
1246            LogicalType::TinyInt => Value::TinyInt(rng.below(7) as i8 - 3),
1247            LogicalType::SmallInt => Value::SmallInt(rng.below(11) as i16 - 5),
1248            LogicalType::Integer => Value::Integer(rng.below(9) as i32 - 4),
1249            LogicalType::BigInt => Value::BigInt(rng.below(9) as i64 - 4),
1250            LogicalType::HugeInt => Value::HugeInt(i128::from(rng.below(9)) - 4),
1251            LogicalType::UInteger => Value::UInteger(rng.below(9) as u32),
1252            // A NaN and a negative zero in the pool on purpose, because DuckDB's float order is
1253            // not IEEE's and the fast path has to reach the same answer the oracle does.
1254            LogicalType::Float => Value::Float(match rng.below(5) {
1255                0 => f32::NAN,
1256                1 => -0.0,
1257                other => other as f32 - 2.0,
1258            }),
1259            LogicalType::Double => Value::Double(match rng.below(5) {
1260                0 => f64::NAN,
1261                1 => -0.0,
1262                other => other as f64 - 2.0,
1263            }),
1264            // The same length written three ways and two lengths that are close to it, because an
1265            // interval that compares as a triple gets every pair here wrong and one that compares
1266            // as a length gets them right.
1267            LogicalType::Interval => match rng.below(6) {
1268                0 => Value::Interval { months: 0, days: 1, micros: 0 },
1269                1 => Value::Interval { months: 0, days: 0, micros: 86_400_000_000 },
1270                2 => Value::Interval { months: 1, days: -29, micros: 86_400_000_000 },
1271                3 => Value::Interval { months: 1, days: 0, micros: 0 },
1272                4 => Value::Interval { months: 0, days: 0, micros: 90_000_000_000 },
1273                _ => Value::Interval { months: -1, days: 0, micros: 0 },
1274            },
1275            // Short, at the inline limit, over it, and sharing a prefix with each other, which is
1276            // where a comparison that trusts the prefix too far goes wrong.
1277            LogicalType::Varchar => Value::Varchar(
1278                match rng.below(6) {
1279                    0 => "",
1280                    1 => "ab",
1281                    2 => "abc",
1282                    3 => "abcdefghijkl",
1283                    4 => "abcdefghijklm",
1284                    _ => "abcdefghijklmnopqrstuvwxyz",
1285                }
1286                .to_owned(),
1287            ),
1288            other => panic!("the generator has no values for {other}"),
1289        }
1290    }
1291
1292    /// The prefix lemma, written as a test because the whole string path rests on it. A view pads
1293    /// a short string with zeros, so prefix order has to agree with byte order on every pair where
1294    /// the prefixes differ, including the pairs where one string is shorter than four bytes.
1295    #[test]
1296    fn prefix_order_is_byte_order_whenever_the_prefixes_differ() {
1297        let words =
1298            ["", "a", "ab", "abc", "abcd", "abcde", "b", "abcdefghijklmnop", "abcdefghijklmnoq"];
1299        let mut column = StringColumn::new();
1300        for word in words {
1301            column.push(word);
1302        }
1303        for (i, one) in words.iter().enumerate() {
1304            for (j, other) in words.iter().enumerate() {
1305                assert_eq!(
1306                    string_order(&column, i, &column, j),
1307                    one.as_bytes().cmp(other.as_bytes()),
1308                    "{one:?} against {other:?}"
1309                );
1310            }
1311        }
1312    }
1313
1314    /// A dictionary is compared once per distinct value, not once per row, and it has to reach the
1315    /// same answer including for the nulls it keeps in the vector it points at.
1316    #[test]
1317    fn a_dictionary_against_a_constant_reads_its_nulls_from_the_values() {
1318        let values = Vector::from_values(
1319            LogicalType::Integer,
1320            &[Value::Integer(1), Value::Null, Value::Integer(9)],
1321        )
1322        .expect("three values");
1323        let dictionary =
1324            Vector::dictionary(vec![0, 1, 2, 1, 0], values).expect("codes are in range");
1325        let constant = Vector::constant(LogicalType::Integer, Value::Integer(5), 5);
1326        let result = compare(Comparison::Less, &dictionary, &constant).expect("compares");
1327        assert_eq!(result.value_at(0), Value::Boolean(true));
1328        assert_eq!(result.value_at(1), Value::Null);
1329        assert_eq!(result.value_at(2), Value::Boolean(false));
1330        assert_eq!(result.value_at(3), Value::Null);
1331        assert_eq!(result.value_at(4), Value::Boolean(true));
1332    }
1333
1334    /// A form pair with no loop is answered correctly and counted, which is the whole contract of
1335    /// the fallback counter. Sequence against a column is the one this file leaves out on purpose.
1336    #[test]
1337    fn a_form_pair_with_no_loop_is_still_right_and_says_so() {
1338        // The counters are per thread in a test build, so this reads its own and nothing else's.
1339        let before = fallback::count(Kernel::Compare, Form::Sequence, Form::Flat);
1340        let sequence = Vector::sequence(10, 1, 4);
1341        let flat = Vector::from_values(
1342            LogicalType::BigInt,
1343            &[Value::BigInt(9), Value::BigInt(11), Value::BigInt(12), Value::Null],
1344        )
1345        .expect("four rows");
1346        let result = compare(Comparison::Less, &sequence, &flat).expect("compares");
1347        assert_eq!(result.value_at(0), Value::Boolean(false));
1348        assert_eq!(result.value_at(1), Value::Boolean(false));
1349        assert_eq!(result.value_at(2), Value::Boolean(false));
1350        assert_eq!(result.value_at(3), Value::Null);
1351        assert!(fallback::count(Kernel::Compare, Form::Sequence, Form::Flat) > before);
1352    }
1353
1354    /// The reason `Vector::dictionary` composes rather than stacks, stated as the thing that breaks
1355    /// if it stops.
1356    ///
1357    /// Every loop in this file reaches for the values behind the codes with `Vector::data`, and a
1358    /// dictionary pointing at a dictionary has no data to hand back, so a second filter over an
1359    /// already filtered chunk used to turn every one of these kernels off and drop the comparison
1360    /// onto the row at a time path. Measured on server3 over a chunk of two numeric columns that was
1361    /// selected twice, that was 3.5 nanoseconds a row becoming 104, and a third and fourth level
1362    /// cost nothing more because the first one had already given up everything there was to give.
1363    #[test]
1364    fn a_second_level_of_codes_does_not_turn_the_loops_off() {
1365        let before = fallback::count(Kernel::Compare, Form::Dictionary, Form::Constant);
1366        let values = Vector::from_values(
1367            LogicalType::Integer,
1368            &[Value::Integer(1), Value::Integer(5), Value::Integer(9)],
1369        )
1370        .expect("three rows");
1371        let once = Vector::dictionary(vec![2, 1, 0], values).expect("codes are in range");
1372        let twice = Vector::dictionary(vec![1, 2], once).expect("codes are in range");
1373        let cut = Vector::constant(LogicalType::Integer, Value::Integer(4), 2);
1374        let result = compare(Comparison::Greater, &twice, &cut).expect("compares");
1375        assert_eq!(result.value_at(0), Value::Boolean(true));
1376        assert_eq!(result.value_at(1), Value::Boolean(false));
1377        assert_eq!(fallback::count(Kernel::Compare, Form::Dictionary, Form::Constant), before);
1378    }
1379
1380    /// Either side all null, on one of the six ordinary comparisons, is every answer null without
1381    /// the data being read. The vector this produces has to be the one the oracle produces, which
1382    /// is a flat run of falses under an all invalid validity rather than a constant.
1383    #[test]
1384    fn a_side_that_is_entirely_null_answers_without_reading_the_other() {
1385        let nulls = Vector::constant(LogicalType::Integer, Value::Null, 6);
1386        let flat = Vector::from_values(
1387            LogicalType::Integer,
1388            &[
1389                Value::Integer(1),
1390                Value::Integer(2),
1391                Value::Integer(3),
1392                Value::Integer(4),
1393                Value::Integer(5),
1394                Value::Integer(6),
1395            ],
1396        )
1397        .expect("six rows");
1398        agrees(Comparison::Less, &nulls, &flat);
1399        agrees(Comparison::Equal, &flat, &nulls);
1400        assert_eq!(
1401            compare(Comparison::Less, &nulls, &flat).expect("compares").validity(),
1402            &Validity::AllInvalid
1403        );
1404    }
1405
1406    /// An empty vector is not a special case anywhere, and the easiest way to keep it that way is
1407    /// to say so in a test rather than to find out from a panic in an operator.
1408    #[test]
1409    fn an_empty_comparison_is_an_empty_answer() {
1410        let left = Vector::from_values(LogicalType::Integer, &[]).expect("no rows");
1411        let right = Vector::constant(LogicalType::Integer, Value::Integer(1), 0);
1412        let result = compare(Comparison::Equal, &left, &right).expect("compares");
1413        assert_eq!(result.len(), 0);
1414    }
1415
1416    /// Six strings, three of them sharing a prefix, and a null, which is the column the two tests
1417    /// below read.
1418    fn words() -> Vector {
1419        Vector::from_values(
1420            LogicalType::Varchar,
1421            &[
1422                Value::Varchar("http://a".into()),
1423                Value::Varchar("http://b".into()),
1424                Value::Null,
1425                Value::Varchar("ab".into()),
1426                Value::Varchar("http://a".into()),
1427                Value::Varchar("z".into()),
1428            ],
1429        )
1430        .expect("six rows")
1431    }
1432
1433    /// A literal built early answers what a literal built per chunk answers.
1434    ///
1435    /// Every operator and both entry points, because the whole claim of the prepared literal is
1436    /// that it changes nothing, and the string column is the one where it changes the most work:
1437    /// what it carries is the four byte prefix the comparison resolves almost every row from.
1438    #[test]
1439    fn a_literal_built_early_answers_what_one_built_here_answers() {
1440        let column = words();
1441        let value = Value::Varchar("http://b".into());
1442        let constant = Vector::constant(LogicalType::Varchar, value.clone(), column.len());
1443        let held = Held::of(&LogicalType::Varchar, &value).expect("a varchar has a column");
1444        let kept = Selection::from_indices(vec![0, 1, 3, 5]);
1445        for op in [
1446            Comparison::Equal,
1447            Comparison::NotEqual,
1448            Comparison::Less,
1449            Comparison::LessOrEqual,
1450            Comparison::Greater,
1451            Comparison::GreaterOrEqual,
1452            Comparison::DistinctFrom,
1453            Comparison::NotDistinctFrom,
1454        ] {
1455            let prepared = compare_prepared(op, &column, &constant, Some(&held)).expect("compares");
1456            assert_eq!(prepared, compare(op, &column, &constant).expect("compares"), "{op:?}");
1457            // And with the literal on the left, which is the same loop turned around.
1458            let flipped = compare_prepared(op, &constant, &column, Some(&held)).expect("compares");
1459            assert_eq!(flipped, compare(op, &constant, &column).expect("compares"), "{op:?}");
1460            let refined =
1461                refine_prepared(op, &column, &constant, &kept, Some(&held)).expect("refines");
1462            assert_eq!(refined, refine(op, &column, &constant, &kept).expect("refines"), "{op:?}");
1463        }
1464    }
1465
1466    /// A literal built for something else is ignored rather than believed.
1467    ///
1468    /// The caller in `rudb-exec` takes the value out of the step it hands the answer back with, so
1469    /// this cannot happen there, and the kernel is public. A wrong answer is a much worse failure
1470    /// than a column built per chunk, so the check is a value comparison per chunk and this is what
1471    /// says it works.
1472    #[test]
1473    fn a_literal_built_for_another_value_is_ignored() {
1474        let column = words();
1475        let constant = Vector::constant(LogicalType::Varchar, Value::Varchar("z".into()), 6);
1476        let wrong = Held::of(&LogicalType::Varchar, &Value::Varchar("ab".into()))
1477            .expect("a varchar has a column");
1478        let answer = compare_prepared(Comparison::Equal, &column, &constant, Some(&wrong))
1479            .expect("compares");
1480        assert_eq!(answer, compare(Comparison::Equal, &column, &constant).expect("compares"));
1481        // And one built for another type, which is what a comparison across two types would hand
1482        // over if the caller took it from the wrong side.
1483        let other = Held::of(&LogicalType::Integer, &Value::Integer(1)).expect("an integer column");
1484        let answer = compare_prepared(Comparison::Equal, &column, &constant, Some(&other))
1485            .expect("compares");
1486        assert_eq!(answer, compare(Comparison::Equal, &column, &constant).expect("compares"));
1487    }
1488
1489    /// A bit packed column against a literal is compared in code space, which has to reach the
1490    /// oracle's answer on all eight comparisons and with the literal on either side.
1491    #[test]
1492    fn a_packed_column_against_a_constant_answers_what_the_oracle_answers() {
1493        let values: Vec<i32> = (0..64).map(|row| 1000 + (row * 37) % 500).collect();
1494        let flat = Vector::flat(LogicalType::Integer, Data::Int32(values.into()))
1495            .expect("integers are an i32 layout");
1496        let packed = flat.bit_packed().expect("a five hundred wide range packs");
1497        assert_eq!(packed.form(), Form::BitPacked);
1498        for literal in [999, 1000, 1200, 1499, 1500, 2000] {
1499            let constant = Vector::constant(LogicalType::Integer, Value::Integer(literal), 64);
1500            for op in EVERY {
1501                agrees(op, &packed, &constant);
1502                agrees(op, &constant, &packed);
1503            }
1504        }
1505    }
1506
1507    /// The nulls of a packed column live in its validity rather than in its bits, so a comparison
1508    /// has to blank them the way it blanks a flat column's, and the bits under them are whatever
1509    /// the packing wrote there.
1510    #[test]
1511    fn a_packed_column_with_nulls_answers_what_the_oracle_answers() {
1512        let values: Vec<i32> = (0..32).map(|row| 40 + row * 3).collect();
1513        let flat = Vector::flat(LogicalType::Integer, Data::Int32(values.into()))
1514            .expect("integers are an i32 layout")
1515            .with_validity(Validity::from_iter(32, |row| row % 5 != 0));
1516        let packed = flat.bit_packed().expect("packs");
1517        let constant = Vector::constant(LogicalType::Integer, Value::Integer(80), 32);
1518        for op in EVERY {
1519            agrees(op, &packed, &constant);
1520        }
1521    }
1522
1523    /// A literal the width cannot hold answers every row without a bit being read, and the answer
1524    /// still has to be the one the oracle gives.
1525    #[test]
1526    fn a_literal_outside_the_packed_range_answers_the_whole_vector_at_once() {
1527        let before = fallback::count(Kernel::Compare, Form::BitPacked, Form::Constant);
1528        let values: Vec<i32> = (0..16).map(|row| 500 + row).collect();
1529        let flat = Vector::flat(LogicalType::Integer, Data::Int32(values.into()))
1530            .expect("integers are an i32 layout");
1531        let packed = flat.bit_packed().expect("packs");
1532        let literals = [-1, 0, 499, 516, 100_000];
1533        for literal in literals {
1534            let constant = Vector::constant(LogicalType::Integer, Value::Integer(literal), 16);
1535            for op in EVERY {
1536                agrees(op, &packed, &constant);
1537            }
1538        }
1539        // The six ordinary comparisons have a loop for this pair and the two that never go null do
1540        // not, because those want the null rule inside the loop and the code space loop does not
1541        // carry one. They take the row at a time path and count themselves, which is the counter
1542        // doing its job rather than a gap being hidden.
1543        let total = EVERY.iter().filter(|op| op.is_total()).count();
1544        assert_eq!(
1545            fallback::count(Kernel::Compare, Form::BitPacked, Form::Constant) - before,
1546            (literals.len() * total) as u64,
1547            "only the two total comparisons fall through"
1548        );
1549    }
1550
1551    /// The conjunct path reads the rows an earlier conjunct kept, so the code space loop has to be
1552    /// reached through the selection rather than through the row number.
1553    #[test]
1554    fn refining_a_selection_over_a_packed_column_keeps_the_same_rows() {
1555        let values: Vec<i32> = (0..64).map(|row| 200 + (row * 11) % 128).collect();
1556        let flat = Vector::flat(LogicalType::Integer, Data::Int32(values.clone().into()))
1557            .expect("integers are an i32 layout");
1558        let packed = flat.bit_packed().expect("packs");
1559        let kept = Selection::from_predicate(64, |row| row % 3 == 0);
1560        let constant = Vector::constant(LogicalType::Integer, Value::Integer(260), 64);
1561        let packed_rows = refine(Comparison::Greater, &packed, &constant, &kept).expect("refines");
1562        let flat_rows = refine(Comparison::Greater, &flat, &constant, &kept).expect("refines");
1563        assert_eq!(packed_rows.indices(), flat_rows.indices());
1564        assert!(!packed_rows.is_empty(), "the literal is inside the range");
1565    }
1566}