1use crate::prelude::*;
2use toasty::stmt::IntoExpr;
3
4#[driver_test]
10pub async fn compare_embedded_relations_preserves_both_variant_guards(
11 test: &mut Test,
12) -> Result<()> {
13 #[derive(Debug, toasty::Model)]
14 struct Human {
15 #[key]
16 #[auto]
17 id: uuid::Uuid,
18 }
19
20 #[derive(Debug, toasty::Embed)]
21 #[index(id)]
22 enum Owner {
23 Primary {
24 #[shared(id)]
25 id: uuid::Uuid,
26 #[shared(active)]
27 active: bool,
28 #[belongs_to(key = id)]
29 human: toasty::Deferred<Human>,
30 },
31 Other {
32 #[shared(id)]
33 id: uuid::Uuid,
34 #[shared(active)]
35 active: bool,
36 #[belongs_to(key = id)]
37 other: toasty::Deferred<Human>,
38 },
39 }
40
41 #[derive(Debug, toasty::Model)]
42 struct Object {
43 #[key]
44 #[auto]
45 id: uuid::Uuid,
46 lhs: Owner,
47 rhs: Owner,
48 }
49
50 let mut db = test.setup_db(models!(Object, Human)).await;
51 let ann = toasty::create!(Human {}).exec(&mut db).await?;
52 let bea = toasty::create!(Human {}).exec(&mut db).await?;
53
54 let mut equal = None;
59 let mut unequal = None;
60 for left_primary in [true, false] {
61 for right_primary in [true, false] {
62 for same in [true, false] {
63 let right = if same { &ann } else { &bea };
64 let lhs = if left_primary {
65 Owner::Primary {
66 id: ann.id,
67 active: true,
68 human: toasty::Deferred::default(),
69 }
70 } else {
71 Owner::Other {
72 id: ann.id,
73 active: true,
74 other: toasty::Deferred::default(),
75 }
76 };
77 let rhs = if right_primary {
78 Owner::Primary {
79 id: right.id,
80 active: same,
81 human: toasty::Deferred::default(),
82 }
83 } else {
84 Owner::Other {
85 id: right.id,
86 active: same,
87 other: toasty::Deferred::default(),
88 }
89 };
90 let object = toasty::create!(Object { lhs, rhs }).exec(&mut db).await?;
91 if left_primary && right_primary {
92 if same {
93 equal = Some(object.id);
94 } else {
95 unequal = Some(object.id);
96 }
97 }
98 }
99 }
100 }
101
102 let lhs = || Object::fields().lhs().primary();
103 let rhs = || Object::fields().rhs().primary();
104 for (predicate, expected) in [
105 (lhs().human().eq(rhs().human()), equal),
107 (rhs().human().eq(lhs().human()), equal),
108 (lhs().human().ne(rhs().human()), unequal),
109 (rhs().human().ne(lhs().human()), unequal),
110 (lhs().id().eq(rhs().id()), equal),
112 (rhs().id().eq(lhs().id()), equal),
113 (lhs().id().ne(rhs().id()), unequal),
114 (rhs().id().ne(lhs().id()), unequal),
115 (lhs().human().into_expr().eq(rhs().human()), equal),
117 (lhs().id().into_expr().ne(rhs().id()), unequal),
118 ] {
119 let found = Object::filter(predicate).exec(&mut db).await?;
120 assert_eq!(found.len(), 1);
121 assert_eq!(Some(found[0].id), expected);
122 }
123
124 let found = Object::filter(lhs().human().eq(rhs().human()).not())
127 .exec(&mut db)
128 .await?;
129 assert_eq!(found.len(), 7);
130
131 let found = Object::filter(lhs().active().eq(true))
133 .exec(&mut db)
134 .await?;
135 assert_eq!(found.len(), 4);
136 let found = Object::filter(rhs().active().eq(false))
137 .exec(&mut db)
138 .await?;
139 assert_eq!(found.len(), 2);
140
141 Ok(())
142}
143
144#[driver_test]
148pub async fn raw_ast_predicate_requires_the_selected_variant(test: &mut Test) -> Result<()> {
149 use toasty_core::stmt as core_stmt;
150
151 #[derive(Debug, toasty::Embed)]
152 enum Owner {
153 Primary {
154 #[shared(active)]
155 active: bool,
156 },
157 Other {
158 #[shared(active)]
159 active: bool,
160 },
161 }
162
163 #[derive(Debug, toasty::Model)]
164 struct Object {
165 #[key]
166 #[auto]
167 id: uuid::Uuid,
168 owner: Owner,
169 }
170
171 let mut db = test.setup_db(models!(Object)).await;
172 let primary = toasty::create!(Object {
173 owner: Owner::Primary { active: true }
174 })
175 .exec(&mut db)
176 .await?;
177 toasty::create!(Object {
178 owner: Owner::Other { active: true }
179 })
180 .exec(&mut db)
181 .await?;
182
183 let active: core_stmt::Path = Object::fields().owner().primary().active().into();
186 let predicate =
187 toasty::stmt::Expr::<bool>::from_untyped(core_stmt::Expr::eq(active.into_stmt(), true));
188 let found = Object::filter(predicate).exec(&mut db).await?;
189 assert_eq!(found.len(), 1);
190 assert_eq!(found[0].id, primary.id);
191
192 Ok(())
193}
194
195mod nested_composite {
198 use crate::prelude::*;
199
200 #[derive(Debug, toasty::Model)]
201 #[key(partition = namespace, local = revision)]
202 pub struct Parent {
203 pub namespace: uuid::Uuid,
204 pub revision: i64,
205 pub name: String,
206 }
207
208 #[derive(Debug, toasty::Embed)]
209 pub enum Owner {
210 Parent {
211 #[index]
212 namespace: uuid::Uuid,
213 revision: i64,
214 #[belongs_to(key = [namespace, revision], references = [namespace, revision])]
215 parent: toasty::Deferred<Parent>,
216 },
217 Empty,
218 }
219
220 #[derive(Debug, toasty::Embed)]
221 pub enum Envelope {
222 First { owner: Owner },
223 Second { other_owner: Owner },
224 }
225
226 #[derive(Debug, toasty::Model)]
227 pub struct Object {
228 #[key]
229 #[auto]
230 pub id: uuid::Uuid,
231 pub envelope: Envelope,
232 }
233
234 pub struct Fixture {
240 pub parents: [Parent; 3],
241 pub objects: [Object; 3],
242 }
243
244 pub fn owner(parent: &Parent) -> Owner {
247 Owner::Parent {
248 namespace: parent.namespace,
249 revision: parent.revision,
250 parent: toasty::Deferred::default(),
251 }
252 }
253
254 pub async fn setup(test: &mut Test) -> Result<(toasty::Db, Fixture)> {
255 let mut db = test.setup_db(models!(Object, Parent)).await;
256 let namespace = uuid::Uuid::new_v4();
257 let mut parents = Vec::with_capacity(3);
258 for (namespace, revision, name) in [
259 (namespace, 1, "first"),
260 (namespace, 2, "second"),
261 (uuid::Uuid::new_v4(), 2, "third"),
262 ] {
263 parents.push(
264 toasty::create!(Parent {
265 namespace,
266 revision,
267 name
268 })
269 .exec(&mut db)
270 .await?,
271 );
272 }
273
274 let mut objects = Vec::with_capacity(3);
275 for parent in &parents {
276 objects.push(
277 toasty::create!(Object {
278 envelope: Envelope::First {
279 owner: owner(parent)
280 },
281 })
282 .exec(&mut db)
283 .await?,
284 );
285 }
286 toasty::create!(Object {
287 envelope: Envelope::Second {
288 other_owner: owner(&parents[0])
289 },
290 })
291 .exec(&mut db)
292 .await?;
293 toasty::create!(Object {
294 envelope: Envelope::First {
295 owner: Owner::Empty
296 }
297 })
298 .exec(&mut db)
299 .await?;
300
301 let fixture = Fixture {
302 parents: parents.try_into().unwrap(),
303 objects: objects.try_into().unwrap(),
304 };
305 Ok((db, fixture))
306 }
307}
308
309#[driver_test]
313pub async fn nested_variant_composite_relation(test: &mut Test) -> Result<()> {
314 use nested_composite::{Envelope, Object, Owner, Parent, owner};
315
316 let (mut db, fixture) = nested_composite::setup(test).await?;
317 let [first, second, _] = &fixture.parents;
318 let [object, ..] = &fixture.objects;
319 let variant = || Object::fields().envelope().first();
320
321 let found = Object::filter(variant().owner().parent().parent().eq(first))
324 .exec(&mut db)
325 .await?;
326 assert_eq!(found.len(), 1);
327 assert_eq!(found[0].id, object.id);
328
329 let path: toasty::stmt::Path<Object, Parent> = variant().owner().parent().parent().into();
330 let found = Object::filter(path.into_expr().eq(first))
331 .exec(&mut db)
332 .await?;
333 assert_eq!(found.len(), 1);
334 assert_eq!(found[0].id, object.id);
335
336 let found = Object::filter(variant().owner().is_parent())
338 .exec(&mut db)
339 .await?;
340 assert_eq!(found.len(), 3);
341 let found = Object::filter(variant().owner().is_empty())
342 .exec(&mut db)
343 .await?;
344 assert_eq!(found.len(), 1);
345
346 let mut object = Object::get_by_id(&mut db, object.id).await?;
348 toasty::update!(object {
349 envelope: Envelope::First {
350 owner: owner(second)
351 }
352 })
353 .exec(&mut db)
354 .await?;
355 assert_struct!(
356 Object::get_by_id(&mut db, object.id).await?.envelope,
357 Envelope::First {
358 owner: Owner::Parent { revision: 2, .. }
359 }
360 );
361
362 Ok(())
363}
364
365#[driver_test(requires(sql))]
369pub async fn nested_variant_composite_relation_traversal(test: &mut Test) -> Result<()> {
370 use nested_composite::Object;
371
372 let (mut db, fixture) = nested_composite::setup(test).await?;
373 let [object, _, third_object] = &fixture.objects;
374 let variant = || Object::fields().envelope().first();
375
376 let found = Object::filter(
377 variant()
378 .owner()
379 .parent()
380 .matches(|v| v.parent().name().eq("first")),
381 )
382 .exec(&mut db)
383 .await?;
384 assert_eq!(found.len(), 1);
385 assert_eq!(found[0].id, object.id);
386
387 let found = Object::filter(
388 variant()
389 .owner()
390 .parent()
391 .matches(|v| v.parent().name().ne("second")),
392 )
393 .exec(&mut db)
394 .await?;
395 assert_eq!(found.len(), 2);
396 assert!(found.iter().any(|row| row.id == object.id));
397 assert!(found.iter().any(|row| row.id == third_object.id));
398
399 let found = Object::filter(
400 variant()
401 .owner()
402 .parent()
403 .matches(|v| v.parent().name().eq("missing")),
404 )
405 .exec(&mut db)
406 .await?;
407 assert!(found.is_empty());
408
409 Ok(())
410}
411
412#[driver_test]
420pub async fn belongs_to_in_enum_variants(test: &mut Test) -> Result<()> {
421 #[derive(Debug, toasty::Model)]
422 struct Human {
423 #[key]
424 #[auto]
425 id: uuid::Uuid,
426 name: String,
427 }
428
429 #[derive(Debug, toasty::Model)]
430 struct Bot {
431 #[key]
432 #[auto]
433 id: uuid::Uuid,
434 #[unique]
435 serial: String,
436 name: String,
437 }
438
439 #[derive(Debug, toasty::Embed)]
440 enum Owner {
441 Human {
442 #[index]
443 id: uuid::Uuid,
444 #[belongs_to(key = id)]
445 human: toasty::Deferred<Human>,
446 },
447 Bot {
448 #[index]
449 serial: String,
450 #[belongs_to(key = serial, references = serial)]
451 bot: toasty::Deferred<Bot>,
452 },
453 }
454
455 #[derive(Debug, toasty::Model)]
456 struct Object {
457 #[key]
458 #[auto]
459 id: uuid::Uuid,
460 owner: Owner,
461 }
462
463 let mut db = test.setup_db(models!(Object, Human, Bot)).await;
464
465 let table = &db.schema().db.tables[0];
468 let names: Vec<_> = table.columns.iter().map(|c| c.name.as_str()).collect();
469 assert_eq!(names, ["id", "owner", "owner_id", "owner_serial"]);
470
471 let alice = toasty::create!(Human { name: "Alice" })
472 .exec(&mut db)
473 .await?;
474 let bot = toasty::create!(Bot {
475 serial: "B-1000",
476 name: "Marvin"
477 })
478 .exec(&mut db)
479 .await?;
480
481 let obj_a = toasty::create!(Object {
483 owner: Owner::Human {
484 id: alice.id,
485 human: toasty::Deferred::default(),
486 }
487 })
488 .exec(&mut db)
489 .await?;
490 let obj_b = toasty::create!(Object {
491 owner: Owner::Bot {
492 serial: bot.serial.clone(),
493 bot: toasty::Deferred::default(),
494 }
495 })
496 .exec(&mut db)
497 .await?;
498
499 let mut obj_a = Object::get_by_id(&mut db, obj_a.id).await?;
502 match &obj_a.owner {
503 Owner::Human { id, human } => {
504 assert!(human.is_unloaded());
505 let human = Human::get_by_id(&mut db, id).await?;
506 assert_eq!(human.name, "Alice");
507 }
508 other => panic!("expected Owner::Human, got {other:?}"),
509 }
510
511 let obj_b = Object::get_by_id(&mut db, obj_b.id).await?;
512 match &obj_b.owner {
513 Owner::Bot { serial, bot } => {
514 assert!(bot.is_unloaded());
515 let bot = Bot::get_by_serial(&mut db, serial).await?;
516 assert_eq!(bot.name, "Marvin");
517 }
518 other => panic!("expected Owner::Bot, got {other:?}"),
519 }
520
521 obj_a
524 .update()
525 .owner(Owner::Bot {
526 serial: bot.serial.clone(),
527 bot: toasty::Deferred::default(),
528 })
529 .exec(&mut db)
530 .await?;
531 let reloaded = Object::get_by_id(&mut db, obj_a.id).await?;
532 assert_struct!(
533 reloaded.owner,
534 Owner::Bot {
535 serial: "B-1000",
536 ..
537 }
538 );
539
540 let obj_a_id = obj_a.id;
541 obj_a.delete().exec(&mut db).await?;
542 assert_err!(Object::get_by_id(&mut db, obj_a_id).await);
543 assert!(matches!(
544 Object::get_by_id(&mut db, obj_b.id).await?.owner,
545 Owner::Bot { .. }
546 ));
547
548 Ok(())
549}
550
551#[driver_test]
556pub async fn filter_by_relation_key_through_variant_path(test: &mut Test) -> Result<()> {
557 #[derive(Debug, toasty::Model)]
558 struct Human {
559 #[key]
560 #[auto]
561 id: uuid::Uuid,
562 name: String,
563 }
564
565 #[derive(Debug, toasty::Model)]
566 struct Animal {
567 #[key]
568 #[auto]
569 id: uuid::Uuid,
570 name: String,
571 }
572
573 #[derive(Debug, toasty::Embed)]
576 #[index(id)]
577 enum Owner {
578 Human {
579 #[shared(id)]
580 id: uuid::Uuid,
581 #[belongs_to(key = id)]
582 human: toasty::Deferred<Human>,
583 },
584 Animal {
585 #[shared(id)]
586 id: uuid::Uuid,
587 #[belongs_to(key = id)]
588 animal: toasty::Deferred<Animal>,
589 },
590 }
591
592 #[derive(Debug, toasty::Model)]
593 struct Object {
594 #[key]
595 #[auto]
596 id: uuid::Uuid,
597 owner: Owner,
598 }
599
600 let mut db = test.setup_db(models!(Object, Human, Animal)).await;
601
602 let table = &db.schema().db.tables[0];
604 let names: Vec<_> = table.columns.iter().map(|c| c.name.as_str()).collect();
605 assert_eq!(names, ["id", "owner", "owner_id"]);
606 let key_col = columns(&db, "objects", &["owner_id"])[0];
607 assert_struct!(table.indices, [
608 { primary_key: true },
609 { unique: false, primary_key: false, columns: [{ column: == key_col }] },
610 ]);
611
612 let alice = toasty::create!(Human { name: "Alice" })
613 .exec(&mut db)
614 .await?;
615 let rex = toasty::create!(Animal { name: "Rex" })
617 .exec(&mut db)
618 .await?;
619
620 let mut human_obj = toasty::create!(Object {
621 owner: Owner::Human {
622 id: alice.id,
623 human: toasty::Deferred::default(),
624 }
625 })
626 .exec(&mut db)
627 .await?;
628 toasty::create!(Object {
629 owner: Owner::Animal {
630 id: rex.id,
631 animal: toasty::Deferred::default(),
632 }
633 })
634 .exec(&mut db)
635 .await?;
636
637 let by_key = |id: uuid::Uuid| {
640 Object::fields()
641 .owner()
642 .human()
643 .matches(move |h| h.id().eq(id))
644 };
645 let found: Vec<Object> = Object::filter(by_key(alice.id)).exec(&mut db).await?;
646 assert_eq!(found.len(), 1);
647 assert_eq!(found[0].id, human_obj.id);
648
649 let humans: Vec<Object> = Object::filter(Object::fields().owner().is_human())
651 .exec(&mut db)
652 .await?;
653 assert_eq!(humans.len(), 1);
654 assert_eq!(humans[0].id, human_obj.id);
655
656 let bea = toasty::create!(Human { name: "Bea" }).exec(&mut db).await?;
659 human_obj
660 .update()
661 .owner(Owner::Human {
662 id: bea.id,
663 human: toasty::Deferred::default(),
664 })
665 .exec(&mut db)
666 .await?;
667 assert!(
668 Object::filter(by_key(alice.id))
669 .exec(&mut db)
670 .await?
671 .is_empty()
672 );
673 let found: Vec<Object> = Object::filter(by_key(bea.id)).exec(&mut db).await?;
674 assert_eq!(found.len(), 1);
675 assert_eq!(found[0].id, human_obj.id);
676
677 let found: Vec<Object> = Object::filter(
679 Object::fields()
680 .owner()
681 .human()
682 .matches(|v| v.human().eq(&bea)),
683 )
684 .exec(&mut db)
685 .await?;
686 assert_eq!(found.len(), 1);
687 assert_eq!(found[0].id, human_obj.id);
688
689 human_obj.delete().exec(&mut db).await?;
691 assert!(
692 Object::filter(by_key(bea.id))
693 .exec(&mut db)
694 .await?
695 .is_empty()
696 );
697 let animals: Vec<Object> = Object::filter(Object::fields().owner().is_animal())
698 .exec(&mut db)
699 .await?;
700 assert_eq!(animals.len(), 1);
701
702 Ok(())
703}
704
705#[driver_test]
709pub async fn belongs_to_in_embedded_struct(test: &mut Test) -> Result<()> {
710 #[derive(Debug, toasty::Model)]
711 struct Author {
712 #[key]
713 #[auto]
714 id: uuid::Uuid,
715 name: String,
716 }
717
718 #[derive(Debug, toasty::Embed)]
719 struct Attribution {
720 #[index]
721 author_id: uuid::Uuid,
722 #[belongs_to(key = author_id)]
723 author: toasty::Deferred<Author>,
724 note: String,
725 }
726
727 #[derive(Debug, toasty::Model)]
728 struct Post {
729 #[key]
730 #[auto]
731 id: uuid::Uuid,
732 attribution: Attribution,
733 }
734
735 let mut db = test.setup_db(models!(Post, Author)).await;
736
737 let table = &db.schema().db.tables[0];
738 let names: Vec<_> = table.columns.iter().map(|c| c.name.as_str()).collect();
739 assert_eq!(names, ["id", "attribution_author_id", "attribution_note"]);
740
741 let author = toasty::create!(Author { name: "Ann" })
742 .exec(&mut db)
743 .await?;
744
745 let post = toasty::create!(Post {
746 attribution: Attribution {
747 author_id: author.id,
748 author: toasty::Deferred::default(),
749 note: "first draft".to_string(),
750 }
751 })
752 .exec(&mut db)
753 .await?;
754
755 let mut post = Post::get_by_id(&mut db, post.id).await?;
756 assert!(post.attribution.author.is_unloaded());
757 assert_eq!(post.attribution.note, "first draft");
758 let author = Author::get_by_id(&mut db, &post.attribution.author_id).await?;
759 assert_eq!(author.name, "Ann");
760
761 let found: Vec<Post> = Post::filter(
763 Post::fields()
764 .attribution()
765 .author_id()
766 .eq(post.attribution.author_id),
767 )
768 .exec(&mut db)
769 .await?;
770 assert_eq!(found.len(), 1);
771
772 let other = toasty::create!(Author { name: "Bea" })
774 .exec(&mut db)
775 .await?;
776 post.update()
777 .attribution(toasty::stmt::patch(
778 Attribution::fields().author_id(),
779 other.id,
780 ))
781 .exec(&mut db)
782 .await?;
783 assert_eq!(post.attribution.author_id, other.id);
784 assert_eq!(
785 Post::get_by_id(&mut db, post.id)
786 .await?
787 .attribution
788 .author_id,
789 other.id
790 );
791
792 let post_id = post.id;
793 post.delete().exec(&mut db).await?;
794 assert_err!(Post::get_by_id(&mut db, post_id).await);
795
796 Ok(())
797}
798
799#[driver_test]
803pub async fn optional_relation_carrying_embed(test: &mut Test) -> Result<()> {
804 #[derive(Debug, toasty::Model)]
805 struct Human {
806 #[key]
807 #[auto]
808 id: uuid::Uuid,
809 }
810
811 #[derive(Debug, toasty::Embed)]
812 enum Owner {
813 Human {
814 #[index]
815 id: uuid::Uuid,
816 #[belongs_to(key = id)]
817 human: toasty::Deferred<Human>,
818 },
819 }
820
821 #[derive(Debug, toasty::Model)]
822 struct Object {
823 #[key]
824 #[auto]
825 id: uuid::Uuid,
826 owner: Option<Owner>,
827 }
828
829 let mut db = test.setup_db(models!(Object, Human)).await;
830
831 let orphan = toasty::create!(Object { owner: None })
832 .exec(&mut db)
833 .await?;
834
835 let human = toasty::create!(Human {}).exec(&mut db).await?;
836 let owned = toasty::create!(Object {
837 owner: Some(Owner::Human {
838 id: human.id,
839 human: toasty::Deferred::default(),
840 })
841 })
842 .exec(&mut db)
843 .await?;
844
845 let mut orphan = Object::get_by_id(&mut db, orphan.id).await?;
846 assert!(orphan.owner.is_none());
847
848 let mut owned = Object::get_by_id(&mut db, owned.id).await?;
849 match &owned.owner {
850 Some(Owner::Human { id, human: rel }) => {
851 assert_eq!(*id, human.id);
852 assert!(rel.is_unloaded());
853 }
854 other => panic!("expected Some(Owner::Human), got {other:?}"),
855 }
856
857 orphan
859 .update()
860 .owner(Some(Owner::Human {
861 id: human.id,
862 human: toasty::Deferred::default(),
863 }))
864 .exec(&mut db)
865 .await?;
866 assert!(matches!(
867 Object::get_by_id(&mut db, orphan.id).await?.owner,
868 Some(Owner::Human { .. })
869 ));
870
871 owned.update().owner(None).exec(&mut db).await?;
872 assert!(Object::get_by_id(&mut db, owned.id).await?.owner.is_none());
873
874 Ok(())
875}
876
877#[driver_test]
884pub async fn write_relation_from_parent_value(test: &mut Test) -> Result<()> {
885 #[derive(Debug, toasty::Model)]
886 struct Human {
887 #[key]
888 #[auto]
889 id: uuid::Uuid,
890 name: String,
891 }
892
893 #[derive(Debug, toasty::Model)]
894 struct Bot {
895 #[key]
896 #[auto]
897 id: uuid::Uuid,
898 #[unique]
899 serial: String,
900 name: String,
901 }
902
903 #[derive(Debug, toasty::Embed)]
904 enum Owner {
905 Human {
906 #[index]
907 id: uuid::Uuid,
908 #[belongs_to(key = id)]
909 human: toasty::Deferred<Human>,
910 },
911 Bot {
912 #[index]
913 serial: String,
914 #[belongs_to(key = serial, references = serial)]
915 bot: toasty::Deferred<Bot>,
916 },
917 }
918
919 #[derive(Debug, toasty::Model)]
920 struct Object {
921 #[key]
922 #[auto]
923 id: uuid::Uuid,
924 owner: Owner,
925 }
926
927 let mut db = test.setup_db(models!(Object, Human, Bot)).await;
928
929 let alice = toasty::create!(Human { name: "Alice" })
930 .exec(&mut db)
931 .await?;
932 let bot = toasty::create!(Bot {
933 serial: "B-1000",
934 name: "Marvin"
935 })
936 .exec(&mut db)
937 .await?;
938
939 let obj = toasty::create!(Object {
941 owner: Owner::Human { human: &alice }
942 })
943 .exec(&mut db)
944 .await?;
945 let mut obj = Object::get_by_id(&mut db, obj.id).await?;
946 assert_struct!(obj.owner, Owner::Human { id: == alice.id, .. });
947
948 let obj_b = toasty::create!(Object {
951 owner: Owner::Bot { bot: &bot }
952 })
953 .exec(&mut db)
954 .await?;
955 let obj_b = Object::get_by_id(&mut db, obj_b.id).await?;
956 assert_struct!(obj_b.owner, Owner::Bot { serial: == bot.serial, .. });
957
958 toasty::update!(obj {
961 owner: Owner::Bot { bot: &bot }
962 })
963 .exec(&mut db)
964 .await?;
965 assert_struct!(
966 Object::get_by_id(&mut db, obj.id).await?.owner,
967 Owner::Bot { serial: == bot.serial, .. }
968 );
969
970 toasty::update!(Object::filter_by_id(obj.id) {
971 owner: Owner::Human { human: &alice }
972 })
973 .exec(&mut db)
974 .await?;
975 assert_struct!(
976 Object::get_by_id(&mut db, obj.id).await?.owner,
977 Owner::Human { id: == alice.id, .. }
978 );
979
980 let carol = toasty::create!(Human { name: "Carol" })
983 .exec(&mut db)
984 .await?;
985 let carol_id = carol.id;
986 let obj_c = toasty::create!(Object {
987 owner: Owner::Human {
988 id: uuid::Uuid::new_v4(),
989 human: toasty::Deferred::from(carol),
990 }
991 })
992 .exec(&mut db)
993 .await?;
994 assert_struct!(
995 Object::get_by_id(&mut db, obj_c.id).await?.owner,
996 Owner::Human { id: == carol_id, .. }
997 );
998
999 Ok(())
1000}
1001
1002#[driver_test]
1006pub async fn write_optional_enum_from_variant_literal(test: &mut Test) -> Result<()> {
1007 #[derive(Debug, toasty::Model)]
1008 struct Human {
1009 #[key]
1010 #[auto]
1011 id: uuid::Uuid,
1012 }
1013
1014 #[derive(Debug, toasty::Embed)]
1015 enum Owner {
1016 Human {
1017 #[index]
1018 id: uuid::Uuid,
1019 #[belongs_to(key = id)]
1020 human: toasty::Deferred<Human>,
1021 },
1022 }
1023
1024 #[derive(Debug, toasty::Model)]
1025 struct Object {
1026 #[key]
1027 #[auto]
1028 id: uuid::Uuid,
1029 owner: Option<Owner>,
1030 }
1031
1032 let mut db = test.setup_db(models!(Object, Human)).await;
1033 let ann = toasty::create!(Human {}).exec(&mut db).await?;
1034 let bea = toasty::create!(Human {}).exec(&mut db).await?;
1035
1036 let obj = toasty::create!(Object {
1037 owner: Owner::Human { human: &ann }
1038 })
1039 .exec(&mut db)
1040 .await?;
1041 let mut obj = Object::get_by_id(&mut db, obj.id).await?;
1042 assert_struct!(obj.owner, Some(Owner::Human { id: == ann.id, .. }));
1043
1044 toasty::update!(obj {
1045 owner: Owner::Human { human: &bea }
1046 })
1047 .exec(&mut db)
1048 .await?;
1049 assert_struct!(
1050 Object::get_by_id(&mut db, obj.id).await?.owner,
1051 Some(Owner::Human { id: == bea.id, .. })
1052 );
1053
1054 toasty::update!(obj { owner: None }).exec(&mut db).await?;
1055 assert!(Object::get_by_id(&mut db, obj.id).await?.owner.is_none());
1056
1057 Ok(())
1058}
1059
1060#[driver_test]
1064pub async fn replace_enum_inside_embedded_patch(test: &mut Test) -> Result<()> {
1065 #[derive(Debug, toasty::Model)]
1066 struct Human {
1067 #[key]
1068 #[auto]
1069 id: uuid::Uuid,
1070 }
1071
1072 #[derive(Debug, toasty::Embed)]
1073 enum Owner {
1074 Human {
1075 #[index]
1076 id: uuid::Uuid,
1077 #[belongs_to(key = id)]
1078 human: toasty::Deferred<Human>,
1079 },
1080 Anonymous {
1081 label: String,
1082 },
1083 }
1084
1085 #[derive(Debug, toasty::Embed)]
1086 struct Meta {
1087 title: String,
1088 owner: Owner,
1089 }
1090
1091 #[derive(Debug, toasty::Model)]
1092 struct Object {
1093 #[key]
1094 #[auto]
1095 id: uuid::Uuid,
1096 meta: Meta,
1097 }
1098
1099 let mut db = test.setup_db(models!(Object, Human)).await;
1100 let ann = toasty::create!(Human {}).exec(&mut db).await?;
1101 let bea = toasty::create!(Human {}).exec(&mut db).await?;
1102
1103 let obj = toasty::create!(Object {
1104 meta: Meta {
1105 title: "draft".to_string(),
1106 owner: Owner::Anonymous {
1107 label: "nobody".to_string(),
1108 },
1109 }
1110 })
1111 .exec(&mut db)
1112 .await?;
1113 let mut obj = Object::get_by_id(&mut db, obj.id).await?;
1114
1115 toasty::update!(obj {
1116 meta: { owner: Owner::Human { human: &ann } }
1117 })
1118 .exec(&mut db)
1119 .await?;
1120 assert_struct!(Object::get_by_id(&mut db, obj.id).await?.meta, {
1121 title: "draft",
1122 owner: Owner::Human { id: == ann.id, .. },
1123 });
1124
1125 toasty::update!(Object::filter_by_id(obj.id) {
1126 meta: { owner: Owner::Human { human: &bea } }
1127 })
1128 .exec(&mut db)
1129 .await?;
1130 assert_struct!(Object::get_by_id(&mut db, obj.id).await?.meta, {
1131 title: "draft",
1132 owner: Owner::Human { id: == bea.id, .. },
1133 });
1134
1135 Ok(())
1136}
1137
1138#[driver_test]
1141pub async fn variant_literal_in_has_many_item(test: &mut Test) -> Result<()> {
1142 #[derive(Debug, toasty::Model)]
1143 struct Human {
1144 #[key]
1145 #[auto]
1146 id: uuid::Uuid,
1147 }
1148
1149 #[derive(Debug, toasty::Embed)]
1150 enum Owner {
1151 Human {
1152 #[index]
1153 id: uuid::Uuid,
1154 #[belongs_to(key = id)]
1155 human: toasty::Deferred<Human>,
1156 },
1157 }
1158
1159 #[derive(Debug, toasty::Model)]
1160 struct List {
1161 #[key]
1162 #[auto]
1163 id: uuid::Uuid,
1164 #[has_many]
1165 items: toasty::Deferred<Vec<Item>>,
1166 }
1167
1168 #[derive(Debug, toasty::Model)]
1169 struct Item {
1170 #[key]
1171 #[auto]
1172 id: uuid::Uuid,
1173 #[index]
1174 list_id: uuid::Uuid,
1175 #[belongs_to(key = list_id)]
1176 list: toasty::Deferred<List>,
1177 owner: Owner,
1178 }
1179
1180 let mut db = test.setup_db(models!(List, Item, Human)).await;
1181 let ann = toasty::create!(Human {}).exec(&mut db).await?;
1182 let bea = toasty::create!(Human {}).exec(&mut db).await?;
1183
1184 let mut list = toasty::create!(List {
1185 items: [{ owner: Owner::Human { human: &ann } }]
1186 })
1187 .exec(&mut db)
1188 .await?;
1189 toasty::update!(list {
1190 items: [{ owner: Owner::Human { human: &bea } }]
1191 })
1192 .exec(&mut db)
1193 .await?;
1194
1195 let items: Vec<Item> = list.items().exec(&mut db).await?;
1196 let mut owners: Vec<_> = items
1197 .into_iter()
1198 .map(|item| match item.owner {
1199 Owner::Human { id, .. } => id,
1200 })
1201 .collect();
1202 owners.sort();
1203 let mut expected = vec![ann.id, bea.id];
1204 expected.sort();
1205 assert_eq!(owners, expected);
1206
1207 Ok(())
1208}
1209
1210#[driver_test]
1215pub async fn update_variant_literal_value_expressions(test: &mut Test) -> Result<()> {
1216 #[derive(Debug, toasty::Model)]
1217 struct Human {
1218 #[key]
1219 #[auto]
1220 id: uuid::Uuid,
1221 name: String,
1222 }
1223
1224 #[derive(Debug, toasty::Embed)]
1225 enum Owner {
1226 Human {
1227 #[index]
1228 id: uuid::Uuid,
1229 #[belongs_to(key = id)]
1230 human: toasty::Deferred<Human>,
1231 },
1232 Anonymous {
1233 label: String,
1234 note: Option<String>,
1235 },
1236 }
1237
1238 #[derive(Debug, toasty::Model)]
1239 struct Object {
1240 #[key]
1241 #[auto]
1242 id: uuid::Uuid,
1243 owner: Owner,
1244 }
1245
1246 fn detached(human: &Human) -> Human {
1247 Human {
1248 id: human.id,
1249 name: human.name.clone(),
1250 }
1251 }
1252
1253 let mut db = test.setup_db(models!(Object, Human)).await;
1254 let ann = toasty::create!(Human { name: "Ann" }).exec(&mut db).await?;
1255 let obj = toasty::create!(Object {
1256 owner: Owner::Anonymous {
1257 label: "nobody".to_string(),
1258 note: None,
1259 }
1260 })
1261 .exec(&mut db)
1262 .await?;
1263 let mut obj = Object::get_by_id(&mut db, obj.id).await?;
1264
1265 toasty::update!(obj {
1267 owner: Owner::Human {
1268 human: &detached(&ann)
1269 }
1270 })
1271 .exec(&mut db)
1272 .await?;
1273 assert_struct!(
1274 Object::get_by_id(&mut db, obj.id).await?.owner,
1275 Owner::Human { id: == ann.id, .. }
1276 );
1277
1278 let note = "note";
1280 toasty::update!(obj {
1281 owner: Owner::Anonymous {
1282 label: "anon".into(),
1283 note: Some(note.to_uppercase()),
1284 }
1285 })
1286 .exec(&mut db)
1287 .await?;
1288 assert_struct!(
1289 Object::get_by_id(&mut db, obj.id).await?.owner,
1290 Owner::Anonymous {
1291 label: "anon",
1292 note: Some("NOTE"),
1293 }
1294 );
1295
1296 Ok(())
1297}
1298
1299#[driver_test]
1302pub async fn write_struct_embed_relation_from_loaded_value(test: &mut Test) -> Result<()> {
1303 #[derive(Debug, toasty::Model)]
1304 struct Author {
1305 #[key]
1306 #[auto]
1307 id: uuid::Uuid,
1308 name: String,
1309 }
1310
1311 #[derive(Debug, toasty::Embed)]
1312 struct Attribution {
1313 #[index]
1314 author_id: uuid::Uuid,
1315 #[belongs_to(key = author_id)]
1316 author: toasty::Deferred<Author>,
1317 }
1318
1319 #[derive(Debug, toasty::Model)]
1320 struct Post {
1321 #[key]
1322 #[auto]
1323 id: uuid::Uuid,
1324 attribution: Attribution,
1325 }
1326
1327 let mut db = test.setup_db(models!(Post, Author)).await;
1328
1329 let ann = toasty::create!(Author { name: "Ann" })
1330 .exec(&mut db)
1331 .await?;
1332 let ann_id = ann.id;
1333
1334 let post = toasty::create!(Post {
1335 attribution: Attribution {
1336 author_id: uuid::Uuid::new_v4(),
1337 author: toasty::Deferred::from(ann),
1338 }
1339 })
1340 .exec(&mut db)
1341 .await?;
1342
1343 let post = Post::get_by_id(&mut db, post.id).await?;
1344 assert_eq!(post.attribution.author_id, ann_id);
1345
1346 Ok(())
1347}
1348
1349#[driver_test]
1354pub async fn filter_by_relation_model_value(test: &mut Test) -> Result<()> {
1355 #[derive(Debug, toasty::Model)]
1356 struct Human {
1357 #[key]
1358 #[auto]
1359 id: uuid::Uuid,
1360 #[index]
1361 name: String,
1362 }
1363
1364 #[derive(Debug, toasty::Model)]
1365 struct Animal {
1366 #[key]
1367 #[auto]
1368 id: uuid::Uuid,
1369 name: String,
1370 }
1371
1372 #[derive(Debug, toasty::Embed)]
1373 #[index(id)]
1374 enum Owner {
1375 Human {
1376 #[shared(id)]
1377 id: uuid::Uuid,
1378 #[belongs_to(key = id)]
1379 human: toasty::Deferred<Human>,
1380 },
1381 Animal {
1382 #[shared(id)]
1383 id: uuid::Uuid,
1384 #[belongs_to(key = id)]
1385 animal: toasty::Deferred<Animal>,
1386 },
1387 }
1388
1389 #[derive(Debug, toasty::Model)]
1390 struct Object {
1391 #[key]
1392 #[auto]
1393 id: uuid::Uuid,
1394 owner: Owner,
1395 }
1396
1397 let mut db = test.setup_db(models!(Object, Human, Animal)).await;
1398
1399 let alice = toasty::create!(Human { name: "Alice" })
1400 .exec(&mut db)
1401 .await?;
1402 let bea = toasty::create!(Human { name: "Bea" }).exec(&mut db).await?;
1403
1404 let alice_obj = toasty::create!(Object {
1405 owner: Owner::Human { human: &alice }
1406 })
1407 .exec(&mut db)
1408 .await?;
1409 let bea_obj = toasty::create!(Object {
1410 owner: Owner::Human { human: &bea }
1411 })
1412 .exec(&mut db)
1413 .await?;
1414 toasty::create!(Object {
1417 owner: Owner::Animal {
1418 id: alice.id,
1419 animal: toasty::Deferred::default(),
1420 }
1421 })
1422 .exec(&mut db)
1423 .await?;
1424
1425 let found: Vec<Object> = Object::filter(
1427 Object::fields()
1428 .owner()
1429 .human()
1430 .matches(|v| v.human().eq(&alice)),
1431 )
1432 .exec(&mut db)
1433 .await?;
1434 assert_eq!(found.len(), 1);
1435 assert_eq!(found[0].id, alice_obj.id);
1436
1437 let found: Vec<Object> = Object::filter(
1439 Object::fields()
1440 .owner()
1441 .human()
1442 .matches(|v| v.id().eq(alice.id)),
1443 )
1444 .exec(&mut db)
1445 .await?;
1446 assert_eq!(found.len(), 1);
1447 assert_eq!(found[0].id, alice_obj.id);
1448
1449 let found: Vec<Object> = Object::filter(
1452 Object::fields()
1453 .owner()
1454 .human()
1455 .matches(|v| v.human().eq(&bea)),
1456 )
1457 .exec(&mut db)
1458 .await?;
1459 assert_eq!(found.len(), 1);
1460 assert_eq!(found[0].id, bea_obj.id);
1461
1462 let found: Vec<Object> = Object::filter(
1464 Object::fields()
1465 .owner()
1466 .human()
1467 .matches(|v| v.human().name().eq("Alice")),
1468 )
1469 .exec(&mut db)
1470 .await?;
1471 assert_eq!(found.len(), 1);
1472 assert_eq!(found[0].id, alice_obj.id);
1473
1474 let found: Vec<Object> = Object::filter(
1476 Object::fields()
1477 .owner()
1478 .human()
1479 .matches(|v| v.human().in_query(Human::filter_by_name("Alice"))),
1480 )
1481 .exec(&mut db)
1482 .await?;
1483 assert_eq!(found.len(), 1);
1484 assert_eq!(found[0].id, alice_obj.id);
1485
1486 Ok(())
1487}
1488
1489#[driver_test]
1494pub async fn filter_enum_embed_relation_in_list(test: &mut Test) -> Result<()> {
1495 #[derive(Debug, toasty::Model)]
1496 struct Human {
1497 #[key]
1498 #[auto]
1499 id: uuid::Uuid,
1500 name: String,
1501 }
1502
1503 #[derive(Debug, toasty::Model)]
1504 struct Animal {
1505 #[key]
1506 #[auto]
1507 id: uuid::Uuid,
1508 name: String,
1509 }
1510
1511 #[derive(Debug, toasty::Embed)]
1512 #[index(id)]
1513 enum Owner {
1514 Human {
1515 #[shared(id)]
1516 id: uuid::Uuid,
1517 #[belongs_to(key = id)]
1518 human: toasty::Deferred<Human>,
1519 },
1520 Animal {
1521 #[shared(id)]
1522 id: uuid::Uuid,
1523 #[belongs_to(key = id)]
1524 animal: toasty::Deferred<Animal>,
1525 },
1526 }
1527
1528 #[derive(Debug, toasty::Model)]
1529 struct Object {
1530 #[key]
1531 #[auto]
1532 id: uuid::Uuid,
1533 owner: Owner,
1534 }
1535
1536 let mut db = test.setup_db(models!(Object, Human, Animal)).await;
1537
1538 let alice = toasty::create!(Human { name: "Alice" })
1539 .exec(&mut db)
1540 .await?;
1541 let bea = toasty::create!(Human { name: "Bea" }).exec(&mut db).await?;
1542 let cid = toasty::create!(Human { name: "Cid" }).exec(&mut db).await?;
1543
1544 let alice_obj = toasty::create!(Object {
1545 owner: Owner::Human { human: &alice }
1546 })
1547 .exec(&mut db)
1548 .await?;
1549 let bea_obj = toasty::create!(Object {
1550 owner: Owner::Human { human: &bea }
1551 })
1552 .exec(&mut db)
1553 .await?;
1554 toasty::create!(Object {
1555 owner: Owner::Human { human: &cid }
1556 })
1557 .exec(&mut db)
1558 .await?;
1559 toasty::create!(Object {
1561 owner: Owner::Animal {
1562 id: alice.id,
1563 animal: toasty::Deferred::default(),
1564 }
1565 })
1566 .exec(&mut db)
1567 .await?;
1568
1569 let mut found: Vec<Object> = Object::filter(
1570 Object::fields()
1571 .owner()
1572 .human()
1573 .matches(|v| toasty::stmt::Expr::in_list(v.human(), [&alice, &bea])),
1574 )
1575 .exec(&mut db)
1576 .await?;
1577 found.sort_by_key(|o| o.id);
1578
1579 let mut expected = [alice_obj.id, bea_obj.id];
1580 expected.sort();
1581 assert_struct!(found, [{ id: == expected[0] }, { id: == expected[1] }]);
1582
1583 Ok(())
1584}
1585
1586#[driver_test]
1589pub async fn filter_struct_embed_relation_in_list(test: &mut Test) -> Result<()> {
1590 #[derive(Debug, toasty::Model)]
1591 struct Author {
1592 #[key]
1593 #[auto]
1594 id: uuid::Uuid,
1595 name: String,
1596 }
1597
1598 #[derive(Debug, toasty::Embed)]
1599 struct Attribution {
1600 #[index]
1601 author_id: uuid::Uuid,
1602 #[belongs_to(key = author_id)]
1603 author: toasty::Deferred<Author>,
1604 }
1605
1606 #[derive(Debug, toasty::Model)]
1607 struct Post {
1608 #[key]
1609 #[auto]
1610 id: uuid::Uuid,
1611 attribution: Attribution,
1612 }
1613
1614 let mut db = test.setup_db(models!(Post, Author)).await;
1615
1616 let alice = toasty::create!(Author { name: "Alice" })
1617 .exec(&mut db)
1618 .await?;
1619 let bea = toasty::create!(Author { name: "Bea" })
1620 .exec(&mut db)
1621 .await?;
1622 let cid = toasty::create!(Author { name: "Cid" })
1623 .exec(&mut db)
1624 .await?;
1625
1626 let alice_post = toasty::create!(Post {
1627 attribution: Attribution {
1628 author_id: alice.id,
1629 author: toasty::Deferred::default(),
1630 }
1631 })
1632 .exec(&mut db)
1633 .await?;
1634 let bea_post = toasty::create!(Post {
1635 attribution: Attribution {
1636 author_id: bea.id,
1637 author: toasty::Deferred::default(),
1638 }
1639 })
1640 .exec(&mut db)
1641 .await?;
1642 toasty::create!(Post {
1643 attribution: Attribution {
1644 author_id: cid.id,
1645 author: toasty::Deferred::default(),
1646 }
1647 })
1648 .exec(&mut db)
1649 .await?;
1650
1651 let mut found: Vec<Post> = Post::filter(toasty::stmt::Expr::in_list(
1652 Post::fields().attribution().author(),
1653 [&alice, &bea],
1654 ))
1655 .exec(&mut db)
1656 .await?;
1657 found.sort_by_key(|p| p.id);
1658
1659 let mut expected = [alice_post.id, bea_post.id];
1660 expected.sort();
1661 assert_struct!(found, [{ id: == expected[0] }, { id: == expected[1] }]);
1662
1663 Ok(())
1664}
1665
1666#[driver_test]
1670pub async fn filter_struct_embed_relation_by_model_value(test: &mut Test) -> Result<()> {
1671 #[derive(Debug, toasty::Model)]
1672 struct Author {
1673 #[key]
1674 #[auto]
1675 id: uuid::Uuid,
1676 name: String,
1677 }
1678
1679 #[derive(Debug, toasty::Embed)]
1680 struct Attribution {
1681 #[index]
1682 author_id: uuid::Uuid,
1683 #[belongs_to(key = author_id)]
1684 author: toasty::Deferred<Author>,
1685 }
1686
1687 #[derive(Debug, toasty::Model)]
1688 struct Post {
1689 #[key]
1690 #[auto]
1691 id: uuid::Uuid,
1692 attribution: Attribution,
1693 }
1694
1695 let mut db = test.setup_db(models!(Post, Author)).await;
1696
1697 let ann = toasty::create!(Author { name: "Ann" })
1698 .exec(&mut db)
1699 .await?;
1700 let bea = toasty::create!(Author { name: "Bea" })
1701 .exec(&mut db)
1702 .await?;
1703
1704 let ann_post = toasty::create!(Post {
1705 attribution: Attribution {
1706 author_id: ann.id,
1707 author: toasty::Deferred::default(),
1708 }
1709 })
1710 .exec(&mut db)
1711 .await?;
1712 let bea_post = toasty::create!(Post {
1713 attribution: Attribution {
1714 author_id: bea.id,
1715 author: toasty::Deferred::default(),
1716 }
1717 })
1718 .exec(&mut db)
1719 .await?;
1720
1721 let found: Vec<Post> = Post::filter(Post::fields().attribution().author().eq(&ann))
1722 .exec(&mut db)
1723 .await?;
1724 assert_eq!(found.len(), 1);
1725 assert_eq!(found[0].id, ann_post.id);
1726
1727 let found: Vec<Post> = Post::filter(Post::fields().attribution().author().name().eq("Bea"))
1728 .exec(&mut db)
1729 .await?;
1730 assert_eq!(found.len(), 1);
1731 assert_eq!(found[0].id, bea_post.id);
1732
1733 Ok(())
1734}
1735
1736#[driver_test]
1741pub async fn compare_embedded_relation_to_embedded_relation(test: &mut Test) -> Result<()> {
1742 #[derive(Debug, toasty::Model)]
1743 struct Author {
1744 #[key]
1745 #[auto]
1746 id: uuid::Uuid,
1747 name: String,
1748 }
1749
1750 #[derive(Debug, toasty::Embed)]
1751 struct Attribution {
1752 #[index]
1753 author_id: uuid::Uuid,
1754 #[belongs_to(key = author_id)]
1755 author: toasty::Deferred<Author>,
1756 }
1757
1758 #[derive(Debug, toasty::Embed)]
1759 struct Review {
1760 #[index]
1761 reviewer_id: uuid::Uuid,
1762 #[belongs_to(key = reviewer_id)]
1763 reviewer: toasty::Deferred<Author>,
1764 }
1765
1766 #[derive(Debug, toasty::Model)]
1767 struct Post {
1768 #[key]
1769 #[auto]
1770 id: uuid::Uuid,
1771 attribution: Attribution,
1772 review: Review,
1773 }
1774
1775 let mut db = test.setup_db(models!(Post, Author)).await;
1776
1777 let ann = toasty::create!(Author { name: "Ann" })
1778 .exec(&mut db)
1779 .await?;
1780 let bea = toasty::create!(Author { name: "Bea" })
1781 .exec(&mut db)
1782 .await?;
1783
1784 let self_reviewed = toasty::create!(Post {
1786 attribution: Attribution {
1787 author_id: ann.id,
1788 author: toasty::Deferred::default(),
1789 },
1790 review: Review {
1791 reviewer_id: ann.id,
1792 reviewer: toasty::Deferred::default(),
1793 }
1794 })
1795 .exec(&mut db)
1796 .await?;
1797 toasty::create!(Post {
1798 attribution: Attribution {
1799 author_id: bea.id,
1800 author: toasty::Deferred::default(),
1801 },
1802 review: Review {
1803 reviewer_id: ann.id,
1804 reviewer: toasty::Deferred::default(),
1805 }
1806 })
1807 .exec(&mut db)
1808 .await?;
1809
1810 let found: Vec<Post> = Post::filter(
1811 Post::fields()
1812 .attribution()
1813 .author()
1814 .eq(Post::fields().review().reviewer()),
1815 )
1816 .exec(&mut db)
1817 .await?;
1818 assert_eq!(found.len(), 1);
1819 assert_eq!(found[0].id, self_reviewed.id);
1820
1821 Ok(())
1822}
1823
1824#[driver_test]
1825pub async fn nested_loaded_relation_with_private_composite_key(test: &mut Test) -> Result<()> {
1826 mod target {
1827 #[derive(Debug, toasty::Model)]
1828 #[key(partition = namespace, local = revision)]
1829 pub struct Parent {
1830 namespace: uuid::Uuid,
1831 revision: i64,
1832 payload: toasty::Deferred<String>,
1833 }
1834 }
1835 use target::Parent;
1836
1837 #[derive(Debug, toasty::Embed)]
1838 struct Attribution {
1839 revision: i64,
1840 #[index]
1841 namespace: uuid::Uuid,
1842 #[belongs_to(key = [namespace, revision], references = [namespace, revision])]
1843 parent: toasty::Deferred<Parent>,
1844 }
1845
1846 #[derive(Debug, toasty::Embed)]
1847 enum Envelope {
1848 Other { label: String },
1849 Parent { attribution: Attribution },
1850 }
1851
1852 #[derive(Debug, toasty::Model)]
1853 struct Object {
1854 #[key]
1855 #[auto]
1856 id: uuid::Uuid,
1857 envelope: Envelope,
1858 }
1859
1860 let mut db = test.setup_db(models!(Object, Parent)).await;
1861 let namespace = uuid::Uuid::new_v4();
1862 toasty::create!(Parent {
1863 namespace,
1864 revision: 7,
1865 payload: "unloaded payload"
1866 })
1867 .exec(&mut db)
1868 .await?;
1869 let parent = Parent::find_by_primary_key((namespace, 7_i64).into_expr())
1870 .get(&mut db)
1871 .await?;
1872 let mut object = toasty::create!(Object {
1873 envelope: Envelope::Parent {
1874 attribution: Attribution {
1875 revision: 0,
1876 namespace: uuid::Uuid::nil(),
1877 parent: toasty::Deferred::from(parent),
1878 }
1879 }
1880 })
1881 .exec(&mut db)
1882 .await?;
1883 assert_struct!(object.envelope, Envelope::Parent {
1884 attribution: Attribution { namespace: == namespace, revision: 7, .. }
1885 });
1886
1887 toasty::create!(Parent {
1888 namespace,
1889 revision: 9,
1890 payload: "unloaded payload"
1891 })
1892 .exec(&mut db)
1893 .await?;
1894 let parent = Parent::find_by_primary_key((namespace, 9_i64).into_expr())
1895 .get(&mut db)
1896 .await?;
1897 object
1898 .update()
1899 .envelope(Envelope::Parent {
1900 attribution: Attribution {
1901 revision: 0,
1902 namespace: uuid::Uuid::nil(),
1903 parent: toasty::Deferred::from(parent),
1904 },
1905 })
1906 .exec(&mut db)
1907 .await?;
1908 assert_struct!(object.envelope, Envelope::Parent {
1909 attribution: Attribution { namespace: == namespace, revision: 9, .. }
1910 });
1911 let stored = Object::get_by_id(&mut db, object.id).await?;
1912 assert_struct!(stored.envelope, Envelope::Parent {
1913 attribution: Attribution { namespace: == namespace, revision: 9, .. }
1914 });
1915 Ok(())
1916}
1917
1918#[driver_test]
1919pub async fn embedded_relation_plans_nested_insert(test: &mut Test) -> Result<()> {
1920 #[derive(Debug, toasty::Model)]
1921 struct User {
1922 #[key]
1923 #[auto]
1924 id: uuid::Uuid,
1925 name: String,
1926 }
1927
1928 #[derive(Debug, toasty::Embed)]
1929 struct Owner {
1930 #[index]
1931 user_id: uuid::Uuid,
1932 #[belongs_to(key = user_id)]
1933 user: toasty::Deferred<User>,
1934 }
1935
1936 #[derive(Debug, toasty::Model)]
1937 struct Object {
1938 #[key]
1939 #[auto]
1940 id: uuid::Uuid,
1941 owner: Owner,
1942 }
1943
1944 fn owner(name: &str) -> toasty::stmt::Expr<Owner> {
1945 let user: toasty::stmt::Expr<User> = toasty::create!(User { name }).into_expr();
1946 toasty::stmt::Expr::from_untyped(toasty_core::stmt::Expr::record([
1947 toasty_core::stmt::Expr::null(),
1948 user.into(),
1949 ]))
1950 }
1951
1952 let mut db = test.setup_db(models!(Object, User)).await;
1953 let mut object = toasty::create!(Object {
1954 owner: owner("first")
1955 })
1956 .exec(&mut db)
1957 .await?;
1958 let first_id = object.owner.user_id;
1959 assert_eq!(User::get_by_id(&mut db, first_id).await?.name, "first");
1960
1961 object.update().owner(owner("second")).exec(&mut db).await?;
1962 let stored = Object::get_by_id(&mut db, object.id).await?;
1963 assert_ne!(stored.owner.user_id, first_id);
1964 assert_eq!(
1965 User::get_by_id(&mut db, stored.owner.user_id).await?.name,
1966 "second"
1967 );
1968 assert_eq!(User::all().exec(&mut db).await?.len(), 2);
1969 Ok(())
1970}
1971
1972#[driver_test(requires(scan))]
1973pub async fn filter_whole_struct_loaded_relation(test: &mut Test) -> Result<()> {
1974 #[derive(Debug, toasty::Model)]
1975 #[key(partition = namespace, local = revision)]
1976 struct Author {
1977 namespace: uuid::Uuid,
1978 revision: i64,
1979 }
1980
1981 #[derive(Debug, toasty::Embed)]
1982 struct Attribution {
1983 revision: i64,
1984 namespace: uuid::Uuid,
1985 #[belongs_to(key = [namespace, revision], references = [namespace, revision])]
1986 author: toasty::Deferred<Author>,
1987 label: String,
1988 }
1989
1990 #[derive(Debug, toasty::Model)]
1991 struct Post {
1992 #[key]
1993 #[auto]
1994 id: uuid::Uuid,
1995 attribution: Attribution,
1996 }
1997
1998 let mut db = test.setup_db(models!(Post, Author)).await;
1999 let namespace = uuid::Uuid::new_v4();
2000 let alice = toasty::create!(Author {
2001 namespace,
2002 revision: 1
2003 })
2004 .exec(&mut db)
2005 .await?;
2006 let bea = toasty::create!(Author {
2007 namespace,
2008 revision: 2
2009 })
2010 .exec(&mut db)
2011 .await?;
2012 let cid = toasty::create!(Author {
2013 namespace: uuid::Uuid::new_v4(),
2014 revision: 1
2015 })
2016 .exec(&mut db)
2017 .await?;
2018 let mut post_ids = Vec::new();
2019 for (author, label) in [
2020 (&alice, "written"),
2021 (&bea, "written"),
2022 (&cid, "written"),
2023 (&alice, "reviewed"),
2024 ] {
2025 post_ids.push(
2026 toasty::create!(Post {
2027 attribution: Attribution {
2028 revision: author.revision,
2029 namespace: author.namespace,
2030 author: toasty::Deferred::default(),
2031 label: label.to_string(),
2032 },
2033 })
2034 .exec(&mut db)
2035 .await?
2036 .id,
2037 );
2038 }
2039 let alice_attribution = Attribution {
2040 revision: 0,
2041 namespace: uuid::Uuid::nil(),
2042 author: toasty::Deferred::from(alice),
2043 label: "written".to_string(),
2044 };
2045 let bea_attribution = Attribution {
2046 revision: 0,
2047 namespace: uuid::Uuid::nil(),
2048 author: toasty::Deferred::from(bea),
2049 label: "written".to_string(),
2050 };
2051 for (filter, mut expected) in [
2052 (
2053 Post::fields().attribution().eq(&alice_attribution),
2054 vec![post_ids[0]],
2055 ),
2056 (
2057 (&alice_attribution)
2058 .into_expr()
2059 .eq(Post::fields().attribution()),
2060 vec![post_ids[0]],
2061 ),
2062 (
2063 Post::fields().attribution().ne(&alice_attribution),
2064 post_ids[1..].to_vec(),
2065 ),
2066 (
2067 toasty::stmt::Expr::in_list(
2068 Post::fields().attribution(),
2069 [&alice_attribution, &bea_attribution],
2070 ),
2071 vec![post_ids[0], post_ids[1]],
2072 ),
2073 ] {
2074 let found: Vec<Post> = Post::filter(filter).exec(&mut db).await?;
2075 let mut actual: Vec<_> = found.into_iter().map(|post| post.id).collect();
2076 actual.sort();
2077 expected.sort();
2078 assert_eq!(actual, expected);
2079 }
2080 Ok(())
2081}
2082
2083#[driver_test(requires(scan))]
2084pub async fn filter_whole_enum_loaded_relation(test: &mut Test) -> Result<()> {
2085 #[derive(Debug, toasty::Model)]
2086 struct Author {
2087 #[key]
2088 #[auto]
2089 id: uuid::Uuid,
2090 }
2091
2092 #[derive(Debug, toasty::Embed)]
2093 enum Owner {
2094 Other {
2095 id: uuid::Uuid,
2096 },
2097 Author {
2098 author_id: uuid::Uuid,
2099 #[belongs_to(key = author_id)]
2100 author: toasty::Deferred<Author>,
2101 },
2102 Nobody,
2103 }
2104
2105 #[derive(Debug, toasty::Model)]
2106 struct Post {
2107 #[key]
2108 #[auto]
2109 id: uuid::Uuid,
2110 owner: Owner,
2111 }
2112
2113 let mut db = test.setup_db(models!(Post, Author)).await;
2114 let alice = toasty::create!(Author {}).exec(&mut db).await?;
2115 let bea = toasty::create!(Author {}).exec(&mut db).await?;
2116 let mut post_ids = Vec::new();
2117 for owner in [
2118 Owner::Author {
2119 author_id: alice.id,
2120 author: toasty::Deferred::default(),
2121 },
2122 Owner::Author {
2123 author_id: bea.id,
2124 author: toasty::Deferred::default(),
2125 },
2126 Owner::Other { id: alice.id },
2127 Owner::Nobody,
2128 ] {
2129 post_ids.push(toasty::create!(Post { owner }).exec(&mut db).await?.id);
2130 }
2131 let alice_owner = Owner::Author {
2132 author_id: uuid::Uuid::nil(),
2133 author: toasty::Deferred::from(alice),
2134 };
2135 let bea_owner = Owner::Author {
2136 author_id: uuid::Uuid::nil(),
2137 author: toasty::Deferred::from(bea),
2138 };
2139 for (filter, mut expected) in [
2140 (Post::fields().owner().eq(&alice_owner), vec![post_ids[0]]),
2141 (
2142 (&alice_owner).into_expr().eq(Post::fields().owner()),
2143 vec![post_ids[0]],
2144 ),
2145 (
2146 Post::fields().owner().ne(&alice_owner),
2147 post_ids[1..].to_vec(),
2148 ),
2149 (
2150 toasty::stmt::Expr::in_list(Post::fields().owner(), [&alice_owner, &bea_owner]),
2151 vec![post_ids[0], post_ids[1]],
2152 ),
2153 ] {
2154 let found: Vec<Post> = Post::filter(filter).exec(&mut db).await?;
2155 let mut actual: Vec<_> = found.into_iter().map(|post| post.id).collect();
2156 actual.sort();
2157 expected.sort();
2158 assert_eq!(actual, expected);
2159 }
2160 Ok(())
2161}