Skip to main content

sea_orm/database/
connection.rs

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