diesel/query_builder/update_statement/
mod.rs

1pub mod changeset;
2pub mod target;
3
4pub use self::changeset::AsChangeset;
5pub use self::target::{IntoUpdateTarget, UpdateTarget};
6
7use backend::Backend;
8use dsl::{Filter, IntoBoxed};
9use expression::{AppearsOnTable, Expression, NonAggregate, SelectableExpression};
10use query_builder::returning_clause::*;
11use query_builder::where_clause::*;
12use query_builder::*;
13use query_dsl::methods::{BoxedDsl, FilterDsl};
14use query_dsl::RunQueryDsl;
15use query_source::Table;
16use result::Error::QueryBuilderError;
17use result::QueryResult;
18
19/// The type returned by [`update`](../fn.update.html). The only thing you can do
20/// with this type is call `set` on it.
21#[deprecated(since = "1.2.0", note = "Use `UpdateStatement<T, U>` instead")]
22#[cfg(feature = "with-deprecated")]
23pub type IncompleteUpdateStatement<T, U> = UpdateStatement<T, U>;
24
25impl<T, U> UpdateStatement<T, U, SetNotCalled> {
26    pub(crate) fn new(target: UpdateTarget<T, U>) -> Self {
27        UpdateStatement {
28            table: target.table,
29            where_clause: target.where_clause,
30            values: SetNotCalled,
31            returning: NoReturningClause,
32        }
33    }
34
35    /// Provides the `SET` clause of the `UPDATE` statement.
36    ///
37    /// See [`update`](../fn.update.html) for usage examples, or [the update
38    /// guide](https://diesel.rs/guides/all-about-updates/) for a more exhaustive
39    /// set of examples.
40    pub fn set<V>(self, values: V) -> UpdateStatement<T, U, V::Changeset>
41    where
42        T: Table,
43        V: changeset::AsChangeset<Target = T>,
44        UpdateStatement<T, U, V::Changeset>: AsQuery,
45    {
46        UpdateStatement {
47            table: self.table,
48            where_clause: self.where_clause,
49            values: values.as_changeset(),
50            returning: self.returning,
51        }
52    }
53}
54
55#[derive(Debug, Copy, Clone)]
56#[must_use = "Queries are only executed when calling `load`, `get_result` or similar."]
57/// Represents a complete `UPDATE` statement.
58///
59/// See [`update`](../fn.update.html) for usage examples, or [the update
60/// guide](https://diesel.rs/guides/all-about-updates/) for a more exhaustive
61/// set of examples.
62pub struct UpdateStatement<T, U, V = SetNotCalled, Ret = NoReturningClause> {
63    table: T,
64    where_clause: U,
65    values: V,
66    returning: Ret,
67}
68
69/// An `UPDATE` statement with a boxed `WHERE` clause.
70pub type BoxedUpdateStatement<'a, DB, T, V = SetNotCalled, Ret = NoReturningClause> =
71    UpdateStatement<T, BoxedWhereClause<'a, DB>, V, Ret>;
72
73impl<T, U, V, Ret> UpdateStatement<T, U, V, Ret> {
74    /// Adds the given predicate to the `WHERE` clause of the statement being
75    /// constructed.
76    ///
77    /// If there is already a `WHERE` clause, the predicate will be appended
78    /// with `AND`. There is no difference in behavior between
79    /// `update(table.filter(x))` and `update(table).filter(x)`.
80    ///
81    /// # Example
82    ///
83    /// ```rust
84    /// # #[macro_use] extern crate diesel;
85    /// # include!("../../doctest_setup.rs");
86    /// #
87    /// # fn main() {
88    /// #     use schema::users::dsl::*;
89    /// #     let connection = establish_connection();
90    /// let updated_rows = diesel::update(users)
91    ///     .set(name.eq("Jim"))
92    ///     .filter(name.eq("Sean"))
93    ///     .execute(&connection);
94    /// assert_eq!(Ok(1), updated_rows);
95    ///
96    /// let expected_names = vec!["Jim".to_string(), "Tess".to_string()];
97    /// let names = users.select(name).order(id).load(&connection);
98    ///
99    /// assert_eq!(Ok(expected_names), names);
100    /// # }
101    /// ```
102    pub fn filter<Predicate>(self, predicate: Predicate) -> Filter<Self, Predicate>
103    where
104        Self: FilterDsl<Predicate>,
105    {
106        FilterDsl::filter(self, predicate)
107    }
108
109    /// Boxes the `WHERE` clause of this update statement.
110    ///
111    /// This is useful for cases where you want to conditionally modify a query,
112    /// but need the type to remain the same. The backend must be specified as
113    /// part of this. It is not possible to box a query and have it be useable
114    /// on multiple backends.
115    ///
116    /// A boxed query will incur a minor performance penalty, as the query builder
117    /// can no longer be inlined by the compiler. For most applications this cost
118    /// will be minimal.
119    ///
120    /// ### Example
121    ///
122    /// ```rust
123    /// # #[macro_use] extern crate diesel;
124    /// # include!("../../doctest_setup.rs");
125    /// #
126    /// # fn main() {
127    /// #     run_test().unwrap();
128    /// # }
129    /// #
130    /// # fn run_test() -> QueryResult<()> {
131    /// #     use std::collections::HashMap;
132    /// #     use schema::users::dsl::*;
133    /// #     let connection = establish_connection();
134    /// #     let mut params = HashMap::new();
135    /// #     params.insert("tess_has_been_a_jerk", false);
136    /// let mut query = diesel::update(users)
137    ///     .set(name.eq("Jerk"))
138    ///     .into_boxed();
139    ///
140    /// if !params["tess_has_been_a_jerk"] {
141    ///     query = query.filter(name.ne("Tess"));
142    /// }
143    ///
144    /// let updated_rows = query.execute(&connection)?;
145    /// assert_eq!(1, updated_rows);
146    ///
147    /// let expected_names = vec!["Jerk", "Tess"];
148    /// let names = users.select(name).order(id).load::<String>(&connection)?;
149    ///
150    /// assert_eq!(expected_names, names);
151    /// #     Ok(())
152    /// # }
153    /// ```
154    pub fn into_boxed<'a, DB>(self) -> IntoBoxed<'a, Self, DB>
155    where
156        DB: Backend,
157        Self: BoxedDsl<'a, DB>,
158    {
159        BoxedDsl::internal_into_boxed(self)
160    }
161}
162
163impl<T, U, V, Ret, Predicate> FilterDsl<Predicate> for UpdateStatement<T, U, V, Ret>
164where
165    U: WhereAnd<Predicate>,
166    Predicate: AppearsOnTable<T>,
167{
168    type Output = UpdateStatement<T, U::Output, V, Ret>;
169
170    fn filter(self, predicate: Predicate) -> Self::Output {
171        UpdateStatement {
172            table: self.table,
173            where_clause: self.where_clause.and(predicate),
174            values: self.values,
175            returning: self.returning,
176        }
177    }
178}
179
180impl<'a, T, U, V, Ret, DB> BoxedDsl<'a, DB> for UpdateStatement<T, U, V, Ret>
181where
182    U: Into<BoxedWhereClause<'a, DB>>,
183{
184    type Output = BoxedUpdateStatement<'a, DB, T, V, Ret>;
185
186    fn internal_into_boxed(self) -> Self::Output {
187        UpdateStatement {
188            table: self.table,
189            where_clause: self.where_clause.into(),
190            values: self.values,
191            returning: self.returning,
192        }
193    }
194}
195
196impl<T, U, V, Ret, DB> QueryFragment<DB> for UpdateStatement<T, U, V, Ret>
197where
198    DB: Backend,
199    T: Table,
200    T::FromClause: QueryFragment<DB>,
201    U: QueryFragment<DB>,
202    V: QueryFragment<DB>,
203    Ret: QueryFragment<DB>,
204{
205    fn walk_ast(&self, mut out: AstPass<DB>) -> QueryResult<()> {
206        if self.values.is_noop()? {
207            return Err(QueryBuilderError(
208                "There are no changes to save. This query cannot be built".into(),
209            ));
210        }
211
212        out.unsafe_to_cache_prepared();
213        out.push_sql("UPDATE ");
214        self.table.from_clause().walk_ast(out.reborrow())?;
215        out.push_sql(" SET ");
216        self.values.walk_ast(out.reborrow())?;
217        self.where_clause.walk_ast(out.reborrow())?;
218        self.returning.walk_ast(out.reborrow())?;
219        Ok(())
220    }
221}
222
223impl<T, U, V, Ret> QueryId for UpdateStatement<T, U, V, Ret> {
224    type QueryId = ();
225
226    const HAS_STATIC_QUERY_ID: bool = false;
227}
228
229impl<T, U, V> AsQuery for UpdateStatement<T, U, V, NoReturningClause>
230where
231    T: Table,
232    UpdateStatement<T, U, V, ReturningClause<T::AllColumns>>: Query,
233{
234    type SqlType = <Self::Query as Query>::SqlType;
235    type Query = UpdateStatement<T, U, V, ReturningClause<T::AllColumns>>;
236
237    fn as_query(self) -> Self::Query {
238        self.returning(T::all_columns())
239    }
240}
241
242impl<T, U, V, Ret> Query for UpdateStatement<T, U, V, ReturningClause<Ret>>
243where
244    T: Table,
245    Ret: Expression + SelectableExpression<T> + NonAggregate,
246{
247    type SqlType = Ret::SqlType;
248}
249
250impl<T, U, V, Ret, Conn> RunQueryDsl<Conn> for UpdateStatement<T, U, V, Ret> {}
251
252impl<T, U, V> UpdateStatement<T, U, V, NoReturningClause> {
253    /// Specify what expression is returned after execution of the `update`.
254    /// # Examples
255    ///
256    /// ### Updating a single record:
257    ///
258    /// ```rust
259    /// # #[macro_use] extern crate diesel;
260    /// # include!("../../doctest_setup.rs");
261    /// #
262    /// # #[cfg(feature = "postgres")]
263    /// # fn main() {
264    /// #     use schema::users::dsl::*;
265    /// #     let connection = establish_connection();
266    /// let updated_name = diesel::update(users.filter(id.eq(1)))
267    ///     .set(name.eq("Dean"))
268    ///     .returning(name)
269    ///     .get_result(&connection);
270    /// assert_eq!(Ok("Dean".to_string()), updated_name);
271    /// # }
272    /// # #[cfg(not(feature = "postgres"))]
273    /// # fn main() {}
274    /// ```
275    pub fn returning<E>(self, returns: E) -> UpdateStatement<T, U, V, ReturningClause<E>>
276    where
277        T: Table,
278        UpdateStatement<T, U, V, ReturningClause<E>>: Query,
279    {
280        UpdateStatement {
281            table: self.table,
282            where_clause: self.where_clause,
283            values: self.values,
284            returning: ReturningClause(returns),
285        }
286    }
287}
288
289/// Indicates that you have not yet called `.set` on an update statement
290#[derive(Debug, Clone, Copy)]
291pub struct SetNotCalled;