Skip to main content

sea_orm/database/
connection.rs

1use std::{future::Future, pin::Pin};
2
3#[cfg(feature = "stream")]
4use futures_util::Stream;
5
6use crate::{
7    DbBackend, DbErr, ExecResult, QueryResult, Statement, StatementBuilder, TransactionError,
8};
9
10/// A connection (or transaction) that can run queries against the database.
11///
12/// Implemented by [`DatabaseConnection`](crate::DatabaseConnection),
13/// [`DatabaseTransaction`](crate::DatabaseTransaction), and the mock/proxy
14/// connections used in testing. Most query and mutation methods in SeaORM
15/// (`.one(db)`, `.all(db)`, `.exec(db)`, ...) take any `&impl ConnectionTrait`,
16/// so the same code works on a pool, a transaction, or a mock.
17#[async_trait::async_trait]
18pub trait ConnectionTrait: Sync {
19    /// Get the database backend for the connection. This depends on feature flags enabled.
20    fn get_database_backend(&self) -> DbBackend;
21
22    /// Execute a [Statement]
23    async fn execute_raw(&self, stmt: Statement) -> Result<ExecResult, DbErr>;
24
25    /// Execute a [`StatementBuilder`]
26    async fn execute<S: StatementBuilder>(&self, stmt: &S) -> Result<ExecResult, DbErr> {
27        let db_backend = self.get_database_backend();
28        let stmt = db_backend.build(stmt);
29        self.execute_raw(stmt).await
30    }
31
32    /// Execute a unprepared [Statement]
33    async fn execute_unprepared(&self, sql: &str) -> Result<ExecResult, DbErr>;
34
35    /// Execute a [Statement] and return a single row of `QueryResult`
36    async fn query_one_raw(&self, stmt: Statement) -> Result<Option<QueryResult>, DbErr>;
37
38    /// Execute a [`StatementBuilder`] and return a single row of `QueryResult`
39    async fn query_one<S: StatementBuilder>(&self, stmt: &S) -> Result<Option<QueryResult>, DbErr> {
40        let db_backend = self.get_database_backend();
41        let stmt = db_backend.build(stmt);
42        self.query_one_raw(stmt).await
43    }
44
45    /// Execute a [Statement] and return a vector of `QueryResult`
46    async fn query_all_raw(&self, stmt: Statement) -> Result<Vec<QueryResult>, DbErr>;
47
48    /// Execute a [`StatementBuilder`] and return a vector of `QueryResult`
49    async fn query_all<S: StatementBuilder>(&self, stmt: &S) -> Result<Vec<QueryResult>, DbErr> {
50        let db_backend = self.get_database_backend();
51        let stmt = db_backend.build(stmt);
52        self.query_all_raw(stmt).await
53    }
54
55    /// Check if the connection supports `RETURNING` syntax on insert and update
56    fn support_returning(&self) -> bool {
57        let db_backend = self.get_database_backend();
58        db_backend.support_returning()
59    }
60
61    /// Check if the connection is a test connection for the Mock database
62    fn is_mock_connection(&self) -> bool {
63        false
64    }
65}
66
67/// Streaming counterpart to [`ConnectionTrait`]: yields query results row by
68/// row rather than collecting them all into a `Vec`. Use this for large
69/// result sets when you don't want to load everything into memory at once.
70#[cfg(feature = "stream")]
71pub trait StreamTrait: Send + Sync {
72    /// Create a stream for the [QueryResult]
73    type Stream<'a>: Stream<Item = Result<QueryResult, DbErr>> + Send
74    where
75        Self: 'a;
76
77    /// Get the database backend for the connection. This depends on feature flags enabled.
78    fn get_database_backend(&self) -> DbBackend;
79
80    /// Execute a [Statement] and return a stream of results
81    fn stream_raw<'a>(
82        &'a self,
83        stmt: Statement,
84    ) -> Pin<Box<dyn Future<Output = Result<Self::Stream<'a>, DbErr>> + 'a + Send>>;
85
86    /// Execute a [`StatementBuilder`] and return a stream of results
87    fn stream<'a, S: StatementBuilder + Sync>(
88        &'a self,
89        stmt: &S,
90    ) -> Pin<Box<dyn Future<Output = Result<Self::Stream<'a>, DbErr>> + 'a + Send>> {
91        let db_backend = self.get_database_backend();
92        let stmt = db_backend.build(stmt);
93        self.stream_raw(stmt)
94    }
95}
96
97#[derive(Copy, Clone, Debug, PartialEq, Eq)]
98/// Isolation level
99pub enum IsolationLevel {
100    /// Consistent reads within the same transaction read the snapshot established by the first read.
101    RepeatableRead,
102    /// Each consistent read, even within the same transaction, sets and reads its own fresh snapshot.
103    ReadCommitted,
104    /// SELECT statements are performed in a nonlocking fashion, but a possible earlier version of a row might be used.
105    ReadUncommitted,
106    /// All statements of the current transaction can only see rows committed before the first query or data-modification statement was executed in this transaction.
107    Serializable,
108}
109
110impl std::fmt::Display for IsolationLevel {
111    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
112        match self {
113            IsolationLevel::RepeatableRead => write!(f, "REPEATABLE READ"),
114            IsolationLevel::ReadCommitted => write!(f, "READ COMMITTED"),
115            IsolationLevel::ReadUncommitted => write!(f, "READ UNCOMMITTED"),
116            IsolationLevel::Serializable => write!(f, "SERIALIZABLE"),
117        }
118    }
119}
120
121#[derive(Copy, Clone, Debug, PartialEq, Eq)]
122/// Access mode
123pub enum AccessMode {
124    /// Data can't be modified in this transaction
125    ReadOnly,
126    /// Data can be modified in this transaction (default)
127    ReadWrite,
128}
129
130impl std::fmt::Display for AccessMode {
131    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
132        match self {
133            AccessMode::ReadOnly => write!(f, "READ ONLY"),
134            AccessMode::ReadWrite => write!(f, "READ WRITE"),
135        }
136    }
137}
138
139#[derive(Copy, Clone, Debug, PartialEq, Eq)]
140/// Which kind of transaction to start. Only supported by SQLite.
141/// <https://www.sqlite.org/lang_transaction.html>
142pub enum SqliteTransactionMode {
143    /// The default. Transaction starts when the next statement is executed, and
144    /// will be a read or write transaction depending on that statement.
145    Deferred,
146    /// Start a write transaction as soon as the BEGIN statement is received.
147    Immediate,
148    /// Start a write transaction as soon as the BEGIN statement is received.
149    /// When in non-WAL mode, also block all other transactions from reading the
150    /// database.
151    Exclusive,
152}
153
154impl SqliteTransactionMode {
155    /// The keyword used to start a transaction in this mode (the word coming after "BEGIN").
156    pub fn sqlite_keyword(&self) -> &'static str {
157        match self {
158            SqliteTransactionMode::Deferred => "DEFERRED",
159            SqliteTransactionMode::Immediate => "IMMEDIATE",
160            SqliteTransactionMode::Exclusive => "EXCLUSIVE",
161        }
162    }
163}
164
165/// Configuration for starting a transaction
166#[derive(Copy, Clone, Debug, PartialEq, Eq, Default)]
167pub struct TransactionOptions {
168    /// Isolation level for the new transaction
169    pub isolation_level: Option<IsolationLevel>,
170    /// Access mode for the new transaction
171    pub access_mode: Option<AccessMode>,
172    /// Transaction mode (deferred, immediate, exclusive) for the new transaction. Supported only by SQLite.
173    pub sqlite_transaction_mode: Option<SqliteTransactionMode>,
174}
175
176/// Begin a database transaction.
177///
178/// Implemented by [`DatabaseConnection`](crate::DatabaseConnection) and
179/// [`DatabaseTransaction`](crate::DatabaseTransaction) (allowing nested
180/// transactions via SAVEPOINTs). Use [`begin`](Self::begin) for a manually
181/// managed transaction, or [`transaction`](Self::transaction) for a closure
182/// that auto-commits on `Ok` and rolls back on `Err`.
183#[async_trait::async_trait]
184pub trait TransactionTrait {
185    /// The concrete type for the transaction
186    type Transaction: ConnectionTrait + TransactionTrait + TransactionSession;
187
188    /// Execute SQL `BEGIN` transaction.
189    /// Returns a Transaction that can be committed or rolled back
190    async fn begin(&self) -> Result<Self::Transaction, DbErr>;
191
192    /// Execute SQL `BEGIN` transaction with isolation level and/or access mode.
193    /// Returns a Transaction that can be committed or rolled back
194    async fn begin_with_config(
195        &self,
196        isolation_level: Option<IsolationLevel>,
197        access_mode: Option<AccessMode>,
198    ) -> Result<Self::Transaction, DbErr>;
199
200    /// Execute SQL `BEGIN` transaction with isolation level and/or access mode.
201    /// Returns a Transaction that can be committed or rolled back
202    async fn begin_with_options(
203        &self,
204        options: TransactionOptions,
205    ) -> Result<Self::Transaction, DbErr>;
206
207    /// Execute the function inside a transaction.
208    /// If the function returns an error, the transaction will be rolled back. If it does not return an error, the transaction will be committed.
209    async fn transaction<F, T, E>(&self, callback: F) -> Result<T, TransactionError<E>>
210    where
211        F: for<'c> FnOnce(
212                &'c Self::Transaction,
213            ) -> Pin<Box<dyn Future<Output = Result<T, E>> + Send + 'c>>
214            + Send,
215        T: Send,
216        E: std::fmt::Display + std::fmt::Debug + Send;
217
218    /// Execute the function inside a transaction with isolation level and/or access mode.
219    /// If the function returns an error, the transaction will be rolled back. If it does not return an error, the transaction will be committed.
220    async fn transaction_with_config<F, T, E>(
221        &self,
222        callback: F,
223        isolation_level: Option<IsolationLevel>,
224        access_mode: Option<AccessMode>,
225    ) -> Result<T, TransactionError<E>>
226    where
227        F: for<'c> FnOnce(
228                &'c Self::Transaction,
229            ) -> Pin<Box<dyn Future<Output = Result<T, E>> + Send + 'c>>
230            + Send,
231        T: Send,
232        E: std::fmt::Display + std::fmt::Debug + Send;
233}
234
235/// Represents an open transaction
236#[async_trait::async_trait]
237pub trait TransactionSession {
238    /// Commit a transaction
239    async fn commit(self) -> Result<(), DbErr>;
240
241    /// Rolls back a transaction explicitly
242    async fn rollback(self) -> Result<(), DbErr>;
243}