diff --git a/Cargo.toml b/Cargo.toml index 0665ae23e..ea187ef85 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -30,7 +30,7 @@ futures-util = { version = "^0.3" } tracing = { version = "0.1", features = ["log"] } rust_decimal = { version = "^1", optional = true } sea-orm-macros = { version = "^0.5.0", path = "sea-orm-macros", optional = true } -sea-query = { version = "^0.20.0", features = ["thread-safe"] } +sea-query = { version = "^0.21.0", features = ["thread-safe"] } sea-strum = { version = "^0.23", features = ["derive", "sea-orm"] } serde = { version = "^1.0", features = ["derive"] } serde_json = { version = "^1", optional = true } diff --git a/sea-orm-codegen/src/entity/column.rs b/sea-orm-codegen/src/entity/column.rs index 359046008..5890ab8cd 100644 --- a/sea-orm-codegen/src/entity/column.rs +++ b/sea-orm-codegen/src/entity/column.rs @@ -38,7 +38,8 @@ impl Column { ColumnType::Json | ColumnType::JsonBinary => "Json".to_owned(), ColumnType::Date => "Date".to_owned(), ColumnType::Time(_) => "Time".to_owned(), - ColumnType::DateTime(_) | ColumnType::Timestamp(_) => "DateTime".to_owned(), + ColumnType::DateTime(_) => "DateTime".to_owned(), + ColumnType::Timestamp(_) => "DateTimeUtc".to_owned(), ColumnType::TimestampWithTimeZone(_) => "DateTimeWithTimeZone".to_owned(), ColumnType::Decimal(_) | ColumnType::Money(_) => "Decimal".to_owned(), ColumnType::Uuid => "Uuid".to_owned(), @@ -271,7 +272,7 @@ mod tests { "Date", "Time", "DateTime", - "DateTime", + "DateTimeUtc", "DateTimeWithTimeZone", ]; for (mut col, rs_type) in columns.into_iter().zip(rs_types) { diff --git a/sea-orm-macros/src/derives/entity_model.rs b/sea-orm-macros/src/derives/entity_model.rs index 7309a6c47..66dae0993 100644 --- a/sea-orm-macros/src/derives/entity_model.rs +++ b/sea-orm-macros/src/derives/entity_model.rs @@ -242,7 +242,7 @@ pub fn expand_derive_entity_model(data: Data, attrs: Vec) -> syn::Res "DateTime" | "NaiveDateTime" => { quote! { DateTime } } - "DateTimeWithTimeZone" => { + "DateTimeUtc" | "DateTimeLocal" | "DateTimeWithTimeZone" => { quote! { TimestampWithTimeZone } } "Uuid" => quote! { Uuid }, diff --git a/src/entity/active_model.rs b/src/entity/active_model.rs index 18dca6960..749fa5e01 100644 --- a/src/entity/active_model.rs +++ b/src/entity/active_model.rs @@ -608,6 +608,14 @@ impl_into_active_value!(crate::prelude::DateTime, Set); #[cfg_attr(docsrs, doc(cfg(feature = "with-chrono")))] impl_into_active_value!(crate::prelude::DateTimeWithTimeZone, Set); +#[cfg(feature = "with-chrono")] +#[cfg_attr(docsrs, doc(cfg(feature = "with-chrono")))] +impl_into_active_value!(crate::prelude::DateTimeUtc, Set); + +#[cfg(feature = "with-chrono")] +#[cfg_attr(docsrs, doc(cfg(feature = "with-chrono")))] +impl_into_active_value!(crate::prelude::DateTimeLocal, Set); + #[cfg(feature = "with-rust_decimal")] #[cfg_attr(docsrs, doc(cfg(feature = "with-rust_decimal")))] impl_into_active_value!(crate::prelude::Decimal, Set); diff --git a/src/entity/column.rs b/src/entity/column.rs index 8beb465b6..c4ea545f0 100644 --- a/src/entity/column.rs +++ b/src/entity/column.rs @@ -9,6 +9,7 @@ pub struct ColumnDef { pub(crate) null: bool, pub(crate) unique: bool, pub(crate) indexed: bool, + pub(crate) default_value: Option, } /// The type of column as defined in the SQL format @@ -300,6 +301,7 @@ impl ColumnType { null: false, unique: false, indexed: false, + default_value: None, } } @@ -335,6 +337,15 @@ impl ColumnDef { self } + /// Set the default value + pub fn default_value(mut self, value: T) -> Self + where + T: Into, + { + self.default_value = Some(value.into()); + self + } + /// Get [ColumnType] as reference pub fn get_column_type(&self) -> &ColumnType { &self.col_type diff --git a/src/entity/prelude.rs b/src/entity/prelude.rs index 28f5fe708..c88f9d7a8 100644 --- a/src/entity/prelude.rs +++ b/src/entity/prelude.rs @@ -24,10 +24,18 @@ pub use chrono::NaiveTime as Time; #[cfg(feature = "with-chrono")] pub use chrono::NaiveDateTime as DateTime; -/// Handles the time and dates +/// Date time with fixed offset #[cfg(feature = "with-chrono")] pub type DateTimeWithTimeZone = chrono::DateTime; +/// Date time represented in UTC +#[cfg(feature = "with-chrono")] +pub type DateTimeUtc = chrono::DateTime; + +/// Date time represented in local time +#[cfg(feature = "with-chrono")] +pub type DateTimeLocal = chrono::DateTime; + #[cfg(feature = "with-rust_decimal")] pub use rust_decimal::Decimal; diff --git a/src/executor/query.rs b/src/executor/query.rs index 3a842fc01..e15b3a884 100644 --- a/src/executor/query.rs +++ b/src/executor/query.rs @@ -271,6 +271,12 @@ try_getable_all!(chrono::NaiveDateTime); #[cfg(feature = "with-chrono")] try_getable_date_time!(chrono::DateTime); +#[cfg(feature = "with-chrono")] +try_getable_all!(chrono::DateTime); + +#[cfg(feature = "with-chrono")] +try_getable_all!(chrono::DateTime); + #[cfg(feature = "with-rust_decimal")] use rust_decimal::Decimal; @@ -614,6 +620,12 @@ try_from_u64_err!(chrono::NaiveDateTime); #[cfg(feature = "with-chrono")] try_from_u64_err!(chrono::DateTime); +#[cfg(feature = "with-chrono")] +try_from_u64_err!(chrono::DateTime); + +#[cfg(feature = "with-chrono")] +try_from_u64_err!(chrono::DateTime); + #[cfg(feature = "with-rust_decimal")] try_from_u64_err!(rust_decimal::Decimal); diff --git a/src/query/combine.rs b/src/query/combine.rs index 2baf3fa2d..a96d5be12 100644 --- a/src/query/combine.rs +++ b/src/query/combine.rs @@ -45,11 +45,21 @@ where None => { let col = match &sel.expr { SimpleExpr::Column(col_ref) => match &col_ref { - ColumnRef::Column(col) | ColumnRef::TableColumn(_, col) => col, + ColumnRef::Column(col) + | ColumnRef::TableColumn(_, col) + | ColumnRef::SchemaTableColumn(_, _, col) => col, + ColumnRef::Asterisk | ColumnRef::TableAsterisk(_) => { + panic!("cannot apply alias for Column with asterisk") + } }, SimpleExpr::AsEnum(_, simple_expr) => match simple_expr.as_ref() { SimpleExpr::Column(col_ref) => match &col_ref { - ColumnRef::Column(col) | ColumnRef::TableColumn(_, col) => col, + ColumnRef::Column(col) + | ColumnRef::TableColumn(_, col) + | ColumnRef::SchemaTableColumn(_, _, col) => col, + ColumnRef::Asterisk | ColumnRef::TableAsterisk(_) => { + panic!("cannot apply alias for AsEnum with asterisk") + } }, _ => { panic!("cannot apply alias for AsEnum with expr other than Column") diff --git a/src/query/util.rs b/src/query/util.rs index b23804f24..b87058456 100644 --- a/src/query/util.rs +++ b/src/query/util.rs @@ -122,7 +122,7 @@ macro_rules! debug_query_stmt { /// let raw_sql = debug_query!(&c, DbBackend::Sqlite); /// assert_eq!( /// raw_sql, -/// r#"INSERT INTO `cake` (`id`, `name`) VALUES (1, 'Apple Pie')"# +/// r#"INSERT INTO "cake" ("id", "name") VALUES (1, 'Apple Pie')"# /// ); /// ``` #[macro_export] diff --git a/src/schema/entity.rs b/src/schema/entity.rs index 9c422ed34..7b59845c3 100644 --- a/src/schema/entity.rs +++ b/src/schema/entity.rs @@ -103,6 +103,9 @@ where if orm_column_def.unique { column_def.unique_key(); } + if let Some(value) = orm_column_def.default_value { + column_def.default(value); + } for primary_key in E::PrimaryKey::iter() { if column.to_string() == primary_key.into_column().to_string() { if E::PrimaryKey::auto_increment() { diff --git a/tests/active_enum_tests.rs b/tests/active_enum_tests.rs index 21e63016d..e3c701c7d 100644 --- a/tests/active_enum_tests.rs +++ b/tests/active_enum_tests.rs @@ -405,8 +405,14 @@ mod tests { #[cfg(any(feature = "sqlx-mysql", feature = "sqlx-sqlite"))] { assert_eq!( - _select.build(DbBackend::MySql).to_string(), _select.build(DbBackend::Sqlite).to_string(), + [ + r#"SELECT "active_enum_child"."id", "active_enum_child"."parent_id", "active_enum_child"."category", "active_enum_child"."color", "active_enum_child"."tea""#, + r#"FROM "active_enum_child""#, + r#"INNER JOIN "active_enum" ON "active_enum"."id" = "active_enum_child"."parent_id""#, + r#"WHERE "active_enum"."id" = 1"#, + ] + .join(" ") ); assert_eq!( _select.build(DbBackend::MySql).to_string(), @@ -435,8 +441,16 @@ mod tests { #[cfg(any(feature = "sqlx-mysql", feature = "sqlx-sqlite"))] { assert_eq!( - _select.build(DbBackend::MySql).to_string(), - _select.build(DbBackend::Sqlite).to_string(), + _select + .build(DbBackend::Sqlite) + .to_string(), + [ + r#"SELECT "active_enum"."id" AS "A_id", "active_enum"."category" AS "A_category", "active_enum"."color" AS "A_color", "active_enum"."tea" AS "A_tea","#, + r#""active_enum_child"."id" AS "B_id", "active_enum_child"."parent_id" AS "B_parent_id", "active_enum_child"."category" AS "B_category", "active_enum_child"."color" AS "B_color", "active_enum_child"."tea" AS "B_tea""#, + r#"FROM "active_enum""#, + r#"LEFT JOIN "active_enum_child" ON "active_enum"."id" = "active_enum_child"."parent_id""#, + ] + .join(" ") ); assert_eq!( _select @@ -478,8 +492,14 @@ mod tests { #[cfg(any(feature = "sqlx-mysql", feature = "sqlx-sqlite"))] { assert_eq!( - _select.build(DbBackend::MySql).to_string(), _select.build(DbBackend::Sqlite).to_string(), + [ + r#"SELECT "active_enum_child"."id", "active_enum_child"."parent_id", "active_enum_child"."category", "active_enum_child"."color", "active_enum_child"."tea""#, + r#"FROM "active_enum_child""#, + r#"INNER JOIN "active_enum" AS "r0" ON "r0"."id" = "active_enum_child"."parent_id""#, + r#"WHERE "r0"."id" = 1"#, + ] + .join(" ") ); assert_eq!( _select.build(DbBackend::MySql).to_string(), @@ -508,8 +528,16 @@ mod tests { #[cfg(any(feature = "sqlx-mysql", feature = "sqlx-sqlite"))] { assert_eq!( - _select.build(DbBackend::MySql).to_string(), - _select.build(DbBackend::Sqlite).to_string(), + _select + .build(DbBackend::Sqlite) + .to_string(), + [ + r#"SELECT "active_enum"."id" AS "A_id", "active_enum"."category" AS "A_category", "active_enum"."color" AS "A_color", "active_enum"."tea" AS "A_tea","#, + r#""r0"."id" AS "B_id", "r0"."parent_id" AS "B_parent_id", "r0"."category" AS "B_category", "r0"."color" AS "B_color", "r0"."tea" AS "B_tea""#, + r#"FROM "active_enum""#, + r#"LEFT JOIN "active_enum_child" AS "r0" ON "active_enum"."id" = "r0"."parent_id""#, + ] + .join(" ") ); assert_eq!( _select @@ -552,8 +580,14 @@ mod tests { #[cfg(any(feature = "sqlx-mysql", feature = "sqlx-sqlite"))] { assert_eq!( - _select.build(DbBackend::MySql).to_string(), _select.build(DbBackend::Sqlite).to_string(), + [ + r#"SELECT "active_enum"."id", "active_enum"."category", "active_enum"."color", "active_enum"."tea""#, + r#"FROM "active_enum""#, + r#"INNER JOIN "active_enum_child" ON "active_enum_child"."parent_id" = "active_enum"."id""#, + r#"WHERE "active_enum_child"."id" = 1"#, + ] + .join(" ") ); assert_eq!( _select.build(DbBackend::MySql).to_string(), @@ -582,8 +616,16 @@ mod tests { #[cfg(any(feature = "sqlx-mysql", feature = "sqlx-sqlite"))] { assert_eq!( - _select.build(DbBackend::MySql).to_string(), - _select.build(DbBackend::Sqlite).to_string(), + _select + .build(DbBackend::Sqlite) + .to_string(), + [ + r#"SELECT "active_enum_child"."id" AS "A_id", "active_enum_child"."parent_id" AS "A_parent_id", "active_enum_child"."category" AS "A_category", "active_enum_child"."color" AS "A_color", "active_enum_child"."tea" AS "A_tea","#, + r#""active_enum"."id" AS "B_id", "active_enum"."category" AS "B_category", "active_enum"."color" AS "B_color", "active_enum"."tea" AS "B_tea""#, + r#"FROM "active_enum_child""#, + r#"LEFT JOIN "active_enum" ON "active_enum_child"."parent_id" = "active_enum"."id""#, + ] + .join(" ") ); assert_eq!( _select @@ -626,8 +668,14 @@ mod tests { #[cfg(any(feature = "sqlx-mysql", feature = "sqlx-sqlite"))] { assert_eq!( - _select.build(DbBackend::MySql).to_string(), _select.build(DbBackend::Sqlite).to_string(), + [ + r#"SELECT "active_enum"."id", "active_enum"."category", "active_enum"."color", "active_enum"."tea""#, + r#"FROM "active_enum""#, + r#"INNER JOIN "active_enum_child" AS "r0" ON "r0"."parent_id" = "active_enum"."id""#, + r#"WHERE "r0"."id" = 1"#, + ] + .join(" ") ); assert_eq!( _select.build(DbBackend::MySql).to_string(), @@ -656,8 +704,16 @@ mod tests { #[cfg(any(feature = "sqlx-mysql", feature = "sqlx-sqlite"))] { assert_eq!( - _select.build(DbBackend::MySql).to_string(), - _select.build(DbBackend::Sqlite).to_string(), + _select + .build(DbBackend::Sqlite) + .to_string(), + [ + r#"SELECT "active_enum_child"."id" AS "A_id", "active_enum_child"."parent_id" AS "A_parent_id", "active_enum_child"."category" AS "A_category", "active_enum_child"."color" AS "A_color", "active_enum_child"."tea" AS "A_tea","#, + r#""r0"."id" AS "B_id", "r0"."category" AS "B_category", "r0"."color" AS "B_color", "r0"."tea" AS "B_tea""#, + r#"FROM "active_enum_child""#, + r#"LEFT JOIN "active_enum" AS "r0" ON "active_enum_child"."parent_id" = "r0"."id""#, + ] + .join(" ") ); assert_eq!( _select diff --git a/tests/common/features/mod.rs b/tests/common/features/mod.rs index ea880fd22..18c0ae78a 100644 --- a/tests/common/features/mod.rs +++ b/tests/common/features/mod.rs @@ -4,6 +4,7 @@ pub mod applog; pub mod byte_primary_key; pub mod metadata; pub mod repository; +pub mod satellite; pub mod schema; pub mod sea_orm_active_enums; pub mod self_join; @@ -14,6 +15,7 @@ pub use applog::Entity as Applog; pub use byte_primary_key::Entity as BytePrimaryKey; pub use metadata::Entity as Metadata; pub use repository::Entity as Repository; +pub use satellite::Entity as Satellite; pub use schema::*; pub use sea_orm_active_enums::*; pub use self_join::Entity as SelfJoin; diff --git a/tests/common/features/satellite.rs b/tests/common/features/satellite.rs new file mode 100644 index 000000000..5db48274f --- /dev/null +++ b/tests/common/features/satellite.rs @@ -0,0 +1,18 @@ +use sea_orm::entity::prelude::*; + +#[derive(Clone, Debug, PartialEq, DeriveEntityModel)] +#[sea_orm(table_name = "satellite")] +pub struct Model { + #[sea_orm(primary_key)] + pub id: i32, + pub satellite_name: String, + #[sea_orm(default_value = "2022-01-26 16:24:00")] + pub launch_date: DateTimeUtc, + #[sea_orm(default_value = "2022-01-26 16:24:00")] + pub deployment_date: DateTimeLocal, +} + +#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] +pub enum Relation {} + +impl ActiveModelBehavior for ActiveModel {} diff --git a/tests/common/features/schema.rs b/tests/common/features/schema.rs index c38feb048..8fd7b9c8a 100644 --- a/tests/common/features/schema.rs +++ b/tests/common/features/schema.rs @@ -16,6 +16,7 @@ pub async fn create_tables(db: &DatabaseConnection) -> Result<(), DbErr> { create_repository_table(db).await?; create_self_join_table(db).await?; create_byte_primary_key_table(db).await?; + create_satellites_table(db).await?; let create_enum_stmts = match db_backend { DbBackend::MySql | DbBackend::Sqlite => Vec::new(), @@ -201,3 +202,35 @@ pub async fn create_active_enum_child_table(db: &DbConn) -> Result Result { + let stmt = sea_query::Table::create() + .table(satellite::Entity) + .col( + ColumnDef::new(satellite::Column::Id) + .integer() + .not_null() + .auto_increment() + .primary_key(), + ) + .col( + ColumnDef::new(satellite::Column::SatelliteName) + .string() + .not_null(), + ) + .col( + ColumnDef::new(satellite::Column::LaunchDate) + .timestamp_with_time_zone() + .not_null() + .default("2022-01-26 16:24:00"), + ) + .col( + ColumnDef::new(satellite::Column::DeploymentDate) + .timestamp_with_time_zone() + .not_null() + .default("2022-01-26 16:24:00"), + ) + .to_owned(); + + create_table(db, &stmt, Satellite).await +} diff --git a/tests/timestamp_tests.rs b/tests/timestamp_tests.rs index cad352709..d0334a5cb 100644 --- a/tests/timestamp_tests.rs +++ b/tests/timestamp_tests.rs @@ -1,5 +1,4 @@ pub mod common; - pub use common::{features::*, setup::*, TestContext}; use sea_orm::{entity::prelude::*, DatabaseConnection, IntoActiveModel}; @@ -13,6 +12,8 @@ async fn main() -> Result<(), DbErr> { let ctx = TestContext::new("bakery_chain_schema_timestamp_tests").await; create_tables(&ctx.db).await?; create_applog(&ctx.db).await?; + create_satellites_log(&ctx.db).await?; + ctx.delete().await; Ok(()) @@ -30,8 +31,26 @@ pub async fn create_applog(db: &DatabaseConnection) -> Result<(), DbErr> { .exec(db) .await?; - assert_eq!(log.id.clone(), res.last_insert_id); + assert_eq!(log.id, res.last_insert_id); assert_eq!(Applog::find().one(db).await?, Some(log.clone())); Ok(()) } + +pub async fn create_satellites_log(db: &DatabaseConnection) -> Result<(), DbErr> { + let archive = satellite::Model { + id: 1, + satellite_name: "Sea-00001-2022".to_owned(), + launch_date: "2022-01-07T12:11:23Z".parse().unwrap(), + deployment_date: "2022-01-07T12:11:23Z".parse().unwrap(), + }; + + let res = Satellite::insert(archive.clone().into_active_model()) + .exec(db) + .await?; + + assert_eq!(archive.id, res.last_insert_id); + assert_eq!(Satellite::find().one(db).await?, Some(archive.clone())); + + Ok(()) +}