pub struct Plan { /* private fields */ }Expand description
A bound logical plan.
Ten flat pools and a root. Everything refers to everything else by u32 index, and the one
structural rule is that a reference always points backwards: a node’s children have smaller
indices than the node, and an expression’s operands have smaller indices than the expression.
Building bottom up gives that for free, it makes a cycle impossible rather than merely unlikely,
and it means a walk of the whole plan is a loop over a vector in either direction instead of a
recursion with a visited set. Plan::validate checks it.
A fresh plan is Node::Dummy at the root, which is one row and no columns. That is a valid
plan rather than a placeholder, so there is no state in which a Plan exists and cannot be
printed.
There is no PartialEq. Two plans that compute the same thing can have different arena layouts
after a rewrite reorders pools, so comparing arenas would report differences that are not
differences. The textual form is what plans are compared by, and it is canonical because
printing walks from the root and never touches an unreachable entry.
Implementations§
Source§impl Plan
impl Plan
Sourcepub fn parse(text: &str) -> Result<Self>
pub fn parse(text: &str) -> Result<Self>
Reads a plan back from its textual form.
Printing the result produces the text that was read, which is a test in
tests/roundtrip.rs rather than a claim here. The plan is validated before it is returned,
so a text that parses is a text that names a plan somebody could have built.
§Errors
With the line number and the column, because the thing a person wants from a dump that will not read back is which character of which operator.
Source§impl Plan
impl Plan
Sourcepub fn node_count(&self) -> usize
pub fn node_count(&self) -> usize
How many nodes are in the arena, reachable or not.
Sourcepub fn expr_count(&self) -> usize
pub fn expr_count(&self) -> usize
How many expressions are in the arena, reachable or not.
Sourcepub fn add_expr(&mut self, expr: Expr, ty: LogicalType) -> ExprRef
pub fn add_expr(&mut self, expr: Expr, ty: LogicalType) -> ExprRef
Appends an expression and the type it evaluates to.
Sourcepub fn add_constant(&mut self, value: Value) -> ExprRef
pub fn add_constant(&mut self, value: Value) -> ExprRef
Appends a constant expression, taking its type from the value.
The shorthand for the common case. A typed null needs Plan::add_expr with
Expr::Constant instead, since a NULL literal knows its type from context and not from
itself.
Sourcepub fn intern(&mut self, text: &str) -> StrRef
pub fn intern(&mut self, text: &str) -> StrRef
Interns a string, returning an existing entry if there is one.
A linear scan, because a plan’s string table is table names, column names and function names and runs to tens of entries. A hash map here would be a second copy of every string to save a scan nobody can measure.
§Panics
If the string table has more than u32::MAX entries. Every pool in the arena is indexed by
a u32 and the reference type says so, so a plan that large is not a plan this type can
hold and there is nothing sensible to return instead.
Sourcepub fn add_expr_list(&mut self, exprs: &[ExprRef]) -> Slice
pub fn add_expr_list(&mut self, exprs: &[ExprRef]) -> Slice
Appends a run to the expression list pool.
Sourcepub fn add_name_list(&mut self, names: &[StrRef]) -> Slice
pub fn add_name_list(&mut self, names: &[StrRef]) -> Slice
Appends a run to the name list pool.
Sourcepub fn add_fields(&mut self, fields: &[Field]) -> Slice
pub fn add_fields(&mut self, fields: &[Field]) -> Slice
Appends a run to the field pool, which is what a scan’s or a VALUES’ output schema is.
Sourcepub fn add_sort_keys(&mut self, keys: &[SortKey]) -> Slice
pub fn add_sort_keys(&mut self, keys: &[SortKey]) -> Slice
Appends a run to the sort key pool.
Sourcepub fn add_rows(&mut self, rows: &[Slice]) -> Slice
pub fn add_rows(&mut self, rows: &[Slice]) -> Slice
Appends a run to the row pool, each element itself a run of the expression list pool.
Sourcepub fn expr_type(&self, reference: ExprRef) -> &LogicalType
pub fn expr_type(&self, reference: ExprRef) -> &LogicalType
Sourcepub fn field_list(&self, slice: Slice) -> &[Field]
pub fn field_list(&self, slice: Slice) -> &[Field]
Sourcepub fn sort_key_list(&self, slice: Slice) -> &[SortKey]
pub fn sort_key_list(&self, slice: Slice) -> &[SortKey]
Sourcepub fn rebind(&mut self, reference: ExprRef, binding: ColumnBinding)
pub fn rebind(&mut self, reference: ExprRef, binding: ColumnBinding)
Points a column reference at a different column.
What column pruning does after it narrows a scan, since dropping a column moves every column after it up. The type does not change, because it is the same column of the same operator read from a different position.
§Panics
If the reference is not in the arena, or if it is not a column reference, both of which are bugs in the pass rather than anything a plan can be.
Sourcepub fn validate(&self) -> Result<()>
pub fn validate(&self) -> Result<()>
Checks the plan invariant.
spec/09-optimizer.md section 9.1 says every pass preserves an invariant that is checked in
debug builds, and this is that check. It is not a type checker and it does not know what
any function returns. What it knows is what this crate can get wrong on its own: an index
that points at nothing, an index that points forwards and could therefore be a cycle, a
projection with more expressions than names, a ragged VALUES, a filter on something that
is not boolean, a one-armed conjunction, and an aggregate somewhere an aggregate cannot be.
Every one of those is a bug that produces a wrong answer or a hang rather than an error, and
spec/16-testing.md section 16.9 is specifically about not shipping the first kind.
§Errors
With a message naming the node or expression index that broke the rule, because the useful question about a malformed plan is always which part of it.
§Panics
If a pool has more than u32::MAX entries, which is the same bound every reference in the
arena already carries.