Skip to main content

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}