Skip to main content

diesel/query_builder/delete_statement/
mod.rs

1use crate::backend::DieselReserveSpecialization;
2use crate::dsl::{Filter, IntoBoxed, 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::RunQueryDsl;
10use crate::query_dsl::methods::{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
78impl<T: QuerySource, U> DeleteStatement<T, U, NoReturningClause> {
79    pub(crate) fn new(table: T, where_clause: U) -> Self {
80        DeleteStatement {
81            from_clause: FromClause::new(table),
82            where_clause,
83            returning: NoReturningClause,
84        }
85    }
86
87    /// Adds the given predicate to the `WHERE` clause of the statement being
88    /// constructed.
89    ///
90    /// If there is already a `WHERE` clause, the predicate will be appended
91    /// with `AND`. There is no difference in behavior between
92    /// `delete(table.filter(x))` and `delete(table).filter(x)`.
93    ///
94    /// # Example
95    ///
96    /// ```rust
97    /// # include!("../../doctest_setup.rs");
98    /// #
99    /// # fn main() {
100    /// #     use schema::users::dsl::*;
101    /// #     let connection = &mut establish_connection();
102    /// let deleted_rows = diesel::delete(users)
103    ///     .filter(name.eq("Sean"))
104    ///     .execute(connection);
105    /// assert_eq!(Ok(1), deleted_rows);
106    ///
107    /// let expected_names = vec!["Tess".to_string()];
108    /// let names = users.select(name).load(connection);
109    ///
110    /// assert_eq!(Ok(expected_names), names);
111    /// # }
112    /// ```
113    pub fn filter<Predicate>(self, predicate: Predicate) -> Filter<Self, Predicate>
114    where
115        Self: FilterDsl<Predicate>,
116    {
117        FilterDsl::filter(self, predicate)
118    }
119
120    /// Adds to the `WHERE` clause of a query using `OR`
121    ///
122    /// If there is already a `WHERE` clause, the result will be `(old OR new)`.
123    /// Calling `foo.filter(bar).or_filter(baz)`
124    /// is identical to `foo.filter(bar.or(baz))`.
125    /// However, the second form is much harder to do dynamically.
126    ///
127    /// # Example
128    ///
129    /// ```rust
130    /// # include!("../../doctest_setup.rs");
131    /// #
132    /// # fn main() {
133    /// #     use schema::users::dsl::*;
134    /// #     let connection = &mut establish_connection();
135    /// let deleted_rows = diesel::delete(users)
136    ///     .filter(name.eq("Sean"))
137    ///     .or_filter(name.eq("Tess"))
138    ///     .execute(connection);
139    /// assert_eq!(Ok(2), deleted_rows);
140    ///
141    /// let num_users = users.count().first(connection);
142    ///
143    /// assert_eq!(Ok(0), num_users);
144    /// # }
145    /// ```
146    pub fn or_filter<Predicate>(self, predicate: Predicate) -> OrFilter<Self, Predicate>
147    where
148        Self: OrFilterDsl<Predicate>,
149    {
150        OrFilterDsl::or_filter(self, predicate)
151    }
152
153    /// Boxes the `WHERE` clause of this delete statement.
154    ///
155    /// This is useful for cases where you want to conditionally modify a query,
156    /// but need the type to remain the same. The backend must be specified as
157    /// part of this. It is not possible to box a query and have it be useable
158    /// on multiple backends.
159    ///
160    /// A boxed query will incur a minor performance penalty, as the query builder
161    /// can no longer be inlined by the compiler. For most applications this cost
162    /// will be minimal.
163    ///
164    /// ### Example
165    ///
166    /// ```rust
167    /// # include!("../../doctest_setup.rs");
168    /// #
169    /// # fn main() {
170    /// #     run_test().unwrap();
171    /// # }
172    /// #
173    /// # fn run_test() -> QueryResult<()> {
174    /// #     use std::collections::HashMap;
175    /// #     use schema::users::dsl::*;
176    /// #     let connection = &mut establish_connection();
177    /// #     let mut params = HashMap::new();
178    /// #     params.insert("sean_has_been_a_jerk", true);
179    /// let mut query = diesel::delete(users).into_boxed();
180    ///
181    /// if params["sean_has_been_a_jerk"] {
182    ///     query = query.filter(name.eq("Sean"));
183    /// }
184    ///
185    /// let deleted_rows = query.execute(connection)?;
186    /// assert_eq!(1, deleted_rows);
187    ///
188    /// let expected_names = vec!["Tess"];
189    /// let names = users.select(name).load::<String>(connection)?;
190    ///
191    /// assert_eq!(expected_names, names);
192    /// #     Ok(())
193    /// # }
194    /// ```
195    pub fn into_boxed<'a, DB>(self) -> IntoBoxed<'a, Self, DB>
196    where
197        DB: Backend,
198        Self: BoxedDsl<'a, DB>,
199    {
200        BoxedDsl::internal_into_boxed(self)
201    }
202}
203
204impl<T, U, Ret, Predicate> FilterDsl<Predicate> for DeleteStatement<T, U, Ret>
205where
206    U: WhereAnd<Predicate>,
207    Predicate: AppearsOnTable<T>,
208    T: QuerySource,
209{
210    type Output = DeleteStatement<T, U::Output, Ret>;
211
212    fn filter(self, predicate: Predicate) -> Self::Output {
213        DeleteStatement {
214            from_clause: self.from_clause,
215            where_clause: self.where_clause.and(predicate),
216            returning: self.returning,
217        }
218    }
219}
220
221impl<T, U, Ret, Predicate> OrFilterDsl<Predicate> for DeleteStatement<T, U, Ret>
222where
223    T: QuerySource,
224    U: WhereOr<Predicate>,
225    Predicate: AppearsOnTable<T>,
226{
227    type Output = DeleteStatement<T, U::Output, Ret>;
228
229    fn or_filter(self, predicate: Predicate) -> Self::Output {
230        DeleteStatement {
231            from_clause: self.from_clause,
232            where_clause: self.where_clause.or(predicate),
233            returning: self.returning,
234        }
235    }
236}
237
238impl<'a, T, U, Ret, DB> BoxedDsl<'a, DB> for DeleteStatement<T, U, Ret>
239where
240    U: Into<BoxedWhereClause<'a, DB>>,
241    T: QuerySource,
242{
243    type Output = BoxedDeleteStatement<'a, DB, T, Ret>;
244
245    fn internal_into_boxed(self) -> Self::Output {
246        DeleteStatement {
247            where_clause: self.where_clause.into(),
248            returning: self.returning,
249            from_clause: self.from_clause,
250        }
251    }
252}
253
254impl<T, U, Ret, DB> QueryFragment<DB> for DeleteStatement<T, U, Ret>
255where
256    DB: Backend + DieselReserveSpecialization,
257    T: Table,
258    FromClause<T>: QueryFragment<DB>,
259    U: QueryFragment<DB>,
260    Ret: QueryFragment<DB>,
261{
262    fn walk_ast<'b>(&'b self, mut out: AstPass<'_, 'b, DB>) -> QueryResult<()> {
263        out.push_sql("DELETE");
264        self.from_clause.walk_ast(out.reborrow())?;
265        self.where_clause.walk_ast(out.reborrow())?;
266        self.returning.walk_ast(out.reborrow())?;
267        Ok(())
268    }
269}
270
271impl<T, U> AsQuery for DeleteStatement<T, U, NoReturningClause>
272where
273    T: Table,
274    DeleteStatement<T, U, ReturningClause<T::AllColumns>>: Query,
275    T::AllColumns: SelectableExpression<ReturningQuerySource<DeleteStmt, T>>,
276{
277    type SqlType = <Self::Query as Query>::SqlType;
278    type Query = DeleteStatement<T, U, ReturningClause<T::AllColumns>>;
279
280    fn as_query(self) -> Self::Query {
281        self.returning(T::all_columns())
282    }
283}
284
285impl<T, U, Ret> Query for DeleteStatement<T, U, ReturningClause<Ret>>
286where
287    T: Table,
288    Ret: SelectableExpression<ReturningQuerySource<DeleteStmt, T>>,
289{
290    type SqlType = <Ret as Expression>::SqlType;
291}
292
293impl<T, U, Ret, Conn> RunQueryDsl<Conn> for DeleteStatement<T, U, Ret> where T: QuerySource {}
294
295impl<T: QuerySource, U> DeleteStatement<T, U, NoReturningClause> {
296    /// Specify what expression is returned after execution of the `delete`.
297    ///
298    /// # Examples
299    ///
300    /// ### Deleting a record:
301    ///
302    /// ```rust
303    /// # include!("../../doctest_setup.rs");
304    /// #
305    /// # #[cfg(feature = "postgres")]
306    /// # fn main() {
307    /// #     use schema::users::dsl::*;
308    /// #     let connection = &mut establish_connection();
309    /// let deleted_name = diesel::delete(users.filter(name.eq("Sean")))
310    ///     .returning(name)
311    ///     .get_result(connection);
312    /// assert_eq!(Ok("Sean".to_string()), deleted_name);
313    /// # }
314    /// # #[cfg(not(feature = "postgres"))]
315    /// # fn main() {}
316    /// ```
317    pub fn returning<E>(self, returns: E) -> DeleteStatement<T, U, ReturningClause<E>>
318    where
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}