sea_orm/query/
update.rs

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/// Defines a structure to perform UPDATE query operations on a ActiveModel
9#[derive(Clone, Debug)]
10pub struct Update;
11
12/// Defines an UPDATE operation on one ActiveModel
13#[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/// Defines an UPDATE operation on multiple ActiveModels
23#[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    /// Update one ActiveModel
34    ///
35    /// ```
36    /// use sea_orm::{entity::*, query::*, tests_cfg::cake, DbBackend};
37    ///
38    /// assert_eq!(
39    ///     Update::one(cake::ActiveModel {
40    ///         id: ActiveValue::set(1),
41    ///         name: ActiveValue::set("Apple Pie".to_owned()),
42    ///     })
43    ///     .build(DbBackend::Postgres)
44    ///     .to_string(),
45    ///     r#"UPDATE "cake" SET "name" = 'Apple Pie' WHERE "cake"."id" = 1"#,
46    /// );
47    /// ```
48    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    /// Update many ActiveModel
64    ///
65    /// ```
66    /// use sea_orm::{entity::*, query::*, sea_query::Expr, tests_cfg::fruit, DbBackend};
67    ///
68    /// assert_eq!(
69    ///     Update::many(fruit::Entity)
70    ///         .col_expr(fruit::Column::Name, Expr::value("Golden Apple"))
71    ///         .filter(fruit::Column::Name.contains("Apple"))
72    ///         .build(DbBackend::Postgres)
73    ///         .to_string(),
74    ///     r#"UPDATE "fruit" SET "name" = 'Golden Apple' WHERE "fruit"."name" LIKE '%Apple%'"#,
75    /// );
76    /// ```
77    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    /// Add the models to update to Self
187    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    /// Creates a [SimpleExpr] from a column
204    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}