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 rorm_sql::value::Value;
9use rorm_sql::DBImpl;
10use tracing::debug;
11
12use crate::futures_util::BoxFuture;
13use crate::transaction::{Transaction, TransactionGuard};
14use crate::{internal, Database, Error};
15
16/// [`QueryStrategy`] returning nothing
17///
18/// `type Result<'result> = impl Future<Output = Result<(), Error>>`
19pub struct Nothing;
20
21impl QueryStrategy for Nothing {}
22
23/// [`QueryStrategy`] returning how many rows have been affected by the query
24///
25/// `type Result<'result> = impl Future<Output = Result<u64, Error>>`
26pub struct AffectedRows;
27
28impl QueryStrategy for AffectedRows {}
29
30/// [`QueryStrategy`] returning a single row
31///
32/// `type Result<'result> = impl Future<Output = Result<Row, Error>>`
33pub struct One;
34
35impl QueryStrategy for One {}
36
37/// [`QueryStrategy`] returning an optional row
38///
39/// `type Result<'result> = impl Future<Output = Result<Option<Row>, Error>>`
40pub struct Optional;
41
42impl QueryStrategy for Optional {}
43
44/// [`QueryStrategy`] returning a vector of rows
45///
46/// `type Result<'result> = impl Future<Output = Result<Vec<Row>, Error>>`
47pub struct All;
48
49impl QueryStrategy for All {}
50
51/// [`QueryStrategy`] returning a stream of rows
52///
53/// `type Result<'result> = impl Stream<Item = Result<Row, Error>>`
54pub struct Stream;
55
56impl QueryStrategy for Stream {}
57
58/// Define how a query is sent to and results retrieved from the database.
59///
60/// This trait is implemented on the following unit structs:
61/// - [`Nothing`] retrieves nothing
62/// - [`Optional`] retrieves an optional row
63/// - [`One`] retrieves a single row
64/// - [`Stream`] retrieves many rows in a stream
65/// - [`All`] retrieves many rows in a vector
66/// - [`AffectedRows`] returns the number of rows affected by the query
67///
68/// This trait has an associated `Result<'result>` type which is returned by [`Executor::execute`].
69/// To avoid boxing, these types are quite big.
70///
71/// Each of those unit structs' docs (follow links above) contains an easy to read `impl Trait` version of the actual types.
72pub trait QueryStrategy: QueryStrategyResult + internal::executor::QueryStrategyImpl {}
73
74/// Helper trait to make the `Result<'result>` public,
75/// while keeping [`QueryStrategyImpl`](internal::executor::QueryStrategyImpl) itself private
76#[doc(hidden)]
77pub trait QueryStrategyResult {
78    type Result<'result>;
79}
80
81/// Some kind of database connection which can execute queries
82///
83/// This trait is implemented by the database connection itself as well as transactions.
84///
85/// # Object Safety
86/// This trait is **not** object safe.
87/// However, there only exist two implementors,
88/// which were combined into the [`DynamicExecutor`] enum.
89pub trait Executor<'executor> {
90    /// Executes a raw SQL query
91    ///
92    /// The query is executed as prepared statement.
93    /// To bind parameter, use ? as placeholder in SQLite and MySQL
94    /// and $1, $2, $n in Postgres.
95    ///
96    /// The generic `Q` is used to "select" what the database is supposed to respond with.
97    /// See [`QueryStrategy`] for a list of available options.
98    ///
99    /// ```skipped
100    /// db.execute::<All>("SELECT * FROM foo;".to_string(), vec![]);
101    /// ```
102    fn execute<'data, 'result, Q>(
103        self,
104        query: String,
105        values: Vec<Value<'data>>,
106    ) -> Q::Result<'result>
107    where
108        'executor: 'result,
109        'data: 'result,
110        Q: QueryStrategy;
111
112    /// Get the executor's sql dialect.
113    fn dialect(&self) -> DBImpl;
114
115    /// Convenience method to convert into a "`dyn Executor`"
116    fn into_dyn(self) -> DynamicExecutor<'executor>;
117
118    /// A future producing a [`TransactionGuard`] returned by [`ensure_transaction`](Executor::ensure_transaction)
119    type EnsureTransactionFuture: Future<Output = Result<TransactionGuard<'executor>, Error>> + Send;
120
121    /// Ensure a piece of code is run inside a transaction using a [`TransactionGuard`].
122    ///
123    /// In generic code an [`Executor`] might and might not be a `&mut Transaction`.
124    /// But sometimes you'd want to ensure your code is run inside a transaction
125    /// (for example [bulk inserts](crate::database::insert_bulk)).
126    ///
127    /// This method solves this by producing a type which is either an owned or borrowed Transaction
128    /// depending on the [`Executor`] it is called on.
129    fn ensure_transaction(self)
130        -> BoxFuture<'executor, Result<TransactionGuard<'executor>, Error>>;
131}
132
133/// Choose whether to use transactions or not at runtime
134///
135/// Like a `Box<dyn Executor<'executor>>`
136pub enum DynamicExecutor<'executor> {
137    /// Use a default database connection
138    Database(&'executor Database),
139    /// Use a transaction
140    Transaction(&'executor mut Transaction),
141}
142
143impl<'executor> Executor<'executor> for DynamicExecutor<'executor> {
144    fn execute<'data, 'result, Q>(
145        self,
146        query: String,
147        values: Vec<Value<'data>>,
148    ) -> Q::Result<'result>
149    where
150        'executor: 'result,
151        'data: 'result,
152        Q: QueryStrategy,
153    {
154        debug!(
155            target: "rorm_db::executor",
156            sql = query,
157            values.len = values.len(),
158            "Executing statement"
159        );
160        match self {
161            DynamicExecutor::Database(db) => db.execute::<Q>(query, values),
162            DynamicExecutor::Transaction(tr) => tr.execute::<Q>(query, values),
163        }
164    }
165
166    fn dialect(&self) -> DBImpl {
167        match self {
168            DynamicExecutor::Database(db) => db.dialect(),
169            DynamicExecutor::Transaction(tr) => tr.dialect(),
170        }
171    }
172
173    fn into_dyn(self) -> DynamicExecutor<'executor> {
174        self
175    }
176
177    type EnsureTransactionFuture = BoxFuture<'executor, Result<TransactionGuard<'executor>, Error>>;
178
179    fn ensure_transaction(self) -> Self::EnsureTransactionFuture {
180        match self {
181            DynamicExecutor::Database(db) => db.ensure_transaction(),
182            DynamicExecutor::Transaction(tr) => Box::pin(tr.ensure_transaction()),
183        }
184    }
185}