Skip to main content

diesel/query_builder/update_statement/
mod.rs

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