1mod cursor;
2mod database;
3mod iterators;
4mod transaction;
5
6pub use self::{cursor::*, database::*, iterators::*, transaction::*};
7use crate::traits::Database;
8
9pub trait OptionalTransaction<'db, 'txn> {
13 fn or_new<'inner>(self, db: &'inner MdbxDatabase) -> TransactionProxy<'db, 'txn>
14 where
15 'db: 'txn,
16 'inner: 'db;
17}
18
19impl<'db, 'txn, T: AsRef<MdbxReadTransaction<'db>>> OptionalTransaction<'db, 'txn>
20 for Option<&'txn T>
21{
22 fn or_new<'inner>(self, db: &'inner MdbxDatabase) -> TransactionProxy<'db, 'txn>
23 where
24 'db: 'txn,
25 'inner: 'db,
26 {
27 match self {
28 Some(txn) => TransactionProxy::Read(txn.as_ref()),
29 None => TransactionProxy::OwnedRead(db.read_transaction()),
30 }
31 }
32}
33
34#[cfg(test)]
35mod tests {
36 use tempfile::tempdir;
37
38 use super::*;
39 use crate::{
40 declare_table,
41 traits::{Database, DupReadCursor, ReadCursor, ReadTransaction, WriteTransaction},
42 };
43
44 declare_table!(TestTable, "test", String => String);
45 declare_table!(DupTestTable, "dup_test", String => dup(u32));
46 declare_table!(U32DupTable, "u32_dup", u32 => dup(u32));
47 declare_table!(U32Table, "u32_nodup", u32 => u32);
48
49 #[test]
50 fn it_can_save_basic_objects() {
51 let tempdir = tempdir().unwrap();
52 {
53 let db = MdbxDatabase::new(
54 tempdir.path().join("test"),
55 DatabaseConfig {
56 max_tables: Some(1),
57 ..Default::default()
58 },
59 )
60 .unwrap();
61 let table = TestTable {};
62 db.create_regular_table(&table);
63
64 {
66 let tx = db.read_transaction();
67 assert!(tx.get(&table, &"test".to_string()).is_none());
68 }
69
70 let mut tx = db.write_transaction();
72 assert!(tx.get(&table, &"test".to_string()).is_none());
73
74 tx.put(&table, &"test".to_string(), &"one".to_string());
76 assert_eq!(tx.get(&table, &"test".to_string()), Some("one".to_string()));
77 tx.put(&table, &"test".to_string(), &"two".to_string());
79 assert_eq!(tx.get(&table, &"test".to_string()), Some("two".to_string()));
80 tx.commit();
81
82 let tx = db.read_transaction();
84 assert_eq!(tx.get(&table, &"test".to_string()), Some("two".to_string()));
85 tx.close();
86
87 let mut tx = db.write_transaction();
89 tx.remove(&table, &"test".to_string());
90 assert!(tx.get(&table, &"test".to_string()).is_none());
91 tx.commit();
92
93 {
95 let tx = db.read_transaction();
96 assert!(tx.get(&table, &"test".to_string()).is_none());
97 }
98
99 let mut tx = db.write_transaction();
101 tx.put(&table, &"test".to_string(), &"one".to_string());
102 tx.abort();
103
104 let tx = db.read_transaction();
106 assert!(tx.get(&table, &"test".to_string()).is_none());
107 }
108 }
109
110 #[test]
111 fn isolation_test() {
112 let tempdir = tempdir().unwrap();
113 {
114 let db = MdbxDatabase::new(
115 tempdir.path().join("test2"),
116 DatabaseConfig {
117 max_tables: Some(1),
118 ..Default::default()
119 },
120 )
121 .unwrap();
122 let table = TestTable {};
123 db.create_regular_table(&table);
124
125 let tx = db.read_transaction();
127 assert!(tx.get(&table, &"test".to_string()).is_none());
128
129 let mut txw = db.write_transaction();
131 assert!(txw.get(&table, &"test".to_string()).is_none());
132 txw.put(&table, &"test".to_string(), &"one".to_string());
133 assert_eq!(
134 txw.get(&table, &"test".to_string()),
135 Some("one".to_string())
136 );
137
138 assert!(tx.get(&table, &"test".to_string()).is_none());
140
141 txw.commit();
143
144 assert!(tx.get(&table, &"test".to_string()).is_none());
146
147 let tx2 = db.read_transaction();
149 assert_eq!(
150 tx2.get(&table, &"test".to_string()),
151 Some("one".to_string())
152 );
153 }
154 tempdir.close().unwrap();
155 }
156
157 #[test]
158 fn duplicates_test() {
159 let tempdir = tempdir().unwrap();
160 {
161 let db = MdbxDatabase::new(
162 tempdir.path().join("test3"),
163 DatabaseConfig {
164 max_tables: Some(1),
165 ..Default::default()
166 },
167 )
168 .unwrap();
169 let table = DupTestTable {};
170 db.create_dup_table(&table);
171
172 let mut txw = db.write_transaction();
174 assert!(txw.get(&table, &"test".to_string()).is_none());
175 txw.put(&table, &"test".to_string(), &125);
176 assert_eq!(txw.get(&table, &"test".to_string()), Some(125));
177 txw.commit();
178
179 {
181 let tx = db.read_transaction();
182 assert_eq!(tx.get(&table, &"test".to_string()), Some(125));
183 }
184
185 let mut txw = db.write_transaction();
187 assert_eq!(txw.get(&table, &"test".to_string()), Some(125));
188 txw.put(&table, &"test".to_string(), &12);
189 assert_eq!(txw.get(&table, &"test".to_string()), Some(12));
190 txw.commit();
191
192 {
194 let tx = db.read_transaction();
195 assert_eq!(tx.get(&table, &"test".to_string()), Some(12));
196 }
197
198 let mut txw = db.write_transaction();
200 assert_eq!(txw.get(&table, &"test".to_string()), Some(12));
201 txw.remove_item(&table, &"test".to_string(), &12);
202 txw.put(&table, &"test".to_string(), &5783);
203 assert_eq!(txw.get(&table, &"test".to_string()), Some(125));
204 txw.commit();
205
206 {
208 let tx = db.read_transaction();
209 assert_eq!(tx.get(&table, &"test".to_string()), Some(125));
210 }
211
212 let mut txw = db.write_transaction();
214 assert_eq!(txw.get(&table, &"test".to_string()), Some(125));
215 txw.remove(&table, &"test".to_string());
216 assert!(txw.get(&table, &"test".to_string()).is_none());
217 txw.commit();
218
219 {
221 let tx = db.read_transaction();
222 assert!(tx.get(&table, &"test".to_string()).is_none());
223 }
224 }
225 tempdir.close().unwrap();
226 }
227
228 #[test]
229 fn cursor_test() {
230 let tempdir = tempdir().unwrap();
231 {
232 let db = MdbxDatabase::new(
233 tempdir.path().join("test4"),
234 DatabaseConfig {
235 max_tables: Some(1),
236 ..Default::default()
237 },
238 )
239 .unwrap();
240 let table = DupTestTable {};
241 db.create_dup_table(&table);
242
243 let test1: String = "test1".to_string();
244 let test2: String = "test2".to_string();
245
246 let mut txw = db.write_transaction();
248 assert!(txw.get(&table, &"test".to_string()).is_none());
249 txw.put(&table, &"test1".to_string(), &125);
250 txw.put(&table, &"test1".to_string(), &12);
251 txw.put(&table, &"test1".to_string(), &5783);
252 txw.put(&table, &"test2".to_string(), &5783);
253 txw.commit();
254
255 let tx = db.read_transaction();
257 let mut cursor = tx.dup_cursor(&table);
258 assert_eq!(cursor.first(), Some((test1.clone(), 12)));
259 assert_eq!(cursor.last(), Some((test2.clone(), 5783)));
260 assert_eq!(cursor.prev(), Some((test1.clone(), 5783)));
261 assert_eq!(cursor.first_duplicate(), Some(12));
262 assert_eq!(cursor.next_duplicate(), Some((test1.clone(), 125)));
263 assert_eq!(cursor.prev_duplicate(), Some((test1.clone(), 12)));
264 assert_eq!(cursor.next_no_duplicate(), Some((test2.clone(), 5783)));
265 assert!(cursor.set_key(&"test".to_string()).is_none());
266 assert_eq!(cursor.set_key(&"test1".to_string()), Some(12));
267 assert_eq!(cursor.count_duplicates(), 3);
268 assert_eq!(cursor.last_duplicate(), Some(5783));
269
270 assert_eq!(cursor.get_current(), Some((test1.clone(), 5783)));
271
272 assert_eq!(cursor.get_current(), Some((test1, 5783)));
273 assert!(cursor.prev_no_duplicate().is_none());
274 assert_eq!(cursor.next(), Some((test2, 5783)));
275 }
276 tempdir.close().unwrap();
277 }
278
279 #[test]
280 fn it_correctly_orders_u32() {
281 let tempdir = tempdir().unwrap();
282 {
283 let db = MdbxDatabase::new(
284 tempdir.path().join("test5"),
285 DatabaseConfig {
286 max_tables: Some(2),
287 ..Default::default()
288 },
289 )
290 .unwrap();
291 let dup_table = U32DupTable {};
292 let table = U32Table {};
293 db.create_dup_table(&dup_table);
294 db.create_regular_table(&table);
295
296 let mut txw = db.write_transaction();
298
299 txw.put(&table, &256, &2);
300 txw.put(&table, &3, &2);
301
302 txw.put(&dup_table, &256, &3);
303 txw.put(&dup_table, &3, &3);
304 txw.put(&dup_table, &256, &2);
305 txw.put(&dup_table, &3, &2);
306 txw.commit();
307
308 let tx = db.read_transaction();
310
311 let mut cursor = tx.cursor(&table);
312 assert_eq!(cursor.first(), Some((3, 2)));
313 assert_eq!(cursor.last(), Some((256, 2)));
314
315 let mut cursor = tx.dup_cursor(&dup_table);
316 assert_eq!(cursor.first(), Some((3, 2)));
317 assert_eq!(cursor.last(), Some((256, 3)));
318 assert_eq!(cursor.prev(), Some((256, 2)));
319 assert_eq!(cursor.prev(), Some((3, 3)));
320 assert_eq!(cursor.first_duplicate(), Some(2));
321 assert_eq!(cursor.last_duplicate(), Some(3));
322 assert_eq!(cursor.next_duplicate(), None);
323 assert_eq!(cursor.next_no_duplicate(), Some((256, 2)));
324 }
325 tempdir.close().unwrap();
326 }
327}