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 fn exec<C>(self, db: &C) -> Result<DeleteResult, DbErr>
33    where
34        C: ConnectionTrait,
35    {
36        exec_delete_only(self.query, db)
37    }
38
39    /// Execute an delete operation and return the deleted model
40    pub 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)
45    }
46}
47
48impl<E> DeleteOne<E>
49where
50    E: EntityTrait,
51{
52    /// Execute a DELETE operation on one ActiveModel
53    pub fn exec<C>(self, db: &C) -> Result<DeleteResult, DbErr>
54    where
55        C: ConnectionTrait,
56    {
57        self.0?.exec(db)
58    }
59
60    /// Execute an delete operation and return the deleted model
61    pub 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)
66    }
67}
68
69impl<'a, E> DeleteMany<E>
70where
71    E: EntityTrait,
72{
73    /// Execute a DELETE operation on many ActiveModels
74    pub fn exec<C>(self, db: &'a C) -> Result<DeleteResult, DbErr>
75    where
76        C: ConnectionTrait,
77    {
78        exec_delete_only(self.query, db)
79    }
80
81    /// Execute an delete operation and return the deleted model
82    pub 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)
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 fn exec<C>(self, db: &C) -> Result<DeleteResult, DbErr>
99    where
100        C: ConnectionTrait,
101    {
102        exec_delete(self.query, db)
103    }
104
105    /// Execute an delete operation and return the deleted model
106    pub 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)
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
127fn exec_delete_only<C>(query: DeleteStatement, db: &C) -> Result<DeleteResult, DbErr>
128where
129    C: ConnectionTrait,
130{
131    Deleter::new(query).exec(db)
132}
133
134fn exec_delete<C>(query: DeleteStatement, db: &C) -> Result<DeleteResult, DbErr>
135where
136    C: ConnectionTrait,
137{
138    let result = db.execute(&query)?;
139    Ok(DeleteResult {
140        rows_affected: result.rows_affected(),
141    })
142}
143
144fn 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).one(db)
160        }
161        false => Err(DbErr::BackendNotSupported {
162            db: db_backend.as_str(),
163            ctx: "DELETE RETURNING",
164        }),
165    }
166}
167
168fn exec_delete_with_returning_many<E, C>(
169    mut query: DeleteStatement,
170    db: &C,
171) -> Result<Vec<E::Model>, DbErr>
172where
173    E: EntityTrait,
174    C: ConnectionTrait,
175{
176    let db_backend = db.get_database_backend();
177    match db.support_returning() {
178        true => {
179            let returning = Query::returning().exprs(
180                E::Column::iter().map(|c| c.select_enum_as(c.into_returning_expr(db_backend))),
181            );
182            query.returning(returning);
183            ReturningSelector::<SelectModel<<E>::Model>, _>::from_query(query).all(db)
184        }
185        false => Err(DbErr::BackendNotSupported {
186            db: db_backend.as_str(),
187            ctx: "DELETE RETURNING",
188        }),
189    }
190}
191
192#[cfg(test)]
193mod tests {
194    use crate::tests_cfg::cake;
195
196    #[test]
197    fn delete_error() {
198        use crate::{DbBackend, DbErr, Delete, EntityTrait, MockDatabase};
199
200        let db = MockDatabase::new(DbBackend::MySql).into_connection();
201
202        assert!(matches!(
203            Delete::one(cake::ActiveModel {
204                ..Default::default()
205            })
206            .exec(&db),
207            Err(DbErr::PrimaryKeyNotSet { .. })
208        ));
209
210        assert!(matches!(
211            cake::Entity::delete(cake::ActiveModel::default()).exec(&db),
212            Err(DbErr::PrimaryKeyNotSet { .. })
213        ));
214    }
215}