Skip to main content

powdb_query/
ast.rs

1use powdb_storage::types::Value;
2
3/// Top-level PowQL statement.
4#[derive(Debug, Clone, PartialEq)]
5pub enum Statement {
6    Query(QueryExpr),
7    Insert(InsertExpr),
8    UpdateQuery(UpdateExpr),
9    DeleteQuery(DeleteExpr),
10    CreateType(CreateTypeExpr),
11    AlterTable(AlterTableExpr),
12    DropTable(DropTableExpr),
13    CreateView(CreateViewExpr),
14    RefreshView(RefreshViewExpr),
15    DropView(DropViewExpr),
16    Union(UnionExpr),
17    Upsert(UpsertExpr),
18    Explain(Box<Statement>),
19    Begin,
20    Commit,
21    Rollback,
22}
23
24/// `alter User add column status: str` / `alter User drop column status`
25#[derive(Debug, Clone, PartialEq)]
26pub struct AlterTableExpr {
27    pub table: String,
28    pub action: AlterAction,
29}
30
31/// An individual ALTER TABLE action.
32#[derive(Debug, Clone, PartialEq)]
33pub enum AlterAction {
34    AddColumn {
35        name: String,
36        type_name: String,
37        required: bool,
38    },
39    DropColumn {
40        name: String,
41    },
42    /// `alter <Table> add index .<column>` — creates a B+Tree index on
43    /// `column`. No-op if the index already exists.
44    AddIndex {
45        column: String,
46    },
47    /// `alter <Table> add unique .<column>` — creates a UNIQUE B+Tree
48    /// index on `column`. Scans existing data first and fails if any
49    /// duplicate (non-null) value is present. Errors if the column is
50    /// already indexed (no in-place upgrade).
51    AddUnique {
52        column: String,
53    },
54}
55
56/// `drop User`
57#[derive(Debug, Clone, PartialEq)]
58pub struct DropTableExpr {
59    pub table: String,
60}
61
62/// `create [materialized] view ActiveUsers as User filter .active = true`
63#[derive(Debug, Clone, PartialEq)]
64pub struct CreateViewExpr {
65    pub name: String,
66    pub query: QueryExpr,
67    /// The original source query text, stored for re-execution on refresh.
68    pub query_text: String,
69}
70
71/// `refresh ActiveUsers`
72#[derive(Debug, Clone, PartialEq)]
73pub struct RefreshViewExpr {
74    pub name: String,
75}
76
77/// `drop view ActiveUsers`
78#[derive(Debug, Clone, PartialEq)]
79pub struct DropViewExpr {
80    pub name: String,
81}
82
83/// `User filter .age > 30 union User filter .status = "vip"`
84#[derive(Debug, Clone, PartialEq)]
85pub struct UnionExpr {
86    pub left: Box<Statement>,
87    pub right: Box<Statement>,
88    /// `true` for `union all` (keep duplicates), `false` for `union` (deduplicate).
89    pub all: bool,
90}
91
92/// A query expression: Type [join ...]* [filter ...] [order ...] [limit ...] [{ projection }]
93#[derive(Debug, Clone, PartialEq)]
94pub struct QueryExpr {
95    pub source: String,
96    /// Optional alias for the primary source (e.g. `User as u`). Used to
97    /// disambiguate qualified column references in join queries. `None` for
98    /// single-table queries.
99    pub alias: Option<String>,
100    /// Zero or more join clauses chained to the primary source. For a
101    /// single-table query this is always empty so existing code paths are
102    /// untouched.
103    pub joins: Vec<JoinClause>,
104    pub filter: Option<Expr>,
105    pub order: Option<OrderClause>,
106    pub limit: Option<Expr>,
107    pub offset: Option<Expr>,
108    pub projection: Option<Vec<ProjectionField>>,
109    pub aggregation: Option<AggregateExpr>,
110    pub distinct: bool,
111    pub group_by: Option<GroupByClause>,
112}
113
114/// GROUP BY clause: `group .field1, .field2 [having <expr>]`.
115#[derive(Debug, Clone, PartialEq)]
116pub struct GroupByClause {
117    pub keys: Vec<String>,
118    pub having: Option<Expr>,
119}
120
121/// A join clause appended to a query's primary source.
122///
123/// Example syntax (Mission E1.1 parser accepts this; executor still errors):
124///   `User as u inner join Order as o on u.id = o.user_id filter o.total > 100`
125#[derive(Debug, Clone, PartialEq)]
126pub struct JoinClause {
127    pub kind: JoinKind,
128    pub source: String,
129    pub alias: Option<String>,
130    /// `on <expr>` — required for every kind except `Cross`.
131    pub on: Option<Expr>,
132}
133
134#[derive(Debug, Clone, Copy, PartialEq, Eq)]
135pub enum JoinKind {
136    Inner,
137    LeftOuter,
138    RightOuter,
139    Cross,
140}
141
142#[derive(Debug, Clone, PartialEq)]
143pub struct ProjectionField {
144    pub alias: Option<String>,
145    pub expr: Expr,
146}
147
148#[derive(Debug, Clone, PartialEq)]
149pub struct OrderClause {
150    pub keys: Vec<OrderKey>,
151}
152
153#[derive(Debug, Clone, PartialEq)]
154pub struct OrderKey {
155    pub field: String,
156    pub descending: bool,
157}
158
159#[derive(Debug, Clone, PartialEq)]
160pub struct InsertExpr {
161    pub target: String,
162    /// One assignment-block per row. Always contains at least one row;
163    /// `insert T { .. }` yields one, `insert T { .. }, { .. }` yields many.
164    pub rows: Vec<Vec<Assignment>>,
165    /// `true` when the statement ends with `returning` — the executor returns
166    /// the inserted rows (all columns) instead of a modified-count.
167    pub returning: bool,
168}
169
170#[derive(Debug, Clone, PartialEq)]
171pub struct UpdateExpr {
172    pub source: String,
173    pub filter: Option<Expr>,
174    pub assignments: Vec<Assignment>,
175    /// `true` when the statement ends with `returning` — the executor returns
176    /// the post-update rows (all columns) instead of a modified-count.
177    pub returning: bool,
178}
179
180#[derive(Debug, Clone, PartialEq)]
181pub struct DeleteExpr {
182    pub source: String,
183    pub filter: Option<Expr>,
184    /// `true` when the statement ends with `returning` — the executor returns
185    /// the pre-delete rows (all columns) instead of a modified-count.
186    pub returning: bool,
187}
188
189#[derive(Debug, Clone, PartialEq)]
190pub struct Assignment {
191    pub field: String,
192    pub value: Expr,
193}
194
195#[derive(Debug, Clone, PartialEq)]
196/// `upsert User on .id { id := 1, name := "Alice" } [on conflict { name := "Alice" }]`
197pub struct UpsertExpr {
198    pub target: String,
199    pub key_column: String,
200    pub assignments: Vec<Assignment>,
201    /// Assignments to apply on conflict. If empty, all non-key assignments
202    /// from `assignments` are used as the update set.
203    pub on_conflict: Vec<Assignment>,
204}
205
206#[derive(Debug, Clone, PartialEq)]
207pub struct CreateTypeExpr {
208    pub name: String,
209    pub fields: Vec<FieldDef>,
210}
211
212#[derive(Debug, Clone, PartialEq)]
213pub struct FieldDef {
214    pub name: String,
215    pub type_name: String,
216    pub required: bool,
217    /// `true` when declared with the `unique` modifier — auto-creates a
218    /// unique B+Tree index on this column at table-create time.
219    pub unique: bool,
220    /// Literal default applied when an insert omits this column. `None` means
221    /// the column has no default (omitting it yields the empty set / null).
222    pub default: Option<Literal>,
223    /// `true` when declared `auto` — an integer column whose value is assigned
224    /// from a monotonic per-table sequence when an insert omits it.
225    pub auto: bool,
226}
227
228#[derive(Debug, Clone, PartialEq)]
229pub struct AggregateExpr {
230    pub function: AggFunc,
231    pub field: Option<String>,
232}
233
234#[derive(Debug, Clone, Copy, PartialEq)]
235pub enum AggFunc {
236    Count,
237    CountDistinct,
238    Avg,
239    Sum,
240    Min,
241    Max,
242}
243
244/// Window function identifier.
245#[derive(Debug, Clone, Copy, PartialEq)]
246pub enum WindowFunc {
247    RowNumber,
248    Rank,
249    DenseRank,
250    Sum,
251    Avg,
252    Count,
253    Min,
254    Max,
255}
256
257/// Scalar (non-aggregate) function — operates on single values.
258#[derive(Debug, Clone, Copy, PartialEq)]
259pub enum ScalarFn {
260    Upper,
261    Lower,
262    Length,
263    Trim,
264    Substring, // substring(expr, start, len) — 1-indexed
265    Concat,    // concat(expr, expr, ...) — variadic
266    // Math
267    Abs,
268    Round, // round(expr) or round(expr, decimals)
269    Ceil,
270    Floor,
271    Sqrt,
272    Pow, // pow(base, exponent)
273    // Date/time
274    Now,      // now() — returns current unix timestamp in microseconds
275    Extract,  // extract("year"|"month"|..., datetime_expr)
276    DateAdd,  // date_add(datetime_expr, amount, "unit")
277    DateDiff, // date_diff(dt1, dt2, "unit")
278}
279
280/// Target type for CAST expressions.
281#[derive(Debug, Clone, Copy, PartialEq)]
282pub enum CastType {
283    Int,
284    Float,
285    Str,
286    Bool,
287    DateTime,
288}
289
290/// Expressions.
291#[derive(Debug, Clone, PartialEq)]
292pub enum Expr {
293    Field(String),
294    /// A table-qualified field reference: `table.field` or `alias.field`.
295    /// Used by join queries to disambiguate columns that appear in multiple
296    /// sources. The single-table read path never emits this variant, so
297    /// existing fast paths keep matching `Expr::Field` unchanged.
298    QualifiedField {
299        qualifier: String,
300        field: String,
301    },
302    Literal(Literal),
303    Param(String),
304    BinaryOp(Box<Expr>, BinOp, Box<Expr>),
305    UnaryOp(UnaryOp, Box<Expr>),
306    FunctionCall(AggFunc, Box<Expr>),
307    /// Scalar (non-aggregate) function call.
308    ScalarFunc(ScalarFn, Vec<Expr>),
309    Coalesce(Box<Expr>, Box<Expr>),
310    /// `expr in (val1, val2, ...)` or `expr not in (val1, val2, ...)`
311    InList {
312        expr: Box<Expr>,
313        list: Vec<Expr>,
314        negated: bool,
315    },
316    /// `expr [not] in (subquery)` — the subquery is a full QueryExpr
317    /// that produces a single column.
318    InSubquery {
319        expr: Box<Expr>,
320        subquery: Box<QueryExpr>,
321        negated: bool,
322    },
323    /// `[not] exists (subquery)` — the subquery is a full QueryExpr.
324    /// Currently uncorrelated only: the executor runs the subquery once
325    /// before the scan loop and replaces this node with a Bool literal.
326    ExistsSubquery {
327        subquery: Box<QueryExpr>,
328        negated: bool,
329    },
330    /// CASE WHEN ... THEN ... [ELSE ...] END
331    Case {
332        whens: Vec<(Box<Expr>, Box<Expr>)>,
333        else_expr: Option<Box<Expr>>,
334    },
335    /// Window function: `func(args) over (partition ... order ...)`
336    Window {
337        function: WindowFunc,
338        args: Vec<Expr>,
339        partition_by: Vec<String>,
340        order_by: Vec<OrderKey>,
341    },
342    /// Type cast: `cast(expr, "int")` or `cast(expr, "str")` etc.
343    Cast(Box<Expr>, CastType),
344    /// A runtime-materialized literal carrying a concrete Value. Produced only
345    /// during subquery/correlated substitution (post-planning) for values that
346    /// have no Literal form (NULL, datetime, uuid, bytes); never emitted by the
347    /// parser/canonicalizer.
348    ValueLit(Value),
349    /// The `null` literal — produces `Value::Empty`.
350    Null,
351}
352
353#[derive(Debug, Clone, PartialEq)]
354pub enum Literal {
355    Int(i64),
356    Float(f64),
357    String(String),
358    Bool(bool),
359}
360
361/// A bound value supplied for a `$N` placeholder in
362/// [`crate::parser::parse_with_params`].
363///
364/// Unlike [`Literal`], this carries a `Null` variant so a parameter can
365/// bind PowQL `null` (substituted as `Token::Null`, not a string). Values
366/// are turned into literal *tokens* before parsing, so an injection-shaped
367/// string is inert data — it can never change the query's shape.
368#[derive(Debug, Clone, PartialEq)]
369pub enum ParamValue {
370    Null,
371    Int(i64),
372    Float(f64),
373    Bool(bool),
374    Str(String),
375}
376
377#[derive(Debug, Clone, Copy, PartialEq)]
378pub enum BinOp {
379    Eq,
380    Neq,
381    Lt,
382    Gt,
383    Lte,
384    Gte,
385    And,
386    Or,
387    Add,
388    Sub,
389    Mul,
390    Div,
391    Like,
392}
393
394#[derive(Debug, Clone, Copy, PartialEq)]
395pub enum UnaryOp {
396    Not,
397    Exists,
398    NotExists,
399    IsNull,
400    IsNotNull,
401}