Skip to main content

rudb_plan/
plan.rs

1//! The arena a plan lives in, and the invariant that keeps its indices honest.
2
3use rudb_common::{Error, Field, LogicalType, Result, Value};
4
5use crate::expr::{Arm, ColumnBinding, Expr, SortKey};
6use crate::node::Node;
7use crate::{ExprRef, NodeRef, Slice, StrRef, ValueRef};
8
9/// A bound logical plan.
10///
11/// Ten flat pools and a root. Everything refers to everything else by `u32` index, and the one
12/// structural rule is that **a reference always points backwards**: a node's children have smaller
13/// indices than the node, and an expression's operands have smaller indices than the expression.
14/// Building bottom up gives that for free, it makes a cycle impossible rather than merely unlikely,
15/// and it means a walk of the whole plan is a loop over a vector in either direction instead of a
16/// recursion with a visited set. [`Plan::validate`] checks it.
17///
18/// A fresh plan is [`Node::Dummy`] at the root, which is one row and no columns. That is a valid
19/// plan rather than a placeholder, so there is no state in which a `Plan` exists and cannot be
20/// printed.
21///
22/// There is no `PartialEq`. Two plans that compute the same thing can have different arena layouts
23/// after a rewrite reorders pools, so comparing arenas would report differences that are not
24/// differences. The textual form is what plans are compared by, and it is canonical because
25/// printing walks from the root and never touches an unreachable entry.
26#[derive(Debug, Clone)]
27pub struct Plan {
28    nodes: Vec<Node>,
29    exprs: Vec<Expr>,
30    /// The type of `exprs[i]`, parallel and always the same length.
31    types: Vec<LogicalType>,
32    values: Vec<Value>,
33    strings: Vec<String>,
34    expr_lists: Vec<ExprRef>,
35    name_lists: Vec<StrRef>,
36    fields: Vec<Field>,
37    sort_keys: Vec<SortKey>,
38    arms: Vec<Arm>,
39    rows: Vec<Slice>,
40    root: NodeRef,
41}
42
43impl Default for Plan {
44    fn default() -> Self {
45        Self::new()
46    }
47}
48
49impl Plan {
50    /// An empty plan, which is one row and no columns.
51    #[must_use]
52    pub fn new() -> Self {
53        let mut plan = Self::without_nodes();
54        plan.add_node(Node::Dummy);
55        plan
56    }
57
58    /// A plan with nothing in it at all, which is not a state anybody outside this crate can hold.
59    ///
60    /// The reader needs it: it builds the root from the text and would otherwise start from a
61    /// [`Node::Dummy`] that nothing points at, and an unreachable node in a freshly parsed plan is
62    /// a difference between a plan and the same plan printed and read back.
63    pub(crate) fn without_nodes() -> Self {
64        Self {
65            nodes: Vec::new(),
66            exprs: Vec::new(),
67            types: Vec::new(),
68            values: Vec::new(),
69            strings: Vec::new(),
70            expr_lists: Vec::new(),
71            name_lists: Vec::new(),
72            fields: Vec::new(),
73            sort_keys: Vec::new(),
74            arms: Vec::new(),
75            rows: Vec::new(),
76            root: 0,
77        }
78    }
79
80    /// The node the plan is rooted at.
81    #[must_use]
82    pub fn root(&self) -> NodeRef {
83        self.root
84    }
85
86    /// Roots the plan at `node`.
87    pub fn set_root(&mut self, node: NodeRef) {
88        self.root = node;
89    }
90
91    /// How many nodes are in the arena, reachable or not.
92    #[must_use]
93    pub fn node_count(&self) -> usize {
94        self.nodes.len()
95    }
96
97    /// How many expressions are in the arena, reachable or not.
98    #[must_use]
99    pub fn expr_count(&self) -> usize {
100        self.exprs.len()
101    }
102
103    // Builders. Each one appends and hands back the index, which is why building bottom up gives
104    // the backwards-reference invariant without anybody having to think about it.
105
106    /// Appends a node.
107    pub fn add_node(&mut self, node: Node) -> NodeRef {
108        push(&mut self.nodes, node)
109    }
110
111    /// Appends an expression and the type it evaluates to.
112    pub fn add_expr(&mut self, expr: Expr, ty: LogicalType) -> ExprRef {
113        self.types.push(ty);
114        push(&mut self.exprs, expr)
115    }
116
117    /// Appends a constant.
118    pub fn add_value(&mut self, value: Value) -> ValueRef {
119        push(&mut self.values, value)
120    }
121
122    /// Appends a constant expression, taking its type from the value.
123    ///
124    /// The shorthand for the common case. A typed null needs [`Plan::add_expr`] with
125    /// [`Expr::Constant`] instead, since a `NULL` literal knows its type from context and not from
126    /// itself.
127    pub fn add_constant(&mut self, value: Value) -> ExprRef {
128        let ty = value.logical_type();
129        let reference = self.add_value(value);
130        self.add_expr(Expr::Constant(reference), ty)
131    }
132
133    /// Interns a string, returning an existing entry if there is one.
134    ///
135    /// A linear scan, because a plan's string table is table names, column names and function
136    /// names and runs to tens of entries. A hash map here would be a second copy of every string
137    /// to save a scan nobody can measure.
138    ///
139    /// # Panics
140    ///
141    /// If the string table has more than `u32::MAX` entries. Every pool in the arena is indexed by
142    /// a `u32` and the reference type says so, so a plan that large is not a plan this type can
143    /// hold and there is nothing sensible to return instead.
144    pub fn intern(&mut self, text: &str) -> StrRef {
145        if let Some(found) = self.strings.iter().position(|held| held == text) {
146            return u32::try_from(found).expect("a string table this large cannot be built");
147        }
148        push(&mut self.strings, text.to_string())
149    }
150
151    /// Appends a run to the expression list pool.
152    pub fn add_expr_list(&mut self, exprs: &[ExprRef]) -> Slice {
153        extend(&mut self.expr_lists, exprs.iter().copied())
154    }
155
156    /// Appends a run to the name list pool.
157    pub fn add_name_list(&mut self, names: &[StrRef]) -> Slice {
158        extend(&mut self.name_lists, names.iter().copied())
159    }
160
161    /// Appends a run to the field pool, which is what a scan's or a `VALUES`' output schema is.
162    pub fn add_fields(&mut self, fields: &[Field]) -> Slice {
163        extend(&mut self.fields, fields.iter().cloned())
164    }
165
166    /// Appends a run to the sort key pool.
167    pub fn add_sort_keys(&mut self, keys: &[SortKey]) -> Slice {
168        extend(&mut self.sort_keys, keys.iter().copied())
169    }
170
171    /// Appends a run to the `CASE` arm pool.
172    pub fn add_arms(&mut self, arms: &[Arm]) -> Slice {
173        extend(&mut self.arms, arms.iter().copied())
174    }
175
176    /// Appends a run to the row pool, each element itself a run of the expression list pool.
177    pub fn add_rows(&mut self, rows: &[Slice]) -> Slice {
178        extend(&mut self.rows, rows.iter().copied())
179    }
180
181    // Accessors. Every one panics on an out of range index rather than returning an option,
182    // because a reference that does not resolve is a bug in whoever built the plan and the useful
183    // thing to do with it is to stop at the place that would otherwise silently do nothing.
184
185    /// The node at `reference`.
186    ///
187    /// # Panics
188    ///
189    /// If the reference is not in the arena.
190    #[must_use]
191    pub fn node(&self, reference: NodeRef) -> &Node {
192        &self.nodes[reference as usize]
193    }
194
195    /// The expression at `reference`.
196    ///
197    /// # Panics
198    ///
199    /// If the reference is not in the arena.
200    #[must_use]
201    pub fn expr(&self, reference: ExprRef) -> &Expr {
202        &self.exprs[reference as usize]
203    }
204
205    /// The type the expression at `reference` evaluates to.
206    ///
207    /// # Panics
208    ///
209    /// If the reference is not in the arena.
210    #[must_use]
211    pub fn expr_type(&self, reference: ExprRef) -> &LogicalType {
212        &self.types[reference as usize]
213    }
214
215    /// The constant at `reference`.
216    ///
217    /// # Panics
218    ///
219    /// If the reference is not in the arena.
220    #[must_use]
221    pub fn value(&self, reference: ValueRef) -> &Value {
222        &self.values[reference as usize]
223    }
224
225    /// The string at `reference`.
226    ///
227    /// # Panics
228    ///
229    /// If the reference is not in the arena.
230    #[must_use]
231    pub fn string(&self, reference: StrRef) -> &str {
232        &self.strings[reference as usize]
233    }
234
235    /// The expression run at `slice`.
236    ///
237    /// # Panics
238    ///
239    /// If the run is not in the pool.
240    #[must_use]
241    pub fn expr_list(&self, slice: Slice) -> &[ExprRef] {
242        &self.expr_lists[slice.range()]
243    }
244
245    /// The name run at `slice`.
246    ///
247    /// # Panics
248    ///
249    /// If the run is not in the pool.
250    #[must_use]
251    pub fn name_list(&self, slice: Slice) -> &[StrRef] {
252        &self.name_lists[slice.range()]
253    }
254
255    /// The field run at `slice`.
256    ///
257    /// # Panics
258    ///
259    /// If the run is not in the pool.
260    #[must_use]
261    pub fn field_list(&self, slice: Slice) -> &[Field] {
262        &self.fields[slice.range()]
263    }
264
265    /// The sort key run at `slice`.
266    ///
267    /// # Panics
268    ///
269    /// If the run is not in the pool.
270    #[must_use]
271    pub fn sort_key_list(&self, slice: Slice) -> &[SortKey] {
272        &self.sort_keys[slice.range()]
273    }
274
275    /// The `CASE` arm run at `slice`.
276    ///
277    /// # Panics
278    ///
279    /// If the run is not in the pool.
280    #[must_use]
281    pub fn arm_list(&self, slice: Slice) -> &[Arm] {
282        &self.arms[slice.range()]
283    }
284
285    /// The row run at `slice`.
286    ///
287    /// # Panics
288    ///
289    /// If the run is not in the pool.
290    #[must_use]
291    pub fn row_list(&self, slice: Slice) -> &[Slice] {
292        &self.rows[slice.range()]
293    }
294
295    // Rewriters. Two of them, both narrow on purpose. A pass that wants to change what an
296    // expression computes adds a new expression and points at it, because the type of an
297    // expression is stored beside it and a general `expr_mut` is a way to change one without the
298    // other. These two cannot: a binding does not carry a type and a node does not have one.
299
300    /// Points a column reference at a different column.
301    ///
302    /// What column pruning does after it narrows a scan, since dropping a column moves every column
303    /// after it up. The type does not change, because it is the same column of the same operator
304    /// read from a different position.
305    ///
306    /// # Panics
307    ///
308    /// If the reference is not in the arena, or if it is not a column reference, both of which are
309    /// bugs in the pass rather than anything a plan can be.
310    pub fn rebind(&mut self, reference: ExprRef, binding: ColumnBinding) {
311        match &mut self.exprs[reference as usize] {
312            Expr::Column(held) => *held = binding,
313            other => panic!("expression {reference} is {other:?}, not a column"),
314        }
315    }
316
317    /// The node at `reference`, to be rewritten in place.
318    ///
319    /// # Panics
320    ///
321    /// If the reference is not in the arena.
322    pub fn node_mut(&mut self, reference: NodeRef) -> &mut Node {
323        &mut self.nodes[reference as usize]
324    }
325
326    /// Checks the plan invariant.
327    ///
328    /// `spec/09-optimizer.md` section 9.1 says every pass preserves an invariant that is checked in
329    /// debug builds, and this is that check. It is not a type checker and it does not know what
330    /// any function returns. What it knows is what this crate can get wrong on its own: an index
331    /// that points at nothing, an index that points forwards and could therefore be a cycle, a
332    /// projection with more expressions than names, a ragged `VALUES`, a filter on something that
333    /// is not boolean, a one-armed conjunction, and an aggregate somewhere an aggregate cannot be.
334    ///
335    /// Every one of those is a bug that produces a wrong answer or a hang rather than an error, and
336    /// `spec/16-testing.md` section 16.9 is specifically about not shipping the first kind.
337    ///
338    /// # Errors
339    ///
340    /// With a message naming the node or expression index that broke the rule, because the useful
341    /// question about a malformed plan is always which part of it.
342    ///
343    /// # Panics
344    ///
345    /// If a pool has more than `u32::MAX` entries, which is the same bound every reference in the
346    /// arena already carries.
347    pub fn validate(&self) -> Result<()> {
348        if self.exprs.len() != self.types.len() {
349            return Err(Error::internal(format!(
350                "the plan has {} expressions and {} types",
351                self.exprs.len(),
352                self.types.len()
353            )));
354        }
355        if self.root as usize >= self.nodes.len() {
356            return Err(Error::internal(format!(
357                "the plan is rooted at node {} and has {} nodes",
358                self.root,
359                self.nodes.len()
360            )));
361        }
362        for index in 0..self.exprs.len() {
363            self.validate_expr(u32::try_from(index).expect("index came from a length"))?;
364        }
365        for index in 0..self.nodes.len() {
366            self.validate_node(u32::try_from(index).expect("index came from a length"))?;
367        }
368        Ok(())
369    }
370
371    fn validate_expr(&self, reference: ExprRef) -> Result<()> {
372        let fail = |what: &str| Err(Error::internal(format!("expression {reference} {what}")));
373        let backwards = |operand: ExprRef| -> Result<()> {
374            if operand < reference {
375                Ok(())
376            } else {
377                Err(Error::internal(format!(
378                    "expression {reference} refers to expression {operand}, which is not behind it"
379                )))
380            }
381        };
382        match *self.expr(reference) {
383            Expr::Column(_) => {}
384            Expr::Constant(value) => {
385                if value as usize >= self.values.len() {
386                    return fail("names a constant that is not in the value table");
387                }
388                // A null literal takes its type from context, so it is the one case where the
389                // stored type is allowed to disagree with the value.
390                let held = self.value(value);
391                if !held.is_null() && held.logical_type() != *self.expr_type(reference) {
392                    return fail("is a constant whose type disagrees with the value it holds");
393                }
394            }
395            Expr::Cast { input, .. } => backwards(input)?,
396            Expr::Compare { left, right, .. } => {
397                backwards(left)?;
398                backwards(right)?;
399                if *self.expr_type(reference) != LogicalType::Boolean {
400                    return fail("is a comparison that does not produce BOOLEAN");
401                }
402            }
403            Expr::Conjunction { children, .. } => {
404                if children.len < 2 {
405                    return fail("is a conjunction with fewer than two operands");
406                }
407                for &child in self.checked_expr_list(children, reference)? {
408                    backwards(child)?;
409                }
410                if *self.expr_type(reference) != LogicalType::Boolean {
411                    return fail("is a conjunction that does not produce BOOLEAN");
412                }
413            }
414            Expr::Function { name, args } | Expr::Aggregate { name, args, .. } => {
415                if name as usize >= self.strings.len() {
416                    return fail("names a function that is not in the string table");
417                }
418                for &arg in self.checked_expr_list(args, reference)? {
419                    backwards(arg)?;
420                }
421                if let Expr::Aggregate { filter: Some(filter), .. } = *self.expr(reference) {
422                    backwards(filter)?;
423                    if *self.expr_type(filter) != LogicalType::Boolean {
424                        return fail("has a FILTER that is not BOOLEAN");
425                    }
426                }
427            }
428            Expr::Case { arms, otherwise } => {
429                if arms.is_empty() {
430                    return fail("is a CASE with no arms");
431                }
432                let end = arms.start as usize + arms.len as usize;
433                if end > self.arms.len() {
434                    return fail("names an arm run that is not in the pool");
435                }
436                for arm in self.arm_list(arms) {
437                    backwards(arm.when)?;
438                    backwards(arm.then)?;
439                    if *self.expr_type(arm.when) != LogicalType::Boolean {
440                        return fail("has a WHEN that is not BOOLEAN");
441                    }
442                }
443                if let Some(otherwise) = otherwise {
444                    backwards(otherwise)?;
445                }
446            }
447        }
448        Ok(())
449    }
450
451    fn validate_node(&self, reference: NodeRef) -> Result<()> {
452        let node = self.node(reference);
453        let fail = |what: &str| {
454            Err(Error::internal(format!("node {reference}, which is a {}, {what}", node.keyword())))
455        };
456        for child in node.children().into_iter().flatten() {
457            if child >= reference {
458                return Err(Error::internal(format!(
459                    "node {reference} has child {child}, which is not behind it"
460                )));
461            }
462        }
463        match *node {
464            Node::Dummy | Node::CrossProduct { .. } => {}
465            Node::Get { catalog, schema, table, alias, columns, .. } => {
466                for name in [catalog, schema, table, alias] {
467                    if name as usize >= self.strings.len() {
468                        return fail("names a string that is not in the table");
469                    }
470                }
471                self.checked_field_list(columns, reference)?;
472            }
473            Node::Values { columns, rows, .. } => {
474                let width = self.checked_field_list(columns, reference)?.len();
475                let end = rows.start as usize + rows.len as usize;
476                if end > self.rows.len() {
477                    return fail("names a row run that is not in the pool");
478                }
479                for row in self.row_list(rows) {
480                    if self.checked_expr_list(*row, reference)?.len() != width {
481                        return fail("has a row whose length is not the number of columns");
482                    }
483                }
484            }
485            Node::TableFunction { function, args, options, settings, columns, .. } => {
486                if function as usize >= self.strings.len() {
487                    return fail("names a string that is not in the table");
488                }
489                self.checked_field_list(columns, reference)?;
490                self.checked_expr_list(args, reference)?;
491                if self.checked_expr_list(settings, reference)?.len() != options.len as usize {
492                    return fail("has a named parameter with no value or a value with no name");
493                }
494                let end = options.start as usize + options.len as usize;
495                if end > self.name_lists.len() {
496                    return fail("names a name run that is not in the pool");
497                }
498                for &name in self.name_list(options) {
499                    if name as usize >= self.strings.len() {
500                        return fail("names a parameter that is not in the string table");
501                    }
502                }
503            }
504            Node::Filter { predicate, .. } => {
505                self.checked_expr(predicate, reference)?;
506                if *self.expr_type(predicate) != LogicalType::Boolean {
507                    return fail("filters on an expression that is not BOOLEAN");
508                }
509            }
510            Node::Project { exprs, names, .. } => {
511                let count = self.checked_expr_list(exprs, reference)?.len();
512                let end = names.start as usize + names.len as usize;
513                if end > self.name_lists.len() {
514                    return fail("names a name run that is not in the pool");
515                }
516                if self.name_list(names).len() != count {
517                    return fail("has a different number of names and expressions");
518                }
519                for &name in self.name_list(names) {
520                    if name as usize >= self.strings.len() {
521                        return fail("names an output name that is not in the string table");
522                    }
523                }
524            }
525            Node::Aggregate { groups, aggregates, .. } => {
526                for &group in self.checked_expr_list(groups, reference)? {
527                    if matches!(self.expr(group), Expr::Aggregate { .. }) {
528                        return fail("groups by an aggregate");
529                    }
530                }
531                for &aggregate in self.checked_expr_list(aggregates, reference)? {
532                    if !matches!(self.expr(aggregate), Expr::Aggregate { .. }) {
533                        return fail(
534                            "has something in its aggregate list that is not an aggregate",
535                        );
536                    }
537                }
538            }
539            Node::Sort { keys, .. } | Node::TopN { keys, .. } => {
540                let end = keys.start as usize + keys.len as usize;
541                if end > self.sort_keys.len() {
542                    return fail("names a sort key run that is not in the pool");
543                }
544                if keys.is_empty() {
545                    return fail("sorts on nothing");
546                }
547                for key in self.sort_key_list(keys) {
548                    self.checked_expr(key.expr, reference)?;
549                }
550            }
551            Node::Limit { .. } => {}
552            Node::Distinct { on, .. } => {
553                self.checked_expr_list(on, reference)?;
554            }
555            Node::Join { conditions, .. } => {
556                for &condition in self.checked_expr_list(conditions, reference)? {
557                    if *self.expr_type(condition) != LogicalType::Boolean {
558                        return fail("joins on a condition that is not BOOLEAN");
559                    }
560                }
561            }
562            Node::SetOp { .. } => {}
563        }
564
565        // An aggregate is legal only as a direct element of an Aggregate node's aggregate list,
566        // per the note on Expr::Aggregate. Everything else that reaches one is a plan the printer
567        // would emit and the reader would misread as a scalar function, which is a wrong answer
568        // rather than an error.
569        for (expr, aggregate_allowed) in self.top_level_exprs(node) {
570            if aggregate_allowed {
571                if let Expr::Aggregate { args, filter, .. } = *self.expr(expr) {
572                    let nested = self
573                        .expr_list(args)
574                        .iter()
575                        .chain(filter.iter())
576                        .any(|&child| self.reaches_an_aggregate(child));
577                    if nested {
578                        return fail("has an aggregate inside an aggregate");
579                    }
580                    continue;
581                }
582            }
583            if self.reaches_an_aggregate(expr) {
584                return fail("has an aggregate outside an aggregate list");
585            }
586        }
587        Ok(())
588    }
589
590    /// Every expression a node holds directly, paired with whether an aggregate is allowed there.
591    ///
592    /// One list rather than a rule restated in each arm of `validate_node`, because the rule is
593    /// about the whole node set and a rule stated twelve times is a rule that is wrong in one of
594    /// them. Runs after the per-operator checks, so every run named here is known to be in range.
595    fn top_level_exprs(&self, node: &Node) -> Vec<(ExprRef, bool)> {
596        let plain = |list: &[ExprRef]| -> Vec<(ExprRef, bool)> {
597            list.iter().map(|&expr| (expr, false)).collect()
598        };
599        match *node {
600            Node::Get { .. }
601            | Node::Dummy
602            | Node::CrossProduct { .. }
603            | Node::SetOp { .. }
604            | Node::Limit { .. } => Vec::new(),
605            Node::Values { rows, .. } => {
606                self.row_list(rows).iter().flat_map(|row| plain(self.expr_list(*row))).collect()
607            }
608            Node::TableFunction { args, .. } => plain(self.expr_list(args)),
609            Node::Filter { predicate, .. } => vec![(predicate, false)],
610            Node::Project { exprs, .. } => plain(self.expr_list(exprs)),
611            Node::Aggregate { groups, aggregates, .. } => {
612                let mut all = plain(self.expr_list(groups));
613                all.extend(self.expr_list(aggregates).iter().map(|&expr| (expr, true)));
614                all
615            }
616            Node::Sort { keys, .. } | Node::TopN { keys, .. } => {
617                self.sort_key_list(keys).iter().map(|key| (key.expr, false)).collect()
618            }
619            Node::Distinct { on, .. } => plain(self.expr_list(on)),
620            Node::Join { conditions, .. } => plain(self.expr_list(conditions)),
621        }
622    }
623
624    /// Whether an aggregate is anywhere in this expression, itself included.
625    ///
626    /// A plain recursion terminates because operands point backwards, which is checked before this
627    /// runs.
628    fn reaches_an_aggregate(&self, reference: ExprRef) -> bool {
629        match *self.expr(reference) {
630            Expr::Aggregate { .. } => true,
631            Expr::Column(_) | Expr::Constant(_) => false,
632            Expr::Cast { input, .. } => self.reaches_an_aggregate(input),
633            Expr::Compare { left, right, .. } => {
634                self.reaches_an_aggregate(left) || self.reaches_an_aggregate(right)
635            }
636            Expr::Conjunction { children: list, .. } | Expr::Function { args: list, .. } => {
637                self.expr_list(list).iter().any(|&child| self.reaches_an_aggregate(child))
638            }
639            Expr::Case { arms, otherwise } => {
640                self.arm_list(arms).iter().any(|arm| {
641                    self.reaches_an_aggregate(arm.when) || self.reaches_an_aggregate(arm.then)
642                }) || otherwise.is_some_and(|child| self.reaches_an_aggregate(child))
643            }
644        }
645    }
646
647    fn checked_expr(&self, reference: ExprRef, node: NodeRef) -> Result<()> {
648        if reference as usize >= self.exprs.len() {
649            return Err(Error::internal(format!(
650                "node {node} names expression {reference}, which is not in the arena"
651            )));
652        }
653        Ok(())
654    }
655
656    fn checked_expr_list(&self, slice: Slice, owner: u32) -> Result<&[ExprRef]> {
657        let end = slice.start as usize + slice.len as usize;
658        if end > self.expr_lists.len() {
659            return Err(Error::internal(format!(
660                "{owner} names an expression run that is not in the pool"
661            )));
662        }
663        let list = self.expr_list(slice);
664        for &reference in list {
665            if reference as usize >= self.exprs.len() {
666                return Err(Error::internal(format!(
667                    "{owner} names expression {reference}, which is not in the arena"
668                )));
669            }
670        }
671        Ok(list)
672    }
673
674    fn checked_field_list(&self, slice: Slice, owner: u32) -> Result<&[Field]> {
675        let end = slice.start as usize + slice.len as usize;
676        if end > self.fields.len() {
677            return Err(Error::internal(format!(
678                "{owner} names a field run that is not in the pool"
679            )));
680        }
681        Ok(self.field_list(slice))
682    }
683}
684
685/// Appends and hands back the index, which is the only place a pool length becomes a reference.
686///
687/// # Panics
688///
689/// If the pool has more than `u32::MAX` entries, which is a plan of four billion nodes and is a
690/// bug somewhere upstream rather than a query anybody wrote.
691fn push<T>(pool: &mut Vec<T>, item: T) -> u32 {
692    let index = u32::try_from(pool.len()).expect("a plan arena cannot hold four billion entries");
693    pool.push(item);
694    index
695}
696
697/// Appends a run and hands back the slice that names it.
698///
699/// # Panics
700///
701/// As [`push`].
702fn extend<T>(pool: &mut Vec<T>, items: impl Iterator<Item = T>) -> Slice {
703    let start = u32::try_from(pool.len()).expect("a plan arena cannot hold four billion entries");
704    pool.extend(items);
705    let len =
706        u32::try_from(pool.len()).expect("a plan arena cannot hold four billion entries") - start;
707    Slice { start, len }
708}
709
710#[cfg(test)]
711mod tests {
712    use super::*;
713    use crate::expr::{ColumnBinding, CompareOp};
714
715    #[test]
716    fn a_fresh_plan_is_a_valid_plan() {
717        let plan = Plan::new();
718        assert_eq!(*plan.node(plan.root()), Node::Dummy);
719        plan.validate().expect("an empty plan is one row and no columns, which is legal");
720    }
721
722    #[test]
723    fn interning_the_same_string_twice_gives_the_same_reference() {
724        let mut plan = Plan::new();
725        let first = plan.intern("hits");
726        let second = plan.intern("hits");
727        let other = plan.intern("visits");
728        assert_eq!(first, second);
729        assert_ne!(first, other);
730        assert_eq!(plan.string(first), "hits");
731    }
732
733    #[test]
734    fn a_constant_takes_its_type_from_its_value() {
735        let mut plan = Plan::new();
736        let one = plan.add_constant(Value::Integer(1));
737        assert_eq!(*plan.expr_type(one), LogicalType::Integer);
738        plan.validate().expect("a constant that agrees with itself is valid");
739    }
740
741    /// The one case where a constant's stored type is allowed to differ from the value's, because
742    /// `NULL::VARCHAR` is a varchar expression holding an untyped null.
743    #[test]
744    fn a_typed_null_is_allowed_to_disagree_with_its_value() {
745        let mut plan = Plan::new();
746        let null = plan.add_value(Value::Null);
747        plan.add_expr(Expr::Constant(null), LogicalType::Varchar);
748        plan.validate().expect("a typed null is the point of carrying types separately");
749    }
750
751    #[test]
752    fn a_constant_that_disagrees_with_its_value_is_caught() {
753        let mut plan = Plan::new();
754        let value = plan.add_value(Value::Integer(1));
755        plan.add_expr(Expr::Constant(value), LogicalType::Varchar);
756        let message = plan.validate().unwrap_err().to_string();
757        assert!(message.contains("disagrees"), "unhelpful message: {message}");
758    }
759
760    #[test]
761    fn a_filter_on_something_that_is_not_boolean_is_caught() {
762        let mut plan = Plan::new();
763        let one = plan.add_constant(Value::Integer(1));
764        let filter = plan.add_node(Node::Filter { input: 0, predicate: one });
765        plan.set_root(filter);
766        let message = plan.validate().unwrap_err().to_string();
767        assert!(message.contains("BOOLEAN"), "unhelpful message: {message}");
768    }
769
770    #[test]
771    fn a_projection_with_more_expressions_than_names_is_caught() {
772        let mut plan = Plan::new();
773        let one = plan.add_constant(Value::Integer(1));
774        let two = plan.add_constant(Value::Integer(2));
775        let exprs = plan.add_expr_list(&[one, two]);
776        let name = plan.intern("a");
777        let names = plan.add_name_list(&[name]);
778        let project = plan.add_node(Node::Project { input: 0, index: 1, exprs, names });
779        plan.set_root(project);
780        let message = plan.validate().unwrap_err().to_string();
781        assert!(message.contains("names and expressions"), "unhelpful message: {message}");
782    }
783
784    #[test]
785    fn a_ragged_values_is_caught() {
786        let mut plan = Plan::new();
787        let one = plan.add_constant(Value::Integer(1));
788        let two = plan.add_constant(Value::Integer(2));
789        let wide = plan.add_expr_list(&[one, two]);
790        let narrow = plan.add_expr_list(&[one]);
791        let rows = plan.add_rows(&[wide, narrow]);
792        let columns = plan.add_fields(&[
793            Field::new("a", LogicalType::Integer),
794            Field::new("b", LogicalType::Integer),
795        ]);
796        let values = plan.add_node(Node::Values { index: 0, columns, rows });
797        plan.set_root(values);
798        let message = plan.validate().unwrap_err().to_string();
799        assert!(message.contains("number of columns"), "unhelpful message: {message}");
800    }
801
802    #[test]
803    fn an_aggregate_outside_an_aggregate_list_is_caught() {
804        let mut plan = Plan::new();
805        let name = plan.intern("count_star");
806        let count = plan.add_expr(
807            Expr::Aggregate { name, args: Slice::EMPTY, distinct: false, filter: None },
808            LogicalType::BigInt,
809        );
810        let zero = plan.add_constant(Value::BigInt(0));
811        let compare = plan.add_expr(
812            Expr::Compare { op: CompareOp::Greater, left: count, right: zero },
813            LogicalType::Boolean,
814        );
815        let filter = plan.add_node(Node::Filter { input: 0, predicate: compare });
816        plan.set_root(filter);
817        let message = plan.validate().unwrap_err().to_string();
818        assert!(message.contains("aggregate outside"), "unhelpful message: {message}");
819    }
820
821    #[test]
822    fn an_aggregate_inside_an_aggregate_list_is_fine() {
823        let mut plan = Plan::new();
824        let name = plan.intern("count_star");
825        let count = plan.add_expr(
826            Expr::Aggregate { name, args: Slice::EMPTY, distinct: false, filter: None },
827            LogicalType::BigInt,
828        );
829        let aggregates = plan.add_expr_list(&[count]);
830        let aggregate =
831            plan.add_node(Node::Aggregate { input: 0, index: 1, groups: Slice::EMPTY, aggregates });
832        plan.set_root(aggregate);
833        plan.validate().expect("this is the one place an aggregate belongs");
834    }
835
836    /// The backwards-reference rule is what makes a cycle impossible, so the check for it has to
837    /// actually fire rather than being a comment about how nobody would do that.
838    #[test]
839    fn a_node_that_refers_to_itself_is_caught() {
840        let mut plan = Plan::new();
841        let filter = plan.add_node(Node::Filter { input: 0, predicate: 0 });
842        let one = plan.add_constant(Value::Boolean(true));
843        plan.nodes[filter as usize] = Node::Filter { input: filter, predicate: one };
844        plan.set_root(filter);
845        let message = plan.validate().unwrap_err().to_string();
846        assert!(message.contains("not behind it"), "unhelpful message: {message}");
847    }
848
849    #[test]
850    fn an_expression_that_refers_forwards_is_caught() {
851        let mut plan = Plan::new();
852        let left = plan.add_constant(Value::Integer(1));
853        let compare = plan.add_expr(
854            Expr::Compare { op: CompareOp::Equal, left, right: left },
855            LogicalType::Boolean,
856        );
857        plan.exprs[compare as usize] =
858            Expr::Compare { op: CompareOp::Equal, left, right: compare + 1 };
859        plan.add_constant(Value::Integer(2));
860        let message = plan.validate().unwrap_err().to_string();
861        assert!(message.contains("not behind it"), "unhelpful message: {message}");
862    }
863
864    #[test]
865    fn a_root_that_is_not_in_the_arena_is_caught() {
866        let mut plan = Plan::new();
867        plan.set_root(17);
868        let message = plan.validate().unwrap_err().to_string();
869        assert!(message.contains("rooted at node 17"), "unhelpful message: {message}");
870    }
871
872    #[test]
873    fn a_column_binding_is_two_numbers_and_nothing_else() {
874        let binding = ColumnBinding::new(3, 7);
875        assert_eq!(binding.table, 3);
876        assert_eq!(binding.column, 7);
877        assert_eq!(size_of::<ColumnBinding>(), 8);
878    }
879}