heed/databases/database.rs
1use std::borrow::Cow;
2use std::ops::{Bound, RangeBounds};
3use std::{any, fmt, marker, mem, ptr};
4
5use heed_traits::{Comparator, LexicographicComparator};
6use types::{DecodeIgnore, LazyDecode};
7
8use crate::cursor::MoveOperation;
9use crate::envs::DefaultComparator;
10use crate::iteration_method::MoveOnCurrentKeyDuplicates;
11use crate::mdb::error::mdb_result;
12use crate::mdb::ffi;
13use crate::mdb::lmdb_flags::{AllDatabaseFlags, DatabaseFlags};
14use crate::*;
15
16/// Options and flags which can be used to configure how a [`Database`] is opened.
17///
18/// # Examples
19///
20/// Opening a file to read:
21///
22/// ```
23/// # use std::fs;
24/// # use std::path::Path;
25/// # use heed::EnvOpenOptions;
26/// use heed::types::*;
27/// use heed::byteorder::BigEndian;
28///
29/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
30/// # let dir = tempfile::tempdir()?;
31/// # let env = unsafe { EnvOpenOptions::new()
32/// # .map_size(10 * 1024 * 1024) // 10MB
33/// # .max_dbs(3000)
34/// # .open(dir.path())?
35/// # };
36/// type BEI64 = I64<BigEndian>;
37///
38/// // Imagine you have an optional name
39/// let conditional_name = Some("big-endian-iter");
40///
41/// let mut wtxn = env.write_txn()?;
42/// let mut options = env.database_options().types::<BEI64, Unit>();
43/// if let Some(name) = conditional_name {
44/// options.name(name);
45/// }
46/// let db = options.create(&mut wtxn)?;
47///
48/// # db.clear(&mut wtxn)?;
49/// db.put(&mut wtxn, &68, &())?;
50/// db.put(&mut wtxn, &35, &())?;
51/// db.put(&mut wtxn, &0, &())?;
52/// db.put(&mut wtxn, &42, &())?;
53///
54/// wtxn.commit()?;
55/// # Ok(()) }
56/// ```
57#[derive(Debug)]
58pub struct DatabaseOpenOptions<'e, 'n, T, KC, DC, C = DefaultComparator, CDUP = DefaultComparator> {
59 env: &'e Env<T>,
60 types: marker::PhantomData<(KC, DC, C, CDUP)>,
61 name: Option<&'n str>,
62 flags: AllDatabaseFlags,
63}
64
65impl<'e, T> DatabaseOpenOptions<'e, 'static, T, Unspecified, Unspecified> {
66 /// Create an options struct to open/create a database with specific flags.
67 pub fn new(env: &'e Env<T>) -> Self {
68 DatabaseOpenOptions {
69 env,
70 types: Default::default(),
71 name: None,
72 flags: AllDatabaseFlags::empty(),
73 }
74 }
75}
76
77impl<'e, 'n, T, KC, DC, C, CDUP> DatabaseOpenOptions<'e, 'n, T, KC, DC, C, CDUP> {
78 /// Change the type of the database.
79 ///
80 /// The default types are [`Unspecified`] and require a call to [`Database::remap_types`]
81 /// to use the [`Database`].
82 pub fn types<NKC, NDC>(self) -> DatabaseOpenOptions<'e, 'n, T, NKC, NDC, C, CDUP> {
83 DatabaseOpenOptions {
84 env: self.env,
85 types: Default::default(),
86 name: self.name,
87 flags: self.flags,
88 }
89 }
90
91 /// Change the customized key compare function of the database.
92 ///
93 /// By default no customized compare function will be set when opening a database.
94 pub fn key_comparator<NC>(self) -> DatabaseOpenOptions<'e, 'n, T, KC, DC, NC, CDUP> {
95 DatabaseOpenOptions {
96 env: self.env,
97 types: Default::default(),
98 name: self.name,
99 flags: self.flags,
100 }
101 }
102
103 /// Change the customized dup sort compare function of the database.
104 ///
105 /// By default no customized compare function will be set when opening a database.
106 pub fn dup_sort_comparator<NCDUP>(self) -> DatabaseOpenOptions<'e, 'n, T, KC, DC, C, NCDUP> {
107 DatabaseOpenOptions {
108 env: self.env,
109 types: Default::default(),
110 name: self.name,
111 flags: self.flags,
112 }
113 }
114
115 /// Change the name of the database.
116 ///
117 /// By default the database is unnamed and there only is a single unnamed database.
118 pub fn name(&mut self, name: &'n str) -> &mut Self {
119 self.name = Some(name);
120 self
121 }
122
123 /// Specify the set of flags used to open the database.
124 pub fn flags(&mut self, flags: DatabaseFlags) -> &mut Self {
125 self.flags = AllDatabaseFlags::from_bits(flags.bits()).unwrap();
126 self
127 }
128
129 /// Opens a typed database that already exists in this environment.
130 ///
131 /// If the database was previously opened in this program run, types will be checked.
132 ///
133 /// ## Important Information
134 ///
135 /// LMDB has an important restriction on the unnamed database when named ones are opened.
136 /// The names of the named databases are stored as keys in the unnamed one and are immutable,
137 /// and these keys can only be read and not written.
138 ///
139 /// ## LMDB read-only access of existing database
140 ///
141 /// In the case of accessing a database in a read-only manner from another process
142 /// where you wrote, you might need to manually call [`RoTxn::commit`] to get metadata
143 /// and the database handles opened and shared with the global [`Env`] handle.
144 ///
145 /// If not done, you might raise `Io(Os { code: 22, kind: InvalidInput, message: "Invalid argument" })`
146 /// known as `EINVAL`.
147 pub fn open(&self, rtxn: &RoTxn) -> Result<Option<Database<KC, DC, C, CDUP>>>
148 where
149 KC: 'static,
150 DC: 'static,
151 C: Comparator + 'static,
152 CDUP: Comparator + 'static,
153 {
154 assert_eq_env_txn!(self.env, rtxn);
155
156 match self.env.raw_init_database::<C, CDUP>(rtxn.txn_ptr(), self.name, self.flags) {
157 Ok(dbi) => Ok(Some(Database::new(self.env.env_mut_ptr().as_ptr() as _, dbi))),
158 Err(Error::Mdb(e)) if e.not_found() => Ok(None),
159 Err(e) => Err(e),
160 }
161 }
162
163 /// Creates a typed database that can already exist in this environment.
164 ///
165 /// If the database was previously opened in this program run, types will be checked.
166 ///
167 /// ## Important Information
168 ///
169 /// LMDB has an important restriction on the unnamed database when named ones are opened.
170 /// The names of the named databases are stored as keys in the unnamed one and are immutable,
171 /// and these keys can only be read and not written.
172 pub fn create(&self, wtxn: &mut RwTxn) -> Result<Database<KC, DC, C, CDUP>>
173 where
174 KC: 'static,
175 DC: 'static,
176 C: Comparator + 'static,
177 CDUP: Comparator + 'static,
178 {
179 assert_eq_env_txn!(self.env, wtxn);
180
181 let flags = self.flags | AllDatabaseFlags::CREATE;
182 match self.env.raw_init_database::<C, CDUP>(wtxn.txn_ptr(), self.name, flags) {
183 Ok(dbi) => Ok(Database::new(self.env.env_mut_ptr().as_ptr() as _, dbi)),
184 Err(e) => Err(e),
185 }
186 }
187}
188
189impl<T, KC, DC, C, CDUP> Clone for DatabaseOpenOptions<'_, '_, T, KC, DC, C, CDUP> {
190 fn clone(&self) -> Self {
191 *self
192 }
193}
194
195impl<T, KC, DC, C, CDUP> Copy for DatabaseOpenOptions<'_, '_, T, KC, DC, C, CDUP> {}
196
197/// A typed database that accepts only the types it was created with.
198///
199/// # Example: Iterate over databases entries
200///
201/// In this example we store numbers in big endian this way those are ordered.
202/// Thanks to their bytes representation, heed is able to iterate over them
203/// from the lowest to the highest.
204///
205/// ```
206/// # use std::fs;
207/// # use std::path::Path;
208/// # use heed::EnvOpenOptions;
209/// use heed::Database;
210/// use heed::types::*;
211/// use heed::byteorder::BigEndian;
212///
213/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
214/// # let dir = tempfile::tempdir()?;
215/// # let env = unsafe { EnvOpenOptions::new()
216/// # .map_size(10 * 1024 * 1024) // 10MB
217/// # .max_dbs(3000)
218/// # .open(dir.path())?
219/// # };
220/// type BEI64 = I64<BigEndian>;
221///
222/// let mut wtxn = env.write_txn()?;
223/// let db: Database<BEI64, Unit> = env.create_database(&mut wtxn, Some("big-endian-iter"))?;
224///
225/// # db.clear(&mut wtxn)?;
226/// db.put(&mut wtxn, &68, &())?;
227/// db.put(&mut wtxn, &35, &())?;
228/// db.put(&mut wtxn, &0, &())?;
229/// db.put(&mut wtxn, &42, &())?;
230///
231/// // you can iterate over database entries in order
232/// let rets: Result<_, _> = db.iter(&wtxn)?.collect();
233/// let rets: Vec<(i64, _)> = rets?;
234///
235/// let expected = vec![
236/// (0, ()),
237/// (35, ()),
238/// (42, ()),
239/// (68, ()),
240/// ];
241///
242/// assert_eq!(rets, expected);
243/// wtxn.commit()?;
244/// # Ok(()) }
245/// ```
246///
247/// # Example: Iterate over and delete ranges of entries
248///
249/// Discern also support ranges and ranges deletions.
250/// Same configuration as above, numbers are ordered, therefore it is safe to specify
251/// a range and be able to iterate over and/or delete it.
252///
253/// ```
254/// # use std::fs;
255/// # use std::path::Path;
256/// # use heed::EnvOpenOptions;
257/// use heed::Database;
258/// use heed::types::*;
259/// use heed::byteorder::BigEndian;
260///
261/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
262/// # let dir = tempfile::tempdir()?;
263/// # let env = unsafe { EnvOpenOptions::new()
264/// # .map_size(10 * 1024 * 1024) // 10MB
265/// # .max_dbs(3000)
266/// # .open(dir.path())?
267/// # };
268/// type BEI64 = I64<BigEndian>;
269///
270/// let mut wtxn = env.write_txn()?;
271/// let db: Database<BEI64, Unit> = env.create_database(&mut wtxn, Some("big-endian-iter"))?;
272///
273/// # db.clear(&mut wtxn)?;
274/// db.put(&mut wtxn, &0, &())?;
275/// db.put(&mut wtxn, &68, &())?;
276/// db.put(&mut wtxn, &35, &())?;
277/// db.put(&mut wtxn, &42, &())?;
278///
279/// // you can iterate over ranges too!!!
280/// let range = 35..=42;
281/// let rets: Result<_, _> = db.range(&wtxn, &range)?.collect();
282/// let rets: Vec<(i64, _)> = rets?;
283///
284/// let expected = vec![
285/// (35, ()),
286/// (42, ()),
287/// ];
288///
289/// assert_eq!(rets, expected);
290///
291/// // even delete a range of keys
292/// let range = 35..=42;
293/// let deleted: usize = db.delete_range(&mut wtxn, &range)?;
294///
295/// let rets: Result<_, _> = db.iter(&wtxn)?.collect();
296/// let rets: Vec<(i64, _)> = rets?;
297///
298/// let expected = vec![
299/// (0, ()),
300/// (68, ()),
301/// ];
302///
303/// assert_eq!(deleted, 2);
304/// assert_eq!(rets, expected);
305///
306/// wtxn.commit()?;
307/// # Ok(()) }
308/// ```
309pub struct Database<KC, DC, C = DefaultComparator, CDUP = DefaultComparator> {
310 pub(crate) env_ident: usize,
311 pub(crate) dbi: ffi::MDB_dbi,
312 marker: marker::PhantomData<(KC, DC, C, CDUP)>,
313}
314
315impl<KC, DC, C, CDUP> Database<KC, DC, C, CDUP> {
316 pub(crate) fn new(env_ident: usize, dbi: ffi::MDB_dbi) -> Database<KC, DC, C, CDUP> {
317 Database { env_ident, dbi, marker: std::marker::PhantomData }
318 }
319
320 /// Retrieves the value associated with a key.
321 ///
322 /// If the key does not exist, then `None` is returned.
323 ///
324 /// ```
325 /// # use std::fs;
326 /// # use std::path::Path;
327 /// # use heed::EnvOpenOptions;
328 /// use heed::Database;
329 /// use heed::types::*;
330 /// use heed::byteorder::BigEndian;
331 ///
332 /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
333 /// # let dir = tempfile::tempdir()?;
334 /// # let env = unsafe { EnvOpenOptions::new()
335 /// # .map_size(10 * 1024 * 1024) // 10MB
336 /// # .max_dbs(3000)
337 /// # .open(dir.path())?
338 /// # };
339 /// type BEI32= U32<BigEndian>;
340 ///
341 /// let mut wtxn = env.write_txn()?;
342 /// let db: Database<Str, BEI32> = env.create_database(&mut wtxn, Some("get-i32"))?;
343 ///
344 /// # db.clear(&mut wtxn)?;
345 /// db.put(&mut wtxn, "i-am-forty-two", &42)?;
346 /// db.put(&mut wtxn, "i-am-twenty-seven", &27)?;
347 ///
348 /// let ret = db.get(&wtxn, "i-am-forty-two")?;
349 /// assert_eq!(ret, Some(42));
350 ///
351 /// let ret = db.get(&wtxn, "i-am-twenty-one")?;
352 /// assert_eq!(ret, None);
353 ///
354 /// wtxn.commit()?;
355 /// # Ok(()) }
356 /// ```
357 pub fn get<'a, 'txn>(&self, txn: &'txn RoTxn, key: &'a KC::EItem) -> Result<Option<DC::DItem>>
358 where
359 KC: BytesEncode<'a>,
360 DC: BytesDecode<'txn>,
361 {
362 assert_eq_env_db_txn!(self, txn);
363
364 let key_bytes: Cow<[u8]> = KC::bytes_encode(key).map_err(Error::Encoding)?;
365
366 let mut key_val = unsafe { crate::into_val(&key_bytes) };
367 let mut data_val = mem::MaybeUninit::uninit();
368
369 let result = unsafe {
370 mdb_result(ffi::mdb_get(
371 txn.txn_ptr().as_mut(),
372 self.dbi,
373 &mut key_val,
374 data_val.as_mut_ptr(),
375 ))
376 };
377
378 match result {
379 Ok(()) => {
380 let data = unsafe { crate::from_val(data_val.assume_init()) };
381 let data = DC::bytes_decode(data).map_err(Error::Decoding)?;
382 Ok(Some(data))
383 }
384 Err(e) if e.not_found() => Ok(None),
385 Err(e) => Err(e.into()),
386 }
387 }
388
389 /// Returns an iterator over all of the values of a single key.
390 ///
391 /// You can make this iterator `Send`able between threads by opening
392 /// the environment with the [`EnvOpenOptions::read_txn_without_tls`]
393 /// method.
394 ///
395 /// ```
396 /// # use std::fs;
397 /// # use std::path::Path;
398 /// # use heed::{DatabaseFlags, EnvOpenOptions};
399 /// use heed::types::*;
400 /// use heed::byteorder::BigEndian;
401 ///
402 /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
403 /// # let dir = tempfile::tempdir()?;
404 /// # let env = unsafe { EnvOpenOptions::new()
405 /// # .map_size(10 * 1024 * 1024) // 10MB
406 /// # .max_dbs(3000)
407 /// # .open(dir.path())?
408 /// # };
409 /// type BEI64 = I64<BigEndian>;
410 ///
411 /// let mut wtxn = env.write_txn()?;
412 /// let db = env.database_options()
413 /// .types::<BEI64, BEI64>()
414 /// .flags(DatabaseFlags::DUP_SORT)
415 /// .name("dup-sort")
416 /// .create(&mut wtxn)?;
417 ///
418 /// # db.clear(&mut wtxn)?;
419 /// db.put(&mut wtxn, &68, &120)?;
420 /// db.put(&mut wtxn, &68, &121)?;
421 /// db.put(&mut wtxn, &68, &122)?;
422 /// db.put(&mut wtxn, &68, &123)?;
423 /// db.put(&mut wtxn, &92, &32)?;
424 /// db.put(&mut wtxn, &35, &120)?;
425 /// db.put(&mut wtxn, &0, &120)?;
426 /// db.put(&mut wtxn, &42, &120)?;
427 ///
428 /// let mut iter = db.get_duplicates(&wtxn, &68)?.expect("the key exists");
429 /// assert_eq!(iter.next().transpose()?, Some((68, 120)));
430 /// assert_eq!(iter.next().transpose()?, Some((68, 121)));
431 /// assert_eq!(iter.next().transpose()?, Some((68, 122)));
432 /// assert_eq!(iter.next().transpose()?, Some((68, 123)));
433 /// assert_eq!(iter.next().transpose()?, None);
434 /// drop(iter);
435 ///
436 /// let mut iter = db.get_duplicates(&wtxn, &68)?.expect("the key exists");
437 /// assert_eq!(iter.last().transpose()?, Some((68, 123)));
438 ///
439 /// wtxn.commit()?;
440 /// # Ok(()) }
441 /// ```
442 pub fn get_duplicates<'a, 'txn>(
443 &self,
444 txn: &'txn RoTxn,
445 key: &'a KC::EItem,
446 ) -> Result<Option<RoIter<'txn, KC, DC, MoveOnCurrentKeyDuplicates>>>
447 where
448 KC: BytesEncode<'a>,
449 {
450 assert_eq_env_db_txn!(self, txn);
451
452 let mut cursor = RoCursor::new(txn, self.dbi)?;
453 let key_bytes: Cow<[u8]> = KC::bytes_encode(key).map_err(Error::Encoding)?;
454 if cursor.move_on_key(&key_bytes)? {
455 Ok(Some(RoIter::new(cursor)))
456 } else {
457 Ok(None)
458 }
459 }
460
461 /// Retrieves the key/value pair lower than the given one in this database.
462 ///
463 /// If the database if empty or there is no key lower than the given one,
464 /// then `None` is returned.
465 ///
466 /// Comparisons are made by using the bytes representation of the key.
467 ///
468 /// ```
469 /// # use std::fs;
470 /// # use std::path::Path;
471 /// # use heed::EnvOpenOptions;
472 /// use heed::Database;
473 /// use heed::types::*;
474 /// use heed::byteorder::BigEndian;
475 ///
476 /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
477 /// # let dir = tempfile::tempdir()?;
478 /// # let env = unsafe { EnvOpenOptions::new()
479 /// # .map_size(10 * 1024 * 1024) // 10MB
480 /// # .max_dbs(3000)
481 /// # .open(dir.path())?
482 /// # };
483 /// type BEU32 = U32<BigEndian>;
484 ///
485 /// let mut wtxn = env.write_txn()?;
486 /// let db = env.create_database::<BEU32, Unit>(&mut wtxn, Some("get-lt-u32"))?;
487 ///
488 /// # db.clear(&mut wtxn)?;
489 /// db.put(&mut wtxn, &27, &())?;
490 /// db.put(&mut wtxn, &42, &())?;
491 /// db.put(&mut wtxn, &43, &())?;
492 ///
493 /// let ret = db.get_lower_than(&wtxn, &4404)?;
494 /// assert_eq!(ret, Some((43, ())));
495 ///
496 /// let ret = db.get_lower_than(&wtxn, &43)?;
497 /// assert_eq!(ret, Some((42, ())));
498 ///
499 /// let ret = db.get_lower_than(&wtxn, &27)?;
500 /// assert_eq!(ret, None);
501 ///
502 /// wtxn.commit()?;
503 /// # Ok(()) }
504 /// ```
505 pub fn get_lower_than<'a, 'txn>(
506 &self,
507 txn: &'txn RoTxn,
508 key: &'a KC::EItem,
509 ) -> Result<Option<(KC::DItem, DC::DItem)>>
510 where
511 KC: BytesEncode<'a> + BytesDecode<'txn>,
512 DC: BytesDecode<'txn>,
513 {
514 assert_eq_env_db_txn!(self, txn);
515
516 let mut cursor = RoCursor::new(txn, self.dbi)?;
517 let key_bytes: Cow<[u8]> = KC::bytes_encode(key).map_err(Error::Encoding)?;
518 cursor.move_on_key_greater_than_or_equal_to(&key_bytes)?;
519
520 match cursor.move_on_prev(MoveOperation::NoDup) {
521 Ok(Some((key, data))) => match (KC::bytes_decode(key), DC::bytes_decode(data)) {
522 (Ok(key), Ok(data)) => Ok(Some((key, data))),
523 (Err(e), _) | (_, Err(e)) => Err(Error::Decoding(e)),
524 },
525 Ok(None) => Ok(None),
526 Err(e) => Err(e),
527 }
528 }
529
530 /// Retrieves the key/value pair lower than or equal to the given one in this database.
531 ///
532 /// If the database if empty or there is no key lower than or equal to the given one,
533 /// then `None` is returned.
534 ///
535 /// Comparisons are made by using the bytes representation of the key.
536 ///
537 /// ```
538 /// # use std::fs;
539 /// # use std::path::Path;
540 /// # use heed::EnvOpenOptions;
541 /// use heed::Database;
542 /// use heed::types::*;
543 /// use heed::byteorder::BigEndian;
544 ///
545 /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
546 /// # let dir = tempfile::tempdir()?;
547 /// # let env = unsafe { EnvOpenOptions::new()
548 /// # .map_size(10 * 1024 * 1024) // 10MB
549 /// # .max_dbs(3000)
550 /// # .open(dir.path())?
551 /// # };
552 /// type BEU32 = U32<BigEndian>;
553 ///
554 /// let mut wtxn = env.write_txn()?;
555 /// let db = env.create_database::<BEU32, Unit>(&mut wtxn, Some("get-lt-u32"))?;
556 ///
557 /// # db.clear(&mut wtxn)?;
558 /// db.put(&mut wtxn, &27, &())?;
559 /// db.put(&mut wtxn, &42, &())?;
560 /// db.put(&mut wtxn, &43, &())?;
561 ///
562 /// let ret = db.get_lower_than_or_equal_to(&wtxn, &4404)?;
563 /// assert_eq!(ret, Some((43, ())));
564 ///
565 /// let ret = db.get_lower_than_or_equal_to(&wtxn, &43)?;
566 /// assert_eq!(ret, Some((43, ())));
567 ///
568 /// let ret = db.get_lower_than_or_equal_to(&wtxn, &26)?;
569 /// assert_eq!(ret, None);
570 ///
571 /// wtxn.commit()?;
572 /// # Ok(()) }
573 /// ```
574 pub fn get_lower_than_or_equal_to<'a, 'txn>(
575 &self,
576 txn: &'txn RoTxn,
577 key: &'a KC::EItem,
578 ) -> Result<Option<(KC::DItem, DC::DItem)>>
579 where
580 KC: BytesEncode<'a> + BytesDecode<'txn>,
581 DC: BytesDecode<'txn>,
582 {
583 assert_eq_env_db_txn!(self, txn);
584
585 let mut cursor = RoCursor::new(txn, self.dbi)?;
586 let key_bytes: Cow<[u8]> = KC::bytes_encode(key).map_err(Error::Encoding)?;
587 let result = match cursor.move_on_key_greater_than_or_equal_to(&key_bytes) {
588 Ok(Some((key, data))) if key == &key_bytes[..] => Ok(Some((key, data))),
589 Ok(_) => cursor.move_on_prev(MoveOperation::NoDup),
590 Err(e) => Err(e),
591 };
592
593 match result {
594 Ok(Some((key, data))) => match (KC::bytes_decode(key), DC::bytes_decode(data)) {
595 (Ok(key), Ok(data)) => Ok(Some((key, data))),
596 (Err(e), _) | (_, Err(e)) => Err(Error::Decoding(e)),
597 },
598 Ok(None) => Ok(None),
599 Err(e) => Err(e),
600 }
601 }
602
603 /// Retrieves the key/value pair greater than the given one in this database.
604 ///
605 /// If the database if empty or there is no key greater than the given one,
606 /// then `None` is returned.
607 ///
608 /// Comparisons are made by using the bytes representation of the key.
609 ///
610 /// ```
611 /// # use std::fs;
612 /// # use std::path::Path;
613 /// # use heed::EnvOpenOptions;
614 /// use heed::Database;
615 /// use heed::types::*;
616 /// use heed::byteorder::BigEndian;
617 ///
618 /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
619 /// # let dir = tempfile::tempdir()?;
620 /// # let env = unsafe { EnvOpenOptions::new()
621 /// # .map_size(10 * 1024 * 1024) // 10MB
622 /// # .max_dbs(3000)
623 /// # .open(dir.path())?
624 /// # };
625 /// type BEU32 = U32<BigEndian>;
626 ///
627 /// let mut wtxn = env.write_txn()?;
628 /// let db = env.create_database::<BEU32, Unit>(&mut wtxn, Some("get-lt-u32"))?;
629 ///
630 /// # db.clear(&mut wtxn)?;
631 /// db.put(&mut wtxn, &27, &())?;
632 /// db.put(&mut wtxn, &42, &())?;
633 /// db.put(&mut wtxn, &43, &())?;
634 ///
635 /// let ret = db.get_greater_than(&wtxn, &0)?;
636 /// assert_eq!(ret, Some((27, ())));
637 ///
638 /// let ret = db.get_greater_than(&wtxn, &42)?;
639 /// assert_eq!(ret, Some((43, ())));
640 ///
641 /// let ret = db.get_greater_than(&wtxn, &43)?;
642 /// assert_eq!(ret, None);
643 ///
644 /// wtxn.commit()?;
645 /// # Ok(()) }
646 /// ```
647 pub fn get_greater_than<'a, 'txn>(
648 &self,
649 txn: &'txn RoTxn,
650 key: &'a KC::EItem,
651 ) -> Result<Option<(KC::DItem, DC::DItem)>>
652 where
653 KC: BytesEncode<'a> + BytesDecode<'txn>,
654 DC: BytesDecode<'txn>,
655 {
656 assert_eq_env_db_txn!(self, txn);
657
658 let mut cursor = RoCursor::new(txn, self.dbi)?;
659 let key_bytes: Cow<[u8]> = KC::bytes_encode(key).map_err(Error::Encoding)?;
660 let entry = match cursor.move_on_key_greater_than_or_equal_to(&key_bytes)? {
661 Some((key, data)) if key > &key_bytes[..] => Some((key, data)),
662 Some((_key, _data)) => cursor.move_on_next(MoveOperation::NoDup)?,
663 None => None,
664 };
665
666 match entry {
667 Some((key, data)) => match (KC::bytes_decode(key), DC::bytes_decode(data)) {
668 (Ok(key), Ok(data)) => Ok(Some((key, data))),
669 (Err(e), _) | (_, Err(e)) => Err(Error::Decoding(e)),
670 },
671 None => Ok(None),
672 }
673 }
674
675 /// Retrieves the key/value pair greater than or equal to the given one in this database.
676 ///
677 /// If the database if empty or there is no key greater than or equal to the given one,
678 /// then `None` is returned.
679 ///
680 /// Comparisons are made by using the bytes representation of the key.
681 ///
682 /// ```
683 /// # use std::fs;
684 /// # use std::path::Path;
685 /// # use heed::EnvOpenOptions;
686 /// use heed::Database;
687 /// use heed::types::*;
688 /// use heed::byteorder::BigEndian;
689 ///
690 /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
691 /// # let dir = tempfile::tempdir()?;
692 /// # let env = unsafe { EnvOpenOptions::new()
693 /// # .map_size(10 * 1024 * 1024) // 10MB
694 /// # .max_dbs(3000)
695 /// # .open(dir.path())?
696 /// # };
697 /// type BEU32 = U32<BigEndian>;
698 ///
699 /// let mut wtxn = env.write_txn()?;
700 /// let db = env.create_database::<BEU32, Unit>(&mut wtxn, Some("get-lt-u32"))?;
701 ///
702 /// # db.clear(&mut wtxn)?;
703 /// db.put(&mut wtxn, &27, &())?;
704 /// db.put(&mut wtxn, &42, &())?;
705 /// db.put(&mut wtxn, &43, &())?;
706 ///
707 /// let ret = db.get_greater_than_or_equal_to(&wtxn, &0)?;
708 /// assert_eq!(ret, Some((27, ())));
709 ///
710 /// let ret = db.get_greater_than_or_equal_to(&wtxn, &42)?;
711 /// assert_eq!(ret, Some((42, ())));
712 ///
713 /// let ret = db.get_greater_than_or_equal_to(&wtxn, &44)?;
714 /// assert_eq!(ret, None);
715 ///
716 /// wtxn.commit()?;
717 /// # Ok(()) }
718 /// ```
719 pub fn get_greater_than_or_equal_to<'a, 'txn>(
720 &self,
721 txn: &'txn RoTxn,
722 key: &'a KC::EItem,
723 ) -> Result<Option<(KC::DItem, DC::DItem)>>
724 where
725 KC: BytesEncode<'a> + BytesDecode<'txn>,
726 DC: BytesDecode<'txn>,
727 {
728 assert_eq_env_db_txn!(self, txn);
729
730 let mut cursor = RoCursor::new(txn, self.dbi)?;
731 let key_bytes: Cow<[u8]> = KC::bytes_encode(key).map_err(Error::Encoding)?;
732 match cursor.move_on_key_greater_than_or_equal_to(&key_bytes) {
733 Ok(Some((key, data))) => match (KC::bytes_decode(key), DC::bytes_decode(data)) {
734 (Ok(key), Ok(data)) => Ok(Some((key, data))),
735 (Err(e), _) | (_, Err(e)) => Err(Error::Decoding(e)),
736 },
737 Ok(None) => Ok(None),
738 Err(e) => Err(e),
739 }
740 }
741
742 /// Retrieves the first key/value pair of this database.
743 ///
744 /// If the database if empty, then `None` is returned.
745 ///
746 /// Comparisons are made by using the bytes representation of the key.
747 ///
748 /// ```
749 /// # use std::fs;
750 /// # use std::path::Path;
751 /// # use heed::EnvOpenOptions;
752 /// use heed::Database;
753 /// use heed::types::*;
754 /// use heed::byteorder::BigEndian;
755 ///
756 /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
757 /// # let dir = tempfile::tempdir()?;
758 /// # let env = unsafe { EnvOpenOptions::new()
759 /// # .map_size(10 * 1024 * 1024) // 10MB
760 /// # .max_dbs(3000)
761 /// # .open(dir.path())?
762 /// # };
763 /// type BEI32 = I32<BigEndian>;
764 ///
765 /// let mut wtxn = env.write_txn()?;
766 /// let db: Database<BEI32, Str> = env.create_database(&mut wtxn, Some("first-i32"))?;
767 ///
768 /// # db.clear(&mut wtxn)?;
769 /// db.put(&mut wtxn, &42, "i-am-forty-two")?;
770 /// db.put(&mut wtxn, &27, "i-am-twenty-seven")?;
771 ///
772 /// let ret = db.first(&wtxn)?;
773 /// assert_eq!(ret, Some((27, "i-am-twenty-seven")));
774 ///
775 /// wtxn.commit()?;
776 /// # Ok(()) }
777 /// ```
778 pub fn first<'txn>(&self, txn: &'txn RoTxn) -> Result<Option<(KC::DItem, DC::DItem)>>
779 where
780 KC: BytesDecode<'txn>,
781 DC: BytesDecode<'txn>,
782 {
783 assert_eq_env_db_txn!(self, txn);
784
785 let mut cursor = RoCursor::new(txn, self.dbi)?;
786 match cursor.move_on_first(MoveOperation::Any) {
787 Ok(Some((key, data))) => match (KC::bytes_decode(key), DC::bytes_decode(data)) {
788 (Ok(key), Ok(data)) => Ok(Some((key, data))),
789 (Err(e), _) | (_, Err(e)) => Err(Error::Decoding(e)),
790 },
791 Ok(None) => Ok(None),
792 Err(e) => Err(e),
793 }
794 }
795
796 /// Retrieves the last key/value pair of this database.
797 ///
798 /// If the database if empty, then `None` is returned.
799 ///
800 /// Comparisons are made by using the bytes representation of the key.
801 ///
802 /// ```
803 /// # use std::fs;
804 /// # use std::path::Path;
805 /// # use heed::EnvOpenOptions;
806 /// use heed::Database;
807 /// use heed::types::*;
808 /// use heed::byteorder::BigEndian;
809 ///
810 /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
811 /// # let dir = tempfile::tempdir()?;
812 /// # let env = unsafe { EnvOpenOptions::new()
813 /// # .map_size(10 * 1024 * 1024) // 10MB
814 /// # .max_dbs(3000)
815 /// # .open(dir.path())?
816 /// # };
817 /// type BEI32 = I32<BigEndian>;
818 ///
819 /// let mut wtxn = env.write_txn()?;
820 /// let db: Database<BEI32, Str> = env.create_database(&mut wtxn, Some("last-i32"))?;
821 ///
822 /// # db.clear(&mut wtxn)?;
823 /// db.put(&mut wtxn, &42, "i-am-forty-two")?;
824 /// db.put(&mut wtxn, &27, "i-am-twenty-seven")?;
825 ///
826 /// let ret = db.last(&wtxn)?;
827 /// assert_eq!(ret, Some((42, "i-am-forty-two")));
828 ///
829 /// wtxn.commit()?;
830 /// # Ok(()) }
831 /// ```
832 pub fn last<'txn>(&self, txn: &'txn RoTxn) -> Result<Option<(KC::DItem, DC::DItem)>>
833 where
834 KC: BytesDecode<'txn>,
835 DC: BytesDecode<'txn>,
836 {
837 assert_eq_env_db_txn!(self, txn);
838
839 let mut cursor = RoCursor::new(txn, self.dbi)?;
840 match cursor.move_on_last(MoveOperation::Any) {
841 Ok(Some((key, data))) => match (KC::bytes_decode(key), DC::bytes_decode(data)) {
842 (Ok(key), Ok(data)) => Ok(Some((key, data))),
843 (Err(e), _) | (_, Err(e)) => Err(Error::Decoding(e)),
844 },
845 Ok(None) => Ok(None),
846 Err(e) => Err(e),
847 }
848 }
849
850 /// Returns the number of elements in this database.
851 ///
852 /// ```
853 /// # use std::fs;
854 /// # use std::path::Path;
855 /// # use heed::EnvOpenOptions;
856 /// use heed::Database;
857 /// use heed::types::*;
858 /// use heed::byteorder::BigEndian;
859 ///
860 /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
861 /// # let dir = tempfile::tempdir()?;
862 /// # let env = unsafe { EnvOpenOptions::new()
863 /// # .map_size(10 * 1024 * 1024) // 10MB
864 /// # .max_dbs(3000)
865 /// # .open(dir.path())?
866 /// # };
867 /// type BEI32 = I32<BigEndian>;
868 ///
869 /// let mut wtxn = env.write_txn()?;
870 /// let db: Database<BEI32, Str> = env.create_database(&mut wtxn, Some("iter-i32"))?;
871 ///
872 /// # db.clear(&mut wtxn)?;
873 /// db.put(&mut wtxn, &42, "i-am-forty-two")?;
874 /// db.put(&mut wtxn, &27, "i-am-twenty-seven")?;
875 /// db.put(&mut wtxn, &13, "i-am-thirteen")?;
876 /// db.put(&mut wtxn, &521, "i-am-five-hundred-and-twenty-one")?;
877 ///
878 /// let ret = db.len(&wtxn)?;
879 /// assert_eq!(ret, 4);
880 ///
881 /// db.delete(&mut wtxn, &27)?;
882 ///
883 /// let ret = db.len(&wtxn)?;
884 /// assert_eq!(ret, 3);
885 ///
886 /// wtxn.commit()?;
887 /// # Ok(()) }
888 /// ```
889 pub fn len(&self, txn: &RoTxn) -> Result<u64> {
890 self.stat(txn).map(|stat| stat.entries as u64)
891 }
892
893 /// Returns `true` if and only if this database is empty.
894 ///
895 /// ```
896 /// # use std::fs;
897 /// # use std::path::Path;
898 /// # use heed::EnvOpenOptions;
899 /// use heed::Database;
900 /// use heed::types::*;
901 /// use heed::byteorder::BigEndian;
902 ///
903 /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
904 /// # let dir = tempfile::tempdir()?;
905 /// # let env = unsafe { EnvOpenOptions::new()
906 /// # .map_size(10 * 1024 * 1024) // 10MB
907 /// # .max_dbs(3000)
908 /// # .open(dir.path())?
909 /// # };
910 /// type BEI32 = I32<BigEndian>;
911 ///
912 /// let mut wtxn = env.write_txn()?;
913 /// let db: Database<BEI32, Str> = env.create_database(&mut wtxn, Some("iter-i32"))?;
914 ///
915 /// # db.clear(&mut wtxn)?;
916 /// db.put(&mut wtxn, &42, "i-am-forty-two")?;
917 /// db.put(&mut wtxn, &27, "i-am-twenty-seven")?;
918 /// db.put(&mut wtxn, &13, "i-am-thirteen")?;
919 /// db.put(&mut wtxn, &521, "i-am-five-hundred-and-twenty-one")?;
920 ///
921 /// let ret = db.is_empty(&wtxn)?;
922 /// assert_eq!(ret, false);
923 ///
924 /// db.clear(&mut wtxn)?;
925 ///
926 /// let ret = db.is_empty(&wtxn)?;
927 /// assert_eq!(ret, true);
928 ///
929 /// wtxn.commit()?;
930 /// # Ok(()) }
931 /// ```
932 pub fn is_empty(&self, txn: &RoTxn) -> Result<bool> {
933 self.len(txn).map(|l| l == 0)
934 }
935
936 /// Returns some statistics for this database.
937 ///
938 /// ```
939 /// # use std::fs;
940 /// # use std::path::Path;
941 /// # use heed::EnvOpenOptions;
942 /// use heed::Database;
943 /// use heed::types::*;
944 /// use heed::byteorder::BigEndian;
945 ///
946 /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
947 /// # let dir = tempfile::tempdir()?;
948 /// # let env = unsafe { EnvOpenOptions::new()
949 /// # .map_size(10 * 1024 * 1024) // 10MB
950 /// # .max_dbs(3000)
951 /// # .open(dir.path())?
952 /// # };
953 /// type BEI32 = I32<BigEndian>;
954 ///
955 /// let mut wtxn = env.write_txn()?;
956 /// let db: Database<BEI32, Str> = env.create_database(&mut wtxn, Some("iter-i32"))?;
957 ///
958 /// # db.clear(&mut wtxn)?;
959 /// db.put(&mut wtxn, &42, "i-am-forty-two")?;
960 /// db.put(&mut wtxn, &27, "i-am-twenty-seven")?;
961 /// db.put(&mut wtxn, &13, "i-am-thirteen")?;
962 /// db.put(&mut wtxn, &521, "i-am-five-hundred-and-twenty-one")?;
963 ///
964 /// let stat = db.stat(&wtxn)?;
965 /// assert_eq!(stat.depth, 1);
966 /// assert_eq!(stat.branch_pages, 0);
967 /// assert_eq!(stat.leaf_pages, 1);
968 /// assert_eq!(stat.overflow_pages, 0);
969 /// assert_eq!(stat.entries, 4);
970 ///
971 /// wtxn.commit()?;
972 /// # Ok(()) }
973 /// ```
974 pub fn stat(&self, txn: &RoTxn) -> Result<DatabaseStat> {
975 assert_eq_env_db_txn!(self, txn);
976
977 let mut db_stat = mem::MaybeUninit::uninit();
978 let result = unsafe {
979 mdb_result(ffi::mdb_stat(txn.txn_ptr().as_mut(), self.dbi, db_stat.as_mut_ptr()))
980 };
981
982 match result {
983 Ok(()) => {
984 let stats = unsafe { db_stat.assume_init() };
985 Ok(DatabaseStat {
986 page_size: stats.ms_psize,
987 depth: stats.ms_depth,
988 branch_pages: stats.ms_branch_pages,
989 leaf_pages: stats.ms_leaf_pages,
990 overflow_pages: stats.ms_overflow_pages,
991 entries: stats.ms_entries,
992 })
993 }
994 Err(e) => Err(e.into()),
995 }
996 }
997
998 /// Return an ordered iterator of all key-value pairs in this database.
999 ///
1000 /// You can make this iterator `Send`able between threads by opening
1001 /// the environment with the [`EnvOpenOptions::read_txn_without_tls`]
1002 /// method.
1003 ///
1004 /// ```
1005 /// # use std::fs;
1006 /// # use std::path::Path;
1007 /// # use heed::EnvOpenOptions;
1008 /// use heed::Database;
1009 /// use heed::types::*;
1010 /// use heed::byteorder::BigEndian;
1011 ///
1012 /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
1013 /// # let dir = tempfile::tempdir()?;
1014 /// # let env = unsafe { EnvOpenOptions::new()
1015 /// # .map_size(10 * 1024 * 1024) // 10MB
1016 /// # .max_dbs(3000)
1017 /// # .open(dir.path())?
1018 /// # };
1019 /// type BEI32 = I32<BigEndian>;
1020 ///
1021 /// let mut wtxn = env.write_txn()?;
1022 /// let db: Database<BEI32, Str> = env.create_database(&mut wtxn, Some("iter-i32"))?;
1023 ///
1024 /// # db.clear(&mut wtxn)?;
1025 /// db.put(&mut wtxn, &42, "i-am-forty-two")?;
1026 /// db.put(&mut wtxn, &27, "i-am-twenty-seven")?;
1027 /// db.put(&mut wtxn, &13, "i-am-thirteen")?;
1028 ///
1029 /// let mut iter = db.iter(&wtxn)?;
1030 /// assert_eq!(iter.next().transpose()?, Some((13, "i-am-thirteen")));
1031 /// assert_eq!(iter.next().transpose()?, Some((27, "i-am-twenty-seven")));
1032 /// assert_eq!(iter.next().transpose()?, Some((42, "i-am-forty-two")));
1033 /// assert_eq!(iter.next().transpose()?, None);
1034 ///
1035 /// drop(iter);
1036 /// wtxn.commit()?;
1037 /// # Ok(()) }
1038 /// ```
1039 pub fn iter<'txn>(&self, txn: &'txn RoTxn) -> Result<RoIter<'txn, KC, DC>> {
1040 assert_eq_env_db_txn!(self, txn);
1041 RoCursor::new(txn, self.dbi).map(|cursor| RoIter::new(cursor))
1042 }
1043
1044 /// Return a mutable ordered iterator of all key-value pairs in this database.
1045 ///
1046 /// ```
1047 /// # use std::fs;
1048 /// # use std::path::Path;
1049 /// # use heed::EnvOpenOptions;
1050 /// use heed::Database;
1051 /// use heed::types::*;
1052 /// use heed::byteorder::BigEndian;
1053 ///
1054 /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
1055 /// # let dir = tempfile::tempdir()?;
1056 /// # let env = unsafe { EnvOpenOptions::new()
1057 /// # .map_size(10 * 1024 * 1024) // 10MB
1058 /// # .max_dbs(3000)
1059 /// # .open(dir.path())?
1060 /// # };
1061 /// type BEI32 = I32<BigEndian>;
1062 ///
1063 /// let mut wtxn = env.write_txn()?;
1064 /// let db: Database<BEI32, Str> = env.create_database(&mut wtxn, Some("iter-i32"))?;
1065 ///
1066 /// # db.clear(&mut wtxn)?;
1067 /// db.put(&mut wtxn, &42, "i-am-forty-two")?;
1068 /// db.put(&mut wtxn, &27, "i-am-twenty-seven")?;
1069 /// db.put(&mut wtxn, &13, "i-am-thirteen")?;
1070 ///
1071 /// let mut iter = db.iter_mut(&mut wtxn)?;
1072 /// assert_eq!(iter.next().transpose()?, Some((13, "i-am-thirteen")));
1073 /// let ret = unsafe { iter.del_current()? };
1074 /// assert!(ret);
1075 ///
1076 /// assert_eq!(iter.next().transpose()?, Some((27, "i-am-twenty-seven")));
1077 /// assert_eq!(iter.next().transpose()?, Some((42, "i-am-forty-two")));
1078 /// let ret = unsafe { iter.put_current(&42, "i-am-the-new-forty-two")? };
1079 /// assert!(ret);
1080 ///
1081 /// assert_eq!(iter.next().transpose()?, None);
1082 ///
1083 /// drop(iter);
1084 ///
1085 /// let ret = db.get(&wtxn, &13)?;
1086 /// assert_eq!(ret, None);
1087 ///
1088 /// let ret = db.get(&wtxn, &42)?;
1089 /// assert_eq!(ret, Some("i-am-the-new-forty-two"));
1090 ///
1091 /// wtxn.commit()?;
1092 /// # Ok(()) }
1093 /// ```
1094 pub fn iter_mut<'txn>(&self, txn: &'txn mut RwTxn) -> Result<RwIter<'txn, KC, DC>> {
1095 assert_eq_env_db_txn!(self, txn);
1096
1097 RwCursor::new(txn, self.dbi).map(|cursor| RwIter::new(cursor))
1098 }
1099
1100 /// Return a reverse ordered iterator of all key-value pairs in this database.
1101 ///
1102 /// You can make this iterator `Send`able between threads by opening
1103 /// the environment with the [`EnvOpenOptions::read_txn_without_tls`]
1104 /// method.
1105 ///
1106 /// ```
1107 /// # use std::fs;
1108 /// # use std::path::Path;
1109 /// # use heed::EnvOpenOptions;
1110 /// use heed::Database;
1111 /// use heed::types::*;
1112 /// use heed::byteorder::BigEndian;
1113 ///
1114 /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
1115 /// # let dir = tempfile::tempdir()?;
1116 /// # let env = unsafe { EnvOpenOptions::new()
1117 /// # .map_size(10 * 1024 * 1024) // 10MB
1118 /// # .max_dbs(3000)
1119 /// # .open(dir.path())?
1120 /// # };
1121 /// type BEI32 = I32<BigEndian>;
1122 ///
1123 /// let mut wtxn = env.write_txn()?;
1124 /// let db: Database<BEI32, Str> = env.create_database(&mut wtxn, Some("iter-i32"))?;
1125 ///
1126 /// # db.clear(&mut wtxn)?;
1127 /// db.put(&mut wtxn, &42, "i-am-forty-two")?;
1128 /// db.put(&mut wtxn, &27, "i-am-twenty-seven")?;
1129 /// db.put(&mut wtxn, &13, "i-am-thirteen")?;
1130 ///
1131 /// let mut iter = db.rev_iter(&wtxn)?;
1132 /// assert_eq!(iter.next().transpose()?, Some((42, "i-am-forty-two")));
1133 /// assert_eq!(iter.next().transpose()?, Some((27, "i-am-twenty-seven")));
1134 /// assert_eq!(iter.next().transpose()?, Some((13, "i-am-thirteen")));
1135 /// assert_eq!(iter.next().transpose()?, None);
1136 ///
1137 /// drop(iter);
1138 /// wtxn.commit()?;
1139 /// # Ok(()) }
1140 /// ```
1141 pub fn rev_iter<'txn>(&self, txn: &'txn RoTxn) -> Result<RoRevIter<'txn, KC, DC>> {
1142 assert_eq_env_db_txn!(self, txn);
1143
1144 RoCursor::new(txn, self.dbi).map(|cursor| RoRevIter::new(cursor))
1145 }
1146
1147 /// Return a mutable reverse ordered iterator of all key-value\
1148 /// pairs in this database.
1149 ///
1150 /// ```
1151 /// # use std::fs;
1152 /// # use std::path::Path;
1153 /// # use heed::EnvOpenOptions;
1154 /// use heed::Database;
1155 /// use heed::types::*;
1156 /// use heed::byteorder::BigEndian;
1157 ///
1158 /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
1159 /// # let dir = tempfile::tempdir()?;
1160 /// # let env = unsafe { EnvOpenOptions::new()
1161 /// # .map_size(10 * 1024 * 1024) // 10MB
1162 /// # .max_dbs(3000)
1163 /// # .open(dir.path())?
1164 /// # };
1165 /// type BEI32 = I32<BigEndian>;
1166 ///
1167 /// let mut wtxn = env.write_txn()?;
1168 /// let db: Database<BEI32, Str> = env.create_database(&mut wtxn, Some("iter-i32"))?;
1169 ///
1170 /// # db.clear(&mut wtxn)?;
1171 /// db.put(&mut wtxn, &42, "i-am-forty-two")?;
1172 /// db.put(&mut wtxn, &27, "i-am-twenty-seven")?;
1173 /// db.put(&mut wtxn, &13, "i-am-thirteen")?;
1174 ///
1175 /// let mut iter = db.rev_iter_mut(&mut wtxn)?;
1176 /// assert_eq!(iter.next().transpose()?, Some((42, "i-am-forty-two")));
1177 /// let ret = unsafe { iter.del_current()? };
1178 /// assert!(ret);
1179 ///
1180 /// assert_eq!(iter.next().transpose()?, Some((27, "i-am-twenty-seven")));
1181 /// assert_eq!(iter.next().transpose()?, Some((13, "i-am-thirteen")));
1182 /// let ret = unsafe { iter.put_current(&13, "i-am-the-new-thirteen")? };
1183 /// assert!(ret);
1184 ///
1185 /// assert_eq!(iter.next().transpose()?, None);
1186 ///
1187 /// drop(iter);
1188 ///
1189 /// let ret = db.get(&wtxn, &42)?;
1190 /// assert_eq!(ret, None);
1191 ///
1192 /// let ret = db.get(&wtxn, &13)?;
1193 /// assert_eq!(ret, Some("i-am-the-new-thirteen"));
1194 ///
1195 /// wtxn.commit()?;
1196 /// # Ok(()) }
1197 /// ```
1198 pub fn rev_iter_mut<'txn>(&self, txn: &'txn mut RwTxn) -> Result<RwRevIter<'txn, KC, DC>> {
1199 assert_eq_env_db_txn!(self, txn);
1200
1201 RwCursor::new(txn, self.dbi).map(|cursor| RwRevIter::new(cursor))
1202 }
1203
1204 /// Return an ordered iterator of a range of key-value pairs in this database.
1205 ///
1206 /// Comparisons are made by using the comparator `C`.
1207 ///
1208 /// You can make this iterator `Send`able between threads by opening
1209 /// the environment with the [`EnvOpenOptions::read_txn_without_tls`]
1210 /// method.
1211 ///
1212 /// ```
1213 /// # use std::fs;
1214 /// # use std::path::Path;
1215 /// # use heed::EnvOpenOptions;
1216 /// use heed::Database;
1217 /// use heed::types::*;
1218 /// use heed::byteorder::BigEndian;
1219 ///
1220 /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
1221 /// # let dir = tempfile::tempdir()?;
1222 /// # let env = unsafe { EnvOpenOptions::new()
1223 /// # .map_size(10 * 1024 * 1024) // 10MB
1224 /// # .max_dbs(3000)
1225 /// # .open(dir.path())?
1226 /// # };
1227 /// type BEI32 = I32<BigEndian>;
1228 ///
1229 /// let mut wtxn = env.write_txn()?;
1230 /// let db: Database<BEI32, Str> = env.create_database(&mut wtxn, Some("iter-i32"))?;
1231 ///
1232 /// # db.clear(&mut wtxn)?;
1233 /// db.put(&mut wtxn, &42, "i-am-forty-two")?;
1234 /// db.put(&mut wtxn, &27, "i-am-twenty-seven")?;
1235 /// db.put(&mut wtxn, &13, "i-am-thirteen")?;
1236 /// db.put(&mut wtxn, &521, "i-am-five-hundred-and-twenty-one")?;
1237 ///
1238 /// let range = 27..=42;
1239 /// let mut iter = db.range(&wtxn, &range)?;
1240 /// assert_eq!(iter.next().transpose()?, Some((27, "i-am-twenty-seven")));
1241 /// assert_eq!(iter.next().transpose()?, Some((42, "i-am-forty-two")));
1242 /// assert_eq!(iter.next().transpose()?, None);
1243 ///
1244 /// drop(iter);
1245 /// wtxn.commit()?;
1246 /// # Ok(()) }
1247 /// ```
1248 ///
1249 /// It can be complex to work with ranges of slices and using
1250 /// the `..` or `..=` range syntax is not the best way to deal
1251 /// with that. We highly recommend using the [`Bound`](std::ops::Bound) enum for that.
1252 ///
1253 /// ```
1254 /// # use std::fs;
1255 /// # use std::path::Path;
1256 /// use std::ops::Bound;
1257 /// # use heed::EnvOpenOptions;
1258 /// use heed::Database;
1259 /// use heed::types::*;
1260 /// use heed::byteorder::BigEndian;
1261 ///
1262 /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
1263 /// # let dir = tempfile::tempdir()?;
1264 /// # let env = unsafe { EnvOpenOptions::new()
1265 /// # .map_size(10 * 1024 * 1024) // 10MB
1266 /// # .max_dbs(30)
1267 /// # .open(dir.path())?
1268 /// # };
1269 ///
1270 /// let mut wtxn = env.write_txn()?;
1271 /// let db: Database<Bytes, Unit> = env.create_database(&mut wtxn, None)?;
1272 ///
1273 /// // make sure to create slices and not ref array
1274 /// // by using the [..] syntax.
1275 /// let start = &[0, 0, 0][..];
1276 /// let end = &[9, 0, 0][..];
1277 ///
1278 /// // equivalent to start..end
1279 /// let range = (Bound::Included(start), Bound::Excluded(end));
1280 ///
1281 /// let iter = db.range(&mut wtxn, &range)?;
1282 ///
1283 /// drop(iter);
1284 /// wtxn.commit()?;
1285 /// # Ok(()) }
1286 /// ```
1287 pub fn range<'a, 'txn, R>(
1288 &self,
1289 txn: &'txn RoTxn,
1290 range: &'a R,
1291 ) -> Result<RoRange<'txn, KC, DC, C>>
1292 where
1293 KC: BytesEncode<'a>,
1294 R: RangeBounds<KC::EItem>,
1295 {
1296 assert_eq_env_db_txn!(self, txn);
1297
1298 let start_bound = match range.start_bound() {
1299 Bound::Included(bound) => {
1300 let bytes = KC::bytes_encode(bound).map_err(Error::Encoding)?;
1301 Bound::Included(bytes.into_owned())
1302 }
1303 Bound::Excluded(bound) => {
1304 let bytes = KC::bytes_encode(bound).map_err(Error::Encoding)?;
1305 Bound::Excluded(bytes.into_owned())
1306 }
1307 Bound::Unbounded => Bound::Unbounded,
1308 };
1309
1310 let end_bound = match range.end_bound() {
1311 Bound::Included(bound) => {
1312 let bytes = KC::bytes_encode(bound).map_err(Error::Encoding)?;
1313 Bound::Included(bytes.into_owned())
1314 }
1315 Bound::Excluded(bound) => {
1316 let bytes = KC::bytes_encode(bound).map_err(Error::Encoding)?;
1317 Bound::Excluded(bytes.into_owned())
1318 }
1319 Bound::Unbounded => Bound::Unbounded,
1320 };
1321
1322 RoCursor::new(txn, self.dbi).map(|cursor| RoRange::new(cursor, start_bound, end_bound))
1323 }
1324
1325 /// Return a mutable ordered iterator of a range of key-value pairs in this database.
1326 ///
1327 /// Comparisons are made by using the comparator `C`.
1328 ///
1329 /// ```
1330 /// # use std::fs;
1331 /// # use std::path::Path;
1332 /// # use heed::EnvOpenOptions;
1333 /// use heed::Database;
1334 /// use heed::types::*;
1335 /// use heed::byteorder::BigEndian;
1336 ///
1337 /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
1338 /// # let dir = tempfile::tempdir()?;
1339 /// # let env = unsafe { EnvOpenOptions::new()
1340 /// # .map_size(10 * 1024 * 1024) // 10MB
1341 /// # .max_dbs(3000)
1342 /// # .open(dir.path())?
1343 /// # };
1344 /// type BEI32 = I32<BigEndian>;
1345 ///
1346 /// let mut wtxn = env.write_txn()?;
1347 /// let db: Database<BEI32, Str> = env.create_database(&mut wtxn, Some("iter-i32"))?;
1348 ///
1349 /// # db.clear(&mut wtxn)?;
1350 /// db.put(&mut wtxn, &42, "i-am-forty-two")?;
1351 /// db.put(&mut wtxn, &27, "i-am-twenty-seven")?;
1352 /// db.put(&mut wtxn, &13, "i-am-thirteen")?;
1353 /// db.put(&mut wtxn, &521, "i-am-five-hundred-and-twenty-one")?;
1354 ///
1355 /// let range = 27..=42;
1356 /// let mut range = db.range_mut(&mut wtxn, &range)?;
1357 /// assert_eq!(range.next().transpose()?, Some((27, "i-am-twenty-seven")));
1358 /// let ret = unsafe { range.del_current()? };
1359 /// assert!(ret);
1360 /// assert_eq!(range.next().transpose()?, Some((42, "i-am-forty-two")));
1361 /// let ret = unsafe { range.put_current(&42, "i-am-the-new-forty-two")? };
1362 /// assert!(ret);
1363 ///
1364 /// assert_eq!(range.next().transpose()?, None);
1365 /// drop(range);
1366 ///
1367 ///
1368 /// let mut iter = db.iter(&wtxn)?;
1369 /// assert_eq!(iter.next().transpose()?, Some((13, "i-am-thirteen")));
1370 /// assert_eq!(iter.next().transpose()?, Some((42, "i-am-the-new-forty-two")));
1371 /// assert_eq!(iter.next().transpose()?, Some((521, "i-am-five-hundred-and-twenty-one")));
1372 /// assert_eq!(iter.next().transpose()?, None);
1373 ///
1374 /// drop(iter);
1375 /// wtxn.commit()?;
1376 /// # Ok(()) }
1377 /// ```
1378 pub fn range_mut<'a, 'txn, R>(
1379 &self,
1380 txn: &'txn mut RwTxn,
1381 range: &'a R,
1382 ) -> Result<RwRange<'txn, KC, DC, C>>
1383 where
1384 KC: BytesEncode<'a>,
1385 R: RangeBounds<KC::EItem>,
1386 {
1387 assert_eq_env_db_txn!(self, txn);
1388
1389 let start_bound = match range.start_bound() {
1390 Bound::Included(bound) => {
1391 let bytes = KC::bytes_encode(bound).map_err(Error::Encoding)?;
1392 Bound::Included(bytes.into_owned())
1393 }
1394 Bound::Excluded(bound) => {
1395 let bytes = KC::bytes_encode(bound).map_err(Error::Encoding)?;
1396 Bound::Excluded(bytes.into_owned())
1397 }
1398 Bound::Unbounded => Bound::Unbounded,
1399 };
1400
1401 let end_bound = match range.end_bound() {
1402 Bound::Included(bound) => {
1403 let bytes = KC::bytes_encode(bound).map_err(Error::Encoding)?;
1404 Bound::Included(bytes.into_owned())
1405 }
1406 Bound::Excluded(bound) => {
1407 let bytes = KC::bytes_encode(bound).map_err(Error::Encoding)?;
1408 Bound::Excluded(bytes.into_owned())
1409 }
1410 Bound::Unbounded => Bound::Unbounded,
1411 };
1412
1413 RwCursor::new(txn, self.dbi).map(|cursor| RwRange::new(cursor, start_bound, end_bound))
1414 }
1415
1416 /// Return a reverse ordered iterator of a range of key-value pairs in this database.
1417 ///
1418 /// Comparisons are made by using the comparator `C`.
1419 ///
1420 /// You can make this iterator `Send`able between threads by opening
1421 /// the environment with the [`EnvOpenOptions::read_txn_without_tls`]
1422 /// method.
1423 ///
1424 /// ```
1425 /// # use std::fs;
1426 /// # use std::path::Path;
1427 /// # use heed::EnvOpenOptions;
1428 /// use heed::Database;
1429 /// use heed::types::*;
1430 /// use heed::byteorder::BigEndian;
1431 ///
1432 /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
1433 /// # let dir = tempfile::tempdir()?;
1434 /// # let env = unsafe { EnvOpenOptions::new()
1435 /// # .map_size(10 * 1024 * 1024) // 10MB
1436 /// # .max_dbs(3000)
1437 /// # .open(dir.path())?
1438 /// # };
1439 /// type BEI32 = I32<BigEndian>;
1440 ///
1441 /// let mut wtxn = env.write_txn()?;
1442 /// let db: Database<BEI32, Str> = env.create_database(&mut wtxn, Some("iter-i32"))?;
1443 ///
1444 /// # db.clear(&mut wtxn)?;
1445 /// db.put(&mut wtxn, &42, "i-am-forty-two")?;
1446 /// db.put(&mut wtxn, &27, "i-am-twenty-seven")?;
1447 /// db.put(&mut wtxn, &13, "i-am-thirteen")?;
1448 /// db.put(&mut wtxn, &521, "i-am-five-hundred-and-twenty-one")?;
1449 ///
1450 /// let range = 27..=43;
1451 /// let mut iter = db.rev_range(&wtxn, &range)?;
1452 /// assert_eq!(iter.next().transpose()?, Some((42, "i-am-forty-two")));
1453 /// assert_eq!(iter.next().transpose()?, Some((27, "i-am-twenty-seven")));
1454 /// assert_eq!(iter.next().transpose()?, None);
1455 ///
1456 /// drop(iter);
1457 /// wtxn.commit()?;
1458 /// # Ok(()) }
1459 /// ```
1460 pub fn rev_range<'a, 'txn, R>(
1461 &self,
1462 txn: &'txn RoTxn,
1463 range: &'a R,
1464 ) -> Result<RoRevRange<'txn, KC, DC, C>>
1465 where
1466 KC: BytesEncode<'a>,
1467 R: RangeBounds<KC::EItem>,
1468 {
1469 assert_eq_env_db_txn!(self, txn);
1470
1471 let start_bound = match range.start_bound() {
1472 Bound::Included(bound) => {
1473 let bytes = KC::bytes_encode(bound).map_err(Error::Encoding)?;
1474 Bound::Included(bytes.into_owned())
1475 }
1476 Bound::Excluded(bound) => {
1477 let bytes = KC::bytes_encode(bound).map_err(Error::Encoding)?;
1478 Bound::Excluded(bytes.into_owned())
1479 }
1480 Bound::Unbounded => Bound::Unbounded,
1481 };
1482
1483 let end_bound = match range.end_bound() {
1484 Bound::Included(bound) => {
1485 let bytes = KC::bytes_encode(bound).map_err(Error::Encoding)?;
1486 Bound::Included(bytes.into_owned())
1487 }
1488 Bound::Excluded(bound) => {
1489 let bytes = KC::bytes_encode(bound).map_err(Error::Encoding)?;
1490 Bound::Excluded(bytes.into_owned())
1491 }
1492 Bound::Unbounded => Bound::Unbounded,
1493 };
1494
1495 RoCursor::new(txn, self.dbi).map(|cursor| RoRevRange::new(cursor, start_bound, end_bound))
1496 }
1497
1498 /// Return a mutable reverse ordered iterator of a range of key-value pairs in this database.
1499 ///
1500 /// Comparisons are made by using the comparator `C`.
1501 ///
1502 /// ```
1503 /// # use std::fs;
1504 /// # use std::path::Path;
1505 /// # use heed::EnvOpenOptions;
1506 /// use heed::Database;
1507 /// use heed::types::*;
1508 /// use heed::byteorder::BigEndian;
1509 ///
1510 /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
1511 /// # let dir = tempfile::tempdir()?;
1512 /// # let env = unsafe { EnvOpenOptions::new()
1513 /// # .map_size(10 * 1024 * 1024) // 10MB
1514 /// # .max_dbs(3000)
1515 /// # .open(dir.path())?
1516 /// # };
1517 /// type BEI32 = I32<BigEndian>;
1518 ///
1519 /// let mut wtxn = env.write_txn()?;
1520 /// let db: Database<BEI32, Str> = env.create_database(&mut wtxn, Some("iter-i32"))?;
1521 ///
1522 /// # db.clear(&mut wtxn)?;
1523 /// db.put(&mut wtxn, &42, "i-am-forty-two")?;
1524 /// db.put(&mut wtxn, &27, "i-am-twenty-seven")?;
1525 /// db.put(&mut wtxn, &13, "i-am-thirteen")?;
1526 /// db.put(&mut wtxn, &521, "i-am-five-hundred-and-twenty-one")?;
1527 ///
1528 /// let range = 27..=42;
1529 /// let mut range = db.rev_range_mut(&mut wtxn, &range)?;
1530 /// assert_eq!(range.next().transpose()?, Some((42, "i-am-forty-two")));
1531 /// let ret = unsafe { range.del_current()? };
1532 /// assert!(ret);
1533 /// assert_eq!(range.next().transpose()?, Some((27, "i-am-twenty-seven")));
1534 /// let ret = unsafe { range.put_current(&27, "i-am-the-new-twenty-seven")? };
1535 /// assert!(ret);
1536 ///
1537 /// assert_eq!(range.next().transpose()?, None);
1538 /// drop(range);
1539 ///
1540 ///
1541 /// let mut iter = db.iter(&wtxn)?;
1542 /// assert_eq!(iter.next().transpose()?, Some((13, "i-am-thirteen")));
1543 /// assert_eq!(iter.next().transpose()?, Some((27, "i-am-the-new-twenty-seven")));
1544 /// assert_eq!(iter.next().transpose()?, Some((521, "i-am-five-hundred-and-twenty-one")));
1545 /// assert_eq!(iter.next().transpose()?, None);
1546 ///
1547 /// drop(iter);
1548 /// wtxn.commit()?;
1549 /// # Ok(()) }
1550 /// ```
1551 pub fn rev_range_mut<'a, 'txn, R>(
1552 &self,
1553 txn: &'txn mut RwTxn,
1554 range: &'a R,
1555 ) -> Result<RwRevRange<'txn, KC, DC, C>>
1556 where
1557 KC: BytesEncode<'a>,
1558 R: RangeBounds<KC::EItem>,
1559 {
1560 assert_eq_env_db_txn!(self, txn);
1561
1562 let start_bound = match range.start_bound() {
1563 Bound::Included(bound) => {
1564 let bytes = KC::bytes_encode(bound).map_err(Error::Encoding)?;
1565 Bound::Included(bytes.into_owned())
1566 }
1567 Bound::Excluded(bound) => {
1568 let bytes = KC::bytes_encode(bound).map_err(Error::Encoding)?;
1569 Bound::Excluded(bytes.into_owned())
1570 }
1571 Bound::Unbounded => Bound::Unbounded,
1572 };
1573
1574 let end_bound = match range.end_bound() {
1575 Bound::Included(bound) => {
1576 let bytes = KC::bytes_encode(bound).map_err(Error::Encoding)?;
1577 Bound::Included(bytes.into_owned())
1578 }
1579 Bound::Excluded(bound) => {
1580 let bytes = KC::bytes_encode(bound).map_err(Error::Encoding)?;
1581 Bound::Excluded(bytes.into_owned())
1582 }
1583 Bound::Unbounded => Bound::Unbounded,
1584 };
1585
1586 RwCursor::new(txn, self.dbi).map(|cursor| RwRevRange::new(cursor, start_bound, end_bound))
1587 }
1588
1589 /// Return a lexicographically ordered iterator of all key-value pairs
1590 /// in this database that starts with the given prefix.
1591 ///
1592 /// Comparisons are made by using the bytes representation of the key.
1593 ///
1594 /// You can make this iterator `Send`able between threads by opening
1595 /// the environment with the [`EnvOpenOptions::read_txn_without_tls`]
1596 /// method.
1597 ///
1598 /// ```
1599 /// # use std::fs;
1600 /// # use std::path::Path;
1601 /// # use heed::EnvOpenOptions;
1602 /// use heed::Database;
1603 /// use heed::types::*;
1604 /// use heed::byteorder::BigEndian;
1605 ///
1606 /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
1607 /// # let dir = tempfile::tempdir()?;
1608 /// # let env = unsafe { EnvOpenOptions::new()
1609 /// # .map_size(10 * 1024 * 1024) // 10MB
1610 /// # .max_dbs(3000)
1611 /// # .open(dir.path())?
1612 /// # };
1613 /// type BEI32 = I32<BigEndian>;
1614 ///
1615 /// let mut wtxn = env.write_txn()?;
1616 /// let db: Database<Str, BEI32> = env.create_database(&mut wtxn, Some("iter-i32"))?;
1617 ///
1618 /// # db.clear(&mut wtxn)?;
1619 /// db.put(&mut wtxn, "i-am-twenty-eight", &28)?;
1620 /// db.put(&mut wtxn, "i-am-twenty-seven", &27)?;
1621 /// db.put(&mut wtxn, "i-am-twenty-nine", &29)?;
1622 /// db.put(&mut wtxn, "i-am-forty-one", &41)?;
1623 /// db.put(&mut wtxn, "i-am-forty-two", &42)?;
1624 ///
1625 /// let mut iter = db.prefix_iter(&mut wtxn, "i-am-twenty")?;
1626 /// assert_eq!(iter.next().transpose()?, Some(("i-am-twenty-eight", 28)));
1627 /// assert_eq!(iter.next().transpose()?, Some(("i-am-twenty-nine", 29)));
1628 /// assert_eq!(iter.next().transpose()?, Some(("i-am-twenty-seven", 27)));
1629 /// assert_eq!(iter.next().transpose()?, None);
1630 ///
1631 /// drop(iter);
1632 /// wtxn.commit()?;
1633 /// # Ok(()) }
1634 /// ```
1635 pub fn prefix_iter<'a, 'txn>(
1636 &self,
1637 txn: &'txn RoTxn,
1638 prefix: &'a KC::EItem,
1639 ) -> Result<RoPrefix<'txn, KC, DC, C>>
1640 where
1641 KC: BytesEncode<'a>,
1642 C: LexicographicComparator,
1643 {
1644 assert_eq_env_db_txn!(self, txn);
1645
1646 let prefix_bytes = KC::bytes_encode(prefix).map_err(Error::Encoding)?;
1647 let prefix_bytes = prefix_bytes.into_owned();
1648 RoCursor::new(txn, self.dbi).map(|cursor| RoPrefix::new(cursor, prefix_bytes))
1649 }
1650
1651 /// Return a mutable lexicographically ordered iterator of all key-value pairs
1652 /// in this database that starts with the given prefix.
1653 ///
1654 /// Comparisons are made by using the bytes representation of the key.
1655 ///
1656 /// ```
1657 /// # use std::fs;
1658 /// # use std::path::Path;
1659 /// # use heed::EnvOpenOptions;
1660 /// use heed::Database;
1661 /// use heed::types::*;
1662 /// use heed::byteorder::BigEndian;
1663 ///
1664 /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
1665 /// # let dir = tempfile::tempdir()?;
1666 /// # let env = unsafe { EnvOpenOptions::new()
1667 /// # .map_size(10 * 1024 * 1024) // 10MB
1668 /// # .max_dbs(3000)
1669 /// # .open(dir.path())?
1670 /// # };
1671 /// type BEI32 = I32<BigEndian>;
1672 ///
1673 /// let mut wtxn = env.write_txn()?;
1674 /// let db: Database<Str, BEI32> = env.create_database(&mut wtxn, Some("iter-i32"))?;
1675 ///
1676 /// # db.clear(&mut wtxn)?;
1677 /// db.put(&mut wtxn, "i-am-twenty-eight", &28)?;
1678 /// db.put(&mut wtxn, "i-am-twenty-seven", &27)?;
1679 /// db.put(&mut wtxn, "i-am-twenty-nine", &29)?;
1680 /// db.put(&mut wtxn, "i-am-forty-one", &41)?;
1681 /// db.put(&mut wtxn, "i-am-forty-two", &42)?;
1682 ///
1683 /// let mut iter = db.prefix_iter_mut(&mut wtxn, "i-am-twenty")?;
1684 /// assert_eq!(iter.next().transpose()?, Some(("i-am-twenty-eight", 28)));
1685 /// let ret = unsafe { iter.del_current()? };
1686 /// assert!(ret);
1687 ///
1688 /// assert_eq!(iter.next().transpose()?, Some(("i-am-twenty-nine", 29)));
1689 /// assert_eq!(iter.next().transpose()?, Some(("i-am-twenty-seven", 27)));
1690 /// let ret = unsafe { iter.put_current("i-am-twenty-seven", &27000)? };
1691 /// assert!(ret);
1692 ///
1693 /// assert_eq!(iter.next().transpose()?, None);
1694 ///
1695 /// drop(iter);
1696 ///
1697 /// let ret = db.get(&wtxn, "i-am-twenty-eight")?;
1698 /// assert_eq!(ret, None);
1699 ///
1700 /// let ret = db.get(&wtxn, "i-am-twenty-seven")?;
1701 /// assert_eq!(ret, Some(27000));
1702 ///
1703 /// wtxn.commit()?;
1704 /// # Ok(()) }
1705 /// ```
1706 pub fn prefix_iter_mut<'a, 'txn>(
1707 &self,
1708 txn: &'txn mut RwTxn,
1709 prefix: &'a KC::EItem,
1710 ) -> Result<RwPrefix<'txn, KC, DC, C>>
1711 where
1712 KC: BytesEncode<'a>,
1713 C: LexicographicComparator,
1714 {
1715 assert_eq_env_db_txn!(self, txn);
1716
1717 let prefix_bytes = KC::bytes_encode(prefix).map_err(Error::Encoding)?;
1718 let prefix_bytes = prefix_bytes.into_owned();
1719 RwCursor::new(txn, self.dbi).map(|cursor| RwPrefix::new(cursor, prefix_bytes))
1720 }
1721
1722 /// Return a reversed lexicographically ordered iterator of all key-value pairs
1723 /// in this database that starts with the given prefix.
1724 ///
1725 /// Comparisons are made by using the bytes representation of the key.
1726 ///
1727 /// You can make this iterator `Send`able between threads by opening
1728 /// the environment with the [`EnvOpenOptions::read_txn_without_tls`]
1729 /// method.
1730 ///
1731 /// ```
1732 /// # use std::fs;
1733 /// # use std::path::Path;
1734 /// # use heed::EnvOpenOptions;
1735 /// use heed::Database;
1736 /// use heed::types::*;
1737 /// use heed::byteorder::BigEndian;
1738 ///
1739 /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
1740 /// # let dir = tempfile::tempdir()?;
1741 /// # let env = unsafe { EnvOpenOptions::new()
1742 /// # .map_size(10 * 1024 * 1024) // 10MB
1743 /// # .max_dbs(3000)
1744 /// # .open(dir.path())?
1745 /// # };
1746 /// type BEI32 = I32<BigEndian>;
1747 ///
1748 /// let mut wtxn = env.write_txn()?;
1749 /// let db: Database<Str, BEI32> = env.create_database(&mut wtxn, Some("iter-i32"))?;
1750 ///
1751 /// # db.clear(&mut wtxn)?;
1752 /// db.put(&mut wtxn, "i-am-twenty-eight", &28)?;
1753 /// db.put(&mut wtxn, "i-am-twenty-seven", &27)?;
1754 /// db.put(&mut wtxn, "i-am-twenty-nine", &29)?;
1755 /// db.put(&mut wtxn, "i-am-forty-one", &41)?;
1756 /// db.put(&mut wtxn, "i-am-forty-two", &42)?;
1757 ///
1758 /// let mut iter = db.rev_prefix_iter(&mut wtxn, "i-am-twenty")?;
1759 /// assert_eq!(iter.next().transpose()?, Some(("i-am-twenty-seven", 27)));
1760 /// assert_eq!(iter.next().transpose()?, Some(("i-am-twenty-nine", 29)));
1761 /// assert_eq!(iter.next().transpose()?, Some(("i-am-twenty-eight", 28)));
1762 /// assert_eq!(iter.next().transpose()?, None);
1763 ///
1764 /// drop(iter);
1765 /// wtxn.commit()?;
1766 /// # Ok(()) }
1767 /// ```
1768 pub fn rev_prefix_iter<'a, 'txn>(
1769 &self,
1770 txn: &'txn RoTxn,
1771 prefix: &'a KC::EItem,
1772 ) -> Result<RoRevPrefix<'txn, KC, DC, C>>
1773 where
1774 KC: BytesEncode<'a>,
1775 C: LexicographicComparator,
1776 {
1777 assert_eq_env_db_txn!(self, txn);
1778
1779 let prefix_bytes = KC::bytes_encode(prefix).map_err(Error::Encoding)?;
1780 let prefix_bytes = prefix_bytes.into_owned();
1781 RoCursor::new(txn, self.dbi).map(|cursor| RoRevPrefix::new(cursor, prefix_bytes))
1782 }
1783
1784 /// Return a mutable reversed lexicographically ordered iterator of all key-value pairs
1785 /// in this database that starts with the given prefix.
1786 ///
1787 /// Comparisons are made by using the bytes representation of the key.
1788 ///
1789 /// ```
1790 /// # use std::fs;
1791 /// # use std::path::Path;
1792 /// # use heed::EnvOpenOptions;
1793 /// use heed::Database;
1794 /// use heed::types::*;
1795 /// use heed::byteorder::BigEndian;
1796 ///
1797 /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
1798 /// # let dir = tempfile::tempdir()?;
1799 /// # let env = unsafe { EnvOpenOptions::new()
1800 /// # .map_size(10 * 1024 * 1024) // 10MB
1801 /// # .max_dbs(3000)
1802 /// # .open(dir.path())?
1803 /// # };
1804 /// type BEI32 = I32<BigEndian>;
1805 ///
1806 /// let mut wtxn = env.write_txn()?;
1807 /// let db: Database<Str, BEI32> = env.create_database(&mut wtxn, Some("iter-i32"))?;
1808 ///
1809 /// # db.clear(&mut wtxn)?;
1810 /// db.put(&mut wtxn, "i-am-twenty-eight", &28)?;
1811 /// db.put(&mut wtxn, "i-am-twenty-seven", &27)?;
1812 /// db.put(&mut wtxn, "i-am-twenty-nine", &29)?;
1813 /// db.put(&mut wtxn, "i-am-forty-one", &41)?;
1814 /// db.put(&mut wtxn, "i-am-forty-two", &42)?;
1815 ///
1816 /// let mut iter = db.rev_prefix_iter_mut(&mut wtxn, "i-am-twenty")?;
1817 /// assert_eq!(iter.next().transpose()?, Some(("i-am-twenty-seven", 27)));
1818 /// let ret = unsafe { iter.del_current()? };
1819 /// assert!(ret);
1820 ///
1821 /// assert_eq!(iter.next().transpose()?, Some(("i-am-twenty-nine", 29)));
1822 /// assert_eq!(iter.next().transpose()?, Some(("i-am-twenty-eight", 28)));
1823 /// let ret = unsafe { iter.put_current("i-am-twenty-eight", &28000)? };
1824 /// assert!(ret);
1825 ///
1826 /// assert_eq!(iter.next().transpose()?, None);
1827 ///
1828 /// drop(iter);
1829 ///
1830 /// let ret = db.get(&wtxn, "i-am-twenty-seven")?;
1831 /// assert_eq!(ret, None);
1832 ///
1833 /// let ret = db.get(&wtxn, "i-am-twenty-eight")?;
1834 /// assert_eq!(ret, Some(28000));
1835 ///
1836 /// wtxn.commit()?;
1837 /// # Ok(()) }
1838 /// ```
1839 pub fn rev_prefix_iter_mut<'a, 'txn>(
1840 &self,
1841 txn: &'txn mut RwTxn,
1842 prefix: &'a KC::EItem,
1843 ) -> Result<RwRevPrefix<'txn, KC, DC, C>>
1844 where
1845 KC: BytesEncode<'a>,
1846 C: LexicographicComparator,
1847 {
1848 assert_eq_env_db_txn!(self, txn);
1849
1850 let prefix_bytes = KC::bytes_encode(prefix).map_err(Error::Encoding)?;
1851 let prefix_bytes = prefix_bytes.into_owned();
1852 RwCursor::new(txn, self.dbi).map(|cursor| RwRevPrefix::new(cursor, prefix_bytes))
1853 }
1854
1855 /// Insert a key-value pair in this database, replacing any previous value. The entry is
1856 /// written with no specific flag.
1857 ///
1858 /// ```
1859 /// # use std::fs;
1860 /// # use std::path::Path;
1861 /// # use heed::EnvOpenOptions;
1862 /// use heed::Database;
1863 /// use heed::types::*;
1864 /// use heed::byteorder::BigEndian;
1865 ///
1866 /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
1867 /// # let dir = tempfile::tempdir()?;
1868 /// # let env = unsafe { EnvOpenOptions::new()
1869 /// # .map_size(10 * 1024 * 1024) // 10MB
1870 /// # .max_dbs(3000)
1871 /// # .open(dir.path())?
1872 /// # };
1873 /// type BEI32 = I32<BigEndian>;
1874 ///
1875 /// let mut wtxn = env.write_txn()?;
1876 /// let db: Database<BEI32, Str> = env.create_database(&mut wtxn, Some("iter-i32"))?;
1877 ///
1878 /// # db.clear(&mut wtxn)?;
1879 /// db.put(&mut wtxn, &42, "i-am-forty-two")?;
1880 /// db.put(&mut wtxn, &27, "i-am-twenty-seven")?;
1881 /// db.put(&mut wtxn, &13, "i-am-thirteen")?;
1882 /// db.put(&mut wtxn, &521, "i-am-five-hundred-and-twenty-one")?;
1883 ///
1884 /// let ret = db.get(&mut wtxn, &27)?;
1885 /// assert_eq!(ret, Some("i-am-twenty-seven"));
1886 ///
1887 /// wtxn.commit()?;
1888 /// # Ok(()) }
1889 /// ```
1890 pub fn put<'a>(&self, txn: &mut RwTxn, key: &'a KC::EItem, data: &'a DC::EItem) -> Result<()>
1891 where
1892 KC: BytesEncode<'a>,
1893 DC: BytesEncode<'a>,
1894 {
1895 assert_eq_env_db_txn!(self, txn);
1896
1897 let key_bytes: Cow<[u8]> = KC::bytes_encode(key).map_err(Error::Encoding)?;
1898 let data_bytes: Cow<[u8]> = DC::bytes_encode(data).map_err(Error::Encoding)?;
1899
1900 let mut key_val = unsafe { crate::into_val(&key_bytes) };
1901 let mut data_val = unsafe { crate::into_val(&data_bytes) };
1902 let flags = 0;
1903
1904 unsafe {
1905 mdb_result(ffi::mdb_put(
1906 txn.txn_ptr().as_mut(),
1907 self.dbi,
1908 &mut key_val,
1909 &mut data_val,
1910 flags,
1911 ))?
1912 }
1913
1914 Ok(())
1915 }
1916
1917 /// Insert a key-value pair where the value can directly be written to disk, replacing any
1918 /// previous value.
1919 ///
1920 /// ```
1921 /// # use std::fs;
1922 /// # use std::path::Path;
1923 /// # use heed::EnvOpenOptions;
1924 /// use std::io::Write;
1925 /// use heed::Database;
1926 /// use heed::types::*;
1927 /// use heed::byteorder::BigEndian;
1928 ///
1929 /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
1930 /// # let dir = tempfile::tempdir()?;
1931 /// # let env = unsafe { EnvOpenOptions::new()
1932 /// # .map_size(10 * 1024 * 1024) // 10MB
1933 /// # .max_dbs(3000)
1934 /// # .open(dir.path())?
1935 /// # };
1936 /// type BEI32 = I32<BigEndian>;
1937 ///
1938 /// let mut wtxn = env.write_txn()?;
1939 /// let db = env.create_database::<BEI32, Str>(&mut wtxn, Some("number-string"))?;
1940 ///
1941 /// # db.clear(&mut wtxn)?;
1942 /// let value = "I am a long long long value";
1943 /// db.put_reserved(&mut wtxn, &42, value.len(), |reserved| {
1944 /// reserved.write_all(value.as_bytes())
1945 /// })?;
1946 ///
1947 /// let ret = db.get(&mut wtxn, &42)?;
1948 /// assert_eq!(ret, Some(value));
1949 ///
1950 /// wtxn.commit()?;
1951 /// # Ok(()) }
1952 /// ```
1953 pub fn put_reserved<'a, F>(
1954 &self,
1955 txn: &mut RwTxn,
1956 key: &'a KC::EItem,
1957 data_size: usize,
1958 write_func: F,
1959 ) -> Result<()>
1960 where
1961 KC: BytesEncode<'a>,
1962 F: FnOnce(&mut ReservedSpace) -> io::Result<()>,
1963 {
1964 assert_eq_env_db_txn!(self, txn);
1965
1966 let key_bytes: Cow<[u8]> = KC::bytes_encode(key).map_err(Error::Encoding)?;
1967 let mut key_val = unsafe { crate::into_val(&key_bytes) };
1968 let mut reserved = ffi::reserve_size_val(data_size);
1969 let flags = ffi::MDB_RESERVE;
1970
1971 unsafe {
1972 mdb_result(ffi::mdb_put(
1973 txn.txn_ptr().as_mut(),
1974 self.dbi,
1975 &mut key_val,
1976 &mut reserved,
1977 flags,
1978 ))?
1979 }
1980
1981 let mut reserved = unsafe { ReservedSpace::from_val(reserved) };
1982 write_func(&mut reserved)?;
1983 if reserved.remaining() == 0 {
1984 Ok(())
1985 } else {
1986 Err(io::Error::from(io::ErrorKind::UnexpectedEof).into())
1987 }
1988 }
1989
1990 /// Insert a key-value pair in this database, replacing any previous value. The entry is
1991 /// written with the specified flags.
1992 ///
1993 /// ```
1994 /// # use std::fs;
1995 /// # use std::path::Path;
1996 /// # use heed::EnvOpenOptions;
1997 /// use heed::{Database, PutFlags, DatabaseFlags, Error, MdbError};
1998 /// use heed::types::*;
1999 /// use heed::byteorder::BigEndian;
2000 ///
2001 /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
2002 /// # let dir = tempfile::tempdir()?;
2003 /// # let env = unsafe { EnvOpenOptions::new()
2004 /// # .map_size(10 * 1024 * 1024) // 10MB
2005 /// # .max_dbs(3000)
2006 /// # .open(dir.path())?
2007 /// # };
2008 /// type BEI32 = I32<BigEndian>;
2009 ///
2010 /// let mut wtxn = env.write_txn()?;
2011 /// let db = env.database_options()
2012 /// .types::<BEI32, Str>()
2013 /// .name("dup-i32")
2014 /// .flags(DatabaseFlags::DUP_SORT)
2015 /// .create(&mut wtxn)?;
2016 ///
2017 /// # db.clear(&mut wtxn)?;
2018 /// db.put(&mut wtxn, &42, "i-am-forty-two")?;
2019 /// db.put(&mut wtxn, &42, "i-am-so-cool")?;
2020 /// db.put(&mut wtxn, &42, "i-am-the-king")?;
2021 /// db.put(&mut wtxn, &42, "i-am-fun")?;
2022 /// db.put_with_flags(&mut wtxn, PutFlags::APPEND, &54, "i-am-older-than-you")?;
2023 /// db.put_with_flags(&mut wtxn, PutFlags::APPEND_DUP, &54, "ok-but-i-am-better-than-you")?;
2024 /// // You can compose flags by OR'ing them
2025 /// db.put_with_flags(&mut wtxn, PutFlags::APPEND_DUP | PutFlags::NO_OVERWRITE, &55, "welcome")?;
2026 ///
2027 /// // The NO_DUP_DATA flag will return KeyExist if we try to insert the exact same key/value pair.
2028 /// let ret = db.put_with_flags(&mut wtxn, PutFlags::NO_DUP_DATA, &54, "ok-but-i-am-better-than-you");
2029 /// assert!(matches!(ret, Err(Error::Mdb(MdbError::KeyExist))));
2030 ///
2031 /// // The NO_OVERWRITE flag will return KeyExist if we try to insert something with an already existing key.
2032 /// let ret = db.put_with_flags(&mut wtxn, PutFlags::NO_OVERWRITE, &54, "there-can-be-only-one-data");
2033 /// assert!(matches!(ret, Err(Error::Mdb(MdbError::KeyExist))));
2034 ///
2035 /// let mut iter = db.iter(&wtxn)?;
2036 /// assert_eq!(iter.next().transpose()?, Some((42, "i-am-forty-two")));
2037 /// assert_eq!(iter.next().transpose()?, Some((42, "i-am-fun")));
2038 /// assert_eq!(iter.next().transpose()?, Some((42, "i-am-so-cool")));
2039 /// assert_eq!(iter.next().transpose()?, Some((42, "i-am-the-king")));
2040 /// assert_eq!(iter.next().transpose()?, Some((54, "i-am-older-than-you")));
2041 /// assert_eq!(iter.next().transpose()?, Some((54, "ok-but-i-am-better-than-you")));
2042 /// assert_eq!(iter.next().transpose()?, Some((55, "welcome")));
2043 /// assert_eq!(iter.next().transpose()?, None);
2044 ///
2045 /// drop(iter);
2046 /// wtxn.commit()?;
2047 /// # Ok(()) }
2048 /// ```
2049 pub fn put_with_flags<'a>(
2050 &self,
2051 txn: &mut RwTxn,
2052 flags: PutFlags,
2053 key: &'a KC::EItem,
2054 data: &'a DC::EItem,
2055 ) -> Result<()>
2056 where
2057 KC: BytesEncode<'a>,
2058 DC: BytesEncode<'a>,
2059 {
2060 assert_eq_env_db_txn!(self, txn);
2061
2062 let key_bytes: Cow<[u8]> = KC::bytes_encode(key).map_err(Error::Encoding)?;
2063 let data_bytes: Cow<[u8]> = DC::bytes_encode(data).map_err(Error::Encoding)?;
2064
2065 let mut key_val = unsafe { crate::into_val(&key_bytes) };
2066 let mut data_val = unsafe { crate::into_val(&data_bytes) };
2067 let flags = flags.bits();
2068
2069 unsafe {
2070 mdb_result(ffi::mdb_put(
2071 txn.txn_ptr().as_mut(),
2072 self.dbi,
2073 &mut key_val,
2074 &mut data_val,
2075 flags,
2076 ))?
2077 }
2078
2079 Ok(())
2080 }
2081
2082 /// Attempt to insert a key-value pair in this database, or if a value already exists for the
2083 /// key, returns the previous value.
2084 ///
2085 /// The entry is always written with the [`NO_OVERWRITE`](PutFlags::NO_OVERWRITE) flag.
2086 ///
2087 /// ```
2088 /// # use heed::EnvOpenOptions;
2089 /// use heed::Database;
2090 /// use heed::types::*;
2091 /// use heed::byteorder::BigEndian;
2092 ///
2093 /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
2094 /// # let dir = tempfile::tempdir()?;
2095 /// # let env = unsafe { EnvOpenOptions::new()
2096 /// # .map_size(10 * 1024 * 1024) // 10MB
2097 /// # .max_dbs(3000)
2098 /// # .open(dir.path())?
2099 /// # };
2100 /// type BEI32 = I32<BigEndian>;
2101 ///
2102 /// let mut wtxn = env.write_txn()?;
2103 /// let db: Database<BEI32, Str> = env.create_database(&mut wtxn, Some("iter-i32"))?;
2104 ///
2105 /// # db.clear(&mut wtxn)?;
2106 /// assert_eq!(db.get_or_put(&mut wtxn, &42, "i-am-forty-two")?, None);
2107 /// assert_eq!(db.get_or_put(&mut wtxn, &42, "the meaning of life")?, Some("i-am-forty-two"));
2108 ///
2109 /// let ret = db.get(&mut wtxn, &42)?;
2110 /// assert_eq!(ret, Some("i-am-forty-two"));
2111 ///
2112 /// wtxn.commit()?;
2113 /// # Ok(()) }
2114 /// ```
2115 pub fn get_or_put<'a, 'txn>(
2116 &'txn self,
2117 txn: &mut RwTxn,
2118 key: &'a KC::EItem,
2119 data: &'a DC::EItem,
2120 ) -> Result<Option<DC::DItem>>
2121 where
2122 KC: BytesEncode<'a>,
2123 DC: BytesEncode<'a> + BytesDecode<'a>,
2124 {
2125 self.get_or_put_with_flags(txn, PutFlags::empty(), key, data)
2126 }
2127
2128 /// Attempt to insert a key-value pair in this database, or if a value already exists for the
2129 /// key, returns the previous value.
2130 ///
2131 /// The entry is written with the specified flags, in addition to
2132 /// [`NO_OVERWRITE`](PutFlags::NO_OVERWRITE) which is always used.
2133 ///
2134 /// ```
2135 /// # use heed::EnvOpenOptions;
2136 /// use heed::{Database, PutFlags};
2137 /// use heed::types::*;
2138 /// use heed::byteorder::BigEndian;
2139 ///
2140 /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
2141 /// # let dir = tempfile::tempdir()?;
2142 /// # let env = unsafe { EnvOpenOptions::new()
2143 /// # .map_size(10 * 1024 * 1024) // 10MB
2144 /// # .max_dbs(3000)
2145 /// # .open(dir.path())?
2146 /// # };
2147 /// type BEI32 = I32<BigEndian>;
2148 ///
2149 /// let mut wtxn = env.write_txn()?;
2150 /// let db: Database<BEI32, Str> = env.create_database(&mut wtxn, Some("iter-i32"))?;
2151 ///
2152 /// # db.clear(&mut wtxn)?;
2153 /// assert_eq!(db.get_or_put_with_flags(&mut wtxn, PutFlags::empty(), &42, "i-am-forty-two")?, None);
2154 /// assert_eq!(db.get_or_put_with_flags(&mut wtxn, PutFlags::empty(), &42, "the meaning of life")?, Some("i-am-forty-two"));
2155 ///
2156 /// let ret = db.get(&mut wtxn, &42)?;
2157 /// assert_eq!(ret, Some("i-am-forty-two"));
2158 ///
2159 /// wtxn.commit()?;
2160 /// # Ok(()) }
2161 /// ```
2162 pub fn get_or_put_with_flags<'a, 'txn>(
2163 &'txn self,
2164 txn: &mut RwTxn,
2165 flags: PutFlags,
2166 key: &'a KC::EItem,
2167 data: &'a DC::EItem,
2168 ) -> Result<Option<DC::DItem>>
2169 where
2170 KC: BytesEncode<'a>,
2171 DC: BytesEncode<'a> + BytesDecode<'a>,
2172 {
2173 assert_eq_env_db_txn!(self, txn);
2174
2175 let key_bytes: Cow<[u8]> = KC::bytes_encode(key).map_err(Error::Encoding)?;
2176 let data_bytes: Cow<[u8]> = DC::bytes_encode(data).map_err(Error::Encoding)?;
2177
2178 let mut key_val = unsafe { crate::into_val(&key_bytes) };
2179 let mut data_val = unsafe { crate::into_val(&data_bytes) };
2180 let flags = (flags | PutFlags::NO_OVERWRITE).bits();
2181
2182 let result = unsafe {
2183 mdb_result(ffi::mdb_put(
2184 txn.txn_ptr().as_mut(),
2185 self.dbi,
2186 &mut key_val,
2187 &mut data_val,
2188 flags,
2189 ))
2190 };
2191
2192 match result {
2193 // the value was successfully inserted
2194 Ok(()) => Ok(None),
2195 // the key already exists: the previous value is stored in the data parameter
2196 Err(MdbError::KeyExist) => {
2197 let bytes = unsafe { crate::from_val(data_val) };
2198 let data = DC::bytes_decode(bytes).map_err(Error::Decoding)?;
2199 Ok(Some(data))
2200 }
2201 Err(error) => Err(error.into()),
2202 }
2203 }
2204
2205 /// Attempt to insert a key-value pair in this database, where the value can be directly
2206 /// written to disk, or if a value already exists for the key, returns the previous value.
2207 ///
2208 /// The entry is always written with the [`NO_OVERWRITE`](PutFlags::NO_OVERWRITE) and
2209 /// [`MDB_RESERVE`](ffi::MDB_RESERVE) flags.
2210 ///
2211 /// ```
2212 /// # use heed::EnvOpenOptions;
2213 /// use std::io::Write;
2214 /// use heed::{Database, PutFlags};
2215 /// use heed::types::*;
2216 /// use heed::byteorder::BigEndian;
2217 ///
2218 /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
2219 /// # let dir = tempfile::tempdir()?;
2220 /// # let env = unsafe { EnvOpenOptions::new()
2221 /// # .map_size(10 * 1024 * 1024) // 10MB
2222 /// # .max_dbs(3000)
2223 /// # .open(dir.path())?
2224 /// # };
2225 /// type BEI32 = I32<BigEndian>;
2226 ///
2227 /// let mut wtxn = env.write_txn()?;
2228 /// let db = env.create_database::<BEI32, Str>(&mut wtxn, Some("number-string"))?;
2229 ///
2230 /// # db.clear(&mut wtxn)?;
2231 /// let long = "I am a long long long value";
2232 /// assert_eq!(
2233 /// db.get_or_put_reserved(&mut wtxn, &42, long.len(), |reserved| {
2234 /// reserved.write_all(long.as_bytes())
2235 /// })?,
2236 /// None
2237 /// );
2238 ///
2239 /// let longer = "I am an even longer long long long value";
2240 /// assert_eq!(
2241 /// db.get_or_put_reserved(&mut wtxn, &42, longer.len(), |reserved| {
2242 /// unreachable!()
2243 /// })?,
2244 /// Some(long)
2245 /// );
2246 ///
2247 /// let ret = db.get(&mut wtxn, &42)?;
2248 /// assert_eq!(ret, Some(long));
2249 ///
2250 /// wtxn.commit()?;
2251 /// # Ok(()) }
2252 /// ```
2253 pub fn get_or_put_reserved<'a, 'txn, F>(
2254 &'txn self,
2255 txn: &mut RwTxn,
2256 key: &'a KC::EItem,
2257 data_size: usize,
2258 write_func: F,
2259 ) -> Result<Option<DC::DItem>>
2260 where
2261 KC: BytesEncode<'a>,
2262 F: FnOnce(&mut ReservedSpace) -> io::Result<()>,
2263 DC: BytesDecode<'a>,
2264 {
2265 self.get_or_put_reserved_with_flags(txn, PutFlags::empty(), key, data_size, write_func)
2266 }
2267
2268 /// Attempt to insert a key-value pair in this database, where the value can be directly
2269 /// written to disk, or if a value already exists for the key, returns the previous value.
2270 ///
2271 /// The entry is written with the specified flags, in addition to
2272 /// [`NO_OVERWRITE`](PutFlags::NO_OVERWRITE) and [`MDB_RESERVE`](ffi::MDB_RESERVE)
2273 /// which are always used.
2274 ///
2275 /// ```
2276 /// # use heed::EnvOpenOptions;
2277 /// use std::io::Write;
2278 /// use heed::{Database, PutFlags};
2279 /// use heed::types::*;
2280 /// use heed::byteorder::BigEndian;
2281 ///
2282 /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
2283 /// # let dir = tempfile::tempdir()?;
2284 /// # let env = unsafe { EnvOpenOptions::new()
2285 /// # .map_size(10 * 1024 * 1024) // 10MB
2286 /// # .max_dbs(3000)
2287 /// # .open(dir.path())?
2288 /// # };
2289 /// type BEI32 = I32<BigEndian>;
2290 ///
2291 /// let mut wtxn = env.write_txn()?;
2292 /// let db = env.create_database::<BEI32, Str>(&mut wtxn, Some("number-string"))?;
2293 ///
2294 /// # db.clear(&mut wtxn)?;
2295 /// let long = "I am a long long long value";
2296 /// assert_eq!(
2297 /// db.get_or_put_reserved_with_flags(&mut wtxn, PutFlags::empty(), &42, long.len(), |reserved| {
2298 /// reserved.write_all(long.as_bytes())
2299 /// })?,
2300 /// None
2301 /// );
2302 ///
2303 /// let longer = "I am an even longer long long long value";
2304 /// assert_eq!(
2305 /// db.get_or_put_reserved_with_flags(&mut wtxn, PutFlags::empty(), &42, longer.len(), |reserved| {
2306 /// unreachable!()
2307 /// })?,
2308 /// Some(long)
2309 /// );
2310 ///
2311 /// let ret = db.get(&mut wtxn, &42)?;
2312 /// assert_eq!(ret, Some(long));
2313 ///
2314 /// wtxn.commit()?;
2315 /// # Ok(()) }
2316 /// ```
2317 pub fn get_or_put_reserved_with_flags<'a, 'txn, F>(
2318 &'txn self,
2319 txn: &mut RwTxn,
2320 flags: PutFlags,
2321 key: &'a KC::EItem,
2322 data_size: usize,
2323 write_func: F,
2324 ) -> Result<Option<DC::DItem>>
2325 where
2326 KC: BytesEncode<'a>,
2327 F: FnOnce(&mut ReservedSpace) -> io::Result<()>,
2328 DC: BytesDecode<'a>,
2329 {
2330 assert_eq_env_db_txn!(self, txn);
2331
2332 let key_bytes: Cow<[u8]> = KC::bytes_encode(key).map_err(Error::Encoding)?;
2333
2334 let mut key_val = unsafe { crate::into_val(&key_bytes) };
2335 let mut reserved = ffi::reserve_size_val(data_size);
2336 let flags = (flags | PutFlags::NO_OVERWRITE).bits() | ffi::MDB_RESERVE;
2337
2338 let result = unsafe {
2339 mdb_result(ffi::mdb_put(
2340 txn.txn.txn_ptr().as_mut(),
2341 self.dbi,
2342 &mut key_val,
2343 &mut reserved,
2344 flags,
2345 ))
2346 };
2347
2348 match result {
2349 // value was inserted: fill the reserved space
2350 Ok(()) => {
2351 let mut reserved = unsafe { ReservedSpace::from_val(reserved) };
2352 write_func(&mut reserved)?;
2353 if reserved.remaining() == 0 {
2354 Ok(None)
2355 } else {
2356 Err(io::Error::from(io::ErrorKind::UnexpectedEof).into())
2357 }
2358 }
2359 // the key already exists: the previous value is stored in the data parameter
2360 Err(MdbError::KeyExist) => {
2361 let bytes = unsafe { crate::from_val(reserved) };
2362 let data = DC::bytes_decode(bytes).map_err(Error::Decoding)?;
2363 Ok(Some(data))
2364 }
2365 Err(error) => Err(error.into()),
2366 }
2367 }
2368
2369 /// Deletes an entry or every duplicate data items of a key
2370 /// if the database supports duplicate data items.
2371 ///
2372 /// If the entry does not exist, then `false` is returned.
2373 ///
2374 /// ```
2375 /// # use std::fs;
2376 /// # use std::path::Path;
2377 /// # use heed::EnvOpenOptions;
2378 /// use heed::Database;
2379 /// use heed::types::*;
2380 /// use heed::byteorder::BigEndian;
2381 ///
2382 /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
2383 /// # let dir = tempfile::tempdir()?;
2384 /// # let env = unsafe { EnvOpenOptions::new()
2385 /// # .map_size(10 * 1024 * 1024) // 10MB
2386 /// # .max_dbs(3000)
2387 /// # .open(dir.path())?
2388 /// # };
2389 /// type BEI32 = I32<BigEndian>;
2390 ///
2391 /// let mut wtxn = env.write_txn()?;
2392 /// let db: Database<BEI32, Str> = env.create_database(&mut wtxn, Some("iter-i32"))?;
2393 ///
2394 /// # db.clear(&mut wtxn)?;
2395 /// db.put(&mut wtxn, &42, "i-am-forty-two")?;
2396 /// db.put(&mut wtxn, &27, "i-am-twenty-seven")?;
2397 /// db.put(&mut wtxn, &13, "i-am-thirteen")?;
2398 /// db.put(&mut wtxn, &521, "i-am-five-hundred-and-twenty-one")?;
2399 ///
2400 /// let ret = db.delete(&mut wtxn, &27)?;
2401 /// assert_eq!(ret, true);
2402 ///
2403 /// let ret = db.get(&mut wtxn, &27)?;
2404 /// assert_eq!(ret, None);
2405 ///
2406 /// let ret = db.delete(&mut wtxn, &467)?;
2407 /// assert_eq!(ret, false);
2408 ///
2409 /// wtxn.commit()?;
2410 /// # Ok(()) }
2411 /// ```
2412 pub fn delete<'a>(&self, txn: &mut RwTxn, key: &'a KC::EItem) -> Result<bool>
2413 where
2414 KC: BytesEncode<'a>,
2415 {
2416 assert_eq_env_db_txn!(self, txn);
2417
2418 let key_bytes: Cow<[u8]> = KC::bytes_encode(key).map_err(Error::Encoding)?;
2419 let mut key_val = unsafe { crate::into_val(&key_bytes) };
2420
2421 let result = unsafe {
2422 mdb_result(ffi::mdb_del(
2423 txn.txn.txn_ptr().as_mut(),
2424 self.dbi,
2425 &mut key_val,
2426 ptr::null_mut(),
2427 ))
2428 };
2429
2430 match result {
2431 Ok(()) => Ok(true),
2432 Err(e) if e.not_found() => Ok(false),
2433 Err(e) => Err(e.into()),
2434 }
2435 }
2436
2437 /// Deletes a single key-value pair in this database.
2438 ///
2439 /// If the database doesn't support duplicate data items the data is ignored.
2440 /// If the key does not exist, then `false` is returned.
2441 ///
2442 /// ```
2443 /// # use std::fs;
2444 /// # use std::path::Path;
2445 /// # use heed::{DatabaseFlags, EnvOpenOptions};
2446 /// use heed::types::*;
2447 /// use heed::byteorder::BigEndian;
2448 ///
2449 /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
2450 /// # let dir = tempfile::tempdir()?;
2451 /// # let env = unsafe { EnvOpenOptions::new()
2452 /// # .map_size(10 * 1024 * 1024) // 10MB
2453 /// # .max_dbs(3000)
2454 /// # .open(dir.path())?
2455 /// # };
2456 /// type BEI64 = I64<BigEndian>;
2457 ///
2458 /// let mut wtxn = env.write_txn()?;
2459 /// let db = env.database_options()
2460 /// .types::<BEI64, BEI64>()
2461 /// .flags(DatabaseFlags::DUP_SORT)
2462 /// .name("dup-sort")
2463 /// .create(&mut wtxn)?;
2464 ///
2465 /// # db.clear(&mut wtxn)?;
2466 /// db.put(&mut wtxn, &68, &120)?;
2467 /// db.put(&mut wtxn, &68, &121)?;
2468 /// db.put(&mut wtxn, &68, &122)?;
2469 /// db.put(&mut wtxn, &68, &123)?;
2470 /// db.put(&mut wtxn, &92, &32)?;
2471 /// db.put(&mut wtxn, &35, &120)?;
2472 /// db.put(&mut wtxn, &0, &120)?;
2473 /// db.put(&mut wtxn, &42, &120)?;
2474 ///
2475 /// let mut iter = db.get_duplicates(&wtxn, &68)?.expect("the key exists");
2476 /// assert_eq!(iter.next().transpose()?, Some((68, 120)));
2477 /// assert_eq!(iter.next().transpose()?, Some((68, 121)));
2478 /// assert_eq!(iter.next().transpose()?, Some((68, 122)));
2479 /// assert_eq!(iter.next().transpose()?, Some((68, 123)));
2480 /// assert_eq!(iter.next().transpose()?, None);
2481 /// drop(iter);
2482 ///
2483 /// assert!(db.delete_one_duplicate(&mut wtxn, &68, &121)?, "The entry must exist");
2484 ///
2485 /// let mut iter = db.get_duplicates(&wtxn, &68)?.expect("the key exists");
2486 /// assert_eq!(iter.next().transpose()?, Some((68, 120)));
2487 /// // No more (68, 121) returned here!
2488 /// assert_eq!(iter.next().transpose()?, Some((68, 122)));
2489 /// assert_eq!(iter.next().transpose()?, Some((68, 123)));
2490 /// assert_eq!(iter.next().transpose()?, None);
2491 /// drop(iter);
2492 ///
2493 /// wtxn.commit()?;
2494 /// # Ok(()) }
2495 /// ```
2496 pub fn delete_one_duplicate<'a>(
2497 &self,
2498 txn: &mut RwTxn,
2499 key: &'a KC::EItem,
2500 data: &'a DC::EItem,
2501 ) -> Result<bool>
2502 where
2503 KC: BytesEncode<'a>,
2504 DC: BytesEncode<'a>,
2505 {
2506 assert_eq_env_db_txn!(self, txn);
2507
2508 let key_bytes: Cow<[u8]> = KC::bytes_encode(key).map_err(Error::Encoding)?;
2509 let data_bytes: Cow<[u8]> = DC::bytes_encode(data).map_err(Error::Encoding)?;
2510 let mut key_val = unsafe { crate::into_val(&key_bytes) };
2511 let mut data_val = unsafe { crate::into_val(&data_bytes) };
2512
2513 let result = unsafe {
2514 mdb_result(ffi::mdb_del(
2515 txn.txn.txn_ptr().as_mut(),
2516 self.dbi,
2517 &mut key_val,
2518 &mut data_val,
2519 ))
2520 };
2521
2522 match result {
2523 Ok(()) => Ok(true),
2524 Err(e) if e.not_found() => Ok(false),
2525 Err(e) => Err(e.into()),
2526 }
2527 }
2528
2529 /// Deletes a range of key-value pairs in this database.
2530 ///
2531 /// Prefer using [`clear`] instead of a call to this method with a full range ([`..`]).
2532 ///
2533 /// Comparisons are made by using the comparator `C`.
2534 ///
2535 /// [`clear`]: crate::Database::clear
2536 /// [`..`]: std::ops::RangeFull
2537 ///
2538 /// ```
2539 /// # use std::fs;
2540 /// # use std::path::Path;
2541 /// # use heed::EnvOpenOptions;
2542 /// use heed::Database;
2543 /// use heed::types::*;
2544 /// use heed::byteorder::BigEndian;
2545 ///
2546 /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
2547 /// # let dir = tempfile::tempdir()?;
2548 /// # let env = unsafe { EnvOpenOptions::new()
2549 /// # .map_size(10 * 1024 * 1024) // 10MB
2550 /// # .max_dbs(3000)
2551 /// # .open(dir.path())?
2552 /// # };
2553 /// type BEI32 = I32<BigEndian>;
2554 ///
2555 /// let mut wtxn = env.write_txn()?;
2556 /// let db: Database<BEI32, Str> = env.create_database(&mut wtxn, Some("iter-i32"))?;
2557 ///
2558 /// # db.clear(&mut wtxn)?;
2559 /// db.put(&mut wtxn, &42, "i-am-forty-two")?;
2560 /// db.put(&mut wtxn, &27, "i-am-twenty-seven")?;
2561 /// db.put(&mut wtxn, &13, "i-am-thirteen")?;
2562 /// db.put(&mut wtxn, &521, "i-am-five-hundred-and-twenty-one")?;
2563 ///
2564 /// let range = 27..=42;
2565 /// let ret = db.delete_range(&mut wtxn, &range)?;
2566 /// assert_eq!(ret, 2);
2567 ///
2568 ///
2569 /// let mut iter = db.iter(&wtxn)?;
2570 /// assert_eq!(iter.next().transpose()?, Some((13, "i-am-thirteen")));
2571 /// assert_eq!(iter.next().transpose()?, Some((521, "i-am-five-hundred-and-twenty-one")));
2572 /// assert_eq!(iter.next().transpose()?, None);
2573 ///
2574 /// drop(iter);
2575 /// wtxn.commit()?;
2576 /// # Ok(()) }
2577 /// ```
2578 pub fn delete_range<'a, 'txn, R>(&self, txn: &'txn mut RwTxn, range: &'a R) -> Result<usize>
2579 where
2580 KC: BytesEncode<'a> + BytesDecode<'txn>,
2581 C: Comparator,
2582 R: RangeBounds<KC::EItem>,
2583 {
2584 assert_eq_env_db_txn!(self, txn);
2585
2586 let mut count = 0;
2587 let mut iter = self.remap_data_type::<DecodeIgnore>().range_mut(txn, range)?;
2588
2589 while iter.next().is_some() {
2590 // safety: We do not keep any reference from the database while using `del_current`.
2591 // The user can't keep any reference inside of the database as we ask for a
2592 // mutable reference to the `txn`.
2593 unsafe { iter.del_current()? };
2594 count += 1;
2595 }
2596
2597 Ok(count)
2598 }
2599
2600 /// Deletes all key/value pairs in this database.
2601 ///
2602 /// Prefer using this method instead of a call to [`delete_range`] with a full range ([`..`]).
2603 ///
2604 /// [`delete_range`]: crate::Database::delete_range
2605 /// [`..`]: std::ops::RangeFull
2606 ///
2607 /// ```
2608 /// # use std::fs;
2609 /// # use std::path::Path;
2610 /// # use heed::EnvOpenOptions;
2611 /// use heed::Database;
2612 /// use heed::types::*;
2613 /// use heed::byteorder::BigEndian;
2614 ///
2615 /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
2616 /// # let dir = tempfile::tempdir()?;
2617 /// # let env = unsafe { EnvOpenOptions::new()
2618 /// # .map_size(10 * 1024 * 1024) // 10MB
2619 /// # .max_dbs(3000)
2620 /// # .open(dir.path())?
2621 /// # };
2622 /// type BEI32 = I32<BigEndian>;
2623 ///
2624 /// let mut wtxn = env.write_txn()?;
2625 /// let db: Database<BEI32, Str> = env.create_database(&mut wtxn, Some("iter-i32"))?;
2626 ///
2627 /// # db.clear(&mut wtxn)?;
2628 /// db.put(&mut wtxn, &42, "i-am-forty-two")?;
2629 /// db.put(&mut wtxn, &27, "i-am-twenty-seven")?;
2630 /// db.put(&mut wtxn, &13, "i-am-thirteen")?;
2631 /// db.put(&mut wtxn, &521, "i-am-five-hundred-and-twenty-one")?;
2632 ///
2633 /// db.clear(&mut wtxn)?;
2634 ///
2635 /// let ret = db.is_empty(&wtxn)?;
2636 /// assert!(ret);
2637 ///
2638 /// wtxn.commit()?;
2639 /// # Ok(()) }
2640 /// ```
2641 pub fn clear(&self, txn: &mut RwTxn) -> Result<()> {
2642 assert_eq_env_db_txn!(self, txn);
2643
2644 unsafe {
2645 mdb_result(ffi::mdb_drop(txn.txn.txn_ptr().as_mut(), self.dbi, 0)).map_err(Into::into)
2646 }
2647 }
2648
2649 /// Removes this database entirely.
2650 ///
2651 /// # Safety
2652 ///
2653 /// Ensure that no other copies of the database exist before calling, as
2654 /// they will become invalid.
2655 /// Do not remove a database if an existing transaction has modified it.
2656 /// Doing so can cause database corruption or other errors.
2657 ///
2658 /// ```
2659 /// # use std::fs;
2660 /// # use std::path::Path;
2661 /// # use heed::EnvOpenOptions;
2662 /// use heed::Database;
2663 /// use heed::types::*;
2664 /// use heed::byteorder::BigEndian;
2665 ///
2666 /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
2667 /// # let dir = tempfile::tempdir()?;
2668 /// # let env = unsafe { EnvOpenOptions::new()
2669 /// # .map_size(10 * 1024 * 1024) // 10MB
2670 /// # .max_dbs(3000)
2671 /// # .open(dir.path())?
2672 /// # };
2673 /// /// List databases in an env
2674 #[cfg_attr(not(master3), doc = concat!(
2675 "fn list_dbs(env: &heed::Env, rotxn: &heed::RoTxn<'_>) -> heed::Result<Vec<String>> {\n",
2676 " let names_db: Database<Str, DecodeIgnore> =",
2677 ))]
2678 #[cfg_attr(master3, doc = concat!(
2679 "fn list_dbs(\n",
2680 " env: &heed::Env,\n",
2681 " rotxn: &heed::RoTxn<'_>,\n",
2682 ") -> Result<Vec<String>, Box<dyn std::error::Error>> {\n",
2683 " // mdb-master3 uses null-terminated C strings as DB names\n",
2684 " let names_db: Database<Bytes, DecodeIgnore> =",
2685 ))]
2686 /// env.open_database(&rotxn, None)?
2687 /// .expect("the unnamed database always exists");
2688 /// let mut names = Vec::new();
2689 /// for item in names_db.iter(&rotxn)? {
2690 /// let (name, ()) = item?;
2691 #[cfg_attr(master3, doc = concat!(
2692 " let name = std::ffi::CStr::from_bytes_with_nul(name)?.to_str()?;",
2693 ))]
2694 /// names.push(name.to_owned());
2695 /// }
2696 /// Ok(names)
2697 /// }
2698 ///
2699 /// type BEI32 = I32<BigEndian>;
2700 ///
2701 /// let mut rwtxn = env.write_txn()?;
2702 /// let db: Database<BEI32, Str> = env.create_database(&mut rwtxn, Some("iter-i32"))?;
2703 /// rwtxn.commit()?;
2704 ///
2705 /// let rotxn = env.read_txn()?;
2706 /// let db_names = list_dbs(&env, &rotxn)?;
2707 /// assert_eq!(db_names, vec!["iter-i32".to_owned()]);
2708 /// drop(rotxn);
2709 ///
2710 /// let mut rwtxn = env.write_txn()?;
2711 /// unsafe { db.remove(&mut rwtxn)? };
2712 /// let db_names = list_dbs(&env, &rwtxn)?;
2713 /// assert!(db_names.is_empty());
2714 /// rwtxn.commit()?;
2715 /// # Ok(()) }
2716 /// ```
2717 pub unsafe fn remove(self, rwtxn: &mut RwTxn) -> Result<()> {
2718 assert_eq_env_db_txn!(self, rwtxn);
2719
2720 unsafe {
2721 mdb_result(ffi::mdb_drop(rwtxn.txn.txn_ptr().as_mut(), self.dbi, 1)).map_err(Into::into)
2722 }
2723 }
2724
2725 /// Change the codec types of this database, specifying the codecs.
2726 ///
2727 /// # Safety
2728 ///
2729 /// It is up to you to ensure that the data read and written using the polymorphic
2730 /// handle correspond to the the typed, uniform one. If an invalid write is made,
2731 /// it can corrupt the database from the eyes of heed.
2732 ///
2733 /// # Example
2734 ///
2735 /// ```
2736 /// # use std::fs;
2737 /// # use std::path::Path;
2738 /// # use heed::EnvOpenOptions;
2739 /// use heed::Database;
2740 /// use heed::types::*;
2741 /// use heed::byteorder::BigEndian;
2742 ///
2743 /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
2744 /// # let dir = tempfile::tempdir()?;
2745 /// # let env = unsafe { EnvOpenOptions::new()
2746 /// # .map_size(10 * 1024 * 1024) // 10MB
2747 /// # .max_dbs(3000)
2748 /// # .open(dir.path())?
2749 /// # };
2750 /// type BEI32 = I32<BigEndian>;
2751 ///
2752 /// let mut wtxn = env.write_txn()?;
2753 /// let db: Database<Unit, Unit> = env.create_database(&mut wtxn, Some("iter-i32"))?;
2754 ///
2755 /// # db.clear(&mut wtxn)?;
2756 /// // We remap the types for ease of use.
2757 /// let db = db.remap_types::<BEI32, Str>();
2758 /// db.put(&mut wtxn, &42, "i-am-forty-two")?;
2759 /// db.put(&mut wtxn, &27, "i-am-twenty-seven")?;
2760 /// db.put(&mut wtxn, &13, "i-am-thirteen")?;
2761 /// db.put(&mut wtxn, &521, "i-am-five-hundred-and-twenty-one")?;
2762 ///
2763 /// wtxn.commit()?;
2764 /// # Ok(()) }
2765 /// ```
2766 pub fn remap_types<KC2, DC2>(&self) -> Database<KC2, DC2, C> {
2767 Database::new(self.env_ident, self.dbi)
2768 }
2769
2770 /// Change the key codec type of this database, specifying the new codec.
2771 pub fn remap_key_type<KC2>(&self) -> Database<KC2, DC, C> {
2772 self.remap_types::<KC2, DC>()
2773 }
2774
2775 /// Change the data codec type of this database, specifying the new codec.
2776 pub fn remap_data_type<DC2>(&self) -> Database<KC, DC2, C> {
2777 self.remap_types::<KC, DC2>()
2778 }
2779
2780 /// Wrap the data bytes into a lazy decoder.
2781 pub fn lazily_decode_data(&self) -> Database<KC, LazyDecode<DC>, C> {
2782 self.remap_types::<KC, LazyDecode<DC>>()
2783 }
2784}
2785
2786impl<KC, DC, C, CDUP> Clone for Database<KC, DC, C, CDUP> {
2787 fn clone(&self) -> Database<KC, DC, C, CDUP> {
2788 *self
2789 }
2790}
2791
2792impl<KC, DC, C, CDUP> Copy for Database<KC, DC, C, CDUP> {}
2793
2794impl<KC, DC, C, CDUP> fmt::Debug for Database<KC, DC, C, CDUP> {
2795 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
2796 f.debug_struct("Database")
2797 .field("key_codec", &any::type_name::<KC>())
2798 .field("data_codec", &any::type_name::<DC>())
2799 .field("key_comparator", &any::type_name::<C>())
2800 .field("dup_sort_comparator", &any::type_name::<CDUP>())
2801 .finish()
2802 }
2803}
2804
2805#[cfg(test)]
2806mod tests {
2807 use byteorder::*;
2808 use heed_types::*;
2809
2810 use super::*;
2811 use crate::IntegerComparator;
2812
2813 #[test]
2814 fn put_overwrite() -> Result<()> {
2815 let dir = tempfile::tempdir()?;
2816 let env = unsafe { EnvOpenOptions::new().open(dir.path())? };
2817 let mut txn = env.write_txn()?;
2818 let db = env.create_database::<Bytes, Bytes>(&mut txn, None)?;
2819
2820 assert_eq!(db.get(&txn, b"hello").unwrap(), None);
2821
2822 db.put(&mut txn, b"hello", b"hi").unwrap();
2823 assert_eq!(db.get(&txn, b"hello").unwrap(), Some(&b"hi"[..]));
2824
2825 db.put(&mut txn, b"hello", b"bye").unwrap();
2826 assert_eq!(db.get(&txn, b"hello").unwrap(), Some(&b"bye"[..]));
2827
2828 Ok(())
2829 }
2830
2831 #[test]
2832 #[cfg(feature = "longer-keys")]
2833 fn longer_keys() -> Result<()> {
2834 let dir = tempfile::tempdir()?;
2835 let env = unsafe { EnvOpenOptions::new().open(dir.path())? };
2836 let mut txn = envs.write_txn()?;
2837 let db = envs.create_database::<Bytes, Bytes>(&mut txn, None)?;
2838
2839 // Try storing a key larger than 511 bytes (the default if MDB_MAXKEYSIZE is not set)
2840 let long_key = b"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut pharetra sit amet aliquam. Sit amet nisl purus in mollis nunc. Eget egestas purus viverra accumsan in nisl nisi scelerisque. Duis ultricies lacus sed turpis tincidunt. Sem nulla pharetra diam sit. Leo vel orci porta non pulvinar. Erat pellentesque adipiscing commodo elit at imperdiet dui. Suspendisse ultrices gravida dictum fusce ut placerat orci nulla. Diam donec adipiscing tristique risus nec feugiat. In fermentum et sollicitudin ac orci. Ut sem nulla pharetra diam sit amet. Aliquam purus sit amet luctus venenatis lectus. Erat pellentesque adipiscing commodo elit at imperdiet dui accumsan. Urna duis convallis convallis tellus id interdum velit laoreet id. Ac feugiat sed lectus vestibulum mattis ullamcorper velit sed. Tincidunt arcu non sodales neque. Habitant morbi tristique senectus et netus et malesuada fames.";
2841
2842 assert_eq!(db.get(&txn, long_key).unwrap(), None);
2843
2844 db.put(&mut txn, long_key, b"hi").unwrap();
2845 assert_eq!(db.get(&txn, long_key).unwrap(), Some(&b"hi"[..]));
2846
2847 db.put(&mut txn, long_key, b"bye").unwrap();
2848 assert_eq!(db.get(&txn, long_key).unwrap(), Some(&b"bye"[..]));
2849
2850 Ok(())
2851 }
2852
2853 #[test]
2854 fn integer_keys() -> Result<()> {
2855 type NEU32 = U32<NativeEndian>;
2856
2857 let dir = tempfile::tempdir()?;
2858 let env = unsafe { EnvOpenOptions::new().open(dir.path())? };
2859 let mut txn = env.write_txn()?;
2860 let db = env
2861 .database_options()
2862 .types::<NEU32, NEU32>()
2863 .key_comparator::<IntegerComparator>()
2864 .create(&mut txn)?;
2865
2866 let range = 1000..2000;
2867
2868 for i in range.clone() {
2869 db.put(&mut txn, &i, &i)?;
2870 }
2871
2872 let mut i = 0;
2873 for (val, expected) in db.range(&txn, &(0..10_000))?.zip(range.clone()) {
2874 assert_eq!(val?.0, expected);
2875 i += 1;
2876 }
2877
2878 assert_eq!(i, range.end - range.start);
2879 Ok(())
2880 }
2881}