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}