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};
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::{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
77impl<T: QuerySource, U, V, Ret> UpdateStatement<T, U, V, Ret> {
78    /// Adds the given predicate to the `WHERE` clause of the statement being
79    /// constructed.
80    ///
81    /// If there is already a `WHERE` clause, the predicate will be appended
82    /// with `AND`. There is no difference in behavior between
83    /// `update(table.filter(x))` and `update(table).filter(x)`.
84    ///
85    /// # Example
86    ///
87    /// ```rust
88    /// # include!("../../doctest_setup.rs");
89    /// #
90    /// # fn main() {
91    /// #     use schema::users::dsl::*;
92    /// #     let connection = &mut establish_connection();
93    /// let updated_rows = diesel::update(users)
94    ///     .set(name.eq("Jim"))
95    ///     .filter(name.eq("Sean"))
96    ///     .execute(connection);
97    /// assert_eq!(Ok(1), updated_rows);
98    ///
99    /// let expected_names = vec!["Jim".to_string(), "Tess".to_string()];
100    /// let names = users.select(name).order(id).load(connection);
101    ///
102    /// assert_eq!(Ok(expected_names), names);
103    /// # }
104    /// ```
105    pub fn filter<Predicate>(self, predicate: Predicate) -> Filter<Self, Predicate>
106    where
107        Self: FilterDsl<Predicate>,
108    {
109        FilterDsl::filter(self, predicate)
110    }
111
112    /// Boxes the `WHERE` clause of this update statement.
113    ///
114    /// This is useful for cases where you want to conditionally modify a query,
115    /// but need the type to remain the same. The backend must be specified as
116    /// part of this. It is not possible to box a query and have it be useable
117    /// on multiple backends.
118    ///
119    /// A boxed query will incur a minor performance penalty, as the query builder
120    /// can no longer be inlined by the compiler. For most applications this cost
121    /// will be minimal.
122    ///
123    /// ### Example
124    ///
125    /// ```rust
126    /// # include!("../../doctest_setup.rs");
127    /// #
128    /// # fn main() {
129    /// #     run_test().unwrap();
130    /// # }
131    /// #
132    /// # fn run_test() -> QueryResult<()> {
133    /// #     use std::collections::HashMap;
134    /// #     use schema::users::dsl::*;
135    /// #     let connection = &mut establish_connection();
136    /// #     let mut params = HashMap::new();
137    /// #     params.insert("tess_has_been_a_jerk", false);
138    /// let mut query = diesel::update(users).set(name.eq("Jerk")).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    T: QuerySource,
166    U: WhereAnd<Predicate>,
167    Predicate: AppearsOnTable<T>,
168{
169    type Output = UpdateStatement<T, U::Output, V, Ret>;
170
171    fn filter(self, predicate: Predicate) -> Self::Output {
172        UpdateStatement {
173            from_clause: self.from_clause,
174            where_clause: self.where_clause.and(predicate),
175            set_clause: self.set_clause,
176            values: self.values,
177            returning: self.returning,
178        }
179    }
180}
181
182impl<'a, T, U, V, Ret, DB> BoxedDsl<'a, DB> for UpdateStatement<T, U, V, Ret>
183where
184    T: QuerySource,
185    U: Into<BoxedWhereClause<'a, DB>>,
186{
187    type Output = BoxedUpdateStatement<'a, DB, T, V, Ret>;
188
189    fn internal_into_boxed(self) -> Self::Output {
190        UpdateStatement {
191            from_clause: self.from_clause,
192            where_clause: self.where_clause.into(),
193            set_clause: self.set_clause,
194            values: self.values,
195            returning: self.returning,
196        }
197    }
198}
199
200impl<T, U, V, Ret, DB> QueryFragment<DB> for UpdateStatement<T, U, V, Ret>
201where
202    DB: Backend + DieselReserveSpecialization,
203    T: Table,
204    T::FromClause: QueryFragment<DB>,
205    U: QueryFragment<DB>,
206    V: QueryFragment<DB> + AllowFilterForUpdate<U>,
207    Ret: QueryFragment<DB>,
208{
209    fn walk_ast<'b>(&'b self, mut out: AstPass<'_, 'b, DB>) -> QueryResult<()> {
210        if self.values.is_noop(out.backend())? {
211            return Err(QueryBuilderError(Box::new(EmptyChangeset)));
212        }
213
214        out.unsafe_to_cache_prepared();
215        out.push_sql("UPDATE ");
216        self.from_clause.walk_ast(out.reborrow())?;
217        self.set_clause.walk_ast(out.reborrow())?;
218        self.values.walk_ast(out.reborrow())?;
219        self.where_clause.walk_ast(out.reborrow())?;
220        self.returning.walk_ast(out.reborrow())?;
221        Ok(())
222    }
223}
224
225impl<T, U, V, Ret> QueryId for UpdateStatement<T, U, V, Ret>
226where
227    T: QuerySource,
228{
229    type QueryId = ();
230
231    const HAS_STATIC_QUERY_ID: bool = false;
232}
233
234impl<T, U, V> AsQuery for UpdateStatement<T, U, V, NoReturningClause>
235where
236    T: Table,
237    UpdateStatement<T, U, V, ReturningClause<T::AllColumns>>: Query,
238    T::AllColumns: SelectableExpression<ReturningQuerySource<UpdateStmt, T>> + ValidGrouping<()>,
239    <T::AllColumns as ValidGrouping<()>>::IsAggregate:
240        MixedAggregates<is_aggregate::No, Output = is_aggregate::No>,
241{
242    type SqlType = <Self::Query as Query>::SqlType;
243    type Query = UpdateStatement<T, U, V, ReturningClause<T::AllColumns>>;
244
245    fn as_query(self) -> Self::Query {
246        self.returning(T::all_columns())
247    }
248}
249
250impl<T, U, V, Ret> Query for UpdateStatement<T, U, V, ReturningClause<Ret>>
251where
252    T: Table,
253    Ret: SelectableExpression<ReturningQuerySource<UpdateStmt, T>> + ValidGrouping<()>,
254    Ret::IsAggregate: MixedAggregates<is_aggregate::No, Output = is_aggregate::No>,
255{
256    type SqlType = <Ret as Expression>::SqlType;
257}
258
259impl<T: QuerySource, U, V, Ret> RunQueryDslSupport for UpdateStatement<T, U, V, Ret> {}
260
261impl<T: QuerySource, U, V> UpdateStatement<T, U, V, NoReturningClause> {
262    /// Specify what expression is returned after execution of the `update`.
263    /// # Examples
264    ///
265    /// ### Updating a single record:
266    ///
267    /// ```rust
268    /// # include!("../../doctest_setup.rs");
269    /// #
270    /// # #[cfg(feature = "postgres")]
271    /// # fn main() {
272    /// #     use schema::users::dsl::*;
273    /// #     let connection = &mut establish_connection();
274    /// let updated_name = diesel::update(users.filter(id.eq(1)))
275    ///     .set(name.eq("Dean"))
276    ///     .returning(name)
277    ///     .get_result(connection);
278    /// assert_eq!(Ok("Dean".to_string()), updated_name);
279    /// # }
280    /// # #[cfg(not(feature = "postgres"))]
281    /// # fn main() {}
282    /// ```
283    ///
284    /// ### Returning the pre-update value (PostgreSQL 18+):
285    ///
286    /// `RETURNING old.col` — exposed via [`diesel::pg::returning::old()`] —
287    /// returns the value of the column **before** the update was applied.
288    /// This requires PostgreSQL 18 or newer.
289    ///
290    /// ```rust
291    /// # include!("../../doctest_setup.rs");
292    /// #
293    /// # #[cfg(feature = "postgres")]
294    /// # fn main() {
295    /// #     use schema::users::dsl::*;
296    /// #     use diesel::pg::returning::old;
297    /// #     let connection = &mut establish_connection();
298    /// #     // `RETURNING old.col` requires PostgreSQL 18+
299    /// #     let pg_version: i32 = diesel::dsl::sql::<diesel::sql_types::Integer>(
300    /// #         "SELECT current_setting('server_version_num')::int",
301    /// #     ).get_result(connection).unwrap();
302    /// #     if pg_version < 180000 { return; }
303    /// let was_and_now = diesel::update(users.filter(id.eq(1)))
304    ///     .set(name.eq("Dean"))
305    ///     .returning((old(name), name))
306    ///     .get_result::<(String, String)>(connection);
307    /// assert_eq!(Ok(("Sean".to_string(), "Dean".to_string())), was_and_now);
308    /// # }
309    /// # #[cfg(not(feature = "postgres"))]
310    /// # fn main() {}
311    /// ```
312    pub fn returning<E>(self, returns: E) -> UpdateStatement<T, U, V, ReturningClause<E>>
313    where
314        T: Table,
315        UpdateStatement<T, U, V, ReturningClause<E>>: Query,
316    {
317        UpdateStatement {
318            from_clause: self.from_clause,
319            where_clause: self.where_clause,
320            set_clause: self.set_clause,
321            values: self.values,
322            returning: ReturningClause(returns),
323        }
324    }
325}
326
327/// Indicates that you have not yet called `.set` on an update statement
328#[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)]
329pub struct SetNotCalled;
330
331pub(crate) mod private {
332    use crate::backend::Backend;
333    use crate::query_builder::where_clause::{BoxedWhereClause, NoWhereClause, WhereClause};
334
335    use super::changeset::Assign;
336
337    /// Helper trait for `#[auto_type]`
338    ///
339    /// This trait allows inferring the return type of `UpdateStatement::set` and
340    /// `IncompleteDoUpdate::set` (via `IntoUpdateTarget`). It is used to define the
341    /// `Set` type alias in `diesel::dsl`.
342    #[allow(unreachable_pub)]
343    pub trait SetAutoTypeHelper<Changes> {
344        type Out;
345    }
346
347    impl<T, W, Changes> SetAutoTypeHelper<Changes> for crate::query_builder::UpdateStatement<T, W>
348    where
349        T: crate::QuerySource,
350        Changes: crate::AsChangeset,
351    {
352        type Out = crate::query_builder::UpdateStatement<T, W, Changes::Changeset>;
353    }
354
355    /// A helper trait to mark values clauses as compatible with a given filter syntax
356    #[diagnostic::on_unimplemented(
357        message = "cannot apply a `WHERE` clause to batch updates",
358        note = "the information about which rows to update are provided as part of the values"
359    )]
360    pub trait AllowFilterForUpdate<P> {}
361
362    impl<U> AllowFilterForUpdate<NoWhereClause> for U {}
363
364    impl<W, C, B> AllowFilterForUpdate<WhereClause<W>> for Assign<C, B> {}
365    impl<W, T> AllowFilterForUpdate<WhereClause<W>> for Option<T> where
366        T: AllowFilterForUpdate<WhereClause<W>>
367    {
368    }
369
370    impl<'a, DB, C, B> AllowFilterForUpdate<BoxedWhereClause<'a, DB>> for Assign<C, B> where DB: Backend {}
371    impl<'a, DB, T> AllowFilterForUpdate<BoxedWhereClause<'a, DB>> for Option<T>
372    where
373        DB: Backend,
374        T: AllowFilterForUpdate<BoxedWhereClause<'a, DB>>,
375    {
376    }
377}
378
379/// Determines when the `SET` part of an update statement will be added to the sql.
380///
381/// Usual single row updates default to [SetClause::Immediate]. Batch row updates
382/// will use [SetClause::Delegated].
383///
384/// - [SetClause::Immediate]
385///   will add `SET` right after the [UpdateStatement::from_clause] `QueryFragment`. \
386///   `Update users SET ... ;`
387/// - [SetClause::Delegated]
388///   hands over the control of adding `SET` to [UpdateStatement::values] `QueryFragment`. \
389///   `Update users ... SET ... ; ` will then be permitted.  \
390///   Batch update for mysql requires this behavior.
391#[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)]
392pub enum SetClause {
393    Immediate,
394    Delegated,
395}
396
397impl<DB> QueryFragment<DB> for SetClause
398where
399    DB: Backend,
400{
401    fn walk_ast<'b>(&'b self, mut out: AstPass<'_, 'b, DB>) -> QueryResult<()> {
402        if let SetClause::Immediate = self {
403            out.push_sql(" SET ");
404        }
405        Ok(())
406    }
407}