qraft_core/builder/
delete.rs1use crate::{
4 HasDialect, ModelQueryPolicy, Qrafting, Query, QueryOf,
5 builder::Update,
6 cte::IntoCtes,
7 emitter::Emitter,
8 query::{LowerFilter, Table, TypedCompiled, rewrite_params},
9};
10
11type SoftDeleteFn<T> = fn(Query) -> Option<Update<T>>;
12
13pub struct Delete<T> {
15 pub table: Table<T>,
17 pub query: Query,
19 soft_delete: Option<SoftDeleteFn<T>>,
20}
21
22pub fn delete_from<T>(table: Table<T>) -> Delete<T> {
37 Delete {
38 table,
39 query: Query::from(table),
40 soft_delete: None,
41 }
42}
43
44impl<T> Delete<T> {
45 pub fn with<C>(mut self, ctes: C) -> Self
47 where
48 C: IntoCtes,
49 {
50 self.query = self.query.with(ctes);
51 self
52 }
53
54 pub fn with_recursive<C>(mut self, ctes: C) -> Self
56 where
57 C: IntoCtes,
58 {
59 self.query = self.query.with_recursive(ctes);
60 self
61 }
62
63 pub fn filter<F>(mut self, clause: F) -> Self
65 where
66 F: LowerFilter,
67 {
68 self.query = self.query.filter(clause);
69 self
70 }
71
72 pub fn or_filter<F>(mut self, clause: F) -> Self
74 where
75 F: LowerFilter,
76 {
77 self.query = self.query.or_filter(clause);
78 self
79 }
80
81 pub fn into_compiled<D: HasDialect>(mut self) -> TypedCompiled<T> {
83 if let Some(soft_delete) = self.soft_delete
84 && let Some(update) = soft_delete(self.query.clone())
85 {
86 return update.into_compiled::<D>();
87 }
88
89 let sql = self.to_sql::<D>();
90
91 TypedCompiled {
92 sql,
93 params: self.query.params,
94 data: self.query.data,
95 marker: std::marker::PhantomData,
96 }
97 }
98
99 pub fn to_debug_sql<D: HasDialect>(&mut self) -> String {
101 if let Some(soft_delete) = self.soft_delete
102 && let Some(mut update) = soft_delete(self.query.clone())
103 {
104 return update.to_debug_sql::<D>();
105 }
106
107 let mut sql = self.to_sql::<D>();
108 self.query.debug_params(&mut sql).unwrap();
109 sql
110 }
111
112 pub fn to_sql<D: HasDialect>(&mut self) -> String {
114 if let Some(soft_delete) = self.soft_delete
115 && let Some(mut update) = soft_delete(self.query.clone())
116 {
117 return update.to_sql::<D>();
118 }
119
120 let mut writer = String::new();
121 let mut directives = Vec::new();
122 let mut indexes = Vec::new();
123
124 let mut emitter = Emitter::new(
125 &mut writer,
126 &self.query.data,
127 D::DIALECT,
128 &mut directives,
129 &mut indexes,
130 );
131
132 emitter.emit_delete(self).unwrap();
133
134 rewrite_params(&indexes, &mut self.query.params);
135
136 writer
137 }
138}
139
140impl<M> QueryOf<M>
141where
142 M: Qrafting,
143{
144 pub fn delete_query(self) -> Delete<M> {
145 let table = Table::new(M::TABLE);
146 Delete {
147 table,
148 query: self.into(),
149 soft_delete: Some(<M::QueryPolicy as ModelQueryPolicy<M>>::soft_delete),
150 }
151 }
152
153 pub async fn delete<E>(self, exec: E) -> quex::Result<quex::ExecResult>
154 where
155 E: quex::Executor,
156 {
157 self.delete_query().execute(exec).await
158 }
159
160 pub fn force_delete_query(self) -> Delete<M> {
161 let table = Table::new(M::TABLE);
162 Delete {
163 table,
164 query: self.into(),
165 soft_delete: None,
166 }
167 }
168
169 pub async fn force_delete<E>(self, exec: E) -> quex::Result<quex::ExecResult>
170 where
171 E: quex::Executor,
172 {
173 self.force_delete_query().execute(exec).await
174 }
175
176 pub fn restore_query(self) -> Update<M> {
177 <M::QueryPolicy as ModelQueryPolicy<M>>::restore(self.into())
178 .expect("QueryOf::restore_query() is only available for soft-deletable qraft models")
179 }
180
181 pub async fn restore<E>(self, exec: E) -> quex::Result<quex::ExecResult>
182 where
183 E: quex::Executor,
184 {
185 self.restore_query().execute(exec).await
186 }
187}
188
189impl<T> Delete<T> {
190 pub fn force(mut self) -> Self {
191 self.soft_delete = None;
192 self
193 }
194}
195
196impl<T> Delete<T> {
197 fn from_query(table: Table<T>, query: Query) -> Self {
198 Self {
199 table,
200 query,
201 soft_delete: None,
202 }
203 }
204}
205
206impl Query {
207 pub fn delete_query(self) -> Delete<()> {
208 let table = self
209 .base_table_static()
210 .expect("Query::delete() requires a base table created from a static table source");
211 Delete::from_query(Table::new(table), self)
212 }
213
214 pub async fn delete<E>(self, exec: E) -> quex::Result<quex::ExecResult>
215 where
216 E: quex::Executor,
217 {
218 self.delete_query().execute(exec).await
219 }
220}
221
222#[cfg(test)]
223mod tests {
224 use super::*;
225 use crate::{
226 Sqlite,
227 expression::{Col, PredicateExt},
228 tests::{User, id, table},
229 };
230
231 #[test]
232 fn test_simple_delete_stmt() {
233 let stmt = delete_from(table).to_debug_sql::<Sqlite>();
234 assert_eq!(stmt, r#"delete from "users"; params=[]"#);
235 }
236
237 #[test]
238 fn test_filter_delete_stmt() {
239 let stmt = delete_from(table)
240 .filter("id".bigint().eq(10))
241 .to_debug_sql::<Sqlite>();
242 assert_eq!(stmt, r#"delete from "users" where "id" = ?; params=[10]"#);
243 }
244
245 #[test]
246 fn test_filter_complex_delete_stmt() {
247 let stmt = delete_from(table)
248 .filter("id".bigint().eq(10).or("name".bigint().eq(1)))
249 .filter("username".bigint().eq(10))
250 .to_debug_sql::<Sqlite>();
251 assert_eq!(
252 stmt,
253 r#"delete from "users" where ("id" = ? or "name" = ?) and "username" = ?; params=[10, 1, 10]"#
254 );
255 }
256
257 #[test]
258 fn test_delete_query() {
259 let stmt = Query::from("users")
260 .filter(id.eq(1))
261 .typed::<User>()
262 .delete_query()
263 .to_debug_sql::<Sqlite>();
264
265 assert_eq!(
266 stmt,
267 r#"delete from "users" where "users"."id" = ?; params=[1]"#
268 );
269 }
270
271 #[test]
272 fn test_delete_or_filter_combines_with_existing_where_clause() {
273 let stmt = delete_from(table)
274 .filter(id.eq(1))
275 .or_filter(id.eq(2))
276 .to_debug_sql::<Sqlite>();
277
278 assert_eq!(
279 stmt,
280 r#"delete from "users" where "users"."id" = ? or "users"."id" = ?; params=[1, 2]"#
281 );
282 }
283}