From 4289fb4cad4e82ec9680dd65f1390c6edb93e4f2 Mon Sep 17 00:00:00 2001 From: Thomas Kosel Date: Fri, 25 Nov 2022 21:11:16 +0100 Subject: [PATCH 1/5] extracting get_column_def from create_table_from_entity to make it available for database upgrade processes. --- src/schema/entity.rs | 116 +++++++++++++++++++++++++++++++------------ 1 file changed, 83 insertions(+), 33 deletions(-) diff --git a/src/schema/entity.rs b/src/schema/entity.rs index 28bca9d5a..24f788cbd 100644 --- a/src/schema/entity.rs +++ b/src/schema/entity.rs @@ -40,6 +40,48 @@ impl Schema { { create_index_from_entity(entity, self.backend) } + + /// Creates a column definition for example to update a table. + /// ``` + /// use sea_query::{MysqlQueryBuilder, TableAlterStatement}; + /// use sea_orm::{ActiveModelBehavior, ColumnDef, ColumnTrait, ColumnType, DbBackend, + /// EntityName, EntityTrait, EnumIter, PrimaryKeyTrait, RelationDef, RelationTrait, Schema}; + /// use sea_orm_macros::{DeriveEntityModel, DerivePrimaryKey}; + /// use crate::sea_orm::IdenStatic; + /// + /// #[derive(Clone, Debug, PartialEq, DeriveEntityModel)] + /// #[sea_orm(table_name = "posts")] + /// pub struct Model { + /// #[sea_orm(primary_key)] + /// pub id: u32, + /// pub title: String, + /// } + /// + /// #[derive(Copy, Clone, Debug, EnumIter)] + /// pub enum Relation {} + /// impl RelationTrait for Relation { + /// fn def(&self) -> RelationDef { + /// panic!("No RelationDef") + /// } + /// } + /// impl ActiveModelBehavior for ActiveModel {} + /// + /// let schema = Schema::new(DbBackend::MySql); + /// + /// let mut alter_table = TableAlterStatement::new() + /// .table(Entity) + /// .add_column(&mut schema.get_column_def::(Column::Title)).take(); + /// assert_eq!( + /// alter_table.to_string(MysqlQueryBuilder::default()), + /// "ALTER TABLE `posts` ADD COLUMN `title` varchar(255) NOT NULL" + /// ); + /// ``` + pub fn get_column_def(&self, column: E::Column) -> ColumnDef + where + E: EntityTrait, + { + column_def_from_entity_column::(column, self.backend) + } } pub(crate) fn create_enum_from_active_enum(backend: DbBackend) -> TypeCreateStatement @@ -117,39 +159,7 @@ where let mut stmt = TableCreateStatement::new(); for column in E::Column::iter() { - let orm_column_def = column.def(); - let types = match orm_column_def.col_type { - ColumnType::Enum { name, variants } => match backend { - DbBackend::MySql => { - let variants: Vec = variants.iter().map(|v| v.to_string()).collect(); - ColumnType::Custom(format!("ENUM('{}')", variants.join("', '"))) - } - DbBackend::Postgres => ColumnType::Custom(name.to_string()), - DbBackend::Sqlite => ColumnType::Text, - } - .into(), - _ => orm_column_def.col_type.into(), - }; - let mut column_def = ColumnDef::new_with_type(column, types); - if !orm_column_def.null { - column_def.not_null(); - } - if orm_column_def.unique { - column_def.unique_key(); - } - if let Some(value) = orm_column_def.default_value { - column_def.default(value); - } - for primary_key in E::PrimaryKey::iter() { - if column.to_string() == primary_key.into_column().to_string() { - if E::PrimaryKey::auto_increment() { - column_def.auto_increment(); - } - if E::PrimaryKey::iter().count() == 1 { - column_def.primary_key(); - } - } - } + let mut column_def = column_def_from_entity_column::(column, backend); stmt.col(&mut column_def); } @@ -217,6 +227,46 @@ where stmt.table(entity.table_ref()).take() } +fn column_def_from_entity_column(column: E::Column, backend: DbBackend) -> ColumnDef +where + E: EntityTrait, +{ + let orm_column_def = column.def(); + let types = match orm_column_def.col_type { + ColumnType::Enum { name, variants } => match backend { + DbBackend::MySql => { + let variants: Vec = variants.iter().map(|v| v.to_string()).collect(); + ColumnType::Custom(format!("ENUM('{}')", variants.join("', '"))) + } + DbBackend::Postgres => ColumnType::Custom(name.to_string()), + DbBackend::Sqlite => ColumnType::Text, + } + .into(), + _ => orm_column_def.col_type.into(), + }; + let mut column_def = ColumnDef::new_with_type(column, types); + if !orm_column_def.null { + column_def.not_null(); + } + if orm_column_def.unique { + column_def.unique_key(); + } + if let Some(value) = orm_column_def.default_value { + column_def.default(value); + } + for primary_key in E::PrimaryKey::iter() { + if column.to_string() == primary_key.into_column().to_string() { + if E::PrimaryKey::auto_increment() { + column_def.auto_increment(); + } + if E::PrimaryKey::iter().count() == 1 { + column_def.primary_key(); + } + } + } + column_def +} + #[cfg(test)] mod tests { use crate::{sea_query::*, tests_cfg::*, DbBackend, EntityName, Schema}; From a4b0c39ac723aa8152ec7dc35c49b1e13c5c94d0 Mon Sep 17 00:00:00 2001 From: mohs8421 Date: Mon, 28 Nov 2022 18:46:57 +0100 Subject: [PATCH 2/5] Align code example formatting --- src/schema/entity.rs | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/schema/entity.rs b/src/schema/entity.rs index 24f788cbd..cbab22715 100644 --- a/src/schema/entity.rs +++ b/src/schema/entity.rs @@ -43,11 +43,13 @@ impl Schema { /// Creates a column definition for example to update a table. /// ``` - /// use sea_query::{MysqlQueryBuilder, TableAlterStatement}; - /// use sea_orm::{ActiveModelBehavior, ColumnDef, ColumnTrait, ColumnType, DbBackend, - /// EntityName, EntityTrait, EnumIter, PrimaryKeyTrait, RelationDef, RelationTrait, Schema}; - /// use sea_orm_macros::{DeriveEntityModel, DerivePrimaryKey}; /// use crate::sea_orm::IdenStatic; + /// use sea_orm::{ + /// ActiveModelBehavior, ColumnDef, ColumnTrait, ColumnType, DbBackend, EntityName, + /// EntityTrait, EnumIter, PrimaryKeyTrait, RelationDef, RelationTrait, Schema, + /// }; + /// use sea_orm_macros::{DeriveEntityModel, DerivePrimaryKey}; + /// use sea_query::{MysqlQueryBuilder, TableAlterStatement}; /// /// #[derive(Clone, Debug, PartialEq, DeriveEntityModel)] /// #[sea_orm(table_name = "posts")] @@ -70,7 +72,8 @@ impl Schema { /// /// let mut alter_table = TableAlterStatement::new() /// .table(Entity) - /// .add_column(&mut schema.get_column_def::(Column::Title)).take(); + /// .add_column(&mut schema.get_column_def::(Column::Title)) + /// .take(); /// assert_eq!( /// alter_table.to_string(MysqlQueryBuilder::default()), /// "ALTER TABLE `posts` ADD COLUMN `title` varchar(255) NOT NULL" From 45fe50f930067d860dad95d96325f5c4977eef06 Mon Sep 17 00:00:00 2001 From: Thomas Kosel Date: Wed, 30 Nov 2022 07:37:14 +0100 Subject: [PATCH 3/5] Converting the foreign key related code from create_table_from_entity into From implementations to make its usage easier in different context, like updating a database. --- src/entity/relation.rs | 132 ++++++++++++++++++++++++++++++++++++++++- src/schema/entity.rs | 53 ++--------------- 2 files changed, 134 insertions(+), 51 deletions(-) diff --git a/src/entity/relation.rs b/src/entity/relation.rs index 06e22a29d..08ba63b57 100644 --- a/src/entity/relation.rs +++ b/src/entity/relation.rs @@ -1,6 +1,9 @@ -use crate::{EntityTrait, Identity, IdentityOf, Iterable, QuerySelect, Select}; +use crate::{unpack_table_ref, EntityTrait, Identity, IdentityOf, Iterable, QuerySelect, Select}; use core::marker::PhantomData; -use sea_query::{Alias, Condition, DynIden, JoinType, SeaRc, TableRef}; +use sea_query::{ + Alias, Condition, DynIden, ForeignKeyCreateStatement, JoinType, SeaRc, TableForeignKey, + TableRef, +}; use std::fmt::Debug; /// Defines the type of relationship @@ -309,6 +312,131 @@ where } } +impl From for ForeignKeyCreateStatement { + fn from(relation: RelationDef) -> Self { + let mut foreign_key_stmt = Self::new(); + let from_tbl = unpack_table_ref(&relation.from_tbl); + let to_tbl = unpack_table_ref(&relation.to_tbl); + let from_cols: Vec = match relation.from_col { + Identity::Unary(o1) => vec![o1], + Identity::Binary(o1, o2) => vec![o1, o2], + Identity::Ternary(o1, o2, o3) => vec![o1, o2, o3], + } + .into_iter() + .map(|col| { + let col_name = col.to_string(); + foreign_key_stmt.from_col(col); + col_name + }) + .collect(); + match relation.to_col { + Identity::Unary(o1) => { + foreign_key_stmt.to_col(o1); + } + Identity::Binary(o1, o2) => { + foreign_key_stmt.to_col(o1); + foreign_key_stmt.to_col(o2); + } + Identity::Ternary(o1, o2, o3) => { + foreign_key_stmt.to_col(o1); + foreign_key_stmt.to_col(o2); + foreign_key_stmt.to_col(o3); + } + } + if let Some(action) = relation.on_delete { + foreign_key_stmt.on_delete(action); + } + if let Some(action) = relation.on_update { + foreign_key_stmt.on_update(action); + } + let name = if let Some(name) = relation.fk_name { + name + } else { + format!("fk-{}-{}", from_tbl.to_string(), from_cols.join("-")) + }; + foreign_key_stmt + .name(&name) + .from_tbl(from_tbl) + .to_tbl(to_tbl) + .take() + } +} + +/// Creates a column definition for example to update a table. +/// ``` +/// use sea_query::{Alias, IntoIden, MysqlQueryBuilder, TableAlterStatement, TableRef}; +/// use sea_orm::{EnumIter, Iden, Identity, PrimaryKeyTrait, RelationDef, RelationTrait, RelationType}; +/// +/// let relation = RelationDef { +/// rel_type: RelationType::HasOne, +/// from_tbl: TableRef::Table(Alias::new("foo").into_iden()), +/// to_tbl: TableRef::Table(Alias::new("bar").into_iden()), +/// from_col: Identity::Unary(Alias::new("bar_id").into_iden()), +/// to_col: Identity::Unary(Alias::new("bar_id").into_iden()), +/// is_owner: false, +/// on_delete: None, +/// on_update: None, +/// on_condition: None, +/// fk_name: Some("foo-bar".to_string()), +/// }; +/// +/// let mut alter_table = TableAlterStatement::new() +/// .table(TableRef::Table(Alias::new("foo").into_iden())) +/// .add_foreign_key(&mut relation.into()).take(); +/// assert_eq!( +/// alter_table.to_string(MysqlQueryBuilder::default()), +/// "ALTER TABLE `foo` ADD CONSTRAINT `foo-bar` FOREIGN KEY (`bar_id`) REFERENCES `bar` (`bar_id`)" +/// ); +/// ``` +impl From for TableForeignKey { + fn from(relation: RelationDef) -> Self { + let from_tbl = unpack_table_ref(&relation.from_tbl); + let mut foreign_key = Self::new() + .from_tbl(relation.from_tbl) + .to_tbl(relation.to_tbl) + .take(); + let from_cols: Vec = match relation.from_col { + Identity::Unary(o1) => vec![o1], + Identity::Binary(o1, o2) => vec![o1, o2], + Identity::Ternary(o1, o2, o3) => vec![o1, o2, o3], + } + .into_iter() + .map(|col| { + let col_name = col.to_string(); + foreign_key.from_col(col); + col_name + }) + .collect(); + match relation.to_col { + Identity::Unary(o1) => { + foreign_key.to_col(o1); + } + Identity::Binary(o1, o2) => { + foreign_key.to_col(o1); + foreign_key.to_col(o2); + } + Identity::Ternary(o1, o2, o3) => { + foreign_key.to_col(o1); + foreign_key.to_col(o2); + foreign_key.to_col(o3); + } + } + if let Some(action) = relation.on_delete { + foreign_key.on_delete(action); + } + if let Some(action) = relation.on_update { + foreign_key.on_update(action); + } + let name = if let Some(name) = relation.fk_name { + name + } else { + format!("fk-{}-{}", from_tbl.to_string(), from_cols.join("-")) + }; + foreign_key.name(&name); + foreign_key + } +} + #[cfg(test)] mod tests { use crate::{ diff --git a/src/schema/entity.rs b/src/schema/entity.rs index cbab22715..c1d23d834 100644 --- a/src/schema/entity.rs +++ b/src/schema/entity.rs @@ -1,10 +1,10 @@ use crate::{ - unpack_table_ref, ActiveEnum, ColumnTrait, ColumnType, DbBackend, EntityTrait, Identity, - Iterable, PrimaryKeyToColumn, PrimaryKeyTrait, RelationTrait, Schema, + ActiveEnum, ColumnTrait, ColumnType, DbBackend, EntityTrait, Iterable, PrimaryKeyToColumn, + PrimaryKeyTrait, RelationTrait, Schema, }; use sea_query::{ extension::postgres::{Type, TypeCreateStatement}, - ColumnDef, ForeignKeyCreateStatement, Iden, Index, IndexCreateStatement, TableCreateStatement, + ColumnDef, Iden, Index, IndexCreateStatement, TableCreateStatement, }; impl Schema { @@ -179,52 +179,7 @@ where if relation.is_owner { continue; } - let mut foreign_key_stmt = ForeignKeyCreateStatement::new(); - let from_tbl = unpack_table_ref(&relation.from_tbl); - let to_tbl = unpack_table_ref(&relation.to_tbl); - let from_cols: Vec = match relation.from_col { - Identity::Unary(o1) => vec![o1], - Identity::Binary(o1, o2) => vec![o1, o2], - Identity::Ternary(o1, o2, o3) => vec![o1, o2, o3], - } - .into_iter() - .map(|col| { - let col_name = col.to_string(); - foreign_key_stmt.from_col(col); - col_name - }) - .collect(); - match relation.to_col { - Identity::Unary(o1) => { - foreign_key_stmt.to_col(o1); - } - Identity::Binary(o1, o2) => { - foreign_key_stmt.to_col(o1); - foreign_key_stmt.to_col(o2); - } - Identity::Ternary(o1, o2, o3) => { - foreign_key_stmt.to_col(o1); - foreign_key_stmt.to_col(o2); - foreign_key_stmt.to_col(o3); - } - } - if let Some(action) = relation.on_delete { - foreign_key_stmt.on_delete(action); - } - if let Some(action) = relation.on_update { - foreign_key_stmt.on_update(action); - } - let name = if let Some(name) = relation.fk_name { - name - } else { - format!("fk-{}-{}", from_tbl.to_string(), from_cols.join("-")) - }; - stmt.foreign_key( - foreign_key_stmt - .name(&name) - .from_tbl(from_tbl) - .to_tbl(to_tbl), - ); + stmt.foreign_key(&mut relation.into()); } stmt.table(entity.table_ref()).take() From 0bbdf25d29c0d38551de16f2495bfb81ddfdcfed Mon Sep 17 00:00:00 2001 From: Billy Chan Date: Fri, 16 Dec 2022 23:33:37 +0800 Subject: [PATCH 4/5] Refactor --- src/entity/relation.rs | 96 ++++++++++++++---------------------------- 1 file changed, 31 insertions(+), 65 deletions(-) diff --git a/src/entity/relation.rs b/src/entity/relation.rs index 08ba63b57..519a89dc3 100644 --- a/src/entity/relation.rs +++ b/src/entity/relation.rs @@ -312,12 +312,9 @@ where } } -impl From for ForeignKeyCreateStatement { - fn from(relation: RelationDef) -> Self { - let mut foreign_key_stmt = Self::new(); - let from_tbl = unpack_table_ref(&relation.from_tbl); - let to_tbl = unpack_table_ref(&relation.to_tbl); - let from_cols: Vec = match relation.from_col { +macro_rules! set_foreign_key_stmt { + ( $relation: ident, $foreign_key: ident ) => { + let from_cols: Vec = match $relation.from_col { Identity::Unary(o1) => vec![o1], Identity::Binary(o1, o2) => vec![o1, o2], Identity::Ternary(o1, o2, o3) => vec![o1, o2, o3], @@ -325,39 +322,47 @@ impl From for ForeignKeyCreateStatement { .into_iter() .map(|col| { let col_name = col.to_string(); - foreign_key_stmt.from_col(col); + $foreign_key.from_col(col); col_name }) .collect(); - match relation.to_col { + match $relation.to_col { Identity::Unary(o1) => { - foreign_key_stmt.to_col(o1); + $foreign_key.to_col(o1); } Identity::Binary(o1, o2) => { - foreign_key_stmt.to_col(o1); - foreign_key_stmt.to_col(o2); + $foreign_key.to_col(o1); + $foreign_key.to_col(o2); } Identity::Ternary(o1, o2, o3) => { - foreign_key_stmt.to_col(o1); - foreign_key_stmt.to_col(o2); - foreign_key_stmt.to_col(o3); + $foreign_key.to_col(o1); + $foreign_key.to_col(o2); + $foreign_key.to_col(o3); } } - if let Some(action) = relation.on_delete { - foreign_key_stmt.on_delete(action); + if let Some(action) = $relation.on_delete { + $foreign_key.on_delete(action); } - if let Some(action) = relation.on_update { - foreign_key_stmt.on_update(action); + if let Some(action) = $relation.on_update { + $foreign_key.on_update(action); } - let name = if let Some(name) = relation.fk_name { + let name = if let Some(name) = $relation.fk_name { name } else { + let from_tbl = unpack_table_ref(&$relation.from_tbl); format!("fk-{}-{}", from_tbl.to_string(), from_cols.join("-")) }; + $foreign_key.name(&name); + }; +} + +impl From for ForeignKeyCreateStatement { + fn from(relation: RelationDef) -> Self { + let mut foreign_key_stmt = Self::new(); + set_foreign_key_stmt!(relation, foreign_key_stmt); foreign_key_stmt - .name(&name) - .from_tbl(from_tbl) - .to_tbl(to_tbl) + .from_tbl(unpack_table_ref(&relation.from_tbl)) + .to_tbl(unpack_table_ref(&relation.to_tbl)) .take() } } @@ -390,50 +395,11 @@ impl From for ForeignKeyCreateStatement { /// ``` impl From for TableForeignKey { fn from(relation: RelationDef) -> Self { - let from_tbl = unpack_table_ref(&relation.from_tbl); - let mut foreign_key = Self::new() - .from_tbl(relation.from_tbl) - .to_tbl(relation.to_tbl) - .take(); - let from_cols: Vec = match relation.from_col { - Identity::Unary(o1) => vec![o1], - Identity::Binary(o1, o2) => vec![o1, o2], - Identity::Ternary(o1, o2, o3) => vec![o1, o2, o3], - } - .into_iter() - .map(|col| { - let col_name = col.to_string(); - foreign_key.from_col(col); - col_name - }) - .collect(); - match relation.to_col { - Identity::Unary(o1) => { - foreign_key.to_col(o1); - } - Identity::Binary(o1, o2) => { - foreign_key.to_col(o1); - foreign_key.to_col(o2); - } - Identity::Ternary(o1, o2, o3) => { - foreign_key.to_col(o1); - foreign_key.to_col(o2); - foreign_key.to_col(o3); - } - } - if let Some(action) = relation.on_delete { - foreign_key.on_delete(action); - } - if let Some(action) = relation.on_update { - foreign_key.on_update(action); - } - let name = if let Some(name) = relation.fk_name { - name - } else { - format!("fk-{}-{}", from_tbl.to_string(), from_cols.join("-")) - }; - foreign_key.name(&name); + let mut foreign_key = Self::new(); + set_foreign_key_stmt!(relation, foreign_key); foreign_key + .from_tbl(unpack_table_ref(&relation.from_tbl)) + .take() } } From ddb76d88fccd3df594fc4d90b58911ec7d3fb34b Mon Sep 17 00:00:00 2001 From: Billy Chan Date: Mon, 19 Dec 2022 15:27:44 +0800 Subject: [PATCH 5/5] Fixup --- src/entity/relation.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/entity/relation.rs b/src/entity/relation.rs index 519a89dc3..9e3c565e7 100644 --- a/src/entity/relation.rs +++ b/src/entity/relation.rs @@ -399,6 +399,7 @@ impl From for TableForeignKey { set_foreign_key_stmt!(relation, foreign_key); foreign_key .from_tbl(unpack_table_ref(&relation.from_tbl)) + .to_tbl(unpack_table_ref(&relation.to_tbl)) .take() } }