diff --git a/src/entity/column.rs b/src/entity/column.rs index 8dbff6ea..92e32fb6 100644 --- a/src/entity/column.rs +++ b/src/entity/column.rs @@ -1,5 +1,5 @@ -use crate::{EntityName, IdenStatic, Iterable}; -use sea_query::{Alias, BinOper, DynIden, Expr, SeaRc, SelectStatement, SimpleExpr, Value}; +use crate::{cast_text_as_enum, EntityName, IdenStatic, IntoSimpleExpr, Iterable}; +use sea_query::{BinOper, DynIden, Expr, SeaRc, SelectStatement, SimpleExpr, Value}; use std::str::FromStr; /// Defines a Column for an Entity @@ -100,13 +100,7 @@ macro_rules! bind_oper_with_enum_casting { where V: Into, { - let val = Expr::val(v); - let col_def = self.def(); - let col_type = col_def.get_column_type(); - let expr = match col_type.get_enum_name() { - Some(enum_name) => val.as_enum(Alias::new(enum_name)), - None => val.into(), - }; + let expr = cast_text_as_enum(Expr::val(v), self); Expr::tbl(self.entity_name(), *self).binary(BinOper::$bin_op, expr) } }; @@ -305,6 +299,11 @@ pub trait ColumnTrait: IdenStatic + Iterable + FromStr { bind_subquery_func!(in_subquery); bind_subquery_func!(not_in_subquery); + + /// Construct a [`SimpleExpr::Column`] wrapped in [`Expr`]. + fn into_expr(self) -> Expr { + Expr::expr(self.into_simple_expr()) + } } impl ColumnType { diff --git a/src/executor/insert.rs b/src/executor/insert.rs index 5f3f9c95..3c7b69c8 100644 --- a/src/executor/insert.rs +++ b/src/executor/insert.rs @@ -1,10 +1,8 @@ use crate::{ - error::*, ActiveModelTrait, ColumnTrait, ConnectionTrait, EntityTrait, Insert, IntoActiveModel, - Iterable, PrimaryKeyTrait, SelectModel, SelectorRaw, Statement, TryFromU64, -}; -use sea_query::{ - Alias, Expr, FromValueTuple, Iden, InsertStatement, IntoColumnRef, Query, ValueTuple, + cast_enum_as_text, error::*, ActiveModelTrait, ConnectionTrait, EntityTrait, Insert, + IntoActiveModel, Iterable, PrimaryKeyTrait, SelectModel, SelectorRaw, Statement, TryFromU64, }; +use sea_query::{Expr, FromValueTuple, Iden, InsertStatement, IntoColumnRef, Query, ValueTuple}; use std::{future::Future, marker::PhantomData}; /// Defines a structure to perform INSERT operations in an ActiveModel @@ -149,16 +147,10 @@ where let db_backend = db.get_database_backend(); let found = match db.support_returning() { true => { - let returning = - Query::returning().exprs(::Column::iter().map(|c| { - let col = Expr::col(c); - let col_def = ColumnTrait::def(&c); - let col_type = col_def.get_column_type(); - match col_type.get_enum_name() { - Some(_) => col.as_enum(Alias::new("text")), - None => col.into(), - } - })); + let returning = Query::returning().exprs( + ::Column::iter() + .map(|c| cast_enum_as_text(Expr::col(c), &c)), + ); insert_statement.returning(returning); SelectorRaw::::Model>>::from_statement( db_backend.build(&insert_statement), diff --git a/src/executor/update.rs b/src/executor/update.rs index f00c536f..a7b343a1 100644 --- a/src/executor/update.rs +++ b/src/executor/update.rs @@ -1,8 +1,8 @@ use crate::{ - error::*, ActiveModelTrait, ColumnTrait, ConnectionTrait, EntityTrait, IntoActiveModel, + cast_enum_as_text, error::*, ActiveModelTrait, ConnectionTrait, EntityTrait, IntoActiveModel, Iterable, SelectModel, SelectorRaw, Statement, UpdateMany, UpdateOne, }; -use sea_query::{Alias, Expr, FromValueTuple, Query, UpdateStatement}; +use sea_query::{Expr, FromValueTuple, Query, UpdateStatement}; use std::future::Future; /// Defines an update operation @@ -91,16 +91,10 @@ where { match db.support_returning() { true => { - let returning = - Query::returning().exprs(::Column::iter().map(|c| { - let col = Expr::col(c); - let col_def = c.def(); - let col_type = col_def.get_column_type(); - match col_type.get_enum_name() { - Some(_) => col.as_enum(Alias::new("text")), - None => col.into(), - } - })); + let returning = Query::returning().exprs( + ::Column::iter() + .map(|c| cast_enum_as_text(Expr::col(c), &c)), + ); query.returning(returning); let db_backend = db.get_database_backend(); let found: Option<::Model> = diff --git a/src/query/combine.rs b/src/query/combine.rs index ef32798b..677d7aa0 100644 --- a/src/query/combine.rs +++ b/src/query/combine.rs @@ -1,12 +1,10 @@ use crate::{ - ColumnTrait, EntityTrait, IdenStatic, IntoSimpleExpr, Iterable, QueryTrait, Select, SelectTwo, - SelectTwoMany, + cast_enum_as_text, ColumnTrait, EntityTrait, IdenStatic, Iterable, QueryTrait, Select, + SelectTwo, SelectTwoMany, }; use core::marker::PhantomData; pub use sea_query::JoinType; -use sea_query::{ - Alias, ColumnRef, DynIden, Expr, Iden, Order, SeaRc, SelectExpr, SelectStatement, SimpleExpr, -}; +use sea_query::{Alias, ColumnRef, Iden, Order, SeaRc, SelectExpr, SelectStatement, SimpleExpr}; macro_rules! select_def { ( $ident: ident, $str: expr ) => { @@ -148,18 +146,10 @@ where F: EntityTrait, S: QueryTrait, { - let text_type = SeaRc::new(Alias::new("text")) as DynIden; for col in ::iter() { - let col_def = col.def(); - let col_type = col_def.get_column_type(); let alias = format!("{}{}", SelectB.as_str(), col.as_str()); - let expr = Expr::expr(col.into_simple_expr()); - let expr = match col_type.get_enum_name() { - Some(_) => expr.as_enum(text_type.clone()), - None => expr.into(), - }; selector.query().expr(SelectExpr { - expr, + expr: cast_enum_as_text(col.into_expr(), &col), alias: Some(SeaRc::new(Alias::new(&alias))), window: None, }); diff --git a/src/query/helper.rs b/src/query/helper.rs index 6ab31d53..f476d60c 100644 --- a/src/query/helper.rs +++ b/src/query/helper.rs @@ -38,11 +38,34 @@ pub trait QuerySelect: Sized { /// r#"SELECT "cake"."name" FROM "cake""# /// ); /// ``` + /// + /// Enum column will be casted into text (PostgreSQL only) + /// + /// ``` + /// use sea_orm::{entity::*, query::*, tests_cfg::lunch_set, DbBackend}; + /// + /// assert_eq!( + /// lunch_set::Entity::find() + /// .select_only() + /// .column(lunch_set::Column::Tea) + /// .build(DbBackend::Postgres) + /// .to_string(), + /// r#"SELECT CAST("lunch_set"."tea" AS text) FROM "lunch_set""# + /// ); + /// assert_eq!( + /// lunch_set::Entity::find() + /// .select_only() + /// .column(lunch_set::Column::Tea) + /// .build(DbBackend::MySql) + /// .to_string(), + /// r#"SELECT `lunch_set`.`tea` FROM `lunch_set`"# + /// ); + /// ``` fn column(mut self, col: C) -> Self where C: ColumnTrait, { - self.query().expr(col.into_simple_expr()); + self.query().expr(cast_enum_as_text(col.into_expr(), &col)); self } @@ -532,3 +555,33 @@ pub(crate) fn unpack_table_alias(table_ref: &TableRef) -> Option { | TableRef::DatabaseSchemaTableAlias(_, _, _, alias) => Some(SeaRc::clone(alias)), } } + +#[derive(Iden)] +struct Text; + +pub(crate) fn cast_enum_as_text(expr: Expr, col: &C) -> SimpleExpr +where + C: ColumnTrait, +{ + cast_enum_text_inner(expr, col, |col, _| col.as_enum(Text)) +} + +pub(crate) fn cast_text_as_enum(expr: Expr, col: &C) -> SimpleExpr +where + C: ColumnTrait, +{ + cast_enum_text_inner(expr, col, |col, enum_name| col.as_enum(enum_name)) +} + +fn cast_enum_text_inner(expr: Expr, col: &C, f: F) -> SimpleExpr +where + C: ColumnTrait, + F: Fn(Expr, DynIden) -> SimpleExpr, +{ + let col_def = col.def(); + let col_type = col_def.get_column_type(); + match col_type.get_enum_name() { + Some(enum_name) => f(expr, SeaRc::new(Alias::new(enum_name))), + None => expr.into(), + } +} diff --git a/src/query/insert.rs b/src/query/insert.rs index a2b1c63f..3ca6f82b 100644 --- a/src/query/insert.rs +++ b/src/query/insert.rs @@ -1,9 +1,9 @@ use crate::{ - ActiveModelTrait, ColumnTrait, EntityName, EntityTrait, IntoActiveModel, Iterable, + cast_text_as_enum, ActiveModelTrait, EntityName, EntityTrait, IntoActiveModel, Iterable, PrimaryKeyTrait, QueryTrait, }; use core::marker::PhantomData; -use sea_query::{Alias, Expr, InsertStatement, OnConflict, ValueTuple}; +use sea_query::{Expr, InsertStatement, OnConflict, ValueTuple}; /// Performs INSERT operations on a ActiveModel #[derive(Debug)] @@ -134,14 +134,7 @@ where } if av_has_val { columns.push(col); - let val = Expr::val(av.into_value().unwrap()); - let col_def = col.def(); - let col_type = col_def.get_column_type(); - let expr = match col_type.get_enum_name() { - Some(enum_name) => val.as_enum(Alias::new(enum_name)), - None => val.into(), - }; - values.push(expr); + values.push(cast_text_as_enum(Expr::val(av.into_value().unwrap()), &col)); } } self.query.columns(columns); diff --git a/src/query/join.rs b/src/query/join.rs index d23c0333..744ed354 100644 --- a/src/query/join.rs +++ b/src/query/join.rs @@ -1,9 +1,9 @@ use crate::{ - join_tbl_on_condition, unpack_table_ref, ColumnTrait, EntityTrait, IdenStatic, Iterable, + cast_enum_as_text, join_tbl_on_condition, unpack_table_ref, EntityTrait, IdenStatic, Iterable, Linked, QuerySelect, Related, Select, SelectA, SelectB, SelectTwo, SelectTwoMany, }; pub use sea_query::JoinType; -use sea_query::{Alias, Condition, DynIden, Expr, IntoIden, SeaRc, SelectExpr}; +use sea_query::{Alias, Condition, Expr, IntoIden, SeaRc, SelectExpr}; impl Select where @@ -92,22 +92,15 @@ where .join_as(JoinType::LeftJoin, table_ref, to_tbl, condition); } slf = slf.apply_alias(SelectA.as_str()); - let text_type = SeaRc::new(Alias::new("text")) as DynIden; let mut select_two = SelectTwo::new_without_prepare(slf.query); for col in ::iter() { - let col_def = col.def(); - let col_type = col_def.get_column_type(); let alias = format!("{}{}", SelectB.as_str(), col.as_str()); let expr = Expr::tbl( Alias::new(&format!("r{}", l.link().len() - 1)).into_iden(), col.into_iden(), ); - let expr = match col_type.get_enum_name() { - Some(_) => expr.as_enum(text_type.clone()), - None => expr.into(), - }; select_two.query().expr(SelectExpr { - expr, + expr: cast_enum_as_text(expr, &col), alias: Some(SeaRc::new(Alias::new(&alias))), window: None, }); diff --git a/src/query/select.rs b/src/query/select.rs index 3c982948..3f53430e 100644 --- a/src/query/select.rs +++ b/src/query/select.rs @@ -1,8 +1,11 @@ -use crate::{ColumnTrait, EntityTrait, Iterable, QueryFilter, QueryOrder, QuerySelect, QueryTrait}; +use crate::{ + cast_enum_as_text, ColumnTrait, EntityTrait, Iterable, QueryFilter, QueryOrder, QuerySelect, + QueryTrait, +}; use core::fmt::Debug; use core::marker::PhantomData; pub use sea_query::JoinType; -use sea_query::{Alias, DynIden, Expr, IntoColumnRef, SeaRc, SelectStatement, SimpleExpr}; +use sea_query::{IntoColumnRef, SelectStatement, SimpleExpr}; /// Defines a structure to perform select operations #[derive(Clone, Debug)] @@ -119,18 +122,8 @@ 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 expr = Expr::tbl(table.clone(), col); - let col_def = col.def(); - let col_type = col_def.get_column_type(); - match col_type.get_enum_name() { - Some(_) => expr.as_enum(text_type.clone()), - None => expr.into(), - } - }) + .map(|col| cast_enum_as_text(col.into_expr(), &col)) .collect() } diff --git a/src/query/update.rs b/src/query/update.rs index a48d19a5..63adac7a 100644 --- a/src/query/update.rs +++ b/src/query/update.rs @@ -1,9 +1,9 @@ use crate::{ - ActiveModelTrait, ColumnTrait, EntityTrait, Iterable, PrimaryKeyToColumn, QueryFilter, - QueryTrait, + cast_text_as_enum, ActiveModelTrait, ColumnTrait, EntityTrait, Iterable, PrimaryKeyToColumn, + QueryFilter, QueryTrait, }; use core::marker::PhantomData; -use sea_query::{Alias, Expr, IntoIden, SimpleExpr, UpdateStatement}; +use sea_query::{Expr, IntoIden, SimpleExpr, UpdateStatement}; /// Defines a structure to perform UPDATE query operations on a ActiveModel #[derive(Clone, Debug)] @@ -109,13 +109,7 @@ where } let av = self.model.get(col); if av.is_set() { - let val = Expr::val(av.into_value().unwrap()); - let col_def = col.def(); - let col_type = col_def.get_column_type(); - let expr = match col_type.get_enum_name() { - Some(enum_name) => val.as_enum(Alias::new(enum_name)), - None => val.into(), - }; + let expr = cast_text_as_enum(Expr::val(av.into_value().unwrap()), &col); self.query.value_expr(col, expr); } } diff --git a/src/tests_cfg/lunch_set.rs b/src/tests_cfg/lunch_set.rs new file mode 100644 index 00000000..b063245e --- /dev/null +++ b/src/tests_cfg/lunch_set.rs @@ -0,0 +1,23 @@ +use super::sea_orm_active_enums::*; +use crate as sea_orm; +use crate::entity::prelude::*; + +#[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel)] +#[sea_orm(table_name = "lunch_set")] +pub struct Model { + #[sea_orm(primary_key)] + pub id: i32, + pub name: String, + pub tea: Tea, +} + +#[derive(Copy, Clone, Debug, EnumIter)] +pub enum Relation {} + +impl RelationTrait for Relation { + fn def(&self) -> RelationDef { + panic!("No RelationDef") + } +} + +impl ActiveModelBehavior for ActiveModel {} diff --git a/src/tests_cfg/mod.rs b/src/tests_cfg/mod.rs index 7f6178ef..9210657c 100644 --- a/src/tests_cfg/mod.rs +++ b/src/tests_cfg/mod.rs @@ -8,7 +8,9 @@ pub mod entity_linked; pub mod filling; pub mod fruit; pub mod indexes; +pub mod lunch_set; pub mod rust_keyword; +pub mod sea_orm_active_enums; pub mod vendor; pub use cake::Entity as Cake; @@ -17,5 +19,6 @@ pub use cake_filling::Entity as CakeFilling; pub use cake_filling_price::Entity as CakeFillingPrice; pub use filling::Entity as Filling; pub use fruit::Entity as Fruit; +pub use lunch_set::Entity as LunchSet; pub use rust_keyword::Entity as RustKeyword; pub use vendor::Entity as Vendor; diff --git a/src/tests_cfg/sea_orm_active_enums.rs b/src/tests_cfg/sea_orm_active_enums.rs new file mode 100644 index 00000000..a5aba5b1 --- /dev/null +++ b/src/tests_cfg/sea_orm_active_enums.rs @@ -0,0 +1,11 @@ +use crate as sea_orm; +use crate::entity::prelude::*; + +#[derive(Debug, Clone, PartialEq, Eq, EnumIter, DeriveActiveEnum)] +#[sea_orm(rs_type = "String", db_type = "Enum", enum_name = "tea")] +pub enum Tea { + #[sea_orm(string_value = "EverydayTea")] + EverydayTea, + #[sea_orm(string_value = "BreakfastTea")] + BreakfastTea, +}