rorm_db/
executor.rs

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