diesel/query_builder/update_statement/
mod.rs

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