1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152
//! This module defines a wrapper for sqlx's Executor
//!
//! Unlike sqlx's Executor which provides several separate methods for different querying strategies,
//! our [`Executor`] has a single method which is generic using the [`QueryStrategy`] trait.
use std::future::Future;
use futures::future::BoxFuture;
use rorm_sql::value::Value;
use rorm_sql::DBImpl;
use crate::transaction::{Transaction, TransactionGuard};
use crate::{internal, Database, Error};
/// [`QueryStrategy`] returning nothing
///
/// `type Result<'result> = impl Future<Output = Result<(), Error>>`
pub struct Nothing;
impl QueryStrategy for Nothing {}
/// [`QueryStrategy`] returning how many rows have been affected by the query
///
/// `type Result<'result> = impl Future<Output = Result<u64, Error>>`
pub struct AffectedRows;
impl QueryStrategy for AffectedRows {}
/// [`QueryStrategy`] returning a single row
///
/// `type Result<'result> = impl Future<Output = Result<Row, Error>>`
pub struct One;
impl QueryStrategy for One {}
/// [`QueryStrategy`] returning an optional row
///
/// `type Result<'result> = impl Future<Output = Result<Option<Row>, Error>>`
pub struct Optional;
impl QueryStrategy for Optional {}
/// [`QueryStrategy`] returning a vector of rows
///
/// `type Result<'result> = impl Future<Output = Result<Vec<Row>, Error>>`
pub struct All;
impl QueryStrategy for All {}
/// [`QueryStrategy`] returning a stream of rows
///
/// `type Result<'result> = impl Stream<Item = Result<Row, Error>>`
pub struct Stream;
impl QueryStrategy for Stream {}
/// Define how a query is send to and results retrieved from the database.
///
/// This trait is implemented on the following unit structs:
/// - [`Nothing`] retrieves nothing
/// - [`Optional`] retrieves an optional row
/// - [`One`] retrieves a single row
/// - [`Stream`] retrieves many rows in a stream
/// - [`All`] retrieves many rows in a vector
/// - [`AffectedRows`] returns the number of rows affected by the query
///
/// This trait has an associated `Result<'result>` type which is returned by [`Executor::execute`].
/// To avoid boxing, these types are quite big.
///
/// Each of those unit structs' docs (follow links above) contains an easy to read `impl Trait` version of the actual types.
pub trait QueryStrategy: QueryStrategyResult + internal::executor::QueryStrategyImpl {}
/// Helper trait to make the `Result<'result>` public,
/// while keeping [`QueryStrategyImpl`](internal::executor::QueryStrategyImpl) itself private
#[doc(hidden)]
pub trait QueryStrategyResult {
type Result<'result>;
}
/// Some kind of database connection which can execute queries
///
/// This trait is implemented by the database connection itself as well as transactions.
pub trait Executor<'executor> {
/// Execute a query
///
/// ```skipped
/// db.execute::<All>("SELECT * FROM foo;".to_string(), vec![]);
/// ```
fn execute<'data, 'result, Q>(
self,
query: String,
values: Vec<Value<'data>>,
) -> Q::Result<'result>
where
'executor: 'result,
'data: 'result,
Q: QueryStrategy;
/// Get the executor's sql dialect.
fn dialect(&self) -> DBImpl;
/// A future producing a [`TransactionGuard`] returned by [`ensure_transaction`](Executor::ensure_transaction)
type EnsureTransactionFuture: Future<Output = Result<TransactionGuard<'executor>, Error>> + Send;
/// Ensure a piece of code is run inside a transaction using a [`TransactionGuard`].
///
/// In generic code an [`Executor`] might and might not be a [`&mut Transaction`].
/// But sometimes you'd want to ensure your code is run inside a transaction
/// (for example [bulk inserts](crate::database::insert_bulk)).
///
/// This method solves this by producing a type which is either an owned or borrowed Transaction
/// depending on the [`Executor`] it is called on.
fn ensure_transaction(self)
-> BoxFuture<'executor, Result<TransactionGuard<'executor>, Error>>;
}
/// Choose whether to use transactions or not at runtime
///
/// Like a `Box<dyn Executor<'executor>>`
pub enum DynamicExecutor<'executor> {
/// Use a default database connection
Database(&'executor Database),
/// Use a transaction
Transaction(&'executor mut Transaction),
}
impl<'executor> Executor<'executor> for DynamicExecutor<'executor> {
fn execute<'data, 'result, Q>(
self,
query: String,
values: Vec<Value<'data>>,
) -> Q::Result<'result>
where
'executor: 'result,
'data: 'result,
Q: QueryStrategy,
{
match self {
DynamicExecutor::Database(db) => db.execute::<Q>(query, values),
DynamicExecutor::Transaction(tr) => tr.execute::<Q>(query, values),
}
}
fn dialect(&self) -> DBImpl {
match self {
DynamicExecutor::Database(db) => db.dialect(),
DynamicExecutor::Transaction(tr) => tr.dialect(),
}
}
type EnsureTransactionFuture = BoxFuture<'executor, Result<TransactionGuard<'executor>, Error>>;
fn ensure_transaction(self) -> Self::EnsureTransactionFuture {
match self {
DynamicExecutor::Database(db) => db.ensure_transaction(),
DynamicExecutor::Transaction(tr) => Box::pin(tr.ensure_transaction()),
}
}
}