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