diff --git a/Cargo.toml b/Cargo.toml index 277a2a68..a2c0384f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -42,6 +42,7 @@ uuid = { version = "^1", features = ["serde", "v4"], optional = true } ouroboros = "0.15" url = "^2.2" once_cell = "1.8" +thiserror = "^1" [dev-dependencies] smol = { version = "^1.2" } diff --git a/issues/324/src/model.rs b/issues/324/src/model.rs index 9ec35a07..37174db9 100644 --- a/issues/324/src/model.rs +++ b/issues/324/src/model.rs @@ -26,10 +26,7 @@ macro_rules! impl_try_from_u64_err { ($newtype: ident) => { impl sea_orm::TryFromU64 for $newtype { fn try_from_u64(_n: u64) -> Result { - Err(sea_orm::DbErr::Exec(format!( - "{} cannot be converted from u64", - stringify!($newtype) - ))) + Err(sea_orm::ConvertFromU64(stringify!($newtype))) } } }; diff --git a/issues/400/src/model.rs b/issues/400/src/model.rs index a53eeb74..6d9044c4 100644 --- a/issues/400/src/model.rs +++ b/issues/400/src/model.rs @@ -1,5 +1,5 @@ -use std::marker::PhantomData; use sea_orm::entity::prelude::*; +use std::marker::PhantomData; #[derive(Clone, Debug, PartialEq, DeriveEntityModel)] #[sea_orm(table_name = "model")] @@ -31,10 +31,7 @@ impl From> for Uuid { impl sea_orm::TryFromU64 for AccountId { fn try_from_u64(_n: u64) -> Result { - Err(sea_orm::DbErr::Exec(format!( - "{} cannot be converted from u64", - stringify!(AccountId) - ))) + Err(sea_orm::ConvertFromU64(stringify!(AccountId))) } } diff --git a/src/database/mock.rs b/src/database/mock.rs index 7af7cdb5..a1dbc795 100644 --- a/src/database/mock.rs +++ b/src/database/mock.rs @@ -102,7 +102,7 @@ impl MockDatabaseTrait for MockDatabase { result: ExecResultHolder::Mock(std::mem::take(&mut self.exec_results[counter])), }) } else { - Err(DbErr::Exec("`exec_results` buffer is empty.".to_owned())) + Err(DbErr::Query("`exec_results` buffer is empty.".to_owned())) } } diff --git a/src/database/transaction.rs b/src/database/transaction.rs index cb1ad122..66ae25e5 100644 --- a/src/database/transaction.rs +++ b/src/database/transaction.rs @@ -238,6 +238,17 @@ impl DatabaseTransaction { } } } + + #[cfg(feature = "sqlx-dep")] + fn map_err_ignore_not_found( + err: Result, sqlx::Error>, + ) -> Result, DbErr> { + if let Err(sqlx::Error::RowNotFound) = err { + Ok(None) + } else { + err.map_err(|e| sqlx_error_to_query_err(e)) + } + } } impl Drop for DatabaseTransaction { @@ -258,13 +269,14 @@ impl ConnectionTrait for DatabaseTransaction { async fn execute(&self, stmt: Statement) -> Result { debug_print!("{}", stmt); - let _res = match &mut *self.conn.lock().await { + match &mut *self.conn.lock().await { #[cfg(feature = "sqlx-mysql")] InnerConnection::MySql(conn) => { let query = crate::driver::sqlx_mysql::sqlx_query(&stmt); crate::metric::metric!(self.metric_callback, &stmt, { query.execute(conn).await.map(Into::into) }) + .map_err(sqlx_error_to_exec_err) } #[cfg(feature = "sqlx-postgres")] InnerConnection::Postgres(conn) => { @@ -272,6 +284,7 @@ impl ConnectionTrait for DatabaseTransaction { crate::metric::metric!(self.metric_callback, &stmt, { query.execute(conn).await.map(Into::into) }) + .map_err(sqlx_error_to_exec_err) } #[cfg(feature = "sqlx-sqlite")] InnerConnection::Sqlite(conn) => { @@ -279,14 +292,13 @@ impl ConnectionTrait for DatabaseTransaction { crate::metric::metric!(self.metric_callback, &stmt, { query.execute(conn).await.map(Into::into) }) + .map_err(sqlx_error_to_exec_err) } #[cfg(feature = "mock")] InnerConnection::Mock(conn) => return conn.execute(stmt), #[allow(unreachable_patterns)] _ => unreachable!(), - }; - #[cfg(feature = "sqlx-dep")] - _res.map_err(sqlx_error_to_exec_err) + } } #[instrument(level = "trace")] @@ -294,32 +306,32 @@ impl ConnectionTrait for DatabaseTransaction { async fn query_one(&self, stmt: Statement) -> Result, DbErr> { debug_print!("{}", stmt); - let _res = match &mut *self.conn.lock().await { + match &mut *self.conn.lock().await { #[cfg(feature = "sqlx-mysql")] InnerConnection::MySql(conn) => { let query = crate::driver::sqlx_mysql::sqlx_query(&stmt); - query.fetch_one(conn).await.map(|row| Some(row.into())) + Self::map_err_ignore_not_found( + query.fetch_one(conn).await.map(|row| Some(row.into())), + ) } #[cfg(feature = "sqlx-postgres")] InnerConnection::Postgres(conn) => { let query = crate::driver::sqlx_postgres::sqlx_query(&stmt); - query.fetch_one(conn).await.map(|row| Some(row.into())) + Self::map_err_ignore_not_found( + query.fetch_one(conn).await.map(|row| Some(row.into())), + ) } #[cfg(feature = "sqlx-sqlite")] InnerConnection::Sqlite(conn) => { let query = crate::driver::sqlx_sqlite::sqlx_query(&stmt); - query.fetch_one(conn).await.map(|row| Some(row.into())) + Self::map_err_ignore_not_found( + query.fetch_one(conn).await.map(|row| Some(row.into())), + ) } #[cfg(feature = "mock")] InnerConnection::Mock(conn) => return conn.query_one(stmt), #[allow(unreachable_patterns)] _ => unreachable!(), - }; - #[cfg(feature = "sqlx-dep")] - if let Err(sqlx::Error::RowNotFound) = _res { - Ok(None) - } else { - _res.map_err(sqlx_error_to_query_err) } } @@ -328,7 +340,7 @@ impl ConnectionTrait for DatabaseTransaction { async fn query_all(&self, stmt: Statement) -> Result, DbErr> { debug_print!("{}", stmt); - let _res = match &mut *self.conn.lock().await { + match &mut *self.conn.lock().await { #[cfg(feature = "sqlx-mysql")] InnerConnection::MySql(conn) => { let query = crate::driver::sqlx_mysql::sqlx_query(&stmt); @@ -336,6 +348,7 @@ impl ConnectionTrait for DatabaseTransaction { .fetch_all(conn) .await .map(|rows| rows.into_iter().map(|r| r.into()).collect()) + .map_err(sqlx_error_to_query_err) } #[cfg(feature = "sqlx-postgres")] InnerConnection::Postgres(conn) => { @@ -344,6 +357,7 @@ impl ConnectionTrait for DatabaseTransaction { .fetch_all(conn) .await .map(|rows| rows.into_iter().map(|r| r.into()).collect()) + .map_err(sqlx_error_to_query_err) } #[cfg(feature = "sqlx-sqlite")] InnerConnection::Sqlite(conn) => { @@ -352,14 +366,13 @@ impl ConnectionTrait for DatabaseTransaction { .fetch_all(conn) .await .map(|rows| rows.into_iter().map(|r| r.into()).collect()) + .map_err(sqlx_error_to_query_err) } #[cfg(feature = "mock")] InnerConnection::Mock(conn) => return conn.query_all(stmt), #[allow(unreachable_patterns)] _ => unreachable!(), - }; - #[cfg(feature = "sqlx-dep")] - _res.map_err(sqlx_error_to_query_err) + } } } diff --git a/src/driver/sqlx_common.rs b/src/driver/sqlx_common.rs index 18ca059d..91509813 100644 --- a/src/driver/sqlx_common.rs +++ b/src/driver/sqlx_common.rs @@ -2,15 +2,15 @@ use crate::DbErr; /// Converts an [sqlx::error] execution error to a [DbErr] pub fn sqlx_error_to_exec_err(err: sqlx::Error) -> DbErr { - DbErr::Exec(err.to_string()) + DbErr::ExecSqlX(err) } /// Converts an [sqlx::error] query error to a [DbErr] pub fn sqlx_error_to_query_err(err: sqlx::Error) -> DbErr { - DbErr::Query(err.to_string()) + DbErr::QuerySqlX(err) } /// Converts an [sqlx::error] connection error to a [DbErr] pub fn sqlx_error_to_conn_err(err: sqlx::Error) -> DbErr { - DbErr::Conn(err.to_string()) + DbErr::ConnSqlX(err) } diff --git a/src/driver/sqlx_mysql.rs b/src/driver/sqlx_mysql.rs index 01b840dd..e1f1d416 100644 --- a/src/driver/sqlx_mysql.rs +++ b/src/driver/sqlx_mysql.rs @@ -45,7 +45,7 @@ impl SqlxMySqlConnector { let mut opt = options .url .parse::() - .map_err(|e| DbErr::Conn(e.to_string()))?; + .map_err(|e| DbErr::ConnSqlX(e))?; use sqlx::ConnectOptions; if !options.sqlx_logging { opt.disable_statement_logging(); @@ -89,9 +89,7 @@ impl SqlxMySqlPoolConnection { } }) } else { - Err(DbErr::Exec( - "Failed to acquire connection from pool.".to_owned(), - )) + Err(DbErr::ConnFromPool) } } @@ -107,14 +105,12 @@ impl SqlxMySqlPoolConnection { Ok(row) => Ok(Some(row.into())), Err(err) => match err { sqlx::Error::RowNotFound => Ok(None), - _ => Err(DbErr::Query(err.to_string())), + _ => Err(sqlx_error_to_query_err(err)), }, } }) } else { - Err(DbErr::Query( - "Failed to acquire connection from pool.".to_owned(), - )) + Err(DbErr::ConnFromPool) } } @@ -132,9 +128,7 @@ impl SqlxMySqlPoolConnection { } }) } else { - Err(DbErr::Query( - "Failed to acquire connection from pool.".to_owned(), - )) + Err(DbErr::ConnFromPool) } } @@ -150,9 +144,7 @@ impl SqlxMySqlPoolConnection { self.metric_callback.clone(), ))) } else { - Err(DbErr::Query( - "Failed to acquire connection from pool.".to_owned(), - )) + Err(DbErr::ConnFromPool) } } @@ -162,9 +154,7 @@ impl SqlxMySqlPoolConnection { if let Ok(conn) = self.pool.acquire().await { DatabaseTransaction::new_mysql(conn, self.metric_callback.clone()).await } else { - Err(DbErr::Query( - "Failed to acquire connection from pool.".to_owned(), - )) + Err(DbErr::ConnFromPool) } } @@ -185,9 +175,7 @@ impl SqlxMySqlPoolConnection { .map_err(|e| TransactionError::Connection(e))?; transaction.run(callback).await } else { - Err(TransactionError::Connection(DbErr::Query( - "Failed to acquire connection from pool.".to_owned(), - ))) + Err(TransactionError::Connection(DbErr::ConnFromPool)) } } diff --git a/src/driver/sqlx_postgres.rs b/src/driver/sqlx_postgres.rs index bd561148..6d070115 100644 --- a/src/driver/sqlx_postgres.rs +++ b/src/driver/sqlx_postgres.rs @@ -45,7 +45,7 @@ impl SqlxPostgresConnector { let mut opt = options .url .parse::() - .map_err(|e| DbErr::Conn(e.to_string()))?; + .map_err(|e| DbErr::ConnSqlX(e))?; use sqlx::ConnectOptions; if !options.sqlx_logging { opt.disable_statement_logging(); @@ -89,9 +89,7 @@ impl SqlxPostgresPoolConnection { } }) } else { - Err(DbErr::Exec( - "Failed to acquire connection from pool.".to_owned(), - )) + Err(DbErr::ConnFromPool) } } @@ -107,14 +105,12 @@ impl SqlxPostgresPoolConnection { Ok(row) => Ok(Some(row.into())), Err(err) => match err { sqlx::Error::RowNotFound => Ok(None), - _ => Err(DbErr::Query(err.to_string())), + _ => Err(sqlx_error_to_query_err(err)), }, } }) } else { - Err(DbErr::Query( - "Failed to acquire connection from pool.".to_owned(), - )) + Err(DbErr::ConnFromPool) } } @@ -132,9 +128,7 @@ impl SqlxPostgresPoolConnection { } }) } else { - Err(DbErr::Query( - "Failed to acquire connection from pool.".to_owned(), - )) + Err(DbErr::ConnFromPool) } } @@ -150,9 +144,7 @@ impl SqlxPostgresPoolConnection { self.metric_callback.clone(), ))) } else { - Err(DbErr::Query( - "Failed to acquire connection from pool.".to_owned(), - )) + Err(DbErr::ConnFromPool) } } @@ -162,9 +154,7 @@ impl SqlxPostgresPoolConnection { if let Ok(conn) = self.pool.acquire().await { DatabaseTransaction::new_postgres(conn, self.metric_callback.clone()).await } else { - Err(DbErr::Query( - "Failed to acquire connection from pool.".to_owned(), - )) + Err(DbErr::ConnFromPool) } } @@ -185,9 +175,7 @@ impl SqlxPostgresPoolConnection { .map_err(|e| TransactionError::Connection(e))?; transaction.run(callback).await } else { - Err(TransactionError::Connection(DbErr::Query( - "Failed to acquire connection from pool.".to_owned(), - ))) + Err(TransactionError::Connection(DbErr::ConnFromPool)) } } diff --git a/src/driver/sqlx_sqlite.rs b/src/driver/sqlx_sqlite.rs index 8bf56214..55e768b0 100644 --- a/src/driver/sqlx_sqlite.rs +++ b/src/driver/sqlx_sqlite.rs @@ -46,7 +46,7 @@ impl SqlxSqliteConnector { let mut opt = options .url .parse::() - .map_err(|e| DbErr::Conn(e.to_string()))?; + .map_err(|e| DbErr::ConnSqlX(e))?; if options.sqlcipher_key.is_some() { opt = opt.pragma("key", options.sqlcipher_key.clone().unwrap()); } @@ -96,9 +96,7 @@ impl SqlxSqlitePoolConnection { } }) } else { - Err(DbErr::Exec( - "Failed to acquire connection from pool.".to_owned(), - )) + Err(DbErr::ConnFromPool) } } @@ -114,14 +112,12 @@ impl SqlxSqlitePoolConnection { Ok(row) => Ok(Some(row.into())), Err(err) => match err { sqlx::Error::RowNotFound => Ok(None), - _ => Err(DbErr::Query(err.to_string())), + _ => Err(sqlx_error_to_query_err(err)), }, } }) } else { - Err(DbErr::Query( - "Failed to acquire connection from pool.".to_owned(), - )) + Err(DbErr::ConnFromPool) } } @@ -139,9 +135,7 @@ impl SqlxSqlitePoolConnection { } }) } else { - Err(DbErr::Query( - "Failed to acquire connection from pool.".to_owned(), - )) + Err(DbErr::ConnFromPool) } } @@ -157,9 +151,7 @@ impl SqlxSqlitePoolConnection { self.metric_callback.clone(), ))) } else { - Err(DbErr::Query( - "Failed to acquire connection from pool.".to_owned(), - )) + Err(DbErr::ConnFromPool) } } @@ -169,9 +161,7 @@ impl SqlxSqlitePoolConnection { if let Ok(conn) = self.pool.acquire().await { DatabaseTransaction::new_sqlite(conn, self.metric_callback.clone()).await } else { - Err(DbErr::Query( - "Failed to acquire connection from pool.".to_owned(), - )) + Err(DbErr::ConnFromPool) } } @@ -192,9 +182,7 @@ impl SqlxSqlitePoolConnection { .map_err(|e| TransactionError::Connection(e))?; transaction.run(callback).await } else { - Err(TransactionError::Connection(DbErr::Query( - "Failed to acquire connection from pool.".to_owned(), - ))) + Err(TransactionError::Connection(DbErr::ConnFromPool)) } } diff --git a/src/error.rs b/src/error.rs index aecc3f52..30b2909d 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,38 +1,64 @@ +#[cfg(feature = "sqlx-dep")] +use sqlx::error::Error as SqlxError; +use thiserror::Error; + /// An error from unsuccessful database operations -#[derive(Debug, PartialEq, Eq, Clone)] +#[derive(Error, Debug)] pub enum DbErr { + /// This error happens, when a pool was not able to create a connection + #[error("Failed to acquire connection from pool.")] + ConnFromPool, + /// Error in case of invalid type conversion attempts + #[error("fail to convert '{0}' into '{1}'")] + CannotConvertInto(String, String), + /// Error in case of invalid type conversion from an u64 + #[error("{0} cannot be converted from u64")] + ConvertFromU64(String), + /// After an insert statement it was impossible to retrieve the last_insert_id + #[error("Fail to unpack last_insert_id")] + InsertCouldNotUnpackInsertId, + /// When updating, a model should know it's primary key to check + /// if the record has been correctly updated, otherwise this error will occur + #[error("Fail to get primary key from model")] + UpdateCouldNotGetPrimaryKey, /// There was a problem with the database connection + #[error("Connection Error: {0}")] Conn(String), + /// There was a problem with the database connection from sqlx + #[cfg(feature = "sqlx-dep")] + #[error("Connection Error: {0}")] + ConnSqlX(#[source] SqlxError), /// An operation did not execute successfully - Exec(String), + #[cfg(feature = "sqlx-dep")] + #[error("Execution Error: {0}")] + ExecSqlX(#[source] SqlxError), + /// An error occurred while performing a query, with more details from sqlx + #[cfg(feature = "sqlx-dep")] + #[error("Query Error: {0}")] + QuerySqlX(#[source] SqlxError), /// An error occurred while performing a query + #[error("Query Error: {0}")] Query(String), /// The record was not found in the database + #[error("RecordNotFound Error: {0}")] RecordNotFound(String), /// A custom error + #[error("Custom Error: {0}")] Custom(String), /// Error occurred while parsing value as target type + #[error("Type Error: {0}")] Type(String), /// Error occurred while parsing json value as target type + #[error("Json Error: {0}")] Json(String), /// A migration error + #[error("Migration Error: {0}")] Migration(String), } -impl std::error::Error for DbErr {} - -impl std::fmt::Display for DbErr { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - match self { - 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), - Self::Custom(s) => write!(f, "Custom Error: {}", s), - Self::Type(s) => write!(f, "Type Error: {}", s), - Self::Json(s) => write!(f, "Json Error: {}", s), - Self::Migration(s) => write!(f, "Migration Error: {}", s), - } +impl PartialEq for DbErr { + fn eq(&self, other: &Self) -> bool { + self.to_string() == other.to_string() } } diff --git a/src/executor/insert.rs b/src/executor/insert.rs index 5f3f9c95..f7bb0629 100644 --- a/src/executor/insert.rs +++ b/src/executor/insert.rs @@ -130,7 +130,7 @@ where Some(value_tuple) => FromValueTuple::from_value_tuple(value_tuple), None => match last_insert_id_opt { Some(last_insert_id) => last_insert_id, - None => return Err(DbErr::Exec("Fail to unpack last_insert_id".to_owned())), + None => return Err(DbErr::InsertCouldNotUnpackInsertId), }, }; Ok(InsertResult { last_insert_id }) @@ -176,6 +176,8 @@ where }; match found { Some(model) => Ok(model), - None => Err(DbErr::Exec("Failed to find inserted item".to_owned())), + None => Err(DbErr::RecordNotFound( + "Failed to find inserted item".to_owned(), + )), } } diff --git a/src/executor/query.rs b/src/executor/query.rs index 06361a45..0f1b22b2 100644 --- a/src/executor/query.rs +++ b/src/executor/query.rs @@ -379,8 +379,9 @@ impl TryGetable for Decimal { use rust_decimal::prelude::FromPrimitive; match val { Some(v) => Decimal::from_f64(v).ok_or_else(|| { - TryGetError::DbErr(DbErr::Query( - "Failed to convert f64 into Decimal".to_owned(), + TryGetError::DbErr(DbErr::CannotConvertInto( + "f64".to_owned(), + "Decimal".to_owned(), )) }), None => Err(TryGetError::Null(column)), @@ -708,10 +709,7 @@ macro_rules! try_from_u64_err { ( $type: ty ) => { impl TryFromU64 for $type { fn try_from_u64(_: u64) -> Result { - Err(DbErr::Exec(format!( - "{} cannot be converted from u64", - stringify!($type) - ))) + Err(DbErr::ConvertFromU64(stringify!($type).to_string())) } } }; @@ -722,10 +720,7 @@ macro_rules! try_from_u64_err { $( $gen_type: TryFromU64, )* { fn try_from_u64(_: u64) -> Result { - Err(DbErr::Exec(format!( - "{} cannot be converted from u64", - stringify!(($($gen_type,)*)) - ))) + Err(DbErr::ConvertFromU64(stringify!($($gen_type,)*).to_string())) } } }; @@ -744,11 +739,7 @@ macro_rules! try_from_u64_numeric { fn try_from_u64(n: u64) -> Result { use std::convert::TryInto; n.try_into().map_err(|_| { - DbErr::Exec(format!( - "fail to convert '{}' into '{}'", - n, - stringify!($type) - )) + DbErr::CannotConvertInto(n.to_string(), stringify!($type).to_string()) }) } } @@ -828,9 +819,11 @@ mod tests { #[test] fn from_try_get_error() { // TryGetError::DbErr - let expected = DbErr::Query("expected error message".to_owned()); - let try_get_error = TryGetError::DbErr(expected.clone()); - assert_eq!(DbErr::from(try_get_error), expected); + let try_get_error = TryGetError::DbErr(DbErr::Query("expected error message".to_owned())); + assert_eq!( + DbErr::from(try_get_error), + DbErr::Query("expected error message".to_owned()) + ); // TryGetError::Null let try_get_error = TryGetError::Null("column".to_owned()); diff --git a/src/executor/update.rs b/src/executor/update.rs index f00c536f..cd9337f7 100644 --- a/src/executor/update.rs +++ b/src/executor/update.rs @@ -122,7 +122,7 @@ where Updater::new(query).check_record_exists().exec(db).await?; let primary_key_value = match model.get_primary_key_value() { Some(val) => FromValueTuple::from_value_tuple(val), - None => return Err(DbErr::Exec("Fail to get primary key from model".to_owned())), + None => return Err(DbErr::UpdateCouldNotGetPrimaryKey), }; let found = ::find_by_id(primary_key_value) .one(db) @@ -130,7 +130,9 @@ where // If we cannot select the updated row from db by the cached primary key match found { Some(model) => Ok(model), - None => Err(DbErr::Exec("Failed to find inserted item".to_owned())), + None => Err(DbErr::RecordNotFound( + "Failed to find inserted item".to_owned(), + )), } } } diff --git a/tests/crud/error.rs b/tests/crud/error.rs new file mode 100644 index 00000000..8dfab3fa --- /dev/null +++ b/tests/crud/error.rs @@ -0,0 +1,56 @@ +pub use super::*; +use rust_decimal_macros::dec; +use sea_orm::DbErr; +#[cfg(any( + feature = "sqlx-mysql", + feature = "sqlx-sqlite", + feature = "sqlx-postgres" +))] +use sqlx::Error; +use uuid::Uuid; + +pub async fn test_cake_error_sqlx(db: &DbConn) { + let mud_cake = cake::ActiveModel { + name: Set("Moldy Cake".to_owned()), + price: Set(dec!(10.25)), + gluten_free: Set(false), + serial: Set(Uuid::new_v4()), + bakery_id: Set(None), + ..Default::default() + }; + + let cake = mud_cake.save(db).await.expect("could not insert cake"); + + // if compiling without sqlx, this assignment will complain, + // but the whole test is useless in that case anyway. + #[allow(unused_variables)] + let error: DbErr = cake + .into_active_model() + .insert(db) + .await + .expect_err("inserting should fail due to duplicate primary key"); + + #[cfg(any(feature = "sqlx-mysql", feature = "sqlx-sqlite"))] + match error { + DbErr::ExecSqlX(sqlx_error) => match sqlx_error { + Error::Database(e) => { + #[cfg(feature = "sqlx-mysql")] + assert_eq!(e.code().unwrap(), "23000"); + #[cfg(feature = "sqlx-sqlite")] + assert_eq!(e.code().unwrap(), "1555"); + } + _ => panic!("Unexpected sqlx-error kind"), + }, + _ => panic!("Unexpected Error kind"), + } + #[cfg(feature = "sqlx-postgres")] + match error { + DbErr::QuerySqlX(sqlx_error) => match sqlx_error { + Error::Database(e) => { + assert_eq!(e.code().unwrap(), "23505"); + } + _ => panic!("Unexpected sqlx-error kind"), + }, + _ => panic!("Unexpected Error kind"), + } +} diff --git a/tests/crud/mod.rs b/tests/crud/mod.rs index 916878c8..3a629d7a 100644 --- a/tests/crud/mod.rs +++ b/tests/crud/mod.rs @@ -3,6 +3,7 @@ pub mod create_cake; pub mod create_lineitem; pub mod create_order; pub mod deletes; +pub mod error; pub mod updates; pub use create_baker::*; @@ -10,6 +11,7 @@ pub use create_cake::*; pub use create_lineitem::*; pub use create_order::*; pub use deletes::*; +pub use error::*; pub use updates::*; pub use super::common::bakery_chain::*; diff --git a/tests/crud_tests.rs b/tests/crud_tests.rs index bc2a3c97..9f7ea404 100644 --- a/tests/crud_tests.rs +++ b/tests/crud_tests.rs @@ -35,5 +35,6 @@ pub async fn create_entities(db: &DatabaseConnection) { test_update_deleted_customer(db).await; test_delete_cake(db).await; + test_cake_error_sqlx(db).await; test_delete_bakery(db).await; }