From 93623f0d0280c43318256c4314c9cd6dee016f9b Mon Sep 17 00:00:00 2001 From: Chris Tsang Date: Thu, 12 Oct 2023 09:37:08 +0100 Subject: [PATCH] Support MySQL / SQLite --- .../src/derives/try_getable_from_json.rs | 11 ++++ src/entity/column.rs | 39 ++++++++++++- tests/active_enum_tests.rs | 6 +- tests/common/features/json_vec_derive.rs | 21 +++---- tests/common/features/mod.rs | 1 + tests/common/features/schema.rs | 57 ++++++++++++------- tests/common/setup/mod.rs | 9 ++- tests/json_vec_tests.rs | 8 ++- 8 files changed, 109 insertions(+), 43 deletions(-) diff --git a/sea-orm-macros/src/derives/try_getable_from_json.rs b/sea-orm-macros/src/derives/try_getable_from_json.rs index 8742d3c9e..b111b2497 100644 --- a/sea-orm-macros/src/derives/try_getable_from_json.rs +++ b/sea-orm-macros/src/derives/try_getable_from_json.rs @@ -2,6 +2,15 @@ use proc_macro2::{Ident, TokenStream}; use quote::quote; pub fn expand_derive_from_json_query_result(ident: Ident) -> syn::Result { + let impl_not_u8 = if cfg!(feature = "postgres-array") { + quote!( + #[automatically_derived] + impl sea_orm::sea_query::value::with_array::NotU8 for #ident {} + ) + } else { + quote!() + }; + Ok(quote!( #[automatically_derived] impl sea_orm::TryGetableFromJson for #ident {} @@ -43,5 +52,7 @@ pub fn expand_derive_from_json_query_result(ident: Ident) -> syn::Result)` into `Json(Vec)` if the column type is `Json`. fn save_enum_as(&self, val: Expr) -> SimpleExpr { cast_enum_as(val, self, |col, enum_name, col_type| { let type_name = match col_type { @@ -412,9 +413,41 @@ where { 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::clone(enum_name), col_type), - None => expr.into(), + + match col_type { + #[cfg(all(feature = "with-json", feature = "postgres-array"))] + ColumnType::Json | ColumnType::JsonBinary => { + use sea_query::ArrayType; + use serde_json::Value as Json; + + #[allow(clippy::boxed_local)] + fn unbox(boxed: Box) -> T { + *boxed + } + + let expr = expr.into(); + match expr { + SimpleExpr::Value(Value::Array(ArrayType::Json, Some(json_vec))) => { + // flatten Array(Vec) into Json + let json_vec: Vec = json_vec + .into_iter() + .filter_map(|val| match val { + Value::Json(Some(json)) => Some(unbox(json)), + _ => None, + }) + .collect(); + SimpleExpr::Value(Value::Json(Some(Box::new(json_vec.into())))) + } + SimpleExpr::Value(Value::Array(ArrayType::Json, None)) => { + SimpleExpr::Value(Value::Json(None)) + } + _ => expr, + } + } + _ => match col_type.get_enum_name() { + Some(enum_name) => f(expr, SeaRc::clone(enum_name), col_type), + None => expr.into(), + }, } } diff --git a/tests/active_enum_tests.rs b/tests/active_enum_tests.rs index 843d51058..547532052 100644 --- a/tests/active_enum_tests.rs +++ b/tests/active_enum_tests.rs @@ -23,12 +23,12 @@ async fn main() -> Result<(), DbErr> { insert_active_enum(&ctx.db).await?; insert_active_enum_child(&ctx.db).await?; - if cfg!(feature = "sqlx-postgres") { - insert_active_enum_vec(&ctx.db).await?; - } + #[cfg(feature = "sqlx-postgres")] + insert_active_enum_vec(&ctx.db).await?; find_related_active_enum(&ctx.db).await?; find_linked_active_enum(&ctx.db).await?; + ctx.delete().await; Ok(()) diff --git a/tests/common/features/json_vec_derive.rs b/tests/common/features/json_vec_derive.rs index dcf16a739..58eb3ed4f 100644 --- a/tests/common/features/json_vec_derive.rs +++ b/tests/common/features/json_vec_derive.rs @@ -4,35 +4,27 @@ pub mod json_string_vec { use serde::{Deserialize, Serialize}; #[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel)] - #[sea_orm(table_name = "json_vec")] + #[sea_orm(table_name = "json_string_vec")] pub struct Model { #[sea_orm(primary_key)] pub id: i32, pub str_vec: Option, } + #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, FromJsonQueryResult)] + pub struct StringVec(pub Vec); + #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] pub enum Relation {} impl ActiveModelBehavior for ActiveModel {} - - #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, FromJsonQueryResult)] - pub struct StringVec(pub Vec); } pub mod json_struct_vec { use sea_orm::entity::prelude::*; use sea_orm_macros::FromJsonQueryResult; - use sea_query::with_array::NotU8; use serde::{Deserialize, Serialize}; - #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, FromJsonQueryResult)] - pub struct JsonColumn { - pub value: String, - } - - impl NotU8 for JsonColumn {} - #[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel)] #[sea_orm(table_name = "json_struct_vec")] pub struct Model { @@ -42,6 +34,11 @@ pub mod json_struct_vec { pub struct_vec: Vec, } + #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, FromJsonQueryResult)] + pub struct JsonColumn { + pub value: String, + } + #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] pub enum Relation {} diff --git a/tests/common/features/mod.rs b/tests/common/features/mod.rs index f60950f86..3d4dee9a5 100644 --- a/tests/common/features/mod.rs +++ b/tests/common/features/mod.rs @@ -44,6 +44,7 @@ pub use event_trigger::Entity as EventTrigger; pub use insert_default::Entity as InsertDefault; pub use json_struct::Entity as JsonStruct; pub use json_vec::Entity as JsonVec; +pub use json_vec_derive::json_string_vec::Entity as JsonStringVec; pub use json_vec_derive::json_struct_vec::Entity as JsonStructVec; pub use metadata::Entity as Metadata; pub use pi::Entity as Pi; diff --git a/tests/common/features/schema.rs b/tests/common/features/schema.rs index dbb5551df..0fb15cf02 100644 --- a/tests/common/features/schema.rs +++ b/tests/common/features/schema.rs @@ -1,5 +1,4 @@ use super::*; -use crate::common::features::json_vec_derive::json_struct_vec; use crate::common::setup::{create_enum, create_table, create_table_without_asserts}; use sea_orm::{ error::*, sea_query, ConnectionTrait, DatabaseConnection, DbBackend, DbConn, EntityName, @@ -20,7 +19,6 @@ pub async fn create_tables(db: &DatabaseConnection) -> Result<(), DbErr> { create_byte_primary_key_table(db).await?; create_satellites_table(db).await?; create_transaction_log_table(db).await?; - create_json_struct_table(db).await?; let create_enum_stmts = match db_backend { DbBackend::MySql | DbBackend::Sqlite => Vec::new(), @@ -51,12 +49,15 @@ pub async fn create_tables(db: &DatabaseConnection) -> Result<(), DbErr> { create_dyn_table_name_lazy_static_table(db).await?; create_value_type_table(db).await?; + create_json_vec_table(db).await?; + create_json_struct_table(db).await?; + create_json_string_vec_table(db).await?; + create_json_struct_vec_table(db).await?; + if DbBackend::Postgres == db_backend { create_value_type_postgres_table(db).await?; create_collection_table(db).await?; create_event_trigger_table(db).await?; - create_json_vec_table(db).await?; - create_json_struct_vec_table(db).await?; create_categories_table(db).await?; } @@ -323,46 +324,62 @@ pub async fn create_json_vec_table(db: &DbConn) -> Result { create_table(db, &create_table_stmt, JsonVec).await } -pub async fn create_json_struct_vec_table(db: &DbConn) -> Result { - let create_table_stmt = sea_query::Table::create() - .table(json_struct_vec::Entity.table_ref()) +pub async fn create_json_struct_table(db: &DbConn) -> Result { + let stmt = sea_query::Table::create() + .table(json_struct::Entity) .col( - ColumnDef::new(json_struct_vec::Column::Id) + ColumnDef::new(json_struct::Column::Id) .integer() .not_null() .auto_increment() .primary_key(), ) + .col(ColumnDef::new(json_struct::Column::Json).json().not_null()) .col( - ColumnDef::new(json_struct_vec::Column::StructVec) - .json_binary() + ColumnDef::new(json_struct::Column::JsonValue) + .json() .not_null(), ) + .col(ColumnDef::new(json_struct::Column::JsonValueOpt).json()) .to_owned(); - create_table(db, &create_table_stmt, JsonStructVec).await + create_table(db, &stmt, JsonStruct).await } -pub async fn create_json_struct_table(db: &DbConn) -> Result { - let stmt = sea_query::Table::create() - .table(json_struct::Entity) +pub async fn create_json_string_vec_table(db: &DbConn) -> Result { + let create_table_stmt = sea_query::Table::create() + .table(JsonStringVec.table_ref()) .col( - ColumnDef::new(json_struct::Column::Id) + ColumnDef::new(json_vec_derive::json_string_vec::Column::Id) .integer() .not_null() .auto_increment() .primary_key(), ) - .col(ColumnDef::new(json_struct::Column::Json).json().not_null()) + .col(ColumnDef::new(json_vec_derive::json_string_vec::Column::StrVec).json()) + .to_owned(); + + create_table(db, &create_table_stmt, JsonStringVec).await +} + +pub async fn create_json_struct_vec_table(db: &DbConn) -> Result { + let create_table_stmt = sea_query::Table::create() + .table(JsonStructVec.table_ref()) .col( - ColumnDef::new(json_struct::Column::JsonValue) - .json() + ColumnDef::new(json_vec_derive::json_struct_vec::Column::Id) + .integer() + .not_null() + .auto_increment() + .primary_key(), + ) + .col( + ColumnDef::new(json_vec_derive::json_struct_vec::Column::StructVec) + .json_binary() .not_null(), ) - .col(ColumnDef::new(json_struct::Column::JsonValueOpt).json()) .to_owned(); - create_table(db, &stmt, JsonStruct).await + create_table(db, &create_table_stmt, JsonStructVec).await } pub async fn create_collection_table(db: &DbConn) -> Result { diff --git a/tests/common/setup/mod.rs b/tests/common/setup/mod.rs index bd4d38bf5..2cdc00d21 100644 --- a/tests/common/setup/mod.rs +++ b/tests/common/setup/mod.rs @@ -1,7 +1,8 @@ use pretty_assertions::assert_eq; use sea_orm::{ - ColumnTrait, ColumnType, ConnectionTrait, Database, DatabaseBackend, DatabaseConnection, - DbBackend, DbConn, DbErr, EntityTrait, ExecResult, Iterable, Schema, Statement, + ColumnTrait, ColumnType, ConnectOptions, ConnectionTrait, Database, DatabaseBackend, + DatabaseConnection, DbBackend, DbConn, DbErr, EntityTrait, ExecResult, Iterable, Schema, + Statement, }; use sea_query::{ extension::postgres::{Type, TypeCreateStatement}, @@ -48,7 +49,9 @@ pub async fn setup(base_url: &str, db_name: &str) -> DatabaseConnection { let url = format!("{base_url}/{db_name}"); Database::connect(&url).await.unwrap() } else { - Database::connect(base_url).await.unwrap() + let mut options: ConnectOptions = base_url.into(); + options.sqlx_logging(false); + Database::connect(options).await.unwrap() } } diff --git a/tests/json_vec_tests.rs b/tests/json_vec_tests.rs index ab5bab2ee..f92cb6732 100644 --- a/tests/json_vec_tests.rs +++ b/tests/json_vec_tests.rs @@ -5,13 +5,17 @@ use pretty_assertions::assert_eq; use sea_orm::{entity::prelude::*, entity::*, DatabaseConnection}; #[sea_orm_macros::test] -#[cfg(feature = "sqlx-postgres")] +#[cfg(any( + feature = "sqlx-mysql", + feature = "sqlx-sqlite", + feature = "sqlx-postgres" +))] async fn main() -> Result<(), DbErr> { let ctx = TestContext::new("json_vec_tests").await; create_tables(&ctx.db).await?; insert_json_vec(&ctx.db).await?; - insert_json_string_vec_derive(&ctx.db).await?; + insert_json_struct_vec_derive(&ctx.db).await?; ctx.delete().await;