1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
//! Lock modes for read operations.
//!
/// Lock mode for read operations.
///
/// Specifies the locking behavior for a read operation. Controls isolation
/// level and whether locks are acquired.
///
///
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
pub enum LockMode {
/// Use the default isolation level for the cursor or transaction.
///
/// For transactional operations, this is typically read-committed.
/// For non-transactional operations, this is read-uncommitted.
#[default]
Default,
/// Read without acquiring locks (dirty reads).
///
/// Reads may return data that is currently being modified by another
/// transaction and may be rolled back. Provides maximum concurrency
/// but minimal isolation.
ReadUncommitted,
/// Read with read-committed isolation.
///
/// A read lock is acquired but released when the cursor moves or
/// the read operation completes. Prevents dirty reads but allows
/// non-repeatable reads.
ReadCommitted,
/// Read-modify-write: acquire write lock on read.
///
/// Acquires a write lock immediately, even though the operation is a read.
/// Use this when you intend to modify the record after reading it, to
/// avoid deadlocks caused by lock upgrades.
Rmw,
}
impl LockMode {
/// Returns whether this mode allows dirty reads.
pub fn allows_dirty_reads(&self) -> bool {
matches!(self, LockMode::ReadUncommitted)
}
/// Returns whether this mode acquires a write lock.
pub fn acquires_write_lock(&self) -> bool {
matches!(self, LockMode::Rmw)
}
/// Returns whether this mode provides read-committed isolation.
pub fn is_read_committed(&self) -> bool {
matches!(self, LockMode::ReadCommitted | LockMode::Default)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_default() {
assert_eq!(LockMode::default(), LockMode::Default);
}
#[test]
fn test_allows_dirty_reads() {
assert!(LockMode::ReadUncommitted.allows_dirty_reads());
assert!(!LockMode::Default.allows_dirty_reads());
assert!(!LockMode::ReadCommitted.allows_dirty_reads());
assert!(!LockMode::Rmw.allows_dirty_reads());
}
#[test]
fn test_acquires_write_lock() {
assert!(LockMode::Rmw.acquires_write_lock());
assert!(!LockMode::Default.acquires_write_lock());
assert!(!LockMode::ReadUncommitted.acquires_write_lock());
assert!(!LockMode::ReadCommitted.acquires_write_lock());
}
#[test]
fn test_is_read_committed() {
assert!(LockMode::Default.is_read_committed());
assert!(LockMode::ReadCommitted.is_read_committed());
assert!(!LockMode::ReadUncommitted.is_read_committed());
assert!(!LockMode::Rmw.is_read_committed());
}
#[test]
fn test_equality() {
assert_eq!(LockMode::Default, LockMode::Default);
assert_ne!(LockMode::Default, LockMode::Rmw);
}
#[test]
fn test_clone() {
let mode1 = LockMode::Rmw;
let mode2 = mode1;
assert_eq!(mode1, mode2);
}
#[test]
fn test_copy() {
let mode1 = LockMode::ReadCommitted;
let mode2 = mode1;
assert_eq!(mode1, mode2);
}
#[test]
fn test_debug() {
let mode = LockMode::Rmw;
let debug = format!("{:?}", mode);
assert_eq!(debug, "Rmw");
}
}