use crate::{ DatabaseTransaction, DbBackend, DbErr, ExecResult, QueryResult, Statement, TransactionError, }; use futures::Stream; use std::{future::Future, pin::Pin}; /// Creates constraints for any structure that can create a database connection /// and execute SQL statements #[async_trait::async_trait] pub trait ConnectionTrait: Sync { /// Fetch the database backend as specified in [DbBackend]. /// This depends on feature flags enabled. fn get_database_backend(&self) -> DbBackend; /// Execute a [Statement] async fn execute(&self, stmt: Statement) -> Result; /// Execute a unprepared [Statement] async fn execute_unprepared(&self, sql: &str) -> Result; /// Execute a [Statement] and return a query async fn query_one(&self, stmt: Statement) -> Result, DbErr>; /// Execute a [Statement] and return a collection Vec<[QueryResult]> on success async fn query_all(&self, stmt: Statement) -> Result, DbErr>; /// Check if the connection supports `RETURNING` syntax on insert and update fn support_returning(&self) -> bool { let db_backend = self.get_database_backend(); db_backend.support_returning() } /// Check if the connection is a test connection for the Mock database fn is_mock_connection(&self) -> bool { false } } /// Stream query results pub trait StreamTrait: Send + Sync { /// Create a stream for the [QueryResult] type Stream<'a>: Stream> + Send where Self: 'a; /// Execute a [Statement] and return a stream of results fn stream<'a>( &'a self, stmt: Statement, ) -> Pin, DbErr>> + 'a + Send>>; } #[derive(Copy, Clone, Debug, PartialEq, Eq)] /// Isolation level pub enum IsolationLevel { /// Consistent reads within the same transaction read the snapshot established by the first read. RepeatableRead, /// Each consistent read, even within the same transaction, sets and reads its own fresh snapshot. ReadCommitted, /// SELECT statements are performed in a nonlocking fashion, but a possible earlier version of a row might be used. ReadUncommitted, /// All statements of the current transaction can only see rows committed before the first query or data-modification statement was executed in this transaction. 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)] /// Access mode pub enum AccessMode { /// Data can't be modified in this transaction ReadOnly, /// Data can be modified in this transaction (default) 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"), } } } /// Spawn database transaction #[async_trait::async_trait] pub trait TransactionTrait { /// Execute SQL `BEGIN` transaction. /// Returns a Transaction that can be committed or rolled back async fn begin(&self) -> Result; /// Execute SQL `BEGIN` transaction with isolation level and/or access mode. /// Returns a Transaction that can be committed or rolled back async fn begin_with_config( &self, isolation_level: Option, access_mode: Option, ) -> Result; /// Execute the function inside a transaction. /// If the function returns an error, the transaction will be rolled back. If it does not return an error, the transaction will be committed. async fn transaction(&self, callback: F) -> Result> where F: for<'c> FnOnce( &'c DatabaseTransaction, ) -> Pin> + Send + 'c>> + Send, T: Send, E: std::error::Error + Send; /// Execute the function inside a transaction with isolation level and/or access mode. /// If the function returns an error, the transaction will be rolled back. If it does not return an error, the transaction will be committed. async fn transaction_with_config( &self, callback: F, isolation_level: Option, access_mode: Option, ) -> Result> where F: for<'c> FnOnce( &'c DatabaseTransaction, ) -> Pin> + Send + 'c>> + Send, T: Send, E: std::error::Error + Send; }