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