diesel/query_builder/delete_statement/
mod.rs

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