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        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::EmbeddedStruct(embedded)) => {
527                                embedded.fields[*step].expr_ty().clone()
528                            }
529                            _ => todo!("project into non-embedded model {id:?}"),
530                        },
531                        Type::List(items) => *items,
532                        expr => todo!(
533                            "returning_expr={returning_expr:#?}; expr={expr:#?}; project={e:#?}"
534                        ),
535                    }
536                }
537
538                base
539            }
540            Expr::Record(e) => Type::Record(
541                e.fields
542                    .iter()
543                    .map(|field| self.infer_expr_ty2(args, field, returning_expr))
544                    .collect(),
545            ),
546            Expr::Value(value) => value.infer_ty(),
547            Expr::Let(expr_let) => {
548                let scope_tys: Vec<_> = expr_let
549                    .bindings
550                    .iter()
551                    .map(|b| self.infer_expr_ty2(args, b, returning_expr))
552                    .collect();
553                let args = args.scope(&scope_tys);
554                self.infer_expr_ty2(&args, &expr_let.body, returning_expr)
555            }
556            Expr::Match(expr_match) => {
557                // Collect the distinct non-null types from all arms and the else
558                // branch. If all agree on one type, return it directly. If they
559                // differ, return a Union so callers know exactly which shapes are
560                // possible at runtime.
561                let mut union = TypeUnion::new();
562                for arm in &expr_match.arms {
563                    let ty = self.infer_expr_ty2(args, &arm.expr, returning_expr);
564                    union.insert(ty);
565                }
566                let else_ty = self.infer_expr_ty2(args, &expr_match.else_expr, returning_expr);
567                union.insert(else_ty);
568                union.simplify()
569            }
570            // Error is a bottom type — it can never be evaluated, so it
571            // could be any type. Return Unknown so it unifies with whatever
572            // the other branches produce.
573            Expr::Error(_) => Type::Unknown,
574            Expr::Exists(_) => Type::Bool,
575            Expr::Func(ExprFunc::Count(_)) => Type::U64,
576            Expr::Func(ExprFunc::LastInsertId(_)) => Type::I64,
577            Expr::Func(ExprFunc::JsonExtract(func)) => func.ty.clone(),
578            _ => todo!("{expr:#?}"),
579        }
580    }
581
582    /// Infers the type of an expression reference (field or column).
583    pub fn infer_expr_reference_ty(&self, expr_reference: &ExprReference) -> Type {
584        match self.resolve_expr_reference(expr_reference) {
585            ResolvedRef::Model(model) => Type::Model(model.id),
586            ResolvedRef::Column(column) => column.ty.clone(),
587            ResolvedRef::Field(field) => field.expr_ty().clone(),
588            ResolvedRef::Cte { .. } => todo!("type inference for CTE columns not implemented"),
589            ResolvedRef::Derived(_) => {
590                todo!("type inference for derived table columns not implemented")
591            }
592        }
593    }
594}
595
596impl<'a> ExprContext<'a, Schema> {
597    /// Returns the context target as a `ModelRoot` reference, or `None` if the target is not a
598    /// model.
599    pub fn target_as_model(&self) -> Option<&'a ModelRoot> {
600        self.target.as_model()
601    }
602
603    /// Creates an `ExprReference::Column` for the given column ID.
604    ///
605    /// # Panics
606    ///
607    /// Panics if the context has no table target (`ExprTarget::Free`), if the column does not
608    /// belong to the table associated with the current target, or if the target's model has no
609    /// mapped database table.
610    pub fn expr_ref_column(&self, column_id: impl Into<ColumnId>) -> ExprReference {
611        let column_id = column_id.into();
612
613        match self.target {
614            ExprTarget::Free => {
615                panic!("Cannot create ExprColumn in free context - no table target available")
616            }
617            ExprTarget::Model(model) => {
618                let Some(table) = self.schema.table_for_model(model) else {
619                    panic!(
620                        "Failed to find database table for model '{:?}' - model may not be mapped to a table",
621                        model.name
622                    )
623                };
624
625                assert_eq!(table.id, column_id.table);
626            }
627            ExprTarget::Table(table) => assert_eq!(table.id, column_id.table),
628            ExprTarget::Source(source_table) => {
629                let [TableRef::Table(table_id)] = source_table.tables[..] else {
630                    panic!(
631                        "Expected exactly one table reference, found {} tables",
632                        source_table.tables.len()
633                    );
634                };
635                assert_eq!(table_id, column_id.table);
636            }
637        }
638
639        ExprReference::Column(ExprColumn {
640            nesting: 0,
641            table: 0,
642            column: column_id.index,
643        })
644    }
645}
646
647impl<'a, T> Clone for ExprContext<'a, T> {
648    fn clone(&self) -> Self {
649        *self
650    }
651}
652
653impl<'a, T> Copy for ExprContext<'a, T> {}
654
655impl<'a> ResolvedRef<'a> {
656    /// Returns the inner `Column` reference.
657    ///
658    /// # Panics
659    ///
660    /// Panics if this is not `ResolvedRef::Column`.
661    #[track_caller]
662    pub fn as_column_unwrap(self) -> &'a Column {
663        match self {
664            ResolvedRef::Column(column) => column,
665            _ => panic!("Expected ResolvedRef::Column, found {:?}", self),
666        }
667    }
668
669    /// Returns the inner `Field` reference.
670    ///
671    /// # Panics
672    ///
673    /// Panics if this is not `ResolvedRef::Field`.
674    #[track_caller]
675    pub fn as_field_unwrap(self) -> &'a Field {
676        match self {
677            ResolvedRef::Field(field) => field,
678            _ => panic!("Expected ResolvedRef::Field, found {:?}", self),
679        }
680    }
681
682    /// Returns the inner `ModelRoot` reference.
683    ///
684    /// # Panics
685    ///
686    /// Panics if this is not `ResolvedRef::Model`.
687    #[track_caller]
688    pub fn as_model_unwrap(self) -> &'a ModelRoot {
689        match self {
690            ResolvedRef::Model(model) => model,
691            _ => panic!("Expected ResolvedRef::Model, found {:?}", self),
692        }
693    }
694}
695
696impl Resolve for Schema {
697    fn model(&self, id: ModelId) -> Option<&Model> {
698        Some(self.app.model(id))
699    }
700
701    fn table(&self, id: TableId) -> Option<&Table> {
702        Some(self.db.table(id))
703    }
704
705    fn table_for_model(&self, model: &ModelRoot) -> Option<&Table> {
706        Some(self.table_for(model.id))
707    }
708}
709
710impl Resolve for crate::schema::app::Schema {
711    fn model(&self, id: ModelId) -> Option<&Model> {
712        self.get_model(id)
713    }
714}
715
716impl Resolve for db::Schema {
717    fn table(&self, id: TableId) -> Option<&Table> {
718        Some(db::Schema::table(self, id))
719    }
720}
721
722impl Resolve for () {}
723
724impl<'a> ExprTarget<'a> {
725    /// Returns the model if this target is [`ExprTarget::Model`], or `None`.
726    pub fn as_model(self) -> Option<&'a ModelRoot> {
727        match self {
728            ExprTarget::Model(model) => Some(model),
729            _ => None,
730        }
731    }
732
733    /// Returns the model, panicking if not [`ExprTarget::Model`].
734    ///
735    /// # Panics
736    ///
737    /// Panics if the target is not `Model`.
738    #[track_caller]
739    pub fn as_model_unwrap(self) -> &'a ModelRoot {
740        match self.as_model() {
741            Some(model) => model,
742            _ => panic!("expected ExprTarget::Model; was {self:#?}"),
743        }
744    }
745
746    /// Returns the model ID if this target is [`ExprTarget::Model`], or `None`.
747    pub fn model_id(self) -> Option<ModelId> {
748        Some(match self {
749            ExprTarget::Model(model) => model.id,
750            _ => return None,
751        })
752    }
753
754    /// Returns the table if this target is [`ExprTarget::Table`], or `None`.
755    pub fn as_table(self) -> Option<&'a Table> {
756        match self {
757            ExprTarget::Table(table) => Some(table),
758            _ => None,
759        }
760    }
761
762    /// Returns the table, panicking if not [`ExprTarget::Table`].
763    ///
764    /// # Panics
765    ///
766    /// Panics if the target is not `Table`.
767    #[track_caller]
768    pub fn as_table_unwrap(self) -> &'a Table {
769        self.as_table()
770            .unwrap_or_else(|| panic!("expected ExprTarget::Table; was {self:#?}"))
771    }
772}
773
774impl<'a, T: Resolve> IntoExprTarget<'a, T> for ExprTarget<'a> {
775    fn into_expr_target(self, schema: &'a T) -> ExprTarget<'a> {
776        match self {
777            ExprTarget::Source(source_table) => {
778                if source_table.from.len() == 1 && source_table.from[0].joins.is_empty() {
779                    match &source_table.from[0].relation {
780                        TableFactor::Table(source_table_id) => {
781                            debug_assert_eq!(0, source_table_id.0);
782                            debug_assert_eq!(1, source_table.tables.len());
783
784                            match &source_table.tables[0] {
785                                TableRef::Table(table_id) => {
786                                    let table = schema.table(*table_id).unwrap();
787                                    ExprTarget::Table(table)
788                                }
789                                _ => self,
790                            }
791                        }
792                    }
793                } else {
794                    self
795                }
796            }
797            _ => self,
798        }
799    }
800}
801
802impl<'a, T> IntoExprTarget<'a, T> for &'a ModelRoot {
803    fn into_expr_target(self, _schema: &'a T) -> ExprTarget<'a> {
804        ExprTarget::Model(self)
805    }
806}
807
808impl<'a, T> IntoExprTarget<'a, T> for &'a Table {
809    fn into_expr_target(self, _schema: &'a T) -> ExprTarget<'a> {
810        ExprTarget::Table(self)
811    }
812}
813
814impl<'a, T: Resolve> IntoExprTarget<'a, T> for &'a Query {
815    fn into_expr_target(self, schema: &'a T) -> ExprTarget<'a> {
816        self.body.into_expr_target(schema)
817    }
818}
819
820impl<'a, T: Resolve> IntoExprTarget<'a, T> for &'a ExprSet {
821    fn into_expr_target(self, schema: &'a T) -> ExprTarget<'a> {
822        match self {
823            ExprSet::Select(select) => select.into_expr_target(schema),
824            ExprSet::SetOp(_) => todo!(),
825            ExprSet::Update(update) => update.into_expr_target(schema),
826            ExprSet::Delete(delete) => delete.into_expr_target(schema),
827            ExprSet::Values(_) => ExprTarget::Free,
828            ExprSet::Insert(insert) => insert.into_expr_target(schema),
829        }
830    }
831}
832
833impl<'a, T: Resolve> IntoExprTarget<'a, T> for &'a Select {
834    fn into_expr_target(self, schema: &'a T) -> ExprTarget<'a> {
835        self.source.into_expr_target(schema)
836    }
837}
838
839impl<'a, T: Resolve> IntoExprTarget<'a, T> for &'a Insert {
840    fn into_expr_target(self, schema: &'a T) -> ExprTarget<'a> {
841        self.target.into_expr_target(schema)
842    }
843}
844
845impl<'a, T: Resolve> IntoExprTarget<'a, T> for &'a Update {
846    fn into_expr_target(self, schema: &'a T) -> ExprTarget<'a> {
847        self.target.into_expr_target(schema)
848    }
849}
850
851impl<'a, T: Resolve> IntoExprTarget<'a, T> for &'a Delete {
852    fn into_expr_target(self, schema: &'a T) -> ExprTarget<'a> {
853        self.from.into_expr_target(schema)
854    }
855}
856
857impl<'a, T: Resolve> IntoExprTarget<'a, T> for &'a InsertTarget {
858    fn into_expr_target(self, schema: &'a T) -> ExprTarget<'a> {
859        match self {
860            InsertTarget::Scope(query) => query.into_expr_target(schema),
861            InsertTarget::Model(model) => {
862                let Some(model) = schema.model(*model) else {
863                    todo!()
864                };
865                ExprTarget::Model(model.as_root_unwrap())
866            }
867            InsertTarget::Table(insert_table) => {
868                let table = schema.table(insert_table.table).unwrap();
869                ExprTarget::Table(table)
870            }
871        }
872    }
873}
874
875impl<'a, T: Resolve> IntoExprTarget<'a, T> for &'a UpdateTarget {
876    fn into_expr_target(self, schema: &'a T) -> ExprTarget<'a> {
877        match self {
878            UpdateTarget::Query(query) => query.into_expr_target(schema),
879            UpdateTarget::Model(model) => {
880                let Some(model) = schema.model(*model) else {
881                    todo!()
882                };
883                ExprTarget::Model(model.as_root_unwrap())
884            }
885            UpdateTarget::Table(table_id) => {
886                let Some(table) = schema.table(*table_id) else {
887                    todo!()
888                };
889                ExprTarget::Table(table)
890            }
891        }
892    }
893}
894
895impl<'a, T: Resolve> IntoExprTarget<'a, T> for &'a Source {
896    fn into_expr_target(self, schema: &'a T) -> ExprTarget<'a> {
897        match self {
898            Source::Model(source_model) => {
899                let Some(model) = schema.model(source_model.id) else {
900                    todo!()
901                };
902                ExprTarget::Model(model.as_root_unwrap())
903            }
904            Source::Table(source_table) => {
905                ExprTarget::Source(source_table).into_expr_target(schema)
906            }
907        }
908    }
909}
910
911impl<'a, T: Resolve> IntoExprTarget<'a, T> for &'a Statement {
912    fn into_expr_target(self, schema: &'a T) -> ExprTarget<'a> {
913        match self {
914            Statement::Delete(stmt) => stmt.into_expr_target(schema),
915            Statement::Insert(stmt) => stmt.into_expr_target(schema),
916            Statement::Query(stmt) => stmt.into_expr_target(schema),
917            Statement::Update(stmt) => stmt.into_expr_target(schema),
918        }
919    }
920}
921
922impl<'a> ArgTyStack<'a> {
923    fn new(tys: &'a [Type]) -> ArgTyStack<'a> {
924        ArgTyStack { tys, parent: None }
925    }
926
927    fn resolve_arg_ty(&self, expr_arg: &ExprArg) -> &'a Type {
928        let mut nesting = expr_arg.nesting;
929        let mut args = self;
930
931        while nesting > 0 {
932            args = args.parent.unwrap();
933            nesting -= 1;
934        }
935
936        &args.tys[expr_arg.position]
937    }
938
939    fn scope<'child>(&'child self, tys: &'child [Type]) -> ArgTyStack<'child> {
940        ArgTyStack {
941            tys,
942            parent: Some(self),
943        }
944    }
945}