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