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()));
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());
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(txn.conn(), &*self.provider, set.as_mut(), meta)
130 .await
131 {
132 Ok(n) => n,
133 Err(e) => return self.fail_save(txn, &save_ctx, e).await,
134 };
135 total_added += inserted;
136
137 let link_indices: Vec<usize> = fixup_links
138 .iter()
139 .enumerate()
140 .filter(|(_, l)| l.parent_type_id == *type_id && l.through_table.is_none())
141 .map(|(i, _)| i)
142 .collect();
143
144 let mut self_ref_updates: Vec<(String, i64, i64)> = Vec::new();
145
146 for link_idx in &link_indices {
147 let link = &fixup_links[*link_idx];
148 let parent_pk = {
149 let Some(parent_saver) = self.savers.get(&link.parent_type_id) else {
150 continue;
151 };
152 let Some(parent_set) = self.sets.get(&link.parent_type_id) else {
153 continue;
154 };
155 parent_saver.get_pk_at(parent_set.as_ref(), link.parent_entry_idx)
156 };
157 let Some(pk) = parent_pk else {
158 continue;
159 };
160
161 {
162 let Some(child_saver) = self.savers.get(&link.child_type_id) else {
163 continue;
164 };
165 let Some(child_set) = self.sets.get_mut(&link.child_type_id) else {
166 continue;
167 };
168 for &child_idx in &link.child_entry_indices {
169 child_saver.set_fk_at(
170 child_set.as_mut(),
171 child_idx,
172 link.fk_target_type_id,
173 pk,
174 );
175 }
176 }
177
178 if link.child_type_id == link.parent_type_id {
179 let Some(child_meta) = configured_metas
180 .get(&link.child_type_id)
181 .or_else(|| self.entity_metas.get(&link.child_type_id))
182 else {
183 continue;
184 };
185 let fk_col = child_meta
186 .properties
187 .iter()
188 .find(|p| p.is_foreign_key)
189 .map(|p| p.column_name.as_ref());
190 let pk_col = child_meta
191 .properties
192 .iter()
193 .find(|p| p.is_primary_key)
194 .map(|p| p.column_name.as_ref())
195 .unwrap_or("id");
196 if let Some(fk_col) = fk_col {
197 for &child_idx in &link.child_entry_indices {
198 let child_pk = {
199 let Some(child_saver) = self.savers.get(&link.child_type_id) else {
200 continue;
201 };
202 let Some(child_set) = self.sets.get(&link.child_type_id) else {
203 continue;
204 };
205 child_saver.get_pk_at(child_set.as_ref(), child_idx)
206 };
207 if let Some(child_pk) = child_pk {
208 let sql = format!(
209 "UPDATE {} SET {} = ? WHERE {} = ?",
210 child_meta.table_name, fk_col, pk_col
211 );
212 self_ref_updates.push((sql, pk, child_pk));
213 }
214 }
215 }
216 }
217 }
218
219 for (sql, fk_val, pk_val) in self_ref_updates {
220 if let Err(e) = txn
221 .conn()
222 .execute(&sql, &[DbValue::from(fk_val), DbValue::from(pk_val)])
223 .await
224 {
225 return self.fail_save(txn, &save_ctx, e).await;
226 }
227 }
228 }
229
230 for link in &fixup_links {
232 if link.through_table.is_none() {
233 continue;
234 }
235 let (Some(table), Some(parent_col), Some(child_col)) = (
236 link.through_table.as_ref(),
237 link.through_parent_fk_col.as_ref(),
238 link.through_child_fk_col.as_ref(),
239 ) else {
240 continue;
241 };
242
243 let parent_pk = {
244 let Some(parent_saver) = self.savers.get(&link.parent_type_id) else {
245 continue;
246 };
247 let Some(parent_set) = self.sets.get(&link.parent_type_id) else {
248 continue;
249 };
250 parent_saver.get_pk_at(parent_set.as_ref(), link.parent_entry_idx)
251 };
252 let Some(parent_pk) = parent_pk else {
253 continue;
254 };
255
256 let mut child_pks: Vec<i64> = Vec::new();
257 {
258 let Some(child_saver) = self.savers.get(&link.child_type_id) else {
259 continue;
260 };
261 let Some(child_set) = self.sets.get(&link.child_type_id) else {
262 continue;
263 };
264 for &child_idx in &link.child_entry_indices {
265 if let Some(child_pk) = child_saver.get_pk_at(child_set.as_ref(), child_idx) {
266 child_pks.push(child_pk);
267 }
268 }
269 }
270
271 if !child_pks.is_empty() {
272 let sql = cascade::m2m_insert_sql(table, parent_col, child_col, child_pks.len());
273 let mut params: Vec<DbValue> = Vec::with_capacity(child_pks.len() * 2);
274 for child_pk in &child_pks {
275 params.push(DbValue::from(parent_pk));
276 params.push(DbValue::from(*child_pk));
277 }
278 if let Err(e) = txn.conn().execute(&sql, ¶ms).await {
279 return self.fail_save(txn, &save_ctx, e).await;
280 }
281 total_added += child_pks.len();
282 }
283 }
284
285 for type_id in &insert_order {
287 if !self.sets.contains_key(type_id) || !self.savers.contains_key(type_id) {
288 continue;
289 }
290 let Some(saver) = self.savers.get(type_id) else {
291 continue;
292 };
293 let Some(set) = self.sets.get_mut(type_id) else {
294 continue;
295 };
296 let Some(meta) = configured_metas
297 .get(type_id)
298 .or_else(|| self.entity_metas.get(type_id))
299 else {
300 return self
301 .fail_save(
302 txn,
303 &save_ctx,
304 EFError::configuration(format!(
305 "entity metadata not found for {:?}",
306 type_id
307 )),
308 )
309 .await;
310 };
311 let n = match saver
312 .upsert_added(txn.conn(), &*self.provider, set.as_mut(), meta)
313 .await
314 {
315 Ok(n) => n,
316 Err(e) => return self.fail_save(txn, &save_ctx, e).await,
317 };
318 total_added += n;
319 }
320
321 for type_id in &insert_order {
323 if !self.sets.contains_key(type_id) || !self.savers.contains_key(type_id) {
324 continue;
325 }
326 let Some(saver) = self.savers.get(type_id) else {
327 continue;
328 };
329 let Some(set) = self.sets.get_mut(type_id) else {
330 continue;
331 };
332 let Some(meta) = configured_metas
333 .get(type_id)
334 .or_else(|| self.entity_metas.get(type_id))
335 else {
336 return self
337 .fail_save(
338 txn,
339 &save_ctx,
340 EFError::configuration(format!(
341 "entity metadata not found for {:?}",
342 type_id
343 )),
344 )
345 .await;
346 };
347 let n = match saver
348 .update_modified(txn.conn(), &*self.provider, set.as_mut(), meta)
349 .await
350 {
351 Ok(n) => n,
352 Err(e) => return self.fail_save(txn, &save_ctx, e).await,
353 };
354 total_updated += n;
355 }
356
357 for directive in &delete_directives {
359 if directive.action != CascadeDeleteAction::SetNull {
360 continue;
361 }
362 let sql = format!(
363 "UPDATE {} SET {} = NULL WHERE {} = ?",
364 directive.table, directive.fk_column, directive.fk_column
365 );
366 let params = vec![DbValue::from(directive.principal_pk)];
367 if let Err(e) = txn.conn().execute(&sql, ¶ms).await {
368 return self.fail_save(txn, &save_ctx, e).await;
369 }
370 }
371
372 for type_id in &delete_order {
374 if !self.sets.contains_key(type_id) || !self.savers.contains_key(type_id) {
375 continue;
376 }
377 let Some(saver) = self.savers.get(type_id) else {
378 continue;
379 };
380 let Some(set) = self.sets.get_mut(type_id) else {
381 continue;
382 };
383 let Some(meta) = configured_metas
384 .get(type_id)
385 .or_else(|| self.entity_metas.get(type_id))
386 else {
387 return self
388 .fail_save(
389 txn,
390 &save_ctx,
391 EFError::configuration(format!(
392 "entity metadata not found for {:?}",
393 type_id
394 )),
395 )
396 .await;
397 };
398 let n = match saver
399 .delete_deleted(txn.conn(), &*self.provider, set.as_mut(), meta)
400 .await
401 {
402 Ok(n) => n,
403 Err(e) => return self.fail_save(txn, &save_ctx, e).await,
404 };
405 total_deleted += n;
406 }
407
408 for directive in &delete_directives {
410 if directive.action != CascadeDeleteAction::Delete {
411 continue;
412 }
413 let sql = format!(
414 "DELETE FROM {} WHERE {} = ?",
415 directive.table, directive.fk_column
416 );
417 let params = vec![DbValue::from(directive.principal_pk)];
418 if let Err(e) = txn.conn().execute(&sql, ¶ms).await {
419 return self.fail_save(txn, &save_ctx, e).await;
420 }
421 }
422
423 match txn {
425 TxnSource::Ambient(t) => {
426 self.ambient_transaction = Some(t);
427 }
428 TxnSource::Managed(mut conn) => {
429 if let Err(e) = conn.commit_transaction().await {
430 self.interceptor_pipeline
431 .on_save_failed(&save_ctx, &e)
432 .await;
433 return Err(e);
434 }
435 }
436 }
437 self.change_tracker.accept_all_changes();
438 for type_id in &type_ids {
439 let Some(saver) = self.savers.get(type_id) else {
440 continue;
441 };
442 let Some(set) = self.sets.get_mut(type_id) else {
443 continue;
444 };
445 saver.accept_all_changes(set.as_mut());
446 }
447
448 let result_ctx = SaveChangesResultContext {
449 added: total_added,
450 updated: total_updated,
451 deleted: total_deleted,
452 };
453 self.interceptor_pipeline
454 .on_saved(&save_ctx, &result_ctx)
455 .await?;
456
457 Ok(SaveChangesResult {
458 added: total_added,
459 updated: total_updated,
460 deleted: total_deleted,
461 })
462 }
463
464 async fn fail_save(
467 &mut self,
468 txn: TxnSource,
469 save_ctx: &SaveChangesContext,
470 e: EFError,
471 ) -> EFResult<SaveChangesResult> {
472 match txn {
473 TxnSource::Managed(mut conn) => {
474 let _ = conn.rollback_transaction().await;
475 }
476 TxnSource::Ambient(t) => {
477 self.ambient_transaction = Some(t);
478 }
479 }
480 self.interceptor_pipeline.on_save_failed(save_ctx, &e).await;
481 Err(e)
482 }
483}