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);