diesel/query_builder/update_statement/
mod.rs1pub(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 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."]
52pub 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
64pub 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 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 pub fn into_boxed<'a, DB>(self) -> IntoBoxed<'a, Self, DB>
146 where
147 DB: Backend,
148 Self: BoxedDsl<'a, DB>,
149 {
150 BoxedDsl::internal_into_boxed(self)
151 }
152}
153
154impl<T, U, V, Ret, Predicate> FilterDsl<Predicate> for UpdateStatement<T, U, V, Ret>
155where
156 T: QuerySource,
157 U: WhereAnd<Predicate>,
158 Predicate: AppearsOnTable<T>,
159{
160 type Output = UpdateStatement<T, U::Output, V, Ret>;
161
162 fn filter(self, predicate: Predicate) -> Self::Output {
163 UpdateStatement {
164 from_clause: self.from_clause,
165 where_clause: self.where_clause.and(predicate),
166 values: self.values,
167 returning: self.returning,
168 }
169 }
170}
171
172impl<'a, T, U, V, Ret, DB> BoxedDsl<'a, DB> for UpdateStatement<T, U, V, Ret>
173where
174 T: QuerySource,
175 U: Into<BoxedWhereClause<'a, DB>>,
176{
177 type Output = BoxedUpdateStatement<'a, DB, T, V, Ret>;
178
179 fn internal_into_boxed(self) -> Self::Output {
180 UpdateStatement {
181 from_clause: self.from_clause,
182 where_clause: self.where_clause.into(),
183 values: self.values,
184 returning: self.returning,
185 }
186 }
187}
188
189impl<T, U, V, Ret, DB> QueryFragment<DB> for UpdateStatement<T, U, V, Ret>
190where
191 DB: Backend + DieselReserveSpecialization,
192 T: Table,
193 T::FromClause: QueryFragment<DB>,
194 U: QueryFragment<DB>,
195 V: QueryFragment<DB>,
196 Ret: QueryFragment<DB>,
197{
198 fn walk_ast<'b>(&'b self, mut out: AstPass<'_, 'b, DB>) -> QueryResult<()> {
199 if self.values.is_noop(out.backend())? {
200 return Err(QueryBuilderError(Box::new(EmptyChangeset)));
201 }
202
203 out.unsafe_to_cache_prepared();
204 out.push_sql("UPDATE ");
205 self.from_clause.walk_ast(out.reborrow())?;
206 out.push_sql(" SET ");
207 self.values.walk_ast(out.reborrow())?;
208 self.where_clause.walk_ast(out.reborrow())?;
209 self.returning.walk_ast(out.reborrow())?;
210 Ok(())
211 }
212}
213
214impl<T, U, V, Ret> QueryId for UpdateStatement<T, U, V, Ret>
215where
216 T: QuerySource,
217{
218 type QueryId = ();
219
220 const HAS_STATIC_QUERY_ID: bool = false;
221}
222
223impl<T, U, V> AsQuery for UpdateStatement<T, U, V, NoReturningClause>
224where
225 T: Table,
226 UpdateStatement<T, U, V, ReturningClause<T::AllColumns>>: Query,
227 T::AllColumns: ValidGrouping<()>,
228 <T::AllColumns as ValidGrouping<()>>::IsAggregate:
229 MixedAggregates<is_aggregate::No, Output = is_aggregate::No>,
230{
231 type SqlType = <Self::Query as Query>::SqlType;
232 type Query = UpdateStatement<T, U, V, ReturningClause<T::AllColumns>>;
233
234 fn as_query(self) -> Self::Query {
235 self.returning(T::all_columns())
236 }
237}
238
239impl<T, U, V, Ret> Query for UpdateStatement<T, U, V, ReturningClause<Ret>>
240where
241 T: Table,
242 Ret: Expression + SelectableExpression<T> + ValidGrouping<()>,
243 Ret::IsAggregate: MixedAggregates<is_aggregate::No, Output = is_aggregate::No>,
244{
245 type SqlType = Ret::SqlType;
246}
247
248impl<T: QuerySource, U, V, Ret, Conn> RunQueryDsl<Conn> for UpdateStatement<T, U, V, Ret> {}
249
250impl<T: QuerySource, U, V> UpdateStatement<T, U, V, NoReturningClause> {
251 pub fn returning<E>(self, returns: E) -> UpdateStatement<T, U, V, ReturningClause<E>>
273 where
274 T: Table,
275 UpdateStatement<T, U, V, ReturningClause<E>>: Query,
276 {
277 UpdateStatement {
278 from_clause: self.from_clause,
279 where_clause: self.where_clause,
280 values: self.values,
281 returning: ReturningClause(returns),
282 }
283 }
284}
285
286#[derive(Debug, Clone, Copy)]
288pub struct SetNotCalled;
289
290mod private {
291 #[allow(unreachable_pub)]
293 pub trait UpdateAutoTypeHelper {
294 type Table;
295 type Where;
296 }
297
298 impl<T, W> UpdateAutoTypeHelper for crate::query_builder::UpdateStatement<T, W>
299 where
300 T: crate::QuerySource,
301 {
302 type Table = T;
303 type Where = W;
304 }
305}