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