Skip to main content

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}