Skip to main content

sea_orm/database/
connection.rs

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