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}