Cleanup panic and unwrap (#1231)
* Add clippy linter checks * Mock * InnerConnection * panic -> Err * panic -> Err * More panic -> Err * Replace unwrap * Fix clippy * add clippy linters * Refactor * Dump DbErr::Mock * Revert if...else rewrite * Remove except * DbErr helper functions * Fix clippy * Negative SQLite last_insert_rowid throw unreachable * Update panics docs * Fixup * Fixup * Fixup * Fixup * Revert adding `ExecResultHolder::Disconnected` * More fixup * Fix * Revert adding `QueryResultRow::Disconnected` * Fix * Refactoring * Fix * Refactoring * More refactoring * More refactoring * Fix * Revert `InnerConnection::Disconnected` * Revert breaking changes * Fix * Fix * Fix * Refactor `.take().expect()` * Revert changing `if ... else` to `match` block * Revert changing return type of `MockDatabaseConnection` transaction method * Borrow sqlcipher_key * Fetching unsupported type from query result will thrown `DbErr::Type(...)` error * Revert adding `DatabaseConnection::try_into_transaction_log()` method * Refactoring * Refactoring
This commit is contained in:
parent
48a9ffec2a
commit
91c4930391
@ -36,6 +36,12 @@ pub enum DatabaseConnection {
|
||||
/// The same as a [DatabaseConnection]
|
||||
pub type DbConn = DatabaseConnection;
|
||||
|
||||
impl Default for DatabaseConnection {
|
||||
fn default() -> Self {
|
||||
Self::Disconnected
|
||||
}
|
||||
}
|
||||
|
||||
/// The type of database backend for real world databases.
|
||||
/// This is enabled by feature flags as specified in the crate documentation
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
@ -50,6 +56,7 @@ pub enum DatabaseBackend {
|
||||
|
||||
/// The same as [DatabaseBackend] just shorter :)
|
||||
pub type DbBackend = DatabaseBackend;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) enum InnerConnection {
|
||||
#[cfg(feature = "sqlx-mysql")]
|
||||
@ -62,12 +69,6 @@ pub(crate) enum InnerConnection {
|
||||
Mock(std::sync::Arc<crate::MockDatabaseConnection>),
|
||||
}
|
||||
|
||||
impl Default for DatabaseConnection {
|
||||
fn default() -> Self {
|
||||
Self::Disconnected
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for DatabaseConnection {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
write!(
|
||||
@ -116,9 +117,7 @@ impl ConnectionTrait for DatabaseConnection {
|
||||
DatabaseConnection::SqlxSqlitePoolConnection(conn) => conn.execute(stmt).await,
|
||||
#[cfg(feature = "mock")]
|
||||
DatabaseConnection::MockDatabaseConnection(conn) => conn.execute(stmt),
|
||||
DatabaseConnection::Disconnected => {
|
||||
Err(DbErr::Conn(RuntimeErr::Internal("Disconnected".to_owned())))
|
||||
}
|
||||
DatabaseConnection::Disconnected => Err(conn_err("Disconnected")),
|
||||
}
|
||||
}
|
||||
|
||||
@ -142,9 +141,7 @@ impl ConnectionTrait for DatabaseConnection {
|
||||
let stmt = Statement::from_string(db_backend, sql.into());
|
||||
conn.execute(stmt)
|
||||
}
|
||||
DatabaseConnection::Disconnected => {
|
||||
Err(DbErr::Conn(RuntimeErr::Internal("Disconnected".to_owned())))
|
||||
}
|
||||
DatabaseConnection::Disconnected => Err(conn_err("Disconnected")),
|
||||
}
|
||||
}
|
||||
|
||||
@ -160,9 +157,7 @@ impl ConnectionTrait for DatabaseConnection {
|
||||
DatabaseConnection::SqlxSqlitePoolConnection(conn) => conn.query_one(stmt).await,
|
||||
#[cfg(feature = "mock")]
|
||||
DatabaseConnection::MockDatabaseConnection(conn) => conn.query_one(stmt),
|
||||
DatabaseConnection::Disconnected => {
|
||||
Err(DbErr::Conn(RuntimeErr::Internal("Disconnected".to_owned())))
|
||||
}
|
||||
DatabaseConnection::Disconnected => Err(conn_err("Disconnected")),
|
||||
}
|
||||
}
|
||||
|
||||
@ -178,9 +173,7 @@ impl ConnectionTrait for DatabaseConnection {
|
||||
DatabaseConnection::SqlxSqlitePoolConnection(conn) => conn.query_all(stmt).await,
|
||||
#[cfg(feature = "mock")]
|
||||
DatabaseConnection::MockDatabaseConnection(conn) => conn.query_all(stmt),
|
||||
DatabaseConnection::Disconnected => {
|
||||
Err(DbErr::Conn(RuntimeErr::Internal("Disconnected".to_owned())))
|
||||
}
|
||||
DatabaseConnection::Disconnected => Err(conn_err("Disconnected")),
|
||||
}
|
||||
}
|
||||
|
||||
@ -195,25 +188,25 @@ impl StreamTrait for DatabaseConnection {
|
||||
type Stream<'a> = crate::QueryStream;
|
||||
|
||||
#[instrument(level = "trace")]
|
||||
#[allow(unused_variables, unreachable_code)]
|
||||
#[allow(unused_variables)]
|
||||
fn stream<'a>(
|
||||
&'a self,
|
||||
stmt: Statement,
|
||||
) -> Pin<Box<dyn Future<Output = Result<Self::Stream<'a>, DbErr>> + 'a + Send>> {
|
||||
Box::pin(async move {
|
||||
Ok(match self {
|
||||
match self {
|
||||
#[cfg(feature = "sqlx-mysql")]
|
||||
DatabaseConnection::SqlxMySqlPoolConnection(conn) => conn.stream(stmt).await?,
|
||||
DatabaseConnection::SqlxMySqlPoolConnection(conn) => conn.stream(stmt).await,
|
||||
#[cfg(feature = "sqlx-postgres")]
|
||||
DatabaseConnection::SqlxPostgresPoolConnection(conn) => conn.stream(stmt).await?,
|
||||
DatabaseConnection::SqlxPostgresPoolConnection(conn) => conn.stream(stmt).await,
|
||||
#[cfg(feature = "sqlx-sqlite")]
|
||||
DatabaseConnection::SqlxSqlitePoolConnection(conn) => conn.stream(stmt).await?,
|
||||
DatabaseConnection::SqlxSqlitePoolConnection(conn) => conn.stream(stmt).await,
|
||||
#[cfg(feature = "mock")]
|
||||
DatabaseConnection::MockDatabaseConnection(conn) => {
|
||||
crate::QueryStream::from((Arc::clone(conn), stmt, None))
|
||||
Ok(crate::QueryStream::from((Arc::clone(conn), stmt, None)))
|
||||
}
|
||||
DatabaseConnection::Disconnected => panic!("Disconnected"),
|
||||
})
|
||||
DatabaseConnection::Disconnected => Err(conn_err("Disconnected")),
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -233,7 +226,7 @@ impl TransactionTrait for DatabaseConnection {
|
||||
DatabaseConnection::MockDatabaseConnection(conn) => {
|
||||
DatabaseTransaction::new_mock(Arc::clone(conn), None).await
|
||||
}
|
||||
DatabaseConnection::Disconnected => panic!("Disconnected"),
|
||||
DatabaseConnection::Disconnected => Err(conn_err("Disconnected")),
|
||||
}
|
||||
}
|
||||
|
||||
@ -260,7 +253,7 @@ impl TransactionTrait for DatabaseConnection {
|
||||
DatabaseConnection::MockDatabaseConnection(conn) => {
|
||||
DatabaseTransaction::new_mock(Arc::clone(conn), None).await
|
||||
}
|
||||
DatabaseConnection::Disconnected => panic!("Disconnected"),
|
||||
DatabaseConnection::Disconnected => Err(conn_err("Disconnected")),
|
||||
}
|
||||
}
|
||||
|
||||
@ -296,7 +289,7 @@ impl TransactionTrait for DatabaseConnection {
|
||||
.map_err(TransactionError::Connection)?;
|
||||
transaction.run(_callback).await
|
||||
}
|
||||
DatabaseConnection::Disconnected => panic!("Disconnected"),
|
||||
DatabaseConnection::Disconnected => Err(conn_err("Disconnected").into()),
|
||||
}
|
||||
}
|
||||
|
||||
@ -340,7 +333,7 @@ impl TransactionTrait for DatabaseConnection {
|
||||
.map_err(TransactionError::Connection)?;
|
||||
transaction.run(_callback).await
|
||||
}
|
||||
DatabaseConnection::Disconnected => panic!("Disconnected"),
|
||||
DatabaseConnection::Disconnected => Err(conn_err("Disconnected").into()),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -348,16 +341,28 @@ impl TransactionTrait for DatabaseConnection {
|
||||
#[cfg(feature = "mock")]
|
||||
impl DatabaseConnection {
|
||||
/// Generate a database connection for testing the Mock database
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if [DbConn] is not a mock connection.
|
||||
pub fn as_mock_connection(&self) -> &crate::MockDatabaseConnection {
|
||||
match self {
|
||||
DatabaseConnection::MockDatabaseConnection(mock_conn) => mock_conn,
|
||||
_ => panic!("not mock connection"),
|
||||
_ => panic!("Not mock connection"),
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the transaction log as a collection Vec<[crate::Transaction]>
|
||||
/// Get the transaction log as a collection Vec<[crate::Transaction]>
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if the mocker mutex is being held by another thread.
|
||||
pub fn into_transaction_log(self) -> Vec<crate::Transaction> {
|
||||
let mut mocker = self.as_mock_connection().get_mocker_mutex().lock().unwrap();
|
||||
let mut mocker = self
|
||||
.as_mock_connection()
|
||||
.get_mocker_mutex()
|
||||
.lock()
|
||||
.expect("Fail to acquire mocker");
|
||||
mocker.drain_transaction_log()
|
||||
}
|
||||
}
|
||||
@ -399,9 +404,7 @@ impl DatabaseConnection {
|
||||
// Nothing to cleanup, we just consume the `DatabaseConnection`
|
||||
Ok(())
|
||||
}
|
||||
DatabaseConnection::Disconnected => {
|
||||
Err(DbErr::Conn(RuntimeErr::Internal("Disconnected".to_owned())))
|
||||
}
|
||||
DatabaseConnection::Disconnected => Err(conn_err("Disconnected")),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -409,6 +412,10 @@ impl DatabaseConnection {
|
||||
#[cfg(feature = "sea-orm-internal")]
|
||||
impl DatabaseConnection {
|
||||
/// Get [sqlx::MySqlPool]
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if [DbConn] is not a MySQL connection.
|
||||
#[cfg(feature = "sqlx-mysql")]
|
||||
pub fn get_mysql_connection_pool(&self) -> &sqlx::MySqlPool {
|
||||
match self {
|
||||
@ -418,6 +425,10 @@ impl DatabaseConnection {
|
||||
}
|
||||
|
||||
/// Get [sqlx::PgPool]
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if [DbConn] is not a Postgres connection.
|
||||
#[cfg(feature = "sqlx-postgres")]
|
||||
pub fn get_postgres_connection_pool(&self) -> &sqlx::PgPool {
|
||||
match self {
|
||||
@ -427,6 +438,10 @@ impl DatabaseConnection {
|
||||
}
|
||||
|
||||
/// Get [sqlx::SqlitePool]
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if [DbConn] is not a SQLite connection.
|
||||
#[cfg(feature = "sqlx-sqlite")]
|
||||
pub fn get_sqlite_connection_pool(&self) -> &sqlx::SqlitePool {
|
||||
match self {
|
||||
@ -440,7 +455,7 @@ impl DbBackend {
|
||||
/// Check if the URI is the same as the specified database backend.
|
||||
/// Returns true if they match.
|
||||
pub fn is_prefix_of(self, base_url: &str) -> bool {
|
||||
let base_url_parsed = Url::parse(base_url).unwrap();
|
||||
let base_url_parsed = Url::parse(base_url).expect("Fail to parse database URL");
|
||||
match self {
|
||||
Self::Postgres => {
|
||||
base_url_parsed.scheme() == "postgres" || base_url_parsed.scheme() == "postgresql"
|
||||
|
@ -123,9 +123,7 @@ impl MockDatabaseTrait for MockDatabase {
|
||||
if counter < self.exec_results.len() {
|
||||
match std::mem::replace(
|
||||
&mut self.exec_results[counter],
|
||||
Err(DbErr::Exec(RuntimeErr::Internal(
|
||||
"this value has been consumed already".to_owned(),
|
||||
))),
|
||||
Err(exec_err("this value has been consumed already")),
|
||||
) {
|
||||
Ok(result) => Ok(ExecResult {
|
||||
result: ExecResultHolder::Mock(result),
|
||||
@ -133,9 +131,7 @@ impl MockDatabaseTrait for MockDatabase {
|
||||
Err(err) => Err(err),
|
||||
}
|
||||
} else {
|
||||
Err(DbErr::Exec(RuntimeErr::Internal(
|
||||
"`exec_results` buffer is empty".to_owned(),
|
||||
)))
|
||||
Err(exec_err("`exec_results` buffer is empty"))
|
||||
}
|
||||
}
|
||||
|
||||
@ -149,9 +145,7 @@ impl MockDatabaseTrait for MockDatabase {
|
||||
if counter < self.query_results.len() {
|
||||
match std::mem::replace(
|
||||
&mut self.query_results[counter],
|
||||
Err(DbErr::Query(RuntimeErr::Internal(
|
||||
"this value has been consumed already".to_owned(),
|
||||
))),
|
||||
Err(query_err("this value has been consumed already")),
|
||||
) {
|
||||
Ok(result) => Ok(result
|
||||
.into_iter()
|
||||
@ -162,45 +156,43 @@ impl MockDatabaseTrait for MockDatabase {
|
||||
Err(err) => Err(err),
|
||||
}
|
||||
} else {
|
||||
Err(DbErr::Query(RuntimeErr::Internal(
|
||||
"`query_results` buffer is empty.".to_owned(),
|
||||
)))
|
||||
Err(query_err("`query_results` buffer is empty."))
|
||||
}
|
||||
}
|
||||
|
||||
#[instrument(level = "trace")]
|
||||
fn begin(&mut self) {
|
||||
if self.transaction.is_some() {
|
||||
self.transaction
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.begin_nested(self.db_backend);
|
||||
} else {
|
||||
self.transaction = Some(OpenTransaction::init());
|
||||
match self.transaction.as_mut() {
|
||||
Some(transaction) => transaction.begin_nested(self.db_backend),
|
||||
None => self.transaction = Some(OpenTransaction::init()),
|
||||
}
|
||||
}
|
||||
|
||||
#[instrument(level = "trace")]
|
||||
fn commit(&mut self) {
|
||||
if self.transaction.is_some() {
|
||||
if self.transaction.as_mut().unwrap().commit(self.db_backend) {
|
||||
let transaction = self.transaction.take().unwrap();
|
||||
self.transaction_log.push(transaction.into_transaction());
|
||||
match self.transaction.as_mut() {
|
||||
Some(transaction) => {
|
||||
if transaction.commit(self.db_backend) {
|
||||
if let Some(transaction) = self.transaction.take() {
|
||||
self.transaction_log.push(transaction.into_transaction());
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
panic!("There is no open transaction to commit");
|
||||
None => panic!("There is no open transaction to commit"),
|
||||
}
|
||||
}
|
||||
|
||||
#[instrument(level = "trace")]
|
||||
fn rollback(&mut self) {
|
||||
if self.transaction.is_some() {
|
||||
if self.transaction.as_mut().unwrap().rollback(self.db_backend) {
|
||||
let transaction = self.transaction.take().unwrap();
|
||||
self.transaction_log.push(transaction.into_transaction());
|
||||
match self.transaction.as_mut() {
|
||||
Some(transaction) => {
|
||||
if transaction.rollback(self.db_backend) {
|
||||
if let Some(transaction) = self.transaction.take() {
|
||||
self.transaction_log.push(transaction.into_transaction());
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
panic!("There is no open transaction to rollback");
|
||||
None => panic!("There is no open transaction to rollback"),
|
||||
}
|
||||
}
|
||||
|
||||
@ -223,17 +215,17 @@ impl MockRow {
|
||||
T::try_from(
|
||||
self.values
|
||||
.get(index)
|
||||
.unwrap_or_else(|| panic!("No column for ColIdx {index:?}"))
|
||||
.ok_or_else(|| query_err(format!("No column for ColIdx {index:?}")))?
|
||||
.clone(),
|
||||
)
|
||||
.map_err(|e| DbErr::Type(e.to_string()))
|
||||
.map_err(type_err)
|
||||
} else if let Some(index) = index.as_usize() {
|
||||
let (_, value) = self.values.iter().nth(*index).ok_or_else(|| {
|
||||
DbErr::Query(RuntimeErr::Internal(format!(
|
||||
"Column at index {index} not found"
|
||||
)))
|
||||
})?;
|
||||
T::try_from(value.clone()).map_err(|e| DbErr::Type(e.to_string()))
|
||||
let (_, value) = self
|
||||
.values
|
||||
.iter()
|
||||
.nth(*index)
|
||||
.ok_or_else(|| query_err(format!("Column at index {index} not found")))?;
|
||||
T::try_from(value.clone()).map_err(type_err)
|
||||
} else {
|
||||
unreachable!("Missing ColIdx implementation for MockRow");
|
||||
}
|
||||
@ -393,10 +385,10 @@ impl OpenTransaction {
|
||||
}
|
||||
|
||||
fn into_transaction(self) -> Transaction {
|
||||
if self.transaction_depth != 0 {
|
||||
panic!("There is uncommitted nested transaction.");
|
||||
match self.transaction_depth {
|
||||
0 => Transaction { stmts: self.stmts },
|
||||
_ => panic!("There is uncommitted nested transaction"),
|
||||
}
|
||||
Transaction { stmts: self.stmts }
|
||||
}
|
||||
}
|
||||
|
||||
@ -404,8 +396,8 @@ impl OpenTransaction {
|
||||
#[cfg(feature = "mock")]
|
||||
mod tests {
|
||||
use crate::{
|
||||
entity::*, tests_cfg::*, DbBackend, DbErr, IntoMockRow, MockDatabase, RuntimeErr,
|
||||
Statement, Transaction, TransactionError, TransactionTrait,
|
||||
entity::*, error::*, tests_cfg::*, DbBackend, DbErr, IntoMockRow, MockDatabase, Statement,
|
||||
Transaction, TransactionError, TransactionTrait,
|
||||
};
|
||||
use pretty_assertions::assert_eq;
|
||||
|
||||
@ -480,7 +472,7 @@ mod tests {
|
||||
Err(TransactionError::Transaction(err)) => {
|
||||
assert_eq!(err, MyErr("test".to_owned()))
|
||||
}
|
||||
_ => panic!(),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
|
||||
assert_eq!(
|
||||
@ -846,34 +838,26 @@ mod tests {
|
||||
#[smol_potat::test]
|
||||
async fn test_query_err() {
|
||||
let db = MockDatabase::new(DbBackend::MySql)
|
||||
.append_query_errors([DbErr::Query(RuntimeErr::Internal(
|
||||
"this is a mock query error".to_owned(),
|
||||
))])
|
||||
.append_query_errors([query_err("this is a mock query error")])
|
||||
.into_connection();
|
||||
|
||||
assert_eq!(
|
||||
cake::Entity::find().all(&db).await,
|
||||
Err(DbErr::Query(RuntimeErr::Internal(
|
||||
"this is a mock query error".to_owned()
|
||||
)))
|
||||
Err(query_err("this is a mock query error"))
|
||||
);
|
||||
}
|
||||
|
||||
#[smol_potat::test]
|
||||
async fn test_exec_err() {
|
||||
let db = MockDatabase::new(DbBackend::MySql)
|
||||
.append_exec_errors([DbErr::Exec(RuntimeErr::Internal(
|
||||
"this is a mock exec error".to_owned(),
|
||||
))])
|
||||
.append_exec_errors([exec_err("this is a mock exec error")])
|
||||
.into_connection();
|
||||
|
||||
let model = cake::ActiveModel::new();
|
||||
|
||||
assert_eq!(
|
||||
model.save(&db).await,
|
||||
Err(DbErr::Exec(RuntimeErr::Internal(
|
||||
"this is a mock exec error".to_owned()
|
||||
)))
|
||||
Err(exec_err("this is a mock exec error"))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -18,7 +18,7 @@ pub use stream::*;
|
||||
use tracing::instrument;
|
||||
pub use transaction::*;
|
||||
|
||||
use crate::{DbErr, RuntimeErr};
|
||||
use crate::error::*;
|
||||
|
||||
/// Defines a database
|
||||
#[derive(Debug, Default)]
|
||||
@ -77,10 +77,10 @@ impl Database {
|
||||
if crate::MockDatabaseConnector::accepts(&opt.url) {
|
||||
return crate::MockDatabaseConnector::connect(&opt.url).await;
|
||||
}
|
||||
Err(DbErr::Conn(RuntimeErr::Internal(format!(
|
||||
Err(conn_err(format!(
|
||||
"The connection string '{}' has no supporting driver.",
|
||||
opt.url
|
||||
))))
|
||||
)))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,9 +1,10 @@
|
||||
#![allow(missing_docs, unreachable_code, unused_variables)]
|
||||
|
||||
use std::{pin::Pin, task::Poll};
|
||||
use tracing::instrument;
|
||||
|
||||
#[cfg(feature = "mock")]
|
||||
use std::sync::Arc;
|
||||
use std::{pin::Pin, task::Poll};
|
||||
|
||||
use futures::Stream;
|
||||
#[cfg(feature = "sqlx-dep")]
|
||||
@ -12,11 +13,10 @@ use futures::TryStreamExt;
|
||||
#[cfg(feature = "sqlx-dep")]
|
||||
use sqlx::{pool::PoolConnection, Executor};
|
||||
|
||||
use tracing::instrument;
|
||||
|
||||
use crate::{DbErr, InnerConnection, QueryResult, Statement};
|
||||
|
||||
use super::metric::MetricStream;
|
||||
#[cfg(feature = "sqlx-dep")]
|
||||
use crate::driver::*;
|
||||
use crate::{DbErr, InnerConnection, QueryResult, Statement};
|
||||
|
||||
/// Creates a stream from a [QueryResult]
|
||||
#[ouroboros::self_referencing]
|
||||
@ -130,7 +130,7 @@ impl QueryStream {
|
||||
let stream = c
|
||||
.fetch(query)
|
||||
.map_ok(Into::into)
|
||||
.map_err(crate::sqlx_error_to_query_err);
|
||||
.map_err(sqlx_error_to_query_err);
|
||||
let elapsed = _start.map(|s| s.elapsed().unwrap_or_default());
|
||||
MetricStream::new(_metric_callback, stmt, elapsed, stream)
|
||||
}
|
||||
@ -141,7 +141,7 @@ impl QueryStream {
|
||||
let stream = c
|
||||
.fetch(query)
|
||||
.map_ok(Into::into)
|
||||
.map_err(crate::sqlx_error_to_query_err);
|
||||
.map_err(sqlx_error_to_query_err);
|
||||
let elapsed = _start.map(|s| s.elapsed().unwrap_or_default());
|
||||
MetricStream::new(_metric_callback, stmt, elapsed, stream)
|
||||
}
|
||||
@ -152,7 +152,7 @@ impl QueryStream {
|
||||
let stream = c
|
||||
.fetch(query)
|
||||
.map_ok(Into::into)
|
||||
.map_err(crate::sqlx_error_to_query_err);
|
||||
.map_err(sqlx_error_to_query_err);
|
||||
let elapsed = _start.map(|s| s.elapsed().unwrap_or_default());
|
||||
MetricStream::new(_metric_callback, stmt, elapsed, stream)
|
||||
}
|
||||
|
@ -1,21 +1,19 @@
|
||||
#![allow(missing_docs)]
|
||||
|
||||
use std::{ops::DerefMut, pin::Pin, task::Poll};
|
||||
use tracing::instrument;
|
||||
|
||||
use futures::Stream;
|
||||
#[cfg(feature = "sqlx-dep")]
|
||||
use futures::TryStreamExt;
|
||||
use futures::{lock::MutexGuard, Stream};
|
||||
|
||||
#[cfg(feature = "sqlx-dep")]
|
||||
use sqlx::Executor;
|
||||
|
||||
use futures::lock::MutexGuard;
|
||||
|
||||
use tracing::instrument;
|
||||
|
||||
use crate::{DbErr, InnerConnection, QueryResult, Statement};
|
||||
|
||||
use super::metric::MetricStream;
|
||||
#[cfg(feature = "sqlx-dep")]
|
||||
use crate::driver::*;
|
||||
use crate::{DbErr, InnerConnection, QueryResult, Statement};
|
||||
|
||||
/// `TransactionStream` cannot be used in a `transaction` closure as it does not impl `Send`.
|
||||
/// It seems to be a Rust limitation right now, and solution to work around this deemed to be extremely hard.
|
||||
@ -55,7 +53,7 @@ impl<'a> TransactionStream<'a> {
|
||||
let stream = c
|
||||
.fetch(query)
|
||||
.map_ok(Into::into)
|
||||
.map_err(crate::sqlx_error_to_query_err);
|
||||
.map_err(sqlx_error_to_query_err);
|
||||
let elapsed = _start.map(|s| s.elapsed().unwrap_or_default());
|
||||
MetricStream::new(_metric_callback, stmt, elapsed, stream)
|
||||
}
|
||||
@ -66,7 +64,7 @@ impl<'a> TransactionStream<'a> {
|
||||
let stream = c
|
||||
.fetch(query)
|
||||
.map_ok(Into::into)
|
||||
.map_err(crate::sqlx_error_to_query_err);
|
||||
.map_err(sqlx_error_to_query_err);
|
||||
let elapsed = _start.map(|s| s.elapsed().unwrap_or_default());
|
||||
MetricStream::new(_metric_callback, stmt, elapsed, stream)
|
||||
}
|
||||
@ -77,7 +75,7 @@ impl<'a> TransactionStream<'a> {
|
||||
let stream = c
|
||||
.fetch(query)
|
||||
.map_ok(Into::into)
|
||||
.map_err(crate::sqlx_error_to_query_err);
|
||||
.map_err(sqlx_error_to_query_err);
|
||||
let elapsed = _start.map(|s| s.elapsed().unwrap_or_default());
|
||||
MetricStream::new(_metric_callback, stmt, elapsed, stream)
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
use crate::{
|
||||
debug_print, AccessMode, ConnectionTrait, DbBackend, DbErr, ExecResult, InnerConnection,
|
||||
IsolationLevel, QueryResult, Statement, StreamTrait, TransactionStream, TransactionTrait,
|
||||
debug_print, error::*, AccessMode, ConnectionTrait, DbBackend, DbErr, ExecResult,
|
||||
InnerConnection, IsolationLevel, QueryResult, Statement, StreamTrait, TransactionStream,
|
||||
TransactionTrait,
|
||||
};
|
||||
#[cfg(feature = "sqlx-dep")]
|
||||
use crate::{sqlx_error_to_exec_err, sqlx_error_to_query_err};
|
||||
@ -95,7 +96,6 @@ impl DatabaseTransaction {
|
||||
}
|
||||
|
||||
#[instrument(level = "trace", skip(metric_callback))]
|
||||
#[allow(unreachable_code)]
|
||||
async fn begin(
|
||||
conn: Arc<Mutex<InnerConnection>>,
|
||||
backend: DbBackend,
|
||||
@ -117,7 +117,7 @@ impl DatabaseTransaction {
|
||||
.await?;
|
||||
<sqlx::MySql as sqlx::Database>::TransactionManager::begin(c)
|
||||
.await
|
||||
.map_err(sqlx_error_to_query_err)?;
|
||||
.map_err(sqlx_error_to_query_err)
|
||||
}
|
||||
#[cfg(feature = "sqlx-postgres")]
|
||||
InnerConnection::Postgres(ref mut c) => {
|
||||
@ -130,7 +130,7 @@ impl DatabaseTransaction {
|
||||
isolation_level,
|
||||
access_mode,
|
||||
)
|
||||
.await?;
|
||||
.await
|
||||
}
|
||||
#[cfg(feature = "sqlx-sqlite")]
|
||||
InnerConnection::Sqlite(ref mut c) => {
|
||||
@ -139,13 +139,16 @@ impl DatabaseTransaction {
|
||||
.await?;
|
||||
<sqlx::Sqlite as sqlx::Database>::TransactionManager::begin(c)
|
||||
.await
|
||||
.map_err(sqlx_error_to_query_err)?;
|
||||
.map_err(sqlx_error_to_query_err)
|
||||
}
|
||||
#[cfg(feature = "mock")]
|
||||
InnerConnection::Mock(ref mut c) => {
|
||||
c.begin();
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
#[allow(unreachable_patterns)]
|
||||
_ => Err(conn_err("Disconnected")),
|
||||
}?;
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
@ -181,25 +184,28 @@ impl DatabaseTransaction {
|
||||
InnerConnection::MySql(ref mut c) => {
|
||||
<sqlx::MySql as sqlx::Database>::TransactionManager::commit(c)
|
||||
.await
|
||||
.map_err(sqlx_error_to_query_err)?
|
||||
.map_err(sqlx_error_to_query_err)
|
||||
}
|
||||
#[cfg(feature = "sqlx-postgres")]
|
||||
InnerConnection::Postgres(ref mut c) => {
|
||||
<sqlx::Postgres as sqlx::Database>::TransactionManager::commit(c)
|
||||
.await
|
||||
.map_err(sqlx_error_to_query_err)?
|
||||
.map_err(sqlx_error_to_query_err)
|
||||
}
|
||||
#[cfg(feature = "sqlx-sqlite")]
|
||||
InnerConnection::Sqlite(ref mut c) => {
|
||||
<sqlx::Sqlite as sqlx::Database>::TransactionManager::commit(c)
|
||||
.await
|
||||
.map_err(sqlx_error_to_query_err)?
|
||||
.map_err(sqlx_error_to_query_err)
|
||||
}
|
||||
#[cfg(feature = "mock")]
|
||||
InnerConnection::Mock(ref mut c) => {
|
||||
c.commit();
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
#[allow(unreachable_patterns)]
|
||||
_ => Err(conn_err("Disconnected")),
|
||||
}?;
|
||||
self.open = false;
|
||||
Ok(())
|
||||
}
|
||||
@ -213,32 +219,35 @@ impl DatabaseTransaction {
|
||||
InnerConnection::MySql(ref mut c) => {
|
||||
<sqlx::MySql as sqlx::Database>::TransactionManager::rollback(c)
|
||||
.await
|
||||
.map_err(sqlx_error_to_query_err)?
|
||||
.map_err(sqlx_error_to_query_err)
|
||||
}
|
||||
#[cfg(feature = "sqlx-postgres")]
|
||||
InnerConnection::Postgres(ref mut c) => {
|
||||
<sqlx::Postgres as sqlx::Database>::TransactionManager::rollback(c)
|
||||
.await
|
||||
.map_err(sqlx_error_to_query_err)?
|
||||
.map_err(sqlx_error_to_query_err)
|
||||
}
|
||||
#[cfg(feature = "sqlx-sqlite")]
|
||||
InnerConnection::Sqlite(ref mut c) => {
|
||||
<sqlx::Sqlite as sqlx::Database>::TransactionManager::rollback(c)
|
||||
.await
|
||||
.map_err(sqlx_error_to_query_err)?
|
||||
.map_err(sqlx_error_to_query_err)
|
||||
}
|
||||
#[cfg(feature = "mock")]
|
||||
InnerConnection::Mock(ref mut c) => {
|
||||
c.rollback();
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
#[allow(unreachable_patterns)]
|
||||
_ => Err(conn_err("Disconnected")),
|
||||
}?;
|
||||
self.open = false;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// the rollback is queued and will be performed on next async operation, like returning the connection to the pool
|
||||
#[instrument(level = "trace")]
|
||||
fn start_rollback(&mut self) {
|
||||
fn start_rollback(&mut self) -> Result<(), DbErr> {
|
||||
if self.open {
|
||||
if let Some(mut conn) = self.conn.try_lock() {
|
||||
match &mut *conn {
|
||||
@ -259,13 +268,14 @@ impl DatabaseTransaction {
|
||||
c.rollback();
|
||||
}
|
||||
#[allow(unreachable_patterns)]
|
||||
_ => unreachable!(),
|
||||
_ => return Err(conn_err("Disconnected")),
|
||||
}
|
||||
} else {
|
||||
//this should never happen
|
||||
panic!("Dropping a locked Transaction");
|
||||
return Err(conn_err("Dropping a locked Transaction"));
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(feature = "sqlx-dep")]
|
||||
@ -282,7 +292,7 @@ impl DatabaseTransaction {
|
||||
|
||||
impl Drop for DatabaseTransaction {
|
||||
fn drop(&mut self) {
|
||||
self.start_rollback();
|
||||
self.start_rollback().expect("Fail to rollback transaction");
|
||||
}
|
||||
}
|
||||
|
||||
@ -326,7 +336,7 @@ impl ConnectionTrait for DatabaseTransaction {
|
||||
#[cfg(feature = "mock")]
|
||||
InnerConnection::Mock(conn) => return conn.execute(stmt),
|
||||
#[allow(unreachable_patterns)]
|
||||
_ => unreachable!(),
|
||||
_ => Err(conn_err("Disconnected")),
|
||||
}
|
||||
}
|
||||
|
||||
@ -358,7 +368,7 @@ impl ConnectionTrait for DatabaseTransaction {
|
||||
conn.execute(stmt)
|
||||
}
|
||||
#[allow(unreachable_patterns)]
|
||||
_ => unreachable!(),
|
||||
_ => Err(conn_err("Disconnected")),
|
||||
}
|
||||
}
|
||||
|
||||
@ -398,7 +408,7 @@ impl ConnectionTrait for DatabaseTransaction {
|
||||
#[cfg(feature = "mock")]
|
||||
InnerConnection::Mock(conn) => return conn.query_one(stmt),
|
||||
#[allow(unreachable_patterns)]
|
||||
_ => unreachable!(),
|
||||
_ => Err(conn_err("Disconnected")),
|
||||
}
|
||||
}
|
||||
|
||||
@ -444,7 +454,7 @@ impl ConnectionTrait for DatabaseTransaction {
|
||||
#[cfg(feature = "mock")]
|
||||
InnerConnection::Mock(conn) => return conn.query_all(stmt),
|
||||
#[allow(unreachable_patterns)]
|
||||
_ => unreachable!(),
|
||||
_ => Err(conn_err("Disconnected")),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -564,3 +574,12 @@ where
|
||||
}
|
||||
|
||||
impl<E> std::error::Error for TransactionError<E> where E: std::error::Error {}
|
||||
|
||||
impl<E> From<DbErr> for TransactionError<E>
|
||||
where
|
||||
E: std::error::Error,
|
||||
{
|
||||
fn from(e: DbErr) -> Self {
|
||||
Self::Connection(e)
|
||||
}
|
||||
}
|
||||
|
@ -115,7 +115,10 @@ impl MockDatabaseConnection {
|
||||
|
||||
/// Get the [DatabaseBackend](crate::DatabaseBackend) being used by the [MockDatabase]
|
||||
pub fn get_database_backend(&self) -> DbBackend {
|
||||
self.mocker.lock().unwrap().get_database_backend()
|
||||
self.mocker
|
||||
.lock()
|
||||
.expect("Fail to acquire mocker")
|
||||
.get_database_backend()
|
||||
}
|
||||
|
||||
/// Execute the SQL statement in the [MockDatabase]
|
||||
@ -123,7 +126,10 @@ impl MockDatabaseConnection {
|
||||
pub fn execute(&self, statement: Statement) -> Result<ExecResult, DbErr> {
|
||||
debug_print!("{}", statement);
|
||||
let counter = self.execute_counter.fetch_add(1, Ordering::SeqCst);
|
||||
self.mocker.lock().unwrap().execute(counter, statement)
|
||||
self.mocker
|
||||
.lock()
|
||||
.map_err(exec_err)?
|
||||
.execute(counter, statement)
|
||||
}
|
||||
|
||||
/// Return one [QueryResult] if the query was successful
|
||||
@ -131,7 +137,11 @@ impl MockDatabaseConnection {
|
||||
pub fn query_one(&self, statement: Statement) -> Result<Option<QueryResult>, DbErr> {
|
||||
debug_print!("{}", statement);
|
||||
let counter = self.query_counter.fetch_add(1, Ordering::SeqCst);
|
||||
let result = self.mocker.lock().unwrap().query(counter, statement)?;
|
||||
let result = self
|
||||
.mocker
|
||||
.lock()
|
||||
.map_err(query_err)?
|
||||
.query(counter, statement)?;
|
||||
Ok(result.into_iter().next())
|
||||
}
|
||||
|
||||
@ -140,7 +150,10 @@ impl MockDatabaseConnection {
|
||||
pub fn query_all(&self, statement: Statement) -> Result<Vec<QueryResult>, DbErr> {
|
||||
debug_print!("{}", statement);
|
||||
let counter = self.query_counter.fetch_add(1, Ordering::SeqCst);
|
||||
self.mocker.lock().unwrap().query(counter, statement)
|
||||
self.mocker
|
||||
.lock()
|
||||
.map_err(query_err)?
|
||||
.query(counter, statement)
|
||||
}
|
||||
|
||||
/// Return [QueryResult]s from a multi-query operation
|
||||
@ -158,18 +171,27 @@ impl MockDatabaseConnection {
|
||||
/// Create a statement block of SQL statements that execute together.
|
||||
#[instrument(level = "trace")]
|
||||
pub fn begin(&self) {
|
||||
self.mocker.lock().unwrap().begin()
|
||||
self.mocker
|
||||
.lock()
|
||||
.expect("Failed to acquire mocker")
|
||||
.begin()
|
||||
}
|
||||
|
||||
/// Commit a transaction atomically to the database
|
||||
#[instrument(level = "trace")]
|
||||
pub fn commit(&self) {
|
||||
self.mocker.lock().unwrap().commit()
|
||||
self.mocker
|
||||
.lock()
|
||||
.expect("Failed to acquire mocker")
|
||||
.commit()
|
||||
}
|
||||
|
||||
/// Roll back a faulty transaction
|
||||
#[instrument(level = "trace")]
|
||||
pub fn rollback(&self) {
|
||||
self.mocker.lock().unwrap().rollback()
|
||||
self.mocker
|
||||
.lock()
|
||||
.expect("Failed to acquire mocker")
|
||||
.rollback()
|
||||
}
|
||||
}
|
||||
|
@ -211,7 +211,7 @@ impl SqlxMySqlPoolConnection {
|
||||
.map_err(|e| TransactionError::Connection(e))?;
|
||||
transaction.run(callback).await
|
||||
} else {
|
||||
Err(TransactionError::Connection(DbErr::ConnectionAcquire))
|
||||
Err(DbErr::ConnectionAcquire.into())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -226,7 +226,7 @@ impl SqlxPostgresPoolConnection {
|
||||
.map_err(|e| TransactionError::Connection(e))?;
|
||||
transaction.run(callback).await
|
||||
} else {
|
||||
Err(TransactionError::Connection(DbErr::ConnectionAcquire))
|
||||
Err(DbErr::ConnectionAcquire.into())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -48,8 +48,8 @@ impl SqlxSqliteConnector {
|
||||
.url
|
||||
.parse::<SqliteConnectOptions>()
|
||||
.map_err(sqlx_error_to_conn_err)?;
|
||||
if options.sqlcipher_key.is_some() {
|
||||
opt = opt.pragma("key", options.sqlcipher_key.clone().unwrap());
|
||||
if let Some(sqlcipher_key) = &options.sqlcipher_key {
|
||||
opt = opt.pragma("key", sqlcipher_key.clone());
|
||||
}
|
||||
use sqlx::ConnectOptions;
|
||||
if !options.sqlx_logging {
|
||||
@ -218,7 +218,7 @@ impl SqlxSqlitePoolConnection {
|
||||
.map_err(|e| TransactionError::Connection(e))?;
|
||||
transaction.run(callback).await
|
||||
} else {
|
||||
Err(TransactionError::Connection(DbErr::ConnectionAcquire))
|
||||
Err(DbErr::ConnectionAcquire.into())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -155,7 +155,7 @@ where
|
||||
fn try_get_by<I: crate::ColIdx>(res: &QueryResult, index: I) -> Result<Self, TryGetError> {
|
||||
<T::ValueVec as TryGetable>::try_get_by(res, index)?
|
||||
.into_iter()
|
||||
.map(|value| T::try_from_value(&value).map_err(TryGetError::DbErr))
|
||||
.map(|value| T::try_from_value(&value).map_err(Into::into))
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
@ -174,7 +174,7 @@ where
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate as sea_orm;
|
||||
use crate::{entity::prelude::*, sea_query::SeaRc, *};
|
||||
use crate::{error::*, sea_query::SeaRc, *};
|
||||
use pretty_assertions::assert_eq;
|
||||
|
||||
#[test]
|
||||
@ -210,9 +210,7 @@ mod tests {
|
||||
match v.as_ref() {
|
||||
"B" => Ok(Self::Big),
|
||||
"S" => Ok(Self::Small),
|
||||
_ => Err(DbErr::Type(format!(
|
||||
"unexpected value for Category enum: {v}"
|
||||
))),
|
||||
_ => Err(type_err(format!("unexpected value for Category enum: {v}"))),
|
||||
}
|
||||
}
|
||||
|
||||
@ -241,9 +239,7 @@ mod tests {
|
||||
|
||||
assert_eq!(
|
||||
Category::try_from_value(&"A".to_owned()).err(),
|
||||
Some(DbErr::Type(
|
||||
"unexpected value for Category enum: A".to_owned()
|
||||
))
|
||||
Some(type_err("unexpected value for Category enum: A"))
|
||||
);
|
||||
assert_eq!(
|
||||
Category::try_from_value(&"B".to_owned()).ok(),
|
||||
@ -255,9 +251,7 @@ mod tests {
|
||||
);
|
||||
assert_eq!(
|
||||
DeriveCategory::try_from_value(&"A".to_owned()).err(),
|
||||
Some(DbErr::Type(
|
||||
"unexpected value for DeriveCategory enum: A".to_owned()
|
||||
))
|
||||
Some(type_err("unexpected value for DeriveCategory enum: A"))
|
||||
);
|
||||
assert_eq!(
|
||||
DeriveCategory::try_from_value(&"B".to_owned()).ok(),
|
||||
@ -326,7 +320,7 @@ mod tests {
|
||||
assert_eq!($ident::try_from_value(&-10).ok(), Some($ident::Negative));
|
||||
assert_eq!(
|
||||
$ident::try_from_value(&2).err(),
|
||||
Some(DbErr::Type(format!(
|
||||
Some(type_err(format!(
|
||||
"unexpected value for {} enum: 2",
|
||||
stringify!($ident)
|
||||
)))
|
||||
@ -391,7 +385,7 @@ mod tests {
|
||||
assert_eq!($ident::try_from_value(&0).ok(), Some($ident::Small));
|
||||
assert_eq!(
|
||||
$ident::try_from_value(&2).err(),
|
||||
Some(DbErr::Type(format!(
|
||||
Some(type_err(format!(
|
||||
"unexpected value for {} enum: 2",
|
||||
stringify!($ident)
|
||||
)))
|
||||
|
@ -119,6 +119,10 @@ pub trait ActiveModelTrait: Clone + Debug {
|
||||
}
|
||||
|
||||
/// Get the primary key of the ActiveModel
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if arity of primary key exceed maximum arity of [ValueTuple]
|
||||
#[allow(clippy::question_mark)]
|
||||
fn get_primary_key_value(&self) -> Option<ValueTuple> {
|
||||
let mut cols = <Self::Entity as EntityTrait>::PrimaryKey::iter();
|
||||
@ -547,15 +551,18 @@ pub trait ActiveModelTrait: Clone + Debug {
|
||||
|
||||
// Convert JSON object into ActiveModel via Model
|
||||
let model: <Self::Entity as EntityTrait>::Model =
|
||||
serde_json::from_value(json).map_err(|e| DbErr::Json(e.to_string()))?;
|
||||
serde_json::from_value(json).map_err(json_err)?;
|
||||
let mut am = model.into_active_model();
|
||||
|
||||
// Transform attribute that exists in JSON object into ActiveValue::Set, otherwise ActiveValue::NotSet
|
||||
for (col, json_key_exists) in json_keys {
|
||||
if json_key_exists && !am.is_not_set(col) {
|
||||
am.set(col, am.get(col).unwrap());
|
||||
} else {
|
||||
am.not_set(col);
|
||||
match (json_key_exists, am.get(col)) {
|
||||
(true, ActiveValue::Set(value) | ActiveValue::Unchanged(value)) => {
|
||||
am.set(col, value);
|
||||
}
|
||||
_ => {
|
||||
am.not_set(col);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -823,6 +830,10 @@ where
|
||||
}
|
||||
|
||||
/// Get an owned value of the [ActiveValue]
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if it is [ActiveValue::NotSet]
|
||||
pub fn unwrap(self) -> V {
|
||||
match self {
|
||||
ActiveValue::Set(value) | ActiveValue::Unchanged(value) => value,
|
||||
@ -861,6 +872,9 @@ impl<V> std::convert::AsRef<V> for ActiveValue<V>
|
||||
where
|
||||
V: Into<Value>,
|
||||
{
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if it is [ActiveValue::NotSet]
|
||||
fn as_ref(&self) -> &V {
|
||||
match self {
|
||||
ActiveValue::Set(value) | ActiveValue::Unchanged(value) => value,
|
||||
|
@ -256,6 +256,10 @@ pub trait EntityTrait: EntityName {
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if arity of input values don't match arity of primary key
|
||||
fn find_by_id<T>(values: T) -> Select<Self>
|
||||
where
|
||||
T: Into<<Self::PrimaryKey as PrimaryKeyTrait>::ValueType>,
|
||||
@ -818,6 +822,10 @@ pub trait EntityTrait: EntityName {
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if arity of input values don't match arity of primary key
|
||||
fn delete_by_id<T>(values: T) -> DeleteMany<Self>
|
||||
where
|
||||
T: Into<<Self::PrimaryKey as PrimaryKeyTrait>::ValueType>,
|
||||
|
@ -88,9 +88,10 @@
|
||||
/// // Create a Relation for the Entity
|
||||
/// impl RelationTrait for Relation {
|
||||
/// fn def(&self) -> RelationDef {
|
||||
/// panic!()
|
||||
/// unimplemented!()
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// // Implement user defined operations for CREATE, UPDATE and DELETE operations
|
||||
/// // to create an ActiveModel using the [ActiveModelBehavior]
|
||||
/// impl ActiveModelBehavior for ActiveModel {}
|
||||
|
@ -303,8 +303,8 @@ where
|
||||
rel_type: b.rel_type,
|
||||
from_tbl: b.from_tbl,
|
||||
to_tbl: b.to_tbl,
|
||||
from_col: b.from_col.unwrap(),
|
||||
to_col: b.to_col.unwrap(),
|
||||
from_col: b.from_col.expect("Reference column is not set"),
|
||||
to_col: b.to_col.expect("Owner column is not set"),
|
||||
is_owner: b.is_owner,
|
||||
on_delete: b.on_delete,
|
||||
on_update: b.on_update,
|
||||
|
40
src/error.rs
40
src/error.rs
@ -99,3 +99,43 @@ impl Eq for DbErr {}
|
||||
#[derive(Error, Debug)]
|
||||
#[error("Failed to match \"{0}\" as Column")]
|
||||
pub struct ColumnFromStrErr(pub String);
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub(crate) fn conn_err<T>(s: T) -> DbErr
|
||||
where
|
||||
T: ToString,
|
||||
{
|
||||
DbErr::Conn(RuntimeErr::Internal(s.to_string()))
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub(crate) fn exec_err<T>(s: T) -> DbErr
|
||||
where
|
||||
T: ToString,
|
||||
{
|
||||
DbErr::Exec(RuntimeErr::Internal(s.to_string()))
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub(crate) fn query_err<T>(s: T) -> DbErr
|
||||
where
|
||||
T: ToString,
|
||||
{
|
||||
DbErr::Query(RuntimeErr::Internal(s.to_string()))
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub(crate) fn type_err<T>(s: T) -> DbErr
|
||||
where
|
||||
T: ToString,
|
||||
{
|
||||
DbErr::Type(s.to_string())
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub(crate) fn json_err<T>(s: T) -> DbErr
|
||||
where
|
||||
T: ToString,
|
||||
{
|
||||
DbErr::Json(s.to_string())
|
||||
}
|
||||
|
@ -28,6 +28,10 @@ pub(crate) enum ExecResultHolder {
|
||||
|
||||
impl ExecResult {
|
||||
/// Get the last id after `AUTOINCREMENT` is done on the primary key
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Postgres does not support retrieving last insert id this way except through `RETURNING` clause
|
||||
pub fn last_insert_id(&self) -> u64 {
|
||||
match &self.result {
|
||||
#[cfg(feature = "sqlx-mysql")]
|
||||
@ -40,7 +44,7 @@ impl ExecResult {
|
||||
ExecResultHolder::SqlxSqlite(result) => {
|
||||
let last_insert_rowid = result.last_insert_rowid();
|
||||
if last_insert_rowid < 0 {
|
||||
panic!("negative last_insert_rowid")
|
||||
unreachable!("negative last_insert_rowid")
|
||||
} else {
|
||||
last_insert_rowid as u64
|
||||
}
|
||||
|
@ -126,7 +126,6 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(unused_variables, unreachable_code)]
|
||||
async fn exec_insert<A, C>(
|
||||
primary_key: Option<ValueTuple>,
|
||||
statement: Statement,
|
||||
|
@ -1,7 +1,13 @@
|
||||
use crate::{error::*, SelectGetableValue, SelectorRaw, Statement};
|
||||
use std::fmt;
|
||||
|
||||
#[cfg(feature = "mock")]
|
||||
use crate::debug_print;
|
||||
use crate::{DbErr, SelectGetableValue, SelectorRaw, Statement};
|
||||
use std::fmt;
|
||||
|
||||
#[cfg(feature = "sqlx-dep")]
|
||||
use crate::driver::*;
|
||||
#[cfg(feature = "sqlx-dep")]
|
||||
use sqlx::Row;
|
||||
|
||||
/// Defines the result of a query operation on a Model
|
||||
#[derive(Debug)]
|
||||
@ -52,12 +58,18 @@ impl From<TryGetError> for DbErr {
|
||||
match e {
|
||||
TryGetError::DbErr(e) => e,
|
||||
TryGetError::Null(s) => {
|
||||
DbErr::Type(format!("A null value was encountered while decoding {s}"))
|
||||
type_err(format!("A null value was encountered while decoding {s}"))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<DbErr> for TryGetError {
|
||||
fn from(e: DbErr) -> TryGetError {
|
||||
Self::DbErr(e)
|
||||
}
|
||||
}
|
||||
|
||||
// QueryResult //
|
||||
|
||||
impl QueryResult {
|
||||
@ -236,26 +248,20 @@ macro_rules! try_getable_all {
|
||||
fn try_get_by<I: ColIdx>(res: &QueryResult, idx: I) -> Result<Self, TryGetError> {
|
||||
match &res.row {
|
||||
#[cfg(feature = "sqlx-mysql")]
|
||||
QueryResultRow::SqlxMySql(row) => {
|
||||
use sqlx::Row;
|
||||
row.try_get::<Option<$type>, _>(idx.as_sqlx_mysql_index())
|
||||
.map_err(|e| TryGetError::DbErr(crate::sqlx_error_to_query_err(e)))
|
||||
.and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx)))
|
||||
}
|
||||
QueryResultRow::SqlxMySql(row) => row
|
||||
.try_get::<Option<$type>, _>(idx.as_sqlx_mysql_index())
|
||||
.map_err(|e| sqlx_error_to_query_err(e).into())
|
||||
.and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx))),
|
||||
#[cfg(feature = "sqlx-postgres")]
|
||||
QueryResultRow::SqlxPostgres(row) => {
|
||||
use sqlx::Row;
|
||||
row.try_get::<Option<$type>, _>(idx.as_sqlx_postgres_index())
|
||||
.map_err(|e| TryGetError::DbErr(crate::sqlx_error_to_query_err(e)))
|
||||
.and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx)))
|
||||
}
|
||||
QueryResultRow::SqlxPostgres(row) => row
|
||||
.try_get::<Option<$type>, _>(idx.as_sqlx_postgres_index())
|
||||
.map_err(|e| sqlx_error_to_query_err(e).into())
|
||||
.and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx))),
|
||||
#[cfg(feature = "sqlx-sqlite")]
|
||||
QueryResultRow::SqlxSqlite(row) => {
|
||||
use sqlx::Row;
|
||||
row.try_get::<Option<$type>, _>(idx.as_sqlx_sqlite_index())
|
||||
.map_err(|e| TryGetError::DbErr(crate::sqlx_error_to_query_err(e)))
|
||||
.and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx)))
|
||||
}
|
||||
QueryResultRow::SqlxSqlite(row) => row
|
||||
.try_get::<Option<$type>, _>(idx.as_sqlx_sqlite_index())
|
||||
.map_err(|e| sqlx_error_to_query_err(e).into())
|
||||
.and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx))),
|
||||
#[cfg(feature = "mock")]
|
||||
QueryResultRow::Mock(row) => row.try_get(idx).map_err(|e| {
|
||||
debug_print!("{:#?}", e.to_string());
|
||||
@ -276,23 +282,21 @@ macro_rules! try_getable_unsigned {
|
||||
fn try_get_by<I: ColIdx>(res: &QueryResult, idx: I) -> Result<Self, TryGetError> {
|
||||
match &res.row {
|
||||
#[cfg(feature = "sqlx-mysql")]
|
||||
QueryResultRow::SqlxMySql(row) => {
|
||||
use sqlx::Row;
|
||||
row.try_get::<Option<$type>, _>(idx.as_sqlx_mysql_index())
|
||||
.map_err(|e| TryGetError::DbErr(crate::sqlx_error_to_query_err(e)))
|
||||
.and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx)))
|
||||
}
|
||||
QueryResultRow::SqlxMySql(row) => row
|
||||
.try_get::<Option<$type>, _>(idx.as_sqlx_mysql_index())
|
||||
.map_err(|e| sqlx_error_to_query_err(e).into())
|
||||
.and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx))),
|
||||
#[cfg(feature = "sqlx-postgres")]
|
||||
QueryResultRow::SqlxPostgres(_) => {
|
||||
panic!("{} unsupported by sqlx-postgres", stringify!($type))
|
||||
}
|
||||
QueryResultRow::SqlxPostgres(_) => Err(type_err(format!(
|
||||
"{} unsupported by sqlx-postgres",
|
||||
stringify!($type)
|
||||
))
|
||||
.into()),
|
||||
#[cfg(feature = "sqlx-sqlite")]
|
||||
QueryResultRow::SqlxSqlite(row) => {
|
||||
use sqlx::Row;
|
||||
row.try_get::<Option<$type>, _>(idx.as_sqlx_sqlite_index())
|
||||
.map_err(|e| TryGetError::DbErr(crate::sqlx_error_to_query_err(e)))
|
||||
.and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx)))
|
||||
}
|
||||
QueryResultRow::SqlxSqlite(row) => row
|
||||
.try_get::<Option<$type>, _>(idx.as_sqlx_sqlite_index())
|
||||
.map_err(|e| sqlx_error_to_query_err(e).into())
|
||||
.and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx))),
|
||||
#[cfg(feature = "mock")]
|
||||
QueryResultRow::Mock(row) => row.try_get(idx).map_err(|e| {
|
||||
debug_print!("{:#?}", e.to_string());
|
||||
@ -313,20 +317,22 @@ macro_rules! try_getable_mysql {
|
||||
fn try_get_by<I: ColIdx>(res: &QueryResult, idx: I) -> Result<Self, TryGetError> {
|
||||
match &res.row {
|
||||
#[cfg(feature = "sqlx-mysql")]
|
||||
QueryResultRow::SqlxMySql(row) => {
|
||||
use sqlx::Row;
|
||||
row.try_get::<Option<$type>, _>(idx.as_sqlx_mysql_index())
|
||||
.map_err(|e| TryGetError::DbErr(crate::sqlx_error_to_query_err(e)))
|
||||
.and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx)))
|
||||
}
|
||||
QueryResultRow::SqlxMySql(row) => row
|
||||
.try_get::<Option<$type>, _>(idx.as_sqlx_mysql_index())
|
||||
.map_err(|e| sqlx_error_to_query_err(e).into())
|
||||
.and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx))),
|
||||
#[cfg(feature = "sqlx-postgres")]
|
||||
QueryResultRow::SqlxPostgres(_) => {
|
||||
panic!("{} unsupported by sqlx-postgres", stringify!($type))
|
||||
}
|
||||
QueryResultRow::SqlxPostgres(_) => Err(type_err(format!(
|
||||
"{} unsupported by sqlx-postgres",
|
||||
stringify!($type)
|
||||
))
|
||||
.into()),
|
||||
#[cfg(feature = "sqlx-sqlite")]
|
||||
QueryResultRow::SqlxSqlite(_) => {
|
||||
panic!("{} unsupported by sqlx-sqlite", stringify!($type))
|
||||
}
|
||||
QueryResultRow::SqlxSqlite(_) => Err(type_err(format!(
|
||||
"{} unsupported by sqlx-sqlite",
|
||||
stringify!($type)
|
||||
))
|
||||
.into()),
|
||||
#[cfg(feature = "mock")]
|
||||
QueryResultRow::Mock(row) => row.try_get(idx).map_err(|e| {
|
||||
debug_print!("{:#?}", e.to_string());
|
||||
@ -350,25 +356,21 @@ macro_rules! try_getable_date_time {
|
||||
#[cfg(feature = "sqlx-mysql")]
|
||||
QueryResultRow::SqlxMySql(row) => {
|
||||
use chrono::{DateTime, Utc};
|
||||
use sqlx::Row;
|
||||
row.try_get::<Option<DateTime<Utc>>, _>(idx.as_sqlx_mysql_index())
|
||||
.map_err(|e| TryGetError::DbErr(crate::sqlx_error_to_query_err(e)))
|
||||
.map_err(|e| sqlx_error_to_query_err(e).into())
|
||||
.and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx)))
|
||||
.map(|v| v.into())
|
||||
}
|
||||
#[cfg(feature = "sqlx-postgres")]
|
||||
QueryResultRow::SqlxPostgres(row) => {
|
||||
use sqlx::Row;
|
||||
row.try_get::<Option<$type>, _>(idx.as_sqlx_postgres_index())
|
||||
.map_err(|e| TryGetError::DbErr(crate::sqlx_error_to_query_err(e)))
|
||||
.and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx)))
|
||||
}
|
||||
QueryResultRow::SqlxPostgres(row) => row
|
||||
.try_get::<Option<$type>, _>(idx.as_sqlx_postgres_index())
|
||||
.map_err(|e| sqlx_error_to_query_err(e).into())
|
||||
.and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx))),
|
||||
#[cfg(feature = "sqlx-sqlite")]
|
||||
QueryResultRow::SqlxSqlite(row) => {
|
||||
use chrono::{DateTime, Utc};
|
||||
use sqlx::Row;
|
||||
row.try_get::<Option<DateTime<Utc>>, _>(idx.as_sqlx_sqlite_index())
|
||||
.map_err(|e| TryGetError::DbErr(crate::sqlx_error_to_query_err(e)))
|
||||
.map_err(|e| sqlx_error_to_query_err(e).into())
|
||||
.and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx)))
|
||||
.map(|v| v.into())
|
||||
}
|
||||
@ -440,32 +442,28 @@ impl TryGetable for Decimal {
|
||||
fn try_get_by<I: ColIdx>(res: &QueryResult, idx: I) -> Result<Self, TryGetError> {
|
||||
match &res.row {
|
||||
#[cfg(feature = "sqlx-mysql")]
|
||||
QueryResultRow::SqlxMySql(row) => {
|
||||
use sqlx::Row;
|
||||
row.try_get::<Option<Decimal>, _>(idx.as_sqlx_mysql_index())
|
||||
.map_err(|e| TryGetError::DbErr(crate::sqlx_error_to_query_err(e)))
|
||||
.and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx)))
|
||||
}
|
||||
QueryResultRow::SqlxMySql(row) => row
|
||||
.try_get::<Option<Decimal>, _>(idx.as_sqlx_mysql_index())
|
||||
.map_err(|e| sqlx_error_to_query_err(e).into())
|
||||
.and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx))),
|
||||
#[cfg(feature = "sqlx-postgres")]
|
||||
QueryResultRow::SqlxPostgres(row) => {
|
||||
use sqlx::Row;
|
||||
row.try_get::<Option<Decimal>, _>(idx.as_sqlx_postgres_index())
|
||||
.map_err(|e| TryGetError::DbErr(crate::sqlx_error_to_query_err(e)))
|
||||
.and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx)))
|
||||
}
|
||||
QueryResultRow::SqlxPostgres(row) => row
|
||||
.try_get::<Option<Decimal>, _>(idx.as_sqlx_postgres_index())
|
||||
.map_err(|e| sqlx_error_to_query_err(e).into())
|
||||
.and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx))),
|
||||
#[cfg(feature = "sqlx-sqlite")]
|
||||
QueryResultRow::SqlxSqlite(row) => {
|
||||
use sqlx::Row;
|
||||
let val: Option<f64> = row
|
||||
.try_get(idx.as_sqlx_sqlite_index())
|
||||
.map_err(|e| TryGetError::DbErr(crate::sqlx_error_to_query_err(e)))?;
|
||||
.map_err(sqlx_error_to_query_err)?;
|
||||
match val {
|
||||
Some(v) => Decimal::try_from(v).map_err(|e| {
|
||||
TryGetError::DbErr(DbErr::TryIntoErr {
|
||||
DbErr::TryIntoErr {
|
||||
from: "f64",
|
||||
into: "Decimal",
|
||||
source: Box::new(e),
|
||||
})
|
||||
}
|
||||
.into()
|
||||
}),
|
||||
None => Err(err_null_idx_col(idx)),
|
||||
}
|
||||
@ -491,32 +489,28 @@ impl TryGetable for BigDecimal {
|
||||
fn try_get_by<I: ColIdx>(res: &QueryResult, idx: I) -> Result<Self, TryGetError> {
|
||||
match &res.row {
|
||||
#[cfg(feature = "sqlx-mysql")]
|
||||
QueryResultRow::SqlxMySql(row) => {
|
||||
use sqlx::Row;
|
||||
row.try_get::<Option<BigDecimal>, _>(idx.as_sqlx_mysql_index())
|
||||
.map_err(|e| TryGetError::DbErr(crate::sqlx_error_to_query_err(e)))
|
||||
.and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx)))
|
||||
}
|
||||
QueryResultRow::SqlxMySql(row) => row
|
||||
.try_get::<Option<BigDecimal>, _>(idx.as_sqlx_mysql_index())
|
||||
.map_err(|e| sqlx_error_to_query_err(e).into())
|
||||
.and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx))),
|
||||
#[cfg(feature = "sqlx-postgres")]
|
||||
QueryResultRow::SqlxPostgres(row) => {
|
||||
use sqlx::Row;
|
||||
row.try_get::<Option<BigDecimal>, _>(idx.as_sqlx_postgres_index())
|
||||
.map_err(|e| TryGetError::DbErr(crate::sqlx_error_to_query_err(e)))
|
||||
.and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx)))
|
||||
}
|
||||
QueryResultRow::SqlxPostgres(row) => row
|
||||
.try_get::<Option<BigDecimal>, _>(idx.as_sqlx_postgres_index())
|
||||
.map_err(|e| sqlx_error_to_query_err(e).into())
|
||||
.and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx))),
|
||||
#[cfg(feature = "sqlx-sqlite")]
|
||||
QueryResultRow::SqlxSqlite(row) => {
|
||||
use sqlx::Row;
|
||||
let val: Option<f64> = row
|
||||
.try_get(idx.as_sqlx_sqlite_index())
|
||||
.map_err(|e| TryGetError::DbErr(crate::sqlx_error_to_query_err(e)))?;
|
||||
.map_err(sqlx_error_to_query_err)?;
|
||||
match val {
|
||||
Some(v) => BigDecimal::try_from(v).map_err(|e| {
|
||||
TryGetError::DbErr(DbErr::TryIntoErr {
|
||||
DbErr::TryIntoErr {
|
||||
from: "f64",
|
||||
into: "BigDecimal",
|
||||
source: Box::new(e),
|
||||
})
|
||||
}
|
||||
.into()
|
||||
}),
|
||||
None => Err(err_null_idx_col(idx)),
|
||||
}
|
||||
@ -541,26 +535,20 @@ macro_rules! try_getable_uuid {
|
||||
fn try_get_by<I: ColIdx>(res: &QueryResult, idx: I) -> Result<Self, TryGetError> {
|
||||
let res: Result<uuid::Uuid, TryGetError> = match &res.row {
|
||||
#[cfg(feature = "sqlx-mysql")]
|
||||
QueryResultRow::SqlxMySql(row) => {
|
||||
use sqlx::Row;
|
||||
row.try_get::<Option<uuid::Uuid>, _>(idx.as_sqlx_mysql_index())
|
||||
.map_err(|e| TryGetError::DbErr(crate::sqlx_error_to_query_err(e)))
|
||||
.and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx)))
|
||||
}
|
||||
QueryResultRow::SqlxMySql(row) => row
|
||||
.try_get::<Option<uuid::Uuid>, _>(idx.as_sqlx_mysql_index())
|
||||
.map_err(|e| sqlx_error_to_query_err(e).into())
|
||||
.and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx))),
|
||||
#[cfg(feature = "sqlx-postgres")]
|
||||
QueryResultRow::SqlxPostgres(row) => {
|
||||
use sqlx::Row;
|
||||
row.try_get::<Option<uuid::Uuid>, _>(idx.as_sqlx_postgres_index())
|
||||
.map_err(|e| TryGetError::DbErr(crate::sqlx_error_to_query_err(e)))
|
||||
.and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx)))
|
||||
}
|
||||
QueryResultRow::SqlxPostgres(row) => row
|
||||
.try_get::<Option<uuid::Uuid>, _>(idx.as_sqlx_postgres_index())
|
||||
.map_err(|e| sqlx_error_to_query_err(e).into())
|
||||
.and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx))),
|
||||
#[cfg(feature = "sqlx-sqlite")]
|
||||
QueryResultRow::SqlxSqlite(row) => {
|
||||
use sqlx::Row;
|
||||
row.try_get::<Option<uuid::Uuid>, _>(idx.as_sqlx_sqlite_index())
|
||||
.map_err(|e| TryGetError::DbErr(crate::sqlx_error_to_query_err(e)))
|
||||
.and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx)))
|
||||
}
|
||||
QueryResultRow::SqlxSqlite(row) => row
|
||||
.try_get::<Option<uuid::Uuid>, _>(idx.as_sqlx_sqlite_index())
|
||||
.map_err(|e| sqlx_error_to_query_err(e).into())
|
||||
.and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx))),
|
||||
#[cfg(feature = "mock")]
|
||||
#[allow(unused_variables)]
|
||||
QueryResultRow::Mock(row) => row.try_get::<uuid::Uuid, _>(idx).map_err(|e| {
|
||||
@ -596,30 +584,25 @@ impl TryGetable for u32 {
|
||||
fn try_get_by<I: ColIdx>(res: &QueryResult, idx: I) -> Result<Self, TryGetError> {
|
||||
match &res.row {
|
||||
#[cfg(feature = "sqlx-mysql")]
|
||||
QueryResultRow::SqlxMySql(row) => {
|
||||
use sqlx::Row;
|
||||
row.try_get::<Option<u32>, _>(idx.as_sqlx_mysql_index())
|
||||
.map_err(|e| TryGetError::DbErr(crate::sqlx_error_to_query_err(e)))
|
||||
.and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx)))
|
||||
}
|
||||
QueryResultRow::SqlxMySql(row) => row
|
||||
.try_get::<Option<u32>, _>(idx.as_sqlx_mysql_index())
|
||||
.map_err(|e| sqlx_error_to_query_err(e).into())
|
||||
.and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx))),
|
||||
#[cfg(feature = "sqlx-postgres")]
|
||||
QueryResultRow::SqlxPostgres(row) => {
|
||||
use sqlx::postgres::types::Oid;
|
||||
// Since 0.6.0, SQLx has dropped direct mapping from PostgreSQL's OID to Rust's `u32`;
|
||||
// Instead, `u32` was wrapped by a `sqlx::Oid`.
|
||||
use sqlx::Row;
|
||||
row.try_get::<Option<Oid>, _>(idx.as_sqlx_postgres_index())
|
||||
.map_err(|e| TryGetError::DbErr(crate::sqlx_error_to_query_err(e)))
|
||||
.map_err(|e| sqlx_error_to_query_err(e).into())
|
||||
.and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx)))
|
||||
.map(|oid| oid.0)
|
||||
}
|
||||
#[cfg(feature = "sqlx-sqlite")]
|
||||
QueryResultRow::SqlxSqlite(row) => {
|
||||
use sqlx::Row;
|
||||
row.try_get::<Option<u32>, _>(idx.as_sqlx_sqlite_index())
|
||||
.map_err(|e| TryGetError::DbErr(crate::sqlx_error_to_query_err(e)))
|
||||
.and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx)))
|
||||
}
|
||||
QueryResultRow::SqlxSqlite(row) => row
|
||||
.try_get::<Option<u32>, _>(idx.as_sqlx_sqlite_index())
|
||||
.map_err(|e| sqlx_error_to_query_err(e).into())
|
||||
.and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx))),
|
||||
#[cfg(feature = "mock")]
|
||||
#[allow(unused_variables)]
|
||||
QueryResultRow::Mock(row) => row.try_get(idx).map_err(|e| {
|
||||
@ -649,20 +632,22 @@ mod postgres_array {
|
||||
fn try_get_by<I: ColIdx>(res: &QueryResult, idx: I) -> Result<Self, TryGetError> {
|
||||
match &res.row {
|
||||
#[cfg(feature = "sqlx-mysql")]
|
||||
QueryResultRow::SqlxMySql(_) => {
|
||||
panic!("{} unsupported by sqlx-mysql", stringify!($type))
|
||||
}
|
||||
QueryResultRow::SqlxMySql(_) => Err(type_err(format!(
|
||||
"{} unsupported by sqlx-mysql",
|
||||
stringify!($type)
|
||||
))
|
||||
.into()),
|
||||
#[cfg(feature = "sqlx-postgres")]
|
||||
QueryResultRow::SqlxPostgres(row) => {
|
||||
use sqlx::Row;
|
||||
row.try_get::<Option<Vec<$type>>, _>(idx.as_sqlx_postgres_index())
|
||||
.map_err(|e| TryGetError::DbErr(crate::sqlx_error_to_query_err(e)))
|
||||
.and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx)))
|
||||
}
|
||||
QueryResultRow::SqlxPostgres(row) => row
|
||||
.try_get::<Option<Vec<$type>>, _>(idx.as_sqlx_postgres_index())
|
||||
.map_err(|e| sqlx_error_to_query_err(e).into())
|
||||
.and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx))),
|
||||
#[cfg(feature = "sqlx-sqlite")]
|
||||
QueryResultRow::SqlxSqlite(_) => {
|
||||
panic!("{} unsupported by sqlx-sqlite", stringify!($type))
|
||||
}
|
||||
QueryResultRow::SqlxSqlite(_) => Err(type_err(format!(
|
||||
"{} unsupported by sqlx-sqlite",
|
||||
stringify!($type)
|
||||
))
|
||||
.into()),
|
||||
#[cfg(feature = "mock")]
|
||||
#[allow(unused_variables)]
|
||||
QueryResultRow::Mock(row) => row.try_get(idx).map_err(|e| {
|
||||
@ -733,20 +718,22 @@ mod postgres_array {
|
||||
fn try_get_by<I: ColIdx>(res: &QueryResult, idx: I) -> Result<Self, TryGetError> {
|
||||
let res: Result<Vec<uuid::Uuid>, TryGetError> = match &res.row {
|
||||
#[cfg(feature = "sqlx-mysql")]
|
||||
QueryResultRow::SqlxMySql(row) => {
|
||||
panic!("{} unsupported by sqlx-mysql", stringify!($type))
|
||||
}
|
||||
QueryResultRow::SqlxMySql(_) => Err(type_err(format!(
|
||||
"{} unsupported by sqlx-mysql",
|
||||
stringify!($type)
|
||||
))
|
||||
.into()),
|
||||
#[cfg(feature = "sqlx-postgres")]
|
||||
QueryResultRow::SqlxPostgres(row) => {
|
||||
use sqlx::Row;
|
||||
row.try_get::<Option<Vec<uuid::Uuid>>, _>(idx.as_sqlx_postgres_index())
|
||||
.map_err(|e| TryGetError::DbErr(crate::sqlx_error_to_query_err(e)))
|
||||
.and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx)))
|
||||
}
|
||||
QueryResultRow::SqlxPostgres(row) => row
|
||||
.try_get::<Option<Vec<uuid::Uuid>>, _>(idx.as_sqlx_postgres_index())
|
||||
.map_err(|e| sqlx_error_to_query_err(e).into())
|
||||
.and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx))),
|
||||
#[cfg(feature = "sqlx-sqlite")]
|
||||
QueryResultRow::SqlxSqlite(_) => {
|
||||
panic!("{} unsupported by sqlx-sqlite", stringify!($type))
|
||||
}
|
||||
QueryResultRow::SqlxSqlite(_) => Err(type_err(format!(
|
||||
"{} unsupported by sqlx-sqlite",
|
||||
stringify!($type)
|
||||
))
|
||||
.into()),
|
||||
#[cfg(feature = "mock")]
|
||||
QueryResultRow::Mock(row) => {
|
||||
row.try_get::<Vec<uuid::Uuid>, _>(idx).map_err(|e| {
|
||||
@ -784,23 +771,24 @@ mod postgres_array {
|
||||
match &res.row {
|
||||
#[cfg(feature = "sqlx-mysql")]
|
||||
QueryResultRow::SqlxMySql(_) => {
|
||||
panic!("{} unsupported by sqlx-mysql", stringify!($type))
|
||||
Err(type_err(format!("{} unsupported by sqlx-mysql", stringify!($type))).into())
|
||||
}
|
||||
#[cfg(feature = "sqlx-postgres")]
|
||||
QueryResultRow::SqlxPostgres(row) => {
|
||||
use sqlx::postgres::types::Oid;
|
||||
// Since 0.6.0, SQLx has dropped direct mapping from PostgreSQL's OID to Rust's `u32`;
|
||||
// Instead, `u32` was wrapped by a `sqlx::Oid`.
|
||||
use sqlx::Row;
|
||||
row.try_get::<Option<Vec<Oid>>, _>(idx.as_sqlx_postgres_index())
|
||||
.map_err(|e| TryGetError::DbErr(crate::sqlx_error_to_query_err(e)))
|
||||
.map_err(|e| sqlx_error_to_query_err(e).into())
|
||||
.and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx)))
|
||||
.map(|oids| oids.into_iter().map(|oid| oid.0).collect())
|
||||
}
|
||||
#[cfg(feature = "sqlx-sqlite")]
|
||||
QueryResultRow::SqlxSqlite(_) => {
|
||||
panic!("{} unsupported by sqlx-sqlite", stringify!($type))
|
||||
}
|
||||
QueryResultRow::SqlxSqlite(_) => Err(type_err(format!(
|
||||
"{} unsupported by sqlx-sqlite",
|
||||
stringify!($type)
|
||||
))
|
||||
.into()),
|
||||
#[cfg(feature = "mock")]
|
||||
#[allow(unused_variables)]
|
||||
QueryResultRow::Mock(row) => row.try_get(idx).map_err(|e| {
|
||||
@ -1050,11 +1038,12 @@ where
|
||||
|
||||
fn try_get_many_with_slice_len_of(len: usize, cols: &[String]) -> Result<(), TryGetError> {
|
||||
if cols.len() < len {
|
||||
Err(TryGetError::DbErr(DbErr::Type(format!(
|
||||
Err(type_err(format!(
|
||||
"Expect {} column names supplied but got slice of length {}",
|
||||
len,
|
||||
cols.len()
|
||||
))))
|
||||
))
|
||||
.into())
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
@ -1073,26 +1062,20 @@ where
|
||||
fn try_get_from_json<I: ColIdx>(res: &QueryResult, idx: I) -> Result<Self, TryGetError> {
|
||||
match &res.row {
|
||||
#[cfg(feature = "sqlx-mysql")]
|
||||
QueryResultRow::SqlxMySql(row) => {
|
||||
use sqlx::Row;
|
||||
row.try_get::<Option<sqlx::types::Json<Self>>, _>(idx.as_sqlx_mysql_index())
|
||||
.map_err(|e| TryGetError::DbErr(crate::sqlx_error_to_query_err(e)))
|
||||
.and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx)).map(|json| json.0))
|
||||
}
|
||||
QueryResultRow::SqlxMySql(row) => row
|
||||
.try_get::<Option<sqlx::types::Json<Self>>, _>(idx.as_sqlx_mysql_index())
|
||||
.map_err(|e| sqlx_error_to_query_err(e).into())
|
||||
.and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx)).map(|json| json.0)),
|
||||
#[cfg(feature = "sqlx-postgres")]
|
||||
QueryResultRow::SqlxPostgres(row) => {
|
||||
use sqlx::Row;
|
||||
row.try_get::<Option<sqlx::types::Json<Self>>, _>(idx.as_sqlx_postgres_index())
|
||||
.map_err(|e| TryGetError::DbErr(crate::sqlx_error_to_query_err(e)))
|
||||
.and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx)).map(|json| json.0))
|
||||
}
|
||||
QueryResultRow::SqlxPostgres(row) => row
|
||||
.try_get::<Option<sqlx::types::Json<Self>>, _>(idx.as_sqlx_postgres_index())
|
||||
.map_err(|e| sqlx_error_to_query_err(e).into())
|
||||
.and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx)).map(|json| json.0)),
|
||||
#[cfg(feature = "sqlx-sqlite")]
|
||||
QueryResultRow::SqlxSqlite(row) => {
|
||||
use sqlx::Row;
|
||||
row.try_get::<Option<sqlx::types::Json<Self>>, _>(idx.as_sqlx_sqlite_index())
|
||||
.map_err(|e| TryGetError::DbErr(crate::sqlx_error_to_query_err(e)))
|
||||
.and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx)).map(|json| json.0))
|
||||
}
|
||||
QueryResultRow::SqlxSqlite(row) => row
|
||||
.try_get::<Option<sqlx::types::Json<Self>>, _>(idx.as_sqlx_sqlite_index())
|
||||
.map_err(|e| sqlx_error_to_query_err(e).into())
|
||||
.and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx)).map(|json| json.0)),
|
||||
#[cfg(feature = "mock")]
|
||||
QueryResultRow::Mock(row) => row
|
||||
.try_get::<serde_json::Value, I>(idx)
|
||||
@ -1100,10 +1083,7 @@ where
|
||||
debug_print!("{:#?}", e.to_string());
|
||||
err_null_idx_col(idx)
|
||||
})
|
||||
.and_then(|json| {
|
||||
serde_json::from_value(json)
|
||||
.map_err(|e| TryGetError::DbErr(DbErr::Json(e.to_string())))
|
||||
}),
|
||||
.and_then(|json| serde_json::from_value(json).map_err(|e| json_err(e).into())),
|
||||
#[allow(unreachable_patterns)]
|
||||
_ => unreachable!(),
|
||||
}
|
||||
|
@ -906,11 +906,11 @@ where
|
||||
}
|
||||
}
|
||||
}
|
||||
if r.is_some() {
|
||||
acc.push((l, vec![r.unwrap()]));
|
||||
} else {
|
||||
acc.push((l, vec![]));
|
||||
}
|
||||
let rows = match r {
|
||||
Some(r) => vec![r],
|
||||
None => vec![],
|
||||
};
|
||||
acc.push((l, rows));
|
||||
}
|
||||
acc
|
||||
}
|
||||
|
@ -2,6 +2,8 @@
|
||||
#![warn(missing_docs)]
|
||||
#![deny(
|
||||
missing_debug_implementations,
|
||||
clippy::missing_panics_doc,
|
||||
clippy::unwrap_used,
|
||||
clippy::print_stderr,
|
||||
clippy::print_stdout
|
||||
)]
|
||||
|
@ -1,6 +1,6 @@
|
||||
use crate::{
|
||||
ActiveModelTrait, ColumnTrait, EntityTrait, IntoActiveModel, Iterable, PrimaryKeyToColumn,
|
||||
QueryFilter, QueryTrait,
|
||||
ActiveModelTrait, ActiveValue, ColumnTrait, EntityTrait, IntoActiveModel, Iterable,
|
||||
PrimaryKeyToColumn, QueryFilter, QueryTrait,
|
||||
};
|
||||
use core::marker::PhantomData;
|
||||
use sea_query::DeleteStatement;
|
||||
@ -109,10 +109,11 @@ where
|
||||
for key in <A::Entity as EntityTrait>::PrimaryKey::iter() {
|
||||
let col = key.into_column();
|
||||
let av = self.model.get(col);
|
||||
if av.is_set() || av.is_unchanged() {
|
||||
self = self.filter(col.eq(av.unwrap()));
|
||||
} else {
|
||||
panic!("PrimaryKey is not set");
|
||||
match av {
|
||||
ActiveValue::Set(value) | ActiveValue::Unchanged(value) => {
|
||||
self = self.filter(col.eq(value));
|
||||
}
|
||||
ActiveValue::NotSet => panic!("PrimaryKey is not set"),
|
||||
}
|
||||
}
|
||||
self
|
||||
|
@ -1,5 +1,5 @@
|
||||
use crate::{
|
||||
ActiveModelTrait, ColumnTrait, EntityName, EntityTrait, IntoActiveModel, Iterable,
|
||||
ActiveModelTrait, ActiveValue, ColumnTrait, EntityName, EntityTrait, IntoActiveModel, Iterable,
|
||||
PrimaryKeyTrait, QueryTrait,
|
||||
};
|
||||
use core::marker::PhantomData;
|
||||
@ -109,6 +109,10 @@ where
|
||||
}
|
||||
|
||||
/// Add a Model to Self
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if the column value has discrepancy across rows
|
||||
#[allow(clippy::should_implement_trait)]
|
||||
pub fn add<M>(mut self, m: M) -> Self
|
||||
where
|
||||
@ -132,9 +136,12 @@ where
|
||||
} else if self.columns[idx] != av_has_val {
|
||||
panic!("columns mismatch");
|
||||
}
|
||||
if av_has_val {
|
||||
columns.push(col);
|
||||
values.push(col.save_as(Expr::val(av.into_value().unwrap())));
|
||||
match av {
|
||||
ActiveValue::Set(value) | ActiveValue::Unchanged(value) => {
|
||||
columns.push(col);
|
||||
values.push(col.save_as(Expr::val(value)));
|
||||
}
|
||||
ActiveValue::NotSet => {}
|
||||
}
|
||||
}
|
||||
self.query.columns(columns);
|
||||
|
@ -1,4 +1,4 @@
|
||||
use crate::{DbErr, FromQueryResult, QueryResult};
|
||||
use crate::{error::*, FromQueryResult, QueryResult};
|
||||
use serde_json::Map;
|
||||
pub use serde_json::Value as JsonValue;
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
use crate::{
|
||||
ColumnTrait, Condition, ConnectionTrait, DbErr, EntityTrait, Identity, ModelTrait, QueryFilter,
|
||||
Related, RelationType, Select,
|
||||
error::*, ColumnTrait, Condition, ConnectionTrait, DbErr, EntityTrait, Identity, ModelTrait,
|
||||
QueryFilter, Related, RelationType, Select,
|
||||
};
|
||||
use async_trait::async_trait;
|
||||
use sea_query::{Expr, IntoColumnRef, SimpleExpr, ValueTuple};
|
||||
@ -76,9 +76,7 @@ where
|
||||
// we verify that is has_one relation
|
||||
match (rel_def).rel_type {
|
||||
RelationType::HasOne => (),
|
||||
RelationType::HasMany => {
|
||||
return Err(DbErr::Type("Relation is HasMany instead of HasOne".into()))
|
||||
}
|
||||
RelationType::HasMany => return Err(type_err("Relation is HasMany instead of HasOne")),
|
||||
}
|
||||
|
||||
let keys: Vec<ValueTuple> = self
|
||||
@ -126,9 +124,7 @@ where
|
||||
// we verify that is has_many relation
|
||||
match (rel_def).rel_type {
|
||||
RelationType::HasMany => (),
|
||||
RelationType::HasOne => {
|
||||
return Err(DbErr::Type("Relation is HasOne instead of HasMany".into()))
|
||||
}
|
||||
RelationType::HasOne => return Err(type_err("Relation is HasOne instead of HasMany")),
|
||||
}
|
||||
|
||||
let keys: Vec<ValueTuple> = self
|
||||
|
@ -1,6 +1,6 @@
|
||||
use crate::{
|
||||
ActiveModelTrait, ColumnTrait, EntityTrait, Iterable, PrimaryKeyToColumn, QueryFilter,
|
||||
QueryTrait,
|
||||
ActiveModelTrait, ActiveValue, ColumnTrait, EntityTrait, Iterable, PrimaryKeyToColumn,
|
||||
QueryFilter, QueryTrait,
|
||||
};
|
||||
use core::marker::PhantomData;
|
||||
use sea_query::{Expr, IntoIden, SimpleExpr, UpdateStatement};
|
||||
@ -92,11 +92,11 @@ where
|
||||
fn prepare_filters(mut self) -> Self {
|
||||
for key in <A::Entity as EntityTrait>::PrimaryKey::iter() {
|
||||
let col = key.into_column();
|
||||
let av = self.model.get(col);
|
||||
if av.is_set() || av.is_unchanged() {
|
||||
self = self.filter(col.eq(av.unwrap()));
|
||||
} else {
|
||||
panic!("PrimaryKey is not set");
|
||||
match self.model.get(col) {
|
||||
ActiveValue::Set(value) | ActiveValue::Unchanged(value) => {
|
||||
self = self.filter(col.eq(value));
|
||||
}
|
||||
ActiveValue::NotSet => panic!("PrimaryKey is not set"),
|
||||
}
|
||||
}
|
||||
self
|
||||
@ -107,10 +107,12 @@ where
|
||||
if <A::Entity as EntityTrait>::PrimaryKey::from_column(col).is_some() {
|
||||
continue;
|
||||
}
|
||||
let av = self.model.get(col);
|
||||
if av.is_set() {
|
||||
let expr = col.save_as(Expr::val(av.into_value().unwrap()));
|
||||
self.query.value(col, expr);
|
||||
match self.model.get(col) {
|
||||
ActiveValue::Set(value) => {
|
||||
let expr = col.save_as(Expr::val(value));
|
||||
self.query.value(col, expr);
|
||||
}
|
||||
ActiveValue::Unchanged(_) | ActiveValue::NotSet => {}
|
||||
}
|
||||
}
|
||||
self
|
||||
@ -187,10 +189,12 @@ where
|
||||
A: ActiveModelTrait<Entity = E>,
|
||||
{
|
||||
for col in E::Column::iter() {
|
||||
let av = model.get(col);
|
||||
if av.is_set() {
|
||||
let expr = col.save_as(Expr::val(av.into_value().unwrap()));
|
||||
self.query.value(col, expr);
|
||||
match model.get(col) {
|
||||
ActiveValue::Set(value) => {
|
||||
let expr = col.save_as(Expr::val(value));
|
||||
self.query.value(col, expr);
|
||||
}
|
||||
ActiveValue::Unchanged(_) | ActiveValue::NotSet => {}
|
||||
}
|
||||
}
|
||||
self
|
||||
|
@ -42,6 +42,7 @@ impl Schema {
|
||||
}
|
||||
|
||||
/// Creates a column definition for example to update a table.
|
||||
///
|
||||
/// ```
|
||||
/// use crate::sea_orm::IdenStatic;
|
||||
/// use sea_orm::{
|
||||
@ -61,6 +62,7 @@ impl Schema {
|
||||
///
|
||||
/// #[derive(Copy, Clone, Debug, EnumIter)]
|
||||
/// pub enum Relation {}
|
||||
///
|
||||
/// impl RelationTrait for Relation {
|
||||
/// fn def(&self) -> RelationDef {
|
||||
/// panic!("No RelationDef")
|
||||
|
@ -44,7 +44,7 @@ impl PrimaryKeyTrait for PrimaryKey {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, EnumIter)]
|
||||
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
|
||||
pub enum Relation {}
|
||||
|
||||
impl ColumnTrait for Column {
|
||||
@ -60,10 +60,4 @@ impl ColumnTrait for Column {
|
||||
}
|
||||
}
|
||||
|
||||
impl RelationTrait for Relation {
|
||||
fn def(&self) -> RelationDef {
|
||||
panic!()
|
||||
}
|
||||
}
|
||||
|
||||
impl ActiveModelBehavior for ActiveModel {}
|
||||
|
@ -62,15 +62,9 @@ pub struct Model {
|
||||
pub r#yield: i32,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, EnumIter)]
|
||||
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
|
||||
pub enum Relation {}
|
||||
|
||||
impl RelationTrait for Relation {
|
||||
fn def(&self) -> RelationDef {
|
||||
panic!("No RelationDef")
|
||||
}
|
||||
}
|
||||
|
||||
impl ActiveModelBehavior for ActiveModel {}
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -9,15 +9,9 @@ pub struct Model {
|
||||
pub name: String,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, EnumIter)]
|
||||
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
|
||||
pub enum Relation {}
|
||||
|
||||
impl RelationTrait for Relation {
|
||||
fn def(&self) -> RelationDef {
|
||||
panic!()
|
||||
}
|
||||
}
|
||||
|
||||
impl Related<super::filling::Entity> for Entity {
|
||||
fn to() -> RelationDef {
|
||||
super::filling::Relation::Vendor.def()
|
||||
|
Loading…
x
Reference in New Issue
Block a user