1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
//! The logical plan, its textual form, and the parser that reads that form back.
//!
//! Rank 9 in the layer rule. See `xtask/layers.toml` and `spec/18-package-layout.md`.
//!
//! This is the bound logical plan: what the binder produces, what the optimizer rewrites, and what
//! the physical planner consumes. `spec/04-architecture.md` calls it "a bound logical plan with
//! fully resolved types", and both halves of that are enforced here rather than assumed. Every
//! expression carries a [`LogicalType`](rudb_common::LogicalType) that is stored next to it, and
//! every column reference is a [`ColumnBinding`] naming the operator that produced the column and
//! the position within that operator's output. There are no names in an expression and nothing in
//! this crate looks anything up in a catalog. Name resolution happened in the binder and a plan
//! that still needs it is a plan that is not bound.
//!
//! # The textual form
//!
//! `spec/00-README.md` requires that every layer has a textual form and a round-trip parser, and
//! that requirement is the reason this crate exists before there is an optimizer to rewrite
//! anything. A plan prints as an indented tree, two spaces a level, parent before children:
//!
//! ```text
//! Project #2 [#1.0::VARCHAR AS SearchPhrase, #1.1::BIGINT AS c]
//! Limit 10 offset 0
//! Sort [#1.1::BIGINT DESC NULLS LAST]
//! Aggregate #1 groups=[#0.0::VARCHAR] aggregates=[count_star()::BIGINT]
//! Filter (#0.0::VARCHAR <> ''::VARCHAR)::BOOLEAN
//! Get memory.main.hits AS hits #0 [SearchPhrase::VARCHAR]
//! ```
//!
//! [`Plan::parse`] reads that back, and printing the result produces the same text. That fixed
//! point is a test rather than a claim, and it is the thing that makes a plan diffable across a
//! rewrite, fuzzable on its own, and bisectable when a pass starts returning a wrong answer.
//!
//! Every expression is written `form::TYPE`. The annotation is on every node and not only on the
//! ones where a reader would need it, because the alternative is a parser that has to re-derive
//! types, and re-deriving types means consulting the function catalog, and a dump that cannot be
//! read without a catalog is not a dump. It is verbose. It is also exact, and exact is the whole
//! job here.
//!
//! # Why an arena
//!
//! Nodes, expressions and their lists all live in flat vectors and refer to each other by `u32`
//! index, the same shape [`rudb_parse::Ast`](https://docs.rs/rudb-parse) uses. A plan is rewritten
//! many times by `spec/09-optimizer.md`'s fixed pass sequence, and a rewrite of a boxed tree is a
//! traversal that allocates at every node. It also makes a plan one owned value that clones with
//! three memcpys, which is what lets a pass be a pure function from plan to plan without that
//! being expensive.
//!
//! The cost is that a reference is a number and a number can point at the wrong thing.
//! [`Plan::validate`] is the answer to that, and it is what section 9.1 means by the invariant
//! every pass has to preserve.
//!
//! # What is not here yet
//!
//! Window functions, subquery expressions, correlated references, `UNNEST`, lambdas, prepared
//! statement parameters, and everything on the write side. The M0 transformer cannot produce any
//! of them, so a representation for them here would be a representation nothing has ever
//! constructed, which is a representation that is wrong in a way nobody finds out about. Neither
//! [`Expr`] nor [`Node`] is `#[non_exhaustive]`, which is deliberate: adding a plan node should
//! stop the build in every optimizer pass that has to decide what to do about it.
pub use ;
pub use ;
pub use Plan;
/// A reference to an expression in [`Plan`]'s expression arena.
pub type ExprRef = u32;
/// A reference to a node in [`Plan`]'s node arena.
pub type NodeRef = u32;
/// A reference to an interned string in [`Plan`]'s string table.
pub type StrRef = u32;
/// A reference to a constant in [`Plan`]'s value table.
pub type ValueRef = u32;
/// A contiguous run in one of [`Plan`]'s pools.
///
/// Which pool is decided by the field that holds the slice, the same way a `u32` reference is only
/// meaningful in the arena it came from. A slice is `Copy` and eight bytes, so a node holding four
/// of them is still a node that fits in a cache line.