diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 6b11432a..5a5e6e91 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -5,6 +5,7 @@ on: push: branches: - master + - 0.2.x env: CARGO_TERM_COLOR: always diff --git a/CHANGELOG.md b/CHANGELOG.md index 0398854a..693c1d0e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,16 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/) and this project adheres to [Semantic Versioning](http://semver.org/). +## 0.2.5 - 2021-10-06 + +- [[#227]] Resolve "Inserting actual none value of Option results in panic" +- [[#219]] [sea-orm-cli] Add `--tables` option +- [[#189]] Add `debug_query` and `debug_query_stmt` macro + +[#227]: https://github.com/SeaQL/sea-orm/issues/227 +[#219]: https://github.com/SeaQL/sea-orm/pull/219 +[#189]: https://github.com/SeaQL/sea-orm/pull/189 + ## 0.2.4 - 2021-10-01 - [[#186]] [sea-orm-cli] Foreign key handling diff --git a/Cargo.toml b/Cargo.toml index a4466152..a8d7a8ca 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,7 +3,7 @@ members = [".", "sea-orm-macros", "sea-orm-codegen"] [package] name = "sea-orm" -version = "0.2.4" +version = "0.2.5" authors = ["Chris Tsang "] edition = "2018" description = "🐚 An async & dynamic ORM for Rust" @@ -29,8 +29,8 @@ futures = { version = "^0.3" } futures-util = { version = "^0.3" } log = { version = "^0.4", optional = true } rust_decimal = { version = "^1", optional = true } -sea-orm-macros = { version = "^0.2.4", path = "sea-orm-macros", optional = true } -sea-query = { version = "^0.16.5", features = ["thread-safe"] } +sea-orm-macros = { version = "^0.2.5", path = "sea-orm-macros", optional = true } +sea-query = { version = "^0.17.0", 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/sea-orm-cli/Cargo.toml b/sea-orm-cli/Cargo.toml index 4b1fc2cd..3c05d08b 100644 --- a/sea-orm-cli/Cargo.toml +++ b/sea-orm-cli/Cargo.toml @@ -3,7 +3,7 @@ [package] name = "sea-orm-cli" -version = "0.2.4" +version = "0.2.5" authors = [ "Billy Chan " ] edition = "2018" description = "Command line utility for SeaORM" @@ -21,7 +21,7 @@ path = "src/main.rs" clap = { version = "^2.33.3" } dotenv = { version = "^0.15" } async-std = { version = "^1.9", features = [ "attributes" ] } -sea-orm-codegen = { version = "^0.2.4", path = "../sea-orm-codegen" } +sea-orm-codegen = { version = "^0.2.5", path = "../sea-orm-codegen" } sea-schema = { version = "^0.2.9", default-features = false, features = [ "debug-print", "sqlx-mysql", diff --git a/sea-orm-codegen/Cargo.toml b/sea-orm-codegen/Cargo.toml index 46e81401..0e8fa624 100644 --- a/sea-orm-codegen/Cargo.toml +++ b/sea-orm-codegen/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sea-orm-codegen" -version = "0.2.4" +version = "0.2.5" authors = ["Billy Chan "] edition = "2018" description = "Code Generator for SeaORM" diff --git a/sea-orm-codegen/src/entity/column.rs b/sea-orm-codegen/src/entity/column.rs index 532f2e91..69eb0fb6 100644 --- a/sea-orm-codegen/src/entity/column.rs +++ b/sea-orm-codegen/src/entity/column.rs @@ -27,8 +27,6 @@ impl Column { ColumnType::Char(_) | ColumnType::String(_) | ColumnType::Text - | ColumnType::Time(_) - | ColumnType::Date | ColumnType::Custom(_) => "String", ColumnType::TinyInteger(_) => "i8", ColumnType::SmallInteger(_) => "i16", @@ -37,6 +35,8 @@ impl Column { ColumnType::Float(_) => "f32", ColumnType::Double(_) => "f64", ColumnType::Json | ColumnType::JsonBinary => "Json", + ColumnType::Date => "Date", + ColumnType::Time(_) => "Time", ColumnType::DateTime(_) | ColumnType::Timestamp(_) => "DateTime", ColumnType::TimestampWithTimeZone(_) => "DateTimeWithTimeZone", ColumnType::Decimal(_) | ColumnType::Money(_) => "Decimal", @@ -194,6 +194,11 @@ mod tests { make_col!("CAKE_FILLING_ID", ColumnType::Double(None)), make_col!("CAKE-FILLING-ID", ColumnType::Binary(None)), make_col!("CAKE", ColumnType::Boolean), + make_col!("date", ColumnType::Date), + make_col!("time", ColumnType::Time(None)), + make_col!("date_time", ColumnType::DateTime(None)), + make_col!("timestamp", ColumnType::Timestamp(None)), + make_col!("timestamp_tz", ColumnType::TimestampWithTimeZone(None)), ] } @@ -211,6 +216,11 @@ mod tests { "cake_filling_id", "cake_filling_id", "cake", + "date", + "time", + "date_time", + "timestamp", + "timestamp_tz", ]; for (col, snack_case) in columns.into_iter().zip(snack_cases) { assert_eq!(col.get_name_snake_case().to_string(), snack_case); @@ -231,6 +241,11 @@ mod tests { "CakeFillingId", "CakeFillingId", "Cake", + "Date", + "Time", + "DateTime", + "Timestamp", + "TimestampTz", ]; for (col, camel_case) in columns.into_iter().zip(camel_cases) { assert_eq!(col.get_name_camel_case().to_string(), camel_case); @@ -241,7 +256,21 @@ mod tests { fn test_get_rs_type() { let columns = setup(); let rs_types = vec![ - "String", "String", "i8", "i16", "i32", "i64", "f32", "f64", "Vec", "bool", + "String", + "String", + "i8", + "i16", + "i32", + "i64", + "f32", + "f64", + "Vec", + "bool", + "Date", + "Time", + "DateTime", + "DateTime", + "DateTimeWithTimeZone", ]; for (mut col, rs_type) in columns.into_iter().zip(rs_types) { let rs_type: TokenStream = rs_type.parse().unwrap(); @@ -271,6 +300,11 @@ mod tests { "ColumnType::Double.def()", "ColumnType::Binary.def()", "ColumnType::Boolean.def()", + "ColumnType::Date.def()", + "ColumnType::Time.def()", + "ColumnType::DateTime.def()", + "ColumnType::Timestamp.def()", + "ColumnType::TimestampWithTimeZone.def()", ]; for (mut col, col_def) in columns.into_iter().zip(col_defs) { let mut col_def: TokenStream = col_def.parse().unwrap(); diff --git a/sea-orm-macros/Cargo.toml b/sea-orm-macros/Cargo.toml index e37c8053..22b58c01 100644 --- a/sea-orm-macros/Cargo.toml +++ b/sea-orm-macros/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sea-orm-macros" -version = "0.2.4" +version = "0.2.5" authors = [ "Billy Chan " ] edition = "2018" description = "Derive macros for SeaORM" diff --git a/src/docs.rs b/src/docs.rs index bab054ef..4d1226c3 100644 --- a/src/docs.rs +++ b/src/docs.rs @@ -163,4 +163,4 @@ //! }, //! ) //! } -//! ``` \ No newline at end of file +//! ``` diff --git a/src/entity/model.rs b/src/entity/model.rs index 318b8e70..cd96b22e 100644 --- a/src/entity/model.rs +++ b/src/entity/model.rs @@ -69,12 +69,10 @@ pub trait FromQueryResult: Sized { /// /// assert_eq!( /// res, - /// vec![ - /// SelectResult { - /// name: "Chocolate Forest".to_owned(), - /// num_of_cakes: 2, - /// }, - /// ] + /// vec![SelectResult { + /// name: "Chocolate Forest".to_owned(), + /// num_of_cakes: 2, + /// },] /// ); /// # /// # Ok(()) diff --git a/src/error.rs b/src/error.rs index 09f80b0a..f8aff775 100644 --- a/src/error.rs +++ b/src/error.rs @@ -3,6 +3,7 @@ pub enum DbErr { Conn(String), Exec(String), Query(String), + RecordNotFound(String), } impl std::error::Error for DbErr {} @@ -13,6 +14,7 @@ impl std::fmt::Display for DbErr { Self::Conn(s) => write!(f, "Connection Error: {}", s), Self::Exec(s) => write!(f, "Execution Error: {}", s), Self::Query(s) => write!(f, "Query Error: {}", s), + Self::RecordNotFound(s) => write!(f, "RecordNotFound Error: {}", s), } } } diff --git a/src/executor/query.rs b/src/executor/query.rs index 0248fa5c..a164e911 100644 --- a/src/executor/query.rs +++ b/src/executor/query.rs @@ -126,12 +126,12 @@ macro_rules! try_getable_unsigned { ( $type: ty ) => { impl TryGetable for $type { fn try_get(res: &QueryResult, pre: &str, col: &str) -> Result { - let column = format!("{}{}", pre, col); + let _column = format!("{}{}", pre, col); match &res.row { #[cfg(feature = "sqlx-mysql")] QueryResultRow::SqlxMySql(row) => { use sqlx::Row; - row.try_get::, _>(column.as_str()) + row.try_get::, _>(_column.as_str()) .map_err(|e| TryGetError::DbErr(crate::sqlx_error_to_query_err(e))) .and_then(|opt| opt.ok_or(TryGetError::Null)) } @@ -142,13 +142,13 @@ macro_rules! try_getable_unsigned { #[cfg(feature = "sqlx-sqlite")] QueryResultRow::SqlxSqlite(row) => { use sqlx::Row; - row.try_get::, _>(column.as_str()) + row.try_get::, _>(_column.as_str()) .map_err(|e| TryGetError::DbErr(crate::sqlx_error_to_query_err(e))) .and_then(|opt| opt.ok_or(TryGetError::Null)) } #[cfg(feature = "mock")] #[allow(unused_variables)] - QueryResultRow::Mock(row) => row.try_get(column.as_str()).map_err(|e| { + QueryResultRow::Mock(row) => row.try_get(_column.as_str()).map_err(|e| { debug_print!("{:#?}", e.to_string()); TryGetError::Null }), @@ -162,12 +162,12 @@ macro_rules! try_getable_mysql { ( $type: ty ) => { impl TryGetable for $type { fn try_get(res: &QueryResult, pre: &str, col: &str) -> Result { - let column = format!("{}{}", pre, col); + let _column = format!("{}{}", pre, col); match &res.row { #[cfg(feature = "sqlx-mysql")] QueryResultRow::SqlxMySql(row) => { use sqlx::Row; - row.try_get::, _>(column.as_str()) + row.try_get::, _>(_column.as_str()) .map_err(|e| TryGetError::DbErr(crate::sqlx_error_to_query_err(e))) .and_then(|opt| opt.ok_or(TryGetError::Null)) } @@ -181,7 +181,7 @@ macro_rules! try_getable_mysql { } #[cfg(feature = "mock")] #[allow(unused_variables)] - QueryResultRow::Mock(row) => row.try_get(column.as_str()).map_err(|e| { + QueryResultRow::Mock(row) => row.try_get(_column.as_str()).map_err(|e| { debug_print!("{:#?}", e.to_string()); TryGetError::Null }), @@ -195,7 +195,7 @@ macro_rules! try_getable_postgres { ( $type: ty ) => { impl TryGetable for $type { fn try_get(res: &QueryResult, pre: &str, col: &str) -> Result { - let column = format!("{}{}", pre, col); + let _column = format!("{}{}", pre, col); match &res.row { #[cfg(feature = "sqlx-mysql")] QueryResultRow::SqlxMySql(_) => { @@ -204,7 +204,7 @@ macro_rules! try_getable_postgres { #[cfg(feature = "sqlx-postgres")] QueryResultRow::SqlxPostgres(row) => { use sqlx::Row; - row.try_get::, _>(column.as_str()) + row.try_get::, _>(_column.as_str()) .map_err(|e| TryGetError::DbErr(crate::sqlx_error_to_query_err(e))) .and_then(|opt| opt.ok_or(TryGetError::Null)) } @@ -214,7 +214,7 @@ macro_rules! try_getable_postgres { } #[cfg(feature = "mock")] #[allow(unused_variables)] - QueryResultRow::Mock(row) => row.try_get(column.as_str()).map_err(|e| { + QueryResultRow::Mock(row) => row.try_get(_column.as_str()).map_err(|e| { debug_print!("{:#?}", e.to_string()); TryGetError::Null }), @@ -326,7 +326,7 @@ pub trait TryGetableMany: Sized { /// # ]]) /// # .into_connection(); /// # - /// use sea_orm::{entity::*, query::*, tests_cfg::cake, EnumIter, DeriveIden, TryGetableMany}; + /// use sea_orm::{entity::*, query::*, tests_cfg::cake, DeriveIden, EnumIter, TryGetableMany}; /// /// #[derive(EnumIter, DeriveIden)] /// enum ResultCol { diff --git a/src/executor/select.rs b/src/executor/select.rs index bb386722..0db698f0 100644 --- a/src/executor/select.rs +++ b/src/executor/select.rs @@ -207,10 +207,7 @@ where /// .all(&db) /// .await?; /// - /// assert_eq!( - /// res, - /// vec![("Chocolate Forest".to_owned(), 2i64)] - /// ); + /// assert_eq!(res, vec![("Chocolate Forest".to_owned(), 2i64)]); /// # /// # Ok(()) /// # }); @@ -222,7 +219,9 @@ where /// vec![ /// r#"SELECT "cake"."name" AS "cake_name", COUNT("cake"."id") AS "num_of_cakes""#, /// r#"FROM "cake" GROUP BY "cake"."name""#, - /// ].join(" ").as_str(), + /// ] + /// .join(" ") + /// .as_str(), /// vec![] /// )] /// ); diff --git a/src/executor/update.rs b/src/executor/update.rs index 6c7a9873..b564165c 100644 --- a/src/executor/update.rs +++ b/src/executor/update.rs @@ -7,9 +7,10 @@ use std::future::Future; #[derive(Clone, Debug)] pub struct Updater { query: UpdateStatement, + check_record_exists: bool, } -#[derive(Clone, Debug)] +#[derive(Clone, Debug, PartialEq)] pub struct UpdateResult { pub rows_affected: u64, } @@ -39,7 +40,15 @@ where impl Updater { pub fn new(query: UpdateStatement) -> Self { - Self { query } + Self { + query, + check_record_exists: false, + } + } + + pub fn check_record_exists(mut self) -> Self { + self.check_record_exists = true; + self } pub fn exec( @@ -47,7 +56,7 @@ impl Updater { db: &DatabaseConnection, ) -> impl Future> + '_ { let builder = db.get_database_backend(); - exec_update(builder.build(&self.query), db) + exec_update(builder.build(&self.query), db, self.check_record_exists) } } @@ -66,14 +75,160 @@ async fn exec_update_and_return_original( where A: ActiveModelTrait, { - Updater::new(query).exec(db).await?; + Updater::new(query).check_record_exists().exec(db).await?; Ok(model) } // Only Statement impl Send -async fn exec_update(statement: Statement, db: &DatabaseConnection) -> Result { +async fn exec_update( + statement: Statement, + db: &DatabaseConnection, + check_record_exists: bool, +) -> Result { let result = db.execute(statement).await?; + if check_record_exists && result.rows_affected() == 0 { + return Err(DbErr::RecordNotFound( + "None of the database rows are affected".to_owned(), + )); + } Ok(UpdateResult { rows_affected: result.rows_affected(), }) } + +#[cfg(test)] +mod tests { + use crate::{entity::prelude::*, tests_cfg::*, *}; + use pretty_assertions::assert_eq; + use sea_query::Expr; + + #[smol_potat::test] + async fn update_record_not_found_1() -> Result<(), DbErr> { + let db = MockDatabase::new(DbBackend::Postgres) + .append_exec_results(vec![ + MockExecResult { + last_insert_id: 0, + rows_affected: 1, + }, + MockExecResult { + last_insert_id: 0, + rows_affected: 0, + }, + MockExecResult { + last_insert_id: 0, + rows_affected: 0, + }, + MockExecResult { + last_insert_id: 0, + rows_affected: 0, + }, + MockExecResult { + last_insert_id: 0, + rows_affected: 0, + }, + ]) + .into_connection(); + + let model = cake::Model { + id: 1, + name: "New York Cheese".to_owned(), + }; + + assert_eq!( + cake::ActiveModel { + name: Set("Cheese Cake".to_owned()), + ..model.into_active_model() + } + .update(&db) + .await?, + cake::Model { + id: 1, + name: "Cheese Cake".to_owned(), + } + .into_active_model() + ); + + let model = cake::Model { + id: 2, + name: "New York Cheese".to_owned(), + }; + + assert_eq!( + cake::ActiveModel { + name: Set("Cheese Cake".to_owned()), + ..model.clone().into_active_model() + } + .update(&db) + .await, + Err(DbErr::RecordNotFound( + "None of the database rows are affected".to_owned() + )) + ); + + assert_eq!( + cake::Entity::update(cake::ActiveModel { + name: Set("Cheese Cake".to_owned()), + ..model.clone().into_active_model() + }) + .exec(&db) + .await, + Err(DbErr::RecordNotFound( + "None of the database rows are affected".to_owned() + )) + ); + + assert_eq!( + Update::one(cake::ActiveModel { + name: Set("Cheese Cake".to_owned()), + ..model.into_active_model() + }) + .exec(&db) + .await, + Err(DbErr::RecordNotFound( + "None of the database rows are affected".to_owned() + )) + ); + + assert_eq!( + Update::many(cake::Entity) + .col_expr(cake::Column::Name, Expr::value("Cheese Cake".to_owned())) + .filter(cake::Column::Id.eq(2)) + .exec(&db) + .await, + Ok(UpdateResult { rows_affected: 0 }) + ); + + assert_eq!( + db.into_transaction_log(), + vec![ + Transaction::from_sql_and_values( + DbBackend::Postgres, + r#"UPDATE "cake" SET "name" = $1 WHERE "cake"."id" = $2"#, + vec!["Cheese Cake".into(), 1i32.into()] + ), + Transaction::from_sql_and_values( + DbBackend::Postgres, + r#"UPDATE "cake" SET "name" = $1 WHERE "cake"."id" = $2"#, + vec!["Cheese Cake".into(), 2i32.into()] + ), + Transaction::from_sql_and_values( + DbBackend::Postgres, + r#"UPDATE "cake" SET "name" = $1 WHERE "cake"."id" = $2"#, + vec!["Cheese Cake".into(), 2i32.into()] + ), + Transaction::from_sql_and_values( + DbBackend::Postgres, + r#"UPDATE "cake" SET "name" = $1 WHERE "cake"."id" = $2"#, + vec!["Cheese Cake".into(), 2i32.into()] + ), + Transaction::from_sql_and_values( + DbBackend::Postgres, + r#"UPDATE "cake" SET "name" = $1 WHERE "cake"."id" = $2"#, + vec!["Cheese Cake".into(), 2i32.into()] + ), + ] + ); + + Ok(()) + } +} diff --git a/src/lib.rs b/src/lib.rs index 910044a5..6ddc442c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -265,6 +265,7 @@ )] mod database; +mod docs; mod driver; pub mod entity; pub mod error; @@ -273,7 +274,6 @@ pub mod query; pub mod schema; #[doc(hidden)] pub mod tests_cfg; -mod docs; mod util; pub use database::*; diff --git a/src/query/mod.rs b/src/query/mod.rs index 54cc12dd..5d2be142 100644 --- a/src/query/mod.rs +++ b/src/query/mod.rs @@ -8,6 +8,7 @@ mod json; mod select; mod traits; mod update; +mod util; pub use combine::{SelectA, SelectB}; pub use delete::*; @@ -19,5 +20,6 @@ pub use json::*; pub use select::*; pub use traits::*; pub use update::*; +pub use util::*; pub use crate::{InsertResult, Statement, UpdateResult, Value, Values}; diff --git a/src/query/util.rs b/src/query/util.rs new file mode 100644 index 00000000..545f8376 --- /dev/null +++ b/src/query/util.rs @@ -0,0 +1,112 @@ +use crate::{database::*, QueryTrait, Statement}; + +#[derive(Debug)] +pub struct DebugQuery<'a, Q, T> { + pub query: &'a Q, + pub value: T, +} + +macro_rules! debug_query_build { + ($impl_obj:ty, $db_expr:expr) => { + impl<'a, Q> DebugQuery<'a, Q, $impl_obj> + where + Q: QueryTrait, + { + pub fn build(&self) -> Statement { + let func = $db_expr; + let db_backend = func(self); + self.query.build(db_backend) + } + } + }; +} + +debug_query_build!(DbBackend, |x: &DebugQuery<_, DbBackend>| x.value); +debug_query_build!(&DbBackend, |x: &DebugQuery<_, &DbBackend>| *x.value); +debug_query_build!( + DatabaseConnection, + |x: &DebugQuery<_, DatabaseConnection>| x.value.get_database_backend() +); +debug_query_build!( + &DatabaseConnection, + |x: &DebugQuery<_, &DatabaseConnection>| x.value.get_database_backend() +); + +/// Helper to get a `Statement` from an object that impl `QueryTrait`. +/// +/// # Example +/// +/// ``` +/// # #[cfg(feature = "mock")] +/// # use sea_orm::{error::*, tests_cfg::*, MockDatabase, MockExecResult, DbBackend}; +/// # +/// # let conn = MockDatabase::new(DbBackend::Postgres) +/// # .into_connection(); +/// # +/// use sea_orm::{entity::*, query::*, tests_cfg::cake, debug_query_stmt}; +/// +/// let c = cake::Entity::insert( +/// cake::ActiveModel { +/// id: ActiveValue::set(1), +/// name: ActiveValue::set("Apple Pie".to_owned()), +/// }); +/// +/// let raw_sql = debug_query_stmt!(&c, &conn).to_string(); +/// assert_eq!(raw_sql, r#"INSERT INTO "cake" ("id", "name") VALUES (1, 'Apple Pie')"#); +/// +/// let raw_sql = debug_query_stmt!(&c, conn).to_string(); +/// assert_eq!(raw_sql, r#"INSERT INTO "cake" ("id", "name") VALUES (1, 'Apple Pie')"#); +/// +/// let raw_sql = debug_query_stmt!(&c, DbBackend::MySql).to_string(); +/// assert_eq!(raw_sql, r#"INSERT INTO `cake` (`id`, `name`) VALUES (1, 'Apple Pie')"#); +/// +/// let raw_sql = debug_query_stmt!(&c, &DbBackend::MySql).to_string(); +/// assert_eq!(raw_sql, r#"INSERT INTO `cake` (`id`, `name`) VALUES (1, 'Apple Pie')"#); +/// +/// ``` +#[macro_export] +macro_rules! debug_query_stmt { + ($query:expr,$value:expr) => { + $crate::DebugQuery { + query: $query, + value: $value, + } + .build(); + }; +} + +/// Helper to get a raw SQL string from an object that impl `QueryTrait`. +/// +/// # Example +/// +/// ``` +/// # #[cfg(feature = "mock")] +/// # use sea_orm::{error::*, tests_cfg::*, MockDatabase, MockExecResult, DbBackend}; +/// # +/// # let conn = MockDatabase::new(DbBackend::Postgres) +/// # .into_connection(); +/// # +/// use sea_orm::{entity::*, query::*, tests_cfg::cake,debug_query}; +/// +/// let c = cake::Entity::insert( +/// cake::ActiveModel { +/// id: ActiveValue::set(1), +/// name: ActiveValue::set("Apple Pie".to_owned()), +/// }); +/// +/// let raw_sql = debug_query!(&c, &conn); +/// assert_eq!(raw_sql, r#"INSERT INTO "cake" ("id", "name") VALUES (1, 'Apple Pie')"#); +/// +/// let raw_sql = debug_query!(&c, conn); +/// assert_eq!(raw_sql, r#"INSERT INTO "cake" ("id", "name") VALUES (1, 'Apple Pie')"#); +/// +/// let raw_sql = debug_query!(&c, DbBackend::Sqlite); +/// assert_eq!(raw_sql, r#"INSERT INTO `cake` (`id`, `name`) VALUES (1, 'Apple Pie')"#); +/// +/// ``` +#[macro_export] +macro_rules! debug_query { + ($query:expr,$value:expr) => { + $crate::debug_query_stmt!($query, $value).to_string(); + }; +} diff --git a/tests/common/bakery_chain/metadata.rs b/tests/common/bakery_chain/metadata.rs index de513a22..2c297cd3 100644 --- a/tests/common/bakery_chain/metadata.rs +++ b/tests/common/bakery_chain/metadata.rs @@ -10,8 +10,8 @@ pub struct Model { pub key: String, pub value: String, pub bytes: Vec, - pub date: Date, - pub time: Time, + pub date: Option, + pub time: Option