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}