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 {
221 let tx = ReadTransaction::new(&env);
222 assert!(tx.get::<str, String>(&db, "test").is_none());
223 }
224
225 let mut tx = WriteTransaction::new(&env);
227 assert!(tx.get::<str, String>(&db, "test").is_none());
228
229 tx.put_reserve(&db, "test", "one");
231 assert_eq!(tx.get::<str, String>(&db, "test"), Some("one".to_string()));
232 tx.put_reserve(&db, "test", "two");
234 assert_eq!(tx.get::<str, String>(&db, "test"), Some("two".to_string()));
235 tx.commit();
236
237 let tx = ReadTransaction::new(&env);
239 assert_eq!(tx.get::<str, String>(&db, "test"), Some("two".to_string()));
240 tx.close();
241
242 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 {
250 let tx = ReadTransaction::new(&env);
251 assert!(tx.get::<str, String>(&db, "test").is_none());
252 }
253
254 let mut tx = WriteTransaction::new(&env);
256 tx.put_reserve(&db, "test", "one");
257 tx.abort();
258
259 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 let tx = ReadTransaction::new(&env);
275 assert!(tx.get::<str, String>(&db, "test").is_none());
276
277 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 assert!(tx.get::<str, String>(&db, "test").is_none());
285
286 txw.commit();
288
289 assert!(tx.get::<str, String>(&db, "test").is_none());
291
292 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 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 {
315 let tx = ReadTransaction::new(&env);
316 assert_eq!(tx.get::<str, u32>(&db, "test"), Some(125));
317 }
318
319 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 {
328 let tx = ReadTransaction::new(&env);
329 assert_eq!(tx.get::<str, u32>(&db, "test"), Some(12));
330 }
331
332 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 {
342 let tx = ReadTransaction::new(&env);
343 assert_eq!(tx.get::<str, u32>(&db, "test"), Some(125));
344 }
345
346 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 {
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 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 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));
395assert!(!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}
405
406 env.drop_database().unwrap();
407 }
408}