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