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