1use crate::change_executor::ChangeExecutor;
60use crate::cascade::{self, CascadeDeleteAction, CascadeDeleteDirective, DrainedChild, FixupLink};
61use crate::db_set::DbSet;
62use crate::dependency_graph::DependencyGraph;
63use crate::entity::{
64 EntityState, IEntitySnapshot, IEntityType, IFromRow, IGetKeyValues, INavigationSetter,
65};
66use crate::error::{EFError, EFResult};
67use crate::interceptor::{InterceptorPipeline, SaveChangesContext, SaveChangesResultContext};
68use crate::metadata::{EntityTypeMeta, NavigationKind};
69use crate::migration::MigrationEngine;
70use crate::model_builder::ModelBuilder;
71use crate::provider::{DbValue, IAsyncConnection, IDatabaseProvider};
72use crate::tracking::{ChangeTracker, EntityEntryView};
73use crate::transaction::{DbTransaction, ITransaction};
74use std::any::{Any, TypeId};
75use std::collections::HashMap;
76use std::future::Future;
77use std::pin::Pin;
78use std::sync::Arc;
79
80#[derive(Clone)]
85pub struct DbContextOptions {
86 pub(crate) connection_string: String,
87 pub(crate) provider_tag: Option<String>,
88 #[allow(clippy::type_complexity)]
89 pub(crate) provider_factory:
90 Option<Arc<dyn Fn(&str) -> EFResult<Arc<dyn IDatabaseProvider>> + Send + Sync>>,
91 pub(crate) provider_cache: Arc<std::sync::Mutex<Option<Arc<dyn IDatabaseProvider>>>>,
98 pub(crate) interceptors: Vec<Arc<dyn crate::interceptor::ISaveChangesInterceptor>>,
99 pub(crate) lazy_loading_enabled: bool,
105 pub(crate) context_key: Option<String>,
106 pub(crate) metadata_cache: Arc<crate::metadata_cache::MetadataCache>,
112 #[cfg(feature = "tracing")]
115 pub(crate) slow_query_threshold: Option<std::time::Duration>,
116}
117
118impl std::fmt::Debug for DbContextOptions {
119 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
120 f.debug_struct("DbContextOptions")
121 .field(
122 "connection_string",
123 &redact_connection_string(&self.connection_string),
124 )
125 .field("provider_tag", &self.provider_tag)
126 .finish()
127 }
128}
129
130fn redact_connection_string(cs: &str) -> String {
135 if let Some(scheme_end) = cs.find("://") {
137 let (scheme, rest) = cs.split_at(scheme_end + 3);
138 if let Some(at) = rest.find('@') {
139 let (userinfo, host_and_rest) = rest.split_at(at);
140 let redacted_user = match userinfo.find(':') {
141 Some(colon) => &userinfo[..colon],
142 None => userinfo,
143 };
144 return format!("{}{}***@{}", scheme, redacted_user, &host_and_rest[1..]);
145 }
146 return cs.to_string();
147 }
148 if cs.contains('=') {
150 return cs
151 .split(';')
152 .map(|pair| {
153 let eq = match pair.find('=') {
154 Some(e) => e,
155 None => return pair.to_string(),
156 };
157 let key = pair[..eq].trim().to_lowercase();
158 if key.contains("password") || key.contains("pwd") {
159 format!("{}=***", &pair[..eq])
160 } else {
161 pair.to_string()
162 }
163 })
164 .collect::<Vec<_>>()
165 .join(";");
166 }
167 cs.to_string()
168}
169
170impl DbContextOptions {
171 pub fn connection_string(&self) -> &str {
172 &self.connection_string
173 }
174 pub fn provider_tag(&self) -> Option<&str> {
175 self.provider_tag.as_deref()
176 }
177 pub fn lazy_loading_enabled(&self) -> bool {
178 self.lazy_loading_enabled
179 }
180 pub fn context_key(&self) -> Option<&str> {
181 self.context_key.as_deref()
182 }
183 pub fn create_provider(&self) -> EFResult<Arc<dyn IDatabaseProvider>> {
184 let mut guard = self
188 .provider_cache
189 .lock()
190 .unwrap_or_else(|p| p.into_inner());
191 if let Some(provider) = guard.as_ref() {
192 return Ok(Arc::clone(provider));
193 }
194 let factory = self.provider_factory.as_ref().ok_or_else(|| {
195 crate::error::EFError::configuration(
196 "No provider configured. Call use_sqlite / use_postgres / use_mysql first.",
197 )
198 })?;
199 let provider = factory(self.connection_string())?;
200 #[cfg(feature = "tracing")]
201 if let Some(threshold) = self.slow_query_threshold {
202 provider.set_slow_query_threshold(threshold);
203 }
204 *guard = Some(Arc::clone(&provider));
205 Ok(provider)
206 }
207}
208
209#[allow(clippy::derivable_impls)]
210impl Default for DbContextOptions {
211 fn default() -> Self {
212 Self {
213 connection_string: String::new(),
214 provider_tag: None,
215 provider_factory: None,
216 provider_cache: Arc::new(std::sync::Mutex::new(None)),
217 interceptors: Vec::new(),
218 lazy_loading_enabled: false,
219 context_key: None,
220 metadata_cache: Arc::new(crate::metadata_cache::MetadataCache::new()),
221 #[cfg(feature = "tracing")]
222 slow_query_threshold: None,
223 }
224 }
225}
226
227pub struct DbContextOptionsBuilder {
228 inner: DbContextOptions,
229}
230
231impl DbContextOptionsBuilder {
232 pub fn new() -> Self {
233 Self {
234 inner: DbContextOptions::default(),
235 }
236 }
237 pub fn connection_string(&mut self, cs: impl Into<String>) -> &mut Self {
238 self.inner.connection_string = cs.into();
239 self
240 }
241 pub fn set_provider(&mut self, tag: &str, cs: impl Into<String>) -> &mut Self {
242 self.inner.provider_tag = Some(tag.to_string());
243 self.inner.connection_string = cs.into();
244 self
245 }
246 #[allow(clippy::type_complexity)]
247 pub fn set_provider_factory(
248 &mut self,
249 tag: &str,
250 cs: impl Into<String>,
251 factory: Arc<dyn Fn(&str) -> EFResult<Arc<dyn IDatabaseProvider>> + Send + Sync>,
252 ) -> &mut Self {
253 self.inner.provider_tag = Some(tag.to_string());
254 self.inner.connection_string = cs.into();
255 self.inner.provider_factory = Some(factory);
256 self
257 }
258 pub fn add_interceptor(
272 &mut self,
273 interceptor: impl crate::interceptor::ISaveChangesInterceptor + 'static,
274 ) -> &mut Self {
275 self.inner.interceptors.push(Arc::new(interceptor));
276 self
277 }
278
279 pub fn use_lazy_loading(&mut self, enabled: bool) -> &mut Self {
297 self.inner.lazy_loading_enabled = enabled;
298 self
299 }
300
301 pub fn context_key(&mut self, key: impl Into<String>) -> &mut Self {
306 self.inner.context_key = Some(key.into());
307 self
308 }
309
310 #[cfg(feature = "tracing")]
315 pub fn slow_query_threshold(&mut self, threshold: std::time::Duration) -> &mut Self {
316 self.inner.slow_query_threshold = Some(threshold);
317 self
318 }
319
320 pub fn build(self) -> DbContextOptions {
321 self.inner
322 }
323}
324
325impl Default for DbContextOptionsBuilder {
326 fn default() -> Self {
327 Self::new()
328 }
329}
330
331#[async_trait::async_trait]
336trait ErasedSetOps: Send + Sync {
337 async fn save(
338 &self,
339 conn: &mut (dyn IAsyncConnection + Send),
340 provider: &dyn IDatabaseProvider,
341 raw_set: &mut (dyn Any + Send + Sync),
342 meta: &EntityTypeMeta,
343 ) -> EFResult<(usize, usize, usize)>;
344 fn detect_changes(&self, raw_set: &mut (dyn Any + Send + Sync));
345 fn accept_all_changes(&self, raw_set: &mut (dyn Any + Send + Sync + 'static));
350 fn collect_entries(&self, raw_set: &(dyn Any + Send + Sync)) -> Vec<EntityEntryView>;
354
355 fn drain_cascade_children(
360 &self,
361 raw_set: &mut (dyn Any + Send + Sync),
362 meta: &EntityTypeMeta,
363 ) -> Vec<DrainedChild>;
364
365 fn add_cascade_child(
368 &self,
369 raw_set: &mut (dyn Any + Send + Sync),
370 child: Box<dyn Any + Send + Sync>,
371 ) -> Option<usize>;
372
373 fn entry_count(&self, raw_set: &(dyn Any + Send + Sync)) -> usize;
375
376 fn get_pk_at(&self, raw_set: &(dyn Any + Send + Sync), idx: usize) -> Option<i64>;
379
380 fn set_fk_at(
382 &self,
383 raw_set: &mut (dyn Any + Send + Sync),
384 idx: usize,
385 target_type: TypeId,
386 key: i64,
387 );
388
389 async fn insert_added(
391 &self,
392 conn: &mut (dyn IAsyncConnection + Send),
393 provider: &dyn IDatabaseProvider,
394 raw_set: &mut (dyn Any + Send + Sync),
395 meta: &EntityTypeMeta,
396 ) -> EFResult<usize>;
397
398 async fn upsert_added(
400 &self,
401 conn: &mut (dyn IAsyncConnection + Send),
402 provider: &dyn IDatabaseProvider,
403 raw_set: &mut (dyn Any + Send + Sync),
404 meta: &EntityTypeMeta,
405 ) -> EFResult<usize>;
406
407 async fn update_modified(
409 &self,
410 conn: &mut (dyn IAsyncConnection + Send),
411 provider: &dyn IDatabaseProvider,
412 raw_set: &mut (dyn Any + Send + Sync),
413 meta: &EntityTypeMeta,
414 ) -> EFResult<usize>;
415
416 async fn delete_deleted(
418 &self,
419 conn: &mut (dyn IAsyncConnection + Send),
420 provider: &dyn IDatabaseProvider,
421 raw_set: &mut (dyn Any + Send + Sync),
422 meta: &EntityTypeMeta,
423 ) -> EFResult<usize>;
424
425 fn drain_cascade_deleted_children(
429 &self,
430 raw_set: &mut (dyn Any + Send + Sync),
431 meta: &EntityTypeMeta,
432 processed: &mut std::collections::HashSet<(TypeId, usize)>,
433 ) -> (
434 Vec<DrainedChild>,
435 Vec<crate::cascade::CascadeDeleteDirective>,
436 );
437
438 fn add_cascade_deleted_child(
441 &self,
442 raw_set: &mut (dyn Any + Send + Sync),
443 child: Box<dyn Any + Send + Sync>,
444 ) -> Option<usize>;
445}
446
447struct SetOps<E> {
448 _phantom: std::marker::PhantomData<E>,
449}
450impl<E> SetOps<E> {
451 fn new() -> Self {
452 Self {
453 _phantom: std::marker::PhantomData,
454 }
455 }
456}
457
458#[async_trait::async_trait]
459impl<E> ErasedSetOps for SetOps<E>
460where
461 E: IEntityType
462 + IEntitySnapshot
463 + IGetKeyValues
464 + IFromRow
465 + INavigationSetter
466 + Send
467 + Sync
468 + 'static,
469{
470 async fn save(
471 &self,
472 conn: &mut (dyn IAsyncConnection + Send),
473 provider: &dyn IDatabaseProvider,
474 raw_set: &mut (dyn Any + Send + Sync),
475 meta: &EntityTypeMeta,
476 ) -> EFResult<(usize, usize, usize)> {
477 let db_set = raw_set
478 .downcast_mut::<DbSet<E>>()
479 .expect("SetOps type mismatch");
480 save_one_set(conn, provider, db_set, meta).await
481 }
482 fn detect_changes(&self, raw_set: &mut (dyn Any + Send + Sync)) {
483 if let Some(db_set) = raw_set.downcast_mut::<DbSet<E>>() {
484 db_set.detect_changes();
485 }
486 }
487 fn accept_all_changes(&self, raw_set: &mut (dyn Any + Send + Sync + 'static)) {
488 if let Some(db_set) = raw_set.downcast_mut::<DbSet<E>>() {
489 db_set.accept_all_changes();
490 }
491 }
492 fn collect_entries(&self, raw_set: &(dyn Any + Send + Sync)) -> Vec<EntityEntryView> {
493 let Some(db_set) = raw_set.downcast_ref::<DbSet<E>>() else {
494 return Vec::new();
495 };
496 let type_name = E::entity_meta().type_name.to_string();
497 db_set
498 .entries
499 .iter()
500 .map(|e| EntityEntryView {
501 type_id: TypeId::of::<E>(),
502 type_name: type_name.clone(),
503 state: e.state,
504 })
505 .collect()
506 }
507
508 fn drain_cascade_children(
509 &self,
510 raw_set: &mut (dyn Any + Send + Sync),
511 meta: &EntityTypeMeta,
512 ) -> Vec<DrainedChild> {
513 let Some(db_set) = raw_set.downcast_mut::<DbSet<E>>() else {
514 return Vec::new();
515 };
516 let mut result = Vec::new();
517 for (entry_idx, entry) in db_set.entries.iter_mut().enumerate() {
518 if entry.state != EntityState::Added || entry.is_upsert {
519 continue;
520 }
521 for nav in &meta.navigations {
522 if !matches!(nav.kind, NavigationKind::HasMany | NavigationKind::ManyToMany) {
523 continue;
524 }
525 if let Some(items) = entry.entity.drain_has_many(nav.field_name.as_ref()) {
526 for item in items {
527 result.push(DrainedChild {
528 parent_type_id: TypeId::of::<E>(),
529 parent_entry_idx: entry_idx,
530 child: item,
531 child_type_id: nav.related_type_id,
532 fk_target_type_id: TypeId::of::<E>(),
533 through_table: nav.through_table.as_ref().map(|s| s.to_string()),
534 through_parent_fk_col: nav
535 .through_parent_fk
536 .as_ref()
537 .map(|s| s.to_string()),
538 through_child_fk_col: nav
539 .through_related_fk
540 .as_ref()
541 .map(|s| s.to_string()),
542 });
543 }
544 }
545 }
546 }
547 result
548 }
549
550 fn add_cascade_child(
551 &self,
552 raw_set: &mut (dyn Any + Send + Sync),
553 child: Box<dyn Any + Send + Sync>,
554 ) -> Option<usize> {
555 let db_set = raw_set.downcast_mut::<DbSet<E>>()?;
556 let child = child.downcast::<E>().ok()?;
557 let pk: i64 = child
558 .key_values()
559 .into_values()
560 .next()
561 .and_then(|v| v.try_into().ok())
562 .unwrap_or(0);
563 let entity = *child;
564 if pk > 0 {
565 db_set.attach(entity);
566 } else {
567 db_set.add(entity);
568 }
569 Some(db_set.entries.len() - 1)
570 }
571
572 fn entry_count(&self, raw_set: &(dyn Any + Send + Sync)) -> usize {
573 raw_set
574 .downcast_ref::<DbSet<E>>()
575 .map(|s| s.entries.len())
576 .unwrap_or(0)
577 }
578
579 fn get_pk_at(&self, raw_set: &(dyn Any + Send + Sync), idx: usize) -> Option<i64> {
580 let db_set = raw_set.downcast_ref::<DbSet<E>>()?;
581 let entry = db_set.entries.get(idx)?;
582 entry
583 .entity
584 .key_values()
585 .into_values()
586 .next()
587 .and_then(|v| v.try_into().ok())
588 }
589
590 fn set_fk_at(
591 &self,
592 raw_set: &mut (dyn Any + Send + Sync),
593 idx: usize,
594 target_type: TypeId,
595 key: i64,
596 ) {
597 if let Some(db_set) = raw_set.downcast_mut::<DbSet<E>>() {
598 if let Some(entry) = db_set.entries.get_mut(idx) {
599 entry.entity.set_foreign_key(target_type, key);
600 }
601 }
602 }
603
604 async fn insert_added(
605 &self,
606 conn: &mut (dyn IAsyncConnection + Send),
607 provider: &dyn IDatabaseProvider,
608 raw_set: &mut (dyn Any + Send + Sync),
609 meta: &EntityTypeMeta,
610 ) -> EFResult<usize> {
611 let db_set = raw_set
612 .downcast_mut::<DbSet<E>>()
613 .expect("SetOps type mismatch");
614 insert_added_phase(conn, provider, db_set, meta).await
615 }
616
617 async fn upsert_added(
618 &self,
619 conn: &mut (dyn IAsyncConnection + Send),
620 provider: &dyn IDatabaseProvider,
621 raw_set: &mut (dyn Any + Send + Sync),
622 meta: &EntityTypeMeta,
623 ) -> EFResult<usize> {
624 let db_set = raw_set
625 .downcast_mut::<DbSet<E>>()
626 .expect("SetOps type mismatch");
627 upsert_added_phase(conn, provider, db_set, meta).await
628 }
629
630 async fn update_modified(
631 &self,
632 conn: &mut (dyn IAsyncConnection + Send),
633 provider: &dyn IDatabaseProvider,
634 raw_set: &mut (dyn Any + Send + Sync),
635 meta: &EntityTypeMeta,
636 ) -> EFResult<usize> {
637 let db_set = raw_set
638 .downcast_mut::<DbSet<E>>()
639 .expect("SetOps type mismatch");
640 let query_filter = db_set.query_filter().cloned();
641 update_modified_phase(conn, provider, db_set, meta, query_filter.as_ref()).await
642 }
643
644 async fn delete_deleted(
645 &self,
646 conn: &mut (dyn IAsyncConnection + Send),
647 provider: &dyn IDatabaseProvider,
648 raw_set: &mut (dyn Any + Send + Sync),
649 meta: &EntityTypeMeta,
650 ) -> EFResult<usize> {
651 let db_set = raw_set
652 .downcast_mut::<DbSet<E>>()
653 .expect("SetOps type mismatch");
654 let query_filter = db_set.query_filter().cloned();
655 delete_deleted_phase(conn, provider, db_set, meta, query_filter.as_ref()).await
656 }
657
658 fn drain_cascade_deleted_children(
659 &self,
660 raw_set: &mut (dyn Any + Send + Sync),
661 meta: &EntityTypeMeta,
662 processed: &mut std::collections::HashSet<(TypeId, usize)>,
663 ) -> (Vec<DrainedChild>, Vec<CascadeDeleteDirective>) {
664 use crate::relations::DeleteBehavior;
665
666 let Some(db_set) = raw_set.downcast_mut::<DbSet<E>>() else {
667 return (Vec::new(), Vec::new());
668 };
669 let mut drained_children = Vec::new();
670 let mut directives = Vec::new();
671
672 for (entry_idx, entry) in db_set.entries.iter_mut().enumerate() {
673 if entry.state != EntityState::Deleted {
674 continue;
675 }
676 if !processed.insert((TypeId::of::<E>(), entry_idx)) {
678 continue;
679 }
680
681 let principal_pk: i64 = entry
683 .entity
684 .key_values()
685 .into_values()
686 .next()
687 .and_then(|v| v.try_into().ok())
688 .unwrap_or(0);
689
690 for nav in &meta.navigations {
691 match nav.kind {
692 NavigationKind::ManyToMany => {
693 if let (Some(table), Some(fk_col), Some(pk)) = (
695 nav.through_table.as_ref(),
696 nav.through_parent_fk.as_ref(),
697 (principal_pk > 0).then_some(principal_pk),
698 ) {
699 directives.push(CascadeDeleteDirective {
700 table: table.to_string(),
701 fk_column: fk_col.to_string(),
702 principal_pk: pk,
703 action: CascadeDeleteAction::Delete,
704 });
705 }
706 }
707 NavigationKind::HasMany => {
708 let behavior = resolve_delete_behavior(nav);
709 match behavior {
710 DeleteBehavior::Cascade => {
711 if let Some(items) = entry.entity.drain_has_many(nav.field_name.as_ref()) {
713 for item in items {
714 drained_children.push(DrainedChild {
715 parent_type_id: TypeId::of::<E>(),
716 parent_entry_idx: entry_idx,
717 child: item,
718 child_type_id: nav.related_type_id,
719 fk_target_type_id: TypeId::of::<E>(),
720 through_table: None,
721 through_parent_fk_col: None,
722 through_child_fk_col: None,
723 });
724 }
725 }
726 if let (Some(table), Some(fk_col), Some(pk)) = (
728 nav.related_table.as_ref(),
729 nav.fk_column.as_ref(),
730 (principal_pk > 0).then_some(principal_pk),
731 ) {
732 directives.push(CascadeDeleteDirective {
733 table: table.to_string(),
734 fk_column: fk_col.to_string(),
735 principal_pk: pk,
736 action: CascadeDeleteAction::Delete,
737 });
738 }
739 }
740 DeleteBehavior::SetNull => {
741 if let (Some(table), Some(fk_col), Some(pk)) = (
743 nav.related_table.as_ref(),
744 nav.fk_column.as_ref(),
745 (principal_pk > 0).then_some(principal_pk),
746 ) {
747 directives.push(CascadeDeleteDirective {
748 table: table.to_string(),
749 fk_column: fk_col.to_string(),
750 principal_pk: pk,
751 action: CascadeDeleteAction::SetNull,
752 });
753 }
754 }
755 DeleteBehavior::Restrict | DeleteBehavior::NoAction => {
756 }
758 }
759 }
760 NavigationKind::BelongsTo | NavigationKind::HasOne => {
761 }
763 }
764 }
765 }
766 (drained_children, directives)
767 }
768
769 fn add_cascade_deleted_child(
770 &self,
771 raw_set: &mut (dyn Any + Send + Sync),
772 child: Box<dyn Any + Send + Sync>,
773 ) -> Option<usize> {
774 let db_set = raw_set.downcast_mut::<DbSet<E>>()?;
775 let child = child.downcast::<E>().ok()?;
776 let pk: i64 = child
777 .key_values()
778 .into_values()
779 .next()
780 .and_then(|v| v.try_into().ok())
781 .unwrap_or(0);
782 if pk == 0 {
783 return None;
784 }
785 let original = child.snapshot();
786 db_set.entries.push(crate::db_set::TrackedEntry {
787 entity: *child,
788 state: EntityState::Deleted,
789 original: Some(original),
790 modified_properties: Vec::new(),
791 is_upsert: false,
792 });
793 Some(db_set.entries.len() - 1)
794 }
795}
796
797pub(crate) fn resolve_delete_behavior(
803 nav: &crate::metadata::NavigationMeta,
804) -> crate::relations::DeleteBehavior {
805 use crate::relations::DeleteBehavior;
806 if let Some(b) = nav.delete_behavior {
807 return b;
808 }
809 if nav.kind == NavigationKind::ManyToMany {
810 return DeleteBehavior::Cascade;
811 }
812 if let Some(meta_fn) = nav.related_entity_meta {
813 let child_meta = meta_fn();
814 if let Some(fk_prop) = child_meta.properties.iter().find(|p| p.is_foreign_key) {
815 let is_nullable = fk_prop.type_name.contains("Option");
818 return if is_nullable {
819 DeleteBehavior::Restrict
820 } else {
821 DeleteBehavior::Cascade
822 };
823 }
824 }
825 DeleteBehavior::Cascade
826}
827
828pub struct DbContext {
833 sets: HashMap<TypeId, Box<dyn Any + Send + Sync>>,
834 savers: HashMap<TypeId, Box<dyn ErasedSetOps>>,
835 entity_metas: HashMap<TypeId, EntityTypeMeta>,
836 model_builder: ModelBuilder,
837 change_tracker: ChangeTracker,
838 provider: Arc<dyn IDatabaseProvider>,
839 interceptor_pipeline: InterceptorPipeline,
840 lazy_loading_enabled: bool,
841 ambient_transaction: Option<Box<dyn ITransaction>>,
850}
851
852impl DbContext {
853 pub fn from_options(options: &DbContextOptions) -> EFResult<Self> {
861 let provider = options.create_provider()?;
862 let built = options
863 .metadata_cache
864 .get_or_build(options.context_key.as_deref());
865 let ctx = Self {
866 sets: HashMap::new(),
867 savers: HashMap::new(),
868 entity_metas: built.entity_metas.clone(),
869 model_builder: ModelBuilder::from_built(&built),
870 change_tracker: ChangeTracker::new(),
871 provider,
872 interceptor_pipeline: InterceptorPipeline::new(options.interceptors.clone()),
873 lazy_loading_enabled: options.lazy_loading_enabled,
874 ambient_transaction: None,
875 };
876 Ok(ctx)
877 }
878
879 pub fn set<T>(&mut self) -> &mut DbSet<T>
880 where
881 T: IEntityType
882 + IEntitySnapshot
883 + IGetKeyValues
884 + IFromRow
885 + INavigationSetter
886 + Send
887 + Sync
888 + 'static,
889 {
890 let type_id = TypeId::of::<T>();
891 self.savers
892 .entry(type_id)
893 .or_insert_with(|| Box::new(SetOps::<T>::new()));
894 self.entity_metas
895 .entry(type_id)
896 .or_insert_with(T::entity_meta);
897 if !self.model_builder.has_entity(type_id) {
898 self.model_builder.register_entity_meta(T::entity_meta());
899 }
900 self.sets.entry(type_id).or_insert_with(|| {
901 let table_name = self
902 .model_builder
903 .build()
904 .into_iter()
905 .find(|m| m.type_id == type_id)
906 .map(|m| m.table_name.to_string())
907 .unwrap_or_else(|| T::entity_meta().table_name.to_string());
908 let mut db_set = DbSet::<T>::with_provider(table_name, Arc::clone(&self.provider));
909 if let Some(filter) = self.model_builder.get_query_filter(&type_id) {
910 db_set.set_query_filter(filter.clone());
911 }
912 db_set.set_filter_map(self.model_builder.filters_by_table());
913 db_set.set_lazy_loading_enabled(self.lazy_loading_enabled);
914 Box::new(db_set)
915 });
916 self.sets
917 .get_mut(&type_id)
918 .and_then(|b| b.downcast_mut::<DbSet<T>>())
919 .expect("DbSet type mismatch")
920 }
921
922 pub fn model(&mut self) -> &mut ModelBuilder {
924 &mut self.model_builder
925 }
926
927 pub fn model_builder(&self) -> &ModelBuilder {
929 &self.model_builder
930 }
931
932 pub fn entity_metas_contains<T: IEntityType>(&self) -> bool {
935 self.entity_metas.contains_key(&TypeId::of::<T>())
936 }
937
938 pub fn discover_entities(&mut self) -> EFResult<()> {
950 Ok(())
951 }
952
953 pub fn detect_changes(&mut self) {
955 let type_ids: Vec<TypeId> = self.sets.keys().copied().collect();
956 for type_id in type_ids {
957 if let Some(set) = self.sets.get_mut(&type_id) {
958 if let Some(saver) = self.savers.get(&type_id) {
959 saver.detect_changes(set.as_mut());
960 }
961 }
962 }
963 }
964
965 fn build_save_context(&self) -> SaveChangesContext {
970 let mut views: Vec<EntityEntryView> = Vec::new();
971 for (type_id, set) in &self.sets {
972 if let Some(saver) = self.savers.get(type_id) {
973 views.extend(saver.collect_entries(set.as_ref()));
974 }
975 }
976 SaveChangesContext::from_views(views)
977 }
978
979 pub async fn ensure_created(&self) -> EFResult<()> {
986 let mut metas: Vec<EntityTypeMeta> = self.model_builder.build();
987 if metas.is_empty() {
988 metas = self.entity_metas.values().cloned().collect();
989 }
990 if metas.is_empty() {
991 return Err(EFError::configuration(
992 "No entity types registered. Call ctx.discover_entities() or ctx.set::<T>() before ensure_created().",
993 ));
994 }
995 let dialect = self.provider.migration_dialect();
996 MigrationEngine::new(dialect)
997 .ensure_created(&*self.provider, &metas)
998 .await?;
999
1000 for meta in &metas {
1001 let rows = self.model_builder.seed_rows_for(&meta.type_id);
1002 if !rows.is_empty() {
1003 MigrationEngine::new(dialect)
1004 .apply_seed_data(&*self.provider, meta, rows)
1005 .await?;
1006 }
1007 }
1008 Ok(())
1009 }
1010
1011 pub async fn ensure_deleted(&self) -> EFResult<()> {
1013 let mut metas: Vec<EntityTypeMeta> = self.model_builder.build();
1014 if metas.is_empty() {
1015 metas = self.entity_metas.values().cloned().collect();
1016 }
1017 if metas.is_empty() {
1018 return Err(EFError::configuration(
1019 "No entity types registered. Call ctx.discover_entities() or ctx.set::<T>() before ensure_deleted().",
1020 ));
1021 }
1022 let dialect = self.provider.migration_dialect();
1023 MigrationEngine::new(dialect)
1024 .ensure_deleted(&*self.provider, &metas)
1025 .await
1026 }
1027}
1028
1029impl DbContext {
1034 pub fn provider(&self) -> &dyn IDatabaseProvider {
1036 &*self.provider
1037 }
1038
1039 pub fn change_tracker(&self) -> &ChangeTracker {
1041 &self.change_tracker
1042 }
1043
1044 pub fn change_tracker_mut(&mut self) -> &mut ChangeTracker {
1046 &mut self.change_tracker
1047 }
1048
1049 pub async fn sql_query<T: IFromRow + IEntityType>(
1062 &self,
1063 sql: &str,
1064 params: &[DbValue],
1065 ) -> EFResult<Vec<T>> {
1066 let mut conn = self.provider.get_connection().await?;
1067 let rows = conn.query(sql, params).await?;
1068 crate::entity::materialize_entities(&rows)
1069 }
1070
1071 pub fn transaction_mut(&mut self) -> Option<&mut Box<dyn ITransaction>> {
1079 self.ambient_transaction.as_mut()
1080 }
1081
1082 pub async fn begin_transaction(&mut self) -> EFResult<Box<dyn ITransaction>> {
1095 let mut conn = self.provider.get_connection().await?;
1096 conn.begin_transaction().await?;
1097 Ok(Box::new(DbTransaction::new(conn)))
1098 }
1099
1100 pub async fn save_changes(&mut self) -> EFResult<SaveChangesResult> {
1105 let _save_guard = crate::observability::SaveChangesGuard::new();
1106 let type_ids: Vec<TypeId> = self.sets.keys().copied().collect();
1107 for type_id in &type_ids {
1108 let set = self.sets.get_mut(type_id).unwrap();
1109 self.savers
1110 .get(type_id)
1111 .unwrap()
1112 .detect_changes(set.as_mut());
1113 }
1114
1115 let configured_metas: HashMap<TypeId, EntityTypeMeta> = self
1118 .model_builder
1119 .build()
1120 .into_iter()
1121 .map(|m| (m.type_id, m))
1122 .collect();
1123
1124 let save_ctx = self.build_save_context();
1128 self.interceptor_pipeline.on_saving(&save_ctx).await?;
1129
1130 enum TxnSource {
1136 Ambient(Box<dyn ITransaction>),
1137 Managed(Box<dyn IAsyncConnection>),
1138 }
1139 let mut txn = match self.ambient_transaction.take() {
1140 Some(t) => TxnSource::Ambient(t),
1141 None => {
1142 let mut c = self.provider.get_connection().await?;
1143 c.begin_transaction().await?;
1144 TxnSource::Managed(c)
1145 }
1146 };
1147
1148 let type_ids: Vec<TypeId> = self.sets.keys().copied().collect();
1149
1150 let mut fixup_links: Vec<FixupLink> = Vec::new();
1156 loop {
1157 let mut all_drained: Vec<DrainedChild> = Vec::new();
1158 for type_id in &type_ids {
1159 let saver = self.savers.get(type_id).expect("saver not registered");
1160 let set = self.sets.get_mut(type_id).unwrap();
1161 let meta = configured_metas
1162 .get(type_id)
1163 .or_else(|| self.entity_metas.get(type_id))
1164 .expect("meta not found");
1165 all_drained.extend(saver.drain_cascade_children(set.as_mut(), meta));
1166 }
1167 if all_drained.is_empty() {
1168 break;
1169 }
1170 for child in all_drained {
1171 let child_saver = self.savers.get(&child.child_type_id).ok_or_else(|| {
1172 EFError::configuration(format!(
1173 "Cannot cascade-save child type {:?}: no DbSet registered. \
1174 Call ctx.set::<ChildType>() before save_changes.",
1175 child.child_type_id
1176 ))
1177 })?;
1178 let child_set = self
1179 .sets
1180 .get_mut(&child.child_type_id)
1181 .expect("set not found for registered saver");
1182 if let Some(child_idx) =
1183 child_saver.add_cascade_child(child_set.as_mut(), child.child)
1184 {
1185 if let Some(link) = fixup_links.iter_mut().find(|l| {
1186 l.parent_type_id == child.parent_type_id
1187 && l.parent_entry_idx == child.parent_entry_idx
1188 && l.child_type_id == child.child_type_id
1189 && l.through_table == child.through_table
1190 }) {
1191 link.child_entry_indices.push(child_idx);
1192 } else {
1193 fixup_links.push(FixupLink {
1194 parent_type_id: child.parent_type_id,
1195 parent_entry_idx: child.parent_entry_idx,
1196 child_type_id: child.child_type_id,
1197 child_entry_indices: vec![child_idx],
1198 fk_target_type_id: child.fk_target_type_id,
1199 through_table: child.through_table,
1200 through_parent_fk_col: child.through_parent_fk_col,
1201 through_child_fk_col: child.through_child_fk_col,
1202 });
1203 }
1204 }
1205 }
1206 }
1207
1208 let mut delete_directives: Vec<CascadeDeleteDirective> = Vec::new();
1214 let mut processed: std::collections::HashSet<(TypeId, usize)> =
1215 std::collections::HashSet::new();
1216 loop {
1217 let mut all_drained_deleted: Vec<DrainedChild> = Vec::new();
1218 for type_id in &type_ids {
1219 let saver = self.savers.get(type_id).expect("saver not registered");
1220 let set = self.sets.get_mut(type_id).unwrap();
1221 let meta = configured_metas
1222 .get(type_id)
1223 .or_else(|| self.entity_metas.get(type_id))
1224 .expect("meta not found");
1225 let (drained, directives) =
1226 saver.drain_cascade_deleted_children(set.as_mut(), meta, &mut processed);
1227 all_drained_deleted.extend(drained);
1228 delete_directives.extend(directives);
1229 }
1230 if all_drained_deleted.is_empty() {
1231 break;
1232 }
1233 for child in all_drained_deleted {
1234 let child_saver = self.savers.get(&child.child_type_id).ok_or_else(|| {
1235 EFError::configuration(format!(
1236 "Cannot cascade-delete child type {:?}: no DbSet registered. \
1237 Call ctx.set::<ChildType>() before save_changes.",
1238 child.child_type_id
1239 ))
1240 })?;
1241 let child_set = self
1242 .sets
1243 .get_mut(&child.child_type_id)
1244 .expect("set not found for registered saver");
1245 child_saver.add_cascade_deleted_child(child_set.as_mut(), child.child);
1246 }
1247 }
1248
1249 let graph = DependencyGraph::build(&configured_metas);
1251 let insert_order = graph.topological_sort();
1252 let delete_order = graph.deletion_order();
1253
1254 let mut total_added = 0usize;
1255 let mut total_updated = 0usize;
1256 let mut total_deleted = 0usize;
1257
1258 for type_id in &insert_order {
1260 if !self.sets.contains_key(type_id) || !self.savers.contains_key(type_id) {
1261 continue;
1262 }
1263 let saver = self.savers.get(type_id).expect("saver not registered");
1264 let set = self.sets.get_mut(type_id).unwrap();
1265 let meta = configured_metas
1266 .get(type_id)
1267 .or_else(|| self.entity_metas.get(type_id))
1268 .expect("meta not found");
1269 let inserted = {
1270 let conn_ref: &mut dyn IAsyncConnection = match &mut txn {
1271 TxnSource::Ambient(t) => t.connection(),
1272 TxnSource::Managed(c) => c.as_mut(),
1273 };
1274 match saver
1275 .insert_added(conn_ref, &*self.provider, set.as_mut(), meta)
1276 .await
1277 {
1278 Ok(n) => n,
1279 Err(e) => {
1280 if let TxnSource::Managed(mut conn) = txn {
1281 let _ = conn.rollback_transaction().await;
1282 } else if let TxnSource::Ambient(t) = txn {
1283 self.ambient_transaction = Some(t);
1284 }
1285 self.interceptor_pipeline
1286 .on_save_failed(&save_ctx, &e)
1287 .await;
1288 return Err(e);
1289 }
1290 }
1291 };
1292 total_added += inserted;
1293
1294 let link_indices: Vec<usize> = fixup_links
1296 .iter()
1297 .enumerate()
1298 .filter(|(_, l)| l.parent_type_id == *type_id && l.through_table.is_none())
1299 .map(|(i, _)| i)
1300 .collect();
1301
1302 let mut self_ref_updates: Vec<(String, i64, i64)> = Vec::new();
1304
1305 for link_idx in &link_indices {
1306 let link = &fixup_links[*link_idx];
1307 let parent_pk = {
1308 let parent_saver = self.savers.get(&link.parent_type_id).unwrap();
1309 let parent_set = self.sets.get(&link.parent_type_id).unwrap();
1310 parent_saver.get_pk_at(parent_set.as_ref(), link.parent_entry_idx)
1311 };
1312 let Some(pk) = parent_pk else {
1313 continue;
1314 };
1315
1316 {
1318 let child_saver = self.savers.get(&link.child_type_id).unwrap();
1319 let child_set = self.sets.get_mut(&link.child_type_id).unwrap();
1320 for &child_idx in &link.child_entry_indices {
1321 child_saver.set_fk_at(
1322 child_set.as_mut(),
1323 child_idx,
1324 link.fk_target_type_id,
1325 pk,
1326 );
1327 }
1328 }
1329
1330 if link.child_type_id == link.parent_type_id {
1332 let child_meta = configured_metas
1333 .get(&link.child_type_id)
1334 .or_else(|| self.entity_metas.get(&link.child_type_id))
1335 .unwrap();
1336 let fk_col = child_meta
1337 .properties
1338 .iter()
1339 .find(|p| p.is_foreign_key)
1340 .map(|p| p.column_name.as_ref());
1341 let pk_col = child_meta
1342 .properties
1343 .iter()
1344 .find(|p| p.is_primary_key)
1345 .map(|p| p.column_name.as_ref())
1346 .unwrap_or("id");
1347 if let Some(fk_col) = fk_col {
1348 for &child_idx in &link.child_entry_indices {
1349 let child_pk = {
1350 let child_saver = self.savers.get(&link.child_type_id).unwrap();
1351 let child_set = self.sets.get(&link.child_type_id).unwrap();
1352 child_saver.get_pk_at(child_set.as_ref(), child_idx)
1353 };
1354 if let Some(child_pk) = child_pk {
1355 let sql = format!(
1356 "UPDATE {} SET {} = ? WHERE {} = ?",
1357 child_meta.table_name, fk_col, pk_col
1358 );
1359 self_ref_updates.push((sql, pk, child_pk));
1360 }
1361 }
1362 }
1363 }
1364 }
1365
1366 for (sql, fk_val, pk_val) in self_ref_updates {
1368 let conn_ref: &mut dyn IAsyncConnection = match &mut txn {
1369 TxnSource::Ambient(t) => t.connection(),
1370 TxnSource::Managed(c) => c.as_mut(),
1371 };
1372 if let Err(e) = conn_ref
1373 .execute(&sql, &[DbValue::from(fk_val), DbValue::from(pk_val)])
1374 .await
1375 {
1376 if let TxnSource::Managed(mut conn) = txn {
1377 let _ = conn.rollback_transaction().await;
1378 } else if let TxnSource::Ambient(t) = txn {
1379 self.ambient_transaction = Some(t);
1380 }
1381 self.interceptor_pipeline
1382 .on_save_failed(&save_ctx, &e)
1383 .await;
1384 return Err(e);
1385 }
1386 }
1387 }
1388
1389 for link in &fixup_links {
1391 if link.through_table.is_none() {
1392 continue;
1393 }
1394 let table = link.through_table.as_ref().unwrap();
1395 let parent_col = link.through_parent_fk_col.as_ref().unwrap();
1396 let child_col = link.through_child_fk_col.as_ref().unwrap();
1397
1398 let parent_pk = {
1399 let parent_saver = self.savers.get(&link.parent_type_id).unwrap();
1400 let parent_set = self.sets.get(&link.parent_type_id).unwrap();
1401 parent_saver.get_pk_at(parent_set.as_ref(), link.parent_entry_idx)
1402 };
1403 let Some(parent_pk) = parent_pk else {
1404 continue;
1405 };
1406
1407 let mut child_pks: Vec<i64> = Vec::new();
1408 {
1409 let child_saver = self.savers.get(&link.child_type_id).unwrap();
1410 let child_set = self.sets.get(&link.child_type_id).unwrap();
1411 for &child_idx in &link.child_entry_indices {
1412 if let Some(child_pk) = child_saver.get_pk_at(child_set.as_ref(), child_idx) {
1413 child_pks.push(child_pk);
1414 }
1415 }
1416 }
1417
1418 if !child_pks.is_empty() {
1419 let sql = cascade::m2m_insert_sql(table, parent_col, child_col, child_pks.len());
1420 let mut params: Vec<DbValue> = Vec::with_capacity(child_pks.len() * 2);
1421 for child_pk in &child_pks {
1422 params.push(DbValue::from(parent_pk));
1423 params.push(DbValue::from(*child_pk));
1424 }
1425 let conn_ref: &mut dyn IAsyncConnection = match &mut txn {
1426 TxnSource::Ambient(t) => t.connection(),
1427 TxnSource::Managed(c) => c.as_mut(),
1428 };
1429 if let Err(e) = conn_ref.execute(&sql, ¶ms).await {
1430 if let TxnSource::Managed(mut conn) = txn {
1431 let _ = conn.rollback_transaction().await;
1432 } else if let TxnSource::Ambient(t) = txn {
1433 self.ambient_transaction = Some(t);
1434 }
1435 self.interceptor_pipeline
1436 .on_save_failed(&save_ctx, &e)
1437 .await;
1438 return Err(e);
1439 }
1440 total_added += child_pks.len();
1441 }
1442 }
1443
1444 for type_id in &insert_order {
1446 if !self.sets.contains_key(type_id) || !self.savers.contains_key(type_id) {
1447 continue;
1448 }
1449 let saver = self.savers.get(type_id).expect("saver not registered");
1450 let set = self.sets.get_mut(type_id).unwrap();
1451 let meta = configured_metas
1452 .get(type_id)
1453 .or_else(|| self.entity_metas.get(type_id))
1454 .expect("meta not found");
1455 let n = {
1456 let conn_ref: &mut dyn IAsyncConnection = match &mut txn {
1457 TxnSource::Ambient(t) => t.connection(),
1458 TxnSource::Managed(c) => c.as_mut(),
1459 };
1460 match saver
1461 .upsert_added(conn_ref, &*self.provider, set.as_mut(), meta)
1462 .await
1463 {
1464 Ok(n) => n,
1465 Err(e) => {
1466 if let TxnSource::Managed(mut conn) = txn {
1467 let _ = conn.rollback_transaction().await;
1468 } else if let TxnSource::Ambient(t) = txn {
1469 self.ambient_transaction = Some(t);
1470 }
1471 self.interceptor_pipeline
1472 .on_save_failed(&save_ctx, &e)
1473 .await;
1474 return Err(e);
1475 }
1476 }
1477 };
1478 total_added += n;
1479 }
1480
1481 for type_id in &insert_order {
1483 if !self.sets.contains_key(type_id) || !self.savers.contains_key(type_id) {
1484 continue;
1485 }
1486 let saver = self.savers.get(type_id).expect("saver not registered");
1487 let set = self.sets.get_mut(type_id).unwrap();
1488 let meta = configured_metas
1489 .get(type_id)
1490 .or_else(|| self.entity_metas.get(type_id))
1491 .expect("meta not found");
1492 let n = {
1493 let conn_ref: &mut dyn IAsyncConnection = match &mut txn {
1494 TxnSource::Ambient(t) => t.connection(),
1495 TxnSource::Managed(c) => c.as_mut(),
1496 };
1497 match saver
1498 .update_modified(conn_ref, &*self.provider, set.as_mut(), meta)
1499 .await
1500 {
1501 Ok(n) => n,
1502 Err(e) => {
1503 if let TxnSource::Managed(mut conn) = txn {
1504 let _ = conn.rollback_transaction().await;
1505 } else if let TxnSource::Ambient(t) = txn {
1506 self.ambient_transaction = Some(t);
1507 }
1508 self.interceptor_pipeline
1509 .on_save_failed(&save_ctx, &e)
1510 .await;
1511 return Err(e);
1512 }
1513 }
1514 };
1515 total_updated += n;
1516 }
1517
1518 for directive in &delete_directives {
1523 if directive.action != CascadeDeleteAction::SetNull {
1524 continue;
1525 }
1526 let sql = format!(
1527 "UPDATE {} SET {} = NULL WHERE {} = ?",
1528 directive.table, directive.fk_column, directive.fk_column
1529 );
1530 let params = vec![DbValue::from(directive.principal_pk)];
1531 let conn_ref: &mut dyn IAsyncConnection = match &mut txn {
1532 TxnSource::Ambient(t) => t.connection(),
1533 TxnSource::Managed(c) => c.as_mut(),
1534 };
1535 if let Err(e) = conn_ref.execute(&sql, ¶ms).await {
1536 if let TxnSource::Managed(mut conn) = txn {
1537 let _ = conn.rollback_transaction().await;
1538 } else if let TxnSource::Ambient(t) = txn {
1539 self.ambient_transaction = Some(t);
1540 }
1541 self.interceptor_pipeline
1542 .on_save_failed(&save_ctx, &e)
1543 .await;
1544 return Err(e);
1545 }
1546 }
1547
1548 for type_id in &delete_order {
1550 if !self.sets.contains_key(type_id) || !self.savers.contains_key(type_id) {
1551 continue;
1552 }
1553 let saver = self.savers.get(type_id).expect("saver not registered");
1554 let set = self.sets.get_mut(type_id).unwrap();
1555 let meta = configured_metas
1556 .get(type_id)
1557 .or_else(|| self.entity_metas.get(type_id))
1558 .expect("meta not found");
1559 let n = {
1560 let conn_ref: &mut dyn IAsyncConnection = match &mut txn {
1561 TxnSource::Ambient(t) => t.connection(),
1562 TxnSource::Managed(c) => c.as_mut(),
1563 };
1564 match saver
1565 .delete_deleted(conn_ref, &*self.provider, set.as_mut(), meta)
1566 .await
1567 {
1568 Ok(n) => n,
1569 Err(e) => {
1570 if let TxnSource::Managed(mut conn) = txn {
1571 let _ = conn.rollback_transaction().await;
1572 } else if let TxnSource::Ambient(t) = txn {
1573 self.ambient_transaction = Some(t);
1574 }
1575 self.interceptor_pipeline
1576 .on_save_failed(&save_ctx, &e)
1577 .await;
1578 return Err(e);
1579 }
1580 }
1581 };
1582 total_deleted += n;
1583 }
1584
1585 for directive in &delete_directives {
1590 if directive.action != CascadeDeleteAction::Delete {
1591 continue;
1592 }
1593 let sql = format!(
1594 "DELETE FROM {} WHERE {} = ?",
1595 directive.table, directive.fk_column
1596 );
1597 let params = vec![DbValue::from(directive.principal_pk)];
1598 let conn_ref: &mut dyn IAsyncConnection = match &mut txn {
1599 TxnSource::Ambient(t) => t.connection(),
1600 TxnSource::Managed(c) => c.as_mut(),
1601 };
1602 if let Err(e) = conn_ref.execute(&sql, ¶ms).await {
1603 if let TxnSource::Managed(mut conn) = txn {
1604 let _ = conn.rollback_transaction().await;
1605 } else if let TxnSource::Ambient(t) = txn {
1606 self.ambient_transaction = Some(t);
1607 }
1608 self.interceptor_pipeline
1609 .on_save_failed(&save_ctx, &e)
1610 .await;
1611 return Err(e);
1612 }
1613 }
1614
1615 match txn {
1616 TxnSource::Ambient(t) => {
1617 self.ambient_transaction = Some(t);
1618 }
1619 TxnSource::Managed(mut conn) => {
1620 if let Err(e) = conn.commit_transaction().await {
1621 self.interceptor_pipeline
1622 .on_save_failed(&save_ctx, &e)
1623 .await;
1624 return Err(e);
1625 }
1626 }
1627 }
1628 self.change_tracker.accept_all_changes();
1629 for type_id in &type_ids {
1630 let saver = self.savers.get(type_id).unwrap();
1631 let set = self.sets.get_mut(type_id).unwrap();
1632 saver.accept_all_changes(set.as_mut());
1633 }
1634
1635 let result_ctx = SaveChangesResultContext {
1637 added: total_added,
1638 updated: total_updated,
1639 deleted: total_deleted,
1640 };
1641 self.interceptor_pipeline
1642 .on_saved(&save_ctx, &result_ctx)
1643 .await?;
1644
1645 Ok(SaveChangesResult {
1646 added: total_added,
1647 updated: total_updated,
1648 deleted: total_deleted,
1649 })
1650 }
1651
1652 pub async fn use_transaction<F, R>(&mut self, f: F) -> EFResult<R>
1673 where
1674 for<'a> F: FnOnce(&'a mut Self) -> Pin<Box<dyn Future<Output = EFResult<R>> + Send + 'a>>,
1675 R: Send + 'static,
1676 {
1677 if self.ambient_transaction.is_some() {
1678 return Err(EFError::transaction(
1679 "ambient transaction already active; nested use_transaction is not supported",
1680 ));
1681 }
1682 let mut conn = self.provider.get_connection().await?;
1683 conn.begin_transaction().await?;
1684 self.ambient_transaction = Some(Box::new(DbTransaction::new(conn)));
1685 let result = f(self).await;
1686 let txn = self
1687 .ambient_transaction
1688 .take()
1689 .expect("ambient_transaction set above");
1690 match result {
1691 Ok(r) => {
1692 txn.commit().await?;
1693 Ok(r)
1694 }
1695 Err(e) => {
1696 let _ = txn.rollback().await;
1697 Err(e)
1698 }
1699 }
1700 }
1701}
1702
1703#[allow(clippy::type_complexity)]
1708pub async fn save_one_set<E>(
1709 conn: &mut dyn IAsyncConnection,
1710 provider: &dyn IDatabaseProvider,
1711 db_set: &mut DbSet<E>,
1712 meta: &EntityTypeMeta,
1713) -> EFResult<(usize, usize, usize)>
1714where
1715 E: IEntityType + IEntitySnapshot + IGetKeyValues + IFromRow,
1716{
1717 let query_filter = db_set.query_filter().cloned();
1718 let ac = insert_added_phase(conn, provider, db_set, meta).await?;
1719 let ac_upsert = upsert_added_phase(conn, provider, db_set, meta).await?;
1720 let uc = update_modified_phase(conn, provider, db_set, meta, query_filter.as_ref()).await?;
1721 let dc = delete_deleted_phase(conn, provider, db_set, meta, query_filter.as_ref()).await?;
1722 Ok((ac + ac_upsert, uc, dc))
1723}
1724
1725pub async fn insert_added_phase<E>(
1727 conn: &mut dyn IAsyncConnection,
1728 provider: &dyn IDatabaseProvider,
1729 db_set: &mut DbSet<E>,
1730 meta: &EntityTypeMeta,
1731) -> EFResult<usize>
1732where
1733 E: IEntityType + IEntitySnapshot + IGetKeyValues,
1734{
1735 let added: Vec<(&E, &EntityTypeMeta)> = db_set
1736 .tracked_by_state(EntityState::Added)
1737 .into_iter()
1738 .filter(|(_, _, _, is_upsert)| !*is_upsert)
1739 .map(|(e, _, _, _)| (e, meta))
1740 .collect();
1741 if added.is_empty() {
1742 return Ok(0);
1743 }
1744 let added_count = added.len();
1745 let mut generated_keys: Vec<i64> = vec![0; added_count];
1746 let inserted = ChangeExecutor::execute_inserts(conn, provider, &added, |idx, key| {
1747 if idx < generated_keys.len() {
1748 generated_keys[idx] = key;
1749 }
1750 })
1751 .await?;
1752 db_set.backfill_added_keys(&generated_keys);
1753 Ok(inserted)
1754}
1755
1756pub async fn upsert_added_phase<E>(
1758 conn: &mut dyn IAsyncConnection,
1759 provider: &dyn IDatabaseProvider,
1760 db_set: &mut DbSet<E>,
1761 meta: &EntityTypeMeta,
1762) -> EFResult<usize>
1763where
1764 E: IEntityType + IEntitySnapshot + IGetKeyValues,
1765{
1766 let upserts: Vec<(&E, &EntityTypeMeta)> = db_set
1767 .tracked_by_state(EntityState::Added)
1768 .into_iter()
1769 .filter(|(_, _, _, is_upsert)| *is_upsert)
1770 .map(|(e, _, _, _)| (e, meta))
1771 .collect();
1772 if upserts.is_empty() {
1773 return Ok(0);
1774 }
1775 ChangeExecutor::execute_upserts(conn, provider, &upserts).await
1776}
1777
1778pub async fn update_modified_phase<E>(
1780 conn: &mut dyn IAsyncConnection,
1781 provider: &dyn IDatabaseProvider,
1782 db_set: &mut DbSet<E>,
1783 meta: &EntityTypeMeta,
1784 query_filter: Option<&crate::query::BoolExpr>,
1785) -> EFResult<usize>
1786where
1787 E: IEntityType + IEntitySnapshot + IGetKeyValues,
1788{
1789 let modified: Vec<(
1790 &E,
1791 &EntityTypeMeta,
1792 Option<&HashMap<String, DbValue>>,
1793 &[String],
1794 )> = db_set
1795 .tracked_by_state(EntityState::Modified)
1796 .into_iter()
1797 .map(|(e, orig, mods, _)| (e, meta, orig, mods))
1798 .collect();
1799 if modified.is_empty() {
1800 return Ok(0);
1801 }
1802 ChangeExecutor::execute_updates(conn, provider, &modified, query_filter).await
1803}
1804
1805pub async fn delete_deleted_phase<E>(
1807 conn: &mut dyn IAsyncConnection,
1808 provider: &dyn IDatabaseProvider,
1809 db_set: &mut DbSet<E>,
1810 meta: &EntityTypeMeta,
1811 query_filter: Option<&crate::query::BoolExpr>,
1812) -> EFResult<usize>
1813where
1814 E: IEntityType + IEntitySnapshot + IGetKeyValues,
1815{
1816 let deleted: Vec<(&E, &EntityTypeMeta, Option<&HashMap<String, DbValue>>)> = db_set
1817 .tracked_by_state(EntityState::Deleted)
1818 .into_iter()
1819 .map(|(e, orig, _, _)| (e, meta, orig))
1820 .collect();
1821 if deleted.is_empty() {
1822 return Ok(0);
1823 }
1824 ChangeExecutor::execute_deletes(conn, provider, &deleted, query_filter).await
1825}
1826
1827#[derive(Debug, Clone)]
1832pub struct SaveChangesResult {
1833 pub added: usize,
1834 pub updated: usize,
1835 pub deleted: usize,
1836}
1837impl SaveChangesResult {
1838 pub fn total(&self) -> usize {
1839 self.added + self.updated + self.deleted
1840 }
1841}
1842impl std::fmt::Display for SaveChangesResult {
1843 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1844 write!(
1845 f,
1846 "{} entities modified ({} added, {} updated, {} deleted)",
1847 self.total(),
1848 self.added,
1849 self.updated,
1850 self.deleted
1851 )
1852 }
1853}