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 entry;
53pub use entry::Entry;
54
55mod entry_mut;
56pub use entry_mut::EntryMut;
57
58mod entry_path;
59pub use entry_path::EntryPath;
60
61mod eval;
62
63mod expr;
64pub use expr::Expr;
65
66mod expr_and;
67pub use expr_and::ExprAnd;
68
69mod expr_any;
70pub use expr_any::ExprAny;
71
72mod expr_arg;
73pub use expr_arg::ExprArg;
74
75mod expr_binary_op;
76pub use expr_binary_op::ExprBinaryOp;
77
78mod expr_cast;
79pub use expr_cast::ExprCast;
80
81mod expr_error;
82pub use expr_error::ExprError;
83
84mod expr_exists;
85pub use expr_exists::ExprExists;
86
87mod expr_func;
88pub use expr_func::ExprFunc;
89
90mod expr_in_list;
91pub use expr_in_list::ExprInList;
92
93mod expr_in_subquery;
94pub use expr_in_subquery::ExprInSubquery;
95
96mod expr_is_null;
97pub use expr_is_null::ExprIsNull;
98
99mod expr_is_variant;
100pub use expr_is_variant::ExprIsVariant;
101
102mod expr_let;
103pub use expr_let::ExprLet;
104
105mod expr_list;
106pub use expr_list::ExprList;
107
108mod expr_map;
109pub use expr_map::ExprMap;
110
111mod expr_match;
112pub use expr_match::{ExprMatch, MatchArm};
113
114mod expr_not;
115pub use expr_not::ExprNot;
116
117mod expr_or;
118pub use expr_or::ExprOr;
119
120mod expr_project;
121pub use expr_project::ExprProject;
122
123mod expr_record;
124pub use expr_record::ExprRecord;
125
126mod expr_reference;
127pub use expr_reference::{ExprColumn, ExprReference};
128
129mod expr_set;
130pub use expr_set::ExprSet;
131
132mod expr_set_op;
133pub use expr_set_op::ExprSetOp;
134
135mod expr_stmt;
136pub use expr_stmt::ExprStmt;
137
138mod filter;
139pub use filter::Filter;
140
141mod hash_index;
142pub use hash_index::HashIndex;
143
144mod sorted_index;
145pub use sorted_index::SortedIndex;
146
147mod func_count;
148pub use func_count::FuncCount;
149
150mod func_last_insert_id;
151pub use func_last_insert_id::FuncLastInsertId;
152
153mod insert;
154pub use insert::Insert;
155
156mod insert_table;
157pub use insert_table::InsertTable;
158
159mod insert_target;
160pub use insert_target::InsertTarget;
161
162mod input;
163pub use input::{ConstInput, Input, TypedInput};
164
165mod join;
166pub use join::{Join, JoinOp};
167
168mod limit;
169pub use limit::{Limit, LimitCursor, LimitOffset};
170
171#[cfg(feature = "assert-struct")]
172mod like;
173
174mod node;
175pub use node::Node;
176
177mod num;
178
179mod op_binary;
180pub use op_binary::BinaryOp;
181
182mod order_by;
183pub use order_by::OrderBy;
184
185mod order_by_expr;
186pub use order_by_expr::OrderByExpr;
187
188mod op_set;
189pub use op_set::SetOp;
190
191mod path;
192pub use path::{Path, PathRoot};
193
194mod path_field_set;
195pub use path_field_set::PathFieldSet;
196
197mod projection;
198pub use projection::{Project, Projection};
199
200mod query;
201pub use query::{Lock, Query};
202
203mod returning;
204pub use returning::Returning;
205
206mod select;
207pub use select::Select;
208
209mod source;
210pub use source::{Source, SourceModel};
211
212mod source_table;
213pub use source_table::SourceTable;
214
215mod source_table_id;
216pub use source_table_id::SourceTableId;
217
218mod sparse_record;
219pub use sparse_record::SparseRecord;
220
221mod substitute;
222use substitute::Substitute;
223
224mod table_derived;
225pub use table_derived::TableDerived;
226
227mod table_ref;
228pub use table_ref::TableRef;
229
230mod table_factor;
231pub use table_factor::TableFactor;
232
233mod table_with_joins;
234pub use table_with_joins::TableWithJoins;
235
236mod ty;
237pub use ty::Type;
238
239mod ty_union;
240pub use ty_union::TypeUnion;
241
242#[cfg(feature = "jiff")]
243mod ty_jiff;
244
245mod update;
246pub use update::{Update, UpdateTarget};
247
248mod value;
249pub use value::Value;
250
251mod value_cmp;
252
253mod values;
254pub use values::Values;
255
256#[cfg(feature = "jiff")]
257mod value_jiff;
258
259mod value_record;
260pub use value_record::ValueRecord;
261
262/// Mutable AST visitor trait and helpers.
263pub mod visit_mut;
264pub use visit_mut::VisitMut;
265
266mod value_list;
267
268mod value_stream;
269pub use value_stream::ValueStream;
270
271/// Read-only AST visitor trait and helpers.
272pub mod visit;
273pub use visit::Visit;
274
275mod with;
276pub use with::With;
277
278use crate::schema::db::TableId;
279use std::fmt;
280
281/// A top-level statement in Toasty's AST.
282///
283/// Each variant corresponds to one of the four fundamental database operations.
284/// A `Statement` is the primary input to the query engine's compilation
285/// pipeline and the output of code generated by `#[derive(Model)]`.
286///
287/// # Examples
288///
289/// ```ignore
290/// use toasty_core::stmt::{Statement, Query, Values};
291///
292/// let query = Query::unit();
293/// let stmt = Statement::from(query);
294/// assert!(stmt.is_query());
295/// assert!(!stmt.is_insert());
296/// ```
297#[derive(Clone, PartialEq)]
298pub enum Statement {
299    /// Delete one or more existing records.
300    Delete(Delete),
301
302    /// Create one or more new records.
303    Insert(Insert),
304
305    /// Query (read) records from the database.
306    Query(Query),
307
308    /// Update one or more existing records.
309    Update(Update),
310}
311
312impl Statement {
313    /// Returns the statement variant name for logging.
314    pub fn name(&self) -> &str {
315        match self {
316            Statement::Query(_) => "query",
317            Statement::Insert(_) => "insert",
318            Statement::Update(_) => "update",
319            Statement::Delete(_) => "delete",
320        }
321    }
322
323    /// Substitutes argument placeholders in this statement with concrete values
324    /// from `input`.
325    pub fn substitute(&mut self, input: impl Input) {
326        Substitute::new(input).visit_stmt_mut(self);
327    }
328
329    /// Returns `true` if this statement is a query whose body contains only
330    /// constant values (no table references or subqueries) and has no CTEs.
331    pub fn is_const(&self) -> bool {
332        match self {
333            Statement::Query(query) => {
334                if query.with.is_some() {
335                    return false;
336                }
337
338                query.body.is_const()
339            }
340            _ => false,
341        }
342    }
343
344    /// Attempts to return a reference to an inner [`Update`].
345    ///
346    /// * If `self` is a [`Statement::Update`], a reference to the inner [`Update`] is
347    ///   returned wrapped in [`Some`].
348    /// * Else, [`None`] is returned.
349    pub fn as_update(&self) -> Option<&Update> {
350        match self {
351            Self::Update(update) => Some(update),
352            _ => None,
353        }
354    }
355
356    /// Consumes `self` and attempts to return the inner [`Update`].
357    ///
358    /// * If `self` is a [`Statement::Update`], inner [`Update`] is returned wrapped in
359    ///   [`Some`].
360    /// * Else, [`None`] is returned.
361    pub fn into_update(self) -> Option<Update> {
362        match self {
363            Self::Update(update) => Some(update),
364            _ => None,
365        }
366    }
367
368    /// Returns `true` if this statement expects at most one result row.
369    pub fn is_single(&self) -> bool {
370        match self {
371            Statement::Query(q) => q.single,
372            Statement::Insert(i) => i.source.single,
373            Statement::Update(i) => match &i.target {
374                UpdateTarget::Query(q) => q.single,
375                UpdateTarget::Model(_) => true,
376                _ => false,
377            },
378            Statement::Delete(d) => d.selection().single,
379        }
380    }
381
382    /// Consumes `self` and returns the inner [`Update`].
383    ///
384    /// # Panics
385    ///
386    /// If `self` is not a [`Statement::Update`].
387    pub fn into_update_unwrap(self) -> Update {
388        match self {
389            Self::Update(update) => update,
390            v => panic!("expected `Update`, found {v:#?}"),
391        }
392    }
393}
394
395impl Node for Statement {
396    fn visit<V: Visit>(&self, mut visit: V) {
397        visit.visit_stmt(self);
398    }
399
400    fn visit_mut<V: VisitMut>(&mut self, mut visit: V) {
401        visit.visit_stmt_mut(self);
402    }
403}
404
405impl fmt::Debug for Statement {
406    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
407        match self {
408            Self::Delete(v) => v.fmt(f),
409            Self::Insert(v) => v.fmt(f),
410            Self::Query(v) => v.fmt(f),
411            Self::Update(v) => v.fmt(f),
412        }
413    }
414}