Skip to main content

rudb_kernels/
compare.rs

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