Skip to main content

powdb_query/
plan.rs

1use crate::ast::{AggFunc, AlterAction, Assignment, Expr, JoinKind, WindowFunc};
2
3/// A column definition carried by `PlanNode::CreateTable`. Replaces the
4/// old `(name, type_name, required)` tuple so the `unique` modifier can
5/// flow from the parser through to the executor's DDL arm.
6#[derive(Debug, Clone)]
7pub struct CreateField {
8    pub name: String,
9    pub type_name: String,
10    pub required: bool,
11    pub unique: bool,
12}
13
14/// Physical plan nodes — what the executor actually runs.
15#[derive(Debug, Clone)]
16pub enum PlanNode {
17    SeqScan {
18        table: String,
19    },
20    /// Mission E1.2: sequential scan that renames output columns to
21    /// `alias.field`. Used exclusively as the leaves of a join plan so
22    /// downstream `NestedLoopJoin` + `Filter` + `Project` nodes can resolve
23    /// `Expr::QualifiedField` lookups by direct column-name match. Kept
24    /// separate from `SeqScan` so the single-table fast paths (which match
25    /// on `PlanNode::SeqScan { .. }` in many places) stay untouched.
26    AliasScan {
27        table: String,
28        alias: String,
29    },
30    IndexScan {
31        table: String,
32        column: String,
33        key: Expr,
34    },
35    /// B+tree range scan: returns rows where the indexed column falls within
36    /// the given bounds. Generated by the planner when it detects inequality
37    /// predicates (>, >=, <, <=, BETWEEN) on an indexed column. The executor
38    /// falls back to SeqScan+Filter if no index exists on the column.
39    RangeScan {
40        table: String,
41        column: String,
42        /// Lower bound: (expr, inclusive). None = unbounded below.
43        start: Option<(Expr, bool)>,
44        /// Upper bound: (expr, inclusive). None = unbounded above.
45        end: Option<(Expr, bool)>,
46    },
47    Filter {
48        input: Box<PlanNode>,
49        predicate: Expr,
50    },
51    Project {
52        input: Box<PlanNode>,
53        fields: Vec<ProjectField>,
54    },
55    Sort {
56        input: Box<PlanNode>,
57        keys: Vec<SortKey>,
58    },
59    Limit {
60        input: Box<PlanNode>,
61        count: Expr,
62    },
63    Offset {
64        input: Box<PlanNode>,
65        count: Expr,
66    },
67    Aggregate {
68        input: Box<PlanNode>,
69        function: AggFunc,
70        field: Option<String>,
71    },
72    /// Mission E1.2: nested-loop join. Correctness-first implementation —
73    /// O(L × R) scan for every join. E1.3 will add a hash-join fast path
74    /// for equijoins (the common case). The executor handles `Inner`,
75    /// `Cross`, and `LeftOuter`; `RightOuter` is rewritten by the planner
76    /// into a `LeftOuter` with swapped inputs.
77    NestedLoopJoin {
78        left: Box<PlanNode>,
79        right: Box<PlanNode>,
80        /// Join predicate. `None` for `Cross` joins (emit every pair).
81        on: Option<Expr>,
82        kind: JoinKind,
83    },
84    Distinct {
85        input: Box<PlanNode>,
86    },
87    /// Mission E2b: grouped aggregation. Output columns are
88    /// `keys ++ [agg.output_name for agg in aggregates]`. The optional
89    /// `having` predicate is evaluated against each output row *after*
90    /// aggregation — it can reference both key columns and aggregate
91    /// output names (the planner rewrites `FunctionCall` nodes in the
92    /// HAVING expression into `Field("__agg_N")` references).
93    GroupBy {
94        input: Box<PlanNode>,
95        keys: Vec<String>,
96        aggregates: Vec<GroupAgg>,
97        having: Option<Expr>,
98    },
99    AlterTable {
100        table: String,
101        action: AlterAction,
102    },
103    DropTable {
104        name: String,
105    },
106    Insert {
107        table: String,
108        /// One assignment-block per row to insert. Always at least one.
109        rows: Vec<Vec<Assignment>>,
110    },
111    /// UPSERT: probe index on `key_column` — if miss, insert; if hit, update.
112    Upsert {
113        table: String,
114        key_column: String,
115        assignments: Vec<Assignment>,
116        on_conflict: Vec<Assignment>,
117    },
118    Update {
119        input: Box<PlanNode>,
120        table: String,
121        assignments: Vec<Assignment>,
122    },
123    Delete {
124        input: Box<PlanNode>,
125        table: String,
126    },
127    CreateTable {
128        name: String,
129        fields: Vec<CreateField>,
130    },
131    /// Create a materialized view: execute query, store results, register.
132    CreateView {
133        name: String,
134        query_text: String,
135    },
136    /// Explicitly refresh a materialized view.
137    RefreshView {
138        name: String,
139    },
140    /// Drop a materialized view (backing table + registry entry).
141    DropView {
142        name: String,
143    },
144    /// Window function computation layer.
145    Window {
146        input: Box<PlanNode>,
147        windows: Vec<WindowDef>,
148    },
149    /// UNION [ALL]: execute both sides, concatenate (ALL) or deduplicate.
150    Union {
151        left: Box<PlanNode>,
152        right: Box<PlanNode>,
153        all: bool,
154    },
155    /// EXPLAIN: format the inner plan tree as a text result without executing.
156    Explain {
157        input: Box<PlanNode>,
158    },
159    Begin,
160    Commit,
161    Rollback,
162}
163
164#[derive(Debug, Clone)]
165pub struct ProjectField {
166    pub alias: Option<String>,
167    pub expr: Expr,
168}
169
170#[derive(Debug, Clone)]
171pub struct SortKey {
172    pub field: String,
173    pub descending: bool,
174}
175
176/// One aggregate computation inside a `PlanNode::GroupBy`.
177#[derive(Debug, Clone)]
178pub struct GroupAgg {
179    pub function: AggFunc,
180    /// Source column name to aggregate over.
181    pub field: String,
182    /// Synthetic output column name (`__agg_0`, `__agg_1`, …).
183    pub output_name: String,
184}
185
186/// One window function definition inside a `PlanNode::Window`.
187#[derive(Debug, Clone)]
188pub struct WindowDef {
189    pub function: WindowFunc,
190    pub args: Vec<Expr>,
191    pub partition_by: Vec<String>,
192    pub order_by: Vec<SortKey>,
193    pub output_name: String,
194}