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}
166
167#[derive(Debug, Clone, PartialEq)]
168pub struct UpdateExpr {
169    pub source: String,
170    pub filter: Option<Expr>,
171    pub assignments: Vec<Assignment>,
172}
173
174#[derive(Debug, Clone, PartialEq)]
175pub struct DeleteExpr {
176    pub source: String,
177    pub filter: Option<Expr>,
178}
179
180#[derive(Debug, Clone, PartialEq)]
181pub struct Assignment {
182    pub field: String,
183    pub value: Expr,
184}
185
186#[derive(Debug, Clone, PartialEq)]
187/// `upsert User on .id { id := 1, name := "Alice" } [on conflict { name := "Alice" }]`
188pub struct UpsertExpr {
189    pub target: String,
190    pub key_column: String,
191    pub assignments: Vec<Assignment>,
192    /// Assignments to apply on conflict. If empty, all non-key assignments
193    /// from `assignments` are used as the update set.
194    pub on_conflict: Vec<Assignment>,
195}
196
197#[derive(Debug, Clone, PartialEq)]
198pub struct CreateTypeExpr {
199    pub name: String,
200    pub fields: Vec<FieldDef>,
201}
202
203#[derive(Debug, Clone, PartialEq)]
204pub struct FieldDef {
205    pub name: String,
206    pub type_name: String,
207    pub required: bool,
208    /// `true` when declared with the `unique` modifier — auto-creates a
209    /// unique B+Tree index on this column at table-create time.
210    pub unique: bool,
211}
212
213#[derive(Debug, Clone, PartialEq)]
214pub struct AggregateExpr {
215    pub function: AggFunc,
216    pub field: Option<String>,
217}
218
219#[derive(Debug, Clone, Copy, PartialEq)]
220pub enum AggFunc {
221    Count,
222    CountDistinct,
223    Avg,
224    Sum,
225    Min,
226    Max,
227}
228
229/// Window function identifier.
230#[derive(Debug, Clone, Copy, PartialEq)]
231pub enum WindowFunc {
232    RowNumber,
233    Rank,
234    DenseRank,
235    Sum,
236    Avg,
237    Count,
238    Min,
239    Max,
240}
241
242/// Scalar (non-aggregate) function — operates on single values.
243#[derive(Debug, Clone, Copy, PartialEq)]
244pub enum ScalarFn {
245    Upper,
246    Lower,
247    Length,
248    Trim,
249    Substring, // substring(expr, start, len) — 1-indexed
250    Concat,    // concat(expr, expr, ...) — variadic
251    // Math
252    Abs,
253    Round, // round(expr) or round(expr, decimals)
254    Ceil,
255    Floor,
256    Sqrt,
257    Pow, // pow(base, exponent)
258    // Date/time
259    Now,      // now() — returns current unix timestamp in microseconds
260    Extract,  // extract("year"|"month"|..., datetime_expr)
261    DateAdd,  // date_add(datetime_expr, amount, "unit")
262    DateDiff, // date_diff(dt1, dt2, "unit")
263}
264
265/// Target type for CAST expressions.
266#[derive(Debug, Clone, Copy, PartialEq)]
267pub enum CastType {
268    Int,
269    Float,
270    Str,
271    Bool,
272    DateTime,
273}
274
275/// Expressions.
276#[derive(Debug, Clone, PartialEq)]
277pub enum Expr {
278    Field(String),
279    /// A table-qualified field reference: `table.field` or `alias.field`.
280    /// Used by join queries to disambiguate columns that appear in multiple
281    /// sources. The single-table read path never emits this variant, so
282    /// existing fast paths keep matching `Expr::Field` unchanged.
283    QualifiedField {
284        qualifier: String,
285        field: String,
286    },
287    Literal(Literal),
288    Param(String),
289    BinaryOp(Box<Expr>, BinOp, Box<Expr>),
290    UnaryOp(UnaryOp, Box<Expr>),
291    FunctionCall(AggFunc, Box<Expr>),
292    /// Scalar (non-aggregate) function call.
293    ScalarFunc(ScalarFn, Vec<Expr>),
294    Coalesce(Box<Expr>, Box<Expr>),
295    /// `expr in (val1, val2, ...)` or `expr not in (val1, val2, ...)`
296    InList {
297        expr: Box<Expr>,
298        list: Vec<Expr>,
299        negated: bool,
300    },
301    /// `expr [not] in (subquery)` — the subquery is a full QueryExpr
302    /// that produces a single column.
303    InSubquery {
304        expr: Box<Expr>,
305        subquery: Box<QueryExpr>,
306        negated: bool,
307    },
308    /// `[not] exists (subquery)` — the subquery is a full QueryExpr.
309    /// Currently uncorrelated only: the executor runs the subquery once
310    /// before the scan loop and replaces this node with a Bool literal.
311    ExistsSubquery {
312        subquery: Box<QueryExpr>,
313        negated: bool,
314    },
315    /// CASE WHEN ... THEN ... [ELSE ...] END
316    Case {
317        whens: Vec<(Box<Expr>, Box<Expr>)>,
318        else_expr: Option<Box<Expr>>,
319    },
320    /// Window function: `func(args) over (partition ... order ...)`
321    Window {
322        function: WindowFunc,
323        args: Vec<Expr>,
324        partition_by: Vec<String>,
325        order_by: Vec<OrderKey>,
326    },
327    /// Type cast: `cast(expr, "int")` or `cast(expr, "str")` etc.
328    Cast(Box<Expr>, CastType),
329    /// A runtime-materialized literal carrying a concrete Value. Produced only
330    /// during subquery/correlated substitution (post-planning) for values that
331    /// have no Literal form (NULL, datetime, uuid, bytes); never emitted by the
332    /// parser/canonicalizer.
333    ValueLit(Value),
334    /// The `null` literal — produces `Value::Empty`.
335    Null,
336}
337
338#[derive(Debug, Clone, PartialEq)]
339pub enum Literal {
340    Int(i64),
341    Float(f64),
342    String(String),
343    Bool(bool),
344}
345
346/// A bound value supplied for a `$N` placeholder in
347/// [`crate::parser::parse_with_params`].
348///
349/// Unlike [`Literal`], this carries a `Null` variant so a parameter can
350/// bind PowQL `null` (substituted as `Token::Null`, not a string). Values
351/// are turned into literal *tokens* before parsing, so an injection-shaped
352/// string is inert data — it can never change the query's shape.
353#[derive(Debug, Clone, PartialEq)]
354pub enum ParamValue {
355    Null,
356    Int(i64),
357    Float(f64),
358    Bool(bool),
359    Str(String),
360}
361
362#[derive(Debug, Clone, Copy, PartialEq)]
363pub enum BinOp {
364    Eq,
365    Neq,
366    Lt,
367    Gt,
368    Lte,
369    Gte,
370    And,
371    Or,
372    Add,
373    Sub,
374    Mul,
375    Div,
376    Like,
377}
378
379#[derive(Debug, Clone, Copy, PartialEq)]
380pub enum UnaryOp {
381    Not,
382    Exists,
383    NotExists,
384    IsNull,
385    IsNotNull,
386}