1use crate::clause::Where;
11use crate::expr::{Dialect, Expr};
12use asupersync::{Cx, Outcome};
13use sqlmodel_core::{
14 Connection, FieldInfo, InheritanceStrategy, Model, Row, TransactionOps, Value,
15};
16use std::collections::HashSet;
17use std::marker::PhantomData;
18
19fn is_joined_inheritance_child<M: Model>() -> bool {
20 let inh = M::inheritance();
21 inh.strategy == InheritanceStrategy::Joined && inh.parent.is_some()
22}
23
24#[derive(Debug, Clone, Copy, PartialEq, Eq)]
25enum JoinedTableTarget {
26 Parent,
27 Child,
28}
29
30type JoinedSetPairs = Vec<(&'static str, Value)>;
31
32#[allow(clippy::result_large_err)]
33fn joined_parent_meta<M: Model>()
34-> Result<(&'static str, &'static [FieldInfo]), sqlmodel_core::Error> {
35 let inh = M::inheritance();
36 let Some(parent_table) = inh.parent else {
37 return Err(sqlmodel_core::Error::Custom(
38 "joined-table inheritance child missing parent table metadata".to_string(),
39 ));
40 };
41 let Some(parent_fields_fn) = inh.parent_fields_fn else {
42 return Err(sqlmodel_core::Error::Custom(
43 "joined-table inheritance child missing parent_fields_fn metadata".to_string(),
44 ));
45 };
46 Ok((parent_table, parent_fields_fn()))
47}
48
49#[allow(clippy::result_large_err)]
50fn classify_joined_column<M: Model>(
51 column: &str,
52 parent_table: &'static str,
53 parent_fields: &'static [FieldInfo],
54) -> Result<(JoinedTableTarget, &'static str), sqlmodel_core::Error> {
55 let child_fields = M::fields();
56
57 let child_lookup = |name: &str| -> Option<&'static str> {
58 child_fields
59 .iter()
60 .find(|f| f.column_name == name)
61 .map(|f| f.column_name)
62 };
63 let parent_lookup = |name: &str| -> Option<&'static str> {
64 parent_fields
65 .iter()
66 .find(|f| f.column_name == name)
67 .map(|f| f.column_name)
68 };
69
70 if let Some((table, col)) = column.split_once('.') {
71 if table == parent_table {
72 return parent_lookup(col)
73 .map(|c| (JoinedTableTarget::Parent, c))
74 .ok_or_else(|| {
75 sqlmodel_core::Error::Custom(format!(
76 "unknown parent column '{col}' for joined-table inheritance child"
77 ))
78 });
79 }
80 if table == M::TABLE_NAME {
81 return child_lookup(col)
82 .map(|c| (JoinedTableTarget::Child, c))
83 .ok_or_else(|| {
84 sqlmodel_core::Error::Custom(format!(
85 "unknown child column '{col}' for joined-table inheritance child"
86 ))
87 });
88 }
89 return Err(sqlmodel_core::Error::Custom(format!(
90 "unknown table qualifier '{table}' for joined-table inheritance DML; expected '{}' or '{}'",
91 parent_table,
92 M::TABLE_NAME
93 )));
94 }
95
96 let in_parent = parent_lookup(column);
97 let in_child = child_lookup(column);
98 match (in_parent, in_child) {
99 (Some(c), None) => Ok((JoinedTableTarget::Parent, c)),
100 (None, Some(c)) => Ok((JoinedTableTarget::Child, c)),
101 (Some(_), Some(_)) => Err(sqlmodel_core::Error::Custom(format!(
102 "ambiguous joined-table inheritance column '{column}' exists in both parent and child tables; qualify as '{parent_table}.{column}' or '{}.{column}'",
103 M::TABLE_NAME
104 ))),
105 (None, None) => Err(sqlmodel_core::Error::Custom(format!(
106 "unknown joined-table inheritance column '{column}'"
107 ))),
108 }
109}
110
111#[allow(clippy::result_large_err)]
112fn build_joined_pk_select_sql<M: Model>(
113 dialect: Dialect,
114 where_clause: Option<&Where>,
115 param_offset: usize,
116) -> Result<(String, Vec<Value>), sqlmodel_core::Error> {
117 let (parent_table, _parent_fields) = joined_parent_meta::<M>()?;
118 let pk_cols = M::PRIMARY_KEY;
119 if pk_cols.is_empty() {
120 return Err(sqlmodel_core::Error::Custom(
121 "joined-table inheritance DML requires a primary key".to_string(),
122 ));
123 }
124
125 let mut sql = String::new();
126 sql.push_str("SELECT ");
127 sql.push_str(
129 &pk_cols
130 .iter()
131 .map(|c| format!("{}.{}", M::TABLE_NAME, c))
132 .collect::<Vec<_>>()
133 .join(", "),
134 );
135 sql.push_str(" FROM ");
136 sql.push_str(M::TABLE_NAME);
137 sql.push_str(" JOIN ");
138 sql.push_str(parent_table);
139 sql.push_str(" ON ");
140 sql.push_str(
141 &pk_cols
142 .iter()
143 .map(|c| format!("{}.{} = {}.{}", M::TABLE_NAME, c, parent_table, c))
144 .collect::<Vec<_>>()
145 .join(" AND "),
146 );
147
148 let mut params = Vec::new();
149 if let Some(w) = where_clause {
150 let (where_sql, where_params) = w.build_with_dialect(dialect, param_offset);
151 sql.push_str(" WHERE ");
152 sql.push_str(&where_sql);
153 params.extend(where_params);
154 }
155
156 Ok((sql, params))
157}
158
159#[allow(clippy::result_large_err)]
160fn extract_pk_values_from_rows(
161 rows: Vec<Row>,
162 pk_col_count: usize,
163) -> Result<Vec<Vec<Value>>, sqlmodel_core::Error> {
164 let mut pk_values = Vec::with_capacity(rows.len());
165 for row in rows {
166 if row.len() < pk_col_count {
167 return Err(sqlmodel_core::Error::Custom(format!(
168 "joined-table inheritance PK lookup returned {} columns; expected at least {}",
169 row.len(),
170 pk_col_count
171 )));
172 }
173 let mut vals = Vec::with_capacity(pk_col_count);
174 for i in 0..pk_col_count {
175 let Some(v) = row.get(i) else {
176 return Err(sqlmodel_core::Error::Custom(format!(
177 "joined-table inheritance PK lookup missing column index {i}"
178 )));
179 };
180 vals.push(v.clone());
181 }
182 pk_values.push(vals);
183 }
184 Ok(pk_values)
185}
186
187async fn select_joined_pk_values_in_tx<Tx: TransactionOps, M: Model>(
188 tx: &Tx,
189 cx: &Cx,
190 dialect: Dialect,
191 where_clause: Option<&Where>,
192) -> Outcome<Vec<Vec<Value>>, sqlmodel_core::Error> {
193 let pk_cols = M::PRIMARY_KEY;
194 let (pk_sql, pk_params) = match build_joined_pk_select_sql::<M>(dialect, where_clause, 0) {
195 Ok(v) => v,
196 Err(e) => return Outcome::Err(e),
197 };
198 match tx.query(cx, &pk_sql, &pk_params).await {
199 Outcome::Ok(rows) => match extract_pk_values_from_rows(rows, pk_cols.len()) {
200 Ok(vals) => Outcome::Ok(vals),
201 Err(e) => Outcome::Err(e),
202 },
203 Outcome::Err(e) => Outcome::Err(e),
204 Outcome::Cancelled(r) => Outcome::Cancelled(r),
205 Outcome::Panicked(p) => Outcome::Panicked(p),
206 }
207}
208
209#[allow(clippy::result_large_err)]
210fn split_explicit_joined_sets<M: Model>(
211 explicit_sets: &[SetClause],
212 parent_table: &'static str,
213 parent_fields: &'static [FieldInfo],
214) -> Result<(JoinedSetPairs, JoinedSetPairs), sqlmodel_core::Error> {
215 let mut parent_sets = Vec::new();
216 let mut child_sets = Vec::new();
217
218 for set in explicit_sets {
219 let (target, col) = classify_joined_column::<M>(&set.column, parent_table, parent_fields)?;
220 if M::PRIMARY_KEY.contains(&col) {
221 return Err(sqlmodel_core::Error::Custom(format!(
222 "joined-table inheritance update does not support setting primary key column '{col}'"
223 )));
224 }
225 match target {
226 JoinedTableTarget::Parent => parent_sets.push((col, set.value.clone())),
227 JoinedTableTarget::Child => child_sets.push((col, set.value.clone())),
228 }
229 }
230
231 Ok((parent_sets, child_sets))
232}
233
234fn build_pk_in_where(
235 dialect: Dialect,
236 pk_cols: &[&'static str],
237 pk_values: &[Vec<Value>],
238 param_offset: usize,
239) -> (String, Vec<Value>) {
240 let mut params: Vec<Value> = Vec::new();
241
242 if pk_cols.is_empty() || pk_values.is_empty() {
243 return (String::new(), params);
244 }
245
246 if pk_cols.len() == 1 {
247 let col = pk_cols[0];
248 let mut placeholders = Vec::new();
249 for vals in pk_values {
250 if vals.len() != 1 {
251 continue;
252 }
253 params.push(vals[0].clone());
254 placeholders.push(dialect.placeholder(param_offset + params.len()));
255 }
256 return (format!("{col} IN ({})", placeholders.join(", ")), params);
257 }
258
259 let cols_tuple = format!("({})", pk_cols.join(", "));
261 let mut groups = Vec::new();
262 for vals in pk_values {
263 if vals.len() != pk_cols.len() {
264 continue;
265 }
266 let mut ph = Vec::new();
267 for v in vals {
268 params.push(v.clone());
269 ph.push(dialect.placeholder(param_offset + params.len()));
270 }
271 groups.push(format!("({})", ph.join(", ")));
272 }
273
274 (format!("{cols_tuple} IN ({})", groups.join(", ")), params)
275}
276
277fn build_pk_in_where_qualified(
278 dialect: Dialect,
279 table: &str,
280 pk_cols: &[&'static str],
281 pk_values: &[Vec<Value>],
282 param_offset: usize,
283) -> (String, Vec<Value>) {
284 let qualified_cols: Vec<String> = pk_cols.iter().map(|c| format!("{table}.{c}")).collect();
285
286 let mut params: Vec<Value> = Vec::new();
287 if qualified_cols.is_empty() || pk_values.is_empty() {
288 return (String::new(), params);
289 }
290
291 if qualified_cols.len() == 1 {
292 let col = &qualified_cols[0];
293 let mut placeholders = Vec::new();
294 for vals in pk_values {
295 if vals.len() != 1 {
296 continue;
297 }
298 params.push(vals[0].clone());
299 placeholders.push(dialect.placeholder(param_offset + params.len()));
300 }
301 return (format!("{col} IN ({})", placeholders.join(", ")), params);
302 }
303
304 let cols_tuple = format!("({})", qualified_cols.join(", "));
305 let mut groups = Vec::new();
306 for vals in pk_values {
307 if vals.len() != qualified_cols.len() {
308 continue;
309 }
310 let mut ph = Vec::new();
311 for v in vals {
312 params.push(v.clone());
313 ph.push(dialect.placeholder(param_offset + params.len()));
314 }
315 groups.push(format!("({})", ph.join(", ")));
316 }
317
318 (format!("{cols_tuple} IN ({})", groups.join(", ")), params)
319}
320
321fn build_update_sql_for_table_pk_in(
322 dialect: Dialect,
323 table: &str,
324 pk_cols: &[&'static str],
325 pk_values: &[Vec<Value>],
326 set_pairs: &[(&'static str, Value)],
327) -> (String, Vec<Value>) {
328 let mut params = Vec::new();
329 let mut set_clauses = Vec::new();
330 for (col, value) in set_pairs {
331 set_clauses.push(format!(
332 "{} = {}",
333 col,
334 dialect.placeholder(params.len() + 1)
335 ));
336 params.push(value.clone());
337 }
338 if set_clauses.is_empty() {
339 return (String::new(), Vec::new());
340 }
341
342 let (pk_where, pk_params) = build_pk_in_where(dialect, pk_cols, pk_values, params.len());
343 if pk_where.is_empty() {
344 return (String::new(), Vec::new());
345 }
346
347 let sql = format!(
348 "UPDATE {} SET {} WHERE {}",
349 table,
350 set_clauses.join(", "),
351 pk_where
352 );
353 params.extend(pk_params);
354 (sql, params)
355}
356
357fn build_delete_sql_for_table_pk_in(
358 dialect: Dialect,
359 table: &str,
360 pk_cols: &[&'static str],
361 pk_values: &[Vec<Value>],
362) -> (String, Vec<Value>) {
363 let (pk_where, pk_params) = build_pk_in_where(dialect, pk_cols, pk_values, 0);
364 if pk_where.is_empty() {
365 return (String::new(), Vec::new());
366 }
367 (format!("DELETE FROM {table} WHERE {pk_where}"), pk_params)
368}
369
370#[allow(clippy::result_large_err)]
371fn build_joined_child_select_sql_by_pk_in<M: Model>(
372 dialect: Dialect,
373 pk_cols: &[&'static str],
374 pk_values: &[Vec<Value>],
375) -> Result<(String, Vec<Value>), sqlmodel_core::Error> {
376 let (parent_table, parent_fields) = joined_parent_meta::<M>()?;
377 if pk_cols.is_empty() {
378 return Err(sqlmodel_core::Error::Custom(
379 "joined-table inheritance returning requires a primary key".to_string(),
380 ));
381 }
382
383 let child_cols: Vec<&'static str> = M::fields().iter().map(|f| f.column_name).collect();
384 let parent_cols: Vec<&'static str> = parent_fields.iter().map(|f| f.column_name).collect();
385
386 let mut col_parts = Vec::new();
387 for col in &child_cols {
388 col_parts.push(format!(
389 "{}.{} AS {}__{}",
390 M::TABLE_NAME,
391 col,
392 M::TABLE_NAME,
393 col
394 ));
395 }
396 for col in &parent_cols {
397 col_parts.push(format!(
398 "{}.{} AS {}__{}",
399 parent_table, col, parent_table, col
400 ));
401 }
402
403 let mut sql = String::new();
404 sql.push_str("SELECT ");
405 sql.push_str(&col_parts.join(", "));
406 sql.push_str(" FROM ");
407 sql.push_str(M::TABLE_NAME);
408 sql.push_str(" JOIN ");
409 sql.push_str(parent_table);
410 sql.push_str(" ON ");
411 sql.push_str(
412 &pk_cols
413 .iter()
414 .map(|c| format!("{}.{} = {}.{}", M::TABLE_NAME, c, parent_table, c))
415 .collect::<Vec<_>>()
416 .join(" AND "),
417 );
418
419 let (pk_where, pk_params) =
420 build_pk_in_where_qualified(dialect, M::TABLE_NAME, pk_cols, pk_values, 0);
421 if pk_where.is_empty() {
422 return Ok((String::new(), Vec::new()));
423 }
424 sql.push_str(" WHERE ");
425 sql.push_str(&pk_where);
426
427 Ok((sql, pk_params))
428}
429
430fn rewrite_insert_as_ignore(sql: &mut String) {
431 if let Some(rest) = sql.strip_prefix("INSERT INTO ") {
432 *sql = format!("INSERT IGNORE INTO {rest}");
433 }
434}
435
436fn append_on_conflict_clause(
437 dialect: Dialect,
438 sql: &mut String,
439 pk_cols: &[&'static str],
440 insert_columns: &[&'static str],
441 on_conflict: &OnConflict,
442) {
443 if dialect == Dialect::Mysql {
444 match on_conflict {
445 OnConflict::DoNothing => {
446 rewrite_insert_as_ignore(sql);
447 return;
448 }
449 OnConflict::DoUpdate { columns, .. } => {
450 let update_cols: Vec<String> = if columns.is_empty() {
451 insert_columns
452 .iter()
453 .filter(|c| !pk_cols.contains(c))
454 .map(|c| (*c).to_string())
455 .collect()
456 } else {
457 columns.clone()
458 };
459
460 if update_cols.is_empty() {
461 rewrite_insert_as_ignore(sql);
462 return;
463 }
464
465 sql.push_str(" ON DUPLICATE KEY UPDATE ");
466 sql.push_str(
467 &update_cols
468 .iter()
469 .map(|c| format!("{c} = VALUES({c})"))
470 .collect::<Vec<_>>()
471 .join(", "),
472 );
473 return;
474 }
475 }
476 }
477
478 match on_conflict {
479 OnConflict::DoNothing => {
480 sql.push_str(" ON CONFLICT DO NOTHING");
481 }
482 OnConflict::DoUpdate { columns, target } => {
483 sql.push_str(" ON CONFLICT");
484
485 let effective_target: Vec<String> = if target.is_empty() {
486 pk_cols.iter().map(|c| (*c).to_string()).collect()
487 } else {
488 target.clone()
489 };
490
491 if effective_target.is_empty() {
492 sql.push_str(" DO NOTHING");
493 return;
494 }
495
496 sql.push_str(" (");
497 sql.push_str(&effective_target.join(", "));
498 sql.push(')');
499
500 let update_cols: Vec<String> = if columns.is_empty() {
501 insert_columns
502 .iter()
503 .filter(|c| !pk_cols.contains(c))
504 .map(|c| (*c).to_string())
505 .collect()
506 } else {
507 columns.clone()
508 };
509
510 if update_cols.is_empty() {
511 sql.push_str(" DO NOTHING");
512 return;
513 }
514
515 sql.push_str(" DO UPDATE SET ");
516 sql.push_str(
517 &update_cols
518 .iter()
519 .map(|c| format!("{c} = EXCLUDED.{c}"))
520 .collect::<Vec<_>>()
521 .join(", "),
522 );
523 }
524 }
525}
526
527fn build_insert_sql_for_table_with_columns(
528 dialect: Dialect,
529 table: &str,
530 fields: &[FieldInfo],
531 row: &[(&'static str, Value)],
532 returning: Option<&str>,
533) -> (String, Vec<Value>, Vec<&'static str>) {
534 let insert_fields: Vec<_> = row
535 .iter()
536 .map(|(name, value)| {
537 let field = fields.iter().find(|f| f.column_name == *name);
538 if let Some(f) = field
539 && f.auto_increment
540 && matches!(value, Value::Null)
541 {
542 return (*name, Value::Default);
543 }
544 (*name, value.clone())
545 })
546 .collect();
547
548 let mut columns = Vec::new();
549 let mut placeholders = Vec::new();
550 let mut params = Vec::new();
551
552 for (name, value) in insert_fields {
553 if matches!(value, Value::Default) && dialect == Dialect::Sqlite {
554 continue;
556 }
557
558 columns.push(name);
559
560 if matches!(value, Value::Default) {
561 placeholders.push("DEFAULT".to_string());
562 } else {
563 params.push(value);
564 placeholders.push(dialect.placeholder(params.len()));
565 }
566 }
567
568 let mut sql = if columns.is_empty() {
569 format!("INSERT INTO {} DEFAULT VALUES", table)
570 } else {
571 format!(
572 "INSERT INTO {} ({}) VALUES ({})",
573 table,
574 columns.join(", "),
575 placeholders.join(", ")
576 )
577 };
578
579 if let Some(ret) = returning {
580 sql.push_str(" RETURNING ");
581 sql.push_str(ret);
582 }
583
584 (sql, params, columns)
585}
586
587fn build_insert_sql_for_table(
588 dialect: Dialect,
589 table: &str,
590 fields: &[FieldInfo],
591 row: &[(&'static str, Value)],
592 returning: Option<&str>,
593) -> (String, Vec<Value>) {
594 let (sql, params, _cols) =
595 build_insert_sql_for_table_with_columns(dialect, table, fields, row, returning);
596 (sql, params)
597}
598
599fn build_update_sql_for_table(
600 dialect: Dialect,
601 table: &str,
602 pk_cols: &[&'static str],
603 pk_vals: &[Value],
604 set_pairs: &[(&'static str, Value)],
605) -> (String, Vec<Value>) {
606 let mut params = Vec::new();
607 let mut set_clauses = Vec::new();
608 for (col, value) in set_pairs {
609 set_clauses.push(format!(
610 "{} = {}",
611 col,
612 dialect.placeholder(params.len() + 1)
613 ));
614 params.push(value.clone());
615 }
616
617 if set_clauses.is_empty() {
618 return (String::new(), Vec::new());
619 }
620
621 let mut sql = format!("UPDATE {} SET {}", table, set_clauses.join(", "));
622 if !pk_cols.is_empty() && pk_cols.len() == pk_vals.len() {
623 let where_parts: Vec<String> = pk_cols
624 .iter()
625 .enumerate()
626 .map(|(i, col)| format!("{} = {}", col, dialect.placeholder(params.len() + i + 1)))
627 .collect();
628 sql.push_str(" WHERE ");
629 sql.push_str(&where_parts.join(" AND "));
630 params.extend_from_slice(pk_vals);
631 }
632
633 (sql, params)
634}
635
636fn extract_single_pk_i64(pk_vals: &[Value]) -> Option<i64> {
637 if pk_vals.len() != 1 {
638 return None;
639 }
640 match &pk_vals[0] {
641 Value::BigInt(v) => Some(*v),
642 Value::Int(v) => Some(i64::from(*v)),
643 _ => None,
644 }
645}
646
647async fn insert_joined_model_in_tx<Tx: TransactionOps, M: Model>(
648 tx: &Tx,
649 cx: &Cx,
650 dialect: Dialect,
651 model: &M,
652 parent_table: &'static str,
653 parent_fields: &'static [FieldInfo],
654) -> Outcome<(u64, Vec<Value>), sqlmodel_core::Error> {
655 let Some(parent_row) = model.joined_parent_row() else {
656 return Outcome::Err(sqlmodel_core::Error::Custom(
657 "joined-table inheritance child missing joined_parent_row() implementation".to_string(),
658 ));
659 };
660
661 let pk_cols = M::PRIMARY_KEY;
662 if pk_cols.is_empty() {
663 return Outcome::Err(sqlmodel_core::Error::Custom(
664 "joined-table inheritance insert requires a primary key column".to_string(),
665 ));
666 }
667 let mut effective_pk_vals = model.primary_key_value();
668 let pk_col = pk_cols.first().copied();
669 let needs_generated_id = pk_col.is_some()
670 && effective_pk_vals.len() == 1
671 && parent_fields
672 .iter()
673 .find(|f| f.column_name == pk_col.unwrap_or("") && f.primary_key)
674 .is_some_and(|f| f.auto_increment)
675 && effective_pk_vals[0].is_null();
676
677 let mut inserted_id: Option<i64> = None;
678 if dialect == Dialect::Postgres && needs_generated_id {
679 let Some(pk_col) = pk_col else {
680 return Outcome::Err(sqlmodel_core::Error::Custom(
681 "joined-table inheritance insert requires a primary key column".to_string(),
682 ));
683 };
684 let (sql, params, _cols) = build_insert_sql_for_table_with_columns(
685 dialect,
686 parent_table,
687 parent_fields,
688 &parent_row,
689 Some(pk_col),
690 );
691 match tx.query_one(cx, &sql, ¶ms).await {
692 Outcome::Ok(Some(row)) => match row.get_as::<i64>(0) {
693 Ok(v) => inserted_id = Some(v),
694 Err(e) => return Outcome::Err(e),
695 },
696 Outcome::Ok(None) => {
697 return Outcome::Err(sqlmodel_core::Error::Custom(
698 "base insert returned no row".to_string(),
699 ));
700 }
701 Outcome::Err(e) => return Outcome::Err(e),
702 Outcome::Cancelled(r) => return Outcome::Cancelled(r),
703 Outcome::Panicked(p) => return Outcome::Panicked(p),
704 }
705 } else {
706 let (sql, params, _cols) = build_insert_sql_for_table_with_columns(
707 dialect,
708 parent_table,
709 parent_fields,
710 &parent_row,
711 None,
712 );
713 match tx.execute(cx, &sql, ¶ms).await {
714 Outcome::Ok(_) => {}
715 Outcome::Err(e) => return Outcome::Err(e),
716 Outcome::Cancelled(r) => return Outcome::Cancelled(r),
717 Outcome::Panicked(p) => return Outcome::Panicked(p),
718 }
719
720 if needs_generated_id {
721 let id_sql = match dialect {
722 Dialect::Sqlite => "SELECT last_insert_rowid()",
723 Dialect::Mysql => "SELECT LAST_INSERT_ID()",
724 Dialect::Postgres => unreachable!(),
725 };
726 match tx.query_one(cx, id_sql, &[]).await {
727 Outcome::Ok(Some(row)) => match row.get_as::<i64>(0) {
728 Ok(v) => inserted_id = Some(v),
729 Err(e) => return Outcome::Err(e),
730 },
731 Outcome::Ok(None) => {
732 return Outcome::Err(sqlmodel_core::Error::Custom(
733 "failed to fetch last insert id".to_string(),
734 ));
735 }
736 Outcome::Err(e) => return Outcome::Err(e),
737 Outcome::Cancelled(r) => return Outcome::Cancelled(r),
738 Outcome::Panicked(p) => return Outcome::Panicked(p),
739 }
740 }
741 }
742
743 let mut child_row = model.to_row();
744 if let (Some(pk_col), Some(id)) = (pk_col, inserted_id) {
745 if pk_cols.len() != 1 {
746 return Outcome::Err(sqlmodel_core::Error::Custom(
747 "joined-table inheritance auto-increment insert currently requires a single-column primary key"
748 .to_string(),
749 ));
750 }
751 for (name, value) in &mut child_row {
752 if *name == pk_col && value.is_null() {
753 *value = Value::BigInt(id);
754 }
755 }
756 if effective_pk_vals.len() == 1 && effective_pk_vals[0].is_null() {
757 effective_pk_vals[0] = Value::BigInt(id);
758 }
759 }
760
761 let (child_sql, child_params, _cols) = build_insert_sql_for_table_with_columns(
762 dialect,
763 M::TABLE_NAME,
764 M::fields(),
765 &child_row,
766 None,
767 );
768 match tx.execute(cx, &child_sql, &child_params).await {
769 Outcome::Ok(count) => Outcome::Ok((count, effective_pk_vals)),
770 Outcome::Err(e) => Outcome::Err(e),
771 Outcome::Cancelled(r) => Outcome::Cancelled(r),
772 Outcome::Panicked(p) => Outcome::Panicked(p),
773 }
774}
775
776async fn tx_rollback_best_effort<Tx: TransactionOps>(tx: Tx, cx: &Cx) {
777 let _ = tx.rollback(cx).await;
778}
779
780#[derive(Debug, Clone)]
784pub enum OnConflict {
785 DoNothing,
787 DoUpdate {
789 columns: Vec<String>,
791 target: Vec<String>,
793 },
794}
795
796#[derive(Debug)]
813pub struct InsertBuilder<'a, M: Model> {
814 model: &'a M,
815 returning: bool,
816 on_conflict: Option<OnConflict>,
817}
818
819impl<'a, M: Model> InsertBuilder<'a, M> {
820 pub fn new(model: &'a M) -> Self {
822 Self {
823 model,
824 returning: false,
825 on_conflict: None,
826 }
827 }
828
829 pub fn returning(mut self) -> Self {
833 self.returning = true;
834 self
835 }
836
837 pub fn on_conflict_do_nothing(mut self) -> Self {
842 self.on_conflict = Some(OnConflict::DoNothing);
843 self
844 }
845
846 pub fn on_conflict_do_update(mut self, columns: &[&str]) -> Self {
860 self.on_conflict = Some(OnConflict::DoUpdate {
861 columns: columns.iter().map(|s| s.to_string()).collect(),
862 target: Vec::new(), });
864 self
865 }
866
867 pub fn on_conflict_target_do_update(mut self, target: &[&str], columns: &[&str]) -> Self {
874 self.on_conflict = Some(OnConflict::DoUpdate {
875 columns: columns.iter().map(|s| s.to_string()).collect(),
876 target: target.iter().map(|s| s.to_string()).collect(),
877 });
878 self
879 }
880
881 pub fn build(&self) -> (String, Vec<Value>) {
883 self.build_with_dialect(Dialect::default())
884 }
885
886 pub fn build_with_dialect(&self, dialect: Dialect) -> (String, Vec<Value>) {
888 let row = self.model.to_row();
889 let fields = M::fields();
890
891 let insert_fields: Vec<_> = row
892 .iter()
893 .map(|(name, value)| {
894 let field = fields.iter().find(|f| f.column_name == *name);
895 if let Some(f) = field
896 && f.auto_increment
897 && matches!(value, Value::Null)
898 {
899 return (*name, Value::Default);
900 }
901 (*name, value.clone())
902 })
903 .collect();
904
905 let mut columns = Vec::new();
906 let mut placeholders = Vec::new();
907 let mut params = Vec::new();
908
909 for (name, value) in insert_fields {
910 if matches!(value, Value::Default) && dialect == Dialect::Sqlite {
911 continue;
913 }
914
915 columns.push(name);
916
917 if matches!(value, Value::Default) {
918 placeholders.push("DEFAULT".to_string());
919 } else {
920 params.push(value);
921 placeholders.push(dialect.placeholder(params.len()));
922 }
923 }
924
925 let mut sql = if columns.is_empty() {
926 format!("INSERT INTO {} DEFAULT VALUES", M::TABLE_NAME)
927 } else {
928 format!(
929 "INSERT INTO {} ({}) VALUES ({})",
930 M::TABLE_NAME,
931 columns.join(", "),
932 placeholders.join(", ")
933 )
934 };
935
936 if let Some(on_conflict) = &self.on_conflict {
938 append_on_conflict_clause(dialect, &mut sql, M::PRIMARY_KEY, &columns, on_conflict);
939 }
940
941 if self.returning {
943 sql.push_str(" RETURNING *");
944 }
945
946 (sql, params)
947 }
948
949 pub async fn execute<C: Connection>(
951 self,
952 cx: &Cx,
953 conn: &C,
954 ) -> Outcome<i64, sqlmodel_core::Error> {
955 if is_joined_inheritance_child::<M>() {
956 let dialect = conn.dialect();
957 let on_conflict = self.on_conflict.clone();
958 let (parent_table, parent_fields) = match joined_parent_meta::<M>() {
959 Ok(v) => v,
960 Err(e) => return Outcome::Err(e),
961 };
962
963 let Some(parent_row) = self.model.joined_parent_row() else {
964 return Outcome::Err(sqlmodel_core::Error::Custom(
965 "joined-table inheritance child missing joined_parent_row() implementation"
966 .to_string(),
967 ));
968 };
969
970 let pk_vals = self.model.primary_key_value();
971 let pk_col = M::PRIMARY_KEY.first().copied();
972 let needs_generated_id = pk_col.is_some()
973 && pk_vals.len() == 1
974 && parent_fields
975 .iter()
976 .find(|f| f.column_name == pk_col.unwrap_or("") && f.primary_key)
977 .is_some_and(|f| f.auto_increment)
978 && pk_vals[0].is_null();
979
980 if on_conflict.is_some() {
981 if needs_generated_id || pk_vals.iter().any(|v| v.is_null()) {
982 return Outcome::Err(sqlmodel_core::Error::Custom(
983 "joined-table inheritance insert ON CONFLICT requires explicit primary key values (auto-increment upsert is not supported yet)"
984 .to_string(),
985 ));
986 }
987 if let Some(OnConflict::DoUpdate { target, .. }) = &on_conflict {
990 let pk_target: Vec<String> =
991 M::PRIMARY_KEY.iter().map(|c| (*c).to_string()).collect();
992 if !target.is_empty() && target != &pk_target {
993 return Outcome::Err(sqlmodel_core::Error::Custom(
994 "joined-table inheritance insert ON CONFLICT currently only supports the primary key as conflict target"
995 .to_string(),
996 ));
997 }
998 }
999 }
1000
1001 let parent_allowed: HashSet<&'static str> =
1002 parent_fields.iter().map(|f| f.column_name).collect();
1003 let child_allowed: HashSet<&'static str> =
1004 M::fields().iter().map(|f| f.column_name).collect();
1005
1006 let (parent_on_conflict, child_on_conflict) = match &on_conflict {
1007 None => (None, None),
1008 Some(OnConflict::DoNothing) => {
1009 (Some(OnConflict::DoNothing), Some(OnConflict::DoNothing))
1010 }
1011 Some(OnConflict::DoUpdate { columns, target }) => {
1012 for c in columns {
1014 if !parent_allowed.contains(c.as_str())
1015 && !child_allowed.contains(c.as_str())
1016 {
1017 return Outcome::Err(sqlmodel_core::Error::Custom(format!(
1018 "unknown joined-table inheritance ON CONFLICT update column '{c}'"
1019 )));
1020 }
1021 }
1022
1023 let parent_cols: Vec<String> = columns
1024 .iter()
1025 .filter(|c| parent_allowed.contains(c.as_str()))
1026 .cloned()
1027 .collect();
1028 let child_cols: Vec<String> = columns
1029 .iter()
1030 .filter(|c| child_allowed.contains(c.as_str()))
1031 .cloned()
1032 .collect();
1033
1034 (
1035 Some(OnConflict::DoUpdate {
1036 columns: parent_cols,
1037 target: target.clone(),
1038 }),
1039 Some(OnConflict::DoUpdate {
1040 columns: child_cols,
1041 target: target.clone(),
1042 }),
1043 )
1044 }
1045 };
1046
1047 let tx_out = conn.begin(cx).await;
1048 let tx = match tx_out {
1049 Outcome::Ok(t) => t,
1050 Outcome::Err(e) => return Outcome::Err(e),
1051 Outcome::Cancelled(r) => return Outcome::Cancelled(r),
1052 Outcome::Panicked(p) => return Outcome::Panicked(p),
1053 };
1054
1055 let mut inserted_id: Option<i64> = None;
1057 if dialect == Dialect::Postgres {
1058 let Some(pk_col) = pk_col else {
1059 tx_rollback_best_effort(tx, cx).await;
1060 return Outcome::Err(sqlmodel_core::Error::Custom(
1061 "joined-table inheritance insert requires a primary key column".to_string(),
1062 ));
1063 };
1064
1065 if needs_generated_id {
1066 let (sql, params, _cols) = build_insert_sql_for_table_with_columns(
1067 dialect,
1068 parent_table,
1069 parent_fields,
1070 &parent_row,
1071 Some(pk_col),
1072 );
1073 match tx.query_one(cx, &sql, ¶ms).await {
1074 Outcome::Ok(Some(row)) => match row.get_as::<i64>(0) {
1075 Ok(v) => inserted_id = Some(v),
1076 Err(e) => {
1077 tx_rollback_best_effort(tx, cx).await;
1078 return Outcome::Err(e);
1079 }
1080 },
1081 Outcome::Ok(None) => {
1082 tx_rollback_best_effort(tx, cx).await;
1083 return Outcome::Err(sqlmodel_core::Error::Custom(
1084 "base insert returned no row".to_string(),
1085 ));
1086 }
1087 Outcome::Err(e) => {
1088 tx_rollback_best_effort(tx, cx).await;
1089 return Outcome::Err(e);
1090 }
1091 Outcome::Cancelled(r) => {
1092 tx_rollback_best_effort(tx, cx).await;
1093 return Outcome::Cancelled(r);
1094 }
1095 Outcome::Panicked(p) => {
1096 tx_rollback_best_effort(tx, cx).await;
1097 return Outcome::Panicked(p);
1098 }
1099 }
1100 } else {
1101 let (mut sql, params, cols) = build_insert_sql_for_table_with_columns(
1102 dialect,
1103 parent_table,
1104 parent_fields,
1105 &parent_row,
1106 None,
1107 );
1108 if let Some(oc) = &parent_on_conflict {
1109 append_on_conflict_clause(dialect, &mut sql, M::PRIMARY_KEY, &cols, oc);
1110 }
1111 match tx.execute(cx, &sql, ¶ms).await {
1112 Outcome::Ok(_) => {}
1113 Outcome::Err(e) => {
1114 tx_rollback_best_effort(tx, cx).await;
1115 return Outcome::Err(e);
1116 }
1117 Outcome::Cancelled(r) => {
1118 tx_rollback_best_effort(tx, cx).await;
1119 return Outcome::Cancelled(r);
1120 }
1121 Outcome::Panicked(p) => {
1122 tx_rollback_best_effort(tx, cx).await;
1123 return Outcome::Panicked(p);
1124 }
1125 }
1126 }
1127 } else {
1128 let (mut sql, params, cols) = build_insert_sql_for_table_with_columns(
1129 dialect,
1130 parent_table,
1131 parent_fields,
1132 &parent_row,
1133 None,
1134 );
1135 if let Some(oc) = &parent_on_conflict {
1136 append_on_conflict_clause(dialect, &mut sql, M::PRIMARY_KEY, &cols, oc);
1137 }
1138 match tx.execute(cx, &sql, ¶ms).await {
1139 Outcome::Ok(_) => {}
1140 Outcome::Err(e) => {
1141 tx_rollback_best_effort(tx, cx).await;
1142 return Outcome::Err(e);
1143 }
1144 Outcome::Cancelled(r) => {
1145 tx_rollback_best_effort(tx, cx).await;
1146 return Outcome::Cancelled(r);
1147 }
1148 Outcome::Panicked(p) => {
1149 tx_rollback_best_effort(tx, cx).await;
1150 return Outcome::Panicked(p);
1151 }
1152 }
1153
1154 if needs_generated_id {
1155 let id_sql = match dialect {
1156 Dialect::Sqlite => "SELECT last_insert_rowid()",
1157 Dialect::Mysql => "SELECT LAST_INSERT_ID()",
1158 Dialect::Postgres => unreachable!(),
1159 };
1160 match tx.query_one(cx, id_sql, &[]).await {
1161 Outcome::Ok(Some(row)) => match row.get_as::<i64>(0) {
1162 Ok(v) => inserted_id = Some(v),
1163 Err(e) => {
1164 tx_rollback_best_effort(tx, cx).await;
1165 return Outcome::Err(e);
1166 }
1167 },
1168 Outcome::Ok(None) => {
1169 tx_rollback_best_effort(tx, cx).await;
1170 return Outcome::Err(sqlmodel_core::Error::Custom(
1171 "failed to fetch last insert id".to_string(),
1172 ));
1173 }
1174 Outcome::Err(e) => {
1175 tx_rollback_best_effort(tx, cx).await;
1176 return Outcome::Err(e);
1177 }
1178 Outcome::Cancelled(r) => {
1179 tx_rollback_best_effort(tx, cx).await;
1180 return Outcome::Cancelled(r);
1181 }
1182 Outcome::Panicked(p) => {
1183 tx_rollback_best_effort(tx, cx).await;
1184 return Outcome::Panicked(p);
1185 }
1186 }
1187 }
1188 }
1189
1190 let mut child_row = self.model.to_row();
1192 if let (Some(pk_col), Some(id)) = (pk_col, inserted_id) {
1193 if M::PRIMARY_KEY.len() != 1 {
1194 tx_rollback_best_effort(tx, cx).await;
1195 return Outcome::Err(sqlmodel_core::Error::Custom(
1196 "joined-table inheritance auto-increment insert currently requires a single-column primary key"
1197 .to_string(),
1198 ));
1199 }
1200
1201 for (name, value) in &mut child_row {
1202 if *name == pk_col && value.is_null() {
1203 *value = Value::BigInt(id);
1204 }
1205 }
1206 }
1207
1208 let (mut child_sql, child_params, child_cols) = build_insert_sql_for_table_with_columns(
1209 dialect,
1210 M::TABLE_NAME,
1211 M::fields(),
1212 &child_row,
1213 None,
1214 );
1215 if let Some(oc) = &child_on_conflict {
1216 append_on_conflict_clause(dialect, &mut child_sql, M::PRIMARY_KEY, &child_cols, oc);
1217 }
1218
1219 match tx.execute(cx, &child_sql, &child_params).await {
1220 Outcome::Ok(_) => {}
1221 Outcome::Err(e) => {
1222 tx_rollback_best_effort(tx, cx).await;
1223 return Outcome::Err(e);
1224 }
1225 Outcome::Cancelled(r) => {
1226 tx_rollback_best_effort(tx, cx).await;
1227 return Outcome::Cancelled(r);
1228 }
1229 Outcome::Panicked(p) => {
1230 tx_rollback_best_effort(tx, cx).await;
1231 return Outcome::Panicked(p);
1232 }
1233 }
1234
1235 match tx.commit(cx).await {
1236 Outcome::Ok(()) => {}
1237 Outcome::Err(e) => return Outcome::Err(e),
1238 Outcome::Cancelled(r) => return Outcome::Cancelled(r),
1239 Outcome::Panicked(p) => return Outcome::Panicked(p),
1240 }
1241
1242 let id = inserted_id
1243 .or_else(|| extract_single_pk_i64(&pk_vals))
1244 .unwrap_or(0);
1245 return Outcome::Ok(id);
1246 }
1247
1248 let (sql, params) = self.build_with_dialect(conn.dialect());
1249 conn.insert(cx, &sql, ¶ms).await
1250 }
1251
1252 pub async fn execute_returning<C: Connection>(
1256 mut self,
1257 cx: &Cx,
1258 conn: &C,
1259 ) -> Outcome<Option<Row>, sqlmodel_core::Error> {
1260 self.returning = true;
1261 if is_joined_inheritance_child::<M>() {
1262 if self.on_conflict.is_some() {
1263 return Outcome::Err(sqlmodel_core::Error::Custom(
1264 "joined-table inheritance insert_returning does not support ON CONFLICT; use execute() for ON CONFLICT semantics"
1265 .to_string(),
1266 ));
1267 }
1268
1269 let dialect = conn.dialect();
1270 let inh = M::inheritance();
1271 let Some(parent_table) = inh.parent else {
1272 return Outcome::Err(sqlmodel_core::Error::Custom(
1273 "joined-table inheritance child missing parent table metadata".to_string(),
1274 ));
1275 };
1276 let Some(parent_fields_fn) = inh.parent_fields_fn else {
1277 return Outcome::Err(sqlmodel_core::Error::Custom(
1278 "joined-table inheritance child missing parent_fields_fn metadata".to_string(),
1279 ));
1280 };
1281 let parent_fields = parent_fields_fn();
1282
1283 let Some(parent_row) = self.model.joined_parent_row() else {
1284 return Outcome::Err(sqlmodel_core::Error::Custom(
1285 "joined-table inheritance child missing joined_parent_row() implementation"
1286 .to_string(),
1287 ));
1288 };
1289
1290 let pk_vals = self.model.primary_key_value();
1291 let pk_col = M::PRIMARY_KEY.first().copied();
1292 let needs_generated_id = pk_col.is_some()
1293 && pk_vals.len() == 1
1294 && parent_fields
1295 .iter()
1296 .find(|f| f.column_name == pk_col.unwrap_or("") && f.primary_key)
1297 .is_some_and(|f| f.auto_increment)
1298 && pk_vals[0].is_null();
1299
1300 let tx_out = conn.begin(cx).await;
1301 let tx = match tx_out {
1302 Outcome::Ok(t) => t,
1303 Outcome::Err(e) => return Outcome::Err(e),
1304 Outcome::Cancelled(r) => return Outcome::Cancelled(r),
1305 Outcome::Panicked(p) => return Outcome::Panicked(p),
1306 };
1307
1308 let mut inserted_id: Option<i64> = None;
1309 if dialect == Dialect::Postgres {
1310 let Some(pk_col) = pk_col else {
1311 tx_rollback_best_effort(tx, cx).await;
1312 return Outcome::Err(sqlmodel_core::Error::Custom(
1313 "joined-table inheritance insert requires a primary key column".to_string(),
1314 ));
1315 };
1316
1317 let (sql, params) = build_insert_sql_for_table(
1318 dialect,
1319 parent_table,
1320 parent_fields,
1321 &parent_row,
1322 Some(pk_col),
1323 );
1324 match tx.query_one(cx, &sql, ¶ms).await {
1325 Outcome::Ok(Some(row)) => match row.get_as::<i64>(0) {
1326 Ok(v) => inserted_id = Some(v),
1327 Err(e) => {
1328 tx_rollback_best_effort(tx, cx).await;
1329 return Outcome::Err(e);
1330 }
1331 },
1332 Outcome::Ok(None) => {
1333 tx_rollback_best_effort(tx, cx).await;
1334 return Outcome::Err(sqlmodel_core::Error::Custom(
1335 "base insert returned no row".to_string(),
1336 ));
1337 }
1338 Outcome::Err(e) => {
1339 tx_rollback_best_effort(tx, cx).await;
1340 return Outcome::Err(e);
1341 }
1342 Outcome::Cancelled(r) => {
1343 tx_rollback_best_effort(tx, cx).await;
1344 return Outcome::Cancelled(r);
1345 }
1346 Outcome::Panicked(p) => {
1347 tx_rollback_best_effort(tx, cx).await;
1348 return Outcome::Panicked(p);
1349 }
1350 }
1351 } else {
1352 let (sql, params) = build_insert_sql_for_table(
1353 dialect,
1354 parent_table,
1355 parent_fields,
1356 &parent_row,
1357 None,
1358 );
1359 match tx.execute(cx, &sql, ¶ms).await {
1360 Outcome::Ok(_) => {}
1361 Outcome::Err(e) => {
1362 tx_rollback_best_effort(tx, cx).await;
1363 return Outcome::Err(e);
1364 }
1365 Outcome::Cancelled(r) => {
1366 tx_rollback_best_effort(tx, cx).await;
1367 return Outcome::Cancelled(r);
1368 }
1369 Outcome::Panicked(p) => {
1370 tx_rollback_best_effort(tx, cx).await;
1371 return Outcome::Panicked(p);
1372 }
1373 }
1374
1375 if needs_generated_id {
1376 let id_sql = match dialect {
1377 Dialect::Sqlite => "SELECT last_insert_rowid()",
1378 Dialect::Mysql => "SELECT LAST_INSERT_ID()",
1379 Dialect::Postgres => unreachable!(),
1380 };
1381 match tx.query_one(cx, id_sql, &[]).await {
1382 Outcome::Ok(Some(row)) => match row.get_as::<i64>(0) {
1383 Ok(v) => inserted_id = Some(v),
1384 Err(e) => {
1385 tx_rollback_best_effort(tx, cx).await;
1386 return Outcome::Err(e);
1387 }
1388 },
1389 Outcome::Ok(None) => {
1390 tx_rollback_best_effort(tx, cx).await;
1391 return Outcome::Err(sqlmodel_core::Error::Custom(
1392 "failed to fetch last insert id".to_string(),
1393 ));
1394 }
1395 Outcome::Err(e) => {
1396 tx_rollback_best_effort(tx, cx).await;
1397 return Outcome::Err(e);
1398 }
1399 Outcome::Cancelled(r) => {
1400 tx_rollback_best_effort(tx, cx).await;
1401 return Outcome::Cancelled(r);
1402 }
1403 Outcome::Panicked(p) => {
1404 tx_rollback_best_effort(tx, cx).await;
1405 return Outcome::Panicked(p);
1406 }
1407 }
1408 }
1409 }
1410
1411 let mut child_row = self.model.to_row();
1412 if let (Some(pk_col), Some(id)) = (pk_col, inserted_id) {
1413 if M::PRIMARY_KEY.len() != 1 {
1414 tx_rollback_best_effort(tx, cx).await;
1415 return Outcome::Err(sqlmodel_core::Error::Custom(
1416 "joined-table inheritance auto-increment insert currently requires a single-column primary key"
1417 .to_string(),
1418 ));
1419 }
1420
1421 for (name, value) in &mut child_row {
1422 if *name == pk_col && value.is_null() {
1423 *value = Value::BigInt(id);
1424 }
1425 }
1426 }
1427
1428 let (child_sql, child_params) = build_insert_sql_for_table(
1429 dialect,
1430 M::TABLE_NAME,
1431 M::fields(),
1432 &child_row,
1433 Some("*"),
1434 );
1435 let row_out = match tx.query_one(cx, &child_sql, &child_params).await {
1436 Outcome::Ok(row) => Outcome::Ok(row),
1437 Outcome::Err(e) => {
1438 tx_rollback_best_effort(tx, cx).await;
1439 return Outcome::Err(e);
1440 }
1441 Outcome::Cancelled(r) => {
1442 tx_rollback_best_effort(tx, cx).await;
1443 return Outcome::Cancelled(r);
1444 }
1445 Outcome::Panicked(p) => {
1446 tx_rollback_best_effort(tx, cx).await;
1447 return Outcome::Panicked(p);
1448 }
1449 };
1450
1451 match tx.commit(cx).await {
1452 Outcome::Ok(()) => {}
1453 Outcome::Err(e) => return Outcome::Err(e),
1454 Outcome::Cancelled(r) => return Outcome::Cancelled(r),
1455 Outcome::Panicked(p) => return Outcome::Panicked(p),
1456 }
1457
1458 return row_out;
1459 }
1460
1461 let (sql, params) = self.build_with_dialect(conn.dialect());
1462 conn.query_one(cx, &sql, ¶ms).await
1463 }
1464}
1465
1466#[derive(Debug)]
1476pub struct InsertManyBuilder<'a, M: Model> {
1477 models: &'a [M],
1478 returning: bool,
1479 on_conflict: Option<OnConflict>,
1480}
1481
1482impl<'a, M: Model> InsertManyBuilder<'a, M> {
1483 pub fn new(models: &'a [M]) -> Self {
1485 Self {
1486 models,
1487 returning: false,
1488 on_conflict: None,
1489 }
1490 }
1491
1492 pub fn returning(mut self) -> Self {
1494 self.returning = true;
1495 self
1496 }
1497
1498 pub fn on_conflict_do_nothing(mut self) -> Self {
1500 self.on_conflict = Some(OnConflict::DoNothing);
1501 self
1502 }
1503
1504 pub fn on_conflict_do_update(mut self, columns: &[&str]) -> Self {
1506 self.on_conflict = Some(OnConflict::DoUpdate {
1507 columns: columns.iter().map(|s| s.to_string()).collect(),
1508 target: Vec::new(),
1509 });
1510 self
1511 }
1512
1513 pub fn build(&self) -> (String, Vec<Value>) {
1515 self.build_with_dialect(Dialect::default())
1516 }
1517
1518 pub fn build_with_dialect(&self, dialect: Dialect) -> (String, Vec<Value>) {
1520 let batches = self.build_batches_with_dialect(dialect);
1521 match batches.len() {
1522 0 => (String::new(), Vec::new()),
1523 1 => batches.into_iter().next().unwrap(),
1524 _ => {
1525 tracing::warn!(
1526 table = M::TABLE_NAME,
1527 "Bulk insert requires multiple statements for this dialect. \
1528 Use build_batches_with_dialect or execute() instead of build_with_dialect."
1529 );
1530 (String::new(), Vec::new())
1531 }
1532 }
1533 }
1534
1535 pub fn build_batches_with_dialect(&self, dialect: Dialect) -> Vec<(String, Vec<Value>)> {
1540 enum Batch {
1541 Values {
1542 columns: Vec<&'static str>,
1543 rows: Vec<Vec<Value>>,
1544 },
1545 DefaultValues,
1546 }
1547
1548 if self.models.is_empty() {
1549 return Vec::new();
1550 }
1551
1552 if is_joined_inheritance_child::<M>() {
1553 tracing::warn!(
1554 table = M::TABLE_NAME,
1555 "build_batches_with_dialect is not available for joined-table inheritance; use execute()/execute_returning()"
1556 );
1557 return Vec::new();
1558 }
1559
1560 if dialect != Dialect::Sqlite {
1561 return vec![self.build_single_with_dialect(dialect)];
1562 }
1563
1564 let fields = M::fields();
1565 let rows: Vec<Vec<(&'static str, Value)>> =
1566 self.models.iter().map(|model| model.to_row()).collect();
1567
1568 let insert_columns: Vec<_> = fields
1570 .iter()
1571 .filter_map(|field| {
1572 if field.auto_increment {
1573 return Some(field.column_name);
1574 }
1575 let has_value = rows.iter().any(|row| {
1576 row.iter()
1577 .find(|(name, _)| name == &field.column_name)
1578 .is_some_and(|(_, v)| !matches!(v, Value::Null))
1579 });
1580 if has_value {
1581 Some(field.column_name)
1582 } else {
1583 None
1584 }
1585 })
1586 .collect();
1587
1588 let mut batches: Vec<Batch> = Vec::new();
1589
1590 for row in &rows {
1591 let mut columns_for_row = Vec::new();
1592 let mut values_for_row = Vec::new();
1593
1594 for col in &insert_columns {
1595 let mut val = row
1596 .iter()
1597 .find(|(name, _)| name == col)
1598 .map_or(Value::Null, |(_, v)| v.clone());
1599
1600 if let Some(f) = fields.iter().find(|f| f.column_name == *col)
1602 && f.auto_increment
1603 && matches!(val, Value::Null)
1604 {
1605 val = Value::Default;
1606 }
1607
1608 if matches!(val, Value::Default) {
1609 continue;
1610 }
1611
1612 columns_for_row.push(*col);
1613 values_for_row.push(val);
1614 }
1615
1616 if columns_for_row.is_empty() {
1617 batches.push(Batch::DefaultValues);
1618 continue;
1619 }
1620
1621 match batches.last_mut() {
1622 Some(Batch::Values { columns, rows }) if *columns == columns_for_row => {
1623 rows.push(values_for_row);
1624 }
1625 _ => batches.push(Batch::Values {
1626 columns: columns_for_row,
1627 rows: vec![values_for_row],
1628 }),
1629 }
1630 }
1631
1632 let mut statements = Vec::new();
1633
1634 for batch in batches {
1635 match batch {
1636 Batch::DefaultValues => {
1637 let mut sql = format!("INSERT INTO {} DEFAULT VALUES", M::TABLE_NAME);
1638 self.append_on_conflict(dialect, &mut sql, &[]);
1639 self.append_returning(&mut sql);
1640 statements.push((sql, Vec::new()));
1641 }
1642 Batch::Values { columns, rows } => {
1643 let (sql, params) = self.build_values_batch_sql(dialect, &columns, &rows);
1644 statements.push((sql, params));
1645 }
1646 }
1647 }
1648
1649 statements
1650 }
1651
1652 fn build_single_with_dialect(&self, dialect: Dialect) -> (String, Vec<Value>) {
1653 let fields = M::fields();
1654 let rows: Vec<Vec<(&'static str, Value)>> =
1655 self.models.iter().map(|model| model.to_row()).collect();
1656
1657 let insert_columns: Vec<_> = fields
1660 .iter()
1661 .filter_map(|field| {
1662 if field.auto_increment {
1663 return Some(field.column_name);
1664 }
1665 let has_value = rows.iter().any(|row| {
1666 row.iter()
1667 .find(|(name, _)| name == &field.column_name)
1668 .is_some_and(|(_, v)| !matches!(v, Value::Null))
1669 });
1670 if has_value {
1671 Some(field.column_name)
1672 } else {
1673 None
1674 }
1675 })
1676 .collect();
1677
1678 let mut all_values = Vec::new();
1679 let mut value_groups = Vec::new();
1680
1681 for row in &rows {
1682 let values: Vec<_> = insert_columns
1683 .iter()
1684 .map(|col| {
1685 let val = row
1686 .iter()
1687 .find(|(name, _)| name == col)
1688 .map_or(Value::Null, |(_, v)| v.clone());
1689
1690 let field = fields.iter().find(|f| f.column_name == *col);
1692 if let Some(f) = field
1693 && f.auto_increment
1694 && matches!(val, Value::Null)
1695 {
1696 return Value::Default;
1697 }
1698 val
1699 })
1700 .collect();
1701
1702 let mut placeholders = Vec::new();
1703 for v in &values {
1704 if matches!(v, Value::Default) {
1705 placeholders.push("DEFAULT".to_string());
1706 } else {
1707 all_values.push(v.clone());
1708 placeholders.push(dialect.placeholder(all_values.len()));
1709 }
1710 }
1711
1712 value_groups.push(format!("({})", placeholders.join(", ")));
1713 }
1714
1715 let mut sql = format!(
1716 "INSERT INTO {} ({}) VALUES {}",
1717 M::TABLE_NAME,
1718 insert_columns.join(", "),
1719 value_groups.join(", ")
1720 );
1721
1722 self.append_on_conflict(dialect, &mut sql, &insert_columns);
1723 self.append_returning(&mut sql);
1724
1725 (sql, all_values)
1726 }
1727
1728 fn build_values_batch_sql(
1729 &self,
1730 dialect: Dialect,
1731 columns: &[&'static str],
1732 rows: &[Vec<Value>],
1733 ) -> (String, Vec<Value>) {
1734 let mut params = Vec::new();
1735 let mut value_groups = Vec::new();
1736
1737 for row in rows {
1738 let mut placeholders = Vec::new();
1739 for value in row {
1740 if matches!(value, Value::Default) {
1741 placeholders.push("DEFAULT".to_string());
1742 } else {
1743 params.push(value.clone());
1744 placeholders.push(dialect.placeholder(params.len()));
1745 }
1746 }
1747 value_groups.push(format!("({})", placeholders.join(", ")));
1748 }
1749
1750 let mut sql = if columns.is_empty() {
1751 format!("INSERT INTO {} DEFAULT VALUES", M::TABLE_NAME)
1752 } else {
1753 format!(
1754 "INSERT INTO {} ({}) VALUES {}",
1755 M::TABLE_NAME,
1756 columns.join(", "),
1757 value_groups.join(", ")
1758 )
1759 };
1760
1761 self.append_on_conflict(dialect, &mut sql, columns);
1762 self.append_returning(&mut sql);
1763
1764 (sql, params)
1765 }
1766
1767 fn append_on_conflict(
1768 &self,
1769 dialect: Dialect,
1770 sql: &mut String,
1771 insert_columns: &[&'static str],
1772 ) {
1773 if let Some(on_conflict) = &self.on_conflict {
1774 append_on_conflict_clause(dialect, sql, M::PRIMARY_KEY, insert_columns, on_conflict);
1775 }
1776 }
1777
1778 fn append_returning(&self, sql: &mut String) {
1779 if self.returning {
1780 sql.push_str(" RETURNING *");
1781 }
1782 }
1783
1784 pub async fn execute<C: Connection>(
1786 self,
1787 cx: &Cx,
1788 conn: &C,
1789 ) -> Outcome<u64, sqlmodel_core::Error> {
1790 if is_joined_inheritance_child::<M>() {
1791 if self.on_conflict.is_some() {
1792 return Outcome::Err(sqlmodel_core::Error::Custom(
1793 "joined-table inheritance bulk insert does not support ON CONFLICT yet"
1794 .to_string(),
1795 ));
1796 }
1797
1798 let dialect = conn.dialect();
1799 let (parent_table, parent_fields) = match joined_parent_meta::<M>() {
1800 Ok(v) => v,
1801 Err(e) => return Outcome::Err(e),
1802 };
1803 let tx_out = conn.begin(cx).await;
1804 let tx = match tx_out {
1805 Outcome::Ok(t) => t,
1806 Outcome::Err(e) => return Outcome::Err(e),
1807 Outcome::Cancelled(r) => return Outcome::Cancelled(r),
1808 Outcome::Panicked(p) => return Outcome::Panicked(p),
1809 };
1810
1811 let mut total_inserted: u64 = 0;
1812 for model in self.models {
1813 match insert_joined_model_in_tx::<_, M>(
1814 &tx,
1815 cx,
1816 dialect,
1817 model,
1818 parent_table,
1819 parent_fields,
1820 )
1821 .await
1822 {
1823 Outcome::Ok((count, _)) => {
1824 total_inserted = total_inserted.saturating_add(count);
1825 }
1826 Outcome::Err(e) => {
1827 tx_rollback_best_effort(tx, cx).await;
1828 return Outcome::Err(e);
1829 }
1830 Outcome::Cancelled(r) => {
1831 tx_rollback_best_effort(tx, cx).await;
1832 return Outcome::Cancelled(r);
1833 }
1834 Outcome::Panicked(p) => {
1835 tx_rollback_best_effort(tx, cx).await;
1836 return Outcome::Panicked(p);
1837 }
1838 }
1839 }
1840
1841 return match tx.commit(cx).await {
1842 Outcome::Ok(()) => Outcome::Ok(total_inserted),
1843 Outcome::Err(e) => Outcome::Err(e),
1844 Outcome::Cancelled(r) => Outcome::Cancelled(r),
1845 Outcome::Panicked(p) => Outcome::Panicked(p),
1846 };
1847 }
1848
1849 let batches = self.build_batches_with_dialect(conn.dialect());
1850 if batches.is_empty() {
1851 return Outcome::Ok(0);
1852 }
1853
1854 if batches.len() == 1 {
1855 let (sql, params) = &batches[0];
1856 return conn.execute(cx, sql, params).await;
1857 }
1858
1859 let outcome = conn.batch(cx, &batches).await;
1860 outcome.map(|counts| counts.into_iter().sum())
1861 }
1862
1863 pub async fn execute_returning<C: Connection>(
1865 mut self,
1866 cx: &Cx,
1867 conn: &C,
1868 ) -> Outcome<Vec<Row>, sqlmodel_core::Error> {
1869 self.returning = true;
1870 if is_joined_inheritance_child::<M>() {
1871 if self.on_conflict.is_some() {
1872 return Outcome::Err(sqlmodel_core::Error::Custom(
1873 "joined-table inheritance bulk insert does not support ON CONFLICT yet"
1874 .to_string(),
1875 ));
1876 }
1877
1878 let dialect = conn.dialect();
1879 let (parent_table, parent_fields) = match joined_parent_meta::<M>() {
1880 Ok(v) => v,
1881 Err(e) => return Outcome::Err(e),
1882 };
1883 let pk_cols = M::PRIMARY_KEY;
1884 if pk_cols.is_empty() {
1885 return Outcome::Err(sqlmodel_core::Error::Custom(
1886 "joined-table inheritance returning requires a primary key".to_string(),
1887 ));
1888 }
1889
1890 let tx_out = conn.begin(cx).await;
1891 let tx = match tx_out {
1892 Outcome::Ok(t) => t,
1893 Outcome::Err(e) => return Outcome::Err(e),
1894 Outcome::Cancelled(r) => return Outcome::Cancelled(r),
1895 Outcome::Panicked(p) => return Outcome::Panicked(p),
1896 };
1897
1898 let mut inserted_pk_values: Vec<Vec<Value>> = Vec::with_capacity(self.models.len());
1899 for model in self.models {
1900 match insert_joined_model_in_tx::<_, M>(
1901 &tx,
1902 cx,
1903 dialect,
1904 model,
1905 parent_table,
1906 parent_fields,
1907 )
1908 .await
1909 {
1910 Outcome::Ok((_count, pk_vals)) => {
1911 if pk_vals.len() != pk_cols.len() || pk_vals.iter().any(Value::is_null) {
1912 tx_rollback_best_effort(tx, cx).await;
1913 return Outcome::Err(sqlmodel_core::Error::Custom(
1914 "joined-table inheritance bulk insert returning requires non-null primary key values"
1915 .to_string(),
1916 ));
1917 }
1918 inserted_pk_values.push(pk_vals);
1919 }
1920 Outcome::Err(e) => {
1921 tx_rollback_best_effort(tx, cx).await;
1922 return Outcome::Err(e);
1923 }
1924 Outcome::Cancelled(r) => {
1925 tx_rollback_best_effort(tx, cx).await;
1926 return Outcome::Cancelled(r);
1927 }
1928 Outcome::Panicked(p) => {
1929 tx_rollback_best_effort(tx, cx).await;
1930 return Outcome::Panicked(p);
1931 }
1932 }
1933 }
1934
1935 if inserted_pk_values.is_empty() {
1936 return match tx.commit(cx).await {
1937 Outcome::Ok(()) => Outcome::Ok(Vec::new()),
1938 Outcome::Err(e) => Outcome::Err(e),
1939 Outcome::Cancelled(r) => Outcome::Cancelled(r),
1940 Outcome::Panicked(p) => Outcome::Panicked(p),
1941 };
1942 }
1943
1944 let (select_sql, select_params) = match build_joined_child_select_sql_by_pk_in::<M>(
1945 dialect,
1946 pk_cols,
1947 &inserted_pk_values,
1948 ) {
1949 Ok(v) => v,
1950 Err(e) => {
1951 tx_rollback_best_effort(tx, cx).await;
1952 return Outcome::Err(e);
1953 }
1954 };
1955 if select_sql.is_empty() {
1956 tx_rollback_best_effort(tx, cx).await;
1957 return Outcome::Ok(Vec::new());
1958 }
1959 let rows = match tx.query(cx, &select_sql, &select_params).await {
1960 Outcome::Ok(rows) => rows,
1961 Outcome::Err(e) => {
1962 tx_rollback_best_effort(tx, cx).await;
1963 return Outcome::Err(e);
1964 }
1965 Outcome::Cancelled(r) => {
1966 tx_rollback_best_effort(tx, cx).await;
1967 return Outcome::Cancelled(r);
1968 }
1969 Outcome::Panicked(p) => {
1970 tx_rollback_best_effort(tx, cx).await;
1971 return Outcome::Panicked(p);
1972 }
1973 };
1974
1975 return match tx.commit(cx).await {
1976 Outcome::Ok(()) => Outcome::Ok(rows),
1977 Outcome::Err(e) => Outcome::Err(e),
1978 Outcome::Cancelled(r) => Outcome::Cancelled(r),
1979 Outcome::Panicked(p) => Outcome::Panicked(p),
1980 };
1981 }
1982
1983 let batches = self.build_batches_with_dialect(conn.dialect());
1984 if batches.is_empty() {
1985 return Outcome::Ok(Vec::new());
1986 }
1987
1988 let mut all_rows = Vec::new();
1989 for (sql, params) in batches {
1990 match conn.query(cx, &sql, ¶ms).await {
1991 Outcome::Ok(mut rows) => all_rows.append(&mut rows),
1992 Outcome::Err(e) => return Outcome::Err(e),
1993 Outcome::Cancelled(r) => return Outcome::Cancelled(r),
1994 Outcome::Panicked(p) => return Outcome::Panicked(p),
1995 }
1996 }
1997
1998 Outcome::Ok(all_rows)
1999 }
2000}
2001
2002#[derive(Debug, Clone)]
2004pub struct SetClause {
2005 column: String,
2006 value: Value,
2007}
2008
2009#[derive(Debug)]
2028pub struct UpdateBuilder<'a, M: Model> {
2029 model: Option<&'a M>,
2030 where_clause: Option<Where>,
2031 set_fields: Option<Vec<&'static str>>,
2032 explicit_sets: Vec<SetClause>,
2033 returning: bool,
2034}
2035
2036impl<'a, M: Model> UpdateBuilder<'a, M> {
2037 pub fn new(model: &'a M) -> Self {
2039 Self {
2040 model: Some(model),
2041 where_clause: None,
2042 set_fields: None,
2043 explicit_sets: Vec::new(),
2044 returning: false,
2045 }
2046 }
2047
2048 pub fn empty() -> Self {
2052 Self {
2053 model: None,
2054 where_clause: None,
2055 set_fields: None,
2056 explicit_sets: Vec::new(),
2057 returning: false,
2058 }
2059 }
2060
2061 pub fn set<V: Into<Value>>(mut self, column: &str, value: V) -> Self {
2066 self.explicit_sets.push(SetClause {
2067 column: column.to_string(),
2068 value: value.into(),
2069 });
2070 self
2071 }
2072
2073 pub fn set_only(mut self, fields: &[&'static str]) -> Self {
2075 self.set_fields = Some(fields.to_vec());
2076 self
2077 }
2078
2079 pub fn filter(mut self, expr: Expr) -> Self {
2081 self.where_clause = Some(match self.where_clause {
2082 Some(existing) => existing.and(expr),
2083 None => Where::new(expr),
2084 });
2085 self
2086 }
2087
2088 pub fn returning(mut self) -> Self {
2090 self.returning = true;
2091 self
2092 }
2093
2094 pub fn build(&self) -> (String, Vec<Value>) {
2096 self.build_with_dialect(Dialect::default())
2097 }
2098
2099 pub fn build_with_dialect(&self, dialect: Dialect) -> (String, Vec<Value>) {
2101 let pk = M::PRIMARY_KEY;
2102 let mut params = Vec::new();
2103 let mut set_clauses = Vec::new();
2104
2105 for set in &self.explicit_sets {
2107 set_clauses.push(format!(
2108 "{} = {}",
2109 set.column,
2110 dialect.placeholder(params.len() + 1)
2111 ));
2112 params.push(set.value.clone());
2113 }
2114
2115 if let Some(model) = &self.model {
2117 let row = model.to_row();
2118
2119 let update_fields: Vec<_> = row
2121 .iter()
2122 .filter(|(name, _)| {
2123 if pk.contains(name) {
2125 return false;
2126 }
2127 if self.explicit_sets.iter().any(|s| s.column == *name) {
2129 return false;
2130 }
2131 if let Some(fields) = &self.set_fields {
2133 return fields.contains(name);
2134 }
2135 true
2136 })
2137 .collect();
2138
2139 for (name, value) in update_fields {
2140 set_clauses.push(format!(
2141 "{} = {}",
2142 name,
2143 dialect.placeholder(params.len() + 1)
2144 ));
2145 params.push(value.clone());
2146 }
2147 }
2148
2149 if set_clauses.is_empty() {
2150 return (String::new(), Vec::new());
2152 }
2153
2154 let mut sql = format!("UPDATE {} SET {}", M::TABLE_NAME, set_clauses.join(", "));
2155
2156 if let Some(where_clause) = &self.where_clause {
2158 let (where_sql, where_params) = where_clause.build_with_dialect(dialect, params.len());
2159 sql.push_str(" WHERE ");
2160 sql.push_str(&where_sql);
2161 params.extend(where_params);
2162 } else if let Some(model) = &self.model {
2163 let pk_values = model.primary_key_value();
2165 let pk_conditions: Vec<_> = pk
2166 .iter()
2167 .zip(pk_values.iter())
2168 .enumerate()
2169 .map(|(i, (col, _))| {
2170 format!("{} = {}", col, dialect.placeholder(params.len() + i + 1))
2171 })
2172 .collect();
2173
2174 if !pk_conditions.is_empty() {
2175 sql.push_str(" WHERE ");
2176 sql.push_str(&pk_conditions.join(" AND "));
2177 params.extend(pk_values);
2178 }
2179 }
2180
2181 if self.returning {
2183 sql.push_str(" RETURNING *");
2184 }
2185
2186 (sql, params)
2187 }
2188
2189 pub async fn execute<C: Connection>(
2196 self,
2197 cx: &Cx,
2198 conn: &C,
2199 ) -> Outcome<u64, sqlmodel_core::Error> {
2200 if is_joined_inheritance_child::<M>() {
2201 if self.model.is_none() {
2202 if self.explicit_sets.is_empty() {
2203 return Outcome::Err(sqlmodel_core::Error::Custom(
2204 "joined-table inheritance explicit update requires at least one SET clause"
2205 .to_string(),
2206 ));
2207 }
2208
2209 let dialect = conn.dialect();
2210 let (parent_table, parent_fields) = match joined_parent_meta::<M>() {
2211 Ok(v) => v,
2212 Err(e) => return Outcome::Err(e),
2213 };
2214 let (parent_sets, child_sets) = match split_explicit_joined_sets::<M>(
2215 &self.explicit_sets,
2216 parent_table,
2217 parent_fields,
2218 ) {
2219 Ok(v) => v,
2220 Err(e) => return Outcome::Err(e),
2221 };
2222
2223 let tx_out = conn.begin(cx).await;
2224 let tx = match tx_out {
2225 Outcome::Ok(t) => t,
2226 Outcome::Err(e) => return Outcome::Err(e),
2227 Outcome::Cancelled(r) => return Outcome::Cancelled(r),
2228 Outcome::Panicked(p) => return Outcome::Panicked(p),
2229 };
2230
2231 let pk_values = match select_joined_pk_values_in_tx::<_, M>(
2232 &tx,
2233 cx,
2234 dialect,
2235 self.where_clause.as_ref(),
2236 )
2237 .await
2238 {
2239 Outcome::Ok(v) => v,
2240 Outcome::Err(e) => {
2241 tx_rollback_best_effort(tx, cx).await;
2242 return Outcome::Err(e);
2243 }
2244 Outcome::Cancelled(r) => {
2245 tx_rollback_best_effort(tx, cx).await;
2246 return Outcome::Cancelled(r);
2247 }
2248 Outcome::Panicked(p) => {
2249 tx_rollback_best_effort(tx, cx).await;
2250 return Outcome::Panicked(p);
2251 }
2252 };
2253
2254 if pk_values.is_empty() {
2255 return match tx.commit(cx).await {
2256 Outcome::Ok(()) => Outcome::Ok(0),
2257 Outcome::Err(e) => Outcome::Err(e),
2258 Outcome::Cancelled(r) => Outcome::Cancelled(r),
2259 Outcome::Panicked(p) => Outcome::Panicked(p),
2260 };
2261 }
2262
2263 let mut total = 0_u64;
2264
2265 if !parent_sets.is_empty() {
2266 let (parent_sql, parent_params) = build_update_sql_for_table_pk_in(
2267 dialect,
2268 parent_table,
2269 M::PRIMARY_KEY,
2270 &pk_values,
2271 &parent_sets,
2272 );
2273 if !parent_sql.is_empty() {
2274 match tx.execute(cx, &parent_sql, &parent_params).await {
2275 Outcome::Ok(n) => total = total.saturating_add(n),
2276 Outcome::Err(e) => {
2277 tx_rollback_best_effort(tx, cx).await;
2278 return Outcome::Err(e);
2279 }
2280 Outcome::Cancelled(r) => {
2281 tx_rollback_best_effort(tx, cx).await;
2282 return Outcome::Cancelled(r);
2283 }
2284 Outcome::Panicked(p) => {
2285 tx_rollback_best_effort(tx, cx).await;
2286 return Outcome::Panicked(p);
2287 }
2288 }
2289 }
2290 }
2291
2292 if !child_sets.is_empty() {
2293 let (child_sql, child_params) = build_update_sql_for_table_pk_in(
2294 dialect,
2295 M::TABLE_NAME,
2296 M::PRIMARY_KEY,
2297 &pk_values,
2298 &child_sets,
2299 );
2300 if !child_sql.is_empty() {
2301 match tx.execute(cx, &child_sql, &child_params).await {
2302 Outcome::Ok(n) => total = total.saturating_add(n),
2303 Outcome::Err(e) => {
2304 tx_rollback_best_effort(tx, cx).await;
2305 return Outcome::Err(e);
2306 }
2307 Outcome::Cancelled(r) => {
2308 tx_rollback_best_effort(tx, cx).await;
2309 return Outcome::Cancelled(r);
2310 }
2311 Outcome::Panicked(p) => {
2312 tx_rollback_best_effort(tx, cx).await;
2313 return Outcome::Panicked(p);
2314 }
2315 }
2316 }
2317 }
2318
2319 return match tx.commit(cx).await {
2320 Outcome::Ok(()) => Outcome::Ok(total),
2321 Outcome::Err(e) => Outcome::Err(e),
2322 Outcome::Cancelled(r) => Outcome::Cancelled(r),
2323 Outcome::Panicked(p) => Outcome::Panicked(p),
2324 };
2325 }
2326
2327 if self.where_clause.is_some() || !self.explicit_sets.is_empty() {
2328 return Outcome::Err(sqlmodel_core::Error::Custom(
2329 "joined-table inheritance update with a model supports model-based updates only; use UpdateBuilder::empty().set(...).filter(...) for explicit WHERE/SET"
2330 .to_string(),
2331 ));
2332 }
2333
2334 let dialect = conn.dialect();
2335 let Some(model) = self.model else {
2336 return Outcome::Err(sqlmodel_core::Error::Custom(
2337 "update called without model".to_string(),
2338 ));
2339 };
2340 let inh = M::inheritance();
2341 let Some(parent_table) = inh.parent else {
2342 return Outcome::Err(sqlmodel_core::Error::Custom(
2343 "joined-table inheritance child missing parent table metadata".to_string(),
2344 ));
2345 };
2346 let Some(parent_fields_fn) = inh.parent_fields_fn else {
2347 return Outcome::Err(sqlmodel_core::Error::Custom(
2348 "joined-table inheritance child missing parent_fields_fn metadata".to_string(),
2349 ));
2350 };
2351 let parent_fields = parent_fields_fn();
2352 let Some(parent_row) = model.joined_parent_row() else {
2353 return Outcome::Err(sqlmodel_core::Error::Custom(
2354 "joined-table inheritance child missing joined_parent_row() implementation"
2355 .to_string(),
2356 ));
2357 };
2358
2359 let pk_cols = M::PRIMARY_KEY;
2360 let pk_vals = model.primary_key_value();
2361
2362 let tx_out = conn.begin(cx).await;
2363 let tx = match tx_out {
2364 Outcome::Ok(t) => t,
2365 Outcome::Err(e) => return Outcome::Err(e),
2366 Outcome::Cancelled(r) => return Outcome::Cancelled(r),
2367 Outcome::Panicked(p) => return Outcome::Panicked(p),
2368 };
2369
2370 let mut total = 0_u64;
2371
2372 let mut parent_sets: Vec<(&'static str, Value)> = Vec::new();
2374 for f in parent_fields {
2375 if f.primary_key || pk_cols.contains(&f.column_name) {
2376 continue;
2377 }
2378 if let Some((_, v)) = parent_row.iter().find(|(k, _)| *k == f.column_name) {
2379 parent_sets.push((f.column_name, v.clone()));
2380 }
2381 }
2382 let (parent_sql, parent_params) =
2383 build_update_sql_for_table(dialect, parent_table, pk_cols, &pk_vals, &parent_sets);
2384 if !parent_sql.is_empty() {
2385 match tx.execute(cx, &parent_sql, &parent_params).await {
2386 Outcome::Ok(n) => total = total.saturating_add(n),
2387 Outcome::Err(e) => {
2388 tx_rollback_best_effort(tx, cx).await;
2389 return Outcome::Err(e);
2390 }
2391 Outcome::Cancelled(r) => {
2392 tx_rollback_best_effort(tx, cx).await;
2393 return Outcome::Cancelled(r);
2394 }
2395 Outcome::Panicked(p) => {
2396 tx_rollback_best_effort(tx, cx).await;
2397 return Outcome::Panicked(p);
2398 }
2399 }
2400 }
2401
2402 let row = model.to_row();
2404 let mut child_sets: Vec<(&'static str, Value)> = Vec::new();
2405 for (name, value) in row {
2406 if pk_cols.contains(&name) {
2407 continue;
2408 }
2409 if let Some(fields) = &self.set_fields
2410 && !fields.contains(&name)
2411 {
2412 continue;
2413 }
2414 child_sets.push((name, value));
2415 }
2416 let (child_sql, child_params) =
2417 build_update_sql_for_table(dialect, M::TABLE_NAME, pk_cols, &pk_vals, &child_sets);
2418 if !child_sql.is_empty() {
2419 match tx.execute(cx, &child_sql, &child_params).await {
2420 Outcome::Ok(n) => total = total.saturating_add(n),
2421 Outcome::Err(e) => {
2422 tx_rollback_best_effort(tx, cx).await;
2423 return Outcome::Err(e);
2424 }
2425 Outcome::Cancelled(r) => {
2426 tx_rollback_best_effort(tx, cx).await;
2427 return Outcome::Cancelled(r);
2428 }
2429 Outcome::Panicked(p) => {
2430 tx_rollback_best_effort(tx, cx).await;
2431 return Outcome::Panicked(p);
2432 }
2433 }
2434 }
2435
2436 match tx.commit(cx).await {
2437 Outcome::Ok(()) => Outcome::Ok(total),
2438 Outcome::Err(e) => Outcome::Err(e),
2439 Outcome::Cancelled(r) => Outcome::Cancelled(r),
2440 Outcome::Panicked(p) => Outcome::Panicked(p),
2441 }
2442 } else {
2443 let (sql, params) = self.build_with_dialect(conn.dialect());
2444 if sql.is_empty() {
2445 return Outcome::Ok(0);
2446 }
2447 conn.execute(cx, &sql, ¶ms).await
2448 }
2449 }
2450
2451 pub async fn execute_returning<C: Connection>(
2453 mut self,
2454 cx: &Cx,
2455 conn: &C,
2456 ) -> Outcome<Vec<Row>, sqlmodel_core::Error> {
2457 self.returning = true;
2458 if is_joined_inheritance_child::<M>() {
2459 if self.model.is_none() {
2460 if self.explicit_sets.is_empty() {
2461 return Outcome::Err(sqlmodel_core::Error::Custom(
2462 "joined-table inheritance explicit update_returning requires at least one SET clause"
2463 .to_string(),
2464 ));
2465 }
2466
2467 let dialect = conn.dialect();
2468 let (parent_table, parent_fields) = match joined_parent_meta::<M>() {
2469 Ok(v) => v,
2470 Err(e) => return Outcome::Err(e),
2471 };
2472 let (parent_sets, child_sets) = match split_explicit_joined_sets::<M>(
2473 &self.explicit_sets,
2474 parent_table,
2475 parent_fields,
2476 ) {
2477 Ok(v) => v,
2478 Err(e) => return Outcome::Err(e),
2479 };
2480
2481 let tx_out = conn.begin(cx).await;
2482 let tx = match tx_out {
2483 Outcome::Ok(t) => t,
2484 Outcome::Err(e) => return Outcome::Err(e),
2485 Outcome::Cancelled(r) => return Outcome::Cancelled(r),
2486 Outcome::Panicked(p) => return Outcome::Panicked(p),
2487 };
2488
2489 let pk_values = match select_joined_pk_values_in_tx::<_, M>(
2490 &tx,
2491 cx,
2492 dialect,
2493 self.where_clause.as_ref(),
2494 )
2495 .await
2496 {
2497 Outcome::Ok(v) => v,
2498 Outcome::Err(e) => {
2499 tx_rollback_best_effort(tx, cx).await;
2500 return Outcome::Err(e);
2501 }
2502 Outcome::Cancelled(r) => {
2503 tx_rollback_best_effort(tx, cx).await;
2504 return Outcome::Cancelled(r);
2505 }
2506 Outcome::Panicked(p) => {
2507 tx_rollback_best_effort(tx, cx).await;
2508 return Outcome::Panicked(p);
2509 }
2510 };
2511
2512 if pk_values.is_empty() {
2513 return match tx.commit(cx).await {
2514 Outcome::Ok(()) => Outcome::Ok(Vec::new()),
2515 Outcome::Err(e) => Outcome::Err(e),
2516 Outcome::Cancelled(r) => Outcome::Cancelled(r),
2517 Outcome::Panicked(p) => Outcome::Panicked(p),
2518 };
2519 }
2520
2521 if !parent_sets.is_empty() {
2522 let (parent_sql, parent_params) = build_update_sql_for_table_pk_in(
2523 dialect,
2524 parent_table,
2525 M::PRIMARY_KEY,
2526 &pk_values,
2527 &parent_sets,
2528 );
2529 if !parent_sql.is_empty() {
2530 match tx.execute(cx, &parent_sql, &parent_params).await {
2531 Outcome::Ok(_) => {}
2532 Outcome::Err(e) => {
2533 tx_rollback_best_effort(tx, cx).await;
2534 return Outcome::Err(e);
2535 }
2536 Outcome::Cancelled(r) => {
2537 tx_rollback_best_effort(tx, cx).await;
2538 return Outcome::Cancelled(r);
2539 }
2540 Outcome::Panicked(p) => {
2541 tx_rollback_best_effort(tx, cx).await;
2542 return Outcome::Panicked(p);
2543 }
2544 }
2545 }
2546 }
2547
2548 if !child_sets.is_empty() {
2549 let (child_sql, child_params) = build_update_sql_for_table_pk_in(
2550 dialect,
2551 M::TABLE_NAME,
2552 M::PRIMARY_KEY,
2553 &pk_values,
2554 &child_sets,
2555 );
2556 if !child_sql.is_empty() {
2557 match tx.execute(cx, &child_sql, &child_params).await {
2558 Outcome::Ok(_) => {}
2559 Outcome::Err(e) => {
2560 tx_rollback_best_effort(tx, cx).await;
2561 return Outcome::Err(e);
2562 }
2563 Outcome::Cancelled(r) => {
2564 tx_rollback_best_effort(tx, cx).await;
2565 return Outcome::Cancelled(r);
2566 }
2567 Outcome::Panicked(p) => {
2568 tx_rollback_best_effort(tx, cx).await;
2569 return Outcome::Panicked(p);
2570 }
2571 }
2572 }
2573 }
2574
2575 let (select_sql, select_params) = match build_joined_child_select_sql_by_pk_in::<M>(
2576 dialect,
2577 M::PRIMARY_KEY,
2578 &pk_values,
2579 ) {
2580 Ok(v) => v,
2581 Err(e) => {
2582 tx_rollback_best_effort(tx, cx).await;
2583 return Outcome::Err(e);
2584 }
2585 };
2586 let rows = if select_sql.is_empty() {
2587 Vec::new()
2588 } else {
2589 match tx.query(cx, &select_sql, &select_params).await {
2590 Outcome::Ok(rows) => rows,
2591 Outcome::Err(e) => {
2592 tx_rollback_best_effort(tx, cx).await;
2593 return Outcome::Err(e);
2594 }
2595 Outcome::Cancelled(r) => {
2596 tx_rollback_best_effort(tx, cx).await;
2597 return Outcome::Cancelled(r);
2598 }
2599 Outcome::Panicked(p) => {
2600 tx_rollback_best_effort(tx, cx).await;
2601 return Outcome::Panicked(p);
2602 }
2603 }
2604 };
2605
2606 return match tx.commit(cx).await {
2607 Outcome::Ok(()) => Outcome::Ok(rows),
2608 Outcome::Err(e) => Outcome::Err(e),
2609 Outcome::Cancelled(r) => Outcome::Cancelled(r),
2610 Outcome::Panicked(p) => Outcome::Panicked(p),
2611 };
2612 }
2613
2614 if self.where_clause.is_some() || !self.explicit_sets.is_empty() {
2615 return Outcome::Err(sqlmodel_core::Error::Custom(
2616 "joined-table inheritance update_returning with a model supports model-based updates only; use UpdateBuilder::empty().set(...).filter(...) for explicit WHERE/SET"
2617 .to_string(),
2618 ));
2619 }
2620
2621 let dialect = conn.dialect();
2622 let Some(model) = self.model else {
2623 return Outcome::Err(sqlmodel_core::Error::Custom(
2624 "update_returning called without model".to_string(),
2625 ));
2626 };
2627 let inh = M::inheritance();
2628 let Some(parent_table) = inh.parent else {
2629 return Outcome::Err(sqlmodel_core::Error::Custom(
2630 "joined-table inheritance child missing parent table metadata".to_string(),
2631 ));
2632 };
2633 let Some(parent_fields_fn) = inh.parent_fields_fn else {
2634 return Outcome::Err(sqlmodel_core::Error::Custom(
2635 "joined-table inheritance child missing parent_fields_fn metadata".to_string(),
2636 ));
2637 };
2638 let parent_fields = parent_fields_fn();
2639 let Some(parent_row) = model.joined_parent_row() else {
2640 return Outcome::Err(sqlmodel_core::Error::Custom(
2641 "joined-table inheritance child missing joined_parent_row() implementation"
2642 .to_string(),
2643 ));
2644 };
2645
2646 let pk_cols = M::PRIMARY_KEY;
2647 let pk_vals = model.primary_key_value();
2648
2649 let tx_out = conn.begin(cx).await;
2650 let tx = match tx_out {
2651 Outcome::Ok(t) => t,
2652 Outcome::Err(e) => return Outcome::Err(e),
2653 Outcome::Cancelled(r) => return Outcome::Cancelled(r),
2654 Outcome::Panicked(p) => return Outcome::Panicked(p),
2655 };
2656
2657 let mut parent_sets: Vec<(&'static str, Value)> = Vec::new();
2659 for f in parent_fields {
2660 if f.primary_key || pk_cols.contains(&f.column_name) {
2661 continue;
2662 }
2663 if let Some((_, v)) = parent_row.iter().find(|(k, _)| *k == f.column_name) {
2664 parent_sets.push((f.column_name, v.clone()));
2665 }
2666 }
2667 let (parent_sql, parent_params) =
2668 build_update_sql_for_table(dialect, parent_table, pk_cols, &pk_vals, &parent_sets);
2669 if !parent_sql.is_empty() {
2670 match tx.execute(cx, &parent_sql, &parent_params).await {
2671 Outcome::Ok(_) => {}
2672 Outcome::Err(e) => {
2673 tx_rollback_best_effort(tx, cx).await;
2674 return Outcome::Err(e);
2675 }
2676 Outcome::Cancelled(r) => {
2677 tx_rollback_best_effort(tx, cx).await;
2678 return Outcome::Cancelled(r);
2679 }
2680 Outcome::Panicked(p) => {
2681 tx_rollback_best_effort(tx, cx).await;
2682 return Outcome::Panicked(p);
2683 }
2684 }
2685 }
2686
2687 let row = model.to_row();
2689 let mut child_sets: Vec<(&'static str, Value)> = Vec::new();
2690 for (name, value) in row {
2691 if pk_cols.contains(&name) {
2692 continue;
2693 }
2694 if let Some(fields) = &self.set_fields
2695 && !fields.contains(&name)
2696 {
2697 continue;
2698 }
2699 child_sets.push((name, value));
2700 }
2701 let (mut child_sql, child_params) =
2702 build_update_sql_for_table(dialect, M::TABLE_NAME, pk_cols, &pk_vals, &child_sets);
2703 if child_sql.is_empty() {
2704 tx_rollback_best_effort(tx, cx).await;
2705 return Outcome::Ok(Vec::new());
2706 }
2707 child_sql.push_str(" RETURNING *");
2708
2709 let rows = match tx.query(cx, &child_sql, &child_params).await {
2710 Outcome::Ok(rows) => rows,
2711 Outcome::Err(e) => {
2712 tx_rollback_best_effort(tx, cx).await;
2713 return Outcome::Err(e);
2714 }
2715 Outcome::Cancelled(r) => {
2716 tx_rollback_best_effort(tx, cx).await;
2717 return Outcome::Cancelled(r);
2718 }
2719 Outcome::Panicked(p) => {
2720 tx_rollback_best_effort(tx, cx).await;
2721 return Outcome::Panicked(p);
2722 }
2723 };
2724
2725 match tx.commit(cx).await {
2726 Outcome::Ok(()) => Outcome::Ok(rows),
2727 Outcome::Err(e) => Outcome::Err(e),
2728 Outcome::Cancelled(r) => Outcome::Cancelled(r),
2729 Outcome::Panicked(p) => Outcome::Panicked(p),
2730 }
2731 } else {
2732 let (sql, params) = self.build_with_dialect(conn.dialect());
2733 if sql.is_empty() {
2734 return Outcome::Ok(Vec::new());
2735 }
2736 conn.query(cx, &sql, ¶ms).await
2737 }
2738 }
2739}
2740
2741#[derive(Debug)]
2762pub struct DeleteBuilder<'a, M: Model> {
2763 model: Option<&'a M>,
2764 where_clause: Option<Where>,
2765 returning: bool,
2766 _marker: PhantomData<M>,
2767}
2768
2769impl<'a, M: Model> DeleteBuilder<'a, M> {
2770 pub fn new() -> Self {
2772 Self {
2773 model: None,
2774 where_clause: None,
2775 returning: false,
2776 _marker: PhantomData,
2777 }
2778 }
2779
2780 pub fn from_model(model: &'a M) -> Self {
2784 Self {
2785 model: Some(model),
2786 where_clause: None,
2787 returning: false,
2788 _marker: PhantomData,
2789 }
2790 }
2791
2792 pub fn filter(mut self, expr: Expr) -> Self {
2794 self.where_clause = Some(match self.where_clause {
2795 Some(existing) => existing.and(expr),
2796 None => Where::new(expr),
2797 });
2798 self
2799 }
2800
2801 pub fn returning(mut self) -> Self {
2803 self.returning = true;
2804 self
2805 }
2806
2807 pub fn build(&self) -> (String, Vec<Value>) {
2809 self.build_with_dialect(Dialect::default())
2810 }
2811
2812 pub fn build_with_dialect(&self, dialect: Dialect) -> (String, Vec<Value>) {
2814 let mut sql = format!("DELETE FROM {}", M::TABLE_NAME);
2815 let mut params = Vec::new();
2816
2817 if let Some(where_clause) = &self.where_clause {
2818 let (where_sql, where_params) = where_clause.build_with_dialect(dialect, 0);
2819 sql.push_str(" WHERE ");
2820 sql.push_str(&where_sql);
2821 params = where_params;
2822 } else if let Some(model) = &self.model {
2823 let pk = M::PRIMARY_KEY;
2825 let pk_values = model.primary_key_value();
2826 let pk_conditions: Vec<_> = pk
2827 .iter()
2828 .zip(pk_values.iter())
2829 .enumerate()
2830 .map(|(i, (col, _))| format!("{} = {}", col, dialect.placeholder(i + 1)))
2831 .collect();
2832
2833 if !pk_conditions.is_empty() {
2834 sql.push_str(" WHERE ");
2835 sql.push_str(&pk_conditions.join(" AND "));
2836 params.extend(pk_values);
2837 }
2838 }
2839
2840 if self.returning {
2842 sql.push_str(" RETURNING *");
2843 }
2844
2845 (sql, params)
2846 }
2847
2848 pub async fn execute<C: Connection>(
2854 self,
2855 cx: &Cx,
2856 conn: &C,
2857 ) -> Outcome<u64, sqlmodel_core::Error> {
2858 if is_joined_inheritance_child::<M>() {
2859 let dialect = conn.dialect();
2860 let (parent_table, _parent_fields) = match joined_parent_meta::<M>() {
2861 Ok(v) => v,
2862 Err(e) => return Outcome::Err(e),
2863 };
2864
2865 let tx_out = conn.begin(cx).await;
2866 let tx = match tx_out {
2867 Outcome::Ok(t) => t,
2868 Outcome::Err(e) => return Outcome::Err(e),
2869 Outcome::Cancelled(r) => return Outcome::Cancelled(r),
2870 Outcome::Panicked(p) => return Outcome::Panicked(p),
2871 };
2872
2873 let pk_values = if let Some(where_clause) = self.where_clause.as_ref() {
2874 match select_joined_pk_values_in_tx::<_, M>(&tx, cx, dialect, Some(where_clause))
2875 .await
2876 {
2877 Outcome::Ok(v) => v,
2878 Outcome::Err(e) => {
2879 tx_rollback_best_effort(tx, cx).await;
2880 return Outcome::Err(e);
2881 }
2882 Outcome::Cancelled(r) => {
2883 tx_rollback_best_effort(tx, cx).await;
2884 return Outcome::Cancelled(r);
2885 }
2886 Outcome::Panicked(p) => {
2887 tx_rollback_best_effort(tx, cx).await;
2888 return Outcome::Panicked(p);
2889 }
2890 }
2891 } else if let Some(model) = self.model {
2892 vec![model.primary_key_value()]
2893 } else {
2894 match select_joined_pk_values_in_tx::<_, M>(&tx, cx, dialect, None).await {
2896 Outcome::Ok(v) => v,
2897 Outcome::Err(e) => {
2898 tx_rollback_best_effort(tx, cx).await;
2899 return Outcome::Err(e);
2900 }
2901 Outcome::Cancelled(r) => {
2902 tx_rollback_best_effort(tx, cx).await;
2903 return Outcome::Cancelled(r);
2904 }
2905 Outcome::Panicked(p) => {
2906 tx_rollback_best_effort(tx, cx).await;
2907 return Outcome::Panicked(p);
2908 }
2909 }
2910 };
2911
2912 if pk_values.is_empty() {
2913 return match tx.commit(cx).await {
2914 Outcome::Ok(()) => Outcome::Ok(0),
2915 Outcome::Err(e) => Outcome::Err(e),
2916 Outcome::Cancelled(r) => Outcome::Cancelled(r),
2917 Outcome::Panicked(p) => Outcome::Panicked(p),
2918 };
2919 }
2920
2921 let (child_sql, child_params) = build_delete_sql_for_table_pk_in(
2922 dialect,
2923 M::TABLE_NAME,
2924 M::PRIMARY_KEY,
2925 &pk_values,
2926 );
2927 let (parent_sql, parent_params) =
2928 build_delete_sql_for_table_pk_in(dialect, parent_table, M::PRIMARY_KEY, &pk_values);
2929
2930 let mut total = 0_u64;
2931
2932 if !child_sql.is_empty() {
2933 match tx.execute(cx, &child_sql, &child_params).await {
2934 Outcome::Ok(n) => total = total.saturating_add(n),
2935 Outcome::Err(e) => {
2936 tx_rollback_best_effort(tx, cx).await;
2937 return Outcome::Err(e);
2938 }
2939 Outcome::Cancelled(r) => {
2940 tx_rollback_best_effort(tx, cx).await;
2941 return Outcome::Cancelled(r);
2942 }
2943 Outcome::Panicked(p) => {
2944 tx_rollback_best_effort(tx, cx).await;
2945 return Outcome::Panicked(p);
2946 }
2947 }
2948 }
2949
2950 if !parent_sql.is_empty() {
2951 match tx.execute(cx, &parent_sql, &parent_params).await {
2952 Outcome::Ok(n) => total = total.saturating_add(n),
2953 Outcome::Err(e) => {
2954 tx_rollback_best_effort(tx, cx).await;
2955 return Outcome::Err(e);
2956 }
2957 Outcome::Cancelled(r) => {
2958 tx_rollback_best_effort(tx, cx).await;
2959 return Outcome::Cancelled(r);
2960 }
2961 Outcome::Panicked(p) => {
2962 tx_rollback_best_effort(tx, cx).await;
2963 return Outcome::Panicked(p);
2964 }
2965 }
2966 }
2967
2968 match tx.commit(cx).await {
2969 Outcome::Ok(()) => Outcome::Ok(total),
2970 Outcome::Err(e) => Outcome::Err(e),
2971 Outcome::Cancelled(r) => Outcome::Cancelled(r),
2972 Outcome::Panicked(p) => Outcome::Panicked(p),
2973 }
2974 } else {
2975 let (sql, params) = self.build_with_dialect(conn.dialect());
2976 conn.execute(cx, &sql, ¶ms).await
2977 }
2978 }
2979
2980 pub async fn execute_returning<C: Connection>(
2985 mut self,
2986 cx: &Cx,
2987 conn: &C,
2988 ) -> Outcome<Vec<Row>, sqlmodel_core::Error> {
2989 self.returning = true;
2990 if is_joined_inheritance_child::<M>() {
2991 let dialect = conn.dialect();
2992 let (parent_table, _parent_fields) = match joined_parent_meta::<M>() {
2993 Ok(v) => v,
2994 Err(e) => return Outcome::Err(e),
2995 };
2996
2997 let tx_out = conn.begin(cx).await;
2998 let tx = match tx_out {
2999 Outcome::Ok(t) => t,
3000 Outcome::Err(e) => return Outcome::Err(e),
3001 Outcome::Cancelled(r) => return Outcome::Cancelled(r),
3002 Outcome::Panicked(p) => return Outcome::Panicked(p),
3003 };
3004
3005 let pk_values = if let Some(where_clause) = self.where_clause.as_ref() {
3006 match select_joined_pk_values_in_tx::<_, M>(&tx, cx, dialect, Some(where_clause))
3007 .await
3008 {
3009 Outcome::Ok(v) => v,
3010 Outcome::Err(e) => {
3011 tx_rollback_best_effort(tx, cx).await;
3012 return Outcome::Err(e);
3013 }
3014 Outcome::Cancelled(r) => {
3015 tx_rollback_best_effort(tx, cx).await;
3016 return Outcome::Cancelled(r);
3017 }
3018 Outcome::Panicked(p) => {
3019 tx_rollback_best_effort(tx, cx).await;
3020 return Outcome::Panicked(p);
3021 }
3022 }
3023 } else if let Some(model) = self.model {
3024 vec![model.primary_key_value()]
3025 } else {
3026 match select_joined_pk_values_in_tx::<_, M>(&tx, cx, dialect, None).await {
3027 Outcome::Ok(v) => v,
3028 Outcome::Err(e) => {
3029 tx_rollback_best_effort(tx, cx).await;
3030 return Outcome::Err(e);
3031 }
3032 Outcome::Cancelled(r) => {
3033 tx_rollback_best_effort(tx, cx).await;
3034 return Outcome::Cancelled(r);
3035 }
3036 Outcome::Panicked(p) => {
3037 tx_rollback_best_effort(tx, cx).await;
3038 return Outcome::Panicked(p);
3039 }
3040 }
3041 };
3042
3043 if pk_values.is_empty() {
3044 return match tx.commit(cx).await {
3045 Outcome::Ok(()) => Outcome::Ok(Vec::new()),
3046 Outcome::Err(e) => Outcome::Err(e),
3047 Outcome::Cancelled(r) => Outcome::Cancelled(r),
3048 Outcome::Panicked(p) => Outcome::Panicked(p),
3049 };
3050 }
3051
3052 let (select_sql, select_params) = match build_joined_child_select_sql_by_pk_in::<M>(
3053 dialect,
3054 M::PRIMARY_KEY,
3055 &pk_values,
3056 ) {
3057 Ok(v) => v,
3058 Err(e) => {
3059 tx_rollback_best_effort(tx, cx).await;
3060 return Outcome::Err(e);
3061 }
3062 };
3063 let rows = if select_sql.is_empty() {
3064 Vec::new()
3065 } else {
3066 match tx.query(cx, &select_sql, &select_params).await {
3067 Outcome::Ok(rows) => rows,
3068 Outcome::Err(e) => {
3069 tx_rollback_best_effort(tx, cx).await;
3070 return Outcome::Err(e);
3071 }
3072 Outcome::Cancelled(r) => {
3073 tx_rollback_best_effort(tx, cx).await;
3074 return Outcome::Cancelled(r);
3075 }
3076 Outcome::Panicked(p) => {
3077 tx_rollback_best_effort(tx, cx).await;
3078 return Outcome::Panicked(p);
3079 }
3080 }
3081 };
3082
3083 let (child_sql, child_params) = build_delete_sql_for_table_pk_in(
3084 dialect,
3085 M::TABLE_NAME,
3086 M::PRIMARY_KEY,
3087 &pk_values,
3088 );
3089 let (parent_sql, parent_params) =
3090 build_delete_sql_for_table_pk_in(dialect, parent_table, M::PRIMARY_KEY, &pk_values);
3091
3092 if !child_sql.is_empty() {
3093 match tx.execute(cx, &child_sql, &child_params).await {
3094 Outcome::Ok(_) => {}
3095 Outcome::Err(e) => {
3096 tx_rollback_best_effort(tx, cx).await;
3097 return Outcome::Err(e);
3098 }
3099 Outcome::Cancelled(r) => {
3100 tx_rollback_best_effort(tx, cx).await;
3101 return Outcome::Cancelled(r);
3102 }
3103 Outcome::Panicked(p) => {
3104 tx_rollback_best_effort(tx, cx).await;
3105 return Outcome::Panicked(p);
3106 }
3107 }
3108 }
3109
3110 if !parent_sql.is_empty() {
3111 match tx.execute(cx, &parent_sql, &parent_params).await {
3112 Outcome::Ok(_) => {}
3113 Outcome::Err(e) => {
3114 tx_rollback_best_effort(tx, cx).await;
3115 return Outcome::Err(e);
3116 }
3117 Outcome::Cancelled(r) => {
3118 tx_rollback_best_effort(tx, cx).await;
3119 return Outcome::Cancelled(r);
3120 }
3121 Outcome::Panicked(p) => {
3122 tx_rollback_best_effort(tx, cx).await;
3123 return Outcome::Panicked(p);
3124 }
3125 }
3126 }
3127
3128 return match tx.commit(cx).await {
3129 Outcome::Ok(()) => Outcome::Ok(rows),
3130 Outcome::Err(e) => Outcome::Err(e),
3131 Outcome::Cancelled(r) => Outcome::Cancelled(r),
3132 Outcome::Panicked(p) => Outcome::Panicked(p),
3133 };
3134 }
3135 let (sql, params) = self.build_with_dialect(conn.dialect());
3136 conn.query(cx, &sql, ¶ms).await
3137 }
3138}
3139
3140impl<M: Model> Default for DeleteBuilder<'_, M> {
3141 fn default() -> Self {
3142 Self::new()
3143 }
3144}
3145
3146#[derive(Debug)]
3148pub struct QueryBuilder {
3149 sql: String,
3150 params: Vec<Value>,
3151}
3152
3153impl QueryBuilder {
3154 pub fn new(sql: impl Into<String>) -> Self {
3156 Self {
3157 sql: sql.into(),
3158 params: Vec::new(),
3159 }
3160 }
3161
3162 pub fn bind(mut self, value: impl Into<Value>) -> Self {
3164 self.params.push(value.into());
3165 self
3166 }
3167
3168 pub fn bind_all(mut self, values: impl IntoIterator<Item = Value>) -> Self {
3170 self.params.extend(values);
3171 self
3172 }
3173
3174 pub fn build(self) -> (String, Vec<Value>) {
3176 (self.sql, self.params)
3177 }
3178}
3179
3180#[cfg(test)]
3181mod tests {
3182 use super::*;
3183 use crate::expr::Dialect;
3184 use sqlmodel_core::field::FieldInfo;
3185 use sqlmodel_core::types::SqlType;
3186
3187 struct TestHero {
3189 id: Option<i64>,
3190 name: String,
3191 age: i32,
3192 }
3193
3194 impl Model for TestHero {
3195 const TABLE_NAME: &'static str = "heroes";
3196 const PRIMARY_KEY: &'static [&'static str] = &["id"];
3197
3198 fn fields() -> &'static [FieldInfo] {
3199 static FIELDS: &[FieldInfo] = &[
3200 FieldInfo::new("id", "id", SqlType::BigInt)
3201 .primary_key(true)
3202 .auto_increment(true)
3203 .nullable(true),
3204 FieldInfo::new("name", "name", SqlType::Text),
3205 FieldInfo::new("age", "age", SqlType::Integer),
3206 ];
3207 FIELDS
3208 }
3209
3210 fn to_row(&self) -> Vec<(&'static str, Value)> {
3211 vec![
3212 ("id", self.id.map_or(Value::Null, Value::BigInt)),
3213 ("name", Value::Text(self.name.clone())),
3214 ("age", Value::Int(self.age)),
3215 ]
3216 }
3217
3218 fn from_row(_row: &Row) -> sqlmodel_core::Result<Self> {
3219 Err(sqlmodel_core::Error::Custom(
3220 "from_row not used in tests".to_string(),
3221 ))
3222 }
3223
3224 fn primary_key_value(&self) -> Vec<Value> {
3225 vec![self.id.map_or(Value::Null, Value::BigInt)]
3226 }
3227
3228 fn is_new(&self) -> bool {
3229 self.id.is_none()
3230 }
3231 }
3232
3233 struct TestOnlyId {
3234 id: Option<i64>,
3235 }
3236
3237 impl Model for TestOnlyId {
3238 const TABLE_NAME: &'static str = "only_ids";
3239 const PRIMARY_KEY: &'static [&'static str] = &["id"];
3240
3241 fn fields() -> &'static [FieldInfo] {
3242 static FIELDS: &[FieldInfo] = &[FieldInfo::new("id", "id", SqlType::BigInt)
3243 .primary_key(true)
3244 .auto_increment(true)
3245 .nullable(true)];
3246 FIELDS
3247 }
3248
3249 fn to_row(&self) -> Vec<(&'static str, Value)> {
3250 vec![("id", self.id.map_or(Value::Null, Value::BigInt))]
3251 }
3252
3253 fn from_row(_row: &Row) -> sqlmodel_core::Result<Self> {
3254 Err(sqlmodel_core::Error::Custom(
3255 "from_row not used in tests".to_string(),
3256 ))
3257 }
3258
3259 fn primary_key_value(&self) -> Vec<Value> {
3260 vec![self.id.map_or(Value::Null, Value::BigInt)]
3261 }
3262
3263 fn is_new(&self) -> bool {
3264 self.id.is_none()
3265 }
3266 }
3267
3268 #[test]
3269 fn test_insert_basic() {
3270 let hero = TestHero {
3271 id: None,
3272 name: "Spider-Man".to_string(),
3273 age: 25,
3274 };
3275 let (sql, params) = InsertBuilder::new(&hero).build();
3276
3277 assert_eq!(
3279 sql,
3280 "INSERT INTO heroes (id, name, age) VALUES (DEFAULT, $1, $2)"
3281 );
3282 assert_eq!(params.len(), 2);
3283 }
3284
3285 #[test]
3286 fn test_insert_returning() {
3287 let hero = TestHero {
3288 id: None,
3289 name: "Spider-Man".to_string(),
3290 age: 25,
3291 };
3292 let (sql, _) = InsertBuilder::new(&hero).returning().build();
3293
3294 assert!(sql.ends_with(" RETURNING *"));
3295 }
3296
3297 #[test]
3298 fn test_insert_on_conflict_do_nothing() {
3299 let hero = TestHero {
3300 id: None,
3301 name: "Spider-Man".to_string(),
3302 age: 25,
3303 };
3304 let (sql, _) = InsertBuilder::new(&hero).on_conflict_do_nothing().build();
3305
3306 assert!(sql.contains("ON CONFLICT DO NOTHING"));
3307 }
3308
3309 #[test]
3310 fn test_insert_on_conflict_do_update() {
3311 let hero = TestHero {
3312 id: None,
3313 name: "Spider-Man".to_string(),
3314 age: 25,
3315 };
3316 let (sql, _) = InsertBuilder::new(&hero)
3317 .on_conflict_do_update(&["name", "age"])
3318 .build();
3319
3320 assert!(sql.contains("ON CONFLICT (id) DO UPDATE SET"));
3321 assert!(sql.contains("name = EXCLUDED.name"));
3322 assert!(sql.contains("age = EXCLUDED.age"));
3323 }
3324
3325 #[test]
3326 fn test_insert_mysql_on_conflict_do_nothing() {
3327 let hero = TestHero {
3328 id: None,
3329 name: "Spider-Man".to_string(),
3330 age: 25,
3331 };
3332 let (sql, _) = InsertBuilder::new(&hero)
3333 .on_conflict_do_nothing()
3334 .build_with_dialect(Dialect::Mysql);
3335
3336 assert!(sql.starts_with("INSERT IGNORE INTO heroes"));
3337 assert!(!sql.contains("ON CONFLICT"));
3338 }
3339
3340 #[test]
3341 fn test_insert_mysql_on_conflict_do_update() {
3342 let hero = TestHero {
3343 id: None,
3344 name: "Spider-Man".to_string(),
3345 age: 25,
3346 };
3347 let (sql, _) = InsertBuilder::new(&hero)
3348 .on_conflict_do_update(&["name", "age"])
3349 .build_with_dialect(Dialect::Mysql);
3350
3351 assert!(sql.contains("ON DUPLICATE KEY UPDATE"));
3352 assert!(sql.contains("name = VALUES(name)"));
3353 assert!(sql.contains("age = VALUES(age)"));
3354 assert!(!sql.contains("ON CONFLICT"));
3355 }
3356
3357 #[test]
3358 fn test_insert_many_mysql_on_conflict_do_update() {
3359 let heroes = vec![
3360 TestHero {
3361 id: None,
3362 name: "Spider-Man".to_string(),
3363 age: 25,
3364 },
3365 TestHero {
3366 id: None,
3367 name: "Iron Man".to_string(),
3368 age: 45,
3369 },
3370 ];
3371 let (sql, params) = InsertManyBuilder::new(&heroes)
3372 .on_conflict_do_update(&["name"])
3373 .build_with_dialect(Dialect::Mysql);
3374
3375 assert!(sql.contains("ON DUPLICATE KEY UPDATE"));
3376 assert!(sql.contains("name = VALUES(name)"));
3377 assert!(!sql.contains("ON CONFLICT"));
3378 assert_eq!(params.len(), 4);
3379 }
3380
3381 #[test]
3382 fn test_insert_many() {
3383 let heroes = vec![
3384 TestHero {
3385 id: None,
3386 name: "Spider-Man".to_string(),
3387 age: 25,
3388 },
3389 TestHero {
3390 id: None,
3391 name: "Iron Man".to_string(),
3392 age: 45,
3393 },
3394 ];
3395 let (sql, params) = InsertManyBuilder::new(&heroes).build();
3396
3397 assert!(sql.starts_with("INSERT INTO heroes (id, name, age) VALUES"));
3399 assert!(sql.contains("(DEFAULT, $1, $2), (DEFAULT, $3, $4)"));
3400 assert_eq!(params.len(), 4);
3401 }
3402
3403 #[test]
3404 fn test_insert_sqlite_omits_default_columns() {
3405 let hero = TestHero {
3406 id: None,
3407 name: "Spider-Man".to_string(),
3408 age: 25,
3409 };
3410 let (sql, params) = InsertBuilder::new(&hero).build_with_dialect(Dialect::Sqlite);
3411
3412 assert_eq!(sql, "INSERT INTO heroes (name, age) VALUES (?1, ?2)");
3413 assert_eq!(params.len(), 2);
3414 }
3415
3416 #[test]
3417 fn test_insert_sqlite_default_values_only() {
3418 let model = TestOnlyId { id: None };
3419 let (sql, params) = InsertBuilder::new(&model).build_with_dialect(Dialect::Sqlite);
3420
3421 assert_eq!(sql, "INSERT INTO only_ids DEFAULT VALUES");
3422 assert!(params.is_empty());
3423 }
3424
3425 #[test]
3426 fn test_insert_many_sqlite_omits_auto_increment() {
3427 let heroes = vec![
3428 TestHero {
3429 id: None,
3430 name: "Spider-Man".to_string(),
3431 age: 25,
3432 },
3433 TestHero {
3434 id: None,
3435 name: "Iron Man".to_string(),
3436 age: 45,
3437 },
3438 ];
3439 let batches = InsertManyBuilder::new(&heroes).build_batches_with_dialect(Dialect::Sqlite);
3440
3441 assert_eq!(batches.len(), 1);
3442 let (sql, params) = &batches[0];
3443 assert!(sql.starts_with("INSERT INTO heroes (name, age) VALUES"));
3444 assert!(sql.contains("(?1, ?2), (?3, ?4)"));
3445 assert_eq!(params.len(), 4);
3446 }
3447
3448 #[test]
3449 fn test_insert_many_sqlite_mixed_defaults_split() {
3450 let heroes = vec![
3451 TestHero {
3452 id: Some(1),
3453 name: "Spider-Man".to_string(),
3454 age: 25,
3455 },
3456 TestHero {
3457 id: None,
3458 name: "Iron Man".to_string(),
3459 age: 45,
3460 },
3461 ];
3462 let batches = InsertManyBuilder::new(&heroes).build_batches_with_dialect(Dialect::Sqlite);
3463
3464 assert_eq!(batches.len(), 2);
3465 assert_eq!(
3466 batches[0].0,
3467 "INSERT INTO heroes (id, name, age) VALUES (?1, ?2, ?3)"
3468 );
3469 assert_eq!(
3470 batches[1].0,
3471 "INSERT INTO heroes (name, age) VALUES (?1, ?2)"
3472 );
3473 assert_eq!(batches[0].1.len(), 3);
3474 assert_eq!(batches[1].1.len(), 2);
3475 }
3476
3477 #[test]
3478 fn test_insert_many_sqlite_default_values_only() {
3479 let rows = vec![TestOnlyId { id: None }, TestOnlyId { id: None }];
3480 let batches = InsertManyBuilder::new(&rows).build_batches_with_dialect(Dialect::Sqlite);
3481
3482 assert_eq!(batches.len(), 2);
3483 assert_eq!(batches[0].0, "INSERT INTO only_ids DEFAULT VALUES");
3484 assert_eq!(batches[1].0, "INSERT INTO only_ids DEFAULT VALUES");
3485 assert!(batches[0].1.is_empty());
3486 assert!(batches[1].1.is_empty());
3487 }
3488
3489 #[test]
3490 fn test_update_basic() {
3491 let hero = TestHero {
3492 id: Some(1),
3493 name: "Spider-Man".to_string(),
3494 age: 26,
3495 };
3496 let (sql, params) = UpdateBuilder::new(&hero).build();
3497
3498 assert!(sql.starts_with("UPDATE heroes SET"));
3499 assert!(sql.contains("WHERE id = "));
3500 assert!(params.len() >= 2); }
3502
3503 #[test]
3504 fn test_update_explicit_set() {
3505 let (sql, params) = UpdateBuilder::<TestHero>::empty()
3506 .set("age", 30)
3507 .filter(Expr::col("id").eq(1))
3508 .build_with_dialect(Dialect::Postgres);
3509
3510 assert_eq!(sql, "UPDATE heroes SET age = $1 WHERE \"id\" = $2");
3511 assert_eq!(params.len(), 2);
3512 }
3513
3514 #[test]
3515 fn test_update_returning() {
3516 let hero = TestHero {
3517 id: Some(1),
3518 name: "Spider-Man".to_string(),
3519 age: 26,
3520 };
3521 let (sql, _) = UpdateBuilder::new(&hero).returning().build();
3522
3523 assert!(sql.ends_with(" RETURNING *"));
3524 }
3525
3526 #[test]
3527 fn test_delete_basic() {
3528 let (sql, _) = DeleteBuilder::<TestHero>::new()
3529 .filter(Expr::col("age").lt(18))
3530 .build_with_dialect(Dialect::Postgres);
3531
3532 assert_eq!(sql, "DELETE FROM heroes WHERE \"age\" < $1");
3533 }
3534
3535 #[test]
3536 fn test_delete_from_model() {
3537 let hero = TestHero {
3538 id: Some(42),
3539 name: "Spider-Man".to_string(),
3540 age: 25,
3541 };
3542 let (sql, params) = DeleteBuilder::from_model(&hero).build();
3543
3544 assert!(sql.contains("WHERE id = $1"));
3545 assert_eq!(params.len(), 1);
3546 }
3547
3548 #[test]
3549 fn test_delete_returning() {
3550 let (sql, _) = DeleteBuilder::<TestHero>::new()
3551 .filter(Expr::col("status").eq("inactive"))
3552 .returning()
3553 .build_with_dialect(Dialect::Postgres);
3554
3555 assert!(sql.ends_with(" RETURNING *"));
3556 }
3557
3558 #[test]
3559 fn test_dialect_sqlite() {
3560 let hero = TestHero {
3561 id: None,
3562 name: "Spider-Man".to_string(),
3563 age: 25,
3564 };
3565 let (sql, _) = InsertBuilder::new(&hero).build_with_dialect(Dialect::Sqlite);
3566
3567 assert!(sql.contains("?1"));
3568 assert!(sql.contains("?2"));
3569 }
3570
3571 #[test]
3572 fn test_dialect_mysql() {
3573 let hero = TestHero {
3574 id: None,
3575 name: "Spider-Man".to_string(),
3576 age: 25,
3577 };
3578 let (sql, _) = InsertBuilder::new(&hero).build_with_dialect(Dialect::Mysql);
3579
3580 assert!(sql.contains('?'));
3582 assert!(!sql.contains("$1"));
3583 }
3584}