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