Skip to main content

rudb_kernels/
compare.rs

1//! Comparing values, which is where SQL's three-valued logic actually lives.
2//!
3//! Six of the eight comparisons return null when either side is null, and the other two never do.
4//! That is not a detail: `WHERE a = b` drops a row where either is null and `WHERE a IS NOT
5//! DISTINCT FROM b` keeps the row where both are, and the binder produces the second one for `IS
6//! NULL` and for a `USING` join under some rewrites. One enum with the null rule attached to the
7//! variant is what stops that difference from being re-decided in every operator.
8//!
9//! The comparison enum here is this crate's own rather than `rudb_plan`'s, because the plan sits
10//! nine ranks above the kernels and a kernel that imports a plan type is a kernel that cannot be
11//! called from anywhere else. The executor maps one to the other, which is four lines it writes
12//! once.
13//!
14//! Float comparison is DuckDB's rather than IEEE's. Two NaNs are equal, NaN sorts above every
15//! number, and negative zero equals zero. IEEE says the first is false and that a NaN comparison is
16//! unordered, which would make `GROUP BY` over a column with a NaN in it produce a group nothing
17//! can ever find again and make a sort's result depend on the order the rows arrived in.
18//!
19//! # How the vectorized path is put together
20//!
21//! `spec/engine/03-data-plane.md` opens with this file as the example of what layer one is for.
22//! What it used to be was a loop from zero to length calling `value_at` on both sides, comparing
23//! two owned `Value`s and pushing into a `Vec<Value>` that a second pass then walked to pack into a
24//! vector. On a varchar column that is a heap allocation and a memcpy per row per side, plus a
25//! match on the operator inside the loop that the compiler has no way to hoist.
26//!
27//! What it is now is three decisions taken once per vector and then a loop that does one thing.
28//!
29//! The first decision is the form pair. Flat against flat, flat against constant and dictionary
30//! against constant each get a hand written path, because those three are what a filter on a scan
31//! actually produces. Constant on the left is the same code with the comparison turned around,
32//! which [`Comparison::swapped`] does, so there is one loop rather than two. Everything else falls
33//! through to the row at a time path, which is still here, is still correct, and now increments a
34//! counter in [`crate::fallback`] on the way past so that a combination worth specializing shows up
35//! as a number rather than as an opinion.
36//!
37//! The second decision is the physical type, which a macro turns into one loop per layout. Fifteen
38//! layouts by three form pairs by eight operators written out by hand is how a wrong answer gets
39//! in, and it is also four thousand lines nobody reads.
40//!
41//! The third decision is the operator, hoisted out of the loop once. The eight operators
42//! become eight monomorphized loops over the same ordering, each with a comparison against a
43//! constant `Ordering` in it, which is what makes the body a compare and a store.
44//!
45//! Validity gets its three cases used rather than collapsed. Two all valid sides skip the mask
46//! entirely and produce an all valid result. Either side all invalid, on one of the six ordinary
47//! comparisons, is every answer null without reading the data at all, which is a real case because
48//! it is what a constant `NULL` in a predicate is.
49//!
50//! A conjunct that is not the first one does not need every row. [`refine`] is the same three
51//! decisions with the output position mapped through the selection the conjuncts before it left, so
52//! every loop in this file serves the threaded path without being written twice. The mapping is a
53//! generic parameter rather than a function in a field, because an index mapping the compiler cannot
54//! see through is an indirect call in a loop that is otherwise three instructions.
55//!
56//! Strings resolve from the four byte prefix in the view. Two views whose prefixes differ are in
57//! that order, which holds because the payload past the end of a short string is zero and zero is
58//! the least byte, so prefix order is byte order whenever the prefixes are not equal. On `hits` the
59//! columns that carry the file are `URL` and `Referer`, and a filter on either of them is now a
60//! four byte compare on almost every row instead of a `String` being built to be thrown away.
61//!
62//! # What is still slow here
63//!
64//! The index into each side goes through a closure so that the same macro serves flat, constant and
65//! dictionary, which means the bounds check on each access survives. That is a known cost and it is
66//! next to nothing beside the allocation it replaced, but it is the reason this file will not hit
67//! the one nanosecond per row target on its own. The way out is a slice narrowed to the vector
68//! length on the identity path, and that wants the benchmark suite to exist first so that the
69//! change is a number rather than a belief.
70
71use std::borrow::Cow;
72use std::cmp::Ordering;
73
74use rudb_common::{Error, LogicalType, Result, Value, interval_micros};
75use rudb_vector::{
76    Coded, Data, Form, Packed, Selection, StringColumn, StringView, Validity, Vector,
77};
78
79use crate::fallback::{self, Kernel};
80use crate::logic::is_true;
81use crate::number::{approximate, integral};
82use crate::peel::Found;
83use crate::prepare::Held;
84use crate::shape::{first, identity, nulls_of, single};
85
86/// Which comparison.
87#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
88pub enum Comparison {
89    /// `=`, null if either side is null.
90    Equal,
91    /// `<>`, null if either side is null.
92    NotEqual,
93    /// `<`, null if either side is null.
94    Less,
95    /// `<=`, null if either side is null.
96    LessOrEqual,
97    /// `>`, null if either side is null.
98    Greater,
99    /// `>=`, null if either side is null.
100    GreaterOrEqual,
101    /// `IS DISTINCT FROM`, which is total and never null.
102    DistinctFrom,
103    /// `IS NOT DISTINCT FROM`, which is total and never null.
104    NotDistinctFrom,
105}
106
107impl Comparison {
108    /// Whether this comparison treats null as a value rather than as an absence.
109    #[must_use]
110    pub fn is_total(self) -> bool {
111        matches!(self, Self::DistinctFrom | Self::NotDistinctFrom)
112    }
113
114    /// The comparison that means the same thing with the two sides exchanged.
115    ///
116    /// This is what halves the number of specialized loops. A constant on the left against a
117    /// column on the right is the column against the constant with the inequality turned around,
118    /// and writing it that way means the column against constant loop is written once and tested
119    /// once rather than twice with a chance of the second one being subtly wrong.
120    #[must_use]
121    pub fn swapped(self) -> Self {
122        match self {
123            Self::Less => Self::Greater,
124            Self::LessOrEqual => Self::GreaterOrEqual,
125            Self::Greater => Self::Less,
126            Self::GreaterOrEqual => Self::LessOrEqual,
127            same => same,
128        }
129    }
130
131    /// Whether this comparison is true of two sides that sit in this order.
132    ///
133    /// For the six ordinary comparisons only. The two total ones read a null as a value and an
134    /// `Ordering` has no way to say which side was null, so there is nothing sensible to return for
135    /// them and they answer false rather than pretending.
136    #[must_use]
137    fn holds(self, order: Ordering) -> bool {
138        match self {
139            Self::Equal => order == Ordering::Equal,
140            Self::NotEqual => order != Ordering::Equal,
141            Self::Less => order == Ordering::Less,
142            Self::LessOrEqual => order != Ordering::Greater,
143            Self::Greater => order == Ordering::Greater,
144            Self::GreaterOrEqual => order != Ordering::Less,
145            Self::DistinctFrom | Self::NotDistinctFrom => false,
146        }
147    }
148}
149
150/// Compares two vectors of the same length, producing a `BOOLEAN` vector.
151///
152/// # Errors
153///
154/// If the two sides are not the same length, or if the two types cannot be compared.
155pub fn compare(op: Comparison, left: &Vector, right: &Vector) -> Result<Vector> {
156    compare_prepared(op, left, right, None)
157}
158
159/// [`compare`], with the constant side already turned into the column the loops read it through.
160///
161/// The same body and the same answer. A caller that built the plan knows which side is a literal
162/// and can hand a [`Held`] built once for the query, which saves the allocations that building it
163/// per chunk costs. A caller that has no plan in front of it passes `None` and nothing changes.
164///
165/// # Errors
166///
167/// The same ones [`compare`] gives.
168pub fn compare_prepared(
169    op: Comparison,
170    left: &Vector,
171    right: &Vector,
172    held: Option<&Held>,
173) -> Result<Vector> {
174    if left.len() != right.len() {
175        return Err(Error::internal(format!(
176            "a comparison of a {} row vector with a {} row one",
177            left.len(),
178            right.len()
179        )));
180    }
181    let len = left.len();
182    if left.form() == Form::Constant && right.form() == Form::Constant && len > 0 {
183        let single = compare_values(op, &left.try_value_at(0)?, &right.try_value_at(0)?)?;
184        return Ok(Vector::constant(LogicalType::Boolean, single, len));
185    }
186
187    let (left_valid, right_valid) = (nulls_of(left), nulls_of(right));
188    // Either side entirely null, on one of the six ordinary comparisons, is every answer null and
189    // the data is never read. This is not a corner case: a `NULL` literal in a predicate is a
190    // constant vector whose validity is exactly this, and so is a column the scan knows is empty.
191    if !op.is_total()
192        && (left_valid == Validity::AllInvalid || right_valid == Validity::AllInvalid)
193        && len > 0
194    {
195        return boolean(vec![false; len], Validity::AllInvalid, len);
196    }
197
198    if let Some(answers) = external_text_literal(op, left, right, len, identity, held)? {
199        let validity = left_valid.and(&right_valid, len);
200        return boolean(blank_the_nulls(answers, &validity), validity, len);
201    }
202    if let Some(answers) =
203        specialized(op, left, right, &left_valid, &right_valid, len, identity, held)
204    {
205        let validity =
206            if op.is_total() { Validity::AllValid } else { left_valid.and(&right_valid, len) };
207        return boolean(blank_the_nulls(answers, &validity), validity, len);
208    }
209
210    fallback::record(Kernel::Compare, left.form(), right.form());
211    let mut values = Vec::with_capacity(len);
212    // row at a time: the path recorded on the line above, which exists to be correct for a pair of
213    // forms no specialization covers and counts itself so that pair shows up in the report.
214    for index in 0..len {
215        values.push(compare_values(op, &left.try_value_at(index)?, &right.try_value_at(index)?)?);
216    }
217    Vector::from_values(LogicalType::Boolean, &values)
218}
219
220/// The rows of `kept` the comparison also keeps.
221///
222/// This is [`compare`] for a conjunct that is not the first one. A filter with four conjuncts
223/// evaluated the obvious way runs all four over every row, so on TPC-H Q6, where each conjunct
224/// passes about a fifth of the rows and the four together pass about two percent, the last conjunct
225/// does fifty times the work it needs to. Handing it the rows the earlier ones kept is the whole
226/// difference, and it is a difference that grows with the number of conjuncts rather than washing
227/// out.
228///
229/// The answer is the rows of `kept`, in the order `kept` has them, for which the comparison is true.
230/// Null is not true, so a row whose either side is null is dropped on the six ordinary comparisons,
231/// which is the same rule [`crate::select::selection`] applies to a flag vector and the reason both
232/// of them are a kernel rather than a line at the call site.
233///
234/// # Errors
235///
236/// If the two sides are not the same length, or if a position in `kept` is past the end of them.
237pub fn refine(
238    op: Comparison,
239    left: &Vector,
240    right: &Vector,
241    kept: &Selection,
242) -> Result<Selection> {
243    refine_prepared(op, left, right, kept, None)
244}
245
246/// [`refine`], with the constant side already built, for the reason [`compare_prepared`] gives.
247///
248/// This is the one that gains the most from it. A conjunct after the first reads the rows the ones
249/// before it kept, so the loop can be eleven rows long while the setup is the same size it would be
250/// for a full chunk.
251///
252/// # Errors
253///
254/// The same ones [`refine`] gives.
255pub fn refine_prepared(
256    op: Comparison,
257    left: &Vector,
258    right: &Vector,
259    kept: &Selection,
260    held: Option<&Held>,
261) -> Result<Selection> {
262    if left.len() != right.len() {
263        return Err(Error::internal(format!(
264            "a comparison of a {} row vector with a {} row one",
265            left.len(),
266            right.len()
267        )));
268    }
269    let len = left.len();
270    // One vectorized pass over a run of `u32` before any of the loops below index with them, which
271    // is what turns a caller's mistake into this message rather than into a panic from inside a
272    // macro generated loop eight frames down.
273    if kept.indices().iter().any(|&row| row as usize >= len) {
274        return Err(Error::internal(format!("a selection past the end of a {len} row vector")));
275    }
276    if kept.is_empty() {
277        return Ok(Selection::empty());
278    }
279    if left.form() == Form::Constant && right.form() == Form::Constant {
280        let single = compare_values(op, &left.try_value_at(0)?, &right.try_value_at(0)?)?;
281        return Ok(if is_true(&single) { kept.clone() } else { Selection::empty() });
282    }
283
284    let (left_valid, right_valid) = (nulls_of(left), nulls_of(right));
285    if !op.is_total() && (left_valid == Validity::AllInvalid || right_valid == Validity::AllInvalid)
286    {
287        return Ok(Selection::empty());
288    }
289
290    let rows = kept.indices();
291    let map = |slot: usize| rows[slot] as usize;
292    if let Some(answers) = external_text_literal(op, left, right, kept.len(), map, held)? {
293        return Ok(narrowed(&answers, rows, |slot| {
294            let row = rows[slot] as usize;
295            left_valid.is_valid(row) && right_valid.is_valid(row)
296        }));
297    }
298    if let Some(answers) =
299        specialized(op, left, right, &left_valid, &right_valid, kept.len(), map, held)
300    {
301        // A total comparison has the nulls in the answer already, and two all valid sides have no
302        // null to drop, so both of those get the loop with nothing in it but the flag.
303        if op.is_total() || (left_valid == Validity::AllValid && right_valid == Validity::AllValid)
304        {
305            return Ok(narrowed(&answers, rows, |_| true));
306        }
307        // A bit at a time rather than a word at a time, which is the one place this path gives up
308        // something `compare` has. The rows are scattered by construction, so the two mask reads for
309        // one row are in different words as often as not and a word oriented loop would reread them.
310        return Ok(narrowed(&answers, rows, |slot| {
311            let row = rows[slot] as usize;
312            left_valid.is_valid(row) && right_valid.is_valid(row)
313        }));
314    }
315
316    fallback::record(Kernel::Compare, left.form(), right.form());
317    let mut out = Vec::with_capacity(kept.len());
318    // row at a time: the path recorded on the line above, for a pair of forms no specialization
319    // covers, reading only the rows the conjuncts before this one kept.
320    for &row in rows {
321        let index = row as usize;
322        if is_true(&compare_values(op, &left.try_value_at(index)?, &right.try_value_at(index)?)?) {
323            out.push(row);
324        }
325    }
326    Ok(Selection::from_indices(out))
327}
328
329/// One of the six ordinary comparisons between storage-backed text and a literal, without
330/// constructing row values.
331///
332/// Where the comparison is an equality, the column is a dictionary that shares its values and the
333/// caller brought the literal it was built with, this decides once per distinct value instead of
334/// once per row. See the `peel` module. Everything else reads the column a row at a time, which is
335/// still better than the general path because it never builds a value.
336///
337/// An ordering comparison gets none of that and is here anyway, because what the general path costs
338/// on a text column is not the comparison. It is `try_value_at`, which allocates a `String` a row so
339/// that `compare_values` has a `Value` to look at, and then drops it. Reading the bytes where they
340/// lie and comparing those is the same answer with neither the allocation nor the dispatch, and the
341/// caller this matters most to is the top N: it asks every chunk whether any row can still beat the
342/// worst candidate it holds, and the constant it asks about changes as it goes, so nothing is memoized
343/// and the row loop is the whole of it.
344fn external_text_literal<M>(
345    op: Comparison,
346    left: &Vector,
347    right: &Vector,
348    len: usize,
349    map: M,
350    held: Option<&Held>,
351) -> Result<Option<Vec<bool>>>
352where
353    M: Fn(usize) -> usize + Copy,
354{
355    if op.is_total()
356        || left.logical_type() != &LogicalType::Varchar
357        || right.logical_type() != &LogicalType::Varchar
358    {
359        return Ok(None);
360    }
361    let (column, literal, swapped) = match (left.constant_value(), right.constant_value()) {
362        (None, Some(Value::Varchar(literal))) if left.positions().is_some() => {
363            (left, literal.as_bytes(), false)
364        }
365        (Some(Value::Varchar(literal)), None) if right.positions().is_some() => {
366            (right, literal.as_bytes(), true)
367        }
368        _ => return Ok(None),
369    };
370    // The column is on the left from here on, so an inequality written the other way round is
371    // turned around once rather than once a row.
372    let op = if swapped { op.swapped() } else { op };
373    let same = op == Comparison::Equal;
374    if matches!(op, Comparison::Equal | Comparison::NotEqual) {
375        // The literal has to be the one the memo was filled against, which it is when the caller
376        // took both from the same comparison node. A caller that gets it wrong is slow rather than
377        // wrong, which is the rule the rest of `Held` keeps.
378        if let Some(held) = held.filter(|held| held.text() == Some(literal)) {
379            // A dictionary that came with its sorted order answers this without reading any value
380            // more than the search does, so try that before filling a memo one value at a time.
381            if let Some(found) = held.lookup().find(column, literal) {
382                return Ok(Some(against_code(column, found?, len, map, same)?));
383            }
384            let decide = |dictionary: &Vector, code: usize| -> Result<bool> {
385                let found = if literal.is_empty() {
386                    dictionary.try_bytes_len_at(code)?.is_some_and(|length| length == 0)
387                } else {
388                    dictionary.try_bytes_at(code)?.is_some_and(|bytes| bytes == literal)
389                };
390                Ok(found)
391            };
392            if let Some(answers) = held.peel().answer(column, len, map, decide) {
393                let mut answers = answers?;
394                if !same {
395                    for answer in &mut answers {
396                        *answer = !*answer;
397                    }
398                }
399                return Ok(Some(answers));
400            }
401        }
402        let mut answers = Vec::with_capacity(len);
403        for slot in 0..len {
404            let row = map(slot);
405            // An empty literal is settled by the length alone, which for a dictionary is one load
406            // of two offsets rather than a walk to wherever the value's bytes live.
407            let equal = if literal.is_empty() {
408                column.try_bytes_len_at(row)?.is_some_and(|length| length == 0)
409            } else {
410                column.try_bytes_at(row)?.is_some_and(|bytes| bytes == literal)
411            };
412            answers.push(equal == same);
413        }
414        return Ok(Some(answers));
415    }
416    let mut answers = Vec::with_capacity(len);
417    for slot in 0..len {
418        let row = map(slot);
419        // A null row's bytes are whatever the column left there, and the answer for it is thrown
420        // away by the caller, which blanks every position the validity says is null. Equal is what
421        // is written there because it is the cheapest thing to write and it is never read.
422        let order = match column.try_bytes_at(row)? {
423            Some(bytes) => bytes.cmp(literal),
424            None => Ordering::Equal,
425        };
426        answers.push(op.holds(order));
427    }
428    Ok(Some(answers))
429}
430
431/// Every row's answer once the literal has been resolved to a code, or to nothing.
432///
433/// This is the whole point of storing a dictionary's sorted order. The comparison is a `u32`
434/// against a `u32` and it never touches the payload, so a filter on a text column costs what a
435/// filter on an integer column costs. A literal the dictionary does not hold is decided for the
436/// whole chunk without looking at the codes at all, because a code that is in the dictionary cannot
437/// be the one that is not.
438fn against_code<M>(
439    column: &Vector,
440    found: Found,
441    len: usize,
442    map: M,
443    same: bool,
444) -> Result<Vec<bool>>
445where
446    M: Fn(usize) -> usize,
447{
448    let Found::At(wanted) = found else { return Ok(vec![!same; len]) };
449    let (codes, _) = column
450        .shared_dictionary_parts()
451        .ok_or_else(|| Error::internal("a resolved literal lost the codes it was resolved for"))?;
452    let mut answers = Vec::with_capacity(len);
453    // row at a time: the comparison is the loop. Nothing here reads a value or allocates.
454    for slot in 0..len {
455        let code = *codes
456            .get(map(slot))
457            .ok_or_else(|| Error::internal("a compared row is past the end of its codes"))?;
458        answers.push((code == wanted) == same);
459    }
460    Ok(answers)
461}
462
463/// The positions of `rows` whose answer is true and whose row is live, without a branch per row.
464///
465/// The same shape as the loop in `crate::select` and for the same reason: which rows a filter keeps is
466/// what the data decides rather than what the code does, so the branch is unpredictable by
467/// construction and a mispredict is worth more than the rest of the loop put together. Every slot
468/// writes its row at the current length and only a slot that is kept moves the length on.
469fn narrowed<L: Fn(usize) -> bool>(answers: &[bool], rows: &[u32], live: L) -> Selection {
470    let mut out = vec![0_u32; answers.len()];
471    let mut count = 0;
472    for (slot, &answer) in answers.iter().enumerate() {
473        out[count] = rows[slot];
474        // A single `&` rather than `&&`, because the short circuit would put back the branch.
475        count += usize::from(answer & live(slot));
476    }
477    out.truncate(count);
478    Selection::from_indices(out)
479}
480
481/// A `BOOLEAN` vector from a run of answers and the validity that says which of them count.
482fn boolean(answers: Vec<bool>, validity: Validity, len: usize) -> Result<Vector> {
483    // An empty vector has no null to record, and `Vector::from_values` normalizes the empty mask it
484    // builds to all valid, so saying the same here is what keeps an empty specialized result the
485    // same vector as the oracle's rather than merely the same length.
486    let validity = if len == 0 { Validity::AllValid } else { validity.normalize(len) };
487    Ok(Vector::flat(LogicalType::Boolean, Data::Bool(answers.into()))?.with_validity(validity))
488}
489
490/// A false in every position the validity says is null.
491///
492/// The comparison at a null position read whatever the zero the null was stored as compared to,
493/// which is a defined value and a meaningless one. Writing false there costs one pass over a run
494/// of bytes, only when there are nulls at all, and it buys the property that a specialized result
495/// is the same vector as the row at a time result rather than merely the same answer. A test that
496/// can compare two vectors with `==` is a much better test than one that has to walk them.
497fn blank_the_nulls(mut answers: Vec<bool>, validity: &Validity) -> Vec<bool> {
498    if let Validity::Mask(mask) = validity {
499        for (index, answer) in answers.iter_mut().enumerate() {
500            if !mask.get(index) {
501                *answer = false;
502            }
503        }
504    }
505    answers
506}
507
508/// The answers for a form pair this file has a loop for, or `None` to say it has not.
509///
510/// `map` turns an output position into the row of `left` and `right` it is the answer for, and
511/// `len` is how many output positions there are. [`compare`] passes [`identity`] and the length of
512/// its operands, which is every row. [`refine`] passes the selection it was handed and the size of
513/// it, which is how a conjunct after the first reads only the rows the conjuncts before it kept.
514///
515/// A generic parameter rather than a `fn(usize) -> usize` in a field, for the reason
516/// `spec/engine/03-data-plane.md` records as the first performance lesson of this layer: an index
517/// mapping the compiler cannot see through is an indirect call per row, and one of those in a loop
518/// that is otherwise three instructions is the whole loop.
519#[expect(
520    clippy::too_many_arguments,
521    reason = "two sides, two validities, the operator, the length, the index mapping and the \
522              literal that was built early, all of which the branches below need"
523)]
524fn specialized<M>(
525    op: Comparison,
526    left: &Vector,
527    right: &Vector,
528    left_valid: &Validity,
529    right_valid: &Validity,
530    len: usize,
531    map: M,
532    held: Option<&Held>,
533) -> Option<Vec<bool>>
534where
535    M: Fn(usize) -> usize + Copy,
536{
537    // Across representations is the fallback's job. `INTEGER` against `BIGINT` reaches the same
538    // answer through `numeric_order`, and a specialized loop that assumed the two runs had the same
539    // layout would compare a four byte column against an eight byte one position by position.
540    if left.logical_type() != right.logical_type() {
541        return None;
542    }
543
544    if let (Some(one), Some(other)) = (left.data(), right.data()) {
545        return dispatch(op, len, one, map, other, map, left_valid, right_valid, map);
546    }
547    // A bit packed column against a literal, which is the pair the form was added for. The literal
548    // is turned into a code once and then the loop compares codes, so nothing is unpacked at all,
549    // and a literal outside what the width can hold answers the whole vector without a bit of it
550    // being read. Only the six comparisons that go null on a null side come here, because the other
551    // two want the null rule inside the loop and this loop does not have it.
552    if !op.is_total() {
553        if let (Some(packed), Some(value)) = (left.packed_parts(), right.constant_value()) {
554            let wanted = exact(held, left.logical_type(), value)?;
555            return Some(packed_against(op, &packed, wanted, len, map));
556        }
557        if let (Some(value), Some(packed)) = (left.constant_value(), right.packed_parts()) {
558            let wanted = exact(held, right.logical_type(), value)?;
559            return Some(packed_against(op.swapped(), &packed, wanted, len, map));
560        }
561    }
562    if let (Some(one), Some(value)) = (left.data(), right.constant_value()) {
563        let column = readied(held, left.logical_type(), value)?;
564        let other = column.data()?;
565        return dispatch(op, len, one, map, other, first, left_valid, right_valid, map);
566    }
567    if let (Some(value), Some(other)) = (left.constant_value(), right.data()) {
568        // The same loop with the comparison turned around, rather than a second loop.
569        let column = readied(held, right.logical_type(), value)?;
570        let one = column.data()?;
571        return dispatch(op.swapped(), len, other, map, one, first, right_valid, left_valid, map);
572    }
573    // A compressed column against a literal, tested in the code space the column is already in.
574    // Only equality, because a symbol code says nothing about where its symbol sorts, so an ordering
575    // comparison has to decompress and does. Equality does not: compressing is a function of the
576    // table and the bytes, so two strings have the same codes exactly when they are the same string.
577    if matches!(op, Comparison::Equal | Comparison::NotEqual) {
578        if let (Some(coded), Some(value)) = (left.coded_parts(), right.constant_value()) {
579            let wanted = encoded(&coded, held, left.logical_type(), value)?;
580            return Some(coded_against(op, &coded, &wanted, len, map));
581        }
582        if let (Some(value), Some(coded)) = (left.constant_value(), right.coded_parts()) {
583            let wanted = encoded(&coded, held, right.logical_type(), value)?;
584            return Some(coded_against(op, &coded, &wanted, len, map));
585        }
586    }
587    // A string column against another one or against a literal, with the views read where they are.
588    // It catches the string view form, whose bytes live in an arena the vector shares and so has no
589    // data slice for the branches above to find, and it catches the flat form as well so that the
590    // two cannot be compared by two different loops. The order itself is the one `view_order`
591    // writes down either way.
592    if let (Some((one, one_arena)), Some((other, other_arena))) =
593        (left.text_parts(), right.text_parts())
594    {
595        return Some(sweep(
596            op,
597            len,
598            |index| view_order(one.get(map(index)), one_arena, other.get(map(index)), other_arena),
599            left_valid,
600            right_valid,
601            map,
602        ));
603    }
604    // A string column against a literal. The literal becomes one view before the loop starts, so
605    // every row is a four byte prefix against the same four bytes and the payload is only read for
606    // the rows the prefix could not settle.
607    if let (Some((one, one_arena)), Some(value)) = (left.text_parts(), right.constant_value()) {
608        let column = readied(held, left.logical_type(), value)?;
609        let (other, other_arena) = column.text_parts()?;
610        let wanted = other.first();
611        return Some(sweep(
612            op,
613            len,
614            |index| view_order(one.get(map(index)), one_arena, wanted, other_arena),
615            left_valid,
616            right_valid,
617            map,
618        ));
619    }
620    if let (Some(value), Some((other, other_arena))) = (left.constant_value(), right.text_parts()) {
621        // The same loop with the comparison turned around, rather than a second loop.
622        let column = readied(held, right.logical_type(), value)?;
623        let (one, one_arena) = column.text_parts()?;
624        let wanted = one.first();
625        return Some(sweep(
626            op.swapped(),
627            len,
628            |index| view_order(other.get(map(index)), other_arena, wanted, one_arena),
629            right_valid,
630            left_valid,
631            map,
632        ));
633    }
634    if let (Some((codes, values)), Some(value)) = (left.positions(), right.constant_value()) {
635        let one = values.data()?;
636        let column = readied(held, left.logical_type(), value)?;
637        let other = column.data()?;
638        let at = |index: usize| codes[map(index)] as usize;
639        return dispatch(op, len, one, at, other, first, left_valid, right_valid, map);
640    }
641    if let (Some(value), Some((codes, values))) = (left.constant_value(), right.positions()) {
642        let other = values.data()?;
643        let column = readied(held, right.logical_type(), value)?;
644        let one = column.data()?;
645        let at = |index: usize| codes[map(index)] as usize;
646        return dispatch(op.swapped(), len, other, at, one, first, right_valid, left_valid, map);
647    }
648    // A dictionary against a flat column. This pair had no loop until the kernel table put a number
649    // on what that cost, which on `server3` was 83 nanoseconds a row against 1.2 for the dictionary
650    // against constant pair beside it, on the same data and the same operator. It is not a rare
651    // shape either: it is what a filtered column compared against an unfiltered one is, which is
652    // every conjunct after the first.
653    if let (Some((codes, values)), Some(other)) = (left.positions(), right.data()) {
654        let one = values.data()?;
655        let at = |index: usize| codes[map(index)] as usize;
656        return dispatch(op, len, one, at, other, map, left_valid, right_valid, map);
657    }
658    if let (Some(one), Some((codes, values))) = (left.data(), right.positions()) {
659        let other = values.data()?;
660        let at = |index: usize| codes[map(index)] as usize;
661        return dispatch(op.swapped(), len, other, at, one, map, right_valid, left_valid, map);
662    }
663    None
664}
665
666/// A literal as the whole number it is, and `None` for one that is not a whole number.
667///
668/// It goes through [`readied`] rather than reading the [`Value`] apart, so that a literal written
669/// as `900` against a `SMALLINT` column is narrowed by the same cast path every other comparison
670/// narrows it with. Reading the value apart here would be a second cast path with its own rounding
671/// and its own overflow rule, which is how two comparisons of the same literal end up disagreeing.
672fn exact(held: Option<&Held>, ty: &LogicalType, value: &Value) -> Option<i128> {
673    let column = readied(held, ty, value)?;
674    let data = column.data()?;
675    data.signed_at(0).or_else(|| data.unsigned_at(0).and_then(|value| i128::try_from(value).ok()))
676}
677
678/// A literal in the code space a compressed column is in, and `None` for one with no bytes.
679///
680/// It goes through [`readied`] for the reason [`exact`] does: the literal is narrowed to the column
681/// type by the same path every other comparison narrows it with, rather than by a second reading of
682/// the [`Value`] that could disagree with the first.
683fn encoded(
684    coded: &Coded<'_>,
685    held: Option<&Held>,
686    ty: &LogicalType,
687    value: &Value,
688) -> Option<Vec<u8>> {
689    let column = readied(held, ty, value)?;
690    let (views, arena) = column.text_parts()?;
691    Some(coded.encode(views.first()?.bytes_in(arena)?))
692}
693
694/// A compressed column against a literal, tested without decompressing a row of it.
695///
696/// The comparison is a byte slice against a byte slice, which is what it would have been on the
697/// strings, over half as many bytes and with no decompression before it. A row whose codes are a
698/// different length is settled by the length alone, which on a column of URLs is most of them.
699fn coded_against<M>(
700    op: Comparison,
701    coded: &Coded<'_>,
702    wanted: &[u8],
703    len: usize,
704    map: M,
705) -> Vec<bool>
706where
707    M: Fn(usize) -> usize + Copy,
708{
709    let same = op == Comparison::Equal;
710    let mut answers = Vec::with_capacity(len);
711    for row in 0..len {
712        answers.push((coded.row(map(row)) == Some(wanted)) == same);
713    }
714    answers
715}
716
717/// A bit packed column against a literal, compared in the code space the column is already in.
718///
719/// The translation is one subtraction done once. After it the loop is a shift, a mask and a compare
720/// of two `u64`, which is what the flat loop would have been doing anyway minus the unpacking, so
721/// the form costs nothing on the operation a filter spends most of its time in.
722fn packed_against<M>(
723    op: Comparison,
724    packed: &Packed<'_>,
725    wanted: i128,
726    len: usize,
727    map: M,
728) -> Vec<bool>
729where
730    M: Fn(usize) -> usize + Copy,
731{
732    let Some(code) = packed.code_of(wanted) else {
733        // The literal is outside the range the width can hold, so every row answers the same way
734        // and the answer is arithmetic on two numbers rather than a pass over the column.
735        let above = wanted > packed.ceiling();
736        let same = match op {
737            Comparison::Equal | Comparison::NotDistinctFrom => false,
738            Comparison::NotEqual | Comparison::DistinctFrom => true,
739            Comparison::Less | Comparison::LessOrEqual => above,
740            Comparison::Greater | Comparison::GreaterOrEqual => !above,
741        };
742        return vec![same; len];
743    };
744    // The operator is decided before the loop rather than inside it, which is the same reason the
745    // generated loops take it as a function rather than matching per row.
746    let test: fn(u64, u64) -> bool = match op {
747        Comparison::Equal | Comparison::NotDistinctFrom => |found, want| found == want,
748        Comparison::NotEqual | Comparison::DistinctFrom => |found, want| found != want,
749        Comparison::Less => |found, want| found < want,
750        Comparison::LessOrEqual => |found, want| found <= want,
751        Comparison::Greater => |found, want| found > want,
752        Comparison::GreaterOrEqual => |found, want| found >= want,
753    };
754    let mut answers = Vec::with_capacity(len);
755    for row in 0..len {
756        answers.push(test(packed.code(map(row)), code));
757    }
758    answers
759}
760
761/// One loop per physical layout, generated rather than written out.
762///
763/// The two index closures are what let the same body serve flat against flat, a column against a
764/// constant and a dictionary against a constant. `identity` on both sides is the first, `first` on
765/// the right is the second, and the codes on the left are the third.
766#[expect(
767    clippy::too_many_arguments,
768    reason = "two sides with an index each, the operator, the length and two validities, all of \
769              which the loop needs and none of which is worth a struct that exists for one call"
770)]
771fn dispatch<L, R, V>(
772    op: Comparison,
773    len: usize,
774    left: &Data,
775    at_left: L,
776    right: &Data,
777    at_right: R,
778    left_valid: &Validity,
779    right_valid: &Validity,
780    at_valid: V,
781) -> Option<Vec<bool>>
782where
783    L: Fn(usize) -> usize,
784    R: Fn(usize) -> usize,
785    V: Fn(usize) -> usize,
786{
787    macro_rules! layouts {
788        ($(($variant:ident, $native:ty, $zero:expr)),+ $(,)?) => {
789            match (left, right) {
790                $(
791                    (Data::$variant(one), Data::$variant(other)) => Some(sweep(
792                        op,
793                        len,
794                        |index| one[at_left(index)].cmp(&other[at_right(index)]),
795                        left_valid,
796                        right_valid,
797                        &at_valid,
798                    )),
799                )+
800                // Floats have their own order, which is DuckDB's rather than IEEE's, and the
801                // widening on a `f32` is free because the comparison is against another `f32`.
802                (Data::Float32(one), Data::Float32(other)) => Some(sweep(
803                    op,
804                    len,
805                    |index| {
806                        float_order(
807                            f64::from(one[at_left(index)]),
808                            f64::from(other[at_right(index)]),
809                        )
810                    },
811                    left_valid,
812                    right_valid,
813                    &at_valid,
814                )),
815                (Data::Float64(one), Data::Float64(other)) => Some(sweep(
816                    op,
817                    len,
818                    |index| float_order(one[at_left(index)], other[at_right(index)]),
819                    left_valid,
820                    right_valid,
821                    &at_valid,
822                )),
823                // An interval is three counts and the order is over the one length they add up to,
824                // so this is not the derived order of the triple and cannot be generated above.
825                (Data::Interval(one), Data::Interval(other)) => Some(sweep(
826                    op,
827                    len,
828                    |index| {
829                        let (months, days, micros) = one[at_left(index)];
830                        let (bm, bd, bu) = other[at_right(index)];
831                        interval_micros(months, days, micros).cmp(&interval_micros(bm, bd, bu))
832                    },
833                    left_valid,
834                    right_valid,
835                    &at_valid,
836                )),
837                (Data::Varlen(one), Data::Varlen(other)) => Some(sweep(
838                    op,
839                    len,
840                    |index| string_order(one, at_left(index), other, at_right(index)),
841                    left_valid,
842                    right_valid,
843                    &at_valid,
844                )),
845                _ => None,
846            }
847        };
848    }
849    rudb_vector::for_each_layout!(ordered, layouts)
850}
851
852/// The one row column for a constant, either the one that was built early or one built here.
853///
854/// Borrowed when a caller handed one over for this side and this value, owned when it did not, and
855/// the loop below cannot tell the two apart. `None` is a type with no column layout, which is what
856/// sends the whole comparison to the row at a time path.
857fn readied<'a>(held: Option<&'a Held>, ty: &LogicalType, value: &Value) -> Option<Cow<'a, Vector>> {
858    match held {
859        Some(held) if held.matches(ty, value) => Some(Cow::Borrowed(held.single())),
860        _ => Some(Cow::Owned(single(ty, value)?)),
861    }
862}
863
864/// Two strings in byte order, resolved from the four byte prefix where it can be.
865///
866/// The lemma this rests on is that prefix order is byte order whenever the two prefixes differ. A
867/// view pads a string shorter than four bytes with zeros, zero is the least byte, and byte order
868/// says a string is less than any string that extends it, so padding compares the same way the
869/// missing bytes would have. When the prefixes are equal the payload settles it, which for an
870/// inline string is the same sixteen bytes already loaded and for a long one is a block read.
871fn string_order(
872    left: &StringColumn,
873    at_left: usize,
874    right: &StringColumn,
875    at_right: usize,
876) -> Ordering {
877    view_order(left.views().get(at_left), left.arena(), right.views().get(at_right), right.arena())
878}
879
880/// The same comparison written against a view and the arena behind it rather than against a column.
881///
882/// Both forms that hold strings come through here, so a flat varchar column and a string view column
883/// order a pair of rows the same way and there is no second copy of the prefix rule to drift from
884/// this one.
885fn view_order(
886    one: Option<&StringView>,
887    one_arena: &[u8],
888    other: Option<&StringView>,
889    other_arena: &[u8],
890) -> Ordering {
891    let (Some(one), Some(other)) = (one, other) else {
892        return Ordering::Equal;
893    };
894    let (prefix, against) = (one.prefix(), other.prefix());
895    if prefix != against {
896        return prefix.cmp(&against);
897    }
898    // Bytes rather than `StringColumn::get`, which validates UTF-8. Everything in a column was
899    // pushed from a `&str` so the validation cannot fail, and on a URL column, where every row
900    // shares the `http` prefix and the payload therefore decides every comparison, it was the
901    // larger half of the per row cost.
902    let bytes = one.bytes_in(one_arena).unwrap_or_default();
903    let against_bytes = other.bytes_in(other_arena).unwrap_or_default();
904    bytes.cmp(against_bytes)
905}
906
907/// The answers for one ordering, with the operator decided once rather than once per row.
908///
909/// This is where the match on the operator gets hoisted. Each arm calls a generic `fill` with a
910/// different predicate, so the compiler produces eight loops whose bodies are an ordering against a
911/// constant, rather than one loop with a branch table in it.
912fn sweep<O, V>(
913    op: Comparison,
914    len: usize,
915    order_at: O,
916    left_valid: &Validity,
917    right_valid: &Validity,
918    at_valid: V,
919) -> Vec<bool>
920where
921    O: Fn(usize) -> Ordering,
922    V: Fn(usize) -> usize,
923{
924    let mut answers = vec![false; len];
925    match op {
926        Comparison::Equal => fill(&mut answers, order_at, |o| o == Ordering::Equal),
927        Comparison::NotEqual => fill(&mut answers, order_at, |o| o != Ordering::Equal),
928        Comparison::Less => fill(&mut answers, order_at, |o| o == Ordering::Less),
929        Comparison::LessOrEqual => fill(&mut answers, order_at, |o| o != Ordering::Greater),
930        Comparison::Greater => fill(&mut answers, order_at, |o| o == Ordering::Greater),
931        Comparison::GreaterOrEqual => fill(&mut answers, order_at, |o| o != Ordering::Less),
932        Comparison::DistinctFrom => {
933            total(&mut answers, order_at, left_valid, right_valid, at_valid);
934            for answer in &mut answers {
935                *answer = !*answer;
936            }
937        }
938        Comparison::NotDistinctFrom => {
939            total(&mut answers, order_at, left_valid, right_valid, at_valid);
940        }
941    }
942    answers
943}
944
945/// One loop, one predicate, no branch on the operator.
946#[inline]
947fn fill<O, H>(answers: &mut [bool], order_at: O, held: H)
948where
949    O: Fn(usize) -> Ordering,
950    H: Fn(Ordering) -> bool,
951{
952    for (index, answer) in answers.iter_mut().enumerate() {
953        *answer = held(order_at(index));
954    }
955}
956
957/// `IS NOT DISTINCT FROM`, which reads validity as data rather than as an absence.
958///
959/// Two nulls are the same value here and a null against anything else is not, which is the whole
960/// difference between this and `=`. The all valid case is checked once so that the common shape,
961/// which is a total comparison inside a join on columns that happen not to be nullable, does not
962/// pay for two validity lookups per row.
963fn total<O, V>(
964    answers: &mut [bool],
965    order_at: O,
966    left_valid: &Validity,
967    right_valid: &Validity,
968    at_valid: V,
969) where
970    O: Fn(usize) -> Ordering,
971    V: Fn(usize) -> usize,
972{
973    if *left_valid == Validity::AllValid && *right_valid == Validity::AllValid {
974        fill(answers, order_at, |o| o == Ordering::Equal);
975        return;
976    }
977    for (index, answer) in answers.iter_mut().enumerate() {
978        let row = at_valid(index);
979        *answer = match (left_valid.is_valid(row), right_valid.is_valid(row)) {
980            (true, true) => order_at(index) == Ordering::Equal,
981            (false, false) => true,
982            _ => false,
983        };
984    }
985}
986
987/// Compares two values, producing `TRUE`, `FALSE` or `NULL`.
988///
989/// # Errors
990///
991/// If the two types cannot be compared, which after binding means one of them is a nested type.
992pub fn compare_values(op: Comparison, left: &Value, right: &Value) -> Result<Value> {
993    if op.is_total() {
994        let same = match (left.is_null(), right.is_null()) {
995            (true, true) => true,
996            (true, false) | (false, true) => false,
997            (false, false) => order(left, right)? == Ordering::Equal,
998        };
999        return Ok(Value::Boolean(match op {
1000            Comparison::NotDistinctFrom => same,
1001            _ => !same,
1002        }));
1003    }
1004    if left.is_null() || right.is_null() {
1005        return Ok(Value::Null);
1006    }
1007    let ordering = order(left, right)?;
1008    let held = match op {
1009        Comparison::Equal => ordering == Ordering::Equal,
1010        Comparison::NotEqual => ordering != Ordering::Equal,
1011        Comparison::Less => ordering == Ordering::Less,
1012        Comparison::LessOrEqual => ordering != Ordering::Greater,
1013        Comparison::Greater => ordering == Ordering::Greater,
1014        Comparison::GreaterOrEqual => ordering != Ordering::Less,
1015        Comparison::DistinctFrom | Comparison::NotDistinctFrom => {
1016            return Err(Error::internal("a total comparison reached the ordered path"));
1017        }
1018    };
1019    Ok(Value::Boolean(held))
1020}
1021
1022/// The order of two values, neither of which is null.
1023///
1024/// This is the one place the sort order of a type is written down. `ORDER BY`, `GROUP BY`, a merge
1025/// join and a min or max aggregate all reach it, and a type that ordered differently in two of
1026/// those would produce a query whose answer depends on which operator the optimizer picked.
1027///
1028/// # Errors
1029///
1030/// If either value is null, which is the caller's mistake rather than a comparison, or if the
1031/// types have no order between them.
1032pub fn order(left: &Value, right: &Value) -> Result<Ordering> {
1033    match (left, right) {
1034        (Value::Null, _) | (_, Value::Null) => {
1035            Err(Error::internal("a null reached the ordering path"))
1036        }
1037        (Value::Boolean(a), Value::Boolean(b)) => Ok(a.cmp(b)),
1038        (Value::Varchar(a), Value::Varchar(b)) => Ok(a.as_bytes().cmp(b.as_bytes())),
1039        (Value::Blob(a), Value::Blob(b)) => Ok(a.cmp(b)),
1040        (Value::Date(a), Value::Date(b)) => Ok(a.cmp(b)),
1041        // A zoned value orders with its own kind and by the same rule, since both of them are the
1042        // count of microseconds from a fixed point and the zone is about printing.
1043        (Value::Time(a), Value::Time(b))
1044        | (Value::TimeTz(a), Value::TimeTz(b))
1045        | (Value::Timestamp(a), Value::Timestamp(b))
1046        | (Value::TimestampTz(a), Value::TimestampTz(b)) => Ok(a.cmp(b)),
1047        (
1048            Value::Interval { months: am, days: ad, micros: au },
1049            Value::Interval { months: bm, days: bd, micros: bu },
1050        ) => Ok(interval_micros(*am, *ad, *au).cmp(&interval_micros(*bm, *bd, *bu))),
1051        _ => numeric_order(left, right),
1052    }
1053}
1054
1055/// The order of two numbers, which is the case that has to work across representations.
1056fn numeric_order(left: &Value, right: &Value) -> Result<Ordering> {
1057    if let (Some(a), Some(b)) = (integral(left), integral(right)) {
1058        return Ok(a.cmp(&b));
1059    }
1060    if let (
1061        Value::Decimal { unscaled: a, scale: sa, .. },
1062        Value::Decimal { unscaled: b, scale: sb, .. },
1063    ) = (left, right)
1064    {
1065        if sa == sb {
1066            return Ok(a.cmp(b));
1067        }
1068    }
1069    match (approximate(left), approximate(right)) {
1070        (Some(a), Some(b)) => Ok(float_order(a, b)),
1071        _ => Err(Error::not_implemented(format!(
1072            "comparing {} with {}",
1073            left.logical_type(),
1074            right.logical_type()
1075        ))),
1076    }
1077}
1078
1079/// DuckDB's float order: NaN is equal to itself and above everything else, and zero has one place.
1080fn float_order(left: f64, right: f64) -> Ordering {
1081    if left == right {
1082        return Ordering::Equal;
1083    }
1084    match (left.is_nan(), right.is_nan()) {
1085        (true, true) => Ordering::Equal,
1086        (true, false) => Ordering::Greater,
1087        (false, true) => Ordering::Less,
1088        (false, false) => left.partial_cmp(&right).unwrap_or(Ordering::Equal),
1089    }
1090}
1091
1092/// The order of two values with nulls in it, for a sort key.
1093///
1094/// A sort has to put nulls somewhere and SQL lets the query say where, so this takes the answer
1095/// rather than deciding it.
1096///
1097/// # Errors
1098///
1099/// If the two types have no order between them.
1100pub fn order_with_nulls(left: &Value, right: &Value, nulls_first: bool) -> Result<Ordering> {
1101    match (left.is_null(), right.is_null()) {
1102        (true, true) => Ok(Ordering::Equal),
1103        (true, false) => Ok(if nulls_first { Ordering::Less } else { Ordering::Greater }),
1104        (false, true) => Ok(if nulls_first { Ordering::Greater } else { Ordering::Less }),
1105        (false, false) => order(left, right),
1106    }
1107}
1108
1109#[cfg(test)]
1110mod tests {
1111    use super::*;
1112
1113    fn compared(op: Comparison, left: Value, right: Value) -> Value {
1114        compare_values(op, &left, &right).expect("these types compare")
1115    }
1116
1117    /// Every comparison, so that a test that sweeps them cannot quietly miss one.
1118    const EVERY: [Comparison; 8] = [
1119        Comparison::Equal,
1120        Comparison::NotEqual,
1121        Comparison::Less,
1122        Comparison::LessOrEqual,
1123        Comparison::Greater,
1124        Comparison::GreaterOrEqual,
1125        Comparison::DistinctFrom,
1126        Comparison::NotDistinctFrom,
1127    ];
1128
1129    /// The row at a time path, kept as the oracle rather than deleted.
1130    ///
1131    /// `spec/engine/03-data-plane.md` is explicit that the slow path becomes the thing the fast
1132    /// path is checked against. This is that, written out here so that a test can call it on a pair
1133    /// of vectors whose forms the fast path does specialize.
1134    fn oracle(op: Comparison, left: &Vector, right: &Vector) -> Vector {
1135        let values: Vec<Value> = (0..left.len())
1136            .map(|index| {
1137                compare_values(op, &left.value_at(index), &right.value_at(index))
1138                    .expect("the oracle is only asked about types that compare")
1139            })
1140            .collect();
1141        Vector::from_values(LogicalType::Boolean, &values).expect("booleans")
1142    }
1143
1144    /// Asserts that the specialized path and the oracle produce the same vector, not merely the
1145    /// same answers. Same vector means the same data, the same validity representation and the
1146    /// same false in every null position, which is a much stronger statement and is free to check.
1147    fn agrees(op: Comparison, left: &Vector, right: &Vector) {
1148        let fast = compare(op, left, right).expect("compares");
1149        let slow = oracle(op, left, right);
1150        assert_eq!(fast, slow, "{op:?} on a {:?} against a {:?}", left.form(), right.form());
1151    }
1152
1153    /// A small deterministic generator, because a property test with no seed is a test that fails
1154    /// on somebody else's machine and passes on yours.
1155    struct Rng(u64);
1156
1157    impl Rng {
1158        fn next(&mut self) -> u64 {
1159            self.0 ^= self.0 << 13;
1160            self.0 ^= self.0 >> 7;
1161            self.0 ^= self.0 << 17;
1162            self.0
1163        }
1164
1165        fn below(&mut self, bound: u64) -> u64 {
1166            self.next() % bound
1167        }
1168    }
1169
1170    #[test]
1171    fn an_ordinary_comparison_is_null_when_either_side_is() {
1172        assert_eq!(compared(Comparison::Equal, Value::Integer(1), Value::Null), Value::Null);
1173        assert_eq!(compared(Comparison::Less, Value::Null, Value::Integer(1)), Value::Null);
1174    }
1175
1176    #[test]
1177    fn a_total_comparison_is_never_null() {
1178        assert_eq!(
1179            compared(Comparison::NotDistinctFrom, Value::Null, Value::Null),
1180            Value::Boolean(true)
1181        );
1182        assert_eq!(
1183            compared(Comparison::NotDistinctFrom, Value::Integer(1), Value::Null),
1184            Value::Boolean(false)
1185        );
1186        assert_eq!(
1187            compared(Comparison::DistinctFrom, Value::Integer(1), Value::Null),
1188            Value::Boolean(true)
1189        );
1190    }
1191
1192    #[test]
1193    fn a_string_compares_by_bytes() {
1194        assert_eq!(
1195            compared(Comparison::Less, Value::Varchar("a".into()), Value::Varchar("b".into())),
1196            Value::Boolean(true)
1197        );
1198        assert_eq!(
1199            compared(Comparison::Less, Value::Varchar("Z".into()), Value::Varchar("a".into())),
1200            Value::Boolean(true)
1201        );
1202    }
1203
1204    /// The reason this crate does not use `f64::partial_cmp` directly. A NaN that compared
1205    /// unordered would make a group by produce a group nothing can find again.
1206    #[test]
1207    fn two_nans_are_one_value_and_they_sort_above_the_numbers() {
1208        assert_eq!(
1209            compared(Comparison::Equal, Value::Double(f64::NAN), Value::Double(f64::NAN)),
1210            Value::Boolean(true)
1211        );
1212        assert_eq!(
1213            compared(Comparison::Greater, Value::Double(f64::NAN), Value::Double(1e300)),
1214            Value::Boolean(true)
1215        );
1216    }
1217
1218    #[test]
1219    fn zero_has_one_value_however_it_is_signed() {
1220        assert_eq!(
1221            compared(Comparison::Equal, Value::Double(0.0), Value::Double(-0.0)),
1222            Value::Boolean(true)
1223        );
1224    }
1225
1226    /// An interval is three counts and two of them that are the same length are one value, at
1227    /// thirty days to a month and twenty four hours to a day, which is what upstream answers. The
1228    /// three counts are still kept apart, because adding a month to a date is not adding thirty
1229    /// days to it, so these pairs are equal and print differently.
1230    #[test]
1231    fn two_intervals_of_the_same_length_are_one_value() {
1232        let day = Value::Interval { months: 0, days: 1, micros: 0 };
1233        let hours = Value::Interval { months: 0, days: 0, micros: 86_400_000_000 };
1234        let month = Value::Interval { months: 1, days: 0, micros: 0 };
1235        let thirty = Value::Interval { months: 0, days: 30, micros: 0 };
1236        let long_day = Value::Interval { months: 0, days: 0, micros: 90_000_000_000 };
1237        assert_eq!(compared(Comparison::Equal, day.clone(), hours), Value::Boolean(true));
1238        assert_eq!(compared(Comparison::Equal, month, thirty), Value::Boolean(true));
1239        assert_eq!(compared(Comparison::Greater, long_day, day), Value::Boolean(true));
1240    }
1241
1242    #[test]
1243    fn a_number_compares_the_same_however_it_is_stored() {
1244        assert_eq!(
1245            compared(Comparison::Equal, Value::Integer(3), Value::BigInt(3)),
1246            Value::Boolean(true)
1247        );
1248        assert_eq!(
1249            compared(Comparison::Less, Value::Integer(3), Value::Double(3.5)),
1250            Value::Boolean(true)
1251        );
1252    }
1253
1254    #[test]
1255    fn nulls_go_where_the_query_asked_for_them() {
1256        assert_eq!(
1257            order_with_nulls(&Value::Null, &Value::Integer(1), true).expect("orders"),
1258            Ordering::Less
1259        );
1260        assert_eq!(
1261            order_with_nulls(&Value::Null, &Value::Integer(1), false).expect("orders"),
1262            Ordering::Greater
1263        );
1264    }
1265
1266    #[test]
1267    fn two_constant_vectors_cost_one_comparison() {
1268        let left = Vector::constant(LogicalType::Integer, Value::Integer(1), 512);
1269        let right = Vector::constant(LogicalType::Integer, Value::Integer(2), 512);
1270        let result = compare(Comparison::Less, &left, &right).expect("compares");
1271        assert_eq!(result.form(), Form::Constant);
1272        assert_eq!(result.value_at(500), Value::Boolean(true));
1273    }
1274
1275    #[test]
1276    fn a_comparison_of_two_vectors_is_one_answer_per_row() {
1277        let left = Vector::from_values(
1278            LogicalType::Integer,
1279            &[Value::Integer(1), Value::Integer(5), Value::Null],
1280        )
1281        .expect("three rows");
1282        let right = Vector::constant(LogicalType::Integer, Value::Integer(3), 3);
1283        let result = compare(Comparison::Greater, &left, &right).expect("compares");
1284        assert_eq!(result.value_at(0), Value::Boolean(false));
1285        assert_eq!(result.value_at(1), Value::Boolean(true));
1286        assert_eq!(result.value_at(2), Value::Null);
1287    }
1288
1289    #[test]
1290    fn two_vectors_of_different_lengths_are_caught() {
1291        let left = Vector::constant(LogicalType::Integer, Value::Integer(1), 4);
1292        let right = Vector::constant(LogicalType::Integer, Value::Integer(1), 5);
1293        let error = compare(Comparison::Equal, &left, &right).expect_err("ragged");
1294        assert!(error.message().contains("4 row vector"), "{error}");
1295    }
1296
1297    #[test]
1298    fn turning_a_comparison_around_is_what_the_other_side_would_have_said() {
1299        for op in EVERY {
1300            let left = Value::Integer(3);
1301            let right = Value::Integer(7);
1302            assert_eq!(
1303                compare_values(op, &left, &right).expect("compares"),
1304                compare_values(op.swapped(), &right, &left).expect("compares"),
1305                "{op:?}"
1306            );
1307        }
1308    }
1309
1310    /// The whole point of the rewrite, stated as a property. Every operator, every physical
1311    /// layout, every form pair the fast path claims, against the row at a time oracle.
1312    #[test]
1313    fn every_specialized_path_agrees_with_the_row_at_a_time_path() {
1314        let mut rng = Rng(0x5eed_1234_9876_4321);
1315        let types: [LogicalType; 11] = [
1316            LogicalType::Boolean,
1317            LogicalType::TinyInt,
1318            LogicalType::SmallInt,
1319            LogicalType::Integer,
1320            LogicalType::BigInt,
1321            LogicalType::HugeInt,
1322            LogicalType::UInteger,
1323            LogicalType::Float,
1324            LogicalType::Double,
1325            LogicalType::Varchar,
1326            LogicalType::Interval,
1327        ];
1328        for ty in &types {
1329            for nulls in [0u64, 1, 3] {
1330                let len = 37;
1331                let make = |rng: &mut Rng| {
1332                    let values: Vec<Value> = (0..len)
1333                        .map(|_| {
1334                            if nulls > 0 && rng.below(nulls + 1) == 0 {
1335                                Value::Null
1336                            } else {
1337                                sample(ty, rng)
1338                            }
1339                        })
1340                        .collect();
1341                    Vector::from_values(ty.clone(), &values).expect("a flat vector")
1342                };
1343                let left = make(&mut rng);
1344                let right = make(&mut rng);
1345                let literal = sample(ty, &mut rng);
1346                let constant = Vector::constant(ty.clone(), literal, len);
1347                let null_constant = Vector::constant(ty.clone(), Value::Null, len);
1348                let codes: Vec<u32> =
1349                    (0..len).map(|_| rng.below(left.len() as u64) as u32).collect();
1350                let dictionary =
1351                    Vector::dictionary(codes, left.clone()).expect("codes are in range");
1352                // Runs over the same values, with the last one cut short so that a run boundary
1353                // does not land on the end of the vector.
1354                let ends: Vec<u32> = (1..=left.len())
1355                    .map(|run| ((run * len) / left.len()).max(run) as u32)
1356                    .collect();
1357                let runs = Vector::runs(ends, left.clone()).expect("one value for each run");
1358
1359                for op in EVERY {
1360                    agrees(op, &left, &right);
1361                    agrees(op, &left, &constant);
1362                    agrees(op, &constant, &left);
1363                    agrees(op, &left, &null_constant);
1364                    agrees(op, &null_constant, &left);
1365                    agrees(op, &dictionary, &constant);
1366                    agrees(op, &constant, &dictionary);
1367                    // The dictionary against a flat column, which reads a null from either side and
1368                    // from the dictionary's values as well, so it is the pair with the most ways to
1369                    // disagree with the oracle and the one that got a loop last.
1370                    agrees(op, &dictionary, &right);
1371                    agrees(op, &right, &dictionary);
1372                    // The same four pairings for run length, which reaches the same loops through
1373                    // the same accessor, so what is being checked is that the positions it works
1374                    // out are the positions the row at a time path reads.
1375                    agrees(op, &runs, &constant);
1376                    agrees(op, &constant, &runs);
1377                    agrees(op, &runs, &right);
1378                    agrees(op, &right, &runs);
1379                }
1380            }
1381        }
1382    }
1383
1384    /// The rows of a selection the row at a time path keeps, which is what [`refine`] has to say.
1385    fn refined(op: Comparison, left: &Vector, right: &Vector, kept: &Selection) -> Selection {
1386        let mut out = Vec::new();
1387        for &row in kept.indices() {
1388            let index = row as usize;
1389            let answer = compare_values(op, &left.value_at(index), &right.value_at(index))
1390                .expect("the oracle is only asked about types that compare");
1391            if is_true(&answer) {
1392                out.push(row);
1393            }
1394        }
1395        Selection::from_indices(out)
1396    }
1397
1398    fn threads(op: Comparison, left: &Vector, right: &Vector, kept: &Selection) {
1399        let fast = refine(op, left, right, kept).expect("compares");
1400        assert_eq!(
1401            fast,
1402            refined(op, left, right, kept),
1403            "{op:?} on a {:?} against a {:?} over {} rows",
1404            left.form(),
1405            right.form(),
1406            kept.len()
1407        );
1408    }
1409
1410    /// Threading a selection through a comparison is the same rows as comparing everything and
1411    /// then keeping the ones that were already kept. Every operator, every form pair that has a
1412    /// loop, at four densities of selection, against the row at a time path.
1413    #[test]
1414    fn a_threaded_comparison_keeps_what_the_row_at_a_time_path_keeps() {
1415        let mut rng = Rng(0x5eed_4321_1234_9876);
1416        let types = [LogicalType::Integer, LogicalType::Double, LogicalType::Varchar];
1417        for ty in &types {
1418            for nulls in [0u64, 1, 3] {
1419                let len = 37;
1420                let make = |rng: &mut Rng| {
1421                    let values: Vec<Value> = (0..len)
1422                        .map(|_| {
1423                            if nulls > 0 && rng.below(nulls + 1) == 0 {
1424                                Value::Null
1425                            } else {
1426                                sample(ty, rng)
1427                            }
1428                        })
1429                        .collect();
1430                    Vector::from_values(ty.clone(), &values).expect("a flat vector")
1431                };
1432                let left = make(&mut rng);
1433                let right = make(&mut rng);
1434                let constant = Vector::constant(ty.clone(), sample(ty, &mut rng), len);
1435                let null_constant = Vector::constant(ty.clone(), Value::Null, len);
1436                let codes: Vec<u32> =
1437                    (0..len).map(|_| rng.below(left.len() as u64) as u32).collect();
1438                let dictionary =
1439                    Vector::dictionary(codes, left.clone()).expect("codes are in range");
1440
1441                // Everything, every third row, a handful including the last one, and nothing,
1442                // which is the state a conjunct chain reaches as soon as one conjunct rejects a
1443                // whole chunk and is the case where the loop below must not read anything at all.
1444                let selections = [
1445                    Selection::identity(len),
1446                    Selection::from_indices((0..len as u32).filter(|row| row % 3 == 0).collect()),
1447                    Selection::from_indices(vec![2, 5, 6, 17, 36]),
1448                    Selection::empty(),
1449                ];
1450                for op in EVERY {
1451                    for kept in &selections {
1452                        threads(op, &left, &right, kept);
1453                        threads(op, &left, &constant, kept);
1454                        threads(op, &constant, &left, kept);
1455                        threads(op, &left, &null_constant, kept);
1456                        threads(op, &null_constant, &left, kept);
1457                        threads(op, &constant, &null_constant, kept);
1458                        threads(op, &dictionary, &constant, kept);
1459                        threads(op, &constant, &dictionary, kept);
1460                        threads(op, &dictionary, &right, kept);
1461                        threads(op, &right, &dictionary, kept);
1462                    }
1463                }
1464            }
1465        }
1466    }
1467
1468    /// Two conjuncts threaded one after the other are the rows both of them keep, which is the
1469    /// property the whole filter path rests on. The second comparison sees the rows the first one
1470    /// left and never looks at the others.
1471    #[test]
1472    fn a_second_conjunct_reads_only_what_the_first_one_left() {
1473        let numbers: Vec<Value> = (0..64).map(|row| Value::Integer(row % 10)).collect();
1474        let column = Vector::from_values(LogicalType::Integer, &numbers).expect("a flat vector");
1475        let three = Vector::constant(LogicalType::Integer, Value::Integer(3), 64);
1476        let seven = Vector::constant(LogicalType::Integer, Value::Integer(7), 64);
1477
1478        let first = refine(Comparison::Greater, &column, &three, &Selection::identity(64))
1479            .expect("compares");
1480        let both = refine(Comparison::Less, &column, &seven, &first).expect("compares");
1481
1482        let expected: Vec<u32> = (0..64)
1483            .filter(|row| {
1484                let value = row % 10;
1485                value > 3 && value < 7
1486            })
1487            .collect();
1488        assert_eq!(both.indices(), expected.as_slice());
1489        assert!(both.len() < first.len(), "the second conjunct narrowed the selection");
1490    }
1491
1492    /// A null is not a true, so a threaded comparison drops the row rather than keeping it with an
1493    /// unknown answer. This is the rule that makes `WHERE a < 5` leave out the rows where `a` is
1494    /// null, and it is the one a branchless loop gets wrong if the validity is left out of it.
1495    #[test]
1496    fn a_null_row_is_not_kept_by_an_ordinary_comparison_and_is_by_a_total_one() {
1497        let column = Vector::from_values(
1498            LogicalType::Integer,
1499            &[Value::Integer(1), Value::Null, Value::Integer(3), Value::Null],
1500        )
1501        .expect("four rows");
1502        let cut = Vector::constant(LogicalType::Integer, Value::Integer(2), 4);
1503        let all = Selection::identity(4);
1504        assert_eq!(
1505            refine(Comparison::Less, &column, &cut, &all).expect("compares").indices(),
1506            &[0]
1507        );
1508        // The total comparison has an answer at every row, so the two nulls are kept here.
1509        let nulls = Vector::constant(LogicalType::Integer, Value::Null, 4);
1510        assert_eq!(
1511            refine(Comparison::NotDistinctFrom, &column, &nulls, &all).expect("compares").indices(),
1512            &[1, 3]
1513        );
1514    }
1515
1516    #[test]
1517    fn a_selection_past_the_end_is_caught() {
1518        let column = Vector::constant(LogicalType::Integer, Value::Integer(1), 4);
1519        let past = Selection::from_indices(vec![0, 4]);
1520        let error = refine(Comparison::Equal, &column, &column, &past).expect_err("out of range");
1521        assert!(error.message().contains("4 row vector"), "{error}");
1522    }
1523
1524    /// One value of a type, for the generator above.
1525    fn sample(ty: &LogicalType, rng: &mut Rng) -> Value {
1526        match ty {
1527            LogicalType::Boolean => Value::Boolean(rng.below(2) == 1),
1528            LogicalType::TinyInt => Value::TinyInt(rng.below(7) as i8 - 3),
1529            LogicalType::SmallInt => Value::SmallInt(rng.below(11) as i16 - 5),
1530            LogicalType::Integer => Value::Integer(rng.below(9) as i32 - 4),
1531            LogicalType::BigInt => Value::BigInt(rng.below(9) as i64 - 4),
1532            LogicalType::HugeInt => Value::HugeInt(i128::from(rng.below(9)) - 4),
1533            LogicalType::UInteger => Value::UInteger(rng.below(9) as u32),
1534            // A NaN and a negative zero in the pool on purpose, because DuckDB's float order is
1535            // not IEEE's and the fast path has to reach the same answer the oracle does.
1536            LogicalType::Float => Value::Float(match rng.below(5) {
1537                0 => f32::NAN,
1538                1 => -0.0,
1539                other => other as f32 - 2.0,
1540            }),
1541            LogicalType::Double => Value::Double(match rng.below(5) {
1542                0 => f64::NAN,
1543                1 => -0.0,
1544                other => other as f64 - 2.0,
1545            }),
1546            // The same length written three ways and two lengths that are close to it, because an
1547            // interval that compares as a triple gets every pair here wrong and one that compares
1548            // as a length gets them right.
1549            LogicalType::Interval => match rng.below(6) {
1550                0 => Value::Interval { months: 0, days: 1, micros: 0 },
1551                1 => Value::Interval { months: 0, days: 0, micros: 86_400_000_000 },
1552                2 => Value::Interval { months: 1, days: -29, micros: 86_400_000_000 },
1553                3 => Value::Interval { months: 1, days: 0, micros: 0 },
1554                4 => Value::Interval { months: 0, days: 0, micros: 90_000_000_000 },
1555                _ => Value::Interval { months: -1, days: 0, micros: 0 },
1556            },
1557            // Short, at the inline limit, over it, and sharing a prefix with each other, which is
1558            // where a comparison that trusts the prefix too far goes wrong.
1559            LogicalType::Varchar => Value::Varchar(
1560                match rng.below(6) {
1561                    0 => "",
1562                    1 => "ab",
1563                    2 => "abc",
1564                    3 => "abcdefghijkl",
1565                    4 => "abcdefghijklm",
1566                    _ => "abcdefghijklmnopqrstuvwxyz",
1567                }
1568                .to_owned(),
1569            ),
1570            other => panic!("the generator has no values for {other}"),
1571        }
1572    }
1573
1574    /// The prefix lemma, written as a test because the whole string path rests on it. A view pads
1575    /// a short string with zeros, so prefix order has to agree with byte order on every pair where
1576    /// the prefixes differ, including the pairs where one string is shorter than four bytes.
1577    #[test]
1578    fn prefix_order_is_byte_order_whenever_the_prefixes_differ() {
1579        let words =
1580            ["", "a", "ab", "abc", "abcd", "abcde", "b", "abcdefghijklmnop", "abcdefghijklmnoq"];
1581        let mut column = StringColumn::new();
1582        for word in words {
1583            column.push(word);
1584        }
1585        for (i, one) in words.iter().enumerate() {
1586            for (j, other) in words.iter().enumerate() {
1587                assert_eq!(
1588                    string_order(&column, i, &column, j),
1589                    one.as_bytes().cmp(other.as_bytes()),
1590                    "{one:?} against {other:?}"
1591                );
1592            }
1593        }
1594    }
1595
1596    /// A dictionary is compared once per distinct value, not once per row, and it has to reach the
1597    /// same answer including for the nulls it keeps in the vector it points at.
1598    #[test]
1599    fn a_dictionary_against_a_constant_reads_its_nulls_from_the_values() {
1600        let values = Vector::from_values(
1601            LogicalType::Integer,
1602            &[Value::Integer(1), Value::Null, Value::Integer(9)],
1603        )
1604        .expect("three values");
1605        let dictionary =
1606            Vector::dictionary(vec![0, 1, 2, 1, 0], values).expect("codes are in range");
1607        let constant = Vector::constant(LogicalType::Integer, Value::Integer(5), 5);
1608        let result = compare(Comparison::Less, &dictionary, &constant).expect("compares");
1609        assert_eq!(result.value_at(0), Value::Boolean(true));
1610        assert_eq!(result.value_at(1), Value::Null);
1611        assert_eq!(result.value_at(2), Value::Boolean(false));
1612        assert_eq!(result.value_at(3), Value::Null);
1613        assert_eq!(result.value_at(4), Value::Boolean(true));
1614    }
1615
1616    /// An ordering comparison on a text column reads the bytes where they lie rather than building a
1617    /// `Value` a row.
1618    ///
1619    /// The assertion that matters is the counter at the end. The answers were already right through
1620    /// the row at a time path, and what was wrong was the cost: `ORDER BY <varchar> LIMIT 10` asks
1621    /// every chunk whether any row in it can still beat the worst candidate the top N holds, and that
1622    /// question used to allocate a `String` for every row of every chunk. On the ClickBench file that
1623    /// was eighteen times the CPU of the same query with an integer sort key.
1624    #[test]
1625    fn an_ordering_on_text_against_a_literal_does_not_fall_back() {
1626        let before = fallback::count(Kernel::Compare, Form::Dictionary, Form::Constant);
1627        let values = Vector::from_values(
1628            LogicalType::Varchar,
1629            &[Value::Varchar("apple".into()), Value::Null, Value::Varchar("pear".into())],
1630        )
1631        .expect("three values");
1632        let column = Vector::dictionary(vec![0, 1, 2, 0], values).expect("codes are in range");
1633        let cut = Vector::constant(LogicalType::Varchar, Value::Varchar("melon".into()), 4);
1634        let result = compare(Comparison::Less, &column, &cut).expect("compares");
1635        assert_eq!(result.value_at(0), Value::Boolean(true));
1636        assert_eq!(result.value_at(1), Value::Null);
1637        assert_eq!(result.value_at(2), Value::Boolean(false));
1638        assert_eq!(result.value_at(3), Value::Boolean(true));
1639        // The literal on the left, which is the same question with the comparison turned around, and
1640        // it has to be turned around once rather than once a row.
1641        let other = compare(Comparison::Greater, &cut, &column).expect("compares");
1642        assert_eq!(other.value_at(0), Value::Boolean(true));
1643        assert_eq!(other.value_at(1), Value::Null);
1644        assert_eq!(other.value_at(2), Value::Boolean(false));
1645        // The selection entry point, which is the one the filter and the top N reach.
1646        let kept = refine(Comparison::GreaterOrEqual, &column, &cut, &Selection::identity(4))
1647            .expect("refines");
1648        assert_eq!(kept.indices(), [2]);
1649        assert_eq!(fallback::count(Kernel::Compare, Form::Dictionary, Form::Constant), before);
1650    }
1651
1652    /// A form pair with no loop is answered correctly and counted, which is the whole contract of
1653    /// the fallback counter. Sequence against a column is the one this file leaves out on purpose.
1654    #[test]
1655    fn a_form_pair_with_no_loop_is_still_right_and_says_so() {
1656        // The counters are per thread in a test build, so this reads its own and nothing else's.
1657        let before = fallback::count(Kernel::Compare, Form::Sequence, Form::Flat);
1658        let sequence = Vector::sequence(10, 1, 4);
1659        let flat = Vector::from_values(
1660            LogicalType::BigInt,
1661            &[Value::BigInt(9), Value::BigInt(11), Value::BigInt(12), Value::Null],
1662        )
1663        .expect("four rows");
1664        let result = compare(Comparison::Less, &sequence, &flat).expect("compares");
1665        assert_eq!(result.value_at(0), Value::Boolean(false));
1666        assert_eq!(result.value_at(1), Value::Boolean(false));
1667        assert_eq!(result.value_at(2), Value::Boolean(false));
1668        assert_eq!(result.value_at(3), Value::Null);
1669        assert!(fallback::count(Kernel::Compare, Form::Sequence, Form::Flat) > before);
1670    }
1671
1672    /// The reason `Vector::dictionary` composes rather than stacks, stated as the thing that breaks
1673    /// if it stops.
1674    ///
1675    /// Every loop in this file reaches for the values behind the codes with `Vector::data`, and a
1676    /// dictionary pointing at a dictionary has no data to hand back, so a second filter over an
1677    /// already filtered chunk used to turn every one of these kernels off and drop the comparison
1678    /// onto the row at a time path. Measured on server3 over a chunk of two numeric columns that was
1679    /// selected twice, that was 3.5 nanoseconds a row becoming 104, and a third and fourth level
1680    /// cost nothing more because the first one had already given up everything there was to give.
1681    #[test]
1682    fn a_second_level_of_codes_does_not_turn_the_loops_off() {
1683        let before = fallback::count(Kernel::Compare, Form::Dictionary, Form::Constant);
1684        let values = Vector::from_values(
1685            LogicalType::Integer,
1686            &[Value::Integer(1), Value::Integer(5), Value::Integer(9)],
1687        )
1688        .expect("three rows");
1689        let once = Vector::dictionary(vec![2, 1, 0], values).expect("codes are in range");
1690        let twice = Vector::dictionary(vec![1, 2], once).expect("codes are in range");
1691        let cut = Vector::constant(LogicalType::Integer, Value::Integer(4), 2);
1692        let result = compare(Comparison::Greater, &twice, &cut).expect("compares");
1693        assert_eq!(result.value_at(0), Value::Boolean(true));
1694        assert_eq!(result.value_at(1), Value::Boolean(false));
1695        assert_eq!(fallback::count(Kernel::Compare, Form::Dictionary, Form::Constant), before);
1696    }
1697
1698    /// Either side all null, on one of the six ordinary comparisons, is every answer null without
1699    /// the data being read. The vector this produces has to be the one the oracle produces, which
1700    /// is a flat run of falses under an all invalid validity rather than a constant.
1701    #[test]
1702    fn a_side_that_is_entirely_null_answers_without_reading_the_other() {
1703        let nulls = Vector::constant(LogicalType::Integer, Value::Null, 6);
1704        let flat = Vector::from_values(
1705            LogicalType::Integer,
1706            &[
1707                Value::Integer(1),
1708                Value::Integer(2),
1709                Value::Integer(3),
1710                Value::Integer(4),
1711                Value::Integer(5),
1712                Value::Integer(6),
1713            ],
1714        )
1715        .expect("six rows");
1716        agrees(Comparison::Less, &nulls, &flat);
1717        agrees(Comparison::Equal, &flat, &nulls);
1718        assert_eq!(
1719            compare(Comparison::Less, &nulls, &flat).expect("compares").validity(),
1720            &Validity::AllInvalid
1721        );
1722    }
1723
1724    /// An empty vector is not a special case anywhere, and the easiest way to keep it that way is
1725    /// to say so in a test rather than to find out from a panic in an operator.
1726    #[test]
1727    fn an_empty_comparison_is_an_empty_answer() {
1728        let left = Vector::from_values(LogicalType::Integer, &[]).expect("no rows");
1729        let right = Vector::constant(LogicalType::Integer, Value::Integer(1), 0);
1730        let result = compare(Comparison::Equal, &left, &right).expect("compares");
1731        assert_eq!(result.len(), 0);
1732    }
1733
1734    /// Six strings, three of them sharing a prefix, and a null, which is the column the two tests
1735    /// below read.
1736    fn words() -> Vector {
1737        Vector::from_values(
1738            LogicalType::Varchar,
1739            &[
1740                Value::Varchar("http://a".into()),
1741                Value::Varchar("http://b".into()),
1742                Value::Null,
1743                Value::Varchar("ab".into()),
1744                Value::Varchar("http://a".into()),
1745                Value::Varchar("z".into()),
1746            ],
1747        )
1748        .expect("six rows")
1749    }
1750
1751    /// A literal built early answers what a literal built per chunk answers.
1752    ///
1753    /// Every operator and both entry points, because the whole claim of the prepared literal is
1754    /// that it changes nothing, and the string column is the one where it changes the most work:
1755    /// what it carries is the four byte prefix the comparison resolves almost every row from.
1756    #[test]
1757    fn a_literal_built_early_answers_what_one_built_here_answers() {
1758        let column = words();
1759        let value = Value::Varchar("http://b".into());
1760        let constant = Vector::constant(LogicalType::Varchar, value.clone(), column.len());
1761        let held = Held::of(&LogicalType::Varchar, &value).expect("a varchar has a column");
1762        let kept = Selection::from_indices(vec![0, 1, 3, 5]);
1763        for op in [
1764            Comparison::Equal,
1765            Comparison::NotEqual,
1766            Comparison::Less,
1767            Comparison::LessOrEqual,
1768            Comparison::Greater,
1769            Comparison::GreaterOrEqual,
1770            Comparison::DistinctFrom,
1771            Comparison::NotDistinctFrom,
1772        ] {
1773            let prepared = compare_prepared(op, &column, &constant, Some(&held)).expect("compares");
1774            assert_eq!(prepared, compare(op, &column, &constant).expect("compares"), "{op:?}");
1775            // And with the literal on the left, which is the same loop turned around.
1776            let flipped = compare_prepared(op, &constant, &column, Some(&held)).expect("compares");
1777            assert_eq!(flipped, compare(op, &constant, &column).expect("compares"), "{op:?}");
1778            let refined =
1779                refine_prepared(op, &column, &constant, &kept, Some(&held)).expect("refines");
1780            assert_eq!(refined, refine(op, &column, &constant, &kept).expect("refines"), "{op:?}");
1781        }
1782    }
1783
1784    /// A literal built for something else is ignored rather than believed.
1785    ///
1786    /// The caller in `rudb-exec` takes the value out of the step it hands the answer back with, so
1787    /// this cannot happen there, and the kernel is public. A wrong answer is a much worse failure
1788    /// than a column built per chunk, so the check is a value comparison per chunk and this is what
1789    /// says it works.
1790    #[test]
1791    fn a_literal_built_for_another_value_is_ignored() {
1792        let column = words();
1793        let constant = Vector::constant(LogicalType::Varchar, Value::Varchar("z".into()), 6);
1794        let wrong = Held::of(&LogicalType::Varchar, &Value::Varchar("ab".into()))
1795            .expect("a varchar has a column");
1796        let answer = compare_prepared(Comparison::Equal, &column, &constant, Some(&wrong))
1797            .expect("compares");
1798        assert_eq!(answer, compare(Comparison::Equal, &column, &constant).expect("compares"));
1799        // And one built for another type, which is what a comparison across two types would hand
1800        // over if the caller took it from the wrong side.
1801        let other = Held::of(&LogicalType::Integer, &Value::Integer(1)).expect("an integer column");
1802        let answer = compare_prepared(Comparison::Equal, &column, &constant, Some(&other))
1803            .expect("compares");
1804        assert_eq!(answer, compare(Comparison::Equal, &column, &constant).expect("compares"));
1805    }
1806
1807    /// A bit packed column against a literal is compared in code space, which has to reach the
1808    /// oracle's answer on all eight comparisons and with the literal on either side.
1809    #[test]
1810    fn a_packed_column_against_a_constant_answers_what_the_oracle_answers() {
1811        let values: Vec<i32> = (0..64).map(|row| 1000 + (row * 37) % 500).collect();
1812        let flat = Vector::flat(LogicalType::Integer, Data::Int32(values.into()))
1813            .expect("integers are an i32 layout");
1814        let packed = flat.bit_packed().expect("a five hundred wide range packs");
1815        assert_eq!(packed.form(), Form::BitPacked);
1816        for literal in [999, 1000, 1200, 1499, 1500, 2000] {
1817            let constant = Vector::constant(LogicalType::Integer, Value::Integer(literal), 64);
1818            for op in EVERY {
1819                agrees(op, &packed, &constant);
1820                agrees(op, &constant, &packed);
1821            }
1822        }
1823    }
1824
1825    /// The nulls of a packed column live in its validity rather than in its bits, so a comparison
1826    /// has to blank them the way it blanks a flat column's, and the bits under them are whatever
1827    /// the packing wrote there.
1828    #[test]
1829    fn a_packed_column_with_nulls_answers_what_the_oracle_answers() {
1830        let values: Vec<i32> = (0..32).map(|row| 40 + row * 3).collect();
1831        let flat = Vector::flat(LogicalType::Integer, Data::Int32(values.into()))
1832            .expect("integers are an i32 layout")
1833            .with_validity(Validity::from_iter(32, |row| row % 5 != 0));
1834        let packed = flat.bit_packed().expect("packs");
1835        let constant = Vector::constant(LogicalType::Integer, Value::Integer(80), 32);
1836        for op in EVERY {
1837            agrees(op, &packed, &constant);
1838        }
1839    }
1840
1841    /// A literal the width cannot hold answers every row without a bit being read, and the answer
1842    /// still has to be the one the oracle gives.
1843    #[test]
1844    fn a_literal_outside_the_packed_range_answers_the_whole_vector_at_once() {
1845        let before = fallback::count(Kernel::Compare, Form::BitPacked, Form::Constant);
1846        let values: Vec<i32> = (0..16).map(|row| 500 + row).collect();
1847        let flat = Vector::flat(LogicalType::Integer, Data::Int32(values.into()))
1848            .expect("integers are an i32 layout");
1849        let packed = flat.bit_packed().expect("packs");
1850        let literals = [-1, 0, 499, 516, 100_000];
1851        for literal in literals {
1852            let constant = Vector::constant(LogicalType::Integer, Value::Integer(literal), 16);
1853            for op in EVERY {
1854                agrees(op, &packed, &constant);
1855            }
1856        }
1857        // The six ordinary comparisons have a loop for this pair and the two that never go null do
1858        // not, because those want the null rule inside the loop and the code space loop does not
1859        // carry one. They take the row at a time path and count themselves, which is the counter
1860        // doing its job rather than a gap being hidden.
1861        let total = EVERY.iter().filter(|op| op.is_total()).count();
1862        assert_eq!(
1863            fallback::count(Kernel::Compare, Form::BitPacked, Form::Constant) - before,
1864            (literals.len() * total) as u64,
1865            "only the two total comparisons fall through"
1866        );
1867    }
1868
1869    /// The conjunct path reads the rows an earlier conjunct kept, so the code space loop has to be
1870    /// reached through the selection rather than through the row number.
1871    #[test]
1872    fn refining_a_selection_over_a_packed_column_keeps_the_same_rows() {
1873        let values: Vec<i32> = (0..64).map(|row| 200 + (row * 11) % 128).collect();
1874        let flat = Vector::flat(LogicalType::Integer, Data::Int32(values.clone().into()))
1875            .expect("integers are an i32 layout");
1876        let packed = flat.bit_packed().expect("packs");
1877        let kept = Selection::from_predicate(64, |row| row % 3 == 0);
1878        let constant = Vector::constant(LogicalType::Integer, Value::Integer(260), 64);
1879        let packed_rows = refine(Comparison::Greater, &packed, &constant, &kept).expect("refines");
1880        let flat_rows = refine(Comparison::Greater, &flat, &constant, &kept).expect("refines");
1881        assert_eq!(packed_rows.indices(), flat_rows.indices());
1882        assert!(!packed_rows.is_empty(), "the literal is inside the range");
1883    }
1884
1885    /// A column of URLs, which is the shape the string view form exists for: a shared prefix that
1886    /// the four bytes in the view cannot settle, and payloads long enough to be in the arena.
1887    fn urls(count: usize) -> Vector {
1888        let mut rng = Rng(0x5eed_1234);
1889        let values: Vec<Value> = (0..count)
1890            .map(|_| {
1891                let host = rng.below(6);
1892                let path = rng.below(40);
1893                Value::Varchar(format!("http://example{host}.test/a/rather/long/path/{path}"))
1894            })
1895            .collect();
1896        Vector::from_values(LogicalType::Varchar, &values).expect("strings")
1897    }
1898
1899    #[test]
1900    fn a_string_view_column_against_a_literal_answers_what_the_oracle_answers() {
1901        let before = fallback::count(Kernel::Compare, Form::StringView, Form::Constant);
1902        let shared = urls(64).shared_text().expect("shares");
1903        assert_eq!(shared.form(), Form::StringView);
1904        let literals = ["http://example3.test/a/rather/long/path/7", "a", "zzz", ""];
1905        for literal in literals {
1906            let value = Value::Varchar(literal.to_owned());
1907            let constant = Vector::constant(LogicalType::Varchar, value, 64);
1908            for op in EVERY {
1909                agrees(op, &shared, &constant);
1910                agrees(op, &constant, &shared);
1911            }
1912        }
1913        assert_eq!(
1914            fallback::count(Kernel::Compare, Form::StringView, Form::Constant),
1915            before,
1916            "the form has a loop of its own for every comparison"
1917        );
1918    }
1919
1920    #[test]
1921    fn a_string_view_column_against_another_one_answers_what_the_oracle_answers() {
1922        let shared = urls(48).shared_text().expect("shares");
1923        let other = urls(48).shared_text().expect("shares");
1924        let flat = urls(48);
1925        for op in EVERY {
1926            agrees(op, &shared, &other);
1927            agrees(op, &shared, &flat);
1928            agrees(op, &flat, &shared);
1929        }
1930    }
1931
1932    #[test]
1933    fn the_nulls_of_a_string_view_column_are_the_nulls_the_oracle_sees() {
1934        let shared = urls(32)
1935            .with_validity(Validity::from_iter(32, |row| row % 4 != 1))
1936            .shared_text()
1937            .expect("shares");
1938        let constant =
1939            Vector::constant(LogicalType::Varchar, Value::Varchar("http://example3".into()), 32);
1940        for op in EVERY {
1941            agrees(op, &shared, &constant);
1942        }
1943    }
1944
1945    #[test]
1946    fn a_compressed_column_against_a_literal_answers_what_the_oracle_answers() {
1947        let before = fallback::count(Kernel::Compare, Form::Fsst, Form::Constant);
1948        let flat = urls(64);
1949        let coded = flat.clone().compressed().expect("compresses");
1950        assert_eq!(coded.form(), Form::Fsst);
1951        let present = match coded.value_at(9) {
1952            Value::Varchar(text) => text,
1953            other => panic!("a string column reads back strings, not {other:?}"),
1954        };
1955        for literal in [present.as_str(), "http://example3.test/nothing/like/it", ""] {
1956            let value = Value::Varchar(literal.to_owned());
1957            let constant = Vector::constant(LogicalType::Varchar, value, 64);
1958            for op in EVERY {
1959                agrees(op, &coded, &constant);
1960                agrees(op, &constant, &coded);
1961            }
1962        }
1963        // Equality has a loop in code space and the six comparisons that need an order do not,
1964        // because a symbol code says nothing about where its symbol sorts. Those decompress a row at
1965        // a time and count themselves, which is the counter doing its job rather than a gap hiding.
1966        let ordered = EVERY.len() - 2;
1967        assert_eq!(
1968            fallback::count(Kernel::Compare, Form::Fsst, Form::Constant) - before,
1969            (3 * ordered) as u64,
1970            "only the comparisons that need an order fall through"
1971        );
1972    }
1973
1974    /// Equality in code space is only right if compressing is a function, so the same string always
1975    /// has the same codes and two different strings never do. This is that claim as a test.
1976    #[test]
1977    fn every_row_of_a_compressed_column_matches_itself_and_nothing_else() {
1978        let flat = urls(48);
1979        let coded = flat.clone().compressed().expect("compresses");
1980        for row in 0..48 {
1981            let constant = Vector::constant(LogicalType::Varchar, flat.value_at(row), 48);
1982            let equal = compare(Comparison::Equal, &coded, &constant).expect("compares");
1983            for other in 0..48 {
1984                let want = flat.value_at(other) == flat.value_at(row);
1985                assert_eq!(
1986                    equal.value_at(other),
1987                    Value::Boolean(want),
1988                    "row {row} against {other}"
1989                );
1990            }
1991        }
1992    }
1993
1994    #[test]
1995    fn the_nulls_of_a_compressed_column_are_the_nulls_the_oracle_sees() {
1996        let coded = urls(32)
1997            .with_validity(Validity::from_iter(32, |row| row % 3 != 0))
1998            .compressed()
1999            .expect("compresses");
2000        let value = coded.value_at(1);
2001        let constant = Vector::constant(LogicalType::Varchar, value, 32);
2002        for op in EVERY {
2003            agrees(op, &coded, &constant);
2004        }
2005    }
2006
2007    /// The two forms hold the same strings in two different places, so a filter over either one has
2008    /// to keep the same rows. This is the differential check that the arena being shared changed
2009    /// nothing about what a comparison means.
2010    #[test]
2011    fn a_filter_over_either_string_form_keeps_the_same_rows() {
2012        let flat = urls(96);
2013        let shared = flat.clone().shared_text().expect("shares");
2014        let constant =
2015            Vector::constant(LogicalType::Varchar, Value::Varchar("http://example3".into()), 96);
2016        let kept = Selection::from_predicate(96, |row| row % 5 != 0);
2017        for op in EVERY {
2018            let over_flat = refine(op, &flat, &constant, &kept).expect("refines");
2019            let over_shared = refine(op, &shared, &constant, &kept).expect("refines");
2020            assert_eq!(over_shared.indices(), over_flat.indices(), "{op:?}");
2021        }
2022    }
2023
2024    /// The peeled path and the row at a time oracle on the same rows, on both spellings and on
2025    /// both entry points. A dictionary that shares its values is what a native scan hands over, so
2026    /// this is the shape every `WHERE URL <> ''` in ClickBench arrives in.
2027    #[test]
2028    fn a_comparison_peeled_over_a_shared_dictionary_answers_what_the_oracle_answers() {
2029        let words = ["", "one", "two", "", "three"];
2030        let values: Vec<Value> = words.iter().map(|text| Value::Varchar((*text).into())).collect();
2031        let values = std::sync::Arc::new(
2032            Vector::from_values(LogicalType::Varchar, &values).expect("a vector of text"),
2033        );
2034        let codes = vec![0, 1, 3, 2, 0, 4, 1, 0];
2035        let column = Vector::stable_dictionary(codes.clone(), values).expect("codes are in range");
2036        same_as_the_oracle(&column);
2037    }
2038
2039    /// Values a storage reader would hand over, which answer one at a time and which know the
2040    /// order the writer sorted them into.
2041    #[derive(Debug)]
2042    struct Filed {
2043        values: Vec<Vec<u8>>,
2044        order: Vec<u32>,
2045    }
2046
2047    impl rudb_vector::TextSource for Filed {
2048        fn len(&self) -> usize {
2049            self.values.len()
2050        }
2051
2052        fn bytes_at(&self, index: usize) -> Result<Option<&[u8]>> {
2053            Ok(self.values.get(index).map(Vec::as_slice))
2054        }
2055
2056        fn footprint(&self) -> usize {
2057            self.values.iter().map(Vec::len).sum()
2058        }
2059
2060        fn ranks(&self) -> Option<usize> {
2061            Some(self.order.len())
2062        }
2063
2064        fn compare_rank(&self, rank: usize, wanted: &[u8]) -> Result<Ordering> {
2065            // Plain bytes rather than the head the native format compares first, because what this
2066            // test is about is the answer the comparison gives and not how few reads it took.
2067            Ok(self.values[self.order[rank] as usize].as_slice().cmp(wanted))
2068        }
2069
2070        fn code_at_rank(&self, rank: usize) -> Result<u32> {
2071            Ok(self.order[rank])
2072        }
2073    }
2074
2075    /// The same comparison over a dictionary whose values came out of a file with their order, so
2076    /// the literal is resolved to a code by search rather than compared against every value.
2077    #[test]
2078    fn a_comparison_against_a_sorted_dictionary_answers_what_the_oracle_answers() {
2079        // Distinct, which is what a source promises by answering with an order at all, and which
2080        // a global dictionary is by construction.
2081        let words = ["", "one", "two", "four", "three"];
2082        let values: Vec<Vec<u8>> = words.iter().map(|text| text.as_bytes().to_vec()).collect();
2083        let mut order = (0..values.len() as u32).collect::<Vec<_>>();
2084        order.sort_by(|&left, &right| values[left as usize].cmp(&values[right as usize]));
2085        let values = Vector::external_text(
2086            LogicalType::Varchar,
2087            std::sync::Arc::new(Filed { values, order }),
2088        )
2089        .expect("a filed vector");
2090        let codes = vec![0, 1, 3, 2, 0, 4, 1, 0];
2091        let column = Vector::stable_dictionary(codes, std::sync::Arc::new(values))
2092            .expect("codes are in range");
2093        same_as_the_oracle(&column);
2094    }
2095
2096    /// Every equality and inequality against a handful of literals, whole and narrowed, checked
2097    /// against the row at a time path. `missing` is in here because a dictionary that does not
2098    /// hold the literal is decided for the whole chunk and that is its own arm of the code.
2099    fn same_as_the_oracle(column: &Vector) {
2100        for literal in ["", "one", "missing"] {
2101            for op in [Comparison::Equal, Comparison::NotEqual] {
2102                let value = Value::Varchar(literal.to_owned());
2103                let held = Held::of(&LogicalType::Varchar, &value).expect("text has a column");
2104                let right = Vector::constant(LogicalType::Varchar, value.clone(), column.len());
2105                let wanted = oracle(op, column, &right);
2106                let got = compare_prepared(op, column, &right, Some(&held))
2107                    .expect("the peeled path answers");
2108                assert_eq!(got, wanted, "{literal:?} under {op:?}");
2109                // A fresh memo for the selection, since the one above belongs to that call's node.
2110                let held = Held::of(&LogicalType::Varchar, &value).expect("text has a column");
2111                let kept = Selection::from_predicate(column.len(), |row| row % 3 != 1);
2112                let refined = refine_prepared(op, column, &right, &kept, Some(&held))
2113                    .expect("the peeled path narrows");
2114                let wanted: Vec<u32> = kept
2115                    .indices()
2116                    .iter()
2117                    .copied()
2118                    .filter(|&row| is_true(&wanted.value_at(row as usize)))
2119                    .collect();
2120                assert_eq!(refined.indices(), wanted, "{literal:?} under {op:?}, narrowed");
2121            }
2122        }
2123    }
2124}