1use super::{ReturningSelector, SelectModel};
2use crate::{
3 ColumnTrait, ConnectionTrait, DeleteMany, DeleteOne, EntityTrait, Iterable, ValidatedDeleteOne,
4 error::*,
5};
6use sea_query::{DeleteStatement, Query};
7
8#[derive(Clone, Debug)]
15pub struct Deleter {
16 query: DeleteStatement,
17}
18
19#[derive(Clone, Debug, PartialEq, Eq)]
21#[non_exhaustive]
22pub struct DeleteResult {
23 pub rows_affected: u64,
25}
26
27impl<E> ValidatedDeleteOne<E>
28where
29 E: EntityTrait,
30{
31 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 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 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 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 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 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 pub fn new(query: DeleteStatement) -> Self {
94 Self { query }
95 }
96
97 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 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}