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
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
use crate::ast::{AggFunc, AlterAction, Assignment, Expr, JoinKind, WindowFunc};
/// Physical plan nodes — what the executor actually runs.
#[derive(Debug, Clone)]
pub enum PlanNode {
SeqScan {
table: String,
},
/// Mission E1.2: sequential scan that renames output columns to
/// `alias.field`. Used exclusively as the leaves of a join plan so
/// downstream `NestedLoopJoin` + `Filter` + `Project` nodes can resolve
/// `Expr::QualifiedField` lookups by direct column-name match. Kept
/// separate from `SeqScan` so the single-table fast paths (which match
/// on `PlanNode::SeqScan { .. }` in many places) stay untouched.
AliasScan {
table: String,
alias: String,
},
IndexScan {
table: String,
column: String,
key: Expr,
},
/// B+tree range scan: returns rows where the indexed column falls within
/// the given bounds. Generated by the planner when it detects inequality
/// predicates (>, >=, <, <=, BETWEEN) on an indexed column. The executor
/// falls back to SeqScan+Filter if no index exists on the column.
RangeScan {
table: String,
column: String,
/// Lower bound: (expr, inclusive). None = unbounded below.
start: Option<(Expr, bool)>,
/// Upper bound: (expr, inclusive). None = unbounded above.
end: Option<(Expr, bool)>,
},
Filter {
input: Box<PlanNode>,
predicate: Expr,
},
Project {
input: Box<PlanNode>,
fields: Vec<ProjectField>,
},
Sort {
input: Box<PlanNode>,
keys: Vec<SortKey>,
},
Limit {
input: Box<PlanNode>,
count: Expr,
},
Offset {
input: Box<PlanNode>,
count: Expr,
},
Aggregate {
input: Box<PlanNode>,
function: AggFunc,
field: Option<String>,
},
/// Mission E1.2: nested-loop join. Correctness-first implementation —
/// O(L × R) scan for every join. E1.3 will add a hash-join fast path
/// for equijoins (the common case). The executor handles `Inner`,
/// `Cross`, and `LeftOuter`; `RightOuter` is rewritten by the planner
/// into a `LeftOuter` with swapped inputs.
NestedLoopJoin {
left: Box<PlanNode>,
right: Box<PlanNode>,
/// Join predicate. `None` for `Cross` joins (emit every pair).
on: Option<Expr>,
kind: JoinKind,
},
Distinct {
input: Box<PlanNode>,
},
/// Mission E2b: grouped aggregation. Output columns are
/// `keys ++ [agg.output_name for agg in aggregates]`. The optional
/// `having` predicate is evaluated against each output row *after*
/// aggregation — it can reference both key columns and aggregate
/// output names (the planner rewrites `FunctionCall` nodes in the
/// HAVING expression into `Field("__agg_N")` references).
GroupBy {
input: Box<PlanNode>,
keys: Vec<String>,
aggregates: Vec<GroupAgg>,
having: Option<Expr>,
},
AlterTable {
table: String,
action: AlterAction,
},
DropTable {
name: String,
},
Insert {
table: String,
/// One assignment-block per row to insert. Always at least one.
rows: Vec<Vec<Assignment>>,
},
/// UPSERT: probe index on `key_column` — if miss, insert; if hit, update.
Upsert {
table: String,
key_column: String,
assignments: Vec<Assignment>,
on_conflict: Vec<Assignment>,
},
Update {
input: Box<PlanNode>,
table: String,
assignments: Vec<Assignment>,
},
Delete {
input: Box<PlanNode>,
table: String,
},
CreateTable {
name: String,
fields: Vec<(String, String, bool)>,
},
/// Create a materialized view: execute query, store results, register.
CreateView {
name: String,
query_text: String,
},
/// Explicitly refresh a materialized view.
RefreshView {
name: String,
},
/// Drop a materialized view (backing table + registry entry).
DropView {
name: String,
},
/// Window function computation layer.
Window {
input: Box<PlanNode>,
windows: Vec<WindowDef>,
},
/// UNION [ALL]: execute both sides, concatenate (ALL) or deduplicate.
Union {
left: Box<PlanNode>,
right: Box<PlanNode>,
all: bool,
},
/// EXPLAIN: format the inner plan tree as a text result without executing.
Explain {
input: Box<PlanNode>,
},
Begin,
Commit,
Rollback,
}
#[derive(Debug, Clone)]
pub struct ProjectField {
pub alias: Option<String>,
pub expr: Expr,
}
#[derive(Debug, Clone)]
pub struct SortKey {
pub field: String,
pub descending: bool,
}
/// One aggregate computation inside a `PlanNode::GroupBy`.
#[derive(Debug, Clone)]
pub struct GroupAgg {
pub function: AggFunc,
/// Source column name to aggregate over.
pub field: String,
/// Synthetic output column name (`__agg_0`, `__agg_1`, …).
pub output_name: String,
}
/// One window function definition inside a `PlanNode::Window`.
#[derive(Debug, Clone)]
pub struct WindowDef {
pub function: WindowFunc,
pub args: Vec<Expr>,
pub partition_by: Vec<String>,
pub order_by: Vec<SortKey>,
pub output_name: String,
}