Skip to main content

surrealguard_syntax/ast/
statement.rs

1//! Statements.
2//!
3//! Statement structs model exactly what type analysis consumes. Clauses that
4//! cannot affect a statement's response type (comments, timeouts, index
5//! hints, ...) are consumed by lowering without record; statements containing
6//! broken syntax lower to [`Statement::Partial`], so analyzers never see a
7//! half-parsed structure. `PERMISSIONS` predicate expressions are the
8//! exception: they are analyzed (undefined fields/functions, always-false and
9//! non-boolean predicates), so lowering retains each `FOR <action> WHERE
10//! <expr>` predicate.
11
12use super::{
13    DataClause, Expr, GroupClause, Idiom, OrderClause, PartialNode, Projection, ReturnMode,
14    Spanned, TypeExpr,
15};
16use crate::span::ByteRange;
17
18/// A lowered source file: statements in source order.
19#[derive(Clone, Debug, Default, PartialEq)]
20pub struct Script {
21    /// Top-level statements, in source order.
22    pub statements: Vec<Spanned<Statement>>,
23}
24
25// Variant payloads differ widely in size by design: statements are built
26// once per parse and matched by reference, never stored in bulk — boxing the
27// large variants would cost matching ergonomics for no real memory win.
28#[allow(clippy::large_enum_variant)]
29/// One SurrealQL statement, dispatched on by the analyzers.
30#[derive(Clone, Debug, PartialEq)]
31pub enum Statement {
32    /// `SELECT`.
33    Select(SelectStmt),
34    /// `CREATE`.
35    Create(CreateStmt),
36    /// `UPDATE`.
37    Update(UpdateStmt),
38    /// `UPSERT`.
39    Upsert(UpsertStmt),
40    /// `DELETE`.
41    Delete(DeleteStmt),
42    /// `INSERT`.
43    Insert(InsertStmt),
44    /// `RELATE`.
45    Relate(RelateStmt),
46    /// `DEFINE ...`.
47    Define(DefineStmt),
48    /// `REMOVE ...`.
49    Remove(RemoveStmt),
50    /// `ALTER ...`.
51    Alter(AlterStmt),
52    /// `LET`.
53    Let(LetStmt),
54    /// `RETURN`.
55    Return(ReturnStmt),
56    /// `IF`/`ELSE`.
57    IfElse(IfElseStmt),
58    /// `FOR`.
59    For(ForStmt),
60    /// A bare `{ ...; ... }` block in statement position.
61    Block(Block),
62    /// `LIVE SELECT`.
63    LiveSelect(LiveSelectStmt),
64    /// `KILL`.
65    Kill(KillStmt),
66    /// `USE`.
67    Use(UseStmt),
68    /// `INFO FOR ...`.
69    Info(InfoStmt),
70    /// `SHOW CHANGES`.
71    Show(ShowStmt),
72    /// `REBUILD INDEX`.
73    Rebuild(RebuildStmt),
74    /// `THROW`.
75    Throw(ThrowStmt),
76    /// `BREAK`.
77    Break(BreakStmt),
78    /// `CONTINUE`.
79    Continue(ContinueStmt),
80    /// `BEGIN`.
81    Begin(BeginStmt),
82    /// `CANCEL`.
83    Cancel(CancelStmt),
84    /// `COMMIT`.
85    Commit(CommitStmt),
86    /// `SLEEP`.
87    Sleep(SleepStmt),
88    /// `OPTION`.
89    Option(OptionStmt),
90    /// A bare expression in statement position — most commonly the trailing
91    /// value of a block (`{ LET $x = 1; $x + 1 }`).
92    Expr(Spanned<Expr>),
93    /// A statement that failed to lower.
94    Partial(PartialNode),
95}
96
97/// `{ ...; ...; }` — also the body of IF/FOR/DEFINE FUNCTION.
98///
99/// The block analyzer dispatches each child to that child's own analyzer;
100/// per-statement invariants stay attached to their own statement kind.
101#[derive(Clone, Debug, Default, PartialEq)]
102pub struct Block {
103    /// The block's statements, in source order.
104    pub statements: Vec<Spanned<Statement>>,
105}
106
107/// `SELECT` — projections over one or more sources, plus its modifier
108/// clauses.
109#[derive(Clone, Debug, PartialEq)]
110pub struct SelectStmt {
111    /// `SELECT ... FROM ONLY ...` — single-object result, not an array.
112    pub only: bool,
113    /// `SELECT VALUE <expr>`.
114    pub value: bool,
115    /// The projection list (output columns).
116    pub projections: Vec<Projection>,
117    /// `FROM` sources.
118    pub from: Vec<Spanned<Expr>>,
119    /// `OMIT <fields>` — fields excluded from the result.
120    pub omit: Vec<Spanned<Idiom>>,
121    /// `FETCH <fields>` — record links to expand.
122    pub fetch: Vec<Spanned<Idiom>>,
123    /// `SPLIT <fields>` — array fields to fan out into separate rows.
124    pub split: Vec<Spanned<Idiom>>,
125    /// `WHERE <expr>` filter.
126    pub where_clause: Option<Spanned<Expr>>,
127    /// `GROUP BY`/`GROUP ALL`.
128    pub group: Option<GroupClause>,
129    /// `ORDER BY`.
130    pub order: Option<OrderClause>,
131    /// `LIMIT <expr>`. Literal-ness is the analyzer's judgment
132    /// (`LIMIT 5` vs `LIMIT $n`).
133    pub limit: Option<Spanned<Expr>>,
134    /// `START <expr>` — result offset.
135    pub start: Option<Spanned<Expr>>,
136    /// `EXPLAIN` — the clause's span, when present.
137    pub explain: Option<ByteRange>,
138    /// `TIMEOUT <duration>`.
139    pub timeout: Option<Spanned<Expr>>,
140    /// `PARALLEL` — the clause's span, when present.
141    pub parallel: Option<ByteRange>,
142}
143
144/// `CREATE` — new rows for a table or specific record ids.
145#[derive(Clone, Debug, PartialEq)]
146pub struct CreateStmt {
147    /// `CREATE ONLY person:one` — single object result, not an array.
148    pub only: bool,
149    /// The tables or record ids to create.
150    pub targets: Vec<Spanned<Expr>>,
151    /// The payload clause (`SET`/`CONTENT`/...), if any.
152    pub data: Option<DataClause>,
153    /// `RETURN` mode, if specified.
154    pub ret: Option<Spanned<ReturnMode>>,
155}
156
157/// `UPDATE` — modifies existing rows.
158#[derive(Clone, Debug, PartialEq)]
159pub struct UpdateStmt {
160    /// `UPDATE ONLY person:one` — single object result, not an array.
161    pub only: bool,
162    /// The tables or record ids to update.
163    pub targets: Vec<Spanned<Expr>>,
164    /// The payload clause (`SET`/`CONTENT`/...), if any.
165    pub data: Option<DataClause>,
166    /// `WHERE <expr>` filter selecting which rows to update.
167    pub where_clause: Option<Spanned<Expr>>,
168    /// `RETURN` mode, if specified.
169    pub ret: Option<Spanned<ReturnMode>>,
170}
171
172/// `UPSERT` — updates rows, creating them when absent.
173#[derive(Clone, Debug, PartialEq)]
174pub struct UpsertStmt {
175    /// `UPSERT ONLY person:one` — single object result, not an array.
176    pub only: bool,
177    /// The tables or record ids to upsert.
178    pub targets: Vec<Spanned<Expr>>,
179    /// The payload clause (`SET`/`CONTENT`/...), if any.
180    pub data: Option<DataClause>,
181    /// `WHERE <expr>` filter selecting which rows to update.
182    pub where_clause: Option<Spanned<Expr>>,
183    /// `RETURN` mode, if specified.
184    pub ret: Option<Spanned<ReturnMode>>,
185}
186
187/// `DELETE` — removes rows.
188#[derive(Clone, Debug, PartialEq)]
189pub struct DeleteStmt {
190    /// `DELETE ONLY person:one` — single object result, not an array.
191    pub only: bool,
192    /// The tables or record ids to delete from.
193    pub targets: Vec<Spanned<Expr>>,
194    /// `WHERE <expr>` filter selecting which rows to delete.
195    pub where_clause: Option<Spanned<Expr>>,
196    /// `RETURN` mode, if specified.
197    pub ret: Option<Spanned<ReturnMode>>,
198}
199
200/// `INSERT` — bulk row insertion with its own payload forms.
201#[derive(Clone, Debug, PartialEq)]
202pub struct InsertStmt {
203    /// `INSERT IGNORE`.
204    pub ignore: bool,
205    /// `INSERT RELATION` (row payloads describe edges).
206    pub relation: bool,
207    /// `INTO <target>`.
208    pub target: Option<Spanned<Expr>>,
209    /// The rows/values to insert.
210    pub data: InsertData,
211    /// `RETURN` mode, if specified.
212    pub ret: Option<Spanned<ReturnMode>>,
213}
214
215/// INSERT's payload forms — distinct from the other mutations' `DataClause`
216/// (the grammar gives INSERT `BulkInsert`/`FieldAssignment` children the
217/// others don't have).
218#[derive(Clone, Debug, PartialEq)]
219pub enum InsertData {
220    /// Object or array-of-objects payload.
221    Values(Vec<Spanned<Expr>>),
222    /// `INSERT INTO t (a, b) VALUES (...), (...)` — each row is lowered to
223    /// `(column, value)` pairs so columns and values cannot misalign.
224    ///
225    /// The grammar flattens all rows' values into one sequence, so when the
226    /// total doesn't divide evenly by the column count the leftover cannot
227    /// be paired: `misaligned` carries the raw `(values, columns)` counts
228    /// with the statement's span for the arity finding.
229    Rows {
230        /// One vector of `(column, value)` pairs per input row.
231        rows: Vec<Vec<(Spanned<Idiom>, Spanned<Expr>)>>,
232        /// Raw `(values, columns)` counts when the flattened values don't
233        /// divide evenly by the column count; carries the statement span.
234        misaligned: Option<Spanned<(usize, usize)>>,
235    },
236    /// `INSERT ... SET`-style field assignments.
237    Assignments(Vec<(Spanned<Idiom>, Spanned<Expr>)>),
238    /// A payload that failed to lower.
239    Partial(PartialNode),
240}
241
242/// `RELATE from->edge->to` — three explicitly spanned positions, because
243/// edge-endpoint diagnostics will point at each independently.
244#[derive(Clone, Debug, PartialEq)]
245pub struct RelateStmt {
246    /// `RELATE ONLY ...` — single-object result, not an array.
247    pub only: bool,
248    /// The `from` endpoint (the edge's `in`).
249    pub from: Option<Spanned<Expr>>,
250    /// The edge table or record being created.
251    pub edge: Option<Spanned<Expr>>,
252    /// The `to` endpoint (the edge's `out`).
253    pub to: Option<Spanned<Expr>>,
254    /// The payload clause (`SET`/`CONTENT`/...), if any.
255    pub data: Option<DataClause>,
256    /// `RETURN` mode, if specified.
257    pub ret: Option<Spanned<ReturnMode>>,
258}
259
260/// DEFINE family. Tier 1 kinds are modeled; the long tail
261/// (ACCESS/API/BUCKET/CONFIG/...) is `Other` until an analyzer needs it.
262#[derive(Clone, Debug, PartialEq)]
263pub enum DefineStmt {
264    /// `DEFINE TABLE`.
265    Table(DefineTable),
266    /// `DEFINE FIELD`.
267    Field(DefineField),
268    /// `DEFINE INDEX`.
269    Index(DefineIndex),
270    /// `DEFINE EVENT`.
271    Event(DefineEvent),
272    /// `DEFINE PARAM`.
273    Param(DefineParam),
274    /// `DEFINE FUNCTION`.
275    Function(DefineFunction),
276    /// `DEFINE ANALYZER`.
277    Analyzer(DefineAnalyzer),
278    /// An unmodeled `DEFINE` kind (ACCESS/API/BUCKET/CONFIG/...).
279    Other(PartialNode),
280}
281
282/// `DEFINE TABLE`.
283#[derive(Clone, Debug, PartialEq)]
284pub struct DefineTable {
285    /// The table name.
286    pub name: Spanned<String>,
287    /// `OVERWRITE` — redefine an existing table.
288    pub overwrite: bool,
289    /// `SCHEMAFULL` (vs `SCHEMALESS`).
290    pub schemafull: bool,
291    /// `TYPE RELATION ...` — present when the table is an edge table.
292    pub relation: Option<RelationDef>,
293    /// `DEFINE TABLE ... DROP` — rows are never retained.
294    pub drop: bool,
295    /// `DEFINE TABLE ... CHANGEFEED <duration>`.
296    pub changefeed: bool,
297    /// `PERMISSIONS FOR <action> WHERE <expr>` predicate expressions. Each
298    /// `WHERE` predicate — from either the basic (`PERMISSIONS WHERE`) or the
299    /// per-action (`PERMISSIONS FOR select ... WHERE`) form — is retained so
300    /// the analyzer can walk it; `NONE`/`FULL` carry no predicate.
301    pub permissions: Vec<Spanned<Expr>>,
302}
303
304/// The `IN`/`OUT` endpoint tables of a relation table.
305#[derive(Clone, Debug, PartialEq)]
306pub struct RelationDef {
307    /// Allowed `in` endpoint tables.
308    pub in_tables: Vec<Spanned<String>>,
309    /// Allowed `out` endpoint tables.
310    pub out_tables: Vec<Spanned<String>>,
311    /// The whole `TYPE RELATION ...` clause.
312    pub span: ByteRange,
313}
314
315/// `DEFINE FIELD` — a (possibly nested) field on a table, with its
316/// declared type.
317#[derive(Clone, Debug, PartialEq)]
318pub struct DefineField {
319    /// The (possibly nested) field path.
320    pub path: Spanned<Idiom>,
321    /// The table the field belongs to.
322    pub table: Spanned<String>,
323    /// `TYPE <type>` — the declared field type, if any.
324    pub ty: Option<Spanned<TypeExpr>>,
325    /// `OVERWRITE` — redefine an existing field.
326    pub overwrite: bool,
327    /// `DEFAULT <expr>` — supplied when a row is created without the field.
328    pub default: Option<Spanned<Expr>>,
329    /// `VALUE <expr>` — the field is computed; writes are overwritten.
330    pub value: Option<Spanned<Expr>>,
331    /// `COMPUTED <expr>` — the field is derived from an expression and never
332    /// stored (SurrealDB 3.0). Like `VALUE`, its type is the expression's; a
333    /// common form is a record-reference back-traversal (`COMPUTED <~team`).
334    pub computed: Option<Spanned<Expr>>,
335    /// `REFERENCE` — the field's `record<...>` link participates in reference
336    /// traversal (`<~`), so a back-reference on the target table can resolve
337    /// through it.
338    pub reference: bool,
339    /// `ASSERT <expr>` — must hold for every write (`$value` in scope).
340    pub assert: Option<Spanned<Expr>>,
341    /// `READONLY` — writable only at creation.
342    pub readonly: bool,
343    /// `PERMISSIONS FOR <action> WHERE <expr>` predicate expressions. Each
344    /// `WHERE` predicate is retained so the analyzer can walk it against the
345    /// row; `NONE`/`FULL` carry no predicate.
346    pub permissions: Vec<Spanned<Expr>>,
347}
348
349/// `DEFINE INDEX`.
350#[derive(Clone, Debug, PartialEq)]
351pub struct DefineIndex {
352    /// The index name.
353    pub name: Spanned<String>,
354    /// The table the index is defined on.
355    pub table: Spanned<String>,
356    /// The indexed field paths.
357    pub fields: Vec<Spanned<Idiom>>,
358    /// What backs the index.
359    pub kind: IndexKind,
360}
361
362/// What backs a `DEFINE INDEX`: full-text search, a vector structure, a
363/// uniqueness constraint, or a plain lookup.
364#[derive(Clone, Copy, Debug, PartialEq, Eq)]
365pub enum IndexKind {
366    /// A plain lookup index.
367    Normal,
368    /// `UNIQUE` — a uniqueness constraint.
369    Unique,
370    /// `SEARCH ANALYZER ...` — full-text.
371    Search,
372    /// `MTREE`/`HNSW` — vector.
373    Vector,
374}
375
376/// `DEFINE EVENT` — a trigger with its condition and body.
377#[derive(Clone, Debug, PartialEq)]
378pub struct DefineEvent {
379    /// The event name.
380    pub name: Spanned<String>,
381    /// The table the event fires on.
382    pub table: Spanned<String>,
383    /// `WHEN <expr>` — the trigger condition.
384    pub when: Option<Spanned<Expr>>,
385    /// `THEN { ... }` / `THEN <expr>` — a block lowers to `Expr::Block`.
386    pub then: Option<Spanned<Expr>>,
387}
388
389/// `DEFINE PARAM` — a database-level parameter with a default value.
390#[derive(Clone, Debug, PartialEq)]
391pub struct DefineParam {
392    /// The parameter name (without `$`).
393    pub name: Spanned<String>,
394    /// `VALUE <expr>` — the parameter's value.
395    pub value: Option<Spanned<Expr>>,
396}
397
398/// `DEFINE FUNCTION fn::name(...)`.
399#[derive(Clone, Debug, PartialEq)]
400pub struct DefineFunction {
401    /// The function name including the `fn::` path.
402    pub name: Spanned<String>,
403    /// Parameters with their declared types, if any.
404    pub params: Vec<(Spanned<String>, Option<Spanned<TypeExpr>>)>,
405    /// The function body.
406    pub body: Option<Block>,
407    /// The declared return type, if any.
408    pub return_ty: Option<Spanned<TypeExpr>>,
409}
410
411/// `DEFINE ANALYZER` — a full-text analyzer pipeline.
412#[derive(Clone, Debug, PartialEq)]
413pub struct DefineAnalyzer {
414    /// The analyzer name.
415    pub name: Spanned<String>,
416    /// `TOKENIZERS ...` — the tokenizers in the pipeline.
417    pub tokenizers: Vec<Spanned<String>>,
418    /// `FILTERS ...` — the token filters in the pipeline.
419    pub filters: Vec<Spanned<String>>,
420}
421
422/// `REMOVE` — drops a schema object.
423#[derive(Clone, Debug, PartialEq)]
424pub struct RemoveStmt {
425    /// The schema object being dropped.
426    pub target: RemoveTarget,
427}
428
429/// What a `REMOVE` statement drops.
430#[derive(Clone, Debug, PartialEq)]
431pub enum RemoveTarget {
432    /// `REMOVE TABLE <name>`.
433    Table(Spanned<String>),
434    /// `REMOVE FIELD <field> ON <table>`.
435    Field {
436        /// The field path being removed.
437        field: Spanned<Idiom>,
438        /// The table the field is on.
439        table: Spanned<String>,
440    },
441    /// `REMOVE INDEX <index> ON <table>`.
442    Index {
443        /// The index name being removed.
444        index: Spanned<String>,
445        /// The table the index is on.
446        table: Spanned<String>,
447    },
448    /// An unmodeled `REMOVE` target.
449    Other(PartialNode),
450}
451
452/// `ALTER TABLE`.
453#[derive(Clone, Debug, PartialEq)]
454pub struct AlterStmt {
455    /// The table being altered.
456    pub table: Option<Spanned<String>>,
457}
458
459/// `LET $name = value` — binds a statement-scope variable.
460#[derive(Clone, Debug, PartialEq)]
461pub struct LetStmt {
462    /// Binding name without `$`.
463    pub name: Spanned<String>,
464    /// The bound value expression.
465    pub value: Spanned<Expr>,
466}
467
468/// `RETURN` — yields a value from the enclosing scope.
469#[derive(Clone, Debug, PartialEq)]
470pub struct ReturnStmt {
471    /// The returned value expression, if any.
472    pub value: Option<Spanned<Expr>>,
473}
474
475/// `IF`/`ELSE IF`/`ELSE` — conditional branches, each with a block body.
476#[derive(Clone, Debug, PartialEq)]
477pub struct IfElseStmt {
478    /// `IF cond body` plus any `ELSE IF` arms, in source order.
479    pub branches: Vec<IfBranch>,
480    /// The trailing `ELSE` block, if any.
481    pub else_branch: Option<Block>,
482}
483
484/// One `IF`/`ELSE IF` arm: a condition and its body.
485#[derive(Clone, Debug, PartialEq)]
486pub struct IfBranch {
487    /// The branch condition.
488    pub condition: Spanned<Expr>,
489    /// The block run when the condition holds.
490    pub body: Block,
491}
492
493/// `FOR $item IN iterable { ... }`.
494#[derive(Clone, Debug, PartialEq)]
495pub struct ForStmt {
496    /// Loop binding name without `$`.
497    pub binding: Spanned<String>,
498    /// The expression iterated over.
499    pub iterable: Spanned<Expr>,
500    /// The loop body.
501    pub body: Block,
502}
503
504/// `LIVE SELECT` — subscribes to changes on a table.
505#[derive(Clone, Debug, PartialEq)]
506pub struct LiveSelectStmt {
507    /// The table subscribed to.
508    pub table: Option<Spanned<String>>,
509}
510
511/// `KILL` — terminates a live query by id.
512#[derive(Clone, Debug, PartialEq)]
513pub struct KillStmt {
514    /// The live-query id to terminate.
515    pub id: Option<Spanned<Expr>>,
516}
517
518/// `USE NS ... DB ...` — selects the active namespace/database.
519#[derive(Clone, Debug, PartialEq)]
520pub struct UseStmt {
521    /// `NS <name>` — the namespace to switch to.
522    pub namespace: Option<Spanned<String>>,
523    /// `DB <name>` — the database to switch to.
524    pub database: Option<Spanned<String>>,
525}
526
527/// `INFO FOR ...` — describes a catalog level.
528#[derive(Clone, Debug, PartialEq)]
529pub struct InfoStmt {
530    /// `INFO FOR TABLE <name>` — the table, when scoped to one.
531    pub table: Option<Spanned<String>>,
532}
533
534/// `SHOW CHANGES FOR TABLE ...` — reads a change feed.
535#[derive(Clone, Debug, PartialEq)]
536pub struct ShowStmt {
537    /// The table whose change feed is read.
538    pub table: Option<Spanned<String>>,
539    /// `SINCE <versionstamp|datetime>` — the raw expression.
540    pub since: Option<Spanned<Expr>>,
541}
542
543/// `REBUILD INDEX ... ON ...`.
544#[derive(Clone, Debug, PartialEq)]
545pub struct RebuildStmt {
546    /// The index being rebuilt.
547    pub index: Option<Spanned<String>>,
548    /// The table the index is on.
549    pub table: Option<Spanned<String>>,
550}
551
552/// `THROW` — raises an error value.
553#[derive(Clone, Debug, PartialEq)]
554pub struct ThrowStmt {
555    /// The error value expression.
556    pub value: Option<Spanned<Expr>>,
557}
558
559/// `SLEEP <duration>`.
560#[derive(Clone, Debug, PartialEq)]
561pub struct SleepStmt {
562    /// The sleep duration expression.
563    pub duration: Option<Spanned<Expr>>,
564}
565
566macro_rules! unit_statements {
567    ($($(#[$doc:meta])* $name:ident),+ $(,)?) => {
568        $(
569            $(#[$doc])*
570            #[derive(Clone, Debug, Default, PartialEq)]
571            pub struct $name {}
572        )+
573    };
574}
575
576unit_statements! {
577    /// `BREAK`.
578    BreakStmt,
579    /// `CONTINUE`.
580    ContinueStmt,
581    /// `BEGIN [TRANSACTION]`.
582    BeginStmt,
583    /// `CANCEL [TRANSACTION]`.
584    CancelStmt,
585    /// `COMMIT [TRANSACTION]`.
586    CommitStmt,
587    /// `OPTION <name> [= <value>]`.
588    OptionStmt,
589}