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) -> Self {
        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 where_clause set_clause values returning",
            &self.from_clause, &self.where_clause, &self.set_clause,
            &self.values, &&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    /// # // Reading the query result requires reading memory allocated by the
95    /// # // native sqlite library, which miri cannot track provenance for.
96    /// # #[cfg(miri)]
97    /// # fn main() {}
98    /// # #[cfg(not(miri))]
99    /// # fn main() {
100    /// #     use schema::users::dsl::*;
101    /// #     let connection = &mut establish_connection();
102    /// let updated_rows = diesel::update(users)
103    ///     .set(name.eq("Jim"))
104    ///     .filter(name.eq("Sean"))
105    ///     .execute(connection);
106    /// assert_eq!(Ok(1), updated_rows);
107    ///
108    /// let expected_names = vec!["Jim".to_string(), "Tess".to_string()];
109    /// let names = users.select(name).order(id).load(connection);
110    ///
111    /// assert_eq!(Ok(expected_names), names);
112    /// # }
113    /// ```
114    pub fn filter<Predicate>(self, predicate: Predicate) -> Filter<Self, Predicate>
115    where
116        Self: FilterDsl<Predicate>,
117    {
118        FilterDsl::filter(self, predicate)
119    }
120
121    /// Boxes the `WHERE` clause of this update statement.
122    ///
123    /// This is useful for cases where you want to conditionally modify a query,
124    /// but need the type to remain the same. The backend must be specified as
125    /// part of this. It is not possible to box a query and have it be useable
126    /// on multiple backends.
127    ///
128    /// A boxed query will incur a minor performance penalty, as the query builder
129    /// can no longer be inlined by the compiler. For most applications this cost
130    /// will be minimal.
131    ///
132    /// ### Example
133    ///
134    /// ```rust
135    /// # include!("../../doctest_setup.rs");
136    /// #
137    /// # // Reading the query result requires reading memory allocated by the
138    /// # // native sqlite library, which miri cannot track provenance for.
139    /// # #[cfg(miri)]
140    /// # fn main() {}
141    /// # #[cfg(not(miri))]
142    /// # fn main() {
143    /// #     run_test().unwrap();
144    /// # }
145    /// #
146    /// # fn run_test() -> QueryResult<()> {
147    /// #     use std::collections::HashMap;
148    /// #     use schema::users::dsl::*;
149    /// #     let connection = &mut establish_connection();
150    /// #     let mut params = HashMap::new();
151    /// #     params.insert("tess_has_been_a_jerk", false);
152    /// let mut query = diesel::update(users).set(name.eq("Jerk")).into_boxed();
153    ///
154    /// if !params["tess_has_been_a_jerk"] {
155    ///     query = query.filter(name.ne("Tess"));
156    /// }
157    ///
158    /// let updated_rows = query.execute(connection)?;
159    /// assert_eq!(1, updated_rows);
160    ///
161    /// let expected_names = vec!["Jerk", "Tess"];
162    /// let names = users.select(name).order(id).load::<String>(connection)?;
163    ///
164    /// assert_eq!(expected_names, names);
165    /// #     Ok(())
166    /// # }
167    /// ```
168    pub fn into_boxed<'a, DB>(self) -> IntoBoxed<'a, Self, DB>
169    where
170        DB: Backend,
171        Self: BoxedDsl<'a, DB>,
172    {
173        BoxedDsl::internal_into_boxed(self)
174    }
175
176    /// Wraps the `WHERE` clause of this update statement in an [`Arc`](alloc::sync::Arc).
177    ///
178    /// This is useful for cases where you want to clone and conditionally
179    /// modify a query, but need the type to remain the same. The backend
180    /// must be specified as part of this. It is not possible to box a query
181    /// and have it be useable on multiple backends.
182    ///
183    /// A cloneable boxed query will incur a slightly greater performance penalty
184    /// than a standard boxed query. In both cases, the query builder can no longer
185    /// be inlined by the compiler. For most applications this cost will be minimal.
186    ///
187    /// ### Example
188    ///
189    /// ```rust
190    /// # include!("../../doctest_setup.rs");
191    /// #
192    /// # // Reading the query result requires reading memory allocated by the
193    /// # // native sqlite library, which miri cannot track provenance for.
194    /// # #[cfg(miri)]
195    /// # fn main() {}
196    /// # #[cfg(not(miri))]
197    /// # fn main() {
198    /// #     run_test().unwrap();
199    /// # }
200    /// #
201    /// # fn run_test() -> QueryResult<()> {
202    /// #     use std::collections::HashMap;
203    /// #     use schema::users::dsl::*;
204    /// #     let connection = &mut establish_connection();
205    /// #     let mut params = HashMap::new();
206    /// #     params.insert("tess_has_been_a_jerk", false);
207    /// let query = diesel::update(users).set(name.eq("Jerk")).into_boxed_clone();
208    /// let mut query = query.clone();
209    ///
210    /// if !params["tess_has_been_a_jerk"] {
211    ///     query = query.filter(name.ne("Tess"));
212    /// }
213    ///
214    /// let updated_rows = query.execute(connection)?;
215    /// assert_eq!(1, updated_rows);
216    ///
217    /// let expected_names = vec!["Jerk", "Tess"];
218    /// let names = users.select(name).order(id).load::<String>(connection)?;
219    ///
220    /// assert_eq!(expected_names, names);
221    /// #     Ok(())
222    /// # }
223    /// ```
224    pub fn into_boxed_clone<'a, DB>(self) -> IntoBoxedClone<'a, Self, DB>
225    where
226        DB: Backend,
227        Self: BoxedCloneDsl<'a, DB>,
228    {
229        BoxedCloneDsl::internal_into_boxed_clone(self)
230    }
231}
232
233impl<T, U, V, Ret, Predicate> FilterDsl<Predicate> for UpdateStatement<T, U, V, Ret>
234where
235    T: QuerySource,
236    U: WhereAnd<Predicate>,
237    Predicate: AppearsOnTable<T>,
238{
239    type Output = UpdateStatement<T, U::Output, V, Ret>;
240
241    fn filter(self, predicate: Predicate) -> Self::Output {
242        UpdateStatement {
243            from_clause: self.from_clause,
244            where_clause: self.where_clause.and(predicate),
245            set_clause: self.set_clause,
246            values: self.values,
247            returning: self.returning,
248        }
249    }
250}
251
252impl<'a, T, U, V, Ret, DB> BoxedDsl<'a, DB> for UpdateStatement<T, U, V, Ret>
253where
254    T: QuerySource,
255    U: Into<BoxedWhereClause<'a, DB>>,
256{
257    type Output = BoxedUpdateStatement<'a, DB, T, V, Ret>;
258
259    fn internal_into_boxed(self) -> Self::Output {
260        UpdateStatement {
261            from_clause: self.from_clause,
262            where_clause: self.where_clause.into(),
263            set_clause: self.set_clause,
264            values: self.values,
265            returning: self.returning,
266        }
267    }
268}
269
270impl<'a, T, U, V, Ret, DB> BoxedCloneDsl<'a, DB> for UpdateStatement<T, U, V, Ret>
271where
272    T: QuerySource,
273    U: Into<BoxedCloneWhereClause<'a, DB>>,
274{
275    type Output = BoxedCloneUpdateStatement<'a, DB, T, V, Ret>;
276
277    fn internal_into_boxed_clone(self) -> Self::Output {
278        UpdateStatement {
279            from_clause: self.from_clause,
280            where_clause: self.where_clause.into(),
281            set_clause: self.set_clause,
282            values: self.values,
283            returning: self.returning,
284        }
285    }
286}
287
288impl<T, U, V, Ret, DB> QueryFragment<DB> for UpdateStatement<T, U, V, Ret>
289where
290    DB: Backend + DieselReserveSpecialization,
291    T: Table,
292    T::FromClause: QueryFragment<DB>,
293    U: QueryFragment<DB>,
294    V: QueryFragment<DB> + AllowFilterForUpdate<U>,
295    Ret: QueryFragment<DB>,
296{
297    fn walk_ast<'b>(&'b self, mut out: AstPass<'_, 'b, DB>) -> QueryResult<()> {
298        if self.values.is_noop(out.backend())? {
299            return Err(QueryBuilderError(Box::new(EmptyChangeset)));
300        }
301
302        out.unsafe_to_cache_prepared();
303        out.push_sql("UPDATE ");
304        self.from_clause.walk_ast(out.reborrow())?;
305        self.set_clause.walk_ast(out.reborrow())?;
306        self.values.walk_ast(out.reborrow())?;
307        self.where_clause.walk_ast(out.reborrow())?;
308        self.returning.walk_ast(out.reborrow())?;
309        Ok(())
310    }
311}
312
313impl<T, U, V, Ret> QueryId for UpdateStatement<T, U, V, Ret>
314where
315    T: QuerySource,
316{
317    type QueryId = ();
318
319    const HAS_STATIC_QUERY_ID: bool = false;
320}
321
322impl<T, U, V> AsQuery for UpdateStatement<T, U, V, NoReturningClause>
323where
324    T: Table,
325    UpdateStatement<T, U, V, ReturningClause<T::AllColumns>>: Query,
326    T::AllColumns: SelectableExpression<ReturningQuerySource<UpdateStmt, T>> + ValidGrouping<()>,
327    <T::AllColumns as ValidGrouping<()>>::IsAggregate:
328        MixedAggregates<is_aggregate::No, Output = is_aggregate::No>,
329{
330    type SqlType = <Self::Query as Query>::SqlType;
331    type Query = UpdateStatement<T, U, V, ReturningClause<T::AllColumns>>;
332
333    fn as_query(self) -> Self::Query {
334        self.returning(T::all_columns())
335    }
336}
337
338impl<T, U, V, Ret> Query for UpdateStatement<T, U, V, ReturningClause<Ret>>
339where
340    T: Table,
341    Ret: SelectableExpression<ReturningQuerySource<UpdateStmt, T>> + ValidGrouping<()>,
342    Ret::IsAggregate: MixedAggregates<is_aggregate::No, Output = is_aggregate::No>,
343{
344    type SqlType = <Ret as Expression>::SqlType;
345}
346
347impl<T: QuerySource, U, V, Ret> RunQueryDslSupport for UpdateStatement<T, U, V, Ret> {}
348
349impl<T: QuerySource, U, V> UpdateStatement<T, U, V, NoReturningClause> {
350    /// Specify what expression is returned after execution of the `update`.
351    ///
352    /// On MariaDB, rows that match the filter but whose values do not change
353    /// are left out of the result, so `get_result` returns `NotFound` where
354    /// `execute` reports one updated row
355    /// ([MDEV-41113](https://jira.mariadb.org/browse/MDEV-41113)).
356    ///
357    /// # Examples
358    ///
359    /// ### Updating a single record:
360    ///
361    /// ```rust
362    /// # include!("../../doctest_setup.rs");
363    /// #
364    /// # #[cfg(feature = "postgres")]
365    /// # fn main() {
366    /// #     use schema::users::dsl::*;
367    /// #     let connection = &mut establish_connection();
368    /// let updated_name = diesel::update(users.filter(id.eq(1)))
369    ///     .set(name.eq("Dean"))
370    ///     .returning(name)
371    ///     .get_result(connection);
372    /// assert_eq!(Ok("Dean".to_string()), updated_name);
373    /// # }
374    /// # #[cfg(not(feature = "postgres"))]
375    /// # fn main() {}
376    /// ```
377    ///
378    /// ### Returning the pre-update value (PostgreSQL 18+):
379    ///
380    /// `RETURNING old.col` — exposed via [`diesel::pg::returning::old()`] —
381    /// returns the value of the column **before** the update was applied.
382    /// This requires PostgreSQL 18 or newer.
383    ///
384    /// ```rust
385    /// # include!("../../doctest_setup.rs");
386    /// #
387    /// # #[cfg(feature = "postgres")]
388    /// # fn main() {
389    /// #     use schema::users::dsl::*;
390    /// #     use diesel::pg::returning::old;
391    /// #     let connection = &mut establish_connection();
392    /// #     // `RETURNING old.col` requires PostgreSQL 18+
393    /// #     if connection.version().unwrap() < diesel::connection::DatabaseVersion::new(18, 0, 0) {
394    /// #         return;
395    /// #     }
396    /// let was_and_now = diesel::update(users.filter(id.eq(1)))
397    ///     .set(name.eq("Dean"))
398    ///     .returning((old(name), name))
399    ///     .get_result::<(String, String)>(connection);
400    /// assert_eq!(Ok(("Sean".to_string(), "Dean".to_string())), was_and_now);
401    /// # }
402    /// # #[cfg(not(feature = "postgres"))]
403    /// # fn main() {}
404    /// ```
405    pub fn returning<E>(self, returns: E) -> UpdateStatement<T, U, V, ReturningClause<E>>
406    where
407        T: Table,
408        UpdateStatement<T, U, V, ReturningClause<E>>: Query,
409    {
410        UpdateStatement {
411            from_clause: self.from_clause,
412            where_clause: self.where_clause,
413            set_clause: self.set_clause,
414            values: self.values,
415            returning: ReturningClause(returns),
416        }
417    }
418}
419
420/// Indicates that you have not yet called `.set` on an update statement
421#[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]#[doc(hidden)]unsafe impl ::core::clone::TrivialClone
    for SetNotCalled {
}
#[automatically_derived]impl ::core::clone::Clone for SetNotCalled {
    #[inline]
    fn clone(&self) -> Self { *self }
}Clone, #[automatically_derived]impl ::core::marker::Copy for SetNotCalled { }Copy)]
422pub struct SetNotCalled;
423
424pub(crate) mod private {
425    use crate::backend::Backend;
426    use crate::query_builder::where_clause::{
427        BoxedCloneWhereClause, BoxedWhereClause, NoWhereClause, WhereClause,
428    };
429
430    use super::changeset::Assign;
431
432    /// Helper trait for `#[auto_type]`
433    ///
434    /// This trait allows inferring the return type of `UpdateStatement::set` and
435    /// `IncompleteDoUpdate::set` (via `IntoUpdateTarget`). It is used to define the
436    /// `Set` type alias in `diesel::dsl`.
437    #[allow(unreachable_pub)]
438    pub trait SetAutoTypeHelper<Changes> {
439        type Out;
440    }
441
442    impl<T, W, Changes> SetAutoTypeHelper<Changes> for crate::query_builder::UpdateStatement<T, W>
443    where
444        T: crate::QuerySource,
445        Changes: crate::AsChangeset,
446    {
447        type Out = crate::query_builder::UpdateStatement<T, W, Changes::Changeset>;
448    }
449
450    /// A helper trait to mark values clauses as compatible with a given filter syntax
451    #[diagnostic::on_unimplemented(
452        message = "cannot apply a `WHERE` clause to batch updates",
453        note = "the information about which rows to update are provided as part of the values"
454    )]
455    pub trait AllowFilterForUpdate<P> {}
456
457    impl<U> AllowFilterForUpdate<NoWhereClause> for U {}
458
459    impl<W, C, B> AllowFilterForUpdate<WhereClause<W>> for Assign<C, B> {}
460    impl<W, T> AllowFilterForUpdate<WhereClause<W>> for Option<T> where
461        T: AllowFilterForUpdate<WhereClause<W>>
462    {
463    }
464
465    impl<'a, DB, C, B> AllowFilterForUpdate<BoxedWhereClause<'a, DB>> for Assign<C, B> where DB: Backend {}
466    impl<'a, DB, T> AllowFilterForUpdate<BoxedWhereClause<'a, DB>> for Option<T>
467    where
468        DB: Backend,
469        T: AllowFilterForUpdate<BoxedWhereClause<'a, DB>>,
470    {
471    }
472
473    impl<'a, DB, C, B> AllowFilterForUpdate<BoxedCloneWhereClause<'a, DB>> for Assign<C, B> where
474        DB: Backend
475    {
476    }
477    impl<'a, DB, T> AllowFilterForUpdate<BoxedCloneWhereClause<'a, DB>> for Option<T>
478    where
479        DB: Backend,
480        T: AllowFilterForUpdate<BoxedCloneWhereClause<'a, DB>>,
481    {
482    }
483}
484
485/// Determines when the `SET` part of an update statement will be added to the sql.
486///
487/// Usual single row updates default to [SetClause::Immediate]. Batch row updates
488/// will use [SetClause::Delegated].
489///
490/// - [SetClause::Immediate]
491///   will add `SET` right after the [UpdateStatement::from_clause] `QueryFragment`. \
492///   `Update users SET ... ;`
493/// - [SetClause::Delegated]
494///   hands over the control of adding `SET` to [UpdateStatement::values] `QueryFragment`. \
495///   `Update users ... SET ... ; ` will then be permitted.  \
496///   Batch update for mysql requires this behavior.
497#[derive(#[automatically_derived]#[doc(hidden)]unsafe impl ::core::clone::TrivialClone
    for SetClause {
}
#[automatically_derived]impl ::core::clone::Clone for SetClause {
    #[inline]
    fn clone(&self) -> Self { *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)]
498pub enum SetClause {
499    Immediate,
500    Delegated,
501}
502
503impl<DB> QueryFragment<DB> for SetClause
504where
505    DB: Backend,
506{
507    fn walk_ast<'b>(&'b self, mut out: AstPass<'_, 'b, DB>) -> QueryResult<()> {
508        if let SetClause::Immediate = self {
509            out.push_sql(" SET ");
510        }
511        Ok(())
512    }
513}