Skip to main content

toasty_core/stmt/
cx.rs

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