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