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