Skip to main content

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