Skip to main content

sea_orm/query/
insert.rs

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/// Single-row `INSERT` builder, returned by
9/// [`EntityTrait::insert`](crate::EntityTrait::insert). Chain `.on_conflict(...)`
10/// or `.on_empty_returning(...)` then dispatch with `.exec(db)` or
11/// `.exec_with_returning(db)`.
12#[derive(Debug)]
13pub struct Insert<A>
14where
15    A: ActiveModelTrait,
16{
17    pub(crate) query: InsertStatement,
18    pub(crate) primary_key: Option<ValueTuple>,
19    pub(crate) model: PhantomData<A>,
20}
21
22/// Multi-row `INSERT` builder, returned by
23/// [`EntityTrait::insert_many`](crate::EntityTrait::insert_many). Adds
24/// `.exec_with_returning_many` and `.exec_with_returning_keys` for batch
25/// inserts that need the resulting rows or primary keys.
26#[derive(Debug)]
27pub struct InsertMany<A>
28where
29    A: ActiveModelTrait,
30{
31    pub(crate) query: InsertStatement,
32    pub(crate) primary_key: Option<ValueTuple>,
33    pub(crate) empty: bool,
34    pub(crate) model: PhantomData<A>,
35}
36
37/// Wrapper of [`Insert`] / [`InsertMany`], treats "no row inserted/id returned" as a normal outcome.
38///
39/// Its `exec*` methods return [`crate::TryInsertResult`].
40/// Mapping empty input to [`crate::TryInsertResult::Empty`] (no SQL executed) and
41/// `DbErr::RecordNotInserted` to [`crate::TryInsertResult::Conflicted`].
42///
43/// Useful for idempotent inserts such as `ON CONFLICT ... DO NOTHING` (Postgres / SQLite) or the
44/// MySQL polyfill (`ON DUPLICATE KEY UPDATE pk = pk`).
45#[derive(Debug)]
46pub struct TryInsert<A>
47where
48    A: ActiveModelTrait,
49{
50    pub(crate) insert_struct: Insert<A>,
51    pub(crate) empty: bool,
52}
53
54impl<A> Insert<A>
55where
56    A: ActiveModelTrait,
57{
58    /// Insert one Model or ActiveModel
59    ///
60    /// Model
61    /// ```
62    /// use sea_orm::{DbBackend, entity::*, query::*, tests_cfg::cake};
63    ///
64    /// assert_eq!(
65    ///     Insert::one(cake::Model {
66    ///         id: 1,
67    ///         name: "Apple Pie".to_owned(),
68    ///     })
69    ///     .build(DbBackend::Postgres)
70    ///     .to_string(),
71    ///     r#"INSERT INTO "cake" ("id", "name") VALUES (1, 'Apple Pie')"#,
72    /// );
73    /// ```
74    /// ActiveModel
75    /// ```
76    /// use sea_orm::{DbBackend, entity::*, query::*, tests_cfg::cake};
77    ///
78    /// assert_eq!(
79    ///     Insert::one(cake::ActiveModel {
80    ///         id: NotSet,
81    ///         name: Set("Apple Pie".to_owned()),
82    ///     })
83    ///     .build(DbBackend::Postgres)
84    ///     .to_string(),
85    ///     r#"INSERT INTO "cake" ("name") VALUES ('Apple Pie')"#,
86    /// );
87    /// ```
88    pub fn one<M>(m: M) -> Self
89    where
90        M: IntoActiveModel<A>,
91    {
92        let mut query = InsertStatement::new();
93        query
94            .into_table(A::Entity::default().table_ref())
95            .or_default_values();
96
97        let mut am: A = m.into_active_model();
98        let primary_key =
99            if !<<A::Entity as EntityTrait>::PrimaryKey as PrimaryKeyTrait>::auto_increment() {
100                am.get_primary_key_value()
101            } else {
102                None
103            };
104        let mut columns = Vec::new();
105        let mut values = Vec::new();
106
107        for col in <A::Entity as EntityTrait>::Column::iter() {
108            let av = am.take(col);
109
110            match av {
111                ActiveValue::Set(value) | ActiveValue::Unchanged(value) => {
112                    columns.push(col);
113                    values.push(col.save_as(Expr::val(value)));
114                }
115                ActiveValue::NotSet => {}
116            }
117        }
118
119        query.columns(columns);
120        query.values_panic(values);
121
122        Self {
123            query,
124            primary_key,
125            model: PhantomData,
126        }
127    }
128
129    /// Insert many Model or ActiveModel.
130    /// Alias to [`InsertMany::many`].
131    ///
132    /// ```
133    /// use sea_orm::{DbBackend, entity::*, query::*, tests_cfg::cake};
134    ///
135    /// assert_eq!(
136    ///     Insert::many([
137    ///         cake::Model {
138    ///             id: 1,
139    ///             name: "Apple Pie".to_owned(),
140    ///         },
141    ///         cake::Model {
142    ///             id: 2,
143    ///             name: "Orange Scone".to_owned(),
144    ///         }
145    ///     ])
146    ///     .build(DbBackend::Postgres)
147    ///     .to_string(),
148    ///     r#"INSERT INTO "cake" ("id", "name") VALUES (1, 'Apple Pie'), (2, 'Orange Scone')"#,
149    /// );
150    /// ```
151    pub fn many<M, I>(models: I) -> InsertMany<A>
152    where
153        M: IntoActiveModel<A>,
154        I: IntoIterator<Item = M>,
155    {
156        InsertMany::many(models)
157    }
158
159    /// Set ON CONFLICT logic
160    ///
161    /// on conflict do nothing
162    /// ```
163    /// use sea_orm::{DbBackend, entity::*, query::*, sea_query::OnConflict, tests_cfg::cake};
164    ///
165    /// let orange = cake::ActiveModel {
166    ///     id: ActiveValue::set(2),
167    ///     name: ActiveValue::set("Orange".to_owned()),
168    /// };
169    /// assert_eq!(
170    ///     cake::Entity::insert(orange)
171    ///         .on_conflict(
172    ///             OnConflict::column(cake::Column::Name)
173    ///                 .do_nothing()
174    ///                 .to_owned()
175    ///         )
176    ///         .build(DbBackend::Postgres)
177    ///         .to_string(),
178    ///     r#"INSERT INTO "cake" ("id", "name") VALUES (2, 'Orange') ON CONFLICT ("name") DO NOTHING"#,
179    /// );
180    /// ```
181    ///
182    /// on conflict do update (upsert)
183    /// ```
184    /// use sea_orm::{entity::*, query::*, sea_query::OnConflict, tests_cfg::cake, DbBackend};
185    ///
186    /// let orange = cake::ActiveModel {
187    ///     id: ActiveValue::set(2),
188    ///     name: ActiveValue::set("Orange".to_owned()),
189    /// };
190    /// let query = cake::Entity::insert(orange)
191    ///     .on_conflict(
192    ///         OnConflict::column(cake::Column::Name)
193    ///             .update_column(cake::Column::Name)
194    ///             .to_owned()
195    ///     );
196    /// assert_eq!(
197    ///     query
198    ///         .build(DbBackend::MySql)
199    ///         .to_string(),
200    ///     "INSERT INTO `cake` (`id`, `name`) VALUES (2, 'Orange') ON DUPLICATE KEY UPDATE `name` = VALUES(`name`)"
201    /// );
202    /// assert_eq!(
203    ///     query
204    ///         .build(DbBackend::Postgres)
205    ///         .to_string(),
206    ///     r#"INSERT INTO "cake" ("id", "name") VALUES (2, 'Orange') ON CONFLICT ("name") DO UPDATE SET "name" = "excluded"."name""#,
207    /// );
208    /// assert_eq!(
209    ///     query
210    ///         .build(DbBackend::Sqlite)
211    ///         .to_string(),
212    ///     r#"INSERT INTO "cake" ("id", "name") VALUES (2, 'Orange') ON CONFLICT ("name") DO UPDATE SET "name" = "excluded"."name""#,
213    /// );
214    /// ```
215    pub fn on_conflict(mut self, on_conflict: OnConflict) -> Self {
216        self.query.on_conflict(on_conflict);
217        self
218    }
219
220    /// Set ON CONFLICT do nothing, but with MySQL specific polyfill.
221    pub fn on_conflict_do_nothing_on<I>(mut self, columns: I) -> TryInsert<A>
222    where
223        I: IntoIterator<Item = <A::Entity as EntityTrait>::Column>,
224    {
225        let primary_keys = <A::Entity as EntityTrait>::PrimaryKey::iter();
226        let mut on_conflict = OnConflict::columns(columns);
227        on_conflict.do_nothing_on(primary_keys);
228        self.query.on_conflict(on_conflict);
229        TryInsert::from_one(self)
230    }
231
232    /// Allow insert statement to return without error if nothing's been inserted.
233    #[deprecated(
234        since = "2.0.0",
235        note = "Please use [`TryInsert::one`] or `on_conflict_do_nothing*` methods that return [`TryInsert`], or [`Insert::try_insert`]."
236    )]
237    pub fn do_nothing(self) -> TryInsert<A>
238    where
239        A: ActiveModelTrait,
240    {
241        TryInsert::from_one(self)
242    }
243
244    /// Convert self into a `TryInsert`. It is just a wrapper for converting `DbErr::RecordNotInserted` -> `TryInsertResult::Conflicted`.
245    pub fn try_insert(self) -> TryInsert<A>
246    where
247        A: ActiveModelTrait,
248    {
249        TryInsert::from_one(self)
250    }
251
252    /// Alias to [`Insert::do_nothing`].
253    #[deprecated(
254        since = "2.0.0",
255        note = "Please use [`TryInsert::one`] or `on_conflict_do_nothing*` methods that return [`TryInsert`]"
256    )]
257    pub fn on_empty_do_nothing(self) -> TryInsert<A>
258    where
259        A: ActiveModelTrait,
260    {
261        TryInsert::from_one(self)
262    }
263
264    /// Set ON CONFLICT on primary key do nothing, but with MySQL specific polyfill.
265    ///
266    /// ```
267    /// use sea_orm::{entity::*, query::*, sea_query::OnConflict, tests_cfg::cake, DbBackend};
268    ///
269    /// let orange = cake::ActiveModel {
270    ///     id: ActiveValue::set(2),
271    ///     name: ActiveValue::set("Orange".to_owned()),
272    /// };
273    ///
274    /// assert_eq!(
275    ///     cake::Entity::insert(orange.clone())
276    ///         .on_conflict_do_nothing()
277    ///         .build(DbBackend::MySql)
278    ///         .to_string(),
279    ///     r#"INSERT INTO `cake` (`id`, `name`) VALUES (2, 'Orange') ON DUPLICATE KEY UPDATE `id` = `id`"#,
280    /// );
281    /// assert_eq!(
282    ///     cake::Entity::insert(orange.clone())
283    ///         .on_conflict_do_nothing()
284    ///         .build(DbBackend::Postgres)
285    ///         .to_string(),
286    ///     r#"INSERT INTO "cake" ("id", "name") VALUES (2, 'Orange') ON CONFLICT ("id") DO NOTHING"#,
287    /// );
288    /// assert_eq!(
289    ///     cake::Entity::insert(orange)
290    ///         .on_conflict_do_nothing()
291    ///         .build(DbBackend::Sqlite)
292    ///         .to_string(),
293    ///     r#"INSERT INTO "cake" ("id", "name") VALUES (2, 'Orange') ON CONFLICT ("id") DO NOTHING"#,
294    /// );
295    /// ```
296    pub fn on_conflict_do_nothing(mut self) -> TryInsert<A>
297    where
298        A: ActiveModelTrait,
299    {
300        self.query.on_conflict(on_conflict_primary_key::<A>());
301
302        TryInsert::from_one(self)
303    }
304}
305
306impl<A> InsertMany<A>
307where
308    A: ActiveModelTrait,
309{
310    /// Insert many Model or ActiveModel
311    pub fn many<M, I>(models: I) -> Self
312    where
313        M: IntoActiveModel<A>,
314        I: IntoIterator<Item = M>,
315    {
316        let mut query = InsertStatement::new();
317        query.into_table(A::Entity::default().table_ref());
318
319        let mut columns: Vec<_> = <A::Entity as EntityTrait>::Column::iter()
320            .map(|_| None)
321            .collect();
322        let mut null_value: Vec<Option<Value>> = std::iter::repeat_n(None, columns.len()).collect();
323        let mut all_values: Vec<Vec<SimpleExpr>> = Vec::new();
324        let mut primary_key = None;
325
326        for model in models.into_iter() {
327            let mut am: A = model.into_active_model();
328            primary_key =
329                if !<<A::Entity as EntityTrait>::PrimaryKey as PrimaryKeyTrait>::auto_increment() {
330                    am.get_primary_key_value()
331                } else {
332                    None
333                };
334            let mut values = Vec::with_capacity(columns.len());
335            for (idx, col) in <A::Entity as EntityTrait>::Column::iter().enumerate() {
336                let av = am.take(col);
337                match av {
338                    ActiveValue::Set(value) | ActiveValue::Unchanged(value) => {
339                        columns[idx] = Some(col); // mark the column as used
340                        null_value[idx] = Some(value.as_null()); // store the null value with the correct type
341                        values.push(col.save_as(Expr::val(value))); // same as add() above
342                    }
343                    ActiveValue::NotSet => {
344                        values.push(SimpleExpr::Keyword(Keyword::Null)); // indicate a missing value
345                    }
346                }
347            }
348            all_values.push(values);
349        }
350
351        let empty = all_values.is_empty();
352
353        if !all_values.is_empty() {
354            // filter only used column
355            query.columns(columns.iter().cloned().flatten());
356        }
357
358        for values in all_values {
359            // since we've aligned the column set, this never panics
360            query.values_panic(values.into_iter().enumerate().filter_map(|(i, v)| {
361                if columns[i].is_some() {
362                    // only if the column is used
363                    if !matches!(v, SimpleExpr::Keyword(Keyword::Null)) {
364                        // use the value expression
365                        Some(v)
366                    } else {
367                        // use null as standin, which must be Some
368                        null_value[i].clone().map(SimpleExpr::Value)
369                    }
370                } else {
371                    None
372                }
373            }));
374        }
375
376        Self {
377            query,
378            primary_key,
379            empty,
380            model: PhantomData,
381        }
382    }
383
384    /// Set ON CONFLICT logic
385    pub fn on_conflict(mut self, on_conflict: OnConflict) -> Self {
386        self.query.on_conflict(on_conflict);
387        self
388    }
389
390    /// Set ON CONFLICT do nothing, but with MySQL specific polyfill.
391    /// ```
392    /// use sea_orm::{entity::*, query::*, sea_query::OnConflict, tests_cfg::cake, DbBackend};
393    /// let orange = cake::ActiveModel {
394    ///     id: ActiveValue::set(2),
395    ///     name: ActiveValue::set("Orange".to_owned()),
396    /// };
397    /// assert_eq!(
398    ///     cake::Entity::insert(orange.clone())
399    ///         .on_conflict_do_nothing_on([cake::Column::Name])
400    ///         .build(DbBackend::Postgres)
401    ///         .to_string(),
402    ///     r#"INSERT INTO "cake" ("id", "name") VALUES (2, 'Orange') ON CONFLICT ("name") DO NOTHING"#,
403    /// );
404    /// assert_eq!(
405    ///     cake::Entity::insert(orange)
406    ///         .on_conflict_do_nothing_on([cake::Column::Name])
407    ///         .build(DbBackend::MySql)
408    ///         .to_string(),
409    ///     r#"INSERT INTO `cake` (`id`, `name`) VALUES (2, 'Orange') ON DUPLICATE KEY UPDATE `id` = `id`"#,
410    /// );
411    /// ```
412    pub fn on_conflict_do_nothing_on<I>(mut self, columns: I) -> TryInsert<A>
413    where
414        I: IntoIterator<Item = <A::Entity as EntityTrait>::Column>,
415    {
416        let primary_keys = <A::Entity as EntityTrait>::PrimaryKey::iter();
417        let mut on_conflict = OnConflict::columns(columns);
418        on_conflict.do_nothing_on(primary_keys);
419        self.query.on_conflict(on_conflict);
420        TryInsert::from_many(self)
421    }
422
423    /// Allow insert statement to return without error if nothing's been inserted.
424    #[deprecated(
425        since = "2.0.0",
426        note = "Please use [`TryInsert::many`] or `on_conflict_do_nothing*` methods that return [`TryInsert`], or [`InsertMany::try_insert`]"
427    )]
428    pub fn do_nothing(self) -> TryInsert<A>
429    where
430        A: ActiveModelTrait,
431    {
432        TryInsert::from_many(self)
433    }
434
435    /// Convert self into a `TryInsert`. It is just a wrapper for converting `DbErr::RecordNotInserted` -> `TryInsertResult::Conflicted`.
436    pub fn try_insert(self) -> TryInsert<A>
437    where
438        A: ActiveModelTrait,
439    {
440        TryInsert::from_many(self)
441    }
442
443    /// Alias to [`InsertMany::do_nothing`].
444    #[deprecated(
445        since = "2.0.0",
446        note = "Empty input is already handled by [`InsertMany::exec`] (no SQL executed). For conflict handling, use [`InsertMany::on_conflict_do_nothing`] or [`InsertMany::on_conflict_do_nothing_on`]."
447    )]
448    pub fn on_empty_do_nothing(self) -> TryInsert<A>
449    where
450        A: ActiveModelTrait,
451    {
452        TryInsert::from_many(self)
453    }
454
455    /// Set ON CONFLICT on primary key do nothing, but with MySQL specific polyfill.
456    /// See also [`Insert::on_conflict_do_nothing`].
457    pub fn on_conflict_do_nothing(mut self) -> TryInsert<A>
458    where
459        A: ActiveModelTrait,
460    {
461        self.query.on_conflict(on_conflict_primary_key::<A>());
462
463        TryInsert::from_many(self)
464    }
465
466    /// panic when self is empty
467    pub(crate) fn into_one(self) -> Insert<A> {
468        assert!(!self.empty);
469
470        let Self {
471            query,
472            primary_key,
473            empty: _,
474            model,
475        } = self;
476
477        Insert {
478            query,
479            primary_key,
480            model,
481        }
482    }
483}
484
485impl<A> QueryTrait for Insert<A>
486where
487    A: ActiveModelTrait,
488{
489    type QueryStatement = InsertStatement;
490
491    fn query(&mut self) -> &mut InsertStatement {
492        &mut self.query
493    }
494
495    fn as_query(&self) -> &InsertStatement {
496        &self.query
497    }
498
499    fn into_query(self) -> InsertStatement {
500        self.query
501    }
502}
503
504impl<A> QueryTrait for InsertMany<A>
505where
506    A: ActiveModelTrait,
507{
508    type QueryStatement = InsertStatement;
509
510    fn query(&mut self) -> &mut InsertStatement {
511        &mut self.query
512    }
513
514    fn as_query(&self) -> &InsertStatement {
515        &self.query
516    }
517
518    fn into_query(self) -> InsertStatement {
519        self.query
520    }
521}
522
523impl<A> TryInsert<A>
524where
525    A: ActiveModelTrait,
526{
527    fn from_one(insert: Insert<A>) -> Self {
528        Self {
529            insert_struct: insert,
530            empty: false,
531        }
532    }
533
534    fn from_many(insert: InsertMany<A>) -> Self {
535        let InsertMany {
536            query,
537            primary_key,
538            empty,
539            model,
540        } = insert;
541
542        Self {
543            insert_struct: Insert {
544                query,
545                primary_key,
546                model,
547            },
548            empty,
549        }
550    }
551
552    /// Try insert one item
553    pub fn one<M>(m: M) -> Self
554    where
555        M: IntoActiveModel<A>,
556    {
557        Self::from_one(Insert::one(m))
558    }
559
560    /// Try insert many items
561    pub fn many<M, I>(models: I) -> Self
562    where
563        M: IntoActiveModel<A>,
564        I: IntoIterator<Item = M>,
565    {
566        Self::from_many(Insert::many(models))
567    }
568
569    /// Set ON CONFLICT logic
570    pub fn on_conflict(mut self, on_conflict: OnConflict) -> Insert<A> {
571        self.insert_struct.query.on_conflict(on_conflict);
572        self.insert_struct
573    }
574
575    /// Set ON CONFLICT on primary key do nothing, but with MySQL specific polyfill.
576    pub fn on_conflict_do_nothing(mut self) -> Self {
577        self.insert_struct
578            .query
579            .on_conflict(on_conflict_primary_key::<A>());
580
581        self
582    }
583
584    /// Set ON CONFLICT do nothing, but with MySQL specific polyfill.
585    pub fn on_conflict_do_nothing_on<I>(mut self, columns: I) -> Self
586    where
587        I: IntoIterator<Item = <A::Entity as EntityTrait>::Column>,
588    {
589        let primary_keys = <A::Entity as EntityTrait>::PrimaryKey::iter();
590        let mut on_conflict = OnConflict::columns(columns);
591        on_conflict.do_nothing_on(primary_keys);
592        self.insert_struct.query.on_conflict(on_conflict);
593        self
594    }
595}
596
597impl<A> QueryTrait for TryInsert<A>
598where
599    A: ActiveModelTrait,
600{
601    type QueryStatement = InsertStatement;
602
603    fn query(&mut self) -> &mut InsertStatement {
604        &mut self.insert_struct.query
605    }
606
607    fn as_query(&self) -> &InsertStatement {
608        &self.insert_struct.query
609    }
610
611    fn into_query(self) -> InsertStatement {
612        self.insert_struct.query
613    }
614}
615
616fn on_conflict_primary_key<A: ActiveModelTrait>() -> OnConflict {
617    let primary_keys = <A::Entity as EntityTrait>::PrimaryKey::iter();
618    let mut on_conflict = OnConflict::columns(primary_keys.clone());
619    on_conflict.do_nothing_on(primary_keys);
620    on_conflict
621}
622
623#[cfg(test)]
624mod tests {
625    use sea_query::OnConflict;
626
627    use crate::tests_cfg::{cake, cake_filling};
628    use crate::{
629        ActiveValue, DbBackend, DbErr, EntityTrait, Insert, IntoActiveModel, NotSet, QueryTrait,
630        Set,
631    };
632
633    #[test]
634    fn insert_1() {
635        assert_eq!(
636            Insert::<cake::ActiveModel>::one(cake::ActiveModel {
637                id: ActiveValue::not_set(),
638                name: ActiveValue::set("Apple Pie".to_owned()),
639            })
640            .build(DbBackend::Postgres)
641            .to_string(),
642            r#"INSERT INTO "cake" ("name") VALUES ('Apple Pie')"#,
643        );
644    }
645
646    #[test]
647    fn insert_2() {
648        assert_eq!(
649            Insert::<cake::ActiveModel>::one(cake::ActiveModel {
650                id: ActiveValue::set(1),
651                name: ActiveValue::set("Apple Pie".to_owned()),
652            })
653            .build(DbBackend::Postgres)
654            .to_string(),
655            r#"INSERT INTO "cake" ("id", "name") VALUES (1, 'Apple Pie')"#,
656        );
657    }
658
659    #[test]
660    fn insert_3() {
661        assert_eq!(
662            Insert::<cake::ActiveModel>::one(cake::Model {
663                id: 1,
664                name: "Apple Pie".to_owned(),
665            })
666            .build(DbBackend::Postgres)
667            .to_string(),
668            r#"INSERT INTO "cake" ("id", "name") VALUES (1, 'Apple Pie')"#,
669        );
670    }
671
672    #[test]
673    fn insert_many_1() {
674        assert_eq!(
675            Insert::<cake::ActiveModel>::many([
676                cake::Model {
677                    id: 1,
678                    name: "Apple Pie".to_owned(),
679                },
680                cake::Model {
681                    id: 2,
682                    name: "Orange Scone".to_owned(),
683                }
684            ])
685            .build(DbBackend::Postgres)
686            .to_string(),
687            r#"INSERT INTO "cake" ("id", "name") VALUES (1, 'Apple Pie'), (2, 'Orange Scone')"#,
688        );
689    }
690
691    #[test]
692    fn insert_many_2() {
693        assert_eq!(
694            Insert::<cake::ActiveModel>::many([
695                cake::ActiveModel {
696                    id: NotSet,
697                    name: Set("Apple Pie".to_owned()),
698                },
699                cake::ActiveModel {
700                    id: NotSet,
701                    name: Set("Orange Scone".to_owned()),
702                }
703            ])
704            .build(DbBackend::Postgres)
705            .to_string(),
706            r#"INSERT INTO "cake" ("name") VALUES ('Apple Pie'), ('Orange Scone')"#,
707        );
708    }
709
710    #[test]
711    fn insert_many_3() {
712        let apple = cake_filling::ActiveModel {
713            cake_id: ActiveValue::set(2),
714            filling_id: ActiveValue::NotSet,
715        };
716        let orange = cake_filling::ActiveModel {
717            cake_id: ActiveValue::NotSet,
718            filling_id: ActiveValue::set(3),
719        };
720        assert_eq!(
721            Insert::<cake_filling::ActiveModel>::many([apple, orange])
722                .build(DbBackend::Postgres)
723                .to_string(),
724            r#"INSERT INTO "cake_filling" ("cake_id", "filling_id") VALUES (2, NULL), (NULL, 3)"#,
725        );
726    }
727
728    #[test]
729    fn insert_6() {
730        let orange = cake::ActiveModel {
731            id: ActiveValue::set(2),
732            name: ActiveValue::set("Orange".to_owned()),
733        };
734
735        assert_eq!(
736            cake::Entity::insert(orange)
737                .on_conflict(
738                    OnConflict::column(cake::Column::Name)
739                        .do_nothing()
740                        .to_owned()
741                )
742                .build(DbBackend::Postgres)
743                .to_string(),
744            r#"INSERT INTO "cake" ("id", "name") VALUES (2, 'Orange') ON CONFLICT ("name") DO NOTHING"#,
745        );
746    }
747
748    #[test]
749    fn insert_7() {
750        let orange = cake::ActiveModel {
751            id: ActiveValue::set(2),
752            name: ActiveValue::set("Orange".to_owned()),
753        };
754
755        assert_eq!(
756            cake::Entity::insert(orange)
757                .on_conflict(
758                    OnConflict::column(cake::Column::Name)
759                        .update_column(cake::Column::Name)
760                        .to_owned()
761                )
762                .build(DbBackend::Postgres)
763                .to_string(),
764            r#"INSERT INTO "cake" ("id", "name") VALUES (2, 'Orange') ON CONFLICT ("name") DO UPDATE SET "name" = "excluded"."name""#,
765        );
766    }
767
768    #[test]
769    fn test_on_conflict_do_nothing_on() {
770        let orange = cake::ActiveModel {
771            id: ActiveValue::set(2),
772            name: ActiveValue::set("Orange".to_owned()),
773        };
774
775        assert_eq!(
776            cake::Entity::insert(orange.clone())
777                .on_conflict_do_nothing_on([cake::Column::Name])
778                .build(DbBackend::Postgres)
779                .to_string(),
780            r#"INSERT INTO "cake" ("id", "name") VALUES (2, 'Orange') ON CONFLICT ("name") DO NOTHING"#,
781        );
782        assert_eq!(
783            cake::Entity::insert(orange)
784                .on_conflict_do_nothing_on([cake::Column::Name])
785                .build(DbBackend::MySql)
786                .to_string(),
787            r#"INSERT INTO `cake` (`id`, `name`) VALUES (2, 'Orange') ON DUPLICATE KEY UPDATE `id` = `id`"#,
788        );
789    }
790
791    #[test]
792    fn insert_8() -> Result<(), DbErr> {
793        use crate::{DbBackend, MockDatabase, Statement, Transaction};
794
795        mod post {
796            use crate as sea_orm;
797            use crate::entity::prelude::*;
798
799            #[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel)]
800            #[sea_orm(table_name = "posts")]
801            pub struct Model {
802                #[sea_orm(primary_key, select_as = "INTEGER", save_as = "TEXT")]
803                pub id: i32,
804                pub title: String,
805                pub text: String,
806            }
807
808            #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
809            pub enum Relation {}
810
811            impl ActiveModelBehavior for ActiveModel {}
812        }
813
814        let model = post::Model {
815            id: 1,
816            title: "News wrap up 2022".into(),
817            text: "brbrbrrrbrbrbrr...".into(),
818        };
819
820        let db = MockDatabase::new(DbBackend::Postgres)
821            .append_query_results([[model.clone()]])
822            .into_connection();
823
824        post::Entity::insert(model.into_active_model()).exec(&db)?;
825
826        assert_eq!(
827            db.into_transaction_log(),
828            [Transaction::many([Statement::from_sql_and_values(
829                DbBackend::Postgres,
830                r#"INSERT INTO "posts" ("id", "title", "text") VALUES (CAST($1 AS TEXT), $2, $3) RETURNING CAST("id" AS INTEGER)"#,
831                [
832                    1.into(),
833                    "News wrap up 2022".into(),
834                    "brbrbrrrbrbrbrr...".into(),
835                ]
836            )])]
837        );
838
839        Ok(())
840    }
841
842    #[test]
843    fn insert_9() -> Result<(), DbErr> {
844        use crate::{DbBackend, MockDatabase, MockExecResult, Statement, Transaction};
845
846        mod post {
847            use crate as sea_orm;
848            use crate::entity::prelude::*;
849
850            #[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel)]
851            #[sea_orm(table_name = "posts")]
852            pub struct Model {
853                #[sea_orm(
854                    primary_key,
855                    auto_increment = false,
856                    select_as = "INTEGER",
857                    save_as = "TEXT"
858                )]
859                pub id_primary: i32,
860                #[sea_orm(
861                    primary_key,
862                    auto_increment = false,
863                    select_as = "INTEGER",
864                    save_as = "TEXT"
865                )]
866                pub id_secondary: i32,
867                pub title: String,
868                pub text: String,
869            }
870
871            #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
872            pub enum Relation {}
873
874            impl ActiveModelBehavior for ActiveModel {}
875        }
876
877        let model = post::Model {
878            id_primary: 1,
879            id_secondary: 1001,
880            title: "News wrap up 2022".into(),
881            text: "brbrbrrrbrbrbrr...".into(),
882        };
883
884        let db = MockDatabase::new(DbBackend::Postgres)
885            .append_query_results([[model.clone()]])
886            .into_connection();
887
888        post::Entity::insert(model.into_active_model()).exec(&db)?;
889
890        assert_eq!(
891            db.into_transaction_log(),
892            [Transaction::many([Statement::from_sql_and_values(
893                DbBackend::Postgres,
894                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)"#,
895                [
896                    1.into(),
897                    1001.into(),
898                    "News wrap up 2022".into(),
899                    "brbrbrrrbrbrbrr...".into(),
900                ]
901            )])]
902        );
903
904        Ok(())
905    }
906}