Skip to main content

toasty_core/
stmt.rs

1//! Statement AST types for Toasty's query compilation pipeline.
2//!
3//! This module defines the abstract syntax tree (AST) for statements that
4//! Toasty's query engine processes. The top-level type is [`Statement`], which
5//! represents one of four operations: [`Query`], [`Insert`], [`Update`], or
6//! [`Delete`].
7//!
8//! Statements exist at two layers:
9//!
10//! - **Model-level**: references models, fields, and associations from the app
11//!   schema. This is what user-facing code produces.
12//! - **Table-level**: references tables, columns, and joins from the DB schema.
13//!   This is what the query engine lowers model-level statements into before
14//!   handing them to a database driver.
15//!
16//! The query engine pipeline transforms statements through several phases:
17//! simplify, lower, plan, and execute. Types in this module appear throughout
18//! all phases.
19//!
20//! # Examples
21//!
22//! ```ignore
23//! use toasty_core::stmt::{Statement, Query, Values};
24//!
25//! // Create a simple values-based query statement
26//! let query = Query::unit();
27//! let stmt = Statement::Query(query);
28//! assert!(stmt.is_query());
29//! ```
30
31mod assignments;
32pub use assignments::{Assignment, Assignments};
33
34mod association;
35pub use association::Association;
36
37mod condition;
38pub use condition::Condition;
39
40mod cte;
41pub use cte::Cte;
42
43mod cx;
44pub use cx::{DerivedRef, ExprContext, ExprTarget, IntoExprTarget, Resolve, ResolvedRef};
45
46mod delete;
47pub use delete::Delete;
48
49mod direction;
50pub use direction::Direction;
51
52mod document_storage_text;
53pub use document_storage_text::DocumentStorageText;
54
55mod entry;
56pub use entry::Entry;
57
58mod entry_mut;
59pub use entry_mut::EntryMut;
60
61mod entry_path;
62pub use entry_path::EntryPath;
63
64mod eval;
65
66mod expr;
67pub use expr::Expr;
68
69mod expr_all_op;
70pub use expr_all_op::ExprAllOp;
71
72mod expr_and;
73pub use expr_and::ExprAnd;
74
75mod expr_between;
76pub use expr_between::ExprBetween;
77
78mod expr_any;
79pub use expr_any::ExprAny;
80
81mod expr_any_op;
82pub use expr_any_op::ExprAnyOp;
83
84mod expr_arg;
85pub use expr_arg::ExprArg;
86
87mod expr_binary_op;
88pub use expr_binary_op::ExprBinaryOp;
89
90mod expr_cast;
91pub use expr_cast::ExprCast;
92
93mod expr_error;
94pub use expr_error::ExprError;
95
96mod expr_exists;
97pub use expr_exists::ExprExists;
98
99mod expr_func;
100pub use expr_func::ExprFunc;
101
102mod expr_in_list;
103pub use expr_in_list::ExprInList;
104
105mod expr_in_subquery;
106pub use expr_in_subquery::ExprInSubquery;
107
108mod expr_intersects;
109pub use expr_intersects::ExprIntersects;
110
111mod expr_is_null;
112pub use expr_is_null::ExprIsNull;
113
114mod expr_is_superset;
115pub use expr_is_superset::ExprIsSuperset;
116
117mod expr_is_variant;
118pub use expr_is_variant::ExprIsVariant;
119
120mod expr_length;
121pub use expr_length::ExprLength;
122
123mod expr_let;
124pub use expr_let::ExprLet;
125
126mod expr_like;
127pub use expr_like::ExprLike;
128
129mod expr_list;
130pub use expr_list::ExprList;
131
132mod expr_map;
133pub use expr_map::ExprMap;
134
135mod expr_match;
136pub use expr_match::{ExprMatch, MatchArm};
137
138mod expr_not;
139pub use expr_not::ExprNot;
140
141mod expr_or;
142pub use expr_or::ExprOr;
143
144mod expr_project;
145pub use expr_project::ExprProject;
146
147mod expr_record;
148pub use expr_record::ExprRecord;
149
150mod expr_reference;
151pub use expr_reference::{ExprColumn, ExprReference};
152
153mod expr_set;
154pub use expr_set::ExprSet;
155
156mod expr_set_op;
157pub use expr_set_op::ExprSetOp;
158
159mod expr_starts_with;
160pub use expr_starts_with::ExprStartsWith;
161
162mod expr_stmt;
163pub use expr_stmt::ExprStmt;
164
165mod filter;
166pub use filter::Filter;
167
168mod hash_index;
169pub use hash_index::HashIndex;
170
171mod sorted_index;
172pub use sorted_index::SortedIndex;
173
174mod func_count;
175pub use func_count::FuncCount;
176
177mod func_json_extract;
178pub use func_json_extract::FuncJsonExtract;
179
180mod func_last_insert_id;
181pub use func_last_insert_id::FuncLastInsertId;
182
183mod insert;
184pub use insert::Insert;
185
186mod insert_table;
187pub use insert_table::InsertTable;
188
189mod insert_target;
190pub use insert_target::InsertTarget;
191
192mod input;
193pub(crate) use input::InputResolve;
194pub use input::{ConstInput, Input, TypedInput};
195
196mod join;
197pub use join::{Join, JoinOp};
198
199mod limit;
200pub use limit::{Limit, LimitCursor, LimitOffset};
201
202#[cfg(feature = "assert-struct")]
203mod like;
204
205mod node;
206pub use node::Node;
207
208mod num;
209
210mod op_binary;
211pub use op_binary::BinaryOp;
212
213mod order_by;
214pub use order_by::OrderBy;
215
216mod order_by_expr;
217pub use order_by_expr::OrderByExpr;
218
219mod op_set;
220pub use op_set::SetOp;
221
222mod path;
223pub use path::{Path, PathRoot};
224
225mod path_field_set;
226pub use path_field_set::PathFieldSet;
227
228mod projection;
229pub use projection::{Project, Projection};
230
231mod query;
232pub use query::{Lock, Query};
233
234mod returning;
235pub use returning::Returning;
236
237mod select;
238pub use select::Select;
239
240mod source;
241pub use source::{Source, SourceModel};
242
243mod source_table;
244pub use source_table::SourceTable;
245
246mod source_table_id;
247pub use source_table_id::SourceTableId;
248
249mod sparse_record;
250pub use sparse_record::SparseRecord;
251
252mod substitute;
253use substitute::Substitute;
254
255mod table_derived;
256pub use table_derived::TableDerived;
257
258mod table_ref;
259pub use table_ref::TableRef;
260
261mod table_factor;
262pub use table_factor::TableFactor;
263
264mod table_with_joins;
265pub use table_with_joins::TableWithJoins;
266
267mod ty;
268pub use ty::Type;
269
270mod ty_union;
271pub use ty_union::TypeUnion;
272
273#[cfg(feature = "jiff")]
274mod ty_jiff;
275
276mod update;
277pub use update::{Update, UpdateTarget};
278
279mod value;
280pub use value::Value;
281
282mod value_cmp;
283
284mod values;
285pub use values::Values;
286
287#[cfg(feature = "jiff")]
288mod value_jiff;
289
290mod value_object;
291pub use value_object::ValueObject;
292
293mod value_record;
294pub use value_record::ValueRecord;
295
296mod value_set;
297pub use value_set::ValueSet;
298
299/// Mutable AST visitor trait and helpers.
300pub mod visit_mut;
301pub use visit_mut::VisitMut;
302
303mod value_list;
304
305mod value_stream;
306pub use value_stream::ValueStream;
307
308/// Read-only AST visitor trait and helpers.
309pub mod visit;
310pub use visit::Visit;
311
312mod with;
313pub use with::With;
314
315use crate::schema::db::TableId;
316use std::fmt;
317
318/// A top-level statement in Toasty's AST.
319///
320/// Each variant corresponds to one of the four fundamental database operations.
321/// A `Statement` is the primary input to the query engine's compilation
322/// pipeline and the output of code generated by `#[derive(Model)]`.
323///
324/// # Examples
325///
326/// ```ignore
327/// use toasty_core::stmt::{Statement, Query, Values};
328///
329/// let query = Query::unit();
330/// let stmt = Statement::from(query);
331/// assert!(stmt.is_query());
332/// assert!(!stmt.is_insert());
333/// ```
334#[derive(Clone, PartialEq)]
335pub enum Statement {
336    /// Delete one or more existing records.
337    Delete(Delete),
338
339    /// Create one or more new records.
340    Insert(Insert),
341
342    /// Query (read) records from the database.
343    Query(Query),
344
345    /// Update one or more existing records.
346    Update(Update),
347}
348
349impl Statement {
350    /// Returns the statement variant name for logging.
351    pub fn name(&self) -> &str {
352        match self {
353            Statement::Query(_) => "query",
354            Statement::Insert(_) => "insert",
355            Statement::Update(_) => "update",
356            Statement::Delete(_) => "delete",
357        }
358    }
359
360    /// Substitutes argument placeholders in this statement with concrete values
361    /// from `input`.
362    pub fn substitute(&mut self, input: impl Input) {
363        Substitute::new(input).visit_stmt_mut(self);
364    }
365
366    /// Returns `true` if this statement is a query whose body contains only
367    /// constant values (no table references or subqueries) and has no CTEs.
368    pub fn is_const(&self) -> bool {
369        match self {
370            Statement::Query(query) => {
371                if query.with.is_some() {
372                    return false;
373                }
374
375                query.body.is_const()
376            }
377            _ => false,
378        }
379    }
380
381    /// Attempts to return a reference to an inner [`Update`].
382    ///
383    /// * If `self` is a [`Statement::Update`], a reference to the inner [`Update`] is
384    ///   returned wrapped in [`Some`].
385    /// * Else, [`None`] is returned.
386    pub fn as_update(&self) -> Option<&Update> {
387        match self {
388            Self::Update(update) => Some(update),
389            _ => None,
390        }
391    }
392
393    /// Consumes `self` and attempts to return the inner [`Update`].
394    ///
395    /// * If `self` is a [`Statement::Update`], inner [`Update`] is returned wrapped in
396    ///   [`Some`].
397    /// * Else, [`None`] is returned.
398    pub fn into_update(self) -> Option<Update> {
399        match self {
400            Self::Update(update) => Some(update),
401            _ => None,
402        }
403    }
404
405    /// Returns `true` if this statement expects at most one result row.
406    pub fn is_single(&self) -> bool {
407        match self {
408            Statement::Query(q) => q.single,
409            Statement::Insert(i) => i.source.single,
410            Statement::Update(i) => match &i.target {
411                UpdateTarget::Query(q) => q.single,
412                UpdateTarget::Model(_) => true,
413                _ => false,
414            },
415            Statement::Delete(d) => d.selection().single,
416        }
417    }
418
419    /// Consumes `self` and returns the inner [`Update`].
420    ///
421    /// # Panics
422    ///
423    /// If `self` is not a [`Statement::Update`].
424    pub fn into_update_unwrap(self) -> Update {
425        match self {
426            Self::Update(update) => update,
427            v => panic!("expected `Update`, found {v:#?}"),
428        }
429    }
430}
431
432impl Node for Statement {
433    fn visit<V: Visit>(&self, mut visit: V) {
434        visit.visit_stmt(self);
435    }
436
437    fn visit_mut<V: VisitMut>(&mut self, mut visit: V) {
438        visit.visit_stmt_mut(self);
439    }
440}
441
442impl fmt::Debug for Statement {
443    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
444        match self {
445            Self::Delete(v) => v.fmt(f),
446            Self::Insert(v) => v.fmt(f),
447            Self::Query(v) => v.fmt(f),
448            Self::Update(v) => v.fmt(f),
449        }
450    }
451}