commit
5d752e60b9
@ -43,6 +43,7 @@ uuid = { version = "^1", features = ["serde", "v4"], optional = true }
|
|||||||
ouroboros = "0.15"
|
ouroboros = "0.15"
|
||||||
url = "^2.2"
|
url = "^2.2"
|
||||||
once_cell = "1.8"
|
once_cell = "1.8"
|
||||||
|
thiserror = "^1"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
smol = { version = "^1.2" }
|
smol = { version = "^1.2" }
|
||||||
|
@ -26,10 +26,7 @@ macro_rules! impl_try_from_u64_err {
|
|||||||
($newtype: ident) => {
|
($newtype: ident) => {
|
||||||
impl sea_orm::TryFromU64 for $newtype {
|
impl sea_orm::TryFromU64 for $newtype {
|
||||||
fn try_from_u64(_n: u64) -> Result<Self, sea_orm::DbErr> {
|
fn try_from_u64(_n: u64) -> Result<Self, sea_orm::DbErr> {
|
||||||
Err(sea_orm::DbErr::Exec(format!(
|
Err(sea_orm::DbErr::ConvertFromU64(stringify!($newtype)))
|
||||||
"{} cannot be converted from u64",
|
|
||||||
stringify!($newtype)
|
|
||||||
)))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
use std::marker::PhantomData;
|
|
||||||
use sea_orm::entity::prelude::*;
|
use sea_orm::entity::prelude::*;
|
||||||
|
use std::marker::PhantomData;
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, DeriveEntityModel)]
|
#[derive(Clone, Debug, PartialEq, DeriveEntityModel)]
|
||||||
#[sea_orm(table_name = "model")]
|
#[sea_orm(table_name = "model")]
|
||||||
@ -31,10 +31,7 @@ impl<T> From<AccountId<T>> for Uuid {
|
|||||||
|
|
||||||
impl<T> sea_orm::TryFromU64 for AccountId<T> {
|
impl<T> sea_orm::TryFromU64 for AccountId<T> {
|
||||||
fn try_from_u64(_n: u64) -> Result<Self, sea_orm::DbErr> {
|
fn try_from_u64(_n: u64) -> Result<Self, sea_orm::DbErr> {
|
||||||
Err(sea_orm::DbErr::Exec(format!(
|
Err(sea_orm::DbErr::ConvertFromU64(stringify!(AccountId<T>)))
|
||||||
"{} cannot be converted from u64",
|
|
||||||
stringify!(AccountId<T>)
|
|
||||||
)))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -71,7 +71,7 @@ pub fn impl_default_as_str(ident: &Ident, data: &Data) -> syn::Result<TokenStrea
|
|||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Implement a column using for an enum using [DeriveColumn](sea_orm::DeriveColumn)
|
/// Implement a column for an enum using [DeriveColumn](sea_orm::DeriveColumn)
|
||||||
pub fn impl_col_from_str(ident: &Ident, data: &Data) -> syn::Result<TokenStream> {
|
pub fn impl_col_from_str(ident: &Ident, data: &Data) -> syn::Result<TokenStream> {
|
||||||
let data_enum = match data {
|
let data_enum = match data {
|
||||||
Data::Enum(data_enum) => data_enum,
|
Data::Enum(data_enum) => data_enum,
|
||||||
@ -99,7 +99,7 @@ pub fn impl_col_from_str(ident: &Ident, data: &Data) -> syn::Result<TokenStream>
|
|||||||
fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
|
fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
|
||||||
match s {
|
match s {
|
||||||
#(#columns),*,
|
#(#columns),*,
|
||||||
_ => Err(sea_orm::ColumnFromStrErr(format!("Failed to parse '{}' as `{}`", s, stringify!(#ident)))),
|
_ => Err(sea_orm::ColumnFromStrErr(s.to_owned())),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -116,7 +116,9 @@ impl ConnectionTrait for DatabaseConnection {
|
|||||||
DatabaseConnection::SqlxSqlitePoolConnection(conn) => conn.execute(stmt).await,
|
DatabaseConnection::SqlxSqlitePoolConnection(conn) => conn.execute(stmt).await,
|
||||||
#[cfg(feature = "mock")]
|
#[cfg(feature = "mock")]
|
||||||
DatabaseConnection::MockDatabaseConnection(conn) => conn.execute(stmt),
|
DatabaseConnection::MockDatabaseConnection(conn) => conn.execute(stmt),
|
||||||
DatabaseConnection::Disconnected => Err(DbErr::Conn("Disconnected".to_owned())),
|
DatabaseConnection::Disconnected => {
|
||||||
|
Err(DbErr::Conn(RuntimeErr::Internal("Disconnected".to_owned())))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -132,7 +134,9 @@ impl ConnectionTrait for DatabaseConnection {
|
|||||||
DatabaseConnection::SqlxSqlitePoolConnection(conn) => conn.query_one(stmt).await,
|
DatabaseConnection::SqlxSqlitePoolConnection(conn) => conn.query_one(stmt).await,
|
||||||
#[cfg(feature = "mock")]
|
#[cfg(feature = "mock")]
|
||||||
DatabaseConnection::MockDatabaseConnection(conn) => conn.query_one(stmt),
|
DatabaseConnection::MockDatabaseConnection(conn) => conn.query_one(stmt),
|
||||||
DatabaseConnection::Disconnected => Err(DbErr::Conn("Disconnected".to_owned())),
|
DatabaseConnection::Disconnected => {
|
||||||
|
Err(DbErr::Conn(RuntimeErr::Internal("Disconnected".to_owned())))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -148,7 +152,9 @@ impl ConnectionTrait for DatabaseConnection {
|
|||||||
DatabaseConnection::SqlxSqlitePoolConnection(conn) => conn.query_all(stmt).await,
|
DatabaseConnection::SqlxSqlitePoolConnection(conn) => conn.query_all(stmt).await,
|
||||||
#[cfg(feature = "mock")]
|
#[cfg(feature = "mock")]
|
||||||
DatabaseConnection::MockDatabaseConnection(conn) => conn.query_all(stmt),
|
DatabaseConnection::MockDatabaseConnection(conn) => conn.query_all(stmt),
|
||||||
DatabaseConnection::Disconnected => Err(DbErr::Conn("Disconnected".to_owned())),
|
DatabaseConnection::Disconnected => {
|
||||||
|
Err(DbErr::Conn(RuntimeErr::Internal("Disconnected".to_owned())))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -102,7 +102,9 @@ impl MockDatabaseTrait for MockDatabase {
|
|||||||
result: ExecResultHolder::Mock(std::mem::take(&mut self.exec_results[counter])),
|
result: ExecResultHolder::Mock(std::mem::take(&mut self.exec_results[counter])),
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
Err(DbErr::Exec("`exec_results` buffer is empty.".to_owned()))
|
Err(DbErr::Exec(RuntimeErr::Internal(
|
||||||
|
"`exec_results` buffer is empty.".to_owned(),
|
||||||
|
)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -121,7 +123,9 @@ impl MockDatabaseTrait for MockDatabase {
|
|||||||
})
|
})
|
||||||
.collect())
|
.collect())
|
||||||
} else {
|
} else {
|
||||||
Err(DbErr::Query("`query_results` buffer is empty.".to_owned()))
|
Err(DbErr::Query(RuntimeErr::Internal(
|
||||||
|
"`query_results` buffer is empty.".to_owned(),
|
||||||
|
)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -176,7 +180,7 @@ impl MockRow {
|
|||||||
where
|
where
|
||||||
T: ValueType,
|
T: ValueType,
|
||||||
{
|
{
|
||||||
T::try_from(self.values.get(col).unwrap().clone()).map_err(|e| DbErr::Query(e.to_string()))
|
T::try_from(self.values.get(col).unwrap().clone()).map_err(|e| DbErr::Type(e.to_string()))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// An iterator over the keys and values of a mock row
|
/// An iterator over the keys and values of a mock row
|
||||||
|
@ -18,7 +18,7 @@ pub use stream::*;
|
|||||||
use tracing::instrument;
|
use tracing::instrument;
|
||||||
pub use transaction::*;
|
pub use transaction::*;
|
||||||
|
|
||||||
use crate::DbErr;
|
use crate::{DbErr, RuntimeErr};
|
||||||
|
|
||||||
/// Defines a database
|
/// Defines a database
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug, Default)]
|
||||||
@ -75,10 +75,10 @@ impl Database {
|
|||||||
if crate::MockDatabaseConnector::accepts(&opt.url) {
|
if crate::MockDatabaseConnector::accepts(&opt.url) {
|
||||||
return crate::MockDatabaseConnector::connect(&opt.url).await;
|
return crate::MockDatabaseConnector::connect(&opt.url).await;
|
||||||
}
|
}
|
||||||
Err(DbErr::Conn(format!(
|
Err(DbErr::Conn(RuntimeErr::Internal(format!(
|
||||||
"The connection string '{}' has no supporting driver.",
|
"The connection string '{}' has no supporting driver.",
|
||||||
opt.url
|
opt.url
|
||||||
)))
|
))))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -238,6 +238,17 @@ impl DatabaseTransaction {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "sqlx-dep")]
|
||||||
|
fn map_err_ignore_not_found<T: std::fmt::Debug>(
|
||||||
|
err: Result<Option<T>, sqlx::Error>,
|
||||||
|
) -> Result<Option<T>, 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 {
|
impl Drop for DatabaseTransaction {
|
||||||
@ -258,13 +269,14 @@ impl ConnectionTrait for DatabaseTransaction {
|
|||||||
async fn execute(&self, stmt: Statement) -> Result<ExecResult, DbErr> {
|
async fn execute(&self, stmt: Statement) -> Result<ExecResult, DbErr> {
|
||||||
debug_print!("{}", stmt);
|
debug_print!("{}", stmt);
|
||||||
|
|
||||||
let _res = match &mut *self.conn.lock().await {
|
match &mut *self.conn.lock().await {
|
||||||
#[cfg(feature = "sqlx-mysql")]
|
#[cfg(feature = "sqlx-mysql")]
|
||||||
InnerConnection::MySql(conn) => {
|
InnerConnection::MySql(conn) => {
|
||||||
let query = crate::driver::sqlx_mysql::sqlx_query(&stmt);
|
let query = crate::driver::sqlx_mysql::sqlx_query(&stmt);
|
||||||
crate::metric::metric!(self.metric_callback, &stmt, {
|
crate::metric::metric!(self.metric_callback, &stmt, {
|
||||||
query.execute(conn).await.map(Into::into)
|
query.execute(conn).await.map(Into::into)
|
||||||
})
|
})
|
||||||
|
.map_err(sqlx_error_to_exec_err)
|
||||||
}
|
}
|
||||||
#[cfg(feature = "sqlx-postgres")]
|
#[cfg(feature = "sqlx-postgres")]
|
||||||
InnerConnection::Postgres(conn) => {
|
InnerConnection::Postgres(conn) => {
|
||||||
@ -272,6 +284,7 @@ impl ConnectionTrait for DatabaseTransaction {
|
|||||||
crate::metric::metric!(self.metric_callback, &stmt, {
|
crate::metric::metric!(self.metric_callback, &stmt, {
|
||||||
query.execute(conn).await.map(Into::into)
|
query.execute(conn).await.map(Into::into)
|
||||||
})
|
})
|
||||||
|
.map_err(sqlx_error_to_exec_err)
|
||||||
}
|
}
|
||||||
#[cfg(feature = "sqlx-sqlite")]
|
#[cfg(feature = "sqlx-sqlite")]
|
||||||
InnerConnection::Sqlite(conn) => {
|
InnerConnection::Sqlite(conn) => {
|
||||||
@ -279,14 +292,13 @@ impl ConnectionTrait for DatabaseTransaction {
|
|||||||
crate::metric::metric!(self.metric_callback, &stmt, {
|
crate::metric::metric!(self.metric_callback, &stmt, {
|
||||||
query.execute(conn).await.map(Into::into)
|
query.execute(conn).await.map(Into::into)
|
||||||
})
|
})
|
||||||
|
.map_err(sqlx_error_to_exec_err)
|
||||||
}
|
}
|
||||||
#[cfg(feature = "mock")]
|
#[cfg(feature = "mock")]
|
||||||
InnerConnection::Mock(conn) => return conn.execute(stmt),
|
InnerConnection::Mock(conn) => return conn.execute(stmt),
|
||||||
#[allow(unreachable_patterns)]
|
#[allow(unreachable_patterns)]
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
};
|
}
|
||||||
#[cfg(feature = "sqlx-dep")]
|
|
||||||
_res.map_err(sqlx_error_to_exec_err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[instrument(level = "trace")]
|
#[instrument(level = "trace")]
|
||||||
@ -294,32 +306,32 @@ impl ConnectionTrait for DatabaseTransaction {
|
|||||||
async fn query_one(&self, stmt: Statement) -> Result<Option<QueryResult>, DbErr> {
|
async fn query_one(&self, stmt: Statement) -> Result<Option<QueryResult>, DbErr> {
|
||||||
debug_print!("{}", stmt);
|
debug_print!("{}", stmt);
|
||||||
|
|
||||||
let _res = match &mut *self.conn.lock().await {
|
match &mut *self.conn.lock().await {
|
||||||
#[cfg(feature = "sqlx-mysql")]
|
#[cfg(feature = "sqlx-mysql")]
|
||||||
InnerConnection::MySql(conn) => {
|
InnerConnection::MySql(conn) => {
|
||||||
let query = crate::driver::sqlx_mysql::sqlx_query(&stmt);
|
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")]
|
#[cfg(feature = "sqlx-postgres")]
|
||||||
InnerConnection::Postgres(conn) => {
|
InnerConnection::Postgres(conn) => {
|
||||||
let query = crate::driver::sqlx_postgres::sqlx_query(&stmt);
|
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")]
|
#[cfg(feature = "sqlx-sqlite")]
|
||||||
InnerConnection::Sqlite(conn) => {
|
InnerConnection::Sqlite(conn) => {
|
||||||
let query = crate::driver::sqlx_sqlite::sqlx_query(&stmt);
|
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")]
|
#[cfg(feature = "mock")]
|
||||||
InnerConnection::Mock(conn) => return conn.query_one(stmt),
|
InnerConnection::Mock(conn) => return conn.query_one(stmt),
|
||||||
#[allow(unreachable_patterns)]
|
#[allow(unreachable_patterns)]
|
||||||
_ => unreachable!(),
|
_ => 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<Vec<QueryResult>, DbErr> {
|
async fn query_all(&self, stmt: Statement) -> Result<Vec<QueryResult>, DbErr> {
|
||||||
debug_print!("{}", stmt);
|
debug_print!("{}", stmt);
|
||||||
|
|
||||||
let _res = match &mut *self.conn.lock().await {
|
match &mut *self.conn.lock().await {
|
||||||
#[cfg(feature = "sqlx-mysql")]
|
#[cfg(feature = "sqlx-mysql")]
|
||||||
InnerConnection::MySql(conn) => {
|
InnerConnection::MySql(conn) => {
|
||||||
let query = crate::driver::sqlx_mysql::sqlx_query(&stmt);
|
let query = crate::driver::sqlx_mysql::sqlx_query(&stmt);
|
||||||
@ -336,6 +348,7 @@ impl ConnectionTrait for DatabaseTransaction {
|
|||||||
.fetch_all(conn)
|
.fetch_all(conn)
|
||||||
.await
|
.await
|
||||||
.map(|rows| rows.into_iter().map(|r| r.into()).collect())
|
.map(|rows| rows.into_iter().map(|r| r.into()).collect())
|
||||||
|
.map_err(sqlx_error_to_query_err)
|
||||||
}
|
}
|
||||||
#[cfg(feature = "sqlx-postgres")]
|
#[cfg(feature = "sqlx-postgres")]
|
||||||
InnerConnection::Postgres(conn) => {
|
InnerConnection::Postgres(conn) => {
|
||||||
@ -344,6 +357,7 @@ impl ConnectionTrait for DatabaseTransaction {
|
|||||||
.fetch_all(conn)
|
.fetch_all(conn)
|
||||||
.await
|
.await
|
||||||
.map(|rows| rows.into_iter().map(|r| r.into()).collect())
|
.map(|rows| rows.into_iter().map(|r| r.into()).collect())
|
||||||
|
.map_err(sqlx_error_to_query_err)
|
||||||
}
|
}
|
||||||
#[cfg(feature = "sqlx-sqlite")]
|
#[cfg(feature = "sqlx-sqlite")]
|
||||||
InnerConnection::Sqlite(conn) => {
|
InnerConnection::Sqlite(conn) => {
|
||||||
@ -352,14 +366,13 @@ impl ConnectionTrait for DatabaseTransaction {
|
|||||||
.fetch_all(conn)
|
.fetch_all(conn)
|
||||||
.await
|
.await
|
||||||
.map(|rows| rows.into_iter().map(|r| r.into()).collect())
|
.map(|rows| rows.into_iter().map(|r| r.into()).collect())
|
||||||
|
.map_err(sqlx_error_to_query_err)
|
||||||
}
|
}
|
||||||
#[cfg(feature = "mock")]
|
#[cfg(feature = "mock")]
|
||||||
InnerConnection::Mock(conn) => return conn.query_all(stmt),
|
InnerConnection::Mock(conn) => return conn.query_all(stmt),
|
||||||
#[allow(unreachable_patterns)]
|
#[allow(unreachable_patterns)]
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
};
|
}
|
||||||
#[cfg(feature = "sqlx-dep")]
|
|
||||||
_res.map_err(sqlx_error_to_query_err)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,16 +1,16 @@
|
|||||||
use crate::DbErr;
|
use crate::{DbErr, RuntimeErr};
|
||||||
|
|
||||||
/// Converts an [sqlx::error] execution error to a [DbErr]
|
/// Converts an [sqlx::error] execution error to a [DbErr]
|
||||||
pub fn sqlx_error_to_exec_err(err: sqlx::Error) -> DbErr {
|
pub fn sqlx_error_to_exec_err(err: sqlx::Error) -> DbErr {
|
||||||
DbErr::Exec(err.to_string())
|
DbErr::Exec(RuntimeErr::SqlxError(err))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Converts an [sqlx::error] query error to a [DbErr]
|
/// Converts an [sqlx::error] query error to a [DbErr]
|
||||||
pub fn sqlx_error_to_query_err(err: sqlx::Error) -> DbErr {
|
pub fn sqlx_error_to_query_err(err: sqlx::Error) -> DbErr {
|
||||||
DbErr::Query(err.to_string())
|
DbErr::Query(RuntimeErr::SqlxError(err))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Converts an [sqlx::error] connection error to a [DbErr]
|
/// Converts an [sqlx::error] connection error to a [DbErr]
|
||||||
pub fn sqlx_error_to_conn_err(err: sqlx::Error) -> DbErr {
|
pub fn sqlx_error_to_conn_err(err: sqlx::Error) -> DbErr {
|
||||||
DbErr::Conn(err.to_string())
|
DbErr::Conn(RuntimeErr::SqlxError(err))
|
||||||
}
|
}
|
||||||
|
@ -45,7 +45,7 @@ impl SqlxMySqlConnector {
|
|||||||
let mut opt = options
|
let mut opt = options
|
||||||
.url
|
.url
|
||||||
.parse::<MySqlConnectOptions>()
|
.parse::<MySqlConnectOptions>()
|
||||||
.map_err(|e| DbErr::Conn(e.to_string()))?;
|
.map_err(sqlx_error_to_conn_err)?;
|
||||||
use sqlx::ConnectOptions;
|
use sqlx::ConnectOptions;
|
||||||
if !options.sqlx_logging {
|
if !options.sqlx_logging {
|
||||||
opt.disable_statement_logging();
|
opt.disable_statement_logging();
|
||||||
@ -89,9 +89,7 @@ impl SqlxMySqlPoolConnection {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
Err(DbErr::Exec(
|
Err(DbErr::ConnectionAcquire)
|
||||||
"Failed to acquire connection from pool.".to_owned(),
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -107,14 +105,12 @@ impl SqlxMySqlPoolConnection {
|
|||||||
Ok(row) => Ok(Some(row.into())),
|
Ok(row) => Ok(Some(row.into())),
|
||||||
Err(err) => match err {
|
Err(err) => match err {
|
||||||
sqlx::Error::RowNotFound => Ok(None),
|
sqlx::Error::RowNotFound => Ok(None),
|
||||||
_ => Err(DbErr::Query(err.to_string())),
|
_ => Err(sqlx_error_to_query_err(err)),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
Err(DbErr::Query(
|
Err(DbErr::ConnectionAcquire)
|
||||||
"Failed to acquire connection from pool.".to_owned(),
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -132,9 +128,7 @@ impl SqlxMySqlPoolConnection {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
Err(DbErr::Query(
|
Err(DbErr::ConnectionAcquire)
|
||||||
"Failed to acquire connection from pool.".to_owned(),
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -150,9 +144,7 @@ impl SqlxMySqlPoolConnection {
|
|||||||
self.metric_callback.clone(),
|
self.metric_callback.clone(),
|
||||||
)))
|
)))
|
||||||
} else {
|
} else {
|
||||||
Err(DbErr::Query(
|
Err(DbErr::ConnectionAcquire)
|
||||||
"Failed to acquire connection from pool.".to_owned(),
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -162,9 +154,7 @@ impl SqlxMySqlPoolConnection {
|
|||||||
if let Ok(conn) = self.pool.acquire().await {
|
if let Ok(conn) = self.pool.acquire().await {
|
||||||
DatabaseTransaction::new_mysql(conn, self.metric_callback.clone()).await
|
DatabaseTransaction::new_mysql(conn, self.metric_callback.clone()).await
|
||||||
} else {
|
} else {
|
||||||
Err(DbErr::Query(
|
Err(DbErr::ConnectionAcquire)
|
||||||
"Failed to acquire connection from pool.".to_owned(),
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -185,9 +175,7 @@ impl SqlxMySqlPoolConnection {
|
|||||||
.map_err(|e| TransactionError::Connection(e))?;
|
.map_err(|e| TransactionError::Connection(e))?;
|
||||||
transaction.run(callback).await
|
transaction.run(callback).await
|
||||||
} else {
|
} else {
|
||||||
Err(TransactionError::Connection(DbErr::Query(
|
Err(TransactionError::Connection(DbErr::ConnectionAcquire))
|
||||||
"Failed to acquire connection from pool.".to_owned(),
|
|
||||||
)))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -45,7 +45,7 @@ impl SqlxPostgresConnector {
|
|||||||
let mut opt = options
|
let mut opt = options
|
||||||
.url
|
.url
|
||||||
.parse::<PgConnectOptions>()
|
.parse::<PgConnectOptions>()
|
||||||
.map_err(|e| DbErr::Conn(e.to_string()))?;
|
.map_err(sqlx_error_to_conn_err)?;
|
||||||
use sqlx::ConnectOptions;
|
use sqlx::ConnectOptions;
|
||||||
if !options.sqlx_logging {
|
if !options.sqlx_logging {
|
||||||
opt.disable_statement_logging();
|
opt.disable_statement_logging();
|
||||||
@ -89,9 +89,7 @@ impl SqlxPostgresPoolConnection {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
Err(DbErr::Exec(
|
Err(DbErr::ConnectionAcquire)
|
||||||
"Failed to acquire connection from pool.".to_owned(),
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -107,14 +105,12 @@ impl SqlxPostgresPoolConnection {
|
|||||||
Ok(row) => Ok(Some(row.into())),
|
Ok(row) => Ok(Some(row.into())),
|
||||||
Err(err) => match err {
|
Err(err) => match err {
|
||||||
sqlx::Error::RowNotFound => Ok(None),
|
sqlx::Error::RowNotFound => Ok(None),
|
||||||
_ => Err(DbErr::Query(err.to_string())),
|
_ => Err(sqlx_error_to_query_err(err)),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
Err(DbErr::Query(
|
Err(DbErr::ConnectionAcquire)
|
||||||
"Failed to acquire connection from pool.".to_owned(),
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -132,9 +128,7 @@ impl SqlxPostgresPoolConnection {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
Err(DbErr::Query(
|
Err(DbErr::ConnectionAcquire)
|
||||||
"Failed to acquire connection from pool.".to_owned(),
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -150,9 +144,7 @@ impl SqlxPostgresPoolConnection {
|
|||||||
self.metric_callback.clone(),
|
self.metric_callback.clone(),
|
||||||
)))
|
)))
|
||||||
} else {
|
} else {
|
||||||
Err(DbErr::Query(
|
Err(DbErr::ConnectionAcquire)
|
||||||
"Failed to acquire connection from pool.".to_owned(),
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -162,9 +154,7 @@ impl SqlxPostgresPoolConnection {
|
|||||||
if let Ok(conn) = self.pool.acquire().await {
|
if let Ok(conn) = self.pool.acquire().await {
|
||||||
DatabaseTransaction::new_postgres(conn, self.metric_callback.clone()).await
|
DatabaseTransaction::new_postgres(conn, self.metric_callback.clone()).await
|
||||||
} else {
|
} else {
|
||||||
Err(DbErr::Query(
|
Err(DbErr::ConnectionAcquire)
|
||||||
"Failed to acquire connection from pool.".to_owned(),
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -185,9 +175,7 @@ impl SqlxPostgresPoolConnection {
|
|||||||
.map_err(|e| TransactionError::Connection(e))?;
|
.map_err(|e| TransactionError::Connection(e))?;
|
||||||
transaction.run(callback).await
|
transaction.run(callback).await
|
||||||
} else {
|
} else {
|
||||||
Err(TransactionError::Connection(DbErr::Query(
|
Err(TransactionError::Connection(DbErr::ConnectionAcquire))
|
||||||
"Failed to acquire connection from pool.".to_owned(),
|
|
||||||
)))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -46,7 +46,7 @@ impl SqlxSqliteConnector {
|
|||||||
let mut opt = options
|
let mut opt = options
|
||||||
.url
|
.url
|
||||||
.parse::<SqliteConnectOptions>()
|
.parse::<SqliteConnectOptions>()
|
||||||
.map_err(|e| DbErr::Conn(e.to_string()))?;
|
.map_err(sqlx_error_to_conn_err)?;
|
||||||
if options.sqlcipher_key.is_some() {
|
if options.sqlcipher_key.is_some() {
|
||||||
opt = opt.pragma("key", options.sqlcipher_key.clone().unwrap());
|
opt = opt.pragma("key", options.sqlcipher_key.clone().unwrap());
|
||||||
}
|
}
|
||||||
@ -96,9 +96,7 @@ impl SqlxSqlitePoolConnection {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
Err(DbErr::Exec(
|
Err(DbErr::ConnectionAcquire)
|
||||||
"Failed to acquire connection from pool.".to_owned(),
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -114,14 +112,12 @@ impl SqlxSqlitePoolConnection {
|
|||||||
Ok(row) => Ok(Some(row.into())),
|
Ok(row) => Ok(Some(row.into())),
|
||||||
Err(err) => match err {
|
Err(err) => match err {
|
||||||
sqlx::Error::RowNotFound => Ok(None),
|
sqlx::Error::RowNotFound => Ok(None),
|
||||||
_ => Err(DbErr::Query(err.to_string())),
|
_ => Err(sqlx_error_to_query_err(err)),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
Err(DbErr::Query(
|
Err(DbErr::ConnectionAcquire)
|
||||||
"Failed to acquire connection from pool.".to_owned(),
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -139,9 +135,7 @@ impl SqlxSqlitePoolConnection {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
Err(DbErr::Query(
|
Err(DbErr::ConnectionAcquire)
|
||||||
"Failed to acquire connection from pool.".to_owned(),
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -157,9 +151,7 @@ impl SqlxSqlitePoolConnection {
|
|||||||
self.metric_callback.clone(),
|
self.metric_callback.clone(),
|
||||||
)))
|
)))
|
||||||
} else {
|
} else {
|
||||||
Err(DbErr::Query(
|
Err(DbErr::ConnectionAcquire)
|
||||||
"Failed to acquire connection from pool.".to_owned(),
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -169,9 +161,7 @@ impl SqlxSqlitePoolConnection {
|
|||||||
if let Ok(conn) = self.pool.acquire().await {
|
if let Ok(conn) = self.pool.acquire().await {
|
||||||
DatabaseTransaction::new_sqlite(conn, self.metric_callback.clone()).await
|
DatabaseTransaction::new_sqlite(conn, self.metric_callback.clone()).await
|
||||||
} else {
|
} else {
|
||||||
Err(DbErr::Query(
|
Err(DbErr::ConnectionAcquire)
|
||||||
"Failed to acquire connection from pool.".to_owned(),
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -192,9 +182,7 @@ impl SqlxSqlitePoolConnection {
|
|||||||
.map_err(|e| TransactionError::Connection(e))?;
|
.map_err(|e| TransactionError::Connection(e))?;
|
||||||
transaction.run(callback).await
|
transaction.run(callback).await
|
||||||
} else {
|
} else {
|
||||||
Err(TransactionError::Connection(DbErr::Query(
|
Err(TransactionError::Connection(DbErr::ConnectionAcquire))
|
||||||
"Failed to acquire connection from pool.".to_owned(),
|
|
||||||
)))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
85
src/error.rs
85
src/error.rs
@ -1,49 +1,80 @@
|
|||||||
|
#[cfg(feature = "sqlx-dep")]
|
||||||
|
use sqlx::error::Error as SqlxError;
|
||||||
|
use thiserror::Error;
|
||||||
|
|
||||||
/// An error from unsuccessful database operations
|
/// An error from unsuccessful database operations
|
||||||
#[derive(Debug, PartialEq, Eq, Clone)]
|
#[derive(Error, Debug)]
|
||||||
pub enum DbErr {
|
pub enum DbErr {
|
||||||
|
/// This error can happen when the connection pool is fully-utilized
|
||||||
|
#[error("Failed to acquire connection from pool")]
|
||||||
|
ConnectionAcquire,
|
||||||
|
/// Runtime type conversion error
|
||||||
|
#[error("Error converting `{from}` into `{into}`: {source}")]
|
||||||
|
TryIntoErr {
|
||||||
|
/// From type
|
||||||
|
from: &'static str,
|
||||||
|
/// Into type
|
||||||
|
into: &'static str,
|
||||||
|
/// TryError
|
||||||
|
source: Box<dyn std::error::Error + Send + Sync>,
|
||||||
|
},
|
||||||
/// There was a problem with the database connection
|
/// There was a problem with the database connection
|
||||||
Conn(String),
|
#[error("Connection Error: {0}")]
|
||||||
|
Conn(#[source] RuntimeErr),
|
||||||
/// An operation did not execute successfully
|
/// An operation did not execute successfully
|
||||||
Exec(String),
|
#[error("Execution Error: {0}")]
|
||||||
|
Exec(#[source] RuntimeErr),
|
||||||
/// An error occurred while performing a query
|
/// An error occurred while performing a query
|
||||||
Query(String),
|
#[error("Query Error: {0}")]
|
||||||
|
Query(#[source] RuntimeErr),
|
||||||
|
/// Type error: the specified type cannot be converted from u64. This is not a runtime error.
|
||||||
|
#[error("Type '{0}' cannot be converted from u64")]
|
||||||
|
ConvertFromU64(&'static str),
|
||||||
|
/// After an insert statement it was impossible to retrieve the last_insert_id
|
||||||
|
#[error("Failed to unpack last_insert_id")]
|
||||||
|
UnpackInsertId,
|
||||||
|
/// 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("Failed to get primary key from model")]
|
||||||
|
UpdateGetPrimeryKey,
|
||||||
/// The record was not found in the database
|
/// The record was not found in the database
|
||||||
|
#[error("RecordNotFound Error: {0}")]
|
||||||
RecordNotFound(String),
|
RecordNotFound(String),
|
||||||
/// A custom error
|
/// A custom error
|
||||||
|
#[error("Custom Error: {0}")]
|
||||||
Custom(String),
|
Custom(String),
|
||||||
/// Error occurred while parsing value as target type
|
/// Error occurred while parsing value as target type
|
||||||
|
#[error("Type Error: {0}")]
|
||||||
Type(String),
|
Type(String),
|
||||||
/// Error occurred while parsing json value as target type
|
/// Error occurred while parsing json value as target type
|
||||||
|
#[error("Json Error: {0}")]
|
||||||
Json(String),
|
Json(String),
|
||||||
/// A migration error
|
/// A migration error
|
||||||
|
#[error("Migration Error: {0}")]
|
||||||
Migration(String),
|
Migration(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::error::Error for DbErr {}
|
/// Runtime error
|
||||||
|
#[derive(Error, Debug)]
|
||||||
|
pub enum RuntimeErr {
|
||||||
|
/// SQLx Error
|
||||||
|
#[cfg(feature = "sqlx-dep")]
|
||||||
|
#[error("{0}")]
|
||||||
|
SqlxError(SqlxError),
|
||||||
|
/// Error generated from within SeaORM
|
||||||
|
#[error("{0}")]
|
||||||
|
Internal(String),
|
||||||
|
}
|
||||||
|
|
||||||
impl std::fmt::Display for DbErr {
|
impl PartialEq for DbErr {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
fn eq(&self, other: &Self) -> bool {
|
||||||
match self {
|
self.to_string() == other.to_string()
|
||||||
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),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// An error from a failed column operation when trying to convert the column to a string
|
impl Eq for DbErr {}
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
|
/// Error during `impl FromStr for Entity::Column`
|
||||||
|
#[derive(Error, Debug)]
|
||||||
|
#[error("Failed to match \"{0}\" as Column")]
|
||||||
pub struct ColumnFromStrErr(pub String);
|
pub struct ColumnFromStrErr(pub String);
|
||||||
|
|
||||||
impl std::error::Error for ColumnFromStrErr {}
|
|
||||||
|
|
||||||
impl std::fmt::Display for ColumnFromStrErr {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
|
||||||
write!(f, "{}", self.0.as_str())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -128,7 +128,7 @@ where
|
|||||||
Some(value_tuple) => FromValueTuple::from_value_tuple(value_tuple),
|
Some(value_tuple) => FromValueTuple::from_value_tuple(value_tuple),
|
||||||
None => match last_insert_id_opt {
|
None => match last_insert_id_opt {
|
||||||
Some(last_insert_id) => last_insert_id,
|
Some(last_insert_id) => last_insert_id,
|
||||||
None => return Err(DbErr::Exec("Fail to unpack last_insert_id".to_owned())),
|
None => return Err(DbErr::UnpackInsertId),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
Ok(InsertResult { last_insert_id })
|
Ok(InsertResult { last_insert_id })
|
||||||
@ -168,6 +168,8 @@ where
|
|||||||
};
|
};
|
||||||
match found {
|
match found {
|
||||||
Some(model) => Ok(model),
|
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(),
|
||||||
|
)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -41,7 +41,7 @@ impl From<TryGetError> for DbErr {
|
|||||||
match e {
|
match e {
|
||||||
TryGetError::DbErr(e) => e,
|
TryGetError::DbErr(e) => e,
|
||||||
TryGetError::Null(s) => {
|
TryGetError::Null(s) => {
|
||||||
DbErr::Query(format!("error occurred while decoding {}: Null", s))
|
DbErr::Type(format!("A null value was encountered while decoding {}", s))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -376,12 +376,13 @@ impl TryGetable for Decimal {
|
|||||||
let val: Option<f64> = row
|
let val: Option<f64> = row
|
||||||
.try_get(column.as_str())
|
.try_get(column.as_str())
|
||||||
.map_err(|e| TryGetError::DbErr(crate::sqlx_error_to_query_err(e)))?;
|
.map_err(|e| TryGetError::DbErr(crate::sqlx_error_to_query_err(e)))?;
|
||||||
use rust_decimal::prelude::FromPrimitive;
|
|
||||||
match val {
|
match val {
|
||||||
Some(v) => Decimal::from_f64(v).ok_or_else(|| {
|
Some(v) => Decimal::try_from(v).map_err(|e| {
|
||||||
TryGetError::DbErr(DbErr::Query(
|
TryGetError::DbErr(DbErr::TryIntoErr {
|
||||||
"Failed to convert f64 into Decimal".to_owned(),
|
from: "f64",
|
||||||
))
|
into: "Decimal",
|
||||||
|
source: Box::new(e),
|
||||||
|
})
|
||||||
}),
|
}),
|
||||||
None => Err(TryGetError::Null(column)),
|
None => Err(TryGetError::Null(column)),
|
||||||
}
|
}
|
||||||
@ -626,7 +627,7 @@ where
|
|||||||
|
|
||||||
fn try_get_many_with_slice_len_of(len: usize, cols: &[String]) -> Result<(), TryGetError> {
|
fn try_get_many_with_slice_len_of(len: usize, cols: &[String]) -> Result<(), TryGetError> {
|
||||||
if cols.len() < len {
|
if cols.len() < len {
|
||||||
Err(TryGetError::DbErr(DbErr::Query(format!(
|
Err(TryGetError::DbErr(DbErr::Type(format!(
|
||||||
"Expect {} column names supplied but got slice of length {}",
|
"Expect {} column names supplied but got slice of length {}",
|
||||||
len,
|
len,
|
||||||
cols.len()
|
cols.len()
|
||||||
@ -708,10 +709,7 @@ macro_rules! try_from_u64_err {
|
|||||||
( $type: ty ) => {
|
( $type: ty ) => {
|
||||||
impl TryFromU64 for $type {
|
impl TryFromU64 for $type {
|
||||||
fn try_from_u64(_: u64) -> Result<Self, DbErr> {
|
fn try_from_u64(_: u64) -> Result<Self, DbErr> {
|
||||||
Err(DbErr::Exec(format!(
|
Err(DbErr::ConvertFromU64(stringify!($type)))
|
||||||
"{} cannot be converted from u64",
|
|
||||||
stringify!($type)
|
|
||||||
)))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -722,10 +720,7 @@ macro_rules! try_from_u64_err {
|
|||||||
$( $gen_type: TryFromU64, )*
|
$( $gen_type: TryFromU64, )*
|
||||||
{
|
{
|
||||||
fn try_from_u64(_: u64) -> Result<Self, DbErr> {
|
fn try_from_u64(_: u64) -> Result<Self, DbErr> {
|
||||||
Err(DbErr::Exec(format!(
|
Err(DbErr::ConvertFromU64(stringify!($($gen_type,)*)))
|
||||||
"{} cannot be converted from u64",
|
|
||||||
stringify!(($($gen_type,)*))
|
|
||||||
)))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -743,12 +738,10 @@ macro_rules! try_from_u64_numeric {
|
|||||||
impl TryFromU64 for $type {
|
impl TryFromU64 for $type {
|
||||||
fn try_from_u64(n: u64) -> Result<Self, DbErr> {
|
fn try_from_u64(n: u64) -> Result<Self, DbErr> {
|
||||||
use std::convert::TryInto;
|
use std::convert::TryInto;
|
||||||
n.try_into().map_err(|_| {
|
n.try_into().map_err(|e| DbErr::TryIntoErr {
|
||||||
DbErr::Exec(format!(
|
from: stringify!(u64),
|
||||||
"fail to convert '{}' into '{}'",
|
into: stringify!($type),
|
||||||
n,
|
source: Box::new(e),
|
||||||
stringify!($type)
|
|
||||||
))
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -823,18 +816,22 @@ try_from_u64_err!(uuid::Uuid);
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::TryGetError;
|
use super::TryGetError;
|
||||||
use crate::error::DbErr;
|
use crate::error::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn from_try_get_error() {
|
fn from_try_get_error() {
|
||||||
// TryGetError::DbErr
|
// TryGetError::DbErr
|
||||||
let expected = DbErr::Query("expected error message".to_owned());
|
let try_get_error = TryGetError::DbErr(DbErr::Query(RuntimeErr::Internal(
|
||||||
let try_get_error = TryGetError::DbErr(expected.clone());
|
"expected error message".to_owned(),
|
||||||
assert_eq!(DbErr::from(try_get_error), expected);
|
)));
|
||||||
|
assert_eq!(
|
||||||
|
DbErr::from(try_get_error),
|
||||||
|
DbErr::Query(RuntimeErr::Internal("expected error message".to_owned()))
|
||||||
|
);
|
||||||
|
|
||||||
// TryGetError::Null
|
// TryGetError::Null
|
||||||
let try_get_error = TryGetError::Null("column".to_owned());
|
let try_get_error = TryGetError::Null("column".to_owned());
|
||||||
let expected = "error occurred while decoding column: Null".to_owned();
|
let expected = "A null value was encountered while decoding column".to_owned();
|
||||||
assert_eq!(DbErr::from(try_get_error), DbErr::Query(expected));
|
assert_eq!(DbErr::from(try_get_error), DbErr::Type(expected));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -116,7 +116,7 @@ where
|
|||||||
Updater::new(query).check_record_exists().exec(db).await?;
|
Updater::new(query).check_record_exists().exec(db).await?;
|
||||||
let primary_key_value = match model.get_primary_key_value() {
|
let primary_key_value = match model.get_primary_key_value() {
|
||||||
Some(val) => FromValueTuple::from_value_tuple(val),
|
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::UpdateGetPrimeryKey),
|
||||||
};
|
};
|
||||||
let found = <A::Entity as EntityTrait>::find_by_id(primary_key_value)
|
let found = <A::Entity as EntityTrait>::find_by_id(primary_key_value)
|
||||||
.one(db)
|
.one(db)
|
||||||
@ -124,7 +124,9 @@ where
|
|||||||
// If we cannot select the updated row from db by the cached primary key
|
// If we cannot select the updated row from db by the cached primary key
|
||||||
match found {
|
match found {
|
||||||
Some(model) => Ok(model),
|
Some(model) => Ok(model),
|
||||||
None => Err(DbErr::Exec("Failed to find inserted item".to_owned())),
|
None => Err(DbErr::RecordNotFound(
|
||||||
|
"Failed to find updated item".to_owned(),
|
||||||
|
)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
56
tests/crud/error.rs
Normal file
56
tests/crud/error.rs
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
pub use super::*;
|
||||||
|
use rust_decimal_macros::dec;
|
||||||
|
use sea_orm::error::*;
|
||||||
|
#[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::Exec(RuntimeErr::SqlxError(error)) => match 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::Query(RuntimeErr::SqlxError(error)) => match error {
|
||||||
|
Error::Database(e) => {
|
||||||
|
assert_eq!(e.code().unwrap(), "23505");
|
||||||
|
}
|
||||||
|
_ => panic!("Unexpected sqlx-error kind"),
|
||||||
|
},
|
||||||
|
_ => panic!("Unexpected Error kind"),
|
||||||
|
}
|
||||||
|
}
|
@ -3,6 +3,7 @@ pub mod create_cake;
|
|||||||
pub mod create_lineitem;
|
pub mod create_lineitem;
|
||||||
pub mod create_order;
|
pub mod create_order;
|
||||||
pub mod deletes;
|
pub mod deletes;
|
||||||
|
pub mod error;
|
||||||
pub mod updates;
|
pub mod updates;
|
||||||
|
|
||||||
pub use create_baker::*;
|
pub use create_baker::*;
|
||||||
@ -10,6 +11,7 @@ pub use create_cake::*;
|
|||||||
pub use create_lineitem::*;
|
pub use create_lineitem::*;
|
||||||
pub use create_order::*;
|
pub use create_order::*;
|
||||||
pub use deletes::*;
|
pub use deletes::*;
|
||||||
|
pub use error::*;
|
||||||
pub use updates::*;
|
pub use updates::*;
|
||||||
|
|
||||||
pub use super::common::bakery_chain::*;
|
pub use super::common::bakery_chain::*;
|
||||||
|
@ -35,5 +35,6 @@ pub async fn create_entities(db: &DatabaseConnection) {
|
|||||||
test_update_deleted_customer(db).await;
|
test_update_deleted_customer(db).await;
|
||||||
|
|
||||||
test_delete_cake(db).await;
|
test_delete_cake(db).await;
|
||||||
|
test_cake_error_sqlx(db).await;
|
||||||
test_delete_bakery(db).await;
|
test_delete_bakery(db).await;
|
||||||
}
|
}
|
||||||
|
@ -508,7 +508,9 @@ pub async fn transaction_nested() {
|
|||||||
assert_eq!(bakeries.len(), 4);
|
assert_eq!(bakeries.len(), 4);
|
||||||
|
|
||||||
if true {
|
if true {
|
||||||
Err(DbErr::Query("Force Rollback!".to_owned()))
|
Err(DbErr::Query(RuntimeErr::Internal(
|
||||||
|
"Force Rollback!".to_owned(),
|
||||||
|
)))
|
||||||
} else {
|
} else {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -633,7 +635,9 @@ pub async fn transaction_nested() {
|
|||||||
assert_eq!(bakeries.len(), 7);
|
assert_eq!(bakeries.len(), 7);
|
||||||
|
|
||||||
if true {
|
if true {
|
||||||
Err(DbErr::Query("Force Rollback!".to_owned()))
|
Err(DbErr::Query(RuntimeErr::Internal(
|
||||||
|
"Force Rollback!".to_owned(),
|
||||||
|
)))
|
||||||
} else {
|
} else {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -652,7 +656,9 @@ pub async fn transaction_nested() {
|
|||||||
assert_eq!(bakeries.len(), 6);
|
assert_eq!(bakeries.len(), 6);
|
||||||
|
|
||||||
if true {
|
if true {
|
||||||
Err(DbErr::Query("Force Rollback!".to_owned()))
|
Err(DbErr::Query(RuntimeErr::Internal(
|
||||||
|
"Force Rollback!".to_owned(),
|
||||||
|
)))
|
||||||
} else {
|
} else {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user