nimiq_database/
volatile.rs

1use super::*;
2use super::lmdb::*;
3use tempdir::TempDir;
4use std::io;
5use std::error::Error;
6use std::fmt;
7
8#[derive(Debug)]
9pub struct VolatileEnvironment {
10    temp_dir: TempDir,
11    env: LmdbEnvironment,
12}
13
14#[derive(Debug)]
15pub enum VolatileDatabaseError {
16    IoError(io::Error),
17    LmdbError(lmdb_zero::Error),
18}
19
20
21impl fmt::Display for VolatileDatabaseError {
22    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
23        match self {
24            VolatileDatabaseError::IoError(e) => e.fmt(f),
25            VolatileDatabaseError::LmdbError(e) => e.fmt(f)
26        }
27    }
28}
29
30impl Error for VolatileDatabaseError {
31    fn description(&self) -> &str {
32        match self {
33            VolatileDatabaseError::IoError(e) => e.description(),
34            VolatileDatabaseError::LmdbError(e) => e.description()
35        }
36    }
37
38    fn source(&self) -> Option<&(dyn Error + 'static)> {
39        match self {
40            VolatileDatabaseError::IoError(e) => Some(e),
41            VolatileDatabaseError::LmdbError(e) => Some(e)
42        }
43    }
44}
45
46
47impl VolatileEnvironment {
48    pub fn new(max_dbs: u32) -> Result<Environment, VolatileDatabaseError> {
49        let temp_dir = TempDir::new("volatile-core").map_err(VolatileDatabaseError::IoError)?;
50        let path = temp_dir.path().to_str().ok_or_else(|| VolatileDatabaseError::IoError(io::Error::new(io::ErrorKind::InvalidInput, "Path cannot be converted into a string.")))?.to_string();
51        Ok(Environment::Volatile(VolatileEnvironment {
52            temp_dir,
53            env: LmdbEnvironment::new_lmdb_environment(&path, 0, max_dbs, open::NOSYNC | open::WRITEMAP).map_err(VolatileDatabaseError::LmdbError)?,
54        }))
55    }
56
57    pub fn new_with_lmdb_flags(max_dbs: u32, flags: open::Flags) -> Result<Environment, VolatileDatabaseError> {
58        let temp_dir = TempDir::new("volatile-core").map_err(VolatileDatabaseError::IoError)?;
59        let path = temp_dir.path().to_str().ok_or_else(|| VolatileDatabaseError::IoError(io::Error::new(io::ErrorKind::InvalidInput, "Path cannot be converted into a string.")))?.to_string();
60        Ok(Environment::Volatile(VolatileEnvironment {
61            temp_dir,
62            env: LmdbEnvironment::new_lmdb_environment(&path, 0, max_dbs, flags | open::NOSYNC | open::WRITEMAP).map_err(VolatileDatabaseError::LmdbError)?,
63        }))
64    }
65
66    pub(in super) fn open_database(&self, name: String, flags: DatabaseFlags) -> VolatileDatabase {
67        VolatileDatabase(self.env.open_database(name, flags))
68    }
69
70    pub(in super) fn drop_database(self) -> io::Result<()> {
71        Ok(())
72    }
73}
74
75#[derive(Debug)]
76pub struct VolatileDatabase<'env>(LmdbDatabase<'env>);
77
78impl<'env> VolatileDatabase<'env> {
79    pub(in super) fn as_lmdb<'a>(&'a self) -> &'a LmdbDatabase<'env> { &self.0 }
80}
81
82
83#[derive(Debug)]
84pub struct VolatileReadTransaction<'env>(LmdbReadTransaction<'env>);
85
86impl<'env> VolatileReadTransaction<'env> {
87    pub(in super) fn new(env: &'env VolatileEnvironment) -> Self {
88        VolatileReadTransaction(LmdbReadTransaction::new(&env.env))
89    }
90
91    pub(in super) fn get<K, V>(&self, db: &VolatileDatabase, key: &K) -> Option<V> where K: AsDatabaseBytes + ?Sized, V: FromDatabaseValue {
92        self.0.get(&db.0, key)
93    }
94
95    pub(in super) fn cursor<'txn, 'db>(&'txn self, db: &'db Database<'env>) -> VolatileCursor<'txn, 'db> {
96        VolatileCursor(self.0.cursor(db))
97    }
98}
99
100#[derive(Debug)]
101pub struct VolatileWriteTransaction<'env>(LmdbWriteTransaction<'env>);
102
103impl<'env> VolatileWriteTransaction<'env> {
104    pub(in super) fn new(env: &'env VolatileEnvironment) -> Self {
105        VolatileWriteTransaction(LmdbWriteTransaction::new(&env.env))
106    }
107
108    pub(in super) fn get<K, V>(&self, db: &VolatileDatabase, key: &K) -> Option<V> where K: AsDatabaseBytes + ?Sized, V: FromDatabaseValue {
109        self.0.get(&db.0, key)
110    }
111
112    pub(in super) fn put_reserve<K, V>(&mut self, db: &VolatileDatabase, key: &K, value: &V) where K: AsDatabaseBytes + ?Sized, V: IntoDatabaseValue + ?Sized {
113        self.0.put_reserve(&db.0, key, value)
114    }
115
116    pub(in super) fn put<K, V>(&mut self, db: &VolatileDatabase, key: &K, value: &V) where K: AsDatabaseBytes + ?Sized, V: AsDatabaseBytes + ?Sized {
117        self.0.put(&db.0, key, value)
118    }
119
120    pub(in super) fn remove<K>(&mut self, db: &VolatileDatabase, key: &K) where K: AsDatabaseBytes + ?Sized {
121        self.0.remove(&db.0, key)
122    }
123
124    pub(in super) fn remove_item<K, V>(&mut self, db: &VolatileDatabase, key: &K, value: &V) where K: AsDatabaseBytes + ?Sized, V: AsDatabaseBytes + ?Sized {
125        self.0.remove_item(&db.0, key, value)
126    }
127
128    pub(in super) fn commit(self) {
129        self.0.commit()
130    }
131
132    pub(in super) fn cursor<'txn, 'db>(&'txn self, db: &'db Database<'env>) -> VolatileCursor<'txn, 'db> {
133        VolatileCursor(self.0.cursor(db))
134    }
135}
136
137pub struct VolatileCursor<'txn, 'db>(LmdbCursor<'txn, 'db>);
138
139impl<'txn, 'db> VolatileCursor<'txn, 'db> {
140    pub(in super) fn first<K, V>(&mut self) -> Option<(K, V)> where K: FromDatabaseValue, V: FromDatabaseValue {
141        self.0.first()
142    }
143
144    pub(in super) fn first_duplicate<V>(&mut self) -> Option<V> where V: FromDatabaseValue {
145        self.0.first_duplicate()
146    }
147
148    pub(in super) fn last<K, V>(&mut self) -> Option<(K, V)> where K: FromDatabaseValue, V: FromDatabaseValue {
149        self.0.last()
150    }
151
152    pub(in super) fn last_duplicate<V>(&mut self) -> Option<V> where V: FromDatabaseValue {
153        self.0.last_duplicate()
154    }
155
156    pub(in super) fn seek_key_value<K, V>(&mut self, key: &K, value: &V) -> bool where K: AsDatabaseBytes + ?Sized, V: AsDatabaseBytes + ?Sized {
157        self.0.seek_key_value(key, value)
158    }
159
160    pub(in super) fn seek_key_nearest_value<K, V>(&mut self, key: &K, value: &V) -> Option<V> where K: AsDatabaseBytes + ?Sized, V: AsDatabaseBytes + FromDatabaseValue {
161        self.0.seek_key_nearest_value(key, value)
162    }
163
164    pub(in super) fn get_current<K, V>(&mut self) -> Option<(K, V)> where K: FromDatabaseValue, V: FromDatabaseValue {
165        self.0.get_current()
166    }
167
168    pub(in super) fn next<K, V>(&mut self) -> Option<(K, V)> where K: FromDatabaseValue, V: FromDatabaseValue {
169        self.0.next()
170    }
171
172    pub(in super) fn next_duplicate<K, V>(&mut self) -> Option<(K, V)> where K: FromDatabaseValue, V: FromDatabaseValue {
173        self.0.next_duplicate()
174    }
175
176    pub(in super) fn next_no_duplicate<K, V>(&mut self) -> Option<(K, V)> where K: FromDatabaseValue, V: FromDatabaseValue {
177        self.0.next_no_duplicate()
178    }
179
180    pub(in super) fn prev<K, V>(&mut self) -> Option<(K, V)> where K: FromDatabaseValue, V: FromDatabaseValue {
181        self.0.prev()
182    }
183
184    pub(in super) fn prev_duplicate<K, V>(&mut self) -> Option<(K, V)> where K: FromDatabaseValue, V: FromDatabaseValue {
185        self.0.prev_duplicate()
186    }
187
188    pub(in super) fn prev_no_duplicate<K, V>(&mut self) -> Option<(K, V)> where K: FromDatabaseValue, V: FromDatabaseValue {
189        self.0.prev_no_duplicate()
190    }
191
192    pub(in super) fn seek_key<K, V>(&mut self, key: &K) -> Option<V> where K: AsDatabaseBytes + ?Sized, V: FromDatabaseValue {
193        self.0.seek_key(key)
194    }
195
196    pub(in super) fn seek_key_both<K, V>(&mut self, key: &K) -> Option<(K, V)> where K: AsDatabaseBytes + FromDatabaseValue, V: FromDatabaseValue {
197        self.0.seek_key_both(key)
198    }
199
200    pub(in super) fn seek_range_key<K, V>(&mut self, key: &K) -> Option<(K, V)> where K: AsDatabaseBytes + FromDatabaseValue, V: FromDatabaseValue {
201        self.0.seek_range_key(key)
202    }
203
204    pub(in super) fn count_duplicates(&mut self) -> usize {
205        self.0.count_duplicates()
206    }
207}
208
209#[cfg(test)]
210mod tests {
211    use super::*;
212
213    #[test]
214    fn it_can_save_basic_objects() {
215        let env = VolatileEnvironment::new(1).unwrap();
216        {
217            let db = env.open_database("test".to_string());
218
219            // Read non-existent value.
220            {
221                let tx = ReadTransaction::new(&env);
222                assert!(tx.get::<str, String>(&db, "test").is_none());
223            }
224
225            // Read non-existent value.
226            let mut tx = WriteTransaction::new(&env);
227            assert!(tx.get::<str, String>(&db, "test").is_none());
228
229            // Write and read value.
230            tx.put_reserve(&db, "test", "one");
231            assert_eq!(tx.get::<str, String>(&db, "test"), Some("one".to_string()));
232            // Overwrite and read value.
233            tx.put_reserve(&db, "test", "two");
234            assert_eq!(tx.get::<str, String>(&db, "test"), Some("two".to_string()));
235            tx.commit();
236
237            // Read value.
238            let tx = ReadTransaction::new(&env);
239            assert_eq!(tx.get::<str, String>(&db, "test"), Some("two".to_string()));
240            tx.close();
241
242            // Remove value.
243            let mut tx = WriteTransaction::new(&env);
244            tx.remove(&db, "test");
245            assert!(tx.get::<str, String>(&db, "test").is_none());
246            tx.commit();
247
248            // Check removal.
249            {
250                let tx = ReadTransaction::new(&env);
251                assert!(tx.get::<str, String>(&db, "test").is_none());
252            }
253
254            // Write and abort.
255            let mut tx = WriteTransaction::new(&env);
256            tx.put_reserve(&db, "test", "one");
257            tx.abort();
258
259            // Check aborted transaction.
260            let tx = ReadTransaction::new(&env);
261            assert!(tx.get::<str, String>(&db, "test").is_none());
262        }
263
264        env.drop_database().unwrap();
265    }
266
267    #[test]
268    fn isolation_test() {
269        let env = VolatileEnvironment::new_with_lmdb_flags( 1, open::NOTLS).unwrap();
270        {
271            let db = env.open_database("test".to_string());
272
273            // Read non-existent value.
274            let tx = ReadTransaction::new(&env);
275            assert!(tx.get::<str, String>(&db, "test").is_none());
276
277            // WriteTransaction.
278            let mut txw = WriteTransaction::new(&env);
279            assert!(txw.get::<str, String>(&db, "test").is_none());
280            txw.put_reserve(&db, "test", "one");
281            assert_eq!(txw.get::<str, String>(&db, "test"), Some("one".to_string()));
282
283            // ReadTransaction should still have the old state.
284            assert!(tx.get::<str, String>(&db, "test").is_none());
285
286            // Commit WriteTransaction.
287            txw.commit();
288
289            // ReadTransaction should still have the old state.
290            assert!(tx.get::<str, String>(&db, "test").is_none());
291
292            // Have a new ReadTransaction read the new state.
293            let tx2 = ReadTransaction::new(&env);
294            assert_eq!(tx2.get::<str, String>(&db, "test"), Some("one".to_string()));
295        }
296
297        env.drop_database().unwrap();
298    }
299
300    #[test]
301    fn duplicates_test() {
302        let env = VolatileEnvironment::new_with_lmdb_flags( 1, open::NOTLS).unwrap();
303        {
304            let db = env.open_database_with_flags("test".to_string(), DatabaseFlags::DUPLICATE_KEYS | DatabaseFlags::DUP_UINT_VALUES);
305
306            // Write one value.
307            let mut txw = WriteTransaction::new(&env);
308            assert!(txw.get::<str, u32>(&db, "test").is_none());
309            txw.put::<str, u32>(&db, "test", &125);
310            assert_eq!(txw.get::<str, u32>(&db, "test"), Some(125));
311            txw.commit();
312
313            // Have a new ReadTransaction read the new state.
314            {
315                let tx = ReadTransaction::new(&env);
316                assert_eq!(tx.get::<str, u32>(&db, "test"), Some(125));
317            }
318
319            // Write a second smaller value.
320            let mut txw = WriteTransaction::new(&env);
321            assert_eq!(txw.get::<str, u32>(&db, "test"), Some(125));
322            txw.put::<str, u32>(&db, "test", &12);
323            assert_eq!(txw.get::<str, u32>(&db, "test"), Some(12));
324            txw.commit();
325
326            // Have a new ReadTransaction read the smaller value.
327            {
328                let tx = ReadTransaction::new(&env);
329                assert_eq!(tx.get::<str, u32>(&db, "test"), Some(12));
330            }
331
332            // Remove smaller value and write larger value.
333            let mut txw = WriteTransaction::new(&env);
334            assert_eq!(txw.get::<str, u32>(&db, "test"), Some(12));
335            txw.remove_item::<str, u32>(&db, "test", &12);
336            txw.put::<str, u32>(&db, "test", &5783);
337            assert_eq!(txw.get::<str, u32>(&db, "test"), Some(125));
338            txw.commit();
339
340            // Have a new ReadTransaction read the smallest value.
341            {
342                let tx = ReadTransaction::new(&env);
343                assert_eq!(tx.get::<str, u32>(&db, "test"), Some(125));
344            }
345
346            // Remove everything.
347            let mut txw = WriteTransaction::new(&env);
348            assert_eq!(txw.get::<str, u32>(&db, "test"), Some(125));
349            txw.remove::<str>(&db, "test");
350            assert!(txw.get::<str, u32>(&db, "test").is_none());
351            txw.commit();
352
353            // Have a new ReadTransaction read the new state.
354            {
355                let tx = ReadTransaction::new(&env);
356                assert!(tx.get::<str, u32>(&db, "test").is_none());
357            }
358        }
359
360        env.drop_database().unwrap();
361    }
362
363    #[test]
364    fn cursor_test() {
365        let env = VolatileEnvironment::new_with_lmdb_flags( 1, open::NOTLS).unwrap();
366        {
367            let db = env.open_database_with_flags("test".to_string(), DatabaseFlags::DUPLICATE_KEYS | DatabaseFlags::DUP_UINT_VALUES);
368
369            let test1: String = "test1".to_string();
370            let test2: String = "test2".to_string();
371
372            // Write some values.
373            let mut txw = WriteTransaction::new(&env);
374            assert!(txw.get::<str, u32>(&db, "test").is_none());
375            txw.put::<str, u32>(&db, "test1", &125);
376            txw.put::<str, u32>(&db, "test1", &12);
377            txw.put::<str, u32>(&db, "test1", &5783);
378            txw.put::<str, u32>(&db, "test2", &5783);
379            txw.commit();
380
381            // Have a new ReadTransaction read the new state.
382            let tx = ReadTransaction::new(&env);
383            let mut cursor = tx.cursor(&db);
384            assert_eq!(cursor.first::<String, u32>(), Some((test1.clone(), 12)));
385            assert_eq!(cursor.last::<String, u32>(), Some((test2.clone(), 5783)));
386            assert_eq!(cursor.prev::<String, u32>(), Some((test1.clone(), 5783)));
387            assert_eq!(cursor.first_duplicate::<u32>(), Some(12));
388            assert_eq!(cursor.next_duplicate::<String, u32>(), Some((test1.clone(), 125)));
389            assert_eq!(cursor.prev_duplicate::<String, u32>(), Some((test1.clone(), 12)));
390            assert_eq!(cursor.next_no_duplicate::<String, u32>(), Some((test2.clone(), 5783)));
391            assert!(cursor.seek_key::<str, u32>("test").is_none());
392            assert_eq!(cursor.seek_key::<str, u32>("test1"), Some(12));
393            assert_eq!(cursor.count_duplicates(), 3);
394            assert_eq!(cursor.last_duplicate::<u32>(), Some(5783));
395//            assert_eq!(cursor.seek_key_both::<String, u32>(&test1), Some((test1.clone(), 12)));
396            assert!(!cursor.seek_key_value::<str, u32>("test1", &15));
397            assert!(cursor.seek_key_value::<str, u32>("test1", &125));
398            assert_eq!(cursor.get_current::<String, u32>(), Some((test1.clone(), 125)));
399            assert_eq!(cursor.seek_key_nearest_value::<str, u32>("test1", &126), Some(5783));
400            assert_eq!(cursor.get_current::<String, u32>(), Some((test1.clone(), 5783)));
401            assert!(cursor.prev_no_duplicate::<String, u32>().is_none());
402            assert_eq!(cursor.next::<String, u32>(), Some((test2.clone(), 5783)));
403//            assert_eq!(cursor.seek_range_key::<String, u32>("test"), Some((test1.clone(), 12)));
404        }
405
406        env.drop_database().unwrap();
407    }
408}