Skip to main content

toasty_core/driver/operation/
transaction.rs

1use super::Operation;
2
3/// SQL transaction isolation levels.
4///
5/// Not all backends support all levels. The driver returns an error if an
6/// unsupported level is requested.
7///
8/// # Examples
9///
10/// ```
11/// use toasty_core::driver::operation::IsolationLevel;
12///
13/// let level = IsolationLevel::Serializable;
14/// assert_eq!(level, IsolationLevel::Serializable);
15/// ```
16#[derive(Debug, Clone, Copy, PartialEq, Eq)]
17pub enum IsolationLevel {
18    /// Transactions can see uncommitted changes from other transactions.
19    ReadUncommitted,
20    /// Transactions only see changes committed before each statement.
21    ReadCommitted,
22    /// Transactions see a consistent snapshot from the start of the transaction.
23    RepeatableRead,
24    /// Full serializability; transactions behave as if executed one at a time.
25    Serializable,
26}
27
28/// How a transaction acquires write locks.
29///
30/// Orthogonal to [`IsolationLevel`]: an isolation level describes *what
31/// anomalies* a transaction can observe; a mode describes *when* the
32/// transaction acquires its locks.
33///
34/// Only SQLite (and SQLite-compatible engines) currently expose this
35/// dimension to clients:
36///
37/// * [`Default`](Self::Default) → whatever the driver picks. For SQLite
38///   that is `BEGIN` (DEFERRED) today; for a future driver it may not
39///   be — e.g. Turso under MVCC plans to default to `BEGIN CONCURRENT`.
40/// * [`Deferred`](Self::Deferred) → `BEGIN` (DEFERRED): explicit
41///   deferred locking. Identical to `Default` on plain SQLite; on a
42///   driver whose default is *not* deferred (Turso MVCC), this is how
43///   the caller opts out of that default.
44/// * [`Immediate`](Self::Immediate) → `BEGIN IMMEDIATE`: write lock
45///   acquired up front, so a later write inside the transaction cannot
46///   fail with `SQLITE_BUSY`.
47/// * [`Exclusive`](Self::Exclusive) → `BEGIN EXCLUSIVE`: exclusive lock
48///   held for the lifetime of the transaction; no other connection —
49///   reader or writer — can make progress against the database file.
50///
51/// Drivers that do not implement a given mode return
52/// [`Error::unsupported_feature`](crate::Error::unsupported_feature) when
53/// the transaction starts. Future drivers may extend this enum (e.g. a
54/// Turso `Concurrent` variant for `BEGIN CONCURRENT` under MVCC).
55#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
56pub enum TransactionMode {
57    /// The driver's natural default. May differ from
58    /// [`Deferred`](Self::Deferred) on drivers that prefer a different
59    /// locking strategy (e.g. Turso MVCC → `BEGIN CONCURRENT`).
60    #[default]
61    Default,
62    /// Explicit deferred locking. SQLite → `BEGIN` (DEFERRED). Use this
63    /// to override a driver whose `Default` is not deferred.
64    Deferred,
65    /// Acquire a write lock when the transaction begins. SQLite →
66    /// `BEGIN IMMEDIATE`. Rejected by drivers without an equivalent.
67    Immediate,
68    /// Hold an exclusive lock for the entire transaction. SQLite →
69    /// `BEGIN EXCLUSIVE`. Rejected by drivers without an equivalent.
70    Exclusive,
71}
72
73/// A transaction lifecycle operation.
74///
75/// Covers the full transaction lifecycle: begin, commit, rollback, and
76/// savepoint management. Convert to [`Operation`] with `.into()`.
77///
78/// # Examples
79///
80/// ```
81/// use toasty_core::driver::operation::{Transaction, Operation};
82///
83/// // Start a default transaction
84/// let op: Operation = Transaction::start().into();
85///
86/// // Commit
87/// let op: Operation = Transaction::Commit.into();
88/// assert!(op.is_transaction_commit());
89/// ```
90#[derive(Debug, Clone)]
91pub enum Transaction {
92    /// Start a transaction with optional configuration.
93    ///
94    /// When `isolation` is `None`, `read_only` is `false`, and `mode` is
95    /// [`TransactionMode::Default`], the database's natural defaults are
96    /// used.
97    Start {
98        /// Optional isolation level. `None` uses the database default.
99        isolation: Option<IsolationLevel>,
100        /// When `true`, the transaction is read-only.
101        read_only: bool,
102        /// Lock-acquisition mode. See [`TransactionMode`].
103        mode: TransactionMode,
104    },
105
106    /// Commit a transaction
107    Commit,
108
109    /// Rollback a transaction
110    Rollback,
111
112    /// Create a savepoint with the given identifier
113    Savepoint(String),
114
115    /// Release (commit) a savepoint
116    ReleaseSavepoint(String),
117
118    /// Rollback to a savepoint, undoing work since it was created
119    RollbackToSavepoint(String),
120}
121
122impl Transaction {
123    /// Start a transaction with database defaults.
124    pub fn start() -> Self {
125        Self::Start {
126            isolation: None,
127            read_only: false,
128            mode: TransactionMode::Default,
129        }
130    }
131}
132
133impl Operation {
134    /// Returns `true` if this is a [`Transaction::Commit`] operation.
135    pub fn is_transaction_commit(&self) -> bool {
136        matches!(self, Operation::Transaction(Transaction::Commit))
137    }
138}
139
140impl From<Transaction> for Operation {
141    fn from(value: Transaction) -> Self {
142        Self::Transaction(value)
143    }
144}