Merge branch 'master' into last-insert-id
This commit is contained in:
commit
8caed80fd7
1
.github/workflows/rust.yml
vendored
1
.github/workflows/rust.yml
vendored
@ -5,6 +5,7 @@ on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
- 0.2.x
|
||||
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
|
10
CHANGELOG.md
10
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<Date> 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
|
||||
|
@ -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 <tyt2y7@gmail.com>"]
|
||||
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 }
|
||||
|
@ -3,7 +3,7 @@
|
||||
|
||||
[package]
|
||||
name = "sea-orm-cli"
|
||||
version = "0.2.4"
|
||||
version = "0.2.5"
|
||||
authors = [ "Billy Chan <ccw.billy.123@gmail.com>" ]
|
||||
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",
|
||||
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "sea-orm-codegen"
|
||||
version = "0.2.4"
|
||||
version = "0.2.5"
|
||||
authors = ["Billy Chan <ccw.billy.123@gmail.com>"]
|
||||
edition = "2018"
|
||||
description = "Code Generator for SeaORM"
|
||||
|
@ -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<u8>", "bool",
|
||||
"String",
|
||||
"String",
|
||||
"i8",
|
||||
"i16",
|
||||
"i32",
|
||||
"i64",
|
||||
"f32",
|
||||
"f64",
|
||||
"Vec<u8>",
|
||||
"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();
|
||||
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "sea-orm-macros"
|
||||
version = "0.2.4"
|
||||
version = "0.2.5"
|
||||
authors = [ "Billy Chan <ccw.billy.123@gmail.com>" ]
|
||||
edition = "2018"
|
||||
description = "Derive macros for SeaORM"
|
||||
|
@ -163,4 +163,4 @@
|
||||
//! },
|
||||
//! )
|
||||
//! }
|
||||
//! ```
|
||||
//! ```
|
||||
|
@ -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(())
|
||||
|
@ -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),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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<Self, TryGetError> {
|
||||
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::<Option<$type>, _>(column.as_str())
|
||||
row.try_get::<Option<$type>, _>(_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::<Option<$type>, _>(column.as_str())
|
||||
row.try_get::<Option<$type>, _>(_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<Self, TryGetError> {
|
||||
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::<Option<$type>, _>(column.as_str())
|
||||
row.try_get::<Option<$type>, _>(_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<Self, TryGetError> {
|
||||
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::<Option<$type>, _>(column.as_str())
|
||||
row.try_get::<Option<$type>, _>(_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 {
|
||||
|
@ -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![]
|
||||
/// )]
|
||||
/// );
|
||||
|
@ -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<Output = Result<UpdateResult, DbErr>> + '_ {
|
||||
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<A>(
|
||||
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<UpdateResult, DbErr> {
|
||||
async fn exec_update(
|
||||
statement: Statement,
|
||||
db: &DatabaseConnection,
|
||||
check_record_exists: bool,
|
||||
) -> Result<UpdateResult, DbErr> {
|
||||
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(())
|
||||
}
|
||||
}
|
||||
|
@ -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::*;
|
||||
|
@ -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};
|
||||
|
112
src/query/util.rs
Normal file
112
src/query/util.rs
Normal file
@ -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();
|
||||
};
|
||||
}
|
@ -10,8 +10,8 @@ pub struct Model {
|
||||
pub key: String,
|
||||
pub value: String,
|
||||
pub bytes: Vec<u8>,
|
||||
pub date: Date,
|
||||
pub time: Time,
|
||||
pub date: Option<Date>,
|
||||
pub time: Option<Time>,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
|
||||
|
@ -287,8 +287,8 @@ pub async fn create_metadata_table(db: &DbConn) -> Result<ExecResult, DbErr> {
|
||||
.col(ColumnDef::new(metadata::Column::Key).string().not_null())
|
||||
.col(ColumnDef::new(metadata::Column::Value).string().not_null())
|
||||
.col(ColumnDef::new(metadata::Column::Bytes).binary().not_null())
|
||||
.col(ColumnDef::new(metadata::Column::Date).date().not_null())
|
||||
.col(ColumnDef::new(metadata::Column::Time).time().not_null())
|
||||
.col(ColumnDef::new(metadata::Column::Date).date())
|
||||
.col(ColumnDef::new(metadata::Column::Time).time())
|
||||
.to_owned();
|
||||
|
||||
create_table(db, &stmt, Metadata).await
|
||||
|
@ -1,5 +1,6 @@
|
||||
pub use super::*;
|
||||
use rust_decimal_macros::dec;
|
||||
use sea_orm::DbErr;
|
||||
use uuid::Uuid;
|
||||
|
||||
pub async fn test_update_cake(db: &DbConn) {
|
||||
@ -119,10 +120,14 @@ pub async fn test_update_deleted_customer(db: &DbConn) {
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let _customer_update_res: customer::ActiveModel = customer
|
||||
.update(db)
|
||||
.await
|
||||
.expect("could not update customer");
|
||||
let customer_update_res = customer.update(db).await;
|
||||
|
||||
assert_eq!(
|
||||
customer_update_res,
|
||||
Err(DbErr::RecordNotFound(
|
||||
"None of the database rows are affected".to_owned()
|
||||
))
|
||||
);
|
||||
|
||||
assert_eq!(Customer::find().count(db).await.unwrap(), init_n_customers);
|
||||
|
||||
|
@ -26,8 +26,8 @@ pub async fn crud_in_parallel(db: &DatabaseConnection) -> Result<(), DbErr> {
|
||||
key: "markup".to_owned(),
|
||||
value: "1.18".to_owned(),
|
||||
bytes: vec![1, 2, 3],
|
||||
date: Date::from_ymd(2021, 9, 27),
|
||||
time: Time::from_hms(11, 32, 55),
|
||||
date: Some(Date::from_ymd(2021, 9, 27)),
|
||||
time: Some(Time::from_hms(11, 32, 55)),
|
||||
},
|
||||
metadata::Model {
|
||||
uuid: Uuid::new_v4(),
|
||||
@ -35,8 +35,8 @@ pub async fn crud_in_parallel(db: &DatabaseConnection) -> Result<(), DbErr> {
|
||||
key: "exchange_rate".to_owned(),
|
||||
value: "0.78".to_owned(),
|
||||
bytes: vec![1, 2, 3],
|
||||
date: Date::from_ymd(2021, 9, 27),
|
||||
time: Time::from_hms(11, 32, 55),
|
||||
date: Some(Date::from_ymd(2021, 9, 27)),
|
||||
time: Some(Time::from_hms(11, 32, 55)),
|
||||
},
|
||||
metadata::Model {
|
||||
uuid: Uuid::new_v4(),
|
||||
@ -44,8 +44,8 @@ pub async fn crud_in_parallel(db: &DatabaseConnection) -> Result<(), DbErr> {
|
||||
key: "service_charge".to_owned(),
|
||||
value: "1.1".to_owned(),
|
||||
bytes: vec![1, 2, 3],
|
||||
date: Date::from_ymd(2021, 9, 27),
|
||||
time: Time::from_hms(11, 32, 55),
|
||||
date: None,
|
||||
time: None,
|
||||
},
|
||||
];
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
pub mod common;
|
||||
|
||||
pub use common::{bakery_chain::*, setup::*, TestContext};
|
||||
use sea_orm::{entity::prelude::*, DatabaseConnection, IntoActiveModel};
|
||||
use sea_orm::{entity::prelude::*, DatabaseConnection, IntoActiveModel, Set};
|
||||
|
||||
#[sea_orm_macros::test]
|
||||
#[cfg(any(
|
||||
@ -24,8 +24,8 @@ pub async fn create_metadata(db: &DatabaseConnection) -> Result<(), DbErr> {
|
||||
key: "markup".to_owned(),
|
||||
value: "1.18".to_owned(),
|
||||
bytes: vec![1, 2, 3],
|
||||
date: Date::from_ymd(2021, 9, 27),
|
||||
time: Time::from_hms(11, 32, 55),
|
||||
date: Some(Date::from_ymd(2021, 9, 27)),
|
||||
time: Some(Time::from_hms(11, 32, 55)),
|
||||
};
|
||||
|
||||
let res = Metadata::insert(metadata.clone().into_active_model())
|
||||
@ -36,5 +36,20 @@ pub async fn create_metadata(db: &DatabaseConnection) -> Result<(), DbErr> {
|
||||
|
||||
assert_eq!(res.last_insert_id, metadata.uuid);
|
||||
|
||||
let update_res = Metadata::update(metadata::ActiveModel {
|
||||
value: Set("0.22".to_owned()),
|
||||
..metadata.clone().into_active_model()
|
||||
})
|
||||
.filter(metadata::Column::Uuid.eq(Uuid::default()))
|
||||
.exec(db)
|
||||
.await;
|
||||
|
||||
assert_eq!(
|
||||
update_res,
|
||||
Err(DbErr::RecordNotFound(
|
||||
"None of the database rows are affected".to_owned()
|
||||
))
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user