1use crate::{
2 ActiveModelTrait, ActiveValue, ColumnTrait, DbErr, EntityTrait, Iterable, PrimaryKeyToColumn,
3 QueryFilter, QueryTrait,
4};
5use core::marker::PhantomData;
6use sea_query::{Expr, IntoIden, SimpleExpr, UpdateStatement};
7
8#[derive(Clone, Debug)]
13pub struct Update;
14
15#[derive(Clone, Debug)]
24pub struct UpdateOne<A: ActiveModelTrait>(pub(crate) Result<ValidatedUpdateOne<A>, DbErr>);
25
26#[derive(Clone, Debug)]
29pub struct ValidatedUpdateOne<A: ActiveModelTrait> {
30 pub(crate) query: UpdateStatement,
31 pub(crate) model: A,
32}
33
34impl<A: ActiveModelTrait> TryFrom<UpdateOne<A>> for ValidatedUpdateOne<A> {
35 type Error = DbErr;
36
37 fn try_from(value: UpdateOne<A>) -> Result<Self, Self::Error> {
38 value.0
39 }
40}
41
42impl<A: ActiveModelTrait> UpdateOne<A> {
43 pub fn validate(self) -> Result<ValidatedUpdateOne<A>, DbErr> {
45 self.try_into()
46 }
47}
48
49#[derive(Clone, Debug)]
54pub struct UpdateMany<E>
55where
56 E: EntityTrait,
57{
58 pub(crate) query: UpdateStatement,
59 pub(crate) entity: PhantomData<E>,
60}
61
62impl Update {
63 pub fn one<E, A>(model: A) -> UpdateOne<A>
85 where
86 E: EntityTrait,
87 A: ActiveModelTrait<Entity = E>,
88 {
89 let mut myself = ValidatedUpdateOne {
90 query: UpdateStatement::new()
91 .table(A::Entity::default().table_ref())
92 .to_owned(),
93 model,
94 };
95 for key in <A::Entity as EntityTrait>::PrimaryKey::iter() {
97 let col = key.into_column();
98 match myself.model.get(col) {
99 ActiveValue::Set(value) | ActiveValue::Unchanged(value) => {
100 myself = myself.filter(col.eq(value));
101 }
102 ActiveValue::NotSet => {
103 return UpdateOne(Err(DbErr::PrimaryKeyNotSet { ctx: "UpdateOne" }));
104 }
105 }
106 }
107 for col in <A::Entity as EntityTrait>::Column::iter() {
109 if <A::Entity as EntityTrait>::PrimaryKey::from_column(col).is_some() {
110 continue;
111 }
112 match myself.model.get(col) {
113 ActiveValue::Set(value) => {
114 let expr = col.save_as(Expr::val(value));
115 myself.query.value(col, expr);
116 }
117 ActiveValue::Unchanged(_) | ActiveValue::NotSet => {}
118 }
119 }
120 UpdateOne(Ok(myself))
121 }
122
123 pub fn many<E>(entity: E) -> UpdateMany<E>
138 where
139 E: EntityTrait,
140 {
141 UpdateMany {
142 query: UpdateStatement::new().table(entity.table_ref()).to_owned(),
143 entity: PhantomData,
144 }
145 }
146}
147
148impl<A> QueryFilter for ValidatedUpdateOne<A>
149where
150 A: ActiveModelTrait,
151{
152 type QueryStatement = UpdateStatement;
153
154 fn query(&mut self) -> &mut UpdateStatement {
155 &mut self.query
156 }
157}
158
159impl<E> QueryFilter for UpdateMany<E>
160where
161 E: EntityTrait,
162{
163 type QueryStatement = UpdateStatement;
164
165 fn query(&mut self) -> &mut UpdateStatement {
166 &mut self.query
167 }
168}
169
170impl<A> QueryTrait for ValidatedUpdateOne<A>
171where
172 A: ActiveModelTrait,
173{
174 type QueryStatement = UpdateStatement;
175
176 fn query(&mut self) -> &mut UpdateStatement {
177 &mut self.query
178 }
179
180 fn as_query(&self) -> &UpdateStatement {
181 &self.query
182 }
183
184 fn into_query(self) -> UpdateStatement {
185 self.query
186 }
187}
188
189impl<E> QueryTrait for UpdateMany<E>
190where
191 E: EntityTrait,
192{
193 type QueryStatement = UpdateStatement;
194
195 fn query(&mut self) -> &mut UpdateStatement {
196 &mut self.query
197 }
198
199 fn as_query(&self) -> &UpdateStatement {
200 &self.query
201 }
202
203 fn into_query(self) -> UpdateStatement {
204 self.query
205 }
206}
207
208impl<E> UpdateMany<E>
209where
210 E: EntityTrait,
211{
212 pub fn set<A>(mut self, model: A) -> Self
214 where
215 A: ActiveModelTrait<Entity = E>,
216 {
217 for col in E::Column::iter() {
218 match model.get(col) {
219 ActiveValue::Set(value) => {
220 let expr = col.save_as(Expr::val(value));
221 self.query.value(col, expr);
222 }
223 ActiveValue::Unchanged(_) | ActiveValue::NotSet => {}
224 }
225 }
226 self
227 }
228
229 pub fn col_expr<T>(mut self, col: T, expr: SimpleExpr) -> Self
231 where
232 T: IntoIden,
233 {
234 self.query.value(col, expr);
235 self
236 }
237}
238
239#[cfg(test)]
240mod tests {
241 use crate::tests_cfg::{cake, fruit, lunch_set, sea_orm_active_enums::Tea};
242 use crate::{DbBackend, entity::*, query::*};
243 use sea_query::{Expr, Value};
244
245 #[test]
246 fn update_1() {
247 assert_eq!(
248 Update::one(cake::ActiveModel {
249 id: ActiveValue::set(1),
250 name: ActiveValue::set("Apple Pie".to_owned()),
251 })
252 .validate()
253 .unwrap()
254 .build(DbBackend::Postgres)
255 .to_string(),
256 r#"UPDATE "cake" SET "name" = 'Apple Pie' WHERE "cake"."id" = 1"#,
257 );
258 }
259
260 #[test]
261 fn update_2() {
262 assert_eq!(
263 Update::one(fruit::ActiveModel {
264 id: ActiveValue::set(1),
265 name: ActiveValue::set("Orange".to_owned()),
266 cake_id: ActiveValue::not_set(),
267 })
268 .validate()
269 .unwrap()
270 .build(DbBackend::Postgres)
271 .to_string(),
272 r#"UPDATE "fruit" SET "name" = 'Orange' WHERE "fruit"."id" = 1"#,
273 );
274 }
275
276 #[test]
277 fn update_3() {
278 assert_eq!(
279 Update::one(fruit::ActiveModel {
280 id: ActiveValue::set(2),
281 name: ActiveValue::unchanged("Apple".to_owned()),
282 cake_id: ActiveValue::set(Some(3)),
283 })
284 .validate()
285 .unwrap()
286 .build(DbBackend::Postgres)
287 .to_string(),
288 r#"UPDATE "fruit" SET "cake_id" = 3 WHERE "fruit"."id" = 2"#,
289 );
290 }
291
292 #[test]
293 fn update_4() {
294 assert_eq!(
295 Update::many(fruit::Entity)
296 .col_expr(fruit::Column::CakeId, Expr::value(Value::Int(None)))
297 .filter(fruit::Column::Id.eq(2))
298 .build(DbBackend::Postgres)
299 .to_string(),
300 r#"UPDATE "fruit" SET "cake_id" = NULL WHERE "fruit"."id" = 2"#,
301 );
302 }
303
304 #[test]
305 fn update_5() {
306 assert_eq!(
307 Update::many(fruit::Entity)
308 .set(fruit::ActiveModel {
309 name: ActiveValue::set("Apple".to_owned()),
310 cake_id: ActiveValue::set(Some(3)),
311 ..Default::default()
312 })
313 .filter(fruit::Column::Id.eq(2))
314 .build(DbBackend::Postgres)
315 .to_string(),
316 r#"UPDATE "fruit" SET "name" = 'Apple', "cake_id" = 3 WHERE "fruit"."id" = 2"#,
317 );
318 }
319
320 #[test]
321 fn update_6() {
322 assert_eq!(
323 Update::many(fruit::Entity)
324 .set(fruit::ActiveModel {
325 id: ActiveValue::set(3),
326 ..Default::default()
327 })
328 .filter(fruit::Column::Id.eq(2))
329 .build(DbBackend::Postgres)
330 .to_string(),
331 r#"UPDATE "fruit" SET "id" = 3 WHERE "fruit"."id" = 2"#,
332 );
333 }
334
335 #[test]
336 fn update_7() {
337 assert_eq!(
338 Update::many(lunch_set::Entity)
339 .set(lunch_set::ActiveModel {
340 tea: Set(Tea::EverydayTea),
341 ..Default::default()
342 })
343 .filter(lunch_set::Column::Tea.eq(Tea::BreakfastTea))
344 .build(DbBackend::Postgres)
345 .to_string(),
346 r#"UPDATE "lunch_set" SET "tea" = CAST('EverydayTea' AS "tea") WHERE "lunch_set"."tea" = (CAST('BreakfastTea' AS "tea"))"#,
347 );
348 }
349
350 #[test]
351 fn update_8() {
352 assert_eq!(
353 Update::one(lunch_set::ActiveModel {
354 id: Unchanged(1),
355 tea: Set(Tea::EverydayTea),
356 ..Default::default()
357 })
358 .validate()
359 .unwrap()
360 .build(DbBackend::Postgres)
361 .to_string(),
362 r#"UPDATE "lunch_set" SET "tea" = CAST('EverydayTea' AS "tea") WHERE "lunch_set"."id" = 1"#,
363 );
364 }
365}