1use crate::cascade::{self, CascadeDeleteAction};
7use crate::dependency_graph::DependencyGraph;
8use crate::error::{EFError, EFResult};
9use crate::interceptor::{SaveChangesContext, SaveChangesResultContext};
10use crate::metadata::EntityTypeMeta;
11use crate::provider::{DbValue, IAsyncConnection};
12use crate::transaction::ConnectionGuard;
13use std::any::TypeId;
14use std::collections::HashMap;
15use std::sync::atomic::{AtomicU64, Ordering};
16
17use super::save_phases::SaveChangesResult;
18
19pub(super) enum TxnSource {
23 Session(Box<dyn IAsyncConnection>),
25 Managed(ConnectionGuard),
27}
28
29impl TxnSource {
30 fn conn(&mut self) -> &mut dyn IAsyncConnection {
31 match self {
32 TxnSource::Session(c) => c.as_mut(),
33 TxnSource::Managed(guard) => guard.as_mut(),
34 }
35 }
36
37 fn is_scoped(&self) -> bool {
39 matches!(self, TxnSource::Session(_))
40 }
41}
42
43static SAVEPOINT_SEQ: AtomicU64 = AtomicU64::new(1);
46
47fn next_savepoint_name() -> String {
48 format!(
49 "rust_ef_save_{}",
50 SAVEPOINT_SEQ.fetch_add(1, Ordering::Relaxed)
51 )
52}
53
54impl super::DbContext {
55 fn build_save_context(&self) -> SaveChangesContext {
60 let mut views: Vec<crate::tracking::EntityEntryView> = Vec::new();
61 for (type_id, set) in &self.sets {
62 if let Some(saver) = self.savers.get(type_id) {
63 views.extend(saver.collect_entries(set.as_ref(), &self.change_tracker));
64 }
65 }
66 SaveChangesContext::from_views(views)
67 }
68
69 pub async fn save_changes(&mut self) -> EFResult<SaveChangesResult> {
74 let _save_guard = crate::observability::SaveChangesGuard::new();
75 let type_ids: Vec<TypeId> = self.sets.keys().copied().collect();
76 for type_id in &type_ids {
77 let Some(set) = self.sets.get_mut(type_id) else {
78 continue;
79 };
80 let Some(saver) = self.savers.get(type_id) else {
81 continue;
82 };
83 saver.detect_changes(set.as_mut(), &mut self.change_tracker);
84 }
85
86 let configured_metas: HashMap<TypeId, EntityTypeMeta> = self
87 .model_builder
88 .build()
89 .into_iter()
90 .map(|m| (m.type_id, m))
91 .collect();
92
93 let save_ctx = self.build_save_context();
94 self.interceptor_pipeline.on_saving(&save_ctx).await?;
95
96 if save_ctx.added_count() == 0
99 && save_ctx.modified_count() == 0
100 && save_ctx.deleted_count() == 0
101 {
102 let result_ctx = SaveChangesResultContext {
103 added: 0,
104 updated: 0,
105 deleted: 0,
106 };
107 self.interceptor_pipeline
108 .on_saved(&save_ctx, &result_ctx)
109 .await?;
110 return Ok(SaveChangesResult {
111 added: 0,
112 updated: 0,
113 deleted: 0,
114 });
115 }
116
117 let mut txn = if self.session.is_active().await {
123 TxnSource::Session(self.provider.get_connection().await?)
124 } else {
125 let mut c = self.provider.get_connection().await?;
126 c.begin_transaction().await?;
127 TxnSource::Managed(ConnectionGuard::new(c))
128 };
129
130 let scoped = txn.is_scoped();
134 let savepoint = if scoped {
135 let name = next_savepoint_name();
136 if let Err(e) = txn.conn().create_savepoint(&name).await {
137 return self.fail_save(txn, &save_ctx, None, e).await;
138 }
139 Some(name)
140 } else {
141 None
142 };
143
144 let type_ids: Vec<TypeId> = self.sets.keys().copied().collect();
145
146 let fixup_links = match self.drain_cascade_adds(&type_ids, &configured_metas) {
148 Ok(links) => links,
149 Err(e) => {
150 return self
151 .fail_save(txn, &save_ctx, savepoint.as_deref(), e)
152 .await
153 }
154 };
155 let mut processed: std::collections::HashSet<(TypeId, usize)> =
156 std::collections::HashSet::new();
157 let delete_directives =
158 match self.drain_cascade_deletes(&type_ids, &configured_metas, &mut processed) {
159 Ok(directives) => directives,
160 Err(e) => {
161 return self
162 .fail_save(txn, &save_ctx, savepoint.as_deref(), e)
163 .await
164 }
165 };
166
167 let graph = DependencyGraph::build(&configured_metas);
168 let insert_order = graph.topological_sort();
169 let delete_order = graph.deletion_order();
170
171 let mut total_added = 0usize;
172 let mut total_updated = 0usize;
173 let mut total_deleted = 0usize;
174
175 for type_id in &insert_order {
177 if !self.sets.contains_key(type_id) || !self.savers.contains_key(type_id) {
178 continue;
179 }
180 let Some(saver) = self.savers.get(type_id) else {
181 continue;
182 };
183 let Some(set) = self.sets.get_mut(type_id) else {
184 continue;
185 };
186 let Some(meta) = configured_metas
187 .get(type_id)
188 .or_else(|| self.entity_metas.get(type_id))
189 else {
190 return self
191 .fail_save(
192 txn,
193 &save_ctx,
194 savepoint.as_deref(),
195 EFError::configuration(format!(
196 "entity metadata not found for {:?}",
197 type_id
198 )),
199 )
200 .await;
201 };
202 let inserted = match saver
203 .insert_added(
204 txn.conn(),
205 &*self.provider,
206 set.as_mut(),
207 &self.change_tracker,
208 meta,
209 )
210 .await
211 {
212 Ok(n) => n,
213 Err(e) => {
214 return self
215 .fail_save(txn, &save_ctx, savepoint.as_deref(), e)
216 .await
217 }
218 };
219 total_added += inserted;
220
221 let link_indices: Vec<usize> = fixup_links
222 .iter()
223 .enumerate()
224 .filter(|(_, l)| l.parent_type_id == *type_id && l.through_table.is_none())
225 .map(|(i, _)| i)
226 .collect();
227
228 let mut self_ref_updates: Vec<(String, i64, i64)> = Vec::new();
229
230 for link_idx in &link_indices {
231 let link = &fixup_links[*link_idx];
232 let parent_pk = {
233 let Some(parent_saver) = self.savers.get(&link.parent_type_id) else {
234 continue;
235 };
236 let Some(parent_set) = self.sets.get(&link.parent_type_id) else {
237 continue;
238 };
239 parent_saver.get_pk_at(parent_set.as_ref(), link.parent_entry_idx)
240 };
241 let Some(pk) = parent_pk else {
242 continue;
243 };
244
245 {
246 let Some(child_saver) = self.savers.get(&link.child_type_id) else {
247 continue;
248 };
249 let Some(child_set) = self.sets.get_mut(&link.child_type_id) else {
250 continue;
251 };
252 for &child_idx in &link.child_entry_indices {
253 child_saver.set_fk_at(
254 child_set.as_mut(),
255 child_idx,
256 link.fk_target_type_id,
257 pk,
258 );
259 }
260 }
261
262 if link.child_type_id == link.parent_type_id {
263 let Some(child_meta) = configured_metas
264 .get(&link.child_type_id)
265 .or_else(|| self.entity_metas.get(&link.child_type_id))
266 else {
267 continue;
268 };
269 let fk_col = child_meta
270 .properties
271 .iter()
272 .find(|p| p.is_foreign_key)
273 .map(|p| p.column_name.as_ref());
274 let pk_col = child_meta
275 .properties
276 .iter()
277 .find(|p| p.is_primary_key)
278 .map(|p| p.column_name.as_ref())
279 .unwrap_or("id");
280 if let Some(fk_col) = fk_col {
281 for &child_idx in &link.child_entry_indices {
282 let child_pk = {
283 let Some(child_saver) = self.savers.get(&link.child_type_id) else {
284 continue;
285 };
286 let Some(child_set) = self.sets.get(&link.child_type_id) else {
287 continue;
288 };
289 child_saver.get_pk_at(child_set.as_ref(), child_idx)
290 };
291 if let Some(child_pk) = child_pk {
292 let sql = format!(
293 "UPDATE {} SET {} = ? WHERE {} = ?",
294 child_meta.table_name, fk_col, pk_col
295 );
296 self_ref_updates.push((sql, pk, child_pk));
297 }
298 }
299 }
300 }
301 }
302
303 for (sql, fk_val, pk_val) in self_ref_updates {
304 if let Err(e) = txn
305 .conn()
306 .execute(&sql, &[DbValue::from(fk_val), DbValue::from(pk_val)])
307 .await
308 {
309 return self
310 .fail_save(txn, &save_ctx, savepoint.as_deref(), e)
311 .await;
312 }
313 }
314 }
315
316 for link in &fixup_links {
318 if link.through_table.is_none() {
319 continue;
320 }
321 let (Some(table), Some(parent_col), Some(child_col)) = (
322 link.through_table.as_ref(),
323 link.through_parent_fk_col.as_ref(),
324 link.through_child_fk_col.as_ref(),
325 ) else {
326 continue;
327 };
328
329 let parent_pk = {
330 let Some(parent_saver) = self.savers.get(&link.parent_type_id) else {
331 continue;
332 };
333 let Some(parent_set) = self.sets.get(&link.parent_type_id) else {
334 continue;
335 };
336 parent_saver.get_pk_at(parent_set.as_ref(), link.parent_entry_idx)
337 };
338 let Some(parent_pk) = parent_pk else {
339 continue;
340 };
341
342 let mut child_pks: Vec<i64> = Vec::new();
343 {
344 let Some(child_saver) = self.savers.get(&link.child_type_id) else {
345 continue;
346 };
347 let Some(child_set) = self.sets.get(&link.child_type_id) else {
348 continue;
349 };
350 for &child_idx in &link.child_entry_indices {
351 if let Some(child_pk) = child_saver.get_pk_at(child_set.as_ref(), child_idx) {
352 child_pks.push(child_pk);
353 }
354 }
355 }
356
357 if !child_pks.is_empty() {
358 let sql = cascade::m2m_insert_sql(table, parent_col, child_col, child_pks.len());
359 let mut params: Vec<DbValue> = Vec::with_capacity(child_pks.len() * 2);
360 for child_pk in &child_pks {
361 params.push(DbValue::from(parent_pk));
362 params.push(DbValue::from(*child_pk));
363 }
364 if let Err(e) = txn.conn().execute(&sql, ¶ms).await {
365 return self
366 .fail_save(txn, &save_ctx, savepoint.as_deref(), e)
367 .await;
368 }
369 total_added += child_pks.len();
370 }
371 }
372
373 for type_id in &insert_order {
375 if !self.sets.contains_key(type_id) || !self.savers.contains_key(type_id) {
376 continue;
377 }
378 let Some(saver) = self.savers.get(type_id) else {
379 continue;
380 };
381 let Some(set) = self.sets.get_mut(type_id) else {
382 continue;
383 };
384 let Some(meta) = configured_metas
385 .get(type_id)
386 .or_else(|| self.entity_metas.get(type_id))
387 else {
388 return self
389 .fail_save(
390 txn,
391 &save_ctx,
392 savepoint.as_deref(),
393 EFError::configuration(format!(
394 "entity metadata not found for {:?}",
395 type_id
396 )),
397 )
398 .await;
399 };
400 let n = match saver
401 .upsert_added(
402 txn.conn(),
403 &*self.provider,
404 set.as_mut(),
405 &self.change_tracker,
406 meta,
407 )
408 .await
409 {
410 Ok(n) => n,
411 Err(e) => {
412 return self
413 .fail_save(txn, &save_ctx, savepoint.as_deref(), e)
414 .await
415 }
416 };
417 total_added += n;
418 }
419
420 for type_id in &insert_order {
422 if !self.sets.contains_key(type_id) || !self.savers.contains_key(type_id) {
423 continue;
424 }
425 let Some(saver) = self.savers.get(type_id) else {
426 continue;
427 };
428 let Some(set) = self.sets.get_mut(type_id) else {
429 continue;
430 };
431 let Some(meta) = configured_metas
432 .get(type_id)
433 .or_else(|| self.entity_metas.get(type_id))
434 else {
435 return self
436 .fail_save(
437 txn,
438 &save_ctx,
439 savepoint.as_deref(),
440 EFError::configuration(format!(
441 "entity metadata not found for {:?}",
442 type_id
443 )),
444 )
445 .await;
446 };
447 let n = match saver
448 .update_modified(
449 txn.conn(),
450 &*self.provider,
451 set.as_mut(),
452 &self.change_tracker,
453 meta,
454 )
455 .await
456 {
457 Ok(n) => n,
458 Err(e) => {
459 return self
460 .fail_save(txn, &save_ctx, savepoint.as_deref(), e)
461 .await
462 }
463 };
464 total_updated += n;
465 }
466
467 for directive in &delete_directives {
469 if directive.action != CascadeDeleteAction::SetNull {
470 continue;
471 }
472 let sql = format!(
473 "UPDATE {} SET {} = NULL WHERE {} = ?",
474 directive.table, directive.fk_column, directive.fk_column
475 );
476 let params = vec![DbValue::from(directive.principal_pk)];
477 if let Err(e) = txn.conn().execute(&sql, ¶ms).await {
478 return self
479 .fail_save(txn, &save_ctx, savepoint.as_deref(), e)
480 .await;
481 }
482 }
483
484 for type_id in &delete_order {
486 if !self.sets.contains_key(type_id) || !self.savers.contains_key(type_id) {
487 continue;
488 }
489 let Some(saver) = self.savers.get(type_id) else {
490 continue;
491 };
492 let Some(set) = self.sets.get_mut(type_id) else {
493 continue;
494 };
495 let Some(meta) = configured_metas
496 .get(type_id)
497 .or_else(|| self.entity_metas.get(type_id))
498 else {
499 return self
500 .fail_save(
501 txn,
502 &save_ctx,
503 savepoint.as_deref(),
504 EFError::configuration(format!(
505 "entity metadata not found for {:?}",
506 type_id
507 )),
508 )
509 .await;
510 };
511 let n = match saver
512 .delete_deleted(
513 txn.conn(),
514 &*self.provider,
515 set.as_mut(),
516 &self.change_tracker,
517 meta,
518 )
519 .await
520 {
521 Ok(n) => n,
522 Err(e) => {
523 return self
524 .fail_save(txn, &save_ctx, savepoint.as_deref(), e)
525 .await
526 }
527 };
528 total_deleted += n;
529 }
530
531 for directive in &delete_directives {
533 if directive.action != CascadeDeleteAction::Delete {
534 continue;
535 }
536 let sql = format!(
537 "DELETE FROM {} WHERE {} = ?",
538 directive.table, directive.fk_column
539 );
540 let params = vec![DbValue::from(directive.principal_pk)];
541 if let Err(e) = txn.conn().execute(&sql, ¶ms).await {
542 return self
543 .fail_save(txn, &save_ctx, savepoint.as_deref(), e)
544 .await;
545 }
546 }
547
548 if let Some(name) = savepoint.as_deref() {
550 let _ = txn.conn().release_savepoint(name).await;
554 }
555 match txn {
556 TxnSource::Session(_) => {
557 }
559 TxnSource::Managed(mut guard) => {
560 if let Err(e) = guard.as_mut().commit_transaction().await {
561 let _ = guard.as_mut().rollback_transaction().await;
562 self.interceptor_pipeline
563 .on_save_failed(&save_ctx, &e)
564 .await;
565 return Err(e);
567 }
568 drop(guard.disarm());
570 }
571 }
572 for type_id in &type_ids {
574 let Some(saver) = self.savers.get(type_id) else {
575 continue;
576 };
577 let Some(set) = self.sets.get_mut(type_id) else {
578 continue;
579 };
580 saver.accept_all_changes(set.as_mut(), &mut self.change_tracker);
581 }
582
583 let result_ctx = SaveChangesResultContext {
584 added: total_added,
585 updated: total_updated,
586 deleted: total_deleted,
587 };
588 self.interceptor_pipeline
589 .on_saved(&save_ctx, &result_ctx)
590 .await?;
591
592 Ok(SaveChangesResult {
593 added: total_added,
594 updated: total_updated,
595 deleted: total_deleted,
596 })
597 }
598
599 async fn fail_save(
603 &mut self,
604 txn: TxnSource,
605 save_ctx: &SaveChangesContext,
606 savepoint: Option<&str>,
607 e: EFError,
608 ) -> EFResult<SaveChangesResult> {
609 match txn {
610 TxnSource::Managed(mut guard) => {
611 if guard.as_mut().rollback_transaction().await.is_ok() {
612 drop(guard.disarm());
613 }
614 }
616 TxnSource::Session(mut conn) => {
617 if let Some(name) = savepoint {
618 let _ = conn.rollback_to_savepoint(name).await;
619 let _ = conn.release_savepoint(name).await;
620 }
621 }
622 }
623 self.interceptor_pipeline.on_save_failed(save_ctx, &e).await;
624 Err(e)
625 }
626}