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<'exe>` public,
75/// while keeping [`QueryStrategyImpl`](internal::executor::QueryStrategyImpl) itself private
76#[doc(hidden)]
77pub trait QueryStrategyResult {
78 type Result<'exe>;
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<'exe> {
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<Q>(self, query: String, values: Vec<Value<'_>>) -> Q::Result<'exe>
103 where
104 Q: QueryStrategy;
105
106 /// Get the executor's sql dialect.
107 fn dialect(&self) -> DBImpl;
108
109 /// Convenience method to convert into a "`dyn Executor`"
110 fn into_dyn(self) -> DynamicExecutor<'exe>;
111
112 /// A future producing a [`TransactionGuard`] returned by [`ensure_transaction`](Executor::ensure_transaction)
113 type EnsureTransactionFuture: Future<Output = Result<TransactionGuard<'exe>, Error>> + Send;
114
115 /// Ensure a piece of code is run inside a transaction using a [`TransactionGuard`].
116 ///
117 /// In generic code an [`Executor`] might and might not be a `&mut Transaction`.
118 /// But sometimes you'd want to ensure your code is run inside a transaction
119 /// (for example [bulk inserts](crate::database::insert_bulk)).
120 ///
121 /// This method solves this by producing a type which is either an owned or borrowed Transaction
122 /// depending on the [`Executor`] it is called on.
123 fn ensure_transaction(self) -> BoxFuture<'exe, Result<TransactionGuard<'exe>, Error>>;
124}
125
126/// Choose whether to use transactions or not at runtime
127///
128/// Like a `Box<dyn Executor<'executor>>`
129pub enum DynamicExecutor<'exe> {
130 /// Use a default database connection
131 Database(&'exe Database),
132 /// Use a transaction
133 Transaction(&'exe mut Transaction),
134}
135
136impl<'exe> Executor<'exe> for DynamicExecutor<'exe> {
137 fn execute<Q>(self, query: String, values: Vec<Value<'_>>) -> Q::Result<'exe>
138 where
139 Q: QueryStrategy,
140 {
141 debug!(
142 target: "rorm_db::executor",
143 sql = query,
144 values.len = values.len(),
145 "Executing statement"
146 );
147 match self {
148 DynamicExecutor::Database(db) => db.execute::<Q>(query, values),
149 DynamicExecutor::Transaction(tr) => tr.execute::<Q>(query, values),
150 }
151 }
152
153 fn dialect(&self) -> DBImpl {
154 match self {
155 DynamicExecutor::Database(db) => db.dialect(),
156 DynamicExecutor::Transaction(tr) => tr.dialect(),
157 }
158 }
159
160 fn into_dyn(self) -> DynamicExecutor<'exe> {
161 self
162 }
163
164 type EnsureTransactionFuture = BoxFuture<'exe, Result<TransactionGuard<'exe>, Error>>;
165
166 fn ensure_transaction(self) -> Self::EnsureTransactionFuture {
167 match self {
168 DynamicExecutor::Database(db) => db.ensure_transaction(),
169 DynamicExecutor::Transaction(tr) => Box::pin(tr.ensure_transaction()),
170 }
171 }
172}