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)]
21pub struct DeleteResult {
22 pub rows_affected: u64,
24}
25
26impl<E> ValidatedDeleteOne<E>
27where
28 E: EntityTrait,
29{
30 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 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 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 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 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 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 pub fn new(query: DeleteStatement) -> Self {
93 Self { query }
94 }
95
96 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 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}