Skip to main content

toasty_core/stmt/
cx.rs

1mod projected_field;
2pub use projected_field::ProjectedField;
3
4use crate::{
5    Schema,
6    schema::{
7        app::{Field, Model, ModelId, ModelRoot},
8        db::{self, Column, Table, TableId},
9    },
10    stmt::{
11        Delete, Expr, ExprArg, ExprFunc, ExprReference, ExprSet, Insert, InsertTarget, Query,
12        Returning, Select, Source, SourceTable, Statement, TableDerived, TableFactor, TableRef,
13        Type, TypeUnion, Update, UpdateTarget,
14    },
15};
16
17/// Provides schema-aware context for expression type inference and reference
18/// resolution.
19///
20/// An `ExprContext` binds a schema reference, an optional parent scope (for
21/// nested queries), and a target indicating what the expressions reference
22/// (a model, table, or source). It is used by the query engine to infer
23/// expression types and resolve column/field references.
24///
25/// # Examples
26///
27/// ```ignore
28/// use toasty_core::stmt::{ExprContext, ExprTarget};
29///
30/// let cx = ExprContext::new(&schema);
31/// let ty = cx.infer_expr_ty(&expr, &[]);
32/// ```
33#[derive(Debug)]
34pub struct ExprContext<'a, T = Schema> {
35    schema: &'a T,
36    parent: Option<&'a ExprContext<'a, T>>,
37    target: ExprTarget<'a>,
38}
39
40/// Result of resolving an `ExprReference` to its concrete schema location.
41///
42/// When an expression references a field or column (e.g., `user.name` in a
43/// WHERE clause), the `ExprContext::resolve_expr_reference()` method returns
44/// this enum to indicate whether the reference points to an application field,
45/// physical table column, or CTE column.
46///
47/// This distinction is important for different processing stages: application
48/// fields are used during high-level query building, physical columns during
49/// SQL generation, and CTE columns require special handling with generated
50/// identifiers based on position.
51#[derive(Debug)]
52pub enum ResolvedRef<'a> {
53    /// A resolved reference to a physical database column.
54    ///
55    /// Contains a reference to the actual Column struct with column metadata including
56    /// name, type, and constraints. Used when resolving ExprReference::Column expressions
57    /// that point to concrete table columns in the database schema.
58    ///
59    /// Example: Resolving `user.name` in a query returns Column with name="name",
60    /// ty=Type::String from the users table schema.
61    Column(&'a Column),
62
63    /// A resolved reference to an application-level field.
64    ///
65    /// Contains a reference to the Field struct from the application schema,
66    /// which includes field metadata like name, type, and model relationships.
67    /// Used when resolving ExprReference::Field expressions that point to
68    /// model fields before they are lowered to database columns.
69    ///
70    /// Example: Resolving `User::name` in a query returns Field with name="name"
71    /// from the User model's field definitions.
72    Field(&'a Field),
73
74    /// A resolved reference to a model
75    Model(&'a ModelRoot),
76
77    /// A resolved reference to a Common Table Expression (CTE) column.
78    ///
79    /// Contains the nesting level and column index for CTE references when resolving
80    /// ExprReference::Column expressions that point to CTE outputs rather than physical
81    /// table columns. The nesting indicates how many query levels to traverse upward,
82    /// and index identifies which column within the CTE's output.
83    ///
84    /// Example: In a WITH clause, resolving a reference to the second column of a CTE
85    /// defined 1 level up returns Cte { nesting: 1, index: 1 }.
86    Cte {
87        /// How many query scopes up from the current scope.
88        nesting: usize,
89        /// Column index within the CTE's output.
90        index: usize,
91    },
92
93    /// A resolved reference to a derived table (subquery in FROM clause) column.
94    ///
95    /// Contains the nesting level, column index, and a reference to the derived
96    /// table itself. This allows consumers to inspect the derived table's
97    /// content (e.g., checking VALUES rows for constant values).
98    Derived(DerivedRef<'a>),
99}
100
101/// A resolved reference into a derived table column.
102#[derive(Debug)]
103pub struct DerivedRef<'a> {
104    /// How many query scopes up from the current scope.
105    pub nesting: usize,
106
107    /// The column index within the derived table's output.
108    pub index: usize,
109
110    /// Reference to the derived table definition.
111    pub derived: &'a TableDerived,
112}
113
114impl DerivedRef<'_> {
115    /// Returns `true` if the derived table is backed by a VALUES body and every
116    /// row has `Null` at this column position.
117    ///
118    /// Returns `false` conservatively when the body is not VALUES, the VALUES
119    /// is empty, or any row doesn't have a recognizable null at the column.
120    pub fn is_column_always_null(&self) -> bool {
121        let ExprSet::Values(values) = &self.derived.subquery.body else {
122            return false;
123        };
124
125        if values.is_empty() {
126            return false;
127        }
128
129        values.rows.iter().all(|row| self.row_column_is_null(row))
130    }
131
132    fn row_column_is_null(&self, row: &Expr) -> bool {
133        match row {
134            Expr::Value(super::Value::Record(record)) => {
135                self.index < record.len() && record[self.index].is_null()
136            }
137            Expr::Record(record) => {
138                self.index < record.len()
139                    && matches!(&record.fields[self.index], Expr::Value(super::Value::Null))
140            }
141            Expr::Value(super::Value::Null) => true,
142            _ => false,
143        }
144    }
145}
146
147/// What an expression in the current scope references.
148///
149/// Determines how column and field references are resolved within an
150/// [`ExprContext`].
151#[derive(Debug, Clone, Copy)]
152pub enum ExprTarget<'a> {
153    /// Expression does *not* reference any model or table.
154    Free,
155
156    /// Expression references a single model
157    Model(&'a ModelRoot),
158
159    /// Expression references a single table
160    ///
161    /// Used primarily by database drivers
162    Table(&'a Table),
163
164    /// Expression references a source table (a FROM clause with table references).
165    Source(&'a SourceTable),
166}
167
168/// Schema resolution trait used by [`ExprContext`] to look up models,
169/// tables, and the model-to-table mapping.
170///
171/// Implemented for [`Schema`], [`db::Schema`](crate::schema::db::Schema),
172/// and `()` (which resolves nothing).
173pub trait Resolve {
174    /// Returns the database table that stores the given model, if any.
175    fn table_for_model(&self, model: &ModelRoot) -> Option<&Table> {
176        let _ = model;
177        None
178    }
179
180    /// Returns a reference to the application Model with the specified ID.
181    ///
182    /// Used during high-level query building to access model metadata such as
183    /// field definitions, relationships, and validation rules. Returns None if
184    /// the model ID is not found in the application schema.
185    fn model(&self, id: ModelId) -> Option<&Model> {
186        let _ = id;
187        None
188    }
189
190    /// Returns a reference to the database Table with the specified ID.
191    ///
192    /// Used during SQL generation and query execution to access table metadata
193    /// including column definitions, constraints, and indexes. Returns None if
194    /// the table ID is not found in the database schema.
195    fn table(&self, id: TableId) -> Option<&Table> {
196        let _ = id;
197        None
198    }
199}
200
201/// Conversion trait for producing an [`ExprTarget`] from a statement or
202/// schema element.
203pub trait IntoExprTarget<'a, T = Schema> {
204    /// Converts `self` into an [`ExprTarget`] using the provided schema.
205    fn into_expr_target(self, schema: &'a T) -> ExprTarget<'a>;
206}
207
208#[derive(Debug)]
209struct ArgTyStack<'a> {
210    tys: &'a [Type],
211    parent: Option<&'a ArgTyStack<'a>>,
212}
213
214impl<'a, T> ExprContext<'a, T> {
215    /// Returns a reference to the schema.
216    pub fn schema(&self) -> &'a T {
217        self.schema
218    }
219
220    /// Returns the current expression target.
221    pub fn target(&self) -> ExprTarget<'a> {
222        self.target
223    }
224
225    /// Return the target at a specific nesting
226    pub fn target_at(&self, nesting: usize) -> &ExprTarget<'a> {
227        &self.context_at(nesting).target
228    }
229
230    fn context_at(&self, nesting: usize) -> &Self {
231        let mut curr = self;
232
233        // Walk up the stack to the correct nesting level
234        for _ in 0..nesting {
235            let Some(parent) = curr.parent else {
236                todo!("bug: invalid nesting level");
237            };
238
239            curr = parent;
240        }
241
242        curr
243    }
244}
245
246impl<'a> ExprContext<'a, ()> {
247    /// Creates a free context with no schema and no target.
248    pub fn new_free() -> ExprContext<'a, ()> {
249        ExprContext {
250            schema: &(),
251            parent: None,
252            target: ExprTarget::Free,
253        }
254    }
255}
256
257impl<'a, T: Resolve> ExprContext<'a, T> {
258    /// Creates a context bound to the given schema with a free target.
259    pub fn new(schema: &'a T) -> ExprContext<'a, T> {
260        ExprContext::new_with_target(schema, ExprTarget::Free)
261    }
262
263    /// Creates a context bound to the given schema and target.
264    pub fn new_with_target(
265        schema: &'a T,
266        target: impl IntoExprTarget<'a, T>,
267    ) -> ExprContext<'a, T> {
268        let target = target.into_expr_target(schema);
269        ExprContext {
270            schema,
271            parent: None,
272            target,
273        }
274    }
275
276    /// Creates a child context with a new target, linked to this context
277    /// as parent for nested scope resolution.
278    pub fn scope<'child>(
279        &'child self,
280        target: impl IntoExprTarget<'child, T>,
281        // target: impl Into<ExprTarget<'child>>,
282    ) -> ExprContext<'child, T> {
283        let target = target.into_expr_target(self.schema);
284        ExprContext {
285            schema: self.schema,
286            parent: Some(self),
287            target,
288        }
289    }
290
291    /// Resolves an ExprReference::Column reference to the actual database Column it
292    /// represents.
293    ///
294    /// Given an ExprReference::Column (which contains table/column indices and nesting
295    /// info), returns the Column struct containing the column's name, type,
296    /// constraints, and other metadata.
297    ///
298    /// Handles:
299    /// - Nested query scopes (walking up parent contexts based on nesting
300    ///   level)
301    /// - Different statement targets (INSERT, UPDATE, SELECT with joins, etc.)
302    /// - Table references in multi-table operations (using the table index)
303    ///
304    /// Used by SQL serialization to get column names, query planning to
305    /// match index columns, and key extraction to identify column IDs.
306    pub fn resolve_expr_reference(&self, expr_reference: &ExprReference) -> ResolvedRef<'a> {
307        let nesting = match expr_reference {
308            ExprReference::Column(expr_column) => expr_column.nesting,
309            ExprReference::Field { nesting, .. } => *nesting,
310            ExprReference::Model { nesting } => *nesting,
311        };
312
313        let target = self.target_at(nesting);
314
315        match target {
316            ExprTarget::Free => todo!("cannot resolve column in free context"),
317            ExprTarget::Model(model) => match expr_reference {
318                ExprReference::Model { .. } => ResolvedRef::Model(model),
319                ExprReference::Field { index, .. } => ResolvedRef::Field(&model.fields[*index]),
320                ExprReference::Column(expr_column) => {
321                    assert_eq!(expr_column.table, 0, "TODO: is this true?");
322
323                    let Some(table) = self.schema.table_for_model(model) else {
324                        panic!(
325                            "Failed to find database table for model '{:?}' - model may not be mapped to a table",
326                            model.name
327                        )
328                    };
329                    ResolvedRef::Column(&table.columns[expr_column.column])
330                }
331            },
332            ExprTarget::Table(table) => match expr_reference {
333                ExprReference::Model { .. } => {
334                    panic!("Cannot resolve ExprReference::Model in Table target context")
335                }
336                ExprReference::Field { .. } => panic!(
337                    "Cannot resolve ExprReference::Field in Table target context - use ExprReference::Column instead"
338                ),
339                ExprReference::Column(expr_column) => {
340                    ResolvedRef::Column(&table.columns[expr_column.column])
341                }
342            },
343            ExprTarget::Source(source_table) => {
344                match expr_reference {
345                    ExprReference::Column(expr_column) => {
346                        // Get the table reference at the specified index
347                        let table_ref = &source_table.tables[expr_column.table];
348                        match table_ref {
349                            TableRef::Table(table_id) => {
350                                let Some(table) = self.schema.table(*table_id) else {
351                                    panic!(
352                                        "Failed to resolve table with ID {:?} - table not found in schema.",
353                                        table_id,
354                                    );
355                                };
356                                ResolvedRef::Column(&table.columns[expr_column.column])
357                            }
358                            TableRef::Derived(derived) => ResolvedRef::Derived(DerivedRef {
359                                nesting: expr_column.nesting,
360                                index: expr_column.column,
361                                derived,
362                            }),
363                            TableRef::Cte {
364                                nesting: cte_nesting,
365                                index,
366                            } => {
367                                // TODO: return more info
368                                ResolvedRef::Cte {
369                                    nesting: expr_column.nesting + cte_nesting,
370                                    index: *index,
371                                }
372                            }
373                            TableRef::Arg(_) => todo!(),
374                        }
375                    }
376                    ExprReference::Model { .. } => {
377                        panic!("Cannot resolve ExprReference::Model in Source::Table context")
378                    }
379                    ExprReference::Field { .. } => panic!(
380                        "Cannot resolve ExprReference::Field in Source::Table context - use ExprReference::Column instead"
381                    ),
382                }
383            }
384        }
385    }
386
387    /// Infers the return type of a statement given argument types.
388    pub fn infer_stmt_ty(&self, stmt: &Statement, args: &[Type]) -> Type {
389        let cx = self.scope(stmt);
390
391        match stmt {
392            Statement::Delete(stmt) => stmt
393                .returning
394                .as_ref()
395                .map(|returning| cx.infer_returning_ty(returning, args, false))
396                .unwrap_or(Type::Unit),
397            Statement::Insert(stmt) => stmt
398                .returning
399                .as_ref()
400                .map(|returning| cx.infer_returning_ty(returning, args, stmt.source.single))
401                .unwrap_or(Type::Unit),
402            Statement::Query(stmt) => match &stmt.body {
403                ExprSet::Select(body) => cx.infer_returning_ty(&body.returning, args, stmt.single),
404                ExprSet::SetOp(_body) => todo!(),
405                ExprSet::Update(_body) => todo!(),
406                ExprSet::Delete(body) => body
407                    .returning
408                    .as_ref()
409                    .map(|returning| cx.infer_returning_ty(returning, args, stmt.single))
410                    .unwrap_or(Type::Unit),
411                ExprSet::Values(_body) => todo!(),
412                ExprSet::Insert(body) => body
413                    .returning
414                    .as_ref()
415                    .map(|returning| cx.infer_returning_ty(returning, args, stmt.single))
416                    .unwrap_or(Type::Unit),
417            },
418            Statement::Update(stmt) => stmt
419                .returning
420                .as_ref()
421                .map(|returning| cx.infer_returning_ty(returning, args, false))
422                .unwrap_or(Type::Unit),
423        }
424    }
425
426    fn infer_returning_ty(&self, returning: &Returning, args: &[Type], single: bool) -> Type {
427        let arg_ty_stack = ArgTyStack::new(args);
428        self.infer_returning_ty2(returning, &arg_ty_stack, single)
429    }
430
431    fn infer_returning_ty2(
432        &self,
433        returning: &Returning,
434        args: &ArgTyStack<'_>,
435        single: bool,
436    ) -> Type {
437        match returning {
438            Returning::Model { .. } => {
439                let ty = Type::Model(
440                    self.target
441                        .model_id()
442                        .expect("returning `Model` when not in model context"),
443                );
444
445                if single { ty } else { Type::list(ty) }
446            }
447            Returning::Changed => todo!(),
448            Returning::Project(expr) => {
449                let ty = self.infer_expr_ty2(args, expr, false);
450
451                if single { ty } else { Type::list(ty) }
452            }
453            Returning::Expr(expr) => self.infer_expr_ty2(args, expr, true),
454        }
455    }
456
457    /// Infers the type of an expression given argument types.
458    pub fn infer_expr_ty(&self, expr: &Expr, args: &[Type]) -> Type {
459        let arg_ty_stack = ArgTyStack::new(args);
460        self.infer_expr_ty2(&arg_ty_stack, expr, false)
461    }
462
463    fn infer_expr_ty2(&self, args: &ArgTyStack<'_>, expr: &Expr, returning_expr: bool) -> Type {
464        match expr {
465            Expr::Arg(e) => args.resolve_arg_ty(e).clone(),
466            Expr::And(_) => Type::Bool,
467            Expr::Any(_) | Expr::AnyOp(_) | Expr::AllOp(_) | Expr::InList(_) => Type::Bool,
468            Expr::BinaryOp(_) => Type::Bool,
469            Expr::Cast(e) => e.ty.clone(),
470            Expr::Reference(expr_ref) => {
471                assert!(
472                    !returning_expr,
473                    "should have been handled in Expr::Project. Invalid expr?"
474                );
475                self.infer_expr_reference_ty2(expr_ref, args)
476            }
477            Expr::IsNull(_) => Type::Bool,
478            Expr::IsVariant(_) => Type::Bool,
479            Expr::Variant(e) => {
480                // The selected variant's payload: its fields, without the
481                // discriminant.
482                let base = self.infer_expr_ty2(args, &e.base, returning_expr);
483                let Type::Model(id) = base else {
484                    todo!("variant selection on non-enum type {base:#?}")
485                };
486                assert_eq!(id, e.variant.model, "variant selection on the wrong enum");
487                let Some(Model::EmbeddedEnum(embedded)) = self.schema.model(id) else {
488                    todo!("variant selection on non-enum model {id:?}")
489                };
490                Type::Record(
491                    embedded
492                        .variant_fields(e.variant.index)
493                        .iter()
494                        .map(|field| field.expr_ty().clone())
495                        .collect(),
496                )
497            }
498            Expr::List(e) => {
499                debug_assert!(!e.items.is_empty());
500                Type::list(self.infer_expr_ty2(args, &e.items[0], returning_expr))
501            }
502            Expr::Map(e) => {
503                // Compute the map base type
504                let base = self.infer_expr_ty2(args, &e.base, returning_expr);
505
506                // The base type should be a list (as it is being mapped)
507                let Type::List(item) = base else {
508                    todo!("error handling; base={base:#?}")
509                };
510
511                let scope_tys = &[*item];
512
513                // Create a new type scope
514                let args = args.scope(scope_tys);
515
516                // Infer the type of each map call
517                let ty = self.infer_expr_ty2(&args, &e.map, returning_expr);
518
519                // The mapped type is a list
520                Type::list(ty)
521            }
522            Expr::Not(_) => Type::Bool,
523            Expr::Or(_) => Type::Bool,
524            Expr::Project(e) => {
525                if returning_expr {
526                    match &*e.base {
527                        Expr::Arg(expr_arg) => {
528                            // When `returning_expr` is `true`, the expression is being
529                            // evaluated from a RETURNING EXPR clause. In this case, the
530                            // returning expression is *not* a projection. Referencing a
531                            // column implies a *list* of
532                            assert!(e.projection.as_slice().len() == 1);
533                            return args.resolve_arg_ty(expr_arg).clone();
534                        }
535                        Expr::Reference(expr_reference) => {
536                            // When `returning_expr` is `true`, the expression is being
537                            // evaluated from a RETURNING EXPR clause. In this case, the
538                            // returning expression is *not* a projection. Referencing a
539                            // column implies a *list* of
540                            assert!(e.projection.as_slice().len() == 1);
541                            return self.infer_expr_reference_ty2(expr_reference, args);
542                        }
543                        _ => {}
544                    }
545                }
546
547                let mut base = self.infer_expr_ty2(args, &e.base, returning_expr);
548
549                for step in e.projection.iter() {
550                    base = match base {
551                        Type::Record(mut fields) => {
552                            std::mem::replace(&mut fields[*step], Type::Null)
553                        }
554                        // A path into an embedded-model document value: descend
555                        // by field index through the embedded model's fields.
556                        // Keeping document projections type-able in the engine is
557                        // what lets them survive as plain `ExprProject` nodes
558                        // (rather than rewritten to a JSON function) until the
559                        // SQL edge.
560                        Type::Model(id) => match self.schema.model(id) {
561                            Some(Model::Root(model)) => model.fields[*step].expr_ty().clone(),
562                            Some(Model::EmbeddedStruct(embedded)) => {
563                                embedded.fields[*step].expr_ty().clone()
564                            }
565                            _ => todo!("project into non-embedded model {id:?}"),
566                        },
567                        Type::List(items) => *items,
568                        expr => todo!(
569                            "returning_expr={returning_expr:#?}; expr={expr:#?}; project={e:#?}"
570                        ),
571                    }
572                }
573
574                base
575            }
576            Expr::Record(e) => Type::Record(
577                e.fields
578                    .iter()
579                    .map(|field| self.infer_expr_ty2(args, field, returning_expr))
580                    .collect(),
581            ),
582            Expr::Value(value) | Expr::Static(value) => value.infer_ty(),
583            Expr::Let(expr_let) => {
584                let scope_tys: Vec<_> = expr_let
585                    .bindings
586                    .iter()
587                    .map(|b| self.infer_expr_ty2(args, b, returning_expr))
588                    .collect();
589                let args = args.scope(&scope_tys);
590                self.infer_expr_ty2(&args, &expr_let.body, returning_expr)
591            }
592            Expr::Match(expr_match) => {
593                // Collect the distinct non-null types from all arms and the else
594                // branch. If all agree on one type, return it directly. If they
595                // differ, return a Union so callers know exactly which shapes are
596                // possible at runtime.
597                let mut union = TypeUnion::new();
598                for arm in &expr_match.arms {
599                    let ty = self.infer_expr_ty2(args, &arm.expr, returning_expr);
600                    union.insert(ty);
601                }
602                let else_ty = self.infer_expr_ty2(args, &expr_match.else_expr, returning_expr);
603                union.insert(else_ty);
604                union.simplify()
605            }
606            // Error is a bottom type — it can never be evaluated, so it
607            // could be any type. Return Unknown so it unifies with whatever
608            // the other branches produce.
609            Expr::Error(_) => Type::Unknown,
610            Expr::Exists(_) => Type::Bool,
611            Expr::Func(ExprFunc::Count(_)) => Type::U64,
612            Expr::Func(ExprFunc::LastInsertId(_)) => Type::I64,
613            Expr::Func(ExprFunc::JsonExtract(func)) => func.ty.clone(),
614            Expr::Incoming(incoming) => match incoming {
615                super::ExprIncoming::Model(model) => Type::Model(*model),
616                super::ExprIncoming::Table(table) => {
617                    let table = self.schema.table(*table).unwrap_or_else(|| {
618                        panic!("incoming table {table:?} is not present in the schema")
619                    });
620                    Type::Record(
621                        table
622                            .columns
623                            .iter()
624                            .map(|column| column.ty.clone())
625                            .collect(),
626                    )
627                }
628            },
629            _ => todo!("{expr:#?}"),
630        }
631    }
632
633    /// Infers the type of an expression reference (field or column).
634    ///
635    /// Derived columns use the types in the `VALUES` rows or the `SELECT` projection.
636    /// An empty `VALUES` body has no type information and yields [`Type::Unknown`].
637    pub fn infer_expr_reference_ty(&self, expr_reference: &ExprReference) -> Type {
638        self.infer_expr_reference_ty2(expr_reference, &ArgTyStack::new(&[]))
639    }
640
641    fn infer_expr_reference_ty2(
642        &self,
643        expr_reference: &ExprReference,
644        args: &ArgTyStack<'_>,
645    ) -> Type {
646        match self.resolve_expr_reference(expr_reference) {
647            ResolvedRef::Model(model) => Type::Model(model.id),
648            ResolvedRef::Column(column) => column.ty.clone(),
649            ResolvedRef::Field(field) => field.expr_ty().clone(),
650            ResolvedRef::Cte { .. } => todo!("type inference for CTE columns not implemented"),
651            ResolvedRef::Derived(derived) => {
652                // Resolve the subquery from the scope that owns the derived table.
653                let owner = self.context_at(derived.nesting);
654
655                let query = &*derived.derived.subquery;
656                let cx = owner.scope(query);
657                let column_ty = |row_ty| match row_ty {
658                    Type::Record(mut fields) => fields.swap_remove(derived.index),
659                    ty => {
660                        assert_eq!(derived.index, 0, "scalar derived column index");
661                        ty
662                    }
663                };
664
665                match &query.body {
666                    ExprSet::Values(values) => {
667                        if values.is_empty() {
668                            return Type::Unknown;
669                        }
670                        let mut union = TypeUnion::new();
671                        for row in &values.rows {
672                            union.insert(column_ty(cx.infer_expr_ty2(args, row, false)));
673                        }
674                        union.simplify()
675                    }
676                    // A derived column refers to one row, regardless of how many
677                    // rows the subquery returns.
678                    _ => column_ty(cx.infer_returning_ty2(query.returning_unwrap(), args, true)),
679                }
680            }
681        }
682    }
683}
684
685impl<'a> ExprContext<'a, Schema> {
686    /// Returns the context target as a `ModelRoot` reference, or `None` if the target is not a
687    /// model.
688    pub fn target_as_model(&self) -> Option<&'a ModelRoot> {
689        self.target.as_model()
690    }
691}
692
693impl<'a, T> Clone for ExprContext<'a, T> {
694    fn clone(&self) -> Self {
695        *self
696    }
697}
698
699impl<'a, T> Copy for ExprContext<'a, T> {}
700
701impl<'a> ResolvedRef<'a> {
702    /// Returns the inner `Column` reference.
703    ///
704    /// # Panics
705    ///
706    /// Panics if this is not `ResolvedRef::Column`.
707    #[track_caller]
708    pub fn as_column_unwrap(self) -> &'a Column {
709        match self {
710            ResolvedRef::Column(column) => column,
711            _ => panic!("Expected ResolvedRef::Column, found {:?}", self),
712        }
713    }
714
715    /// Returns the inner `Field` reference.
716    ///
717    /// # Panics
718    ///
719    /// Panics if this is not `ResolvedRef::Field`.
720    #[track_caller]
721    pub fn as_field_unwrap(self) -> &'a Field {
722        match self {
723            ResolvedRef::Field(field) => field,
724            _ => panic!("Expected ResolvedRef::Field, found {:?}", self),
725        }
726    }
727
728    /// Returns the inner `ModelRoot` reference.
729    ///
730    /// # Panics
731    ///
732    /// Panics if this is not `ResolvedRef::Model`.
733    #[track_caller]
734    pub fn as_model_unwrap(self) -> &'a ModelRoot {
735        match self {
736            ResolvedRef::Model(model) => model,
737            _ => panic!("Expected ResolvedRef::Model, found {:?}", self),
738        }
739    }
740}
741
742impl Resolve for Schema {
743    fn model(&self, id: ModelId) -> Option<&Model> {
744        Some(self.app.model(id))
745    }
746
747    fn table(&self, id: TableId) -> Option<&Table> {
748        Some(self.db.table(id))
749    }
750
751    fn table_for_model(&self, model: &ModelRoot) -> Option<&Table> {
752        Some(self.table_for(model.id))
753    }
754}
755
756impl Resolve for crate::schema::app::Schema {
757    fn model(&self, id: ModelId) -> Option<&Model> {
758        self.get_model(id)
759    }
760}
761
762impl Resolve for db::Schema {
763    fn table(&self, id: TableId) -> Option<&Table> {
764        Some(db::Schema::table(self, id))
765    }
766}
767
768impl Resolve for () {}
769
770impl<'a> ExprTarget<'a> {
771    /// Returns the model if this target is [`ExprTarget::Model`], or `None`.
772    pub fn as_model(self) -> Option<&'a ModelRoot> {
773        match self {
774            ExprTarget::Model(model) => Some(model),
775            _ => None,
776        }
777    }
778
779    /// Returns the model, panicking if not [`ExprTarget::Model`].
780    ///
781    /// # Panics
782    ///
783    /// Panics if the target is not `Model`.
784    #[track_caller]
785    pub fn as_model_unwrap(self) -> &'a ModelRoot {
786        match self.as_model() {
787            Some(model) => model,
788            _ => panic!("expected ExprTarget::Model; was {self:#?}"),
789        }
790    }
791
792    /// Returns the model ID if this target is [`ExprTarget::Model`], or `None`.
793    fn model_id(self) -> Option<ModelId> {
794        Some(match self {
795            ExprTarget::Model(model) => model.id,
796            _ => return None,
797        })
798    }
799}
800
801impl<'a, T: Resolve> IntoExprTarget<'a, T> for ExprTarget<'a> {
802    fn into_expr_target(self, schema: &'a T) -> ExprTarget<'a> {
803        match self {
804            ExprTarget::Source(source_table) => {
805                if source_table.from.len() == 1 && source_table.from[0].joins.is_empty() {
806                    match &source_table.from[0].relation {
807                        TableFactor::Table(source_table_id) => {
808                            debug_assert_eq!(0, source_table_id.0);
809                            debug_assert_eq!(1, source_table.tables.len());
810
811                            match &source_table.tables[0] {
812                                TableRef::Table(table_id) => {
813                                    let table = schema.table(*table_id).unwrap();
814                                    ExprTarget::Table(table)
815                                }
816                                _ => self,
817                            }
818                        }
819                    }
820                } else {
821                    self
822                }
823            }
824            _ => self,
825        }
826    }
827}
828
829impl<'a, T> IntoExprTarget<'a, T> for &'a ModelRoot {
830    fn into_expr_target(self, _schema: &'a T) -> ExprTarget<'a> {
831        ExprTarget::Model(self)
832    }
833}
834
835impl<'a, T> IntoExprTarget<'a, T> for &'a Table {
836    fn into_expr_target(self, _schema: &'a T) -> ExprTarget<'a> {
837        ExprTarget::Table(self)
838    }
839}
840
841impl<'a, T: Resolve> IntoExprTarget<'a, T> for &'a Query {
842    fn into_expr_target(self, schema: &'a T) -> ExprTarget<'a> {
843        self.body.into_expr_target(schema)
844    }
845}
846
847impl<'a, T: Resolve> IntoExprTarget<'a, T> for &'a ExprSet {
848    fn into_expr_target(self, schema: &'a T) -> ExprTarget<'a> {
849        match self {
850            ExprSet::Select(select) => select.into_expr_target(schema),
851            ExprSet::SetOp(_) => todo!(),
852            ExprSet::Update(update) => update.into_expr_target(schema),
853            ExprSet::Delete(delete) => delete.into_expr_target(schema),
854            ExprSet::Values(_) => ExprTarget::Free,
855            ExprSet::Insert(insert) => insert.into_expr_target(schema),
856        }
857    }
858}
859
860impl<'a, T: Resolve> IntoExprTarget<'a, T> for &'a Select {
861    fn into_expr_target(self, schema: &'a T) -> ExprTarget<'a> {
862        self.source.into_expr_target(schema)
863    }
864}
865
866impl<'a, T: Resolve> IntoExprTarget<'a, T> for &'a Insert {
867    fn into_expr_target(self, schema: &'a T) -> ExprTarget<'a> {
868        self.target.into_expr_target(schema)
869    }
870}
871
872impl<'a, T: Resolve> IntoExprTarget<'a, T> for &'a Update {
873    fn into_expr_target(self, schema: &'a T) -> ExprTarget<'a> {
874        self.target.into_expr_target(schema)
875    }
876}
877
878impl<'a, T: Resolve> IntoExprTarget<'a, T> for &'a Delete {
879    fn into_expr_target(self, schema: &'a T) -> ExprTarget<'a> {
880        self.from.into_expr_target(schema)
881    }
882}
883
884impl<'a, T: Resolve> IntoExprTarget<'a, T> for &'a InsertTarget {
885    fn into_expr_target(self, schema: &'a T) -> ExprTarget<'a> {
886        match self {
887            InsertTarget::Scope(query) => query.into_expr_target(schema),
888            InsertTarget::Model(model) => {
889                let Some(model) = schema.model(*model) else {
890                    todo!()
891                };
892                ExprTarget::Model(model.as_root_unwrap())
893            }
894            InsertTarget::Table(insert_table) => {
895                let table = schema.table(insert_table.table).unwrap();
896                ExprTarget::Table(table)
897            }
898        }
899    }
900}
901
902impl<'a, T: Resolve> IntoExprTarget<'a, T> for &'a UpdateTarget {
903    fn into_expr_target(self, schema: &'a T) -> ExprTarget<'a> {
904        match self {
905            UpdateTarget::Query(query) => query.into_expr_target(schema),
906            UpdateTarget::Model(model) => {
907                let Some(model) = schema.model(*model) else {
908                    todo!()
909                };
910                ExprTarget::Model(model.as_root_unwrap())
911            }
912            UpdateTarget::Table(table_id) => {
913                let Some(table) = schema.table(*table_id) else {
914                    todo!()
915                };
916                ExprTarget::Table(table)
917            }
918        }
919    }
920}
921
922impl<'a, T: Resolve> IntoExprTarget<'a, T> for &'a Source {
923    fn into_expr_target(self, schema: &'a T) -> ExprTarget<'a> {
924        match self {
925            Source::Model(source_model) => {
926                let Some(model) = schema.model(source_model.id) else {
927                    todo!()
928                };
929                ExprTarget::Model(model.as_root_unwrap())
930            }
931            Source::Table(source_table) => {
932                ExprTarget::Source(source_table).into_expr_target(schema)
933            }
934        }
935    }
936}
937
938impl<'a, T: Resolve> IntoExprTarget<'a, T> for &'a Statement {
939    fn into_expr_target(self, schema: &'a T) -> ExprTarget<'a> {
940        match self {
941            Statement::Delete(stmt) => stmt.into_expr_target(schema),
942            Statement::Insert(stmt) => stmt.into_expr_target(schema),
943            Statement::Query(stmt) => stmt.into_expr_target(schema),
944            Statement::Update(stmt) => stmt.into_expr_target(schema),
945        }
946    }
947}
948
949impl<'a> ArgTyStack<'a> {
950    fn new(tys: &'a [Type]) -> ArgTyStack<'a> {
951        ArgTyStack { tys, parent: None }
952    }
953
954    fn resolve_arg_ty(&self, expr_arg: &ExprArg) -> &'a Type {
955        let mut nesting = expr_arg.nesting;
956        let mut args = self;
957
958        while nesting > 0 {
959            args = args.parent.unwrap();
960            nesting -= 1;
961        }
962
963        &args.tys[expr_arg.position]
964    }
965
966    fn scope<'child>(&'child self, tys: &'child [Type]) -> ArgTyStack<'child> {
967        ArgTyStack {
968            tys,
969            parent: Some(self),
970        }
971    }
972}