diesel/query_builder/delete_statement/
mod.rs

1use backend::Backend;
2use dsl::{Filter, IntoBoxed};
3use expression::{AppearsOnTable, SelectableExpression};
4use query_builder::returning_clause::*;
5use query_builder::where_clause::*;
6use query_builder::*;
7use query_dsl::methods::{BoxedDsl, FilterDsl};
8use query_dsl::RunQueryDsl;
9use query_source::Table;
10use result::QueryResult;
11
12#[derive(Debug, Clone, Copy, QueryId)]
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, U, Ret = NoReturningClause> {
25    table: T,
26    where_clause: U,
27    returning: Ret,
28}
29
30/// A `DELETE` statement with a boxed `WHERE` clause
31pub type BoxedDeleteStatement<'a, DB, T, Ret = NoReturningClause> =
32    DeleteStatement<T, BoxedWhereClause<'a, DB>, Ret>;
33
34impl<T, U> DeleteStatement<T, U, NoReturningClause> {
35    pub(crate) fn new(table: T, where_clause: U) -> Self {
36        DeleteStatement {
37            table: table,
38            where_clause: where_clause,
39            returning: NoReturningClause,
40        }
41    }
42
43    /// Adds the given predicate to the `WHERE` clause of the statement being
44    /// constructed.
45    ///
46    /// If there is already a `WHERE` clause, the predicate will be appended
47    /// with `AND`. There is no difference in behavior between
48    /// `delete(table.filter(x))` and `delete(table).filter(x)`.
49    ///
50    /// # Example
51    ///
52    /// ```rust
53    /// # #[macro_use] extern crate diesel;
54    /// # include!("../../doctest_setup.rs");
55    /// #
56    /// # fn main() {
57    /// #     use schema::users::dsl::*;
58    /// #     let connection = establish_connection();
59    /// let deleted_rows = diesel::delete(users)
60    ///     .filter(name.eq("Sean"))
61    ///     .execute(&connection);
62    /// assert_eq!(Ok(1), deleted_rows);
63    ///
64    /// let expected_names = vec!["Tess".to_string()];
65    /// let names = users.select(name).load(&connection);
66    ///
67    /// assert_eq!(Ok(expected_names), names);
68    /// # }
69    /// ```
70    pub fn filter<Predicate>(self, predicate: Predicate) -> Filter<Self, Predicate>
71    where
72        Self: FilterDsl<Predicate>,
73    {
74        FilterDsl::filter(self, predicate)
75    }
76
77    /// Boxes the `WHERE` clause of this delete statement.
78    ///
79    /// This is useful for cases where you want to conditionally modify a query,
80    /// but need the type to remain the same. The backend must be specified as
81    /// part of this. It is not possible to box a query and have it be useable
82    /// on multiple backends.
83    ///
84    /// A boxed query will incur a minor performance penalty, as the query builder
85    /// can no longer be inlined by the compiler. For most applications this cost
86    /// will be minimal.
87    ///
88    /// ### Example
89    ///
90    /// ```rust
91    /// # #[macro_use] extern crate diesel;
92    /// # include!("../../doctest_setup.rs");
93    /// #
94    /// # fn main() {
95    /// #     run_test().unwrap();
96    /// # }
97    /// #
98    /// # fn run_test() -> QueryResult<()> {
99    /// #     use std::collections::HashMap;
100    /// #     use schema::users::dsl::*;
101    /// #     let connection = establish_connection();
102    /// #     let mut params = HashMap::new();
103    /// #     params.insert("sean_has_been_a_jerk", true);
104    /// let mut query = diesel::delete(users)
105    ///     .into_boxed();
106    ///
107    /// if params["sean_has_been_a_jerk"] {
108    ///     query = query.filter(name.eq("Sean"));
109    /// }
110    ///
111    /// let deleted_rows = query.execute(&connection)?;
112    /// assert_eq!(1, deleted_rows);
113    ///
114    /// let expected_names = vec!["Tess"];
115    /// let names = users.select(name).load::<String>(&connection)?;
116    ///
117    /// assert_eq!(expected_names, names);
118    /// #     Ok(())
119    /// # }
120    /// ```
121    pub fn into_boxed<'a, DB>(self) -> IntoBoxed<'a, Self, DB>
122    where
123        DB: Backend,
124        Self: BoxedDsl<'a, DB>,
125    {
126        BoxedDsl::internal_into_boxed(self)
127    }
128}
129
130impl<T, U, Ret, Predicate> FilterDsl<Predicate> for DeleteStatement<T, U, Ret>
131where
132    U: WhereAnd<Predicate>,
133    Predicate: AppearsOnTable<T>,
134{
135    type Output = DeleteStatement<T, U::Output, Ret>;
136
137    fn filter(self, predicate: Predicate) -> Self::Output {
138        DeleteStatement {
139            table: self.table,
140            where_clause: self.where_clause.and(predicate),
141            returning: self.returning,
142        }
143    }
144}
145
146impl<'a, T, U, Ret, DB> BoxedDsl<'a, DB> for DeleteStatement<T, U, Ret>
147where
148    U: Into<BoxedWhereClause<'a, DB>>,
149{
150    type Output = BoxedDeleteStatement<'a, DB, T, Ret>;
151
152    fn internal_into_boxed(self) -> Self::Output {
153        DeleteStatement {
154            table: self.table,
155            where_clause: self.where_clause.into(),
156            returning: self.returning,
157        }
158    }
159}
160
161impl<T, U, Ret, DB> QueryFragment<DB> for DeleteStatement<T, U, Ret>
162where
163    DB: Backend,
164    T: Table,
165    T::FromClause: QueryFragment<DB>,
166    U: QueryFragment<DB>,
167    Ret: QueryFragment<DB>,
168{
169    fn walk_ast(&self, mut out: AstPass<DB>) -> QueryResult<()> {
170        out.push_sql("DELETE FROM ");
171        self.table.from_clause().walk_ast(out.reborrow())?;
172        self.where_clause.walk_ast(out.reborrow())?;
173        self.returning.walk_ast(out.reborrow())?;
174        Ok(())
175    }
176}
177
178impl<T, U> AsQuery for DeleteStatement<T, U, NoReturningClause>
179where
180    T: Table,
181    T::AllColumns: SelectableExpression<T>,
182    DeleteStatement<T, U, ReturningClause<T::AllColumns>>: Query,
183{
184    type SqlType = <Self::Query as Query>::SqlType;
185    type Query = DeleteStatement<T, U, ReturningClause<T::AllColumns>>;
186
187    fn as_query(self) -> Self::Query {
188        self.returning(T::all_columns())
189    }
190}
191
192impl<T, U, Ret> Query for DeleteStatement<T, U, ReturningClause<Ret>>
193where
194    T: Table,
195    Ret: SelectableExpression<T>,
196{
197    type SqlType = Ret::SqlType;
198}
199
200impl<T, U, Ret, Conn> RunQueryDsl<Conn> for DeleteStatement<T, U, Ret> {}
201
202impl<T, U> DeleteStatement<T, U, NoReturningClause> {
203    /// Specify what expression is returned after execution of the `delete`.
204    ///
205    /// # Examples
206    ///
207    /// ### Deleting a record:
208    ///
209    /// ```rust
210    /// # #[macro_use] extern crate diesel;
211    /// # include!("../../doctest_setup.rs");
212    /// #
213    /// # #[cfg(feature = "postgres")]
214    /// # fn main() {
215    /// #     use schema::users::dsl::*;
216    /// #     let connection = establish_connection();
217    /// let deleted_name = diesel::delete(users.filter(name.eq("Sean")))
218    ///     .returning(name)
219    ///     .get_result(&connection);
220    /// assert_eq!(Ok("Sean".to_string()), deleted_name);
221    /// # }
222    /// # #[cfg(not(feature = "postgres"))]
223    /// # fn main() {}
224    /// ```
225    pub fn returning<E>(self, returns: E) -> DeleteStatement<T, U, ReturningClause<E>>
226    where
227        E: SelectableExpression<T>,
228        DeleteStatement<T, U, ReturningClause<E>>: Query,
229    {
230        DeleteStatement {
231            table: self.table,
232            where_clause: self.where_clause,
233            returning: ReturningClause(returns),
234        }
235    }
236}