Skip to main content

rudb_exec/
prepared.rs

1//! An expression prepared once for a pipeline and then evaluated over every chunk.
2//!
3//! `spec/engine/04-expressions.md`. [`evaluate`](crate::evaluate) walks the plan's expression tree
4//! on every chunk, which means it does four things per chunk that depend on nothing about the
5//! chunk: it recurses, it resolves every column reference by a linear search through the schema, it
6//! clones a [`LogicalType`] for every node, and it copies the whole column a [`Expr::Column`] names.
7//! Over `hits` at a hundred thousand chunks that is a hundred thousand schema searches per column
8//! reference and a hundred thousand copies of every column any expression mentions.
9//!
10//! This type does all four once. The tree is flattened into a post order array, so evaluating it is
11//! a loop over that array and the recursion is gone with it. Column references are resolved to
12//! positions when the pipeline is built. Types are held here rather than cloned out of the plan.
13//! And a column reference is not a step that produces anything: it is read straight out of the chunk
14//! at the point an operand is wanted, so the column is never copied at all.
15//!
16//! # What is shared and what is not
17//!
18//! [`Prepared`] is immutable after it is built and is `Send` and `Sync`, so one of them serves every
19//! thread running a copy of the pipeline. [`Scratch`] is the per chunk working space and there is
20//! one per pipeline instance. That split is not for this layer's benefit. It is the same split every
21//! operator needs at layer eight, where the scheduler runs one pipeline on as many threads as it has
22//! morsels for, and building it here means the operators above are written against it from the start
23//! rather than retrofitted onto it.
24//!
25//! # What is still allocated per chunk
26//!
27//! Two things, and both are named rather than hidden. A node with four or more operands gathers
28//! references to them into a `Vec<&Vector>` so a kernel can take a slice, which is one allocation of
29//! pointers rather than a copy of any data, and which a node of one, two or three operands does on
30//! the stack instead. And every kernel allocates the vector it returns, because no kernel in
31//! `rudb-kernels` takes an output parameter. The second is much the larger of the two and it is the
32//! one tier 1 fusion removes, which is scheduled after layer six for the reason
33//! `spec/engine/04-expressions.md` gives: once the tree walk is gone what is left to save is pass
34//! count, and at 1024 rows the intermediate vectors are eight kilobytes and stay in L1.
35
36use rudb_common::{Error, LogicalType, PhysicalType, Result, Value};
37use rudb_kernels::{
38    Comparison, Connective, Held, Members, Recipe, cast, combine, compare_prepared, in_set,
39    is_true, refine_flags, refine_prepared, selection,
40};
41use rudb_plan::{CompareOp, ConjunctionOp, Expr, ExprRef, Plan};
42use rudb_vector::{Chunk, Selection, Vector};
43
44use crate::ordering::Ordering;
45use crate::schema::Schema;
46use crate::written::written;
47
48/// The scheduler's half of the expression contract, imposed now rather than at layer eight.
49///
50/// A prepared expression is the immutable half of a pipeline and layer eight hands one of them to
51/// every thread running that pipeline. That is only sound if it holds nothing thread local, and the
52/// way to find out on the commit that breaks it rather than eight layers later is to ask the
53/// compiler here, exactly as [`Chunk`] does for the data plane.
54const _: () = {
55    const fn assert_shareable<T: Send + Sync>() {}
56    assert_shareable::<Prepared>();
57};
58
59/// One or more bound expressions, flattened and resolved against a schema.
60///
61/// Built once per pipeline with [`Prepared::new`] and evaluated per chunk with
62/// [`Prepared::evaluate`] or [`Prepared::evaluate_one`], each of which wants the [`Scratch`] that
63/// [`Prepared::scratch`] hands out.
64#[derive(Debug)]
65pub struct Prepared {
66    /// The nodes in post order, so every node's operands have already been computed when it runs.
67    steps: Vec<Step>,
68    /// The type each step produces, indexed the same way as `steps`.
69    ///
70    /// A parallel array rather than a field in the variant, for the reason [`Expr`] gives: a
71    /// [`LogicalType`] owns a `Vec` for its nested cases and putting one in every variant would make
72    /// the common variants several times larger for the benefit of the rare ones.
73    types: Vec<LogicalType>,
74    /// The operand lists of the steps that have one, as runs of step indices.
75    operands: Vec<usize>,
76    /// The last step that reads each step's slot, or `usize::MAX` for one nothing reads.
77    ///
78    /// A slot is emptied as soon as the step that was the last to read it has run. Keeping every
79    /// intermediate alive to the end of the array instead is what the first measured version of this
80    /// did, and a chain of eight additions was slower prepared than walked because of it: nine live
81    /// intermediates at eight kilobytes each is seventy two kilobytes of working set where the tree
82    /// walk has two, and two is the pair the allocator hands back and forth and that stays in L1.
83    /// Everything else about the prepared form was faster and this one thing paid all of it back.
84    last_use: Vec<usize>,
85    /// The step index each expression this was built from ends at.
86    roots: Vec<usize>,
87}
88
89/// One node of a flattened expression.
90///
91/// A step refers to its operands by their index in [`Prepared::steps`], which is always smaller than
92/// its own because the array is in post order.
93#[derive(Debug)]
94enum Step {
95    /// A column of the chunk, by resolved position.
96    ///
97    /// This step computes nothing. Its slot stays empty and an operand that names it is read out of
98    /// the chunk, which is the whole of what makes a column reference free rather than a copy.
99    Column(usize),
100    /// A literal, materialized into a constant vector as long as the chunk.
101    Constant(Value),
102    /// A cast to this step's own type.
103    Cast {
104        /// The step being cast.
105        input: usize,
106        /// Whether a failed cast yields null instead of raising.
107        try_cast: bool,
108    },
109    /// A binary comparison.
110    Compare {
111        /// Which comparison.
112        op: Comparison,
113        /// The left operand's step.
114        left: usize,
115        /// The right operand's step.
116        right: usize,
117        /// The side that is a literal, in the one row column the comparison loops read it through,
118        /// and `None` when neither side is one.
119        ///
120        /// Built here because the loops read both sides through a slice, so the constant side has
121        /// to become a column somewhere, and the plan says which side that is. For a string it is
122        /// also where the four byte prefix comes from, which is what almost every row of a string
123        /// comparison is decided by.
124        held: Option<Held>,
125    },
126    /// An `AND` or `OR` over a run of [`Prepared::operands`].
127    Conjunction {
128        /// Which connective.
129        op: Connective,
130        /// Where the operand list starts.
131        start: usize,
132        /// How many operands it has.
133        len: usize,
134    },
135    /// A scalar function over a run of [`Prepared::operands`].
136    Function {
137        /// The call, with the name resolved and whatever the kernel could work out from the
138        /// arguments that were literals already worked out.
139        ///
140        /// Held here so the plan is not consulted per chunk, and built here so that a regular
141        /// expression is compiled once for the query rather than once for each of the hundred
142        /// thousand chunks a pipeline over `hits` runs.
143        recipe: Recipe,
144        /// How the call is written, for the one error message that quotes it.
145        ///
146        /// Rendered when the pipeline is built rather than when a chunk arrives, because the plan
147        /// is here and is not there. It is a short string per function node in the query and it is
148        /// built once, which is a different cost from the tree walk's, where the plan is still to
149        /// hand and the rendering can wait until the row that fails.
150        written: String,
151        /// Where the argument list starts.
152        start: usize,
153        /// How many arguments it has.
154        len: usize,
155    },
156    /// A membership test over a list the query wrote out.
157    ///
158    /// The binder has no `IN` node: `x IN (1, 2, 3)` arrives as an `OR` of three equalities and
159    /// `x NOT IN (1, 2, 3)` as an `AND` of three inequalities. That is the right shape for a binder
160    /// to produce, because nothing after it then needs a second set of rules for null, and it is the
161    /// wrong shape to run, because it is a pass over the column and an output vector per entry.
162    /// This is that shape folded back up, and folding it here rather than after the operands are
163    /// pushed is what keeps the equalities from being run anyway.
164    InSet {
165        /// The step being tested.
166        input: usize,
167        /// The list, as a set, with the null rule and the direction it is read in.
168        members: Members,
169    },
170    /// A searched `CASE`, whose branches are prepared expressions of their own.
171    ///
172    /// Nested rather than flattened into the same array because a branch is not evaluated over the
173    /// chunk, it is evaluated over the rows no earlier arm claimed, and a step in the outer array
174    /// would have no way to say that. The selection threaded form in #57 replaces this whole
175    /// variant, and when it does the branches stop being separate arrays.
176    Case {
177        /// The `WHEN`/`THEN` pairs, in order.
178        arms: Vec<PreparedArm>,
179        /// The `ELSE`, if there is one. Absent means null.
180        otherwise: Option<Prepared>,
181    },
182}
183
184/// One `WHEN`/`THEN` pair of a prepared [`Step::Case`].
185#[derive(Debug)]
186struct PreparedArm {
187    /// The condition.
188    when: Prepared,
189    /// The result if the condition is true.
190    then: Prepared,
191}
192
193/// The per chunk working space of one [`Prepared`].
194///
195/// One per pipeline instance and never shared, which is the mutable half of the split the module
196/// documentation describes. It is handed back in rather than made inside [`Prepared::evaluate`] so
197/// that the array of slots survives from one chunk to the next instead of being allocated a hundred
198/// thousand times over a scan.
199#[derive(Debug)]
200pub struct Scratch {
201    /// What each step produced, or `None` for a step that produces nothing and for one that has not
202    /// run yet.
203    slots: Vec<Option<Vector>>,
204    /// What each connective step has learned about its operands, indexed by step.
205    ///
206    /// Empty for every step that is not a connective and for a connective a filter has not reached
207    /// yet, since it is built the first time one runs and the shape it needs is not known before
208    /// then. This is the mutable half of the adaptive ordering and it is here rather than in
209    /// [`Prepared`] because a prepared expression is shared by every thread running the pipeline.
210    orders: Vec<Option<Ordering>>,
211}
212
213impl Scratch {
214    /// The order a connective's operands are run in.
215    ///
216    /// For the tests that say the learning reached the walk. Nothing in the engine asks a scratch
217    /// this, because the walk is the only thing that reads an ordering and it reads its own.
218    #[cfg(test)]
219    fn order(&self, step: usize) -> Option<&[usize]> {
220        self.orders[step].as_ref().map(Ordering::order)
221    }
222}
223
224impl Prepared {
225    /// Prepares `exprs` against `schema`.
226    ///
227    /// # Errors
228    ///
229    /// If a column reference names a binding the schema does not have, or if an aggregate appears
230    /// where an ordinary expression was expected. Both are failures of the plan rather than of the
231    /// data, which is why they are found here, once, rather than on some chunk in the middle of a
232    /// scan.
233    pub fn new(plan: &Plan, exprs: &[ExprRef], schema: &Schema) -> Result<Self> {
234        let mut prepared = Self {
235            steps: Vec::new(),
236            types: Vec::new(),
237            operands: Vec::new(),
238            last_use: Vec::new(),
239            roots: Vec::new(),
240        };
241        for &expr in exprs {
242            let root = prepared.push(plan, expr, schema)?;
243            prepared.roots.push(root);
244        }
245        prepared.last_use = prepared.last_uses();
246        Ok(prepared)
247    }
248
249    /// Which step is the last to read each step, computed once when the expression is prepared.
250    ///
251    /// A root is never freed, because the whole point of running the array was to produce it. A
252    /// step nothing reads and that is not a root cannot happen, since every step is pushed by the
253    /// node that wanted it, but saying `usize::MAX` rather than asserting that keeps this a fact
254    /// about the array rather than a claim about the builder.
255    fn last_uses(&self) -> Vec<usize> {
256        let mut last = vec![usize::MAX; self.steps.len()];
257        for index in 0..self.steps.len() {
258            self.for_each_operand(index, |operand| last[operand] = index);
259        }
260        for &root in &self.roots {
261            last[root] = usize::MAX;
262        }
263        last
264    }
265
266    /// Visits the steps one step reads, whatever shape its operands are held in.
267    fn for_each_operand(&self, index: usize, mut visit: impl FnMut(usize)) {
268        match &self.steps[index] {
269            // A case's branches are arrays of their own and read nothing out of this one.
270            Step::Column(_) | Step::Constant(_) | Step::Case { .. } => {}
271            Step::Cast { input, .. } | Step::InSet { input, .. } => visit(*input),
272            Step::Compare { left, right, .. } => {
273                visit(*left);
274                visit(*right);
275            }
276            Step::Conjunction { start, len, .. } | Step::Function { start, len, .. } => {
277                for &operand in &self.operands[*start..*start + *len] {
278                    visit(operand);
279                }
280            }
281        }
282    }
283
284    /// Prepares one expression, which is the common case and saves the caller a slice.
285    ///
286    /// # Errors
287    ///
288    /// Whatever [`Prepared::new`] reports.
289    pub fn one(plan: &Plan, expr: ExprRef, schema: &Schema) -> Result<Self> {
290        Self::new(plan, &[expr], schema)
291    }
292
293    /// Working space sized for this expression.
294    #[must_use]
295    pub fn scratch(&self) -> Scratch {
296        Scratch {
297            slots: (0..self.steps.len()).map(|_| None).collect(),
298            orders: (0..self.steps.len()).map(|_| None).collect(),
299        }
300    }
301
302    /// How many expressions this was built from.
303    #[must_use]
304    pub fn len(&self) -> usize {
305        self.roots.len()
306    }
307
308    /// How many comparisons have their literal side already built.
309    ///
310    /// For the tests, for the same reason as [`Self::sets`]: an answer that moved would be a bug,
311    /// so the only thing a test can look at is whether the building happened.
312    #[cfg(test)]
313    fn literals_built(&self) -> usize {
314        self.steps.iter().filter(|step| matches!(step, Step::Compare { held: Some(_), .. })).count()
315    }
316
317    /// How many of the steps are an `IN` list folded back up.
318    ///
319    /// For the tests, which cannot see the fold in an answer because an answer that changed would
320    /// be a bug.
321    #[cfg(test)]
322    fn sets(&self) -> usize {
323        self.steps.iter().filter(|step| matches!(step, Step::InSet { .. })).count()
324    }
325
326    /// How many of the function steps worked something out when this was built.
327    ///
328    /// For the tests, which cannot see the hoisting in an answer because an answer that changed
329    /// would be a bug.
330    #[cfg(test)]
331    fn hoisted(&self) -> usize {
332        self.steps
333            .iter()
334            .filter(|step| matches!(step, Step::Function { recipe, .. } if recipe.hoists()))
335            .count()
336    }
337
338    /// Whether it was built from no expressions at all.
339    #[must_use]
340    pub fn is_empty(&self) -> bool {
341        self.roots.is_empty()
342    }
343
344    /// Evaluates every expression over `chunk`, appending one vector each to `out`.
345    ///
346    /// Appends rather than returns a `Vec`, so a caller in a loop reuses one buffer.
347    ///
348    /// # Errors
349    ///
350    /// Anything a kernel reports, on the first expression that reports it.
351    pub fn evaluate(
352        &self,
353        chunk: &Chunk,
354        scratch: &mut Scratch,
355        out: &mut Vec<Vector>,
356    ) -> Result<()> {
357        self.run(chunk, scratch)?;
358        for &root in &self.roots {
359            // The one place a column is copied, and it is copied because the caller is taking
360            // ownership of a vector that has to outlive the chunk it came from. `SELECT a` is that
361            // shape and a projection of a bare column is the only expression where it happens.
362            match self.steps[root] {
363                Step::Column(position) => out.push(chunk.column(position)?.clone()),
364                _ => out.push(scratch.slots[root].take().ok_or_else(|| missing(root))?),
365            }
366        }
367        Ok(())
368    }
369
370    /// Evaluates a single expression over `chunk`, handing back a reference to the answer.
371    ///
372    /// A reference rather than a vector, because the caller of this is a filter, which reads the
373    /// flags to build a selection and then drops them. Nothing about that wants ownership, and a
374    /// predicate that is a bare column reference, which `WHERE flag` is, would otherwise copy the
375    /// column to hand it over.
376    ///
377    /// # Errors
378    ///
379    /// Anything a kernel reports, and an internal error if this was not built from exactly one
380    /// expression.
381    pub fn evaluate_one<'s>(
382        &'s self,
383        chunk: &'s Chunk,
384        scratch: &'s mut Scratch,
385    ) -> Result<&'s Vector> {
386        let [root] = self.roots[..] else {
387            return Err(Error::internal(format!(
388                "evaluate_one over a prepared expression of {} roots",
389                self.roots.len()
390            )));
391        };
392        self.run(chunk, scratch)?;
393        self.operand(root, chunk, &scratch.slots)
394    }
395
396    /// Evaluates a single expression as a filter, handing back the rows it keeps.
397    ///
398    /// The difference between this and [`evaluate_one`](Self::evaluate_one) followed by
399    /// [`selection`] is the whole of what a threaded filter is. An `AND` evaluated as an expression
400    /// runs every conjunct over every row and then combines the flag vectors, so a predicate of four
401    /// conjuncts that each pass a fifth of the rows does five times the work of one that stops
402    /// looking at a row as soon as a conjunct rejects it. TPC-H Q6 is exactly that predicate.
403    ///
404    /// So the conjuncts of a top level `AND` are run one at a time, each over the rows the ones
405    /// before it left, and the moment nothing is left the rest of the predicate is not run at all.
406    /// The order they run in starts as the order the plan gives and then moves, because which
407    /// conjunct is worth running first is a question about the data and the scan is the thing
408    /// holding the answer. The `ordering` module has what is measured and how.
409    ///
410    /// A top level `OR` is threaded the same way against the complement. A row the first branch
411    /// accepts is a row the filter keeps whatever the rest of the predicate says about it, so each
412    /// branch is run over the rows no branch before it accepted, and the moment every row has been
413    /// accepted the rest of the predicate is not run either. That is the mirror of the `AND` case
414    /// and not an approximation of it: the answer is the same set of rows, because `OR` over three
415    /// valued logic is true wherever any branch is true and nothing a later branch says can take a
416    /// row back. It is worth less than the `AND` case in practice, since an `OR` of selective
417    /// branches leaves almost every row in play for the branch after, and it is worth having anyway
418    /// because the cost of finding that out is one merge per branch.
419    ///
420    /// What is threaded is the operand's own comparison rather than the whole of its subtree. A
421    /// conjunct of `a + b > 5` still adds over the whole chunk, because the scalar kernels take a
422    /// vector rather than a selection, and it is the comparison and everything downstream of it that
423    /// reads only the rows still in play. An operand that is a bare column or a function produces
424    /// flags over the chunk and is narrowed with [`refine_flags`], which is what keeps one awkward
425    /// operand from putting the others back on the unthreaded path. An operand that is itself a
426    /// connective recurses, so the two conjuncts of each half of `(a AND b) OR (c AND d)` are
427    /// threaded the same way the halves are.
428    ///
429    /// None of this is available to a projection. `SELECT a > 5 AND b LIKE 'x%'` wants a value per
430    /// row and the rows a selection dropped have no value in it, so [`evaluate`](Self::evaluate) and
431    /// [`evaluate_one`](Self::evaluate_one) evaluate the whole tree over the whole chunk and combine
432    /// flags. The two are separate entry points picked when the pipeline is built rather than one
433    /// path with a flag in it, because conflating them is a wrong answer rather than a slow one.
434    ///
435    /// # Errors
436    ///
437    /// Anything a kernel reports, and an internal error if this was not built from exactly one
438    /// expression.
439    pub fn evaluate_filter(&self, chunk: &Chunk, scratch: &mut Scratch) -> Result<Selection> {
440        let [root] = self.roots[..] else {
441            return Err(Error::internal(format!(
442                "evaluate_filter over a prepared expression of {} roots",
443                self.roots.len()
444            )));
445        };
446        scratch.slots.clear();
447        scratch.slots.resize_with(self.steps.len(), || None);
448        // A predicate that is not a connective at all is the same walk over one operand, which is
449        // where [`thread`](Self::thread) starts: it runs the tree and turns the flags into a
450        // selection, with no narrowing to do because nothing has narrowed anything yet.
451        self.thread(root, 0, chunk, scratch, None)
452    }
453
454    /// The operands of one connective, run in order, each over the rows the ones before it left.
455    ///
456    /// `live` is the rows this connective has to decide about and `None` means every row of the
457    /// chunk, which is not the same as a selection of all of them: it lets the first operand take
458    /// the unthreaded kernel rather than a pass over an identity selection. The answer is the rows
459    /// out of `live` the connective is true for.
460    ///
461    /// The walk is the same for both connectives and only the bookkeeping differs. `AND` carries the
462    /// rows every operand so far has kept, so each answer replaces it. `OR` carries the rows no
463    /// operand so far has accepted, so each answer comes out of it and the rows the connective keeps
464    /// are the ones that went missing along the way.
465    ///
466    /// The operand is not `steps[begin..=operand]` evaluated and then narrowed. Its subtree is run
467    /// over the whole chunk and it is the operand itself that reads only the rows in play, except
468    /// where the operand is another connective, which recurses and threads its own operands from
469    /// here rather than falling back to a flag vector. That is what makes `(a AND b) OR (c AND d)`
470    /// four threaded comparisons rather than two threaded ones and two flag passes.
471    fn branches(
472        &self,
473        index: usize,
474        begin: usize,
475        chunk: &Chunk,
476        scratch: &mut Scratch,
477        live: Option<&Selection>,
478    ) -> Result<Selection> {
479        let Step::Conjunction { op, start, len } = self.steps[index] else {
480            return Err(Error::internal("a connective walk over a step that is not a connective"));
481        };
482        let operands = &self.operands[start..start + len];
483        let rows = chunk.len();
484        // Out of the scratch for the length of the walk, because the walk runs steps and running a
485        // step wants the scratch. It goes back at the end, which is also where it learns. A walk
486        // that fails leaves the slot empty and the next chunk starts the connective over, which is
487        // a history lost on a query that is about to stop running anyway.
488        let mut order = scratch.orders[index]
489            .take()
490            .unwrap_or_else(|| Ordering::new(op, self.weights(operands, begin)));
491        let mut carried: Option<Selection> = live.cloned();
492        for slot in 0..len {
493            if carried.as_ref().is_some_and(Selection::is_empty) {
494                break;
495            }
496            let which = order.at(slot);
497            let operand = operands[which];
498            // The array is in post order and an operand's whole subtree sits between the operand
499            // before it and the operand itself, which is a range the run order cannot move. That is
500            // what lets the operands run in any order at all without a second structure to say
501            // where each one starts.
502            let from = if which == 0 { begin } else { operands[which - 1] + 1 };
503            let given = carried.as_ref().map_or(rows, Selection::len);
504            let answered = self.thread(operand, from, chunk, scratch, carried.as_ref())?;
505            order.observed(which, given, answered.len());
506            carried = Some(match (op, carried) {
507                (Connective::And, _) => answered,
508                (Connective::Or, None) => answered.complement(rows),
509                (Connective::Or, Some(carried)) => carried.without(&answered),
510            });
511            // An operand's subtree is its own, because nothing here looks for a common subexpression
512            // and so no step outside the range is reading one inside it.
513            for step in from..=operand {
514                scratch.slots[step] = None;
515            }
516        }
517        order.relearn();
518        scratch.orders[index] = Some(order);
519        Ok(match (op, carried) {
520            // A connective with no operands, which the binder does not build and which is answered
521            // here rather than left to index arithmetic: an empty `AND` is every row and an empty
522            // `OR` is none.
523            (Connective::And, None) => live.cloned().unwrap_or_else(|| Selection::identity(rows)),
524            (Connective::And, Some(kept)) => kept,
525            (Connective::Or, None) => Selection::empty(),
526            (Connective::Or, Some(missed)) => match live {
527                None => missed.complement(rows),
528                Some(live) => live.without(&missed),
529            },
530        })
531    }
532
533    /// What each operand of a connective costs to run over a chunk, for the ordering to divide by.
534    ///
535    /// An operand costs what its whole subtree costs, which is the steps from where the operand
536    /// before it ended up to the operand itself.
537    fn weights(&self, operands: &[usize], begin: usize) -> Vec<f64> {
538        let mut costs = Vec::with_capacity(operands.len());
539        let mut from = begin;
540        for &operand in operands {
541            costs.push((from..=operand).map(|step| self.weight(step)).sum());
542            from = operand + 1;
543        }
544        costs
545    }
546
547    /// Roughly what one step costs to run over a chunk, against a comparison of two fixed width
548    /// columns as the unit.
549    ///
550    /// A ranking rather than a prediction. Nothing downstream reads the number itself, only which
551    /// of two of them is larger, and the differences that decide an order are the big ones: a
552    /// column reference costs nothing because it is read in place, a string function costs many
553    /// times what an integer comparison costs, and a comparison over a variable length type costs
554    /// several times what the same comparison over a fixed width one costs. Everything finer than
555    /// that is below the noise of what the window is measuring anyway.
556    fn weight(&self, index: usize) -> f64 {
557        match &self.steps[index] {
558            // Read straight out of the chunk at the point an operand is wanted, so there is no step
559            // to run and nothing to charge for.
560            Step::Column(_) => 0.0,
561            // One vector built per chunk, however many rows the chunk has.
562            Step::Constant(_) => 0.25,
563            // The operands carry the cost of a connective, and they are steps of their own.
564            Step::Conjunction { .. } => 0.0,
565            Step::Cast { input, .. } => 2.0 * touching(&self.types[*input]),
566            Step::Compare { left, .. } => touching(&self.types[*left]),
567            // One hash and one probe a row, whatever the list holds, which is the point of it. It
568            // is dearer than a comparison and much cheaper than the chain of them it replaced.
569            Step::InSet { input, .. } => 2.0 * touching(&self.types[*input]),
570            Step::Function { start, len, .. } => {
571                let widest = self.operands[*start..*start + *len]
572                    .iter()
573                    .map(|&argument| touching(&self.types[argument]))
574                    .fold(1.0, f64::max);
575                4.0 * widest
576            }
577            // A branch per arm, each of which is a prepared expression of its own that this does
578            // not look inside. Charging for the arms alone understates it and says the right thing
579            // about the order, which is that a `CASE` is not what you want in front.
580            Step::Case { arms, .. } => 4.0 * arms.len() as f64,
581        }
582    }
583
584    /// One operand of a connective, over the rows it is still worth asking about.
585    ///
586    /// `begin` is the first step of the operand's subtree, which the caller knows because the steps
587    /// are in post order.
588    fn thread(
589        &self,
590        index: usize,
591        begin: usize,
592        chunk: &Chunk,
593        scratch: &mut Scratch,
594        live: Option<&Selection>,
595    ) -> Result<Selection> {
596        if matches!(self.steps[index], Step::Conjunction { .. }) {
597            return self.branches(index, begin, chunk, scratch, live);
598        }
599        for step in begin..index {
600            self.run_step(step, chunk, scratch)?;
601        }
602        if let Step::Compare { op, left, right, held } = &self.steps[index] {
603            let one = self.operand(*left, chunk, &scratch.slots)?;
604            let other = self.operand(*right, chunk, &scratch.slots)?;
605            let held = held.as_ref();
606            return match live {
607                // The first operand has every row in play, and asking the threaded kernel for that
608                // would be a pass over an identity selection the unthreaded one does not need.
609                None => Ok(selection(&compare_prepared(*op, one, other, held)?, chunk.len())),
610                Some(live) => refine_prepared(*op, one, other, live, held),
611            };
612        }
613        self.run_step(index, chunk, scratch)?;
614        let flags = self.operand(index, chunk, &scratch.slots)?;
615        match live {
616            None => Ok(selection(flags, chunk.len())),
617            Some(live) => refine_flags(flags, live),
618        }
619    }
620
621    /// Runs every step in order, filling the slots.
622    fn run(&self, chunk: &Chunk, scratch: &mut Scratch) -> Result<()> {
623        scratch.slots.clear();
624        scratch.slots.resize_with(self.steps.len(), || None);
625        for index in 0..self.steps.len() {
626            self.run_step(index, chunk, scratch)?;
627        }
628        Ok(())
629    }
630
631    /// Runs one step and empties the slot of every operand this was the last step to read.
632    fn run_step(&self, index: usize, chunk: &Chunk, scratch: &mut Scratch) -> Result<()> {
633        let produced = self.step(index, chunk, &scratch.slots)?;
634        scratch.slots[index] = produced;
635        let slots = &mut scratch.slots;
636        self.for_each_operand(index, |operand| {
637            if self.last_use[operand] == index {
638                slots[operand] = None;
639            }
640        });
641        Ok(())
642    }
643
644    /// Runs one step, given what the steps before it produced.
645    fn step(
646        &self,
647        index: usize,
648        chunk: &Chunk,
649        slots: &[Option<Vector>],
650    ) -> Result<Option<Vector>> {
651        let ty = &self.types[index];
652        let produced = match &self.steps[index] {
653            Step::Column(_) => None,
654            Step::Constant(value) => Some(Vector::constant(ty.clone(), value.clone(), chunk.len())),
655            Step::Cast { input, try_cast } => {
656                Some(cast(self.operand(*input, chunk, slots)?, ty, *try_cast)?)
657            }
658            Step::Compare { op, left, right, held } => Some(compare_prepared(
659                *op,
660                self.operand(*left, chunk, slots)?,
661                self.operand(*right, chunk, slots)?,
662                held.as_ref(),
663            )?),
664            Step::Conjunction { op, start, len } => {
665                Some(
666                    self.with_operands(*start, *len, chunk, slots, |children| {
667                        combine(*op, children)
668                    })?,
669                )
670            }
671            Step::Function { recipe, written, start, len } => {
672                Some(self.with_operands(*start, *len, chunk, slots, |args| {
673                    rudb_kernels::call_prepared(recipe, args, ty, Some(&|| written.clone()))
674                })?)
675            }
676            Step::InSet { input, members } => {
677                Some(in_set(self.operand(*input, chunk, slots)?, members, ty)?)
678            }
679            Step::Case { arms, otherwise } => {
680                Some(self.case(chunk, arms, otherwise.as_ref(), ty)?)
681            }
682        };
683        Ok(produced)
684    }
685
686    /// The vector a step produced, or the chunk's column if the step is a column reference.
687    fn operand<'v>(
688        &self,
689        index: usize,
690        chunk: &'v Chunk,
691        slots: &'v [Option<Vector>],
692    ) -> Result<&'v Vector> {
693        if let Step::Column(position) = self.steps[index] {
694            return chunk.column(position);
695        }
696        slots[index].as_ref().ok_or_else(|| missing(index))
697    }
698
699    /// Hands a kernel the references to an operand list, without allocating for the usual widths.
700    ///
701    /// One, two and three because those are what a bound tree is made of: every scalar function in
702    /// the catalog is unary or binary, a comparison is binary, and a conjunction is two or three
703    /// often enough to be worth a line. A stack array for those means a chain of eight additions
704    /// makes zero allocations for its operand lists over a chunk instead of eight, and eight
705    /// allocations a chunk at the rate a pipeline produces chunks is a real number rather than a
706    /// tidiness argument. Anything wider falls back to [`gather`](Self::gather), which is a `Vec`
707    /// of pointers and still moves no data.
708    fn with_operands<'v, T>(
709        &self,
710        start: usize,
711        len: usize,
712        chunk: &'v Chunk,
713        slots: &'v [Option<Vector>],
714        run: impl FnOnce(&[&'v Vector]) -> Result<T>,
715    ) -> Result<T> {
716        match self.operands[start..start + len] {
717            [a] => run(&[self.operand(a, chunk, slots)?]),
718            [a, b] => run(&[self.operand(a, chunk, slots)?, self.operand(b, chunk, slots)?]),
719            [a, b, c] => run(&[
720                self.operand(a, chunk, slots)?,
721                self.operand(b, chunk, slots)?,
722                self.operand(c, chunk, slots)?,
723            ]),
724            _ => {
725                let gathered = self.gather(start, len, chunk, slots)?;
726                run(&gathered)
727            }
728        }
729    }
730
731    /// References to an operand list, for a kernel that takes a slice of them.
732    ///
733    /// The `Vec` here is the allocation the module documentation names: it holds pointers rather
734    /// than vectors, so it is a dozen bytes an operand and no data moves.
735    fn gather<'v>(
736        &self,
737        start: usize,
738        len: usize,
739        chunk: &'v Chunk,
740        slots: &'v [Option<Vector>],
741    ) -> Result<Vec<&'v Vector>> {
742        let mut gathered = Vec::with_capacity(len);
743        for &operand in &self.operands[start..start + len] {
744            gathered.push(self.operand(operand, chunk, slots)?);
745        }
746        Ok(gathered)
747    }
748
749    /// A searched `CASE` over the rows no earlier arm claimed.
750    ///
751    /// The same shape [`evaluate`](crate::evaluate) has, because the thing that makes it that shape
752    /// is a correctness rule rather than a performance one: `CASE WHEN x <> 0 THEN 1 / x ELSE 0 END`
753    /// divides by zero on the rows the arm excludes if the arm is evaluated for them. What is left
754    /// of it after #57 is the same rule expressed as a selection rather than as a narrowed chunk,
755    /// with the answers scattered back instead of assembled out of a `Vec<Value>`.
756    fn case(
757        &self,
758        chunk: &Chunk,
759        arms: &[PreparedArm],
760        otherwise: Option<&Prepared>,
761        ty: &LogicalType,
762    ) -> Result<Vector> {
763        let mut answers = vec![Value::Null; chunk.len()];
764        let mut pending: Vec<usize> = (0..chunk.len()).collect();
765        for arm in arms {
766            if pending.is_empty() {
767                break;
768            }
769            let narrowed = narrow(chunk, &pending)?;
770            let mut scratch = arm.when.scratch();
771            let flags = arm.when.evaluate_one(&narrowed, &mut scratch)?;
772            let mut taken = Vec::new();
773            let mut still = Vec::new();
774            // row at a time: the scatter that replaces these three loops is #57, and this variant
775            // goes with it.
776            for (at, &row) in pending.iter().enumerate() {
777                if is_true(&flags.value_at(at)) {
778                    taken.push((at, row));
779                } else {
780                    still.push(row);
781                }
782            }
783            if !taken.is_empty() {
784                let positions: Vec<usize> = taken.iter().map(|&(at, _)| at).collect();
785                let matched = narrow(&narrowed, &positions)?;
786                let mut scratch = arm.then.scratch();
787                let results = arm.then.evaluate_one(&matched, &mut scratch)?;
788                // row at a time: the scatter this wants is #57, same as the loop above.
789                for (slot, &(_, row)) in taken.iter().enumerate() {
790                    answers[row] = results.value_at(slot);
791                }
792            }
793            pending = still;
794        }
795        if let Some(otherwise) = otherwise {
796            if !pending.is_empty() {
797                let narrowed = narrow(chunk, &pending)?;
798                let mut scratch = otherwise.scratch();
799                let results = otherwise.evaluate_one(&narrowed, &mut scratch)?;
800                // row at a time: the scatter this wants is #57, same as the two above.
801                for (slot, &row) in pending.iter().enumerate() {
802                    answers[row] = results.value_at(slot);
803                }
804            }
805        }
806        Vector::from_values(ty.clone(), &answers)
807    }
808
809    /// Flattens one expression, appending its steps and returning the index of its last one.
810    fn push(&mut self, plan: &Plan, expr: ExprRef, schema: &Schema) -> Result<usize> {
811        let ty = plan.expr_type(expr).clone();
812        let step = match *plan.expr(expr) {
813            Expr::Column(binding) => {
814                let position = schema.position_of(binding).ok_or_else(|| {
815                    Error::internal(format!(
816                        "column #{}.{} is not in the schema this operator was given",
817                        binding.table, binding.column
818                    ))
819                })?;
820                Step::Column(position)
821            }
822            Expr::Constant(reference) => Step::Constant(plan.value(reference).clone()),
823            Expr::Cast { input, try_cast } => {
824                Step::Cast { input: self.push(plan, input, schema)?, try_cast }
825            }
826            Expr::Compare { op, left, right } => {
827                let left = self.push(plan, left, schema)?;
828                let right = self.push(plan, right, schema)?;
829                Step::Compare { op: comparison(op), left, right, held: self.held(left, right) }
830            }
831            Expr::Conjunction { op, children } => {
832                let list = plan.expr_list(children).to_vec();
833                match self.membership(plan, connective(op), &list, schema)? {
834                    Some(step) => step,
835                    None => {
836                        let (start, len) = self.push_list(plan, &list, schema)?;
837                        Step::Conjunction { op: connective(op), start, len }
838                    }
839                }
840            }
841            Expr::Function { name, args } => {
842                let (start, len) = self.push_list(plan, plan.expr_list(args), schema)?;
843                Step::Function {
844                    recipe: Recipe::new(plan.string(name), &self.literals(start, len)),
845                    written: written(plan, expr, schema),
846                    start,
847                    len,
848                }
849            }
850            Expr::Aggregate { name, .. } => {
851                return Err(Error::internal(format!(
852                    "the {} aggregate was evaluated as an ordinary expression",
853                    plan.string(name)
854                )));
855            }
856            Expr::Case { arms, otherwise } => {
857                let mut prepared = Vec::new();
858                for &arm in plan.arm_list(arms) {
859                    prepared.push(PreparedArm {
860                        when: Self::one(plan, arm.when, schema)?,
861                        then: Self::one(plan, arm.then, schema)?,
862                    });
863                }
864                let otherwise = match otherwise {
865                    Some(otherwise) => Some(Self::one(plan, otherwise, schema)?),
866                    None => None,
867                };
868                Step::Case { arms: prepared, otherwise }
869            }
870        };
871        self.steps.push(step);
872        self.types.push(ty);
873        Ok(self.steps.len() - 1)
874    }
875
876    /// Flattens a list of expressions and records where its operand run starts and how long it is.
877    ///
878    /// The operand run is written after every child has been flattened rather than as they go,
879    /// because a child that is itself a list would otherwise interleave its run with this one.
880    fn push_list(
881        &mut self,
882        plan: &Plan,
883        exprs: &[ExprRef],
884        schema: &Schema,
885    ) -> Result<(usize, usize)> {
886        let mut indices = Vec::with_capacity(exprs.len());
887        for &expr in exprs {
888            indices.push(self.push(plan, expr, schema)?);
889        }
890        let start = self.operands.len();
891        let len = indices.len();
892        self.operands.extend(indices);
893        Ok((start, len))
894    }
895
896    /// This connective folded back into the `IN` the user wrote, or `None` when it is not one.
897    ///
898    /// What the binder writes for `x IN (1, 2, 3)` is `x = 1 OR x = 2 OR x = 3`, and for
899    /// `x NOT IN (1, 2, 3)` it is `x <> 1 AND x <> 2 AND x <> 3`. So the shape looked for is every
900    /// child a comparison of the one direction, every left the same expression, and every right a
901    /// literal. Anything else is left alone, which covers the `OR` that was written as an `OR` and
902    /// the one where an `IN` has been flattened together with another branch. The second is a fold
903    /// this could make and does not, and it is worth having later out of a query that wants it
904    /// rather than now out of a guess.
905    ///
906    /// This runs before the children are pushed, and that is the whole reason it is here rather than
907    /// as a pass over the finished array. A step that nothing reads is still a step the walk runs,
908    /// because the walk over a subtree is a range and not a graph, so folding after the fact would
909    /// leave every equality in place and running.
910    fn membership(
911        &mut self,
912        plan: &Plan,
913        op: Connective,
914        children: &[ExprRef],
915        schema: &Schema,
916    ) -> Result<Option<Step>> {
917        let wanted = match op {
918            Connective::Or => CompareOp::Equal,
919            Connective::And => CompareOp::NotEqual,
920        };
921        let mut subject: Option<ExprRef> = None;
922        let mut values = Vec::with_capacity(children.len());
923        for &child in children {
924            let Expr::Compare { op: found, left, right } = *plan.expr(child) else {
925                return Ok(None);
926            };
927            if found != wanted || !same(plan, *subject.get_or_insert(left), left) {
928                return Ok(None);
929            }
930            let Expr::Constant(reference) = *plan.expr(right) else {
931                return Ok(None);
932            };
933            values.push(plan.value(reference).clone());
934        }
935        let (Some(subject), Some(members)) = (subject, Members::of(&values, op == Connective::And))
936        else {
937            return Ok(None);
938        };
939        Ok(Some(Step::InSet { input: self.push(plan, subject, schema)?, members }))
940    }
941
942    /// The literal side of a comparison, in the one row column the comparison reads it through.
943    ///
944    /// The right side first, because that is the side the binder puts a literal on and the side the
945    /// loops are written for. Two literals is a comparison the optimizer folded, and if it did not
946    /// then the kernel answers it once for the whole vector and never reads either column, so
947    /// neither side is built here.
948    fn held(&self, left: usize, right: usize) -> Option<Held> {
949        let (at, other) = match (&self.steps[left], &self.steps[right]) {
950            (Step::Constant(_), Step::Constant(_)) => return None,
951            (_, Step::Constant(value)) => (right, value),
952            (Step::Constant(value), _) => (left, value),
953            _ => return None,
954        };
955        Held::of(&self.types[at], other)
956    }
957
958    /// The literal behind each argument in a run of the operand list, and `None` for an argument
959    /// that is anything else.
960    ///
961    /// This is what a [`Recipe`] hoists from. An argument that is a literal in the plan arrives as a
962    /// constant vector holding exactly this value on every chunk, so what a kernel reads here is
963    /// what it would have read per chunk. An argument that is a cast of a literal reads as `None`,
964    /// which is a call the kernel decides per chunk as it always did, and the optimizer folds most
965    /// of those before the plan gets here anyway.
966    fn literals(&self, start: usize, len: usize) -> Vec<Option<Value>> {
967        self.operands[start..start + len]
968            .iter()
969            .map(|&operand| match &self.steps[operand] {
970                Step::Constant(value) => Some(value.clone()),
971                _ => None,
972            })
973            .collect()
974    }
975}
976
977/// Whether two expressions of one plan are the same expression, written once or written twice.
978///
979/// The binder binds the subject of an `IN` once and points every comparison it writes at that one
980/// reference, so the answer is almost always the first line. A plan that has been through a rewrite,
981/// and a plan read back from its own text, hold two copies of the same tree instead, and for the
982/// fold in [`Prepared::membership`] those are the same expression.
983///
984/// The four shapes handled are what an `IN` is written over: a column, a literal, a cast of either,
985/// and a call, which is TPC-H query 22 asking whether the first two digits of a phone number are in
986/// a list. Anything else answers no, which costs a fold that could have happened rather than a wrong
987/// one. The walk is bounded by the size of the subject and a subject is small.
988fn same(plan: &Plan, left: ExprRef, right: ExprRef) -> bool {
989    if left == right {
990        return true;
991    }
992    if plan.expr_type(left) != plan.expr_type(right) {
993        return false;
994    }
995    match (plan.expr(left), plan.expr(right)) {
996        (Expr::Column(one), Expr::Column(other)) => one == other,
997        (Expr::Constant(one), Expr::Constant(other)) => plan.value(*one) == plan.value(*other),
998        (
999            Expr::Cast { input: one, try_cast: first },
1000            Expr::Cast { input: other, try_cast: second },
1001        ) => first == second && same(plan, *one, *other),
1002        (
1003            Expr::Function { name: one, args: first },
1004            Expr::Function { name: other, args: second },
1005        ) => {
1006            let (first, second) = (plan.expr_list(*first), plan.expr_list(*second));
1007            plan.string(*one) == plan.string(*other)
1008                && first.len() == second.len()
1009                && first.iter().zip(second).all(|(&one, &other)| same(plan, one, other))
1010        }
1011        _ => false,
1012    }
1013}
1014
1015/// What touching a value of this type costs, against a fixed width one as the unit.
1016///
1017/// A variable length value is a pointer to follow and a length that is not the same twice, and a
1018/// nested one is that per element. Four is not measured, and what it has to be is large enough that
1019/// the ordering puts a fixed width comparison in front of a string one and small enough that it does
1020/// not put one in front of a string comparison that rejects every row.
1021fn touching(ty: &LogicalType) -> f64 {
1022    match ty.physical() {
1023        PhysicalType::Varlen => 4.0,
1024        PhysicalType::List | PhysicalType::Array | PhysicalType::Struct => 8.0,
1025        _ => 1.0,
1026    }
1027}
1028
1029/// The error for a slot that should have held something and did not.
1030///
1031/// This cannot happen while the array is in post order, since every operand's index is smaller than
1032/// the index of the step using it and every step runs in order. It is an error rather than a panic
1033/// because the property it depends on is a property of [`Prepared::push`], and the day somebody
1034/// writes a pass that reorders the array is the day it stops holding.
1035fn missing(index: usize) -> Error {
1036    Error::internal(format!("step {index} was used as an operand before it produced anything"))
1037}
1038
1039/// The chunk cut down to the given rows.
1040///
1041/// The reason `CASE` is written with this rather than by evaluating every arm over the whole chunk
1042/// and picking afterwards. `CASE WHEN x <> 0 THEN 1 // x ELSE 0 END` divides by zero on the rows the
1043/// arm does not apply to if the arm is evaluated for them, and a `CASE` that raises on a row it was
1044/// written to exclude is the classic wrong answer this shape prevents.
1045pub(crate) fn narrow(chunk: &Chunk, rows: &[usize]) -> Result<Chunk> {
1046    let mut selection = Selection::with_capacity(rows.len());
1047    for &row in rows {
1048        selection.push(row);
1049    }
1050    chunk.clone().select(&selection)
1051}
1052
1053/// The kernels' comparison for the plan's.
1054///
1055/// A translation rather than one shared enum, because the kernels are rank 3 and the plan is rank
1056/// 9. This function is the whole of what that separation costs.
1057pub(crate) fn comparison(op: CompareOp) -> Comparison {
1058    match op {
1059        CompareOp::Equal => Comparison::Equal,
1060        CompareOp::NotEqual => Comparison::NotEqual,
1061        CompareOp::Less => Comparison::Less,
1062        CompareOp::LessOrEqual => Comparison::LessOrEqual,
1063        CompareOp::Greater => Comparison::Greater,
1064        CompareOp::GreaterOrEqual => Comparison::GreaterOrEqual,
1065        CompareOp::DistinctFrom => Comparison::DistinctFrom,
1066        CompareOp::NotDistinctFrom => Comparison::NotDistinctFrom,
1067    }
1068}
1069
1070/// The kernels' connective for the plan's.
1071pub(crate) fn connective(op: ConjunctionOp) -> Connective {
1072    match op {
1073        ConjunctionOp::And => Connective::And,
1074        ConjunctionOp::Or => Connective::Or,
1075    }
1076}
1077
1078#[cfg(test)]
1079mod tests {
1080    use rudb_common::{Field, LogicalType, Value};
1081    use rudb_kernels::is_true;
1082    use rudb_plan::{ExprRef, Node, Plan};
1083    use rudb_vector::{Chunk, Selection, Vector};
1084
1085    use super::{Prepared, narrow};
1086    use crate::expr::evaluate;
1087    use crate::schema::Schema;
1088
1089    /// Two columns with a null in each, because every disagreement between these two evaluators
1090    /// that is worth finding is a disagreement about which rows are null.
1091    fn input() -> (Schema, Chunk) {
1092        let schema = Schema::numbered(
1093            vec![Field::new("x", LogicalType::Integer), Field::new("s", LogicalType::Varchar)],
1094            0,
1095        );
1096        let x = Vector::from_values(
1097            LogicalType::Integer,
1098            &[Value::Integer(3), Value::Integer(1), Value::Null, Value::Integer(2)],
1099        )
1100        .expect("four integers");
1101        let s = Vector::from_values(
1102            LogicalType::Varchar,
1103            &[
1104                Value::Varchar("a".to_string()),
1105                Value::Null,
1106                Value::Varchar("c".to_string()),
1107                Value::Varchar("a".to_string()),
1108            ],
1109        )
1110        .expect("four strings");
1111        (schema, Chunk::new(vec![x, s]).expect("two columns of four rows"))
1112    }
1113
1114    /// The expressions of a projection written in the plan's textual form, over the two columns
1115    /// [`input`] produces.
1116    ///
1117    /// Going through the text rather than the arena builders for the reason the other test module
1118    /// gives: a test that says what it evaluates in the notation a plan dump uses is a test whose
1119    /// failure can be pasted into a plan and vice versa.
1120    fn projection(exprs: &str) -> (Plan, Vec<ExprRef>) {
1121        let text =
1122            format!("Project #1 [{exprs}]\n  Get memory.main.t AS t #0 [x::INTEGER, s::VARCHAR]");
1123        let plan = Plan::parse(&text).expect("a well formed plan");
1124        let Node::Project { exprs, .. } = *plan.node(plan.root()) else {
1125            panic!("the root of that text is a projection");
1126        };
1127        let list = plan.expr_list(exprs).to_vec();
1128        (plan, list)
1129    }
1130
1131    /// Every expression shape, evaluated both ways over the same chunk.
1132    ///
1133    /// This is the agreement the module documentation claims and it is the only thing that makes
1134    /// the prepared form safe to put in front of the tree walk. The generated well typed trees the
1135    /// test gate of #57 asks for are a wider version of this and are worth building once the
1136    /// selection threaded shapes exist to disagree about.
1137    fn agrees(exprs: &str) {
1138        let (schema, chunk) = input();
1139        let (plan, list) = projection(exprs);
1140        let prepared = Prepared::new(&plan, &list, &schema).expect("the expressions resolve");
1141        let mut scratch = prepared.scratch();
1142        let mut fast = Vec::new();
1143        prepared.evaluate(&chunk, &mut scratch, &mut fast).expect("the prepared form runs");
1144        for (at, &expr) in list.iter().enumerate() {
1145            let slow = evaluate(&plan, expr, &schema, &chunk).expect("the tree walk runs");
1146            for row in 0..chunk.len() {
1147                assert_eq!(
1148                    fast[at].value_at(row),
1149                    slow.value_at(row),
1150                    "expression {at} of `{exprs}` at row {row}"
1151                );
1152            }
1153        }
1154    }
1155
1156    #[test]
1157    fn a_column_reference_agrees() {
1158        agrees("#0.0::INTEGER AS a, #0.1::VARCHAR AS b");
1159    }
1160
1161    #[test]
1162    fn a_constant_agrees() {
1163        agrees("7::INTEGER AS a, NULL::INTEGER AS b");
1164    }
1165
1166    #[test]
1167    fn a_cast_agrees() {
1168        agrees("CAST(#0.0::INTEGER)::BIGINT AS a, CAST(#0.0::INTEGER)::VARCHAR AS b");
1169    }
1170
1171    #[test]
1172    fn a_comparison_agrees() {
1173        agrees("(#0.0::INTEGER > 1::INTEGER)::BOOLEAN AS a");
1174    }
1175
1176    #[test]
1177    fn a_conjunction_agrees() {
1178        agrees(
1179            "((#0.0::INTEGER > 1::INTEGER)::BOOLEAN AND (#0.0::INTEGER < 3::INTEGER)::BOOLEAN)\
1180             ::BOOLEAN AS a",
1181        );
1182    }
1183
1184    #[test]
1185    fn a_function_agrees() {
1186        agrees("\"+\"(#0.0::INTEGER, 1::INTEGER)::INTEGER AS a");
1187    }
1188
1189    /// The two evaluators quote the same expression when a divisor is zero. Per #262.
1190    ///
1191    /// This is the one message in the engine that depends on how an expression is written rather
1192    /// than on what it computes, and the two evaluators render it at different times: the prepared
1193    /// form when the pipeline is built, the tree walk on the row that fails. Same renderer, so the
1194    /// same sentence, and this is what says so.
1195    #[test]
1196    fn both_evaluators_quote_the_same_expression_when_a_divisor_is_zero() {
1197        let (schema, chunk) = input();
1198        let (plan, list) = projection("\"//\"(#0.0::INTEGER, 0::INTEGER)::INTEGER AS a");
1199        let prepared = Prepared::new(&plan, &list, &schema).expect("the expression resolves");
1200        let mut scratch = prepared.scratch();
1201        let mut out = Vec::new();
1202        let fast = prepared.evaluate(&chunk, &mut scratch, &mut out).expect_err("divides by zero");
1203        let slow = evaluate(&plan, list[0], &schema, &chunk).expect_err("divides by zero");
1204        assert_eq!(fast.message(), slow.message());
1205        assert!(fast.message().starts_with("Division by zero in expression (x // 0)."), "{fast}");
1206    }
1207
1208    #[test]
1209    fn a_case_agrees() {
1210        agrees(
1211            "CASE WHEN (#0.0::INTEGER > 1::INTEGER)::BOOLEAN THEN 10::INTEGER \
1212             ELSE 20::INTEGER END::INTEGER AS a",
1213        );
1214    }
1215
1216    /// The same expression twice, which is where the tree walk copies the column twice and this
1217    /// does not, and the answers still have to be identical.
1218    #[test]
1219    fn a_column_mentioned_three_times_agrees() {
1220        agrees("\"+\"(\"+\"(#0.0::INTEGER, #0.0::INTEGER)::INTEGER, #0.0::INTEGER)::INTEGER AS a");
1221    }
1222
1223    /// The intermediates of a chain are not all held to the end of it.
1224    ///
1225    /// This is the whole difference between the prepared form being faster than the tree walk on a
1226    /// deep chain and being slower than it, and it is a property of the slot array rather than of
1227    /// any answer, so it is asserted here rather than left to the benchmark to catch.
1228    #[test]
1229    fn a_chain_holds_one_intermediate_at_a_time() {
1230        let (schema, chunk) = input();
1231        let mut expr = "#0.0::INTEGER".to_string();
1232        for _ in 0..8 {
1233            expr = format!("\"+\"({expr}, 1::INTEGER)::INTEGER");
1234        }
1235        let (plan, list) = projection(&format!("{expr} AS a"));
1236        let prepared = Prepared::new(&plan, &list, &schema).expect("the chain resolves");
1237        let mut scratch = prepared.scratch();
1238        prepared.run(&chunk, &mut scratch).expect("the chain runs");
1239        let live = scratch.slots.iter().filter(|slot| slot.is_some()).count();
1240        assert_eq!(live, 1, "a chain that has run should be holding its answer and nothing else");
1241    }
1242
1243    /// The rows a threaded filter keeps are the rows the tree walk says the predicate is true for.
1244    ///
1245    /// Every threaded conjunct is a chance to disagree with the unthreaded answer about a null,
1246    /// about a row an earlier conjunct had already dropped, or about a chunk nothing survives, and
1247    /// the answer is a set of row numbers rather than a vector, so this is checked against the tree
1248    /// walk read a row at a time rather than against the prepared form it is part of.
1249    fn filters(predicate: &str) {
1250        let (schema, chunk) = input();
1251        let (plan, list) = projection(&format!("{predicate} AS p"));
1252        let prepared = Prepared::new(&plan, &list, &schema).expect("the predicate resolves");
1253        let mut scratch = prepared.scratch();
1254        let threaded = prepared.evaluate_filter(&chunk, &mut scratch).expect("the filter runs");
1255        let flags = evaluate(&plan, list[0], &schema, &chunk).expect("the tree walk runs");
1256        let expected = Selection::from_predicate(chunk.len(), |row| is_true(&flags.value_at(row)));
1257        assert_eq!(threaded, expected, "`{predicate}`");
1258        // And running it again over the same scratch is the same answer, because a pipeline calls
1259        // this once a chunk and a slot left behind by the conjunct before would show up here.
1260        let again = prepared.evaluate_filter(&chunk, &mut scratch).expect("the filter runs again");
1261        assert_eq!(again, expected, "`{predicate}` a second time");
1262    }
1263
1264    /// A predicate with no `AND` in it is not threaded and has to keep saying the same thing.
1265    #[test]
1266    fn a_single_comparison_filters_the_same_rows() {
1267        filters("(#0.0::INTEGER > 1::INTEGER)::BOOLEAN");
1268        filters("(#0.1::VARCHAR = 'a'::VARCHAR)::BOOLEAN");
1269        filters("(#0.0::INTEGER IS NOT DISTINCT FROM NULL::INTEGER)::BOOLEAN");
1270    }
1271
1272    #[test]
1273    fn a_chain_of_conjuncts_keeps_what_all_of_them_keep() {
1274        filters(
1275            "((#0.0::INTEGER > 1::INTEGER)::BOOLEAN AND (#0.0::INTEGER < 3::INTEGER)::BOOLEAN)\
1276             ::BOOLEAN",
1277        );
1278        filters(
1279            "((#0.0::INTEGER >= 1::INTEGER)::BOOLEAN AND (#0.0::INTEGER <= 3::INTEGER)::BOOLEAN \
1280             AND (#0.1::VARCHAR = 'a'::VARCHAR)::BOOLEAN AND (#0.0::INTEGER <> 2::INTEGER)\
1281             ::BOOLEAN)::BOOLEAN",
1282        );
1283    }
1284
1285    /// A conjunct that rejects every row, in front of one that would have kept some. The rows are
1286    /// the same either way and the point of the shape is that the second conjunct never runs.
1287    #[test]
1288    fn a_conjunct_that_keeps_nothing_ends_the_predicate() {
1289        filters(
1290            "((#0.0::INTEGER > 9::INTEGER)::BOOLEAN AND (#0.0::INTEGER < 9::INTEGER)::BOOLEAN)\
1291             ::BOOLEAN",
1292        );
1293    }
1294
1295    /// A conjunct whose operands are computed rather than read, which is the shape where the
1296    /// comparison is threaded and the arithmetic under it is not.
1297    #[test]
1298    fn a_conjunct_over_a_computed_operand_keeps_the_same_rows() {
1299        filters(
1300            "((#0.0::INTEGER > 1::INTEGER)::BOOLEAN AND \
1301             (\"+\"(#0.0::INTEGER, 1::INTEGER)::INTEGER < 4::INTEGER)::BOOLEAN)::BOOLEAN",
1302        );
1303    }
1304
1305    /// A conjunct that is not a comparison at all, which is the one that goes through the flag
1306    /// kernel rather than the comparison kernel.
1307    #[test]
1308    fn a_conjunct_that_is_not_a_comparison_is_threaded_too() {
1309        filters(
1310            "((#0.0::INTEGER > 1::INTEGER)::BOOLEAN AND ((#0.1::VARCHAR = 'a'::VARCHAR)::BOOLEAN \
1311             OR (#0.0::INTEGER = 1::INTEGER)::BOOLEAN)::BOOLEAN)::BOOLEAN",
1312        );
1313        filters(
1314            "(((#0.1::VARCHAR = 'c'::VARCHAR)::BOOLEAN OR (#0.0::INTEGER = 3::INTEGER)::BOOLEAN)\
1315             ::BOOLEAN AND (#0.0::INTEGER <> 1::INTEGER)::BOOLEAN)::BOOLEAN",
1316        );
1317    }
1318
1319    /// An `OR` at the top threads the complement: the second branch only sees the rows the first
1320    /// one did not accept, and the rows it accepts are added to them rather than replacing them.
1321    ///
1322    /// The input has a row where the first branch is true, one where the second is, one where both
1323    /// are false and one where the first is null and the second is true, which is the row that says
1324    /// whether the complement was taken over "not true" or over "false".
1325    #[test]
1326    fn an_or_at_the_top_threads_the_complement() {
1327        filters(
1328            "((#0.0::INTEGER > 2::INTEGER)::BOOLEAN OR (#0.1::VARCHAR = 'c'::VARCHAR)::BOOLEAN)\
1329             ::BOOLEAN",
1330        );
1331        filters(
1332            "((#0.0::INTEGER = 1::INTEGER)::BOOLEAN OR (#0.1::VARCHAR = 'a'::VARCHAR)::BOOLEAN \
1333             OR (#0.0::INTEGER > 2::INTEGER)::BOOLEAN)::BOOLEAN",
1334        );
1335    }
1336
1337    /// A branch that accepts every row, in front of one that would have accepted none. The rows are
1338    /// the same either way and the point of the shape is that the second branch never runs.
1339    #[test]
1340    fn a_branch_that_keeps_everything_ends_the_predicate() {
1341        filters(
1342            "((#0.0::INTEGER IS NOT DISTINCT FROM #0.0::INTEGER)::BOOLEAN OR \
1343             (#0.0::INTEGER > 9::INTEGER)::BOOLEAN)::BOOLEAN",
1344        );
1345    }
1346
1347    /// The branches after one that has accepted every row really are skipped.
1348    ///
1349    /// Every other test here says the threaded answer matches the unthreaded one, which it would
1350    /// even if nothing were threaded at all. This one puts a division by zero behind a branch that
1351    /// accepts everything, so the predicate raises if the second branch runs and does not if the
1352    /// walk stopped where it was supposed to.
1353    #[test]
1354    fn a_branch_behind_one_that_accepted_every_row_does_not_run() {
1355        let (schema, chunk) = input();
1356        let predicate = "((#0.0::INTEGER IS NOT DISTINCT FROM #0.0::INTEGER)::BOOLEAN OR \
1357                         (\"//\"(#0.0::INTEGER, 0::INTEGER)::INTEGER > 0::INTEGER)::BOOLEAN)\
1358                         ::BOOLEAN";
1359        let (plan, list) = projection(&format!("{predicate} AS p"));
1360        let prepared = Prepared::new(&plan, &list, &schema).expect("the predicate resolves");
1361        let mut scratch = prepared.scratch();
1362        let kept =
1363            prepared.evaluate_filter(&chunk, &mut scratch).expect("the second branch never runs");
1364        assert_eq!(kept, Selection::identity(chunk.len()));
1365        // And the same predicate evaluated as an expression does divide by zero, which is what says
1366        // the test is testing the threading rather than a predicate that happens not to raise.
1367        evaluate(&plan, list[0], &schema, &chunk).expect_err("the tree walk divides by zero");
1368    }
1369
1370    /// The conjunct that rejects the most rows ends up in front of the one that rejects none.
1371    ///
1372    /// The predicate is written the wrong way round on purpose. The plan order costs two passes a
1373    /// chunk where one would do, and after a chunk of watching it the filter runs the selective one
1374    /// first and the other one stops running at all.
1375    #[test]
1376    fn a_filter_learns_which_conjunct_to_run_first() {
1377        let (schema, chunk) = input();
1378        let predicate = "((#0.0::INTEGER > 0::INTEGER)::BOOLEAN AND (#0.0::INTEGER > 9::INTEGER)\
1379                         ::BOOLEAN)::BOOLEAN";
1380        let (plan, list) = projection(&format!("{predicate} AS p"));
1381        let prepared = Prepared::new(&plan, &list, &schema).expect("the predicate resolves");
1382        let mut scratch = prepared.scratch();
1383        let root = prepared.roots[0];
1384        assert_eq!(scratch.order(root), None, "nothing has run yet");
1385        let kept = prepared.evaluate_filter(&chunk, &mut scratch).expect("the filter runs");
1386        assert!(kept.is_empty());
1387        assert_eq!(scratch.order(root), Some(&[1, 0][..]), "the second conjunct rejects the most");
1388        // And it stays there, because the conjunct that now runs first empties the selection and
1389        // the one behind it keeps the history it already had rather than losing it.
1390        let kept = prepared.evaluate_filter(&chunk, &mut scratch).expect("the filter runs again");
1391        assert!(kept.is_empty());
1392        assert_eq!(scratch.order(root), Some(&[1, 0][..]));
1393    }
1394
1395    /// Whatever order it settles on, the rows are the rows.
1396    ///
1397    /// Run for longer than the window is wide, because an order that changes halfway through a scan
1398    /// is the shape where a walk that got the subtree bookkeeping wrong would start reading the
1399    /// wrong steps, and the first chunk would not show it.
1400    #[test]
1401    fn reordering_never_changes_which_rows_survive() {
1402        let (schema, chunk) = input();
1403        let predicate = "((#0.0::INTEGER >= 1::INTEGER)::BOOLEAN AND \
1404                         (\"+\"(#0.0::INTEGER, 1::INTEGER)::INTEGER < 4::INTEGER)::BOOLEAN AND \
1405                         (#0.1::VARCHAR = 'a'::VARCHAR)::BOOLEAN)::BOOLEAN";
1406        let (plan, list) = projection(&format!("{predicate} AS p"));
1407        let prepared = Prepared::new(&plan, &list, &schema).expect("the predicate resolves");
1408        let mut scratch = prepared.scratch();
1409        let flags = evaluate(&plan, list[0], &schema, &chunk).expect("the tree walk runs");
1410        let expected = Selection::from_predicate(chunk.len(), |row| is_true(&flags.value_at(row)));
1411        for round in 0..40 {
1412            let kept = prepared.evaluate_filter(&chunk, &mut scratch).expect("the filter runs");
1413            assert_eq!(kept, expected, "round {round}");
1414        }
1415    }
1416
1417    /// A nested connective is threaded rather than evaluated into flags.
1418    ///
1419    /// The inner `AND` keeps nothing, so its second conjunct is never reached and the division by
1420    /// zero in it never happens. Evaluating the branch as an expression and narrowing the flags
1421    /// afterwards, which is what an operand that is not a connective still does, would have run it.
1422    #[test]
1423    fn a_nested_connective_stops_where_the_outer_one_would() {
1424        let (schema, chunk) = input();
1425        let predicate = "((#0.0::INTEGER > 9::INTEGER)::BOOLEAN OR ((#0.0::INTEGER > 9::INTEGER)\
1426                         ::BOOLEAN AND (\"//\"(#0.0::INTEGER, 0::INTEGER)::INTEGER > 0::INTEGER)\
1427                         ::BOOLEAN)::BOOLEAN)::BOOLEAN";
1428        let (plan, list) = projection(&format!("{predicate} AS p"));
1429        let prepared = Prepared::new(&plan, &list, &schema).expect("the predicate resolves");
1430        let mut scratch = prepared.scratch();
1431        let kept =
1432            prepared.evaluate_filter(&chunk, &mut scratch).expect("the division never happens");
1433        assert!(kept.is_empty());
1434        evaluate(&plan, list[0], &schema, &chunk).expect_err("the tree walk divides by zero");
1435    }
1436
1437    /// A branch that is not a comparison, which is the one that goes through the flag kernel.
1438    #[test]
1439    fn an_or_branch_that_is_not_a_comparison_is_threaded_too() {
1440        filters(
1441            "((#0.0::INTEGER > 2::INTEGER)::BOOLEAN OR \
1442             \"~~\"(#0.1::VARCHAR, 'a%'::VARCHAR)::BOOLEAN)::BOOLEAN",
1443        );
1444        filters(
1445            "(\"~~\"(#0.1::VARCHAR, 'c%'::VARCHAR)::BOOLEAN OR (#0.0::INTEGER = 1::INTEGER)\
1446             ::BOOLEAN)::BOOLEAN",
1447        );
1448    }
1449
1450    /// A connective inside a connective, which recurses rather than falling back to flags.
1451    ///
1452    /// Both nestings, because the two carry opposite things: an `AND` under an `OR` starts from the
1453    /// rows no branch has accepted, and an `OR` under an `AND` starts from the rows every conjunct
1454    /// has kept, and getting either one backwards is a wrong set of rows.
1455    #[test]
1456    fn a_connective_inside_a_connective_threads_both_ways() {
1457        filters(
1458            "(((#0.0::INTEGER >= 2::INTEGER)::BOOLEAN AND (#0.1::VARCHAR = 'a'::VARCHAR)::BOOLEAN)\
1459             ::BOOLEAN OR ((#0.0::INTEGER < 2::INTEGER)::BOOLEAN AND (#0.1::VARCHAR <> 'c'\
1460             ::VARCHAR)::BOOLEAN)::BOOLEAN)::BOOLEAN",
1461        );
1462        filters(
1463            "(((#0.1::VARCHAR = 'c'::VARCHAR)::BOOLEAN OR (#0.0::INTEGER = 3::INTEGER)::BOOLEAN)\
1464             ::BOOLEAN AND ((#0.0::INTEGER <> 1::INTEGER)::BOOLEAN OR (#0.1::VARCHAR = 'a'\
1465             ::VARCHAR)::BOOLEAN)::BOOLEAN)::BOOLEAN",
1466        );
1467        // Three deep, since two levels is where an off by one in the subtree bookkeeping can still
1468        // be hidden by the ranges lining up.
1469        filters(
1470            "((#0.0::INTEGER > 9::INTEGER)::BOOLEAN OR ((#0.0::INTEGER >= 1::INTEGER)::BOOLEAN \
1471             AND ((#0.1::VARCHAR = 'a'::VARCHAR)::BOOLEAN OR (#0.0::INTEGER = 1::INTEGER)\
1472             ::BOOLEAN)::BOOLEAN)::BOOLEAN)::BOOLEAN",
1473        );
1474    }
1475
1476    /// A predicate where one side is null and the other is true, in both orders. `OR` is true there
1477    /// and a complement taken over the rows a branch rejected rather than the rows it accepted
1478    /// would drop the row, which is the one way this can be wrong and is not a wrong vector but a
1479    /// missing row.
1480    #[test]
1481    fn a_null_branch_beside_a_true_one_keeps_the_row() {
1482        filters(
1483            "((#0.0::INTEGER > 2::INTEGER)::BOOLEAN OR (#0.1::VARCHAR = 'c'::VARCHAR)::BOOLEAN \
1484             OR (#0.0::INTEGER IS NOT DISTINCT FROM NULL::INTEGER)::BOOLEAN)::BOOLEAN",
1485        );
1486        filters(
1487            "((#0.1::VARCHAR > 'b'::VARCHAR)::BOOLEAN OR (#0.0::INTEGER = 1::INTEGER)::BOOLEAN)\
1488             ::BOOLEAN",
1489        );
1490    }
1491
1492    /// A filter over a chunk that has already been narrowed, which is what a second filter in a
1493    /// pipeline sees and is the form pair the threaded kernels have to handle rather than fall
1494    /// through on.
1495    #[test]
1496    fn a_filter_over_a_selected_chunk_keeps_the_same_rows() {
1497        let (schema, chunk) = input();
1498        let predicate = "((#0.0::INTEGER >= 1::INTEGER)::BOOLEAN AND \
1499                         (#0.1::VARCHAR = 'a'::VARCHAR)::BOOLEAN)::BOOLEAN";
1500        let (plan, list) = projection(&format!("{predicate} AS p"));
1501        let prepared = Prepared::new(&plan, &list, &schema).expect("the predicate resolves");
1502        let mut scratch = prepared.scratch();
1503        let narrowed = narrow(&chunk, &[0, 3]).expect("two of the four rows");
1504        let threaded = prepared.evaluate_filter(&narrowed, &mut scratch).expect("the filter runs");
1505        let flags = evaluate(&plan, list[0], &schema, &narrowed).expect("the tree walk runs");
1506        let expected =
1507            Selection::from_predicate(narrowed.len(), |row| is_true(&flags.value_at(row)));
1508        assert_eq!(threaded, expected);
1509    }
1510
1511    /// Preparing is per pipeline and evaluating is per chunk, so the scratch has to survive being
1512    /// used again and give the same answer the second time.
1513    #[test]
1514    fn a_scratch_used_twice_gives_the_same_answer_twice() {
1515        let (schema, chunk) = input();
1516        let (plan, list) = projection("\"+\"(#0.0::INTEGER, 1::INTEGER)::INTEGER AS a");
1517        let prepared = Prepared::new(&plan, &list, &schema).expect("the expressions resolve");
1518        let mut scratch = prepared.scratch();
1519        let mut once = Vec::new();
1520        prepared.evaluate(&chunk, &mut scratch, &mut once).expect("the first chunk runs");
1521        let mut twice = Vec::new();
1522        prepared.evaluate(&chunk, &mut scratch, &mut twice).expect("the second chunk runs");
1523        assert_eq!(once, twice);
1524    }
1525
1526    /// A chunk shorter than the last one, because a scan's final chunk is that and a constant
1527    /// materialized to the wrong length would be an out of range read rather than a wrong answer.
1528    #[test]
1529    fn a_shorter_chunk_after_a_longer_one_is_evaluated_at_its_own_length() {
1530        let (schema, chunk) = input();
1531        let (plan, list) = projection("7::INTEGER AS a");
1532        let prepared = Prepared::new(&plan, &list, &schema).expect("the expressions resolve");
1533        let mut scratch = prepared.scratch();
1534        let mut full = Vec::new();
1535        prepared.evaluate(&chunk, &mut scratch, &mut full).expect("the full chunk runs");
1536        assert_eq!(full[0].len(), 4);
1537        let short = chunk
1538            .clone()
1539            .select(&{
1540                let mut selection = Selection::with_capacity(2);
1541                selection.push(0);
1542                selection.push(2);
1543                selection
1544            })
1545            .expect("two of the four rows");
1546        let mut cut = Vec::new();
1547        prepared.evaluate(&short, &mut scratch, &mut cut).expect("the short chunk runs");
1548        assert_eq!(cut[0].len(), 2);
1549    }
1550
1551    /// An aggregate is not an expression and saying so when the pipeline is built is better than
1552    /// saying it on the first chunk.
1553    #[test]
1554    fn an_aggregate_is_refused_when_it_is_prepared() {
1555        let (schema, _) = input();
1556        let text = "Aggregate #1 groups=[] aggregates=[sum(#0.0::INTEGER)::HUGEINT]\n  \
1557                    Get memory.main.t AS t #0 [x::INTEGER, s::VARCHAR]";
1558        let plan = Plan::parse(text).expect("a well formed plan");
1559        let Node::Aggregate { aggregates, .. } = *plan.node(plan.root()) else {
1560            panic!("the root of that text is an aggregate");
1561        };
1562        let list = plan.expr_list(aggregates).to_vec();
1563        let error = Prepared::new(&plan, &list, &schema).expect_err("sum is not a scalar");
1564        assert!(error.message().contains("sum"), "{error}");
1565    }
1566
1567    /// How many of an expression's function steps worked something out when it was prepared, and
1568    /// whether the answer it gives is still the tree walk's answer.
1569    ///
1570    /// The count is the point of the assertion, because an answer that moved would be a bug. The
1571    /// agreement is what says the answer did not move.
1572    fn prepares(expr: &str, lifted: usize) {
1573        let (schema, _) = input();
1574        let projected = format!("{expr} AS a");
1575        let (plan, list) = projection(&projected);
1576        let prepared = Prepared::new(&plan, &list, &schema).expect("the expression resolves");
1577        assert_eq!(prepared.hoisted(), lifted, "`{expr}`");
1578        agrees(&projected);
1579    }
1580
1581    /// A pattern the user wrote is compiled where the plan is, which is once.
1582    #[test]
1583    fn a_literal_pattern_is_compiled_when_the_pipeline_is_built() {
1584        prepares("\"~~\"(#0.1::VARCHAR, 'a%'::VARCHAR)::BOOLEAN", 1);
1585        prepares("\"~~*\"(#0.1::VARCHAR, '%A%'::VARCHAR)::BOOLEAN", 1);
1586    }
1587
1588    /// A regular expression, which is the one where the compiling is worth real time.
1589    ///
1590    /// ClickBench query 29 runs one pattern over a hundred million rows, which is a hundred thousand
1591    /// chunks, and before this each of those hundred thousand compiled the pattern again.
1592    #[test]
1593    fn a_regular_expression_is_compiled_when_the_pipeline_is_built() {
1594        prepares("\"regexp_matches\"(#0.1::VARCHAR, '^a'::VARCHAR)::BOOLEAN", 1);
1595        prepares("\"regexp_replace\"(#0.1::VARCHAR, 'a'::VARCHAR, 'b'::VARCHAR)::VARCHAR", 1);
1596    }
1597
1598    /// A pattern that is not a literal, which is legal SQL and is decided per chunk as it was.
1599    #[test]
1600    fn a_pattern_that_is_not_a_literal_is_left_to_the_chunk() {
1601        prepares("\"~~\"(#0.1::VARCHAR, #0.1::VARCHAR)::BOOLEAN", 0);
1602    }
1603
1604    /// A function with nothing to work out, which is almost all of them.
1605    #[test]
1606    fn a_function_with_no_prepare_step_prepares_nothing() {
1607        prepares("\"upper\"(#0.1::VARCHAR)::VARCHAR", 0);
1608    }
1609
1610    /// How many of an expression's steps are a folded `IN`, and whether the answer still agrees.
1611    fn folds(expr: &str, sets: usize) {
1612        let (schema, _) = input();
1613        let projected = format!("{expr} AS a");
1614        let (plan, list) = projection(&projected);
1615        let prepared = Prepared::new(&plan, &list, &schema).expect("the expression resolves");
1616        assert_eq!(prepared.sets(), sets, "`{expr}`");
1617        agrees(&projected);
1618    }
1619
1620    /// What the binder writes for `x IN (1, 3)`, folded back into one lookup.
1621    ///
1622    /// The test goes through the plan's text, where the three mentions of the column are three
1623    /// expressions rather than one, which is the case `same` exists for. A plan the binder built has
1624    /// one mention and takes the first line of it.
1625    #[test]
1626    fn an_in_list_becomes_one_lookup() {
1627        folds(
1628            "((#0.0::INTEGER = 1::INTEGER)::BOOLEAN OR (#0.0::INTEGER = 3::INTEGER)::BOOLEAN)\
1629             ::BOOLEAN",
1630            1,
1631        );
1632        folds(
1633            "((#0.1::VARCHAR = 'a'::VARCHAR)::BOOLEAN OR (#0.1::VARCHAR = 'z'::VARCHAR)::BOOLEAN)\
1634             ::BOOLEAN",
1635            1,
1636        );
1637    }
1638
1639    /// `NOT IN`, which the binder writes as an `AND` of inequalities and which reads the same
1640    /// lookup the other way round.
1641    #[test]
1642    fn a_not_in_list_becomes_the_same_lookup() {
1643        folds(
1644            "((#0.0::INTEGER <> 1::INTEGER)::BOOLEAN AND (#0.0::INTEGER <> 3::INTEGER)::BOOLEAN)\
1645             ::BOOLEAN",
1646            1,
1647        );
1648    }
1649
1650    /// A list with a null in it, which is the rule that makes an `IN` not a set lookup.
1651    ///
1652    /// A row that is not in the list is null rather than false, because it might have equalled the
1653    /// value the null stands for. `agrees` is what says the fold kept that, since the `OR` of
1654    /// comparisons it is checked against gets it from three valued logic for free.
1655    #[test]
1656    fn a_list_with_a_null_in_it_folds_and_keeps_the_null_rule() {
1657        folds(
1658            "((#0.0::INTEGER = 1::INTEGER)::BOOLEAN OR (#0.0::INTEGER = NULL::INTEGER)::BOOLEAN \
1659             OR (#0.0::INTEGER = 3::INTEGER)::BOOLEAN)::BOOLEAN",
1660            1,
1661        );
1662        folds(
1663            "((#0.0::INTEGER <> 1::INTEGER)::BOOLEAN AND (#0.0::INTEGER <> NULL::INTEGER)\
1664             ::BOOLEAN AND (#0.0::INTEGER <> 3::INTEGER)::BOOLEAN)::BOOLEAN",
1665            1,
1666        );
1667    }
1668
1669    /// The connectives that are not an `IN`, each for its own reason.
1670    #[test]
1671    fn a_connective_that_is_not_an_in_list_is_left_alone() {
1672        // Two different columns.
1673        folds(
1674            "((#0.0::INTEGER = 1::INTEGER)::BOOLEAN OR (#0.1::VARCHAR = 'a'::VARCHAR)::BOOLEAN)\
1675             ::BOOLEAN",
1676            0,
1677        );
1678        // One equality and one of something else.
1679        folds(
1680            "((#0.0::INTEGER = 1::INTEGER)::BOOLEAN OR (#0.0::INTEGER > 3::INTEGER)::BOOLEAN)\
1681             ::BOOLEAN",
1682            0,
1683        );
1684        // The right hand side is a column rather than a literal.
1685        folds(
1686            "((#0.0::INTEGER = 1::INTEGER)::BOOLEAN OR (#0.0::INTEGER = #0.0::INTEGER)::BOOLEAN)\
1687             ::BOOLEAN",
1688            0,
1689        );
1690        // An `AND` of equalities is not a `NOT IN`, it is a predicate that is false unless the two
1691        // literals are the same. Folding it as one would answer true where it answers false.
1692        folds(
1693            "((#0.0::INTEGER = 1::INTEGER)::BOOLEAN AND (#0.0::INTEGER = 3::INTEGER)::BOOLEAN)\
1694             ::BOOLEAN",
1695            0,
1696        );
1697    }
1698
1699    /// The same thing in a filter, which is the shape it is written in.
1700    #[test]
1701    fn an_in_list_filters_the_same_rows() {
1702        filters(
1703            "((#0.0::INTEGER = 1::INTEGER)::BOOLEAN OR (#0.0::INTEGER = 3::INTEGER)::BOOLEAN)\
1704             ::BOOLEAN",
1705        );
1706        filters(
1707            "((#0.0::INTEGER <> 1::INTEGER)::BOOLEAN AND (#0.0::INTEGER <> 3::INTEGER)::BOOLEAN)\
1708             ::BOOLEAN",
1709        );
1710        // Inside a larger predicate, where the fold is one operand of the connective above it.
1711        filters(
1712            "(((#0.0::INTEGER = 1::INTEGER)::BOOLEAN OR (#0.0::INTEGER = 3::INTEGER)::BOOLEAN)\
1713             ::BOOLEAN AND (#0.1::VARCHAR = 'a'::VARCHAR)::BOOLEAN)::BOOLEAN",
1714        );
1715    }
1716
1717    /// The literal side of a comparison is turned into a column when the pipeline is built.
1718    #[test]
1719    fn a_comparison_against_a_literal_builds_it_once() {
1720        let (schema, _) = input();
1721        for (expr, built) in [
1722            ("(#0.1::VARCHAR = 'a'::VARCHAR)::BOOLEAN AS p", 1),
1723            ("(#0.0::INTEGER > 1::INTEGER)::BOOLEAN AS p", 1),
1724            // The literal on the left, which is the same comparison written the other way round.
1725            ("(1::INTEGER < #0.0::INTEGER)::BOOLEAN AS p", 1),
1726            // Two columns, which has no literal side to build.
1727            ("(#0.0::INTEGER = #0.0::INTEGER)::BOOLEAN AS p", 0),
1728            // Two literals, which the kernel answers once for the whole vector without reading a
1729            // column, so building one would be work that nothing reads.
1730            ("(1::INTEGER = 2::INTEGER)::BOOLEAN AS p", 0),
1731        ] {
1732            let (plan, list) = projection(expr);
1733            let prepared = Prepared::new(&plan, &list, &schema).expect("the expression resolves");
1734            assert_eq!(prepared.literals_built(), built, "`{expr}`");
1735            agrees(expr);
1736        }
1737    }
1738
1739    /// A pattern that does not compile still fails where the query said it does.
1740    ///
1741    /// Preparing is not allowed to move an error earlier. Compiling at build time and reporting
1742    /// there would raise before a row had been read, and under a `CASE` arm it would raise on a
1743    /// query whose rows never reach the call at all.
1744    #[test]
1745    fn a_pattern_that_does_not_compile_fails_on_the_chunk_and_not_before() {
1746        let (schema, chunk) = input();
1747        let (plan, list) =
1748            projection("\"regexp_matches\"(#0.1::VARCHAR, 'a('::VARCHAR)::BOOLEAN AS a");
1749        let prepared = Prepared::new(&plan, &list, &schema).expect("preparing does not compile it");
1750        assert_eq!(prepared.hoisted(), 0);
1751        let mut scratch = prepared.scratch();
1752        let mut out = Vec::new();
1753        prepared.evaluate(&chunk, &mut scratch, &mut out).expect_err("the chunk raises");
1754    }
1755}