diff --git a/sea-orm-macros/src/derives/primary_key.rs b/sea-orm-macros/src/derives/primary_key.rs index 8378323e..dec22da8 100644 --- a/sea-orm-macros/src/derives/primary_key.rs +++ b/sea-orm-macros/src/derives/primary_key.rs @@ -50,7 +50,14 @@ pub fn expand_derive_primary_key(ident: Ident, data: Data) -> syn::Result for #ident { fn into_column(self) -> ::Column { match self { - #(Self::#variant => Column::#variant),* + #(Self::#variant => Column::#variant,)* + } + } + + fn from_column(col: ::Column) -> Option { + match col { + #(Column::#variant => Some(Self::#variant),)* + _ => None, } } } diff --git a/src/entity/active_model.rs b/src/entity/active_model.rs index 714c8ca9..953f0384 100644 --- a/src/entity/active_model.rs +++ b/src/entity/active_model.rs @@ -78,6 +78,10 @@ where } } + pub fn is_unchanged(&self) -> bool { + matches!(self.state, ActiveValueState::Unchanged) + } + pub fn unset() -> Self { Self { value: V::default(), diff --git a/src/entity/base.rs b/src/entity/base.rs index 48583362..4b51f7ed 100644 --- a/src/entity/base.rs +++ b/src/entity/base.rs @@ -1,7 +1,7 @@ use crate::{ - ActiveModelTrait, ColumnTrait, FromQueryResult, Insert, ModelTrait, - OneOrManyActiveModel, PrimaryKeyToColumn, PrimaryKeyTrait, QueryFilter, RelationBuilder, - RelationTrait, RelationType, Select, + ActiveModelTrait, ColumnTrait, FromQueryResult, Insert, ModelTrait, OneOrManyActiveModel, + PrimaryKeyToColumn, PrimaryKeyTrait, QueryFilter, RelationBuilder, RelationTrait, RelationType, + Select, }; use sea_query::{Iden, IntoValueTuple}; use std::fmt::Debug; diff --git a/src/entity/prelude.rs b/src/entity/prelude.rs index 6e26318e..1edc6508 100644 --- a/src/entity/prelude.rs +++ b/src/entity/prelude.rs @@ -1,6 +1,6 @@ pub use crate::{ - ActiveModelTrait, ColumnTrait, ColumnType, DeriveActiveModel, DeriveColumn, - DeriveEntity, DeriveModel, DerivePrimaryKey, EntityName, EntityTrait, EnumIter, Iden, - IdenStatic, ModelTrait, PrimaryKeyToColumn, PrimaryKeyTrait, QueryFilter, QueryResult, Related, - RelationDef, RelationTrait, Select, TypeErr, Value, + ActiveModelTrait, ColumnTrait, ColumnType, DeriveActiveModel, DeriveColumn, DeriveEntity, + DeriveModel, DerivePrimaryKey, EntityName, EntityTrait, EnumIter, Iden, IdenStatic, ModelTrait, + PrimaryKeyToColumn, PrimaryKeyTrait, QueryFilter, QueryResult, Related, RelationDef, + RelationTrait, Select, TypeErr, Value, }; diff --git a/src/entity/primary_key.rs b/src/entity/primary_key.rs index 0ea5ee3f..7f004b40 100644 --- a/src/entity/primary_key.rs +++ b/src/entity/primary_key.rs @@ -7,4 +7,6 @@ where E: EntityTrait, { fn into_column(self) -> E::Column; + + fn from_column(col: E::Column) -> Option where Self: std::marker::Sized; } diff --git a/src/query/insert.rs b/src/query/insert.rs index 5051a1fc..302775ac 100644 --- a/src/query/insert.rs +++ b/src/query/insert.rs @@ -14,7 +14,7 @@ where impl Default for Insert where - A: ActiveModelTrait + A: ActiveModelTrait, { fn default() -> Self { Self::new() @@ -50,7 +50,7 @@ where } else if self.columns[idx] != av.is_set() { panic!("columns mismatch"); } - if !av.is_unset() { + if av.is_set() { columns.push(col); values.push(av.into_value()); } diff --git a/src/query/mod.rs b/src/query/mod.rs index 6c0c7e82..f064dab1 100644 --- a/src/query/mod.rs +++ b/src/query/mod.rs @@ -7,7 +7,7 @@ mod json; mod result; mod select; mod traits; -// mod update; +mod update; // pub use combine::*; pub use helper::*; @@ -18,4 +18,4 @@ pub use json::*; pub use result::*; pub use select::*; pub use traits::*; -// pub use update::*; +pub use update::*; diff --git a/src/query/update.rs b/src/query/update.rs new file mode 100644 index 00000000..e9719131 --- /dev/null +++ b/src/query/update.rs @@ -0,0 +1,132 @@ +use crate::{ + ActiveModelTrait, ColumnTrait, EntityTrait, Iterable, PrimaryKeyToColumn, QueryFilter, + QueryTrait, +}; +use sea_query::{IntoIden, UpdateStatement}; + +#[derive(Clone, Debug)] +pub struct Update +where + A: ActiveModelTrait, +{ + pub(crate) query: UpdateStatement, + pub(crate) model: A, +} + +impl Update +where + A: ActiveModelTrait, +{ + pub fn new(model: M) -> Self + where + M: Into, + { + let myself = Self { + query: UpdateStatement::new() + .table(A::Entity::default().into_iden()) + .to_owned(), + model: model.into(), + }; + myself.prepare() + } + + pub(crate) fn prepare(mut self) -> Self { + for key in ::PrimaryKey::iter() { + let col = key.into_column(); + let av = self.model.get(col); + if av.is_set() || av.is_unchanged() { + self = self.filter(col.eq(av.unwrap())); + } else { + panic!("PrimaryKey is not set"); + } + } + for col in ::Column::iter() { + if ::PrimaryKey::from_column(col).is_some() { + continue; + } + let av = self.model.get(col); + if av.is_set() { + self.query.value(col, av.unwrap()); + } + } + self + } +} + +impl QueryFilter for Update +where + A: ActiveModelTrait, +{ + type QueryStatement = UpdateStatement; + + fn query(&mut self) -> &mut UpdateStatement { + &mut self.query + } +} + +impl QueryTrait for Update +where + A: ActiveModelTrait, +{ + type QueryStatement = UpdateStatement; + + fn query(&mut self) -> &mut UpdateStatement { + &mut self.query + } + + fn as_query(&self) -> &UpdateStatement { + &self.query + } + + fn into_query(self) -> UpdateStatement { + self.query + } +} + +#[cfg(test)] +mod tests { + use crate::tests_cfg::{cake, fruit}; + use crate::{Update, QueryTrait, Val}; + use sea_query::PostgresQueryBuilder; + + #[test] + fn update_1() { + assert_eq!( + Update::::new(cake::ActiveModel { + id: Val::set(1), + name: Val::set("Apple Pie".to_owned()), + }) + .build(PostgresQueryBuilder) + .to_string(), + r#"UPDATE "cake" SET "name" = 'Apple Pie' WHERE "cake"."id" = 1"#, + ); + } + + #[test] + fn update_2() { + assert_eq!( + Update::::new(fruit::ActiveModel { + id: Val::set(1), + name: Val::set("Orange".to_owned()), + cake_id: Val::unset(), + }) + .build(PostgresQueryBuilder) + .to_string(), + r#"UPDATE "fruit" SET "name" = 'Orange' WHERE "fruit"."id" = 1"#, + ); + } + + #[test] + fn update_3() { + assert_eq!( + Update::::new(fruit::ActiveModel { + id: Val::set(2), + name: Val::unset(), + cake_id: Val::set(Some(3)), + }) + .build(PostgresQueryBuilder) + .to_string(), + r#"UPDATE "fruit" SET "cake_id" = 3 WHERE "fruit"."id" = 2"#, + ); + } +} \ No newline at end of file