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::RowsFrom(_) => todo!(),
374                            TableRef::Arg(_) => todo!(),
375                        }
376                    }
377                    ExprReference::Model { .. } => {
378                        panic!("Cannot resolve ExprReference::Model in Source::Table context")
379                    }
380                    ExprReference::Field { .. } => panic!(
381                        "Cannot resolve ExprReference::Field in Source::Table context - use ExprReference::Column instead"
382                    ),
383                }
384            }
385        }
386    }
387
388    /// Infers the return type of a statement given argument types.
389    pub fn infer_stmt_ty(&self, stmt: &Statement, args: &[Type]) -> Type {
390        let cx = self.scope(stmt);
391
392        match stmt {
393            Statement::Delete(stmt) => stmt
394                .returning
395                .as_ref()
396                .map(|returning| cx.infer_returning_ty(returning, args, false))
397                .unwrap_or(Type::Unit),
398            Statement::Insert(stmt) => stmt
399                .returning
400                .as_ref()
401                .map(|returning| cx.infer_returning_ty(returning, args, stmt.source.single))
402                .unwrap_or(Type::Unit),
403            Statement::Query(stmt) => match &stmt.body {
404                ExprSet::Select(body) => cx.infer_returning_ty(&body.returning, args, stmt.single),
405                ExprSet::SetOp(_body) => todo!(),
406                ExprSet::Update(_body) => todo!(),
407                ExprSet::Delete(body) => body
408                    .returning
409                    .as_ref()
410                    .map(|returning| cx.infer_returning_ty(returning, args, stmt.single))
411                    .unwrap_or(Type::Unit),
412                ExprSet::Values(_body) => todo!(),
413                ExprSet::Insert(body) => body
414                    .returning
415                    .as_ref()
416                    .map(|returning| cx.infer_returning_ty(returning, args, stmt.single))
417                    .unwrap_or(Type::Unit),
418            },
419            Statement::Update(stmt) => stmt
420                .returning
421                .as_ref()
422                .map(|returning| cx.infer_returning_ty(returning, args, false))
423                .unwrap_or(Type::Unit),
424        }
425    }
426
427    fn infer_returning_ty(&self, returning: &Returning, args: &[Type], single: bool) -> Type {
428        let arg_ty_stack = ArgTyStack::new(args);
429        self.infer_returning_ty2(returning, &arg_ty_stack, single)
430    }
431
432    fn infer_returning_ty2(
433        &self,
434        returning: &Returning,
435        args: &ArgTyStack<'_>,
436        single: bool,
437    ) -> Type {
438        match returning {
439            Returning::Model { .. } => {
440                let ty = Type::Model(
441                    self.target
442                        .model_id()
443                        .expect("returning `Model` when not in model context"),
444                );
445
446                if single { ty } else { Type::list(ty) }
447            }
448            Returning::Changed => todo!(),
449            Returning::Star => todo!(),
450            Returning::Project(expr) => {
451                let ty = self.infer_expr_ty2(args, expr, false);
452
453                if single { ty } else { Type::list(ty) }
454            }
455            Returning::Expr(expr) => self.infer_expr_ty2(args, expr, true),
456        }
457    }
458
459    /// Infers the type of an expression given argument types.
460    pub fn infer_expr_ty(&self, expr: &Expr, args: &[Type]) -> Type {
461        let arg_ty_stack = ArgTyStack::new(args);
462        self.infer_expr_ty2(&arg_ty_stack, expr, false)
463    }
464
465    fn infer_expr_ty2(&self, args: &ArgTyStack<'_>, expr: &Expr, returning_expr: bool) -> Type {
466        match expr {
467            Expr::Arg(e) => args.resolve_arg_ty(e).clone(),
468            Expr::And(_) => Type::Bool,
469            Expr::Any(_) | Expr::AnyOp(_) | Expr::AllOp(_) | Expr::InList(_) => Type::Bool,
470            Expr::BinaryOp(_) => Type::Bool,
471            Expr::Cast(e) => e.ty.clone(),
472            Expr::Reference(expr_ref) => {
473                assert!(
474                    !returning_expr,
475                    "should have been handled in Expr::Project. Invalid expr?"
476                );
477                self.infer_expr_reference_ty2(expr_ref, args)
478            }
479            Expr::IsNull(_) => Type::Bool,
480            Expr::IsVariant(_) => Type::Bool,
481            Expr::Variant(e) => {
482                // The selected variant's payload: its fields, without the
483                // discriminant.
484                let base = self.infer_expr_ty2(args, &e.base, returning_expr);
485                let Type::Model(id) = base else {
486                    todo!("variant selection on non-enum type {base:#?}")
487                };
488                assert_eq!(id, e.variant.model, "variant selection on the wrong enum");
489                let Some(Model::EmbeddedEnum(embedded)) = self.schema.model(id) else {
490                    todo!("variant selection on non-enum model {id:?}")
491                };
492                Type::Record(
493                    embedded
494                        .variant_fields(e.variant.index)
495                        .iter()
496                        .map(|field| field.expr_ty().clone())
497                        .collect(),
498                )
499            }
500            Expr::List(e) => {
501                debug_assert!(!e.items.is_empty());
502                Type::list(self.infer_expr_ty2(args, &e.items[0], returning_expr))
503            }
504            Expr::Map(e) => {
505                // Compute the map base type
506                let base = self.infer_expr_ty2(args, &e.base, returning_expr);
507
508                // The base type should be a list (as it is being mapped)
509                let Type::List(item) = base else {
510                    todo!("error handling; base={base:#?}")
511                };
512
513                let scope_tys = &[*item];
514
515                // Create a new type scope
516                let args = args.scope(scope_tys);
517
518                // Infer the type of each map call
519                let ty = self.infer_expr_ty2(&args, &e.map, returning_expr);
520
521                // The mapped type is a list
522                Type::list(ty)
523            }
524            Expr::Not(_) => Type::Bool,
525            Expr::Or(_) => Type::Bool,
526            Expr::Project(e) => {
527                if returning_expr {
528                    match &*e.base {
529                        Expr::Arg(expr_arg) => {
530                            // When `returning_expr` is `true`, the expression is being
531                            // evaluated from a RETURNING EXPR clause. In this case, the
532                            // returning expression is *not* a projection. Referencing a
533                            // column implies a *list* of
534                            assert!(e.projection.as_slice().len() == 1);
535                            return args.resolve_arg_ty(expr_arg).clone();
536                        }
537                        Expr::Reference(expr_reference) => {
538                            // When `returning_expr` is `true`, the expression is being
539                            // evaluated from a RETURNING EXPR clause. In this case, the
540                            // returning expression is *not* a projection. Referencing a
541                            // column implies a *list* of
542                            assert!(e.projection.as_slice().len() == 1);
543                            return self.infer_expr_reference_ty2(expr_reference, args);
544                        }
545                        _ => {}
546                    }
547                }
548
549                let mut base = self.infer_expr_ty2(args, &e.base, returning_expr);
550
551                for step in e.projection.iter() {
552                    base = match base {
553                        Type::Record(mut fields) => {
554                            std::mem::replace(&mut fields[*step], Type::Null)
555                        }
556                        // A path into an embedded-model document value: descend
557                        // by field index through the embedded model's fields.
558                        // Keeping document projections type-able in the engine is
559                        // what lets them survive as plain `ExprProject` nodes
560                        // (rather than rewritten to a JSON function) until the
561                        // SQL edge.
562                        Type::Model(id) => match self.schema.model(id) {
563                            Some(Model::Root(model)) => model.fields[*step].expr_ty().clone(),
564                            Some(Model::EmbeddedStruct(embedded)) => {
565                                embedded.fields[*step].expr_ty().clone()
566                            }
567                            _ => todo!("project into non-embedded model {id:?}"),
568                        },
569                        Type::List(items) => *items,
570                        expr => todo!(
571                            "returning_expr={returning_expr:#?}; expr={expr:#?}; project={e:#?}"
572                        ),
573                    }
574                }
575
576                base
577            }
578            Expr::Record(e) => Type::Record(
579                e.fields
580                    .iter()
581                    .map(|field| self.infer_expr_ty2(args, field, returning_expr))
582                    .collect(),
583            ),
584            Expr::Value(value) | Expr::Static(value) => value.infer_ty(),
585            Expr::Let(expr_let) => {
586                let scope_tys: Vec<_> = expr_let
587                    .bindings
588                    .iter()
589                    .map(|b| self.infer_expr_ty2(args, b, returning_expr))
590                    .collect();
591                let args = args.scope(&scope_tys);
592                self.infer_expr_ty2(&args, &expr_let.body, returning_expr)
593            }
594            Expr::Match(expr_match) => {
595                // Collect the distinct non-null types from all arms and the else
596                // branch. If all agree on one type, return it directly. If they
597                // differ, return a Union so callers know exactly which shapes are
598                // possible at runtime.
599                let mut union = TypeUnion::new();
600                for arm in &expr_match.arms {
601                    let ty = self.infer_expr_ty2(args, &arm.expr, returning_expr);
602                    union.insert(ty);
603                }
604                let else_ty = self.infer_expr_ty2(args, &expr_match.else_expr, returning_expr);
605                union.insert(else_ty);
606                union.simplify()
607            }
608            // Error is a bottom type — it can never be evaluated, so it
609            // could be any type. Return Unknown so it unifies with whatever
610            // the other branches produce.
611            Expr::Error(_) => Type::Unknown,
612            Expr::Exists(_) => Type::Bool,
613            Expr::Func(ExprFunc::Count(_)) => Type::U64,
614            Expr::Func(ExprFunc::LastInsertId(_)) => Type::I64,
615            Expr::Func(ExprFunc::JsonExtract(func)) => func.ty.clone(),
616            Expr::Incoming(incoming) => match incoming {
617                super::ExprIncoming::Model(model) => Type::Model(*model),
618                super::ExprIncoming::Table(table) => {
619                    let table = self.schema.table(*table).unwrap_or_else(|| {
620                        panic!("incoming table {table:?} is not present in the schema")
621                    });
622                    Type::Record(
623                        table
624                            .columns
625                            .iter()
626                            .map(|column| column.ty.clone())
627                            .collect(),
628                    )
629                }
630            },
631            _ => todo!("{expr:#?}"),
632        }
633    }
634
635    /// Infers the type of an expression reference (field or column).
636    ///
637    /// Derived columns use the types in the `VALUES` rows or the `SELECT` projection.
638    /// An empty `VALUES` body has no type information and yields [`Type::Unknown`].
639    pub fn infer_expr_reference_ty(&self, expr_reference: &ExprReference) -> Type {
640        self.infer_expr_reference_ty2(expr_reference, &ArgTyStack::new(&[]))
641    }
642
643    fn infer_expr_reference_ty2(
644        &self,
645        expr_reference: &ExprReference,
646        args: &ArgTyStack<'_>,
647    ) -> Type {
648        match self.resolve_expr_reference(expr_reference) {
649            ResolvedRef::Model(model) => Type::Model(model.id),
650            ResolvedRef::Column(column) => column.ty.clone(),
651            ResolvedRef::Field(field) => field.expr_ty().clone(),
652            ResolvedRef::Cte { .. } => todo!("type inference for CTE columns not implemented"),
653            ResolvedRef::Derived(derived) => {
654                // Resolve the subquery from the scope that owns the derived table.
655                let owner = self.context_at(derived.nesting);
656
657                let query = &*derived.derived.subquery;
658                let cx = owner.scope(query);
659                let column_ty = |row_ty| match row_ty {
660                    Type::Record(mut fields) => fields.swap_remove(derived.index),
661                    ty => {
662                        assert_eq!(derived.index, 0, "scalar derived column index");
663                        ty
664                    }
665                };
666
667                match &query.body {
668                    ExprSet::Values(values) => {
669                        if values.is_empty() {
670                            return Type::Unknown;
671                        }
672                        let mut union = TypeUnion::new();
673                        for row in &values.rows {
674                            union.insert(column_ty(cx.infer_expr_ty2(args, row, false)));
675                        }
676                        union.simplify()
677                    }
678                    // A derived column refers to one row, regardless of how many
679                    // rows the subquery returns.
680                    _ => column_ty(cx.infer_returning_ty2(query.returning_unwrap(), args, true)),
681                }
682            }
683        }
684    }
685}
686
687impl<'a> ExprContext<'a, Schema> {
688    /// Returns the context target as a `ModelRoot` reference, or `None` if the target is not a
689    /// model.
690    pub fn target_as_model(&self) -> Option<&'a ModelRoot> {
691        self.target.as_model()
692    }
693}
694
695impl<'a, T> Clone for ExprContext<'a, T> {
696    fn clone(&self) -> Self {
697        *self
698    }
699}
700
701impl<'a, T> Copy for ExprContext<'a, T> {}
702
703impl<'a> ResolvedRef<'a> {
704    /// Returns the inner `Column` reference.
705    ///
706    /// # Panics
707    ///
708    /// Panics if this is not `ResolvedRef::Column`.
709    #[track_caller]
710    pub fn as_column_unwrap(self) -> &'a Column {
711        match self {
712            ResolvedRef::Column(column) => column,
713            _ => panic!("Expected ResolvedRef::Column, found {:?}", self),
714        }
715    }
716
717    /// Returns the inner `Field` reference.
718    ///
719    /// # Panics
720    ///
721    /// Panics if this is not `ResolvedRef::Field`.
722    #[track_caller]
723    pub fn as_field_unwrap(self) -> &'a Field {
724        match self {
725            ResolvedRef::Field(field) => field,
726            _ => panic!("Expected ResolvedRef::Field, found {:?}", self),
727        }
728    }
729
730    /// Returns the inner `ModelRoot` reference.
731    ///
732    /// # Panics
733    ///
734    /// Panics if this is not `ResolvedRef::Model`.
735    #[track_caller]
736    pub fn as_model_unwrap(self) -> &'a ModelRoot {
737        match self {
738            ResolvedRef::Model(model) => model,
739            _ => panic!("Expected ResolvedRef::Model, found {:?}", self),
740        }
741    }
742}
743
744impl Resolve for Schema {
745    fn model(&self, id: ModelId) -> Option<&Model> {
746        Some(self.app.model(id))
747    }
748
749    fn table(&self, id: TableId) -> Option<&Table> {
750        Some(self.db.table(id))
751    }
752
753    fn table_for_model(&self, model: &ModelRoot) -> Option<&Table> {
754        Some(self.table_for(model.id))
755    }
756}
757
758impl Resolve for crate::schema::app::Schema {
759    fn model(&self, id: ModelId) -> Option<&Model> {
760        self.get_model(id)
761    }
762}
763
764impl Resolve for db::Schema {
765    fn table(&self, id: TableId) -> Option<&Table> {
766        Some(db::Schema::table(self, id))
767    }
768}
769
770impl Resolve for () {}
771
772impl<'a> ExprTarget<'a> {
773    /// Returns the model if this target is [`ExprTarget::Model`], or `None`.
774    pub fn as_model(self) -> Option<&'a ModelRoot> {
775        match self {
776            ExprTarget::Model(model) => Some(model),
777            _ => None,
778        }
779    }
780
781    /// Returns the model, panicking if not [`ExprTarget::Model`].
782    ///
783    /// # Panics
784    ///
785    /// Panics if the target is not `Model`.
786    #[track_caller]
787    pub fn as_model_unwrap(self) -> &'a ModelRoot {
788        match self.as_model() {
789            Some(model) => model,
790            _ => panic!("expected ExprTarget::Model; was {self:#?}"),
791        }
792    }
793
794    /// Returns the model ID if this target is [`ExprTarget::Model`], or `None`.
795    fn model_id(self) -> Option<ModelId> {
796        Some(match self {
797            ExprTarget::Model(model) => model.id,
798            _ => return None,
799        })
800    }
801}
802
803impl<'a, T: Resolve> IntoExprTarget<'a, T> for ExprTarget<'a> {
804    fn into_expr_target(self, schema: &'a T) -> ExprTarget<'a> {
805        match self {
806            ExprTarget::Source(source_table) => {
807                if source_table.from.len() == 1 && source_table.from[0].joins.is_empty() {
808                    match &source_table.from[0].relation {
809                        TableFactor::Table(source_table_id) => {
810                            debug_assert_eq!(0, source_table_id.0);
811                            debug_assert_eq!(1, source_table.tables.len());
812
813                            match &source_table.tables[0] {
814                                TableRef::Table(table_id) => {
815                                    let table = schema.table(*table_id).unwrap();
816                                    ExprTarget::Table(table)
817                                }
818                                _ => self,
819                            }
820                        }
821                    }
822                } else {
823                    self
824                }
825            }
826            _ => self,
827        }
828    }
829}
830
831impl<'a, T> IntoExprTarget<'a, T> for &'a ModelRoot {
832    fn into_expr_target(self, _schema: &'a T) -> ExprTarget<'a> {
833        ExprTarget::Model(self)
834    }
835}
836
837impl<'a, T> IntoExprTarget<'a, T> for &'a Table {
838    fn into_expr_target(self, _schema: &'a T) -> ExprTarget<'a> {
839        ExprTarget::Table(self)
840    }
841}
842
843impl<'a, T: Resolve> IntoExprTarget<'a, T> for &'a Query {
844    fn into_expr_target(self, schema: &'a T) -> ExprTarget<'a> {
845        self.body.into_expr_target(schema)
846    }
847}
848
849impl<'a, T: Resolve> IntoExprTarget<'a, T> for &'a ExprSet {
850    fn into_expr_target(self, schema: &'a T) -> ExprTarget<'a> {
851        match self {
852            ExprSet::Select(select) => select.into_expr_target(schema),
853            ExprSet::SetOp(_) => todo!(),
854            ExprSet::Update(update) => update.into_expr_target(schema),
855            ExprSet::Delete(delete) => delete.into_expr_target(schema),
856            ExprSet::Values(_) => ExprTarget::Free,
857            ExprSet::Insert(insert) => insert.into_expr_target(schema),
858        }
859    }
860}
861
862impl<'a, T: Resolve> IntoExprTarget<'a, T> for &'a Select {
863    fn into_expr_target(self, schema: &'a T) -> ExprTarget<'a> {
864        self.source.into_expr_target(schema)
865    }
866}
867
868impl<'a, T: Resolve> IntoExprTarget<'a, T> for &'a Insert {
869    fn into_expr_target(self, schema: &'a T) -> ExprTarget<'a> {
870        self.target.into_expr_target(schema)
871    }
872}
873
874impl<'a, T: Resolve> IntoExprTarget<'a, T> for &'a Update {
875    fn into_expr_target(self, schema: &'a T) -> ExprTarget<'a> {
876        self.target.into_expr_target(schema)
877    }
878}
879
880impl<'a, T: Resolve> IntoExprTarget<'a, T> for &'a Delete {
881    fn into_expr_target(self, schema: &'a T) -> ExprTarget<'a> {
882        self.from.into_expr_target(schema)
883    }
884}
885
886impl<'a, T: Resolve> IntoExprTarget<'a, T> for &'a InsertTarget {
887    fn into_expr_target(self, schema: &'a T) -> ExprTarget<'a> {
888        match self {
889            InsertTarget::Scope(query) => query.into_expr_target(schema),
890            InsertTarget::Model(model) => {
891                let Some(model) = schema.model(*model) else {
892                    todo!()
893                };
894                ExprTarget::Model(model.as_root_unwrap())
895            }
896            InsertTarget::Table(insert_table) => {
897                let table = schema.table(insert_table.table).unwrap();
898                ExprTarget::Table(table)
899            }
900        }
901    }
902}
903
904impl<'a, T: Resolve> IntoExprTarget<'a, T> for &'a UpdateTarget {
905    fn into_expr_target(self, schema: &'a T) -> ExprTarget<'a> {
906        match self {
907            UpdateTarget::Query(query) => query.into_expr_target(schema),
908            UpdateTarget::Model(model) => {
909                let Some(model) = schema.model(*model) else {
910                    todo!()
911                };
912                ExprTarget::Model(model.as_root_unwrap())
913            }
914            UpdateTarget::Table(table_id) => {
915                let Some(table) = schema.table(*table_id) else {
916                    todo!()
917                };
918                ExprTarget::Table(table)
919            }
920        }
921    }
922}
923
924impl<'a, T: Resolve> IntoExprTarget<'a, T> for &'a Source {
925    fn into_expr_target(self, schema: &'a T) -> ExprTarget<'a> {
926        match self {
927            Source::Model(source_model) => {
928                let Some(model) = schema.model(source_model.id) else {
929                    todo!()
930                };
931                ExprTarget::Model(model.as_root_unwrap())
932            }
933            Source::Table(source_table) => {
934                ExprTarget::Source(source_table).into_expr_target(schema)
935            }
936        }
937    }
938}
939
940impl<'a, T: Resolve> IntoExprTarget<'a, T> for &'a Statement {
941    fn into_expr_target(self, schema: &'a T) -> ExprTarget<'a> {
942        match self {
943            Statement::Delete(stmt) => stmt.into_expr_target(schema),
944            Statement::Insert(stmt) => stmt.into_expr_target(schema),
945            Statement::Query(stmt) => stmt.into_expr_target(schema),
946            Statement::Update(stmt) => stmt.into_expr_target(schema),
947        }
948    }
949}
950
951impl<'a> ArgTyStack<'a> {
952    fn new(tys: &'a [Type]) -> ArgTyStack<'a> {
953        ArgTyStack { tys, parent: None }
954    }
955
956    fn resolve_arg_ty(&self, expr_arg: &ExprArg) -> &'a Type {
957        let mut nesting = expr_arg.nesting;
958        let mut args = self;
959
960        while nesting > 0 {
961            args = args.parent.unwrap();
962            nesting -= 1;
963        }
964
965        &args.tys[expr_arg.position]
966    }
967
968    fn scope<'child>(&'child self, tys: &'child [Type]) -> ArgTyStack<'child> {
969        ArgTyStack {
970            tys,
971            parent: Some(self),
972        }
973    }
974}