1#![deny(missing_docs)]
2use std::marker::PhantomData;
5use std::sync::Arc;
6
7use type_bridge_contract::id::{TypeId, is_canonical_thing_iid};
8use type_bridge_orm::_descriptor::{EntityDescriptor, RelationDescriptor};
9use type_bridge_orm::_manager::{DynamicEntityManager, DynamicRelationManager};
10use type_bridge_orm::_registry::DescriptorRegistry;
11use type_bridge_orm::session::backend::TxType;
12use type_bridge_orm::session::context::TransactionContext;
13use type_bridge_orm::{
14 AnswerCancellation, DynamicAttributeMap, DynamicRolePlayerInput, InstalledRuntimeProjection,
15 ProjectedBatchOperation, ProjectedCrudExecutor, QueryExecutionResourceLimits,
16};
17
18use crate::__codegen::{CompleteModel, EntityModel, HydrationCapability, RelationModel};
19use crate::entity_codec::{
20 hydrate_entity, lower_entity_create, map_validation_error, resolve_entity_authority,
21};
22use crate::entity_manager::rehydrate_written_entity;
23use crate::error::{Error, ModelValidationPhase};
24use crate::hooks::{CrudOperation, ModelKind};
25use crate::projected_batch::{
26 create_rows, delete_rows, execute_borrowed_delete, execute_borrowed_things, prepare_batch,
27 update_rows, uses_successor_batch_runtime, validate_binding_row_count,
28};
29use crate::projected_codec::{materialize_projected, project_create};
30use crate::relation_codec::{hydrate_relation, lower_relation_create, resolve_relation_authority};
31use crate::relation_manager::rehydrate_written_relation;
32use crate::schema::Schema;
33use crate::{Database, Result};
34
35#[cfg(test)]
36mod tests;
37
38fn invalid_iid() -> Error {
39 Error::model_validation(
40 ModelValidationPhase::Input,
41 "invalid_iid",
42 vec!["iid".into()],
43 "IID is not canonical",
44 None,
45 )
46}
47
48fn schema_not_bound() -> Error {
49 Error::model_validation(
50 ModelValidationPhase::Input,
51 "schema_not_bound",
52 vec![],
53 "database is not schema-bound",
54 None,
55 )
56}
57
58pub struct ReadTransaction<'db, S: Schema> {
64 tx: TransactionContext,
65 db: &'db Database<S>,
66 installed: Arc<InstalledRuntimeProjection>,
67 registry: Arc<DescriptorRegistry>,
68}
69
70impl<S: Schema> std::fmt::Debug for ReadTransaction<'_, S> {
71 fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
72 formatter
73 .debug_struct("ReadTransaction")
74 .field("database", &self.db.database_name())
75 .finish_non_exhaustive()
76 }
77}
78
79impl<'db, S: Schema> ReadTransaction<'db, S> {
80 pub(crate) async fn open(db: &'db Database<S>) -> Result<Self> {
81 let installed = Arc::clone(db.installed_schema().ok_or_else(schema_not_bound)?);
82 let registry = Arc::clone(db.match_registry().ok_or_else(schema_not_bound)?);
83 let tx = db
84 .inner_orm()
85 .transaction_context(TxType::Read)
86 .await
87 .map_err(Error::from_orm)?;
88 Ok(Self {
89 tx,
90 db,
91 installed,
92 registry,
93 })
94 }
95
96 pub fn entities<M>(&self) -> crate::projected_filter::ReadEntityManager<'_, S, M>
99 where
100 M: crate::__codegen::EntityModel<Schema = S> + crate::__codegen::CompleteModel,
101 {
102 crate::projected_filter::ReadEntityManager::new(&self.installed, &self.tx)
103 }
104
105 pub fn relations<M>(&self) -> crate::projected_filter::ReadRelationManager<'_, S, M>
108 where
109 M: crate::__codegen::RelationModel<Schema = S> + crate::__codegen::CompleteModel,
110 {
111 crate::projected_filter::ReadRelationManager::new(&self.installed, &self.tx)
112 }
113
114 #[must_use]
116 pub fn query(&self) -> crate::query::QuerySession<'_, S> {
117 self.query_with_resources(
118 QueryExecutionResourceLimits::default(),
119 AnswerCancellation::default(),
120 )
121 }
122
123 #[must_use]
126 pub fn query_with_resources(
127 &self,
128 resources: QueryExecutionResourceLimits,
129 cancellation: AnswerCancellation,
130 ) -> crate::query::QuerySession<'_, S> {
131 crate::query::QuerySession::borrowed(
132 &self.installed,
133 Arc::clone(&self.registry),
134 &self.tx,
135 self.db.operation_limits(resources),
136 cancellation,
137 )
138 }
139
140 pub async fn close(self) -> Result<()> {
142 self.tx.close().await.map_err(Error::from_orm)
143 }
144}
145
146pub struct WriteTransaction<'db, S: Schema> {
158 tx: TransactionContext,
159 db: &'db Database<S>,
160}
161
162impl<S: Schema> std::fmt::Debug for WriteTransaction<'_, S> {
163 fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
164 formatter
165 .debug_struct("WriteTransaction")
166 .field("database", &self.db.database_name())
167 .finish_non_exhaustive()
168 }
169}
170
171impl<'db, S: Schema> WriteTransaction<'db, S> {
172 pub(crate) async fn open(db: &'db Database<S>) -> Result<WriteTransaction<'db, S>> {
173 db.installed_schema().ok_or_else(schema_not_bound)?;
174 let tx = db
175 .inner_orm()
176 .transaction_context(TxType::Write)
177 .await
178 .map_err(Error::from_orm)?;
179 Ok(WriteTransaction { tx, db })
180 }
181
182 pub fn entities<M>(&self) -> TransactionEntityManager<'_, S, M>
184 where
185 M: EntityModel<Schema = S>,
186 {
187 TransactionEntityManager {
188 transaction: self,
189 marker: PhantomData,
190 }
191 }
192
193 pub fn relations<M>(&self) -> TransactionRelationManager<'_, S, M>
195 where
196 M: RelationModel<Schema = S>,
197 {
198 TransactionRelationManager {
199 transaction: self,
200 marker: PhantomData,
201 }
202 }
203
204 pub async fn commit(self) -> Result<()> {
206 if uses_successor_batch_runtime(self.installed()?) {
207 self.tx
208 .commit_sdk()
209 .await
210 .map_err(|error| Error::from_projected_batch(error, ModelValidationPhase::Input))
211 } else {
212 self.tx.commit().await.map_err(Error::from_orm)
213 }
214 }
215
216 pub async fn rollback(self) -> Result<()> {
218 self.tx.rollback().await.map_err(Error::from_orm)
219 }
220
221 fn installed(&self) -> Result<&InstalledRuntimeProjection> {
222 self.db
223 .installed_schema()
224 .map(Arc::as_ref)
225 .ok_or_else(schema_not_bound)
226 }
227}
228
229pub struct TransactionEntityManager<'t, S: Schema, M: EntityModel<Schema = S>> {
234 transaction: &'t WriteTransaction<'t, S>,
235 marker: PhantomData<M>,
236}
237
238impl<'t, S: Schema, M: EntityModel<Schema = S>> Copy for TransactionEntityManager<'t, S, M> {}
239impl<'t, S: Schema, M: EntityModel<Schema = S>> Clone for TransactionEntityManager<'t, S, M> {
240 fn clone(&self) -> Self {
241 *self
242 }
243}
244
245impl<S, M> TransactionEntityManager<'_, S, M>
246where
247 S: Schema,
248 M: EntityModel<Schema = S> + CompleteModel,
249{
250 fn exact(
251 &self,
252 ) -> Result<(
253 TypeId,
254 &InstalledRuntimeProjection,
255 DynamicEntityManager<'static>,
256 )> {
257 let installed = self.transaction.installed()?;
258 let (id, descriptor): (TypeId, EntityDescriptor) = resolve_entity_authority(
259 M::TYPE_ID_JSON,
260 installed,
261 ModelValidationPhase::Input,
262 true,
263 )?;
264 let manager = DynamicEntityManager::with_canonical_transaction(
265 self.transaction.tx.clone(),
266 Arc::new(descriptor),
267 );
268 Ok((id, installed, manager))
269 }
270
271 pub async fn insert(&self, input: M::Create) -> Result<M> {
274 let (id, installed, _manager) = self.exact()?;
275 let create = project_create(input, &id, installed)?;
276 let projected = ProjectedCrudExecutor::new(installed)
277 .insert_entity_in_transaction_with_compatibility(&self.transaction.tx, &create)
278 .await
279 .map_err(|error| {
280 Error::from_projected_crud(error, ModelKind::Entity, Some(CrudOperation::Insert))
281 })?;
282 materialize_projected(projected, installed)
283 }
284
285 pub async fn put(&self, input: M::Create) -> Result<M> {
289 let (id, installed, _manager) = self.exact()?;
290 let create = project_create(input, &id, installed)?;
291 let projected = ProjectedCrudExecutor::new(installed)
292 .put_entity_in_transaction_with_compatibility(&self.transaction.tx, &create)
293 .await
294 .map_err(|error| {
295 Error::from_projected_crud(error, ModelKind::Entity, Some(CrudOperation::Put))
296 })?;
297 materialize_projected(projected, installed)
298 }
299
300 pub async fn insert_many(&self, inputs: Vec<M::Create>) -> Result<Vec<M>> {
305 if uses_successor_batch_runtime(self.transaction.installed()?) {
306 validate_binding_row_count(inputs.len())?;
307 }
308 self.write_many(inputs, false).await
309 }
310
311 pub async fn put_many(&self, inputs: Vec<M::Create>) -> Result<Vec<M>> {
316 if uses_successor_batch_runtime(self.transaction.installed()?) {
317 validate_binding_row_count(inputs.len())?;
318 }
319 self.write_many(inputs, true).await
320 }
321
322 async fn write_many(&self, inputs: Vec<M::Create>, put: bool) -> Result<Vec<M>> {
323 let successor = uses_successor_batch_runtime(self.transaction.installed()?);
324 if inputs.is_empty() && !successor {
325 return Ok(Vec::new());
326 }
327 let (id, installed, manager) = self.exact()?;
328 if successor {
329 let rows = create_rows(installed, &id, inputs)?;
330 let operation = if put {
331 ProjectedBatchOperation::Put
332 } else {
333 ProjectedBatchOperation::Insert
334 };
335 let batch = prepare_batch(installed, id, operation, rows)?;
336 return execute_borrowed_things(&self.transaction.tx, installed, &batch).await;
337 }
338 let mut lowered = Vec::with_capacity(inputs.len());
339 for input in inputs {
340 lowered.push(lower_entity_create(input, &id, installed)?);
341 }
342 let iids = if put {
343 manager.put_many_exact(&lowered).await
344 } else {
345 manager.insert_many(&lowered).await
346 }
347 .map_err(Error::from_orm)?;
348 if iids.len() != lowered.len() {
349 return Err(Error::model_validation(
350 ModelValidationPhase::Hydration,
351 "iid_count_mismatch",
352 vec!["iid".into()],
353 "provider returned an unexpected IID count",
354 None,
355 ));
356 }
357 let mut out = Vec::with_capacity(iids.len());
358 for iid in iids {
359 out.push(rehydrate_written_entity(&manager, &iid, &id, installed).await?);
360 }
361 Ok(out)
362 }
363
364 pub async fn update(&self, iid: &str, input: M::Create) -> Result<M> {
367 if !is_canonical_thing_iid(iid) {
368 return Err(invalid_iid());
369 }
370 let (id, installed, _manager) = self.exact()?;
371 let create = project_create(input, &id, installed)?;
372 let projected = ProjectedCrudExecutor::new(installed)
373 .update_entity_in_transaction_with_compatibility(&self.transaction.tx, iid, &create)
374 .await
375 .map_err(|error| {
376 Error::from_projected_crud(error, ModelKind::Entity, Some(CrudOperation::Update))
377 })?;
378 materialize_projected(projected, installed)
379 }
380
381 pub async fn delete(&self, iid: &str) -> Result<()> {
384 if !is_canonical_thing_iid(iid) {
385 return Err(invalid_iid());
386 }
387 let (id, installed, _manager) = self.exact()?;
388 ProjectedCrudExecutor::new(installed)
389 .delete_entity_by_iid_in_transaction_with_compatibility(&self.transaction.tx, &id, iid)
390 .await
391 .map_err(|error| {
392 Error::from_projected_crud(error, ModelKind::Entity, Some(CrudOperation::Delete))
393 })
394 }
395
396 pub async fn update_many(&self, inputs: Vec<(String, M::Create)>) -> Result<Vec<M>> {
400 let successor = uses_successor_batch_runtime(self.transaction.installed()?);
401 if successor {
402 validate_binding_row_count(inputs.len())?;
403 }
404 if inputs.is_empty() && !successor {
405 return Ok(Vec::new());
406 }
407 if successor {
408 let (id, installed, _manager) = self.exact()?;
409 let rows = update_rows(installed, &id, inputs)?;
410 let batch = prepare_batch(installed, id, ProjectedBatchOperation::Update, rows)?;
411 return execute_borrowed_things(&self.transaction.tx, installed, &batch).await;
412 }
413 if inputs.iter().any(|(iid, _)| !is_canonical_thing_iid(iid)) {
414 return Err(invalid_iid());
415 }
416 let (id, installed, manager) = self.exact()?;
417
418 let mut prepared = Vec::with_capacity(inputs.len());
419 for (iid, input) in inputs {
420 prepared.push((iid, lower_entity_create(input, &id, installed)?));
421 }
422 let mut output = Vec::with_capacity(prepared.len());
423 for (iid, attributes) in prepared {
424 manager
425 .update_exact(&iid, &attributes)
426 .await
427 .map_err(Error::from_orm)?;
428 output.push(rehydrate_written_entity(&manager, &iid, &id, installed).await?);
429 }
430 Ok(output)
431 }
432
433 pub async fn delete_many(&self, iids: &[String]) -> Result<()> {
437 let successor = uses_successor_batch_runtime(self.transaction.installed()?);
438 if successor {
439 validate_binding_row_count(iids.len())?;
440 }
441 if iids.is_empty() && !successor {
442 return Ok(());
443 }
444 if successor {
445 let (id, installed, _manager) = self.exact()?;
446 let batch = prepare_batch(
447 installed,
448 id,
449 ProjectedBatchOperation::Delete,
450 delete_rows(iids)?,
451 )?;
452 return execute_borrowed_delete(&self.transaction.tx, installed, &batch).await;
453 }
454 if iids.iter().any(|iid| !is_canonical_thing_iid(iid)) {
455 return Err(invalid_iid());
456 }
457 let (_id, _installed, manager) = self.exact()?;
458 for iid in iids {
459 manager
460 .delete_by_iid_exact(iid)
461 .await
462 .map_err(Error::from_orm)?;
463 }
464 Ok(())
465 }
466
467 pub async fn get_by_iid(&self, iid: &str) -> Result<Option<M>> {
470 if !is_canonical_thing_iid(iid) {
471 return Err(invalid_iid());
472 }
473 let (id, installed, _manager) = self.exact()?;
474 ProjectedCrudExecutor::new(installed)
475 .get_entity_by_iid_in_transaction_with_compatibility(&self.transaction.tx, &id, iid)
476 .await
477 .map_err(|error| Error::from_projected_crud(error, ModelKind::Entity, None))?
478 .map(|projected| materialize_projected(projected, installed))
479 .transpose()
480 }
481
482 pub async fn all(&self) -> Result<Vec<M>> {
485 let (id, installed, manager) = self.exact()?;
486 let rows = manager.all_exact().await.map_err(Error::from_orm)?;
487 rows.into_iter()
488 .map(|row| {
489 let hydrated = hydrate_entity(row, &id, installed)?;
490 M::materialize(&hydrated, &HydrationCapability::new())
491 .map_err(|error| map_validation_error(error, ModelValidationPhase::Hydration))
492 })
493 .collect()
494 }
495
496 pub async fn count(&self) -> Result<u64> {
498 let (id, installed, _manager) = self.exact()?;
499 ProjectedCrudExecutor::new(installed)
500 .count_entities_in_transaction_with_compatibility(&self.transaction.tx, &id)
501 .await
502 .map_err(|error| Error::from_projected_crud(error, ModelKind::Entity, None))
503 }
504}
505
506pub struct TransactionRelationManager<'t, S: Schema, M: RelationModel<Schema = S>> {
511 transaction: &'t WriteTransaction<'t, S>,
512 marker: PhantomData<M>,
513}
514
515impl<'t, S: Schema, M: RelationModel<Schema = S>> Copy for TransactionRelationManager<'t, S, M> {}
516impl<'t, S: Schema, M: RelationModel<Schema = S>> Clone for TransactionRelationManager<'t, S, M> {
517 fn clone(&self) -> Self {
518 *self
519 }
520}
521
522impl<S, M> TransactionRelationManager<'_, S, M>
523where
524 S: Schema,
525 M: RelationModel<Schema = S> + CompleteModel,
526{
527 fn exact(
528 &self,
529 ) -> Result<(
530 TypeId,
531 &InstalledRuntimeProjection,
532 DynamicRelationManager<'static>,
533 )> {
534 let installed = self.transaction.installed()?;
535 let (id, descriptor): (TypeId, RelationDescriptor) = resolve_relation_authority(
536 M::TYPE_ID_JSON,
537 installed,
538 ModelValidationPhase::Input,
539 true,
540 )?;
541 let manager = DynamicRelationManager::with_canonical_transaction(
542 self.transaction.tx.clone(),
543 Arc::new(descriptor),
544 );
545 Ok((id, installed, manager))
546 }
547
548 pub async fn insert(&self, input: M::Create) -> Result<M> {
552 let (id, installed, _manager) = self.exact()?;
553 let create = project_create(input, &id, installed)?;
554 let projected = ProjectedCrudExecutor::new(installed)
555 .insert_relation_in_transaction_with_compatibility(&self.transaction.tx, &create)
556 .await
557 .map_err(|error| {
558 Error::from_projected_crud(error, ModelKind::Relation, Some(CrudOperation::Insert))
559 })?;
560 materialize_projected(projected, installed)
561 }
562
563 pub async fn put(&self, input: M::Create) -> Result<M> {
567 let (id, installed, _manager) = self.exact()?;
568 let create = project_create(input, &id, installed)?;
569 let projected = ProjectedCrudExecutor::new(installed)
570 .put_relation_in_transaction_with_compatibility(&self.transaction.tx, &create)
571 .await
572 .map_err(|error| {
573 Error::from_projected_crud(error, ModelKind::Relation, Some(CrudOperation::Put))
574 })?;
575 materialize_projected(projected, installed)
576 }
577
578 pub async fn insert_many(&self, inputs: Vec<M::Create>) -> Result<Vec<M>> {
583 if uses_successor_batch_runtime(self.transaction.installed()?) {
584 validate_binding_row_count(inputs.len())?;
585 }
586 self.write_many(inputs, false).await
587 }
588
589 pub async fn put_many(&self, inputs: Vec<M::Create>) -> Result<Vec<M>> {
594 if uses_successor_batch_runtime(self.transaction.installed()?) {
595 validate_binding_row_count(inputs.len())?;
596 }
597 self.write_many(inputs, true).await
598 }
599
600 async fn write_many(&self, inputs: Vec<M::Create>, put: bool) -> Result<Vec<M>> {
601 let successor = uses_successor_batch_runtime(self.transaction.installed()?);
602 if inputs.is_empty() && !successor {
603 return Ok(Vec::new());
604 }
605 let (id, installed, manager) = self.exact()?;
606 if successor {
607 let rows = create_rows(installed, &id, inputs)?;
608 let operation = if put {
609 ProjectedBatchOperation::Put
610 } else {
611 ProjectedBatchOperation::Insert
612 };
613 let batch = prepare_batch(installed, id, operation, rows)?;
614 return execute_borrowed_things(&self.transaction.tx, installed, &batch).await;
615 }
616 let mut lowered: Vec<(DynamicAttributeMap, Vec<DynamicRolePlayerInput>)> =
617 Vec::with_capacity(inputs.len());
618 for input in inputs {
619 let prepared = lower_relation_create(input, &id, installed)?;
620 lowered.push((prepared.attributes, prepared.role_players));
621 }
622 let iids = if put {
623 manager.put_many_exact(&lowered).await
624 } else {
625 manager.insert_many(&lowered).await
626 }
627 .map_err(Error::from_orm)?;
628 if iids.len() != lowered.len() {
629 return Err(Error::model_validation(
630 ModelValidationPhase::Hydration,
631 "iid_count_mismatch",
632 vec!["iid".into()],
633 "provider returned an unexpected IID count",
634 None,
635 ));
636 }
637 let mut out = Vec::with_capacity(iids.len());
638 for iid in iids {
639 out.push(rehydrate_written_relation(&manager, &iid, &id, installed).await?);
640 }
641 Ok(out)
642 }
643
644 pub async fn update(&self, iid: &str, input: M::Create) -> Result<M> {
648 if !is_canonical_thing_iid(iid) {
649 return Err(invalid_iid());
650 }
651 let (id, installed, _manager) = self.exact()?;
652 let create = project_create(input, &id, installed)?;
653 let projected = ProjectedCrudExecutor::new(installed)
654 .update_relation_in_transaction_with_compatibility(&self.transaction.tx, iid, &create)
655 .await
656 .map_err(|error| {
657 Error::from_projected_crud(error, ModelKind::Relation, Some(CrudOperation::Update))
658 })?;
659 materialize_projected(projected, installed)
660 }
661
662 pub async fn delete(&self, iid: &str) -> Result<()> {
665 if !is_canonical_thing_iid(iid) {
666 return Err(invalid_iid());
667 }
668 let (id, installed, _manager) = self.exact()?;
669 ProjectedCrudExecutor::new(installed)
670 .delete_relation_by_iid_in_transaction_with_compatibility(
671 &self.transaction.tx,
672 &id,
673 iid,
674 )
675 .await
676 .map_err(|error| {
677 Error::from_projected_crud(error, ModelKind::Relation, Some(CrudOperation::Delete))
678 })
679 }
680
681 pub async fn update_many(&self, inputs: Vec<(String, M::Create)>) -> Result<Vec<M>> {
685 let successor = uses_successor_batch_runtime(self.transaction.installed()?);
686 if successor {
687 validate_binding_row_count(inputs.len())?;
688 }
689 if inputs.is_empty() && !successor {
690 return Ok(Vec::new());
691 }
692 if successor {
693 let (id, installed, _manager) = self.exact()?;
694 let rows = update_rows(installed, &id, inputs)?;
695 let batch = prepare_batch(installed, id, ProjectedBatchOperation::Update, rows)?;
696 return execute_borrowed_things(&self.transaction.tx, installed, &batch).await;
697 }
698 if inputs.iter().any(|(iid, _)| !is_canonical_thing_iid(iid)) {
699 return Err(invalid_iid());
700 }
701 let (id, installed, manager) = self.exact()?;
702
703 let mut prepared = Vec::with_capacity(inputs.len());
704 for (iid, input) in inputs {
705 prepared.push((iid, lower_relation_create(input, &id, installed)?));
706 }
707 let mut output = Vec::with_capacity(prepared.len());
708 for (iid, replacement) in prepared {
709 manager
710 .update_exact(&iid, &replacement.attributes, &replacement.role_players)
711 .await
712 .map_err(Error::from_orm)?;
713 output.push(rehydrate_written_relation(&manager, &iid, &id, installed).await?);
714 }
715 Ok(output)
716 }
717
718 pub async fn delete_many(&self, iids: &[String]) -> Result<()> {
722 let successor = uses_successor_batch_runtime(self.transaction.installed()?);
723 if successor {
724 validate_binding_row_count(iids.len())?;
725 }
726 if iids.is_empty() && !successor {
727 return Ok(());
728 }
729 if successor {
730 let (id, installed, _manager) = self.exact()?;
731 let batch = prepare_batch(
732 installed,
733 id,
734 ProjectedBatchOperation::Delete,
735 delete_rows(iids)?,
736 )?;
737 return execute_borrowed_delete(&self.transaction.tx, installed, &batch).await;
738 }
739 if iids.iter().any(|iid| !is_canonical_thing_iid(iid)) {
740 return Err(invalid_iid());
741 }
742 let (_id, _installed, manager) = self.exact()?;
743 for iid in iids {
744 manager
745 .delete_by_iid_exact(iid)
746 .await
747 .map_err(Error::from_orm)?;
748 }
749 Ok(())
750 }
751
752 pub async fn get_by_iid(&self, iid: &str) -> Result<Option<M>> {
755 if !is_canonical_thing_iid(iid) {
756 return Err(invalid_iid());
757 }
758 let (id, installed, _manager) = self.exact()?;
759 ProjectedCrudExecutor::new(installed)
760 .get_relation_by_iid_in_transaction_with_compatibility(&self.transaction.tx, &id, iid)
761 .await
762 .map_err(|error| Error::from_projected_crud(error, ModelKind::Relation, None))?
763 .map(|projected| materialize_projected(projected, installed))
764 .transpose()
765 }
766
767 pub async fn all(&self) -> Result<Vec<M>> {
770 let (id, installed, manager) = self.exact()?;
771 let rows = manager.all_exact().await.map_err(Error::from_orm)?;
772 rows.into_iter()
773 .map(|row| {
774 let hydrated = hydrate_relation(row, &id, installed)?;
775 M::materialize(&hydrated, &HydrationCapability::new())
776 .map_err(|error| map_validation_error(error, ModelValidationPhase::Hydration))
777 })
778 .collect()
779 }
780
781 pub async fn count(&self) -> Result<u64> {
783 let (id, installed, _manager) = self.exact()?;
784 ProjectedCrudExecutor::new(installed)
785 .count_relations_in_transaction_with_compatibility(&self.transaction.tx, &id)
786 .await
787 .map_err(|error| Error::from_projected_crud(error, ModelKind::Relation, None))
788 }
789}