From 734608471c2b14c96bda1e61df84fb83b2ee539f Mon Sep 17 00:00:00 2001 From: Billy Chan Date: Wed, 20 Oct 2021 18:54:08 +0800 Subject: [PATCH 01/16] WIP --- src/entity/active_enum.rs | 18 +++++++++--------- src/entity/column.rs | 4 ++++ tests/active_enum_tests.rs | 8 ++++---- tests/common/features/active_enum.rs | 10 +++++----- 4 files changed, 22 insertions(+), 18 deletions(-) diff --git a/src/entity/active_enum.rs b/src/entity/active_enum.rs index 5eb77b9f2..edca0aac1 100644 --- a/src/entity/active_enum.rs +++ b/src/entity/active_enum.rs @@ -1,4 +1,4 @@ -use crate::{ColumnDef, DbErr, TryGetable}; +use crate::{ColumnDef, DbErr, Iterable, TryGetable}; use sea_query::{Nullable, Value, ValueType}; /// A Rust representation of enum defined in database. @@ -17,7 +17,7 @@ use sea_query::{Nullable, Value, ValueType}; /// use sea_orm::entity::prelude::*; /// /// // Using the derive macro -/// #[derive(Debug, PartialEq, DeriveActiveEnum)] +/// #[derive(Debug, PartialEq, EnumIter, DeriveActiveEnum)] /// #[sea_orm(rs_type = "String", db_type = "String(Some(1))")] /// pub enum DeriveCategory { /// #[sea_orm(string_value = "B")] @@ -27,7 +27,7 @@ use sea_query::{Nullable, Value, ValueType}; /// } /// /// // Implementing it manually -/// #[derive(Debug, PartialEq)] +/// #[derive(Debug, PartialEq, EnumIter)] /// pub enum Category { /// Big, /// Small, @@ -80,7 +80,7 @@ use sea_query::{Nullable, Value, ValueType}; /// Small, /// } /// -/// #[derive(Clone, Debug, PartialEq, DeriveEntityModel)] +/// #[derive(Clone, Debug, PartialEq, EnumIter, DeriveEntityModel)] /// #[sea_orm(table_name = "active_enum")] /// pub struct Model { /// #[sea_orm(primary_key)] @@ -95,7 +95,7 @@ use sea_query::{Nullable, Value, ValueType}; /// /// impl ActiveModelBehavior for ActiveModel {} /// ``` -pub trait ActiveEnum: Sized { +pub trait ActiveEnum: Sized + Iterable { /// Define the Rust type that each enum variant represents. type Value: Into + ValueType + Nullable + TryGetable; @@ -117,7 +117,7 @@ mod tests { #[test] fn active_enum_string() { - #[derive(Debug, PartialEq)] + #[derive(Debug, PartialEq, EnumIter)] pub enum Category { Big, Small, @@ -150,7 +150,7 @@ mod tests { } } - #[derive(Debug, PartialEq, DeriveActiveEnum)] + #[derive(Debug, PartialEq, EnumIter, DeriveActiveEnum)] #[sea_orm(rs_type = "String", db_type = "String(Some(1))")] pub enum DeriveCategory { #[sea_orm(string_value = "B")] @@ -201,7 +201,7 @@ mod tests { fn active_enum_derive_signed_integers() { macro_rules! test_int { ($ident: ident, $rs_type: expr, $db_type: expr, $col_def: ident) => { - #[derive(Debug, PartialEq, DeriveActiveEnum)] + #[derive(Debug, PartialEq, EnumIter, DeriveActiveEnum)] #[sea_orm(rs_type = $rs_type, db_type = $db_type)] pub enum $ident { #[sea_orm(num_value = 1)] @@ -241,7 +241,7 @@ mod tests { fn active_enum_derive_unsigned_integers() { macro_rules! test_uint { ($ident: ident, $rs_type: expr, $db_type: expr, $col_def: ident) => { - #[derive(Debug, PartialEq, DeriveActiveEnum)] + #[derive(Debug, PartialEq, EnumIter, DeriveActiveEnum)] #[sea_orm(rs_type = $rs_type, db_type = $db_type)] pub enum $ident { #[sea_orm(num_value = 1)] diff --git a/src/entity/column.rs b/src/entity/column.rs index 25ed84473..384be24a0 100644 --- a/src/entity/column.rs +++ b/src/entity/column.rs @@ -34,6 +34,7 @@ pub enum ColumnType { JsonBinary, Custom(String), Uuid, + Enum(String), } macro_rules! bind_oper { @@ -295,6 +296,9 @@ impl From for sea_query::ColumnType { sea_query::ColumnType::Custom(sea_query::SeaRc::new(sea_query::Alias::new(&s))) } ColumnType::Uuid => sea_query::ColumnType::Uuid, + ColumnType::Enum(s) => { + sea_query::ColumnType::Custom(sea_query::SeaRc::new(sea_query::Alias::new(&s))) + } } } } diff --git a/tests/active_enum_tests.rs b/tests/active_enum_tests.rs index 568524814..b0c72a822 100644 --- a/tests/active_enum_tests.rs +++ b/tests/active_enum_tests.rs @@ -24,7 +24,7 @@ pub async fn insert_active_enum(db: &DatabaseConnection) -> Result<(), DbErr> { let am = ActiveModel { category: Set(None), color: Set(None), - // tea: Set(None), + tea: Set(None), ..Default::default() } .insert(db) @@ -36,14 +36,14 @@ pub async fn insert_active_enum(db: &DatabaseConnection) -> Result<(), DbErr> { id: 1, category: None, color: None, - // tea: None, + tea: None, } ); ActiveModel { category: Set(Some(Category::Big)), color: Set(Some(Color::Black)), - // tea: Set(Some(Tea::EverydayTea)), + tea: Set(Some(Tea::EverydayTea)), ..am } .save(db) @@ -55,7 +55,7 @@ pub async fn insert_active_enum(db: &DatabaseConnection) -> Result<(), DbErr> { id: 1, category: Some(Category::Big), color: Some(Color::Black), - // tea: Some(Tea::EverydayTea), + tea: Some(Tea::EverydayTea), } ); diff --git a/tests/common/features/active_enum.rs b/tests/common/features/active_enum.rs index d7b15443e..229d44466 100644 --- a/tests/common/features/active_enum.rs +++ b/tests/common/features/active_enum.rs @@ -7,7 +7,7 @@ pub struct Model { pub id: i32, pub category: Option, pub color: Option, - // pub tea: Option, + pub tea: Option, } #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] @@ -15,7 +15,7 @@ pub enum Relation {} impl ActiveModelBehavior for ActiveModel {} -#[derive(Debug, Clone, PartialEq, DeriveActiveEnum)] +#[derive(Debug, Clone, PartialEq, EnumIter, DeriveActiveEnum)] #[sea_orm(rs_type = "String", db_type = "String(Some(1))")] pub enum Category { #[sea_orm(string_value = "B")] @@ -24,7 +24,7 @@ pub enum Category { Small, } -#[derive(Debug, Clone, PartialEq, DeriveActiveEnum)] +#[derive(Debug, Clone, PartialEq, EnumIter, DeriveActiveEnum)] #[sea_orm(rs_type = "i32", db_type = r#"Integer"#)] pub enum Color { #[sea_orm(num_value = 0)] @@ -33,8 +33,8 @@ pub enum Color { White, } -#[derive(Debug, Clone, PartialEq, DeriveActiveEnum)] -#[sea_orm(rs_type = "String", db_type = r#"Custom("tea".to_owned())"#)] +#[derive(Debug, Clone, PartialEq, EnumIter, DeriveActiveEnum)] +#[sea_orm(rs_type = "String", db_type = r#"Enum("tea".to_owned())"#)] pub enum Tea { #[sea_orm(string_value = "EverydayTea")] EverydayTea, From 80c72004d15afa8c4e6920abab76e91d55f5161a Mon Sep 17 00:00:00 2001 From: Billy Chan Date: Thu, 21 Oct 2021 15:40:22 +0800 Subject: [PATCH 02/16] Try Postgres enum --- Cargo.toml | 2 +- src/entity/active_enum.rs | 2 +- src/entity/column.rs | 7 ++++++ src/query/insert.rs | 19 +++++++++++---- src/query/select.rs | 20 ++++++++++++--- src/query/update.rs | 12 +++++++-- tests/active_enum_tests.rs | 7 +++++- tests/common/features/schema.rs | 43 +++++++++++++++++++++++++++++++-- 8 files changed, 96 insertions(+), 16 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 716903748..42f97b62e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -30,7 +30,7 @@ futures-util = { version = "^0.3" } log = { version = "^0.4", optional = true } rust_decimal = { version = "^1", optional = true } sea-orm-macros = { version = "^0.3.0", path = "sea-orm-macros", optional = true } -sea-query = { version = "^0.18.0", features = ["thread-safe"] } +sea-query = { version = "^0.18.0", git = "https://github.com/SeaQL/sea-query.git", branch = "sea-orm/active-enum-1", features = ["thread-safe"] } sea-strum = { version = "^0.21", features = ["derive", "sea-orm"] } serde = { version = "^1.0", features = ["derive"] } serde_json = { version = "^1", optional = true } diff --git a/src/entity/active_enum.rs b/src/entity/active_enum.rs index edca0aac1..b42b31f7b 100644 --- a/src/entity/active_enum.rs +++ b/src/entity/active_enum.rs @@ -71,7 +71,7 @@ use sea_query::{Nullable, Value, ValueType}; /// use sea_orm::entity::prelude::*; /// /// // Define the `Category` active enum -/// #[derive(Debug, Clone, PartialEq, DeriveActiveEnum)] +/// #[derive(Debug, Clone, PartialEq, EnumIter, DeriveActiveEnum)] /// #[sea_orm(rs_type = "String", db_type = "String(Some(1))")] /// pub enum Category { /// #[sea_orm(string_value = "B")] diff --git a/src/entity/column.rs b/src/entity/column.rs index 384be24a0..32fe030c6 100644 --- a/src/entity/column.rs +++ b/src/entity/column.rs @@ -242,6 +242,13 @@ impl ColumnType { indexed: false, } } + + pub(crate) fn get_enum_name(&self) -> Option<&String> { + match self { + ColumnType::Enum(s) => Some(s), + _ => None, + } + } } impl ColumnDef { diff --git a/src/query/insert.rs b/src/query/insert.rs index 5e504a0c5..f7f7c7d38 100644 --- a/src/query/insert.rs +++ b/src/query/insert.rs @@ -1,9 +1,9 @@ use crate::{ - ActiveModelTrait, EntityName, EntityTrait, IntoActiveModel, Iterable, PrimaryKeyTrait, - QueryTrait, + ActiveModelTrait, ColumnTrait, EntityName, EntityTrait, IntoActiveModel, Iterable, + PrimaryKeyTrait, QueryTrait, }; use core::marker::PhantomData; -use sea_query::{InsertStatement, ValueTuple}; +use sea_query::{Alias, Expr, Func, InsertStatement, ValueTuple}; #[derive(Debug)] pub struct Insert @@ -124,6 +124,9 @@ where for (idx, col) in ::Column::iter().enumerate() { let av = am.take(col); let av_has_val = av.is_set() || av.is_unchanged(); + let col_def = col.def(); + let enum_name = col_def.get_column_type().get_enum_name(); + if columns_empty { self.columns.push(av_has_val); } else if self.columns[idx] != av_has_val { @@ -131,11 +134,17 @@ where } if av_has_val { columns.push(col); - values.push(av.into_value().unwrap()); + let val = av.into_value().unwrap(); + let expr = if let Some(enum_name) = enum_name { + Func::cast_as(val, Alias::new(&enum_name)) + } else { + Expr::val(val).into() + }; + values.push(expr); } } self.query.columns(columns); - self.query.values_panic(values); + self.query.exprs_panic(values); self } diff --git a/src/query/select.rs b/src/query/select.rs index 1b0c93c3d..5a2e0cdc2 100644 --- a/src/query/select.rs +++ b/src/query/select.rs @@ -2,7 +2,7 @@ use crate::{ColumnTrait, EntityTrait, Iterable, QueryFilter, QueryOrder, QuerySe use core::fmt::Debug; use core::marker::PhantomData; pub use sea_query::JoinType; -use sea_query::{DynIden, IntoColumnRef, SeaRc, SelectStatement, SimpleExpr}; +use sea_query::{Alias, DynIden, Expr, Func, IntoColumnRef, SeaRc, SelectStatement, SimpleExpr}; #[derive(Clone, Debug)] pub struct Select @@ -109,13 +109,25 @@ where } fn prepare_select(mut self) -> Self { - self.query.columns(self.column_list()); + self.query.exprs(self.column_list()); self } - fn column_list(&self) -> Vec<(DynIden, E::Column)> { + fn column_list(&self) -> Vec { let table = SeaRc::new(E::default()) as DynIden; - E::Column::iter().map(|col| (table.clone(), col)).collect() + E::Column::iter() + .map(|col| { + let col_def = col.def(); + let enum_name = col_def.get_column_type().get_enum_name(); + let col_expr = Expr::tbl(table.clone(), col); + let col_expr = if let Some(_) = enum_name { + Func::cast_expr_as(col_expr, Alias::new("text")) + } else { + col_expr.into() + }; + col_expr + }) + .collect() } fn prepare_from(mut self) -> Self { diff --git a/src/query/update.rs b/src/query/update.rs index fec7757cc..2f9bdb3bc 100644 --- a/src/query/update.rs +++ b/src/query/update.rs @@ -3,7 +3,7 @@ use crate::{ QueryTrait, }; use core::marker::PhantomData; -use sea_query::{IntoIden, SimpleExpr, UpdateStatement}; +use sea_query::{Alias, Expr, Func, IntoIden, SimpleExpr, UpdateStatement}; #[derive(Clone, Debug)] pub struct Update; @@ -104,9 +104,17 @@ where if ::PrimaryKey::from_column(col).is_some() { continue; } + let col_def = col.def(); + let enum_name = col_def.get_column_type().get_enum_name(); let av = self.model.get(col); if av.is_set() { - self.query.value(col, av.unwrap()); + let val = av.into_value().unwrap(); + let expr = if let Some(enum_name) = enum_name { + Func::cast_as(val, Alias::new(&enum_name)) + } else { + Expr::val(val).into() + }; + self.query.value_expr(col, expr); } } self diff --git a/tests/active_enum_tests.rs b/tests/active_enum_tests.rs index b0c72a822..ca9bf7d99 100644 --- a/tests/active_enum_tests.rs +++ b/tests/active_enum_tests.rs @@ -40,7 +40,7 @@ pub async fn insert_active_enum(db: &DatabaseConnection) -> Result<(), DbErr> { } ); - ActiveModel { + let am = ActiveModel { category: Set(Some(Category::Big)), color: Set(Some(Color::Black)), tea: Set(Some(Tea::EverydayTea)), @@ -59,5 +59,10 @@ pub async fn insert_active_enum(db: &DatabaseConnection) -> Result<(), DbErr> { } ); + let res = am.delete(db).await?; + + assert_eq!(res.rows_affected, 1); + assert_eq!(Entity::find().one(db).await?, None); + Ok(()) } diff --git a/tests/common/features/schema.rs b/tests/common/features/schema.rs index 823ccdfd9..ffb47f269 100644 --- a/tests/common/features/schema.rs +++ b/tests/common/features/schema.rs @@ -3,7 +3,7 @@ pub use super::super::bakery_chain::*; use super::*; use crate::common::setup::create_table; use sea_orm::{error::*, sea_query, DatabaseConnection, DbConn, ExecResult}; -use sea_query::{ColumnDef, ForeignKeyCreateStatement}; +use sea_query::{Alias, ColumnDef, ForeignKeyCreateStatement}; pub async fn create_tables(db: &DatabaseConnection) -> Result<(), DbErr> { create_log_table(db).await?; @@ -103,6 +103,8 @@ pub async fn create_self_join_table(db: &DbConn) -> Result { } pub async fn create_active_enum_table(db: &DbConn) -> Result { + let tea_enum = Alias::new("tea"); + let stmt = sea_query::Table::create() .table(active_enum::Entity) .col( @@ -114,8 +116,45 @@ pub async fn create_active_enum_table(db: &DbConn) -> Result ) .col(ColumnDef::new(active_enum::Column::Category).string_len(1)) .col(ColumnDef::new(active_enum::Column::Color).integer()) - // .col(ColumnDef::new(active_enum::Column::Tea).custom(Alias::new("tea"))) + .col(ColumnDef::new(active_enum::Column::Tea).custom(tea_enum.clone())) .to_owned(); + match db { + #[cfg(feature = "sqlx-postgres")] + DatabaseConnection::SqlxPostgresPoolConnection(_) => { + use sea_orm::{ConnectionTrait, Statement}; + use sea_query::{extension::postgres::Type, PostgresQueryBuilder}; + + let drop_type_stmt = Type::drop() + .name(tea_enum.clone()) + .cascade() + .if_exists() + .to_owned(); + let (sql, values) = drop_type_stmt.build(PostgresQueryBuilder); + let stmt = Statement::from_sql_and_values(db.get_database_backend(), &sql, values); + db.execute(stmt).await?; + + let create_type_stmt = Type::create() + .as_enum(tea_enum) + .values(vec![Alias::new("EverydayTea"), Alias::new("BreakfastTea")]) + .to_owned(); + + // FIXME: This is not working + { + let (sql, values) = create_type_stmt.build(PostgresQueryBuilder); + let _stmt = Statement::from_sql_and_values(db.get_database_backend(), &sql, values); + } + + // But this is working... + let stmt = Statement::from_string( + db.get_database_backend(), + create_type_stmt.to_string(PostgresQueryBuilder), + ); + + db.execute(stmt).await?; + } + _ => {} + } + create_table(db, &stmt, ActiveEnum).await } From 20c66b2f059824a2148c2b9fd064cf9dc5c956af Mon Sep 17 00:00:00 2001 From: Billy Chan Date: Thu, 21 Oct 2021 15:50:18 +0800 Subject: [PATCH 03/16] Refactoring --- src/query/insert.rs | 2 +- src/query/select.rs | 5 ++--- src/query/update.rs | 2 +- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/query/insert.rs b/src/query/insert.rs index f7f7c7d38..3a7c91d67 100644 --- a/src/query/insert.rs +++ b/src/query/insert.rs @@ -136,7 +136,7 @@ where columns.push(col); let val = av.into_value().unwrap(); let expr = if let Some(enum_name) = enum_name { - Func::cast_as(val, Alias::new(&enum_name)) + Func::cast_as(val, Alias::new(enum_name)) } else { Expr::val(val).into() }; diff --git a/src/query/select.rs b/src/query/select.rs index 5a2e0cdc2..0f2db65bf 100644 --- a/src/query/select.rs +++ b/src/query/select.rs @@ -120,12 +120,11 @@ where let col_def = col.def(); let enum_name = col_def.get_column_type().get_enum_name(); let col_expr = Expr::tbl(table.clone(), col); - let col_expr = if let Some(_) = enum_name { + if enum_name.is_some() { Func::cast_expr_as(col_expr, Alias::new("text")) } else { col_expr.into() - }; - col_expr + } }) .collect() } diff --git a/src/query/update.rs b/src/query/update.rs index 2f9bdb3bc..a881ad115 100644 --- a/src/query/update.rs +++ b/src/query/update.rs @@ -110,7 +110,7 @@ where if av.is_set() { let val = av.into_value().unwrap(); let expr = if let Some(enum_name) = enum_name { - Func::cast_as(val, Alias::new(&enum_name)) + Func::cast_as(val, Alias::new(enum_name)) } else { Expr::val(val).into() }; From 1ee2dab3b7df2f57b684a16ebc9d39d5b4344b33 Mon Sep 17 00:00:00 2001 From: Billy Chan Date: Thu, 21 Oct 2021 15:53:46 +0800 Subject: [PATCH 04/16] Fixup --- src/entity/active_enum.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/entity/active_enum.rs b/src/entity/active_enum.rs index b42b31f7b..230ee8f2a 100644 --- a/src/entity/active_enum.rs +++ b/src/entity/active_enum.rs @@ -80,7 +80,7 @@ use sea_query::{Nullable, Value, ValueType}; /// Small, /// } /// -/// #[derive(Clone, Debug, PartialEq, EnumIter, DeriveEntityModel)] +/// #[derive(Clone, Debug, PartialEq, DeriveEntityModel)] /// #[sea_orm(table_name = "active_enum")] /// pub struct Model { /// #[sea_orm(primary_key)] From 8858d64dd0641cc0ef28a3969711ceae79e9f138 Mon Sep 17 00:00:00 2001 From: Billy Chan Date: Mon, 25 Oct 2021 15:48:01 +0800 Subject: [PATCH 05/16] create_table_from_entity with DB backend --- src/schema/entity.rs | 17 ++++++++++------- tests/common/setup/mod.rs | 5 ++++- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/src/schema/entity.rs b/src/schema/entity.rs index a95b70478..2d0aa13a1 100644 --- a/src/schema/entity.rs +++ b/src/schema/entity.rs @@ -1,19 +1,19 @@ use crate::{ - unpack_table_ref, ColumnTrait, EntityTrait, Identity, Iterable, PrimaryKeyToColumn, + unpack_table_ref, ColumnTrait, DbBackend, EntityTrait, Identity, Iterable, PrimaryKeyToColumn, PrimaryKeyTrait, RelationTrait, Schema, }; use sea_query::{ColumnDef, ForeignKeyCreateStatement, Iden, Index, TableCreateStatement}; impl Schema { - pub fn create_table_from_entity(entity: E) -> TableCreateStatement + pub fn create_table_from_entity(entity: E, db_backend: DbBackend) -> TableCreateStatement where E: EntityTrait, { - create_table_from_entity(entity) + create_table_from_entity(entity, db_backend) } } -pub(crate) fn create_table_from_entity(entity: E) -> TableCreateStatement +pub(crate) fn create_table_from_entity(entity: E, db_backend: DbBackend) -> TableCreateStatement where E: EntityTrait, { @@ -21,7 +21,9 @@ where for column in E::Column::iter() { let orm_column_def = column.def(); - let types = orm_column_def.col_type.into(); + let types = match db_backend { + _ => 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(); @@ -121,13 +123,14 @@ where #[cfg(test)] mod tests { - use crate::{sea_query::*, tests_cfg::*, Schema}; + use crate::{sea_query::*, tests_cfg::*, DbBackend, Schema}; use pretty_assertions::assert_eq; #[test] fn test_create_table_from_entity() { assert_eq!( - Schema::create_table_from_entity(CakeFillingPrice).to_string(MysqlQueryBuilder), + Schema::create_table_from_entity(CakeFillingPrice, DbBackend::MySql) + .to_string(MysqlQueryBuilder), Table::create() .table(CakeFillingPrice) .col( diff --git a/tests/common/setup/mod.rs b/tests/common/setup/mod.rs index 615de234c..7266d1759 100644 --- a/tests/common/setup/mod.rs +++ b/tests/common/setup/mod.rs @@ -95,7 +95,10 @@ where let stmt = builder.build(create); assert_eq!( - builder.build(&Schema::create_table_from_entity(entity)), + builder.build(&Schema::create_table_from_entity( + entity, + db.get_database_backend() + )), stmt ); db.execute(stmt).await From f20c64988df6c4657cd625bfd33c69daee92cc7d Mon Sep 17 00:00:00 2001 From: Billy Chan Date: Mon, 25 Oct 2021 16:52:02 +0800 Subject: [PATCH 06/16] Tests all DB --- src/entity/column.rs | 7 ++++--- src/schema/entity.rs | 14 ++++++++++--- tests/common/features/active_enum.rs | 5 ++++- tests/common/features/schema.rs | 30 +++++++++++++++++----------- 4 files changed, 37 insertions(+), 19 deletions(-) diff --git a/src/entity/column.rs b/src/entity/column.rs index 32fe030c6..4b9af89fe 100644 --- a/src/entity/column.rs +++ b/src/entity/column.rs @@ -34,7 +34,7 @@ pub enum ColumnType { JsonBinary, Custom(String), Uuid, - Enum(String), + Enum(String, Vec), } macro_rules! bind_oper { @@ -245,7 +245,8 @@ impl ColumnType { pub(crate) fn get_enum_name(&self) -> Option<&String> { match self { - ColumnType::Enum(s) => Some(s), + // FIXME: How to get rid of this feature gate? + ColumnType::Enum(s, _) if cfg!(feature = "sqlx-postgres") => Some(s), _ => None, } } @@ -303,7 +304,7 @@ impl From for sea_query::ColumnType { sea_query::ColumnType::Custom(sea_query::SeaRc::new(sea_query::Alias::new(&s))) } ColumnType::Uuid => sea_query::ColumnType::Uuid, - ColumnType::Enum(s) => { + ColumnType::Enum(s, _) => { sea_query::ColumnType::Custom(sea_query::SeaRc::new(sea_query::Alias::new(&s))) } } diff --git a/src/schema/entity.rs b/src/schema/entity.rs index 2d0aa13a1..3f99f8928 100644 --- a/src/schema/entity.rs +++ b/src/schema/entity.rs @@ -1,6 +1,6 @@ use crate::{ - unpack_table_ref, ColumnTrait, DbBackend, EntityTrait, Identity, Iterable, PrimaryKeyToColumn, - PrimaryKeyTrait, RelationTrait, Schema, + unpack_table_ref, ColumnTrait, ColumnType, DbBackend, EntityTrait, Identity, Iterable, + PrimaryKeyToColumn, PrimaryKeyTrait, RelationTrait, Schema, }; use sea_query::{ColumnDef, ForeignKeyCreateStatement, Iden, Index, TableCreateStatement}; @@ -21,7 +21,15 @@ where for column in E::Column::iter() { let orm_column_def = column.def(); - let types = match db_backend { + let types = match orm_column_def.col_type { + ColumnType::Enum(s, variants) => match db_backend { + DbBackend::MySql => { + ColumnType::Custom(format!("ENUM('{}')", variants.join("', '"))) + } + DbBackend::Postgres => ColumnType::Custom(s), + DbBackend::Sqlite => ColumnType::Text, + } + .into(), _ => orm_column_def.col_type.into(), }; let mut column_def = ColumnDef::new_with_type(column, types); diff --git a/tests/common/features/active_enum.rs b/tests/common/features/active_enum.rs index 229d44466..78f217900 100644 --- a/tests/common/features/active_enum.rs +++ b/tests/common/features/active_enum.rs @@ -34,7 +34,10 @@ pub enum Color { } #[derive(Debug, Clone, PartialEq, EnumIter, DeriveActiveEnum)] -#[sea_orm(rs_type = "String", db_type = r#"Enum("tea".to_owned())"#)] +#[sea_orm( + rs_type = "String", + db_type = r#"Enum("tea".to_owned(), vec!["EverydayTea".to_owned(), "BreakfastTea".to_owned()])"# +)] pub enum Tea { #[sea_orm(string_value = "EverydayTea")] EverydayTea, diff --git a/tests/common/features/schema.rs b/tests/common/features/schema.rs index ffb47f269..0eeb5c045 100644 --- a/tests/common/features/schema.rs +++ b/tests/common/features/schema.rs @@ -2,8 +2,13 @@ pub use super::super::bakery_chain::*; use super::*; use crate::common::setup::create_table; -use sea_orm::{error::*, sea_query, DatabaseConnection, DbConn, ExecResult}; -use sea_query::{Alias, ColumnDef, ForeignKeyCreateStatement}; +use sea_orm::{ + error::*, sea_query, ConnectionTrait, DatabaseConnection, DbBackend, DbConn, ExecResult, + Statement, +}; +use sea_query::{ + extension::postgres::Type, Alias, ColumnDef, ForeignKeyCreateStatement, PostgresQueryBuilder, +}; pub async fn create_tables(db: &DatabaseConnection) -> Result<(), DbErr> { create_log_table(db).await?; @@ -103,8 +108,16 @@ pub async fn create_self_join_table(db: &DbConn) -> Result { } pub async fn create_active_enum_table(db: &DbConn) -> Result { + let db_backend = db.get_database_backend(); let tea_enum = Alias::new("tea"); + let mut tea_col = ColumnDef::new(active_enum::Column::Tea); + match db_backend { + DbBackend::MySql => tea_col.custom(Alias::new("ENUM('EverydayTea', 'BreakfastTea')")), + DbBackend::Postgres => tea_col.custom(tea_enum.clone()), + DbBackend::Sqlite => tea_col.text(), + }; + let stmt = sea_query::Table::create() .table(active_enum::Entity) .col( @@ -116,15 +129,11 @@ pub async fn create_active_enum_table(db: &DbConn) -> Result ) .col(ColumnDef::new(active_enum::Column::Category).string_len(1)) .col(ColumnDef::new(active_enum::Column::Color).integer()) - .col(ColumnDef::new(active_enum::Column::Tea).custom(tea_enum.clone())) + .col(&mut tea_col) .to_owned(); - match db { - #[cfg(feature = "sqlx-postgres")] - DatabaseConnection::SqlxPostgresPoolConnection(_) => { - use sea_orm::{ConnectionTrait, Statement}; - use sea_query::{extension::postgres::Type, PostgresQueryBuilder}; - + match db_backend { + DbBackend::Postgres => { let drop_type_stmt = Type::drop() .name(tea_enum.clone()) .cascade() @@ -138,19 +147,16 @@ pub async fn create_active_enum_table(db: &DbConn) -> Result .as_enum(tea_enum) .values(vec![Alias::new("EverydayTea"), Alias::new("BreakfastTea")]) .to_owned(); - // FIXME: This is not working { let (sql, values) = create_type_stmt.build(PostgresQueryBuilder); let _stmt = Statement::from_sql_and_values(db.get_database_backend(), &sql, values); } - // But this is working... let stmt = Statement::from_string( db.get_database_backend(), create_type_stmt.to_string(PostgresQueryBuilder), ); - db.execute(stmt).await?; } _ => {} From 6059cdd9adf7af4768b1dd727b6730dfcc930c46 Mon Sep 17 00:00:00 2001 From: Billy Chan Date: Mon, 25 Oct 2021 16:56:36 +0800 Subject: [PATCH 07/16] Remove unused EnumIter --- src/entity/active_enum.rs | 18 +++++++++--------- tests/common/features/active_enum.rs | 6 +++--- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/entity/active_enum.rs b/src/entity/active_enum.rs index 230ee8f2a..5eb77b9f2 100644 --- a/src/entity/active_enum.rs +++ b/src/entity/active_enum.rs @@ -1,4 +1,4 @@ -use crate::{ColumnDef, DbErr, Iterable, TryGetable}; +use crate::{ColumnDef, DbErr, TryGetable}; use sea_query::{Nullable, Value, ValueType}; /// A Rust representation of enum defined in database. @@ -17,7 +17,7 @@ use sea_query::{Nullable, Value, ValueType}; /// use sea_orm::entity::prelude::*; /// /// // Using the derive macro -/// #[derive(Debug, PartialEq, EnumIter, DeriveActiveEnum)] +/// #[derive(Debug, PartialEq, DeriveActiveEnum)] /// #[sea_orm(rs_type = "String", db_type = "String(Some(1))")] /// pub enum DeriveCategory { /// #[sea_orm(string_value = "B")] @@ -27,7 +27,7 @@ use sea_query::{Nullable, Value, ValueType}; /// } /// /// // Implementing it manually -/// #[derive(Debug, PartialEq, EnumIter)] +/// #[derive(Debug, PartialEq)] /// pub enum Category { /// Big, /// Small, @@ -71,7 +71,7 @@ use sea_query::{Nullable, Value, ValueType}; /// use sea_orm::entity::prelude::*; /// /// // Define the `Category` active enum -/// #[derive(Debug, Clone, PartialEq, EnumIter, DeriveActiveEnum)] +/// #[derive(Debug, Clone, PartialEq, DeriveActiveEnum)] /// #[sea_orm(rs_type = "String", db_type = "String(Some(1))")] /// pub enum Category { /// #[sea_orm(string_value = "B")] @@ -95,7 +95,7 @@ use sea_query::{Nullable, Value, ValueType}; /// /// impl ActiveModelBehavior for ActiveModel {} /// ``` -pub trait ActiveEnum: Sized + Iterable { +pub trait ActiveEnum: Sized { /// Define the Rust type that each enum variant represents. type Value: Into + ValueType + Nullable + TryGetable; @@ -117,7 +117,7 @@ mod tests { #[test] fn active_enum_string() { - #[derive(Debug, PartialEq, EnumIter)] + #[derive(Debug, PartialEq)] pub enum Category { Big, Small, @@ -150,7 +150,7 @@ mod tests { } } - #[derive(Debug, PartialEq, EnumIter, DeriveActiveEnum)] + #[derive(Debug, PartialEq, DeriveActiveEnum)] #[sea_orm(rs_type = "String", db_type = "String(Some(1))")] pub enum DeriveCategory { #[sea_orm(string_value = "B")] @@ -201,7 +201,7 @@ mod tests { fn active_enum_derive_signed_integers() { macro_rules! test_int { ($ident: ident, $rs_type: expr, $db_type: expr, $col_def: ident) => { - #[derive(Debug, PartialEq, EnumIter, DeriveActiveEnum)] + #[derive(Debug, PartialEq, DeriveActiveEnum)] #[sea_orm(rs_type = $rs_type, db_type = $db_type)] pub enum $ident { #[sea_orm(num_value = 1)] @@ -241,7 +241,7 @@ mod tests { fn active_enum_derive_unsigned_integers() { macro_rules! test_uint { ($ident: ident, $rs_type: expr, $db_type: expr, $col_def: ident) => { - #[derive(Debug, PartialEq, EnumIter, DeriveActiveEnum)] + #[derive(Debug, PartialEq, DeriveActiveEnum)] #[sea_orm(rs_type = $rs_type, db_type = $db_type)] pub enum $ident { #[sea_orm(num_value = 1)] diff --git a/tests/common/features/active_enum.rs b/tests/common/features/active_enum.rs index 78f217900..f152f37c3 100644 --- a/tests/common/features/active_enum.rs +++ b/tests/common/features/active_enum.rs @@ -15,7 +15,7 @@ pub enum Relation {} impl ActiveModelBehavior for ActiveModel {} -#[derive(Debug, Clone, PartialEq, EnumIter, DeriveActiveEnum)] +#[derive(Debug, Clone, PartialEq, DeriveActiveEnum)] #[sea_orm(rs_type = "String", db_type = "String(Some(1))")] pub enum Category { #[sea_orm(string_value = "B")] @@ -24,7 +24,7 @@ pub enum Category { Small, } -#[derive(Debug, Clone, PartialEq, EnumIter, DeriveActiveEnum)] +#[derive(Debug, Clone, PartialEq, DeriveActiveEnum)] #[sea_orm(rs_type = "i32", db_type = r#"Integer"#)] pub enum Color { #[sea_orm(num_value = 0)] @@ -33,7 +33,7 @@ pub enum Color { White, } -#[derive(Debug, Clone, PartialEq, EnumIter, DeriveActiveEnum)] +#[derive(Debug, Clone, PartialEq, DeriveActiveEnum)] #[sea_orm( rs_type = "String", db_type = r#"Enum("tea".to_owned(), vec!["EverydayTea".to_owned(), "BreakfastTea".to_owned()])"# From 4b1cac7f354242dac9af2b9fa6698e9e30ef3f07 Mon Sep 17 00:00:00 2001 From: Billy Chan Date: Mon, 25 Oct 2021 17:05:27 +0800 Subject: [PATCH 08/16] Refactoring --- tests/common/features/schema.rs | 51 ++++++++++++++++----------------- 1 file changed, 24 insertions(+), 27 deletions(-) diff --git a/tests/common/features/schema.rs b/tests/common/features/schema.rs index 0eeb5c045..f630ccb79 100644 --- a/tests/common/features/schema.rs +++ b/tests/common/features/schema.rs @@ -132,34 +132,31 @@ pub async fn create_active_enum_table(db: &DbConn) -> Result .col(&mut tea_col) .to_owned(); - match db_backend { - DbBackend::Postgres => { - let drop_type_stmt = Type::drop() - .name(tea_enum.clone()) - .cascade() - .if_exists() - .to_owned(); - let (sql, values) = drop_type_stmt.build(PostgresQueryBuilder); - let stmt = Statement::from_sql_and_values(db.get_database_backend(), &sql, values); - db.execute(stmt).await?; - - let create_type_stmt = Type::create() - .as_enum(tea_enum) - .values(vec![Alias::new("EverydayTea"), Alias::new("BreakfastTea")]) - .to_owned(); - // FIXME: This is not working - { - let (sql, values) = create_type_stmt.build(PostgresQueryBuilder); - let _stmt = Statement::from_sql_and_values(db.get_database_backend(), &sql, values); - } - // But this is working... - let stmt = Statement::from_string( - db.get_database_backend(), - create_type_stmt.to_string(PostgresQueryBuilder), - ); - db.execute(stmt).await?; + if db_backend = DbBackend::Postgres { + let drop_type_stmt = Type::drop() + .name(tea_enum.clone()) + .cascade() + .if_exists() + .to_owned(); + let (sql, values) = drop_type_stmt.build(PostgresQueryBuilder); + let stmt = Statement::from_sql_and_values(db.get_database_backend(), &sql, values); + db.execute(stmt).await?; + + let create_type_stmt = Type::create() + .as_enum(tea_enum) + .values(vec![Alias::new("EverydayTea"), Alias::new("BreakfastTea")]) + .to_owned(); + // FIXME: This is not working + { + let (sql, values) = create_type_stmt.build(PostgresQueryBuilder); + let _stmt = Statement::from_sql_and_values(db.get_database_backend(), &sql, values); } - _ => {} + // But this is working... + let stmt = Statement::from_string( + db.get_database_backend(), + create_type_stmt.to_string(PostgresQueryBuilder), + ); + db.execute(stmt).await?; } create_table(db, &stmt, ActiveEnum).await From cf52839c3ac4564a10790b060d0e97b37f3daf1f Mon Sep 17 00:00:00 2001 From: Billy Chan Date: Mon, 25 Oct 2021 17:12:17 +0800 Subject: [PATCH 09/16] Typo --- tests/common/features/schema.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/common/features/schema.rs b/tests/common/features/schema.rs index f630ccb79..bbc9faf8b 100644 --- a/tests/common/features/schema.rs +++ b/tests/common/features/schema.rs @@ -132,7 +132,7 @@ pub async fn create_active_enum_table(db: &DbConn) -> Result .col(&mut tea_col) .to_owned(); - if db_backend = DbBackend::Postgres { + if db_backend == DbBackend::Postgres { let drop_type_stmt = Type::drop() .name(tea_enum.clone()) .cascade() From 2ee376ddd117b2e14f16dd6e3952645c5fc7d007 Mon Sep 17 00:00:00 2001 From: Billy Chan Date: Tue, 26 Oct 2021 16:22:24 +0800 Subject: [PATCH 10/16] Try `EnumValue` --- src/entity/column.rs | 3 +-- src/query/insert.rs | 4 ++-- src/query/select.rs | 4 ++-- src/query/update.rs | 4 ++-- 4 files changed, 7 insertions(+), 8 deletions(-) diff --git a/src/entity/column.rs b/src/entity/column.rs index 4b9af89fe..8cd65d9cb 100644 --- a/src/entity/column.rs +++ b/src/entity/column.rs @@ -245,8 +245,7 @@ impl ColumnType { pub(crate) fn get_enum_name(&self) -> Option<&String> { match self { - // FIXME: How to get rid of this feature gate? - ColumnType::Enum(s, _) if cfg!(feature = "sqlx-postgres") => Some(s), + ColumnType::Enum(s, _) => Some(s), _ => None, } } diff --git a/src/query/insert.rs b/src/query/insert.rs index 3a7c91d67..a1b34f501 100644 --- a/src/query/insert.rs +++ b/src/query/insert.rs @@ -3,7 +3,7 @@ use crate::{ PrimaryKeyTrait, QueryTrait, }; use core::marker::PhantomData; -use sea_query::{Alias, Expr, Func, InsertStatement, ValueTuple}; +use sea_query::{Expr, InsertStatement, SimpleExpr, ValueTuple}; #[derive(Debug)] pub struct Insert @@ -136,7 +136,7 @@ where columns.push(col); let val = av.into_value().unwrap(); let expr = if let Some(enum_name) = enum_name { - Func::cast_as(val, Alias::new(enum_name)) + SimpleExpr::EnumValue(enum_name.to_owned(), Box::new(Expr::val(val).into())) } else { Expr::val(val).into() }; diff --git a/src/query/select.rs b/src/query/select.rs index 0f2db65bf..1bdfce1a0 100644 --- a/src/query/select.rs +++ b/src/query/select.rs @@ -2,7 +2,7 @@ use crate::{ColumnTrait, EntityTrait, Iterable, QueryFilter, QueryOrder, QuerySe use core::fmt::Debug; use core::marker::PhantomData; pub use sea_query::JoinType; -use sea_query::{Alias, DynIden, Expr, Func, IntoColumnRef, SeaRc, SelectStatement, SimpleExpr}; +use sea_query::{DynIden, Expr, IntoColumnRef, SeaRc, SelectStatement, SimpleExpr}; #[derive(Clone, Debug)] pub struct Select @@ -121,7 +121,7 @@ where let enum_name = col_def.get_column_type().get_enum_name(); let col_expr = Expr::tbl(table.clone(), col); if enum_name.is_some() { - Func::cast_expr_as(col_expr, Alias::new("text")) + SimpleExpr::EnumValue("text".to_owned(), Box::new(col_expr.into())) } else { col_expr.into() } diff --git a/src/query/update.rs b/src/query/update.rs index a881ad115..a81b5607e 100644 --- a/src/query/update.rs +++ b/src/query/update.rs @@ -3,7 +3,7 @@ use crate::{ QueryTrait, }; use core::marker::PhantomData; -use sea_query::{Alias, Expr, Func, IntoIden, SimpleExpr, UpdateStatement}; +use sea_query::{Expr, IntoIden, SimpleExpr, UpdateStatement}; #[derive(Clone, Debug)] pub struct Update; @@ -110,7 +110,7 @@ where if av.is_set() { let val = av.into_value().unwrap(); let expr = if let Some(enum_name) = enum_name { - Func::cast_as(val, Alias::new(enum_name)) + SimpleExpr::EnumValue(enum_name.to_owned(), Box::new(Expr::val(val).into())) } else { Expr::val(val).into() }; From db22e70c6339f65e0cb40d9b2a0aa034c6183d60 Mon Sep 17 00:00:00 2001 From: Billy Chan Date: Tue, 26 Oct 2021 17:51:36 +0800 Subject: [PATCH 11/16] Refactoring --- src/query/insert.rs | 8 ++++---- src/query/select.rs | 2 +- src/query/update.rs | 6 +++--- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/query/insert.rs b/src/query/insert.rs index a1b34f501..63f22c35b 100644 --- a/src/query/insert.rs +++ b/src/query/insert.rs @@ -3,7 +3,7 @@ use crate::{ PrimaryKeyTrait, QueryTrait, }; use core::marker::PhantomData; -use sea_query::{Expr, InsertStatement, SimpleExpr, ValueTuple}; +use sea_query::{Expr, InsertStatement, ValueTuple}; #[derive(Debug)] pub struct Insert @@ -134,11 +134,11 @@ where } if av_has_val { columns.push(col); - let val = av.into_value().unwrap(); + let val = Expr::val(av.into_value().unwrap()); let expr = if let Some(enum_name) = enum_name { - SimpleExpr::EnumValue(enum_name.to_owned(), Box::new(Expr::val(val).into())) + Expr::enum_value(enum_name, val) } else { - Expr::val(val).into() + val.into() }; values.push(expr); } diff --git a/src/query/select.rs b/src/query/select.rs index 1bdfce1a0..5a270f6d8 100644 --- a/src/query/select.rs +++ b/src/query/select.rs @@ -121,7 +121,7 @@ where let enum_name = col_def.get_column_type().get_enum_name(); let col_expr = Expr::tbl(table.clone(), col); if enum_name.is_some() { - SimpleExpr::EnumValue("text".to_owned(), Box::new(col_expr.into())) + Expr::enum_value("text", col_expr) } else { col_expr.into() } diff --git a/src/query/update.rs b/src/query/update.rs index a81b5607e..18e4594b9 100644 --- a/src/query/update.rs +++ b/src/query/update.rs @@ -108,11 +108,11 @@ where let enum_name = col_def.get_column_type().get_enum_name(); let av = self.model.get(col); if av.is_set() { - let val = av.into_value().unwrap(); + let val = Expr::val(av.into_value().unwrap()); let expr = if let Some(enum_name) = enum_name { - SimpleExpr::EnumValue(enum_name.to_owned(), Box::new(Expr::val(val).into())) + Expr::enum_value(enum_name, val) } else { - Expr::val(val).into() + val.into() }; self.query.value_expr(col, expr); } From ded28be2c01a744a80bcb92cae4e3922b29f7baa Mon Sep 17 00:00:00 2001 From: Billy Chan Date: Tue, 26 Oct 2021 18:58:06 +0800 Subject: [PATCH 12/16] Refactoring --- src/entity/column.rs | 5 +---- src/query/insert.rs | 4 ++-- src/query/select.rs | 5 +++-- src/query/update.rs | 4 ++-- 4 files changed, 8 insertions(+), 10 deletions(-) diff --git a/src/entity/column.rs b/src/entity/column.rs index 8cd65d9cb..9a1f5c9de 100644 --- a/src/entity/column.rs +++ b/src/entity/column.rs @@ -299,13 +299,10 @@ impl From for sea_query::ColumnType { ColumnType::Money(s) => sea_query::ColumnType::Money(s), ColumnType::Json => sea_query::ColumnType::Json, ColumnType::JsonBinary => sea_query::ColumnType::JsonBinary, - ColumnType::Custom(s) => { + ColumnType::Custom(s) | ColumnType::Enum(s, _) => { sea_query::ColumnType::Custom(sea_query::SeaRc::new(sea_query::Alias::new(&s))) } ColumnType::Uuid => sea_query::ColumnType::Uuid, - ColumnType::Enum(s, _) => { - sea_query::ColumnType::Custom(sea_query::SeaRc::new(sea_query::Alias::new(&s))) - } } } } diff --git a/src/query/insert.rs b/src/query/insert.rs index 63f22c35b..8719e0118 100644 --- a/src/query/insert.rs +++ b/src/query/insert.rs @@ -3,7 +3,7 @@ use crate::{ PrimaryKeyTrait, QueryTrait, }; use core::marker::PhantomData; -use sea_query::{Expr, InsertStatement, ValueTuple}; +use sea_query::{Alias, Expr, InsertStatement, ValueTuple}; #[derive(Debug)] pub struct Insert @@ -136,7 +136,7 @@ where columns.push(col); let val = Expr::val(av.into_value().unwrap()); let expr = if let Some(enum_name) = enum_name { - Expr::enum_value(enum_name, val) + val.as_enum(Alias::new(enum_name)) } else { val.into() }; diff --git a/src/query/select.rs b/src/query/select.rs index 5a270f6d8..5c6d1f3b2 100644 --- a/src/query/select.rs +++ b/src/query/select.rs @@ -2,7 +2,7 @@ use crate::{ColumnTrait, EntityTrait, Iterable, QueryFilter, QueryOrder, QuerySe use core::fmt::Debug; use core::marker::PhantomData; pub use sea_query::JoinType; -use sea_query::{DynIden, Expr, IntoColumnRef, SeaRc, SelectStatement, SimpleExpr}; +use sea_query::{Alias, DynIden, Expr, IntoColumnRef, SeaRc, SelectStatement, SimpleExpr}; #[derive(Clone, Debug)] pub struct Select @@ -115,13 +115,14 @@ where fn column_list(&self) -> Vec { let table = SeaRc::new(E::default()) as DynIden; + let text_type = SeaRc::new(Alias::new("text")) as DynIden; E::Column::iter() .map(|col| { let col_def = col.def(); let enum_name = col_def.get_column_type().get_enum_name(); let col_expr = Expr::tbl(table.clone(), col); if enum_name.is_some() { - Expr::enum_value("text", col_expr) + col_expr.as_enum(text_type.clone()) } else { col_expr.into() } diff --git a/src/query/update.rs b/src/query/update.rs index 18e4594b9..6b6f48f83 100644 --- a/src/query/update.rs +++ b/src/query/update.rs @@ -3,7 +3,7 @@ use crate::{ QueryTrait, }; use core::marker::PhantomData; -use sea_query::{Expr, IntoIden, SimpleExpr, UpdateStatement}; +use sea_query::{Alias, Expr, IntoIden, SimpleExpr, UpdateStatement}; #[derive(Clone, Debug)] pub struct Update; @@ -110,7 +110,7 @@ where if av.is_set() { let val = Expr::val(av.into_value().unwrap()); let expr = if let Some(enum_name) = enum_name { - Expr::enum_value(enum_name, val) + val.as_enum(Alias::new(enum_name)) } else { val.into() }; From fac528a3699b0b6c9e0ced88553ad5720acc4db4 Mon Sep 17 00:00:00 2001 From: Billy Chan Date: Wed, 27 Oct 2021 10:58:38 +0800 Subject: [PATCH 13/16] Refactoring --- src/query/insert.rs | 8 +++----- src/query/select.rs | 10 ++++------ src/query/update.rs | 8 +++----- 3 files changed, 10 insertions(+), 16 deletions(-) diff --git a/src/query/insert.rs b/src/query/insert.rs index 8719e0118..f8b151d7b 100644 --- a/src/query/insert.rs +++ b/src/query/insert.rs @@ -125,7 +125,6 @@ where let av = am.take(col); let av_has_val = av.is_set() || av.is_unchanged(); let col_def = col.def(); - let enum_name = col_def.get_column_type().get_enum_name(); if columns_empty { self.columns.push(av_has_val); @@ -135,10 +134,9 @@ where if av_has_val { columns.push(col); let val = Expr::val(av.into_value().unwrap()); - let expr = if let Some(enum_name) = enum_name { - val.as_enum(Alias::new(enum_name)) - } else { - val.into() + let expr = match col_def.get_column_type().get_enum_name() { + Some(enum_name) => val.as_enum(Alias::new(enum_name)), + None => val.into(), }; values.push(expr); } diff --git a/src/query/select.rs b/src/query/select.rs index 5c6d1f3b2..037527c76 100644 --- a/src/query/select.rs +++ b/src/query/select.rs @@ -119,12 +119,10 @@ where E::Column::iter() .map(|col| { let col_def = col.def(); - let enum_name = col_def.get_column_type().get_enum_name(); - let col_expr = Expr::tbl(table.clone(), col); - if enum_name.is_some() { - col_expr.as_enum(text_type.clone()) - } else { - col_expr.into() + let expr = Expr::tbl(table.clone(), col); + match col_def.get_column_type().get_enum_name() { + Some(_) => expr.as_enum(text_type.clone()), + None => expr.into(), } }) .collect() diff --git a/src/query/update.rs b/src/query/update.rs index 6b6f48f83..7c2e9c2e6 100644 --- a/src/query/update.rs +++ b/src/query/update.rs @@ -105,14 +105,12 @@ where continue; } let col_def = col.def(); - let enum_name = col_def.get_column_type().get_enum_name(); let av = self.model.get(col); if av.is_set() { let val = Expr::val(av.into_value().unwrap()); - let expr = if let Some(enum_name) = enum_name { - val.as_enum(Alias::new(enum_name)) - } else { - val.into() + let expr = match col_def.get_column_type().get_enum_name() { + Some(enum_name) => val.as_enum(Alias::new(enum_name)), + None => val.into(), }; self.query.value_expr(col, expr); } From e04495b94d2e1849132ce4bd679ce32f4d7a4756 Mon Sep 17 00:00:00 2001 From: Billy Chan Date: Wed, 27 Oct 2021 11:28:33 +0800 Subject: [PATCH 14/16] Refactoring --- src/query/insert.rs | 4 +--- src/query/select.rs | 3 +-- src/query/update.rs | 3 +-- 3 files changed, 3 insertions(+), 7 deletions(-) diff --git a/src/query/insert.rs b/src/query/insert.rs index f8b151d7b..f7f77d7d0 100644 --- a/src/query/insert.rs +++ b/src/query/insert.rs @@ -124,8 +124,6 @@ where for (idx, col) in ::Column::iter().enumerate() { let av = am.take(col); let av_has_val = av.is_set() || av.is_unchanged(); - let col_def = col.def(); - if columns_empty { self.columns.push(av_has_val); } else if self.columns[idx] != av_has_val { @@ -134,7 +132,7 @@ where if av_has_val { columns.push(col); let val = Expr::val(av.into_value().unwrap()); - let expr = match col_def.get_column_type().get_enum_name() { + let expr = match col.def().get_column_type().get_enum_name() { Some(enum_name) => val.as_enum(Alias::new(enum_name)), None => val.into(), }; diff --git a/src/query/select.rs b/src/query/select.rs index 037527c76..5e433e8a4 100644 --- a/src/query/select.rs +++ b/src/query/select.rs @@ -118,9 +118,8 @@ where let text_type = SeaRc::new(Alias::new("text")) as DynIden; E::Column::iter() .map(|col| { - let col_def = col.def(); let expr = Expr::tbl(table.clone(), col); - match col_def.get_column_type().get_enum_name() { + match col.def().get_column_type().get_enum_name() { Some(_) => expr.as_enum(text_type.clone()), None => expr.into(), } diff --git a/src/query/update.rs b/src/query/update.rs index 7c2e9c2e6..89348229a 100644 --- a/src/query/update.rs +++ b/src/query/update.rs @@ -104,11 +104,10 @@ where if ::PrimaryKey::from_column(col).is_some() { continue; } - let col_def = col.def(); let av = self.model.get(col); if av.is_set() { let val = Expr::val(av.into_value().unwrap()); - let expr = match col_def.get_column_type().get_enum_name() { + let expr = match col.def().get_column_type().get_enum_name() { Some(enum_name) => val.as_enum(Alias::new(enum_name)), None => val.into(), }; From 55de1968bbb049c506c60b2df9f84a252ce0e4ad Mon Sep 17 00:00:00 2001 From: Billy Chan Date: Wed, 27 Oct 2021 12:37:35 +0800 Subject: [PATCH 15/16] Add `create_enum_from_entity` --- src/database/statement.rs | 24 ++++++++++++++ src/schema/entity.rs | 39 ++++++++++++++++++++++- tests/common/features/schema.rs | 51 +++++++++--------------------- tests/common/setup/mod.rs | 55 +++++++++++++++++++++++++++++++-- 4 files changed, 129 insertions(+), 40 deletions(-) diff --git a/src/database/statement.rs b/src/database/statement.rs index 12b074873..86d7c630d 100644 --- a/src/database/statement.rs +++ b/src/database/statement.rs @@ -73,6 +73,15 @@ macro_rules! build_any_stmt { }; } +macro_rules! build_postgres_stmt { + ($stmt: expr, $db_backend: expr) => { + match $db_backend { + DbBackend::Postgres => $stmt.to_string(PostgresQueryBuilder), + DbBackend::MySql | DbBackend::Sqlite => unimplemented!(), + } + }; +} + macro_rules! build_query_stmt { ($stmt: ty) => { impl StatementBuilder for $stmt { @@ -105,3 +114,18 @@ build_schema_stmt!(sea_query::TableDropStatement); build_schema_stmt!(sea_query::TableAlterStatement); build_schema_stmt!(sea_query::TableRenameStatement); build_schema_stmt!(sea_query::TableTruncateStatement); + +macro_rules! build_type_stmt { + ($stmt: ty) => { + impl StatementBuilder for $stmt { + fn build(&self, db_backend: &DbBackend) -> Statement { + let stmt = build_postgres_stmt!(self, db_backend); + Statement::from_string(*db_backend, stmt) + } + } + }; +} + +build_type_stmt!(sea_query::extension::postgres::TypeAlterStatement); +build_type_stmt!(sea_query::extension::postgres::TypeCreateStatement); +build_type_stmt!(sea_query::extension::postgres::TypeDropStatement); diff --git a/src/schema/entity.rs b/src/schema/entity.rs index 3f99f8928..238bde36e 100644 --- a/src/schema/entity.rs +++ b/src/schema/entity.rs @@ -2,9 +2,19 @@ use crate::{ unpack_table_ref, ColumnTrait, ColumnType, DbBackend, EntityTrait, Identity, Iterable, PrimaryKeyToColumn, PrimaryKeyTrait, RelationTrait, Schema, }; -use sea_query::{ColumnDef, ForeignKeyCreateStatement, Iden, Index, TableCreateStatement}; +use sea_query::{ + extension::postgres::{Type, TypeCreateStatement}, + Alias, ColumnDef, ForeignKeyCreateStatement, Iden, Index, TableCreateStatement, +}; impl Schema { + pub fn create_enum_from_entity(entity: E, db_backend: DbBackend) -> Vec + where + E: EntityTrait, + { + create_enum_from_entity(entity, db_backend) + } + pub fn create_table_from_entity(entity: E, db_backend: DbBackend) -> TableCreateStatement where E: EntityTrait, @@ -13,6 +23,33 @@ impl Schema { } } +pub(crate) fn create_enum_from_entity(_: E, db_backend: DbBackend) -> Vec +where + E: EntityTrait, +{ + if matches!(db_backend, DbBackend::MySql | DbBackend::Sqlite) { + return Vec::new(); + } + let mut vec = Vec::new(); + for col in E::Column::iter() { + let col_def = col.def(); + let col_type = col_def.get_column_type(); + if !matches!(col_type, ColumnType::Enum(_, _)) { + continue; + } + let (name, values) = match col_type { + ColumnType::Enum(s, v) => (s.as_str(), v), + _ => unreachable!(), + }; + let stmt = Type::create() + .as_enum(Alias::new(name)) + .values(values.into_iter().map(|val| Alias::new(val.as_str()))) + .to_owned(); + vec.push(stmt); + } + vec +} + pub(crate) fn create_table_from_entity(entity: E, db_backend: DbBackend) -> TableCreateStatement where E: EntityTrait, diff --git a/tests/common/features/schema.rs b/tests/common/features/schema.rs index bbc9faf8b..7e9f3de7b 100644 --- a/tests/common/features/schema.rs +++ b/tests/common/features/schema.rs @@ -1,14 +1,11 @@ pub use super::super::bakery_chain::*; use super::*; -use crate::common::setup::create_table; +use crate::{common::setup::create_table, create_enum}; use sea_orm::{ error::*, sea_query, ConnectionTrait, DatabaseConnection, DbBackend, DbConn, ExecResult, - Statement, -}; -use sea_query::{ - extension::postgres::Type, Alias, ColumnDef, ForeignKeyCreateStatement, PostgresQueryBuilder, }; +use sea_query::{extension::postgres::Type, Alias, ColumnDef, ForeignKeyCreateStatement}; pub async fn create_tables(db: &DatabaseConnection) -> Result<(), DbErr> { create_log_table(db).await?; @@ -111,14 +108,23 @@ pub async fn create_active_enum_table(db: &DbConn) -> Result let db_backend = db.get_database_backend(); let tea_enum = Alias::new("tea"); + let create_enum_stmts = match db_backend { + DbBackend::MySql | DbBackend::Sqlite => Vec::new(), + DbBackend::Postgres => vec![Type::create() + .as_enum(tea_enum.clone()) + .values(vec![Alias::new("EverydayTea"), Alias::new("BreakfastTea")]) + .to_owned()], + }; + + create_enum(db, &create_enum_stmts, ActiveEnum).await?; + let mut tea_col = ColumnDef::new(active_enum::Column::Tea); match db_backend { DbBackend::MySql => tea_col.custom(Alias::new("ENUM('EverydayTea', 'BreakfastTea')")), - DbBackend::Postgres => tea_col.custom(tea_enum.clone()), DbBackend::Sqlite => tea_col.text(), + DbBackend::Postgres => tea_col.custom(tea_enum), }; - - let stmt = sea_query::Table::create() + let create_table_stmt = sea_query::Table::create() .table(active_enum::Entity) .col( ColumnDef::new(active_enum::Column::Id) @@ -132,32 +138,5 @@ pub async fn create_active_enum_table(db: &DbConn) -> Result .col(&mut tea_col) .to_owned(); - if db_backend == DbBackend::Postgres { - let drop_type_stmt = Type::drop() - .name(tea_enum.clone()) - .cascade() - .if_exists() - .to_owned(); - let (sql, values) = drop_type_stmt.build(PostgresQueryBuilder); - let stmt = Statement::from_sql_and_values(db.get_database_backend(), &sql, values); - db.execute(stmt).await?; - - let create_type_stmt = Type::create() - .as_enum(tea_enum) - .values(vec![Alias::new("EverydayTea"), Alias::new("BreakfastTea")]) - .to_owned(); - // FIXME: This is not working - { - let (sql, values) = create_type_stmt.build(PostgresQueryBuilder); - let _stmt = Statement::from_sql_and_values(db.get_database_backend(), &sql, values); - } - // But this is working... - let stmt = Statement::from_string( - db.get_database_backend(), - create_type_stmt.to_string(PostgresQueryBuilder), - ); - db.execute(stmt).await?; - } - - create_table(db, &stmt, ActiveEnum).await + create_table(db, &create_table_stmt, ActiveEnum).await } diff --git a/tests/common/setup/mod.rs b/tests/common/setup/mod.rs index 7266d1759..263e19f2f 100644 --- a/tests/common/setup/mod.rs +++ b/tests/common/setup/mod.rs @@ -1,9 +1,12 @@ use pretty_assertions::assert_eq; use sea_orm::{ - ConnectionTrait, Database, DatabaseBackend, DatabaseConnection, DbBackend, DbConn, DbErr, - EntityTrait, ExecResult, Schema, Statement, + ColumnTrait, ColumnType, ConnectionTrait, Database, DatabaseBackend, DatabaseConnection, + DbBackend, DbConn, DbErr, EntityTrait, ExecResult, Iterable, Schema, Statement, +}; +use sea_query::{ + extension::postgres::{Type, TypeCreateStatement}, + Alias, Table, TableCreateStatement, }; -use sea_query::{Alias, Table, TableCreateStatement}; pub async fn setup(base_url: &str, db_name: &str) -> DatabaseConnection { let db = if cfg!(feature = "sqlx-mysql") { @@ -74,6 +77,52 @@ pub async fn tear_down(base_url: &str, db_name: &str) { }; } +pub async fn create_enum( + db: &DbConn, + creates: &[TypeCreateStatement], + entity: E, +) -> Result<(), DbErr> +where + E: EntityTrait, +{ + let builder = db.get_database_backend(); + if builder == DbBackend::Postgres { + for col in E::Column::iter() { + let col_def = col.def(); + let col_type = col_def.get_column_type(); + if !matches!(col_type, ColumnType::Enum(_, _)) { + continue; + } + let name = match col_type { + ColumnType::Enum(s, _) => s.as_str(), + _ => unreachable!(), + }; + let drop_type_stmt = Type::drop() + .name(Alias::new(name)) + .if_exists() + .cascade() + .to_owned(); + let stmt = builder.build(&drop_type_stmt); + db.execute(stmt).await?; + } + } + + let expect_stmts: Vec = creates.iter().map(|stmt| builder.build(stmt)).collect(); + let create_from_entity_stmts: Vec = + Schema::create_enum_from_entity(entity, db.get_database_backend()) + .iter() + .map(|stmt| builder.build(stmt)) + .collect(); + + assert_eq!(expect_stmts, create_from_entity_stmts); + + for stmt in expect_stmts { + db.execute(stmt).await.map(|_| ())?; + } + + Ok(()) +} + pub async fn create_table( db: &DbConn, create: &TableCreateStatement, From f88c7259fe116c1bbcba69564e2090df4de17b70 Mon Sep 17 00:00:00 2001 From: Billy Chan Date: Wed, 27 Oct 2021 12:50:02 +0800 Subject: [PATCH 16/16] Fixup --- tests/common/features/schema.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/common/features/schema.rs b/tests/common/features/schema.rs index 7e9f3de7b..977b4b61c 100644 --- a/tests/common/features/schema.rs +++ b/tests/common/features/schema.rs @@ -1,7 +1,7 @@ pub use super::super::bakery_chain::*; use super::*; -use crate::{common::setup::create_table, create_enum}; +use crate::common::setup::{create_table, create_enum}; use sea_orm::{ error::*, sea_query, ConnectionTrait, DatabaseConnection, DbBackend, DbConn, ExecResult, };