Skip to main content

turso_orm_driver/
connection.rs

1//! The connection traits implemented by [`Database`] and [`Transaction`].
2//!
3//! Code above the driver should not care whether it runs on the pooled
4//! handle or inside a transaction, so both implement the same three traits
5//! and the entity layer is written against `impl ConnectionTrait`. The
6//! traits are split by capability rather than merged into one so that a
7//! type can offer execution without transactions, and so that the streaming
8//! method — which cannot go through `async_trait` because its future borrows
9//! the connection for the stream's lifetime — keeps its explicit signature.
10//!
11//! This module owns the trait definitions and their implementations for
12//! [`Database`]; the [`Transaction`] implementations live next to the
13//! transaction type. The actual work is delegated to `crate::executor`.
14//!
15//! - [`ConnectionTrait`]: execute statements and fetch rows;
16//! - [`StreamTrait`]: stream rows lazily;
17//! - [`TransactionTrait`]: start transactions, including the closure form.
18
19use std::future::Future;
20use std::pin::Pin;
21
22use async_trait::async_trait;
23use turso_sql::Statement;
24
25use crate::database::Database;
26use crate::error::Result;
27use crate::executor::{self, Conn, ExecResult, Row, RowStream};
28use crate::transaction::{Transaction, TransactionMode};
29
30/// Executes statements and fetches rows.
31///
32/// # Errors
33///
34/// Every method returns [`Error::Turso`](crate::Error::Turso) when the
35/// engine rejects the statement — with
36/// [`ErrorKind::Busy`](crate::ErrorKind::Busy) under lock contention,
37/// [`ErrorKind::Constraint`](crate::ErrorKind::Constraint) on a constraint
38/// violation and [`ErrorKind::Other`](crate::ErrorKind::Other) otherwise.
39/// The [`Database`] implementation additionally returns
40/// [`Error::PoolTimeout`](crate::Error::PoolTimeout) when no connection is
41/// free within the acquire timeout.
42#[async_trait]
43pub trait ConnectionTrait: Send + Sync {
44    /// Runs a statement that returns no rows.
45    async fn execute(&self, statement: Statement) -> Result<ExecResult>;
46
47    /// Runs one or more `;`-separated statements without parameters.
48    async fn execute_unprepared(&self, sql: &str) -> Result<ExecResult>;
49
50    /// Fetches at most one row.
51    async fn query_one(&self, statement: Statement) -> Result<Option<Row>>;
52
53    /// Fetches all rows.
54    async fn query_all(&self, statement: Statement) -> Result<Vec<Row>>;
55}
56
57/// Streams rows lazily.
58pub trait StreamTrait: Send + Sync {
59    /// Runs a query and streams its rows.
60    ///
61    /// # Errors
62    ///
63    /// The future fails with [`Error::Turso`](crate::Error::Turso) when the
64    /// statement cannot be prepared or started, and the [`Database`]
65    /// implementation with [`Error::PoolTimeout`](crate::Error::PoolTimeout)
66    /// when no connection is free. Errors while stepping are yielded as
67    /// items of the stream.
68    fn stream<'a>(
69        &'a self,
70        statement: Statement,
71    ) -> Pin<Box<dyn Future<Output = Result<RowStream<'a>>> + Send + 'a>>;
72}
73
74/// Starts transactions.
75#[async_trait]
76pub trait TransactionTrait: Send + Sync {
77    /// Begins with `BEGIN DEFERRED`, or a savepoint when called on a
78    /// transaction.
79    ///
80    /// # Errors
81    ///
82    /// Returns [`Error::Turso`](crate::Error::Turso) when the engine cannot
83    /// start the transaction, and on [`Database`]
84    /// [`Error::PoolTimeout`](crate::Error::PoolTimeout) when no connection
85    /// is free within the acquire timeout.
86    async fn begin(&self) -> Result<Transaction>;
87
88    /// Begins with an explicit mode; the mode is ignored for nested
89    /// transactions, which are always savepoints.
90    ///
91    /// # Errors
92    ///
93    /// Returns [`Error::Turso`](crate::Error::Turso) when the engine cannot
94    /// start the transaction — with
95    /// [`ErrorKind::Busy`](crate::ErrorKind::Busy) when an `IMMEDIATE` or
96    /// `EXCLUSIVE` lock cannot be taken within the busy timeout — and on
97    /// [`Database`] [`Error::PoolTimeout`](crate::Error::PoolTimeout) when
98    /// no connection is free.
99    async fn begin_with_mode(&self, mode: TransactionMode) -> Result<Transaction>;
100
101    /// Runs `callback` inside a transaction, committing on `Ok` and rolling
102    /// back on `Err`.
103    ///
104    /// The callback receives `&Transaction` rather than owning it so that it
105    /// cannot commit or roll back on its own and leave this method with a
106    /// finished transaction.
107    ///
108    /// # Errors
109    ///
110    /// Returns the callback's error after rolling back, or the driver error
111    /// converted through `E::from` when beginning, committing or rolling
112    /// back fails.
113    async fn transaction<F, T, E>(&self, callback: F) -> std::result::Result<T, E>
114    where
115        F: for<'c> FnOnce(
116                &'c Transaction,
117            )
118                -> Pin<Box<dyn Future<Output = std::result::Result<T, E>> + Send + 'c>>
119            + Send,
120        T: Send,
121        E: From<crate::Error> + Send,
122    {
123        let txn = self.begin().await?;
124        match callback(&txn).await {
125            Ok(value) => {
126                txn.commit().await?;
127                Ok(value)
128            }
129            Err(err) => {
130                txn.rollback().await?;
131                Err(err)
132            }
133        }
134    }
135}
136
137#[async_trait]
138impl ConnectionTrait for Database {
139    async fn execute(&self, statement: Statement) -> Result<ExecResult> {
140        let conn = self.acquire().await?;
141        executor::execute(&conn, &statement).await
142    }
143
144    async fn execute_unprepared(&self, sql: &str) -> Result<ExecResult> {
145        let conn = self.acquire().await?;
146        executor::execute_unprepared(&conn, sql).await
147    }
148
149    async fn query_one(&self, statement: Statement) -> Result<Option<Row>> {
150        let conn = self.acquire().await?;
151        executor::query_one(&conn, &statement).await
152    }
153
154    async fn query_all(&self, statement: Statement) -> Result<Vec<Row>> {
155        let conn = self.acquire().await?;
156        executor::query_all(&conn, &statement).await
157    }
158}
159
160impl StreamTrait for Database {
161    fn stream<'a>(
162        &'a self,
163        statement: Statement,
164    ) -> Pin<Box<dyn Future<Output = Result<RowStream<'a>>> + Send + 'a>> {
165        Box::pin(async move {
166            let conn = self.acquire().await?;
167            // The stream must outlive this future, so it cannot borrow the
168            // pooled guard. A clone of the engine connection is used to
169            // run the query while the guard itself travels inside the
170            // stream as the holder, returning the slot to the pool when the
171            // stream is dropped.
172            let raw: Conn = (*conn).clone();
173            executor::stream(&raw, &statement, conn).await
174        })
175    }
176}
177
178#[async_trait]
179impl TransactionTrait for Database {
180    async fn begin(&self) -> Result<Transaction> {
181        self.begin_with_mode(TransactionMode::Deferred).await
182    }
183
184    async fn begin_with_mode(&self, mode: TransactionMode) -> Result<Transaction> {
185        let conn = self.acquire().await?;
186        Transaction::begin_top(conn, mode).await
187    }
188}
189
190/// Implements [`ConnectionTrait`] for `&T` by forwarding, so that a
191/// borrowed handle satisfies `impl ConnectionTrait` bounds.
192macro_rules! forward_ref {
193    ($($t:ty),*) => {$(
194        #[async_trait]
195        impl ConnectionTrait for &$t {
196            async fn execute(&self, statement: Statement) -> Result<ExecResult> {
197                (**self).execute(statement).await
198            }
199            async fn execute_unprepared(&self, sql: &str) -> Result<ExecResult> {
200                (**self).execute_unprepared(sql).await
201            }
202            async fn query_one(&self, statement: Statement) -> Result<Option<Row>> {
203                (**self).query_one(statement).await
204            }
205            async fn query_all(&self, statement: Statement) -> Result<Vec<Row>> {
206                (**self).query_all(statement).await
207            }
208        }
209    )*};
210}
211forward_ref!(Database, Transaction);