Skip to main content

diesel/query_builder/update_statement/
mod.rs

1pub(crate) mod batch_update;
2pub(crate) mod changeset;
3pub(super) mod target;
4
5use private::AllowFilterForUpdate;
6
7use crate::QuerySource;
8use crate::backend::DieselReserveSpecialization;
9use crate::dsl::{Filter, IntoBoxed, IntoBoxedClone};
10use crate::expression::{
11    AppearsOnTable, Expression, MixedAggregates, SelectableExpression, ValidGrouping, is_aggregate,
12};
13use crate::query_builder::returning::{
14    NoReturningClause, ReturningClause, ReturningQuerySource, UpdateStmt,
15};
16use crate::query_builder::where_clause::*;
17use crate::query_builder::*;
18use crate::query_dsl::RunQueryDslSupport;
19use crate::query_dsl::methods::{BoxedCloneDsl, BoxedDsl, FilterDsl};
20use crate::query_source::Table;
21use crate::result::EmptyChangeset;
22use crate::result::Error::QueryBuilderError;
23
24pub(crate) use self::private::SetAutoTypeHelper;
25
26impl<T: QuerySource, U> UpdateStatement<T, U, SetNotCalled> {
27    pub(crate) fn new(target: UpdateTarget<T, U>) -> Self {
28        UpdateStatement {
29            from_clause: target.table.from_clause(),
30            where_clause: target.where_clause,
31            set_clause: SetClause::Immediate,
32            values: SetNotCalled,
33            returning: NoReturningClause,
34        }
35    }
36
37    /// Provides the `SET` clause of the `UPDATE` statement.
38    ///
39    /// See [`update`](crate::update()) for usage examples, or [the update
40    /// guide](https://diesel.rs/guides/all-about-updates/) for a more exhaustive
41    /// set of examples.
42    pub fn set<V>(self, values: V) -> crate::dsl::Set<Self, V>
43    where
44        T: Table,
45        V: changeset::AsChangeset<Target = T>,
46        UpdateStatement<T, U, V::Changeset>: AsQuery,
47    {
48        UpdateStatement {
49            from_clause: self.from_clause,
50            where_clause: self.where_clause,
51            set_clause: <V as AsChangeset>::SET_CLAUSE,
52            values: values.as_changeset(),
53            returning: self.returning,
54        }
55    }
56}
57
58#[derive(#[automatically_derived]
impl<T: ::core::clone::Clone + QuerySource, U: ::core::clone::Clone,
    V: ::core::clone::Clone, Ret: ::core::clone::Clone> ::core::clone::Clone
    for UpdateStatement<T, U, V, Ret> where
    T::FromClause: ::core::clone::Clone {
    #[inline]
    fn clone(&self) -> UpdateStatement<T, U, V, Ret> {
        UpdateStatement {
            from_clause: ::core::clone::Clone::clone(&self.from_clause),
            where_clause: ::core::clone::Clone::clone(&self.where_clause),
            set_clause: ::core::clone::Clone::clone(&self.set_clause),
            values: ::core::clone::Clone::clone(&self.values),
            returning: ::core::clone::Clone::clone(&self.returning),
        }
    }
}Clone, #[automatically_derived]
impl<T: ::core::fmt::Debug + QuerySource, U: ::core::fmt::Debug,
    V: ::core::fmt::Debug, Ret: ::core::fmt::Debug> ::core::fmt::Debug for
    UpdateStatement<T, U, V, Ret> where T::FromClause: ::core::fmt::Debug {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::debug_struct_field5_finish(f,
            "UpdateStatement", "from_clause", &self.from_clause,
            "where_clause", &self.where_clause, "set_clause",
            &self.set_clause, "values", &self.values, "returning",
            &&self.returning)
    }
}Debug)]
59#[must_use = "Queries are only executed when calling `load`, `get_result` or similar."]
60/// Represents a complete `UPDATE` statement.
61///
62/// See [`update`](crate::update()) for usage examples, or [the update
63/// guide](https://diesel.rs/guides/all-about-updates/) for a more exhaustive
64/// set of examples.
65pub struct UpdateStatement<T: QuerySource, U, V = SetNotCalled, Ret = NoReturningClause> {
66    from_clause: T::FromClause,
67    where_clause: U,
68    set_clause: SetClause,
69    values: V,
70    returning: Ret,
71}
72
73/// An `UPDATE` statement with a boxed `WHERE` clause.
74pub type BoxedUpdateStatement<'a, DB, T, V = SetNotCalled, Ret = NoReturningClause> =
75    UpdateStatement<T, BoxedWhereClause<'a, DB>, V, Ret>;
76
77/// An `UPDATE` statement with a boxed cloneable `WHERE` clause.
78pub type BoxedCloneUpdateStatement<'a, DB, T, V = SetNotCalled, Ret = NoReturningClause> =
79    UpdateStatement<T, BoxedCloneWhereClause<'a, DB>, V, Ret>;
80
81impl<T: QuerySource, U, V, Ret> UpdateStatement<T, U, V, Ret> {
82    /// Adds the given predicate to the `WHERE` clause of the statement being
83    /// constructed.
84    ///
85    /// If there is already a `WHERE` clause, the predicate will be appended
86    /// with `AND`. There is no difference in behavior between
87    /// `update(table.filter(x))` and `update(table).filter(x)`.
88    ///
89    /// # Example
90    ///
91    /// ```rust
92    /// # include!("../../doctest_setup.rs");
93    /// #
94    /// # fn main() {
95    /// #     use schema::users::dsl::*;
96    /// #     let connection = &mut establish_connection();
97    /// let updated_rows = diesel::update(users)
98    ///     .set(name.eq("Jim"))
99    ///     .filter(name.eq("Sean"))
100    ///     .execute(connection);
101    /// assert_eq!(Ok(1), updated_rows);
102    ///
103    /// let expected_names = vec!["Jim".to_string(), "Tess".to_string()];
104    /// let names = users.select(name).order(id).load(connection);
105    ///
106    /// assert_eq!(Ok(expected_names), names);
107    /// # }
108    /// ```
109    pub fn filter<Predicate>(self, predicate: Predicate) -> Filter<Self, Predicate>
110    where
111        Self: FilterDsl<Predicate>,
112    {
113        FilterDsl::filter(self, predicate)
114    }
115
116    /// Boxes the `WHERE` clause of this update statement.
117    ///
118    /// This is useful for cases where you want to conditionally modify a query,
119    /// but need the type to remain the same. The backend must be specified as
120    /// part of this. It is not possible to box a query and have it be useable
121    /// on multiple backends.
122    ///
123    /// A boxed query will incur a minor performance penalty, as the query builder
124    /// can no longer be inlined by the compiler. For most applications this cost
125    /// will be minimal.
126    ///
127    /// ### Example
128    ///
129    /// ```rust
130    /// # include!("../../doctest_setup.rs");
131    /// #
132    /// # fn main() {
133    /// #     run_test().unwrap();
134    /// # }
135    /// #
136    /// # fn run_test() -> QueryResult<()> {
137    /// #     use std::collections::HashMap;
138    /// #     use schema::users::dsl::*;
139    /// #     let connection = &mut establish_connection();
140    /// #     let mut params = HashMap::new();
141    /// #     params.insert("tess_has_been_a_jerk", false);
142    /// let mut query = diesel::update(users).set(name.eq("Jerk")).into_boxed();
143    ///
144    /// if !params["tess_has_been_a_jerk"] {
145    ///     query = query.filter(name.ne("Tess"));
146    /// }
147    ///
148    /// let updated_rows = query.execute(connection)?;
149    /// assert_eq!(1, updated_rows);
150    ///
151    /// let expected_names = vec!["Jerk", "Tess"];
152    /// let names = users.select(name).order(id).load::<String>(connection)?;
153    ///
154    /// assert_eq!(expected_names, names);
155    /// #     Ok(())
156    /// # }
157    /// ```
158    pub fn into_boxed<'a, DB>(self) -> IntoBoxed<'a, Self, DB>
159    where
160        DB: Backend,
161        Self: BoxedDsl<'a, DB>,
162    {
163        BoxedDsl::internal_into_boxed(self)
164    }
165
166    /// Wraps the `WHERE` clause of this update statement in an [`Arc`](alloc::sync::Arc).
167    ///
168    /// This is useful for cases where you want to clone and conditionally
169    /// modify a query, but need the type to remain the same. The backend
170    /// must be specified as part of this. It is not possible to box a query
171    /// and have it be useable on multiple backends.
172    ///
173    /// A cloneable boxed query will incur a slightly greater performance penalty
174    /// than a standard boxed query. In both cases, the query builder can no longer
175    /// be inlined by the compiler. For most applications this cost will be minimal.
176    ///
177    /// ### Example
178    ///
179    /// ```rust
180    /// # include!("../../doctest_setup.rs");
181    /// #
182    /// # fn main() {
183    /// #     run_test().unwrap();
184    /// # }
185    /// #
186    /// # fn run_test() -> QueryResult<()> {
187    /// #     use std::collections::HashMap;
188    /// #     use schema::users::dsl::*;
189    /// #     let connection = &mut establish_connection();
190    /// #     let mut params = HashMap::new();
191    /// #     params.insert("tess_has_been_a_jerk", false);
192    /// let query = diesel::update(users).set(name.eq("Jerk")).into_boxed_clone();
193    /// let mut query = query.clone();
194    ///
195    /// if !params["tess_has_been_a_jerk"] {
196    ///     query = query.filter(name.ne("Tess"));
197    /// }
198    ///
199    /// let updated_rows = query.execute(connection)?;
200    /// assert_eq!(1, updated_rows);
201    ///
202    /// let expected_names = vec!["Jerk", "Tess"];
203    /// let names = users.select(name).order(id).load::<String>(connection)?;
204    ///
205    /// assert_eq!(expected_names, names);
206    /// #     Ok(())
207    /// # }
208    /// ```
209    pub fn into_boxed_clone<'a, DB>(self) -> IntoBoxedClone<'a, Self, DB>
210    where
211        DB: Backend,
212        Self: BoxedCloneDsl<'a, DB>,
213    {
214        BoxedCloneDsl::internal_into_boxed_clone(self)
215    }
216}
217
218impl<T, U, V, Ret, Predicate> FilterDsl<Predicate> for UpdateStatement<T, U, V, Ret>
219where
220    T: QuerySource,
221    U: WhereAnd<Predicate>,
222    Predicate: AppearsOnTable<T>,
223{
224    type Output = UpdateStatement<T, U::Output, V, Ret>;
225
226    fn filter(self, predicate: Predicate) -> Self::Output {
227        UpdateStatement {
228            from_clause: self.from_clause,
229            where_clause: self.where_clause.and(predicate),
230            set_clause: self.set_clause,
231            values: self.values,
232            returning: self.returning,
233        }
234    }
235}
236
237impl<'a, T, U, V, Ret, DB> BoxedDsl<'a, DB> for UpdateStatement<T, U, V, Ret>
238where
239    T: QuerySource,
240    U: Into<BoxedWhereClause<'a, DB>>,
241{
242    type Output = BoxedUpdateStatement<'a, DB, T, V, Ret>;
243
244    fn internal_into_boxed(self) -> Self::Output {
245        UpdateStatement {
246            from_clause: self.from_clause,
247            where_clause: self.where_clause.into(),
248            set_clause: self.set_clause,
249            values: self.values,
250            returning: self.returning,
251        }
252    }
253}
254
255impl<'a, T, U, V, Ret, DB> BoxedCloneDsl<'a, DB> for UpdateStatement<T, U, V, Ret>
256where
257    T: QuerySource,
258    U: Into<BoxedCloneWhereClause<'a, DB>>,
259{
260    type Output = BoxedCloneUpdateStatement<'a, DB, T, V, Ret>;
261
262    fn internal_into_boxed_clone(self) -> Self::Output {
263        UpdateStatement {
264            from_clause: self.from_clause,
265            where_clause: self.where_clause.into(),
266            set_clause: self.set_clause,
267            values: self.values,
268            returning: self.returning,
269        }
270    }
271}
272
273impl<T, U, V, Ret, DB> QueryFragment<DB> for UpdateStatement<T, U, V, Ret>
274where
275    DB: Backend + DieselReserveSpecialization,
276    T: Table,
277    T::FromClause: QueryFragment<DB>,
278    U: QueryFragment<DB>,
279    V: QueryFragment<DB> + AllowFilterForUpdate<U>,
280    Ret: QueryFragment<DB>,
281{
282    fn walk_ast<'b>(&'b self, mut out: AstPass<'_, 'b, DB>) -> QueryResult<()> {
283        if self.values.is_noop(out.backend())? {
284            return Err(QueryBuilderError(Box::new(EmptyChangeset)));
285        }
286
287        out.unsafe_to_cache_prepared();
288        out.push_sql("UPDATE ");
289        self.from_clause.walk_ast(out.reborrow())?;
290        self.set_clause.walk_ast(out.reborrow())?;
291        self.values.walk_ast(out.reborrow())?;
292        self.where_clause.walk_ast(out.reborrow())?;
293        self.returning.walk_ast(out.reborrow())?;
294        Ok(())
295    }
296}
297
298impl<T, U, V, Ret> QueryId for UpdateStatement<T, U, V, Ret>
299where
300    T: QuerySource,
301{
302    type QueryId = ();
303
304    const HAS_STATIC_QUERY_ID: bool = false;
305}
306
307impl<T, U, V> AsQuery for UpdateStatement<T, U, V, NoReturningClause>
308where
309    T: Table,
310    UpdateStatement<T, U, V, ReturningClause<T::AllColumns>>: Query,
311    T::AllColumns: SelectableExpression<ReturningQuerySource<UpdateStmt, T>> + ValidGrouping<()>,
312    <T::AllColumns as ValidGrouping<()>>::IsAggregate:
313        MixedAggregates<is_aggregate::No, Output = is_aggregate::No>,
314{
315    type SqlType = <Self::Query as Query>::SqlType;
316    type Query = UpdateStatement<T, U, V, ReturningClause<T::AllColumns>>;
317
318    fn as_query(self) -> Self::Query {
319        self.returning(T::all_columns())
320    }
321}
322
323impl<T, U, V, Ret> Query for UpdateStatement<T, U, V, ReturningClause<Ret>>
324where
325    T: Table,
326    Ret: SelectableExpression<ReturningQuerySource<UpdateStmt, T>> + ValidGrouping<()>,
327    Ret::IsAggregate: MixedAggregates<is_aggregate::No, Output = is_aggregate::No>,
328{
329    type SqlType = <Ret as Expression>::SqlType;
330}
331
332impl<T: QuerySource, U, V, Ret> RunQueryDslSupport for UpdateStatement<T, U, V, Ret> {}
333
334impl<T: QuerySource, U, V> UpdateStatement<T, U, V, NoReturningClause> {
335    /// Specify what expression is returned after execution of the `update`.
336    /// # Examples
337    ///
338    /// ### Updating a single record:
339    ///
340    /// ```rust
341    /// # include!("../../doctest_setup.rs");
342    /// #
343    /// # #[cfg(feature = "postgres")]
344    /// # fn main() {
345    /// #     use schema::users::dsl::*;
346    /// #     let connection = &mut establish_connection();
347    /// let updated_name = diesel::update(users.filter(id.eq(1)))
348    ///     .set(name.eq("Dean"))
349    ///     .returning(name)
350    ///     .get_result(connection);
351    /// assert_eq!(Ok("Dean".to_string()), updated_name);
352    /// # }
353    /// # #[cfg(not(feature = "postgres"))]
354    /// # fn main() {}
355    /// ```
356    ///
357    /// ### Returning the pre-update value (PostgreSQL 18+):
358    ///
359    /// `RETURNING old.col` — exposed via [`diesel::pg::returning::old()`] —
360    /// returns the value of the column **before** the update was applied.
361    /// This requires PostgreSQL 18 or newer.
362    ///
363    /// ```rust
364    /// # include!("../../doctest_setup.rs");
365    /// #
366    /// # #[cfg(feature = "postgres")]
367    /// # fn main() {
368    /// #     use schema::users::dsl::*;
369    /// #     use diesel::pg::returning::old;
370    /// #     let connection = &mut establish_connection();
371    /// #     // `RETURNING old.col` requires PostgreSQL 18+
372    /// #     let pg_version: i32 = diesel::dsl::sql::<diesel::sql_types::Integer>(
373    /// #         "SELECT current_setting('server_version_num')::int",
374    /// #     ).get_result(connection).unwrap();
375    /// #     if pg_version < 180000 { return; }
376    /// let was_and_now = diesel::update(users.filter(id.eq(1)))
377    ///     .set(name.eq("Dean"))
378    ///     .returning((old(name), name))
379    ///     .get_result::<(String, String)>(connection);
380    /// assert_eq!(Ok(("Sean".to_string(), "Dean".to_string())), was_and_now);
381    /// # }
382    /// # #[cfg(not(feature = "postgres"))]
383    /// # fn main() {}
384    /// ```
385    pub fn returning<E>(self, returns: E) -> UpdateStatement<T, U, V, ReturningClause<E>>
386    where
387        T: Table,
388        UpdateStatement<T, U, V, ReturningClause<E>>: Query,
389    {
390        UpdateStatement {
391            from_clause: self.from_clause,
392            where_clause: self.where_clause,
393            set_clause: self.set_clause,
394            values: self.values,
395            returning: ReturningClause(returns),
396        }
397    }
398}
399
400/// Indicates that you have not yet called `.set` on an update statement
401#[derive(#[automatically_derived]
impl ::core::fmt::Debug for SetNotCalled {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::write_str(f, "SetNotCalled")
    }
}Debug, #[automatically_derived]
impl ::core::clone::Clone for SetNotCalled {
    #[inline]
    fn clone(&self) -> SetNotCalled { *self }
}Clone, #[automatically_derived]
impl ::core::marker::Copy for SetNotCalled { }Copy)]
402pub struct SetNotCalled;
403
404pub(crate) mod private {
405    use crate::backend::Backend;
406    use crate::query_builder::where_clause::{
407        BoxedCloneWhereClause, BoxedWhereClause, NoWhereClause, WhereClause,
408    };
409
410    use super::changeset::Assign;
411
412    /// Helper trait for `#[auto_type]`
413    ///
414    /// This trait allows inferring the return type of `UpdateStatement::set` and
415    /// `IncompleteDoUpdate::set` (via `IntoUpdateTarget`). It is used to define the
416    /// `Set` type alias in `diesel::dsl`.
417    #[allow(unreachable_pub)]
418    pub trait SetAutoTypeHelper<Changes> {
419        type Out;
420    }
421
422    impl<T, W, Changes> SetAutoTypeHelper<Changes> for crate::query_builder::UpdateStatement<T, W>
423    where
424        T: crate::QuerySource,
425        Changes: crate::AsChangeset,
426    {
427        type Out = crate::query_builder::UpdateStatement<T, W, Changes::Changeset>;
428    }
429
430    /// A helper trait to mark values clauses as compatible with a given filter syntax
431    #[diagnostic::on_unimplemented(
432        message = "cannot apply a `WHERE` clause to batch updates",
433        note = "the information about which rows to update are provided as part of the values"
434    )]
435    pub trait AllowFilterForUpdate<P> {}
436
437    impl<U> AllowFilterForUpdate<NoWhereClause> for U {}
438
439    impl<W, C, B> AllowFilterForUpdate<WhereClause<W>> for Assign<C, B> {}
440    impl<W, T> AllowFilterForUpdate<WhereClause<W>> for Option<T> where
441        T: AllowFilterForUpdate<WhereClause<W>>
442    {
443    }
444
445    impl<'a, DB, C, B> AllowFilterForUpdate<BoxedWhereClause<'a, DB>> for Assign<C, B> where DB: Backend {}
446    impl<'a, DB, T> AllowFilterForUpdate<BoxedWhereClause<'a, DB>> for Option<T>
447    where
448        DB: Backend,
449        T: AllowFilterForUpdate<BoxedWhereClause<'a, DB>>,
450    {
451    }
452
453    impl<'a, DB, C, B> AllowFilterForUpdate<BoxedCloneWhereClause<'a, DB>> for Assign<C, B> where
454        DB: Backend
455    {
456    }
457    impl<'a, DB, T> AllowFilterForUpdate<BoxedCloneWhereClause<'a, DB>> for Option<T>
458    where
459        DB: Backend,
460        T: AllowFilterForUpdate<BoxedCloneWhereClause<'a, DB>>,
461    {
462    }
463}
464
465/// Determines when the `SET` part of an update statement will be added to the sql.
466///
467/// Usual single row updates default to [SetClause::Immediate]. Batch row updates
468/// will use [SetClause::Delegated].
469///
470/// - [SetClause::Immediate]
471///   will add `SET` right after the [UpdateStatement::from_clause] `QueryFragment`. \
472///   `Update users SET ... ;`
473/// - [SetClause::Delegated]
474///   hands over the control of adding `SET` to [UpdateStatement::values] `QueryFragment`. \
475///   `Update users ... SET ... ; ` will then be permitted.  \
476///   Batch update for mysql requires this behavior.
477#[derive(#[automatically_derived]
impl ::core::clone::Clone for SetClause {
    #[inline]
    fn clone(&self) -> SetClause { *self }
}Clone, #[automatically_derived]
impl ::core::marker::Copy for SetClause { }Copy, #[automatically_derived]
impl ::core::fmt::Debug for SetClause {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::write_str(f,
            match self {
                SetClause::Immediate => "Immediate",
                SetClause::Delegated => "Delegated",
            })
    }
}Debug)]
478pub enum SetClause {
479    Immediate,
480    Delegated,
481}
482
483impl<DB> QueryFragment<DB> for SetClause
484where
485    DB: Backend,
486{
487    fn walk_ast<'b>(&'b self, mut out: AstPass<'_, 'b, DB>) -> QueryResult<()> {
488        if let SetClause::Immediate = self {
489            out.push_sql(" SET ");
490        }
491        Ok(())
492    }
493}