toasty_core/stmt/expr_reference.rs
1use crate::{
2 schema::{app::FieldId, db::ColumnId},
3 stmt::Expr,
4};
5
6/// A reference to a model, field, or column.
7///
8/// References use scope-based nesting to support subqueries. A nesting level of
9/// `0` refers to the current query scope, while higher levels reference higher
10/// scope queries.
11///
12/// # Examples
13///
14/// ```text
15/// ref(field: 0, nesting: 0) // field 0 in current query
16/// ref(field: 2, nesting: 1) // field 2 in parent query
17/// ref(column: 0, table: 1) // column 0 in table 1
18/// ```
19#[derive(Debug, Clone, Copy, PartialEq, Hash, Eq)]
20pub enum ExprReference {
21 /// A reference to a column in a database-level statement.
22 ///
23 /// ExprReference::Column represents resolved column references after lowering from the
24 /// application schema to the database schema. It uses a scope-based approach
25 /// similar to ExprReference::Field, referencing a specific column within a target
26 /// at a given nesting level.
27 Column(ExprColumn),
28
29 /// Reference a specific field in a query's relation.
30 ///
31 /// For Query/Delete statements, the relation is the Source.
32 /// For Insert/Update statements, the relation is the target.
33 Field {
34 /// Query scope nesting level: 0 = current query, 1+ = higher scope queries
35 nesting: usize,
36 /// Index of the field within the relation
37 index: usize,
38 },
39
40 /// Reference a model at a specific nesting level.
41 ///
42 /// This is roughly referencing the full record instead of a specific field.
43 Model {
44 /// Query scope nesting level: 0 = current query, 1+ = higher scope queries.
45 nesting: usize,
46 },
47}
48
49/// A reference to a database column.
50///
51/// Used after lowering from the application schema to the database schema.
52#[derive(Debug, Clone, Copy, PartialEq, Hash, Eq)]
53pub struct ExprColumn {
54 /// Query scope nesting level: `0` = current query, `1`+ = higher scope queries.
55 pub nesting: usize,
56
57 /// Index into the table references vector for this column's source relation.
58 ///
59 /// For statements with multiple tables (SELECT with JOINs), this indexes into
60 /// the `SourceTable::tables` field to identify which specific table contains
61 /// this column. For single-target statements (INSERT, UPDATE), this is
62 /// typically 0 since these operations target only one relation at a time.
63 pub table: usize,
64
65 /// The index of the column in the table
66 pub column: usize,
67}
68
69impl Expr {
70 /// Returns `true` if this expression is a reference (field, column, or model).
71 pub fn is_expr_reference(&self) -> bool {
72 matches!(self, Expr::Reference(..))
73 }
74
75 /// Creates an expression that references a field in the current query.
76 ///
77 /// This creates an `ExprReference::Field` with `nesting = 0`, meaning it
78 /// references a field in the current query scope rather than an outer query.
79 ///
80 /// # Arguments
81 ///
82 /// * `field` - A field identifier that can be converted into a `FieldId`
83 ///
84 /// # Returns
85 ///
86 /// An `Expr::Reference` containing an `ExprReference::Field` that points to
87 /// the specified field in the current query's relation.
88 pub fn ref_self_field(field: impl Into<FieldId>) -> Self {
89 ExprReference::field(field).into()
90 }
91
92 /// Create a reference to a field at a specified nesting level
93 pub fn ref_field(nesting: usize, field: impl Into<FieldId>) -> Self {
94 ExprReference::Field {
95 nesting,
96 index: field.into().index,
97 }
98 .into()
99 }
100
101 /// Create a reference to a field one level up
102 pub fn ref_parent_field(field: impl Into<FieldId>) -> Self {
103 ExprReference::Field {
104 nesting: 1,
105 index: field.into().index,
106 }
107 .into()
108 }
109
110 /// Returns `true` if this is a field reference.
111 pub fn is_field(&self) -> bool {
112 matches!(self, Self::Reference(ExprReference::Field { .. }))
113 }
114
115 /// Creates a model reference to the parent (nesting level 1).
116 pub fn ref_parent_model() -> Self {
117 Self::ref_ancestor_model(1)
118 }
119
120 /// Create a model reference to the specified nesting level
121 pub fn ref_ancestor_model(nesting: usize) -> Self {
122 ExprReference::Model { nesting }.into()
123 }
124
125 /// Creates a column reference expression.
126 pub fn column(column: impl Into<ExprReference>) -> Self {
127 column.into().into()
128 }
129
130 /// Returns `true` if this expression is a column reference.
131 pub fn is_column(&self) -> bool {
132 matches!(self, Self::Reference(ExprReference::Column(..)))
133 }
134
135 /// Returns a reference to the inner [`ExprReference`] if this is a
136 /// reference expression, or `None` otherwise.
137 pub fn as_expr_reference(&self) -> Option<&ExprReference> {
138 match self {
139 Expr::Reference(expr_reference) => Some(expr_reference),
140 _ => None,
141 }
142 }
143
144 /// Returns a reference to the inner [`ExprReference`].
145 ///
146 /// # Panics
147 ///
148 /// Panics if `self` is not `Expr::Reference`.
149 #[track_caller]
150 pub fn as_expr_reference_unwrap(&self) -> &ExprReference {
151 self.as_expr_reference()
152 .unwrap_or_else(|| panic!("expected ExprReference; actual={self:#?}"))
153 }
154
155 /// Returns a reference to the inner [`ExprColumn`] if this is a column
156 /// reference, or `None` otherwise.
157 pub fn as_expr_column(&self) -> Option<&ExprColumn> {
158 match self {
159 Expr::Reference(ExprReference::Column(expr_column)) => Some(expr_column),
160 _ => None,
161 }
162 }
163
164 /// Returns a reference to the inner [`ExprColumn`].
165 ///
166 /// # Panics
167 ///
168 /// Panics if `self` is not `Expr::Reference(ExprReference::Column(_))`.
169 #[track_caller]
170 pub fn as_expr_column_unwrap(&self) -> &ExprColumn {
171 self.as_expr_column()
172 .unwrap_or_else(|| panic!("expected ExprColumn; actual={self:#?}"))
173 }
174}
175
176impl ExprReference {
177 /// Creates a field reference in the current scope (nesting = 0).
178 pub fn field(field: impl Into<FieldId>) -> Self {
179 ExprReference::Field {
180 nesting: 0,
181 index: field.into().index,
182 }
183 }
184
185 /// Returns `true` if this is a field reference.
186 pub fn is_field(&self) -> bool {
187 matches!(self, ExprReference::Field { .. })
188 }
189
190 /// Returns `true` if this is a model reference.
191 pub fn is_model(&self) -> bool {
192 matches!(self, ExprReference::Model { .. })
193 }
194
195 /// Creates a column reference in the current scope (nesting = 0).
196 pub fn column(table: usize, column: usize) -> Self {
197 ExprReference::Column(ExprColumn {
198 nesting: 0,
199 table,
200 column,
201 })
202 }
203
204 /// Returns `true` if this is a column reference.
205 pub fn is_column(&self) -> bool {
206 matches!(self, ExprReference::Column(..))
207 }
208
209 /// Returns a reference to the inner [`ExprColumn`] if this is a column
210 /// reference, or `None` otherwise.
211 pub fn as_expr_column(&self) -> Option<&ExprColumn> {
212 match self {
213 ExprReference::Column(expr_column) => Some(expr_column),
214 _ => None,
215 }
216 }
217
218 /// Returns a reference to the inner [`ExprColumn`].
219 ///
220 /// # Panics
221 ///
222 /// Panics if `self` is not `ExprReference::Column`.
223 #[track_caller]
224 pub fn as_expr_column_unwrap(&self) -> &ExprColumn {
225 self.as_expr_column()
226 .unwrap_or_else(|| panic!("expected ExprColumn; actual={self:#?}"))
227 }
228
229 /// Returns a mutable reference to the inner [`ExprColumn`] if this is a
230 /// column reference, or `None` otherwise.
231 pub fn as_expr_column_mut(&mut self) -> Option<&mut ExprColumn> {
232 match self {
233 ExprReference::Column(expr_column) => Some(expr_column),
234 _ => None,
235 }
236 }
237
238 /// Returns a mutable reference to the inner [`ExprColumn`].
239 ///
240 /// # Panics
241 ///
242 /// Panics if `self` is not `ExprReference::Column`.
243 #[track_caller]
244 pub fn as_expr_column_mut_unwrap(&mut self) -> &mut ExprColumn {
245 match self {
246 ExprReference::Column(expr_column) => expr_column,
247 _ => panic!("expected ExprColumn; actual={self:#?}"),
248 }
249 }
250}
251
252impl From<ExprColumn> for ExprReference {
253 fn from(value: ExprColumn) -> Self {
254 ExprReference::Column(value)
255 }
256}
257
258impl From<ExprReference> for Expr {
259 fn from(value: ExprReference) -> Self {
260 Expr::Reference(value)
261 }
262}
263
264impl From<ExprColumn> for Expr {
265 fn from(value: ExprColumn) -> Self {
266 Expr::Reference(ExprReference::Column(value))
267 }
268}
269
270impl From<&ExprReference> for Expr {
271 fn from(value: &ExprReference) -> Self {
272 Expr::Reference(*value)
273 }
274}
275
276impl From<&ExprColumn> for Expr {
277 fn from(value: &ExprColumn) -> Self {
278 Expr::Reference(ExprReference::Column(*value))
279 }
280}
281
282impl From<ColumnId> for ExprReference {
283 fn from(value: ColumnId) -> Self {
284 ExprReference::Column(value.into())
285 }
286}
287
288impl From<ColumnId> for ExprColumn {
289 fn from(value: ColumnId) -> Self {
290 ExprColumn {
291 nesting: 0,
292 table: 0,
293 column: value.index,
294 }
295 }
296}