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`.
171pub trait TransactionTrait {
172 /// The concrete type for the transaction
173 type Transaction: ConnectionTrait + TransactionTrait + TransactionSession;
174
175 /// Execute SQL `BEGIN` transaction.
176 /// Returns a Transaction that can be committed or rolled back
177 fn begin(&self) -> Result<Self::Transaction, DbErr>;
178
179 /// Execute SQL `BEGIN` transaction with isolation level and/or access mode.
180 /// Returns a Transaction that can be committed or rolled back
181 fn begin_with_config(
182 &self,
183 isolation_level: Option<IsolationLevel>,
184 access_mode: Option<AccessMode>,
185 ) -> Result<Self::Transaction, DbErr>;
186
187 /// Execute SQL `BEGIN` transaction with isolation level and/or access mode.
188 /// Returns a Transaction that can be committed or rolled back
189 fn begin_with_options(&self, options: TransactionOptions) -> Result<Self::Transaction, DbErr>;
190
191 /// Execute the function inside a transaction.
192 /// If the function returns an error, the transaction will be rolled back. If it does not return an error, the transaction will be committed.
193 fn transaction<F, T, E>(&self, callback: F) -> Result<T, TransactionError<E>>
194 where
195 F: for<'c> FnOnce(&'c Self::Transaction) -> Result<T, E>,
196 E: std::fmt::Display + std::fmt::Debug;
197
198 /// Execute the function inside a transaction with isolation level and/or access mode.
199 /// If the function returns an error, the transaction will be rolled back. If it does not return an error, the transaction will be committed.
200 fn transaction_with_config<F, T, E>(
201 &self,
202 callback: F,
203 isolation_level: Option<IsolationLevel>,
204 access_mode: Option<AccessMode>,
205 ) -> Result<T, TransactionError<E>>
206 where
207 F: for<'c> FnOnce(&'c Self::Transaction) -> Result<T, E>,
208 E: std::fmt::Display + std::fmt::Debug;
209}
210
211/// Represents an open transaction
212pub trait TransactionSession {
213 /// Commit a transaction
214 fn commit(self) -> Result<(), DbErr>;
215
216 /// Rolls back a transaction explicitly
217 fn rollback(self) -> Result<(), DbErr>;
218}