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::ITransaction;
13use std::any::TypeId;
14use std::collections::HashMap;
15
16use super::save_phases::SaveChangesResult;
17
18pub(super) enum TxnSource {
21 Ambient(Box<dyn ITransaction>),
22 Managed(Box<dyn IAsyncConnection>),
23}
24
25impl TxnSource {
26 fn conn(&mut self) -> &mut dyn IAsyncConnection {
27 match self {
28 TxnSource::Ambient(t) => t.connection(),
29 TxnSource::Managed(c) => c.as_mut(),
30 }
31 }
32}
33
34impl super::DbContext {
35 fn build_save_context(&self) -> SaveChangesContext {
40 let mut views: Vec<crate::tracking::EntityEntryView> = Vec::new();
41 for (type_id, set) in &self.sets {
42 if let Some(saver) = self.savers.get(type_id) {
43 views.extend(saver.collect_entries(set.as_ref(), &self.change_tracker));
44 }
45 }
46 SaveChangesContext::from_views(views)
47 }
48
49 pub async fn save_changes(&mut self) -> EFResult<SaveChangesResult> {
54 let _save_guard = crate::observability::SaveChangesGuard::new();
55 let type_ids: Vec<TypeId> = self.sets.keys().copied().collect();
56 for type_id in &type_ids {
57 let Some(set) = self.sets.get_mut(type_id) else {
58 continue;
59 };
60 let Some(saver) = self.savers.get(type_id) else {
61 continue;
62 };
63 saver.detect_changes(set.as_mut(), &mut self.change_tracker);
64 }
65
66 let configured_metas: HashMap<TypeId, EntityTypeMeta> = self
67 .model_builder
68 .build()
69 .into_iter()
70 .map(|m| (m.type_id, m))
71 .collect();
72
73 let save_ctx = self.build_save_context();
74 self.interceptor_pipeline.on_saving(&save_ctx).await?;
75
76 let mut txn = match self.ambient_transaction.take() {
77 Some(t) => TxnSource::Ambient(t),
78 None => {
79 let mut c = self.provider.get_connection().await?;
80 c.begin_transaction().await?;
81 TxnSource::Managed(c)
82 }
83 };
84
85 let type_ids: Vec<TypeId> = self.sets.keys().copied().collect();
86
87 let fixup_links = self.drain_cascade_adds(&type_ids, &configured_metas)?;
89 let mut processed: std::collections::HashSet<(TypeId, usize)> =
90 std::collections::HashSet::new();
91 let delete_directives =
92 self.drain_cascade_deletes(&type_ids, &configured_metas, &mut processed)?;
93
94 let graph = DependencyGraph::build(&configured_metas);
95 let insert_order = graph.topological_sort();
96 let delete_order = graph.deletion_order();
97
98 let mut total_added = 0usize;
99 let mut total_updated = 0usize;
100 let mut total_deleted = 0usize;
101
102 for type_id in &insert_order {
104 if !self.sets.contains_key(type_id) || !self.savers.contains_key(type_id) {
105 continue;
106 }
107 let Some(saver) = self.savers.get(type_id) else {
108 continue;
109 };
110 let Some(set) = self.sets.get_mut(type_id) else {
111 continue;
112 };
113 let Some(meta) = configured_metas
114 .get(type_id)
115 .or_else(|| self.entity_metas.get(type_id))
116 else {
117 return self
118 .fail_save(
119 txn,
120 &save_ctx,
121 EFError::configuration(format!(
122 "entity metadata not found for {:?}",
123 type_id
124 )),
125 )
126 .await;
127 };
128 let inserted = match saver
129 .insert_added(
130 txn.conn(),
131 &*self.provider,
132 set.as_mut(),
133 &self.change_tracker,
134 meta,
135 )
136 .await
137 {
138 Ok(n) => n,
139 Err(e) => return self.fail_save(txn, &save_ctx, e).await,
140 };
141 total_added += inserted;
142
143 let link_indices: Vec<usize> = fixup_links
144 .iter()
145 .enumerate()
146 .filter(|(_, l)| l.parent_type_id == *type_id && l.through_table.is_none())
147 .map(|(i, _)| i)
148 .collect();
149
150 let mut self_ref_updates: Vec<(String, i64, i64)> = Vec::new();
151
152 for link_idx in &link_indices {
153 let link = &fixup_links[*link_idx];
154 let parent_pk = {
155 let Some(parent_saver) = self.savers.get(&link.parent_type_id) else {
156 continue;
157 };
158 let Some(parent_set) = self.sets.get(&link.parent_type_id) else {
159 continue;
160 };
161 parent_saver.get_pk_at(parent_set.as_ref(), link.parent_entry_idx)
162 };
163 let Some(pk) = parent_pk else {
164 continue;
165 };
166
167 {
168 let Some(child_saver) = self.savers.get(&link.child_type_id) else {
169 continue;
170 };
171 let Some(child_set) = self.sets.get_mut(&link.child_type_id) else {
172 continue;
173 };
174 for &child_idx in &link.child_entry_indices {
175 child_saver.set_fk_at(
176 child_set.as_mut(),
177 child_idx,
178 link.fk_target_type_id,
179 pk,
180 );
181 }
182 }
183
184 if link.child_type_id == link.parent_type_id {
185 let Some(child_meta) = configured_metas
186 .get(&link.child_type_id)
187 .or_else(|| self.entity_metas.get(&link.child_type_id))
188 else {
189 continue;
190 };
191 let fk_col = child_meta
192 .properties
193 .iter()
194 .find(|p| p.is_foreign_key)
195 .map(|p| p.column_name.as_ref());
196 let pk_col = child_meta
197 .properties
198 .iter()
199 .find(|p| p.is_primary_key)
200 .map(|p| p.column_name.as_ref())
201 .unwrap_or("id");
202 if let Some(fk_col) = fk_col {
203 for &child_idx in &link.child_entry_indices {
204 let child_pk = {
205 let Some(child_saver) = self.savers.get(&link.child_type_id) else {
206 continue;
207 };
208 let Some(child_set) = self.sets.get(&link.child_type_id) else {
209 continue;
210 };
211 child_saver.get_pk_at(child_set.as_ref(), child_idx)
212 };
213 if let Some(child_pk) = child_pk {
214 let sql = format!(
215 "UPDATE {} SET {} = ? WHERE {} = ?",
216 child_meta.table_name, fk_col, pk_col
217 );
218 self_ref_updates.push((sql, pk, child_pk));
219 }
220 }
221 }
222 }
223 }
224
225 for (sql, fk_val, pk_val) in self_ref_updates {
226 if let Err(e) = txn
227 .conn()
228 .execute(&sql, &[DbValue::from(fk_val), DbValue::from(pk_val)])
229 .await
230 {
231 return self.fail_save(txn, &save_ctx, e).await;
232 }
233 }
234 }
235
236 for link in &fixup_links {
238 if link.through_table.is_none() {
239 continue;
240 }
241 let (Some(table), Some(parent_col), Some(child_col)) = (
242 link.through_table.as_ref(),
243 link.through_parent_fk_col.as_ref(),
244 link.through_child_fk_col.as_ref(),
245 ) else {
246 continue;
247 };
248
249 let parent_pk = {
250 let Some(parent_saver) = self.savers.get(&link.parent_type_id) else {
251 continue;
252 };
253 let Some(parent_set) = self.sets.get(&link.parent_type_id) else {
254 continue;
255 };
256 parent_saver.get_pk_at(parent_set.as_ref(), link.parent_entry_idx)
257 };
258 let Some(parent_pk) = parent_pk else {
259 continue;
260 };
261
262 let mut child_pks: Vec<i64> = Vec::new();
263 {
264 let Some(child_saver) = self.savers.get(&link.child_type_id) else {
265 continue;
266 };
267 let Some(child_set) = self.sets.get(&link.child_type_id) else {
268 continue;
269 };
270 for &child_idx in &link.child_entry_indices {
271 if let Some(child_pk) = child_saver.get_pk_at(child_set.as_ref(), child_idx) {
272 child_pks.push(child_pk);
273 }
274 }
275 }
276
277 if !child_pks.is_empty() {
278 let sql = cascade::m2m_insert_sql(table, parent_col, child_col, child_pks.len());
279 let mut params: Vec<DbValue> = Vec::with_capacity(child_pks.len() * 2);
280 for child_pk in &child_pks {
281 params.push(DbValue::from(parent_pk));
282 params.push(DbValue::from(*child_pk));
283 }
284 if let Err(e) = txn.conn().execute(&sql, ¶ms).await {
285 return self.fail_save(txn, &save_ctx, e).await;
286 }
287 total_added += child_pks.len();
288 }
289 }
290
291 for type_id in &insert_order {
293 if !self.sets.contains_key(type_id) || !self.savers.contains_key(type_id) {
294 continue;
295 }
296 let Some(saver) = self.savers.get(type_id) else {
297 continue;
298 };
299 let Some(set) = self.sets.get_mut(type_id) else {
300 continue;
301 };
302 let Some(meta) = configured_metas
303 .get(type_id)
304 .or_else(|| self.entity_metas.get(type_id))
305 else {
306 return self
307 .fail_save(
308 txn,
309 &save_ctx,
310 EFError::configuration(format!(
311 "entity metadata not found for {:?}",
312 type_id
313 )),
314 )
315 .await;
316 };
317 let n = match saver
318 .upsert_added(
319 txn.conn(),
320 &*self.provider,
321 set.as_mut(),
322 &self.change_tracker,
323 meta,
324 )
325 .await
326 {
327 Ok(n) => n,
328 Err(e) => return self.fail_save(txn, &save_ctx, e).await,
329 };
330 total_added += n;
331 }
332
333 for type_id in &insert_order {
335 if !self.sets.contains_key(type_id) || !self.savers.contains_key(type_id) {
336 continue;
337 }
338 let Some(saver) = self.savers.get(type_id) else {
339 continue;
340 };
341 let Some(set) = self.sets.get_mut(type_id) else {
342 continue;
343 };
344 let Some(meta) = configured_metas
345 .get(type_id)
346 .or_else(|| self.entity_metas.get(type_id))
347 else {
348 return self
349 .fail_save(
350 txn,
351 &save_ctx,
352 EFError::configuration(format!(
353 "entity metadata not found for {:?}",
354 type_id
355 )),
356 )
357 .await;
358 };
359 let n = match saver
360 .update_modified(
361 txn.conn(),
362 &*self.provider,
363 set.as_mut(),
364 &self.change_tracker,
365 meta,
366 )
367 .await
368 {
369 Ok(n) => n,
370 Err(e) => return self.fail_save(txn, &save_ctx, e).await,
371 };
372 total_updated += n;
373 }
374
375 for directive in &delete_directives {
377 if directive.action != CascadeDeleteAction::SetNull {
378 continue;
379 }
380 let sql = format!(
381 "UPDATE {} SET {} = NULL WHERE {} = ?",
382 directive.table, directive.fk_column, directive.fk_column
383 );
384 let params = vec![DbValue::from(directive.principal_pk)];
385 if let Err(e) = txn.conn().execute(&sql, ¶ms).await {
386 return self.fail_save(txn, &save_ctx, e).await;
387 }
388 }
389
390 for type_id in &delete_order {
392 if !self.sets.contains_key(type_id) || !self.savers.contains_key(type_id) {
393 continue;
394 }
395 let Some(saver) = self.savers.get(type_id) else {
396 continue;
397 };
398 let Some(set) = self.sets.get_mut(type_id) else {
399 continue;
400 };
401 let Some(meta) = configured_metas
402 .get(type_id)
403 .or_else(|| self.entity_metas.get(type_id))
404 else {
405 return self
406 .fail_save(
407 txn,
408 &save_ctx,
409 EFError::configuration(format!(
410 "entity metadata not found for {:?}",
411 type_id
412 )),
413 )
414 .await;
415 };
416 let n = match saver
417 .delete_deleted(
418 txn.conn(),
419 &*self.provider,
420 set.as_mut(),
421 &self.change_tracker,
422 meta,
423 )
424 .await
425 {
426 Ok(n) => n,
427 Err(e) => return self.fail_save(txn, &save_ctx, e).await,
428 };
429 total_deleted += n;
430 }
431
432 for directive in &delete_directives {
434 if directive.action != CascadeDeleteAction::Delete {
435 continue;
436 }
437 let sql = format!(
438 "DELETE FROM {} WHERE {} = ?",
439 directive.table, directive.fk_column
440 );
441 let params = vec![DbValue::from(directive.principal_pk)];
442 if let Err(e) = txn.conn().execute(&sql, ¶ms).await {
443 return self.fail_save(txn, &save_ctx, e).await;
444 }
445 }
446
447 match txn {
449 TxnSource::Ambient(t) => {
450 self.ambient_transaction = Some(t);
451 }
452 TxnSource::Managed(mut conn) => {
453 if let Err(e) = conn.commit_transaction().await {
454 self.interceptor_pipeline
455 .on_save_failed(&save_ctx, &e)
456 .await;
457 return Err(e);
458 }
459 }
460 }
461 for type_id in &type_ids {
463 let Some(saver) = self.savers.get(type_id) else {
464 continue;
465 };
466 let Some(set) = self.sets.get_mut(type_id) else {
467 continue;
468 };
469 saver.accept_all_changes(set.as_mut(), &mut self.change_tracker);
470 }
471
472 let result_ctx = SaveChangesResultContext {
473 added: total_added,
474 updated: total_updated,
475 deleted: total_deleted,
476 };
477 self.interceptor_pipeline
478 .on_saved(&save_ctx, &result_ctx)
479 .await?;
480
481 Ok(SaveChangesResult {
482 added: total_added,
483 updated: total_updated,
484 deleted: total_deleted,
485 })
486 }
487
488 async fn fail_save(
491 &mut self,
492 txn: TxnSource,
493 save_ctx: &SaveChangesContext,
494 e: EFError,
495 ) -> EFResult<SaveChangesResult> {
496 match txn {
497 TxnSource::Managed(mut conn) => {
498 let _ = conn.rollback_transaction().await;
499 }
500 TxnSource::Ambient(t) => {
501 self.ambient_transaction = Some(t);
502 }
503 }
504 self.interceptor_pipeline.on_save_failed(save_ctx, &e).await;
505 Err(e)
506 }
507}