Merge pull request #489 from SeaQL/date-time-utc-local

Support `DateTime<Utc>` & `DateTime<Local>`
This commit is contained in:
Chris Tsang 2022-02-01 15:25:39 +08:00 committed by GitHub
commit e63d463155
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 203 additions and 22 deletions

View File

@ -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 }

View File

@ -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) {

View File

@ -242,7 +242,7 @@ pub fn expand_derive_entity_model(data: Data, attrs: Vec<Attribute>) -> syn::Res
"DateTime" | "NaiveDateTime" => {
quote! { DateTime }
}
"DateTimeWithTimeZone" => {
"DateTimeUtc" | "DateTimeLocal" | "DateTimeWithTimeZone" => {
quote! { TimestampWithTimeZone }
}
"Uuid" => quote! { Uuid },

View File

@ -632,6 +632,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);

View File

@ -9,6 +9,7 @@ pub struct ColumnDef {
pub(crate) null: bool,
pub(crate) unique: bool,
pub(crate) indexed: bool,
pub(crate) default_value: Option<Value>,
}
/// 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<T>(mut self, value: T) -> Self
where
T: Into<Value>,
{
self.default_value = Some(value.into());
self
}
/// Get [ColumnType] as reference
pub fn get_column_type(&self) -> &ColumnType {
&self.col_type

View File

@ -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<chrono::FixedOffset>;
/// Date time represented in UTC
#[cfg(feature = "with-chrono")]
pub type DateTimeUtc = chrono::DateTime<chrono::Utc>;
/// Date time represented in local time
#[cfg(feature = "with-chrono")]
pub type DateTimeLocal = chrono::DateTime<chrono::Local>;
#[cfg(feature = "with-rust_decimal")]
pub use rust_decimal::Decimal;

View File

@ -271,6 +271,12 @@ try_getable_all!(chrono::NaiveDateTime);
#[cfg(feature = "with-chrono")]
try_getable_date_time!(chrono::DateTime<chrono::FixedOffset>);
#[cfg(feature = "with-chrono")]
try_getable_all!(chrono::DateTime<chrono::Utc>);
#[cfg(feature = "with-chrono")]
try_getable_all!(chrono::DateTime<chrono::Local>);
#[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<chrono::FixedOffset>);
#[cfg(feature = "with-chrono")]
try_from_u64_err!(chrono::DateTime<chrono::Utc>);
#[cfg(feature = "with-chrono")]
try_from_u64_err!(chrono::DateTime<chrono::Local>);
#[cfg(feature = "with-rust_decimal")]
try_from_u64_err!(rust_decimal::Decimal);

View File

@ -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")

View File

@ -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]

View File

@ -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() {

View File

@ -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

View File

@ -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;

View File

@ -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 {}

View File

@ -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<ExecResult, D
create_table(db, &create_table_stmt, ActiveEnumChild).await
}
pub async fn create_satellites_table(db: &DbConn) -> Result<ExecResult, DbErr> {
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
}

View File

@ -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(())
}