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