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#[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#[derive(Debug)]
49pub enum ResolvedRef<'a> {
50 Column(&'a Column),
59
60 Field(&'a Field),
70
71 Model(&'a ModelRoot),
73
74 Cte {
84 nesting: usize,
86 index: usize,
88 },
89
90 Derived(DerivedRef<'a>),
96}
97
98#[derive(Debug)]
100pub struct DerivedRef<'a> {
101 pub nesting: usize,
103
104 pub index: usize,
106
107 pub derived: &'a TableDerived,
109}
110
111impl DerivedRef<'_> {
112 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#[derive(Debug, Clone, Copy)]
149pub enum ExprTarget<'a> {
150 Free,
152
153 Model(&'a ModelRoot),
155
156 Table(&'a Table),
160
161 Source(&'a SourceTable),
163}
164
165pub trait Resolve {
171 fn table_for_model(&self, model: &ModelRoot) -> Option<&Table> {
173 let _ = model;
174 None
175 }
176
177 fn model(&self, id: ModelId) -> Option<&Model> {
183 let _ = id;
184 None
185 }
186
187 fn table(&self, id: TableId) -> Option<&Table> {
193 let _ = id;
194 None
195 }
196}
197
198pub trait IntoExprTarget<'a, T = Schema> {
201 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 pub fn schema(&self) -> &'a T {
214 self.schema
215 }
216
217 pub fn target(&self) -> ExprTarget<'a> {
219 self.target
220 }
221
222 pub fn target_at(&self, nesting: usize) -> &ExprTarget<'a> {
224 let mut curr = self;
225
226 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 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 pub fn new(schema: &'a T) -> ExprContext<'a, T> {
253 ExprContext::new_with_target(schema, ExprTarget::Free)
254 }
255
256 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 pub fn scope<'child>(
272 &'child self,
273 target: impl IntoExprTarget<'child, T>,
274 ) -> 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 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 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 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 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 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 let base = self.infer_expr_ty2(args, &e.base, returning_expr);
471
472 let Type::List(item) = base else {
474 todo!("error handling; base={base:#?}")
475 };
476
477 let scope_tys = &[*item];
478
479 let args = args.scope(scope_tys);
481
482 let ty = self.infer_expr_ty2(&args, &e.map, returning_expr);
484
485 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 assert!(e.projection.as_slice().len() == 1);
498 return args.resolve_arg_ty(expr_arg).clone();
499 }
500 Expr::Reference(expr_reference) => {
501 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 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 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 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 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 pub fn target_as_model(&self) -> Option<&'a ModelRoot> {
600 self.target.as_model()
601 }
602
603 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 #[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 #[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 #[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 pub fn as_model(self) -> Option<&'a ModelRoot> {
727 match self {
728 ExprTarget::Model(model) => Some(model),
729 _ => None,
730 }
731 }
732
733 #[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 pub fn model_id(self) -> Option<ModelId> {
748 Some(match self {
749 ExprTarget::Model(model) => model.id,
750 _ => return None,
751 })
752 }
753
754 pub fn as_table(self) -> Option<&'a Table> {
756 match self {
757 ExprTarget::Table(table) => Some(table),
758 _ => None,
759 }
760 }
761
762 #[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}