diesel/query_builder/delete_statement/mod.rs
1use crate::backend::DieselReserveSpecialization;
2use crate::dsl::{Filter, IntoBoxed, IntoBoxedClone, OrFilter};
3use crate::expression::{AppearsOnTable, Expression, SelectableExpression};
4use crate::query_builder::returning::{
5 DeleteStmt, NoReturningClause, ReturningClause, ReturningQuerySource,
6};
7use crate::query_builder::where_clause::*;
8use crate::query_builder::*;
9use crate::query_dsl::RunQueryDslSupport;
10use crate::query_dsl::methods::{BoxedCloneDsl, BoxedDsl, FilterDsl, OrFilterDsl};
11use crate::query_source::{QuerySource, Table};
12
13#[must_use = "Queries are only executed when calling `load`, `get_result` or similar."]
14/// Represents a SQL `DELETE` statement.
15///
16/// The type parameters on this struct represent:
17///
18/// - `T`: The table we are deleting from.
19/// - `U`: The `WHERE` clause of this query. The exact types used to represent
20/// this are private, and you should not make any assumptions about them.
21/// - `Ret`: The `RETURNING` clause of this query. The exact types used to
22/// represent this are private. You can safely rely on the default type
23/// representing the lack of a `RETURNING` clause.
24pub struct DeleteStatement<T: QuerySource, U, Ret = NoReturningClause> {
25 from_clause: FromClause<T>,
26 where_clause: U,
27 returning: Ret,
28}
29
30impl<T, U, Ret> Clone for DeleteStatement<T, U, Ret>
31where
32 T: QuerySource,
33 FromClause<T>: Clone,
34 U: Clone,
35 Ret: Clone,
36{
37 fn clone(&self) -> Self {
38 Self {
39 from_clause: self.from_clause.clone(),
40 where_clause: self.where_clause.clone(),
41 returning: self.returning.clone(),
42 }
43 }
44}
45
46impl<T, U, Ret> core::fmt::Debug for DeleteStatement<T, U, Ret>
47where
48 T: QuerySource,
49 FromClause<T>: core::fmt::Debug,
50 U: core::fmt::Debug,
51 Ret: core::fmt::Debug,
52{
53 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
54 f.debug_struct("DeleteStatement")
55 .field("from_clause", &self.from_clause)
56 .field("where_clause", &self.where_clause)
57 .field("returning", &self.returning)
58 .finish()
59 }
60}
61
62impl<T, U, Ret> QueryId for DeleteStatement<T, U, Ret>
63where
64 T: QuerySource + QueryId + 'static,
65 U: QueryId,
66 Ret: QueryId,
67{
68 type QueryId = DeleteStatement<T, U::QueryId, Ret::QueryId>;
69
70 const HAS_STATIC_QUERY_ID: bool =
71 T::HAS_STATIC_QUERY_ID && U::HAS_STATIC_QUERY_ID && Ret::HAS_STATIC_QUERY_ID;
72}
73
74/// A `DELETE` statement with a boxed `WHERE` clause
75pub type BoxedDeleteStatement<'a, DB, T, Ret = NoReturningClause> =
76 DeleteStatement<T, BoxedWhereClause<'a, DB>, Ret>;
77
78/// A `DELETE` statement with a boxed cloneable `WHERE` clause
79pub type BoxedCloneDeleteStatement<'a, DB, T, Ret = NoReturningClause> =
80 DeleteStatement<T, BoxedCloneWhereClause<'a, DB>, Ret>;
81
82impl<T: QuerySource, U> DeleteStatement<T, U, NoReturningClause> {
83 pub(crate) fn new(table: T, where_clause: U) -> Self {
84 DeleteStatement {
85 from_clause: FromClause::new(table),
86 where_clause,
87 returning: NoReturningClause,
88 }
89 }
90
91 /// Adds the given predicate to the `WHERE` clause of the statement being
92 /// constructed.
93 ///
94 /// If there is already a `WHERE` clause, the predicate will be appended
95 /// with `AND`. There is no difference in behavior between
96 /// `delete(table.filter(x))` and `delete(table).filter(x)`.
97 ///
98 /// # Example
99 ///
100 /// ```rust
101 /// # include!("../../doctest_setup.rs");
102 /// #
103 /// # // Reading the query result requires reading memory allocated by the
104 /// # // native sqlite library, which miri cannot track provenance for.
105 /// # #[cfg(miri)]
106 /// # fn main() {}
107 /// # #[cfg(not(miri))]
108 /// # fn main() {
109 /// # use schema::users::dsl::*;
110 /// # let connection = &mut establish_connection();
111 /// let deleted_rows = diesel::delete(users)
112 /// .filter(name.eq("Sean"))
113 /// .execute(connection);
114 /// assert_eq!(Ok(1), deleted_rows);
115 ///
116 /// let expected_names = vec!["Tess".to_string()];
117 /// let names = users.select(name).load(connection);
118 ///
119 /// assert_eq!(Ok(expected_names), names);
120 /// # }
121 /// ```
122 pub fn filter<Predicate>(self, predicate: Predicate) -> Filter<Self, Predicate>
123 where
124 Self: FilterDsl<Predicate>,
125 {
126 FilterDsl::filter(self, predicate)
127 }
128
129 /// Adds to the `WHERE` clause of a query using `OR`
130 ///
131 /// If there is already a `WHERE` clause, the result will be `(old OR new)`.
132 /// Calling `foo.filter(bar).or_filter(baz)`
133 /// is identical to `foo.filter(bar.or(baz))`.
134 /// However, the second form is much harder to do dynamically.
135 ///
136 /// # Example
137 ///
138 /// ```rust
139 /// # include!("../../doctest_setup.rs");
140 /// #
141 /// # fn main() {
142 /// # use schema::users::dsl::*;
143 /// # let connection = &mut establish_connection();
144 /// let deleted_rows = diesel::delete(users)
145 /// .filter(name.eq("Sean"))
146 /// .or_filter(name.eq("Tess"))
147 /// .execute(connection);
148 /// assert_eq!(Ok(2), deleted_rows);
149 ///
150 /// let num_users = users.count().first(connection);
151 ///
152 /// assert_eq!(Ok(0), num_users);
153 /// # }
154 /// ```
155 pub fn or_filter<Predicate>(self, predicate: Predicate) -> OrFilter<Self, Predicate>
156 where
157 Self: OrFilterDsl<Predicate>,
158 {
159 OrFilterDsl::or_filter(self, predicate)
160 }
161
162 /// Boxes the `WHERE` clause of this delete statement.
163 ///
164 /// This is useful for cases where you want to conditionally modify a query,
165 /// but need the type to remain the same. The backend must be specified as
166 /// part of this. It is not possible to box a query and have it be useable
167 /// on multiple backends.
168 ///
169 /// A boxed query will incur a minor performance penalty, as the query builder
170 /// can no longer be inlined by the compiler. For most applications this cost
171 /// will be minimal.
172 ///
173 /// ### Example
174 ///
175 /// ```rust
176 /// # include!("../../doctest_setup.rs");
177 /// #
178 /// # // Reading the query result requires reading memory allocated by the
179 /// # // native sqlite library, which miri cannot track provenance for.
180 /// # #[cfg(miri)]
181 /// # fn main() {}
182 /// # #[cfg(not(miri))]
183 /// # fn main() {
184 /// # run_test().unwrap();
185 /// # }
186 /// #
187 /// # fn run_test() -> QueryResult<()> {
188 /// # use std::collections::HashMap;
189 /// # use schema::users::dsl::*;
190 /// # let connection = &mut establish_connection();
191 /// # let mut params = HashMap::new();
192 /// # params.insert("sean_has_been_a_jerk", true);
193 /// let mut query = diesel::delete(users).into_boxed();
194 ///
195 /// if params["sean_has_been_a_jerk"] {
196 /// query = query.filter(name.eq("Sean"));
197 /// }
198 ///
199 /// let deleted_rows = query.execute(connection)?;
200 /// assert_eq!(1, deleted_rows);
201 ///
202 /// let expected_names = vec!["Tess"];
203 /// let names = users.select(name).load::<String>(connection)?;
204 ///
205 /// assert_eq!(expected_names, names);
206 /// # Ok(())
207 /// # }
208 /// ```
209 pub fn into_boxed<'a, DB>(self) -> IntoBoxed<'a, Self, DB>
210 where
211 DB: Backend,
212 Self: BoxedDsl<'a, DB>,
213 {
214 BoxedDsl::internal_into_boxed(self)
215 }
216
217 /// Wraps the `WHERE` clause of this delete statement in an [`Arc`](alloc::sync::Arc).
218 ///
219 /// This is useful for cases where you want to clone and conditionally
220 /// modify a query, but need the type to remain the same. The backend
221 /// must be specified as part of this. It is not possible to box a query
222 /// and have it be useable on multiple backends.
223 ///
224 /// A cloneable boxed query will incur a slightly greater performance penalty
225 /// than a standard boxed query. In both cases, the query builder can no longer
226 /// be inlined by the compiler. For most applications this cost will be minimal.
227 ///
228 /// ### Example
229 ///
230 /// ```rust
231 /// # include!("../../doctest_setup.rs");
232 /// #
233 /// # // Reading the query result requires reading memory allocated by the
234 /// # // native sqlite library, which miri cannot track provenance for.
235 /// # #[cfg(miri)]
236 /// # fn main() {}
237 /// # #[cfg(not(miri))]
238 /// # fn main() {
239 /// # run_test().unwrap();
240 /// # }
241 /// #
242 /// # fn run_test() -> QueryResult<()> {
243 /// # use std::collections::HashMap;
244 /// # use schema::users::dsl::*;
245 /// # let connection = &mut establish_connection();
246 /// # let mut params = HashMap::new();
247 /// # params.insert("sean_has_been_a_jerk", true);
248 /// let query = diesel::delete(users).into_boxed_clone();
249 /// let mut query = query.clone();
250 ///
251 /// if params["sean_has_been_a_jerk"] {
252 /// query = query.filter(name.eq("Sean"));
253 /// }
254 ///
255 /// let deleted_rows = query.execute(connection)?;
256 /// assert_eq!(1, deleted_rows);
257 ///
258 /// let expected_names = vec!["Tess"];
259 /// let names = users.select(name).load::<String>(connection)?;
260 ///
261 /// assert_eq!(expected_names, names);
262 /// # Ok(())
263 /// # }
264 /// ```
265 pub fn into_boxed_clone<'a, DB>(self) -> IntoBoxedClone<'a, Self, DB>
266 where
267 DB: Backend,
268 Self: BoxedCloneDsl<'a, DB>,
269 {
270 BoxedCloneDsl::internal_into_boxed_clone(self)
271 }
272}
273
274impl<T, U, Ret, Predicate> FilterDsl<Predicate> for DeleteStatement<T, U, Ret>
275where
276 U: WhereAnd<Predicate>,
277 Predicate: AppearsOnTable<T>,
278 T: QuerySource,
279{
280 type Output = DeleteStatement<T, U::Output, Ret>;
281
282 fn filter(self, predicate: Predicate) -> Self::Output {
283 DeleteStatement {
284 from_clause: self.from_clause,
285 where_clause: self.where_clause.and(predicate),
286 returning: self.returning,
287 }
288 }
289}
290
291impl<T, U, Ret, Predicate> OrFilterDsl<Predicate> for DeleteStatement<T, U, Ret>
292where
293 T: QuerySource,
294 U: WhereOr<Predicate>,
295 Predicate: AppearsOnTable<T>,
296{
297 type Output = DeleteStatement<T, U::Output, Ret>;
298
299 fn or_filter(self, predicate: Predicate) -> Self::Output {
300 DeleteStatement {
301 from_clause: self.from_clause,
302 where_clause: self.where_clause.or(predicate),
303 returning: self.returning,
304 }
305 }
306}
307
308impl<'a, T, U, Ret, DB> BoxedDsl<'a, DB> for DeleteStatement<T, U, Ret>
309where
310 U: Into<BoxedWhereClause<'a, DB>>,
311 T: QuerySource,
312{
313 type Output = BoxedDeleteStatement<'a, DB, T, Ret>;
314
315 fn internal_into_boxed(self) -> Self::Output {
316 DeleteStatement {
317 where_clause: self.where_clause.into(),
318 returning: self.returning,
319 from_clause: self.from_clause,
320 }
321 }
322}
323
324impl<'a, T, U, Ret, DB> BoxedCloneDsl<'a, DB> for DeleteStatement<T, U, Ret>
325where
326 U: Into<BoxedCloneWhereClause<'a, DB>>,
327 T: QuerySource,
328{
329 type Output = BoxedCloneDeleteStatement<'a, DB, T, Ret>;
330
331 fn internal_into_boxed_clone(self) -> Self::Output {
332 DeleteStatement {
333 where_clause: self.where_clause.into(),
334 returning: self.returning,
335 from_clause: self.from_clause,
336 }
337 }
338}
339
340impl<T, U, Ret, DB> QueryFragment<DB> for DeleteStatement<T, U, Ret>
341where
342 DB: Backend + DieselReserveSpecialization,
343 T: Table,
344 FromClause<T>: QueryFragment<DB>,
345 U: QueryFragment<DB>,
346 Ret: QueryFragment<DB>,
347{
348 fn walk_ast<'b>(&'b self, mut out: AstPass<'_, 'b, DB>) -> QueryResult<()> {
349 out.push_sql("DELETE");
350 self.from_clause.walk_ast(out.reborrow())?;
351 self.where_clause.walk_ast(out.reborrow())?;
352 self.returning.walk_ast(out.reborrow())?;
353 Ok(())
354 }
355}
356
357impl<T, U> AsQuery for DeleteStatement<T, U, NoReturningClause>
358where
359 T: Table,
360 DeleteStatement<T, U, ReturningClause<T::AllColumns>>: Query,
361 T::AllColumns: SelectableExpression<ReturningQuerySource<DeleteStmt, T>>,
362{
363 type SqlType = <Self::Query as Query>::SqlType;
364 type Query = DeleteStatement<T, U, ReturningClause<T::AllColumns>>;
365
366 fn as_query(self) -> Self::Query {
367 self.returning(T::all_columns())
368 }
369}
370
371impl<T, U, Ret> Query for DeleteStatement<T, U, ReturningClause<Ret>>
372where
373 T: Table,
374 Ret: SelectableExpression<ReturningQuerySource<DeleteStmt, T>>,
375{
376 type SqlType = <Ret as Expression>::SqlType;
377}
378
379impl<T, U, Ret> RunQueryDslSupport for DeleteStatement<T, U, Ret> where T: QuerySource {}
380
381impl<T: QuerySource, U> DeleteStatement<T, U, NoReturningClause> {
382 /// Specify what expression is returned after execution of the `delete`.
383 ///
384 /// # Examples
385 ///
386 /// ### Deleting a record:
387 ///
388 /// ```rust
389 /// # include!("../../doctest_setup.rs");
390 /// #
391 /// # #[cfg(feature = "postgres")]
392 /// # fn main() {
393 /// # use schema::users::dsl::*;
394 /// # let connection = &mut establish_connection();
395 /// let deleted_name = diesel::delete(users.filter(name.eq("Sean")))
396 /// .returning(name)
397 /// .get_result(connection);
398 /// assert_eq!(Ok("Sean".to_string()), deleted_name);
399 /// # }
400 /// # #[cfg(not(feature = "postgres"))]
401 /// # fn main() {}
402 /// ```
403 pub fn returning<E>(self, returns: E) -> DeleteStatement<T, U, ReturningClause<E>>
404 where
405 DeleteStatement<T, U, ReturningClause<E>>: Query,
406 {
407 DeleteStatement {
408 where_clause: self.where_clause,
409 from_clause: self.from_clause,
410 returning: ReturningClause(returns),
411 }
412 }
413}