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, ColumnId, Table, TableId},
6    },
7    stmt::{
8        Delete, Expr, ExprArg, ExprColumn, ExprFunc, ExprReference, ExprSet, Insert, InsertTarget,
9        Query, Returning, Select, Source, SourceTable, Statement, TableDerived, TableFactor,
10        TableRef, 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
174    /// Returns a reference to the application Model with the specified ID.
175    ///
176    /// Used during high-level query building to access model metadata such as
177    /// field definitions, relationships, and validation rules. Returns None if
178    /// the model ID is not found in the application schema.
179    fn model(&self, id: ModelId) -> Option<&Model>;
180
181    /// Returns a reference to the database Table with the specified ID.
182    ///
183    /// Used during SQL generation and query execution to access table metadata
184    /// including column definitions, constraints, and indexes. Returns None if
185    /// the table ID is not found in the database schema.
186    fn table(&self, id: TableId) -> Option<&Table>;
187}
188
189/// Conversion trait for producing an [`ExprTarget`] from a statement or
190/// schema element.
191pub trait IntoExprTarget<'a, T = Schema> {
192    /// Converts `self` into an [`ExprTarget`] using the provided schema.
193    fn into_expr_target(self, schema: &'a T) -> ExprTarget<'a>;
194}
195
196#[derive(Debug)]
197struct ArgTyStack<'a> {
198    tys: &'a [Type],
199    parent: Option<&'a ArgTyStack<'a>>,
200}
201
202impl<'a, T> ExprContext<'a, T> {
203    /// Returns a reference to the schema.
204    pub fn schema(&self) -> &'a T {
205        self.schema
206    }
207
208    /// Returns the current expression target.
209    pub fn target(&self) -> ExprTarget<'a> {
210        self.target
211    }
212
213    /// Return the target at a specific nesting
214    pub fn target_at(&self, nesting: usize) -> &ExprTarget<'a> {
215        let mut curr = self;
216
217        // Walk up the stack to the correct nesting level
218        for _ in 0..nesting {
219            let Some(parent) = curr.parent else {
220                todo!("bug: invalid nesting level");
221            };
222
223            curr = parent;
224        }
225
226        &curr.target
227    }
228}
229
230impl<'a> ExprContext<'a, ()> {
231    /// Creates a free context with no schema and no target.
232    pub fn new_free() -> ExprContext<'a, ()> {
233        ExprContext {
234            schema: &(),
235            parent: None,
236            target: ExprTarget::Free,
237        }
238    }
239}
240
241impl<'a, T: Resolve> ExprContext<'a, T> {
242    /// Creates a context bound to the given schema with a free target.
243    pub fn new(schema: &'a T) -> ExprContext<'a, T> {
244        ExprContext::new_with_target(schema, ExprTarget::Free)
245    }
246
247    /// Creates a context bound to the given schema and target.
248    pub fn new_with_target(
249        schema: &'a T,
250        target: impl IntoExprTarget<'a, T>,
251    ) -> ExprContext<'a, T> {
252        let target = target.into_expr_target(schema);
253        ExprContext {
254            schema,
255            parent: None,
256            target,
257        }
258    }
259
260    /// Creates a child context with a new target, linked to this context
261    /// as parent for nested scope resolution.
262    pub fn scope<'child>(
263        &'child self,
264        target: impl IntoExprTarget<'child, T>,
265        // target: impl Into<ExprTarget<'child>>,
266    ) -> ExprContext<'child, T> {
267        let target = target.into_expr_target(self.schema);
268        ExprContext {
269            schema: self.schema,
270            parent: Some(self),
271            target,
272        }
273    }
274
275    /// Resolves an ExprReference::Column reference to the actual database Column it
276    /// represents.
277    ///
278    /// Given an ExprReference::Column (which contains table/column indices and nesting
279    /// info), returns the Column struct containing the column's name, type,
280    /// constraints, and other metadata.
281    ///
282    /// Handles:
283    /// - Nested query scopes (walking up parent contexts based on nesting
284    ///   level)
285    /// - Different statement targets (INSERT, UPDATE, SELECT with joins, etc.)
286    /// - Table references in multi-table operations (using the table index)
287    ///
288    /// Used by SQL serialization to get column names, query planning to
289    /// match index columns, and key extraction to identify column IDs.
290    pub fn resolve_expr_reference(&self, expr_reference: &ExprReference) -> ResolvedRef<'a> {
291        let nesting = match expr_reference {
292            ExprReference::Column(expr_column) => expr_column.nesting,
293            ExprReference::Field { nesting, .. } => *nesting,
294            ExprReference::Model { nesting } => *nesting,
295        };
296
297        let target = self.target_at(nesting);
298
299        match target {
300            ExprTarget::Free => todo!("cannot resolve column in free context"),
301            ExprTarget::Model(model) => match expr_reference {
302                ExprReference::Model { .. } => ResolvedRef::Model(model),
303                ExprReference::Field { index, .. } => ResolvedRef::Field(&model.fields[*index]),
304                ExprReference::Column(expr_column) => {
305                    assert_eq!(expr_column.table, 0, "TODO: is this true?");
306
307                    let Some(table) = self.schema.table_for_model(model) else {
308                        panic!(
309                            "Failed to find database table for model '{:?}' - model may not be mapped to a table",
310                            model.name
311                        )
312                    };
313                    ResolvedRef::Column(&table.columns[expr_column.column])
314                }
315            },
316            ExprTarget::Table(table) => match expr_reference {
317                ExprReference::Model { .. } => {
318                    panic!("Cannot resolve ExprReference::Model in Table target context")
319                }
320                ExprReference::Field { .. } => panic!(
321                    "Cannot resolve ExprReference::Field in Table target context - use ExprReference::Column instead"
322                ),
323                ExprReference::Column(expr_column) => {
324                    ResolvedRef::Column(&table.columns[expr_column.column])
325                }
326            },
327            ExprTarget::Source(source_table) => {
328                match expr_reference {
329                    ExprReference::Column(expr_column) => {
330                        // Get the table reference at the specified index
331                        let table_ref = &source_table.tables[expr_column.table];
332                        match table_ref {
333                            TableRef::Table(table_id) => {
334                                let Some(table) = self.schema.table(*table_id) else {
335                                    panic!(
336                                        "Failed to resolve table with ID {:?} - table not found in schema.",
337                                        table_id,
338                                    );
339                                };
340                                ResolvedRef::Column(&table.columns[expr_column.column])
341                            }
342                            TableRef::Derived(derived) => ResolvedRef::Derived(DerivedRef {
343                                nesting: expr_column.nesting,
344                                index: expr_column.column,
345                                derived,
346                            }),
347                            TableRef::Cte {
348                                nesting: cte_nesting,
349                                index,
350                            } => {
351                                // TODO: return more info
352                                ResolvedRef::Cte {
353                                    nesting: expr_column.nesting + cte_nesting,
354                                    index: *index,
355                                }
356                            }
357                            TableRef::Arg(_) => todo!(),
358                        }
359                    }
360                    ExprReference::Model { .. } => {
361                        panic!("Cannot resolve ExprReference::Model in Source::Table context")
362                    }
363                    ExprReference::Field { .. } => panic!(
364                        "Cannot resolve ExprReference::Field in Source::Table context - use ExprReference::Column instead"
365                    ),
366                }
367            }
368        }
369    }
370
371    /// Infers the return type of a statement given argument types.
372    pub fn infer_stmt_ty(&self, stmt: &Statement, args: &[Type]) -> Type {
373        let cx = self.scope(stmt);
374
375        match stmt {
376            Statement::Delete(stmt) => stmt
377                .returning
378                .as_ref()
379                .map(|returning| cx.infer_returning_ty(returning, args, false))
380                .unwrap_or(Type::Unit),
381            Statement::Insert(stmt) => stmt
382                .returning
383                .as_ref()
384                .map(|returning| cx.infer_returning_ty(returning, args, stmt.source.single))
385                .unwrap_or(Type::Unit),
386            Statement::Query(stmt) => match &stmt.body {
387                ExprSet::Select(body) => cx.infer_returning_ty(&body.returning, args, stmt.single),
388                ExprSet::SetOp(_body) => todo!(),
389                ExprSet::Update(_body) => todo!(),
390                ExprSet::Delete(body) => body
391                    .returning
392                    .as_ref()
393                    .map(|returning| cx.infer_returning_ty(returning, args, stmt.single))
394                    .unwrap_or(Type::Unit),
395                ExprSet::Values(_body) => todo!(),
396                ExprSet::Insert(body) => body
397                    .returning
398                    .as_ref()
399                    .map(|returning| cx.infer_returning_ty(returning, args, stmt.single))
400                    .unwrap_or(Type::Unit),
401            },
402            Statement::Update(stmt) => stmt
403                .returning
404                .as_ref()
405                .map(|returning| cx.infer_returning_ty(returning, args, false))
406                .unwrap_or(Type::Unit),
407        }
408    }
409
410    fn infer_returning_ty(&self, returning: &Returning, args: &[Type], single: bool) -> Type {
411        let arg_ty_stack = ArgTyStack::new(args);
412
413        match returning {
414            Returning::Model { .. } => {
415                let ty = Type::Model(
416                    self.target
417                        .model_id()
418                        .expect("returning `Model` when not in model context"),
419                );
420
421                if single { ty } else { Type::list(ty) }
422            }
423            Returning::Changed => todo!(),
424            Returning::Project(expr) => {
425                let ty = self.infer_expr_ty2(&arg_ty_stack, expr, false);
426
427                if single { ty } else { Type::list(ty) }
428            }
429            Returning::Expr(expr) => self.infer_expr_ty2(&arg_ty_stack, expr, true),
430        }
431    }
432
433    /// Infers the type of an expression given argument types.
434    pub fn infer_expr_ty(&self, expr: &Expr, args: &[Type]) -> Type {
435        let arg_ty_stack = ArgTyStack::new(args);
436        self.infer_expr_ty2(&arg_ty_stack, expr, false)
437    }
438
439    fn infer_expr_ty2(&self, args: &ArgTyStack<'_>, expr: &Expr, returning_expr: bool) -> Type {
440        match expr {
441            Expr::Arg(e) => args.resolve_arg_ty(e).clone(),
442            Expr::And(_) => Type::Bool,
443            Expr::AnyOp(_) | Expr::AllOp(_) => Type::Bool,
444            Expr::BinaryOp(_) => Type::Bool,
445            Expr::Cast(e) => e.ty.clone(),
446            Expr::Reference(expr_ref) => {
447                assert!(
448                    !returning_expr,
449                    "should have been handled in Expr::Project. Invalid expr?"
450                );
451                self.infer_expr_reference_ty(expr_ref)
452            }
453            Expr::IsNull(_) => Type::Bool,
454            Expr::IsVariant(_) => Type::Bool,
455            Expr::List(e) => {
456                debug_assert!(!e.items.is_empty());
457                Type::list(self.infer_expr_ty2(args, &e.items[0], returning_expr))
458            }
459            Expr::Map(e) => {
460                // Compute the map base type
461                let base = self.infer_expr_ty2(args, &e.base, returning_expr);
462
463                // The base type should be a list (as it is being mapped)
464                let Type::List(item) = base else {
465                    todo!("error handling; base={base:#?}")
466                };
467
468                let scope_tys = &[*item];
469
470                // Create a new type scope
471                let args = args.scope(scope_tys);
472
473                // Infer the type of each map call
474                let ty = self.infer_expr_ty2(&args, &e.map, returning_expr);
475
476                // The mapped type is a list
477                Type::list(ty)
478            }
479            Expr::Or(_) => Type::Bool,
480            Expr::Project(e) => {
481                if returning_expr {
482                    match &*e.base {
483                        Expr::Arg(expr_arg) => {
484                            // When `returning_expr` is `true`, the expression is being
485                            // evaluated from a RETURNING EXPR clause. In this case, the
486                            // returning expression is *not* a projection. Referencing a
487                            // column implies a *list* of
488                            assert!(e.projection.as_slice().len() == 1);
489                            return args.resolve_arg_ty(expr_arg).clone();
490                        }
491                        Expr::Reference(expr_reference) => {
492                            // When `returning_expr` is `true`, the expression is being
493                            // evaluated from a RETURNING EXPR clause. In this case, the
494                            // returning expression is *not* a projection. Referencing a
495                            // column implies a *list* of
496                            assert!(e.projection.as_slice().len() == 1);
497                            return self.infer_expr_reference_ty(expr_reference);
498                        }
499                        _ => {}
500                    }
501                }
502
503                let mut base = self.infer_expr_ty2(args, &e.base, returning_expr);
504
505                for step in e.projection.iter() {
506                    base = match base {
507                        Type::Record(mut fields) => {
508                            std::mem::replace(&mut fields[*step], Type::Null)
509                        }
510                        Type::List(items) => *items,
511                        expr => todo!(
512                            "returning_expr={returning_expr:#?}; expr={expr:#?}; project={e:#?}"
513                        ),
514                    }
515                }
516
517                base
518            }
519            Expr::Record(e) => Type::Record(
520                e.fields
521                    .iter()
522                    .map(|field| self.infer_expr_ty2(args, field, returning_expr))
523                    .collect(),
524            ),
525            Expr::Value(value) => value.infer_ty(),
526            Expr::Let(expr_let) => {
527                let scope_tys: Vec<_> = expr_let
528                    .bindings
529                    .iter()
530                    .map(|b| self.infer_expr_ty2(args, b, returning_expr))
531                    .collect();
532                let args = args.scope(&scope_tys);
533                self.infer_expr_ty2(&args, &expr_let.body, returning_expr)
534            }
535            Expr::Match(expr_match) => {
536                // Collect the distinct non-null types from all arms and the else
537                // branch. If all agree on one type, return it directly. If they
538                // differ, return a Union so callers know exactly which shapes are
539                // possible at runtime.
540                let mut union = TypeUnion::new();
541                for arm in &expr_match.arms {
542                    let ty = self.infer_expr_ty2(args, &arm.expr, returning_expr);
543                    union.insert(ty);
544                }
545                let else_ty = self.infer_expr_ty2(args, &expr_match.else_expr, returning_expr);
546                union.insert(else_ty);
547                union.simplify()
548            }
549            // Error is a bottom type — it can never be evaluated, so it
550            // could be any type. Return Unknown so it unifies with whatever
551            // the other branches produce.
552            Expr::Error(_) => Type::Unknown,
553            Expr::Exists(_) => Type::Bool,
554            Expr::Func(ExprFunc::Count(_)) => Type::U64,
555            Expr::Func(ExprFunc::LastInsertId(_)) => Type::I64,
556            _ => todo!("{expr:#?}"),
557        }
558    }
559
560    /// Infers the type of an expression reference (field or column).
561    pub fn infer_expr_reference_ty(&self, expr_reference: &ExprReference) -> Type {
562        match self.resolve_expr_reference(expr_reference) {
563            ResolvedRef::Model(model) => Type::Model(model.id),
564            ResolvedRef::Column(column) => column.ty.clone(),
565            ResolvedRef::Field(field) => field.expr_ty().clone(),
566            ResolvedRef::Cte { .. } => todo!("type inference for CTE columns not implemented"),
567            ResolvedRef::Derived(_) => {
568                todo!("type inference for derived table columns not implemented")
569            }
570        }
571    }
572}
573
574impl<'a> ExprContext<'a, Schema> {
575    /// Returns the context target as a `ModelRoot` reference, or `None` if the target is not a
576    /// model.
577    pub fn target_as_model(&self) -> Option<&'a ModelRoot> {
578        self.target.as_model()
579    }
580
581    /// Creates an `ExprReference::Column` for the given column ID.
582    ///
583    /// # Panics
584    ///
585    /// Panics if the context has no table target (`ExprTarget::Free`), if the column does not
586    /// belong to the table associated with the current target, or if the target's model has no
587    /// mapped database table.
588    pub fn expr_ref_column(&self, column_id: impl Into<ColumnId>) -> ExprReference {
589        let column_id = column_id.into();
590
591        match self.target {
592            ExprTarget::Free => {
593                panic!("Cannot create ExprColumn in free context - no table target available")
594            }
595            ExprTarget::Model(model) => {
596                let Some(table) = self.schema.table_for_model(model) else {
597                    panic!(
598                        "Failed to find database table for model '{:?}' - model may not be mapped to a table",
599                        model.name
600                    )
601                };
602
603                assert_eq!(table.id, column_id.table);
604            }
605            ExprTarget::Table(table) => assert_eq!(table.id, column_id.table),
606            ExprTarget::Source(source_table) => {
607                let [TableRef::Table(table_id)] = source_table.tables[..] else {
608                    panic!(
609                        "Expected exactly one table reference, found {} tables",
610                        source_table.tables.len()
611                    );
612                };
613                assert_eq!(table_id, column_id.table);
614            }
615        }
616
617        ExprReference::Column(ExprColumn {
618            nesting: 0,
619            table: 0,
620            column: column_id.index,
621        })
622    }
623}
624
625impl<'a, T> Clone for ExprContext<'a, T> {
626    fn clone(&self) -> Self {
627        *self
628    }
629}
630
631impl<'a, T> Copy for ExprContext<'a, T> {}
632
633impl<'a> ResolvedRef<'a> {
634    /// Returns the inner `Column` reference.
635    ///
636    /// # Panics
637    ///
638    /// Panics if this is not `ResolvedRef::Column`.
639    #[track_caller]
640    pub fn as_column_unwrap(self) -> &'a Column {
641        match self {
642            ResolvedRef::Column(column) => column,
643            _ => panic!("Expected ResolvedRef::Column, found {:?}", self),
644        }
645    }
646
647    /// Returns the inner `Field` reference.
648    ///
649    /// # Panics
650    ///
651    /// Panics if this is not `ResolvedRef::Field`.
652    #[track_caller]
653    pub fn as_field_unwrap(self) -> &'a Field {
654        match self {
655            ResolvedRef::Field(field) => field,
656            _ => panic!("Expected ResolvedRef::Field, found {:?}", self),
657        }
658    }
659
660    /// Returns the inner `ModelRoot` reference.
661    ///
662    /// # Panics
663    ///
664    /// Panics if this is not `ResolvedRef::Model`.
665    #[track_caller]
666    pub fn as_model_unwrap(self) -> &'a ModelRoot {
667        match self {
668            ResolvedRef::Model(model) => model,
669            _ => panic!("Expected ResolvedRef::Model, found {:?}", self),
670        }
671    }
672}
673
674impl Resolve for Schema {
675    fn model(&self, id: ModelId) -> Option<&Model> {
676        Some(self.app.model(id))
677    }
678
679    fn table(&self, id: TableId) -> Option<&Table> {
680        Some(self.db.table(id))
681    }
682
683    fn table_for_model(&self, model: &ModelRoot) -> Option<&Table> {
684        Some(self.table_for(model.id))
685    }
686}
687
688impl Resolve for db::Schema {
689    fn model(&self, _id: ModelId) -> Option<&Model> {
690        None
691    }
692
693    fn table(&self, id: TableId) -> Option<&Table> {
694        Some(db::Schema::table(self, id))
695    }
696
697    fn table_for_model(&self, _model: &ModelRoot) -> Option<&Table> {
698        None
699    }
700}
701
702impl Resolve for () {
703    fn model(&self, _id: ModelId) -> Option<&Model> {
704        None
705    }
706
707    fn table(&self, _id: TableId) -> Option<&Table> {
708        None
709    }
710
711    fn table_for_model(&self, _model: &ModelRoot) -> Option<&Table> {
712        None
713    }
714}
715
716impl<'a> ExprTarget<'a> {
717    /// Returns the model if this target is [`ExprTarget::Model`], or `None`.
718    pub fn as_model(self) -> Option<&'a ModelRoot> {
719        match self {
720            ExprTarget::Model(model) => Some(model),
721            _ => None,
722        }
723    }
724
725    /// Returns the model, panicking if not [`ExprTarget::Model`].
726    ///
727    /// # Panics
728    ///
729    /// Panics if the target is not `Model`.
730    #[track_caller]
731    pub fn as_model_unwrap(self) -> &'a ModelRoot {
732        match self.as_model() {
733            Some(model) => model,
734            _ => panic!("expected ExprTarget::Model; was {self:#?}"),
735        }
736    }
737
738    /// Returns the model ID if this target is [`ExprTarget::Model`], or `None`.
739    pub fn model_id(self) -> Option<ModelId> {
740        Some(match self {
741            ExprTarget::Model(model) => model.id,
742            _ => return None,
743        })
744    }
745
746    /// Returns the table if this target is [`ExprTarget::Table`], or `None`.
747    pub fn as_table(self) -> Option<&'a Table> {
748        match self {
749            ExprTarget::Table(table) => Some(table),
750            _ => None,
751        }
752    }
753
754    /// Returns the table, panicking if not [`ExprTarget::Table`].
755    ///
756    /// # Panics
757    ///
758    /// Panics if the target is not `Table`.
759    #[track_caller]
760    pub fn as_table_unwrap(self) -> &'a Table {
761        self.as_table()
762            .unwrap_or_else(|| panic!("expected ExprTarget::Table; was {self:#?}"))
763    }
764}
765
766impl<'a, T: Resolve> IntoExprTarget<'a, T> for ExprTarget<'a> {
767    fn into_expr_target(self, schema: &'a T) -> ExprTarget<'a> {
768        match self {
769            ExprTarget::Source(source_table) => {
770                if source_table.from.len() == 1 && source_table.from[0].joins.is_empty() {
771                    match &source_table.from[0].relation {
772                        TableFactor::Table(source_table_id) => {
773                            debug_assert_eq!(0, source_table_id.0);
774                            debug_assert_eq!(1, source_table.tables.len());
775
776                            match &source_table.tables[0] {
777                                TableRef::Table(table_id) => {
778                                    let table = schema.table(*table_id).unwrap();
779                                    ExprTarget::Table(table)
780                                }
781                                _ => self,
782                            }
783                        }
784                    }
785                } else {
786                    self
787                }
788            }
789            _ => self,
790        }
791    }
792}
793
794impl<'a, T> IntoExprTarget<'a, T> for &'a ModelRoot {
795    fn into_expr_target(self, _schema: &'a T) -> ExprTarget<'a> {
796        ExprTarget::Model(self)
797    }
798}
799
800impl<'a, T> IntoExprTarget<'a, T> for &'a Table {
801    fn into_expr_target(self, _schema: &'a T) -> ExprTarget<'a> {
802        ExprTarget::Table(self)
803    }
804}
805
806impl<'a, T: Resolve> IntoExprTarget<'a, T> for &'a Query {
807    fn into_expr_target(self, schema: &'a T) -> ExprTarget<'a> {
808        self.body.into_expr_target(schema)
809    }
810}
811
812impl<'a, T: Resolve> IntoExprTarget<'a, T> for &'a ExprSet {
813    fn into_expr_target(self, schema: &'a T) -> ExprTarget<'a> {
814        match self {
815            ExprSet::Select(select) => select.into_expr_target(schema),
816            ExprSet::SetOp(_) => todo!(),
817            ExprSet::Update(update) => update.into_expr_target(schema),
818            ExprSet::Delete(delete) => delete.into_expr_target(schema),
819            ExprSet::Values(_) => ExprTarget::Free,
820            ExprSet::Insert(insert) => insert.into_expr_target(schema),
821        }
822    }
823}
824
825impl<'a, T: Resolve> IntoExprTarget<'a, T> for &'a Select {
826    fn into_expr_target(self, schema: &'a T) -> ExprTarget<'a> {
827        self.source.into_expr_target(schema)
828    }
829}
830
831impl<'a, T: Resolve> IntoExprTarget<'a, T> for &'a Insert {
832    fn into_expr_target(self, schema: &'a T) -> ExprTarget<'a> {
833        self.target.into_expr_target(schema)
834    }
835}
836
837impl<'a, T: Resolve> IntoExprTarget<'a, T> for &'a Update {
838    fn into_expr_target(self, schema: &'a T) -> ExprTarget<'a> {
839        self.target.into_expr_target(schema)
840    }
841}
842
843impl<'a, T: Resolve> IntoExprTarget<'a, T> for &'a Delete {
844    fn into_expr_target(self, schema: &'a T) -> ExprTarget<'a> {
845        self.from.into_expr_target(schema)
846    }
847}
848
849impl<'a, T: Resolve> IntoExprTarget<'a, T> for &'a InsertTarget {
850    fn into_expr_target(self, schema: &'a T) -> ExprTarget<'a> {
851        match self {
852            InsertTarget::Scope(query) => query.into_expr_target(schema),
853            InsertTarget::Model(model) => {
854                let Some(model) = schema.model(*model) else {
855                    todo!()
856                };
857                ExprTarget::Model(model.as_root_unwrap())
858            }
859            InsertTarget::Table(insert_table) => {
860                let table = schema.table(insert_table.table).unwrap();
861                ExprTarget::Table(table)
862            }
863        }
864    }
865}
866
867impl<'a, T: Resolve> IntoExprTarget<'a, T> for &'a UpdateTarget {
868    fn into_expr_target(self, schema: &'a T) -> ExprTarget<'a> {
869        match self {
870            UpdateTarget::Query(query) => query.into_expr_target(schema),
871            UpdateTarget::Model(model) => {
872                let Some(model) = schema.model(*model) else {
873                    todo!()
874                };
875                ExprTarget::Model(model.as_root_unwrap())
876            }
877            UpdateTarget::Table(table_id) => {
878                let Some(table) = schema.table(*table_id) else {
879                    todo!()
880                };
881                ExprTarget::Table(table)
882            }
883        }
884    }
885}
886
887impl<'a, T: Resolve> IntoExprTarget<'a, T> for &'a Source {
888    fn into_expr_target(self, schema: &'a T) -> ExprTarget<'a> {
889        match self {
890            Source::Model(source_model) => {
891                let Some(model) = schema.model(source_model.id) else {
892                    todo!()
893                };
894                ExprTarget::Model(model.as_root_unwrap())
895            }
896            Source::Table(source_table) => {
897                ExprTarget::Source(source_table).into_expr_target(schema)
898            }
899        }
900    }
901}
902
903impl<'a, T: Resolve> IntoExprTarget<'a, T> for &'a Statement {
904    fn into_expr_target(self, schema: &'a T) -> ExprTarget<'a> {
905        match self {
906            Statement::Delete(stmt) => stmt.into_expr_target(schema),
907            Statement::Insert(stmt) => stmt.into_expr_target(schema),
908            Statement::Query(stmt) => stmt.into_expr_target(schema),
909            Statement::Update(stmt) => stmt.into_expr_target(schema),
910        }
911    }
912}
913
914impl<'a> ArgTyStack<'a> {
915    fn new(tys: &'a [Type]) -> ArgTyStack<'a> {
916        ArgTyStack { tys, parent: None }
917    }
918
919    fn resolve_arg_ty(&self, expr_arg: &ExprArg) -> &'a Type {
920        let mut nesting = expr_arg.nesting;
921        let mut args = self;
922
923        while nesting > 0 {
924            args = args.parent.unwrap();
925            nesting -= 1;
926        }
927
928        &args.tys[expr_arg.position]
929    }
930
931    fn scope<'child>(&'child self, tys: &'child [Type]) -> ArgTyStack<'child> {
932        ArgTyStack {
933            tys,
934            parent: Some(self),
935        }
936    }
937}