sea_orm/executor/
delete.rs

1use super::{ReturningSelector, SelectModel};
2use crate::{
3    ActiveModelTrait, ColumnTrait, ConnectionTrait, DeleteMany, DeleteOne, EntityTrait, Iterable,
4    error::*,
5};
6use sea_query::{DeleteStatement, Query};
7use std::future::Future;
8
9/// Handles DELETE operations in a ActiveModel using [DeleteStatement]
10#[derive(Clone, Debug)]
11pub struct Deleter {
12    query: DeleteStatement,
13}
14
15/// The result of a DELETE operation
16#[derive(Clone, Debug, PartialEq, Eq)]
17pub struct DeleteResult {
18    /// The number of rows affected by the DELETE operation
19    pub rows_affected: u64,
20}
21
22impl<A> DeleteOne<A>
23where
24    A: ActiveModelTrait,
25{
26    /// Execute a DELETE operation on one ActiveModel
27    pub async fn exec<C>(self, db: &C) -> Result<DeleteResult, DbErr>
28    where
29        C: ConnectionTrait,
30    {
31        if let Some(err) = self.error {
32            return Err(err);
33        }
34        exec_delete_only(self.query, db).await
35    }
36
37    /// Execute an delete operation and return the deleted model
38    pub async fn exec_with_returning<C>(
39        self,
40        db: &C,
41    ) -> Result<Option<<A::Entity as EntityTrait>::Model>, DbErr>
42    where
43        C: ConnectionTrait,
44    {
45        if let Some(err) = self.error {
46            return Err(err);
47        }
48        exec_delete_with_returning_one::<A::Entity, _>(self.query, db).await
49    }
50}
51
52impl<'a, E> DeleteMany<E>
53where
54    E: EntityTrait,
55{
56    /// Execute a DELETE operation on many ActiveModels
57    pub fn exec<C>(self, db: &'a C) -> impl Future<Output = Result<DeleteResult, DbErr>> + 'a
58    where
59        C: ConnectionTrait,
60    {
61        // so that self is dropped before entering await
62        exec_delete_only(self.query, db)
63    }
64
65    /// Execute an delete operation and return the deleted model
66    pub fn exec_with_returning<C>(
67        self,
68        db: &C,
69    ) -> impl Future<Output = Result<Vec<E::Model>, DbErr>> + '_
70    where
71        E: EntityTrait,
72        C: ConnectionTrait,
73    {
74        exec_delete_with_returning_many::<E, _>(self.query, db)
75    }
76}
77
78impl Deleter {
79    /// Instantiate a new [Deleter] by passing it a [DeleteStatement]
80    pub fn new(query: DeleteStatement) -> Self {
81        Self { query }
82    }
83
84    /// Execute a DELETE operation
85    pub fn exec<C>(self, db: &C) -> impl Future<Output = Result<DeleteResult, DbErr>> + '_
86    where
87        C: ConnectionTrait,
88    {
89        exec_delete(self.query, db)
90    }
91
92    /// Execute an delete operation and return the deleted model
93    pub fn exec_with_returning<E, C>(
94        self,
95        db: &C,
96    ) -> impl Future<Output = Result<Vec<E::Model>, DbErr>> + '_
97    where
98        E: EntityTrait,
99        C: ConnectionTrait,
100    {
101        exec_delete_with_returning_many::<E, _>(self.query, db)
102    }
103}
104
105async fn exec_delete_only<C>(query: DeleteStatement, db: &C) -> Result<DeleteResult, DbErr>
106where
107    C: ConnectionTrait,
108{
109    Deleter::new(query).exec(db).await
110}
111
112async fn exec_delete<C>(query: DeleteStatement, db: &C) -> Result<DeleteResult, DbErr>
113where
114    C: ConnectionTrait,
115{
116    let result = db.execute(&query).await?;
117    Ok(DeleteResult {
118        rows_affected: result.rows_affected(),
119    })
120}
121
122async fn exec_delete_with_returning_one<E, C>(
123    mut query: DeleteStatement,
124    db: &C,
125) -> Result<Option<E::Model>, DbErr>
126where
127    E: EntityTrait,
128    C: ConnectionTrait,
129{
130    let db_backend = db.get_database_backend();
131    match db.support_returning() {
132        true => {
133            query.returning_all();
134            ReturningSelector::<SelectModel<<E>::Model>, _>::from_query(query)
135                .one(db)
136                .await
137        }
138        false => Err(DbErr::BackendNotSupported {
139            db: db_backend.as_str(),
140            ctx: "DELETE RETURNING",
141        }),
142    }
143}
144
145async fn exec_delete_with_returning_many<E, C>(
146    mut query: DeleteStatement,
147    db: &C,
148) -> Result<Vec<E::Model>, DbErr>
149where
150    E: EntityTrait,
151    C: ConnectionTrait,
152{
153    let db_backend = db.get_database_backend();
154    match db.support_returning() {
155        true => {
156            let returning = Query::returning().exprs(
157                E::Column::iter().map(|c| c.select_enum_as(c.into_returning_expr(db_backend))),
158            );
159            query.returning(returning);
160            ReturningSelector::<SelectModel<<E>::Model>, _>::from_query(query)
161                .all(db)
162                .await
163        }
164        false => Err(DbErr::BackendNotSupported {
165            db: db_backend.as_str(),
166            ctx: "DELETE RETURNING",
167        }),
168    }
169}
170
171#[cfg(test)]
172mod tests {
173    use crate::tests_cfg::cake;
174
175    #[smol_potat::test]
176    async fn delete_error() {
177        use crate::{DbBackend, DbErr, Delete, EntityTrait, MockDatabase};
178
179        let db = MockDatabase::new(DbBackend::MySql).into_connection();
180
181        assert!(matches!(
182            Delete::one(cake::ActiveModel {
183                ..Default::default()
184            })
185            .exec(&db)
186            .await,
187            Err(DbErr::PrimaryKeyNotSet { .. })
188        ));
189
190        assert!(matches!(
191            cake::Entity::delete(cake::ActiveModel::default())
192                .exec(&db)
193                .await,
194            Err(DbErr::PrimaryKeyNotSet { .. })
195        ));
196    }
197}