1use crate::{
2 ActiveModelTrait, ActiveValue, ColumnTrait, 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)]
14pub struct UpdateOne<A>
15where
16 A: ActiveModelTrait,
17{
18 pub(crate) query: UpdateStatement,
19 pub(crate) model: A,
20}
21
22#[derive(Clone, Debug)]
24pub struct UpdateMany<E>
25where
26 E: EntityTrait,
27{
28 pub(crate) query: UpdateStatement,
29 pub(crate) entity: PhantomData<E>,
30}
31
32impl Update {
33 pub fn one<E, A>(model: A) -> UpdateOne<A>
49 where
50 E: EntityTrait,
51 A: ActiveModelTrait<Entity = E>,
52 {
53 UpdateOne {
54 query: UpdateStatement::new()
55 .table(A::Entity::default().table_ref())
56 .to_owned(),
57 model,
58 }
59 .prepare_filters()
60 .prepare_values()
61 }
62
63 pub fn many<E>(entity: E) -> UpdateMany<E>
78 where
79 E: EntityTrait,
80 {
81 UpdateMany {
82 query: UpdateStatement::new().table(entity.table_ref()).to_owned(),
83 entity: PhantomData,
84 }
85 }
86}
87
88impl<A> UpdateOne<A>
89where
90 A: ActiveModelTrait,
91{
92 fn prepare_filters(mut self) -> Self {
93 for key in <A::Entity as EntityTrait>::PrimaryKey::iter() {
94 let col = key.into_column();
95 match self.model.get(col) {
96 ActiveValue::Set(value) | ActiveValue::Unchanged(value) => {
97 self = self.filter(col.eq(value));
98 }
99 ActiveValue::NotSet => panic!("PrimaryKey is not set"),
100 }
101 }
102 self
103 }
104
105 fn prepare_values(mut self) -> Self {
106 for col in <A::Entity as EntityTrait>::Column::iter() {
107 if <A::Entity as EntityTrait>::PrimaryKey::from_column(col).is_some() {
108 continue;
109 }
110 match self.model.get(col) {
111 ActiveValue::Set(value) => {
112 let expr = col.save_as(Expr::val(value));
113 self.query.value(col, expr);
114 }
115 ActiveValue::Unchanged(_) | ActiveValue::NotSet => {}
116 }
117 }
118 self
119 }
120}
121
122impl<A> QueryFilter for UpdateOne<A>
123where
124 A: ActiveModelTrait,
125{
126 type QueryStatement = UpdateStatement;
127
128 fn query(&mut self) -> &mut UpdateStatement {
129 &mut self.query
130 }
131}
132
133impl<E> QueryFilter for UpdateMany<E>
134where
135 E: EntityTrait,
136{
137 type QueryStatement = UpdateStatement;
138
139 fn query(&mut self) -> &mut UpdateStatement {
140 &mut self.query
141 }
142}
143
144impl<A> QueryTrait for UpdateOne<A>
145where
146 A: ActiveModelTrait,
147{
148 type QueryStatement = UpdateStatement;
149
150 fn query(&mut self) -> &mut UpdateStatement {
151 &mut self.query
152 }
153
154 fn as_query(&self) -> &UpdateStatement {
155 &self.query
156 }
157
158 fn into_query(self) -> UpdateStatement {
159 self.query
160 }
161}
162
163impl<E> QueryTrait for UpdateMany<E>
164where
165 E: EntityTrait,
166{
167 type QueryStatement = UpdateStatement;
168
169 fn query(&mut self) -> &mut UpdateStatement {
170 &mut self.query
171 }
172
173 fn as_query(&self) -> &UpdateStatement {
174 &self.query
175 }
176
177 fn into_query(self) -> UpdateStatement {
178 self.query
179 }
180}
181
182impl<E> UpdateMany<E>
183where
184 E: EntityTrait,
185{
186 pub fn set<A>(mut self, model: A) -> Self
188 where
189 A: ActiveModelTrait<Entity = E>,
190 {
191 for col in E::Column::iter() {
192 match model.get(col) {
193 ActiveValue::Set(value) => {
194 let expr = col.save_as(Expr::val(value));
195 self.query.value(col, expr);
196 }
197 ActiveValue::Unchanged(_) | ActiveValue::NotSet => {}
198 }
199 }
200 self
201 }
202
203 pub fn col_expr<T>(mut self, col: T, expr: SimpleExpr) -> Self
205 where
206 T: IntoIden,
207 {
208 self.query.value(col, expr);
209 self
210 }
211}
212
213#[cfg(test)]
214mod tests {
215 use crate::tests_cfg::{cake, fruit, lunch_set, sea_orm_active_enums::Tea};
216 use crate::{entity::*, query::*, DbBackend};
217 use sea_query::{Expr, Value};
218
219 #[test]
220 fn update_1() {
221 assert_eq!(
222 Update::one(cake::ActiveModel {
223 id: ActiveValue::set(1),
224 name: ActiveValue::set("Apple Pie".to_owned()),
225 })
226 .build(DbBackend::Postgres)
227 .to_string(),
228 r#"UPDATE "cake" SET "name" = 'Apple Pie' WHERE "cake"."id" = 1"#,
229 );
230 }
231
232 #[test]
233 fn update_2() {
234 assert_eq!(
235 Update::one(fruit::ActiveModel {
236 id: ActiveValue::set(1),
237 name: ActiveValue::set("Orange".to_owned()),
238 cake_id: ActiveValue::not_set(),
239 })
240 .build(DbBackend::Postgres)
241 .to_string(),
242 r#"UPDATE "fruit" SET "name" = 'Orange' WHERE "fruit"."id" = 1"#,
243 );
244 }
245
246 #[test]
247 fn update_3() {
248 assert_eq!(
249 Update::one(fruit::ActiveModel {
250 id: ActiveValue::set(2),
251 name: ActiveValue::unchanged("Apple".to_owned()),
252 cake_id: ActiveValue::set(Some(3)),
253 })
254 .build(DbBackend::Postgres)
255 .to_string(),
256 r#"UPDATE "fruit" SET "cake_id" = 3 WHERE "fruit"."id" = 2"#,
257 );
258 }
259
260 #[test]
261 fn update_4() {
262 assert_eq!(
263 Update::many(fruit::Entity)
264 .col_expr(fruit::Column::CakeId, Expr::value(Value::Int(None)))
265 .filter(fruit::Column::Id.eq(2))
266 .build(DbBackend::Postgres)
267 .to_string(),
268 r#"UPDATE "fruit" SET "cake_id" = NULL WHERE "fruit"."id" = 2"#,
269 );
270 }
271
272 #[test]
273 fn update_5() {
274 assert_eq!(
275 Update::many(fruit::Entity)
276 .set(fruit::ActiveModel {
277 name: ActiveValue::set("Apple".to_owned()),
278 cake_id: ActiveValue::set(Some(3)),
279 ..Default::default()
280 })
281 .filter(fruit::Column::Id.eq(2))
282 .build(DbBackend::Postgres)
283 .to_string(),
284 r#"UPDATE "fruit" SET "name" = 'Apple', "cake_id" = 3 WHERE "fruit"."id" = 2"#,
285 );
286 }
287
288 #[test]
289 fn update_6() {
290 assert_eq!(
291 Update::many(fruit::Entity)
292 .set(fruit::ActiveModel {
293 id: ActiveValue::set(3),
294 ..Default::default()
295 })
296 .filter(fruit::Column::Id.eq(2))
297 .build(DbBackend::Postgres)
298 .to_string(),
299 r#"UPDATE "fruit" SET "id" = 3 WHERE "fruit"."id" = 2"#,
300 );
301 }
302
303 #[test]
304 fn update_7() {
305 assert_eq!(
306 Update::many(lunch_set::Entity)
307 .set(lunch_set::ActiveModel {
308 tea: Set(Tea::EverydayTea),
309 ..Default::default()
310 })
311 .filter(lunch_set::Column::Tea.eq(Tea::BreakfastTea))
312 .build(DbBackend::Postgres)
313 .to_string(),
314 r#"UPDATE "lunch_set" SET "tea" = CAST('EverydayTea' AS tea) WHERE "lunch_set"."tea" = (CAST('BreakfastTea' AS tea))"#,
315 );
316 }
317
318 #[test]
319 fn update_8() {
320 assert_eq!(
321 Update::one(lunch_set::ActiveModel {
322 id: Unchanged(1),
323 tea: Set(Tea::EverydayTea),
324 ..Default::default()
325 })
326 .build(DbBackend::Postgres)
327 .to_string(),
328 r#"UPDATE "lunch_set" SET "tea" = CAST('EverydayTea' AS tea) WHERE "lunch_set"."id" = 1"#,
329 );
330 }
331}