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
174 fn model(&self, id: ModelId) -> Option<&Model>;
180
181 fn table(&self, id: TableId) -> Option<&Table>;
187}
188
189pub trait IntoExprTarget<'a, T = Schema> {
192 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 pub fn schema(&self) -> &'a T {
205 self.schema
206 }
207
208 pub fn target(&self) -> ExprTarget<'a> {
210 self.target
211 }
212
213 pub fn target_at(&self, nesting: usize) -> &ExprTarget<'a> {
215 let mut curr = self;
216
217 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 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 pub fn new(schema: &'a T) -> ExprContext<'a, T> {
244 ExprContext::new_with_target(schema, ExprTarget::Free)
245 }
246
247 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 pub fn scope<'child>(
263 &'child self,
264 target: impl IntoExprTarget<'child, T>,
265 ) -> 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 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 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 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 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 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 let base = self.infer_expr_ty2(args, &e.base, returning_expr);
462
463 let Type::List(item) = base else {
465 todo!("error handling; base={base:#?}")
466 };
467
468 let scope_tys = &[*item];
469
470 let args = args.scope(scope_tys);
472
473 let ty = self.infer_expr_ty2(&args, &e.map, returning_expr);
475
476 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 assert!(e.projection.as_slice().len() == 1);
489 return args.resolve_arg_ty(expr_arg).clone();
490 }
491 Expr::Reference(expr_reference) => {
492 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 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 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 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 pub fn target_as_model(&self) -> Option<&'a ModelRoot> {
578 self.target.as_model()
579 }
580
581 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 #[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 #[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 #[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 pub fn as_model(self) -> Option<&'a ModelRoot> {
719 match self {
720 ExprTarget::Model(model) => Some(model),
721 _ => None,
722 }
723 }
724
725 #[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 pub fn model_id(self) -> Option<ModelId> {
740 Some(match self {
741 ExprTarget::Model(model) => model.id,
742 _ => return None,
743 })
744 }
745
746 pub fn as_table(self) -> Option<&'a Table> {
748 match self {
749 ExprTarget::Table(table) => Some(table),
750 _ => None,
751 }
752 }
753
754 #[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}