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