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:
Billy Chan 2023-02-02 00:02:53 +08:00 committed by GitHub
parent 48a9ffec2a
commit 91c4930391
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
30 changed files with 481 additions and 409 deletions

View File

@ -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"

View File

@ -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"))
);
}
}

View File

@ -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
))))
)))
}
}

View File

@ -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)
}

View File

@ -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)
}

View File

@ -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)
}
}

View File

@ -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()
}
}

View File

@ -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())
}
}

View File

@ -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())
}
}

View File

@ -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())
}
}

View File

@ -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)
)))

View File

@ -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,

View File

@ -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>,

View File

@ -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 {}

View File

@ -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,

View File

@ -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())
}

View File

@ -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
}

View File

@ -126,7 +126,6 @@ where
}
}
#[allow(unused_variables, unreachable_code)]
async fn exec_insert<A, C>(
primary_key: Option<ValueTuple>,
statement: Statement,

View File

@ -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!(),
}

View File

@ -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
}

View File

@ -2,6 +2,8 @@
#![warn(missing_docs)]
#![deny(
missing_debug_implementations,
clippy::missing_panics_doc,
clippy::unwrap_used,
clippy::print_stderr,
clippy::print_stdout
)]

View File

@ -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

View File

@ -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);

View File

@ -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;

View File

@ -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

View File

@ -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

View File

@ -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")

View File

@ -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 {}

View File

@ -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)]

View File

@ -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()