From 83a2b540b960123e51d7c787e17be5c9d9fca8c7 Mon Sep 17 00:00:00 2001 From: Bobby Ng Date: Mon, 23 Aug 2021 18:19:20 +0800 Subject: [PATCH 01/12] Create Table Statement --- src/entity/create_stmt.rs | 151 ++++++++++++++++++++++++++++++++++++++ src/entity/mod.rs | 2 + 2 files changed, 153 insertions(+) create mode 100644 src/entity/create_stmt.rs diff --git a/src/entity/create_stmt.rs b/src/entity/create_stmt.rs new file mode 100644 index 00000000..581a8265 --- /dev/null +++ b/src/entity/create_stmt.rs @@ -0,0 +1,151 @@ +use crate::{ColumnDef as Sea_Orm_Column_Def, EntityTrait, PrimaryKeyTrait, RelationDef}; +use crate::entity::column::ColumnTrait; +use crate::entity::relation::RelationTrait; +use crate::entity::primary_key::PrimaryKeyToColumn; +use sea_query::{Alias, ColumnDef as Sea_Query_Column_Def, ColumnType as Sea_Query_Column_Type, ForeignKeyCreateStatement, IndexCreateStatement, SeaRc, TableCreateStatement, TableRef}; +pub use sea_strum::IntoEnumIterator; +pub trait CreateStatementOf +{ + fn create_table_statement_of(entity: E) -> TableCreateStatement where E: EntityTrait { + let mut stmt = TableCreateStatement::new(); + stmt.table(entity); + for relation in E::Relation::iter() { + let mut foreign_key_stmt = ForeignKeyCreateStatement::new(); + let relation_trait: RelationDef = relation.def(); + // foreign_key_stmt.name("Temp"); + match relation_trait.from_tbl { + TableRef::Table(tbl) => { foreign_key_stmt.from_tbl(tbl); }, + _ => todo!(), + } + match relation_trait.to_tbl { + TableRef::Table(tbl) => { foreign_key_stmt.to_tbl(tbl); }, + _ => todo!(), + } + match relation_trait.from_col { + crate::Identity::Unary(o1) => { + foreign_key_stmt.from_col(o1); + }, + crate::Identity::Binary(o1, o2) => { + foreign_key_stmt.from_col(o1); + foreign_key_stmt.from_col(o2); + }, + crate::Identity::Ternary(o1, o2, o3) => { + foreign_key_stmt.from_col(o1); + foreign_key_stmt.from_col(o2); + foreign_key_stmt.from_col(o3); + }, + } + match relation_trait.to_col { + crate::Identity::Unary(o1) => { + foreign_key_stmt.to_col(o1); + }, + crate::Identity::Binary(o1, o2) => { + foreign_key_stmt.to_col(o1); + foreign_key_stmt.to_col(o2); + }, + crate::Identity::Ternary(o1, o2, o3) => { + foreign_key_stmt.to_col(o1); + foreign_key_stmt.to_col(o2); + foreign_key_stmt.to_col(o3); + }, + } + stmt.foreign_key(&mut foreign_key_stmt); + } + for col in E::Column::iter() { + let sea_orm_column_def: Sea_Orm_Column_Def = col.def().into(); + let mut index = IndexCreateStatement::new(); + let mut sea_query_column_def = Sea_Query_Column_Def::new(col); + for key in E::PrimaryKey::iter() { // enum: Id, Name ... + if sea_query_column_def.get_column_name() == Sea_Query_Column_Def::new(key.into_column()).get_column_name() { + sea_query_column_def.primary_key(); + if E::PrimaryKey::auto_increment() { + sea_query_column_def.auto_increment(); + } + index.primary(); + } + } + if !sea_orm_column_def.null { + sea_query_column_def.not_null(); + } + if sea_orm_column_def.unique { + sea_query_column_def.unique_key(); + index.unique(); + } + if sea_orm_column_def.indexed { + index.table(entity); + index.col(col); + stmt.index(&mut index); + } + match Sea_Query_Column_Type::from(sea_orm_column_def.col_type) { + Sea_Query_Column_Type::Char(length) => match length { + Some(length) => { sea_query_column_def.char_len(length); }, + None => { sea_query_column_def.char(); }, + }, + Sea_Query_Column_Type::String(length) => match length { + Some(length) => { sea_query_column_def.string_len(length); }, + None => { sea_query_column_def.string(); }, + }, + Sea_Query_Column_Type::Text => { sea_query_column_def.text(); }, + Sea_Query_Column_Type::TinyInteger(length) => match length { + Some(length) => { sea_query_column_def.tiny_integer_len(length); }, + None => { sea_query_column_def.tiny_integer(); }, + }, + // Sea_Query_Column_Type::TinyInteger => { sea_query_column_def.tiny_integer(); }, + Sea_Query_Column_Type::SmallInteger(length) => match length { + Some(length) => { sea_query_column_def.small_integer_len(length); }, + None => { sea_query_column_def.small_integer(); }, + }, + Sea_Query_Column_Type::Integer(length) => match length { + Some(length) => { sea_query_column_def.integer_len(length); }, + None => { sea_query_column_def.integer(); }, + }, + Sea_Query_Column_Type::BigInteger(length) => match length { + Some(length) => { sea_query_column_def.big_integer_len(length); }, + None => { sea_query_column_def.big_integer(); }, + }, + Sea_Query_Column_Type::Float(precision) => match precision { + Some(precision) => { sea_query_column_def.float_len(precision); }, + None => { sea_query_column_def.float(); }, + }, + Sea_Query_Column_Type::Double(precision) => match precision { + Some(precision) => { sea_query_column_def.double_len(precision); }, + None => { sea_query_column_def.double(); }, + }, + Sea_Query_Column_Type::Decimal(_) => { sea_query_column_def.decimal(); }, + Sea_Query_Column_Type::DateTime(precision) => match precision { + Some(precision) => { sea_query_column_def.date_time_len(precision); }, + None => { sea_query_column_def.date_time(); }, + }, + Sea_Query_Column_Type::Timestamp(precision) => match precision { + Some(precision) => { sea_query_column_def.timestamp_len(precision); }, + None => { sea_query_column_def.timestamp(); }, + }, + Sea_Query_Column_Type::Time(precision) => match precision { + Some(precision) => { sea_query_column_def.time_len(precision); }, + None => { sea_query_column_def.time(); }, + }, + Sea_Query_Column_Type::Date => { sea_query_column_def.date(); }, + Sea_Query_Column_Type::Binary(length) => match length { + Some(length) => { sea_query_column_def.binary_len(length); }, + None => { sea_query_column_def.binary(); }, + }, + Sea_Query_Column_Type::Boolean => { sea_query_column_def.boolean(); }, + Sea_Query_Column_Type::Money(_) => { sea_query_column_def.money(); }, + Sea_Query_Column_Type::Json => { sea_query_column_def.json(); }, + Sea_Query_Column_Type::JsonBinary => { sea_query_column_def.json_binary(); }, + Sea_Query_Column_Type::Custom(iden) => { sea_query_column_def.custom(Alias::new(&iden.to_string())); }, + Sea_Query_Column_Type::Uuid => { sea_query_column_def.uuid(); }, + Sea_Query_Column_Type::TimestampWithTimeZone(length) => match length { + Some(length) => { sea_query_column_def.timestamp_with_time_zone_len(length); }, + None => { sea_query_column_def.timestamp_with_time_zone(); }, + }, + } + stmt.col(&mut sea_query_column_def); + } + stmt.if_not_exists(); + + stmt + } +} + +impl CreateStatementOf for EntityTrait {} diff --git a/src/entity/mod.rs b/src/entity/mod.rs index 6da5e1b6..9d911d8c 100644 --- a/src/entity/mod.rs +++ b/src/entity/mod.rs @@ -6,6 +6,7 @@ mod model; pub mod prelude; mod primary_key; mod relation; +mod create_stmt; pub use active_model::*; pub use base_entity::*; @@ -15,3 +16,4 @@ pub use model::*; // pub use prelude::*; pub use primary_key::*; pub use relation::*; +pub use create_stmt::*; From d80ea396abce30044e78b7e71a223d8999858436 Mon Sep 17 00:00:00 2001 From: Bobby Ng Date: Thu, 26 Aug 2021 18:15:29 +0800 Subject: [PATCH 02/12] test1 --- src/entity/create_stmt.rs | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/src/entity/create_stmt.rs b/src/entity/create_stmt.rs index 581a8265..fbf77c02 100644 --- a/src/entity/create_stmt.rs +++ b/src/entity/create_stmt.rs @@ -149,3 +149,24 @@ pub trait CreateStatementOf } impl CreateStatementOf for EntityTrait {} + +#[cfg(test)] +mod tests { + + use crate::{CreateStatementOf, tests_cfg}; + + #[test] + fn test_create_statement_tests_cfg_cake() { + let create_statement = tests_cfg::cake::Entity::create_table_statement_of(tests_cfg::cake::Entity); + let table = format!("{:?}", create_statement.get_table_name()); + let columns = format!("{:?}", create_statement.get_columns()); + let relations = format!("{:?}", create_statement.get_foreign_key_create_stmts()); + let indexs = format!("{:?}", create_statement.get_indexes()); + let result = format!("{:?}", create_statement); + assert_eq!("TableCreateStatement { table: Some(cake), columns: [ColumnDef { table: Some(cake), name: id, types: Some(Integer(None)), spec: [PrimaryKey, AutoIncrement, NotNull] }, ColumnDef { table: Some(cake), name: name, types: Some(String(None)), spec: [NotNull] }], options: [], partitions: [], indexes: [], foreign_keys: [ForeignKeyCreateStatement { foreign_key: TableForeignKey { name: None, table: Some(cake), ref_table: Some(fruit), columns: [id], ref_columns: [cake_id], on_delete: None, on_update: None } }], if_not_exists: true }", result); + assert_eq!(r#"Some("cake")"#, table); + assert_eq!("[ForeignKeyCreateStatement { foreign_key: TableForeignKey { name: None, table: Some(cake), ref_table: Some(fruit), columns: [id], ref_columns: [cake_id], on_delete: None, on_update: None } }]", relations); + assert_eq!(r#"[ColumnDef { table: Some(cake), name: id, types: Some(Integer(None)), spec: [PrimaryKey, AutoIncrement, NotNull] }, ColumnDef { table: Some(cake), name: name, types: Some(String(None)), spec: [NotNull] }]"#, columns); + assert_eq!("[]", indexs); + } +} From 8a81e43d2e507ff90aeeb54b095f3f055fc759ea Mon Sep 17 00:00:00 2001 From: Billy Chan Date: Tue, 31 Aug 2021 12:49:07 +0800 Subject: [PATCH 03/12] Rename to schema --- src/entity/mod.rs | 4 ++-- src/entity/{create_stmt.rs => schema.rs} | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) rename src/entity/{create_stmt.rs => schema.rs} (99%) diff --git a/src/entity/mod.rs b/src/entity/mod.rs index 9d911d8c..47eb4fa5 100644 --- a/src/entity/mod.rs +++ b/src/entity/mod.rs @@ -6,7 +6,7 @@ mod model; pub mod prelude; mod primary_key; mod relation; -mod create_stmt; +mod schema; pub use active_model::*; pub use base_entity::*; @@ -16,4 +16,4 @@ pub use model::*; // pub use prelude::*; pub use primary_key::*; pub use relation::*; -pub use create_stmt::*; +pub use schema::*; diff --git a/src/entity/create_stmt.rs b/src/entity/schema.rs similarity index 99% rename from src/entity/create_stmt.rs rename to src/entity/schema.rs index fbf77c02..cbbae75c 100644 --- a/src/entity/create_stmt.rs +++ b/src/entity/schema.rs @@ -152,7 +152,6 @@ impl CreateStatementOf for EntityTrait {} #[cfg(test)] mod tests { - use crate::{CreateStatementOf, tests_cfg}; #[test] From d1d37fab070415123eeb90afbb62f8848379936b Mon Sep 17 00:00:00 2001 From: Billy Chan Date: Tue, 31 Aug 2021 12:49:33 +0800 Subject: [PATCH 04/12] cargo fmt --- src/entity/column.rs | 2 +- src/entity/schema.rs | 194 +++++++++++++++++++++++++++++++------------ 2 files changed, 141 insertions(+), 55 deletions(-) diff --git a/src/entity/column.rs b/src/entity/column.rs index 16546057..611950f5 100644 --- a/src/entity/column.rs +++ b/src/entity/column.rs @@ -1,6 +1,6 @@ -use std::str::FromStr; use crate::{EntityName, IdenStatic, Iterable}; use sea_query::{DynIden, Expr, SeaRc, SelectStatement, SimpleExpr, Value}; +use std::str::FromStr; #[derive(Debug, Clone)] pub struct ColumnDef { diff --git a/src/entity/schema.rs b/src/entity/schema.rs index cbbae75c..6c334112 100644 --- a/src/entity/schema.rs +++ b/src/entity/schema.rs @@ -1,12 +1,17 @@ -use crate::{ColumnDef as Sea_Orm_Column_Def, EntityTrait, PrimaryKeyTrait, RelationDef}; use crate::entity::column::ColumnTrait; -use crate::entity::relation::RelationTrait; use crate::entity::primary_key::PrimaryKeyToColumn; -use sea_query::{Alias, ColumnDef as Sea_Query_Column_Def, ColumnType as Sea_Query_Column_Type, ForeignKeyCreateStatement, IndexCreateStatement, SeaRc, TableCreateStatement, TableRef}; +use crate::entity::relation::RelationTrait; +use crate::{ColumnDef as Sea_Orm_Column_Def, EntityTrait, PrimaryKeyTrait, RelationDef}; +use sea_query::{ + Alias, ColumnDef as Sea_Query_Column_Def, ColumnType as Sea_Query_Column_Type, + ForeignKeyCreateStatement, IndexCreateStatement, SeaRc, TableCreateStatement, TableRef, +}; pub use sea_strum::IntoEnumIterator; -pub trait CreateStatementOf -{ - fn create_table_statement_of(entity: E) -> TableCreateStatement where E: EntityTrait { +pub trait CreateStatementOf { + fn create_table_statement_of(entity: E) -> TableCreateStatement + where + E: EntityTrait, + { let mut stmt = TableCreateStatement::new(); stmt.table(entity); for relation in E::Relation::iter() { @@ -14,40 +19,44 @@ pub trait CreateStatementOf let relation_trait: RelationDef = relation.def(); // foreign_key_stmt.name("Temp"); match relation_trait.from_tbl { - TableRef::Table(tbl) => { foreign_key_stmt.from_tbl(tbl); }, + TableRef::Table(tbl) => { + foreign_key_stmt.from_tbl(tbl); + } _ => todo!(), } match relation_trait.to_tbl { - TableRef::Table(tbl) => { foreign_key_stmt.to_tbl(tbl); }, + TableRef::Table(tbl) => { + foreign_key_stmt.to_tbl(tbl); + } _ => todo!(), } match relation_trait.from_col { crate::Identity::Unary(o1) => { foreign_key_stmt.from_col(o1); - }, + } crate::Identity::Binary(o1, o2) => { foreign_key_stmt.from_col(o1); foreign_key_stmt.from_col(o2); - }, + } crate::Identity::Ternary(o1, o2, o3) => { foreign_key_stmt.from_col(o1); foreign_key_stmt.from_col(o2); foreign_key_stmt.from_col(o3); - }, + } } match relation_trait.to_col { crate::Identity::Unary(o1) => { foreign_key_stmt.to_col(o1); - }, + } crate::Identity::Binary(o1, o2) => { foreign_key_stmt.to_col(o1); foreign_key_stmt.to_col(o2); - }, + } crate::Identity::Ternary(o1, o2, o3) => { foreign_key_stmt.to_col(o1); foreign_key_stmt.to_col(o2); foreign_key_stmt.to_col(o3); - }, + } } stmt.foreign_key(&mut foreign_key_stmt); } @@ -55,8 +64,11 @@ pub trait CreateStatementOf let sea_orm_column_def: Sea_Orm_Column_Def = col.def().into(); let mut index = IndexCreateStatement::new(); let mut sea_query_column_def = Sea_Query_Column_Def::new(col); - for key in E::PrimaryKey::iter() { // enum: Id, Name ... - if sea_query_column_def.get_column_name() == Sea_Query_Column_Def::new(key.into_column()).get_column_name() { + for key in E::PrimaryKey::iter() { + // enum: Id, Name ... + if sea_query_column_def.get_column_name() + == Sea_Query_Column_Def::new(key.into_column()).get_column_name() + { sea_query_column_def.primary_key(); if E::PrimaryKey::auto_increment() { sea_query_column_def.auto_increment(); @@ -78,66 +90,136 @@ pub trait CreateStatementOf } match Sea_Query_Column_Type::from(sea_orm_column_def.col_type) { Sea_Query_Column_Type::Char(length) => match length { - Some(length) => { sea_query_column_def.char_len(length); }, - None => { sea_query_column_def.char(); }, + Some(length) => { + sea_query_column_def.char_len(length); + } + None => { + sea_query_column_def.char(); + } }, Sea_Query_Column_Type::String(length) => match length { - Some(length) => { sea_query_column_def.string_len(length); }, - None => { sea_query_column_def.string(); }, + Some(length) => { + sea_query_column_def.string_len(length); + } + None => { + sea_query_column_def.string(); + } }, - Sea_Query_Column_Type::Text => { sea_query_column_def.text(); }, + Sea_Query_Column_Type::Text => { + sea_query_column_def.text(); + } Sea_Query_Column_Type::TinyInteger(length) => match length { - Some(length) => { sea_query_column_def.tiny_integer_len(length); }, - None => { sea_query_column_def.tiny_integer(); }, + Some(length) => { + sea_query_column_def.tiny_integer_len(length); + } + None => { + sea_query_column_def.tiny_integer(); + } }, // Sea_Query_Column_Type::TinyInteger => { sea_query_column_def.tiny_integer(); }, Sea_Query_Column_Type::SmallInteger(length) => match length { - Some(length) => { sea_query_column_def.small_integer_len(length); }, - None => { sea_query_column_def.small_integer(); }, + Some(length) => { + sea_query_column_def.small_integer_len(length); + } + None => { + sea_query_column_def.small_integer(); + } }, Sea_Query_Column_Type::Integer(length) => match length { - Some(length) => { sea_query_column_def.integer_len(length); }, - None => { sea_query_column_def.integer(); }, + Some(length) => { + sea_query_column_def.integer_len(length); + } + None => { + sea_query_column_def.integer(); + } }, Sea_Query_Column_Type::BigInteger(length) => match length { - Some(length) => { sea_query_column_def.big_integer_len(length); }, - None => { sea_query_column_def.big_integer(); }, + Some(length) => { + sea_query_column_def.big_integer_len(length); + } + None => { + sea_query_column_def.big_integer(); + } }, Sea_Query_Column_Type::Float(precision) => match precision { - Some(precision) => { sea_query_column_def.float_len(precision); }, - None => { sea_query_column_def.float(); }, + Some(precision) => { + sea_query_column_def.float_len(precision); + } + None => { + sea_query_column_def.float(); + } }, Sea_Query_Column_Type::Double(precision) => match precision { - Some(precision) => { sea_query_column_def.double_len(precision); }, - None => { sea_query_column_def.double(); }, + Some(precision) => { + sea_query_column_def.double_len(precision); + } + None => { + sea_query_column_def.double(); + } }, - Sea_Query_Column_Type::Decimal(_) => { sea_query_column_def.decimal(); }, + Sea_Query_Column_Type::Decimal(_) => { + sea_query_column_def.decimal(); + } Sea_Query_Column_Type::DateTime(precision) => match precision { - Some(precision) => { sea_query_column_def.date_time_len(precision); }, - None => { sea_query_column_def.date_time(); }, + Some(precision) => { + sea_query_column_def.date_time_len(precision); + } + None => { + sea_query_column_def.date_time(); + } }, Sea_Query_Column_Type::Timestamp(precision) => match precision { - Some(precision) => { sea_query_column_def.timestamp_len(precision); }, - None => { sea_query_column_def.timestamp(); }, + Some(precision) => { + sea_query_column_def.timestamp_len(precision); + } + None => { + sea_query_column_def.timestamp(); + } }, Sea_Query_Column_Type::Time(precision) => match precision { - Some(precision) => { sea_query_column_def.time_len(precision); }, - None => { sea_query_column_def.time(); }, + Some(precision) => { + sea_query_column_def.time_len(precision); + } + None => { + sea_query_column_def.time(); + } }, - Sea_Query_Column_Type::Date => { sea_query_column_def.date(); }, + Sea_Query_Column_Type::Date => { + sea_query_column_def.date(); + } Sea_Query_Column_Type::Binary(length) => match length { - Some(length) => { sea_query_column_def.binary_len(length); }, - None => { sea_query_column_def.binary(); }, + Some(length) => { + sea_query_column_def.binary_len(length); + } + None => { + sea_query_column_def.binary(); + } }, - Sea_Query_Column_Type::Boolean => { sea_query_column_def.boolean(); }, - Sea_Query_Column_Type::Money(_) => { sea_query_column_def.money(); }, - Sea_Query_Column_Type::Json => { sea_query_column_def.json(); }, - Sea_Query_Column_Type::JsonBinary => { sea_query_column_def.json_binary(); }, - Sea_Query_Column_Type::Custom(iden) => { sea_query_column_def.custom(Alias::new(&iden.to_string())); }, - Sea_Query_Column_Type::Uuid => { sea_query_column_def.uuid(); }, + Sea_Query_Column_Type::Boolean => { + sea_query_column_def.boolean(); + } + Sea_Query_Column_Type::Money(_) => { + sea_query_column_def.money(); + } + Sea_Query_Column_Type::Json => { + sea_query_column_def.json(); + } + Sea_Query_Column_Type::JsonBinary => { + sea_query_column_def.json_binary(); + } + Sea_Query_Column_Type::Custom(iden) => { + sea_query_column_def.custom(Alias::new(&iden.to_string())); + } + Sea_Query_Column_Type::Uuid => { + sea_query_column_def.uuid(); + } Sea_Query_Column_Type::TimestampWithTimeZone(length) => match length { - Some(length) => { sea_query_column_def.timestamp_with_time_zone_len(length); }, - None => { sea_query_column_def.timestamp_with_time_zone(); }, + Some(length) => { + sea_query_column_def.timestamp_with_time_zone_len(length); + } + None => { + sea_query_column_def.timestamp_with_time_zone(); + } }, } stmt.col(&mut sea_query_column_def); @@ -152,11 +234,12 @@ impl CreateStatementOf for EntityTrait {} #[cfg(test)] mod tests { - use crate::{CreateStatementOf, tests_cfg}; + use crate::{tests_cfg, CreateStatementOf}; #[test] fn test_create_statement_tests_cfg_cake() { - let create_statement = tests_cfg::cake::Entity::create_table_statement_of(tests_cfg::cake::Entity); + let create_statement = + tests_cfg::cake::Entity::create_table_statement_of(tests_cfg::cake::Entity); let table = format!("{:?}", create_statement.get_table_name()); let columns = format!("{:?}", create_statement.get_columns()); let relations = format!("{:?}", create_statement.get_foreign_key_create_stmts()); @@ -165,7 +248,10 @@ mod tests { assert_eq!("TableCreateStatement { table: Some(cake), columns: [ColumnDef { table: Some(cake), name: id, types: Some(Integer(None)), spec: [PrimaryKey, AutoIncrement, NotNull] }, ColumnDef { table: Some(cake), name: name, types: Some(String(None)), spec: [NotNull] }], options: [], partitions: [], indexes: [], foreign_keys: [ForeignKeyCreateStatement { foreign_key: TableForeignKey { name: None, table: Some(cake), ref_table: Some(fruit), columns: [id], ref_columns: [cake_id], on_delete: None, on_update: None } }], if_not_exists: true }", result); assert_eq!(r#"Some("cake")"#, table); assert_eq!("[ForeignKeyCreateStatement { foreign_key: TableForeignKey { name: None, table: Some(cake), ref_table: Some(fruit), columns: [id], ref_columns: [cake_id], on_delete: None, on_update: None } }]", relations); - assert_eq!(r#"[ColumnDef { table: Some(cake), name: id, types: Some(Integer(None)), spec: [PrimaryKey, AutoIncrement, NotNull] }, ColumnDef { table: Some(cake), name: name, types: Some(String(None)), spec: [NotNull] }]"#, columns); + assert_eq!( + r#"[ColumnDef { table: Some(cake), name: id, types: Some(Integer(None)), spec: [PrimaryKey, AutoIncrement, NotNull] }, ColumnDef { table: Some(cake), name: name, types: Some(String(None)), spec: [NotNull] }]"#, + columns + ); assert_eq!("[]", indexs); } } From 5fbc7b146a8a853b90936a5f674d5f0101fb2448 Mon Sep 17 00:00:00 2001 From: Billy Chan Date: Tue, 31 Aug 2021 18:30:02 +0800 Subject: [PATCH 05/12] Refactoring & Testing --- Cargo.toml | 2 +- src/entity/base_entity.rs | 6 +- src/entity/column.rs | 6 +- src/entity/relation.rs | 10 +- src/entity/schema.rs | 385 ++++++++++---------------- src/query/helper.rs | 2 +- tests/common/bakery_chain/baker.rs | 2 +- tests/common/bakery_chain/bakery.rs | 2 +- tests/common/bakery_chain/cake.rs | 4 +- tests/common/bakery_chain/customer.rs | 2 +- tests/common/setup/schema.rs | 100 ++++--- 11 files changed, 220 insertions(+), 301 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index ca61c819..19a79f5e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -32,7 +32,7 @@ futures = { version = "^0.3" } futures-util = { version = "^0.3" } rust_decimal = { version = "^1", optional = true } sea-orm-macros = { version = "^0.1.1", optional = true } -sea-query = { version = "^0.15", features = ["thread-safe"] } +sea-query = { version = "^0.15", git = "https://github.com/SeaQL/sea-query.git", branch = "sea-orm/create-table-stmt", features = ["thread-safe"] } sea-strum = { version = "^0.21", features = ["derive", "sea-orm"] } serde = { version = "^1.0", features = ["derive"] } sqlx = { version = "^0.5", optional = true } diff --git a/src/entity/base_entity.rs b/src/entity/base_entity.rs index d0740307..29984953 100644 --- a/src/entity/base_entity.rs +++ b/src/entity/base_entity.rs @@ -55,21 +55,21 @@ pub trait EntityTrait: EntityName { where R: EntityTrait, { - RelationBuilder::new(RelationType::HasOne, Self::default(), related) + RelationBuilder::new(RelationType::HasOne, Self::default(), related, false) } fn has_one(_: R) -> RelationBuilder where R: EntityTrait + Related, { - RelationBuilder::from_rel(RelationType::HasOne, R::to().rev()) + RelationBuilder::from_rel(RelationType::HasOne, R::to().rev(), true) } fn has_many(_: R) -> RelationBuilder where R: EntityTrait + Related, { - RelationBuilder::from_rel(RelationType::HasMany, R::to().rev()) + RelationBuilder::from_rel(RelationType::HasMany, R::to().rev(), true) } /// Construct select statement to find one / all models diff --git a/src/entity/column.rs b/src/entity/column.rs index 611950f5..b58a181c 100644 --- a/src/entity/column.rs +++ b/src/entity/column.rs @@ -26,7 +26,7 @@ pub enum ColumnType { Timestamp, Time, Date, - Binary, + Binary(Option), Boolean, Money(Option<(u32, u32)>), Json, @@ -276,7 +276,7 @@ impl From for sea_query::ColumnType { ColumnType::Timestamp => sea_query::ColumnType::Timestamp(None), ColumnType::Time => sea_query::ColumnType::Time(None), ColumnType::Date => sea_query::ColumnType::Date, - ColumnType::Binary => sea_query::ColumnType::Binary(None), + ColumnType::Binary(s) => sea_query::ColumnType::Binary(s), ColumnType::Boolean => sea_query::ColumnType::Boolean, ColumnType::Money(s) => sea_query::ColumnType::Money(s), ColumnType::Json => sea_query::ColumnType::Json, @@ -307,7 +307,7 @@ impl From for ColumnType { sea_query::ColumnType::Timestamp(_) => Self::Timestamp, sea_query::ColumnType::Time(_) => Self::Time, sea_query::ColumnType::Date => Self::Date, - sea_query::ColumnType::Binary(_) => Self::Binary, + sea_query::ColumnType::Binary(s) => Self::Binary(s), sea_query::ColumnType::Boolean => Self::Boolean, sea_query::ColumnType::Money(s) => Self::Money(s), sea_query::ColumnType::Json => Self::Json, diff --git a/src/entity/relation.rs b/src/entity/relation.rs index 955660e2..afc8aba1 100644 --- a/src/entity/relation.rs +++ b/src/entity/relation.rs @@ -34,6 +34,7 @@ pub struct RelationDef { pub to_tbl: TableRef, pub from_col: Identity, pub to_col: Identity, + pub is_owner: bool, } pub struct RelationBuilder @@ -47,6 +48,7 @@ where to_tbl: TableRef, from_col: Option, to_col: Option, + is_owner: bool, } impl RelationDef { @@ -58,6 +60,7 @@ impl RelationDef { to_tbl: self.from_tbl, from_col: self.to_col, to_col: self.from_col, + is_owner: !self.is_owner, } } } @@ -67,7 +70,7 @@ where E: EntityTrait, R: EntityTrait, { - pub(crate) fn new(rel_type: RelationType, from: E, to: R) -> Self { + pub(crate) fn new(rel_type: RelationType, from: E, to: R, is_owner: bool) -> Self { Self { entities: PhantomData, rel_type, @@ -75,10 +78,11 @@ where to_tbl: to.table_ref(), from_col: None, to_col: None, + is_owner, } } - pub(crate) fn from_rel(rel_type: RelationType, rel: RelationDef) -> Self { + pub(crate) fn from_rel(rel_type: RelationType, rel: RelationDef, is_owner: bool) -> Self { Self { entities: PhantomData, rel_type, @@ -86,6 +90,7 @@ where to_tbl: rel.to_tbl, from_col: Some(rel.from_col), to_col: Some(rel.to_col), + is_owner, } } @@ -118,6 +123,7 @@ where to_tbl: b.to_tbl, from_col: b.from_col.unwrap(), to_col: b.to_col.unwrap(), + is_owner: b.is_owner, } } } diff --git a/src/entity/schema.rs b/src/entity/schema.rs index 6c334112..e743fae0 100644 --- a/src/entity/schema.rs +++ b/src/entity/schema.rs @@ -1,257 +1,154 @@ -use crate::entity::column::ColumnTrait; -use crate::entity::primary_key::PrimaryKeyToColumn; -use crate::entity::relation::RelationTrait; -use crate::{ColumnDef as Sea_Orm_Column_Def, EntityTrait, PrimaryKeyTrait, RelationDef}; -use sea_query::{ - Alias, ColumnDef as Sea_Query_Column_Def, ColumnType as Sea_Query_Column_Type, - ForeignKeyCreateStatement, IndexCreateStatement, SeaRc, TableCreateStatement, TableRef, +use crate::{ + unpack_table_ref, ColumnTrait, EntityTrait, Identity, Iterable, PrimaryKeyToColumn, + PrimaryKeyTrait, RelationTrait, }; -pub use sea_strum::IntoEnumIterator; -pub trait CreateStatementOf { - fn create_table_statement_of(entity: E) -> TableCreateStatement - where - E: EntityTrait, - { - let mut stmt = TableCreateStatement::new(); - stmt.table(entity); - for relation in E::Relation::iter() { - let mut foreign_key_stmt = ForeignKeyCreateStatement::new(); - let relation_trait: RelationDef = relation.def(); - // foreign_key_stmt.name("Temp"); - match relation_trait.from_tbl { - TableRef::Table(tbl) => { - foreign_key_stmt.from_tbl(tbl); - } - _ => todo!(), - } - match relation_trait.to_tbl { - TableRef::Table(tbl) => { - foreign_key_stmt.to_tbl(tbl); - } - _ => todo!(), - } - match relation_trait.from_col { - crate::Identity::Unary(o1) => { - foreign_key_stmt.from_col(o1); - } - crate::Identity::Binary(o1, o2) => { - foreign_key_stmt.from_col(o1); - foreign_key_stmt.from_col(o2); - } - crate::Identity::Ternary(o1, o2, o3) => { - foreign_key_stmt.from_col(o1); - foreign_key_stmt.from_col(o2); - foreign_key_stmt.from_col(o3); - } - } - match relation_trait.to_col { - crate::Identity::Unary(o1) => { - foreign_key_stmt.to_col(o1); - } - crate::Identity::Binary(o1, o2) => { - foreign_key_stmt.to_col(o1); - foreign_key_stmt.to_col(o2); - } - crate::Identity::Ternary(o1, o2, o3) => { - foreign_key_stmt.to_col(o1); - foreign_key_stmt.to_col(o2); - foreign_key_stmt.to_col(o3); - } - } - stmt.foreign_key(&mut foreign_key_stmt); - } - for col in E::Column::iter() { - let sea_orm_column_def: Sea_Orm_Column_Def = col.def().into(); - let mut index = IndexCreateStatement::new(); - let mut sea_query_column_def = Sea_Query_Column_Def::new(col); - for key in E::PrimaryKey::iter() { - // enum: Id, Name ... - if sea_query_column_def.get_column_name() - == Sea_Query_Column_Def::new(key.into_column()).get_column_name() - { - sea_query_column_def.primary_key(); - if E::PrimaryKey::auto_increment() { - sea_query_column_def.auto_increment(); - } - index.primary(); - } - } - if !sea_orm_column_def.null { - sea_query_column_def.not_null(); - } - if sea_orm_column_def.unique { - sea_query_column_def.unique_key(); - index.unique(); - } - if sea_orm_column_def.indexed { - index.table(entity); - index.col(col); - stmt.index(&mut index); - } - match Sea_Query_Column_Type::from(sea_orm_column_def.col_type) { - Sea_Query_Column_Type::Char(length) => match length { - Some(length) => { - sea_query_column_def.char_len(length); - } - None => { - sea_query_column_def.char(); - } - }, - Sea_Query_Column_Type::String(length) => match length { - Some(length) => { - sea_query_column_def.string_len(length); - } - None => { - sea_query_column_def.string(); - } - }, - Sea_Query_Column_Type::Text => { - sea_query_column_def.text(); - } - Sea_Query_Column_Type::TinyInteger(length) => match length { - Some(length) => { - sea_query_column_def.tiny_integer_len(length); - } - None => { - sea_query_column_def.tiny_integer(); - } - }, - // Sea_Query_Column_Type::TinyInteger => { sea_query_column_def.tiny_integer(); }, - Sea_Query_Column_Type::SmallInteger(length) => match length { - Some(length) => { - sea_query_column_def.small_integer_len(length); - } - None => { - sea_query_column_def.small_integer(); - } - }, - Sea_Query_Column_Type::Integer(length) => match length { - Some(length) => { - sea_query_column_def.integer_len(length); - } - None => { - sea_query_column_def.integer(); - } - }, - Sea_Query_Column_Type::BigInteger(length) => match length { - Some(length) => { - sea_query_column_def.big_integer_len(length); - } - None => { - sea_query_column_def.big_integer(); - } - }, - Sea_Query_Column_Type::Float(precision) => match precision { - Some(precision) => { - sea_query_column_def.float_len(precision); - } - None => { - sea_query_column_def.float(); - } - }, - Sea_Query_Column_Type::Double(precision) => match precision { - Some(precision) => { - sea_query_column_def.double_len(precision); - } - None => { - sea_query_column_def.double(); - } - }, - Sea_Query_Column_Type::Decimal(_) => { - sea_query_column_def.decimal(); - } - Sea_Query_Column_Type::DateTime(precision) => match precision { - Some(precision) => { - sea_query_column_def.date_time_len(precision); - } - None => { - sea_query_column_def.date_time(); - } - }, - Sea_Query_Column_Type::Timestamp(precision) => match precision { - Some(precision) => { - sea_query_column_def.timestamp_len(precision); - } - None => { - sea_query_column_def.timestamp(); - } - }, - Sea_Query_Column_Type::Time(precision) => match precision { - Some(precision) => { - sea_query_column_def.time_len(precision); - } - None => { - sea_query_column_def.time(); - } - }, - Sea_Query_Column_Type::Date => { - sea_query_column_def.date(); - } - Sea_Query_Column_Type::Binary(length) => match length { - Some(length) => { - sea_query_column_def.binary_len(length); - } - None => { - sea_query_column_def.binary(); - } - }, - Sea_Query_Column_Type::Boolean => { - sea_query_column_def.boolean(); - } - Sea_Query_Column_Type::Money(_) => { - sea_query_column_def.money(); - } - Sea_Query_Column_Type::Json => { - sea_query_column_def.json(); - } - Sea_Query_Column_Type::JsonBinary => { - sea_query_column_def.json_binary(); - } - Sea_Query_Column_Type::Custom(iden) => { - sea_query_column_def.custom(Alias::new(&iden.to_string())); - } - Sea_Query_Column_Type::Uuid => { - sea_query_column_def.uuid(); - } - Sea_Query_Column_Type::TimestampWithTimeZone(length) => match length { - Some(length) => { - sea_query_column_def.timestamp_with_time_zone_len(length); - } - None => { - sea_query_column_def.timestamp_with_time_zone(); - } - }, - } - stmt.col(&mut sea_query_column_def); - } - stmt.if_not_exists(); +use sea_query::{ColumnDef, ForeignKeyCreateStatement, Iden, Index, TableCreateStatement}; - stmt +pub fn entity_to_table_create_statement(entity: E) -> TableCreateStatement +where + E: EntityTrait, +{ + let mut stmt = TableCreateStatement::new(); + + for column in E::Column::iter() { + let orm_column_def = column.def(); + let types = 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(); + } + for primary_key in E::PrimaryKey::iter() { + if column.to_string() == primary_key.into_column().to_string() + && E::PrimaryKey::auto_increment() + { + column_def.auto_increment(); + if E::PrimaryKey::iter().count() == 1 { + column_def.primary_key(); + } + } + } + if orm_column_def.indexed { + stmt.index( + Index::create() + .name(&format!( + "idx-{}-{}", + entity.to_string(), + column.to_string() + )) + .table(entity) + .col(column), + ); + } + stmt.col(&mut column_def); } -} -impl CreateStatementOf for EntityTrait {} + if E::PrimaryKey::iter().count() > 1 { + let mut idx_pk = Index::create(); + for primary_key in E::PrimaryKey::iter() { + idx_pk.col(primary_key); + } + stmt.primary_key(idx_pk.name(&format!("pk-{}", entity.to_string())).primary()); + } + + for relation in E::Relation::iter() { + let relation = relation.def(); + 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); + match relation.from_col { + Identity::Unary(o1) => { + foreign_key_stmt.from_col(o1); + } + Identity::Binary(o1, o2) => { + foreign_key_stmt.from_col(o1); + foreign_key_stmt.from_col(o2); + } + Identity::Ternary(o1, o2, o3) => { + foreign_key_stmt.from_col(o1); + foreign_key_stmt.from_col(o2); + foreign_key_stmt.from_col(o3); + } + } + 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); + } + crate::Identity::Ternary(o1, o2, o3) => { + foreign_key_stmt.to_col(o1); + foreign_key_stmt.to_col(o2); + foreign_key_stmt.to_col(o3); + } + } + stmt.foreign_key( + foreign_key_stmt + .name(&format!( + "fk-{}-{}", + from_tbl.to_string(), + to_tbl.to_string() + )) + .from_tbl(from_tbl) + .to_tbl(to_tbl), + ); + } + + stmt.table(entity).if_not_exists().take() +} #[cfg(test)] mod tests { - use crate::{tests_cfg, CreateStatementOf}; + use crate::{entity_to_table_create_statement, tests_cfg::*}; + use sea_query::*; #[test] - fn test_create_statement_tests_cfg_cake() { - let create_statement = - tests_cfg::cake::Entity::create_table_statement_of(tests_cfg::cake::Entity); - let table = format!("{:?}", create_statement.get_table_name()); - let columns = format!("{:?}", create_statement.get_columns()); - let relations = format!("{:?}", create_statement.get_foreign_key_create_stmts()); - let indexs = format!("{:?}", create_statement.get_indexes()); - let result = format!("{:?}", create_statement); - assert_eq!("TableCreateStatement { table: Some(cake), columns: [ColumnDef { table: Some(cake), name: id, types: Some(Integer(None)), spec: [PrimaryKey, AutoIncrement, NotNull] }, ColumnDef { table: Some(cake), name: name, types: Some(String(None)), spec: [NotNull] }], options: [], partitions: [], indexes: [], foreign_keys: [ForeignKeyCreateStatement { foreign_key: TableForeignKey { name: None, table: Some(cake), ref_table: Some(fruit), columns: [id], ref_columns: [cake_id], on_delete: None, on_update: None } }], if_not_exists: true }", result); - assert_eq!(r#"Some("cake")"#, table); - assert_eq!("[ForeignKeyCreateStatement { foreign_key: TableForeignKey { name: None, table: Some(cake), ref_table: Some(fruit), columns: [id], ref_columns: [cake_id], on_delete: None, on_update: None } }]", relations); + fn test_entity_to_table_create_statement() { assert_eq!( - r#"[ColumnDef { table: Some(cake), name: id, types: Some(Integer(None)), spec: [PrimaryKey, AutoIncrement, NotNull] }, ColumnDef { table: Some(cake), name: name, types: Some(String(None)), spec: [NotNull] }]"#, - columns + entity_to_table_create_statement(CakeFillingPrice).to_string(MysqlQueryBuilder), + Table::create() + .table(CakeFillingPrice) + .if_not_exists() + .col( + ColumnDef::new(cake_filling_price::Column::CakeId) + .integer() + .not_null() + ) + .col( + ColumnDef::new(cake_filling_price::Column::FillingId) + .integer() + .not_null() + ) + .col( + ColumnDef::new(cake_filling_price::Column::Price) + .decimal() + .not_null() + ) + .primary_key( + Index::create() + .name("pk-cake_filling_price") + .col(cake_filling_price::Column::CakeId) + .col(cake_filling_price::Column::FillingId) + .primary() + ) + .foreign_key( + ForeignKeyCreateStatement::new() + .name("fk-cake_filling_price-cake_filling") + .from_tbl(CakeFillingPrice) + .from_col(cake_filling_price::Column::CakeId) + .from_col(cake_filling_price::Column::FillingId) + .to_tbl(CakeFilling) + .to_col(cake_filling::Column::CakeId) + .to_col(cake_filling::Column::FillingId) + ) + .to_string(MysqlQueryBuilder) ); - assert_eq!("[]", indexs); } } diff --git a/src/query/helper.rs b/src/query/helper.rs index 6ade581a..57fafe3f 100644 --- a/src/query/helper.rs +++ b/src/query/helper.rs @@ -295,7 +295,7 @@ fn join_condition(rel: RelationDef) -> SimpleExpr { } } -fn unpack_table_ref(table_ref: &TableRef) -> DynIden { +pub(crate) fn unpack_table_ref(table_ref: &TableRef) -> DynIden { match table_ref { TableRef::Table(tbl) => SeaRc::clone(tbl), TableRef::SchemaTable(_, tbl) => SeaRc::clone(tbl), diff --git a/tests/common/bakery_chain/baker.rs b/tests/common/bakery_chain/baker.rs index 0c63e721..0967fcf5 100644 --- a/tests/common/bakery_chain/baker.rs +++ b/tests/common/bakery_chain/baker.rs @@ -49,7 +49,7 @@ impl ColumnTrait for Column { Self::Id => ColumnType::Integer.def(), Self::Name => ColumnType::String(None).def(), Self::ContactDetails => ColumnType::Json.def(), - Self::BakeryId => ColumnType::Integer.def(), + Self::BakeryId => ColumnType::Integer.def().null(), } } } diff --git a/tests/common/bakery_chain/bakery.rs b/tests/common/bakery_chain/bakery.rs index 61803329..a020cfce 100644 --- a/tests/common/bakery_chain/bakery.rs +++ b/tests/common/bakery_chain/bakery.rs @@ -48,7 +48,7 @@ impl ColumnTrait for Column { match self { Self::Id => ColumnType::Integer.def(), Self::Name => ColumnType::String(None).def(), - Self::ProfitMargin => ColumnType::Float.def(), + Self::ProfitMargin => ColumnType::Double.def(), } } } diff --git a/tests/common/bakery_chain/cake.rs b/tests/common/bakery_chain/cake.rs index 72e649ce..29e2335a 100644 --- a/tests/common/bakery_chain/cake.rs +++ b/tests/common/bakery_chain/cake.rs @@ -54,9 +54,9 @@ impl ColumnTrait for Column { Self::Id => ColumnType::Integer.def(), Self::Name => ColumnType::String(None).def(), Self::Price => ColumnType::Decimal(Some((19, 4))).def(), - Self::BakeryId => ColumnType::Integer.def(), + Self::BakeryId => ColumnType::Integer.def().null(), Self::GlutenFree => ColumnType::Boolean.def(), - Self::Serial => ColumnType::String(None).def(), + Self::Serial => ColumnType::Binary(Some(16)).def(), } } } diff --git a/tests/common/bakery_chain/customer.rs b/tests/common/bakery_chain/customer.rs index ce4319ff..7e4d9e0c 100644 --- a/tests/common/bakery_chain/customer.rs +++ b/tests/common/bakery_chain/customer.rs @@ -46,7 +46,7 @@ impl ColumnTrait for Column { match self { Self::Id => ColumnType::Integer.def(), Self::Name => ColumnType::String(None).def(), - Self::Notes => ColumnType::Text.def(), + Self::Notes => ColumnType::Text.def().null(), } } } diff --git a/tests/common/setup/schema.rs b/tests/common/setup/schema.rs index 4eba40ab..4d461fd1 100644 --- a/tests/common/setup/schema.rs +++ b/tests/common/setup/schema.rs @@ -1,15 +1,29 @@ -use sea_orm::{error::*, sea_query, DbConn, ExecResult}; -use sea_query::{ColumnDef, ForeignKey, ForeignKeyAction, Index, TableCreateStatement}; +use sea_orm::{ + entity_to_table_create_statement, error::*, sea_query, DbConn, EntityTrait, ExecResult, +}; +use sea_query::{ColumnDef, ForeignKey, ForeignKeyAction, Index, Table, TableCreateStatement}; pub use super::super::bakery_chain::*; -async fn create_table(db: &DbConn, stmt: &TableCreateStatement) -> Result { +async fn create_table( + db: &DbConn, + stmt: &TableCreateStatement, + entity: E, +) -> Result +where + E: EntityTrait, +{ let builder = db.get_database_backend(); - db.execute(builder.build(stmt)).await + let stmt = builder.build(stmt); + assert_eq!( + builder.build(&entity_to_table_create_statement(entity)), + stmt + ); + db.execute(stmt).await } pub async fn create_bakery_table(db: &DbConn) -> Result { - let stmt = sea_query::Table::create() + let stmt = Table::create() .table(bakery::Entity) .if_not_exists() .col( @@ -27,16 +41,17 @@ pub async fn create_bakery_table(db: &DbConn) -> Result { ) .to_owned(); - create_table(db, &stmt).await + create_table(db, &stmt, Bakery).await } pub async fn create_baker_table(db: &DbConn) -> Result { - let stmt = sea_query::Table::create() + let stmt = Table::create() .table(baker::Entity) .if_not_exists() .col( ColumnDef::new(baker::Column::Id) .integer() + .not_null() .auto_increment() .primary_key(), ) @@ -49,19 +64,19 @@ pub async fn create_baker_table(db: &DbConn) -> Result { .col(ColumnDef::new(baker::Column::BakeryId).integer()) .foreign_key( ForeignKey::create() - .name("FK_baker_bakery") + .name("fk-baker-bakery") .from(baker::Entity, baker::Column::BakeryId) .to(bakery::Entity, bakery::Column::Id) - .on_delete(ForeignKeyAction::Cascade) - .on_update(ForeignKeyAction::Cascade), + // .on_delete(ForeignKeyAction::Cascade) + // .on_update(ForeignKeyAction::Cascade), ) .to_owned(); - create_table(db, &stmt).await + create_table(db, &stmt, Baker).await } pub async fn create_customer_table(db: &DbConn) -> Result { - let stmt = sea_query::Table::create() + let stmt = Table::create() .table(customer::Entity) .if_not_exists() .col( @@ -75,11 +90,11 @@ pub async fn create_customer_table(db: &DbConn) -> Result { .col(ColumnDef::new(customer::Column::Notes).text()) .to_owned(); - create_table(db, &stmt).await + create_table(db, &stmt, Customer).await } pub async fn create_order_table(db: &DbConn) -> Result { - let stmt = sea_query::Table::create() + let stmt = Table::create() .table(order::Entity) .if_not_exists() .col( @@ -107,27 +122,27 @@ pub async fn create_order_table(db: &DbConn) -> Result { ) .foreign_key( ForeignKey::create() - .name("FK_order_bakery") + .name("fk-order-bakery") .from(order::Entity, order::Column::BakeryId) .to(bakery::Entity, bakery::Column::Id) - .on_delete(ForeignKeyAction::Cascade) - .on_update(ForeignKeyAction::Cascade), + // .on_delete(ForeignKeyAction::Cascade) + // .on_update(ForeignKeyAction::Cascade), ) .foreign_key( ForeignKey::create() - .name("FK_order_customer") + .name("fk-order-customer") .from(order::Entity, order::Column::CustomerId) .to(customer::Entity, customer::Column::Id) - .on_delete(ForeignKeyAction::Cascade) - .on_update(ForeignKeyAction::Cascade), + // .on_delete(ForeignKeyAction::Cascade) + // .on_update(ForeignKeyAction::Cascade), ) .to_owned(); - create_table(db, &stmt).await + create_table(db, &stmt, Order).await } pub async fn create_lineitem_table(db: &DbConn) -> Result { - let stmt = sea_query::Table::create() + let stmt = Table::create() .table(lineitem::Entity) .if_not_exists() .col( @@ -159,27 +174,27 @@ pub async fn create_lineitem_table(db: &DbConn) -> Result { ) .foreign_key( ForeignKey::create() - .name("FK_lineitem_order") + .name("fk-lineitem-order") .from(lineitem::Entity, lineitem::Column::OrderId) .to(order::Entity, order::Column::Id) - .on_delete(ForeignKeyAction::Cascade) - .on_update(ForeignKeyAction::Cascade), + // .on_delete(ForeignKeyAction::Cascade) + // .on_update(ForeignKeyAction::Cascade), ) .foreign_key( ForeignKey::create() - .name("FK_lineitem_cake") + .name("fk-lineitem-cake") .from(lineitem::Entity, lineitem::Column::CakeId) .to(cake::Entity, cake::Column::Id) - .on_delete(ForeignKeyAction::Cascade) - .on_update(ForeignKeyAction::Cascade), + // .on_delete(ForeignKeyAction::Cascade) + // .on_update(ForeignKeyAction::Cascade), ) .to_owned(); - create_table(db, &stmt).await + create_table(db, &stmt, Lineitem).await } pub async fn create_cakes_bakers_table(db: &DbConn) -> Result { - let stmt = sea_query::Table::create() + let stmt = Table::create() .table(cakes_bakers::Entity) .if_not_exists() .col( @@ -194,32 +209,33 @@ pub async fn create_cakes_bakers_table(db: &DbConn) -> Result ) .primary_key( Index::create() + .name("pk-cakes_bakers") .col(cakes_bakers::Column::CakeId) .col(cakes_bakers::Column::BakerId), ) .foreign_key( ForeignKey::create() - .name("FK_cakes_bakers_cake") + .name("fk-cakes_bakers-cake") .from(cakes_bakers::Entity, cakes_bakers::Column::CakeId) .to(cake::Entity, cake::Column::Id) - .on_delete(ForeignKeyAction::Cascade) - .on_update(ForeignKeyAction::Cascade), + // .on_delete(ForeignKeyAction::Cascade) + // .on_update(ForeignKeyAction::Cascade), ) .foreign_key( ForeignKey::create() - .name("FK_cakes_bakers_baker") + .name("fk-cakes_bakers-baker") .from(cakes_bakers::Entity, cakes_bakers::Column::BakerId) .to(baker::Entity, baker::Column::Id) - .on_delete(ForeignKeyAction::Cascade) - .on_update(ForeignKeyAction::Cascade), + // .on_delete(ForeignKeyAction::Cascade) + // .on_update(ForeignKeyAction::Cascade), ) .to_owned(); - create_table(db, &stmt).await + create_table(db, &stmt, CakesBakers).await } pub async fn create_cake_table(db: &DbConn) -> Result { - let stmt = sea_query::Table::create() + let stmt = Table::create() .table(cake::Entity) .if_not_exists() .col( @@ -238,11 +254,11 @@ pub async fn create_cake_table(db: &DbConn) -> Result { .col(ColumnDef::new(cake::Column::BakeryId).integer()) .foreign_key( ForeignKey::create() - .name("FK_cake_bakery") + .name("fk-cake-bakery") .from(cake::Entity, cake::Column::BakeryId) .to(bakery::Entity, bakery::Column::Id) - .on_delete(ForeignKeyAction::Cascade) - .on_update(ForeignKeyAction::Cascade), + // .on_delete(ForeignKeyAction::Cascade) + // .on_update(ForeignKeyAction::Cascade), ) .col( ColumnDef::new(cake::Column::GlutenFree) @@ -252,5 +268,5 @@ pub async fn create_cake_table(db: &DbConn) -> Result { .col(ColumnDef::new(cake::Column::Serial).uuid().not_null()) .to_owned(); - create_table(db, &stmt).await + create_table(db, &stmt, Cake).await } From 5a0f1d0fd1887dfa6d036a00c0035ee46a5baa5c Mon Sep 17 00:00:00 2001 From: Billy Chan Date: Tue, 31 Aug 2021 19:05:54 +0800 Subject: [PATCH 06/12] Try pretty_assertions --- Cargo.toml | 1 + src/entity/schema.rs | 1 + tests/common/setup/schema.rs | 1 + 3 files changed, 3 insertions(+) diff --git a/Cargo.toml b/Cargo.toml index 19a79f5e..e303dac6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -50,6 +50,7 @@ actix-rt = { version = "2.2.0" } maplit = { version = "^1" } rust_decimal_macros = { version = "^1" } sea-orm = { path = ".", features = ["debug-print"] } +pretty_assertions = { version = "^0.7" } [features] debug-print = [] diff --git a/src/entity/schema.rs b/src/entity/schema.rs index e743fae0..930ce6aa 100644 --- a/src/entity/schema.rs +++ b/src/entity/schema.rs @@ -107,6 +107,7 @@ where #[cfg(test)] mod tests { use crate::{entity_to_table_create_statement, tests_cfg::*}; + use pretty_assertions::assert_eq; use sea_query::*; #[test] diff --git a/tests/common/setup/schema.rs b/tests/common/setup/schema.rs index 4d461fd1..321d6512 100644 --- a/tests/common/setup/schema.rs +++ b/tests/common/setup/schema.rs @@ -1,3 +1,4 @@ +use pretty_assertions::assert_eq; use sea_orm::{ entity_to_table_create_statement, error::*, sea_query, DbConn, EntityTrait, ExecResult, }; From bdf6593dfe4f8e0fbb2c33b161aac44545074a8a Mon Sep 17 00:00:00 2001 From: Billy Chan Date: Tue, 31 Aug 2021 22:31:58 +0800 Subject: [PATCH 07/12] Hotfix --- tests/common/bakery_chain/cake.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/common/bakery_chain/cake.rs b/tests/common/bakery_chain/cake.rs index 29e2335a..3630d29a 100644 --- a/tests/common/bakery_chain/cake.rs +++ b/tests/common/bakery_chain/cake.rs @@ -56,7 +56,7 @@ impl ColumnTrait for Column { Self::Price => ColumnType::Decimal(Some((19, 4))).def(), Self::BakeryId => ColumnType::Integer.def().null(), Self::GlutenFree => ColumnType::Boolean.def(), - Self::Serial => ColumnType::Binary(Some(16)).def(), + Self::Serial => ColumnType::Uuid.def(), } } } From 1ad1767457a5e320692b9a61db7d60560695c11c Mon Sep 17 00:00:00 2001 From: Billy Chan Date: Tue, 31 Aug 2021 23:10:39 +0800 Subject: [PATCH 08/12] cargo fmt --- tests/common/setup/schema.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/common/setup/schema.rs b/tests/common/setup/schema.rs index 321d6512..7e668f29 100644 --- a/tests/common/setup/schema.rs +++ b/tests/common/setup/schema.rs @@ -210,7 +210,7 @@ pub async fn create_cakes_bakers_table(db: &DbConn) -> Result ) .primary_key( Index::create() - .name("pk-cakes_bakers") + .name("pk-cakes_bakers") .col(cakes_bakers::Column::CakeId) .col(cakes_bakers::Column::BakerId), ) From 5073c6f4aad5b21eaafae5d63c7fbf80082cc068 Mon Sep 17 00:00:00 2001 From: Billy Chan Date: Wed, 1 Sep 2021 10:35:51 +0800 Subject: [PATCH 09/12] Relation with optional ForeignKeyAction --- src/entity/prelude.rs | 6 ++--- src/entity/relation.rs | 24 +++++++++++++++++ src/entity/schema.rs | 6 +++++ tests/common/bakery_chain/baker.rs | 2 ++ tests/common/bakery_chain/cake.rs | 2 ++ tests/common/bakery_chain/cakes_bakers.rs | 4 +++ tests/common/bakery_chain/lineitem.rs | 4 +++ tests/common/bakery_chain/order.rs | 4 +++ tests/common/setup/schema.rs | 32 +++++++++++------------ 9 files changed, 65 insertions(+), 19 deletions(-) diff --git a/src/entity/prelude.rs b/src/entity/prelude.rs index 447117b7..c0c0f5b5 100644 --- a/src/entity/prelude.rs +++ b/src/entity/prelude.rs @@ -1,9 +1,9 @@ pub use crate::{ error::*, ActiveModelBehavior, ActiveModelTrait, ColumnDef, ColumnTrait, ColumnType, DeriveActiveModel, DeriveActiveModelBehavior, DeriveColumn, DeriveCustomColumn, DeriveEntity, - DeriveModel, DerivePrimaryKey, EntityName, EntityTrait, EnumIter, Iden, IdenStatic, ModelTrait, - PrimaryKeyToColumn, PrimaryKeyTrait, QueryFilter, QueryResult, Related, RelationDef, - RelationTrait, Select, Value, + DeriveModel, DerivePrimaryKey, EntityName, EntityTrait, EnumIter, ForeignKeyAction, Iden, + IdenStatic, ModelTrait, PrimaryKeyToColumn, PrimaryKeyTrait, QueryFilter, QueryResult, Related, + RelationDef, RelationTrait, Select, Value, }; #[cfg(feature = "with-json")] diff --git a/src/entity/relation.rs b/src/entity/relation.rs index afc8aba1..6e90975a 100644 --- a/src/entity/relation.rs +++ b/src/entity/relation.rs @@ -9,6 +9,8 @@ pub enum RelationType { HasMany, } +pub type ForeignKeyAction = sea_query::ForeignKeyAction; + pub trait RelationTrait: Iterable + Debug + 'static { fn def(&self) -> RelationDef; } @@ -35,6 +37,8 @@ pub struct RelationDef { pub from_col: Identity, pub to_col: Identity, pub is_owner: bool, + pub on_delete: Option, + pub on_update: Option, } pub struct RelationBuilder @@ -49,6 +53,8 @@ where from_col: Option, to_col: Option, is_owner: bool, + on_delete: Option, + on_update: Option, } impl RelationDef { @@ -61,6 +67,8 @@ impl RelationDef { from_col: self.to_col, to_col: self.from_col, is_owner: !self.is_owner, + on_delete: self.on_delete, + on_update: self.on_update, } } } @@ -79,6 +87,8 @@ where from_col: None, to_col: None, is_owner, + on_delete: None, + on_update: None, } } @@ -91,6 +101,8 @@ where from_col: Some(rel.from_col), to_col: Some(rel.to_col), is_owner, + on_delete: None, + on_update: None, } } @@ -109,6 +121,16 @@ where self.to_col = Some(identifier.identity_of()); self } + + pub fn on_delete(mut self, action: ForeignKeyAction) -> Self { + self.on_delete = Some(action); + self + } + + pub fn on_update(mut self, action: ForeignKeyAction) -> Self { + self.on_update = Some(action); + self + } } impl From> for RelationDef @@ -124,6 +146,8 @@ where from_col: b.from_col.unwrap(), to_col: b.to_col.unwrap(), is_owner: b.is_owner, + on_delete: b.on_delete, + on_update: b.on_update, } } } diff --git a/src/entity/schema.rs b/src/entity/schema.rs index 930ce6aa..727eab06 100644 --- a/src/entity/schema.rs +++ b/src/entity/schema.rs @@ -89,6 +89,12 @@ where 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); + } stmt.foreign_key( foreign_key_stmt .name(&format!( diff --git a/tests/common/bakery_chain/baker.rs b/tests/common/bakery_chain/baker.rs index 0967fcf5..3dff66aa 100644 --- a/tests/common/bakery_chain/baker.rs +++ b/tests/common/bakery_chain/baker.rs @@ -60,6 +60,8 @@ impl RelationTrait for Relation { Self::Bakery => Entity::belongs_to(super::bakery::Entity) .from(Column::BakeryId) .to(super::bakery::Column::Id) + .on_delete(ForeignKeyAction::Cascade) + .on_update(ForeignKeyAction::Cascade) .into(), } } diff --git a/tests/common/bakery_chain/cake.rs b/tests/common/bakery_chain/cake.rs index 3630d29a..8eaf8b9e 100644 --- a/tests/common/bakery_chain/cake.rs +++ b/tests/common/bakery_chain/cake.rs @@ -67,6 +67,8 @@ impl RelationTrait for Relation { Self::Bakery => Entity::belongs_to(super::bakery::Entity) .from(Column::BakeryId) .to(super::bakery::Column::Id) + .on_delete(ForeignKeyAction::Cascade) + .on_update(ForeignKeyAction::Cascade) .into(), Self::Lineitem => Entity::has_many(super::lineitem::Entity).into(), } diff --git a/tests/common/bakery_chain/cakes_bakers.rs b/tests/common/bakery_chain/cakes_bakers.rs index 8106bbdf..e2067b59 100644 --- a/tests/common/bakery_chain/cakes_bakers.rs +++ b/tests/common/bakery_chain/cakes_bakers.rs @@ -56,10 +56,14 @@ impl RelationTrait for Relation { Self::Cake => Entity::belongs_to(super::cake::Entity) .from(Column::CakeId) .to(super::cake::Column::Id) + .on_delete(ForeignKeyAction::Cascade) + .on_update(ForeignKeyAction::Cascade) .into(), Self::Baker => Entity::belongs_to(super::baker::Entity) .from(Column::BakerId) .to(super::baker::Column::Id) + .on_delete(ForeignKeyAction::Cascade) + .on_update(ForeignKeyAction::Cascade) .into(), } } diff --git a/tests/common/bakery_chain/lineitem.rs b/tests/common/bakery_chain/lineitem.rs index 45a6037f..26ec828e 100644 --- a/tests/common/bakery_chain/lineitem.rs +++ b/tests/common/bakery_chain/lineitem.rs @@ -64,10 +64,14 @@ impl RelationTrait for Relation { Self::Order => Entity::belongs_to(super::order::Entity) .from(Column::OrderId) .to(super::order::Column::Id) + .on_delete(ForeignKeyAction::Cascade) + .on_update(ForeignKeyAction::Cascade) .into(), Self::Cake => Entity::belongs_to(super::cake::Entity) .from(Column::CakeId) .to(super::cake::Column::Id) + .on_delete(ForeignKeyAction::Cascade) + .on_update(ForeignKeyAction::Cascade) .into(), } } diff --git a/tests/common/bakery_chain/order.rs b/tests/common/bakery_chain/order.rs index 82b02dee..6fb8d212 100644 --- a/tests/common/bakery_chain/order.rs +++ b/tests/common/bakery_chain/order.rs @@ -65,10 +65,14 @@ impl RelationTrait for Relation { Self::Bakery => Entity::belongs_to(super::bakery::Entity) .from(Column::BakeryId) .to(super::bakery::Column::Id) + .on_delete(ForeignKeyAction::Cascade) + .on_update(ForeignKeyAction::Cascade) .into(), Self::Customer => Entity::belongs_to(super::customer::Entity) .from(Column::CustomerId) .to(super::customer::Column::Id) + .on_delete(ForeignKeyAction::Cascade) + .on_update(ForeignKeyAction::Cascade) .into(), Self::Lineitem => Entity::has_many(super::lineitem::Entity).into(), } diff --git a/tests/common/setup/schema.rs b/tests/common/setup/schema.rs index 7e668f29..c9523572 100644 --- a/tests/common/setup/schema.rs +++ b/tests/common/setup/schema.rs @@ -68,8 +68,8 @@ pub async fn create_baker_table(db: &DbConn) -> Result { .name("fk-baker-bakery") .from(baker::Entity, baker::Column::BakeryId) .to(bakery::Entity, bakery::Column::Id) - // .on_delete(ForeignKeyAction::Cascade) - // .on_update(ForeignKeyAction::Cascade), + .on_delete(ForeignKeyAction::Cascade) + .on_update(ForeignKeyAction::Cascade), ) .to_owned(); @@ -126,16 +126,16 @@ pub async fn create_order_table(db: &DbConn) -> Result { .name("fk-order-bakery") .from(order::Entity, order::Column::BakeryId) .to(bakery::Entity, bakery::Column::Id) - // .on_delete(ForeignKeyAction::Cascade) - // .on_update(ForeignKeyAction::Cascade), + .on_delete(ForeignKeyAction::Cascade) + .on_update(ForeignKeyAction::Cascade), ) .foreign_key( ForeignKey::create() .name("fk-order-customer") .from(order::Entity, order::Column::CustomerId) .to(customer::Entity, customer::Column::Id) - // .on_delete(ForeignKeyAction::Cascade) - // .on_update(ForeignKeyAction::Cascade), + .on_delete(ForeignKeyAction::Cascade) + .on_update(ForeignKeyAction::Cascade), ) .to_owned(); @@ -178,16 +178,16 @@ pub async fn create_lineitem_table(db: &DbConn) -> Result { .name("fk-lineitem-order") .from(lineitem::Entity, lineitem::Column::OrderId) .to(order::Entity, order::Column::Id) - // .on_delete(ForeignKeyAction::Cascade) - // .on_update(ForeignKeyAction::Cascade), + .on_delete(ForeignKeyAction::Cascade) + .on_update(ForeignKeyAction::Cascade), ) .foreign_key( ForeignKey::create() .name("fk-lineitem-cake") .from(lineitem::Entity, lineitem::Column::CakeId) .to(cake::Entity, cake::Column::Id) - // .on_delete(ForeignKeyAction::Cascade) - // .on_update(ForeignKeyAction::Cascade), + .on_delete(ForeignKeyAction::Cascade) + .on_update(ForeignKeyAction::Cascade), ) .to_owned(); @@ -219,16 +219,16 @@ pub async fn create_cakes_bakers_table(db: &DbConn) -> Result .name("fk-cakes_bakers-cake") .from(cakes_bakers::Entity, cakes_bakers::Column::CakeId) .to(cake::Entity, cake::Column::Id) - // .on_delete(ForeignKeyAction::Cascade) - // .on_update(ForeignKeyAction::Cascade), + .on_delete(ForeignKeyAction::Cascade) + .on_update(ForeignKeyAction::Cascade), ) .foreign_key( ForeignKey::create() .name("fk-cakes_bakers-baker") .from(cakes_bakers::Entity, cakes_bakers::Column::BakerId) .to(baker::Entity, baker::Column::Id) - // .on_delete(ForeignKeyAction::Cascade) - // .on_update(ForeignKeyAction::Cascade), + .on_delete(ForeignKeyAction::Cascade) + .on_update(ForeignKeyAction::Cascade), ) .to_owned(); @@ -258,8 +258,8 @@ pub async fn create_cake_table(db: &DbConn) -> Result { .name("fk-cake-bakery") .from(cake::Entity, cake::Column::BakeryId) .to(bakery::Entity, bakery::Column::Id) - // .on_delete(ForeignKeyAction::Cascade) - // .on_update(ForeignKeyAction::Cascade), + .on_delete(ForeignKeyAction::Cascade) + .on_update(ForeignKeyAction::Cascade), ) .col( ColumnDef::new(cake::Column::GlutenFree) From 56a07d58c3b469727a37972be5fd4a3cac52e8fe Mon Sep 17 00:00:00 2001 From: Billy Chan Date: Wed, 1 Sep 2021 10:37:36 +0800 Subject: [PATCH 10/12] Update sea-query dep --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index e303dac6..885c8144 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -32,7 +32,7 @@ futures = { version = "^0.3" } futures-util = { version = "^0.3" } rust_decimal = { version = "^1", optional = true } sea-orm-macros = { version = "^0.1.1", optional = true } -sea-query = { version = "^0.15", git = "https://github.com/SeaQL/sea-query.git", branch = "sea-orm/create-table-stmt", features = ["thread-safe"] } +sea-query = { version = "^0.15", git = "https://github.com/SeaQL/sea-query.git", features = ["thread-safe"] } sea-strum = { version = "^0.21", features = ["derive", "sea-orm"] } serde = { version = "^1.0", features = ["derive"] } sqlx = { version = "^0.5", optional = true } From ba4b938b69c8954ea22dd916faef8c4888893773 Mon Sep 17 00:00:00 2001 From: Billy Chan Date: Wed, 1 Sep 2021 13:02:57 +0800 Subject: [PATCH 11/12] Revert ColumnType::Binary changes --- src/entity/column.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/entity/column.rs b/src/entity/column.rs index b58a181c..611950f5 100644 --- a/src/entity/column.rs +++ b/src/entity/column.rs @@ -26,7 +26,7 @@ pub enum ColumnType { Timestamp, Time, Date, - Binary(Option), + Binary, Boolean, Money(Option<(u32, u32)>), Json, @@ -276,7 +276,7 @@ impl From for sea_query::ColumnType { ColumnType::Timestamp => sea_query::ColumnType::Timestamp(None), ColumnType::Time => sea_query::ColumnType::Time(None), ColumnType::Date => sea_query::ColumnType::Date, - ColumnType::Binary(s) => sea_query::ColumnType::Binary(s), + ColumnType::Binary => sea_query::ColumnType::Binary(None), ColumnType::Boolean => sea_query::ColumnType::Boolean, ColumnType::Money(s) => sea_query::ColumnType::Money(s), ColumnType::Json => sea_query::ColumnType::Json, @@ -307,7 +307,7 @@ impl From for ColumnType { sea_query::ColumnType::Timestamp(_) => Self::Timestamp, sea_query::ColumnType::Time(_) => Self::Time, sea_query::ColumnType::Date => Self::Date, - sea_query::ColumnType::Binary(s) => Self::Binary(s), + sea_query::ColumnType::Binary(_) => Self::Binary, sea_query::ColumnType::Boolean => Self::Boolean, sea_query::ColumnType::Money(s) => Self::Money(s), sea_query::ColumnType::Json => Self::Json, From 4e111aea64040ebf8a536d5c9ae92185b9bc1a02 Mon Sep 17 00:00:00 2001 From: Billy Chan Date: Fri, 3 Sep 2021 11:54:24 +0800 Subject: [PATCH 12/12] Use sea-query 0.16 --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 885c8144..9b9d291d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -32,7 +32,7 @@ futures = { version = "^0.3" } futures-util = { version = "^0.3" } rust_decimal = { version = "^1", optional = true } sea-orm-macros = { version = "^0.1.1", optional = true } -sea-query = { version = "^0.15", git = "https://github.com/SeaQL/sea-query.git", features = ["thread-safe"] } +sea-query = { version = "^0.16", features = ["thread-safe"] } sea-strum = { version = "^0.21", features = ["derive", "sea-orm"] } serde = { version = "^1.0", features = ["derive"] } sqlx = { version = "^0.5", optional = true }