use std::{future::Future, pin::Pin};
#[cfg(feature = "stream")]
use futures_util::Stream;
use crate::{
DbBackend, DbErr, ExecResult, QueryResult, Statement, StatementBuilder, TransactionError,
};
#[async_trait::async_trait]
pub trait ConnectionTrait: Sync {
fn get_database_backend(&self) -> DbBackend;
async fn execute_raw(&self, stmt: Statement) -> Result<ExecResult, DbErr>;
async fn execute<S: StatementBuilder>(&self, stmt: &S) -> Result<ExecResult, DbErr> {
let db_backend = self.get_database_backend();
let stmt = db_backend.build(stmt);
self.execute_raw(stmt).await
}
async fn execute_unprepared(&self, sql: &str) -> Result<ExecResult, DbErr>;
async fn query_one_raw(&self, stmt: Statement) -> Result<Option<QueryResult>, DbErr>;
async fn query_one<S: StatementBuilder>(&self, stmt: &S) -> Result<Option<QueryResult>, DbErr> {
let db_backend = self.get_database_backend();
let stmt = db_backend.build(stmt);
self.query_one_raw(stmt).await
}
async fn query_all_raw(&self, stmt: Statement) -> Result<Vec<QueryResult>, DbErr>;
async fn query_all<S: StatementBuilder>(&self, stmt: &S) -> Result<Vec<QueryResult>, DbErr> {
let db_backend = self.get_database_backend();
let stmt = db_backend.build(stmt);
self.query_all_raw(stmt).await
}
fn support_returning(&self) -> bool {
let db_backend = self.get_database_backend();
db_backend.support_returning()
}
fn is_mock_connection(&self) -> bool {
false
}
}
#[cfg(feature = "stream")]
pub trait StreamTrait: Send + Sync {
type Stream<'a>: Stream<Item = Result<QueryResult, DbErr>> + Send
where
Self: 'a;
fn get_database_backend(&self) -> DbBackend;
fn stream_raw<'a>(
&'a self,
stmt: Statement,
) -> Pin<Box<dyn Future<Output = Result<Self::Stream<'a>, DbErr>> + 'a + Send>>;
fn stream<'a, S: StatementBuilder + Sync>(
&'a self,
stmt: &S,
) -> Pin<Box<dyn Future<Output = Result<Self::Stream<'a>, DbErr>> + 'a + Send>> {
let db_backend = self.get_database_backend();
let stmt = db_backend.build(stmt);
self.stream_raw(stmt)
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum IsolationLevel {
RepeatableRead,
ReadCommitted,
ReadUncommitted,
Serializable,
}
impl std::fmt::Display for IsolationLevel {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
IsolationLevel::RepeatableRead => write!(f, "REPEATABLE READ"),
IsolationLevel::ReadCommitted => write!(f, "READ COMMITTED"),
IsolationLevel::ReadUncommitted => write!(f, "READ UNCOMMITTED"),
IsolationLevel::Serializable => write!(f, "SERIALIZABLE"),
}
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum AccessMode {
ReadOnly,
ReadWrite,
}
impl std::fmt::Display for AccessMode {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
AccessMode::ReadOnly => write!(f, "READ ONLY"),
AccessMode::ReadWrite => write!(f, "READ WRITE"),
}
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum SqliteTransactionMode {
Deferred,
Immediate,
Exclusive,
}
impl SqliteTransactionMode {
pub fn sqlite_keyword(&self) -> &'static str {
match self {
SqliteTransactionMode::Deferred => "DEFERRED",
SqliteTransactionMode::Immediate => "IMMEDIATE",
SqliteTransactionMode::Exclusive => "EXCLUSIVE",
}
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Default)]
pub struct TransactionOptions {
pub isolation_level: Option<IsolationLevel>,
pub access_mode: Option<AccessMode>,
pub sqlite_transaction_mode: Option<SqliteTransactionMode>,
}
#[async_trait::async_trait]
pub trait TransactionTrait {
type Transaction: ConnectionTrait + TransactionTrait + TransactionSession;
async fn begin(&self) -> Result<Self::Transaction, DbErr>;
async fn begin_with_config(
&self,
isolation_level: Option<IsolationLevel>,
access_mode: Option<AccessMode>,
) -> Result<Self::Transaction, DbErr>;
async fn begin_with_options(
&self,
options: TransactionOptions,
) -> Result<Self::Transaction, DbErr>;
async fn transaction<F, T, E>(&self, callback: F) -> Result<T, TransactionError<E>>
where
F: for<'c> FnOnce(
&'c Self::Transaction,
) -> Pin<Box<dyn Future<Output = Result<T, E>> + Send + 'c>>
+ Send,
T: Send,
E: std::fmt::Display + std::fmt::Debug + Send;
async fn transaction_with_config<F, T, E>(
&self,
callback: F,
isolation_level: Option<IsolationLevel>,
access_mode: Option<AccessMode>,
) -> Result<T, TransactionError<E>>
where
F: for<'c> FnOnce(
&'c Self::Transaction,
) -> Pin<Box<dyn Future<Output = Result<T, E>> + Send + 'c>>
+ Send,
T: Send,
E: std::fmt::Display + std::fmt::Debug + Send;
}
#[async_trait::async_trait]
pub trait TransactionSession {
async fn commit(self) -> Result<(), DbErr>;
async fn rollback(self) -> Result<(), DbErr>;
}