1use crate::{
2 ActiveModelTrait, ActiveValue, ColumnTrait, EntityName, EntityTrait, IntoActiveModel, Iterable,
3 PrimaryKeyTrait, QueryTrait,
4};
5use core::marker::PhantomData;
6use sea_query::{Expr, InsertStatement, Keyword, OnConflict, SimpleExpr, Value, ValueTuple};
7
8#[derive(Debug)]
10pub struct Insert<A>
11where
12 A: ActiveModelTrait,
13{
14 pub(crate) query: InsertStatement,
15 pub(crate) primary_key: Option<ValueTuple>,
16 pub(crate) model: PhantomData<A>,
17}
18
19#[derive(Debug)]
21pub struct InsertMany<A>
22where
23 A: ActiveModelTrait,
24{
25 pub(crate) query: InsertStatement,
26 pub(crate) primary_key: Option<ValueTuple>,
27 pub(crate) empty: bool,
28 pub(crate) model: PhantomData<A>,
29}
30
31#[derive(Debug)]
35pub struct TryInsert<A>
36where
37 A: ActiveModelTrait,
38{
39 pub(crate) insert_struct: Insert<A>,
40 pub(crate) empty: bool,
41}
42
43impl<A> Insert<A>
44where
45 A: ActiveModelTrait,
46{
47 pub fn one<M>(m: M) -> Self
78 where
79 M: IntoActiveModel<A>,
80 {
81 let mut query = InsertStatement::new();
82 query
83 .into_table(A::Entity::default().table_ref())
84 .or_default_values();
85
86 let mut am: A = m.into_active_model();
87 let primary_key =
88 if !<<A::Entity as EntityTrait>::PrimaryKey as PrimaryKeyTrait>::auto_increment() {
89 am.get_primary_key_value()
90 } else {
91 None
92 };
93 let mut columns = Vec::new();
94 let mut values = Vec::new();
95
96 for col in <A::Entity as EntityTrait>::Column::iter() {
97 let av = am.take(col);
98
99 match av {
100 ActiveValue::Set(value) | ActiveValue::Unchanged(value) => {
101 columns.push(col);
102 values.push(col.save_as(Expr::val(value)));
103 }
104 ActiveValue::NotSet => {}
105 }
106 }
107
108 query.columns(columns);
109 query.values_panic(values);
110
111 Self {
112 query,
113 primary_key,
114 model: PhantomData,
115 }
116 }
117
118 pub fn many<M, I>(models: I) -> InsertMany<A>
141 where
142 M: IntoActiveModel<A>,
143 I: IntoIterator<Item = M>,
144 {
145 InsertMany::many(models)
146 }
147
148 pub fn on_conflict(mut self, on_conflict: OnConflict) -> Self {
205 self.query.on_conflict(on_conflict);
206 self
207 }
208
209 pub fn do_nothing(self) -> TryInsert<A>
211 where
212 A: ActiveModelTrait,
213 {
214 TryInsert::from_one(self)
215 }
216
217 pub fn on_empty_do_nothing(self) -> TryInsert<A>
219 where
220 A: ActiveModelTrait,
221 {
222 TryInsert::from_one(self)
223 }
224
225 pub fn on_conflict_do_nothing(mut self) -> TryInsert<A>
258 where
259 A: ActiveModelTrait,
260 {
261 self.query.on_conflict(on_conflict_primary_key::<A>());
262
263 TryInsert::from_one(self)
264 }
265}
266
267impl<A> InsertMany<A>
268where
269 A: ActiveModelTrait,
270{
271 pub fn many<M, I>(models: I) -> Self
273 where
274 M: IntoActiveModel<A>,
275 I: IntoIterator<Item = M>,
276 {
277 let mut query = InsertStatement::new();
278 query.into_table(A::Entity::default().table_ref());
279
280 let mut columns: Vec<_> = <A::Entity as EntityTrait>::Column::iter()
281 .map(|_| None)
282 .collect();
283 let mut null_value: Vec<Option<Value>> = std::iter::repeat_n(None, columns.len()).collect();
284 let mut all_values: Vec<Vec<SimpleExpr>> = Vec::new();
285 let mut primary_key = None;
286
287 for model in models.into_iter() {
288 let mut am: A = model.into_active_model();
289 primary_key =
290 if !<<A::Entity as EntityTrait>::PrimaryKey as PrimaryKeyTrait>::auto_increment() {
291 am.get_primary_key_value()
292 } else {
293 None
294 };
295 let mut values = Vec::with_capacity(columns.len());
296 for (idx, col) in <A::Entity as EntityTrait>::Column::iter().enumerate() {
297 let av = am.take(col);
298 match av {
299 ActiveValue::Set(value) | ActiveValue::Unchanged(value) => {
300 columns[idx] = Some(col); null_value[idx] = Some(value.as_null()); values.push(col.save_as(Expr::val(value))); }
304 ActiveValue::NotSet => {
305 values.push(SimpleExpr::Keyword(Keyword::Null)); }
307 }
308 }
309 all_values.push(values);
310 }
311
312 let empty = all_values.is_empty();
313
314 if !all_values.is_empty() {
315 query.columns(columns.iter().cloned().flatten());
317 }
318
319 for values in all_values {
320 query.values_panic(values.into_iter().enumerate().filter_map(|(i, v)| {
322 if columns[i].is_some() {
323 if !matches!(v, SimpleExpr::Keyword(Keyword::Null)) {
325 Some(v)
327 } else {
328 null_value[i].clone().map(SimpleExpr::Value)
330 }
331 } else {
332 None
333 }
334 }));
335 }
336
337 Self {
338 query,
339 primary_key,
340 empty,
341 model: PhantomData,
342 }
343 }
344
345 pub fn on_conflict(mut self, on_conflict: OnConflict) -> Self {
347 self.query.on_conflict(on_conflict);
348 self
349 }
350
351 pub fn do_nothing(self) -> TryInsert<A>
353 where
354 A: ActiveModelTrait,
355 {
356 TryInsert::from_many(self)
357 }
358
359 pub fn on_empty_do_nothing(self) -> TryInsert<A>
361 where
362 A: ActiveModelTrait,
363 {
364 TryInsert::from_many(self)
365 }
366
367 pub fn on_conflict_do_nothing(mut self) -> TryInsert<A>
370 where
371 A: ActiveModelTrait,
372 {
373 self.query.on_conflict(on_conflict_primary_key::<A>());
374
375 TryInsert::from_many(self)
376 }
377
378 pub(crate) fn into_one(self) -> Insert<A> {
380 assert!(!self.empty);
381
382 let Self {
383 query,
384 primary_key,
385 empty: _,
386 model,
387 } = self;
388
389 Insert {
390 query,
391 primary_key,
392 model,
393 }
394 }
395}
396
397impl<A> QueryTrait for Insert<A>
398where
399 A: ActiveModelTrait,
400{
401 type QueryStatement = InsertStatement;
402
403 fn query(&mut self) -> &mut InsertStatement {
404 &mut self.query
405 }
406
407 fn as_query(&self) -> &InsertStatement {
408 &self.query
409 }
410
411 fn into_query(self) -> InsertStatement {
412 self.query
413 }
414}
415
416impl<A> QueryTrait for InsertMany<A>
417where
418 A: ActiveModelTrait,
419{
420 type QueryStatement = InsertStatement;
421
422 fn query(&mut self) -> &mut InsertStatement {
423 &mut self.query
424 }
425
426 fn as_query(&self) -> &InsertStatement {
427 &self.query
428 }
429
430 fn into_query(self) -> InsertStatement {
431 self.query
432 }
433}
434
435impl<A> TryInsert<A>
436where
437 A: ActiveModelTrait,
438{
439 fn from_one(insert: Insert<A>) -> Self {
440 Self {
441 insert_struct: insert,
442 empty: false,
443 }
444 }
445
446 fn from_many(insert: InsertMany<A>) -> Self {
447 let InsertMany {
448 query,
449 primary_key,
450 empty,
451 model,
452 } = insert;
453
454 Self {
455 insert_struct: Insert {
456 query,
457 primary_key,
458 model,
459 },
460 empty,
461 }
462 }
463
464 pub fn one<M>(m: M) -> Self
466 where
467 M: IntoActiveModel<A>,
468 {
469 Self::from_one(Insert::one(m))
470 }
471
472 pub fn many<M, I>(models: I) -> Self
474 where
475 M: IntoActiveModel<A>,
476 I: IntoIterator<Item = M>,
477 {
478 Self::from_many(Insert::many(models))
479 }
480
481 pub fn on_conflict(mut self, on_conflict: OnConflict) -> Insert<A> {
483 self.insert_struct.query.on_conflict(on_conflict);
484 self.insert_struct
485 }
486
487 pub fn on_conflict_do_nothing(mut self) -> Self {
489 self.insert_struct
490 .query
491 .on_conflict(on_conflict_primary_key::<A>());
492
493 self
494 }
495}
496
497impl<A> QueryTrait for TryInsert<A>
498where
499 A: ActiveModelTrait,
500{
501 type QueryStatement = InsertStatement;
502
503 fn query(&mut self) -> &mut InsertStatement {
504 &mut self.insert_struct.query
505 }
506
507 fn as_query(&self) -> &InsertStatement {
508 &self.insert_struct.query
509 }
510
511 fn into_query(self) -> InsertStatement {
512 self.insert_struct.query
513 }
514}
515
516fn on_conflict_primary_key<A: ActiveModelTrait>() -> OnConflict {
517 let primary_keys = <A::Entity as EntityTrait>::PrimaryKey::iter();
518 let mut on_conflict = OnConflict::columns(primary_keys.clone());
519 on_conflict.do_nothing_on(primary_keys);
520 on_conflict
521}
522
523#[cfg(test)]
524mod tests {
525 use sea_query::OnConflict;
526
527 use crate::tests_cfg::{cake, cake_filling};
528 use crate::{
529 ActiveValue, DbBackend, DbErr, EntityTrait, Insert, IntoActiveModel, NotSet, QueryTrait,
530 Set,
531 };
532
533 #[test]
534 fn insert_1() {
535 assert_eq!(
536 Insert::<cake::ActiveModel>::one(cake::ActiveModel {
537 id: ActiveValue::not_set(),
538 name: ActiveValue::set("Apple Pie".to_owned()),
539 })
540 .build(DbBackend::Postgres)
541 .to_string(),
542 r#"INSERT INTO "cake" ("name") VALUES ('Apple Pie')"#,
543 );
544 }
545
546 #[test]
547 fn insert_2() {
548 assert_eq!(
549 Insert::<cake::ActiveModel>::one(cake::ActiveModel {
550 id: ActiveValue::set(1),
551 name: ActiveValue::set("Apple Pie".to_owned()),
552 })
553 .build(DbBackend::Postgres)
554 .to_string(),
555 r#"INSERT INTO "cake" ("id", "name") VALUES (1, 'Apple Pie')"#,
556 );
557 }
558
559 #[test]
560 fn insert_3() {
561 assert_eq!(
562 Insert::<cake::ActiveModel>::one(cake::Model {
563 id: 1,
564 name: "Apple Pie".to_owned(),
565 })
566 .build(DbBackend::Postgres)
567 .to_string(),
568 r#"INSERT INTO "cake" ("id", "name") VALUES (1, 'Apple Pie')"#,
569 );
570 }
571
572 #[test]
573 fn insert_many_1() {
574 assert_eq!(
575 Insert::<cake::ActiveModel>::many([
576 cake::Model {
577 id: 1,
578 name: "Apple Pie".to_owned(),
579 },
580 cake::Model {
581 id: 2,
582 name: "Orange Scone".to_owned(),
583 }
584 ])
585 .build(DbBackend::Postgres)
586 .to_string(),
587 r#"INSERT INTO "cake" ("id", "name") VALUES (1, 'Apple Pie'), (2, 'Orange Scone')"#,
588 );
589 }
590
591 #[test]
592 fn insert_many_2() {
593 assert_eq!(
594 Insert::<cake::ActiveModel>::many([
595 cake::ActiveModel {
596 id: NotSet,
597 name: Set("Apple Pie".to_owned()),
598 },
599 cake::ActiveModel {
600 id: NotSet,
601 name: Set("Orange Scone".to_owned()),
602 }
603 ])
604 .build(DbBackend::Postgres)
605 .to_string(),
606 r#"INSERT INTO "cake" ("name") VALUES ('Apple Pie'), ('Orange Scone')"#,
607 );
608 }
609
610 #[test]
611 fn insert_many_3() {
612 let apple = cake_filling::ActiveModel {
613 cake_id: ActiveValue::set(2),
614 filling_id: ActiveValue::NotSet,
615 };
616 let orange = cake_filling::ActiveModel {
617 cake_id: ActiveValue::NotSet,
618 filling_id: ActiveValue::set(3),
619 };
620 assert_eq!(
621 Insert::<cake_filling::ActiveModel>::many([apple, orange])
622 .build(DbBackend::Postgres)
623 .to_string(),
624 r#"INSERT INTO "cake_filling" ("cake_id", "filling_id") VALUES (2, NULL), (NULL, 3)"#,
625 );
626 }
627
628 #[test]
629 fn insert_6() {
630 let orange = cake::ActiveModel {
631 id: ActiveValue::set(2),
632 name: ActiveValue::set("Orange".to_owned()),
633 };
634
635 assert_eq!(
636 cake::Entity::insert(orange)
637 .on_conflict(
638 OnConflict::column(cake::Column::Name)
639 .do_nothing()
640 .to_owned()
641 )
642 .build(DbBackend::Postgres)
643 .to_string(),
644 r#"INSERT INTO "cake" ("id", "name") VALUES (2, 'Orange') ON CONFLICT ("name") DO NOTHING"#,
645 );
646 }
647
648 #[test]
649 fn insert_7() {
650 let orange = cake::ActiveModel {
651 id: ActiveValue::set(2),
652 name: ActiveValue::set("Orange".to_owned()),
653 };
654
655 assert_eq!(
656 cake::Entity::insert(orange)
657 .on_conflict(
658 OnConflict::column(cake::Column::Name)
659 .update_column(cake::Column::Name)
660 .to_owned()
661 )
662 .build(DbBackend::Postgres)
663 .to_string(),
664 r#"INSERT INTO "cake" ("id", "name") VALUES (2, 'Orange') ON CONFLICT ("name") DO UPDATE SET "name" = "excluded"."name""#,
665 );
666 }
667
668 #[test]
669 fn insert_8() -> Result<(), DbErr> {
670 use crate::{DbBackend, MockDatabase, Statement, Transaction};
671
672 mod post {
673 use crate as sea_orm;
674 use crate::entity::prelude::*;
675
676 #[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel)]
677 #[sea_orm(table_name = "posts")]
678 pub struct Model {
679 #[sea_orm(primary_key, select_as = "INTEGER", save_as = "TEXT")]
680 pub id: i32,
681 pub title: String,
682 pub text: String,
683 }
684
685 #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
686 pub enum Relation {}
687
688 impl ActiveModelBehavior for ActiveModel {}
689 }
690
691 let model = post::Model {
692 id: 1,
693 title: "News wrap up 2022".into(),
694 text: "brbrbrrrbrbrbrr...".into(),
695 };
696
697 let db = MockDatabase::new(DbBackend::Postgres)
698 .append_query_results([[model.clone()]])
699 .into_connection();
700
701 post::Entity::insert(model.into_active_model()).exec(&db)?;
702
703 assert_eq!(
704 db.into_transaction_log(),
705 [Transaction::many([Statement::from_sql_and_values(
706 DbBackend::Postgres,
707 r#"INSERT INTO "posts" ("id", "title", "text") VALUES (CAST($1 AS TEXT), $2, $3) RETURNING CAST("id" AS INTEGER)"#,
708 [
709 1.into(),
710 "News wrap up 2022".into(),
711 "brbrbrrrbrbrbrr...".into(),
712 ]
713 )])]
714 );
715
716 Ok(())
717 }
718
719 #[test]
720 fn insert_9() -> Result<(), DbErr> {
721 use crate::{DbBackend, MockDatabase, MockExecResult, Statement, Transaction};
722
723 mod post {
724 use crate as sea_orm;
725 use crate::entity::prelude::*;
726
727 #[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel)]
728 #[sea_orm(table_name = "posts")]
729 pub struct Model {
730 #[sea_orm(
731 primary_key,
732 auto_increment = false,
733 select_as = "INTEGER",
734 save_as = "TEXT"
735 )]
736 pub id_primary: i32,
737 #[sea_orm(
738 primary_key,
739 auto_increment = false,
740 select_as = "INTEGER",
741 save_as = "TEXT"
742 )]
743 pub id_secondary: i32,
744 pub title: String,
745 pub text: String,
746 }
747
748 #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
749 pub enum Relation {}
750
751 impl ActiveModelBehavior for ActiveModel {}
752 }
753
754 let model = post::Model {
755 id_primary: 1,
756 id_secondary: 1001,
757 title: "News wrap up 2022".into(),
758 text: "brbrbrrrbrbrbrr...".into(),
759 };
760
761 let db = MockDatabase::new(DbBackend::Postgres)
762 .append_query_results([[model.clone()]])
763 .into_connection();
764
765 post::Entity::insert(model.into_active_model()).exec(&db)?;
766
767 assert_eq!(
768 db.into_transaction_log(),
769 [Transaction::many([Statement::from_sql_and_values(
770 DbBackend::Postgres,
771 r#"INSERT INTO "posts" ("id_primary", "id_secondary", "title", "text") VALUES (CAST($1 AS TEXT), CAST($2 AS TEXT), $3, $4) RETURNING CAST("id_primary" AS INTEGER), CAST("id_secondary" AS INTEGER)"#,
772 [
773 1.into(),
774 1001.into(),
775 "News wrap up 2022".into(),
776 "brbrbrrrbrbrbrr...".into(),
777 ]
778 )])]
779 );
780
781 Ok(())
782 }
783}