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::{
37    Error, LogicalType, PhysicalType, Result, Session, SessionTimeZone, Span, Value,
38};
39use rudb_kernels::{
40    Comparison, Connective, Found, Held, Lookup, Members, Recipe, cast_in_time_zone, combine,
41    compare_prepared, in_set, is_true, refine_flags, refine_prepared, select_prepared, selection,
42};
43use rudb_plan::{CompareOp, ConjunctionOp, Expr, ExprRef, Plan};
44use rudb_vector::{Assembly, Chunk, Selection, Vector};
45use std::collections::HashMap;
46use std::sync::Arc;
47
48use crate::lambda::{Lambda, lambda_call};
49use crate::ordering::Ordering;
50use crate::schema::Schema;
51use crate::written::written;
52
53/// The scheduler's half of the expression contract, imposed now rather than at layer eight.
54///
55/// A prepared expression is the immutable half of a pipeline and layer eight hands one of them to
56/// every thread running that pipeline. That is only sound if it holds nothing thread local, and the
57/// way to find out on the commit that breaks it rather than eight layers later is to ask the
58/// compiler here, exactly as [`Chunk`] does for the data plane.
59const _: () = {
60    const fn assert_shareable<T: Send + Sync>() {}
61    assert_shareable::<Prepared>();
62};
63
64/// One or more bound expressions, flattened and resolved against a schema.
65///
66/// Built once per pipeline with [`Prepared::new`] and evaluated per chunk with
67/// [`Prepared::evaluate`] or [`Prepared::evaluate_one`], each of which wants the [`Scratch`] that
68/// [`Prepared::scratch`] hands out.
69#[derive(Debug)]
70pub struct Prepared {
71    /// The nodes in post order, so every node's operands have already been computed when it runs.
72    steps: Vec<Step>,
73    /// The type each step produces, indexed the same way as `steps`.
74    ///
75    /// A parallel array rather than a field in the variant, for the reason [`Expr`] gives: a
76    /// [`LogicalType`] owns a `Vec` for its nested cases and putting one in every variant would make
77    /// the common variants several times larger for the benefit of the rare ones.
78    types: Vec<LogicalType>,
79    /// The source range each step came from, indexed the same way as `steps`.
80    spans: Vec<Span>,
81    /// The operand lists of the steps that have one, as runs of step indices.
82    operands: Vec<usize>,
83    /// The last step that reads each step's slot, or `usize::MAX` for one nothing reads.
84    ///
85    /// A slot is emptied as soon as the step that was the last to read it has run. Keeping every
86    /// intermediate alive to the end of the array instead is what the first measured version of this
87    /// did, and a chain of eight additions was slower prepared than walked because of it: nine live
88    /// intermediates at eight kilobytes each is seventy two kilobytes of working set where the tree
89    /// walk has two, and two is the pair the allocator hands back and forth and that stays in L1.
90    /// Everything else about the prepared form was faster and this one thing paid all of it back.
91    last_use: Vec<usize>,
92    /// The step index each expression this was built from ends at.
93    roots: Vec<usize>,
94    /// The step already compiled for each shared plan expression.
95    shared: HashMap<ExprRef, usize>,
96    share: bool,
97    /// The parsed zone used only by casts whose answer depends on the session.
98    time_zone: SessionTimeZone,
99}
100
101/// One node of a flattened expression.
102///
103/// A step refers to its operands by their index in [`Prepared::steps`], which is always smaller than
104/// its own because the array is in post order.
105#[derive(Debug)]
106enum Step {
107    /// A column of the chunk, by resolved position.
108    ///
109    /// This step computes nothing. Its slot stays empty and an operand that names it is read out of
110    /// the chunk, which is the whole of what makes a column reference free rather than a copy.
111    Column(usize),
112    /// A literal, materialized into a constant vector as long as the chunk.
113    Constant(Value),
114    /// A cast to this step's own type.
115    Cast {
116        /// The step being cast.
117        input: usize,
118        /// Whether a failed cast yields null instead of raising.
119        try_cast: bool,
120    },
121    /// A binary comparison.
122    Compare {
123        /// Which comparison.
124        op: Comparison,
125        /// The left operand's step.
126        left: usize,
127        /// The right operand's step.
128        right: usize,
129        /// The side that is a literal, in the one row column the comparison loops read it through,
130        /// and `None` when neither side is one.
131        ///
132        /// Built here because the loops read both sides through a slice, so the constant side has
133        /// to become a column somewhere, and the plan says which side that is. For a string it is
134        /// also where the four byte prefix comes from, which is what almost every row of a string
135        /// comparison is decided by.
136        held: Option<Held>,
137    },
138    /// An `AND` or `OR` over a run of [`Prepared::operands`].
139    Conjunction {
140        /// Which connective.
141        op: Connective,
142        /// Where the operand list starts.
143        start: usize,
144        /// How many operands it has.
145        len: usize,
146    },
147    /// A scalar function over a run of [`Prepared::operands`].
148    Function {
149        /// The call, with the name resolved and whatever the kernel could work out from the
150        /// arguments that were literals already worked out.
151        ///
152        /// Held here so the plan is not consulted per chunk, and built here so that a regular
153        /// expression is compiled once for the query rather than once for each of the hundred
154        /// thousand chunks a pipeline over `hits` runs.
155        recipe: Recipe,
156        /// How the call is written, for the one error message that quotes it.
157        ///
158        /// Rendered when the pipeline is built rather than when a chunk arrives, because the plan
159        /// is here and is not there. It is a short string per function node in the query and it is
160        /// built once, which is a different cost from the tree walk's, where the plan is still to
161        /// hand and the rendering can wait until the row that fails.
162        written: String,
163        /// Where the argument list starts.
164        start: usize,
165        /// How many arguments it has.
166        len: usize,
167    },
168    /// A membership test over a list the query wrote out.
169    ///
170    /// The binder has no `IN` node: `x IN (1, 2, 3)` arrives as an `OR` of three equalities and
171    /// `x NOT IN (1, 2, 3)` as an `AND` of three inequalities. That is the right shape for a binder
172    /// to produce, because nothing after it then needs a second set of rules for null, and it is the
173    /// wrong shape to run, because it is a pass over the column and an output vector per entry.
174    /// This is that shape folded back up, and folding it here rather than after the operands are
175    /// pushed is what keeps the equalities from being run anyway.
176    InSet {
177        /// The step being tested.
178        input: usize,
179        /// The list, as a set, with the null rule and the direction it is read in.
180        members: Members,
181    },
182    /// A searched `CASE`, whose branches are prepared expressions of their own.
183    ///
184    /// Nested rather than flattened into the same array because a branch is not evaluated over the
185    /// chunk, it is evaluated over the rows no earlier arm claimed, and a step in the outer array
186    /// would have no way to say that. The selection threaded form in #57 replaces this whole
187    /// variant, and when it does the branches stop being separate arrays.
188    Case {
189        /// The `WHEN`/`THEN` pairs, in order.
190        arms: Vec<PreparedArm>,
191        /// The `ELSE`, if there is one. Absent means null.
192        otherwise: Option<Prepared>,
193        /// How to answer it as codes, for the shape that can be. Absent means read the values.
194        blend: Option<Blend>,
195    },
196    /// A call to a function that takes a lambda, whose body is a prepared expression of its own.
197    ///
198    /// Nested for the reason a case's branches are: the body does not run over the chunk, it runs
199    /// over a chunk with a row per element that [`Lambda`] builds, and a step in the outer array has
200    /// no way to say that.
201    Lambda {
202        /// The steps of the call's other arguments: the list and `list_reduce`'s initial value, or
203        /// `invoke`'s parameters.
204        inputs: Vec<usize>,
205        /// The layout of what the body runs over and what to do with its answers.
206        runner: Box<Lambda>,
207        /// The body, prepared against the runner's schema.
208        body: Box<Prepared>,
209    },
210}
211
212/// One `WHEN`/`THEN` pair of a prepared [`Step::Case`].
213#[derive(Debug)]
214struct PreparedArm {
215    /// The condition.
216    when: Prepared,
217    /// The result if the condition is true.
218    then: Prepared,
219}
220
221/// A `CASE` over text whose every branch is a column or a literal, answered as codes.
222///
223/// What the general path does with the branches is read their values and write them into a vector of
224/// their own, which for a text column out of a native file decodes a compressed dictionary block per
225/// row and then throws the dictionary away. An operator above that has to work with strings even
226/// though every string it sees came out of one dictionary it could have kept.
227///
228/// It does not have to. The branches here name values rather than compute them, so if they all name
229/// values of one dictionary then so does the answer, and the answer is the codes: one code per row
230/// copied from the branch that claimed the row, and a literal is one code for all of its rows once
231/// the dictionary has been searched for it. Nothing is read and the dictionary comes out the other
232/// side, so a group by over the `CASE` groups on codes the way a group by over the bare column does.
233///
234/// ClickBench 39 is the query this is for. It groups by `CASE WHEN (SearchEngineID = 0 AND
235/// AdvEngineID = 0) THEN Referer ELSE '' END` beside `URL`, and writing that one column out as
236/// strings was a quarter of the query.
237///
238/// The shape is narrow on purpose. A branch that computes anything is not here, because then the
239/// answer is a value that no dictionary holds. A literal the dictionary does not hold is not here
240/// either, for the same reason, and that is decided per dictionary at run time rather than when the
241/// expression is prepared. And a `CASE` with no `ELSE` is not here, because the rows nothing claims
242/// are null and a null is not a code.
243#[derive(Debug)]
244struct Blend {
245    /// Where each branch takes its value from: one per arm in order, and the `ELSE` last.
246    branches: Vec<Branch>,
247    /// The literals the branches name, each with the search that finds it in a dictionary.
248    literals: Vec<(String, Lookup)>,
249}
250
251/// Where one branch of a [`Blend`] takes its value from.
252#[derive(Debug, Clone, Copy)]
253enum Branch {
254    /// A column of the chunk, by resolved position. Its rows keep the codes they arrived with.
255    Column(usize),
256    /// The literal at this index of [`Blend::literals`]. Its rows all get one code.
257    Literal(usize),
258}
259
260/// The per chunk working space of one [`Prepared`].
261///
262/// One per pipeline instance and never shared, which is the mutable half of the split the module
263/// documentation describes. It is handed back in rather than made inside [`Prepared::evaluate`] so
264/// that the array of slots survives from one chunk to the next instead of being allocated a hundred
265/// thousand times over a scan.
266#[derive(Debug, Default)]
267pub struct Scratch {
268    /// What each step produced, or `None` for a step that produces nothing and for one that has not
269    /// run yet.
270    slots: Vec<Option<Vector>>,
271    /// What each connective step has learned about its operands, indexed by step.
272    ///
273    /// Empty for every step that is not a connective and for a connective a filter has not reached
274    /// yet, since it is built the first time one runs and the shape it needs is not known before
275    /// then. This is the mutable half of the adaptive ordering and it is here rather than in
276    /// [`Prepared`] because a prepared expression is shared by every thread running the pipeline.
277    orders: Vec<Option<Ordering>>,
278}
279
280impl Scratch {
281    /// The order a connective's operands are run in.
282    ///
283    /// For the tests that say the learning reached the walk. Nothing in the engine asks a scratch
284    /// this, because the walk is the only thing that reads an ordering and it reads its own.
285    #[cfg(test)]
286    fn order(&self, step: usize) -> Option<&[usize]> {
287        self.orders[step].as_ref().map(Ordering::order)
288    }
289}
290
291impl Prepared {
292    /// Prepares `exprs` against `schema`.
293    ///
294    /// # Errors
295    ///
296    /// If a column reference names a binding the schema does not have, or if an aggregate appears
297    /// where an ordinary expression was expected. Both are failures of the plan rather than of the
298    /// data, which is why they are found here, once, rather than on some chunk in the middle of a
299    /// scan.
300    pub fn new(plan: &Plan, exprs: &[ExprRef], schema: &Schema) -> Result<Self> {
301        Self::build(plan, exprs, schema, false)
302    }
303
304    /// Prepares expressions whose caller can evaluate a shared expression graph as one unit.
305    pub(crate) fn shared(plan: &Plan, exprs: &[ExprRef], schema: &Schema) -> Result<Self> {
306        Self::build(plan, exprs, schema, true)
307    }
308
309    fn build(plan: &Plan, exprs: &[ExprRef], schema: &Schema, share: bool) -> Result<Self> {
310        let mut prepared = Self {
311            steps: Vec::new(),
312            types: Vec::new(),
313            spans: Vec::new(),
314            operands: Vec::new(),
315            last_use: Vec::new(),
316            roots: Vec::new(),
317            shared: HashMap::new(),
318            share,
319            time_zone: SessionTimeZone::default(),
320        };
321        for &expr in exprs {
322            let root = prepared.push(plan, expr, schema)?;
323            prepared.roots.push(root);
324        }
325        prepared.last_use = prepared.last_uses();
326        Ok(prepared)
327    }
328
329    /// Uses the zone of the session that owns this prepared expression.
330    #[must_use]
331    pub fn in_session(mut self, session: &Session) -> Self {
332        self.set_time_zone(session.session_time_zone());
333        self
334    }
335
336    /// Sets the zone here and in every lambda body, which is prepared before the session is known.
337    fn set_time_zone(&mut self, time_zone: SessionTimeZone) {
338        self.time_zone = time_zone;
339        for step in &mut self.steps {
340            if let Step::Lambda { body, .. } = step {
341                body.set_time_zone(time_zone);
342            }
343        }
344    }
345
346    /// Which step is the last to read each step, computed once when the expression is prepared.
347    ///
348    /// A root is never freed, because the whole point of running the array was to produce it. A
349    /// step nothing reads and that is not a root cannot happen, since every step is pushed by the
350    /// node that wanted it, but saying `usize::MAX` rather than asserting that keeps this a fact
351    /// about the array rather than a claim about the builder.
352    fn last_uses(&self) -> Vec<usize> {
353        let mut last = vec![usize::MAX; self.steps.len()];
354        for index in 0..self.steps.len() {
355            self.for_each_operand(index, |operand| last[operand] = index);
356        }
357        for &root in &self.roots {
358            last[root] = usize::MAX;
359        }
360        last
361    }
362
363    /// Visits the steps one step reads, whatever shape its operands are held in.
364    fn for_each_operand(&self, index: usize, mut visit: impl FnMut(usize)) {
365        match &self.steps[index] {
366            // A case's branches are arrays of their own and read nothing out of this one.
367            Step::Column(_) | Step::Constant(_) | Step::Case { .. } => {}
368            Step::Cast { input, .. } | Step::InSet { input, .. } => visit(*input),
369            Step::Lambda { inputs, .. } => inputs.iter().for_each(|&input| visit(input)),
370            Step::Compare { left, right, .. } => {
371                visit(*left);
372                visit(*right);
373            }
374            Step::Conjunction { start, len, .. } | Step::Function { start, len, .. } => {
375                for &operand in &self.operands[*start..*start + *len] {
376                    visit(operand);
377                }
378            }
379        }
380    }
381
382    /// Prepares one expression, which is the common case and saves the caller a slice.
383    ///
384    /// # Errors
385    ///
386    /// Whatever [`Prepared::new`] reports.
387    pub fn one(plan: &Plan, expr: ExprRef, schema: &Schema) -> Result<Self> {
388        Self::new(plan, &[expr], schema)
389    }
390
391    /// Working space sized for this expression.
392    #[must_use]
393    pub fn scratch(&self) -> Scratch {
394        Scratch {
395            slots: (0..self.steps.len()).map(|_| None).collect(),
396            orders: (0..self.steps.len()).map(|_| None).collect(),
397        }
398    }
399
400    /// How many expressions this was built from.
401    #[must_use]
402    pub fn len(&self) -> usize {
403        self.roots.len()
404    }
405
406    /// How many comparisons have their literal side already built.
407    ///
408    /// For the tests, for the same reason as [`Self::sets`]: an answer that moved would be a bug,
409    /// so the only thing a test can look at is whether the building happened.
410    #[cfg(test)]
411    fn literals_built(&self) -> usize {
412        self.steps.iter().filter(|step| matches!(step, Step::Compare { held: Some(_), .. })).count()
413    }
414
415    /// How many of the steps are an `IN` list folded back up.
416    ///
417    /// For the tests, which cannot see the fold in an answer because an answer that changed would
418    /// be a bug.
419    #[cfg(test)]
420    fn sets(&self) -> usize {
421        self.steps.iter().filter(|step| matches!(step, Step::InSet { .. })).count()
422    }
423
424    /// How many of the function steps worked something out when this was built.
425    ///
426    /// For the tests, which cannot see the hoisting in an answer because an answer that changed
427    /// would be a bug.
428    #[cfg(test)]
429    fn hoisted(&self) -> usize {
430        self.steps
431            .iter()
432            .filter(|step| matches!(step, Step::Function { recipe, .. } if recipe.hoists()))
433            .count()
434    }
435
436    /// Whether it was built from no expressions at all.
437    #[must_use]
438    pub fn is_empty(&self) -> bool {
439        self.roots.is_empty()
440    }
441
442    /// How many of the steps do something to a row.
443    ///
444    /// A column reference and a literal are not among them. A column reference computes nothing at
445    /// all, which is what makes a step that names one free rather than a copy, and a literal is
446    /// materialized once for the whole chunk rather than once a row. What is left is a pass over
447    /// the rows each, so this is roughly what one row costs, counted in the same unit the scan's
448    /// own reading of that row is counted in.
449    ///
450    /// What reads it is the scan, through the weight an operator reports to the pipeline. See
451    /// [`Stream::weight`](rudb_pipeline::Stream::weight).
452    #[must_use]
453    pub fn passes(&self) -> usize {
454        self.steps
455            .iter()
456            .filter(|step| !matches!(step, Step::Column(_) | Step::Constant(_)))
457            .count()
458    }
459
460    /// Evaluates every expression over `chunk`, appending one vector each to `out`.
461    ///
462    /// Appends rather than returns a `Vec`, so a caller in a loop reuses one buffer.
463    ///
464    /// # Errors
465    ///
466    /// Anything a kernel reports, on the first expression that reports it.
467    pub fn evaluate(
468        &self,
469        chunk: &Chunk,
470        scratch: &mut Scratch,
471        out: &mut Vec<Vector>,
472    ) -> Result<()> {
473        self.run(chunk, scratch)?;
474        let mut remaining: HashMap<usize, usize> = HashMap::new();
475        for &root in &self.roots {
476            *remaining.entry(root).or_default() += 1;
477        }
478        for &root in &self.roots {
479            // The one place a column is copied, and it is copied because the caller is taking
480            // ownership of a vector that has to outlive the chunk it came from. `SELECT a` is that
481            // shape and a projection of a bare column is the only expression where it happens.
482            match self.steps[root] {
483                Step::Column(position) => out.push(chunk.column(position)?.clone()),
484                _ => {
485                    let Some(left) = remaining.get_mut(&root) else {
486                        return Err(Error::internal("a prepared root was not counted"));
487                    };
488                    *left -= 1;
489                    if *left == 0 {
490                        out.push(scratch.slots[root].take().ok_or_else(|| missing(root))?);
491                    } else {
492                        out.push(
493                            scratch.slots[root].as_ref().ok_or_else(|| missing(root))?.clone(),
494                        );
495                    }
496                }
497            }
498        }
499        Ok(())
500    }
501
502    /// Evaluates a single expression over `chunk`, handing back a reference to the answer.
503    ///
504    /// A reference rather than a vector, because the caller of this is a filter, which reads the
505    /// flags to build a selection and then drops them. Nothing about that wants ownership, and a
506    /// predicate that is a bare column reference, which `WHERE flag` is, would otherwise copy the
507    /// column to hand it over.
508    ///
509    /// # Errors
510    ///
511    /// Anything a kernel reports, and an internal error if this was not built from exactly one
512    /// expression.
513    pub fn evaluate_one<'s>(
514        &'s self,
515        chunk: &'s Chunk,
516        scratch: &'s mut Scratch,
517    ) -> Result<&'s Vector> {
518        let [root] = self.roots[..] else {
519            return Err(Error::internal(format!(
520                "evaluate_one over a prepared expression of {} roots",
521                self.roots.len()
522            )));
523        };
524        self.run(chunk, scratch)?;
525        self.operand(root, chunk, &scratch.slots)
526    }
527
528    /// Evaluates a single expression as a filter, handing back the rows it keeps.
529    ///
530    /// The difference between this and [`evaluate_one`](Self::evaluate_one) followed by
531    /// [`selection`] is the whole of what a threaded filter is. An `AND` evaluated as an expression
532    /// runs every conjunct over every row and then combines the flag vectors, so a predicate of four
533    /// conjuncts that each pass a fifth of the rows does five times the work of one that stops
534    /// looking at a row as soon as a conjunct rejects it. TPC-H Q6 is exactly that predicate.
535    ///
536    /// So the conjuncts of a top level `AND` are run one at a time, each over the rows the ones
537    /// before it left, and the moment nothing is left the rest of the predicate is not run at all.
538    /// The order they run in starts as the order the plan gives and then moves, because which
539    /// conjunct is worth running first is a question about the data and the scan is the thing
540    /// holding the answer. The `ordering` module has what is measured and how.
541    ///
542    /// A top level `OR` is threaded the same way against the complement. A row the first branch
543    /// accepts is a row the filter keeps whatever the rest of the predicate says about it, so each
544    /// branch is run over the rows no branch before it accepted, and the moment every row has been
545    /// accepted the rest of the predicate is not run either. That is the mirror of the `AND` case
546    /// and not an approximation of it: the answer is the same set of rows, because `OR` over three
547    /// valued logic is true wherever any branch is true and nothing a later branch says can take a
548    /// row back. It is worth less than the `AND` case in practice, since an `OR` of selective
549    /// branches leaves almost every row in play for the branch after, and it is worth having anyway
550    /// because the cost of finding that out is one merge per branch.
551    ///
552    /// What is threaded is the operand's own comparison rather than the whole of its subtree. A
553    /// conjunct of `a + b > 5` still adds over the whole chunk, because the scalar kernels take a
554    /// vector rather than a selection, and it is the comparison and everything downstream of it that
555    /// reads only the rows still in play. An operand that is a bare column or a function produces
556    /// flags over the chunk and is narrowed with [`refine_flags`], which is what keeps one awkward
557    /// operand from putting the others back on the unthreaded path. An operand that is itself a
558    /// connective recurses, so the two conjuncts of each half of `(a AND b) OR (c AND d)` are
559    /// threaded the same way the halves are.
560    ///
561    /// None of this is available to a projection. `SELECT a > 5 AND b LIKE 'x%'` wants a value per
562    /// row and the rows a selection dropped have no value in it, so [`evaluate`](Self::evaluate) and
563    /// [`evaluate_one`](Self::evaluate_one) evaluate the whole tree over the whole chunk and combine
564    /// flags. The two are separate entry points picked when the pipeline is built rather than one
565    /// path with a flag in it, because conflating them is a wrong answer rather than a slow one.
566    ///
567    /// # Errors
568    ///
569    /// Anything a kernel reports, and an internal error if this was not built from exactly one
570    /// expression.
571    pub fn evaluate_filter(&self, chunk: &Chunk, scratch: &mut Scratch) -> Result<Selection> {
572        let [root] = self.roots[..] else {
573            return Err(Error::internal(format!(
574                "evaluate_filter over a prepared expression of {} roots",
575                self.roots.len()
576            )));
577        };
578        scratch.slots.clear();
579        scratch.slots.resize_with(self.steps.len(), || None);
580        // A predicate that is not a connective at all is the same walk over one operand, which is
581        // where [`thread`](Self::thread) starts: it runs the tree and turns the flags into a
582        // selection, with no narrowing to do because nothing has narrowed anything yet.
583        self.thread(root, 0, chunk, scratch, None)
584    }
585
586    /// The operands of one connective, run in order, each over the rows the ones before it left.
587    ///
588    /// `live` is the rows this connective has to decide about and `None` means every row of the
589    /// chunk, which is not the same as a selection of all of them: it lets the first operand take
590    /// the unthreaded kernel rather than a pass over an identity selection. The answer is the rows
591    /// out of `live` the connective is true for.
592    ///
593    /// The walk is the same for both connectives and only the bookkeeping differs. `AND` carries the
594    /// rows every operand so far has kept, so each answer replaces it. `OR` carries the rows no
595    /// operand so far has accepted, so each answer comes out of it and the rows the connective keeps
596    /// are the ones that went missing along the way.
597    ///
598    /// The operand is not `steps[begin..=operand]` evaluated and then narrowed. Its subtree is run
599    /// over the whole chunk and it is the operand itself that reads only the rows in play, except
600    /// where the operand is another connective, which recurses and threads its own operands from
601    /// here rather than falling back to a flag vector. That is what makes `(a AND b) OR (c AND d)`
602    /// four threaded comparisons rather than two threaded ones and two flag passes.
603    fn branches(
604        &self,
605        index: usize,
606        begin: usize,
607        chunk: &Chunk,
608        scratch: &mut Scratch,
609        live: Option<&Selection>,
610    ) -> Result<Selection> {
611        let Step::Conjunction { op, start, len } = self.steps[index] else {
612            return Err(Error::internal("a connective walk over a step that is not a connective"));
613        };
614        let operands = &self.operands[start..start + len];
615        let rows = chunk.len();
616        // Out of the scratch for the length of the walk, because the walk runs steps and running a
617        // step wants the scratch. It goes back at the end, which is also where it learns. A walk
618        // that fails leaves the slot empty and the next chunk starts the connective over, which is
619        // a history lost on a query that is about to stop running anyway.
620        let mut order = scratch.orders[index]
621            .take()
622            .unwrap_or_else(|| Ordering::new(op, self.weights(operands, begin)));
623        let mut carried: Option<Selection> = live.cloned();
624        for slot in 0..len {
625            if carried.as_ref().is_some_and(Selection::is_empty) {
626                break;
627            }
628            let which = order.at(slot);
629            let operand = operands[which];
630            // The array is in post order and an operand's whole subtree sits between the operand
631            // before it and the operand itself, which is a range the run order cannot move. That is
632            // what lets the operands run in any order at all without a second structure to say
633            // where each one starts.
634            let from = if which == 0 { begin } else { operands[which - 1] + 1 };
635            let given = carried.as_ref().map_or(rows, Selection::len);
636            let answered = self.thread(operand, from, chunk, scratch, carried.as_ref())?;
637            order.observed(which, given, answered.len());
638            carried = Some(match (op, carried) {
639                (Connective::And, _) => answered,
640                (Connective::Or, None) => answered.complement(rows),
641                (Connective::Or, Some(carried)) => carried.without(&answered),
642            });
643            // Keep a shared step alive when a later operand still reads it.
644            for step in from..=operand {
645                if self.last_use[step] <= operand {
646                    scratch.slots[step] = None;
647                }
648            }
649        }
650        order.relearn();
651        scratch.orders[index] = Some(order);
652        Ok(match (op, carried) {
653            // A connective with no operands, which the binder does not build and which is answered
654            // here rather than left to index arithmetic: an empty `AND` is every row and an empty
655            // `OR` is none.
656            (Connective::And, None) => live.cloned().unwrap_or_else(|| Selection::identity(rows)),
657            (Connective::And, Some(kept)) => kept,
658            (Connective::Or, None) => Selection::empty(),
659            (Connective::Or, Some(missed)) => match live {
660                None => missed.complement(rows),
661                Some(live) => live.without(&missed),
662            },
663        })
664    }
665
666    /// What each operand of a connective costs to run over a chunk, for the ordering to divide by.
667    ///
668    /// An operand costs what its whole subtree costs, which is the steps from where the operand
669    /// before it ended up to the operand itself.
670    fn weights(&self, operands: &[usize], begin: usize) -> Vec<f64> {
671        let mut costs = Vec::with_capacity(operands.len());
672        let mut from = begin;
673        for &operand in operands {
674            costs.push((from..=operand).map(|step| self.weight(step)).sum());
675            from = operand + 1;
676        }
677        costs
678    }
679
680    /// Roughly what one step costs to run over a chunk, against a comparison of two fixed width
681    /// columns as the unit.
682    ///
683    /// A ranking rather than a prediction. Nothing downstream reads the number itself, only which
684    /// of two of them is larger, and the differences that decide an order are the big ones: a
685    /// column reference costs nothing because it is read in place, a string function costs many
686    /// times what an integer comparison costs, and a comparison over a variable length type costs
687    /// several times what the same comparison over a fixed width one costs. Everything finer than
688    /// that is below the noise of what the window is measuring anyway.
689    fn weight(&self, index: usize) -> f64 {
690        match &self.steps[index] {
691            // Read straight out of the chunk at the point an operand is wanted, so there is no step
692            // to run and nothing to charge for.
693            Step::Column(_) => 0.0,
694            // One vector built per chunk, however many rows the chunk has.
695            Step::Constant(_) => 0.25,
696            // The operands carry the cost of a connective, and they are steps of their own.
697            Step::Conjunction { .. } => 0.0,
698            Step::Cast { input, .. } => 2.0 * touching(&self.types[*input]),
699            Step::Compare { left, .. } => touching(&self.types[*left]),
700            // One hash and one probe a row, whatever the list holds, which is the point of it. It
701            // is dearer than a comparison and much cheaper than the chain of them it replaced.
702            Step::InSet { input, .. } => 2.0 * touching(&self.types[*input]),
703            Step::Function { start, len, .. } => {
704                let widest = self.operands[*start..*start + *len]
705                    .iter()
706                    .map(|&argument| touching(&self.types[argument]))
707                    .fold(1.0, f64::max);
708                4.0 * widest
709            }
710            // A branch per arm, each of which is a prepared expression of its own that this does
711            // not look inside. Charging for the arms alone understates it and says the right thing
712            // about the order, which is that a `CASE` is not what you want in front.
713            Step::Case { arms, .. } => 4.0 * arms.len() as f64,
714            // A run of the body per element, which is several a row, and a list to take apart and
715            // put back together around it.
716            Step::Lambda { .. } => 16.0,
717        }
718    }
719
720    /// One operand of a connective, over the rows it is still worth asking about.
721    ///
722    /// `begin` is the first step of the operand's subtree, which the caller knows because the steps
723    /// are in post order.
724    fn thread(
725        &self,
726        index: usize,
727        begin: usize,
728        chunk: &Chunk,
729        scratch: &mut Scratch,
730        live: Option<&Selection>,
731    ) -> Result<Selection> {
732        if matches!(self.steps[index], Step::Conjunction { .. }) {
733            return self.branches(index, begin, chunk, scratch, live);
734        }
735        for step in begin..index {
736            self.run_step(step, chunk, scratch)?;
737        }
738        if let Step::Compare { op, left, right, held } = &self.steps[index] {
739            let one = self.operand(*left, chunk, &scratch.slots)?;
740            let other = self.operand(*right, chunk, &scratch.slots)?;
741            let held = held.as_ref();
742            return match live {
743                // The first operand has every row in play, and asking the threaded kernel for that
744                // would be a pass over an identity selection the unthreaded one does not need.
745                None => select_prepared(*op, one, other, held),
746                Some(live) => refine_prepared(*op, one, other, live, held),
747            };
748        }
749        // A later LIKE in a threaded filter often sees only a handful of survivors.
750        // Gather its arguments, not the whole chunk, while preserving the stable
751        // dictionary behind a gathered string column. The ordinary full-vector
752        // path remains cheaper when most rows are still live.
753        if let (Some(live), Step::Function { recipe, written, start, len }) =
754            (live, &self.steps[index])
755        {
756            if matches!(recipe.name(), "~~" | "!~~" | "~~*" | "!~~*")
757                && live.len().saturating_mul(4) <= chunk.len()
758            {
759                let flags = self
760                    .with_operands(*start, *len, chunk, &scratch.slots, |args| {
761                        let gathered = args
762                            .iter()
763                            .map(|arg| arg.gather(live.indices()))
764                            .collect::<Result<Vec<_>>>()?;
765                        let narrowed = gathered.iter().collect::<Vec<_>>();
766                        rudb_kernels::call_prepared(
767                            recipe,
768                            &narrowed,
769                            &self.types[index],
770                            Some(&|| written.clone()),
771                        )
772                    })
773                    .map_err(|error| error.with_fallback_span(self.spans[index]))?;
774                return Ok(selection(&flags, live.len()).compose(live));
775            }
776        }
777        self.run_step(index, chunk, scratch)?;
778        let flags = self.operand(index, chunk, &scratch.slots)?;
779        match live {
780            None => Ok(selection(flags, chunk.len())),
781            Some(live) => refine_flags(flags, live),
782        }
783    }
784
785    /// Runs every step in order, filling the slots.
786    fn run(&self, chunk: &Chunk, scratch: &mut Scratch) -> Result<()> {
787        scratch.slots.clear();
788        scratch.slots.resize_with(self.steps.len(), || None);
789        for index in 0..self.steps.len() {
790            self.run_step(index, chunk, scratch)?;
791        }
792        Ok(())
793    }
794
795    /// Runs one step and empties the slot of every operand this was the last step to read.
796    fn run_step(&self, index: usize, chunk: &Chunk, scratch: &mut Scratch) -> Result<()> {
797        let produced = self
798            .step(index, chunk, &scratch.slots)
799            .map_err(|error| error.with_fallback_span(self.spans[index]))?;
800        scratch.slots[index] = produced;
801        let slots = &mut scratch.slots;
802        self.for_each_operand(index, |operand| {
803            if self.last_use[operand] == index {
804                slots[operand] = None;
805            }
806        });
807        Ok(())
808    }
809
810    /// Runs one step, given what the steps before it produced.
811    fn step(
812        &self,
813        index: usize,
814        chunk: &Chunk,
815        slots: &[Option<Vector>],
816    ) -> Result<Option<Vector>> {
817        let ty = &self.types[index];
818        let produced = match &self.steps[index] {
819            Step::Column(_) => None,
820            Step::Constant(value) => Some(Vector::constant(ty.clone(), value.clone(), chunk.len())),
821            Step::Cast { input, try_cast } => Some(cast_in_time_zone(
822                self.operand(*input, chunk, slots)?,
823                ty,
824                *try_cast,
825                Some(self.time_zone),
826            )?),
827            Step::Compare { op, left, right, held } => Some(compare_prepared(
828                *op,
829                self.operand(*left, chunk, slots)?,
830                self.operand(*right, chunk, slots)?,
831                held.as_ref(),
832            )?),
833            Step::Conjunction { op, start, len } => {
834                Some(
835                    self.with_operands(*start, *len, chunk, slots, |children| {
836                        combine(*op, children)
837                    })?,
838                )
839            }
840            Step::Function { recipe, written, start, len } => {
841                Some(self.with_operands(*start, *len, chunk, slots, |args| {
842                    rudb_kernels::call_prepared(recipe, args, ty, Some(&|| written.clone()))
843                })?)
844            }
845            Step::InSet { input, members } => {
846                Some(in_set(self.operand(*input, chunk, slots)?, members, ty)?)
847            }
848            Step::Case { arms, otherwise, blend } => {
849                Some(self.case(chunk, arms, otherwise.as_ref(), blend.as_ref(), ty)?)
850            }
851            Step::Lambda { inputs, runner, body } => {
852                let mut operands = Vec::with_capacity(inputs.len());
853                for &input in inputs {
854                    operands.push(self.operand(input, chunk, slots)?);
855                }
856                let mut scratch = body.scratch();
857                Some(runner.run(&operands, chunk, &mut |inner| {
858                    body.evaluate_one(inner, &mut scratch).cloned()
859                })?)
860            }
861        };
862        Ok(produced)
863    }
864
865    /// The vector a step produced, or the chunk's column if the step is a column reference.
866    fn operand<'v>(
867        &self,
868        index: usize,
869        chunk: &'v Chunk,
870        slots: &'v [Option<Vector>],
871    ) -> Result<&'v Vector> {
872        if let Step::Column(position) = self.steps[index] {
873            return chunk.column(position);
874        }
875        slots[index].as_ref().ok_or_else(|| missing(index))
876    }
877
878    /// Hands a kernel the references to an operand list, without allocating for the usual widths.
879    ///
880    /// One, two and three because those are what a bound tree is made of: every scalar function in
881    /// the catalog is unary or binary, a comparison is binary, and a conjunction is two or three
882    /// often enough to be worth a line. A stack array for those means a chain of eight additions
883    /// makes zero allocations for its operand lists over a chunk instead of eight, and eight
884    /// allocations a chunk at the rate a pipeline produces chunks is a real number rather than a
885    /// tidiness argument. Anything wider falls back to [`gather`](Self::gather), which is a `Vec`
886    /// of pointers and still moves no data.
887    fn with_operands<'v, T>(
888        &self,
889        start: usize,
890        len: usize,
891        chunk: &'v Chunk,
892        slots: &'v [Option<Vector>],
893        run: impl FnOnce(&[&'v Vector]) -> Result<T>,
894    ) -> Result<T> {
895        match self.operands[start..start + len] {
896            [a] => run(&[self.operand(a, chunk, slots)?]),
897            [a, b] => run(&[self.operand(a, chunk, slots)?, self.operand(b, chunk, slots)?]),
898            [a, b, c] => run(&[
899                self.operand(a, chunk, slots)?,
900                self.operand(b, chunk, slots)?,
901                self.operand(c, chunk, slots)?,
902            ]),
903            _ => {
904                let gathered = self.gather(start, len, chunk, slots)?;
905                run(&gathered)
906            }
907        }
908    }
909
910    /// References to an operand list, for a kernel that takes a slice of them.
911    ///
912    /// The `Vec` here is the allocation the module documentation names: it holds pointers rather
913    /// than vectors, so it is a dozen bytes an operand and no data moves.
914    fn gather<'v>(
915        &self,
916        start: usize,
917        len: usize,
918        chunk: &'v Chunk,
919        slots: &'v [Option<Vector>],
920    ) -> Result<Vec<&'v Vector>> {
921        let mut gathered = Vec::with_capacity(len);
922        for &operand in &self.operands[start..start + len] {
923            gathered.push(self.operand(operand, chunk, slots)?);
924        }
925        Ok(gathered)
926    }
927
928    /// A searched `CASE` over the rows no earlier arm claimed.
929    ///
930    /// The same shape [`evaluate`](crate::evaluate) has, because the thing that makes it that shape
931    /// is a correctness rule rather than a performance one: `CASE WHEN x <> 0 THEN 1 / x ELSE 0 END`
932    /// divides by zero on the rows the arm excludes if the arm is evaluated for them.
933    ///
934    /// Each arm answers the rows no earlier arm claimed, so the answers come back short and out of
935    /// order and have to be put back in the order the rows arrived in. That is what [`Assembly`] is:
936    /// the arms are laid end to end into one run of data and the interleave is a single typed copy
937    /// over it. It used to be a `Vec<Value>` filled a row at a time and handed to
938    /// `Vector::from_values`, which is a heap allocation and a drop for every string in the answer.
939    /// On the ClickBench query that groups by a `CASE` over `Referer` that was about a quarter of
940    /// the whole query.
941    ///
942    /// What is left of #57 here is the narrowing. An arm still narrows the whole chunk rather than
943    /// the columns it reads, and the selection threading that replaces the narrowing entirely is
944    /// the item this one was carved out of.
945    fn case(
946        &self,
947        chunk: &Chunk,
948        arms: &[PreparedArm],
949        otherwise: Option<&Prepared>,
950        blend: Option<&Blend>,
951        ty: &LogicalType,
952    ) -> Result<Vector> {
953        let claimed = self.claims(chunk, arms)?;
954        if let Some(blend) = blend {
955            if let Some(blended) = blended(chunk, &claimed, blend)? {
956                return Ok(blended);
957            }
958        }
959        let mut built = Assembly::new(ty.clone(), chunk.len())?;
960        let branches = arms.iter().map(|arm| &arm.then).map(Some).chain([otherwise]);
961        for (branch, rows) in branches.zip(&claimed) {
962            let (Some(branch), false) = (branch, rows.is_empty()) else { continue };
963            // The same cut the conditions skip above, skipped here for the same reason: a branch
964            // that claimed every row claimed them in order, so narrowing to them is a copy of every
965            // column in the chunk to arrive back at the chunk.
966            let cut;
967            let matched = if rows.len() == chunk.len() {
968                chunk
969            } else {
970                cut = narrow(chunk, rows)?;
971                &cut
972            };
973            let mut scratch = branch.scratch();
974            let results = branch.evaluate_one(matched, &mut scratch)?;
975            built.place(&placed(rows)?, results)?;
976        }
977        built.finish()
978    }
979
980    /// The rows each branch of a `CASE` answers, one list per arm in order and the `ELSE` last.
981    ///
982    /// Only the conditions are run here, which is what keeps the rule the doc above states: an arm's
983    /// condition is evaluated over the rows no earlier arm claimed, so a condition that would raise
984    /// on a row an earlier arm took is never asked about it. The results are worked out afterwards,
985    /// once, from these lists, and both ways of working them out want the same thing, which is the
986    /// rows of one branch in the order they arrived in.
987    fn claims(&self, chunk: &Chunk, arms: &[PreparedArm]) -> Result<Vec<Vec<usize>>> {
988        let mut claimed = Vec::with_capacity(arms.len() + 1);
989        let mut pending: Vec<usize> = (0..chunk.len()).collect();
990        for arm in arms {
991            if pending.is_empty() {
992                claimed.push(Vec::new());
993                continue;
994            }
995            // `pending` starts as every row in order and only ever shrinks, so the same length is
996            // the same rows in the same order and there is nothing to cut. That is the whole of the
997            // first arm of a one armed `CASE`, which is the shape of the ClickBench query this was
998            // measured on, and cutting it was a copy of every column in the chunk for nothing.
999            let cut;
1000            let narrowed = if pending.len() == chunk.len() {
1001                chunk
1002            } else {
1003                cut = narrow(chunk, &pending)?;
1004                &cut
1005            };
1006            let mut scratch = arm.when.scratch();
1007            let flags = arm.when.evaluate_one(narrowed, &mut scratch)?;
1008            let mut taken = Vec::new();
1009            let mut still = Vec::new();
1010            // row at a time: splitting the rows an arm claims from the ones it leaves is a test per
1011            // row, and what replaces it is the selection threading the rest of #57 asks for rather
1012            // than anything that can be done here.
1013            for (at, &row) in pending.iter().enumerate() {
1014                if is_true(&flags.value_at(at)) {
1015                    taken.push(row);
1016                } else {
1017                    still.push(row);
1018                }
1019            }
1020            claimed.push(taken);
1021            pending = still;
1022        }
1023        claimed.push(pending);
1024        Ok(claimed)
1025    }
1026
1027    /// Flattens one expression, appending its steps and returning the index of its last one.
1028    fn push(&mut self, plan: &Plan, expr: ExprRef, schema: &Schema) -> Result<usize> {
1029        if self.share {
1030            if let Some(&step) = self.shared.get(&expr) {
1031                return Ok(step);
1032            }
1033        }
1034        let ty = plan.expr_type(expr).clone();
1035        let step = match *plan.expr(expr) {
1036            Expr::Column(binding) => {
1037                let position = schema.position_of(binding).ok_or_else(|| {
1038                    Error::internal(format!(
1039                        "column #{}.{} is not in the schema this operator was given",
1040                        binding.table, binding.column
1041                    ))
1042                })?;
1043                Step::Column(position)
1044            }
1045            Expr::Constant(reference) => Step::Constant(plan.value(reference).clone()),
1046            Expr::Cast { input, try_cast } => {
1047                Step::Cast { input: self.push(plan, input, schema)?, try_cast }
1048            }
1049            Expr::Compare { op, left, right } => {
1050                let left = self.push(plan, left, schema)?;
1051                let right = self.push(plan, right, schema)?;
1052                Step::Compare { op: comparison(op), left, right, held: self.held(left, right) }
1053            }
1054            Expr::Conjunction { op, children } => {
1055                let list = plan.expr_list(children).to_vec();
1056                match self.membership(plan, connective(op), &list, schema)? {
1057                    Some(step) => step,
1058                    None => {
1059                        let (start, len) = self.push_list(plan, &list, schema)?;
1060                        Step::Conjunction { op: connective(op), start, len }
1061                    }
1062                }
1063            }
1064            Expr::Function { name, args } if lambda_call(plan, args).is_some() => {
1065                let Some((lambda, inputs)) = lambda_call(plan, args) else {
1066                    return Err(Error::internal("a lambda call without a lambda"));
1067                };
1068                let Expr::Lambda { body, .. } = *plan.expr(lambda) else {
1069                    return Err(Error::internal("a lambda call without a lambda"));
1070                };
1071                let runner = Lambda::new(plan, plan.string(name), lambda, &inputs, schema)?;
1072                let body = Self::one(plan, body, runner.schema())?;
1073                let mut steps = Vec::with_capacity(inputs.len());
1074                for &input in &inputs {
1075                    steps.push(self.push(plan, input, schema)?);
1076                }
1077                Step::Lambda { inputs: steps, runner: Box::new(runner), body: Box::new(body) }
1078            }
1079            Expr::LambdaParam(binding) => {
1080                let position = schema.position_of(binding).ok_or_else(|| {
1081                    Error::internal(format!(
1082                        "lambda parameter @{}.{} is not in the schema its body was given",
1083                        binding.table, binding.column
1084                    ))
1085                })?;
1086                Step::Column(position)
1087            }
1088            Expr::Lambda { .. } => {
1089                return Err(Error::internal(
1090                    "a lambda was evaluated outside the function that takes it",
1091                ));
1092            }
1093            Expr::Function { name, args } => {
1094                let (start, len) = self.push_list(plan, plan.expr_list(args), schema)?;
1095                Step::Function {
1096                    recipe: Recipe::new(plan.string(name), &self.literals(start, len)),
1097                    written: written(plan, expr, schema),
1098                    start,
1099                    len,
1100                }
1101            }
1102            Expr::Aggregate { name, .. } => {
1103                return Err(Error::internal(format!(
1104                    "the {} aggregate was evaluated as an ordinary expression",
1105                    plan.string(name)
1106                )));
1107            }
1108            Expr::Window { name, .. } => {
1109                return Err(Error::internal(format!(
1110                    "the {} window function was evaluated as an ordinary expression",
1111                    plan.string(name)
1112                )));
1113            }
1114            Expr::Case { arms, otherwise } => {
1115                let mut prepared = Vec::new();
1116                for &arm in plan.arm_list(arms) {
1117                    prepared.push(PreparedArm {
1118                        when: Self::one(plan, arm.when, schema)?,
1119                        then: Self::one(plan, arm.then, schema)?,
1120                    });
1121                }
1122                let otherwise = match otherwise {
1123                    Some(otherwise) => Some(Self::one(plan, otherwise, schema)?),
1124                    None => None,
1125                };
1126                let blend = blending(&ty, &prepared, otherwise.as_ref());
1127                Step::Case { arms: prepared, otherwise, blend }
1128            }
1129        };
1130        self.steps.push(step);
1131        self.types.push(ty);
1132        self.spans.push(plan.expr_span(expr));
1133        let step = self.steps.len() - 1;
1134        if self.share {
1135            self.shared.insert(expr, step);
1136        }
1137        Ok(step)
1138    }
1139
1140    /// Flattens a list of expressions and records where its operand run starts and how long it is.
1141    ///
1142    /// The operand run is written after every child has been flattened rather than as they go,
1143    /// because a child that is itself a list would otherwise interleave its run with this one.
1144    fn push_list(
1145        &mut self,
1146        plan: &Plan,
1147        exprs: &[ExprRef],
1148        schema: &Schema,
1149    ) -> Result<(usize, usize)> {
1150        let mut indices = Vec::with_capacity(exprs.len());
1151        for &expr in exprs {
1152            indices.push(self.push(plan, expr, schema)?);
1153        }
1154        let start = self.operands.len();
1155        let len = indices.len();
1156        self.operands.extend(indices);
1157        Ok((start, len))
1158    }
1159
1160    /// This connective folded back into the `IN` the user wrote, or `None` when it is not one.
1161    ///
1162    /// What the binder writes for `x IN (1, 2, 3)` is `x = 1 OR x = 2 OR x = 3`, and for
1163    /// `x NOT IN (1, 2, 3)` it is `x <> 1 AND x <> 2 AND x <> 3`. So the shape looked for is every
1164    /// child a comparison of the one direction, every left the same expression, and every right a
1165    /// literal. Anything else is left alone, which covers the `OR` that was written as an `OR` and
1166    /// the one where an `IN` has been flattened together with another branch. The second is a fold
1167    /// this could make and does not, and it is worth having later out of a query that wants it
1168    /// rather than now out of a guess.
1169    ///
1170    /// This runs before the children are pushed, and that is the whole reason it is here rather than
1171    /// as a pass over the finished array. A step that nothing reads is still a step the walk runs,
1172    /// because the walk over a subtree is a range and not a graph, so folding after the fact would
1173    /// leave every equality in place and running.
1174    fn membership(
1175        &mut self,
1176        plan: &Plan,
1177        op: Connective,
1178        children: &[ExprRef],
1179        schema: &Schema,
1180    ) -> Result<Option<Step>> {
1181        let wanted = match op {
1182            Connective::Or => CompareOp::Equal,
1183            Connective::And => CompareOp::NotEqual,
1184        };
1185        let mut subject: Option<ExprRef> = None;
1186        let mut values = Vec::with_capacity(children.len());
1187        for &child in children {
1188            let Expr::Compare { op: found, left, right } = *plan.expr(child) else {
1189                return Ok(None);
1190            };
1191            if found != wanted || !same(plan, *subject.get_or_insert(left), left) {
1192                return Ok(None);
1193            }
1194            let Expr::Constant(reference) = *plan.expr(right) else {
1195                return Ok(None);
1196            };
1197            values.push(plan.value(reference).clone());
1198        }
1199        let (Some(subject), Some(members)) = (subject, Members::of(&values, op == Connective::And))
1200        else {
1201            return Ok(None);
1202        };
1203        Ok(Some(Step::InSet { input: self.push(plan, subject, schema)?, members }))
1204    }
1205
1206    /// The literal side of a comparison, in the one row column the comparison reads it through.
1207    ///
1208    /// The right side first, because that is the side the binder puts a literal on and the side the
1209    /// loops are written for. Two literals is a comparison the optimizer folded, and if it did not
1210    /// then the kernel answers it once for the whole vector and never reads either column, so
1211    /// neither side is built here.
1212    fn held(&self, left: usize, right: usize) -> Option<Held> {
1213        let (at, other) = match (&self.steps[left], &self.steps[right]) {
1214            (Step::Constant(_), Step::Constant(_)) => return None,
1215            (_, Step::Constant(value)) => (right, value),
1216            (Step::Constant(value), _) => (left, value),
1217            _ => return None,
1218        };
1219        Held::of(&self.types[at], other)
1220    }
1221
1222    /// The literal behind each argument in a run of the operand list, and `None` for an argument
1223    /// that is anything else.
1224    ///
1225    /// This is what a [`Recipe`] hoists from. An argument that is a literal in the plan arrives as a
1226    /// constant vector holding exactly this value on every chunk, so what a kernel reads here is
1227    /// what it would have read per chunk. An argument that is a cast of a literal reads as `None`,
1228    /// which is a call the kernel decides per chunk as it always did, and the optimizer folds most
1229    /// of those before the plan gets here anyway.
1230    fn literals(&self, start: usize, len: usize) -> Vec<Option<Value>> {
1231        self.operands[start..start + len]
1232            .iter()
1233            .map(|&operand| match &self.steps[operand] {
1234                Step::Constant(value) => Some(value.clone()),
1235                _ => None,
1236            })
1237            .collect()
1238    }
1239}
1240
1241/// Whether two expressions of one plan are the same expression, written once or written twice.
1242///
1243/// The binder binds the subject of an `IN` once and points every comparison it writes at that one
1244/// reference, so the answer is almost always the first line. A plan that has been through a rewrite,
1245/// and a plan read back from its own text, hold two copies of the same tree instead, and for the
1246/// fold in [`Prepared::membership`] those are the same expression.
1247///
1248/// The four shapes handled are what an `IN` is written over: a column, a literal, a cast of either,
1249/// and a call, which is TPC-H query 22 asking whether the first two digits of a phone number are in
1250/// a list. Anything else answers no, which costs a fold that could have happened rather than a wrong
1251/// one. The walk is bounded by the size of the subject and a subject is small.
1252fn same(plan: &Plan, left: ExprRef, right: ExprRef) -> bool {
1253    if left == right {
1254        return true;
1255    }
1256    if plan.expr_type(left) != plan.expr_type(right) {
1257        return false;
1258    }
1259    match (plan.expr(left), plan.expr(right)) {
1260        (Expr::Column(one), Expr::Column(other)) => one == other,
1261        (Expr::Constant(one), Expr::Constant(other)) => plan.value(*one) == plan.value(*other),
1262        (
1263            Expr::Cast { input: one, try_cast: first },
1264            Expr::Cast { input: other, try_cast: second },
1265        ) => first == second && same(plan, *one, *other),
1266        (
1267            Expr::Function { name: one, args: first },
1268            Expr::Function { name: other, args: second },
1269        ) => {
1270            let (first, second) = (plan.expr_list(*first), plan.expr_list(*second));
1271            plan.string(*one) == plan.string(*other)
1272                && first.len() == second.len()
1273                && first.iter().zip(second).all(|(&one, &other)| same(plan, one, other))
1274        }
1275        _ => false,
1276    }
1277}
1278
1279/// What touching a value of this type costs, against a fixed width one as the unit.
1280///
1281/// A variable length value is a pointer to follow and a length that is not the same twice, and a
1282/// nested one is that per element. Four is not measured, and what it has to be is large enough that
1283/// the ordering puts a fixed width comparison in front of a string one and small enough that it does
1284/// not put one in front of a string comparison that rejects every row.
1285fn touching(ty: &LogicalType) -> f64 {
1286    match ty.physical() {
1287        PhysicalType::Varlen => 4.0,
1288        PhysicalType::List | PhysicalType::Array | PhysicalType::Struct => 8.0,
1289        _ => 1.0,
1290    }
1291}
1292
1293/// The error for a slot that should have held something and did not.
1294///
1295/// This cannot happen while the array is in post order, since every operand's index is smaller than
1296/// the index of the step using it and every step runs in order. It is an error rather than a panic
1297/// because the property it depends on is a property of [`Prepared::push`], and the day somebody
1298/// writes a pass that reorders the array is the day it stops holding.
1299fn missing(index: usize) -> Error {
1300    Error::internal(format!("step {index} was used as an operand before it produced anything"))
1301}
1302
1303/// Chunk rows as the positions an [`Assembly`] places a piece at.
1304///
1305/// A chunk is at most [`VECTOR_SIZE`](rudb_vector::VECTOR_SIZE) rows, so the conversion cannot fail
1306/// in practice. It is checked rather than cast because a silent truncation here would put a value in
1307/// the wrong row, and a wrong row is the one kind of bug nothing downstream can notice.
1308fn placed(rows: &[usize]) -> Result<Vec<u32>> {
1309    rows.iter()
1310        .map(|&row| {
1311            u32::try_from(row).map_err(|_| Error::internal("a chunk of more than u32 rows"))
1312        })
1313        .collect()
1314}
1315
1316/// A `CASE` answered as codes over the dictionary its branches share, or `None` for a chunk that
1317/// cannot be.
1318///
1319/// Declined per chunk rather than once, because whether a column arrives coded is a fact about the
1320/// chunk and not about the expression. The same query reads codes out of a native file and plain
1321/// strings out of rows held in memory, and one file can hand a column over as a dictionary in one
1322/// part and as plain data in the next. Everything that declines does so before a code is written, so
1323/// the caller starts the general path from nothing rather than from a half filled answer.
1324fn blended(chunk: &Chunk, claimed: &[Vec<usize>], blend: &Blend) -> Result<Option<Vector>> {
1325    let Some((dictionary, literals)) = agreed(chunk, blend)? else { return Ok(None) };
1326    let mut codes = vec![0; chunk.len()];
1327    for (branch, rows) in blend.branches.iter().zip(claimed) {
1328        match *branch {
1329            Branch::Column(position) => {
1330                let Some((from, _)) = chunk.column(position)?.stable_dictionary_parts() else {
1331                    return Ok(None);
1332                };
1333                for &row in rows {
1334                    codes[row] = from[row];
1335                }
1336            }
1337            Branch::Literal(at) => {
1338                for &row in rows {
1339                    codes[row] = literals[at];
1340                }
1341            }
1342        }
1343    }
1344    Vector::stable_dictionary(codes, dictionary).map(Some)
1345}
1346
1347/// The one dictionary every branch of a blend names values in, and the code each literal sits at.
1348///
1349/// Three things say no. A column that did not arrive as a stable dictionary has no codes to copy. A
1350/// second column over a different dictionary would have codes that mean something else, and a code
1351/// is a position in one dictionary and nothing anywhere else. And a literal the dictionary does not
1352/// hold has no code at all, which for `ELSE ''` over a column where no row is empty is the honest
1353/// answer rather than a missing one.
1354///
1355/// The null check is the fourth. A dictionary keeps its nulls in the values it points at rather than
1356/// beside its codes, so a column carrying its own validity is one whose codes do not say everything
1357/// the column says, and copying them would turn its nulls into whatever their codes happen to name.
1358fn agreed(chunk: &Chunk, blend: &Blend) -> Result<Option<(Arc<Vector>, Vec<u32>)>> {
1359    let mut held: Option<(&Vector, &Arc<Vector>)> = None;
1360    for branch in &blend.branches {
1361        let Branch::Column(position) = *branch else { continue };
1362        let column = chunk.column(position)?;
1363        let Some((_, dictionary)) = column.stable_dictionary_parts() else { return Ok(None) };
1364        if column.validity().has_nulls(chunk.len()) {
1365            return Ok(None);
1366        }
1367        match held {
1368            Some((_, first)) if !Arc::ptr_eq(first, dictionary) => return Ok(None),
1369            Some(_) => {}
1370            None => held = Some((column, dictionary)),
1371        }
1372    }
1373    let Some((column, dictionary)) = held else { return Ok(None) };
1374    let mut codes = Vec::with_capacity(blend.literals.len());
1375    for (text, lookup) in &blend.literals {
1376        match lookup.find(column, text.as_bytes()) {
1377            Some(Ok(Found::At(code))) => codes.push(code),
1378            Some(Err(error)) => return Err(error),
1379            Some(Ok(Found::Absent)) | None => return Ok(None),
1380        }
1381    }
1382    Ok(Some((Arc::clone(dictionary), codes)))
1383}
1384
1385/// The blend a `CASE` can be answered by, or `None` for one that has to read its branches' values.
1386fn blending(ty: &LogicalType, arms: &[PreparedArm], otherwise: Option<&Prepared>) -> Option<Blend> {
1387    if !matches!(ty, LogicalType::Varchar) {
1388        return None;
1389    }
1390    let otherwise = otherwise?;
1391    let mut branches = Vec::with_capacity(arms.len() + 1);
1392    let mut literals = Vec::new();
1393    for branch in arms.iter().map(|arm| &arm.then).chain([otherwise]) {
1394        branches.push(named(branch, &mut literals)?);
1395    }
1396    // All of them literals means there is no dictionary to name any of them in, and a `CASE` whose
1397    // every branch is a constant is not a thing anybody writes.
1398    let any = branches.iter().any(|branch| matches!(branch, Branch::Column(_)));
1399    any.then_some(Blend { branches, literals })
1400}
1401
1402/// The branch a prepared expression stands for, when it names a value rather than computing one.
1403fn named(prepared: &Prepared, literals: &mut Vec<(String, Lookup)>) -> Option<Branch> {
1404    match prepared.steps.as_slice() {
1405        [Step::Column(position)] => Some(Branch::Column(*position)),
1406        [Step::Constant(Value::Varchar(text))] => {
1407            literals.push((text.clone(), Lookup::default()));
1408            Some(Branch::Literal(literals.len() - 1))
1409        }
1410        _ => None,
1411    }
1412}
1413
1414/// The chunk cut down to the given rows.
1415///
1416/// The reason `CASE` is written with this rather than by evaluating every arm over the whole chunk
1417/// and picking afterwards. `CASE WHEN x <> 0 THEN 1 // x ELSE 0 END` divides by zero on the rows the
1418/// arm does not apply to if the arm is evaluated for them, and a `CASE` that raises on a row it was
1419/// written to exclude is the classic wrong answer this shape prevents.
1420pub(crate) fn narrow(chunk: &Chunk, rows: &[usize]) -> Result<Chunk> {
1421    let mut selection = Selection::with_capacity(rows.len());
1422    for &row in rows {
1423        selection.push(row);
1424    }
1425    chunk.clone().select(&selection)
1426}
1427
1428/// The kernels' comparison for the plan's.
1429///
1430/// A translation rather than one shared enum, because the kernels are rank 3 and the plan is rank
1431/// 9. This function is the whole of what that separation costs.
1432pub(crate) fn comparison(op: CompareOp) -> Comparison {
1433    match op {
1434        CompareOp::Equal => Comparison::Equal,
1435        CompareOp::NotEqual => Comparison::NotEqual,
1436        CompareOp::Less => Comparison::Less,
1437        CompareOp::LessOrEqual => Comparison::LessOrEqual,
1438        CompareOp::Greater => Comparison::Greater,
1439        CompareOp::GreaterOrEqual => Comparison::GreaterOrEqual,
1440        CompareOp::DistinctFrom => Comparison::DistinctFrom,
1441        CompareOp::NotDistinctFrom => Comparison::NotDistinctFrom,
1442    }
1443}
1444
1445/// The kernels' connective for the plan's.
1446pub(crate) fn connective(op: ConjunctionOp) -> Connective {
1447    match op {
1448        ConjunctionOp::And => Connective::And,
1449        ConjunctionOp::Or => Connective::Or,
1450    }
1451}
1452
1453#[cfg(test)]
1454mod tests {
1455    use rudb_common::{Field, LogicalType, Value};
1456    use rudb_kernels::is_true;
1457    use rudb_plan::{ExprRef, Node, Plan};
1458    use rudb_vector::{Chunk, Selection, Vector};
1459
1460    use super::{Prepared, narrow};
1461    use crate::expr::evaluate;
1462    use crate::schema::Schema;
1463
1464    /// Two columns with a null in each, because every disagreement between these two evaluators
1465    /// that is worth finding is a disagreement about which rows are null.
1466    fn input() -> (Schema, Chunk) {
1467        let schema = Schema::numbered(
1468            vec![Field::new("x", LogicalType::Integer), Field::new("s", LogicalType::Varchar)],
1469            0,
1470        );
1471        let x = Vector::from_values(
1472            LogicalType::Integer,
1473            &[Value::Integer(3), Value::Integer(1), Value::Null, Value::Integer(2)],
1474        )
1475        .expect("four integers");
1476        let s = Vector::from_values(
1477            LogicalType::Varchar,
1478            &[
1479                Value::Varchar("a".to_string()),
1480                Value::Null,
1481                Value::Varchar("c".to_string()),
1482                Value::Varchar("a".to_string()),
1483            ],
1484        )
1485        .expect("four strings");
1486        (schema, Chunk::new(vec![x, s]).expect("two columns of four rows"))
1487    }
1488
1489    /// The expressions of a projection written in the plan's textual form, over the two columns
1490    /// [`input`] produces.
1491    ///
1492    /// Going through the text rather than the arena builders for the reason the other test module
1493    /// gives: a test that says what it evaluates in the notation a plan dump uses is a test whose
1494    /// failure can be pasted into a plan and vice versa.
1495    fn projection(exprs: &str) -> (Plan, Vec<ExprRef>) {
1496        let text =
1497            format!("Project #1 [{exprs}]\n  Get memory.main.t AS t #0 [x::INTEGER, s::VARCHAR]");
1498        let plan = Plan::parse(&text).expect("a well formed plan");
1499        let Node::Project { exprs, .. } = *plan.node(plan.root()) else {
1500            panic!("the root of that text is a projection");
1501        };
1502        let list = plan.expr_list(exprs).to_vec();
1503        (plan, list)
1504    }
1505
1506    /// Every expression shape, evaluated both ways over the same chunk.
1507    ///
1508    /// This is the agreement the module documentation claims and it is the only thing that makes
1509    /// the prepared form safe to put in front of the tree walk. The generated well typed trees the
1510    /// test gate of #57 asks for are a wider version of this and are worth building once the
1511    /// selection threaded shapes exist to disagree about.
1512    fn agrees(exprs: &str) {
1513        let (schema, chunk) = input();
1514        let (plan, list) = projection(exprs);
1515        let prepared = Prepared::new(&plan, &list, &schema).expect("the expressions resolve");
1516        let mut scratch = prepared.scratch();
1517        let mut fast = Vec::new();
1518        prepared.evaluate(&chunk, &mut scratch, &mut fast).expect("the prepared form runs");
1519        for (at, &expr) in list.iter().enumerate() {
1520            let slow = evaluate(&plan, expr, &schema, &chunk).expect("the tree walk runs");
1521            for row in 0..chunk.len() {
1522                assert_eq!(
1523                    fast[at].value_at(row),
1524                    slow.value_at(row),
1525                    "expression {at} of `{exprs}` at row {row}"
1526                );
1527            }
1528        }
1529    }
1530
1531    #[test]
1532    fn a_column_reference_agrees() {
1533        agrees("#0.0::INTEGER AS a, #0.1::VARCHAR AS b");
1534    }
1535
1536    #[test]
1537    fn a_constant_agrees() {
1538        agrees("7::INTEGER AS a, NULL::INTEGER AS b");
1539    }
1540
1541    #[test]
1542    fn a_cast_agrees() {
1543        agrees("CAST(#0.0::INTEGER)::BIGINT AS a, CAST(#0.0::INTEGER)::VARCHAR AS b");
1544    }
1545
1546    #[test]
1547    fn a_comparison_agrees() {
1548        agrees("(#0.0::INTEGER > 1::INTEGER)::BOOLEAN AS a");
1549    }
1550
1551    #[test]
1552    fn a_conjunction_agrees() {
1553        agrees(
1554            "((#0.0::INTEGER > 1::INTEGER)::BOOLEAN AND (#0.0::INTEGER < 3::INTEGER)::BOOLEAN)\
1555             ::BOOLEAN AS a",
1556        );
1557    }
1558
1559    #[test]
1560    fn a_function_agrees() {
1561        agrees("\"+\"(#0.0::INTEGER, 1::INTEGER)::INTEGER AS a");
1562    }
1563
1564    /// The two evaluators quote the same expression when a divisor is zero. Per #262.
1565    ///
1566    /// This is the one message in the engine that depends on how an expression is written rather
1567    /// than on what it computes, and the two evaluators render it at different times: the prepared
1568    /// form when the pipeline is built, the tree walk on the row that fails. Same renderer, so the
1569    /// same sentence, and this is what says so.
1570    #[test]
1571    fn both_evaluators_quote_the_same_expression_when_a_divisor_is_zero() {
1572        let (schema, chunk) = input();
1573        let (plan, list) = projection("\"//\"(#0.0::INTEGER, 0::INTEGER)::INTEGER AS a");
1574        let prepared = Prepared::new(&plan, &list, &schema).expect("the expression resolves");
1575        let mut scratch = prepared.scratch();
1576        let mut out = Vec::new();
1577        let fast = prepared.evaluate(&chunk, &mut scratch, &mut out).expect_err("divides by zero");
1578        let slow = evaluate(&plan, list[0], &schema, &chunk).expect_err("divides by zero");
1579        assert_eq!(fast.message(), slow.message());
1580        assert!(fast.message().starts_with("Division by zero in expression (x // 0)."), "{fast}");
1581    }
1582
1583    #[test]
1584    fn a_case_agrees() {
1585        agrees(
1586            "CASE WHEN (#0.0::INTEGER > 1::INTEGER)::BOOLEAN THEN 10::INTEGER \
1587             ELSE 20::INTEGER END::INTEGER AS a",
1588        );
1589    }
1590
1591    /// A second arm, which is the first one that sees a cut chunk rather than the whole one.
1592    ///
1593    /// The first arm of any `CASE` runs over every row, so it takes the path that does not cut at
1594    /// all, and a `CASE` of one arm never exercises the other one. Two arms and an `ELSE` puts a
1595    /// different set of rows in front of each of the three.
1596    ///
1597    /// That this is the only test here reaching the cut was checked rather than assumed, by gating a
1598    /// panic on it and rerunning the seven. This one failed and the other six did not.
1599    #[test]
1600    fn a_case_of_two_arms_agrees() {
1601        agrees(
1602            "CASE WHEN (#0.0::INTEGER > 2::INTEGER)::BOOLEAN THEN 10::INTEGER \
1603             WHEN (#0.0::INTEGER > 1::INTEGER)::BOOLEAN THEN 20::INTEGER \
1604             ELSE 30::INTEGER END::INTEGER AS a",
1605        );
1606    }
1607
1608    /// No `ELSE`, so the rows no arm claims are null rather than anything.
1609    ///
1610    /// The case a run of data with a hole in it gets wrong: a null still occupies a position, and an
1611    /// assembly that skipped it would put every value after it one row early.
1612    #[test]
1613    fn a_case_with_no_else_agrees() {
1614        agrees(
1615            "CASE WHEN (#0.0::INTEGER > 2::INTEGER)::BOOLEAN THEN 10::INTEGER \
1616             END::INTEGER AS a",
1617        );
1618    }
1619
1620    /// An arm no row takes, so it contributes nothing to the answer and must not shift it.
1621    #[test]
1622    fn a_case_whose_arm_claims_nothing_agrees() {
1623        agrees(
1624            "CASE WHEN (#0.0::INTEGER > 99::INTEGER)::BOOLEAN THEN 10::INTEGER \
1625             ELSE 20::INTEGER END::INTEGER AS a",
1626        );
1627    }
1628
1629    /// Strings, which is the case that used to allocate one of them per row and drop it afterwards.
1630    ///
1631    /// The arm reads a column and the `ELSE` is a constant, which is the shape of the ClickBench
1632    /// query this path was rewritten for: the arm arrives as views over an arena and the `ELSE` as
1633    /// one value repeated, and the two have to be laid end to end into a single arena.
1634    #[test]
1635    fn a_case_over_strings_agrees() {
1636        agrees(
1637            "CASE WHEN (#0.0::INTEGER > 1::INTEGER)::BOOLEAN THEN #0.1::VARCHAR \
1638             ELSE ''::VARCHAR END::VARCHAR AS a",
1639        );
1640    }
1641
1642    /// A null inside an arm, which is a different thing from a row no arm claimed.
1643    ///
1644    /// Both come out null and they reach the validity mask by different routes, so a mask built for
1645    /// one of them and not the other reads correct on whichever test only has the other in it.
1646    #[test]
1647    fn a_case_whose_arm_answers_null_agrees() {
1648        agrees(
1649            "CASE WHEN (#0.0::INTEGER > 1::INTEGER)::BOOLEAN THEN #0.1::VARCHAR \
1650             ELSE NULL::VARCHAR END::VARCHAR AS a",
1651        );
1652    }
1653
1654    /// A `WHEN` over a column that is null on some rows, which is neither true nor false there.
1655    ///
1656    /// A three valued `WHEN` is what decides whether a row goes to the arm or falls through, and
1657    /// treating unknown as true would claim a row the `ELSE` should have had.
1658    #[test]
1659    fn a_case_whose_test_is_null_on_some_rows_agrees() {
1660        agrees(
1661            "CASE WHEN (#0.1::VARCHAR = 'a'::VARCHAR)::BOOLEAN THEN 10::INTEGER \
1662             ELSE 20::INTEGER END::INTEGER AS a",
1663        );
1664    }
1665
1666    /// The same expression twice, which is where the tree walk copies the column twice and this
1667    /// does not, and the answers still have to be identical.
1668    #[test]
1669    fn a_column_mentioned_three_times_agrees() {
1670        agrees("\"+\"(\"+\"(#0.0::INTEGER, #0.0::INTEGER)::INTEGER, #0.0::INTEGER)::INTEGER AS a");
1671    }
1672
1673    /// The intermediates of a chain are not all held to the end of it.
1674    ///
1675    /// This is the whole difference between the prepared form being faster than the tree walk on a
1676    /// deep chain and being slower than it, and it is a property of the slot array rather than of
1677    /// any answer, so it is asserted here rather than left to the benchmark to catch.
1678    #[test]
1679    fn a_chain_holds_one_intermediate_at_a_time() {
1680        let (schema, chunk) = input();
1681        let mut expr = "#0.0::INTEGER".to_string();
1682        for _ in 0..8 {
1683            expr = format!("\"+\"({expr}, 1::INTEGER)::INTEGER");
1684        }
1685        let (plan, list) = projection(&format!("{expr} AS a"));
1686        let prepared = Prepared::new(&plan, &list, &schema).expect("the chain resolves");
1687        let mut scratch = prepared.scratch();
1688        prepared.run(&chunk, &mut scratch).expect("the chain runs");
1689        let live = scratch.slots.iter().filter(|slot| slot.is_some()).count();
1690        assert_eq!(live, 1, "a chain that has run should be holding its answer and nothing else");
1691    }
1692
1693    /// The rows a threaded filter keeps are the rows the tree walk says the predicate is true for.
1694    ///
1695    /// Every threaded conjunct is a chance to disagree with the unthreaded answer about a null,
1696    /// about a row an earlier conjunct had already dropped, or about a chunk nothing survives, and
1697    /// the answer is a set of row numbers rather than a vector, so this is checked against the tree
1698    /// walk read a row at a time rather than against the prepared form it is part of.
1699    fn filters(predicate: &str) {
1700        let (schema, chunk) = input();
1701        let (plan, list) = projection(&format!("{predicate} AS p"));
1702        let prepared = Prepared::new(&plan, &list, &schema).expect("the predicate resolves");
1703        let mut scratch = prepared.scratch();
1704        let threaded = prepared.evaluate_filter(&chunk, &mut scratch).expect("the filter runs");
1705        let flags = evaluate(&plan, list[0], &schema, &chunk).expect("the tree walk runs");
1706        let expected = Selection::from_predicate(chunk.len(), |row| is_true(&flags.value_at(row)));
1707        assert_eq!(threaded, expected, "`{predicate}`");
1708        // And running it again over the same scratch is the same answer, because a pipeline calls
1709        // this once a chunk and a slot left behind by the conjunct before would show up here.
1710        let again = prepared.evaluate_filter(&chunk, &mut scratch).expect("the filter runs again");
1711        assert_eq!(again, expected, "`{predicate}` a second time");
1712    }
1713
1714    /// A predicate with no `AND` in it is not threaded and has to keep saying the same thing.
1715    #[test]
1716    fn a_single_comparison_filters_the_same_rows() {
1717        filters("(#0.0::INTEGER > 1::INTEGER)::BOOLEAN");
1718        filters("(#0.1::VARCHAR = 'a'::VARCHAR)::BOOLEAN");
1719        filters("(#0.0::INTEGER IS NOT DISTINCT FROM NULL::INTEGER)::BOOLEAN");
1720    }
1721
1722    #[test]
1723    fn a_chain_of_conjuncts_keeps_what_all_of_them_keep() {
1724        filters(
1725            "((#0.0::INTEGER > 1::INTEGER)::BOOLEAN AND (#0.0::INTEGER < 3::INTEGER)::BOOLEAN)\
1726             ::BOOLEAN",
1727        );
1728        filters(
1729            "((#0.0::INTEGER >= 1::INTEGER)::BOOLEAN AND (#0.0::INTEGER <= 3::INTEGER)::BOOLEAN \
1730             AND (#0.1::VARCHAR = 'a'::VARCHAR)::BOOLEAN AND (#0.0::INTEGER <> 2::INTEGER)\
1731             ::BOOLEAN)::BOOLEAN",
1732        );
1733    }
1734
1735    /// A conjunct that rejects every row, in front of one that would have kept some. The rows are
1736    /// the same either way and the point of the shape is that the second conjunct never runs.
1737    #[test]
1738    fn a_conjunct_that_keeps_nothing_ends_the_predicate() {
1739        filters(
1740            "((#0.0::INTEGER > 9::INTEGER)::BOOLEAN AND (#0.0::INTEGER < 9::INTEGER)::BOOLEAN)\
1741             ::BOOLEAN",
1742        );
1743    }
1744
1745    /// A conjunct whose operands are computed rather than read, which is the shape where the
1746    /// comparison is threaded and the arithmetic under it is not.
1747    #[test]
1748    fn a_conjunct_over_a_computed_operand_keeps_the_same_rows() {
1749        filters(
1750            "((#0.0::INTEGER > 1::INTEGER)::BOOLEAN AND \
1751             (\"+\"(#0.0::INTEGER, 1::INTEGER)::INTEGER < 4::INTEGER)::BOOLEAN)::BOOLEAN",
1752        );
1753    }
1754
1755    /// A conjunct that is not a comparison at all, which is the one that goes through the flag
1756    /// kernel rather than the comparison kernel.
1757    #[test]
1758    fn a_conjunct_that_is_not_a_comparison_is_threaded_too() {
1759        filters(
1760            "((#0.0::INTEGER > 1::INTEGER)::BOOLEAN AND ((#0.1::VARCHAR = 'a'::VARCHAR)::BOOLEAN \
1761             OR (#0.0::INTEGER = 1::INTEGER)::BOOLEAN)::BOOLEAN)::BOOLEAN",
1762        );
1763        filters(
1764            "(((#0.1::VARCHAR = 'c'::VARCHAR)::BOOLEAN OR (#0.0::INTEGER = 3::INTEGER)::BOOLEAN)\
1765             ::BOOLEAN AND (#0.0::INTEGER <> 1::INTEGER)::BOOLEAN)::BOOLEAN",
1766        );
1767    }
1768
1769    #[test]
1770    fn a_selective_conjunct_evaluates_later_like_on_its_survivors() {
1771        filters(
1772            "((#0.0::INTEGER > 2::INTEGER)::BOOLEAN AND \
1773             \"~~\"(#0.1::VARCHAR, '%a%'::VARCHAR)::BOOLEAN)::BOOLEAN",
1774        );
1775        filters(
1776            "((#0.0::INTEGER > 2::INTEGER)::BOOLEAN AND \
1777             \"!~~\"(#0.1::VARCHAR, '%a%'::VARCHAR)::BOOLEAN)::BOOLEAN",
1778        );
1779    }
1780
1781    /// An `OR` at the top threads the complement: the second branch only sees the rows the first
1782    /// one did not accept, and the rows it accepts are added to them rather than replacing them.
1783    ///
1784    /// The input has a row where the first branch is true, one where the second is, one where both
1785    /// are false and one where the first is null and the second is true, which is the row that says
1786    /// whether the complement was taken over "not true" or over "false".
1787    #[test]
1788    fn an_or_at_the_top_threads_the_complement() {
1789        filters(
1790            "((#0.0::INTEGER > 2::INTEGER)::BOOLEAN OR (#0.1::VARCHAR = 'c'::VARCHAR)::BOOLEAN)\
1791             ::BOOLEAN",
1792        );
1793        filters(
1794            "((#0.0::INTEGER = 1::INTEGER)::BOOLEAN OR (#0.1::VARCHAR = 'a'::VARCHAR)::BOOLEAN \
1795             OR (#0.0::INTEGER > 2::INTEGER)::BOOLEAN)::BOOLEAN",
1796        );
1797    }
1798
1799    /// A branch that accepts every row, in front of one that would have accepted none. The rows are
1800    /// the same either way and the point of the shape is that the second branch never runs.
1801    #[test]
1802    fn a_branch_that_keeps_everything_ends_the_predicate() {
1803        filters(
1804            "((#0.0::INTEGER IS NOT DISTINCT FROM #0.0::INTEGER)::BOOLEAN OR \
1805             (#0.0::INTEGER > 9::INTEGER)::BOOLEAN)::BOOLEAN",
1806        );
1807    }
1808
1809    /// The branches after one that has accepted every row really are skipped.
1810    ///
1811    /// Every other test here says the threaded answer matches the unthreaded one, which it would
1812    /// even if nothing were threaded at all. This one puts a division by zero behind a branch that
1813    /// accepts everything, so the predicate raises if the second branch runs and does not if the
1814    /// walk stopped where it was supposed to.
1815    #[test]
1816    fn a_branch_behind_one_that_accepted_every_row_does_not_run() {
1817        let (schema, chunk) = input();
1818        let predicate = "((#0.0::INTEGER IS NOT DISTINCT FROM #0.0::INTEGER)::BOOLEAN OR \
1819                         (\"//\"(#0.0::INTEGER, 0::INTEGER)::INTEGER > 0::INTEGER)::BOOLEAN)\
1820                         ::BOOLEAN";
1821        let (plan, list) = projection(&format!("{predicate} AS p"));
1822        let prepared = Prepared::new(&plan, &list, &schema).expect("the predicate resolves");
1823        let mut scratch = prepared.scratch();
1824        let kept =
1825            prepared.evaluate_filter(&chunk, &mut scratch).expect("the second branch never runs");
1826        assert_eq!(kept, Selection::identity(chunk.len()));
1827        // And the same predicate evaluated as an expression does divide by zero, which is what says
1828        // the test is testing the threading rather than a predicate that happens not to raise.
1829        evaluate(&plan, list[0], &schema, &chunk).expect_err("the tree walk divides by zero");
1830    }
1831
1832    /// The conjunct that rejects the most rows ends up in front of the one that rejects none.
1833    ///
1834    /// The predicate is written the wrong way round on purpose. The plan order costs two passes a
1835    /// chunk where one would do, and after a chunk of watching it the filter runs the selective one
1836    /// first and the other one stops running at all.
1837    #[test]
1838    fn a_filter_learns_which_conjunct_to_run_first() {
1839        let (schema, chunk) = input();
1840        let predicate = "((#0.0::INTEGER > 0::INTEGER)::BOOLEAN AND (#0.0::INTEGER > 9::INTEGER)\
1841                         ::BOOLEAN)::BOOLEAN";
1842        let (plan, list) = projection(&format!("{predicate} AS p"));
1843        let prepared = Prepared::new(&plan, &list, &schema).expect("the predicate resolves");
1844        let mut scratch = prepared.scratch();
1845        let root = prepared.roots[0];
1846        assert_eq!(scratch.order(root), None, "nothing has run yet");
1847        let kept = prepared.evaluate_filter(&chunk, &mut scratch).expect("the filter runs");
1848        assert!(kept.is_empty());
1849        assert_eq!(scratch.order(root), Some(&[1, 0][..]), "the second conjunct rejects the most");
1850        // And it stays there, because the conjunct that now runs first empties the selection and
1851        // the one behind it keeps the history it already had rather than losing it.
1852        let kept = prepared.evaluate_filter(&chunk, &mut scratch).expect("the filter runs again");
1853        assert!(kept.is_empty());
1854        assert_eq!(scratch.order(root), Some(&[1, 0][..]));
1855    }
1856
1857    /// Whatever order it settles on, the rows are the rows.
1858    ///
1859    /// Run for longer than the window is wide, because an order that changes halfway through a scan
1860    /// is the shape where a walk that got the subtree bookkeeping wrong would start reading the
1861    /// wrong steps, and the first chunk would not show it.
1862    #[test]
1863    fn reordering_never_changes_which_rows_survive() {
1864        let (schema, chunk) = input();
1865        let predicate = "((#0.0::INTEGER >= 1::INTEGER)::BOOLEAN AND \
1866                         (\"+\"(#0.0::INTEGER, 1::INTEGER)::INTEGER < 4::INTEGER)::BOOLEAN AND \
1867                         (#0.1::VARCHAR = 'a'::VARCHAR)::BOOLEAN)::BOOLEAN";
1868        let (plan, list) = projection(&format!("{predicate} AS p"));
1869        let prepared = Prepared::new(&plan, &list, &schema).expect("the predicate resolves");
1870        let mut scratch = prepared.scratch();
1871        let flags = evaluate(&plan, list[0], &schema, &chunk).expect("the tree walk runs");
1872        let expected = Selection::from_predicate(chunk.len(), |row| is_true(&flags.value_at(row)));
1873        for round in 0..40 {
1874            let kept = prepared.evaluate_filter(&chunk, &mut scratch).expect("the filter runs");
1875            assert_eq!(kept, expected, "round {round}");
1876        }
1877    }
1878
1879    /// A nested connective is threaded rather than evaluated into flags.
1880    ///
1881    /// The inner `AND` keeps nothing, so its second conjunct is never reached and the division by
1882    /// zero in it never happens. Evaluating the branch as an expression and narrowing the flags
1883    /// afterwards, which is what an operand that is not a connective still does, would have run it.
1884    #[test]
1885    fn a_nested_connective_stops_where_the_outer_one_would() {
1886        let (schema, chunk) = input();
1887        let predicate = "((#0.0::INTEGER > 9::INTEGER)::BOOLEAN OR ((#0.0::INTEGER > 9::INTEGER)\
1888                         ::BOOLEAN AND (\"//\"(#0.0::INTEGER, 0::INTEGER)::INTEGER > 0::INTEGER)\
1889                         ::BOOLEAN)::BOOLEAN)::BOOLEAN";
1890        let (plan, list) = projection(&format!("{predicate} AS p"));
1891        let prepared = Prepared::new(&plan, &list, &schema).expect("the predicate resolves");
1892        let mut scratch = prepared.scratch();
1893        let kept =
1894            prepared.evaluate_filter(&chunk, &mut scratch).expect("the division never happens");
1895        assert!(kept.is_empty());
1896        evaluate(&plan, list[0], &schema, &chunk).expect_err("the tree walk divides by zero");
1897    }
1898
1899    /// A branch that is not a comparison, which is the one that goes through the flag kernel.
1900    #[test]
1901    fn an_or_branch_that_is_not_a_comparison_is_threaded_too() {
1902        filters(
1903            "((#0.0::INTEGER > 2::INTEGER)::BOOLEAN OR \
1904             \"~~\"(#0.1::VARCHAR, 'a%'::VARCHAR)::BOOLEAN)::BOOLEAN",
1905        );
1906        filters(
1907            "(\"~~\"(#0.1::VARCHAR, 'c%'::VARCHAR)::BOOLEAN OR (#0.0::INTEGER = 1::INTEGER)\
1908             ::BOOLEAN)::BOOLEAN",
1909        );
1910    }
1911
1912    /// A connective inside a connective, which recurses rather than falling back to flags.
1913    ///
1914    /// Both nestings, because the two carry opposite things: an `AND` under an `OR` starts from the
1915    /// rows no branch has accepted, and an `OR` under an `AND` starts from the rows every conjunct
1916    /// has kept, and getting either one backwards is a wrong set of rows.
1917    #[test]
1918    fn a_connective_inside_a_connective_threads_both_ways() {
1919        filters(
1920            "(((#0.0::INTEGER >= 2::INTEGER)::BOOLEAN AND (#0.1::VARCHAR = 'a'::VARCHAR)::BOOLEAN)\
1921             ::BOOLEAN OR ((#0.0::INTEGER < 2::INTEGER)::BOOLEAN AND (#0.1::VARCHAR <> 'c'\
1922             ::VARCHAR)::BOOLEAN)::BOOLEAN)::BOOLEAN",
1923        );
1924        filters(
1925            "(((#0.1::VARCHAR = 'c'::VARCHAR)::BOOLEAN OR (#0.0::INTEGER = 3::INTEGER)::BOOLEAN)\
1926             ::BOOLEAN AND ((#0.0::INTEGER <> 1::INTEGER)::BOOLEAN OR (#0.1::VARCHAR = 'a'\
1927             ::VARCHAR)::BOOLEAN)::BOOLEAN)::BOOLEAN",
1928        );
1929        // Three deep, since two levels is where an off by one in the subtree bookkeeping can still
1930        // be hidden by the ranges lining up.
1931        filters(
1932            "((#0.0::INTEGER > 9::INTEGER)::BOOLEAN OR ((#0.0::INTEGER >= 1::INTEGER)::BOOLEAN \
1933             AND ((#0.1::VARCHAR = 'a'::VARCHAR)::BOOLEAN OR (#0.0::INTEGER = 1::INTEGER)\
1934             ::BOOLEAN)::BOOLEAN)::BOOLEAN)::BOOLEAN",
1935        );
1936    }
1937
1938    /// A predicate where one side is null and the other is true, in both orders. `OR` is true there
1939    /// and a complement taken over the rows a branch rejected rather than the rows it accepted
1940    /// would drop the row, which is the one way this can be wrong and is not a wrong vector but a
1941    /// missing row.
1942    #[test]
1943    fn a_null_branch_beside_a_true_one_keeps_the_row() {
1944        filters(
1945            "((#0.0::INTEGER > 2::INTEGER)::BOOLEAN OR (#0.1::VARCHAR = 'c'::VARCHAR)::BOOLEAN \
1946             OR (#0.0::INTEGER IS NOT DISTINCT FROM NULL::INTEGER)::BOOLEAN)::BOOLEAN",
1947        );
1948        filters(
1949            "((#0.1::VARCHAR > 'b'::VARCHAR)::BOOLEAN OR (#0.0::INTEGER = 1::INTEGER)::BOOLEAN)\
1950             ::BOOLEAN",
1951        );
1952    }
1953
1954    /// A filter over a chunk that has already been narrowed, which is what a second filter in a
1955    /// pipeline sees and is the form pair the threaded kernels have to handle rather than fall
1956    /// through on.
1957    #[test]
1958    fn a_filter_over_a_selected_chunk_keeps_the_same_rows() {
1959        let (schema, chunk) = input();
1960        let predicate = "((#0.0::INTEGER >= 1::INTEGER)::BOOLEAN AND \
1961                         (#0.1::VARCHAR = 'a'::VARCHAR)::BOOLEAN)::BOOLEAN";
1962        let (plan, list) = projection(&format!("{predicate} AS p"));
1963        let prepared = Prepared::new(&plan, &list, &schema).expect("the predicate resolves");
1964        let mut scratch = prepared.scratch();
1965        let narrowed = narrow(&chunk, &[0, 3]).expect("two of the four rows");
1966        let threaded = prepared.evaluate_filter(&narrowed, &mut scratch).expect("the filter runs");
1967        let flags = evaluate(&plan, list[0], &schema, &narrowed).expect("the tree walk runs");
1968        let expected =
1969            Selection::from_predicate(narrowed.len(), |row| is_true(&flags.value_at(row)));
1970        assert_eq!(threaded, expected);
1971    }
1972
1973    /// Preparing is per pipeline and evaluating is per chunk, so the scratch has to survive being
1974    /// used again and give the same answer the second time.
1975    #[test]
1976    fn a_scratch_used_twice_gives_the_same_answer_twice() {
1977        let (schema, chunk) = input();
1978        let (plan, list) = projection("\"+\"(#0.0::INTEGER, 1::INTEGER)::INTEGER AS a");
1979        let prepared = Prepared::new(&plan, &list, &schema).expect("the expressions resolve");
1980        let mut scratch = prepared.scratch();
1981        let mut once = Vec::new();
1982        prepared.evaluate(&chunk, &mut scratch, &mut once).expect("the first chunk runs");
1983        let mut twice = Vec::new();
1984        prepared.evaluate(&chunk, &mut scratch, &mut twice).expect("the second chunk runs");
1985        assert_eq!(once, twice);
1986    }
1987
1988    #[test]
1989    fn a_shared_computed_root_is_compiled_once() {
1990        let (schema, chunk) = input();
1991        let (plan, list) = projection("\"+\"(#0.0::INTEGER, 1::INTEGER)::INTEGER AS a");
1992        let prepared = Prepared::shared(&plan, &[list[0], list[0]], &schema)
1993            .expect("the shared expression resolves");
1994        assert_eq!(prepared.steps.len(), 3);
1995        let mut scratch = prepared.scratch();
1996        let mut answers = Vec::new();
1997        prepared.evaluate(&chunk, &mut scratch, &mut answers).expect("both roots are returned");
1998        assert_eq!(answers[0], answers[1]);
1999    }
2000
2001    /// A chunk shorter than the last one, because a scan's final chunk is that and a constant
2002    /// materialized to the wrong length would be an out of range read rather than a wrong answer.
2003    #[test]
2004    fn a_shorter_chunk_after_a_longer_one_is_evaluated_at_its_own_length() {
2005        let (schema, chunk) = input();
2006        let (plan, list) = projection("7::INTEGER AS a");
2007        let prepared = Prepared::new(&plan, &list, &schema).expect("the expressions resolve");
2008        let mut scratch = prepared.scratch();
2009        let mut full = Vec::new();
2010        prepared.evaluate(&chunk, &mut scratch, &mut full).expect("the full chunk runs");
2011        assert_eq!(full[0].len(), 4);
2012        let short = chunk
2013            .clone()
2014            .select(&{
2015                let mut selection = Selection::with_capacity(2);
2016                selection.push(0);
2017                selection.push(2);
2018                selection
2019            })
2020            .expect("two of the four rows");
2021        let mut cut = Vec::new();
2022        prepared.evaluate(&short, &mut scratch, &mut cut).expect("the short chunk runs");
2023        assert_eq!(cut[0].len(), 2);
2024    }
2025
2026    /// An aggregate is not an expression and saying so when the pipeline is built is better than
2027    /// saying it on the first chunk.
2028    #[test]
2029    fn an_aggregate_is_refused_when_it_is_prepared() {
2030        let (schema, _) = input();
2031        let text = "Aggregate #1 groups=[] aggregates=[sum(#0.0::INTEGER)::HUGEINT]\n  \
2032                    Get memory.main.t AS t #0 [x::INTEGER, s::VARCHAR]";
2033        let plan = Plan::parse(text).expect("a well formed plan");
2034        let Node::Aggregate { aggregates, .. } = *plan.node(plan.root()) else {
2035            panic!("the root of that text is an aggregate");
2036        };
2037        let list = plan.expr_list(aggregates).to_vec();
2038        let error = Prepared::new(&plan, &list, &schema).expect_err("sum is not a scalar");
2039        assert!(error.message().contains("sum"), "{error}");
2040    }
2041
2042    /// How many of an expression's function steps worked something out when it was prepared, and
2043    /// whether the answer it gives is still the tree walk's answer.
2044    ///
2045    /// The count is the point of the assertion, because an answer that moved would be a bug. The
2046    /// agreement is what says the answer did not move.
2047    fn prepares(expr: &str, lifted: usize) {
2048        let (schema, _) = input();
2049        let projected = format!("{expr} AS a");
2050        let (plan, list) = projection(&projected);
2051        let prepared = Prepared::new(&plan, &list, &schema).expect("the expression resolves");
2052        assert_eq!(prepared.hoisted(), lifted, "`{expr}`");
2053        agrees(&projected);
2054    }
2055
2056    /// A pattern the user wrote is compiled where the plan is, which is once.
2057    #[test]
2058    fn a_literal_pattern_is_compiled_when_the_pipeline_is_built() {
2059        prepares("\"~~\"(#0.1::VARCHAR, 'a%'::VARCHAR)::BOOLEAN", 1);
2060        prepares("\"~~*\"(#0.1::VARCHAR, '%A%'::VARCHAR)::BOOLEAN", 1);
2061    }
2062
2063    /// A regular expression, which is the one where the compiling is worth real time.
2064    ///
2065    /// ClickBench query 29 runs one pattern over a hundred million rows, which is a hundred thousand
2066    /// chunks, and before this each of those hundred thousand compiled the pattern again.
2067    #[test]
2068    fn a_regular_expression_is_compiled_when_the_pipeline_is_built() {
2069        prepares("\"regexp_matches\"(#0.1::VARCHAR, '^a'::VARCHAR)::BOOLEAN", 1);
2070        prepares("\"regexp_replace\"(#0.1::VARCHAR, 'a'::VARCHAR, 'b'::VARCHAR)::VARCHAR", 1);
2071    }
2072
2073    /// A pattern that is not a literal, which is legal SQL and is decided per chunk as it was.
2074    #[test]
2075    fn a_pattern_that_is_not_a_literal_is_left_to_the_chunk() {
2076        prepares("\"~~\"(#0.1::VARCHAR, #0.1::VARCHAR)::BOOLEAN", 0);
2077    }
2078
2079    /// A function with nothing to work out, which is almost all of them.
2080    #[test]
2081    fn a_function_with_no_prepare_step_prepares_nothing() {
2082        prepares("\"upper\"(#0.1::VARCHAR)::VARCHAR", 0);
2083    }
2084
2085    /// How many of an expression's steps are a folded `IN`, and whether the answer still agrees.
2086    fn folds(expr: &str, sets: usize) {
2087        let (schema, _) = input();
2088        let projected = format!("{expr} AS a");
2089        let (plan, list) = projection(&projected);
2090        let prepared = Prepared::new(&plan, &list, &schema).expect("the expression resolves");
2091        assert_eq!(prepared.sets(), sets, "`{expr}`");
2092        agrees(&projected);
2093    }
2094
2095    /// What the binder writes for `x IN (1, 3)`, folded back into one lookup.
2096    ///
2097    /// The test goes through the plan's text, where the three mentions of the column are three
2098    /// expressions rather than one, which is the case `same` exists for. A plan the binder built has
2099    /// one mention and takes the first line of it.
2100    #[test]
2101    fn an_in_list_becomes_one_lookup() {
2102        folds(
2103            "((#0.0::INTEGER = 1::INTEGER)::BOOLEAN OR (#0.0::INTEGER = 3::INTEGER)::BOOLEAN)\
2104             ::BOOLEAN",
2105            1,
2106        );
2107        folds(
2108            "((#0.1::VARCHAR = 'a'::VARCHAR)::BOOLEAN OR (#0.1::VARCHAR = 'z'::VARCHAR)::BOOLEAN)\
2109             ::BOOLEAN",
2110            1,
2111        );
2112    }
2113
2114    /// `NOT IN`, which the binder writes as an `AND` of inequalities and which reads the same
2115    /// lookup the other way round.
2116    #[test]
2117    fn a_not_in_list_becomes_the_same_lookup() {
2118        folds(
2119            "((#0.0::INTEGER <> 1::INTEGER)::BOOLEAN AND (#0.0::INTEGER <> 3::INTEGER)::BOOLEAN)\
2120             ::BOOLEAN",
2121            1,
2122        );
2123    }
2124
2125    /// A list with a null in it, which is the rule that makes an `IN` not a set lookup.
2126    ///
2127    /// A row that is not in the list is null rather than false, because it might have equalled the
2128    /// value the null stands for. `agrees` is what says the fold kept that, since the `OR` of
2129    /// comparisons it is checked against gets it from three valued logic for free.
2130    #[test]
2131    fn a_list_with_a_null_in_it_folds_and_keeps_the_null_rule() {
2132        folds(
2133            "((#0.0::INTEGER = 1::INTEGER)::BOOLEAN OR (#0.0::INTEGER = NULL::INTEGER)::BOOLEAN \
2134             OR (#0.0::INTEGER = 3::INTEGER)::BOOLEAN)::BOOLEAN",
2135            1,
2136        );
2137        folds(
2138            "((#0.0::INTEGER <> 1::INTEGER)::BOOLEAN AND (#0.0::INTEGER <> NULL::INTEGER)\
2139             ::BOOLEAN AND (#0.0::INTEGER <> 3::INTEGER)::BOOLEAN)::BOOLEAN",
2140            1,
2141        );
2142    }
2143
2144    /// The connectives that are not an `IN`, each for its own reason.
2145    #[test]
2146    fn a_connective_that_is_not_an_in_list_is_left_alone() {
2147        // Two different columns.
2148        folds(
2149            "((#0.0::INTEGER = 1::INTEGER)::BOOLEAN OR (#0.1::VARCHAR = 'a'::VARCHAR)::BOOLEAN)\
2150             ::BOOLEAN",
2151            0,
2152        );
2153        // One equality and one of something else.
2154        folds(
2155            "((#0.0::INTEGER = 1::INTEGER)::BOOLEAN OR (#0.0::INTEGER > 3::INTEGER)::BOOLEAN)\
2156             ::BOOLEAN",
2157            0,
2158        );
2159        // The right hand side is a column rather than a literal.
2160        folds(
2161            "((#0.0::INTEGER = 1::INTEGER)::BOOLEAN OR (#0.0::INTEGER = #0.0::INTEGER)::BOOLEAN)\
2162             ::BOOLEAN",
2163            0,
2164        );
2165        // An `AND` of equalities is not a `NOT IN`, it is a predicate that is false unless the two
2166        // literals are the same. Folding it as one would answer true where it answers false.
2167        folds(
2168            "((#0.0::INTEGER = 1::INTEGER)::BOOLEAN AND (#0.0::INTEGER = 3::INTEGER)::BOOLEAN)\
2169             ::BOOLEAN",
2170            0,
2171        );
2172    }
2173
2174    /// The same thing in a filter, which is the shape it is written in.
2175    #[test]
2176    fn an_in_list_filters_the_same_rows() {
2177        filters(
2178            "((#0.0::INTEGER = 1::INTEGER)::BOOLEAN OR (#0.0::INTEGER = 3::INTEGER)::BOOLEAN)\
2179             ::BOOLEAN",
2180        );
2181        filters(
2182            "((#0.0::INTEGER <> 1::INTEGER)::BOOLEAN AND (#0.0::INTEGER <> 3::INTEGER)::BOOLEAN)\
2183             ::BOOLEAN",
2184        );
2185        // Inside a larger predicate, where the fold is one operand of the connective above it.
2186        filters(
2187            "(((#0.0::INTEGER = 1::INTEGER)::BOOLEAN OR (#0.0::INTEGER = 3::INTEGER)::BOOLEAN)\
2188             ::BOOLEAN AND (#0.1::VARCHAR = 'a'::VARCHAR)::BOOLEAN)::BOOLEAN",
2189        );
2190    }
2191
2192    /// The literal side of a comparison is turned into a column when the pipeline is built.
2193    #[test]
2194    fn a_comparison_against_a_literal_builds_it_once() {
2195        let (schema, _) = input();
2196        for (expr, built) in [
2197            ("(#0.1::VARCHAR = 'a'::VARCHAR)::BOOLEAN AS p", 1),
2198            ("(#0.0::INTEGER > 1::INTEGER)::BOOLEAN AS p", 1),
2199            // The literal on the left, which is the same comparison written the other way round.
2200            ("(1::INTEGER < #0.0::INTEGER)::BOOLEAN AS p", 1),
2201            // Two columns, which has no literal side to build.
2202            ("(#0.0::INTEGER = #0.0::INTEGER)::BOOLEAN AS p", 0),
2203            // Two literals, which the kernel answers once for the whole vector without reading a
2204            // column, so building one would be work that nothing reads.
2205            ("(1::INTEGER = 2::INTEGER)::BOOLEAN AS p", 0),
2206        ] {
2207            let (plan, list) = projection(expr);
2208            let prepared = Prepared::new(&plan, &list, &schema).expect("the expression resolves");
2209            assert_eq!(prepared.literals_built(), built, "`{expr}`");
2210            agrees(expr);
2211        }
2212    }
2213
2214    /// A pattern that does not compile still fails where the query said it does.
2215    ///
2216    /// Preparing is not allowed to move an error earlier. Compiling at build time and reporting
2217    /// there would raise before a row had been read, and under a `CASE` arm it would raise on a
2218    /// query whose rows never reach the call at all.
2219    #[test]
2220    fn a_pattern_that_does_not_compile_fails_on_the_chunk_and_not_before() {
2221        let (schema, chunk) = input();
2222        let (plan, list) =
2223            projection("\"regexp_matches\"(#0.1::VARCHAR, 'a('::VARCHAR)::BOOLEAN AS a");
2224        let prepared = Prepared::new(&plan, &list, &schema).expect("preparing does not compile it");
2225        assert_eq!(prepared.hoisted(), 0);
2226        let mut scratch = prepared.scratch();
2227        let mut out = Vec::new();
2228        prepared.evaluate(&chunk, &mut scratch, &mut out).expect_err("the chunk raises");
2229    }
2230}