Skip to main content

toasty_driver_integration_suite/tests/
embed_relation.rs

1use crate::prelude::*;
2use toasty::stmt::IntoExpr;
3
4/// Comparing two relations that live in enum variants requires *both*
5/// variants. Every variant here stores its key in the same shared column,
6/// so without both checks a row holding `Other` on either side would match
7/// on key equality alone. `ne` keeps both checks as well, and negating an
8/// equality negates the guarded comparison as a whole.
9#[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    // Eight rows: every combination of variant on each side, with the two
55    // keys equal or not. Only the row with Primary on both sides and equal
56    // keys satisfies the equality; only the Primary/Primary row with
57    // different keys satisfies the inequality.
58    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        // Relation against relation, in both directions.
106        (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        // The key fields themselves.
111        (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        // A path converted to an expression first.
116        (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    // Negating the equality negates the whole guarded comparison: every row
125    // but the equal Primary/Primary one.
126    let found = Object::filter(lhs().human().eq(rhs().human()).not())
127        .exec(&mut db)
128        .await?;
129    assert_eq!(found.len(), 7);
130
131    // A single-side predicate on a shared column still requires its variant.
132    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/// A predicate assembled from the statement AST, rather than through the
145/// typed constructors, requires the variant it selects all the same: the
146/// engine attaches the check when it normalizes the statement.
147#[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    // Both rows store `true` in the shared column; only the Primary row
184    // satisfies a predicate on the Primary variant's field.
185    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
195/// A composite-key relation inside a variant of an enum that is itself a
196/// variant field of an outer enum, shared by the nested-variant tests.
197mod 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    /// The rows the tests query. `Second { other_owner }` stores the same
235    /// keys in other columns; it must never match a predicate through
236    /// `first().owner()`. `parents` are `first`, `second` (same namespace),
237    /// and `third` (another namespace). `objects` are the `First` objects
238    /// owned by each of them, in the same order.
239    pub struct Fixture {
240        pub parents: [Parent; 3],
241        pub objects: [Object; 3],
242    }
243
244    /// The nested literal is written out with its keys: `create!` fills
245    /// keys from a parent value only for the outer variant literal.
246    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/// Every predicate through the nested path requires both variants, whether
310/// the comparison is at the relation, a path converted through `IntoExpr`,
311/// or a check of the inner variant.
312#[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    // A model value against the relation, and the same through a path
322    // converted with `IntoExpr`.
323    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    // The inner variant check on its own requires the outer variant too.
337    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    // Replacing the nested owner writes the new keys through both variants.
347    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/// Traversal into the target of the nested composite-key relation. A filter
366/// through a composite foreign key lifts to a tuple `IN` subquery, which
367/// only the SQL backends evaluate.
368#[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/// The polymorphic-owner shape: `#[belongs_to]` fields inside embedded enum
413/// variants, exercised through the full CRUD cycle. The relation fields map
414/// to no columns — the discriminant and the key fields own the storage.
415/// Creating supplies the variant value with explicit keys, `match` reads the
416/// stored keys back, the owner loads with an ordinary `get_by_*`, and
417/// changing the owner — including its kind — is a whole-value replacement of
418/// the embed.
419#[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    // The relation fields contribute no columns: discriminant + one column
466    // per key field.
467    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    // Create with explicit keys; the relation stays unloaded.
482    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    // `match` gives direct access to the stored keys; the owner loads with an
500    // ordinary lookup.
501    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    // Changing the owner — including its kind — is a whole-value replacement
522    // of the embed.
523    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/// Key fields of relation-carrying variants stay queryable through the
552/// existing variant filter paths — the variant closure gates on the
553/// discriminant and compares the shared key column — and stay consistent
554/// as rows are re-pointed and deleted.
555#[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    // `Human` and `Animal` share one key column; `#[index(id)]` indexes it
574    // once for both.
575    #[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    // One shared key column, indexed by the enum-level attribute.
603    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    // An animal holding the same UUID as Alice, to prove the variant gate.
616    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    // Variant-gated key filter: only the Human row matches, even though the
638    // Animal row stores its key in the same column.
639    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    // The discriminant filter alone works as before.
650    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    // Re-point the relation within the same variant by replacing the embed
657    // value; the key filter follows.
658    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    // The relation accessor reaches the same row by model value.
678    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    // Deleting the row empties the key filter; the Animal row is untouched.
690    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/// `#[belongs_to]` inside an embedded struct: same storage rule — the key
706/// field owns the column, the relation maps to nothing — through the full
707/// CRUD cycle.
708#[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    // The key field stays queryable through the embed path.
762    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    // And assignable through the embed update builder.
773    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/// An `Option<Owner>` field: an ownerless row stores NULL in the discriminant
800/// column, per existing optional-embed support, and updates move rows in and
801/// out of ownership.
802#[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    // Assign an owner to the ownerless row and clear the owned row.
858    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/// Setting an embedded relation from a parent model value. In `create!` and
878/// `update!`, a variant literal passes the parent by reference and the key
879/// field fills from the parent's referenced field — including a non-primary
880/// `references = serial` key. In a plain (complete) literal, a loaded
881/// relation value fills the key the same way, winning over the explicitly
882/// written key.
883#[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    // `create!` with the parent by reference; no explicit key.
940    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    // A non-primary `references` key fills from the referenced field, not
949    // the parent's primary key.
950    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    // `update!` changes the owner — including its kind — through the same
959    // sugar, on an instance and on a query target.
960    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    // A loaded relation value in a complete literal fills the key too; the
981    // written-out key loses to the parent value.
982    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/// A variant literal written into an `Option<Enum>` field: the builder
1003/// converts to the optional expression, so no `Some(..)` wrapper is needed
1004/// in `create!` or `update!`.
1005#[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/// A variant literal inside an embedded partial patch replaces the nested
1061/// enum field as a whole while the patch leaves the sibling field alone,
1062/// for both instance and query targets.
1063#[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/// A variant literal inside a has-many item literal reaches its builder
1139/// through the relation's list handle, in both `create!` and `update!`.
1140#[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/// The values written in a variant literal are evaluated before the update
1211/// target is borrowed, as a `match` scrutinee: a reference to a temporary
1212/// stays valid for the chain, and an inference-dependent value takes its
1213/// type from the monomorphic builder setter.
1214#[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    // A reference to a temporary.
1266    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    // Inference-dependent values, and one that reads the target.
1279    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/// A loaded relation value inside an embedded struct fills its key slot on
1300/// write. The engine resolves the referenced field from the parent expression.
1301#[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/// Filtering an embedded relation by model value. The comparison gates on
1350/// the variant's discriminant and compares the key column — a row of another
1351/// variant holding the same key in the shared column never matches — and
1352/// traversal into the target model lifts to a subquery behind the same gate.
1353#[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    // An Animal row holding *Alice's* uuid in the shared key column: the
1415    // proof that relation comparisons gate on the discriminant.
1416    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    // eq by model value.
1426    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    // The variant-scoped key path agrees with the model-value form.
1438    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    // Comparing against a different human matches only that human's row —
1450    // never the Animal row, whose shared key also differs from Bea's.
1451    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    // Traversal into the target model.
1463    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    // Subquery form.
1475    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/// List membership on a relation inside an embedded enum variant: the
1490/// relation resolves to its variant-scoped key column, so an Animal row
1491/// holding one of the listed humans' ids in the shared key column does not
1492/// match.
1493#[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    // An Animal row holding Alice's uuid in the shared key column.
1560    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/// List membership on a relation inside an embedded struct resolves to the
1587/// key column through the embed path.
1588#[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/// Filtering a relation inside an embedded struct by model value: no
1667/// discriminant exists, the comparison resolves to the key column through
1668/// the embed path, and traversal lifts to a subquery.
1669#[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/// Comparing one embedded relation to another substitutes the key on both
1737/// sides of the comparison. Rewriting only one side would leave the other
1738/// as the relation's storage-less record slot, which lowers to `Null` and
1739/// matches nothing.
1740#[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    // Ann reviewed her own post; Ann also reviewed Bea's post.
1785    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}