Skip to content

Commit d9431f1

Browse files
committed
Add SQLite transaction modes
1 parent 9a59e1e commit d9431f1

File tree

2 files changed

+47
-3
lines changed

2 files changed

+47
-3
lines changed

src/database/connection.rs

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,45 @@ impl std::fmt::Display for AccessMode {
125125
}
126126
}
127127

128+
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
129+
/// Which kind of transaction to start. Only supported by SQLite.
130+
/// <https://www.sqlite.org/lang_transaction.html>
131+
#[cfg(feature = "sqlx-sqlite")]
132+
pub enum SqliteTransactionMode {
133+
/// The default. Transaction starts when the next statement is executed, and
134+
/// will be a read or write transaction depending on that statement.
135+
Deferred,
136+
/// Start a write transaction as soon as the BEGIN statement is received.
137+
Immediate,
138+
/// Start a write transaction as soon as the BEGIN statement is received.
139+
/// When in non-WAL mode, also block all other transactions from reading the
140+
/// database.
141+
Exclusive,
142+
}
143+
144+
#[cfg(feature = "sqlx-sqlite")]
145+
impl SqliteTransactionMode {
146+
/// The keyword used to start a transaction in this mode (the word coming after "BEGIN").
147+
pub fn sqlite_keyword(&self) -> &'static str {
148+
match self {
149+
SqliteTransactionMode::Deferred => "DEFERRED",
150+
SqliteTransactionMode::Immediate => "IMMEDIATE",
151+
SqliteTransactionMode::Exclusive => "EXCLUSIVE",
152+
}
153+
}
154+
}
155+
156+
/// Configuration for starting a transaction
157+
#[derive(Copy, Clone, Debug, PartialEq, Eq, Default)]
158+
pub struct TransactionConfig {
159+
#[cfg(any(feature = "sqlx-postgres", feature = "sqlx-mysql"))]
160+
pub isolation_level: Option<IsolationLevel>,
161+
#[cfg(any(feature = "sqlx-postgres", feature = "sqlx-mysql"))]
162+
pub access_mode: Option<AccessMode>,
163+
#[cfg(feature = "sqlx-sqlite")]
164+
pub sqlite_transaction_mode: Option<SqliteTransactionMode>,
165+
}
166+
128167
/// Spawn database transaction
129168
#[async_trait::async_trait]
130169
pub trait TransactionTrait {

src/database/transaction.rs

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
#![allow(unused_assignments)]
22
use crate::{
33
AccessMode, ConnectionTrait, DbBackend, DbErr, ExecResult, InnerConnection, IsolationLevel,
4-
QueryResult, Statement, StreamTrait, TransactionSession, TransactionStream, TransactionTrait,
5-
debug_print, error::*,
4+
QueryResult, SqliteTransactionMode, Statement, StreamTrait, TransactionSession,
5+
TransactionStream, TransactionTrait, debug_print, error::*,
66
};
77
#[cfg(feature = "sqlx-dep")]
88
use crate::{sqlx_error_to_exec_err, sqlx_error_to_query_err};
@@ -92,7 +92,12 @@ impl DatabaseTransaction {
9292
access_mode,
9393
)
9494
.await?;
95-
<sqlx::Sqlite as sqlx::Database>::TransactionManager::begin(c, None)
95+
// TODO using this for beginning a nested transaction currently causes an error. Should we make it a warning instead?
96+
let statement = match config.sqlite_transaction_mode {
97+
Some(mode) => Some(format!("BEGIN {}", mode.sqlite_keyword())),
98+
None => None,
99+
};
100+
<sqlx::Sqlite as sqlx::Database>::TransactionManager::begin(c, statement)
96101
.await
97102
.map_err(sqlx_error_to_query_err)
98103
}

0 commit comments

Comments
 (0)