Skip to main content

sea_orm/executor/
delete.rs

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