From 83a2b540b960123e51d7c787e17be5c9d9fca8c7 Mon Sep 17 00:00:00 2001 From: Bobby Ng Date: Mon, 23 Aug 2021 18:19:20 +0800 Subject: [PATCH 01/43] 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 31941d3af678eb46d510c31c941fa056d5d01015 Mon Sep 17 00:00:00 2001 From: Billy Chan Date: Mon, 23 Aug 2021 23:11:08 +0800 Subject: [PATCH 02/43] WIP: Linked (#89) --- src/entity/column.rs | 2 +- src/entity/model.rs | 9 ++++++- src/entity/prelude.rs | 6 ++--- src/entity/relation.rs | 16 ++++++++++++ src/query/join.rs | 55 +++++++++++++++++++++++++++++++++++++++++- src/tests_cfg/cake.rs | 15 ++++++++++++ 6 files changed, 97 insertions(+), 6 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/model.rs b/src/entity/model.rs index 15ebdb58..2672d8cc 100644 --- a/src/entity/model.rs +++ b/src/entity/model.rs @@ -1,4 +1,4 @@ -use crate::{DbErr, EntityTrait, QueryFilter, QueryResult, Related, Select}; +use crate::{DbErr, EntityTrait, Linked, QueryFilter, QueryResult, Related, Select}; pub use sea_query::Value; use std::fmt::Debug; @@ -16,6 +16,13 @@ pub trait ModelTrait: Clone + Debug { { >::find_related().belongs_to(self) } + + fn find_linked(&self, _: L) -> Select + where + L: Linked, + { + L::find_linked() + } } pub trait FromQueryResult { diff --git a/src/entity/prelude.rs b/src/entity/prelude.rs index 447117b7..cda217d9 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, Iden, IdenStatic, Linked, + 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 955660e2..f75b3a89 100644 --- a/src/entity/relation.rs +++ b/src/entity/relation.rs @@ -28,6 +28,22 @@ where } } +pub trait Linked { + type FromEntity: EntityTrait; + + type ToEntity: EntityTrait; + + fn link() -> Vec; + + fn find_linked() -> Select { + let mut select = Select::new(); + for rel in Self::link() { + select = select.join(JoinType::InnerJoin, rel); + } + select + } +} + pub struct RelationDef { pub rel_type: RelationType, pub from_tbl: TableRef, diff --git a/src/query/join.rs b/src/query/join.rs index 72726d14..12999db8 100644 --- a/src/query/join.rs +++ b/src/query/join.rs @@ -1,4 +1,4 @@ -use crate::{EntityTrait, QuerySelect, Related, Select, SelectTwo, SelectTwoMany}; +use crate::{EntityTrait, Linked, QuerySelect, Related, Select, SelectTwo, SelectTwoMany}; pub use sea_query::JoinType; impl Select @@ -57,6 +57,19 @@ where { self.left_join(r).select_with(r) } + + /// Left Join with a Linked Entity and select both Entity. + pub fn find_also_linked(self, _: L) -> SelectTwo + where + L: Linked, + T: EntityTrait, + { + let mut slf = self; + for rel in L::link() { + slf = slf.join(JoinType::LeftJoin, rel); + } + slf.select_also(T::default()) + } } #[cfg(test)] @@ -220,4 +233,44 @@ mod tests { .join(" ") ); } + + #[test] + fn join_10() { + let cake_model = cake::Model { + id: 12, + name: "".to_owned(), + }; + + assert_eq!( + cake_model + .find_linked(cake::CakeToFilling) + .build(DbBackend::MySql) + .to_string(), + [ + r#"SELECT `filling`.`id`, `filling`.`name`"#, + r#"FROM `filling`"#, + r#"INNER JOIN `cake_filling` ON `cake`.`id` = `cake_filling`.`cake_id`"#, + r#"INNER JOIN `filling` ON `cake_filling`.`filling_id` = `filling`.`id`"#, + ] + .join(" ") + ); + } + + #[test] + fn join_11() { + assert_eq!( + cake::Entity::find() + .find_also_linked(cake::CakeToFilling) + .build(DbBackend::MySql) + .to_string(), + [ + r#"SELECT `cake`.`id` AS `A_id`, `cake`.`name` AS `A_name`,"#, + r#"`filling`.`id` AS `B_id`, `filling`.`name` AS `B_name`"#, + r#"FROM `cake`"#, + r#"LEFT JOIN `cake_filling` ON `cake`.`id` = `cake_filling`.`cake_id`"#, + r#"LEFT JOIN `filling` ON `cake_filling`.`filling_id` = `filling`.`id`"#, + ] + .join(" ") + ); + } } diff --git a/src/tests_cfg/cake.rs b/src/tests_cfg/cake.rs index f8a35d6c..56ca8797 100644 --- a/src/tests_cfg/cake.rs +++ b/src/tests_cfg/cake.rs @@ -73,4 +73,19 @@ impl Related for Entity { } } +pub struct CakeToFilling; + +impl Linked for CakeToFilling { + type FromEntity = Entity; + + type ToEntity = super::filling::Entity; + + fn link() -> Vec { + vec![ + super::cake_filling::Relation::Cake.def().rev(), + super::cake_filling::Relation::Filling.def(), + ] + } +} + impl ActiveModelBehavior for ActiveModel {} From d80ea396abce30044e78b7e71a223d8999858436 Mon Sep 17 00:00:00 2001 From: Bobby Ng Date: Thu, 26 Aug 2021 18:15:29 +0800 Subject: [PATCH 03/43] 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 447947e3556c36ec441e844fd8e0db7bfa29d311 Mon Sep 17 00:00:00 2001 From: Billy Chan Date: Thu, 26 Aug 2021 23:28:27 +0800 Subject: [PATCH 04/43] Fix find_linked join direction --- src/entity/relation.rs | 4 ++-- src/query/join.rs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/entity/relation.rs b/src/entity/relation.rs index f75b3a89..441a0524 100644 --- a/src/entity/relation.rs +++ b/src/entity/relation.rs @@ -37,8 +37,8 @@ pub trait Linked { fn find_linked() -> Select { let mut select = Select::new(); - for rel in Self::link() { - select = select.join(JoinType::InnerJoin, rel); + for rel in Self::link().into_iter().rev() { + select = select.join_rev(JoinType::InnerJoin, rel); } select } diff --git a/src/query/join.rs b/src/query/join.rs index 12999db8..be759e97 100644 --- a/src/query/join.rs +++ b/src/query/join.rs @@ -249,8 +249,8 @@ mod tests { [ r#"SELECT `filling`.`id`, `filling`.`name`"#, r#"FROM `filling`"#, - r#"INNER JOIN `cake_filling` ON `cake`.`id` = `cake_filling`.`cake_id`"#, - r#"INNER JOIN `filling` ON `cake_filling`.`filling_id` = `filling`.`id`"#, + r#"INNER JOIN `cake_filling` ON `cake_filling`.`filling_id` = `filling`.`id`"#, + r#"INNER JOIN `cake` ON `cake`.`id` = `cake_filling`.`cake_id`"#, ] .join(" ") ); From bfaa7d4539afc033cb3f240fa4fed4ff1344c7c0 Mon Sep 17 00:00:00 2001 From: Billy Chan Date: Thu, 26 Aug 2021 23:28:44 +0800 Subject: [PATCH 05/43] Test Linked --- tests/common/bakery_chain/baker.rs | 18 +++ tests/relational_tests.rs | 202 ++++++++++++++++++++++++++++- 2 files changed, 219 insertions(+), 1 deletion(-) diff --git a/tests/common/bakery_chain/baker.rs b/tests/common/bakery_chain/baker.rs index 0c63e721..287e5deb 100644 --- a/tests/common/bakery_chain/baker.rs +++ b/tests/common/bakery_chain/baker.rs @@ -81,4 +81,22 @@ impl Related for Entity { } } +pub struct BakedForCustomer; + +impl Linked for BakedForCustomer { + type FromEntity = Entity; + + type ToEntity = super::customer::Entity; + + fn link() -> Vec { + vec![ + super::cakes_bakers::Relation::Baker.def().rev(), + super::cakes_bakers::Relation::Cake.def(), + super::lineitem::Relation::Cake.def().rev(), + super::lineitem::Relation::Order.def(), + super::order::Relation::Customer.def(), + ] + } +} + impl ActiveModelBehavior for ActiveModel {} diff --git a/tests/relational_tests.rs b/tests/relational_tests.rs index ae1236a2..1ddee902 100644 --- a/tests/relational_tests.rs +++ b/tests/relational_tests.rs @@ -1,7 +1,8 @@ use chrono::offset::Utc; use rust_decimal::prelude::*; use rust_decimal_macros::dec; -use sea_orm::{entity::*, query::*, FromQueryResult}; +use sea_orm::{entity::*, query::*, DbErr, FromQueryResult}; +use uuid::Uuid; pub mod common; pub use common::{bakery_chain::*, setup::*, TestContext}; @@ -474,3 +475,202 @@ pub async fn having() { ctx.delete().await; } + +#[sea_orm_macros::test] +#[cfg(any( + feature = "sqlx-mysql", + feature = "sqlx-sqlite", + feature = "sqlx-postgres" +))] +pub async fn linked() -> Result<(), DbErr> { + use common::bakery_chain::Order; + + let ctx = TestContext::new("test_linked").await; + + // SeaSide Bakery + let seaside_bakery = bakery::ActiveModel { + name: Set("SeaSide Bakery".to_owned()), + profit_margin: Set(10.4), + ..Default::default() + }; + let seaside_bakery_res: InsertResult = Bakery::insert(seaside_bakery).exec(&ctx.db).await?; + + // Bob's Baker, Cake & Cake Baker + let baker_bob = baker::ActiveModel { + name: Set("Baker Bob".to_owned()), + contact_details: Set(serde_json::json!({ + "mobile": "+61424000000", + "home": "0395555555", + "address": "12 Test St, Testville, Vic, Australia" + })), + bakery_id: Set(Some(seaside_bakery_res.last_insert_id as i32)), + ..Default::default() + }; + let baker_bob_res: InsertResult = Baker::insert(baker_bob).exec(&ctx.db).await?; + let mud_cake = cake::ActiveModel { + name: Set("Mud Cake".to_owned()), + price: Set(dec!(10.25)), + gluten_free: Set(false), + serial: Set(Uuid::new_v4()), + bakery_id: Set(Some(seaside_bakery_res.last_insert_id as i32)), + ..Default::default() + }; + let mud_cake_res: InsertResult = Cake::insert(mud_cake).exec(&ctx.db).await?; + let bob_cakes_bakers = cakes_bakers::ActiveModel { + cake_id: Set(mud_cake_res.last_insert_id as i32), + baker_id: Set(baker_bob_res.last_insert_id as i32), + ..Default::default() + }; + CakesBakers::insert(bob_cakes_bakers).exec(&ctx.db).await?; + + // Bobby's Baker, Cake & Cake Baker + let baker_bobby = baker::ActiveModel { + name: Set("Baker Bobby".to_owned()), + contact_details: Set(serde_json::json!({ + "mobile": "+85212345678", + })), + bakery_id: Set(Some(seaside_bakery_res.last_insert_id as i32)), + ..Default::default() + }; + let baker_bobby_res: InsertResult = Baker::insert(baker_bobby).exec(&ctx.db).await?; + let cheese_cake = cake::ActiveModel { + name: Set("Cheese Cake".to_owned()), + price: Set(dec!(20.5)), + gluten_free: Set(false), + serial: Set(Uuid::new_v4()), + bakery_id: Set(Some(seaside_bakery_res.last_insert_id as i32)), + ..Default::default() + }; + let cheese_cake_res: InsertResult = Cake::insert(cheese_cake).exec(&ctx.db).await?; + let bobby_cakes_bakers = cakes_bakers::ActiveModel { + cake_id: Set(cheese_cake_res.last_insert_id as i32), + baker_id: Set(baker_bobby_res.last_insert_id as i32), + ..Default::default() + }; + CakesBakers::insert(bobby_cakes_bakers) + .exec(&ctx.db) + .await?; + let chocolate_cake = cake::ActiveModel { + name: Set("Chocolate Cake".to_owned()), + price: Set(dec!(30.15)), + gluten_free: Set(false), + serial: Set(Uuid::new_v4()), + bakery_id: Set(Some(seaside_bakery_res.last_insert_id as i32)), + ..Default::default() + }; + let chocolate_cake_res: InsertResult = Cake::insert(chocolate_cake).exec(&ctx.db).await?; + let bobby_cakes_bakers = cakes_bakers::ActiveModel { + cake_id: Set(chocolate_cake_res.last_insert_id as i32), + baker_id: Set(baker_bobby_res.last_insert_id as i32), + ..Default::default() + }; + CakesBakers::insert(bobby_cakes_bakers) + .exec(&ctx.db) + .await?; + + // Kate's Customer, Order & Line Item + let customer_kate = customer::ActiveModel { + name: Set("Kate".to_owned()), + notes: Set(Some("Loves cheese cake".to_owned())), + ..Default::default() + }; + let customer_kate_res: InsertResult = Customer::insert(customer_kate).exec(&ctx.db).await?; + let kate_order_1 = order::ActiveModel { + bakery_id: Set(seaside_bakery_res.last_insert_id as i32), + customer_id: Set(customer_kate_res.last_insert_id as i32), + total: Set(dec!(15.10)), + placed_at: Set(Utc::now().naive_utc()), + ..Default::default() + }; + let kate_order_1_res: InsertResult = Order::insert(kate_order_1).exec(&ctx.db).await?; + lineitem::ActiveModel { + cake_id: Set(cheese_cake_res.last_insert_id as i32), + order_id: Set(kate_order_1_res.last_insert_id as i32), + price: Set(dec!(7.55)), + quantity: Set(2), + ..Default::default() + } + .save(&ctx.db) + .await?; + let kate_order_2 = order::ActiveModel { + bakery_id: Set(seaside_bakery_res.last_insert_id as i32), + customer_id: Set(customer_kate_res.last_insert_id as i32), + total: Set(dec!(29.7)), + placed_at: Set(Utc::now().naive_utc()), + ..Default::default() + }; + let kate_order_2_res: InsertResult = Order::insert(kate_order_2).exec(&ctx.db).await?; + lineitem::ActiveModel { + cake_id: Set(chocolate_cake_res.last_insert_id as i32), + order_id: Set(kate_order_2_res.last_insert_id as i32), + price: Set(dec!(9.9)), + quantity: Set(3), + ..Default::default() + } + .save(&ctx.db) + .await?; + + // Kara's Customer, Order & Line Item + let customer_kara = customer::ActiveModel { + name: Set("Kara".to_owned()), + notes: Set(Some("Loves all cakes".to_owned())), + ..Default::default() + }; + let customer_kara_res: InsertResult = Customer::insert(customer_kara).exec(&ctx.db).await?; + let kara_order_1 = order::ActiveModel { + bakery_id: Set(seaside_bakery_res.last_insert_id as i32), + customer_id: Set(customer_kara_res.last_insert_id as i32), + total: Set(dec!(15.10)), + placed_at: Set(Utc::now().naive_utc()), + ..Default::default() + }; + let kara_order_1_res: InsertResult = Order::insert(kara_order_1).exec(&ctx.db).await?; + lineitem::ActiveModel { + cake_id: Set(mud_cake_res.last_insert_id as i32), + order_id: Set(kara_order_1_res.last_insert_id as i32), + price: Set(dec!(7.55)), + quantity: Set(2), + ..Default::default() + } + .save(&ctx.db) + .await?; + let kara_order_2 = order::ActiveModel { + bakery_id: Set(seaside_bakery_res.last_insert_id as i32), + customer_id: Set(customer_kara_res.last_insert_id as i32), + total: Set(dec!(29.7)), + placed_at: Set(Utc::now().naive_utc()), + ..Default::default() + }; + let kara_order_2_res: InsertResult = Order::insert(kara_order_2).exec(&ctx.db).await?; + lineitem::ActiveModel { + cake_id: Set(cheese_cake_res.last_insert_id as i32), + order_id: Set(kara_order_2_res.last_insert_id as i32), + price: Set(dec!(9.9)), + quantity: Set(3), + ..Default::default() + } + .save(&ctx.db) + .await?; + + /* + SELECT `baker`.`id` AS `A_id`, `baker`.`name` AS `A_name`, + `baker`.`contact_details` AS `A_contact_details`, + `baker`.`bakery_id` AS `A_bakery_id`, `customer`.`id` AS `B_id`, + `customer`.`name` AS `B_name`, `customer`.`notes` AS `B_notes` + FROM `baker` + LEFT JOIN `cakes_bakers` ON `baker`.`id` = `cakes_bakers`.`baker_id` + LEFT JOIN `cake` ON `cakes_bakers`.`cake_id` = `cake`.`id` + LEFT JOIN `lineitem` ON `cake`.`id` = `lineitem`.`cake_id` + LEFT JOIN `order` ON `lineitem`.`order_id` = `order`.`id` + LEFT JOIN `customer` ON `order`.`customer_id` = `customer`.`id` + */ + let baked_for_customers = Baker::find() + .find_also_linked(baker::BakedForCustomer) + .all(&ctx.db) + .await?; + println!("{:#?}", baked_for_customers); + + ctx.delete().await; + + Ok(()) +} From 484da8f6b61abf124be8ace694be95e731664f01 Mon Sep 17 00:00:00 2001 From: Billy Chan Date: Sat, 28 Aug 2021 20:00:59 +0800 Subject: [PATCH 06/43] WIP --- examples/async-std/src/example_cake.rs | 2 ++ .../async-std/src/example_cake_filling.rs | 2 ++ examples/async-std/src/example_filling.rs | 2 ++ examples/async-std/src/example_fruit.rs | 2 ++ examples/tokio/src/cake.rs | 2 ++ sea-orm-codegen/src/entity/base_entity.rs | 24 ++++++++++++++++++- sea-orm-codegen/src/entity/writer.rs | 3 +++ sea-orm-codegen/tests/entity/cake.rs | 2 ++ sea-orm-codegen/tests/entity/cake_filling.rs | 2 ++ sea-orm-codegen/tests/entity/filling.rs | 2 ++ sea-orm-codegen/tests/entity/fruit.rs | 2 ++ sea-orm-codegen/tests/entity/vendor.rs | 2 ++ src/entity/column.rs | 2 +- src/entity/primary_key.rs | 2 ++ src/tests_cfg/cake.rs | 2 ++ src/tests_cfg/cake_filling.rs | 2 ++ src/tests_cfg/cake_filling_price.rs | 2 ++ src/tests_cfg/filling.rs | 2 ++ src/tests_cfg/fruit.rs | 2 ++ tests/common/bakery_chain/baker.rs | 2 ++ tests/common/bakery_chain/bakery.rs | 2 ++ tests/common/bakery_chain/cake.rs | 2 ++ tests/common/bakery_chain/cakes_bakers.rs | 2 ++ tests/common/bakery_chain/customer.rs | 2 ++ tests/common/bakery_chain/lineitem.rs | 2 ++ tests/common/bakery_chain/order.rs | 2 ++ 26 files changed, 73 insertions(+), 2 deletions(-) diff --git a/examples/async-std/src/example_cake.rs b/examples/async-std/src/example_cake.rs index 475315e8..114347ee 100644 --- a/examples/async-std/src/example_cake.rs +++ b/examples/async-std/src/example_cake.rs @@ -27,6 +27,8 @@ pub enum PrimaryKey { } impl PrimaryKeyTrait for PrimaryKey { + type ValueType = i32; + fn auto_increment() -> bool { true } diff --git a/examples/async-std/src/example_cake_filling.rs b/examples/async-std/src/example_cake_filling.rs index 19de83e4..c24e7296 100644 --- a/examples/async-std/src/example_cake_filling.rs +++ b/examples/async-std/src/example_cake_filling.rs @@ -28,6 +28,8 @@ pub enum PrimaryKey { } impl PrimaryKeyTrait for PrimaryKey { + type ValueType = i32; + fn auto_increment() -> bool { false } diff --git a/examples/async-std/src/example_filling.rs b/examples/async-std/src/example_filling.rs index 925b92fc..2a39a7de 100644 --- a/examples/async-std/src/example_filling.rs +++ b/examples/async-std/src/example_filling.rs @@ -27,6 +27,8 @@ pub enum PrimaryKey { } impl PrimaryKeyTrait for PrimaryKey { + type ValueType = i32; + fn auto_increment() -> bool { true } diff --git a/examples/async-std/src/example_fruit.rs b/examples/async-std/src/example_fruit.rs index b875da24..8802e707 100644 --- a/examples/async-std/src/example_fruit.rs +++ b/examples/async-std/src/example_fruit.rs @@ -29,6 +29,8 @@ pub enum PrimaryKey { } impl PrimaryKeyTrait for PrimaryKey { + type ValueType = i32; + fn auto_increment() -> bool { true } diff --git a/examples/tokio/src/cake.rs b/examples/tokio/src/cake.rs index 0b1a4439..21b83331 100644 --- a/examples/tokio/src/cake.rs +++ b/examples/tokio/src/cake.rs @@ -27,6 +27,8 @@ pub enum PrimaryKey { } impl PrimaryKeyTrait for PrimaryKey { + type ValueType = i32; + fn auto_increment() -> bool { true } diff --git a/sea-orm-codegen/src/entity/base_entity.rs b/sea-orm-codegen/src/entity/base_entity.rs index f1e03b21..2cc6dbb4 100644 --- a/sea-orm-codegen/src/entity/base_entity.rs +++ b/sea-orm-codegen/src/entity/base_entity.rs @@ -117,6 +117,18 @@ impl Entity { format_ident!("{}", auto_increment) } + pub fn get_primary_key_rs_type(&self) -> TokenStream { + if let Some(primary_key) = self.primary_keys.first() { + self.columns + .iter() + .find(|col| col.name.eq(&primary_key.name)) + .unwrap() + .get_rs_type() + } else { + TokenStream::new() + } + } + pub fn get_conjunct_relations_via_snake_case(&self) -> Vec { self.conjunct_relations .iter() @@ -151,7 +163,7 @@ mod tests { columns: vec![ Column { name: "id".to_owned(), - col_type: ColumnType::String(None), + col_type: ColumnType::Integer(None), auto_increment: false, not_null: false, unique: false, @@ -373,6 +385,16 @@ mod tests { ); } + #[test] + fn test_get_primary_key_rs_type() { + let entity = setup(); + + assert_eq!( + entity.get_primary_key_rs_type().to_string(), + entity.columns[0].get_rs_type().to_string() + ); + } + #[test] fn test_get_conjunct_relations_via_snake_case() { let entity = setup(); diff --git a/sea-orm-codegen/src/entity/writer.rs b/sea-orm-codegen/src/entity/writer.rs index 207864ba..7accac2d 100644 --- a/sea-orm-codegen/src/entity/writer.rs +++ b/sea-orm-codegen/src/entity/writer.rs @@ -173,8 +173,11 @@ impl EntityWriter { pub fn gen_impl_primary_key(entity: &Entity) -> TokenStream { let primary_key_auto_increment = entity.get_primary_key_auto_increment(); + let value_type = entity.get_primary_key_rs_type(); quote! { impl PrimaryKeyTrait for PrimaryKey { + type ValueType = #value_type; + fn auto_increment() -> bool { #primary_key_auto_increment } diff --git a/sea-orm-codegen/tests/entity/cake.rs b/sea-orm-codegen/tests/entity/cake.rs index 29f55ac6..55fa279f 100644 --- a/sea-orm-codegen/tests/entity/cake.rs +++ b/sea-orm-codegen/tests/entity/cake.rs @@ -29,6 +29,8 @@ pub enum PrimaryKey { } impl PrimaryKeyTrait for PrimaryKey { + type ValueType = i32; + fn auto_increment() -> bool { true } diff --git a/sea-orm-codegen/tests/entity/cake_filling.rs b/sea-orm-codegen/tests/entity/cake_filling.rs index d0f00560..227740c1 100644 --- a/sea-orm-codegen/tests/entity/cake_filling.rs +++ b/sea-orm-codegen/tests/entity/cake_filling.rs @@ -30,6 +30,8 @@ pub enum PrimaryKey { } impl PrimaryKeyTrait for PrimaryKey { + type ValueType = i32; + fn auto_increment() -> bool { false } diff --git a/sea-orm-codegen/tests/entity/filling.rs b/sea-orm-codegen/tests/entity/filling.rs index bedb1ab4..73d58152 100644 --- a/sea-orm-codegen/tests/entity/filling.rs +++ b/sea-orm-codegen/tests/entity/filling.rs @@ -29,6 +29,8 @@ pub enum PrimaryKey { } impl PrimaryKeyTrait for PrimaryKey { + type ValueType = i32; + fn auto_increment() -> bool { true } diff --git a/sea-orm-codegen/tests/entity/fruit.rs b/sea-orm-codegen/tests/entity/fruit.rs index 72c37c1b..12919021 100644 --- a/sea-orm-codegen/tests/entity/fruit.rs +++ b/sea-orm-codegen/tests/entity/fruit.rs @@ -31,6 +31,8 @@ pub enum PrimaryKey { } impl PrimaryKeyTrait for PrimaryKey { + type ValueType = i32; + fn auto_increment() -> bool { true } diff --git a/sea-orm-codegen/tests/entity/vendor.rs b/sea-orm-codegen/tests/entity/vendor.rs index 320400f4..ffc211a8 100644 --- a/sea-orm-codegen/tests/entity/vendor.rs +++ b/sea-orm-codegen/tests/entity/vendor.rs @@ -31,6 +31,8 @@ pub enum PrimaryKey { } impl PrimaryKeyTrait for PrimaryKey { + type ValueType = i32; + fn auto_increment() -> bool { true } 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/primary_key.rs b/src/entity/primary_key.rs index 81a28915..a36b04e0 100644 --- a/src/entity/primary_key.rs +++ b/src/entity/primary_key.rs @@ -2,6 +2,8 @@ use super::{ColumnTrait, IdenStatic, Iterable}; //LINT: composite primary key cannot auto increment pub trait PrimaryKeyTrait: IdenStatic + Iterable { + type ValueType: Sized; + fn auto_increment() -> bool; } diff --git a/src/tests_cfg/cake.rs b/src/tests_cfg/cake.rs index f8a35d6c..b4306313 100644 --- a/src/tests_cfg/cake.rs +++ b/src/tests_cfg/cake.rs @@ -28,6 +28,8 @@ pub enum PrimaryKey { } impl PrimaryKeyTrait for PrimaryKey { + type ValueType = i32; + fn auto_increment() -> bool { true } diff --git a/src/tests_cfg/cake_filling.rs b/src/tests_cfg/cake_filling.rs index b1151ee4..4336d2fa 100644 --- a/src/tests_cfg/cake_filling.rs +++ b/src/tests_cfg/cake_filling.rs @@ -29,6 +29,8 @@ pub enum PrimaryKey { } impl PrimaryKeyTrait for PrimaryKey { + type ValueType = i32; + fn auto_increment() -> bool { false } diff --git a/src/tests_cfg/cake_filling_price.rs b/src/tests_cfg/cake_filling_price.rs index c0bcbea1..bd2bc8eb 100644 --- a/src/tests_cfg/cake_filling_price.rs +++ b/src/tests_cfg/cake_filling_price.rs @@ -35,6 +35,8 @@ pub enum PrimaryKey { } impl PrimaryKeyTrait for PrimaryKey { + type ValueType = i32; + fn auto_increment() -> bool { false } diff --git a/src/tests_cfg/filling.rs b/src/tests_cfg/filling.rs index b439af7b..5e691980 100644 --- a/src/tests_cfg/filling.rs +++ b/src/tests_cfg/filling.rs @@ -41,6 +41,8 @@ pub enum PrimaryKey { } impl PrimaryKeyTrait for PrimaryKey { + type ValueType = i32; + fn auto_increment() -> bool { true } diff --git a/src/tests_cfg/fruit.rs b/src/tests_cfg/fruit.rs index 0511ae58..b0b8bc79 100644 --- a/src/tests_cfg/fruit.rs +++ b/src/tests_cfg/fruit.rs @@ -30,6 +30,8 @@ pub enum PrimaryKey { } impl PrimaryKeyTrait for PrimaryKey { + type ValueType = i32; + fn auto_increment() -> bool { true } diff --git a/tests/common/bakery_chain/baker.rs b/tests/common/bakery_chain/baker.rs index 0c63e721..071a1fbc 100644 --- a/tests/common/bakery_chain/baker.rs +++ b/tests/common/bakery_chain/baker.rs @@ -31,6 +31,8 @@ pub enum PrimaryKey { } impl PrimaryKeyTrait for PrimaryKey { + type ValueType = i32; + fn auto_increment() -> bool { true } diff --git a/tests/common/bakery_chain/bakery.rs b/tests/common/bakery_chain/bakery.rs index 61803329..2baa7b6b 100644 --- a/tests/common/bakery_chain/bakery.rs +++ b/tests/common/bakery_chain/bakery.rs @@ -29,6 +29,8 @@ pub enum PrimaryKey { } impl PrimaryKeyTrait for PrimaryKey { + type ValueType = i32; + fn auto_increment() -> bool { true } diff --git a/tests/common/bakery_chain/cake.rs b/tests/common/bakery_chain/cake.rs index 72e649ce..f699773c 100644 --- a/tests/common/bakery_chain/cake.rs +++ b/tests/common/bakery_chain/cake.rs @@ -35,6 +35,8 @@ pub enum PrimaryKey { } impl PrimaryKeyTrait for PrimaryKey { + type ValueType = i32; + fn auto_increment() -> bool { true } diff --git a/tests/common/bakery_chain/cakes_bakers.rs b/tests/common/bakery_chain/cakes_bakers.rs index 8106bbdf..853cf6da 100644 --- a/tests/common/bakery_chain/cakes_bakers.rs +++ b/tests/common/bakery_chain/cakes_bakers.rs @@ -28,6 +28,8 @@ pub enum PrimaryKey { } impl PrimaryKeyTrait for PrimaryKey { + type ValueType = i32; + fn auto_increment() -> bool { false } diff --git a/tests/common/bakery_chain/customer.rs b/tests/common/bakery_chain/customer.rs index ce4319ff..51668131 100644 --- a/tests/common/bakery_chain/customer.rs +++ b/tests/common/bakery_chain/customer.rs @@ -29,6 +29,8 @@ pub enum PrimaryKey { } impl PrimaryKeyTrait for PrimaryKey { + type ValueType = i32; + fn auto_increment() -> bool { true } diff --git a/tests/common/bakery_chain/lineitem.rs b/tests/common/bakery_chain/lineitem.rs index 45a6037f..1d338ed1 100644 --- a/tests/common/bakery_chain/lineitem.rs +++ b/tests/common/bakery_chain/lineitem.rs @@ -33,6 +33,8 @@ pub enum PrimaryKey { } impl PrimaryKeyTrait for PrimaryKey { + type ValueType = i32; + fn auto_increment() -> bool { true } diff --git a/tests/common/bakery_chain/order.rs b/tests/common/bakery_chain/order.rs index 82b02dee..f2d48971 100644 --- a/tests/common/bakery_chain/order.rs +++ b/tests/common/bakery_chain/order.rs @@ -33,6 +33,8 @@ pub enum PrimaryKey { } impl PrimaryKeyTrait for PrimaryKey { + type ValueType = i32; + fn auto_increment() -> bool { true } From 9db0748c64844ac6d63b63aae691bd610125716d Mon Sep 17 00:00:00 2001 From: Billy Chan Date: Sat, 28 Aug 2021 20:20:34 +0800 Subject: [PATCH 07/43] Non-static link --- src/entity/model.rs | 4 ++-- src/entity/relation.rs | 6 +++--- src/query/join.rs | 4 ++-- src/tests_cfg/cake.rs | 2 +- tests/common/bakery_chain/baker.rs | 2 +- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/entity/model.rs b/src/entity/model.rs index 2672d8cc..4774e1dc 100644 --- a/src/entity/model.rs +++ b/src/entity/model.rs @@ -17,11 +17,11 @@ pub trait ModelTrait: Clone + Debug { >::find_related().belongs_to(self) } - fn find_linked(&self, _: L) -> Select + fn find_linked(&self, l: L) -> Select where L: Linked, { - L::find_linked() + l.find_linked() } } diff --git a/src/entity/relation.rs b/src/entity/relation.rs index 441a0524..29378f2a 100644 --- a/src/entity/relation.rs +++ b/src/entity/relation.rs @@ -33,11 +33,11 @@ pub trait Linked { type ToEntity: EntityTrait; - fn link() -> Vec; + fn link(&self) -> Vec; - fn find_linked() -> Select { + fn find_linked(&self) -> Select { let mut select = Select::new(); - for rel in Self::link().into_iter().rev() { + for rel in self.link().into_iter().rev() { select = select.join_rev(JoinType::InnerJoin, rel); } select diff --git a/src/query/join.rs b/src/query/join.rs index be759e97..42bc993c 100644 --- a/src/query/join.rs +++ b/src/query/join.rs @@ -59,13 +59,13 @@ where } /// Left Join with a Linked Entity and select both Entity. - pub fn find_also_linked(self, _: L) -> SelectTwo + pub fn find_also_linked(self, l: L) -> SelectTwo where L: Linked, T: EntityTrait, { let mut slf = self; - for rel in L::link() { + for rel in l.link() { slf = slf.join(JoinType::LeftJoin, rel); } slf.select_also(T::default()) diff --git a/src/tests_cfg/cake.rs b/src/tests_cfg/cake.rs index 56ca8797..32cc22fd 100644 --- a/src/tests_cfg/cake.rs +++ b/src/tests_cfg/cake.rs @@ -80,7 +80,7 @@ impl Linked for CakeToFilling { type ToEntity = super::filling::Entity; - fn link() -> Vec { + fn link(&self) -> Vec { vec![ super::cake_filling::Relation::Cake.def().rev(), super::cake_filling::Relation::Filling.def(), diff --git a/tests/common/bakery_chain/baker.rs b/tests/common/bakery_chain/baker.rs index 287e5deb..d3b01651 100644 --- a/tests/common/bakery_chain/baker.rs +++ b/tests/common/bakery_chain/baker.rs @@ -88,7 +88,7 @@ impl Linked for BakedForCustomer { type ToEntity = super::customer::Entity; - fn link() -> Vec { + fn link(&self) -> Vec { vec![ super::cakes_bakers::Relation::Baker.def().rev(), super::cakes_bakers::Relation::Cake.def(), From ccfda51a5e5f1e50265d7607af193753579e4c89 Mon Sep 17 00:00:00 2001 From: Billy Chan Date: Sat, 28 Aug 2021 23:48:13 +0800 Subject: [PATCH 08/43] WIP --- src/driver/sqlx_postgres.rs | 7 +++-- src/entity/active_model.rs | 8 +++-- src/entity/primary_key.rs | 5 +++- src/executor/insert.rs | 59 +++++++++++++++++++++++++++---------- 4 files changed, 58 insertions(+), 21 deletions(-) diff --git a/src/driver/sqlx_postgres.rs b/src/driver/sqlx_postgres.rs index a283ddf3..9c45413f 100644 --- a/src/driver/sqlx_postgres.rs +++ b/src/driver/sqlx_postgres.rs @@ -110,8 +110,11 @@ impl From for ExecResult { } } -pub(crate) fn query_result_into_exec_result(res: QueryResult) -> Result { - let last_insert_id: i32 = res.try_get("", "last_insert_id")?; +pub(crate) fn query_result_into_exec_result(res: QueryResult) -> Result +where + T: TryGetable, +{ + let last_insert_id: T = res.try_get("", "last_insert_id")?; Ok(ExecResult { result: ExecResultHolder::SqlxPostgres { last_insert_id: last_insert_id as u64, diff --git a/src/entity/active_model.rs b/src/entity/active_model.rs index 54bf037b..612e285d 100644 --- a/src/entity/active_model.rs +++ b/src/entity/active_model.rs @@ -221,7 +221,11 @@ where let exec = E::insert(am).exec(db); let res = exec.await?; // TODO: if the entity does not have auto increment primary key, then last_insert_id is a wrong value - if ::auto_increment() && res.last_insert_id != 0 { + // FIXME: Assumed valid last_insert_id is not equals to Default::default() + if ::auto_increment() + && res.last_insert_id != ::ValueType::default() + { + let last_insert_id = res.last_insert_id.to_string(); let find = E::find_by_id(res.last_insert_id).one(db); let found = find.await; let model: Option = found?; @@ -230,7 +234,7 @@ where None => Err(DbErr::Exec(format!( "Failed to find inserted item: {} {}", E::default().to_string(), - res.last_insert_id + last_insert_id ))), } } else { diff --git a/src/entity/primary_key.rs b/src/entity/primary_key.rs index a36b04e0..1d434c13 100644 --- a/src/entity/primary_key.rs +++ b/src/entity/primary_key.rs @@ -1,8 +1,11 @@ use super::{ColumnTrait, IdenStatic, Iterable}; +use crate::TryGetable; +use sea_query::IntoValueTuple; +use std::fmt::{Debug, Display}; //LINT: composite primary key cannot auto increment pub trait PrimaryKeyTrait: IdenStatic + Iterable { - type ValueType: Sized; + type ValueType: Sized + Default + Debug + Display + PartialEq + IntoValueTuple + TryGetable; fn auto_increment() -> bool; } diff --git a/src/executor/insert.rs b/src/executor/insert.rs index 9414fea6..a644d6e9 100644 --- a/src/executor/insert.rs +++ b/src/executor/insert.rs @@ -1,15 +1,24 @@ -use crate::{error::*, ActiveModelTrait, DatabaseConnection, Insert, Statement}; +use crate::{ + error::*, ActiveModelTrait, DatabaseConnection, EntityTrait, Insert, PrimaryKeyTrait, Statement, +}; use sea_query::InsertStatement; -use std::future::Future; +use std::{future::Future, marker::PhantomData}; #[derive(Clone, Debug)] -pub struct Inserter { +pub struct Inserter +where + A: ActiveModelTrait, +{ query: InsertStatement, + model: PhantomData, } -#[derive(Clone, Debug)] -pub struct InsertResult { - pub last_insert_id: u64, +#[derive(Debug)] +pub struct InsertResult +where + A: ActiveModelTrait, +{ + pub last_insert_id: <<::Entity as EntityTrait>::PrimaryKey as PrimaryKeyTrait>::ValueType, } impl Insert @@ -17,10 +26,13 @@ where A: ActiveModelTrait, { #[allow(unused_mut)] - pub fn exec( + pub fn exec<'a>( self, - db: &DatabaseConnection, - ) -> impl Future> + '_ { + db: &'a DatabaseConnection, + ) -> impl Future, DbErr>> + 'a + where + A: 'a, + { // so that self is dropped before entering await let mut query = self.query; #[cfg(feature = "sqlx-postgres")] @@ -35,26 +47,41 @@ where ); } } - Inserter::new(query).exec(db) + Inserter::::new(query).exec(db) } } -impl Inserter { +impl Inserter +where + A: ActiveModelTrait, +{ pub fn new(query: InsertStatement) -> Self { - Self { query } + Self { + query, + model: PhantomData, + } } - pub fn exec( + pub fn exec<'a>( self, - db: &DatabaseConnection, - ) -> impl Future> + '_ { + db: &'a DatabaseConnection, + ) -> impl Future, DbErr>> + 'a + where + A: 'a, + { let builder = db.get_database_backend(); exec_insert(builder.build(&self.query), db) } } // Only Statement impl Send -async fn exec_insert(statement: Statement, db: &DatabaseConnection) -> Result { +async fn exec_insert( + statement: Statement, + db: &DatabaseConnection, +) -> Result, DbErr> +where + A: ActiveModelTrait, +{ // TODO: Postgres instead use query_one + returning clause let result = match db { #[cfg(feature = "sqlx-postgres")] From 3bada7fbc3befd5f3316611dff889c87fa7ff2e7 Mon Sep 17 00:00:00 2001 From: Billy Chan Date: Sun, 29 Aug 2021 12:05:55 +0800 Subject: [PATCH 09/43] WIP --- README.md | 2 +- examples/async-std/src/operation.rs | 2 +- src/driver/sqlx_postgres.rs | 20 +++++++------------ src/executor/execute.rs | 31 +++++++++++++++++++---------- src/lib.rs | 2 +- tests/crud/create_baker.rs | 4 ++-- tests/crud/create_cake.rs | 8 ++++---- tests/crud/create_lineitem.rs | 14 ++++++------- tests/crud/create_order.rs | 14 ++++++------- tests/crud/deletes.rs | 2 +- tests/crud/mod.rs | 4 ++-- tests/crud/updates.rs | 6 +++--- tests/sequential_op_tests.rs | 8 ++++---- 13 files changed, 61 insertions(+), 56 deletions(-) diff --git a/README.md b/README.md index f2d262e9..c625268d 100644 --- a/README.md +++ b/README.md @@ -87,7 +87,7 @@ let pear = fruit::ActiveModel { }; // insert one -let res: InsertResult = Fruit::insert(pear).exec(db).await?; +let res = Fruit::insert(pear).exec(db).await?; println!("InsertResult: {}", res.last_insert_id); diff --git a/examples/async-std/src/operation.rs b/examples/async-std/src/operation.rs index b1273e10..3fa5cc85 100644 --- a/examples/async-std/src/operation.rs +++ b/examples/async-std/src/operation.rs @@ -20,7 +20,7 @@ pub async fn insert_and_update(db: &DbConn) -> Result<(), DbErr> { name: Set("pear".to_owned()), ..Default::default() }; - let res: InsertResult = Fruit::insert(pear).exec(db).await?; + let res = Fruit::insert(pear).exec(db).await?; println!(); println!("Inserted: last_insert_id = {}\n", res.last_insert_id); diff --git a/src/driver/sqlx_postgres.rs b/src/driver/sqlx_postgres.rs index 9c45413f..5456d8f7 100644 --- a/src/driver/sqlx_postgres.rs +++ b/src/driver/sqlx_postgres.rs @@ -102,24 +102,18 @@ impl From for QueryResult { impl From for ExecResult { fn from(result: PgQueryResult) -> ExecResult { ExecResult { - result: ExecResultHolder::SqlxPostgres { - last_insert_id: 0, - rows_affected: result.rows_affected(), - }, + result: ExecResultHolder::SqlxPostgres(result), } } } -pub(crate) fn query_result_into_exec_result(res: QueryResult) -> Result -where - T: TryGetable, -{ - let last_insert_id: T = res.try_get("", "last_insert_id")?; +pub(crate) fn query_result_into_exec_result(res: QueryResult) -> Result { + let result = match res { + QueryResult::SqlxPostgres(result) => result, + _ => panic!("Should be QueryResult::SqlxPostgres"), + }; Ok(ExecResult { - result: ExecResultHolder::SqlxPostgres { - last_insert_id: last_insert_id as u64, - rows_affected: 0, - }, + result: ExecResultHolder::SqlxPostgres(result), }) } diff --git a/src/executor/execute.rs b/src/executor/execute.rs index 00375bb7..f522aa6c 100644 --- a/src/executor/execute.rs +++ b/src/executor/execute.rs @@ -1,3 +1,5 @@ +use crate::TryGetable; + #[derive(Debug)] pub struct ExecResult { pub(crate) result: ExecResultHolder, @@ -8,10 +10,7 @@ pub(crate) enum ExecResultHolder { #[cfg(feature = "sqlx-mysql")] SqlxMySql(sqlx::mysql::MySqlQueryResult), #[cfg(feature = "sqlx-postgres")] - SqlxPostgres { - last_insert_id: u64, - rows_affected: u64, - }, + SqlxPostgres(sqlx::postgres::PgQueryResult), #[cfg(feature = "sqlx-sqlite")] SqlxSqlite(sqlx::sqlite::SqliteQueryResult), #[cfg(feature = "mock")] @@ -21,23 +20,35 @@ pub(crate) enum ExecResultHolder { // ExecResult // impl ExecResult { - pub fn last_insert_id(&self) -> u64 { + pub fn last_insert_id(&self) -> T + where + T: TryGetable + Default, + { match &self.result { #[cfg(feature = "sqlx-mysql")] - ExecResultHolder::SqlxMySql(result) => result.last_insert_id(), + ExecResultHolder::SqlxMySql(result) => { + // result.last_insert_id() + T::default() + } #[cfg(feature = "sqlx-postgres")] - ExecResultHolder::SqlxPostgres { last_insert_id, .. } => last_insert_id.to_owned(), + ExecResultHolder::SqlxPostgres(result) => { + res.try_get("", "last_insert_id").unwrap_or_default() + } #[cfg(feature = "sqlx-sqlite")] ExecResultHolder::SqlxSqlite(result) => { let last_insert_rowid = result.last_insert_rowid(); if last_insert_rowid < 0 { panic!("negative last_insert_rowid") } else { - last_insert_rowid as u64 + // last_insert_rowid + T::default() } } #[cfg(feature = "mock")] - ExecResultHolder::Mock(result) => result.last_insert_id, + ExecResultHolder::Mock(result) => { + // result.last_insert_id + T::default() + } } } @@ -46,7 +57,7 @@ impl ExecResult { #[cfg(feature = "sqlx-mysql")] ExecResultHolder::SqlxMySql(result) => result.rows_affected(), #[cfg(feature = "sqlx-postgres")] - ExecResultHolder::SqlxPostgres { rows_affected, .. } => rows_affected.to_owned(), + ExecResultHolder::SqlxPostgres(result) => result.rows_affected(), #[cfg(feature = "sqlx-sqlite")] ExecResultHolder::SqlxSqlite(result) => result.rows_affected(), #[cfg(feature = "mock")] diff --git a/src/lib.rs b/src/lib.rs index 377bd62b..8469ede7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -93,7 +93,7 @@ //! }; //! //! // insert one -//! let res: InsertResult = Fruit::insert(pear).exec(db).await?; +//! let res = Fruit::insert(pear).exec(db).await?; //! //! println!("InsertResult: {}", res.last_insert_id); //! # Ok(()) diff --git a/tests/crud/create_baker.rs b/tests/crud/create_baker.rs index 8653b692..085b4005 100644 --- a/tests/crud/create_baker.rs +++ b/tests/crud/create_baker.rs @@ -7,7 +7,7 @@ pub async fn test_create_baker(db: &DbConn) { profit_margin: Set(10.4), ..Default::default() }; - let bakery_insert_res: InsertResult = Bakery::insert(seaside_bakery) + let bakery_insert_res = Bakery::insert(seaside_bakery) .exec(db) .await .expect("could not insert bakery"); @@ -30,7 +30,7 @@ pub async fn test_create_baker(db: &DbConn) { bakery_id: Set(Some(bakery_insert_res.last_insert_id as i32)), ..Default::default() }; - let res: InsertResult = Baker::insert(baker_bob) + let res = Baker::insert(baker_bob) .exec(db) .await .expect("could not insert baker"); diff --git a/tests/crud/create_cake.rs b/tests/crud/create_cake.rs index 07f9c4b3..fb4566eb 100644 --- a/tests/crud/create_cake.rs +++ b/tests/crud/create_cake.rs @@ -8,7 +8,7 @@ pub async fn test_create_cake(db: &DbConn) { profit_margin: Set(10.4), ..Default::default() }; - let bakery_insert_res: InsertResult = Bakery::insert(seaside_bakery) + let bakery_insert_res = Bakery::insert(seaside_bakery) .exec(db) .await .expect("could not insert bakery"); @@ -23,7 +23,7 @@ pub async fn test_create_cake(db: &DbConn) { bakery_id: Set(Some(bakery_insert_res.last_insert_id as i32)), ..Default::default() }; - let baker_insert_res: InsertResult = Baker::insert(baker_bob) + let baker_insert_res = Baker::insert(baker_bob) .exec(db) .await .expect("could not insert baker"); @@ -38,7 +38,7 @@ pub async fn test_create_cake(db: &DbConn) { ..Default::default() }; - let cake_insert_res: InsertResult = Cake::insert(mud_cake) + let cake_insert_res = Cake::insert(mud_cake) .exec(db) .await .expect("could not insert cake"); @@ -53,7 +53,7 @@ pub async fn test_create_cake(db: &DbConn) { baker_id: Set(baker_insert_res.last_insert_id as i32), ..Default::default() }; - let _cake_baker_res: InsertResult = CakesBakers::insert(cake_baker) + let _cake_baker_res = CakesBakers::insert(cake_baker) .exec(db) .await .expect("could not insert cake_baker"); diff --git a/tests/crud/create_lineitem.rs b/tests/crud/create_lineitem.rs index c82958f9..22810913 100644 --- a/tests/crud/create_lineitem.rs +++ b/tests/crud/create_lineitem.rs @@ -10,7 +10,7 @@ pub async fn test_create_lineitem(db: &DbConn) { profit_margin: Set(10.4), ..Default::default() }; - let bakery_insert_res: InsertResult = Bakery::insert(seaside_bakery) + let bakery_insert_res = Bakery::insert(seaside_bakery) .exec(db) .await .expect("could not insert bakery"); @@ -26,7 +26,7 @@ pub async fn test_create_lineitem(db: &DbConn) { bakery_id: Set(Some(bakery_insert_res.last_insert_id as i32)), ..Default::default() }; - let baker_insert_res: InsertResult = Baker::insert(baker_bob) + let baker_insert_res = Baker::insert(baker_bob) .exec(db) .await .expect("could not insert baker"); @@ -41,7 +41,7 @@ pub async fn test_create_lineitem(db: &DbConn) { ..Default::default() }; - let cake_insert_res: InsertResult = Cake::insert(mud_cake) + let cake_insert_res = Cake::insert(mud_cake) .exec(db) .await .expect("could not insert cake"); @@ -52,7 +52,7 @@ pub async fn test_create_lineitem(db: &DbConn) { baker_id: Set(baker_insert_res.last_insert_id as i32), ..Default::default() }; - let _cake_baker_res: InsertResult = CakesBakers::insert(cake_baker) + let _cake_baker_res = CakesBakers::insert(cake_baker) .exec(db) .await .expect("could not insert cake_baker"); @@ -63,7 +63,7 @@ pub async fn test_create_lineitem(db: &DbConn) { notes: Set(Some("Loves cheese cake".to_owned())), ..Default::default() }; - let customer_insert_res: InsertResult = Customer::insert(customer_kate) + let customer_insert_res = Customer::insert(customer_kate) .exec(db) .await .expect("could not insert customer"); @@ -76,7 +76,7 @@ pub async fn test_create_lineitem(db: &DbConn) { placed_at: Set(Utc::now().naive_utc()), ..Default::default() }; - let order_insert_res: InsertResult = Order::insert(order_1) + let order_insert_res = Order::insert(order_1) .exec(db) .await .expect("could not insert order"); @@ -89,7 +89,7 @@ pub async fn test_create_lineitem(db: &DbConn) { quantity: Set(1), ..Default::default() }; - let lineitem_insert_res: InsertResult = Lineitem::insert(lineitem_1) + let lineitem_insert_res = Lineitem::insert(lineitem_1) .exec(db) .await .expect("could not insert lineitem"); diff --git a/tests/crud/create_order.rs b/tests/crud/create_order.rs index 46ebcf09..e613802b 100644 --- a/tests/crud/create_order.rs +++ b/tests/crud/create_order.rs @@ -10,7 +10,7 @@ pub async fn test_create_order(db: &DbConn) { profit_margin: Set(10.4), ..Default::default() }; - let bakery_insert_res: InsertResult = Bakery::insert(seaside_bakery) + let bakery_insert_res = Bakery::insert(seaside_bakery) .exec(db) .await .expect("could not insert bakery"); @@ -26,7 +26,7 @@ pub async fn test_create_order(db: &DbConn) { bakery_id: Set(Some(bakery_insert_res.last_insert_id as i32)), ..Default::default() }; - let baker_insert_res: InsertResult = Baker::insert(baker_bob) + let baker_insert_res = Baker::insert(baker_bob) .exec(db) .await .expect("could not insert baker"); @@ -41,7 +41,7 @@ pub async fn test_create_order(db: &DbConn) { ..Default::default() }; - let cake_insert_res: InsertResult = Cake::insert(mud_cake) + let cake_insert_res = Cake::insert(mud_cake) .exec(db) .await .expect("could not insert cake"); @@ -52,7 +52,7 @@ pub async fn test_create_order(db: &DbConn) { baker_id: Set(baker_insert_res.last_insert_id as i32), ..Default::default() }; - let _cake_baker_res: InsertResult = CakesBakers::insert(cake_baker) + let _cake_baker_res = CakesBakers::insert(cake_baker) .exec(db) .await .expect("could not insert cake_baker"); @@ -63,7 +63,7 @@ pub async fn test_create_order(db: &DbConn) { notes: Set(Some("Loves cheese cake".to_owned())), ..Default::default() }; - let customer_insert_res: InsertResult = Customer::insert(customer_kate) + let customer_insert_res = Customer::insert(customer_kate) .exec(db) .await .expect("could not insert customer"); @@ -76,7 +76,7 @@ pub async fn test_create_order(db: &DbConn) { placed_at: Set(Utc::now().naive_utc()), ..Default::default() }; - let order_insert_res: InsertResult = Order::insert(order_1) + let order_insert_res = Order::insert(order_1) .exec(db) .await .expect("could not insert order"); @@ -89,7 +89,7 @@ pub async fn test_create_order(db: &DbConn) { quantity: Set(2), ..Default::default() }; - let _lineitem_insert_res: InsertResult = Lineitem::insert(lineitem_1) + let _lineitem_insert_res = Lineitem::insert(lineitem_1) .exec(db) .await .expect("could not insert lineitem"); diff --git a/tests/crud/deletes.rs b/tests/crud/deletes.rs index ebff4b5f..4c34d36b 100644 --- a/tests/crud/deletes.rs +++ b/tests/crud/deletes.rs @@ -10,7 +10,7 @@ pub async fn test_delete_cake(db: &DbConn) { profit_margin: Set(10.4), ..Default::default() }; - let bakery_insert_res: InsertResult = Bakery::insert(seaside_bakery) + let bakery_insert_res = Bakery::insert(seaside_bakery) .exec(db) .await .expect("could not insert bakery"); diff --git a/tests/crud/mod.rs b/tests/crud/mod.rs index 457f639d..82bdbc0d 100644 --- a/tests/crud/mod.rs +++ b/tests/crud/mod.rs @@ -15,7 +15,7 @@ pub async fn test_create_bakery(db: &DbConn) { profit_margin: Set(10.4), ..Default::default() }; - let res: InsertResult = Bakery::insert(seaside_bakery) + let res = Bakery::insert(seaside_bakery) .exec(db) .await .expect("could not insert bakery"); @@ -37,7 +37,7 @@ pub async fn test_create_customer(db: &DbConn) { notes: Set(Some("Loves cheese cake".to_owned())), ..Default::default() }; - let res: InsertResult = Customer::insert(customer_kate) + let res = Customer::insert(customer_kate) .exec(db) .await .expect("could not insert customer"); diff --git a/tests/crud/updates.rs b/tests/crud/updates.rs index 505e4837..94c11aaf 100644 --- a/tests/crud/updates.rs +++ b/tests/crud/updates.rs @@ -8,7 +8,7 @@ pub async fn test_update_cake(db: &DbConn) { profit_margin: Set(10.4), ..Default::default() }; - let bakery_insert_res: InsertResult = Bakery::insert(seaside_bakery) + let bakery_insert_res = Bakery::insert(seaside_bakery) .exec(db) .await .expect("could not insert bakery"); @@ -22,7 +22,7 @@ pub async fn test_update_cake(db: &DbConn) { ..Default::default() }; - let cake_insert_res: InsertResult = Cake::insert(mud_cake) + let cake_insert_res = Cake::insert(mud_cake) .exec(db) .await .expect("could not insert cake"); @@ -62,7 +62,7 @@ pub async fn test_update_bakery(db: &DbConn) { profit_margin: Set(10.4), ..Default::default() }; - let bakery_insert_res: InsertResult = Bakery::insert(seaside_bakery) + let bakery_insert_res = Bakery::insert(seaside_bakery) .exec(db) .await .expect("could not insert bakery"); diff --git a/tests/sequential_op_tests.rs b/tests/sequential_op_tests.rs index a854c768..6e1377bc 100644 --- a/tests/sequential_op_tests.rs +++ b/tests/sequential_op_tests.rs @@ -67,7 +67,7 @@ async fn init_setup(db: &DatabaseConnection) { ..Default::default() }; - let cake_insert_res: InsertResult = Cake::insert(mud_cake) + let cake_insert_res = Cake::insert(mud_cake) .exec(db) .await .expect("could not insert cake"); @@ -78,7 +78,7 @@ async fn init_setup(db: &DatabaseConnection) { ..Default::default() }; - let _cake_baker_res: InsertResult = CakesBakers::insert(cake_baker) + let _cake_baker_res = CakesBakers::insert(cake_baker) .exec(db) .await .expect("could not insert cake_baker"); @@ -200,7 +200,7 @@ async fn create_cake(db: &DatabaseConnection, baker: baker::Model) -> Option Option Date: Sun, 29 Aug 2021 19:39:01 +0800 Subject: [PATCH 10/43] WIP --- src/entity/primary_key.rs | 14 ++++++++++++-- src/executor/execute.rs | 24 +++++++++++++----------- tests/crud/mod.rs | 2 +- 3 files changed, 26 insertions(+), 14 deletions(-) diff --git a/src/entity/primary_key.rs b/src/entity/primary_key.rs index 1d434c13..f24c62df 100644 --- a/src/entity/primary_key.rs +++ b/src/entity/primary_key.rs @@ -1,11 +1,21 @@ use super::{ColumnTrait, IdenStatic, Iterable}; use crate::TryGetable; use sea_query::IntoValueTuple; -use std::fmt::{Debug, Display}; +use std::{ + fmt::{Debug, Display}, + str::FromStr, +}; //LINT: composite primary key cannot auto increment pub trait PrimaryKeyTrait: IdenStatic + Iterable { - type ValueType: Sized + Default + Debug + Display + PartialEq + IntoValueTuple + TryGetable; + type ValueType: Sized + + Default + + Debug + + Display + + PartialEq + + IntoValueTuple + + TryGetable + + FromStr; fn auto_increment() -> bool; } diff --git a/src/executor/execute.rs b/src/executor/execute.rs index f522aa6c..db7e8a39 100644 --- a/src/executor/execute.rs +++ b/src/executor/execute.rs @@ -1,4 +1,5 @@ use crate::TryGetable; +use std::str::FromStr; #[derive(Debug)] pub struct ExecResult { @@ -22,14 +23,15 @@ pub(crate) enum ExecResultHolder { impl ExecResult { pub fn last_insert_id(&self) -> T where - T: TryGetable + Default, + T: TryGetable + Default + FromStr, { match &self.result { #[cfg(feature = "sqlx-mysql")] - ExecResultHolder::SqlxMySql(result) => { - // result.last_insert_id() - T::default() - } + ExecResultHolder::SqlxMySql(result) => result + .last_insert_id() + .to_string() + .parse() + .unwrap_or_default(), #[cfg(feature = "sqlx-postgres")] ExecResultHolder::SqlxPostgres(result) => { res.try_get("", "last_insert_id").unwrap_or_default() @@ -40,15 +42,15 @@ impl ExecResult { if last_insert_rowid < 0 { panic!("negative last_insert_rowid") } else { - // last_insert_rowid - T::default() + last_insert_rowid.to_string().parse().unwrap_or_default() } } #[cfg(feature = "mock")] - ExecResultHolder::Mock(result) => { - // result.last_insert_id - T::default() - } + ExecResultHolder::Mock(result) => result + .last_insert_id + .to_string() + .parse() + .unwrap_or_default(), } } diff --git a/tests/crud/mod.rs b/tests/crud/mod.rs index 82bdbc0d..7e024055 100644 --- a/tests/crud/mod.rs +++ b/tests/crud/mod.rs @@ -1,4 +1,4 @@ -use sea_orm::{entity::*, DbConn, InsertResult}; +use sea_orm::{entity::*, DbConn}; pub use super::common::bakery_chain::*; From 4e5a8d2dc553a26668c89b5740bb6a0d4b5e33f3 Mon Sep 17 00:00:00 2001 From: Billy Chan Date: Sun, 29 Aug 2021 20:57:25 +0800 Subject: [PATCH 11/43] WIP --- src/driver/sqlx_postgres.rs | 10 ---------- src/executor/execute.rs | 26 +++++--------------------- src/executor/insert.rs | 16 +++++++++++----- 3 files changed, 16 insertions(+), 36 deletions(-) diff --git a/src/driver/sqlx_postgres.rs b/src/driver/sqlx_postgres.rs index 5456d8f7..b3d71f9a 100644 --- a/src/driver/sqlx_postgres.rs +++ b/src/driver/sqlx_postgres.rs @@ -107,16 +107,6 @@ impl From for ExecResult { } } -pub(crate) fn query_result_into_exec_result(res: QueryResult) -> Result { - let result = match res { - QueryResult::SqlxPostgres(result) => result, - _ => panic!("Should be QueryResult::SqlxPostgres"), - }; - Ok(ExecResult { - result: ExecResultHolder::SqlxPostgres(result), - }) -} - fn sqlx_query(stmt: &Statement) -> sqlx::query::Query<'_, Postgres, PgArguments> { let mut query = sqlx::query(&stmt.sql); if let Some(values) = &stmt.values { diff --git a/src/executor/execute.rs b/src/executor/execute.rs index db7e8a39..f05ee963 100644 --- a/src/executor/execute.rs +++ b/src/executor/execute.rs @@ -1,6 +1,3 @@ -use crate::TryGetable; -use std::str::FromStr; - #[derive(Debug)] pub struct ExecResult { pub(crate) result: ExecResultHolder, @@ -21,36 +18,23 @@ pub(crate) enum ExecResultHolder { // ExecResult // impl ExecResult { - pub fn last_insert_id(&self) -> T - where - T: TryGetable + Default + FromStr, - { + pub fn last_insert_id(&self) -> u64 { match &self.result { #[cfg(feature = "sqlx-mysql")] - ExecResultHolder::SqlxMySql(result) => result - .last_insert_id() - .to_string() - .parse() - .unwrap_or_default(), + ExecResultHolder::SqlxMySql(result) => result.last_insert_id(), #[cfg(feature = "sqlx-postgres")] - ExecResultHolder::SqlxPostgres(result) => { - res.try_get("", "last_insert_id").unwrap_or_default() - } + ExecResultHolder::SqlxPostgres(_) => panic!("Should not retrieve last_insert_id this way"), #[cfg(feature = "sqlx-sqlite")] ExecResultHolder::SqlxSqlite(result) => { let last_insert_rowid = result.last_insert_rowid(); if last_insert_rowid < 0 { panic!("negative last_insert_rowid") } else { - last_insert_rowid.to_string().parse().unwrap_or_default() + last_insert_rowid as u64 } } #[cfg(feature = "mock")] - ExecResultHolder::Mock(result) => result - .last_insert_id - .to_string() - .parse() - .unwrap_or_default(), + ExecResultHolder::Mock(result) => result.last_insert_id, } } diff --git a/src/executor/insert.rs b/src/executor/insert.rs index a644d6e9..7e5e24aa 100644 --- a/src/executor/insert.rs +++ b/src/executor/insert.rs @@ -37,7 +37,7 @@ where let mut query = self.query; #[cfg(feature = "sqlx-postgres")] if let DatabaseConnection::SqlxPostgresPoolConnection(_) = db { - use crate::{EntityTrait, Iterable}; + use crate::Iterable; use sea_query::{Alias, Expr, Query}; for key in ::PrimaryKey::iter() { query.returning( @@ -83,15 +83,21 @@ where A: ActiveModelTrait, { // TODO: Postgres instead use query_one + returning clause - let result = match db { + let last_insert_id = match db { #[cfg(feature = "sqlx-postgres")] DatabaseConnection::SqlxPostgresPoolConnection(conn) => { let res = conn.query_one(statement).await?.unwrap(); - crate::query_result_into_exec_result(res)? + res.try_get("", "last_insert_id").unwrap_or_default() } - _ => db.execute(statement).await?, + _ => { + db.execute(statement).await? + .last_insert_id() + .to_string() + .parse() + .unwrap_or_default() + }, }; Ok(InsertResult { - last_insert_id: result.last_insert_id(), + last_insert_id, }) } From 4d0c5f0b0f54a0b20986cb7cc69e54438123578c Mon Sep 17 00:00:00 2001 From: Billy Chan Date: Sun, 29 Aug 2021 22:11:19 +0800 Subject: [PATCH 12/43] Hotfix - Postgres errors --- tests/crud/create_lineitem.rs | 2 +- tests/crud/create_order.rs | 4 ++-- tests/crud/updates.rs | 2 +- tests/sequential_op_tests.rs | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/crud/create_lineitem.rs b/tests/crud/create_lineitem.rs index 22810913..1030651a 100644 --- a/tests/crud/create_lineitem.rs +++ b/tests/crud/create_lineitem.rs @@ -105,7 +105,7 @@ pub async fn test_create_lineitem(db: &DbConn) { assert_eq!(lineitem_model.price, dec!(7.55)); - let cake: Option = Cake::find_by_id(lineitem_model.cake_id as u64) + let cake: Option = Cake::find_by_id(lineitem_model.cake_id) .one(db) .await .expect("could not find cake"); diff --git a/tests/crud/create_order.rs b/tests/crud/create_order.rs index e613802b..e3b12b52 100644 --- a/tests/crud/create_order.rs +++ b/tests/crud/create_order.rs @@ -103,7 +103,7 @@ pub async fn test_create_order(db: &DbConn) { let order_model = order.unwrap(); assert_eq!(order_model.total, dec!(15.10)); - let customer: Option = Customer::find_by_id(order_model.customer_id as u64) + let customer: Option = Customer::find_by_id(order_model.customer_id) .one(db) .await .expect("could not find customer"); @@ -111,7 +111,7 @@ pub async fn test_create_order(db: &DbConn) { let customer_model = customer.unwrap(); assert_eq!(customer_model.name, "Kate"); - let bakery: Option = Bakery::find_by_id(order_model.bakery_id as i64) + let bakery: Option = Bakery::find_by_id(order_model.bakery_id) .one(db) .await .expect("could not find bakery"); diff --git a/tests/crud/updates.rs b/tests/crud/updates.rs index 94c11aaf..df797ed3 100644 --- a/tests/crud/updates.rs +++ b/tests/crud/updates.rs @@ -131,7 +131,7 @@ pub async fn test_update_deleted_customer(db: &DbConn) { assert_eq!(Customer::find().count(db).await.unwrap(), init_n_customers); let customer: Option = - Customer::find_by_id(customer_id.clone().unwrap() as i64) + Customer::find_by_id(customer_id.clone().unwrap()) .one(db) .await .expect("could not find customer"); diff --git a/tests/sequential_op_tests.rs b/tests/sequential_op_tests.rs index 6e1377bc..300812cf 100644 --- a/tests/sequential_op_tests.rs +++ b/tests/sequential_op_tests.rs @@ -183,7 +183,7 @@ async fn find_baker_least_sales(db: &DatabaseConnection) -> Option results.sort_by(|a, b| b.cakes_sold.cmp(&a.cakes_sold)); - Baker::find_by_id(results.last().unwrap().id as i64) + Baker::find_by_id(results.last().unwrap().id) .one(db) .await .unwrap() From 333f199c1a566066fb06e6bc6d1c5b4158101948 Mon Sep 17 00:00:00 2001 From: Billy Chan Date: Sun, 29 Aug 2021 22:18:53 +0800 Subject: [PATCH 13/43] cargo fmt --- src/executor/execute.rs | 4 +++- src/executor/insert.rs | 12 +++++------- tests/crud/updates.rs | 9 ++++----- 3 files changed, 12 insertions(+), 13 deletions(-) diff --git a/src/executor/execute.rs b/src/executor/execute.rs index f05ee963..46ba2d69 100644 --- a/src/executor/execute.rs +++ b/src/executor/execute.rs @@ -23,7 +23,9 @@ impl ExecResult { #[cfg(feature = "sqlx-mysql")] ExecResultHolder::SqlxMySql(result) => result.last_insert_id(), #[cfg(feature = "sqlx-postgres")] - ExecResultHolder::SqlxPostgres(_) => panic!("Should not retrieve last_insert_id this way"), + ExecResultHolder::SqlxPostgres(_) => { + panic!("Should not retrieve last_insert_id this way") + } #[cfg(feature = "sqlx-sqlite")] ExecResultHolder::SqlxSqlite(result) => { let last_insert_rowid = result.last_insert_rowid(); diff --git a/src/executor/insert.rs b/src/executor/insert.rs index 7e5e24aa..e806fe62 100644 --- a/src/executor/insert.rs +++ b/src/executor/insert.rs @@ -89,15 +89,13 @@ where let res = conn.query_one(statement).await?.unwrap(); res.try_get("", "last_insert_id").unwrap_or_default() } - _ => { - db.execute(statement).await? + _ => db + .execute(statement) + .await? .last_insert_id() .to_string() .parse() - .unwrap_or_default() - }, + .unwrap_or_default(), }; - Ok(InsertResult { - last_insert_id, - }) + Ok(InsertResult { last_insert_id }) } diff --git a/tests/crud/updates.rs b/tests/crud/updates.rs index df797ed3..172c9c21 100644 --- a/tests/crud/updates.rs +++ b/tests/crud/updates.rs @@ -130,11 +130,10 @@ pub async fn test_update_deleted_customer(db: &DbConn) { assert_eq!(Customer::find().count(db).await.unwrap(), init_n_customers); - let customer: Option = - Customer::find_by_id(customer_id.clone().unwrap()) - .one(db) - .await - .expect("could not find customer"); + let customer: Option = Customer::find_by_id(customer_id.clone().unwrap()) + .one(db) + .await + .expect("could not find customer"); assert_eq!(customer, None); } From 8a81e43d2e507ff90aeeb54b095f3f055fc759ea Mon Sep 17 00:00:00 2001 From: Billy Chan Date: Tue, 31 Aug 2021 12:49:07 +0800 Subject: [PATCH 14/43] 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 15/43] 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 16/43] 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 17/43] 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 18/43] 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 19/43] 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 20/43] 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 21/43] 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 22/43] 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 0e0ee0ede6bf37ad9d40634527fb023466334cda Mon Sep 17 00:00:00 2001 From: Billy Chan Date: Wed, 1 Sep 2021 16:53:54 +0800 Subject: [PATCH 23/43] Add TryFromU64 & test Uuid as primary key --- src/entity/primary_key.rs | 9 ++-- src/error.rs | 2 +- src/executor/insert.rs | 16 +++---- src/executor/query.rs | 46 ++++++++++++++++++++ tests/common/bakery_chain/metadata.rs | 61 +++++++++++++++++++++++++++ tests/common/bakery_chain/mod.rs | 2 + tests/common/setup/mod.rs | 15 ++++--- tests/common/setup/schema.rs | 17 ++++++++ tests/primary_key_tests.rs | 41 ++++++++++++++++++ 9 files changed, 186 insertions(+), 23 deletions(-) create mode 100644 tests/common/bakery_chain/metadata.rs create mode 100644 tests/primary_key_tests.rs diff --git a/src/entity/primary_key.rs b/src/entity/primary_key.rs index f24c62df..9702b8a1 100644 --- a/src/entity/primary_key.rs +++ b/src/entity/primary_key.rs @@ -1,10 +1,7 @@ use super::{ColumnTrait, IdenStatic, Iterable}; -use crate::TryGetable; +use crate::{TryFromU64, TryGetable}; use sea_query::IntoValueTuple; -use std::{ - fmt::{Debug, Display}, - str::FromStr, -}; +use std::fmt::{Debug, Display}; //LINT: composite primary key cannot auto increment pub trait PrimaryKeyTrait: IdenStatic + Iterable { @@ -15,7 +12,7 @@ pub trait PrimaryKeyTrait: IdenStatic + Iterable { + PartialEq + IntoValueTuple + TryGetable - + FromStr; + + TryFromU64; fn auto_increment() -> bool; } diff --git a/src/error.rs b/src/error.rs index 8a695dac..09f80b0a 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,4 +1,4 @@ -#[derive(Debug)] +#[derive(Debug, PartialEq)] pub enum DbErr { Conn(String), Exec(String), diff --git a/src/executor/insert.rs b/src/executor/insert.rs index e806fe62..6e657d23 100644 --- a/src/executor/insert.rs +++ b/src/executor/insert.rs @@ -1,5 +1,6 @@ use crate::{ - error::*, ActiveModelTrait, DatabaseConnection, EntityTrait, Insert, PrimaryKeyTrait, Statement, + error::*, ActiveModelTrait, DatabaseConnection, EntityTrait, Insert, PrimaryKeyTrait, + Statement, TryFromU64, }; use sea_query::InsertStatement; use std::{future::Future, marker::PhantomData}; @@ -82,20 +83,17 @@ async fn exec_insert( where A: ActiveModelTrait, { - // TODO: Postgres instead use query_one + returning clause + type ValueTypeOf = <<::Entity as EntityTrait>::PrimaryKey as PrimaryKeyTrait>::ValueType; let last_insert_id = match db { #[cfg(feature = "sqlx-postgres")] DatabaseConnection::SqlxPostgresPoolConnection(conn) => { let res = conn.query_one(statement).await?.unwrap(); res.try_get("", "last_insert_id").unwrap_or_default() } - _ => db - .execute(statement) - .await? - .last_insert_id() - .to_string() - .parse() - .unwrap_or_default(), + _ => { + let last_insert_id = db.execute(statement).await?.last_insert_id(); + ValueTypeOf::::try_from_u64(last_insert_id)? + } }; Ok(InsertResult { last_insert_id }) } diff --git a/src/executor/query.rs b/src/executor/query.rs index b7e1d12e..5420c4c9 100644 --- a/src/executor/query.rs +++ b/src/executor/query.rs @@ -405,3 +405,49 @@ impl TryGetable for Option { #[cfg(feature = "with-uuid")] try_getable_all!(uuid::Uuid); + +pub trait TryFromU64: Sized { + fn try_from_u64(n: u64) -> Result; +} + +macro_rules! try_from_u64 { + ( $type: ty ) => { + impl TryFromU64 for $type { + fn try_from_u64(n: u64) -> Result { + use std::convert::TryInto; + n.try_into().map_err(|_| { + DbErr::Exec(format!( + "fail to convert '{}' into '{}'", + n, + stringify!($type) + )) + }) + } + } + }; +} + +try_from_u64!(i8); +try_from_u64!(i16); +try_from_u64!(i32); +try_from_u64!(i64); +try_from_u64!(u8); +try_from_u64!(u16); +try_from_u64!(u32); +try_from_u64!(u64); + +macro_rules! try_from_u64_err { + ( $type: ty ) => { + impl TryFromU64 for $type { + fn try_from_u64(_: u64) -> Result { + Err(DbErr::Exec(format!( + "{} cannot be converted from u64", + stringify!($type) + ))) + } + } + }; +} + +#[cfg(feature = "with-uuid")] +try_from_u64_err!(uuid::Uuid); diff --git a/tests/common/bakery_chain/metadata.rs b/tests/common/bakery_chain/metadata.rs new file mode 100644 index 00000000..ecef26c3 --- /dev/null +++ b/tests/common/bakery_chain/metadata.rs @@ -0,0 +1,61 @@ +use sea_orm::entity::prelude::*; +use uuid::Uuid; + +#[derive(Copy, Clone, Default, Debug, DeriveEntity)] +pub struct Entity; + +impl EntityName for Entity { + fn table_name(&self) -> &str { + "metadata" + } +} + +#[derive(Clone, Debug, PartialEq, DeriveModel, DeriveActiveModel)] +pub struct Model { + pub uuid: Uuid, + pub key: String, + pub value: String, +} + +#[derive(Copy, Clone, Debug, EnumIter, DeriveColumn)] +pub enum Column { + Uuid, + Key, + Value, +} + +#[derive(Copy, Clone, Debug, EnumIter, DerivePrimaryKey)] +pub enum PrimaryKey { + Uuid, +} + +impl PrimaryKeyTrait for PrimaryKey { + type ValueType = Uuid; + + fn auto_increment() -> bool { + false + } +} + +#[derive(Copy, Clone, Debug, EnumIter)] +pub enum Relation {} + +impl ColumnTrait for Column { + type EntityName = Entity; + + fn def(&self) -> ColumnDef { + match self { + Self::Uuid => ColumnType::Uuid.def(), + Self::Key => ColumnType::String(None).def(), + Self::Value => ColumnType::String(None).def(), + } + } +} + +impl RelationTrait for Relation { + fn def(&self) -> RelationDef { + unreachable!() + } +} + +impl ActiveModelBehavior for ActiveModel {} diff --git a/tests/common/bakery_chain/mod.rs b/tests/common/bakery_chain/mod.rs index 89028aab..3282766d 100644 --- a/tests/common/bakery_chain/mod.rs +++ b/tests/common/bakery_chain/mod.rs @@ -4,6 +4,7 @@ pub mod cake; pub mod cakes_bakers; pub mod customer; pub mod lineitem; +pub mod metadata; pub mod order; pub use super::baker::Entity as Baker; @@ -12,4 +13,5 @@ pub use super::cake::Entity as Cake; pub use super::cakes_bakers::Entity as CakesBakers; pub use super::customer::Entity as Customer; pub use super::lineitem::Entity as Lineitem; +pub use super::metadata::Entity as Metadata; pub use super::order::Entity as Order; diff --git a/tests/common/setup/mod.rs b/tests/common/setup/mod.rs index 7528cf6c..74e35b45 100644 --- a/tests/common/setup/mod.rs +++ b/tests/common/setup/mod.rs @@ -45,13 +45,14 @@ pub async fn setup(base_url: &str, db_name: &str) -> DatabaseConnection { Database::connect(base_url).await.unwrap() }; - assert!(schema::create_bakery_table(&db).await.is_ok()); - assert!(schema::create_baker_table(&db).await.is_ok()); - assert!(schema::create_customer_table(&db).await.is_ok()); - assert!(schema::create_order_table(&db).await.is_ok()); - assert!(schema::create_cake_table(&db).await.is_ok()); - assert!(schema::create_cakes_bakers_table(&db).await.is_ok()); - assert!(schema::create_lineitem_table(&db).await.is_ok()); + schema::create_bakery_table(&db).await.unwrap(); + schema::create_baker_table(&db).await.unwrap(); + schema::create_customer_table(&db).await.unwrap(); + schema::create_order_table(&db).await.unwrap(); + schema::create_cake_table(&db).await.unwrap(); + schema::create_cakes_bakers_table(&db).await.unwrap(); + schema::create_lineitem_table(&db).await.unwrap(); + schema::create_metadata_table(&db).await.unwrap(); db } diff --git a/tests/common/setup/schema.rs b/tests/common/setup/schema.rs index 4eba40ab..0e124fa2 100644 --- a/tests/common/setup/schema.rs +++ b/tests/common/setup/schema.rs @@ -254,3 +254,20 @@ pub async fn create_cake_table(db: &DbConn) -> Result { create_table(db, &stmt).await } + +pub async fn create_metadata_table(db: &DbConn) -> Result { + let stmt = sea_query::Table::create() + .table(metadata::Entity) + .if_not_exists() + .col( + ColumnDef::new(metadata::Column::Uuid) + .uuid() + .not_null() + .primary_key(), + ) + .col(ColumnDef::new(metadata::Column::Key).string().not_null()) + .col(ColumnDef::new(metadata::Column::Value).string().not_null()) + .to_owned(); + + create_table(db, &stmt).await +} diff --git a/tests/primary_key_tests.rs b/tests/primary_key_tests.rs new file mode 100644 index 00000000..2f74eafc --- /dev/null +++ b/tests/primary_key_tests.rs @@ -0,0 +1,41 @@ +use sea_orm::{entity::prelude::*, DatabaseConnection, Set}; +pub mod common; +pub use common::{bakery_chain::*, setup::*, TestContext}; +use uuid::Uuid; + +#[sea_orm_macros::test] +#[cfg(any( + feature = "sqlx-mysql", + feature = "sqlx-sqlite", + feature = "sqlx-postgres" +))] +async fn main() -> Result<(), DbErr> { + let ctx = TestContext::new("bakery_chain_schema_primary_key_tests").await; + + create_metadata(&ctx.db).await?; + + ctx.delete().await; + + Ok(()) +} + +async fn create_metadata(db: &DatabaseConnection) -> Result<(), DbErr> { + let metadata = metadata::ActiveModel { + uuid: Set(Uuid::new_v4()), + key: Set("markup".to_owned()), + value: Set("1.18".to_owned()), + }; + + let res = Metadata::insert(metadata.clone()).exec(db).await; + + if cfg!(feature = "sqlx-postgres") { + assert_eq!(metadata.uuid.unwrap(), res?.last_insert_id); + } else { + assert_eq!( + res.unwrap_err(), + DbErr::Exec("uuid::Uuid cannot be converted from u64".to_owned()) + ); + } + + Ok(()) +} From a49f880f4f6e64cf65da640149ca35cd82d08683 Mon Sep 17 00:00:00 2001 From: Billy Chan Date: Wed, 1 Sep 2021 18:23:23 +0800 Subject: [PATCH 24/43] WIP: composite primary keys --- src/executor/insert.rs | 4 +- src/executor/query.rs | 83 +++++++++++++++++++++++++------------- tests/primary_key_tests.rs | 14 +++---- 3 files changed, 65 insertions(+), 36 deletions(-) diff --git a/src/executor/insert.rs b/src/executor/insert.rs index 6e657d23..ce8f32d7 100644 --- a/src/executor/insert.rs +++ b/src/executor/insert.rs @@ -92,7 +92,9 @@ where } _ => { let last_insert_id = db.execute(statement).await?.last_insert_id(); - ValueTypeOf::::try_from_u64(last_insert_id)? + ValueTypeOf::::try_from_u64(last_insert_id) + .ok() + .unwrap_or_default() } }; Ok(InsertResult { last_insert_id }) diff --git a/src/executor/query.rs b/src/executor/query.rs index 5420c4c9..2d9d4db0 100644 --- a/src/executor/query.rs +++ b/src/executor/query.rs @@ -410,32 +410,6 @@ pub trait TryFromU64: Sized { fn try_from_u64(n: u64) -> Result; } -macro_rules! try_from_u64 { - ( $type: ty ) => { - impl TryFromU64 for $type { - fn try_from_u64(n: u64) -> Result { - use std::convert::TryInto; - n.try_into().map_err(|_| { - DbErr::Exec(format!( - "fail to convert '{}' into '{}'", - n, - stringify!($type) - )) - }) - } - } - }; -} - -try_from_u64!(i8); -try_from_u64!(i16); -try_from_u64!(i32); -try_from_u64!(i64); -try_from_u64!(u8); -try_from_u64!(u16); -try_from_u64!(u32); -try_from_u64!(u64); - macro_rules! try_from_u64_err { ( $type: ty ) => { impl TryFromU64 for $type { @@ -449,5 +423,60 @@ macro_rules! try_from_u64_err { }; } +macro_rules! try_from_u64_tuple { + ( $type: ty ) => { + try_from_u64_err!(($type, $type)); + try_from_u64_err!(($type, $type, $type)); + }; +} + +macro_rules! try_from_u64_numeric { + ( $type: ty ) => { + impl TryFromU64 for $type { + fn try_from_u64(n: u64) -> Result { + use std::convert::TryInto; + n.try_into().map_err(|_| { + DbErr::Exec(format!( + "fail to convert '{}' into '{}'", + n, + stringify!($type) + )) + }) + } + } + try_from_u64_tuple!($type); + }; +} + +try_from_u64_numeric!(i8); +try_from_u64_numeric!(i16); +try_from_u64_numeric!(i32); +try_from_u64_numeric!(i64); +try_from_u64_numeric!(u8); +try_from_u64_numeric!(u16); +try_from_u64_numeric!(u32); +try_from_u64_numeric!(u64); + +macro_rules! try_from_u64_string { + ( $type: ty ) => { + impl TryFromU64 for $type { + fn try_from_u64(n: u64) -> Result { + Ok(n.to_string()) + } + } + try_from_u64_tuple!($type); + }; +} + +try_from_u64_string!(String); + +macro_rules! try_from_u64_dummy { + ( $type: ty ) => { + try_from_u64_err!($type); + try_from_u64_err!(($type, $type)); + try_from_u64_err!(($type, $type, $type)); + }; +} + #[cfg(feature = "with-uuid")] -try_from_u64_err!(uuid::Uuid); +try_from_u64_dummy!(uuid::Uuid); diff --git a/tests/primary_key_tests.rs b/tests/primary_key_tests.rs index 2f74eafc..15fdfe43 100644 --- a/tests/primary_key_tests.rs +++ b/tests/primary_key_tests.rs @@ -26,16 +26,14 @@ async fn create_metadata(db: &DatabaseConnection) -> Result<(), DbErr> { value: Set("1.18".to_owned()), }; - let res = Metadata::insert(metadata.clone()).exec(db).await; + let res = Metadata::insert(metadata.clone()).exec(db).await?; - if cfg!(feature = "sqlx-postgres") { - assert_eq!(metadata.uuid.unwrap(), res?.last_insert_id); + let expected_uuid = if cfg!(feature = "sqlx-postgres") { + metadata.uuid.unwrap() } else { - assert_eq!( - res.unwrap_err(), - DbErr::Exec("uuid::Uuid cannot be converted from u64".to_owned()) - ); - } + Uuid::default() + }; + assert_eq!(res.last_insert_id, expected_uuid); Ok(()) } From d14322ad752472b4f2f2d7132922272492fdeeaa Mon Sep 17 00:00:00 2001 From: Billy Chan Date: Wed, 1 Sep 2021 19:05:59 +0800 Subject: [PATCH 25/43] WIP (workflow will fail) --- .../async-std/src/example_cake_filling.rs | 2 +- sea-orm-codegen/tests/entity/cake_filling.rs | 2 +- src/entity/active_model.rs | 4 +- src/entity/primary_key.rs | 7 +- src/executor/query.rs | 72 +++++++++++++++++-- src/tests_cfg/cake_filling.rs | 2 +- src/tests_cfg/cake_filling_price.rs | 2 +- tests/common/bakery_chain/cakes_bakers.rs | 2 +- 8 files changed, 77 insertions(+), 16 deletions(-) diff --git a/examples/async-std/src/example_cake_filling.rs b/examples/async-std/src/example_cake_filling.rs index c24e7296..4fa188bc 100644 --- a/examples/async-std/src/example_cake_filling.rs +++ b/examples/async-std/src/example_cake_filling.rs @@ -28,7 +28,7 @@ pub enum PrimaryKey { } impl PrimaryKeyTrait for PrimaryKey { - type ValueType = i32; + type ValueType = (i32, i32); fn auto_increment() -> bool { false diff --git a/sea-orm-codegen/tests/entity/cake_filling.rs b/sea-orm-codegen/tests/entity/cake_filling.rs index 227740c1..d100fa8c 100644 --- a/sea-orm-codegen/tests/entity/cake_filling.rs +++ b/sea-orm-codegen/tests/entity/cake_filling.rs @@ -30,7 +30,7 @@ pub enum PrimaryKey { } impl PrimaryKeyTrait for PrimaryKey { - type ValueType = i32; + type ValueType = (i32, i32); fn auto_increment() -> bool { false diff --git a/src/entity/active_model.rs b/src/entity/active_model.rs index 612e285d..f823411c 100644 --- a/src/entity/active_model.rs +++ b/src/entity/active_model.rs @@ -225,16 +225,14 @@ where if ::auto_increment() && res.last_insert_id != ::ValueType::default() { - let last_insert_id = res.last_insert_id.to_string(); let find = E::find_by_id(res.last_insert_id).one(db); let found = find.await; let model: Option = found?; match model { Some(model) => Ok(model.into_active_model()), None => Err(DbErr::Exec(format!( - "Failed to find inserted item: {} {}", + "Failed to find inserted item: {}", E::default().to_string(), - last_insert_id ))), } } else { diff --git a/src/entity/primary_key.rs b/src/entity/primary_key.rs index 9702b8a1..b9c381d4 100644 --- a/src/entity/primary_key.rs +++ b/src/entity/primary_key.rs @@ -1,17 +1,16 @@ use super::{ColumnTrait, IdenStatic, Iterable}; -use crate::{TryFromU64, TryGetable}; +use crate::{TryFromU64, TryGetableMany}; use sea_query::IntoValueTuple; -use std::fmt::{Debug, Display}; +use std::fmt::Debug; //LINT: composite primary key cannot auto increment pub trait PrimaryKeyTrait: IdenStatic + Iterable { type ValueType: Sized + Default + Debug - + Display + PartialEq + IntoValueTuple - + TryGetable + + TryGetableMany + TryFromU64; fn auto_increment() -> bool; diff --git a/src/executor/query.rs b/src/executor/query.rs index 2d9d4db0..88d6a157 100644 --- a/src/executor/query.rs +++ b/src/executor/query.rs @@ -17,10 +17,8 @@ pub(crate) enum QueryResultRow { Mock(crate::MockRow), } -pub trait TryGetable { - fn try_get(res: &QueryResult, pre: &str, col: &str) -> Result - where - Self: Sized; +pub trait TryGetable: Sized { + fn try_get(res: &QueryResult, pre: &str, col: &str) -> Result; } // QueryResult // @@ -406,6 +404,72 @@ impl TryGetable for Option { #[cfg(feature = "with-uuid")] try_getable_all!(uuid::Uuid); +// TryGetableMany // + +pub trait TryGetableMany: Sized { + fn try_get_many(res: &QueryResult, pre: &str, cols: &[String]) -> Result; +} + +impl TryGetableMany for T +where + T: TryGetable, +{ + fn try_get_many(res: &QueryResult, pre: &str, cols: &[String]) -> Result { + let expect_len = 1; + if cols.len() < expect_len { + return Err(DbErr::Query(format!( + "Expect {} column names supplied but got slice of length {}", + expect_len, + cols.len() + ))); + } + T::try_get(res, pre, &cols[0]) + } +} + +impl TryGetableMany for (T, T) +where + T: TryGetable, +{ + fn try_get_many(res: &QueryResult, pre: &str, cols: &[String]) -> Result { + let expect_len = 2; + if cols.len() < expect_len { + return Err(DbErr::Query(format!( + "Expect {} column names supplied but got slice of length {}", + expect_len, + cols.len() + ))); + } + Ok(( + T::try_get(res, pre, &cols[0])?, + T::try_get(res, pre, &cols[1])?, + )) + } +} + +impl TryGetableMany for (T, T, T) +where + T: TryGetable, +{ + fn try_get_many(res: &QueryResult, pre: &str, cols: &[String]) -> Result { + let expect_len = 3; + if cols.len() < expect_len { + return Err(DbErr::Query(format!( + "Expect {} column names supplied but got slice of length {}", + expect_len, + cols.len() + ))); + } + Ok(( + T::try_get(res, pre, &cols[0])?, + T::try_get(res, pre, &cols[1])?, + T::try_get(res, pre, &cols[2])?, + )) + } +} + +// TryFromU64 // + pub trait TryFromU64: Sized { fn try_from_u64(n: u64) -> Result; } diff --git a/src/tests_cfg/cake_filling.rs b/src/tests_cfg/cake_filling.rs index 4336d2fa..ed6d0af0 100644 --- a/src/tests_cfg/cake_filling.rs +++ b/src/tests_cfg/cake_filling.rs @@ -29,7 +29,7 @@ pub enum PrimaryKey { } impl PrimaryKeyTrait for PrimaryKey { - type ValueType = i32; + type ValueType = (i32, i32); fn auto_increment() -> bool { false diff --git a/src/tests_cfg/cake_filling_price.rs b/src/tests_cfg/cake_filling_price.rs index bd2bc8eb..e820ae0f 100644 --- a/src/tests_cfg/cake_filling_price.rs +++ b/src/tests_cfg/cake_filling_price.rs @@ -35,7 +35,7 @@ pub enum PrimaryKey { } impl PrimaryKeyTrait for PrimaryKey { - type ValueType = i32; + type ValueType = (i32, i32); fn auto_increment() -> bool { false diff --git a/tests/common/bakery_chain/cakes_bakers.rs b/tests/common/bakery_chain/cakes_bakers.rs index 853cf6da..98a25682 100644 --- a/tests/common/bakery_chain/cakes_bakers.rs +++ b/tests/common/bakery_chain/cakes_bakers.rs @@ -28,7 +28,7 @@ pub enum PrimaryKey { } impl PrimaryKeyTrait for PrimaryKey { - type ValueType = i32; + type ValueType = (i32, i32); fn auto_increment() -> bool { false From 23f2a3d7119eb61efdcb70d27a2e8ea2045761aa Mon Sep 17 00:00:00 2001 From: Billy Chan Date: Wed, 1 Sep 2021 22:04:43 +0800 Subject: [PATCH 26/43] Hotfix - git merge conflict --- src/executor/query.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/executor/query.rs b/src/executor/query.rs index 8546a5e7..7be17b4b 100644 --- a/src/executor/query.rs +++ b/src/executor/query.rs @@ -18,7 +18,7 @@ pub(crate) enum QueryResultRow { } pub trait TryGetable: Sized { - fn try_get(res: &QueryResult, pre: &str, col: &str) -> Result; + fn try_get(res: &QueryResult, pre: &str, col: &str) -> Result; } pub enum TryGetError { From d664985ea9fd1a80dd49c1ee798b807ca959eff8 Mon Sep 17 00:00:00 2001 From: Billy Chan Date: Wed, 1 Sep 2021 23:24:43 +0800 Subject: [PATCH 27/43] WIP --- sea-orm-codegen/src/entity/base_entity.rs | 25 +++++++--- src/executor/insert.rs | 2 +- src/executor/query.rs | 56 ++++++++++------------- 3 files changed, 45 insertions(+), 38 deletions(-) diff --git a/sea-orm-codegen/src/entity/base_entity.rs b/sea-orm-codegen/src/entity/base_entity.rs index 2cc6dbb4..7b28f9e4 100644 --- a/sea-orm-codegen/src/entity/base_entity.rs +++ b/sea-orm-codegen/src/entity/base_entity.rs @@ -118,12 +118,25 @@ impl Entity { } pub fn get_primary_key_rs_type(&self) -> TokenStream { - if let Some(primary_key) = self.primary_keys.first() { - self.columns - .iter() - .find(|col| col.name.eq(&primary_key.name)) - .unwrap() - .get_rs_type() + let types = self + .primary_keys + .iter() + .map(|primary_key| { + self.columns + .iter() + .find(|col| col.name.eq(&primary_key.name)) + .unwrap() + .get_rs_type() + .to_string() + }) + .collect::>(); + if !types.is_empty() { + let value_type = if types.len() > 1 { + vec!["(".to_owned(), types.join(", "), ")".to_owned()] + } else { + types + }; + value_type.join("").parse().unwrap() } else { TokenStream::new() } diff --git a/src/executor/insert.rs b/src/executor/insert.rs index ce8f32d7..a0577e4b 100644 --- a/src/executor/insert.rs +++ b/src/executor/insert.rs @@ -88,7 +88,7 @@ where #[cfg(feature = "sqlx-postgres")] DatabaseConnection::SqlxPostgresPoolConnection(conn) => { let res = conn.query_one(statement).await?.unwrap(); - res.try_get("", "last_insert_id").unwrap_or_default() + res.try_get_many("", "last_insert_id").unwrap_or_default() } _ => { let last_insert_id = db.execute(statement).await?.last_insert_id(); diff --git a/src/executor/query.rs b/src/executor/query.rs index 7be17b4b..407e963c 100644 --- a/src/executor/query.rs +++ b/src/executor/query.rs @@ -262,9 +262,12 @@ impl TryGetable for Decimal { .map_err(|e| TryGetError::DbErr(crate::sqlx_error_to_query_err(e)))?; use rust_decimal::prelude::FromPrimitive; match val { - Some(v) => Decimal::from_f64(v) - .ok_or_else(|| TryGetError::DbErr(DbErr::Query("Failed to convert f64 into Decimal".to_owned()))), - None => Err(TryGetError::Null) + Some(v) => Decimal::from_f64(v).ok_or_else(|| { + TryGetError::DbErr(DbErr::Query( + "Failed to convert f64 into Decimal".to_owned(), + )) + }), + None => Err(TryGetError::Null), } } #[cfg(feature = "mock")] @@ -282,22 +285,15 @@ try_getable_all!(uuid::Uuid); // TryGetableMany // pub trait TryGetableMany: Sized { - fn try_get_many(res: &QueryResult, pre: &str, cols: &[String]) -> Result; + fn try_get_many(res: &QueryResult, pre: &str, cols: &[String]) -> Result; } impl TryGetableMany for T where T: TryGetable, { - fn try_get_many(res: &QueryResult, pre: &str, cols: &[String]) -> Result { - let expect_len = 1; - if cols.len() < expect_len { - return Err(DbErr::Query(format!( - "Expect {} column names supplied but got slice of length {}", - expect_len, - cols.len() - ))); - } + fn try_get_many(res: &QueryResult, pre: &str, cols: &[String]) -> Result { + try_get_many_with_slice_len_of(1, cols)?; T::try_get(res, pre, &cols[0]) } } @@ -306,15 +302,8 @@ impl TryGetableMany for (T, T) where T: TryGetable, { - fn try_get_many(res: &QueryResult, pre: &str, cols: &[String]) -> Result { - let expect_len = 2; - if cols.len() < expect_len { - return Err(DbErr::Query(format!( - "Expect {} column names supplied but got slice of length {}", - expect_len, - cols.len() - ))); - } + fn try_get_many(res: &QueryResult, pre: &str, cols: &[String]) -> Result { + try_get_many_with_slice_len_of(2, cols)?; Ok(( T::try_get(res, pre, &cols[0])?, T::try_get(res, pre, &cols[1])?, @@ -326,15 +315,8 @@ impl TryGetableMany for (T, T, T) where T: TryGetable, { - fn try_get_many(res: &QueryResult, pre: &str, cols: &[String]) -> Result { - let expect_len = 3; - if cols.len() < expect_len { - return Err(DbErr::Query(format!( - "Expect {} column names supplied but got slice of length {}", - expect_len, - cols.len() - ))); - } + fn try_get_many(res: &QueryResult, pre: &str, cols: &[String]) -> Result { + try_get_many_with_slice_len_of(3, cols)?; Ok(( T::try_get(res, pre, &cols[0])?, T::try_get(res, pre, &cols[1])?, @@ -343,6 +325,18 @@ where } } +fn try_get_many_with_slice_len_of(len: usize, cols: &[String]) -> Result<(), TryGetError> { + if cols.len() < len { + Err(TryGetError::DbErr(DbErr::Query(format!( + "Expect {} column names supplied but got slice of length {}", + len, + cols.len() + )))) + } else { + Ok(()) + } +} + // TryFromU64 // pub trait TryFromU64: Sized { From e72d8a9e049e4656ca4918021aee1ccfa2cc7fab Mon Sep 17 00:00:00 2001 From: Billy Chan Date: Thu, 2 Sep 2021 11:26:40 +0800 Subject: [PATCH 28/43] Use TryGetableMany --- src/executor/insert.rs | 18 +++++++++++------- src/executor/query.rs | 7 +++++++ 2 files changed, 18 insertions(+), 7 deletions(-) diff --git a/src/executor/insert.rs b/src/executor/insert.rs index a0577e4b..90065675 100644 --- a/src/executor/insert.rs +++ b/src/executor/insert.rs @@ -38,13 +38,12 @@ where let mut query = self.query; #[cfg(feature = "sqlx-postgres")] if let DatabaseConnection::SqlxPostgresPoolConnection(_) = db { - use crate::Iterable; - use sea_query::{Alias, Expr, Query}; - for key in ::PrimaryKey::iter() { + use crate::{sea_query::Query, Iterable}; + if ::PrimaryKey::iter().count() > 0 { query.returning( Query::select() - .expr_as(Expr::col(key), Alias::new("last_insert_id")) - .to_owned(), + .columns(::PrimaryKey::iter()) + .take(), ); } } @@ -83,12 +82,17 @@ async fn exec_insert( where A: ActiveModelTrait, { - type ValueTypeOf = <<::Entity as EntityTrait>::PrimaryKey as PrimaryKeyTrait>::ValueType; + type PrimaryKey = <::Entity as EntityTrait>::PrimaryKey; + type ValueTypeOf = as PrimaryKeyTrait>::ValueType; let last_insert_id = match db { #[cfg(feature = "sqlx-postgres")] DatabaseConnection::SqlxPostgresPoolConnection(conn) => { + use crate::{sea_query::Iden, Iterable}; + let cols = PrimaryKey::::iter() + .map(|col| col.to_string()) + .collect::>(); let res = conn.query_one(statement).await?.unwrap(); - res.try_get_many("", "last_insert_id").unwrap_or_default() + res.try_get_many("", cols.as_ref()).unwrap_or_default() } _ => { let last_insert_id = db.execute(statement).await?.last_insert_id(); diff --git a/src/executor/query.rs b/src/executor/query.rs index 407e963c..1c2ce4a1 100644 --- a/src/executor/query.rs +++ b/src/executor/query.rs @@ -44,6 +44,13 @@ impl QueryResult { { Ok(T::try_get(self, pre, col)?) } + + pub fn try_get_many(&self, pre: &str, cols: &[String]) -> Result + where + T: TryGetableMany, + { + Ok(T::try_get_many(self, pre, cols)?) + } } impl fmt::Debug for QueryResultRow { From 8e2b7e561e6f1f209f366286bf25a122939ccd20 Mon Sep 17 00:00:00 2001 From: Billy Chan Date: Thu, 2 Sep 2021 11:47:57 +0800 Subject: [PATCH 29/43] Testing composite primary keys --- tests/crud/create_cake.rs | 10 +++++++++- tests/crud/create_lineitem.rs | 10 +++++++++- tests/crud/create_order.rs | 10 +++++++++- tests/primary_key_tests.rs | 14 ++++++++------ tests/sequential_op_tests.rs | 20 ++++++++++++++++++-- 5 files changed, 53 insertions(+), 11 deletions(-) diff --git a/tests/crud/create_cake.rs b/tests/crud/create_cake.rs index fb4566eb..eea74350 100644 --- a/tests/crud/create_cake.rs +++ b/tests/crud/create_cake.rs @@ -53,10 +53,18 @@ pub async fn test_create_cake(db: &DbConn) { baker_id: Set(baker_insert_res.last_insert_id as i32), ..Default::default() }; - let _cake_baker_res = CakesBakers::insert(cake_baker) + let cake_baker_res = CakesBakers::insert(cake_baker.clone()) .exec(db) .await .expect("could not insert cake_baker"); + assert_eq!( + cake_baker_res.last_insert_id, + if cfg!(feature = "sqlx-postgres") { + (cake_baker.cake_id.unwrap(), cake_baker.baker_id.unwrap()) + } else { + Default::default() + } + ); assert!(cake.is_some()); let cake_model = cake.unwrap(); diff --git a/tests/crud/create_lineitem.rs b/tests/crud/create_lineitem.rs index 1030651a..9acba961 100644 --- a/tests/crud/create_lineitem.rs +++ b/tests/crud/create_lineitem.rs @@ -52,10 +52,18 @@ pub async fn test_create_lineitem(db: &DbConn) { baker_id: Set(baker_insert_res.last_insert_id as i32), ..Default::default() }; - let _cake_baker_res = CakesBakers::insert(cake_baker) + let cake_baker_res = CakesBakers::insert(cake_baker.clone()) .exec(db) .await .expect("could not insert cake_baker"); + assert_eq!( + cake_baker_res.last_insert_id, + if cfg!(feature = "sqlx-postgres") { + (cake_baker.cake_id.unwrap(), cake_baker.baker_id.unwrap()) + } else { + Default::default() + } + ); // Customer let customer_kate = customer::ActiveModel { diff --git a/tests/crud/create_order.rs b/tests/crud/create_order.rs index e3b12b52..8d10cfcd 100644 --- a/tests/crud/create_order.rs +++ b/tests/crud/create_order.rs @@ -52,10 +52,18 @@ pub async fn test_create_order(db: &DbConn) { baker_id: Set(baker_insert_res.last_insert_id as i32), ..Default::default() }; - let _cake_baker_res = CakesBakers::insert(cake_baker) + let cake_baker_res = CakesBakers::insert(cake_baker.clone()) .exec(db) .await .expect("could not insert cake_baker"); + assert_eq!( + cake_baker_res.last_insert_id, + if cfg!(feature = "sqlx-postgres") { + (cake_baker.cake_id.unwrap(), cake_baker.baker_id.unwrap()) + } else { + Default::default() + } + ); // Customer let customer_kate = customer::ActiveModel { diff --git a/tests/primary_key_tests.rs b/tests/primary_key_tests.rs index 15fdfe43..74dd46e5 100644 --- a/tests/primary_key_tests.rs +++ b/tests/primary_key_tests.rs @@ -28,12 +28,14 @@ async fn create_metadata(db: &DatabaseConnection) -> Result<(), DbErr> { let res = Metadata::insert(metadata.clone()).exec(db).await?; - let expected_uuid = if cfg!(feature = "sqlx-postgres") { - metadata.uuid.unwrap() - } else { - Uuid::default() - }; - assert_eq!(res.last_insert_id, expected_uuid); + assert_eq!( + res.last_insert_id, + if cfg!(feature = "sqlx-postgres") { + metadata.uuid.unwrap() + } else { + Default::default() + } + ); Ok(()) } diff --git a/tests/sequential_op_tests.rs b/tests/sequential_op_tests.rs index 300812cf..6c490762 100644 --- a/tests/sequential_op_tests.rs +++ b/tests/sequential_op_tests.rs @@ -78,10 +78,18 @@ async fn init_setup(db: &DatabaseConnection) { ..Default::default() }; - let _cake_baker_res = CakesBakers::insert(cake_baker) + let cake_baker_res = CakesBakers::insert(cake_baker.clone()) .exec(db) .await .expect("could not insert cake_baker"); + assert_eq!( + cake_baker_res.last_insert_id, + if cfg!(feature = "sqlx-postgres") { + (cake_baker.cake_id.unwrap(), cake_baker.baker_id.unwrap()) + } else { + Default::default() + } + ); let customer_kate = customer::ActiveModel { name: Set("Kate".to_owned()), @@ -211,10 +219,18 @@ async fn create_cake(db: &DatabaseConnection, baker: baker::Model) -> Option Date: Thu, 2 Sep 2021 16:30:57 +0800 Subject: [PATCH 30/43] Update tests --- src/executor/select.rs | 2 +- src/query/helper.rs | 29 +++++++++++++++++- tests/relational_tests.rs | 63 +++++++++++++++++++++++++++++++-------- 3 files changed, 79 insertions(+), 15 deletions(-) diff --git a/src/executor/select.rs b/src/executor/select.rs index 6196ec73..836ea495 100644 --- a/src/executor/select.rs +++ b/src/executor/select.rs @@ -128,7 +128,7 @@ where E: EntityTrait, F: EntityTrait, { - fn into_model(self) -> Selector> + pub fn into_model(self) -> Selector> where M: FromQueryResult, N: FromQueryResult, diff --git a/src/query/helper.rs b/src/query/helper.rs index 6ade581a..4ca44905 100644 --- a/src/query/helper.rs +++ b/src/query/helper.rs @@ -3,7 +3,7 @@ use crate::{ RelationDef, }; use sea_query::{ - Alias, Expr, IntoCondition, SeaRc, SelectExpr, SelectStatement, SimpleExpr, TableRef, + Alias, Expr, Iden, IntoCondition, SeaRc, SelectExpr, SelectStatement, SimpleExpr, TableRef, }; pub use sea_query::{Condition, ConditionalStatement, DynIden, JoinType, Order, OrderedStatement}; @@ -66,6 +66,33 @@ pub trait QuerySelect: Sized { self } + /// Add a select column with prefixed alias + /// ``` + /// use sea_orm::{entity::*, query::*, tests_cfg::cake, DbBackend}; + /// + /// assert_eq!( + /// cake::Entity::find() + /// .select_only() + /// .column_as_prefixed(cake::Column::Id.count(), "A_", cake::Column::Id) + /// .build(DbBackend::Postgres) + /// .to_string(), + /// r#"SELECT COUNT("cake"."id") AS "A_id" FROM "cake""# + /// ); + /// ``` + fn column_as_prefixed(mut self, col: C, prefix: &str, alias: I) -> Self + where + C: IntoSimpleExpr, + I: Iden, + { + self.query().expr(SelectExpr { + expr: col.into_simple_expr(), + alias: Some(SeaRc::new(Alias::new( + vec![prefix, alias.to_string().as_str()].join("").as_str(), + ))), + }); + self + } + /// Add a group by column /// ``` /// use sea_orm::{entity::*, query::*, tests_cfg::cake, DbBackend}; diff --git a/tests/relational_tests.rs b/tests/relational_tests.rs index 1ddee902..f67d4e88 100644 --- a/tests/relational_tests.rs +++ b/tests/relational_tests.rs @@ -652,23 +652,60 @@ pub async fn linked() -> Result<(), DbErr> { .save(&ctx.db) .await?; - /* - SELECT `baker`.`id` AS `A_id`, `baker`.`name` AS `A_name`, - `baker`.`contact_details` AS `A_contact_details`, - `baker`.`bakery_id` AS `A_bakery_id`, `customer`.`id` AS `B_id`, - `customer`.`name` AS `B_name`, `customer`.`notes` AS `B_notes` - FROM `baker` - LEFT JOIN `cakes_bakers` ON `baker`.`id` = `cakes_bakers`.`baker_id` - LEFT JOIN `cake` ON `cakes_bakers`.`cake_id` = `cake`.`id` - LEFT JOIN `lineitem` ON `cake`.`id` = `lineitem`.`cake_id` - LEFT JOIN `order` ON `lineitem`.`order_id` = `order`.`id` - LEFT JOIN `customer` ON `order`.`customer_id` = `customer`.`id` - */ + #[derive(Debug, FromQueryResult, PartialEq)] + struct BakerLite { + name: String, + } + + #[derive(Debug, FromQueryResult, PartialEq)] + struct CustomerLite { + name: String, + } + let baked_for_customers = Baker::find() .find_also_linked(baker::BakedForCustomer) + .select_only() + .column_as_prefixed(baker::Column::Name, "A_", baker::Column::Name) + .column_as_prefixed(customer::Column::Name, "B_", customer::Column::Name) + .group_by(baker::Column::Id) + .group_by(customer::Column::Id) + .group_by(baker::Column::Name) + .group_by(customer::Column::Name) + .order_by_asc(baker::Column::Id) + .order_by_asc(customer::Column::Id) + .into_model::() .all(&ctx.db) .await?; - println!("{:#?}", baked_for_customers); + + assert_eq!( + baked_for_customers, + vec![ + ( + BakerLite { + name: "Baker Bob".to_owned(), + }, + Some(CustomerLite { + name: "Kara".to_owned(), + }) + ), + ( + BakerLite { + name: "Baker Bobby".to_owned(), + }, + Some(CustomerLite { + name: "Kate".to_owned(), + }) + ), + ( + BakerLite { + name: "Baker Bobby".to_owned(), + }, + Some(CustomerLite { + name: "Kara".to_owned(), + }) + ), + ] + ); ctx.delete().await; From cd43ff7143b50b339e0b01df5b74b1ddce77aec0 Mon Sep 17 00:00:00 2001 From: Chris Tsang Date: Thu, 2 Sep 2021 17:43:29 +0800 Subject: [PATCH 31/43] Remove turbofish --- tests/relational_tests.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/relational_tests.rs b/tests/relational_tests.rs index f67d4e88..2d129d2d 100644 --- a/tests/relational_tests.rs +++ b/tests/relational_tests.rs @@ -8,7 +8,7 @@ pub mod common; pub use common::{bakery_chain::*, setup::*, TestContext}; // Run the test locally: -// DATABASE_URL="mysql://root:@localhost" cargo test --features sqlx-mysql,runtime-async-std --test relational_tests +// DATABASE_URL="mysql://root:@localhost" cargo test --features sqlx-mysql,runtime-async-std-native-tls --test relational_tests #[sea_orm_macros::test] #[cfg(any( feature = "sqlx-mysql", @@ -662,7 +662,7 @@ pub async fn linked() -> Result<(), DbErr> { name: String, } - let baked_for_customers = Baker::find() + let baked_for_customers: Vec<(BakerLite, Option)> = Baker::find() .find_also_linked(baker::BakedForCustomer) .select_only() .column_as_prefixed(baker::Column::Name, "A_", baker::Column::Name) @@ -673,7 +673,7 @@ pub async fn linked() -> Result<(), DbErr> { .group_by(customer::Column::Name) .order_by_asc(baker::Column::Id) .order_by_asc(customer::Column::Id) - .into_model::() + .into_model() .all(&ctx.db) .await?; From 541b94f15d6b968733485a7e3ffde1c33d26dafc Mon Sep 17 00:00:00 2001 From: Billy Chan Date: Fri, 3 Sep 2021 11:23:29 +0800 Subject: [PATCH 32/43] Update query helper column_as --- src/entity/identity.rs | 34 ++++++++++++++++++++++++++++++++- src/executor/select.rs | 8 ++++---- src/query/combine.rs | 33 ++++++++++++++++++++++++++------ src/query/helper.rs | 40 ++++++--------------------------------- src/query/mod.rs | 2 +- tests/relational_tests.rs | 5 +++-- 6 files changed, 74 insertions(+), 48 deletions(-) diff --git a/src/entity/identity.rs b/src/entity/identity.rs index d1cc3170..d8623b7d 100644 --- a/src/entity/identity.rs +++ b/src/entity/identity.rs @@ -1,5 +1,6 @@ use crate::{ColumnTrait, EntityTrait, IdenStatic}; -use sea_query::{DynIden, IntoIden}; +use sea_query::{Alias, DynIden, Iden, IntoIden, SeaRc}; +use std::fmt; #[derive(Debug, Clone)] pub enum Identity { @@ -8,6 +9,25 @@ pub enum Identity { Ternary(DynIden, DynIden, DynIden), } +impl Iden for Identity { + fn unquoted(&self, s: &mut dyn fmt::Write) { + match self { + Identity::Unary(iden) => { + write!(s, "{}", iden.to_string()).unwrap(); + } + Identity::Binary(iden1, iden2) => { + write!(s, "{}", iden1.to_string()).unwrap(); + write!(s, "{}", iden2.to_string()).unwrap(); + } + Identity::Ternary(iden1, iden2, iden3) => { + write!(s, "{}", iden1.to_string()).unwrap(); + write!(s, "{}", iden2.to_string()).unwrap(); + write!(s, "{}", iden3.to_string()).unwrap(); + } + } + } +} + pub trait IntoIdentity { fn into_identity(self) -> Identity; } @@ -19,6 +39,18 @@ where fn identity_of(self) -> Identity; } +impl IntoIdentity for String { + fn into_identity(self) -> Identity { + self.as_str().into_identity() + } +} + +impl IntoIdentity for &str { + fn into_identity(self) -> Identity { + Identity::Unary(SeaRc::new(Alias::new(self))) + } +} + impl IntoIdentity for T where T: IdenStatic, diff --git a/src/executor/select.rs b/src/executor/select.rs index 836ea495..fc3a970b 100644 --- a/src/executor/select.rs +++ b/src/executor/select.rs @@ -1,6 +1,6 @@ use crate::{ - error::*, query::combine, DatabaseConnection, EntityTrait, FromQueryResult, Iterable, - JsonValue, ModelTrait, Paginator, PrimaryKeyToColumn, QueryResult, Select, SelectTwo, + error::*, DatabaseConnection, EntityTrait, FromQueryResult, IdenStatic, Iterable, JsonValue, + ModelTrait, Paginator, PrimaryKeyToColumn, QueryResult, Select, SelectA, SelectB, SelectTwo, SelectTwoMany, Statement, }; use sea_query::SelectStatement; @@ -66,8 +66,8 @@ where fn from_raw_query_result(res: QueryResult) -> Result { Ok(( - M::from_query_result(&res, combine::SELECT_A)?, - N::from_query_result_optional(&res, combine::SELECT_B)?, + M::from_query_result(&res, SelectA.as_str())?, + N::from_query_result_optional(&res, SelectB.as_str())?, )) } } diff --git a/src/query/combine.rs b/src/query/combine.rs index 8cce0510..0c0f151f 100644 --- a/src/query/combine.rs +++ b/src/query/combine.rs @@ -1,10 +1,31 @@ -use crate::{EntityTrait, IntoSimpleExpr, Iterable, QueryTrait, Select, SelectTwo, SelectTwoMany}; +use crate::{ + EntityTrait, IdenStatic, IntoSimpleExpr, Iterable, QueryTrait, Select, SelectTwo, SelectTwoMany, +}; use core::marker::PhantomData; pub use sea_query::JoinType; use sea_query::{Alias, ColumnRef, Iden, Order, SeaRc, SelectExpr, SelectStatement, SimpleExpr}; -pub const SELECT_A: &str = "A_"; -pub const SELECT_B: &str = "B_"; +macro_rules! select_def { + ( $ident: ident, $str: expr ) => { + #[derive(Debug, Clone, Copy)] + pub struct $ident; + + impl Iden for $ident { + fn unquoted(&self, s: &mut dyn std::fmt::Write) { + write!(s, "{}", self.as_str()).unwrap(); + } + } + + impl IdenStatic for $ident { + fn as_str(&self) -> &str { + $str + } + } + }; +} + +select_def!(SelectA, "A_"); +select_def!(SelectB, "B_"); impl Select where @@ -37,7 +58,7 @@ where where F: EntityTrait, { - self = self.apply_alias(SELECT_A); + self = self.apply_alias(SelectA.as_str()); SelectTwo::new(self.into_query()) } @@ -45,7 +66,7 @@ where where F: EntityTrait, { - self = self.apply_alias(SELECT_A); + self = self.apply_alias(SelectA.as_str()); SelectTwoMany::new(self.into_query()) } } @@ -102,7 +123,7 @@ where S: QueryTrait, { for col in ::iter() { - let alias = format!("{}{}", SELECT_B, col.to_string().as_str()); + let alias = format!("{}{}", SelectB.as_str(), col.as_str()); selector.query().expr(SelectExpr { expr: col.into_simple_expr(), alias: Some(SeaRc::new(Alias::new(&alias))), diff --git a/src/query/helper.rs b/src/query/helper.rs index 4ca44905..88a5c55c 100644 --- a/src/query/helper.rs +++ b/src/query/helper.rs @@ -1,11 +1,9 @@ use crate::{ - ColumnTrait, EntityTrait, Identity, IntoSimpleExpr, Iterable, ModelTrait, PrimaryKeyToColumn, - RelationDef, -}; -use sea_query::{ - Alias, Expr, Iden, IntoCondition, SeaRc, SelectExpr, SelectStatement, SimpleExpr, TableRef, + ColumnTrait, EntityTrait, Identity, IntoIdentity, IntoSimpleExpr, Iterable, ModelTrait, + PrimaryKeyToColumn, RelationDef, }; pub use sea_query::{Condition, ConditionalStatement, DynIden, JoinType, Order, OrderedStatement}; +use sea_query::{Expr, IntoCondition, SeaRc, SelectExpr, SelectStatement, SimpleExpr, TableRef}; // LINT: when the column does not appear in tables selected from // LINT: when there is a group by clause, but some columns don't have aggregate functions @@ -55,40 +53,14 @@ pub trait QuerySelect: Sized { /// r#"SELECT COUNT("cake"."id") AS "count" FROM "cake""# /// ); /// ``` - fn column_as(mut self, col: C, alias: &str) -> Self + fn column_as(mut self, col: C, alias: I) -> Self where C: IntoSimpleExpr, + I: IntoIdentity, { self.query().expr(SelectExpr { expr: col.into_simple_expr(), - alias: Some(SeaRc::new(Alias::new(alias))), - }); - self - } - - /// Add a select column with prefixed alias - /// ``` - /// use sea_orm::{entity::*, query::*, tests_cfg::cake, DbBackend}; - /// - /// assert_eq!( - /// cake::Entity::find() - /// .select_only() - /// .column_as_prefixed(cake::Column::Id.count(), "A_", cake::Column::Id) - /// .build(DbBackend::Postgres) - /// .to_string(), - /// r#"SELECT COUNT("cake"."id") AS "A_id" FROM "cake""# - /// ); - /// ``` - fn column_as_prefixed(mut self, col: C, prefix: &str, alias: I) -> Self - where - C: IntoSimpleExpr, - I: Iden, - { - self.query().expr(SelectExpr { - expr: col.into_simple_expr(), - alias: Some(SeaRc::new(Alias::new( - vec![prefix, alias.to_string().as_str()].join("").as_str(), - ))), + alias: Some(SeaRc::new(alias.into_identity())), }); self } diff --git a/src/query/mod.rs b/src/query/mod.rs index 899882ba..54cc12dd 100644 --- a/src/query/mod.rs +++ b/src/query/mod.rs @@ -9,7 +9,7 @@ mod select; mod traits; mod update; -// pub use combine::*; +pub use combine::{SelectA, SelectB}; pub use delete::*; pub use helper::*; pub use insert::*; diff --git a/tests/relational_tests.rs b/tests/relational_tests.rs index 2d129d2d..198d4b24 100644 --- a/tests/relational_tests.rs +++ b/tests/relational_tests.rs @@ -484,6 +484,7 @@ pub async fn having() { ))] pub async fn linked() -> Result<(), DbErr> { use common::bakery_chain::Order; + use sea_orm::{SelectA, SelectB}; let ctx = TestContext::new("test_linked").await; @@ -665,8 +666,8 @@ pub async fn linked() -> Result<(), DbErr> { let baked_for_customers: Vec<(BakerLite, Option)> = Baker::find() .find_also_linked(baker::BakedForCustomer) .select_only() - .column_as_prefixed(baker::Column::Name, "A_", baker::Column::Name) - .column_as_prefixed(customer::Column::Name, "B_", customer::Column::Name) + .column_as(baker::Column::Name, (SelectA, baker::Column::Name)) + .column_as(customer::Column::Name, (SelectB, customer::Column::Name)) .group_by(baker::Column::Id) .group_by(customer::Column::Id) .group_by(baker::Column::Name) From 4e111aea64040ebf8a536d5c9ae92185b9bc1a02 Mon Sep 17 00:00:00 2001 From: Billy Chan Date: Fri, 3 Sep 2021 11:54:24 +0800 Subject: [PATCH 33/43] 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 } From c8e223287590095f98e2b28ffd6b82d191cafcd1 Mon Sep 17 00:00:00 2001 From: Chris Tsang Date: Fri, 3 Sep 2021 14:17:07 +0800 Subject: [PATCH 34/43] Reduce panic --- src/executor/query.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/executor/query.rs b/src/executor/query.rs index 1c2ce4a1..a817a9ce 100644 --- a/src/executor/query.rs +++ b/src/executor/query.rs @@ -59,9 +59,9 @@ impl fmt::Debug for QueryResultRow { #[cfg(feature = "sqlx-mysql")] Self::SqlxMySql(row) => write!(f, "{:?}", row), #[cfg(feature = "sqlx-postgres")] - Self::SqlxPostgres(_) => panic!("QueryResultRow::SqlxPostgres cannot be inspected"), + Self::SqlxPostgres(_) => write!("QueryResultRow::SqlxPostgres cannot be inspected"), #[cfg(feature = "sqlx-sqlite")] - Self::SqlxSqlite(_) => panic!("QueryResultRow::SqlxSqlite cannot be inspected"), + Self::SqlxSqlite(_) => write!("QueryResultRow::SqlxSqlite cannot be inspected"), #[cfg(feature = "mock")] Self::Mock(row) => write!(f, "{:?}", row), } From a781cc66107033b964a72c7a271b1d42ec4c75ae Mon Sep 17 00:00:00 2001 From: Chris Tsang Date: Fri, 3 Sep 2021 14:21:51 +0800 Subject: [PATCH 35/43] Reduce panic --- src/database/connection.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/database/connection.rs b/src/database/connection.rs index 3d4215f5..6a39b240 100644 --- a/src/database/connection.rs +++ b/src/database/connection.rs @@ -75,7 +75,7 @@ impl DatabaseConnection { DatabaseConnection::SqlxSqlitePoolConnection(conn) => conn.execute(stmt).await, #[cfg(feature = "mock")] DatabaseConnection::MockDatabaseConnection(conn) => conn.execute(stmt).await, - DatabaseConnection::Disconnected => panic!("Disconnected"), + DatabaseConnection::Disconnected => Err(DbErr::Conn("Disconnected".to_owned())), } } @@ -89,7 +89,7 @@ impl DatabaseConnection { DatabaseConnection::SqlxSqlitePoolConnection(conn) => conn.query_one(stmt).await, #[cfg(feature = "mock")] DatabaseConnection::MockDatabaseConnection(conn) => conn.query_one(stmt).await, - DatabaseConnection::Disconnected => panic!("Disconnected"), + DatabaseConnection::Disconnected => Err(DbErr::Conn("Disconnected".to_owned())), } } @@ -103,7 +103,7 @@ impl DatabaseConnection { DatabaseConnection::SqlxSqlitePoolConnection(conn) => conn.query_all(stmt).await, #[cfg(feature = "mock")] DatabaseConnection::MockDatabaseConnection(conn) => conn.query_all(stmt).await, - DatabaseConnection::Disconnected => panic!("Disconnected"), + DatabaseConnection::Disconnected => Err(DbErr::Conn("Disconnected".to_owned())), } } From 7f38621c3c82ad2c02ed19d843692b6d08b248d6 Mon Sep 17 00:00:00 2001 From: Billy Chan Date: Thu, 2 Sep 2021 18:34:44 +0800 Subject: [PATCH 36/43] Set clippy deny --- src/driver/mock.rs | 5 ++++- src/driver/sqlx_mysql.rs | 2 ++ src/driver/sqlx_postgres.rs | 2 ++ src/driver/sqlx_sqlite.rs | 2 ++ src/entity/relation.rs | 2 ++ src/executor/query.rs | 1 + src/executor/select.rs | 1 + src/lib.rs | 7 +++++++ 8 files changed, 21 insertions(+), 1 deletion(-) diff --git a/src/driver/mock.rs b/src/driver/mock.rs index 0a1629bf..823ddb32 100644 --- a/src/driver/mock.rs +++ b/src/driver/mock.rs @@ -2,19 +2,22 @@ use crate::{ debug_print, error::*, DatabaseConnection, DbBackend, ExecResult, MockDatabase, QueryResult, Statement, Transaction, }; +use std::fmt::Debug; use std::sync::{ atomic::{AtomicUsize, Ordering}, Mutex, }; +#[derive(Debug)] pub struct MockDatabaseConnector; +#[derive(Debug)] pub struct MockDatabaseConnection { counter: AtomicUsize, mocker: Mutex>, } -pub trait MockDatabaseTrait: Send { +pub trait MockDatabaseTrait: Send + Debug { fn execute(&mut self, counter: usize, stmt: Statement) -> Result; fn query(&mut self, counter: usize, stmt: Statement) -> Result, DbErr>; diff --git a/src/driver/sqlx_mysql.rs b/src/driver/sqlx_mysql.rs index 48da88a0..e91df037 100644 --- a/src/driver/sqlx_mysql.rs +++ b/src/driver/sqlx_mysql.rs @@ -10,8 +10,10 @@ use crate::{debug_print, error::*, executor::*, DatabaseConnection, Statement}; use super::sqlx_common::*; +#[derive(Debug)] pub struct SqlxMySqlConnector; +#[derive(Debug)] pub struct SqlxMySqlPoolConnection { pool: MySqlPool, } diff --git a/src/driver/sqlx_postgres.rs b/src/driver/sqlx_postgres.rs index b3d71f9a..086dc995 100644 --- a/src/driver/sqlx_postgres.rs +++ b/src/driver/sqlx_postgres.rs @@ -10,8 +10,10 @@ use crate::{debug_print, error::*, executor::*, DatabaseConnection, Statement}; use super::sqlx_common::*; +#[derive(Debug)] pub struct SqlxPostgresConnector; +#[derive(Debug)] pub struct SqlxPostgresPoolConnection { pool: PgPool, } diff --git a/src/driver/sqlx_sqlite.rs b/src/driver/sqlx_sqlite.rs index ac275b72..5fa4bdcd 100644 --- a/src/driver/sqlx_sqlite.rs +++ b/src/driver/sqlx_sqlite.rs @@ -10,8 +10,10 @@ use crate::{debug_print, error::*, executor::*, DatabaseConnection, Statement}; use super::sqlx_common::*; +#[derive(Debug)] pub struct SqlxSqliteConnector; +#[derive(Debug)] pub struct SqlxSqlitePoolConnection { pool: SqlitePool, } diff --git a/src/entity/relation.rs b/src/entity/relation.rs index 4a35b8ba..7b2d7b4d 100644 --- a/src/entity/relation.rs +++ b/src/entity/relation.rs @@ -46,6 +46,7 @@ pub trait Linked { } } +#[derive(Debug)] pub struct RelationDef { pub rel_type: RelationType, pub from_tbl: TableRef, @@ -57,6 +58,7 @@ pub struct RelationDef { pub on_update: Option, } +#[derive(Debug)] pub struct RelationBuilder where E: EntityTrait, diff --git a/src/executor/query.rs b/src/executor/query.rs index a817a9ce..b853771a 100644 --- a/src/executor/query.rs +++ b/src/executor/query.rs @@ -21,6 +21,7 @@ pub trait TryGetable: Sized { fn try_get(res: &QueryResult, pre: &str, col: &str) -> Result; } +#[derive(Debug)] pub enum TryGetError { DbErr(DbErr), Null, diff --git a/src/executor/select.rs b/src/executor/select.rs index fc3a970b..0a5163de 100644 --- a/src/executor/select.rs +++ b/src/executor/select.rs @@ -30,6 +30,7 @@ pub trait SelectorTrait { fn from_raw_query_result(res: QueryResult) -> Result; } +#[derive(Debug)] pub struct SelectModel where M: FromQueryResult, diff --git a/src/lib.rs b/src/lib.rs index 441df8bd..ee5bb8cb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,3 +1,10 @@ +#![cfg_attr(docsrs, feature(doc_cfg))] +#![deny( + missing_debug_implementations, + clippy::print_stderr, + clippy::print_stdout +)] + //!
//! //! From 9d5ce08ff891497cd9ef591ca692b47ac2c57f68 Mon Sep 17 00:00:00 2001 From: Billy Chan Date: Thu, 2 Sep 2021 18:35:10 +0800 Subject: [PATCH 37/43] Logging --- Cargo.toml | 6 ++++-- sea-orm-macros/src/lib.rs | 4 ++++ src/util.rs | 2 +- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 3db574bb..387d27d1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -31,7 +31,7 @@ chrono = { version = "^0", optional = true } 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-orm-macros = { version = "^0.1.1", path = "sea-orm-macros", optional = true } sea-query = { version = "^0.16", features = ["thread-safe"] } sea-strum = { version = "^0.21", features = ["derive", "sea-orm"] } serde = { version = "^1.0", features = ["derive"] } @@ -40,6 +40,7 @@ sqlx-core = { version = "^0.5", optional = true } sqlx-macros = { version = "^0.5", optional = true } serde_json = { version = "^1", optional = true } uuid = { version = "0.8", features = ["serde", "v4"], optional = true } +log = { version = "^0.4", optional = true } [dev-dependencies] smol = { version = "^1.2" } @@ -49,11 +50,12 @@ tokio = { version = "^1.6", features = ["full"] } actix-rt = { version = "2.2.0" } maplit = { version = "^1" } rust_decimal_macros = { version = "^1" } +env_logger = { version = "^0.9" } sea-orm = { path = ".", features = ["debug-print"] } pretty_assertions = { version = "^0.7" } [features] -debug-print = [] +debug-print = ["log"] default = [ "macros", "mock", diff --git a/sea-orm-macros/src/lib.rs b/sea-orm-macros/src/lib.rs index a2217aa8..a9abf8b2 100644 --- a/sea-orm-macros/src/lib.rs +++ b/sea-orm-macros/src/lib.rs @@ -99,6 +99,10 @@ pub fn test(_: TokenStream, input: TokenStream) -> TokenStream { #[test] #(#attrs)* fn #name() #ret { + let _ = ::env_logger::builder() + .filter_level(::log::LevelFilter::Debug) + .is_test(true) + .try_init(); crate::block_on!(async { #body }) } ) diff --git a/src/util.rs b/src/util.rs index f6ddfd54..20cc8eb3 100644 --- a/src/util.rs +++ b/src/util.rs @@ -1,7 +1,7 @@ #[macro_export] #[cfg(feature = "debug-print")] macro_rules! debug_print { - ($( $args:expr ),*) => { println!( $( $args ),* ); } + ($( $args:expr ),*) => { log::debug!( $( $args ),* ); } } #[macro_export] From d212b62ce5b064397cc9df1778088ed02fe66eca Mon Sep 17 00:00:00 2001 From: Chris Tsang Date: Fri, 3 Sep 2021 14:34:20 +0800 Subject: [PATCH 38/43] Fixup --- src/executor/query.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/executor/query.rs b/src/executor/query.rs index a817a9ce..f16a420c 100644 --- a/src/executor/query.rs +++ b/src/executor/query.rs @@ -59,9 +59,9 @@ impl fmt::Debug for QueryResultRow { #[cfg(feature = "sqlx-mysql")] Self::SqlxMySql(row) => write!(f, "{:?}", row), #[cfg(feature = "sqlx-postgres")] - Self::SqlxPostgres(_) => write!("QueryResultRow::SqlxPostgres cannot be inspected"), + Self::SqlxPostgres(_) => write!(f, "QueryResultRow::SqlxPostgres cannot be inspected"), #[cfg(feature = "sqlx-sqlite")] - Self::SqlxSqlite(_) => write!("QueryResultRow::SqlxSqlite cannot be inspected"), + Self::SqlxSqlite(_) => write!(f, "QueryResultRow::SqlxSqlite cannot be inspected"), #[cfg(feature = "mock")] Self::Mock(row) => write!(f, "{:?}", row), } From b5d85486dd8b2347ca94ffc142c6d70177dbfb02 Mon Sep 17 00:00:00 2001 From: Billy Chan Date: Fri, 3 Sep 2021 14:35:20 +0800 Subject: [PATCH 39/43] Fixup --- src/entity/schema.rs | 8 ++++---- src/tests_cfg/cake.rs | 1 + tests/common/setup/schema.rs | 2 +- tests/relational_tests.rs | 24 ++++++++++++------------ 4 files changed, 18 insertions(+), 17 deletions(-) diff --git a/src/entity/schema.rs b/src/entity/schema.rs index 727eab06..06c688a8 100644 --- a/src/entity/schema.rs +++ b/src/entity/schema.rs @@ -21,10 +21,10 @@ where 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 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(); } diff --git a/src/tests_cfg/cake.rs b/src/tests_cfg/cake.rs index 49ada3aa..0eeb0738 100644 --- a/src/tests_cfg/cake.rs +++ b/src/tests_cfg/cake.rs @@ -75,6 +75,7 @@ impl Related for Entity { } } +#[derive(Debug)] pub struct CakeToFilling; impl Linked for CakeToFilling { diff --git a/tests/common/setup/schema.rs b/tests/common/setup/schema.rs index 2dc5f239..32e7d8a5 100644 --- a/tests/common/setup/schema.rs +++ b/tests/common/setup/schema.rs @@ -286,5 +286,5 @@ pub async fn create_metadata_table(db: &DbConn) -> Result { .col(ColumnDef::new(metadata::Column::Value).string().not_null()) .to_owned(); - create_table(db, &stmt).await + create_table(db, &stmt, Metadata).await } diff --git a/tests/relational_tests.rs b/tests/relational_tests.rs index 198d4b24..c950ddc6 100644 --- a/tests/relational_tests.rs +++ b/tests/relational_tests.rs @@ -494,7 +494,7 @@ pub async fn linked() -> Result<(), DbErr> { profit_margin: Set(10.4), ..Default::default() }; - let seaside_bakery_res: InsertResult = Bakery::insert(seaside_bakery).exec(&ctx.db).await?; + let seaside_bakery_res = Bakery::insert(seaside_bakery).exec(&ctx.db).await?; // Bob's Baker, Cake & Cake Baker let baker_bob = baker::ActiveModel { @@ -507,7 +507,7 @@ pub async fn linked() -> Result<(), DbErr> { bakery_id: Set(Some(seaside_bakery_res.last_insert_id as i32)), ..Default::default() }; - let baker_bob_res: InsertResult = Baker::insert(baker_bob).exec(&ctx.db).await?; + let baker_bob_res = Baker::insert(baker_bob).exec(&ctx.db).await?; let mud_cake = cake::ActiveModel { name: Set("Mud Cake".to_owned()), price: Set(dec!(10.25)), @@ -516,7 +516,7 @@ pub async fn linked() -> Result<(), DbErr> { bakery_id: Set(Some(seaside_bakery_res.last_insert_id as i32)), ..Default::default() }; - let mud_cake_res: InsertResult = Cake::insert(mud_cake).exec(&ctx.db).await?; + let mud_cake_res = Cake::insert(mud_cake).exec(&ctx.db).await?; let bob_cakes_bakers = cakes_bakers::ActiveModel { cake_id: Set(mud_cake_res.last_insert_id as i32), baker_id: Set(baker_bob_res.last_insert_id as i32), @@ -533,7 +533,7 @@ pub async fn linked() -> Result<(), DbErr> { bakery_id: Set(Some(seaside_bakery_res.last_insert_id as i32)), ..Default::default() }; - let baker_bobby_res: InsertResult = Baker::insert(baker_bobby).exec(&ctx.db).await?; + let baker_bobby_res = Baker::insert(baker_bobby).exec(&ctx.db).await?; let cheese_cake = cake::ActiveModel { name: Set("Cheese Cake".to_owned()), price: Set(dec!(20.5)), @@ -542,7 +542,7 @@ pub async fn linked() -> Result<(), DbErr> { bakery_id: Set(Some(seaside_bakery_res.last_insert_id as i32)), ..Default::default() }; - let cheese_cake_res: InsertResult = Cake::insert(cheese_cake).exec(&ctx.db).await?; + let cheese_cake_res = Cake::insert(cheese_cake).exec(&ctx.db).await?; let bobby_cakes_bakers = cakes_bakers::ActiveModel { cake_id: Set(cheese_cake_res.last_insert_id as i32), baker_id: Set(baker_bobby_res.last_insert_id as i32), @@ -559,7 +559,7 @@ pub async fn linked() -> Result<(), DbErr> { bakery_id: Set(Some(seaside_bakery_res.last_insert_id as i32)), ..Default::default() }; - let chocolate_cake_res: InsertResult = Cake::insert(chocolate_cake).exec(&ctx.db).await?; + let chocolate_cake_res = Cake::insert(chocolate_cake).exec(&ctx.db).await?; let bobby_cakes_bakers = cakes_bakers::ActiveModel { cake_id: Set(chocolate_cake_res.last_insert_id as i32), baker_id: Set(baker_bobby_res.last_insert_id as i32), @@ -575,7 +575,7 @@ pub async fn linked() -> Result<(), DbErr> { notes: Set(Some("Loves cheese cake".to_owned())), ..Default::default() }; - let customer_kate_res: InsertResult = Customer::insert(customer_kate).exec(&ctx.db).await?; + let customer_kate_res = Customer::insert(customer_kate).exec(&ctx.db).await?; let kate_order_1 = order::ActiveModel { bakery_id: Set(seaside_bakery_res.last_insert_id as i32), customer_id: Set(customer_kate_res.last_insert_id as i32), @@ -583,7 +583,7 @@ pub async fn linked() -> Result<(), DbErr> { placed_at: Set(Utc::now().naive_utc()), ..Default::default() }; - let kate_order_1_res: InsertResult = Order::insert(kate_order_1).exec(&ctx.db).await?; + let kate_order_1_res = Order::insert(kate_order_1).exec(&ctx.db).await?; lineitem::ActiveModel { cake_id: Set(cheese_cake_res.last_insert_id as i32), order_id: Set(kate_order_1_res.last_insert_id as i32), @@ -600,7 +600,7 @@ pub async fn linked() -> Result<(), DbErr> { placed_at: Set(Utc::now().naive_utc()), ..Default::default() }; - let kate_order_2_res: InsertResult = Order::insert(kate_order_2).exec(&ctx.db).await?; + let kate_order_2_res = Order::insert(kate_order_2).exec(&ctx.db).await?; lineitem::ActiveModel { cake_id: Set(chocolate_cake_res.last_insert_id as i32), order_id: Set(kate_order_2_res.last_insert_id as i32), @@ -617,7 +617,7 @@ pub async fn linked() -> Result<(), DbErr> { notes: Set(Some("Loves all cakes".to_owned())), ..Default::default() }; - let customer_kara_res: InsertResult = Customer::insert(customer_kara).exec(&ctx.db).await?; + let customer_kara_res = Customer::insert(customer_kara).exec(&ctx.db).await?; let kara_order_1 = order::ActiveModel { bakery_id: Set(seaside_bakery_res.last_insert_id as i32), customer_id: Set(customer_kara_res.last_insert_id as i32), @@ -625,7 +625,7 @@ pub async fn linked() -> Result<(), DbErr> { placed_at: Set(Utc::now().naive_utc()), ..Default::default() }; - let kara_order_1_res: InsertResult = Order::insert(kara_order_1).exec(&ctx.db).await?; + let kara_order_1_res = Order::insert(kara_order_1).exec(&ctx.db).await?; lineitem::ActiveModel { cake_id: Set(mud_cake_res.last_insert_id as i32), order_id: Set(kara_order_1_res.last_insert_id as i32), @@ -642,7 +642,7 @@ pub async fn linked() -> Result<(), DbErr> { placed_at: Set(Utc::now().naive_utc()), ..Default::default() }; - let kara_order_2_res: InsertResult = Order::insert(kara_order_2).exec(&ctx.db).await?; + let kara_order_2_res = Order::insert(kara_order_2).exec(&ctx.db).await?; lineitem::ActiveModel { cake_id: Set(cheese_cake_res.last_insert_id as i32), order_id: Set(kara_order_2_res.last_insert_id as i32), From 4d9789fef48236780702423c4bb10b82a0183429 Mon Sep 17 00:00:00 2001 From: Billy Chan Date: Thu, 2 Sep 2021 15:05:01 +0800 Subject: [PATCH 40/43] cargo fmt doc comments --- rustfmt.toml | 1 + 1 file changed, 1 insertion(+) create mode 100644 rustfmt.toml diff --git a/rustfmt.toml b/rustfmt.toml new file mode 100644 index 00000000..521706a0 --- /dev/null +++ b/rustfmt.toml @@ -0,0 +1 @@ +format_code_in_doc_comments=true \ No newline at end of file From 5060890888fc38e4693eaa9a329cda1b7d2c69bd Mon Sep 17 00:00:00 2001 From: Billy Chan Date: Fri, 3 Sep 2021 14:56:58 +0800 Subject: [PATCH 41/43] cargo +nightly fmt --- src/entity/base_entity.rs | 26 +++++++++----- src/entity/column.rs | 4 +-- src/executor/select.rs | 76 ++++++++++++++++++++++----------------- src/lib.rs | 6 ++-- src/query/insert.rs | 42 +++++++++++----------- src/query/update.rs | 2 +- 6 files changed, 86 insertions(+), 70 deletions(-) diff --git a/src/entity/base_entity.rs b/src/entity/base_entity.rs index 29984953..67f817a7 100644 --- a/src/entity/base_entity.rs +++ b/src/entity/base_entity.rs @@ -137,13 +137,18 @@ pub trait EntityTrait: EntityName { /// assert_eq!( /// db.into_transaction_log(), /// vec![ - /// Transaction::from_sql_and_values( - /// DbBackend::Postgres, r#"SELECT "cake"."id", "cake"."name" FROM "cake" LIMIT $1"#, vec![1u64.into()] - /// ), - /// Transaction::from_sql_and_values( - /// DbBackend::Postgres, r#"SELECT "cake"."id", "cake"."name" FROM "cake""#, vec![] - /// ), - /// ]); + /// Transaction::from_sql_and_values( + /// DbBackend::Postgres, + /// r#"SELECT "cake"."id", "cake"."name" FROM "cake" LIMIT $1"#, + /// vec![1u64.into()] + /// ), + /// Transaction::from_sql_and_values( + /// DbBackend::Postgres, + /// r#"SELECT "cake"."id", "cake"."name" FROM "cake""#, + /// vec![] + /// ), + /// ] + /// ); /// ``` fn find() -> Select { Select::new() @@ -186,8 +191,11 @@ pub trait EntityTrait: EntityName { /// assert_eq!( /// db.into_transaction_log(), /// vec![Transaction::from_sql_and_values( - /// DbBackend::Postgres, r#"SELECT "cake"."id", "cake"."name" FROM "cake" WHERE "cake"."id" = $1"#, vec![11i32.into()] - /// )]); + /// DbBackend::Postgres, + /// r#"SELECT "cake"."id", "cake"."name" FROM "cake" WHERE "cake"."id" = $1"#, + /// vec![11i32.into()] + /// )] + /// ); /// ``` /// Find by composite key /// ``` diff --git a/src/entity/column.rs b/src/entity/column.rs index 611950f5..baaf190c 100644 --- a/src/entity/column.rs +++ b/src/entity/column.rs @@ -103,7 +103,7 @@ pub trait ColumnTrait: IdenStatic + Iterable + FromStr { /// /// assert_eq!( /// cake::Entity::find() - /// .filter(cake::Column::Id.between(2,3)) + /// .filter(cake::Column::Id.between(2, 3)) /// .build(DbBackend::MySql) /// .to_string(), /// "SELECT `cake`.`id`, `cake`.`name` FROM `cake` WHERE `cake`.`id` BETWEEN 2 AND 3" @@ -121,7 +121,7 @@ pub trait ColumnTrait: IdenStatic + Iterable + FromStr { /// /// assert_eq!( /// cake::Entity::find() - /// .filter(cake::Column::Id.not_between(2,3)) + /// .filter(cake::Column::Id.not_between(2, 3)) /// .build(DbBackend::MySql) /// .to_string(), /// "SELECT `cake`.`id`, `cake`.`name` FROM `cake` WHERE `cake`.`id` NOT BETWEEN 2 AND 3" diff --git a/src/executor/select.rs b/src/executor/select.rs index 0a5163de..ef30bb3d 100644 --- a/src/executor/select.rs +++ b/src/executor/select.rs @@ -290,14 +290,15 @@ where /// /// # let _: Result<(), DbErr> = smol::block_on(async { /// # - /// let res: Vec = cake::Entity::find().from_raw_sql( - /// Statement::from_sql_and_values( - /// DbBackend::Postgres, r#"SELECT "cake"."name", count("cake"."id") AS "num_of_cakes" FROM "cake""#, vec![] - /// ) - /// ) - /// .into_model::() - /// .all(&db) - /// .await?; + /// let res: Vec = cake::Entity::find() + /// .from_raw_sql(Statement::from_sql_and_values( + /// DbBackend::Postgres, + /// r#"SELECT "cake"."name", count("cake"."id") AS "num_of_cakes" FROM "cake""#, + /// vec![], + /// )) + /// .into_model::() + /// .all(&db) + /// .await?; /// /// assert_eq!( /// res, @@ -318,11 +319,12 @@ where /// /// assert_eq!( /// db.into_transaction_log(), - /// vec![ - /// Transaction::from_sql_and_values( - /// DbBackend::Postgres, r#"SELECT "cake"."name", count("cake"."id") AS "num_of_cakes" FROM "cake""#, vec![] - /// ), - /// ]); + /// vec![Transaction::from_sql_and_values( + /// DbBackend::Postgres, + /// r#"SELECT "cake"."name", count("cake"."id") AS "num_of_cakes" FROM "cake""#, + /// vec![] + /// ),] + /// ); /// ``` pub fn into_model(self) -> SelectorRaw> where @@ -407,22 +409,26 @@ where /// /// # let _: Result<(), DbErr> = smol::block_on(async { /// # - /// let _: Option = cake::Entity::find().from_raw_sql( - /// Statement::from_sql_and_values( - /// DbBackend::Postgres, r#"SELECT "cake"."id", "cake"."name" FROM "cake" WHERE "id" = $1"#, vec![1.into()] - /// ) - /// ).one(&db).await?; + /// let _: Option = cake::Entity::find() + /// .from_raw_sql(Statement::from_sql_and_values( + /// DbBackend::Postgres, + /// r#"SELECT "cake"."id", "cake"."name" FROM "cake" WHERE "id" = $1"#, + /// vec![1.into()], + /// )) + /// .one(&db) + /// .await?; /// # /// # Ok(()) /// # }); /// /// assert_eq!( /// db.into_transaction_log(), - /// vec![ - /// Transaction::from_sql_and_values( - /// DbBackend::Postgres, r#"SELECT "cake"."id", "cake"."name" FROM "cake" WHERE "id" = $1"#, vec![1.into()] - /// ), - /// ]); + /// vec![Transaction::from_sql_and_values( + /// DbBackend::Postgres, + /// r#"SELECT "cake"."id", "cake"."name" FROM "cake" WHERE "id" = $1"#, + /// vec![1.into()] + /// ),] + /// ); /// ``` pub async fn one(self, db: &DatabaseConnection) -> Result, DbErr> { let row = db.query_one(self.stmt).await?; @@ -442,22 +448,26 @@ where /// /// # let _: Result<(), DbErr> = smol::block_on(async { /// # - /// let _: Vec = cake::Entity::find().from_raw_sql( - /// Statement::from_sql_and_values( - /// DbBackend::Postgres, r#"SELECT "cake"."id", "cake"."name" FROM "cake""#, vec![] - /// ) - /// ).all(&db).await?; + /// let _: Vec = cake::Entity::find() + /// .from_raw_sql(Statement::from_sql_and_values( + /// DbBackend::Postgres, + /// r#"SELECT "cake"."id", "cake"."name" FROM "cake""#, + /// vec![], + /// )) + /// .all(&db) + /// .await?; /// # /// # Ok(()) /// # }); /// /// assert_eq!( /// db.into_transaction_log(), - /// vec![ - /// Transaction::from_sql_and_values( - /// DbBackend::Postgres, r#"SELECT "cake"."id", "cake"."name" FROM "cake""#, vec![] - /// ), - /// ]); + /// vec![Transaction::from_sql_and_values( + /// DbBackend::Postgres, + /// r#"SELECT "cake"."id", "cake"."name" FROM "cake""#, + /// vec![] + /// ),] + /// ); /// ``` pub async fn all(self, db: &DatabaseConnection) -> Result, DbErr> { let rows = db.query_all(self.stmt).await?; diff --git a/src/lib.rs b/src/lib.rs index ee5bb8cb..24022090 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -77,10 +77,8 @@ //! let fruits: Vec = cheese.find_related(Fruit).all(db).await?; //! //! // find related models (eager) -//! let cake_with_fruits: Vec<(cake::Model, Vec)> = Cake::find() -//! .find_with_related(Fruit) -//! .all(db) -//! .await?; +//! let cake_with_fruits: Vec<(cake::Model, Vec)> = +//! Cake::find().find_with_related(Fruit).all(db).await?; //! //! # Ok(()) //! # } diff --git a/src/query/insert.rs b/src/query/insert.rs index abd52b4b..a65071e1 100644 --- a/src/query/insert.rs +++ b/src/query/insert.rs @@ -43,11 +43,11 @@ where /// /// assert_eq!( /// Insert::one(cake::Model { - /// id: 1, - /// name: "Apple Pie".to_owned(), - /// }) - /// .build(DbBackend::Postgres) - /// .to_string(), + /// id: 1, + /// name: "Apple Pie".to_owned(), + /// }) + /// .build(DbBackend::Postgres) + /// .to_string(), /// r#"INSERT INTO "cake" ("id", "name") VALUES (1, 'Apple Pie')"#, /// ); /// ``` @@ -57,11 +57,11 @@ where /// /// assert_eq!( /// Insert::one(cake::ActiveModel { - /// id: Unset(None), - /// name: Set("Apple Pie".to_owned()), - /// }) - /// .build(DbBackend::Postgres) - /// .to_string(), + /// id: Unset(None), + /// name: Set("Apple Pie".to_owned()), + /// }) + /// .build(DbBackend::Postgres) + /// .to_string(), /// r#"INSERT INTO "cake" ("name") VALUES ('Apple Pie')"#, /// ); /// ``` @@ -79,17 +79,17 @@ where /// /// assert_eq!( /// Insert::many(vec![ - /// cake::Model { - /// id: 1, - /// name: "Apple Pie".to_owned(), - /// }, - /// cake::Model { - /// id: 2, - /// name: "Orange Scone".to_owned(), - /// } - /// ]) - /// .build(DbBackend::Postgres) - /// .to_string(), + /// cake::Model { + /// id: 1, + /// name: "Apple Pie".to_owned(), + /// }, + /// cake::Model { + /// id: 2, + /// name: "Orange Scone".to_owned(), + /// } + /// ]) + /// .build(DbBackend::Postgres) + /// .to_string(), /// r#"INSERT INTO "cake" ("id", "name") VALUES (1, 'Apple Pie'), (2, 'Orange Scone')"#, /// ); /// ``` diff --git a/src/query/update.rs b/src/query/update.rs index 21fd39cb..8cd66c37 100644 --- a/src/query/update.rs +++ b/src/query/update.rs @@ -59,7 +59,7 @@ impl Update { /// Update many ActiveModel /// /// ``` - /// use sea_orm::{entity::*, query::*, tests_cfg::fruit, sea_query::Expr, DbBackend}; + /// use sea_orm::{entity::*, query::*, sea_query::Expr, tests_cfg::fruit, DbBackend}; /// /// assert_eq!( /// Update::many(fruit::Entity) From ca56f2790941480753b413ed4c86da4d509515fc Mon Sep 17 00:00:00 2001 From: Billy Chan Date: Fri, 3 Sep 2021 16:20:57 +0800 Subject: [PATCH 42/43] cargo clippy --- .github/workflows/rust.yml | 18 ++++++++++++++++++ tests/basic.rs | 3 +-- tests/common/mod.rs | 2 +- tests/common/setup/schema.rs | 3 +-- tests/crud/create_cake.rs | 3 +-- tests/crud/create_lineitem.rs | 1 - tests/crud/create_order.rs | 1 - tests/crud/mod.rs | 16 +++++++++++----- tests/crud/updates.rs | 6 +++--- tests/crud_tests.rs | 32 ++++++++++++++++---------------- tests/primary_key_tests.rs | 5 +++-- tests/query_tests.rs | 6 +++--- tests/relational_tests.rs | 12 ++++++------ tests/sequential_op_tests.rs | 12 ++++++------ 14 files changed, 70 insertions(+), 50 deletions(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 426cb003..f2fc7579 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -11,6 +11,24 @@ env: jobs: + clippy: + name: Clippy + runs-on: ubuntu-20.04 + steps: + - uses: actions/checkout@v2 + + - uses: actions-rs/toolchain@v1 + with: + profile: minimal + toolchain: stable + components: clippy + override: true + + - uses: actions-rs/clippy-check@v1 + with: + token: ${{ secrets.GITHUB_TOKEN }} + args: --all-targets + compile-sqlite: name: Compile SQLite runs-on: ubuntu-20.04 diff --git a/tests/basic.rs b/tests/basic.rs index 78e3f25c..e62f1ad6 100644 --- a/tests/basic.rs +++ b/tests/basic.rs @@ -1,7 +1,6 @@ pub mod common; -#[allow(unused_imports)] -use sea_orm::{entity::*, error::*, sea_query, tests_cfg::*, Database, DbConn}; +pub use sea_orm::{entity::*, error::*, sea_query, tests_cfg::*, Database, DbConn}; // DATABASE_URL="sqlite::memory:" cargo test --features sqlx-sqlit,runtime-async-std --test basic #[sea_orm_macros::test] diff --git a/tests/common/mod.rs b/tests/common/mod.rs index 54630999..2392ff72 100644 --- a/tests/common/mod.rs +++ b/tests/common/mod.rs @@ -19,7 +19,7 @@ impl TestContext { let db: DatabaseConnection = setup::setup(&base_url, test_name).await; Self { - base_url: base_url, + base_url, db_name: test_name.to_string(), db, } diff --git a/tests/common/setup/schema.rs b/tests/common/setup/schema.rs index 32e7d8a5..e3844dd2 100644 --- a/tests/common/setup/schema.rs +++ b/tests/common/setup/schema.rs @@ -1,11 +1,10 @@ +pub use super::super::bakery_chain::*; use pretty_assertions::assert_eq; 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, diff --git a/tests/crud/create_cake.rs b/tests/crud/create_cake.rs index eea74350..4fa914a5 100644 --- a/tests/crud/create_cake.rs +++ b/tests/crud/create_cake.rs @@ -51,7 +51,6 @@ pub async fn test_create_cake(db: &DbConn) { let cake_baker = cakes_bakers::ActiveModel { cake_id: Set(cake_insert_res.last_insert_id as i32), baker_id: Set(baker_insert_res.last_insert_id as i32), - ..Default::default() }; let cake_baker_res = CakesBakers::insert(cake_baker.clone()) .exec(db) @@ -70,7 +69,7 @@ pub async fn test_create_cake(db: &DbConn) { let cake_model = cake.unwrap(); assert_eq!(cake_model.name, "Mud Cake"); assert_eq!(cake_model.price, dec!(10.25)); - assert_eq!(cake_model.gluten_free, false); + assert!(!cake_model.gluten_free); assert_eq!( cake_model .find_related(Bakery) diff --git a/tests/crud/create_lineitem.rs b/tests/crud/create_lineitem.rs index 9acba961..da82cc82 100644 --- a/tests/crud/create_lineitem.rs +++ b/tests/crud/create_lineitem.rs @@ -50,7 +50,6 @@ pub async fn test_create_lineitem(db: &DbConn) { let cake_baker = cakes_bakers::ActiveModel { cake_id: Set(cake_insert_res.last_insert_id as i32), baker_id: Set(baker_insert_res.last_insert_id as i32), - ..Default::default() }; let cake_baker_res = CakesBakers::insert(cake_baker.clone()) .exec(db) diff --git a/tests/crud/create_order.rs b/tests/crud/create_order.rs index 8d10cfcd..ba8ff09b 100644 --- a/tests/crud/create_order.rs +++ b/tests/crud/create_order.rs @@ -50,7 +50,6 @@ pub async fn test_create_order(db: &DbConn) { let cake_baker = cakes_bakers::ActiveModel { cake_id: Set(cake_insert_res.last_insert_id as i32), baker_id: Set(baker_insert_res.last_insert_id as i32), - ..Default::default() }; let cake_baker_res = CakesBakers::insert(cake_baker.clone()) .exec(db) diff --git a/tests/crud/mod.rs b/tests/crud/mod.rs index 7e024055..916878c8 100644 --- a/tests/crud/mod.rs +++ b/tests/crud/mod.rs @@ -1,7 +1,3 @@ -use sea_orm::{entity::*, DbConn}; - -pub use super::common::bakery_chain::*; - pub mod create_baker; pub mod create_cake; pub mod create_lineitem; @@ -9,6 +5,16 @@ pub mod create_order; pub mod deletes; pub mod updates; +pub use create_baker::*; +pub use create_cake::*; +pub use create_lineitem::*; +pub use create_order::*; +pub use deletes::*; +pub use updates::*; + +pub use super::common::bakery_chain::*; +use sea_orm::{entity::*, DbConn}; + pub async fn test_create_bakery(db: &DbConn) { let seaside_bakery = bakery::ActiveModel { name: Set("SeaSide Bakery".to_owned()), @@ -28,7 +34,7 @@ pub async fn test_create_bakery(db: &DbConn) { assert!(bakery.is_some()); let bakery_model = bakery.unwrap(); assert_eq!(bakery_model.name, "SeaSide Bakery"); - assert_eq!(bakery_model.profit_margin, 10.4); + assert!((bakery_model.profit_margin - 10.4).abs() < f64::EPSILON); } pub async fn test_create_customer(db: &DbConn) { diff --git a/tests/crud/updates.rs b/tests/crud/updates.rs index 172c9c21..83b9a5d3 100644 --- a/tests/crud/updates.rs +++ b/tests/crud/updates.rs @@ -36,7 +36,7 @@ pub async fn test_update_cake(db: &DbConn) { let cake_model = cake.unwrap(); assert_eq!(cake_model.name, "Mud Cake"); assert_eq!(cake_model.price, dec!(10.25)); - assert_eq!(cake_model.gluten_free, false); + assert!(!cake_model.gluten_free); let mut cake_am: cake::ActiveModel = cake_model.into(); cake_am.name = Set("Extra chocolate mud cake".to_owned()); @@ -75,7 +75,7 @@ pub async fn test_update_bakery(db: &DbConn) { assert!(bakery.is_some()); let bakery_model = bakery.unwrap(); assert_eq!(bakery_model.name, "SeaSide Bakery"); - assert_eq!(bakery_model.profit_margin, 10.4); + assert!((bakery_model.profit_margin - 10.40).abs() < f64::EPSILON); let mut bakery_am: bakery::ActiveModel = bakery_model.into(); bakery_am.name = Set("SeaBreeze Bakery".to_owned()); @@ -92,7 +92,7 @@ pub async fn test_update_bakery(db: &DbConn) { .expect("could not find bakery"); let bakery_model = bakery.unwrap(); assert_eq!(bakery_model.name, "SeaBreeze Bakery"); - assert_eq!(bakery_model.profit_margin, 12.00); + assert!((bakery_model.profit_margin - 12.00).abs() < f64::EPSILON); } pub async fn test_update_deleted_customer(db: &DbConn) { diff --git a/tests/crud_tests.rs b/tests/crud_tests.rs index 3c26ddfd..7edb3500 100644 --- a/tests/crud_tests.rs +++ b/tests/crud_tests.rs @@ -1,10 +1,10 @@ -use sea_orm::DatabaseConnection; - pub mod common; -pub use common::{bakery_chain::*, setup::*, TestContext}; - mod crud; +pub use common::{bakery_chain::*, setup::*, TestContext}; +pub use crud::*; +use sea_orm::DatabaseConnection; + // Run the test locally: // DATABASE_URL="mysql://root:root@localhost" cargo test --features sqlx-mysql,runtime-async-std --test crud_tests // DATABASE_URL="postgres://root:root@localhost" cargo test --features sqlx-postgres,runtime-async-std --test crud_tests @@ -20,18 +20,18 @@ async fn main() { ctx.delete().await; } -async fn create_entities(db: &DatabaseConnection) { - crud::test_create_bakery(db).await; - crud::create_baker::test_create_baker(db).await; - crud::test_create_customer(db).await; - crud::create_cake::test_create_cake(db).await; - crud::create_lineitem::test_create_lineitem(db).await; - crud::create_order::test_create_order(db).await; +pub async fn create_entities(db: &DatabaseConnection) { + test_create_bakery(db).await; + test_create_baker(db).await; + test_create_customer(db).await; + test_create_cake(db).await; + test_create_lineitem(db).await; + test_create_order(db).await; - crud::updates::test_update_cake(db).await; - crud::updates::test_update_bakery(db).await; - crud::updates::test_update_deleted_customer(db).await; + test_update_cake(db).await; + test_update_bakery(db).await; + test_update_deleted_customer(db).await; - crud::deletes::test_delete_cake(db).await; - crud::deletes::test_delete_bakery(db).await; + test_delete_cake(db).await; + test_delete_bakery(db).await; } diff --git a/tests/primary_key_tests.rs b/tests/primary_key_tests.rs index 74dd46e5..ea8255e9 100644 --- a/tests/primary_key_tests.rs +++ b/tests/primary_key_tests.rs @@ -1,6 +1,7 @@ -use sea_orm::{entity::prelude::*, DatabaseConnection, Set}; pub mod common; + pub use common::{bakery_chain::*, setup::*, TestContext}; +use sea_orm::{entity::prelude::*, DatabaseConnection, Set}; use uuid::Uuid; #[sea_orm_macros::test] @@ -19,7 +20,7 @@ async fn main() -> Result<(), DbErr> { Ok(()) } -async fn create_metadata(db: &DatabaseConnection) -> Result<(), DbErr> { +pub async fn create_metadata(db: &DatabaseConnection) -> Result<(), DbErr> { let metadata = metadata::ActiveModel { uuid: Set(Uuid::new_v4()), key: Set("markup".to_owned()), diff --git a/tests/query_tests.rs b/tests/query_tests.rs index 4e905686..2b5a2295 100644 --- a/tests/query_tests.rs +++ b/tests/query_tests.rs @@ -1,8 +1,8 @@ -use sea_orm::entity::*; -use sea_orm::QueryFilter; - pub mod common; + pub use common::{bakery_chain::*, setup::*, TestContext}; +pub use sea_orm::entity::*; +pub use sea_orm::QueryFilter; // Run the test locally: // DATABASE_URL="mysql://root:@localhost" cargo test --features sqlx-mysql,runtime-async-std --test query_tests diff --git a/tests/relational_tests.rs b/tests/relational_tests.rs index c950ddc6..caef9c3c 100644 --- a/tests/relational_tests.rs +++ b/tests/relational_tests.rs @@ -1,11 +1,11 @@ -use chrono::offset::Utc; -use rust_decimal::prelude::*; -use rust_decimal_macros::dec; -use sea_orm::{entity::*, query::*, DbErr, FromQueryResult}; -use uuid::Uuid; - pub mod common; + +pub use chrono::offset::Utc; pub use common::{bakery_chain::*, setup::*, TestContext}; +pub use rust_decimal::prelude::*; +pub use rust_decimal_macros::dec; +pub use sea_orm::{entity::*, query::*, DbErr, FromQueryResult}; +pub use uuid::Uuid; // Run the test locally: // DATABASE_URL="mysql://root:@localhost" cargo test --features sqlx-mysql,runtime-async-std-native-tls --test relational_tests diff --git a/tests/sequential_op_tests.rs b/tests/sequential_op_tests.rs index 6c490762..28333d84 100644 --- a/tests/sequential_op_tests.rs +++ b/tests/sequential_op_tests.rs @@ -1,11 +1,11 @@ -use chrono::offset::Utc; -use rust_decimal::prelude::*; -use rust_decimal_macros::dec; -use sea_orm::{entity::*, query::*, DatabaseConnection, FromQueryResult}; -use uuid::Uuid; - pub mod common; + +pub use chrono::offset::Utc; pub use common::{bakery_chain::*, setup::*, TestContext}; +pub use rust_decimal::prelude::*; +pub use rust_decimal_macros::dec; +pub use sea_orm::{entity::*, query::*, DatabaseConnection, FromQueryResult}; +pub use uuid::Uuid; // Run the test locally: // DATABASE_URL="mysql://root:@localhost" cargo test --features sqlx-mysql,runtime-async-std --test sequential_op_tests From e852a09498e7a7ad8ad24af5688f1f77d148d05f Mon Sep 17 00:00:00 2001 From: Billy Chan Date: Fri, 3 Sep 2021 16:51:22 +0800 Subject: [PATCH 43/43] cargo clippy --- .github/workflows/rust.yml | 2 +- sea-orm-codegen/src/entity/column.rs | 27 +++++------------------ sea-orm-codegen/src/entity/transformer.rs | 14 ++++++------ sea-orm-codegen/src/entity/writer.rs | 2 +- 4 files changed, 15 insertions(+), 30 deletions(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index f2fc7579..b7f014e1 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -27,7 +27,7 @@ jobs: - uses: actions-rs/clippy-check@v1 with: token: ${{ secrets.GITHUB_TOKEN }} - args: --all-targets + args: --all-targets --all compile-sqlite: name: Compile SQLite diff --git a/sea-orm-codegen/src/entity/column.rs b/sea-orm-codegen/src/entity/column.rs index a2c3b4f5..0b9eb8de 100644 --- a/sea-orm-codegen/src/entity/column.rs +++ b/sea-orm-codegen/src/entity/column.rs @@ -119,33 +119,18 @@ impl From<&ColumnDef> for Column { Some(ty) => ty.clone(), None => panic!("ColumnType should not be empty"), }; - let auto_increments: Vec = col_def + let auto_increment = col_def .get_column_spec() .iter() - .filter_map(|spec| match spec { - ColumnSpec::AutoIncrement => Some(true), - _ => None, - }) - .collect(); - let auto_increment = !auto_increments.is_empty(); - let not_nulls: Vec = col_def + .any(|spec| matches!(spec, ColumnSpec::AutoIncrement)); + let not_null = col_def .get_column_spec() .iter() - .filter_map(|spec| match spec { - ColumnSpec::NotNull => Some(true), - _ => None, - }) - .collect(); - let not_null = !not_nulls.is_empty(); - let uniques: Vec = col_def + .any(|spec| matches!(spec, ColumnSpec::NotNull)); + let unique = col_def .get_column_spec() .iter() - .filter_map(|spec| match spec { - ColumnSpec::UniqueKey => Some(true), - _ => None, - }) - .collect(); - let unique = !uniques.is_empty(); + .any(|spec| matches!(spec, ColumnSpec::UniqueKey)); Self { name, col_type, diff --git a/sea-orm-codegen/src/entity/transformer.rs b/sea-orm-codegen/src/entity/transformer.rs index 75dc25c7..840eade0 100644 --- a/sea-orm-codegen/src/entity/transformer.rs +++ b/sea-orm-codegen/src/entity/transformer.rs @@ -34,11 +34,6 @@ impl EntityTransformer { .iter() .map(|col_def| col_def.into()) .collect(); - let unique_columns: Vec = columns - .iter() - .filter(|col| col.unique) - .map(|col| col.name.clone()) - .collect(); let relations = table_create .get_foreign_key_create_stmts() .iter() @@ -85,8 +80,13 @@ impl EntityTransformer { false => { let ref_table = rel.ref_table; let mut unique = true; - for col in rel.columns.iter() { - if !unique_columns.contains(col) { + for column in rel.columns.iter() { + if !entity + .columns + .iter() + .filter(|col| col.unique) + .any(|col| col.name.as_str() == column) + { unique = false; break; } diff --git a/sea-orm-codegen/src/entity/writer.rs b/sea-orm-codegen/src/entity/writer.rs index 7accac2d..19e9af1c 100644 --- a/sea-orm-codegen/src/entity/writer.rs +++ b/sea-orm-codegen/src/entity/writer.rs @@ -308,7 +308,7 @@ mod tests { use sea_query::ColumnType; use std::io::{self, BufRead, BufReader}; - const ENTITY_FILES: [&'static str; 5] = [ + const ENTITY_FILES: [&str; 5] = [ include_str!("../../tests/entity/cake.rs"), include_str!("../../tests/entity/cake_filling.rs"), include_str!("../../tests/entity/filling.rs"),