sea_orm/executor/
insert.rs

1use super::ReturningSelector;
2use crate::{
3    ActiveModelTrait, ColumnTrait, ConnectionTrait, DbBackend, EntityTrait, Insert, InsertMany,
4    IntoActiveModel, Iterable, PrimaryKeyToColumn, PrimaryKeyTrait, SelectModel, TryFromU64,
5    TryInsert, error::*,
6};
7use sea_query::{FromValueTuple, Iden, InsertStatement, Query, ReturningClause, ValueTuple};
8use std::marker::PhantomData;
9
10type PrimaryKey<A> = <<A as ActiveModelTrait>::Entity as EntityTrait>::PrimaryKey;
11
12/// Defines a structure to perform INSERT operations in an ActiveModel
13#[derive(Debug)]
14pub struct Inserter<A>
15where
16    A: ActiveModelTrait,
17{
18    primary_key: Option<ValueTuple>,
19    query: InsertStatement,
20    model: PhantomData<A>,
21}
22
23/// The result of an INSERT operation on an ActiveModel
24#[derive(Debug)]
25#[non_exhaustive]
26pub struct InsertResult<A>
27where
28    A: ActiveModelTrait,
29{
30    /// The primary key value of the last inserted row
31    pub last_insert_id: <PrimaryKey<A> as PrimaryKeyTrait>::ValueType,
32}
33
34/// The result of an INSERT many operation for a set of ActiveModels
35#[derive(Debug)]
36#[non_exhaustive]
37pub struct InsertManyResult<A>
38where
39    A: ActiveModelTrait,
40{
41    /// The primary key value of the last inserted row
42    pub last_insert_id: Option<<PrimaryKey<A> as PrimaryKeyTrait>::ValueType>,
43}
44
45/// The types of results for an INSERT operation
46#[derive(Debug)]
47pub enum TryInsertResult<T> {
48    /// The INSERT statement did not have any value to insert
49    Empty,
50    /// The INSERT operation did not insert any valid value
51    Conflicted,
52    /// Successfully inserted
53    Inserted(T),
54}
55
56impl<A> TryInsertResult<InsertResult<A>>
57where
58    A: ActiveModelTrait,
59{
60    /// Empty: `Ok(None)`. Inserted: `Ok(Some(last_insert_id))`. Conflicted: `Err(DbErr::RecordNotInserted)`.
61    pub fn last_insert_id(
62        self,
63    ) -> Result<Option<<PrimaryKey<A> as PrimaryKeyTrait>::ValueType>, DbErr> {
64        match self {
65            Self::Empty => Ok(None),
66            Self::Inserted(v) => Ok(Some(v.last_insert_id)),
67            Self::Conflicted => Err(DbErr::RecordNotInserted),
68        }
69    }
70}
71
72impl<A> TryInsert<A>
73where
74    A: ActiveModelTrait,
75{
76    /// Execute an insert operation
77    pub fn exec<C>(self, db: &C) -> Result<TryInsertResult<InsertResult<A>>, DbErr>
78    where
79        C: ConnectionTrait,
80    {
81        if self.empty {
82            return Ok(TryInsertResult::Empty);
83        }
84        let res = self.insert_struct.exec(db);
85        match res {
86            Ok(res) => Ok(TryInsertResult::Inserted(res)),
87            Err(DbErr::RecordNotInserted) => Ok(TryInsertResult::Conflicted),
88            Err(err) => Err(err),
89        }
90    }
91
92    /// Execute an insert operation without returning (don't use `RETURNING` syntax)
93    /// Number of rows affected is returned
94    pub fn exec_without_returning<C>(self, db: &C) -> Result<TryInsertResult<u64>, DbErr>
95    where
96        C: ConnectionTrait,
97    {
98        if self.empty {
99            return Ok(TryInsertResult::Empty);
100        }
101        let res = self.insert_struct.exec_without_returning(db);
102        match res {
103            Ok(res) => Ok(TryInsertResult::Inserted(res)),
104            Err(DbErr::RecordNotInserted) => Ok(TryInsertResult::Conflicted),
105            Err(err) => Err(err),
106        }
107    }
108
109    /// Execute an insert operation and return the inserted model (use `RETURNING` syntax if supported)
110    pub fn exec_with_returning<C>(
111        self,
112        db: &C,
113    ) -> Result<TryInsertResult<<A::Entity as EntityTrait>::Model>, DbErr>
114    where
115        <A::Entity as EntityTrait>::Model: IntoActiveModel<A>,
116        C: ConnectionTrait,
117    {
118        if self.empty {
119            return Ok(TryInsertResult::Empty);
120        }
121        let res = self.insert_struct.exec_with_returning(db);
122        match res {
123            Ok(res) => Ok(TryInsertResult::Inserted(res)),
124            Err(DbErr::RecordNotInserted) => Ok(TryInsertResult::Conflicted),
125            Err(err) => Err(err),
126        }
127    }
128
129    /// Execute an insert operation and return primary keys of inserted models
130    pub fn exec_with_returning_keys<C>(
131        self,
132        db: &C,
133    ) -> Result<TryInsertResult<Vec<<PrimaryKey<A> as PrimaryKeyTrait>::ValueType>>, DbErr>
134    where
135        <A::Entity as EntityTrait>::Model: IntoActiveModel<A>,
136        C: ConnectionTrait,
137    {
138        if self.empty {
139            return Ok(TryInsertResult::Empty);
140        }
141
142        let res = self.insert_struct.exec_with_returning_keys(db);
143        match res {
144            Ok(res) => Ok(TryInsertResult::Inserted(res)),
145            Err(DbErr::RecordNotInserted) => Ok(TryInsertResult::Conflicted),
146            Err(err) => Err(err),
147        }
148    }
149
150    /// Execute an insert operation and return all inserted models
151    pub fn exec_with_returning_many<C>(
152        self,
153        db: &C,
154    ) -> Result<TryInsertResult<Vec<<A::Entity as EntityTrait>::Model>>, DbErr>
155    where
156        <A::Entity as EntityTrait>::Model: IntoActiveModel<A>,
157        C: ConnectionTrait,
158    {
159        if self.empty {
160            return Ok(TryInsertResult::Empty);
161        }
162
163        let res = self.insert_struct.exec_with_returning_many(db);
164        match res {
165            Ok(res) => Ok(TryInsertResult::Inserted(res)),
166            Err(DbErr::RecordNotInserted) => Ok(TryInsertResult::Conflicted),
167            Err(err) => Err(err),
168        }
169    }
170}
171
172impl<A> Insert<A>
173where
174    A: ActiveModelTrait,
175{
176    /// Execute an insert operation
177    pub fn exec<'a, C>(self, db: &'a C) -> Result<InsertResult<A>, DbErr>
178    where
179        C: ConnectionTrait,
180        A: 'a,
181    {
182        // so that self is dropped before entering await
183        let mut query = self.query;
184        if db.support_returning() {
185            query.returning(returning_pk::<A>(db.get_database_backend()));
186        }
187        Inserter::<A>::new(self.primary_key, query).exec(db)
188    }
189
190    /// Execute an insert operation without returning (don't use `RETURNING` syntax)
191    /// Number of rows affected is returned
192    pub fn exec_without_returning<'a, C>(self, db: &'a C) -> Result<u64, DbErr>
193    where
194        C: ConnectionTrait,
195        A: 'a,
196    {
197        Inserter::<A>::new(self.primary_key, self.query).exec_without_returning(db)
198    }
199
200    /// Execute an insert operation and return the inserted model (use `RETURNING` syntax if supported)
201    ///
202    /// + To get back all inserted models, use [`exec_with_returning_many`].
203    /// + To get back all inserted primary keys, use [`exec_with_returning_keys`].
204    pub fn exec_with_returning<'a, C>(
205        self,
206        db: &'a C,
207    ) -> Result<<A::Entity as EntityTrait>::Model, DbErr>
208    where
209        <A::Entity as EntityTrait>::Model: IntoActiveModel<A>,
210        C: ConnectionTrait,
211        A: 'a,
212    {
213        Inserter::<A>::new(self.primary_key, self.query).exec_with_returning(db)
214    }
215
216    /// Execute an insert operation and return primary keys of inserted models
217    pub fn exec_with_returning_keys<'a, C>(
218        self,
219        db: &'a C,
220    ) -> Result<Vec<<PrimaryKey<A> as PrimaryKeyTrait>::ValueType>, DbErr>
221    where
222        <A::Entity as EntityTrait>::Model: IntoActiveModel<A>,
223        C: ConnectionTrait,
224        A: 'a,
225    {
226        Inserter::<A>::new(self.primary_key, self.query).exec_with_returning_keys(db)
227    }
228
229    /// Execute an insert operation and return all inserted models
230    pub fn exec_with_returning_many<'a, C>(
231        self,
232        db: &'a C,
233    ) -> Result<Vec<<A::Entity as EntityTrait>::Model>, DbErr>
234    where
235        <A::Entity as EntityTrait>::Model: IntoActiveModel<A>,
236        C: ConnectionTrait,
237        A: 'a,
238    {
239        Inserter::<A>::new(self.primary_key, self.query).exec_with_returning_many(db)
240    }
241}
242
243impl<A> InsertMany<A>
244where
245    A: ActiveModelTrait,
246{
247    /// Execute an insert operation
248    pub fn exec<C>(self, db: &C) -> Result<InsertManyResult<A>, DbErr>
249    where
250        C: ConnectionTrait,
251    {
252        if self.empty {
253            return Ok(InsertManyResult {
254                last_insert_id: None,
255            });
256        }
257        let res = self.into_one().exec(db);
258        match res {
259            Ok(r) => Ok(InsertManyResult {
260                last_insert_id: Some(r.last_insert_id),
261            }),
262            Err(err) => Err(err),
263        }
264    }
265
266    /// Execute an insert operation without returning (don't use `RETURNING` syntax)
267    /// Number of rows affected is returned
268    pub fn exec_without_returning<C>(self, db: &C) -> Result<u64, DbErr>
269    where
270        C: ConnectionTrait,
271    {
272        if self.empty {
273            return Ok(0);
274        }
275        self.into_one().exec_without_returning(db)
276    }
277
278    /// Execute an insert operation and return all inserted models
279    pub fn exec_with_returning<C>(
280        self,
281        db: &C,
282    ) -> Result<Vec<<A::Entity as EntityTrait>::Model>, DbErr>
283    where
284        <A::Entity as EntityTrait>::Model: IntoActiveModel<A>,
285        C: ConnectionTrait,
286    {
287        if self.empty {
288            return Ok(Vec::new());
289        }
290
291        self.into_one().exec_with_returning_many(db)
292    }
293
294    /// Alias to [`InsertMany::exec_with_returning`].
295    #[deprecated(
296        since = "1.2.0",
297        note = "Please use [`InsertMany::exec_with_returning`]"
298    )]
299    pub fn exec_with_returning_many<C>(
300        self,
301        db: &C,
302    ) -> Result<Vec<<A::Entity as EntityTrait>::Model>, DbErr>
303    where
304        <A::Entity as EntityTrait>::Model: IntoActiveModel<A>,
305        C: ConnectionTrait,
306    {
307        if self.empty {
308            return Ok(Vec::new());
309        }
310
311        self.into_one().exec_with_returning_many(db)
312    }
313
314    /// Execute an insert operation and return primary keys of inserted models
315    pub fn exec_with_returning_keys<C>(
316        self,
317        db: &C,
318    ) -> Result<Vec<<PrimaryKey<A> as PrimaryKeyTrait>::ValueType>, DbErr>
319    where
320        <A::Entity as EntityTrait>::Model: IntoActiveModel<A>,
321        C: ConnectionTrait,
322    {
323        if self.empty {
324            return Ok(Vec::new());
325        }
326
327        self.into_one().exec_with_returning_keys(db)
328    }
329}
330
331impl<A> Inserter<A>
332where
333    A: ActiveModelTrait,
334{
335    /// Instantiate a new insert operation
336    pub fn new(primary_key: Option<ValueTuple>, query: InsertStatement) -> Self {
337        Self {
338            primary_key,
339            query,
340            model: PhantomData,
341        }
342    }
343
344    /// Execute an insert operation, returning the last inserted id
345    pub fn exec<'a, C>(self, db: &'a C) -> Result<InsertResult<A>, DbErr>
346    where
347        C: ConnectionTrait,
348        A: 'a,
349    {
350        exec_insert(self.primary_key, self.query, db)
351    }
352
353    /// Execute an insert operation
354    pub fn exec_without_returning<'a, C>(self, db: &'a C) -> Result<u64, DbErr>
355    where
356        C: ConnectionTrait,
357        A: 'a,
358    {
359        exec_insert_without_returning(self.query, db)
360    }
361
362    /// Execute an insert operation and return the inserted model (use `RETURNING` syntax if supported)
363    pub fn exec_with_returning<'a, C>(
364        self,
365        db: &'a C,
366    ) -> Result<<A::Entity as EntityTrait>::Model, DbErr>
367    where
368        <A::Entity as EntityTrait>::Model: IntoActiveModel<A>,
369        C: ConnectionTrait,
370        A: 'a,
371    {
372        exec_insert_with_returning::<A, _>(self.primary_key, self.query, db)
373    }
374
375    /// Execute an insert operation and return primary keys of inserted models
376    pub fn exec_with_returning_keys<'a, C>(
377        self,
378        db: &'a C,
379    ) -> Result<Vec<<PrimaryKey<A> as PrimaryKeyTrait>::ValueType>, DbErr>
380    where
381        <A::Entity as EntityTrait>::Model: IntoActiveModel<A>,
382        C: ConnectionTrait,
383        A: 'a,
384    {
385        exec_insert_with_returning_keys::<A, _>(self.query, db)
386    }
387
388    /// Execute an insert operation and return all inserted models
389    pub fn exec_with_returning_many<'a, C>(
390        self,
391        db: &'a C,
392    ) -> Result<Vec<<A::Entity as EntityTrait>::Model>, DbErr>
393    where
394        <A::Entity as EntityTrait>::Model: IntoActiveModel<A>,
395        C: ConnectionTrait,
396        A: 'a,
397    {
398        exec_insert_with_returning_many::<A, _>(self.query, db)
399    }
400}
401
402fn exec_insert<A, C>(
403    primary_key: Option<ValueTuple>,
404    statement: InsertStatement,
405    db: &C,
406) -> Result<InsertResult<A>, DbErr>
407where
408    C: ConnectionTrait,
409    A: ActiveModelTrait,
410{
411    type ValueTypeOf<A> = <PrimaryKey<A> as PrimaryKeyTrait>::ValueType;
412
413    let db_backend = db.get_database_backend();
414
415    let last_insert_id = match (primary_key, db.support_returning()) {
416        (_, true) => {
417            let mut rows = db.query_all(&statement)?;
418            let row = match rows.pop() {
419                Some(row) => row,
420                None => return Err(DbErr::RecordNotInserted),
421            };
422            let cols = PrimaryKey::<A>::iter()
423                .map(|col| col.to_string())
424                .collect::<Vec<_>>();
425            row.try_get_many("", cols.as_ref())
426                .map_err(|_| DbErr::UnpackInsertId)?
427        }
428        (Some(value_tuple), false) => {
429            let res = db.execute(&statement)?;
430            if res.rows_affected() == 0 {
431                return Err(DbErr::RecordNotInserted);
432            }
433            FromValueTuple::from_value_tuple(value_tuple)
434        }
435        (None, false) => {
436            let res = db.execute(&statement)?;
437            if res.rows_affected() == 0 {
438                return Err(DbErr::RecordNotInserted);
439            }
440            let last_insert_id = res.last_insert_id();
441            // For MySQL, the affected-rows number:
442            //   - The affected-rows value per row is `1` if the row is inserted as a new row,
443            //   - `2` if an existing row is updated,
444            //   - and `0` if an existing row is set to its current values.
445            // Reference: https://dev.mysql.com/doc/refman/8.4/en/insert-on-duplicate.html
446            if db_backend == DbBackend::MySql && last_insert_id == 0 {
447                return Err(DbErr::RecordNotInserted);
448            }
449            ValueTypeOf::<A>::try_from_u64(last_insert_id).map_err(|_| DbErr::UnpackInsertId)?
450        }
451    };
452
453    Ok(InsertResult { last_insert_id })
454}
455
456fn exec_insert_without_returning<C>(insert_statement: InsertStatement, db: &C) -> Result<u64, DbErr>
457where
458    C: ConnectionTrait,
459{
460    let exec_result = db.execute(&insert_statement)?;
461    Ok(exec_result.rows_affected())
462}
463
464fn exec_insert_with_returning<A, C>(
465    primary_key: Option<ValueTuple>,
466    mut insert_statement: InsertStatement,
467    db: &C,
468) -> Result<<A::Entity as EntityTrait>::Model, DbErr>
469where
470    <A::Entity as EntityTrait>::Model: IntoActiveModel<A>,
471    C: ConnectionTrait,
472    A: ActiveModelTrait,
473{
474    let db_backend = db.get_database_backend();
475    let found = match db.support_returning() {
476        true => {
477            let returning = Query::returning().exprs(
478                <A::Entity as EntityTrait>::Column::iter()
479                    .map(|c| c.select_as(c.into_returning_expr(db_backend))),
480            );
481            insert_statement.returning(returning);
482            ReturningSelector::<SelectModel<<A::Entity as EntityTrait>::Model>, _>::from_query(
483                insert_statement,
484            )
485            .one(db)?
486        }
487        false => {
488            let insert_res = exec_insert::<A, _>(primary_key, insert_statement, db)?;
489            <A::Entity as EntityTrait>::find_by_id(insert_res.last_insert_id).one(db)?
490        }
491    };
492    match found {
493        Some(model) => Ok(model),
494        None => Err(DbErr::RecordNotFound(
495            "Failed to find inserted item".to_owned(),
496        )),
497    }
498}
499
500fn exec_insert_with_returning_keys<A, C>(
501    mut insert_statement: InsertStatement,
502    db: &C,
503) -> Result<Vec<<PrimaryKey<A> as PrimaryKeyTrait>::ValueType>, DbErr>
504where
505    <A::Entity as EntityTrait>::Model: IntoActiveModel<A>,
506    C: ConnectionTrait,
507    A: ActiveModelTrait,
508{
509    let db_backend = db.get_database_backend();
510    match db.support_returning() {
511        true => {
512            insert_statement.returning(returning_pk::<A>(db_backend));
513            let rows = db.query_all(&insert_statement)?;
514            let cols = PrimaryKey::<A>::iter()
515                .map(|col| col.to_string())
516                .collect::<Vec<_>>();
517            let mut keys = Vec::new();
518            for row in rows {
519                keys.push(
520                    row.try_get_many("", cols.as_ref())
521                        .map_err(|_| DbErr::UnpackInsertId)?,
522                );
523            }
524            Ok(keys)
525        }
526        false => Err(DbErr::BackendNotSupported {
527            db: db_backend.as_str(),
528            ctx: "INSERT RETURNING",
529        }),
530    }
531}
532
533fn exec_insert_with_returning_many<A, C>(
534    mut insert_statement: InsertStatement,
535    db: &C,
536) -> Result<Vec<<A::Entity as EntityTrait>::Model>, DbErr>
537where
538    <A::Entity as EntityTrait>::Model: IntoActiveModel<A>,
539    C: ConnectionTrait,
540    A: ActiveModelTrait,
541{
542    let db_backend = db.get_database_backend();
543    match db.support_returning() {
544        true => {
545            let returning = Query::returning().exprs(
546                <A::Entity as EntityTrait>::Column::iter()
547                    .map(|c| c.select_as(c.into_returning_expr(db_backend))),
548            );
549            insert_statement.returning(returning);
550            ReturningSelector::<SelectModel<<A::Entity as EntityTrait>::Model>, _>::from_query(
551                insert_statement,
552            )
553            .all(db)
554        }
555        false => Err(DbErr::BackendNotSupported {
556            db: db_backend.as_str(),
557            ctx: "INSERT RETURNING",
558        }),
559    }
560}
561
562fn returning_pk<A>(db_backend: DbBackend) -> ReturningClause
563where
564    A: ActiveModelTrait,
565{
566    Query::returning().exprs(<A::Entity as EntityTrait>::PrimaryKey::iter().map(|c| {
567        c.into_column()
568            .select_as(c.into_column().into_returning_expr(db_backend))
569    }))
570}