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;
73use std::sync::Arc;
74
75use rudb_common::{Error, LogicalType, Result, Value, interval_micros};
76use rudb_vector::{
77    Coded, Data, Form, Packed, Selection, StringColumn, StringView, Validity, Vector,
78};
79
80use crate::fallback::{self, Kernel};
81use crate::logic::is_true;
82use crate::number::{approximate, integral};
83use crate::peel::{self, Found};
84use crate::prepare::Held;
85use crate::shape::{first, identity, nulls_of, single};
86
87/// Which comparison.
88#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
89pub enum Comparison {
90    /// `=`, null if either side is null.
91    Equal,
92    /// `<>`, null if either side is null.
93    NotEqual,
94    /// `<`, null if either side is null.
95    Less,
96    /// `<=`, null if either side is null.
97    LessOrEqual,
98    /// `>`, null if either side is null.
99    Greater,
100    /// `>=`, null if either side is null.
101    GreaterOrEqual,
102    /// `IS DISTINCT FROM`, which is total and never null.
103    DistinctFrom,
104    /// `IS NOT DISTINCT FROM`, which is total and never null.
105    NotDistinctFrom,
106}
107
108impl Comparison {
109    /// Whether this comparison treats null as a value rather than as an absence.
110    #[must_use]
111    pub fn is_total(self) -> bool {
112        matches!(self, Self::DistinctFrom | Self::NotDistinctFrom)
113    }
114
115    /// The comparison that means the same thing with the two sides exchanged.
116    ///
117    /// This is what halves the number of specialized loops. A constant on the left against a
118    /// column on the right is the column against the constant with the inequality turned around,
119    /// and writing it that way means the column against constant loop is written once and tested
120    /// once rather than twice with a chance of the second one being subtly wrong.
121    #[must_use]
122    pub fn swapped(self) -> Self {
123        match self {
124            Self::Less => Self::Greater,
125            Self::LessOrEqual => Self::GreaterOrEqual,
126            Self::Greater => Self::Less,
127            Self::GreaterOrEqual => Self::LessOrEqual,
128            same => same,
129        }
130    }
131
132    /// Whether this comparison is true of two sides that sit in this order.
133    ///
134    /// For the six ordinary comparisons only. The two total ones read a null as a value and an
135    /// `Ordering` has no way to say which side was null, so there is nothing sensible to return for
136    /// them and they answer false rather than pretending.
137    #[must_use]
138    fn holds(self, order: Ordering) -> bool {
139        match self {
140            Self::Equal => order == Ordering::Equal,
141            Self::NotEqual => order != Ordering::Equal,
142            Self::Less => order == Ordering::Less,
143            Self::LessOrEqual => order != Ordering::Greater,
144            Self::Greater => order == Ordering::Greater,
145            Self::GreaterOrEqual => order != Ordering::Less,
146            Self::DistinctFrom | Self::NotDistinctFrom => false,
147        }
148    }
149}
150
151/// Compares two vectors of the same length, producing a `BOOLEAN` vector.
152///
153/// # Errors
154///
155/// If the two sides are not the same length, or if the two types cannot be compared.
156pub fn compare(op: Comparison, left: &Vector, right: &Vector) -> Result<Vector> {
157    compare_prepared(op, left, right, None)
158}
159
160/// [`compare`], with the constant side already turned into the column the loops read it through.
161///
162/// The same body and the same answer. A caller that built the plan knows which side is a literal
163/// and can hand a [`Held`] built once for the query, which saves the allocations that building it
164/// per chunk costs. A caller that has no plan in front of it passes `None` and nothing changes.
165///
166/// # Errors
167///
168/// The same ones [`compare`] gives.
169pub fn compare_prepared(
170    op: Comparison,
171    left: &Vector,
172    right: &Vector,
173    held: Option<&Held>,
174) -> Result<Vector> {
175    if left.len() != right.len() {
176        return Err(Error::internal(format!(
177            "a comparison of a {} row vector with a {} row one",
178            left.len(),
179            right.len()
180        )));
181    }
182    let len = left.len();
183    if left.form() == Form::Constant && right.form() == Form::Constant && len > 0 {
184        let single = compare_values(op, &left.try_value_at(0)?, &right.try_value_at(0)?)?;
185        return Ok(Vector::constant(LogicalType::Boolean, single, len));
186    }
187
188    let (left_valid, right_valid) = (nulls_of(left), nulls_of(right));
189    // Either side entirely null, on one of the six ordinary comparisons, is every answer null and
190    // the data is never read. This is not a corner case: a `NULL` literal in a predicate is a
191    // constant vector whose validity is exactly this, and so is a column the scan knows is empty.
192    if !op.is_total()
193        && (left_valid == Validity::AllInvalid || right_valid == Validity::AllInvalid)
194        && len > 0
195    {
196        return boolean(vec![false; len], Validity::AllInvalid, len);
197    }
198
199    if let Some(answers) = external_text_literal(op, left, right, len, identity, held)? {
200        let validity = left_valid.and(&right_valid, len);
201        return boolean(blank_the_nulls(answers, &validity), validity, len);
202    }
203    if let Some(answers) =
204        specialized(op, left, right, &left_valid, &right_valid, len, identity, held)
205    {
206        let validity =
207            if op.is_total() { Validity::AllValid } else { left_valid.and(&right_valid, len) };
208        return boolean(blank_the_nulls(answers, &validity), validity, len);
209    }
210
211    fallback::record(Kernel::Compare, left.form(), right.form());
212    let mut values = Vec::with_capacity(len);
213    // row at a time: the path recorded on the line above, which exists to be correct for a pair of
214    // forms no specialization covers and counts itself so that pair shows up in the report.
215    for index in 0..len {
216        values.push(compare_values(op, &left.try_value_at(index)?, &right.try_value_at(index)?)?);
217    }
218    Vector::from_values(LogicalType::Boolean, &values)
219}
220
221/// The rows the comparison is true on, which is [`compare_prepared`] and then
222/// [`crate::select::selection`] without the flag vector in between.
223///
224/// The first conjunct of a filter asks exactly this, and going the long way round built a
225/// `BOOLEAN` vector, wrote false into every null of it, and then had `selection` take the vector
226/// apart again to find the booleans it had just been handed. Here the answers go straight to the
227/// loop that picks the rows, and a null is dropped by the validity the way `selection` drops a
228/// null flag, so the rows are the same ones. A pair of forms with no loop of its own goes the long
229/// way, which is where it is counted.
230///
231/// # Errors
232///
233/// The same ones [`compare`] gives.
234pub fn select_prepared(
235    op: Comparison,
236    left: &Vector,
237    right: &Vector,
238    held: Option<&Held>,
239) -> Result<Selection> {
240    if left.len() != right.len() {
241        return Err(Error::internal(format!(
242            "a comparison of a {} row vector with a {} row one",
243            left.len(),
244            right.len()
245        )));
246    }
247    let len = left.len();
248    if len == 0 {
249        return Ok(Selection::empty());
250    }
251    if left.form() == Form::Constant && right.form() == Form::Constant {
252        let single = compare_values(op, &left.try_value_at(0)?, &right.try_value_at(0)?)?;
253        return Ok(if is_true(&single) { Selection::identity(len) } else { Selection::empty() });
254    }
255    let (left_valid, right_valid) = (nulls_of(left), nulls_of(right));
256    if !op.is_total() && (left_valid == Validity::AllInvalid || right_valid == Validity::AllInvalid)
257    {
258        return Ok(Selection::empty());
259    }
260    // A selection holds `u32` rows, and a chunk is nowhere near that, but the long way checks it
261    // and so this does too rather than casting past it.
262    if u32::try_from(len).is_ok() {
263        if let Some(answers) = external_text_literal(op, left, right, len, identity, held)? {
264            return Ok(crate::select::picked(
265                &answers,
266                identity,
267                len,
268                &left_valid.and(&right_valid, len),
269            ));
270        }
271        if let Some(answers) =
272            specialized(op, left, right, &left_valid, &right_valid, len, identity, held)
273        {
274            let validity =
275                if op.is_total() { Validity::AllValid } else { left_valid.and(&right_valid, len) };
276            return Ok(crate::select::picked(&answers, identity, len, &validity));
277        }
278    }
279    Ok(crate::select::selection(&compare_prepared(op, left, right, held)?, len))
280}
281
282/// The rows of `kept` the comparison also keeps.
283///
284/// This is [`compare`] for a conjunct that is not the first one. A filter with four conjuncts
285/// evaluated the obvious way runs all four over every row, so on TPC-H Q6, where each conjunct
286/// passes about a fifth of the rows and the four together pass about two percent, the last conjunct
287/// does fifty times the work it needs to. Handing it the rows the earlier ones kept is the whole
288/// difference, and it is a difference that grows with the number of conjuncts rather than washing
289/// out.
290///
291/// The answer is the rows of `kept`, in the order `kept` has them, for which the comparison is true.
292/// Null is not true, so a row whose either side is null is dropped on the six ordinary comparisons,
293/// which is the same rule [`crate::select::selection`] applies to a flag vector and the reason both
294/// of them are a kernel rather than a line at the call site.
295///
296/// # Errors
297///
298/// If the two sides are not the same length, or if a position in `kept` is past the end of them.
299pub fn refine(
300    op: Comparison,
301    left: &Vector,
302    right: &Vector,
303    kept: &Selection,
304) -> Result<Selection> {
305    refine_prepared(op, left, right, kept, None)
306}
307
308/// [`refine`], with the constant side already built, for the reason [`compare_prepared`] gives.
309///
310/// This is the one that gains the most from it. A conjunct after the first reads the rows the ones
311/// before it kept, so the loop can be eleven rows long while the setup is the same size it would be
312/// for a full chunk.
313///
314/// # Errors
315///
316/// The same ones [`refine`] gives.
317pub fn refine_prepared(
318    op: Comparison,
319    left: &Vector,
320    right: &Vector,
321    kept: &Selection,
322    held: Option<&Held>,
323) -> Result<Selection> {
324    if left.len() != right.len() {
325        return Err(Error::internal(format!(
326            "a comparison of a {} row vector with a {} row one",
327            left.len(),
328            right.len()
329        )));
330    }
331    let len = left.len();
332    // One vectorized pass over a run of `u32` before any of the loops below index with them, which
333    // is what turns a caller's mistake into this message rather than into a panic from inside a
334    // macro generated loop eight frames down.
335    if kept.indices().iter().any(|&row| row as usize >= len) {
336        return Err(Error::internal(format!("a selection past the end of a {len} row vector")));
337    }
338    if kept.is_empty() {
339        return Ok(Selection::empty());
340    }
341    if left.form() == Form::Constant && right.form() == Form::Constant {
342        let single = compare_values(op, &left.try_value_at(0)?, &right.try_value_at(0)?)?;
343        return Ok(if is_true(&single) { kept.clone() } else { Selection::empty() });
344    }
345
346    let (left_valid, right_valid) = (nulls_of(left), nulls_of(right));
347    if !op.is_total() && (left_valid == Validity::AllInvalid || right_valid == Validity::AllInvalid)
348    {
349        return Ok(Selection::empty());
350    }
351
352    let rows = kept.indices();
353    let map = |slot: usize| rows[slot] as usize;
354    if let Some(answers) = external_text_literal(op, left, right, kept.len(), map, held)? {
355        return Ok(narrowed(&answers, rows, |slot| {
356            let row = rows[slot] as usize;
357            left_valid.is_valid(row) && right_valid.is_valid(row)
358        }));
359    }
360    if let Some(answers) =
361        specialized(op, left, right, &left_valid, &right_valid, kept.len(), map, held)
362    {
363        // A total comparison has the nulls in the answer already, and two all valid sides have no
364        // null to drop, so both of those get the loop with nothing in it but the flag.
365        if op.is_total() || (left_valid == Validity::AllValid && right_valid == Validity::AllValid)
366        {
367            return Ok(narrowed(&answers, rows, |_| true));
368        }
369        // A bit at a time rather than a word at a time, which is the one place this path gives up
370        // something `compare` has. The rows are scattered by construction, so the two mask reads for
371        // one row are in different words as often as not and a word oriented loop would reread them.
372        return Ok(narrowed(&answers, rows, |slot| {
373            let row = rows[slot] as usize;
374            left_valid.is_valid(row) && right_valid.is_valid(row)
375        }));
376    }
377
378    fallback::record(Kernel::Compare, left.form(), right.form());
379    let mut out = Vec::with_capacity(kept.len());
380    // row at a time: the path recorded on the line above, for a pair of forms no specialization
381    // covers, reading only the rows the conjuncts before this one kept.
382    for &row in rows {
383        let index = row as usize;
384        if is_true(&compare_values(op, &left.try_value_at(index)?, &right.try_value_at(index)?)?) {
385            out.push(row);
386        }
387    }
388    Ok(Selection::from_indices(out))
389}
390
391/// One of the six ordinary comparisons between storage-backed text and a literal, without
392/// constructing row values.
393///
394/// Where the comparison is an equality, the column is a dictionary that shares its values and the
395/// caller brought the literal it was built with, this decides once per distinct value instead of
396/// once per row. See the `peel` module. Everything else reads the column a row at a time, which is
397/// still better than the general path because it never builds a value.
398///
399/// An ordering comparison gets none of that and is here anyway, because what the general path costs
400/// on a text column is not the comparison. It is `try_value_at`, which allocates a `String` a row so
401/// that `compare_values` has a `Value` to look at, and then drops it. Reading the bytes where they
402/// lie and comparing those is the same answer with neither the allocation nor the dispatch, and the
403/// caller this matters most to is the top N: it asks every chunk whether any row can still beat the
404/// worst candidate it holds, and the constant it asks about changes as it goes, so nothing is memoized
405/// and the row loop is the whole of it.
406fn external_text_literal<M>(
407    op: Comparison,
408    left: &Vector,
409    right: &Vector,
410    len: usize,
411    map: M,
412    held: Option<&Held>,
413) -> Result<Option<Vec<bool>>>
414where
415    M: Fn(usize) -> usize + Copy,
416{
417    if op.is_total()
418        || left.logical_type() != &LogicalType::Varchar
419        || right.logical_type() != &LogicalType::Varchar
420    {
421        return Ok(None);
422    }
423    let (column, literal, swapped) = match (left.constant_value(), right.constant_value()) {
424        (None, Some(Value::Varchar(literal))) if left.positions().is_some() => {
425            (left, literal.as_bytes(), false)
426        }
427        (Some(Value::Varchar(literal)), None) if right.positions().is_some() => {
428            (right, literal.as_bytes(), true)
429        }
430        _ => return Ok(None),
431    };
432    // The column is on the left from here on, so an inequality written the other way round is
433    // turned around once rather than once a row.
434    let op = if swapped { op.swapped() } else { op };
435    let same = op == Comparison::Equal;
436    if matches!(op, Comparison::Equal | Comparison::NotEqual) {
437        // The literal has to be the one the memo was filled against, which it is when the caller
438        // took both from the same comparison node. A caller that gets it wrong is slow rather than
439        // wrong, which is the rule the rest of `Held` keeps.
440        if let Some(held) = held.filter(|held| held.text() == Some(literal)) {
441            // A dictionary that came with its sorted order answers this without reading any value
442            // more than the search does, so try that before filling a memo one value at a time.
443            if let Some(found) = held.lookup().find(column, literal) {
444                return Ok(Some(against_code(column, found?, len, map, same)?));
445            }
446            let decide = |dictionary: &Vector, code: usize| -> Result<bool> {
447                let found = if literal.is_empty() {
448                    dictionary.try_bytes_len_at(code)?.is_some_and(|length| length == 0)
449                } else {
450                    dictionary.try_bytes_at(code)?.is_some_and(|bytes| bytes == literal)
451                };
452                Ok(found)
453            };
454            if let Some(answers) = held.peel().answer(column, len, map, decide) {
455                let mut answers = answers?;
456                if !same {
457                    for answer in &mut answers {
458                        *answer = !*answer;
459                    }
460                }
461                return Ok(Some(answers));
462            }
463        }
464        let mut answers = Vec::with_capacity(len);
465        for slot in 0..len {
466            let row = map(slot);
467            // An empty literal is settled by the length alone, which for a dictionary is one load
468            // of two offsets rather than a walk to wherever the value's bytes live.
469            let equal = if literal.is_empty() {
470                column.try_bytes_len_at(row)?.is_some_and(|length| length == 0)
471            } else {
472                column.try_bytes_at(row)?.is_some_and(|bytes| bytes == literal)
473            };
474            answers.push(equal == same);
475        }
476        return Ok(Some(answers));
477    }
478    if let Some(answers) = by_rank(op, column, literal, len, map)? {
479        return Ok(Some(answers));
480    }
481    let mut answers = Vec::with_capacity(len);
482    for slot in 0..len {
483        let row = map(slot);
484        // A null row's bytes are whatever the column left there, and the answer for it is thrown
485        // away by the caller, which blanks every position the validity says is null. Equal is what
486        // is written there because it is the cheapest thing to write and it is never read.
487        let order = match column.try_bytes_at(row)? {
488            Some(bytes) => bytes.cmp(literal),
489            None => Ordering::Equal,
490        };
491        answers.push(op.holds(order));
492    }
493    Ok(Some(answers))
494}
495
496/// An inequality against a literal answered out of the dictionary's sorted order, or `None` when
497/// there is no order to answer it from.
498///
499/// The equality path has resolved a literal to a code and compared integers since the sorted order
500/// was written, and this is the same trick for the other four comparisons. One binary search puts the
501/// literal at a rank boundary, the inverse of the order turns each row's code into its rank, and then
502/// the whole comparison is one `usize` against another. No value is read and no bytes are compared.
503///
504/// The caller this was written for is the top N. `ORDER BY <varchar> LIMIT 10` asks every chunk
505/// whether any row in it can still beat the tenth best candidate, which is a comparison of the key
506/// column against a literal that changes as the query runs, so there is nothing to memoize and the
507/// row loop is the whole of the operator. On ClickBench 25, which is that query over `SearchPhrase`,
508/// the top N was 0.377 of the 0.791 milliseconds the pipeline's operators spent per instance, against
509/// 0.097 for the same query ordered by a timestamp. The gap was the byte compare.
510///
511/// The filter gets it too, for `WHERE <varchar> < 'literal'` and for both halves of a `BETWEEN`.
512///
513/// Two things have to be true and the source decides both. It has to know its own order, and it has
514/// to be willing to hand the order back inverted, which is a `u32` per value it builds once and keeps.
515/// A source that would rather not answers `None` to one of them and the byte loop below runs instead,
516/// which is what every in memory column does today.
517fn by_rank<M>(
518    op: Comparison,
519    column: &Vector,
520    literal: &[u8],
521    len: usize,
522    map: M,
523) -> Result<Option<Vec<bool>>>
524where
525    M: Fn(usize) -> usize,
526{
527    let Some((codes, dictionary)) = column.shared_dictionary_parts() else { return Ok(None) };
528    let Some(ranks) = dictionary.ranks() else { return Ok(None) };
529    let Some(order) = dictionary.code_ranks() else { return Ok(None) };
530    let (below, equal) = peel::below(dictionary, ranks, literal)?;
531    // The two boundaries `peel::below` describes, picked by which side of the literal the comparison
532    // wants and whether the literal itself counts as being on that side.
533    let cut = match op {
534        Comparison::Less | Comparison::GreaterOrEqual => below,
535        _ => below + usize::from(equal),
536    };
537    let under = matches!(op, Comparison::Less | Comparison::LessOrEqual);
538    let mut answers = Vec::with_capacity(len);
539    // row at a time: the comparison is the loop, and a row of it is two loads and an integer compare.
540    for slot in 0..len {
541        let code = *codes
542            .get(map(slot))
543            .ok_or_else(|| Error::internal("a ranked row is past the end of its codes"))?;
544        let rank = *order
545            .get(code as usize)
546            .ok_or_else(|| Error::internal("a ranked code is past the end of its dictionary"))?;
547        answers.push(((rank as usize) < cut) == under);
548    }
549    Ok(Some(answers))
550}
551
552/// The rows that compare true against the value already known to sit at `rank`, or `None`.
553///
554/// The `by_rank` path with the search taken out. That path searches because it is handed a literal
555/// and has to find out where the literal sits, and the search is not cheap: about nineteen probes of
556/// a dictionary of a hundred thousand, and a probe that cannot settle on the eight bytes the file
557/// stores per rank has to read a value, which decodes the block the value sits in. On ClickBench 25
558/// that search and the block decoding under it were most of the query.
559///
560/// A caller that already knows the rank pays none of it. The one this was written for is the top N,
561/// whose bound is not a literal from the query at all: it is a value that came out of this same
562/// dictionary, carried by a row that arrived with its code, so its rank was known the moment it was
563/// kept and nothing has to be found. See `crate::topn`.
564///
565/// `None` when the column is not a dictionary over `dictionary`, when that dictionary will not hand
566/// its order back inverted, or for a comparison that is not one of the four inequalities. The
567/// identity check is the whole of what makes this safe to offer: a rank means nothing except against
568/// the dictionary it was read out of, so the caller hands that dictionary over and this refuses
569/// rather than trusting it.
570///
571/// Null rows are dropped, which is what [`crate::select::selection`] does with a null flag and
572/// therefore what the caller would have got by going the long way round.
573#[must_use]
574pub fn select_against_rank(
575    op: Comparison,
576    column: &Vector,
577    dictionary: &Arc<Vector>,
578    rank: u32,
579    rows: usize,
580) -> Option<Selection> {
581    let (codes, values) = column.shared_dictionary_parts()?;
582    if !Arc::ptr_eq(values, dictionary) {
583        return None;
584    }
585    let order = values.code_ranks()?;
586    let validity = column.validity();
587    let live = !validity.has_nulls(rows);
588    let rows = rows.min(codes.len());
589    let mut kept = vec![0_u32; rows];
590    let mut count = 0;
591    // row at a time: the comparison is the loop, and a row of it is two loads and an integer
592    // compare. Every slot writes its row at the current length and only a kept slot moves the
593    // length on, for the reason `narrowed` gives.
594    for (row, &code) in codes.iter().enumerate().take(rows) {
595        let at = *order.get(code as usize)?;
596        let held = match op {
597            Comparison::Less => at < rank,
598            Comparison::LessOrEqual => at <= rank,
599            Comparison::Greater => at > rank,
600            Comparison::GreaterOrEqual => at >= rank,
601            _ => return None,
602        };
603        kept[count] = u32::try_from(row).ok()?;
604        // A single `&` rather than `&&`, because the short circuit would put back the branch.
605        count += usize::from(held & (live || validity.is_valid(row)));
606    }
607    kept.truncate(count);
608    Some(Selection::from_indices(kept))
609}
610
611/// The rank of the value one row of a dictionary column holds, when that is knowable.
612///
613/// The other half of [`select_against_rank`]. A caller that wants to compare against a row's value
614/// later without searching for it asks for this when the row goes by and keeps the answer.
615///
616/// `None` for a column that is not a dictionary over a source that knows its order, for a row that
617/// is null, and for a code the order does not cover. The caller treats all three the same way, by
618/// giving up on ranks and doing what it did before.
619#[must_use]
620pub fn rank_at(column: &Vector, row: usize) -> Option<(Arc<Vector>, u32)> {
621    let (codes, values) = column.shared_dictionary_parts()?;
622    if !column.validity().is_valid(row) {
623        return None;
624    }
625    let order = values.code_ranks()?;
626    let code = *codes.get(row)?;
627    let rank = *order.get(code as usize)?;
628    Some((Arc::clone(values), rank))
629}
630
631/// The rank of one row's value in a dictionary the caller is already holding.
632///
633/// [`rank_at`] for a caller that has a dictionary in hand and wants to know where one row sits in
634/// it, which is a top n rejecting a row against a candidate it kept earlier. It answers the question
635/// without the clone of the `Arc` that `rank_at` has to make, because a reject runs on every row
636/// that reaches the operator and an atomic increment per row is not nothing.
637///
638/// The identity check is what makes a rank mean anything, and it is the same one
639/// [`select_against_rank`] makes: a rank is a position in one dictionary and says nothing at all
640/// about any other, so this refuses a column that is not over the dictionary it was given.
641///
642/// `None` in every case `rank_at` answers `None` in, and additionally for a column over a different
643/// dictionary. The caller reads the value and compares it the old way.
644#[must_use]
645pub fn rank_within(column: &Vector, row: usize, dictionary: &Arc<Vector>) -> Option<u32> {
646    let (codes, values) = column.shared_dictionary_parts()?;
647    if !Arc::ptr_eq(values, dictionary) || !column.validity().is_valid(row) {
648        return None;
649    }
650    let order = values.code_ranks()?;
651    let code = *codes.get(row)?;
652    Some(*order.get(code as usize)?)
653}
654
655/// Every row's answer once the literal has been resolved to a code, or to nothing.
656///
657/// This is the whole point of storing a dictionary's sorted order. The comparison is a `u32`
658/// against a `u32` and it never touches the payload, so a filter on a text column costs what a
659/// filter on an integer column costs. A literal the dictionary does not hold is decided for the
660/// whole chunk without looking at the codes at all, because a code that is in the dictionary cannot
661/// be the one that is not.
662fn against_code<M>(
663    column: &Vector,
664    found: Found,
665    len: usize,
666    map: M,
667    same: bool,
668) -> Result<Vec<bool>>
669where
670    M: Fn(usize) -> usize,
671{
672    let Found::At(wanted) = found else { return Ok(vec![!same; len]) };
673    let (codes, _) = column
674        .shared_dictionary_parts()
675        .ok_or_else(|| Error::internal("a resolved literal lost the codes it was resolved for"))?;
676    // Every row the map can name is a row of the column, which both callers check before they get
677    // here, so codes as long as the column settle the bound for the whole chunk at once. Asking per
678    // row put an error path and a push in a loop that is otherwise one compare, and that loop was
679    // fifteen instructions a row on ClickBench 28 rather than one.
680    if codes.len() < column.len() {
681        return Err(Error::internal("a compared column is longer than its codes"));
682    }
683    // row at a time: the comparison is the loop. Nothing here reads a value or allocates.
684    Ok((0..len).map(|slot| (codes[map(slot)] == wanted) == same).collect())
685}
686
687/// The positions of `rows` whose answer is true and whose row is live, without a branch per row.
688///
689/// The same shape as the loop in `crate::select` and for the same reason: which rows a filter keeps is
690/// what the data decides rather than what the code does, so the branch is unpredictable by
691/// construction and a mispredict is worth more than the rest of the loop put together. Every slot
692/// writes its row at the current length and only a slot that is kept moves the length on.
693fn narrowed<L: Fn(usize) -> bool>(answers: &[bool], rows: &[u32], live: L) -> Selection {
694    let mut out = vec![0_u32; answers.len()];
695    let mut count = 0;
696    for (slot, &answer) in answers.iter().enumerate() {
697        out[count] = rows[slot];
698        // A single `&` rather than `&&`, because the short circuit would put back the branch.
699        count += usize::from(answer & live(slot));
700    }
701    out.truncate(count);
702    Selection::from_indices(out)
703}
704
705/// A `BOOLEAN` vector from a run of answers and the validity that says which of them count.
706fn boolean(answers: Vec<bool>, validity: Validity, len: usize) -> Result<Vector> {
707    // An empty vector has no null to record, and `Vector::from_values` normalizes the empty mask it
708    // builds to all valid, so saying the same here is what keeps an empty specialized result the
709    // same vector as the oracle's rather than merely the same length.
710    let validity = if len == 0 { Validity::AllValid } else { validity.normalize(len) };
711    Ok(Vector::flat(LogicalType::Boolean, Data::Bool(answers.into()))?.with_validity(validity))
712}
713
714/// A false in every position the validity says is null.
715///
716/// The comparison at a null position read whatever the zero the null was stored as compared to,
717/// which is a defined value and a meaningless one. Writing false there costs one pass over a run
718/// of bytes, only when there are nulls at all, and it buys the property that a specialized result
719/// is the same vector as the row at a time result rather than merely the same answer. A test that
720/// can compare two vectors with `==` is a much better test than one that has to walk them.
721fn blank_the_nulls(mut answers: Vec<bool>, validity: &Validity) -> Vec<bool> {
722    if let Validity::Mask(mask) = validity {
723        for (index, answer) in answers.iter_mut().enumerate() {
724            if !mask.get(index) {
725                *answer = false;
726            }
727        }
728    }
729    answers
730}
731
732/// The answers for a form pair this file has a loop for, or `None` to say it has not.
733///
734/// `map` turns an output position into the row of `left` and `right` it is the answer for, and
735/// `len` is how many output positions there are. [`compare`] passes [`identity`] and the length of
736/// its operands, which is every row. [`refine`] passes the selection it was handed and the size of
737/// it, which is how a conjunct after the first reads only the rows the conjuncts before it kept.
738///
739/// A generic parameter rather than a `fn(usize) -> usize` in a field, for the reason
740/// `spec/engine/03-data-plane.md` records as the first performance lesson of this layer: an index
741/// mapping the compiler cannot see through is an indirect call per row, and one of those in a loop
742/// that is otherwise three instructions is the whole loop.
743#[expect(
744    clippy::too_many_arguments,
745    reason = "two sides, two validities, the operator, the length, the index mapping and the \
746              literal that was built early, all of which the branches below need"
747)]
748fn specialized<M>(
749    op: Comparison,
750    left: &Vector,
751    right: &Vector,
752    left_valid: &Validity,
753    right_valid: &Validity,
754    len: usize,
755    map: M,
756    held: Option<&Held>,
757) -> Option<Vec<bool>>
758where
759    M: Fn(usize) -> usize + Copy,
760{
761    // Across representations is the fallback's job. `INTEGER` against `BIGINT` reaches the same
762    // answer through `numeric_order`, and a specialized loop that assumed the two runs had the same
763    // layout would compare a four byte column against an eight byte one position by position.
764    if left.logical_type() != right.logical_type() {
765        return None;
766    }
767
768    // Where each side keeps its values and how a row of it is reached, which is what turns flat,
769    // dictionary and run length into one branch below rather than nine. See [`Through`].
770    let (one, other) = (through(left), through(right));
771
772    if let (Some(one), Some(other)) = (&one, &other) {
773        return match (one, other) {
774            (Through::Direct(a), Through::Direct(b)) => {
775                dispatch(op, len, a, map, b, map, left_valid, right_valid, map)
776            }
777            (Through::Coded(codes, a), Through::Direct(b)) => dispatch(
778                op,
779                len,
780                a,
781                |slot| codes[map(slot)] as usize,
782                b,
783                map,
784                left_valid,
785                right_valid,
786                map,
787            ),
788            (Through::Direct(a), Through::Coded(codes, b)) => dispatch(
789                op,
790                len,
791                a,
792                map,
793                b,
794                |slot| codes[map(slot)] as usize,
795                left_valid,
796                right_valid,
797                map,
798            ),
799            (Through::Coded(left_codes, a), Through::Coded(right_codes, b)) => dispatch(
800                op,
801                len,
802                a,
803                |slot| left_codes[map(slot)] as usize,
804                b,
805                |slot| right_codes[map(slot)] as usize,
806                left_valid,
807                right_valid,
808                map,
809            ),
810        };
811    }
812    // A bit packed column against a literal, which is the pair the form was added for. The literal
813    // is turned into a code once and then the loop compares codes, so nothing is unpacked at all,
814    // and a literal outside what the width can hold answers the whole vector without a bit of it
815    // being read. Only the six comparisons that go null on a null side come here, because the other
816    // two want the null rule inside the loop and this loop does not have it.
817    if !op.is_total() {
818        if let (Some(packed), Some(value)) = (left.packed_parts(), right.constant_value()) {
819            let wanted = exact(held, left.logical_type(), value)?;
820            return Some(packed_against(op, &packed, wanted, len, map));
821        }
822        if let (Some(value), Some(packed)) = (left.constant_value(), right.packed_parts()) {
823            let wanted = exact(held, right.logical_type(), value)?;
824            return Some(packed_against(op.swapped(), &packed, wanted, len, map));
825        }
826        if let (Some(one), Some(other)) = (packing(left), packing(right)) {
827            return packings_against_each_other(op, &one, &other, len, map);
828        }
829        // A bit packed column against a flat one, which is the pair a clustered table hands the
830        // filter. Inside a narrow partition a date column takes few enough distinct values to pack
831        // and the column beside it does not, so the better encoding the clustering buys is what
832        // used to send the comparison to the row at a time path. Both sides are read where they
833        // are, the packed one as its base plus its code and the flat one as the number it already
834        // is. On TPC-H this is `l_commitdate < l_receiptdate` on the clustered file, which is q4
835        // and q21.
836        if let (Some(one), Some(other)) = (packing(left), &other) {
837            return packing_against_run(op, &one, other, len, map);
838        }
839        if let (Some(one), Some(other)) = (&one, packing(right)) {
840            return packing_against_run(op.swapped(), &other, one, len, map);
841        }
842    }
843    if let (Some(one), Some(value)) = (&one, right.constant_value()) {
844        let column = readied(held, left.logical_type(), value)?;
845        let other = column.data()?;
846        return match one {
847            Through::Direct(a) => {
848                dispatch(op, len, a, map, other, first, left_valid, right_valid, map)
849            }
850            Through::Coded(codes, a) => dispatch(
851                op,
852                len,
853                a,
854                |slot| codes[map(slot)] as usize,
855                other,
856                first,
857                left_valid,
858                right_valid,
859                map,
860            ),
861        };
862    }
863    if let (Some(value), Some(other)) = (left.constant_value(), &other) {
864        // The same loop with the comparison turned around, rather than a second loop.
865        let column = readied(held, right.logical_type(), value)?;
866        let one = column.data()?;
867        return match other {
868            Through::Direct(b) => {
869                dispatch(op.swapped(), len, b, map, one, first, right_valid, left_valid, map)
870            }
871            Through::Coded(codes, b) => dispatch(
872                op.swapped(),
873                len,
874                b,
875                |slot| codes[map(slot)] as usize,
876                one,
877                first,
878                right_valid,
879                left_valid,
880                map,
881            ),
882        };
883    }
884    // A compressed column against a literal, tested in the code space the column is already in.
885    // Only equality, because a symbol code says nothing about where its symbol sorts, so an ordering
886    // comparison has to decompress and does. Equality does not: compressing is a function of the
887    // table and the bytes, so two strings have the same codes exactly when they are the same string.
888    if matches!(op, Comparison::Equal | Comparison::NotEqual) {
889        if let (Some(coded), Some(value)) = (left.coded_parts(), right.constant_value()) {
890            let wanted = encoded(&coded, held, left.logical_type(), value)?;
891            return Some(coded_against(op, &coded, &wanted, len, map));
892        }
893        if let (Some(value), Some(coded)) = (left.constant_value(), right.coded_parts()) {
894            let wanted = encoded(&coded, held, right.logical_type(), value)?;
895            return Some(coded_against(op, &coded, &wanted, len, map));
896        }
897    }
898    // A string column against another one or against a literal, with the views read where they are.
899    // It catches the string view form, whose bytes live in an arena the vector shares and so has no
900    // data slice for the branches above to find, and it catches the flat form as well so that the
901    // two cannot be compared by two different loops. The order itself is the one `view_order`
902    // writes down either way.
903    if let (Some((one, one_arena)), Some((other, other_arena))) =
904        (left.text_parts(), right.text_parts())
905    {
906        return Some(sweep(
907            op,
908            len,
909            |index| view_order(one.get(map(index)), one_arena, other.get(map(index)), other_arena),
910            left_valid,
911            right_valid,
912            map,
913        ));
914    }
915    // A string column against a literal. The literal becomes one view before the loop starts, so
916    // every row is a four byte prefix against the same four bytes and the payload is only read for
917    // the rows the prefix could not settle.
918    if let (Some((one, one_arena)), Some(value)) = (left.text_parts(), right.constant_value()) {
919        let column = readied(held, left.logical_type(), value)?;
920        let (other, other_arena) = column.text_parts()?;
921        let wanted = other.first();
922        return Some(sweep(
923            op,
924            len,
925            |index| view_order(one.get(map(index)), one_arena, wanted, other_arena),
926            left_valid,
927            right_valid,
928            map,
929        ));
930    }
931    if let (Some(value), Some((other, other_arena))) = (left.constant_value(), right.text_parts()) {
932        // The same loop with the comparison turned around, rather than a second loop.
933        let column = readied(held, right.logical_type(), value)?;
934        let (one, one_arena) = column.text_parts()?;
935        let wanted = one.first();
936        return Some(sweep(
937            op.swapped(),
938            len,
939            |index| view_order(other.get(map(index)), other_arena, wanted, one_arena),
940            right_valid,
941            left_valid,
942            map,
943        ));
944    }
945    if let (Some((codes, values)), Some(value)) = (left.positions(), right.constant_value()) {
946        let one = values.data()?;
947        let column = readied(held, left.logical_type(), value)?;
948        let other = column.data()?;
949        let at = |index: usize| codes[map(index)] as usize;
950        return dispatch(op, len, one, at, other, first, left_valid, right_valid, map);
951    }
952    if let (Some(value), Some((codes, values))) = (left.constant_value(), right.positions()) {
953        let other = values.data()?;
954        let column = readied(held, right.logical_type(), value)?;
955        let one = column.data()?;
956        let at = |index: usize| codes[map(index)] as usize;
957        return dispatch(op.swapped(), len, other, at, one, first, right_valid, left_valid, map);
958    }
959    // A dictionary against a flat column. This pair had no loop until the kernel table put a number
960    // on what that cost, which on `server3` was 83 nanoseconds a row against 1.2 for the dictionary
961    // against constant pair beside it, on the same data and the same operator. It is not a rare
962    // shape either: it is what a filtered column compared against an unfiltered one is, which is
963    // every conjunct after the first.
964    if let (Some((codes, values)), Some(other)) = (left.positions(), right.data()) {
965        let one = values.data()?;
966        let at = |index: usize| codes[map(index)] as usize;
967        return dispatch(op, len, one, at, other, map, left_valid, right_valid, map);
968    }
969    if let (Some(one), Some((codes, values))) = (left.data(), right.positions()) {
970        let other = values.data()?;
971        let at = |index: usize| codes[map(index)] as usize;
972        return dispatch(op.swapped(), len, other, at, one, map, right_valid, left_valid, map);
973    }
974    None
975}
976
977/// A literal as the whole number it is, and `None` for one that is not a whole number.
978///
979/// It goes through [`readied`] rather than reading the [`Value`] apart, so that a literal written
980/// as `900` against a `SMALLINT` column is narrowed by the same cast path every other comparison
981/// narrows it with. Reading the value apart here would be a second cast path with its own rounding
982/// and its own overflow rule, which is how two comparisons of the same literal end up disagreeing.
983fn exact(held: Option<&Held>, ty: &LogicalType, value: &Value) -> Option<i128> {
984    let column = readied(held, ty, value)?;
985    let data = column.data()?;
986    data.signed_at(0).or_else(|| data.unsigned_at(0).and_then(|value| i128::try_from(value).ok()))
987}
988
989/// A literal in the code space a compressed column is in, and `None` for one with no bytes.
990///
991/// It goes through [`readied`] for the reason [`exact`] does: the literal is narrowed to the column
992/// type by the same path every other comparison narrows it with, rather than by a second reading of
993/// the [`Value`] that could disagree with the first.
994fn encoded(
995    coded: &Coded<'_>,
996    held: Option<&Held>,
997    ty: &LogicalType,
998    value: &Value,
999) -> Option<Vec<u8>> {
1000    let column = readied(held, ty, value)?;
1001    let (views, arena) = column.text_parts()?;
1002    Some(coded.encode(views.first()?.bytes_in(arena)?))
1003}
1004
1005/// A compressed column against a literal, tested without decompressing a row of it.
1006///
1007/// The comparison is a byte slice against a byte slice, which is what it would have been on the
1008/// strings, over half as many bytes and with no decompression before it. A row whose codes are a
1009/// different length is settled by the length alone, which on a column of URLs is most of them.
1010fn coded_against<M>(
1011    op: Comparison,
1012    coded: &Coded<'_>,
1013    wanted: &[u8],
1014    len: usize,
1015    map: M,
1016) -> Vec<bool>
1017where
1018    M: Fn(usize) -> usize + Copy,
1019{
1020    let same = op == Comparison::Equal;
1021    let mut answers = Vec::with_capacity(len);
1022    for row in 0..len {
1023        answers.push((coded.row(map(row)) == Some(wanted)) == same);
1024    }
1025    answers
1026}
1027
1028/// A bit packed column against a literal, compared in the code space the column is already in.
1029///
1030/// The translation is one subtraction done once. After it the loop is a shift, a mask and a compare
1031/// of two `u64`, which is what the flat loop would have been doing anyway minus the unpacking, so
1032/// the form costs nothing on the operation a filter spends most of its time in.
1033///
1034/// The operator is a match out here and a separate loop under each arm rather than one loop calling
1035/// a function it was handed. A function pointer is an indirect call per row, and a loop with one in
1036/// it is a loop the compiler will not widen, so the compare that should have been a few rows at a
1037/// time was one row at a time with a call in the middle. On a sample of the TPC-H q20 filter, which
1038/// is two comparisons of a packed date column over six million rows, this pair of loops was two
1039/// thirds of everything the scan did.
1040fn packed_against<M>(
1041    op: Comparison,
1042    packed: &Packed<'_>,
1043    wanted: i128,
1044    len: usize,
1045    map: M,
1046) -> Vec<bool>
1047where
1048    M: Fn(usize) -> usize + Copy,
1049{
1050    let Some(code) = packed.code_of(wanted) else {
1051        // The literal is outside the range the width can hold, so every row answers the same way
1052        // and the answer is arithmetic on two numbers rather than a pass over the column.
1053        let above = wanted > packed.ceiling();
1054        let same = match op {
1055            Comparison::Equal | Comparison::NotDistinctFrom => false,
1056            Comparison::NotEqual | Comparison::DistinctFrom => true,
1057            Comparison::Less | Comparison::LessOrEqual => above,
1058            Comparison::Greater | Comparison::GreaterOrEqual => !above,
1059        };
1060        return vec![same; len];
1061    };
1062    let mut answers = vec![false; len];
1063    /// One pass over the rows with the comparison inlined into it.
1064    macro_rules! sweep {
1065        ($test:expr) => {{
1066            let test = $test;
1067            for (row, answer) in answers.iter_mut().enumerate() {
1068                *answer = test(packed.code(map(row)), code);
1069            }
1070        }};
1071    }
1072    match op {
1073        Comparison::Equal | Comparison::NotDistinctFrom => sweep!(|found, want| found == want),
1074        Comparison::NotEqual | Comparison::DistinctFrom => sweep!(|found, want| found != want),
1075        Comparison::Less => sweep!(|found, want| found < want),
1076        Comparison::LessOrEqual => sweep!(|found, want| found <= want),
1077        Comparison::Greater => sweep!(|found, want| found > want),
1078        Comparison::GreaterOrEqual => sweep!(|found, want| found >= want),
1079    }
1080    answers
1081}
1082
1083/// Two bit packed columns against each other, which is the pair a stored table produces.
1084///
1085/// A packed vector's value is its base plus its code, so a row is compared by adding each side's
1086/// base to each side's code and comparing the two sums. Nothing is unpacked into a vector of its
1087/// own and no `Value` is built, which is what the row at a time path underneath this was doing for
1088/// both sides of every row.
1089///
1090/// The two bases are almost never the same number, since each one is the smallest value in its own
1091/// column, so there is no shortcut in comparing the codes directly. What there is instead is that
1092/// the ranges may not overlap at all: a column whose largest value is below the other's smallest
1093/// answers every row the same way, and that is decided here out of four numbers with no bit of
1094/// either column being read.
1095///
1096/// `None` for a pair whose base plus width does not fit, which sends it to the row at a time path
1097/// rather than wrapping. A `DECIMAL(38)` column can be packed and its base can sit near the end of
1098/// the range, and the loop adds without asking so the asking happens once out here.
1099///
1100/// Each side takes its own index mapping, which is what lets a dictionary over a packed run of
1101/// values reach this with its codes as the mapping. A stored column of dates is that as often as it
1102/// is a packed run on its own, and from here the two are one loop with a different index.
1103///
1104/// On TPC-H this is `l_commitdate < l_receiptdate` and `l_shipdate < l_commitdate`, two packed date
1105/// columns of six million rows, which is q4, q12 and q21.
1106fn packed_against_packed<L, R, M>(
1107    op: Comparison,
1108    left: &Packed<'_>,
1109    at_left: L,
1110    right: &Packed<'_>,
1111    at_right: R,
1112    len: usize,
1113    map: M,
1114) -> Option<Vec<bool>>
1115where
1116    L: Fn(usize) -> usize,
1117    R: Fn(usize) -> usize,
1118    M: Fn(usize) -> usize + Copy,
1119{
1120    let (low, high) = (left.base(), ceiling_of(left)?);
1121    let (other_low, other_high) = (right.base(), ceiling_of(right)?);
1122    if high < other_low || other_high < low {
1123        // The two ranges are disjoint, so every row of the left column is on the same side of every
1124        // row of the right one and the answer is arithmetic on four numbers.
1125        let below = high < other_low;
1126        let same = match op {
1127            Comparison::Equal | Comparison::NotDistinctFrom => false,
1128            Comparison::NotEqual | Comparison::DistinctFrom => true,
1129            Comparison::Less | Comparison::LessOrEqual => below,
1130            Comparison::Greater | Comparison::GreaterOrEqual => !below,
1131        };
1132        return Some(vec![same; len]);
1133    }
1134    // The operator is a match out here with a loop under each arm, the same way `packed_against`
1135    // takes it and for the same reason.
1136    let mut answers = vec![false; len];
1137    /// One pass over the rows with the comparison inlined into it.
1138    macro_rules! sweep {
1139        ($test:expr) => {{
1140            let test = $test;
1141            for (slot, answer) in answers.iter_mut().enumerate() {
1142                let row = map(slot);
1143                *answer = test(
1144                    low + i128::from(left.code(at_left(row))),
1145                    other_low + i128::from(right.code(at_right(row))),
1146                );
1147            }
1148        }};
1149    }
1150    match op {
1151        Comparison::Equal | Comparison::NotDistinctFrom => sweep!(|one, other| one == other),
1152        Comparison::NotEqual | Comparison::DistinctFrom => sweep!(|one, other| one != other),
1153        Comparison::Less => sweep!(|one, other| one < other),
1154        Comparison::LessOrEqual => sweep!(|one, other| one <= other),
1155        Comparison::Greater => sweep!(|one, other| one > other),
1156        Comparison::GreaterOrEqual => sweep!(|one, other| one >= other),
1157    }
1158    Some(answers)
1159}
1160
1161/// A side whose values are a packed run, either its own or one a dictionary points into.
1162///
1163/// A dictionary of few distinct numbers over a wide range is what our own storage writes for a
1164/// column like a date, and [`through`] answers `None` for it because a packed run is not a run of
1165/// `Data` to index. This is the same question asked of the form underneath.
1166enum Packing<'a> {
1167    /// The run is this vector's own and row `n` is code `n` of it.
1168    Straight(Packed<'a>),
1169    /// The run belongs to a dictionary and row `n` is the code the run names for it.
1170    Coded(Cow<'a, [u32]>, Packed<'a>),
1171}
1172
1173/// How `vector` reaches a packed run, or `None` when it does not have one.
1174fn packing(vector: &Vector) -> Option<Packing<'_>> {
1175    if let Some(packed) = vector.packed_parts() {
1176        return Some(Packing::Straight(packed));
1177    }
1178    let (codes, values) = vector.positions()?;
1179    Some(Packing::Coded(codes, values.packed_parts()?))
1180}
1181
1182/// The four ways a pair of packed runs can be indexed, resolved once out here so that the loop
1183/// underneath is monomorphized on both mappings rather than calling through a pointer per row.
1184fn packings_against_each_other<M>(
1185    op: Comparison,
1186    left: &Packing<'_>,
1187    right: &Packing<'_>,
1188    len: usize,
1189    map: M,
1190) -> Option<Vec<bool>>
1191where
1192    M: Fn(usize) -> usize + Copy,
1193{
1194    match (left, right) {
1195        (Packing::Straight(one), Packing::Straight(other)) => {
1196            packed_against_packed(op, one, identity, other, identity, len, map)
1197        }
1198        (Packing::Straight(one), Packing::Coded(codes, other)) => {
1199            packed_against_packed(op, one, identity, other, |row| codes[row] as usize, len, map)
1200        }
1201        (Packing::Coded(codes, one), Packing::Straight(other)) => {
1202            packed_against_packed(op, one, |row| codes[row] as usize, other, identity, len, map)
1203        }
1204        (Packing::Coded(codes, one), Packing::Coded(others, other)) => packed_against_packed(
1205            op,
1206            one,
1207            |row| codes[row] as usize,
1208            other,
1209            |row| others[row] as usize,
1210            len,
1211            map,
1212        ),
1213    }
1214}
1215
1216/// A packed run against a run of whole numbers, with the four ways to index the pair resolved here.
1217///
1218/// The answer is for `packed op run`, so a caller with the flat side on the left hands the swapped
1219/// operator rather than a second loop. It is the same arrangement [`packings_against_each_other`]
1220/// has and it is here for the same reason: the mapping is picked once out here so that the loop
1221/// underneath is monomorphized on it rather than calling through a pointer per row.
1222fn packing_against_run<M>(
1223    op: Comparison,
1224    packed: &Packing<'_>,
1225    run: &Through<'_>,
1226    len: usize,
1227    map: M,
1228) -> Option<Vec<bool>>
1229where
1230    M: Fn(usize) -> usize + Copy,
1231{
1232    match (packed, run) {
1233        (Packing::Straight(one), Through::Direct(other)) => {
1234            packed_against_flat(op, one, identity, other, identity, len, map)
1235        }
1236        (Packing::Straight(one), Through::Coded(codes, other)) => {
1237            packed_against_flat(op, one, identity, other, |row| codes[row] as usize, len, map)
1238        }
1239        (Packing::Coded(codes, one), Through::Direct(other)) => {
1240            packed_against_flat(op, one, |row| codes[row] as usize, other, identity, len, map)
1241        }
1242        (Packing::Coded(codes, one), Through::Coded(others, other)) => packed_against_flat(
1243            op,
1244            one,
1245            |row| codes[row] as usize,
1246            other,
1247            |row| others[row] as usize,
1248            len,
1249            map,
1250        ),
1251    }
1252}
1253
1254/// A bit packed column against a flat one, with neither side turned into the other.
1255///
1256/// The packed side is its base plus its code and the flat side is the number it already holds, so
1257/// the loop is an add, a widen and a compare, and nothing is unpacked into a vector of its own. The
1258/// row at a time path underneath this was building a [`Value`] for both sides of every row.
1259///
1260/// `None` for a layout whose values do not all fit in an `i128`, and for a packed side whose base
1261/// plus width does not fit either, which is the question [`ceiling_of`] asks. The loop adds without
1262/// asking so the asking happens once out here, the same way [`packed_against_packed`] does it.
1263///
1264/// This is the pair a clustered table produces. Sorting lineitem by month of `l_shipdate` leaves
1265/// `l_commitdate` with about a hundred distinct values inside a partition, which packs, while
1266/// `l_receiptdate` arrives flat, and `l_commitdate < l_receiptdate` is q4, q12 and q21.
1267fn packed_against_flat<L, R, M>(
1268    op: Comparison,
1269    packed: &Packed<'_>,
1270    at_packed: L,
1271    flat: &Data,
1272    at_flat: R,
1273    len: usize,
1274    map: M,
1275) -> Option<Vec<bool>>
1276where
1277    L: Fn(usize) -> usize,
1278    R: Fn(usize) -> usize,
1279    M: Fn(usize) -> usize + Copy,
1280{
1281    ceiling_of(packed)?;
1282    let base = packed.base();
1283    macro_rules! layouts {
1284        ($(($variant:ident, $native:ty, $zero:expr)),+ $(,)?) => {
1285            match flat {
1286                $(
1287                    Data::$variant(run) => Some(numbers_compared(op, len, |slot| {
1288                        let row = map(slot);
1289                        (
1290                            base + i128::from(packed.code(at_packed(row))),
1291                            i128::from(run[at_flat(row)]),
1292                        )
1293                    })),
1294                )+
1295                // A layout with no whole number in it, which is every string, float and interval
1296                // column, plus `UBIGINT`'s wider sibling whose largest value has no `i128`.
1297                _ => None,
1298            }
1299        };
1300    }
1301    rudb_vector::for_each_layout!(exact, layouts)
1302}
1303
1304/// One pass over the rows with the comparison inlined into it, over a pair of numbers a row.
1305///
1306/// The operator is a match out here and a separate loop under each arm rather than one loop calling
1307/// a function it was handed, for the reason [`packed_against`] gives: a function pointer is an
1308/// indirect call per row and a loop with one in it is a loop the compiler will not widen.
1309fn numbers_compared<P>(op: Comparison, len: usize, pair: P) -> Vec<bool>
1310where
1311    P: Fn(usize) -> (i128, i128),
1312{
1313    let mut answers = vec![false; len];
1314    /// One pass over the rows with `$test` as the body.
1315    macro_rules! sweep {
1316        ($test:expr) => {{
1317            let test = $test;
1318            for (slot, answer) in answers.iter_mut().enumerate() {
1319                let (one, other) = pair(slot);
1320                *answer = test(one, other);
1321            }
1322        }};
1323    }
1324    match op {
1325        Comparison::Equal | Comparison::NotDistinctFrom => sweep!(|one, other| one == other),
1326        Comparison::NotEqual | Comparison::DistinctFrom => sweep!(|one, other| one != other),
1327        Comparison::Less => sweep!(|one, other| one < other),
1328        Comparison::LessOrEqual => sweep!(|one, other| one <= other),
1329        Comparison::Greater => sweep!(|one, other| one > other),
1330        Comparison::GreaterOrEqual => sweep!(|one, other| one >= other),
1331    }
1332    answers
1333}
1334
1335/// The largest value a packed vector can hold, or `None` if that number does not exist.
1336///
1337/// [`Packed::ceiling`] adds without asking, which is right where the caller has already put a
1338/// literal through [`Packed::code_of`] and so knows the base and the width are a pair that works.
1339/// A loop that adds a code to a base for every row has not asked anything yet, so it asks here.
1340fn ceiling_of(packed: &Packed<'_>) -> Option<i128> {
1341    let mask = u64::MAX >> (u64::BITS - packed.width());
1342    packed.base().checked_add(i128::from(mask))
1343}
1344
1345/// Where a side keeps its values, for the forms that reach them through a run of them.
1346///
1347/// A flat vector holds its values itself and row `n` is at position `n`. A dictionary holds them
1348/// somewhere else and a code per row, so row `n` is at position `codes[n]` of that. A run length
1349/// vector is the same shape with the run index standing in for the code, which is why both come
1350/// from `Vector::positions` rather than from an accessor per form: from here there is no difference
1351/// between them and writing two branches would only be two chances to write one of them wrong.
1352///
1353/// The point of naming it is that the loop underneath does not change. A comparison of two
1354/// dictionary columns is the flat loop with a different index mapping, so it costs one extra load
1355/// per row per coded side against a path that was allocating a `Value` per row and calling the
1356/// generic comparison on it. On TPC-H that path was `l_commitdate < l_receiptdate`, two dictionary
1357/// encoded date columns of six million rows, which is q4, q12 and q21.
1358enum Through<'a> {
1359    /// The values are this vector's own and row `n` is at position `n`.
1360    Direct(&'a Data),
1361    /// The values are somewhere else and row `n` is at the position this run names for it.
1362    Coded(Cow<'a, [u32]>, &'a Data),
1363}
1364
1365/// How `vector` reaches its values, or `None` for a form that does not reach them through a run.
1366///
1367/// A constant, a bit packed column and a compressed one all answer `None` here, and each has a
1368/// branch of its own further down [`specialized`] that does something better than reading a run
1369/// would. A dictionary whose values are themselves not flat answers `None` too, because then there
1370/// is no run to index and the only thing left is the row at a time path.
1371fn through(vector: &Vector) -> Option<Through<'_>> {
1372    if let Some(data) = vector.data() {
1373        return Some(Through::Direct(data));
1374    }
1375    let (codes, values) = vector.positions()?;
1376    Some(Through::Coded(codes, values.data()?))
1377}
1378
1379/// One loop per physical layout, generated rather than written out.
1380///
1381/// The two index closures are what let the same body serve flat against flat, a column against a
1382/// constant and a dictionary against a constant. `identity` on both sides is the first, `first` on
1383/// the right is the second, and the codes on the left are the third.
1384#[expect(
1385    clippy::too_many_arguments,
1386    reason = "two sides with an index each, the operator, the length and two validities, all of \
1387              which the loop needs and none of which is worth a struct that exists for one call"
1388)]
1389fn dispatch<L, R, V>(
1390    op: Comparison,
1391    len: usize,
1392    left: &Data,
1393    at_left: L,
1394    right: &Data,
1395    at_right: R,
1396    left_valid: &Validity,
1397    right_valid: &Validity,
1398    at_valid: V,
1399) -> Option<Vec<bool>>
1400where
1401    L: Fn(usize) -> usize,
1402    R: Fn(usize) -> usize,
1403    V: Fn(usize) -> usize,
1404{
1405    macro_rules! layouts {
1406        ($(($variant:ident, $native:ty, $zero:expr)),+ $(,)?) => {
1407            match (left, right) {
1408                $(
1409                    (Data::$variant(one), Data::$variant(other)) => Some(sweep(
1410                        op,
1411                        len,
1412                        |index| one[at_left(index)].cmp(&other[at_right(index)]),
1413                        left_valid,
1414                        right_valid,
1415                        &at_valid,
1416                    )),
1417                )+
1418                // Floats have their own order, which is DuckDB's rather than IEEE's, and the
1419                // widening on a `f32` is free because the comparison is against another `f32`.
1420                (Data::Float32(one), Data::Float32(other)) => Some(sweep(
1421                    op,
1422                    len,
1423                    |index| {
1424                        float_order(
1425                            f64::from(one[at_left(index)]),
1426                            f64::from(other[at_right(index)]),
1427                        )
1428                    },
1429                    left_valid,
1430                    right_valid,
1431                    &at_valid,
1432                )),
1433                (Data::Float64(one), Data::Float64(other)) => Some(sweep(
1434                    op,
1435                    len,
1436                    |index| float_order(one[at_left(index)], other[at_right(index)]),
1437                    left_valid,
1438                    right_valid,
1439                    &at_valid,
1440                )),
1441                // An interval is three counts and the order is over the one length they add up to,
1442                // so this is not the derived order of the triple and cannot be generated above.
1443                (Data::Interval(one), Data::Interval(other)) => Some(sweep(
1444                    op,
1445                    len,
1446                    |index| {
1447                        let (months, days, micros) = one[at_left(index)];
1448                        let (bm, bd, bu) = other[at_right(index)];
1449                        interval_micros(months, days, micros).cmp(&interval_micros(bm, bd, bu))
1450                    },
1451                    left_valid,
1452                    right_valid,
1453                    &at_valid,
1454                )),
1455                (Data::Varlen(one), Data::Varlen(other)) => Some(sweep(
1456                    op,
1457                    len,
1458                    |index| string_order(one, at_left(index), other, at_right(index)),
1459                    left_valid,
1460                    right_valid,
1461                    &at_valid,
1462                )),
1463                _ => None,
1464            }
1465        };
1466    }
1467    rudb_vector::for_each_layout!(ordered, layouts)
1468}
1469
1470/// The one row column for a constant, either the one that was built early or one built here.
1471///
1472/// Borrowed when a caller handed one over for this side and this value, owned when it did not, and
1473/// the loop below cannot tell the two apart. `None` is a type with no column layout, which is what
1474/// sends the whole comparison to the row at a time path.
1475fn readied<'a>(held: Option<&'a Held>, ty: &LogicalType, value: &Value) -> Option<Cow<'a, Vector>> {
1476    match held {
1477        Some(held) if held.matches(ty, value) => Some(Cow::Borrowed(held.single())),
1478        _ => Some(Cow::Owned(single(ty, value)?)),
1479    }
1480}
1481
1482/// Two strings in byte order, resolved from the four byte prefix where it can be.
1483///
1484/// The lemma this rests on is that prefix order is byte order whenever the two prefixes differ. A
1485/// view pads a string shorter than four bytes with zeros, zero is the least byte, and byte order
1486/// says a string is less than any string that extends it, so padding compares the same way the
1487/// missing bytes would have. When the prefixes are equal the payload settles it, which for an
1488/// inline string is the same sixteen bytes already loaded and for a long one is a block read.
1489fn string_order(
1490    left: &StringColumn,
1491    at_left: usize,
1492    right: &StringColumn,
1493    at_right: usize,
1494) -> Ordering {
1495    view_order(left.views().get(at_left), left.arena(), right.views().get(at_right), right.arena())
1496}
1497
1498/// The same comparison written against a view and the arena behind it rather than against a column.
1499///
1500/// Both forms that hold strings come through here, so a flat varchar column and a string view column
1501/// order a pair of rows the same way and there is no second copy of the prefix rule to drift from
1502/// this one.
1503fn view_order(
1504    one: Option<&StringView>,
1505    one_arena: &[u8],
1506    other: Option<&StringView>,
1507    other_arena: &[u8],
1508) -> Ordering {
1509    let (Some(one), Some(other)) = (one, other) else {
1510        return Ordering::Equal;
1511    };
1512    let (prefix, against) = (one.prefix(), other.prefix());
1513    if prefix != against {
1514        return prefix.cmp(&against);
1515    }
1516    // Bytes rather than `StringColumn::get`, which validates UTF-8. Everything in a column was
1517    // pushed from a `&str` so the validation cannot fail, and on a URL column, where every row
1518    // shares the `http` prefix and the payload therefore decides every comparison, it was the
1519    // larger half of the per row cost.
1520    let bytes = one.bytes_in(one_arena).unwrap_or_default();
1521    let against_bytes = other.bytes_in(other_arena).unwrap_or_default();
1522    bytes.cmp(against_bytes)
1523}
1524
1525/// The answers for one ordering, with the operator decided once rather than once per row.
1526///
1527/// This is where the match on the operator gets hoisted. Each arm calls a generic `fill` with a
1528/// different predicate, so the compiler produces eight loops whose bodies are an ordering against a
1529/// constant, rather than one loop with a branch table in it.
1530fn sweep<O, V>(
1531    op: Comparison,
1532    len: usize,
1533    order_at: O,
1534    left_valid: &Validity,
1535    right_valid: &Validity,
1536    at_valid: V,
1537) -> Vec<bool>
1538where
1539    O: Fn(usize) -> Ordering,
1540    V: Fn(usize) -> usize,
1541{
1542    let mut answers = vec![false; len];
1543    match op {
1544        Comparison::Equal => fill(&mut answers, order_at, |o| o == Ordering::Equal),
1545        Comparison::NotEqual => fill(&mut answers, order_at, |o| o != Ordering::Equal),
1546        Comparison::Less => fill(&mut answers, order_at, |o| o == Ordering::Less),
1547        Comparison::LessOrEqual => fill(&mut answers, order_at, |o| o != Ordering::Greater),
1548        Comparison::Greater => fill(&mut answers, order_at, |o| o == Ordering::Greater),
1549        Comparison::GreaterOrEqual => fill(&mut answers, order_at, |o| o != Ordering::Less),
1550        Comparison::DistinctFrom => {
1551            total(&mut answers, order_at, left_valid, right_valid, at_valid);
1552            for answer in &mut answers {
1553                *answer = !*answer;
1554            }
1555        }
1556        Comparison::NotDistinctFrom => {
1557            total(&mut answers, order_at, left_valid, right_valid, at_valid);
1558        }
1559    }
1560    answers
1561}
1562
1563/// One loop, one predicate, no branch on the operator.
1564#[inline]
1565fn fill<O, H>(answers: &mut [bool], order_at: O, held: H)
1566where
1567    O: Fn(usize) -> Ordering,
1568    H: Fn(Ordering) -> bool,
1569{
1570    for (index, answer) in answers.iter_mut().enumerate() {
1571        *answer = held(order_at(index));
1572    }
1573}
1574
1575/// `IS NOT DISTINCT FROM`, which reads validity as data rather than as an absence.
1576///
1577/// Two nulls are the same value here and a null against anything else is not, which is the whole
1578/// difference between this and `=`. The all valid case is checked once so that the common shape,
1579/// which is a total comparison inside a join on columns that happen not to be nullable, does not
1580/// pay for two validity lookups per row.
1581fn total<O, V>(
1582    answers: &mut [bool],
1583    order_at: O,
1584    left_valid: &Validity,
1585    right_valid: &Validity,
1586    at_valid: V,
1587) where
1588    O: Fn(usize) -> Ordering,
1589    V: Fn(usize) -> usize,
1590{
1591    if *left_valid == Validity::AllValid && *right_valid == Validity::AllValid {
1592        fill(answers, order_at, |o| o == Ordering::Equal);
1593        return;
1594    }
1595    for (index, answer) in answers.iter_mut().enumerate() {
1596        let row = at_valid(index);
1597        *answer = match (left_valid.is_valid(row), right_valid.is_valid(row)) {
1598            (true, true) => order_at(index) == Ordering::Equal,
1599            (false, false) => true,
1600            _ => false,
1601        };
1602    }
1603}
1604
1605/// Compares two values, producing `TRUE`, `FALSE` or `NULL`.
1606///
1607/// # Errors
1608///
1609/// If the two types cannot be compared, which after binding means one of them is a nested type.
1610pub fn compare_values(op: Comparison, left: &Value, right: &Value) -> Result<Value> {
1611    if op.is_total() {
1612        let same = match (left.is_null(), right.is_null()) {
1613            (true, true) => true,
1614            (true, false) | (false, true) => false,
1615            (false, false) => order(left, right)? == Ordering::Equal,
1616        };
1617        return Ok(Value::Boolean(match op {
1618            Comparison::NotDistinctFrom => same,
1619            _ => !same,
1620        }));
1621    }
1622    if left.is_null() || right.is_null() {
1623        return Ok(Value::Null);
1624    }
1625    let ordering = order(left, right)?;
1626    let held = match op {
1627        Comparison::Equal => ordering == Ordering::Equal,
1628        Comparison::NotEqual => ordering != Ordering::Equal,
1629        Comparison::Less => ordering == Ordering::Less,
1630        Comparison::LessOrEqual => ordering != Ordering::Greater,
1631        Comparison::Greater => ordering == Ordering::Greater,
1632        Comparison::GreaterOrEqual => ordering != Ordering::Less,
1633        Comparison::DistinctFrom | Comparison::NotDistinctFrom => {
1634            return Err(Error::internal("a total comparison reached the ordered path"));
1635        }
1636    };
1637    Ok(Value::Boolean(held))
1638}
1639
1640/// The order of two values, neither of which is null.
1641///
1642/// This is the one place the sort order of a type is written down. `ORDER BY`, `GROUP BY`, a merge
1643/// join and a min or max aggregate all reach it, and a type that ordered differently in two of
1644/// those would produce a query whose answer depends on which operator the optimizer picked.
1645///
1646/// # Errors
1647///
1648/// If either value is null, which is the caller's mistake rather than a comparison, or if the
1649/// types have no order between them.
1650pub fn order(left: &Value, right: &Value) -> Result<Ordering> {
1651    match (left, right) {
1652        (Value::Null, _) | (_, Value::Null) => {
1653            Err(Error::internal("a null reached the ordering path"))
1654        }
1655        (Value::Boolean(a), Value::Boolean(b)) => Ok(a.cmp(b)),
1656        (Value::Varchar(a), Value::Varchar(b)) => Ok(a.as_bytes().cmp(b.as_bytes())),
1657        (Value::Blob(a), Value::Blob(b)) => Ok(a.cmp(b)),
1658        (Value::Date(a), Value::Date(b)) => Ok(a.cmp(b)),
1659        // A zoned value orders with its own kind and by the same rule, since both of them are the
1660        // count of microseconds from a fixed point and the zone is about printing.
1661        (Value::Time(a), Value::Time(b))
1662        | (Value::TimeTz(a), Value::TimeTz(b))
1663        | (Value::Timestamp(a), Value::Timestamp(b))
1664        | (Value::TimestampTz(a), Value::TimestampTz(b)) => Ok(a.cmp(b)),
1665        (
1666            Value::Interval { months: am, days: ad, micros: au },
1667            Value::Interval { months: bm, days: bd, micros: bu },
1668        ) => Ok(interval_micros(*am, *ad, *au).cmp(&interval_micros(*bm, *bd, *bu))),
1669        (Value::List { values: a, .. }, Value::List { values: b, .. }) => list_order(a, b),
1670        _ => numeric_order(left, right),
1671    }
1672}
1673
1674/// Two lists in the order DuckDB puts them in, which is the order two words are in a dictionary.
1675///
1676/// The first element the two disagree on decides, and if neither ran out of elements before that
1677/// happened then the shorter one is the smaller one. So `[] < [1]` and `[1, 2] < [1, 2, 3]`, and a
1678/// list is never equal to a longer list that starts with it.
1679///
1680/// A null element is not an absence here, it is the largest value there is, which is the one place
1681/// a comparison inside a list disagrees with the same comparison outside one. `[1, NULL] > [1, 2]`
1682/// is true on the pin and `[NULL] < [1]` is false, and two nulls in the same position are equal,
1683/// which is what makes `[1, NULL] = [1, NULL]` true while `NULL = NULL` is null. That is
1684/// [`order_with_nulls`] with nulls last, so it is the sort's rule and not a second one written here.
1685///
1686/// A null list, as opposed to a list with a null in it, never reaches this. It is handled by the
1687/// caller the way a null of any other type is, which is why `NULL::INT[] = [1]` is null.
1688fn list_order(left: &[Value], right: &[Value]) -> Result<Ordering> {
1689    for (one, other) in left.iter().zip(right) {
1690        let ordering = order_with_nulls(one, other, false)?;
1691        if ordering != Ordering::Equal {
1692            return Ok(ordering);
1693        }
1694    }
1695    Ok(left.len().cmp(&right.len()))
1696}
1697
1698/// The order of two numbers, which is the case that has to work across representations.
1699fn numeric_order(left: &Value, right: &Value) -> Result<Ordering> {
1700    if let (Some(a), Some(b)) = (integral(left), integral(right)) {
1701        return Ok(a.cmp(&b));
1702    }
1703    if let (
1704        Value::Decimal { unscaled: a, scale: sa, .. },
1705        Value::Decimal { unscaled: b, scale: sb, .. },
1706    ) = (left, right)
1707    {
1708        if sa == sb {
1709            return Ok(a.cmp(b));
1710        }
1711    }
1712    match (approximate(left), approximate(right)) {
1713        (Some(a), Some(b)) => Ok(float_order(a, b)),
1714        _ => Err(Error::not_implemented(format!(
1715            "comparing {} with {}",
1716            left.logical_type(),
1717            right.logical_type()
1718        ))),
1719    }
1720}
1721
1722/// DuckDB's float order: NaN is equal to itself and above everything else, and zero has one place.
1723fn float_order(left: f64, right: f64) -> Ordering {
1724    if left == right {
1725        return Ordering::Equal;
1726    }
1727    match (left.is_nan(), right.is_nan()) {
1728        (true, true) => Ordering::Equal,
1729        (true, false) => Ordering::Greater,
1730        (false, true) => Ordering::Less,
1731        (false, false) => left.partial_cmp(&right).unwrap_or(Ordering::Equal),
1732    }
1733}
1734
1735/// The order of two values with nulls in it, for a sort key.
1736///
1737/// A sort has to put nulls somewhere and SQL lets the query say where, so this takes the answer
1738/// rather than deciding it.
1739///
1740/// # Errors
1741///
1742/// If the two types have no order between them.
1743pub fn order_with_nulls(left: &Value, right: &Value, nulls_first: bool) -> Result<Ordering> {
1744    match (left.is_null(), right.is_null()) {
1745        (true, true) => Ok(Ordering::Equal),
1746        (true, false) => Ok(if nulls_first { Ordering::Less } else { Ordering::Greater }),
1747        (false, true) => Ok(if nulls_first { Ordering::Greater } else { Ordering::Less }),
1748        (false, false) => order(left, right),
1749    }
1750}
1751
1752#[cfg(test)]
1753mod tests {
1754    use super::*;
1755
1756    fn compared(op: Comparison, left: Value, right: Value) -> Value {
1757        compare_values(op, &left, &right).expect("these types compare")
1758    }
1759
1760    /// Every comparison, so that a test that sweeps them cannot quietly miss one.
1761    const EVERY: [Comparison; 8] = [
1762        Comparison::Equal,
1763        Comparison::NotEqual,
1764        Comparison::Less,
1765        Comparison::LessOrEqual,
1766        Comparison::Greater,
1767        Comparison::GreaterOrEqual,
1768        Comparison::DistinctFrom,
1769        Comparison::NotDistinctFrom,
1770    ];
1771
1772    /// The row at a time path, kept as the oracle rather than deleted.
1773    ///
1774    /// `spec/engine/03-data-plane.md` is explicit that the slow path becomes the thing the fast
1775    /// path is checked against. This is that, written out here so that a test can call it on a pair
1776    /// of vectors whose forms the fast path does specialize.
1777    fn oracle(op: Comparison, left: &Vector, right: &Vector) -> Vector {
1778        let values: Vec<Value> = (0..left.len())
1779            .map(|index| {
1780                compare_values(op, &left.value_at(index), &right.value_at(index))
1781                    .expect("the oracle is only asked about types that compare")
1782            })
1783            .collect();
1784        Vector::from_values(LogicalType::Boolean, &values).expect("booleans")
1785    }
1786
1787    /// Asserts that the specialized path and the oracle produce the same vector, not merely the
1788    /// same answers. Same vector means the same data, the same validity representation and the
1789    /// same false in every null position, which is a much stronger statement and is free to check.
1790    fn agrees(op: Comparison, left: &Vector, right: &Vector) {
1791        let fast = compare(op, left, right).expect("compares");
1792        let slow = oracle(op, left, right);
1793        assert_eq!(fast, slow, "{op:?} on a {:?} against a {:?}", left.form(), right.form());
1794    }
1795
1796    /// [`agrees`], and the rows picked straight from the answers are the rows the flag vector
1797    /// gives. Kept apart from `agrees` because a pair of forms with no loop goes the long way here
1798    /// too, and the tests that count the long way would count it twice.
1799    fn agrees_and_selects(op: Comparison, left: &Vector, right: &Vector) {
1800        agrees(op, left, right);
1801        let slow = oracle(op, left, right);
1802        let picked = select_prepared(op, left, right, None).expect("selects");
1803        assert_eq!(
1804            picked.indices(),
1805            crate::select::selection(&slow, slow.len()).indices(),
1806            "{op:?} selected on a {:?} against a {:?}",
1807            left.form(),
1808            right.form()
1809        );
1810    }
1811
1812    /// A small deterministic generator, because a property test with no seed is a test that fails
1813    /// on somebody else's machine and passes on yours.
1814    struct Rng(u64);
1815
1816    impl Rng {
1817        fn next(&mut self) -> u64 {
1818            self.0 ^= self.0 << 13;
1819            self.0 ^= self.0 >> 7;
1820            self.0 ^= self.0 << 17;
1821            self.0
1822        }
1823
1824        fn below(&mut self, bound: u64) -> u64 {
1825            self.next() % bound
1826        }
1827    }
1828
1829    #[test]
1830    fn an_ordinary_comparison_is_null_when_either_side_is() {
1831        assert_eq!(compared(Comparison::Equal, Value::Integer(1), Value::Null), Value::Null);
1832        assert_eq!(compared(Comparison::Less, Value::Null, Value::Integer(1)), Value::Null);
1833    }
1834
1835    #[test]
1836    fn a_total_comparison_is_never_null() {
1837        assert_eq!(
1838            compared(Comparison::NotDistinctFrom, Value::Null, Value::Null),
1839            Value::Boolean(true)
1840        );
1841        assert_eq!(
1842            compared(Comparison::NotDistinctFrom, Value::Integer(1), Value::Null),
1843            Value::Boolean(false)
1844        );
1845        assert_eq!(
1846            compared(Comparison::DistinctFrom, Value::Integer(1), Value::Null),
1847            Value::Boolean(true)
1848        );
1849    }
1850
1851    #[test]
1852    fn a_string_compares_by_bytes() {
1853        assert_eq!(
1854            compared(Comparison::Less, Value::Varchar("a".into()), Value::Varchar("b".into())),
1855            Value::Boolean(true)
1856        );
1857        assert_eq!(
1858            compared(Comparison::Less, Value::Varchar("Z".into()), Value::Varchar("a".into())),
1859            Value::Boolean(true)
1860        );
1861    }
1862
1863    /// The reason this crate does not use `f64::partial_cmp` directly. A NaN that compared
1864    /// unordered would make a group by produce a group nothing can find again.
1865    #[test]
1866    fn two_nans_are_one_value_and_they_sort_above_the_numbers() {
1867        assert_eq!(
1868            compared(Comparison::Equal, Value::Double(f64::NAN), Value::Double(f64::NAN)),
1869            Value::Boolean(true)
1870        );
1871        assert_eq!(
1872            compared(Comparison::Greater, Value::Double(f64::NAN), Value::Double(1e300)),
1873            Value::Boolean(true)
1874        );
1875    }
1876
1877    #[test]
1878    fn zero_has_one_value_however_it_is_signed() {
1879        assert_eq!(
1880            compared(Comparison::Equal, Value::Double(0.0), Value::Double(-0.0)),
1881            Value::Boolean(true)
1882        );
1883    }
1884
1885    /// An interval is three counts and two of them that are the same length are one value, at
1886    /// thirty days to a month and twenty four hours to a day, which is what upstream answers. The
1887    /// three counts are still kept apart, because adding a month to a date is not adding thirty
1888    /// days to it, so these pairs are equal and print differently.
1889    #[test]
1890    fn two_intervals_of_the_same_length_are_one_value() {
1891        let day = Value::Interval { months: 0, days: 1, micros: 0 };
1892        let hours = Value::Interval { months: 0, days: 0, micros: 86_400_000_000 };
1893        let month = Value::Interval { months: 1, days: 0, micros: 0 };
1894        let thirty = Value::Interval { months: 0, days: 30, micros: 0 };
1895        let long_day = Value::Interval { months: 0, days: 0, micros: 90_000_000_000 };
1896        assert_eq!(compared(Comparison::Equal, day.clone(), hours), Value::Boolean(true));
1897        assert_eq!(compared(Comparison::Equal, month, thirty), Value::Boolean(true));
1898        assert_eq!(compared(Comparison::Greater, long_day, day), Value::Boolean(true));
1899    }
1900
1901    #[test]
1902    fn a_number_compares_the_same_however_it_is_stored() {
1903        assert_eq!(
1904            compared(Comparison::Equal, Value::Integer(3), Value::BigInt(3)),
1905            Value::Boolean(true)
1906        );
1907        assert_eq!(
1908            compared(Comparison::Less, Value::Integer(3), Value::Double(3.5)),
1909            Value::Boolean(true)
1910        );
1911    }
1912
1913    #[test]
1914    fn nulls_go_where_the_query_asked_for_them() {
1915        assert_eq!(
1916            order_with_nulls(&Value::Null, &Value::Integer(1), true).expect("orders"),
1917            Ordering::Less
1918        );
1919        assert_eq!(
1920            order_with_nulls(&Value::Null, &Value::Integer(1), false).expect("orders"),
1921            Ordering::Greater
1922        );
1923    }
1924
1925    #[test]
1926    fn two_constant_vectors_cost_one_comparison() {
1927        let left = Vector::constant(LogicalType::Integer, Value::Integer(1), 512);
1928        let right = Vector::constant(LogicalType::Integer, Value::Integer(2), 512);
1929        let result = compare(Comparison::Less, &left, &right).expect("compares");
1930        assert_eq!(result.form(), Form::Constant);
1931        assert_eq!(result.value_at(500), Value::Boolean(true));
1932    }
1933
1934    #[test]
1935    fn a_comparison_of_two_vectors_is_one_answer_per_row() {
1936        let left = Vector::from_values(
1937            LogicalType::Integer,
1938            &[Value::Integer(1), Value::Integer(5), Value::Null],
1939        )
1940        .expect("three rows");
1941        let right = Vector::constant(LogicalType::Integer, Value::Integer(3), 3);
1942        let result = compare(Comparison::Greater, &left, &right).expect("compares");
1943        assert_eq!(result.value_at(0), Value::Boolean(false));
1944        assert_eq!(result.value_at(1), Value::Boolean(true));
1945        assert_eq!(result.value_at(2), Value::Null);
1946    }
1947
1948    #[test]
1949    fn two_vectors_of_different_lengths_are_caught() {
1950        let left = Vector::constant(LogicalType::Integer, Value::Integer(1), 4);
1951        let right = Vector::constant(LogicalType::Integer, Value::Integer(1), 5);
1952        let error = compare(Comparison::Equal, &left, &right).expect_err("ragged");
1953        assert!(error.message().contains("4 row vector"), "{error}");
1954    }
1955
1956    #[test]
1957    fn turning_a_comparison_around_is_what_the_other_side_would_have_said() {
1958        for op in EVERY {
1959            let left = Value::Integer(3);
1960            let right = Value::Integer(7);
1961            assert_eq!(
1962                compare_values(op, &left, &right).expect("compares"),
1963                compare_values(op.swapped(), &right, &left).expect("compares"),
1964                "{op:?}"
1965            );
1966        }
1967    }
1968
1969    /// The whole point of the rewrite, stated as a property. Every operator, every physical
1970    /// layout, every form pair the fast path claims, against the row at a time oracle.
1971    #[test]
1972    fn every_specialized_path_agrees_with_the_row_at_a_time_path() {
1973        let mut rng = Rng(0x5eed_1234_9876_4321);
1974        let types: [LogicalType; 11] = [
1975            LogicalType::Boolean,
1976            LogicalType::TinyInt,
1977            LogicalType::SmallInt,
1978            LogicalType::Integer,
1979            LogicalType::BigInt,
1980            LogicalType::HugeInt,
1981            LogicalType::UInteger,
1982            LogicalType::Float,
1983            LogicalType::Double,
1984            LogicalType::Varchar,
1985            LogicalType::Interval,
1986        ];
1987        for ty in &types {
1988            for nulls in [0u64, 1, 3] {
1989                let len = 37;
1990                let make = |rng: &mut Rng| {
1991                    let values: Vec<Value> = (0..len)
1992                        .map(|_| {
1993                            if nulls > 0 && rng.below(nulls + 1) == 0 {
1994                                Value::Null
1995                            } else {
1996                                sample(ty, rng)
1997                            }
1998                        })
1999                        .collect();
2000                    Vector::from_values(ty.clone(), &values).expect("a flat vector")
2001                };
2002                let left = make(&mut rng);
2003                let right = make(&mut rng);
2004                let literal = sample(ty, &mut rng);
2005                let constant = Vector::constant(ty.clone(), literal, len);
2006                let null_constant = Vector::constant(ty.clone(), Value::Null, len);
2007                let codes: Vec<u32> =
2008                    (0..len).map(|_| rng.below(left.len() as u64) as u32).collect();
2009                let dictionary =
2010                    Vector::dictionary(codes, left.clone()).expect("codes are in range");
2011                // Runs over the same values, with the last one cut short so that a run boundary
2012                // does not land on the end of the vector.
2013                let ends: Vec<u32> = (1..=left.len())
2014                    .map(|run| ((run * len) / left.len()).max(run) as u32)
2015                    .collect();
2016                let runs =
2017                    Vector::runs(ends.clone(), left.clone()).expect("one value for each run");
2018                // A second pair over the other column's values, so that a coded side against a
2019                // coded side is two different runs of values reached through two different sets of
2020                // codes rather than one set read twice.
2021                let other_codes: Vec<u32> =
2022                    (0..len).map(|_| rng.below(right.len() as u64) as u32).collect();
2023                let other_dictionary =
2024                    Vector::dictionary(other_codes, right.clone()).expect("codes are in range");
2025                let other_runs = Vector::runs(ends, right.clone()).expect("one value for each run");
2026
2027                for op in EVERY {
2028                    agrees_and_selects(op, &left, &right);
2029                    agrees_and_selects(op, &left, &constant);
2030                    agrees_and_selects(op, &constant, &left);
2031                    agrees_and_selects(op, &left, &null_constant);
2032                    agrees_and_selects(op, &null_constant, &left);
2033                    agrees_and_selects(op, &dictionary, &constant);
2034                    agrees_and_selects(op, &constant, &dictionary);
2035                    // The dictionary against a flat column, which reads a null from either side and
2036                    // from the dictionary's values as well, so it is the pair with the most ways to
2037                    // disagree with the oracle and the one that got a loop last.
2038                    agrees_and_selects(op, &dictionary, &right);
2039                    agrees_and_selects(op, &right, &dictionary);
2040                    // The same four pairings for run length, which reaches the same loops through
2041                    // the same accessor, so what is being checked is that the positions it works
2042                    // out are the positions the row at a time path reads.
2043                    agrees_and_selects(op, &runs, &constant);
2044                    agrees_and_selects(op, &constant, &runs);
2045                    agrees_and_selects(op, &runs, &right);
2046                    agrees_and_selects(op, &right, &runs);
2047                    // Both sides reached through codes, which is the pair TPC-H actually hits:
2048                    // `l_commitdate < l_receiptdate` is two dictionary encoded columns of one
2049                    // table. A null here can be in four places at once, the two dictionaries' own
2050                    // masks and the two value vectors, and the answer has to be null if it is in
2051                    // any of them.
2052                    agrees_and_selects(op, &dictionary, &other_dictionary);
2053                    agrees_and_selects(op, &runs, &other_runs);
2054                    agrees_and_selects(op, &dictionary, &other_runs);
2055                    agrees_and_selects(op, &runs, &other_dictionary);
2056                }
2057            }
2058        }
2059    }
2060
2061    /// The rows of a selection the row at a time path keeps, which is what [`refine`] has to say.
2062    fn refined(op: Comparison, left: &Vector, right: &Vector, kept: &Selection) -> Selection {
2063        let mut out = Vec::new();
2064        for &row in kept.indices() {
2065            let index = row as usize;
2066            let answer = compare_values(op, &left.value_at(index), &right.value_at(index))
2067                .expect("the oracle is only asked about types that compare");
2068            if is_true(&answer) {
2069                out.push(row);
2070            }
2071        }
2072        Selection::from_indices(out)
2073    }
2074
2075    fn threads(op: Comparison, left: &Vector, right: &Vector, kept: &Selection) {
2076        let fast = refine(op, left, right, kept).expect("compares");
2077        assert_eq!(
2078            fast,
2079            refined(op, left, right, kept),
2080            "{op:?} on a {:?} against a {:?} over {} rows",
2081            left.form(),
2082            right.form(),
2083            kept.len()
2084        );
2085    }
2086
2087    /// Threading a selection through a comparison is the same rows as comparing everything and
2088    /// then keeping the ones that were already kept. Every operator, every form pair that has a
2089    /// loop, at four densities of selection, against the row at a time path.
2090    #[test]
2091    fn a_threaded_comparison_keeps_what_the_row_at_a_time_path_keeps() {
2092        let mut rng = Rng(0x5eed_4321_1234_9876);
2093        let types = [LogicalType::Integer, LogicalType::Double, LogicalType::Varchar];
2094        for ty in &types {
2095            for nulls in [0u64, 1, 3] {
2096                let len = 37;
2097                let make = |rng: &mut Rng| {
2098                    let values: Vec<Value> = (0..len)
2099                        .map(|_| {
2100                            if nulls > 0 && rng.below(nulls + 1) == 0 {
2101                                Value::Null
2102                            } else {
2103                                sample(ty, rng)
2104                            }
2105                        })
2106                        .collect();
2107                    Vector::from_values(ty.clone(), &values).expect("a flat vector")
2108                };
2109                let left = make(&mut rng);
2110                let right = make(&mut rng);
2111                let constant = Vector::constant(ty.clone(), sample(ty, &mut rng), len);
2112                let null_constant = Vector::constant(ty.clone(), Value::Null, len);
2113                let codes: Vec<u32> =
2114                    (0..len).map(|_| rng.below(left.len() as u64) as u32).collect();
2115                let dictionary =
2116                    Vector::dictionary(codes, left.clone()).expect("codes are in range");
2117
2118                // Everything, every third row, a handful including the last one, and nothing,
2119                // which is the state a conjunct chain reaches as soon as one conjunct rejects a
2120                // whole chunk and is the case where the loop below must not read anything at all.
2121                let selections = [
2122                    Selection::identity(len),
2123                    Selection::from_indices((0..len as u32).filter(|row| row % 3 == 0).collect()),
2124                    Selection::from_indices(vec![2, 5, 6, 17, 36]),
2125                    Selection::empty(),
2126                ];
2127                for op in EVERY {
2128                    for kept in &selections {
2129                        threads(op, &left, &right, kept);
2130                        threads(op, &left, &constant, kept);
2131                        threads(op, &constant, &left, kept);
2132                        threads(op, &left, &null_constant, kept);
2133                        threads(op, &null_constant, &left, kept);
2134                        threads(op, &constant, &null_constant, kept);
2135                        threads(op, &dictionary, &constant, kept);
2136                        threads(op, &constant, &dictionary, kept);
2137                        threads(op, &dictionary, &right, kept);
2138                        threads(op, &right, &dictionary, kept);
2139                    }
2140                }
2141            }
2142        }
2143    }
2144
2145    /// Two conjuncts threaded one after the other are the rows both of them keep, which is the
2146    /// property the whole filter path rests on. The second comparison sees the rows the first one
2147    /// left and never looks at the others.
2148    #[test]
2149    fn a_second_conjunct_reads_only_what_the_first_one_left() {
2150        let numbers: Vec<Value> = (0..64).map(|row| Value::Integer(row % 10)).collect();
2151        let column = Vector::from_values(LogicalType::Integer, &numbers).expect("a flat vector");
2152        let three = Vector::constant(LogicalType::Integer, Value::Integer(3), 64);
2153        let seven = Vector::constant(LogicalType::Integer, Value::Integer(7), 64);
2154
2155        let first = refine(Comparison::Greater, &column, &three, &Selection::identity(64))
2156            .expect("compares");
2157        let both = refine(Comparison::Less, &column, &seven, &first).expect("compares");
2158
2159        let expected: Vec<u32> = (0..64)
2160            .filter(|row| {
2161                let value = row % 10;
2162                value > 3 && value < 7
2163            })
2164            .collect();
2165        assert_eq!(both.indices(), expected.as_slice());
2166        assert!(both.len() < first.len(), "the second conjunct narrowed the selection");
2167    }
2168
2169    /// A null is not a true, so a threaded comparison drops the row rather than keeping it with an
2170    /// unknown answer. This is the rule that makes `WHERE a < 5` leave out the rows where `a` is
2171    /// null, and it is the one a branchless loop gets wrong if the validity is left out of it.
2172    #[test]
2173    fn a_null_row_is_not_kept_by_an_ordinary_comparison_and_is_by_a_total_one() {
2174        let column = Vector::from_values(
2175            LogicalType::Integer,
2176            &[Value::Integer(1), Value::Null, Value::Integer(3), Value::Null],
2177        )
2178        .expect("four rows");
2179        let cut = Vector::constant(LogicalType::Integer, Value::Integer(2), 4);
2180        let all = Selection::identity(4);
2181        assert_eq!(
2182            refine(Comparison::Less, &column, &cut, &all).expect("compares").indices(),
2183            &[0]
2184        );
2185        // The total comparison has an answer at every row, so the two nulls are kept here.
2186        let nulls = Vector::constant(LogicalType::Integer, Value::Null, 4);
2187        assert_eq!(
2188            refine(Comparison::NotDistinctFrom, &column, &nulls, &all).expect("compares").indices(),
2189            &[1, 3]
2190        );
2191    }
2192
2193    #[test]
2194    fn a_selection_past_the_end_is_caught() {
2195        let column = Vector::constant(LogicalType::Integer, Value::Integer(1), 4);
2196        let past = Selection::from_indices(vec![0, 4]);
2197        let error = refine(Comparison::Equal, &column, &column, &past).expect_err("out of range");
2198        assert!(error.message().contains("4 row vector"), "{error}");
2199    }
2200
2201    /// One value of a type, for the generator above.
2202    fn sample(ty: &LogicalType, rng: &mut Rng) -> Value {
2203        match ty {
2204            LogicalType::Boolean => Value::Boolean(rng.below(2) == 1),
2205            LogicalType::TinyInt => Value::TinyInt(rng.below(7) as i8 - 3),
2206            LogicalType::SmallInt => Value::SmallInt(rng.below(11) as i16 - 5),
2207            LogicalType::Integer => Value::Integer(rng.below(9) as i32 - 4),
2208            LogicalType::BigInt => Value::BigInt(rng.below(9) as i64 - 4),
2209            LogicalType::HugeInt => Value::HugeInt(i128::from(rng.below(9)) - 4),
2210            LogicalType::UInteger => Value::UInteger(rng.below(9) as u32),
2211            // A NaN and a negative zero in the pool on purpose, because DuckDB's float order is
2212            // not IEEE's and the fast path has to reach the same answer the oracle does.
2213            LogicalType::Float => Value::Float(match rng.below(5) {
2214                0 => f32::NAN,
2215                1 => -0.0,
2216                other => other as f32 - 2.0,
2217            }),
2218            LogicalType::Double => Value::Double(match rng.below(5) {
2219                0 => f64::NAN,
2220                1 => -0.0,
2221                other => other as f64 - 2.0,
2222            }),
2223            // The same length written three ways and two lengths that are close to it, because an
2224            // interval that compares as a triple gets every pair here wrong and one that compares
2225            // as a length gets them right.
2226            LogicalType::Interval => match rng.below(6) {
2227                0 => Value::Interval { months: 0, days: 1, micros: 0 },
2228                1 => Value::Interval { months: 0, days: 0, micros: 86_400_000_000 },
2229                2 => Value::Interval { months: 1, days: -29, micros: 86_400_000_000 },
2230                3 => Value::Interval { months: 1, days: 0, micros: 0 },
2231                4 => Value::Interval { months: 0, days: 0, micros: 90_000_000_000 },
2232                _ => Value::Interval { months: -1, days: 0, micros: 0 },
2233            },
2234            // Short, at the inline limit, over it, and sharing a prefix with each other, which is
2235            // where a comparison that trusts the prefix too far goes wrong.
2236            LogicalType::Varchar => Value::Varchar(
2237                match rng.below(6) {
2238                    0 => "",
2239                    1 => "ab",
2240                    2 => "abc",
2241                    3 => "abcdefghijkl",
2242                    4 => "abcdefghijklm",
2243                    _ => "abcdefghijklmnopqrstuvwxyz",
2244                }
2245                .to_owned(),
2246            ),
2247            other => panic!("the generator has no values for {other}"),
2248        }
2249    }
2250
2251    /// The prefix lemma, written as a test because the whole string path rests on it. A view pads
2252    /// a short string with zeros, so prefix order has to agree with byte order on every pair where
2253    /// the prefixes differ, including the pairs where one string is shorter than four bytes.
2254    #[test]
2255    fn prefix_order_is_byte_order_whenever_the_prefixes_differ() {
2256        let words =
2257            ["", "a", "ab", "abc", "abcd", "abcde", "b", "abcdefghijklmnop", "abcdefghijklmnoq"];
2258        let mut column = StringColumn::new();
2259        for word in words {
2260            column.push(word);
2261        }
2262        for (i, one) in words.iter().enumerate() {
2263            for (j, other) in words.iter().enumerate() {
2264                assert_eq!(
2265                    string_order(&column, i, &column, j),
2266                    one.as_bytes().cmp(other.as_bytes()),
2267                    "{one:?} against {other:?}"
2268                );
2269            }
2270        }
2271    }
2272
2273    /// A dictionary is compared once per distinct value, not once per row, and it has to reach the
2274    /// same answer including for the nulls it keeps in the vector it points at.
2275    #[test]
2276    fn a_dictionary_against_a_constant_reads_its_nulls_from_the_values() {
2277        let values = Vector::from_values(
2278            LogicalType::Integer,
2279            &[Value::Integer(1), Value::Null, Value::Integer(9)],
2280        )
2281        .expect("three values");
2282        let dictionary =
2283            Vector::dictionary(vec![0, 1, 2, 1, 0], values).expect("codes are in range");
2284        let constant = Vector::constant(LogicalType::Integer, Value::Integer(5), 5);
2285        let result = compare(Comparison::Less, &dictionary, &constant).expect("compares");
2286        assert_eq!(result.value_at(0), Value::Boolean(true));
2287        assert_eq!(result.value_at(1), Value::Null);
2288        assert_eq!(result.value_at(2), Value::Boolean(false));
2289        assert_eq!(result.value_at(3), Value::Null);
2290        assert_eq!(result.value_at(4), Value::Boolean(true));
2291    }
2292
2293    /// An ordering comparison on a text column reads the bytes where they lie rather than building a
2294    /// `Value` a row.
2295    ///
2296    /// The assertion that matters is the counter at the end. The answers were already right through
2297    /// the row at a time path, and what was wrong was the cost: `ORDER BY <varchar> LIMIT 10` asks
2298    /// every chunk whether any row in it can still beat the worst candidate the top N holds, and that
2299    /// question used to allocate a `String` for every row of every chunk. On the ClickBench file that
2300    /// was eighteen times the CPU of the same query with an integer sort key.
2301    #[test]
2302    fn an_ordering_on_text_against_a_literal_does_not_fall_back() {
2303        let before = fallback::count(Kernel::Compare, Form::Dictionary, Form::Constant);
2304        let values = Vector::from_values(
2305            LogicalType::Varchar,
2306            &[Value::Varchar("apple".into()), Value::Null, Value::Varchar("pear".into())],
2307        )
2308        .expect("three values");
2309        let column = Vector::dictionary(vec![0, 1, 2, 0], values).expect("codes are in range");
2310        let cut = Vector::constant(LogicalType::Varchar, Value::Varchar("melon".into()), 4);
2311        let result = compare(Comparison::Less, &column, &cut).expect("compares");
2312        assert_eq!(result.value_at(0), Value::Boolean(true));
2313        assert_eq!(result.value_at(1), Value::Null);
2314        assert_eq!(result.value_at(2), Value::Boolean(false));
2315        assert_eq!(result.value_at(3), Value::Boolean(true));
2316        // The literal on the left, which is the same question with the comparison turned around, and
2317        // it has to be turned around once rather than once a row.
2318        let other = compare(Comparison::Greater, &cut, &column).expect("compares");
2319        assert_eq!(other.value_at(0), Value::Boolean(true));
2320        assert_eq!(other.value_at(1), Value::Null);
2321        assert_eq!(other.value_at(2), Value::Boolean(false));
2322        // The selection entry point, which is the one the filter and the top N reach.
2323        let kept = refine(Comparison::GreaterOrEqual, &column, &cut, &Selection::identity(4))
2324            .expect("refines");
2325        assert_eq!(kept.indices(), [2]);
2326        assert_eq!(fallback::count(Kernel::Compare, Form::Dictionary, Form::Constant), before);
2327    }
2328
2329    /// Two dictionary columns compared with each other, which is the pair the TPC-H suite spends
2330    /// the most row at a time calls on and had no loop until it was counted.
2331    ///
2332    /// `l_commitdate < l_receiptdate` in q4, q12 and q21 is two dictionary encoded date columns of
2333    /// six million rows each, and every one of those rows was going through the generic comparison
2334    /// on a pair of freshly allocated `Value`s. The shape here is the same one: different codes,
2335    /// different values, and a null reached through the codes rather than sitting at the row.
2336    #[test]
2337    fn two_dictionary_columns_compared_with_each_other_do_not_fall_back() {
2338        let before = fallback::count(Kernel::Compare, Form::Dictionary, Form::Dictionary);
2339        let dates = |values: &[Value]| {
2340            Vector::from_values(LogicalType::Date, values).expect("a flat date column")
2341        };
2342        // 10, 20, 10, 30 against 15, null, 5, 15.
2343        let committed = Vector::dictionary(
2344            vec![0, 1, 0, 2],
2345            dates(&[Value::Date(10), Value::Date(20), Value::Date(30)]),
2346        )
2347        .expect("codes are in range");
2348        let received = Vector::dictionary(
2349            vec![0, 1, 2, 0],
2350            dates(&[Value::Date(15), Value::Null, Value::Date(5)]),
2351        )
2352        .expect("codes are in range");
2353        let result = compare(Comparison::Less, &committed, &received).expect("compares");
2354        assert_eq!(result.value_at(0), Value::Boolean(true), "10 < 15");
2355        assert_eq!(result.value_at(1), Value::Null, "20 against a null");
2356        assert_eq!(result.value_at(2), Value::Boolean(false), "10 against 5");
2357        assert_eq!(result.value_at(3), Value::Boolean(false), "30 against 15");
2358        // The selection entry point, which is the one the filter reaches and the one q4 is made of.
2359        let kept = refine(Comparison::Less, &committed, &received, &Selection::identity(4))
2360            .expect("refines");
2361        assert_eq!(kept.indices(), [0]);
2362        assert_eq!(fallback::count(Kernel::Compare, Form::Dictionary, Form::Dictionary), before);
2363    }
2364
2365    /// A form pair with no loop is answered correctly and counted, which is the whole contract of
2366    /// the fallback counter. Sequence against a column is the one this file leaves out on purpose.
2367    #[test]
2368    fn a_form_pair_with_no_loop_is_still_right_and_says_so() {
2369        // The counters are per thread in a test build, so this reads its own and nothing else's.
2370        let before = fallback::count(Kernel::Compare, Form::Sequence, Form::Flat);
2371        let sequence = Vector::sequence(10, 1, 4);
2372        let flat = Vector::from_values(
2373            LogicalType::BigInt,
2374            &[Value::BigInt(9), Value::BigInt(11), Value::BigInt(12), Value::Null],
2375        )
2376        .expect("four rows");
2377        let result = compare(Comparison::Less, &sequence, &flat).expect("compares");
2378        assert_eq!(result.value_at(0), Value::Boolean(false));
2379        assert_eq!(result.value_at(1), Value::Boolean(false));
2380        assert_eq!(result.value_at(2), Value::Boolean(false));
2381        assert_eq!(result.value_at(3), Value::Null);
2382        assert!(fallback::count(Kernel::Compare, Form::Sequence, Form::Flat) > before);
2383    }
2384
2385    /// The reason `Vector::dictionary` composes rather than stacks, stated as the thing that breaks
2386    /// if it stops.
2387    ///
2388    /// Every loop in this file reaches for the values behind the codes with `Vector::data`, and a
2389    /// dictionary pointing at a dictionary has no data to hand back, so a second filter over an
2390    /// already filtered chunk used to turn every one of these kernels off and drop the comparison
2391    /// onto the row at a time path. Measured on server3 over a chunk of two numeric columns that was
2392    /// selected twice, that was 3.5 nanoseconds a row becoming 104, and a third and fourth level
2393    /// cost nothing more because the first one had already given up everything there was to give.
2394    #[test]
2395    fn a_second_level_of_codes_does_not_turn_the_loops_off() {
2396        let before = fallback::count(Kernel::Compare, Form::Dictionary, Form::Constant);
2397        let values = Vector::from_values(
2398            LogicalType::Integer,
2399            &[Value::Integer(1), Value::Integer(5), Value::Integer(9)],
2400        )
2401        .expect("three rows");
2402        let once = Vector::dictionary(vec![2, 1, 0], values).expect("codes are in range");
2403        let twice = Vector::dictionary(vec![1, 2], once).expect("codes are in range");
2404        let cut = Vector::constant(LogicalType::Integer, Value::Integer(4), 2);
2405        let result = compare(Comparison::Greater, &twice, &cut).expect("compares");
2406        assert_eq!(result.value_at(0), Value::Boolean(true));
2407        assert_eq!(result.value_at(1), Value::Boolean(false));
2408        assert_eq!(fallback::count(Kernel::Compare, Form::Dictionary, Form::Constant), before);
2409    }
2410
2411    /// Either side all null, on one of the six ordinary comparisons, is every answer null without
2412    /// the data being read. The vector this produces has to be the one the oracle produces, which
2413    /// is a flat run of falses under an all invalid validity rather than a constant.
2414    #[test]
2415    fn a_side_that_is_entirely_null_answers_without_reading_the_other() {
2416        let nulls = Vector::constant(LogicalType::Integer, Value::Null, 6);
2417        let flat = Vector::from_values(
2418            LogicalType::Integer,
2419            &[
2420                Value::Integer(1),
2421                Value::Integer(2),
2422                Value::Integer(3),
2423                Value::Integer(4),
2424                Value::Integer(5),
2425                Value::Integer(6),
2426            ],
2427        )
2428        .expect("six rows");
2429        agrees(Comparison::Less, &nulls, &flat);
2430        agrees(Comparison::Equal, &flat, &nulls);
2431        assert_eq!(
2432            compare(Comparison::Less, &nulls, &flat).expect("compares").validity(),
2433            &Validity::AllInvalid
2434        );
2435    }
2436
2437    /// An empty vector is not a special case anywhere, and the easiest way to keep it that way is
2438    /// to say so in a test rather than to find out from a panic in an operator.
2439    #[test]
2440    fn an_empty_comparison_is_an_empty_answer() {
2441        let left = Vector::from_values(LogicalType::Integer, &[]).expect("no rows");
2442        let right = Vector::constant(LogicalType::Integer, Value::Integer(1), 0);
2443        let result = compare(Comparison::Equal, &left, &right).expect("compares");
2444        assert_eq!(result.len(), 0);
2445    }
2446
2447    /// Six strings, three of them sharing a prefix, and a null, which is the column the two tests
2448    /// below read.
2449    fn words() -> Vector {
2450        Vector::from_values(
2451            LogicalType::Varchar,
2452            &[
2453                Value::Varchar("http://a".into()),
2454                Value::Varchar("http://b".into()),
2455                Value::Null,
2456                Value::Varchar("ab".into()),
2457                Value::Varchar("http://a".into()),
2458                Value::Varchar("z".into()),
2459            ],
2460        )
2461        .expect("six rows")
2462    }
2463
2464    /// A literal built early answers what a literal built per chunk answers.
2465    ///
2466    /// Every operator and both entry points, because the whole claim of the prepared literal is
2467    /// that it changes nothing, and the string column is the one where it changes the most work:
2468    /// what it carries is the four byte prefix the comparison resolves almost every row from.
2469    #[test]
2470    fn a_literal_built_early_answers_what_one_built_here_answers() {
2471        let column = words();
2472        let value = Value::Varchar("http://b".into());
2473        let constant = Vector::constant(LogicalType::Varchar, value.clone(), column.len());
2474        let held = Held::of(&LogicalType::Varchar, &value).expect("a varchar has a column");
2475        let kept = Selection::from_indices(vec![0, 1, 3, 5]);
2476        for op in [
2477            Comparison::Equal,
2478            Comparison::NotEqual,
2479            Comparison::Less,
2480            Comparison::LessOrEqual,
2481            Comparison::Greater,
2482            Comparison::GreaterOrEqual,
2483            Comparison::DistinctFrom,
2484            Comparison::NotDistinctFrom,
2485        ] {
2486            let prepared = compare_prepared(op, &column, &constant, Some(&held)).expect("compares");
2487            assert_eq!(prepared, compare(op, &column, &constant).expect("compares"), "{op:?}");
2488            // And with the literal on the left, which is the same loop turned around.
2489            let flipped = compare_prepared(op, &constant, &column, Some(&held)).expect("compares");
2490            assert_eq!(flipped, compare(op, &constant, &column).expect("compares"), "{op:?}");
2491            let refined =
2492                refine_prepared(op, &column, &constant, &kept, Some(&held)).expect("refines");
2493            assert_eq!(refined, refine(op, &column, &constant, &kept).expect("refines"), "{op:?}");
2494        }
2495    }
2496
2497    /// A literal built for something else is ignored rather than believed.
2498    ///
2499    /// The caller in `rudb-exec` takes the value out of the step it hands the answer back with, so
2500    /// this cannot happen there, and the kernel is public. A wrong answer is a much worse failure
2501    /// than a column built per chunk, so the check is a value comparison per chunk and this is what
2502    /// says it works.
2503    #[test]
2504    fn a_literal_built_for_another_value_is_ignored() {
2505        let column = words();
2506        let constant = Vector::constant(LogicalType::Varchar, Value::Varchar("z".into()), 6);
2507        let wrong = Held::of(&LogicalType::Varchar, &Value::Varchar("ab".into()))
2508            .expect("a varchar has a column");
2509        let answer = compare_prepared(Comparison::Equal, &column, &constant, Some(&wrong))
2510            .expect("compares");
2511        assert_eq!(answer, compare(Comparison::Equal, &column, &constant).expect("compares"));
2512        // And one built for another type, which is what a comparison across two types would hand
2513        // over if the caller took it from the wrong side.
2514        let other = Held::of(&LogicalType::Integer, &Value::Integer(1)).expect("an integer column");
2515        let answer = compare_prepared(Comparison::Equal, &column, &constant, Some(&other))
2516            .expect("compares");
2517        assert_eq!(answer, compare(Comparison::Equal, &column, &constant).expect("compares"));
2518    }
2519
2520    /// A bit packed column against a literal is compared in code space, which has to reach the
2521    /// oracle's answer on all eight comparisons and with the literal on either side.
2522    #[test]
2523    fn a_packed_column_against_a_constant_answers_what_the_oracle_answers() {
2524        let values: Vec<i32> = (0..64).map(|row| 1000 + (row * 37) % 500).collect();
2525        let flat = Vector::flat(LogicalType::Integer, Data::Int32(values.into()))
2526            .expect("integers are an i32 layout");
2527        let packed = flat.bit_packed().expect("a five hundred wide range packs");
2528        assert_eq!(packed.form(), Form::BitPacked);
2529        for literal in [999, 1000, 1200, 1499, 1500, 2000] {
2530            let constant = Vector::constant(LogicalType::Integer, Value::Integer(literal), 64);
2531            for op in EVERY {
2532                agrees(op, &packed, &constant);
2533                agrees(op, &constant, &packed);
2534            }
2535        }
2536    }
2537
2538    /// The nulls of a packed column live in its validity rather than in its bits, so a comparison
2539    /// has to blank them the way it blanks a flat column's, and the bits under them are whatever
2540    /// the packing wrote there.
2541    #[test]
2542    fn a_packed_column_with_nulls_answers_what_the_oracle_answers() {
2543        let values: Vec<i32> = (0..32).map(|row| 40 + row * 3).collect();
2544        let flat = Vector::flat(LogicalType::Integer, Data::Int32(values.into()))
2545            .expect("integers are an i32 layout")
2546            .with_validity(Validity::from_iter(32, |row| row % 5 != 0));
2547        let packed = flat.bit_packed().expect("packs");
2548        let constant = Vector::constant(LogicalType::Integer, Value::Integer(80), 32);
2549        for op in EVERY {
2550            agrees(op, &packed, &constant);
2551        }
2552    }
2553
2554    /// A literal the width cannot hold answers every row without a bit being read, and the answer
2555    /// still has to be the one the oracle gives.
2556    #[test]
2557    fn a_literal_outside_the_packed_range_answers_the_whole_vector_at_once() {
2558        let before = fallback::count(Kernel::Compare, Form::BitPacked, Form::Constant);
2559        let values: Vec<i32> = (0..16).map(|row| 500 + row).collect();
2560        let flat = Vector::flat(LogicalType::Integer, Data::Int32(values.into()))
2561            .expect("integers are an i32 layout");
2562        let packed = flat.bit_packed().expect("packs");
2563        let literals = [-1, 0, 499, 516, 100_000];
2564        for literal in literals {
2565            let constant = Vector::constant(LogicalType::Integer, Value::Integer(literal), 16);
2566            for op in EVERY {
2567                agrees(op, &packed, &constant);
2568            }
2569        }
2570        // The six ordinary comparisons have a loop for this pair and the two that never go null do
2571        // not, because those want the null rule inside the loop and the code space loop does not
2572        // carry one. They take the row at a time path and count themselves, which is the counter
2573        // doing its job rather than a gap being hidden.
2574        let total = EVERY.iter().filter(|op| op.is_total()).count();
2575        assert_eq!(
2576            fallback::count(Kernel::Compare, Form::BitPacked, Form::Constant) - before,
2577            (literals.len() * total) as u64,
2578            "only the two total comparisons fall through"
2579        );
2580    }
2581
2582    /// The conjunct path reads the rows an earlier conjunct kept, so the code space loop has to be
2583    /// reached through the selection rather than through the row number.
2584    #[test]
2585    fn refining_a_selection_over_a_packed_column_keeps_the_same_rows() {
2586        let values: Vec<i32> = (0..64).map(|row| 200 + (row * 11) % 128).collect();
2587        let flat = Vector::flat(LogicalType::Integer, Data::Int32(values.clone().into()))
2588            .expect("integers are an i32 layout");
2589        let packed = flat.bit_packed().expect("packs");
2590        let kept = Selection::from_predicate(64, |row| row % 3 == 0);
2591        let constant = Vector::constant(LogicalType::Integer, Value::Integer(260), 64);
2592        let packed_rows = refine(Comparison::Greater, &packed, &constant, &kept).expect("refines");
2593        let flat_rows = refine(Comparison::Greater, &flat, &constant, &kept).expect("refines");
2594        assert_eq!(packed_rows.indices(), flat_rows.indices());
2595        assert!(!packed_rows.is_empty(), "the literal is inside the range");
2596    }
2597
2598    /// Two packed columns whose ranges overlap, which is the pair a stored table produces and the
2599    /// pair `l_commitdate < l_receiptdate` is. The two bases differ, so the answer has to come out
2600    /// of the sums rather than out of the codes, and it has to be the oracle's answer.
2601    #[test]
2602    fn two_packed_columns_against_each_other_answer_what_the_oracle_answers() {
2603        let before = fallback::count(Kernel::Compare, Form::BitPacked, Form::BitPacked);
2604        let one: Vec<i32> = (0..64).map(|row| 9000 + (row * 37) % 500).collect();
2605        let other: Vec<i32> = (0..64).map(|row| 9200 + (row * 53) % 400).collect();
2606        let left = Vector::flat(LogicalType::Integer, Data::Int32(one.into()))
2607            .expect("integers are an i32 layout")
2608            .bit_packed()
2609            .expect("a five hundred wide range packs");
2610        let right = Vector::flat(LogicalType::Integer, Data::Int32(other.into()))
2611            .expect("integers are an i32 layout")
2612            .bit_packed()
2613            .expect("a four hundred wide range packs");
2614        assert_eq!(left.form(), Form::BitPacked);
2615        assert_eq!(right.form(), Form::BitPacked);
2616        assert_ne!(
2617            left.packed_parts().expect("packed").base(),
2618            right.packed_parts().expect("packed").base(),
2619            "the two bases are the two column minimums and this test wants them apart"
2620        );
2621        for op in EVERY {
2622            agrees(op, &left, &right);
2623            agrees(op, &right, &left);
2624        }
2625        // The two total comparisons want the null rule inside the loop and the code space loop does
2626        // not carry one, so those are the only ones that count themselves, the same way they do for
2627        // a packed column against a literal.
2628        let total = EVERY.iter().filter(|op| op.is_total()).count();
2629        assert_eq!(
2630            fallback::count(Kernel::Compare, Form::BitPacked, Form::BitPacked) - before,
2631            (total * 2) as u64,
2632            "only the two total comparisons fall through"
2633        );
2634    }
2635
2636    /// A column whose largest value is below the other's smallest answers every row the same way,
2637    /// and the answer still has to be the one the oracle gives on all eight comparisons.
2638    #[test]
2639    fn two_packed_columns_whose_ranges_do_not_overlap_answer_the_whole_vector_at_once() {
2640        let one: Vec<i32> = (0..32).map(|row| 100 + row).collect();
2641        let other: Vec<i32> = (0..32).map(|row| 500 + row * 2).collect();
2642        let low = Vector::flat(LogicalType::Integer, Data::Int32(one.into()))
2643            .expect("integers are an i32 layout")
2644            .bit_packed()
2645            .expect("packs");
2646        let high = Vector::flat(LogicalType::Integer, Data::Int32(other.into()))
2647            .expect("integers are an i32 layout")
2648            .bit_packed()
2649            .expect("packs");
2650        for op in EVERY {
2651            agrees(op, &low, &high);
2652            agrees(op, &high, &low);
2653        }
2654    }
2655
2656    /// The nulls of a packed column live in its validity rather than in its bits, so a pair of them
2657    /// has to blank the rows either side is null in, and the bits under those rows are whatever the
2658    /// packing wrote there.
2659    #[test]
2660    fn two_packed_columns_with_nulls_answer_what_the_oracle_answers() {
2661        let one: Vec<i32> = (0..32).map(|row| 40 + row * 3).collect();
2662        let other: Vec<i32> = (0..32).map(|row| 60 + row * 2).collect();
2663        let left = Vector::flat(LogicalType::Integer, Data::Int32(one.into()))
2664            .expect("integers are an i32 layout")
2665            .with_validity(Validity::from_iter(32, |row| row % 5 != 0))
2666            .bit_packed()
2667            .expect("packs");
2668        let right = Vector::flat(LogicalType::Integer, Data::Int32(other.into()))
2669            .expect("integers are an i32 layout")
2670            .with_validity(Validity::from_iter(32, |row| row % 3 != 0))
2671            .bit_packed()
2672            .expect("packs");
2673        for op in EVERY {
2674            agrees(op, &left, &right);
2675        }
2676    }
2677
2678    /// A dictionary over a packed run on either side or both, which is what a stored date column
2679    /// comes back as and is the pair q04 and q21 fall through on. The nulls are in three places at
2680    /// once here, the outer mask, the dictionary's values and the rows a code repeats, and the
2681    /// oracle sees all three.
2682    #[test]
2683    fn a_dictionary_over_a_packed_run_answers_what_the_oracle_answers() {
2684        let before = fallback::count(Kernel::Compare, Form::Dictionary, Form::Dictionary);
2685        let distinct = |start: i32, step: i32, nulls: usize| {
2686            let values: Vec<i32> = (0..24).map(|row| start + row * step).collect();
2687            Vector::flat(LogicalType::Date, Data::Int32(values.into()))
2688                .expect("dates are an i32 layout")
2689                .with_validity(Validity::from_iter(24, |row| row % nulls != 0))
2690                .bit_packed()
2691                .expect("packs")
2692        };
2693        let codes =
2694            |seed: usize| -> Vec<u32> { (0..64).map(|row| ((row * seed) % 24) as u32).collect() };
2695        let one = Vector::dictionary(codes(7), distinct(9_000, 3, 5)).expect("codes are in range");
2696        let other =
2697            Vector::dictionary(codes(5), distinct(9_020, 2, 7)).expect("codes are in range");
2698        let straight = Vector::flat(
2699            LogicalType::Date,
2700            Data::Int32((0..64).map(|row| 9_010 + row).collect::<Vec<i32>>().into()),
2701        )
2702        .expect("dates are an i32 layout")
2703        .bit_packed()
2704        .expect("packs");
2705        assert_eq!(one.form(), Form::Dictionary);
2706        assert_eq!(straight.form(), Form::BitPacked);
2707        for op in EVERY {
2708            agrees(op, &one, &other);
2709            agrees(op, &one, &straight);
2710            agrees(op, &straight, &other);
2711        }
2712        // Three pairs, and only the two total comparisons of each are left to count themselves.
2713        let total = EVERY.iter().filter(|op| op.is_total()).count();
2714        assert_eq!(
2715            fallback::count(Kernel::Compare, Form::Dictionary, Form::Dictionary) - before,
2716            total as u64,
2717            "only the two total comparisons fall through"
2718        );
2719    }
2720
2721    /// A bit packed column against a flat one, which is the pair a clustered table hands the filter.
2722    ///
2723    /// Sorting lineitem by month of `l_shipdate` leaves `l_commitdate` with few enough distinct
2724    /// values inside a partition to pack, while `l_receiptdate` arrives flat, so the better encoding
2725    /// the clustering buys was what turned `l_commitdate < l_receiptdate` into a `Value` a side a
2726    /// row. All four ways the two sides can be indexed are here, because a stored date column comes
2727    /// back as a dictionary over a packed run about as often as it comes back as a packed run on its
2728    /// own.
2729    #[test]
2730    fn a_packed_column_against_a_flat_one_answers_what_the_oracle_answers() {
2731        let before = fallback::count(Kernel::Compare, Form::BitPacked, Form::Flat);
2732        let dates = |start: i32, step: i32, nulls: usize| {
2733            Vector::flat(
2734                LogicalType::Date,
2735                Data::Int32((0..64).map(|row| start + row * step).collect::<Vec<i32>>().into()),
2736            )
2737            .expect("dates are an i32 layout")
2738            .with_validity(Validity::from_iter(64, |row| row % nulls != 0))
2739        };
2740        let codes =
2741            |seed: usize| -> Vec<u32> { (0..64).map(|row| ((row * seed) % 64) as u32).collect() };
2742        let packed = dates(9_000, 3, 5).bit_packed().expect("packs");
2743        let flat = dates(9_040, 2, 7);
2744        let over_packed = Vector::dictionary(codes(7), packed.clone()).expect("codes are in range");
2745        let over_flat = Vector::dictionary(codes(11), flat.clone()).expect("codes are in range");
2746        assert_eq!(packed.form(), Form::BitPacked);
2747        assert_eq!(flat.form(), Form::Flat);
2748        // The ranges overlap, which is what makes the comparison a pass over the rows rather than
2749        // arithmetic on four numbers, and this test wants the pass.
2750        assert!(packed.packed_parts().expect("packed").base() < 9_040 + 63 * 2);
2751        for op in EVERY {
2752            agrees(op, &packed, &flat);
2753            agrees(op, &flat, &packed);
2754            agrees(op, &packed, &over_flat);
2755            agrees(op, &over_packed, &flat);
2756            agrees(op, &over_packed, &over_flat);
2757        }
2758        // Only the two total comparisons are left to count themselves, the same way they are for a
2759        // pair of packed columns, and of the five pairs above only the first is packed against flat.
2760        let total = EVERY.iter().filter(|op| op.is_total()).count();
2761        assert_eq!(
2762            fallback::count(Kernel::Compare, Form::BitPacked, Form::Flat) - before,
2763            total as u64,
2764            "only the two total comparisons fall through"
2765        );
2766    }
2767
2768    /// The conjunct path reads the rows an earlier conjunct kept, so a pair of packed columns has
2769    /// to be reached through the selection rather than through the row number, which is what q12
2770    /// does with its two date comparisons one after the other.
2771    #[test]
2772    fn refining_a_selection_over_two_packed_columns_keeps_the_same_rows() {
2773        let one: Vec<i32> = (0..64).map(|row| 200 + (row * 11) % 128).collect();
2774        let other: Vec<i32> = (0..64).map(|row| 240 + (row * 17) % 96).collect();
2775        let left = Vector::flat(LogicalType::Integer, Data::Int32(one.clone().into()))
2776            .expect("integers are an i32 layout");
2777        let right = Vector::flat(LogicalType::Integer, Data::Int32(other.clone().into()))
2778            .expect("integers are an i32 layout");
2779        let kept = Selection::from_predicate(64, |row| row % 3 == 0);
2780        let packed_rows = refine(
2781            Comparison::Less,
2782            &left.bit_packed().expect("packs"),
2783            &right.bit_packed().expect("packs"),
2784            &kept,
2785        )
2786        .expect("refines");
2787        let flat_rows = refine(Comparison::Less, &left, &right, &kept).expect("refines");
2788        assert_eq!(packed_rows.indices(), flat_rows.indices());
2789        assert!(!packed_rows.is_empty(), "the two ranges overlap");
2790    }
2791
2792    /// A column of URLs, which is the shape the string view form exists for: a shared prefix that
2793    /// the four bytes in the view cannot settle, and payloads long enough to be in the arena.
2794    fn urls(count: usize) -> Vector {
2795        let mut rng = Rng(0x5eed_1234);
2796        let values: Vec<Value> = (0..count)
2797            .map(|_| {
2798                let host = rng.below(6);
2799                let path = rng.below(40);
2800                Value::Varchar(format!("http://example{host}.test/a/rather/long/path/{path}"))
2801            })
2802            .collect();
2803        Vector::from_values(LogicalType::Varchar, &values).expect("strings")
2804    }
2805
2806    #[test]
2807    fn a_string_view_column_against_a_literal_answers_what_the_oracle_answers() {
2808        let before = fallback::count(Kernel::Compare, Form::StringView, Form::Constant);
2809        let shared = urls(64).shared_text().expect("shares");
2810        assert_eq!(shared.form(), Form::StringView);
2811        let literals = ["http://example3.test/a/rather/long/path/7", "a", "zzz", ""];
2812        for literal in literals {
2813            let value = Value::Varchar(literal.to_owned());
2814            let constant = Vector::constant(LogicalType::Varchar, value, 64);
2815            for op in EVERY {
2816                agrees(op, &shared, &constant);
2817                agrees(op, &constant, &shared);
2818            }
2819        }
2820        assert_eq!(
2821            fallback::count(Kernel::Compare, Form::StringView, Form::Constant),
2822            before,
2823            "the form has a loop of its own for every comparison"
2824        );
2825    }
2826
2827    #[test]
2828    fn a_string_view_column_against_another_one_answers_what_the_oracle_answers() {
2829        let shared = urls(48).shared_text().expect("shares");
2830        let other = urls(48).shared_text().expect("shares");
2831        let flat = urls(48);
2832        for op in EVERY {
2833            agrees(op, &shared, &other);
2834            agrees(op, &shared, &flat);
2835            agrees(op, &flat, &shared);
2836        }
2837    }
2838
2839    #[test]
2840    fn the_nulls_of_a_string_view_column_are_the_nulls_the_oracle_sees() {
2841        let shared = urls(32)
2842            .with_validity(Validity::from_iter(32, |row| row % 4 != 1))
2843            .shared_text()
2844            .expect("shares");
2845        let constant =
2846            Vector::constant(LogicalType::Varchar, Value::Varchar("http://example3".into()), 32);
2847        for op in EVERY {
2848            agrees(op, &shared, &constant);
2849        }
2850    }
2851
2852    #[test]
2853    fn a_compressed_column_against_a_literal_answers_what_the_oracle_answers() {
2854        let before = fallback::count(Kernel::Compare, Form::Fsst, Form::Constant);
2855        let flat = urls(64);
2856        let coded = flat.clone().compressed().expect("compresses");
2857        assert_eq!(coded.form(), Form::Fsst);
2858        let present = match coded.value_at(9) {
2859            Value::Varchar(text) => text,
2860            other => panic!("a string column reads back strings, not {other:?}"),
2861        };
2862        for literal in [present.as_str(), "http://example3.test/nothing/like/it", ""] {
2863            let value = Value::Varchar(literal.to_owned());
2864            let constant = Vector::constant(LogicalType::Varchar, value, 64);
2865            for op in EVERY {
2866                agrees(op, &coded, &constant);
2867                agrees(op, &constant, &coded);
2868            }
2869        }
2870        // Equality has a loop in code space and the six comparisons that need an order do not,
2871        // because a symbol code says nothing about where its symbol sorts. Those decompress a row at
2872        // a time and count themselves, which is the counter doing its job rather than a gap hiding.
2873        let ordered = EVERY.len() - 2;
2874        assert_eq!(
2875            fallback::count(Kernel::Compare, Form::Fsst, Form::Constant) - before,
2876            (3 * ordered) as u64,
2877            "only the comparisons that need an order fall through"
2878        );
2879    }
2880
2881    /// Equality in code space is only right if compressing is a function, so the same string always
2882    /// has the same codes and two different strings never do. This is that claim as a test.
2883    #[test]
2884    fn every_row_of_a_compressed_column_matches_itself_and_nothing_else() {
2885        let flat = urls(48);
2886        let coded = flat.clone().compressed().expect("compresses");
2887        for row in 0..48 {
2888            let constant = Vector::constant(LogicalType::Varchar, flat.value_at(row), 48);
2889            let equal = compare(Comparison::Equal, &coded, &constant).expect("compares");
2890            for other in 0..48 {
2891                let want = flat.value_at(other) == flat.value_at(row);
2892                assert_eq!(
2893                    equal.value_at(other),
2894                    Value::Boolean(want),
2895                    "row {row} against {other}"
2896                );
2897            }
2898        }
2899    }
2900
2901    #[test]
2902    fn the_nulls_of_a_compressed_column_are_the_nulls_the_oracle_sees() {
2903        let coded = urls(32)
2904            .with_validity(Validity::from_iter(32, |row| row % 3 != 0))
2905            .compressed()
2906            .expect("compresses");
2907        let value = coded.value_at(1);
2908        let constant = Vector::constant(LogicalType::Varchar, value, 32);
2909        for op in EVERY {
2910            agrees(op, &coded, &constant);
2911        }
2912    }
2913
2914    /// The two forms hold the same strings in two different places, so a filter over either one has
2915    /// to keep the same rows. This is the differential check that the arena being shared changed
2916    /// nothing about what a comparison means.
2917    #[test]
2918    fn a_filter_over_either_string_form_keeps_the_same_rows() {
2919        let flat = urls(96);
2920        let shared = flat.clone().shared_text().expect("shares");
2921        let constant =
2922            Vector::constant(LogicalType::Varchar, Value::Varchar("http://example3".into()), 96);
2923        let kept = Selection::from_predicate(96, |row| row % 5 != 0);
2924        for op in EVERY {
2925            let over_flat = refine(op, &flat, &constant, &kept).expect("refines");
2926            let over_shared = refine(op, &shared, &constant, &kept).expect("refines");
2927            assert_eq!(over_shared.indices(), over_flat.indices(), "{op:?}");
2928        }
2929    }
2930
2931    /// The peeled path and the row at a time oracle on the same rows, on both spellings and on
2932    /// both entry points. A dictionary that shares its values is what a native scan hands over, so
2933    /// this is the shape every `WHERE URL <> ''` in ClickBench arrives in.
2934    #[test]
2935    fn a_comparison_peeled_over_a_shared_dictionary_answers_what_the_oracle_answers() {
2936        let words = ["", "one", "two", "", "three"];
2937        let values: Vec<Value> = words.iter().map(|text| Value::Varchar((*text).into())).collect();
2938        let values =
2939            Arc::new(Vector::from_values(LogicalType::Varchar, &values).expect("a vector of text"));
2940        let codes = vec![0, 1, 3, 2, 0, 4, 1, 0];
2941        let column = Vector::stable_dictionary(codes.clone(), values).expect("codes are in range");
2942        same_as_the_oracle(&column);
2943    }
2944
2945    /// Values a storage reader would hand over, which answer one at a time and which know the
2946    /// order the writer sorted them into.
2947    #[derive(Debug)]
2948    struct Filed {
2949        values: Vec<Vec<u8>>,
2950        order: Vec<u32>,
2951        /// [`Self::order`] turned round, built on the first ask the way a reader's is.
2952        ranked: std::sync::OnceLock<Option<Vec<u32>>>,
2953    }
2954
2955    impl rudb_vector::TextSource for Filed {
2956        fn len(&self) -> usize {
2957            self.values.len()
2958        }
2959
2960        fn bytes_at(&self, index: usize) -> Result<Option<&[u8]>> {
2961            Ok(self.values.get(index).map(Vec::as_slice))
2962        }
2963
2964        fn footprint(&self) -> usize {
2965            self.values.iter().map(Vec::len).sum()
2966        }
2967
2968        fn ranks(&self) -> Option<usize> {
2969            Some(self.order.len())
2970        }
2971
2972        fn compare_rank(&self, rank: usize, wanted: &[u8]) -> Result<Ordering> {
2973            // Plain bytes rather than the head the native format compares first, because what this
2974            // test is about is the answer the comparison gives and not how few reads it took.
2975            Ok(self.values[self.order[rank] as usize].as_slice().cmp(wanted))
2976        }
2977
2978        fn code_at_rank(&self, rank: usize) -> Result<u32> {
2979            Ok(self.order[rank])
2980        }
2981
2982        fn code_ranks(&self) -> Option<&[u32]> {
2983            self.ranked
2984                .get_or_init(|| {
2985                    let mut ranks = vec![0; self.order.len()];
2986                    for (rank, &code) in self.order.iter().enumerate() {
2987                        ranks[code as usize] = rank as u32;
2988                    }
2989                    Some(ranks)
2990                })
2991                .as_deref()
2992        }
2993    }
2994
2995    /// The same comparison over a dictionary whose values came out of a file with their order, so
2996    /// the literal is resolved to a code by search rather than compared against every value.
2997    #[test]
2998    fn a_comparison_against_a_sorted_dictionary_answers_what_the_oracle_answers() {
2999        // Distinct, which is what a source promises by answering with an order at all, and which
3000        // a global dictionary is by construction.
3001        let (column, _) = filed(&["", "one", "two", "four", "three"], vec![0, 1, 3, 2, 0, 4, 1, 0]);
3002        same_as_the_oracle(&column);
3003    }
3004
3005    /// A dictionary column over values that arrived from a file with their sorted order, handed
3006    /// back with the dictionary so a caller can check what it is holding a rank against.
3007    fn filed(words: &[&str], codes: Vec<u32>) -> (Vector, Arc<Vector>) {
3008        let values: Vec<Vec<u8>> = words.iter().map(|text| text.as_bytes().to_vec()).collect();
3009        let mut order = (0..values.len() as u32).collect::<Vec<_>>();
3010        order.sort_by(|&left, &right| values[left as usize].cmp(&values[right as usize]));
3011        let dictionary = Arc::new(
3012            Vector::external_text(
3013                LogicalType::Varchar,
3014                Arc::new(Filed { values, order, ranked: std::sync::OnceLock::new() }),
3015            )
3016            .expect("a filed vector"),
3017        );
3018        let column =
3019            Vector::stable_dictionary(codes, Arc::clone(&dictionary)).expect("codes are in range");
3020        (column, dictionary)
3021    }
3022
3023    /// The top N's path. A bound whose rank is already known is compared without a search, and what
3024    /// it keeps is what comparing against the same value the long way round keeps.
3025    #[test]
3026    fn a_comparison_against_a_known_rank_keeps_what_a_search_for_it_keeps() {
3027        let (column, dictionary) =
3028            filed(&["", "one", "two", "four", "three"], vec![0, 1, 3, 2, 0, 4, 1, 0]);
3029        let rows = column.len();
3030        for row in 0..rows {
3031            let (held, rank) = rank_at(&column, row).expect("a row of a ranked dictionary");
3032            assert!(Arc::ptr_eq(&held, &dictionary), "the dictionary it came from");
3033            let value = column.try_value_at(row).expect("a value");
3034            for op in [
3035                Comparison::Less,
3036                Comparison::LessOrEqual,
3037                Comparison::Greater,
3038                Comparison::GreaterOrEqual,
3039            ] {
3040                let against = Vector::constant(LogicalType::Varchar, value.clone(), rows);
3041                let flags = compare(op, &column, &against).expect("the search path answers");
3042                let wanted = crate::select::selection(&flags, rows);
3043                let got = select_against_rank(op, &column, &dictionary, rank, rows)
3044                    .expect("the rank path answers");
3045                assert_eq!(got.indices(), wanted.indices(), "row {row} under {op:?}");
3046            }
3047        }
3048    }
3049
3050    /// A rank is a position in one dictionary and means nothing in another, so a rank offered
3051    /// against the wrong one is declined rather than answered out of the wrong order.
3052    #[test]
3053    fn a_rank_offered_against_another_dictionary_is_declined() {
3054        let (column, dictionary) = filed(&["one", "two"], vec![0, 1]);
3055        let (other, _) = filed(&["one", "two"], vec![1, 0]);
3056        assert!(select_against_rank(Comparison::Less, &column, &dictionary, 1, 2).is_some());
3057        assert!(select_against_rank(Comparison::Less, &other, &dictionary, 1, 2).is_none());
3058        let flat = Vector::constant(LogicalType::Varchar, Value::Varchar("one".into()), 2);
3059        assert!(select_against_rank(Comparison::Less, &flat, &dictionary, 1, 2).is_none());
3060        assert!(rank_at(&flat, 0).is_none(), "a column with no dictionary has no ranks");
3061        assert!(rank_within(&other, 0, &dictionary).is_none(), "another dictionary says nothing");
3062        assert!(rank_within(&flat, 0, &dictionary).is_none(), "no dictionary says nothing");
3063    }
3064
3065    /// The top N's reject. Two rows of the same column ordered by their ranks come out in the order
3066    /// their values come out in, which is the whole of what the rank comparison assumes.
3067    #[test]
3068    fn two_ranks_in_one_dictionary_order_their_values() {
3069        let (column, dictionary) =
3070            filed(&["", "one", "two", "four", "three"], vec![0, 1, 3, 2, 0, 4, 1, 0]);
3071        let rows = column.len();
3072        for left in 0..rows {
3073            for right in 0..rows {
3074                let here = rank_within(&column, left, &dictionary).expect("a ranked row");
3075                let there = rank_within(&column, right, &dictionary).expect("a ranked row");
3076                let values = (
3077                    column.try_value_at(left).expect("a value"),
3078                    column.try_value_at(right).expect("a value"),
3079                );
3080                let wanted = order(&values.0, &values.1).expect("two strings compare");
3081                assert_eq!(here.cmp(&there), wanted, "rows {left} and {right}");
3082                let (held, rank) = rank_at(&column, left).expect("a ranked row");
3083                assert!(Arc::ptr_eq(&held, &dictionary), "the dictionary it came from");
3084                assert_eq!(rank, here, "the same rank whichever way it is asked for");
3085            }
3086        }
3087    }
3088
3089    /// Every equality and inequality against a handful of literals, whole and narrowed, checked
3090    /// against the row at a time path. `missing` is in here because a dictionary that does not
3091    /// hold the literal is decided for the whole chunk and that is its own arm of the code.
3092    fn same_as_the_oracle(column: &Vector) {
3093        for literal in ["", "one", "missing", "zzz"] {
3094            for op in [
3095                Comparison::Equal,
3096                Comparison::NotEqual,
3097                Comparison::Less,
3098                Comparison::LessOrEqual,
3099                Comparison::Greater,
3100                Comparison::GreaterOrEqual,
3101            ] {
3102                let value = Value::Varchar(literal.to_owned());
3103                let held = Held::of(&LogicalType::Varchar, &value).expect("text has a column");
3104                let right = Vector::constant(LogicalType::Varchar, value.clone(), column.len());
3105                let wanted = oracle(op, column, &right);
3106                let got = compare_prepared(op, column, &right, Some(&held))
3107                    .expect("the peeled path answers");
3108                assert_eq!(got, wanted, "{literal:?} under {op:?}");
3109                let held = Held::of(&LogicalType::Varchar, &value).expect("text has a column");
3110                let picked = select_prepared(op, column, &right, Some(&held))
3111                    .expect("the peeled path selects");
3112                assert_eq!(
3113                    picked.indices(),
3114                    crate::select::selection(&wanted, column.len()).indices(),
3115                    "{literal:?} under {op:?}, selected"
3116                );
3117                // A fresh memo for the selection, since the one above belongs to that call's node.
3118                let held = Held::of(&LogicalType::Varchar, &value).expect("text has a column");
3119                let kept = Selection::from_predicate(column.len(), |row| row % 3 != 1);
3120                let refined = refine_prepared(op, column, &right, &kept, Some(&held))
3121                    .expect("the peeled path narrows");
3122                let wanted: Vec<u32> = kept
3123                    .indices()
3124                    .iter()
3125                    .copied()
3126                    .filter(|&row| is_true(&wanted.value_at(row as usize)))
3127                    .collect();
3128                assert_eq!(refined.indices(), wanted, "{literal:?} under {op:?}, narrowed");
3129            }
3130        }
3131    }
3132}