noxu_db/cursor_config.rs
1//! Cursor configuration.
2//!
3
4/// Configuration for opening a cursor.
5///
6/// Specifies the configuration parameters used to open a cursor on a database.
7///
8/// # Changes in v1.5.1
9///
10/// Four fields that used to live on this struct (`read_committed`,
11/// `non_sticky`, `evict_ln`, `prefix_constraint`) were removed because
12/// the engine never consulted them. See
13/// the 2026 review for the full rationale
14/// and migration notes.
15///
16/// * `read_committed` — to use read-committed isolation, set it on the
17/// surrounding [`crate::transaction_config::TransactionConfig`] (it
18/// is honoured by [`crate::cursor::Cursor`] via the txn's locker)
19/// or pass [`crate::lock_mode::LockMode::ReadCommitted`] to a
20/// per-operation [`crate::read_options::ReadOptions`].
21/// * `non_sticky` — Rust cursors are bound to their owning scope and
22/// are not sticky to a transaction in the JE sense; the flag had
23/// no observable effect.
24/// * `evict_ln` — use [`crate::cache_mode::CacheMode::Unchanged`] /
25/// `EvictLn` on the surrounding `DatabaseConfig` instead.
26/// * `prefix_constraint` — application code should compare the
27/// returned key against its own prefix and stop iterating; the
28/// engine's BIN-level prefix is independent of the user's
29/// range-scan termination condition.
30#[derive(Debug, Clone, PartialEq, Eq, Default)]
31#[must_use]
32pub struct CursorConfig {
33 /// Use read-uncommitted isolation (dirty reads).
34 ///
35 /// When `true`, the cursor is opened in read-only mode and skips
36 /// read-lock acquisition. This mirrors JE's
37 /// `CursorConfig.setReadUncommitted(true)` shape and is the only
38 /// isolation override consulted at cursor-open time. Per-operation
39 /// `LockMode` overrides (passed via
40 /// [`crate::read_options::ReadOptions`]) take precedence at the
41 /// individual `get` call.
42 pub read_uncommitted: bool,
43}
44
45impl CursorConfig {
46 /// Creates a new CursorConfig with default settings.
47 pub fn new() -> Self {
48 Self::default()
49 }
50
51 /// Sets read-uncommitted isolation.
52 pub fn set_read_uncommitted(
53 &mut self,
54 read_uncommitted: bool,
55 ) -> &mut Self {
56 self.read_uncommitted = read_uncommitted;
57 self
58 }
59
60 /// Builder-style method to set read_uncommitted.
61 pub fn with_read_uncommitted(mut self, read_uncommitted: bool) -> Self {
62 self.read_uncommitted = read_uncommitted;
63 self
64 }
65
66 /// Creates a CursorConfig for read-uncommitted isolation.
67 pub fn read_uncommitted() -> Self {
68 Self::new().with_read_uncommitted(true)
69 }
70}
71
72#[cfg(test)]
73mod tests {
74 use super::*;
75
76 #[test]
77 fn test_new_defaults_to_no_read_uncommitted() {
78 let config = CursorConfig::new();
79 assert!(!config.read_uncommitted);
80 }
81
82 #[test]
83 fn test_set_read_uncommitted() {
84 let mut config = CursorConfig::new();
85 config.set_read_uncommitted(true);
86 assert!(config.read_uncommitted);
87 }
88
89 #[test]
90 fn test_with_read_uncommitted() {
91 let config = CursorConfig::new().with_read_uncommitted(true);
92 assert!(config.read_uncommitted);
93 }
94
95 #[test]
96 fn test_read_uncommitted_factory() {
97 let config = CursorConfig::read_uncommitted();
98 assert!(config.read_uncommitted);
99 }
100
101 #[test]
102 fn test_default() {
103 let config = CursorConfig::default();
104 assert!(!config.read_uncommitted);
105 }
106
107 #[test]
108 fn test_clone_eq() {
109 let a = CursorConfig::read_uncommitted();
110 let b = a.clone();
111 assert_eq!(a, b);
112
113 let c = CursorConfig::new();
114 assert_ne!(a, c);
115 }
116
117 #[test]
118 fn test_debug_format_mentions_field() {
119 let config = CursorConfig::read_uncommitted();
120 let debug = format!("{:?}", config);
121 assert!(debug.contains("read_uncommitted"));
122 }
123}