Skip to content

Commit 401f692

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

File tree

2 files changed

+68
-12
lines changed

2 files changed

+68
-12
lines changed

src/database/connection.rs

Lines changed: 43 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
1+
use std::{future::Future, pin::Pin};
2+
3+
use futures_util::Stream;
4+
15
use crate::{
26
DbBackend, DbErr, ExecResult, QueryResult, Statement, StatementBuilder, TransactionError,
37
};
4-
use futures_util::Stream;
5-
use std::{future::Future, pin::Pin};
68

79
/// The generic API for a database connection that can perform query or execute statements.
810
/// It abstracts database connection and transaction
@@ -125,6 +127,45 @@ impl std::fmt::Display for AccessMode {
125127
}
126128
}
127129

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

src/database/transaction.rs

Lines changed: 25 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,20 @@
11
#![allow(unused_assignments)]
2+
use std::{future::Future, pin::Pin, sync::Arc};
3+
4+
use futures_util::lock::Mutex;
5+
#[cfg(feature = "sqlx-dep")]
6+
use sqlx::TransactionManager;
7+
use tracing::instrument;
8+
9+
#[cfg(feature = "sqlx-sqlite")]
10+
use crate::SqliteTransactionMode;
211
use crate::{
312
AccessMode, ConnectionTrait, DbBackend, DbErr, ExecResult, InnerConnection, IsolationLevel,
4-
QueryResult, Statement, StreamTrait, TransactionSession, TransactionStream, TransactionTrait,
5-
debug_print, error::*,
13+
QueryResult, SqliteTransactionMode, Statement, StreamTrait, TransactionSession,
14+
TransactionStream, TransactionTrait, debug_print, error::*,
615
};
716
#[cfg(feature = "sqlx-dep")]
817
use crate::{sqlx_error_to_exec_err, sqlx_error_to_query_err};
9-
use futures_util::lock::Mutex;
10-
#[cfg(feature = "sqlx-dep")]
11-
use sqlx::TransactionManager;
12-
use std::{future::Future, pin::Pin, sync::Arc};
13-
use tracing::instrument;
1418

1519
/// Defines a database transaction, whether it is an open transaction and the type of
1620
/// backend to use.
@@ -92,9 +96,20 @@ impl DatabaseTransaction {
9296
access_mode,
9397
)
9498
.await?;
95-
<sqlx::Sqlite as sqlx::Database>::TransactionManager::begin(c, None)
96-
.await
97-
.map_err(sqlx_error_to_query_err)
99+
// TODO using this for beginning a nested transaction currently causes an error. Should we make it a warning instead?
100+
let statement = match config.sqlite_transaction_mode {
101+
Some(mode) => Some(std::borrow::Cow::from(format!(
102+
"BEGIN {}",
103+
mode.sqlite_keyword()
104+
))),
105+
None => None,
106+
};
107+
<sqlx::Sqlite as sqlx::Database>::TransactionManager::begin(
108+
c,
109+
statement.into(),
110+
)
111+
.await
112+
.map_err(sqlx_error_to_query_err)
98113
}
99114
#[cfg(feature = "rusqlite")]
100115
InnerConnection::Rusqlite(c) => c.begin(),

0 commit comments

Comments
 (0)