From addaf82aaae2a25f1432abace2adcb286e10a53e Mon Sep 17 00:00:00 2001 From: Kerollmops Date: Tue, 4 Mar 2025 17:26:24 +0100 Subject: [PATCH 1/5] Clarify some Ro/RwTxn names --- heed/src/envs/env.rs | 8 ++++---- heed/src/iterator/iter.rs | 4 ++++ heed/src/txn.rs | 8 ++++---- 3 files changed, 12 insertions(+), 8 deletions(-) diff --git a/heed/src/envs/env.rs b/heed/src/envs/env.rs index f481b20d..74858909 100644 --- a/heed/src/envs/env.rs +++ b/heed/src/envs/env.rs @@ -315,7 +315,7 @@ impl Env { /// the thread initiating the new one will wait on a mutex upon completion of the previous /// transaction. pub fn write_txn(&self) -> Result { - RwTxn::new(self) + RwTxn::begin(self) } /// Create a nested transaction with read and write access for use with the environment. @@ -326,7 +326,7 @@ impl Env { /// A parent transaction and its cursors may not issue any other operations than _commit_ and /// _abort_ while it has active child transactions. pub fn nested_write_txn<'p>(&'p self, parent: &'p mut RwTxn) -> Result> { - RwTxn::nested(self, parent) + RwTxn::begin_nested(self, parent) } /// Create a transaction with read-only access for use with the environment. @@ -356,7 +356,7 @@ impl Env { /// * [`crate::MdbError::ReadersFull`]: a read-only transaction was requested, and the reader lock table is /// full pub fn read_txn(&self) -> Result> { - RoTxn::new(self) + RoTxn::begin(self) } /// Create a transaction with read-only access for use with the environment. @@ -386,7 +386,7 @@ impl Env { /// * [`crate::MdbError::ReadersFull`]: a read-only transaction was requested, and the reader lock table is /// full pub fn static_read_txn(self) -> Result> { - RoTxn::static_read_txn(self) + RoTxn::begin_static_read_txn(self) } /// Copy an LMDB environment to the specified path, with options. diff --git a/heed/src/iterator/iter.rs b/heed/src/iterator/iter.rs index 503c86d8..ee4dd1d7 100644 --- a/heed/src/iterator/iter.rs +++ b/heed/src/iterator/iter.rs @@ -223,6 +223,10 @@ impl<'txn, KC, DC, IM> RwIter<'txn, KC, DC, IM> { RwIter { cursor, move_on_first: true, _phantom: marker::PhantomData } } + pub fn as_wtxn<'p>(&mut self) -> &mut RwTxn<'p> { + todo!() + } + /// Delete the entry the cursor is currently pointing to. /// /// Returns `true` if the entry was successfully deleted. diff --git a/heed/src/txn.rs b/heed/src/txn.rs index 8f41ddb2..0d33b1b9 100644 --- a/heed/src/txn.rs +++ b/heed/src/txn.rs @@ -60,7 +60,7 @@ struct RoTxnInner<'e> { } impl<'e, T> RoTxn<'e, T> { - pub(crate) fn new(env: &'e Env) -> Result> { + pub(crate) fn begin(env: &'e Env) -> Result> { let mut txn: *mut ffi::MDB_txn = ptr::null_mut(); unsafe { @@ -78,7 +78,7 @@ impl<'e, T> RoTxn<'e, T> { }) } - pub(crate) fn static_read_txn(env: Env) -> Result> { + pub(crate) fn begin_static_read_txn(env: Env) -> Result> { let mut txn: *mut ffi::MDB_txn = ptr::null_mut(); unsafe { @@ -258,7 +258,7 @@ pub struct RwTxn<'p> { } impl<'p> RwTxn<'p> { - pub(crate) fn new(env: &'p Env) -> Result> { + pub(crate) fn begin(env: &'p Env) -> Result> { let mut txn: *mut ffi::MDB_txn = ptr::null_mut(); unsafe { @@ -279,7 +279,7 @@ impl<'p> RwTxn<'p> { }) } - pub(crate) fn nested(env: &'p Env, parent: &'p mut RwTxn) -> Result> { + pub(crate) fn begin_nested(env: &'p Env, parent: &'p mut RwTxn) -> Result> { let mut txn: *mut ffi::MDB_txn = ptr::null_mut(); let parent_ptr: *mut ffi::MDB_txn = unsafe { parent.txn.inner.txn.unwrap().as_mut() }; From d5c30e6150182126cc340807d4bb59cf2950b712 Mon Sep 17 00:00:00 2001 From: Kerollmops Date: Tue, 4 Mar 2025 17:27:38 +0100 Subject: [PATCH 2/5] Clarify Ro/RwCursor names --- heed/src/cursor.rs | 6 +++--- heed/src/databases/database.rs | 38 +++++++++++++++++----------------- heed/src/envs/env.rs | 2 +- 3 files changed, 23 insertions(+), 23 deletions(-) diff --git a/heed/src/cursor.rs b/heed/src/cursor.rs index 8c859316..d43b77ce 100644 --- a/heed/src/cursor.rs +++ b/heed/src/cursor.rs @@ -12,7 +12,7 @@ pub struct RoCursor<'txn> { impl<'txn> RoCursor<'txn> { // TODO should I ask for a &mut RoTxn<'_, T>, here? - pub(crate) fn new(txn: &'txn RoTxn<'_, T>, dbi: ffi::MDB_dbi) -> Result> { + pub(crate) fn open(txn: &'txn RoTxn<'_, T>, dbi: ffi::MDB_dbi) -> Result> { let mut cursor: *mut ffi::MDB_cursor = ptr::null_mut(); let mut txn = txn.txn_ptr(); unsafe { mdb_result(ffi::mdb_cursor_open(txn.as_mut(), dbi, &mut cursor))? } @@ -248,8 +248,8 @@ pub struct RwCursor<'txn> { } impl<'txn> RwCursor<'txn> { - pub(crate) fn new(txn: &'txn RwTxn, dbi: ffi::MDB_dbi) -> Result> { - Ok(RwCursor { cursor: RoCursor::new(txn, dbi)? }) + pub(crate) fn open(txn: &'txn RwTxn, dbi: ffi::MDB_dbi) -> Result> { + Ok(RwCursor { cursor: RoCursor::open(txn, dbi)? }) } /// Delete the entry the cursor is currently pointing to. diff --git a/heed/src/databases/database.rs b/heed/src/databases/database.rs index 011de40f..b96c1f1c 100644 --- a/heed/src/databases/database.rs +++ b/heed/src/databases/database.rs @@ -439,7 +439,7 @@ impl Database { { assert_eq_env_db_txn!(self, txn); - let mut cursor = RoCursor::new(txn, self.dbi)?; + let mut cursor = RoCursor::open(txn, self.dbi)?; let key_bytes: Cow<[u8]> = KC::bytes_encode(key).map_err(Error::Encoding)?; if cursor.move_on_key(&key_bytes)? { Ok(Some(RoIter::new(cursor))) @@ -503,7 +503,7 @@ impl Database { { assert_eq_env_db_txn!(self, txn); - let mut cursor = RoCursor::new(txn, self.dbi)?; + let mut cursor = RoCursor::open(txn, self.dbi)?; let key_bytes: Cow<[u8]> = KC::bytes_encode(key).map_err(Error::Encoding)?; cursor.move_on_key_greater_than_or_equal_to(&key_bytes)?; @@ -572,7 +572,7 @@ impl Database { { assert_eq_env_db_txn!(self, txn); - let mut cursor = RoCursor::new(txn, self.dbi)?; + let mut cursor = RoCursor::open(txn, self.dbi)?; let key_bytes: Cow<[u8]> = KC::bytes_encode(key).map_err(Error::Encoding)?; let result = match cursor.move_on_key_greater_than_or_equal_to(&key_bytes) { Ok(Some((key, data))) if key == &key_bytes[..] => Ok(Some((key, data))), @@ -645,7 +645,7 @@ impl Database { { assert_eq_env_db_txn!(self, txn); - let mut cursor = RoCursor::new(txn, self.dbi)?; + let mut cursor = RoCursor::open(txn, self.dbi)?; let key_bytes: Cow<[u8]> = KC::bytes_encode(key).map_err(Error::Encoding)?; let entry = match cursor.move_on_key_greater_than_or_equal_to(&key_bytes)? { Some((key, data)) if key > &key_bytes[..] => Some((key, data)), @@ -717,7 +717,7 @@ impl Database { { assert_eq_env_db_txn!(self, txn); - let mut cursor = RoCursor::new(txn, self.dbi)?; + let mut cursor = RoCursor::open(txn, self.dbi)?; let key_bytes: Cow<[u8]> = KC::bytes_encode(key).map_err(Error::Encoding)?; match cursor.move_on_key_greater_than_or_equal_to(&key_bytes) { Ok(Some((key, data))) => match (KC::bytes_decode(key), DC::bytes_decode(data)) { @@ -772,7 +772,7 @@ impl Database { { assert_eq_env_db_txn!(self, txn); - let mut cursor = RoCursor::new(txn, self.dbi)?; + let mut cursor = RoCursor::open(txn, self.dbi)?; match cursor.move_on_first(MoveOperation::Any) { Ok(Some((key, data))) => match (KC::bytes_decode(key), DC::bytes_decode(data)) { (Ok(key), Ok(data)) => Ok(Some((key, data))), @@ -826,7 +826,7 @@ impl Database { { assert_eq_env_db_txn!(self, txn); - let mut cursor = RoCursor::new(txn, self.dbi)?; + let mut cursor = RoCursor::open(txn, self.dbi)?; match cursor.move_on_last(MoveOperation::Any) { Ok(Some((key, data))) => match (KC::bytes_decode(key), DC::bytes_decode(data)) { (Ok(key), Ok(data)) => Ok(Some((key, data))), @@ -1028,7 +1028,7 @@ impl Database { /// ``` pub fn iter<'txn, T>(&self, txn: &'txn RoTxn) -> Result> { assert_eq_env_db_txn!(self, txn); - RoCursor::new(txn, self.dbi).map(|cursor| RoIter::new(cursor)) + RoCursor::open(txn, self.dbi).map(|cursor| RoIter::new(cursor)) } /// Return a mutable ordered iterator of all key-value pairs in this database. @@ -1084,7 +1084,7 @@ impl Database { pub fn iter_mut<'txn>(&self, txn: &'txn mut RwTxn) -> Result> { assert_eq_env_db_txn!(self, txn); - RwCursor::new(txn, self.dbi).map(|cursor| RwIter::new(cursor)) + RwCursor::open(txn, self.dbi).map(|cursor| RwIter::new(cursor)) } /// Return a reverse ordered iterator of all key-value pairs in this database. @@ -1131,7 +1131,7 @@ impl Database { pub fn rev_iter<'txn, T>(&self, txn: &'txn RoTxn) -> Result> { assert_eq_env_db_txn!(self, txn); - RoCursor::new(txn, self.dbi).map(|cursor| RoRevIter::new(cursor)) + RoCursor::open(txn, self.dbi).map(|cursor| RoRevIter::new(cursor)) } /// Return a mutable reverse ordered iterator of all key-value\ @@ -1188,7 +1188,7 @@ impl Database { pub fn rev_iter_mut<'txn>(&self, txn: &'txn mut RwTxn) -> Result> { assert_eq_env_db_txn!(self, txn); - RwCursor::new(txn, self.dbi).map(|cursor| RwRevIter::new(cursor)) + RwCursor::open(txn, self.dbi).map(|cursor| RwRevIter::new(cursor)) } /// Return an ordered iterator of a range of key-value pairs in this database. @@ -1270,7 +1270,7 @@ impl Database { Bound::Unbounded => Bound::Unbounded, }; - RoCursor::new(txn, self.dbi).map(|cursor| RoRange::new(cursor, start_bound, end_bound)) + RoCursor::open(txn, self.dbi).map(|cursor| RoRange::new(cursor, start_bound, end_bound)) } /// Return a mutable ordered iterator of a range of key-value pairs in this database. @@ -1361,7 +1361,7 @@ impl Database { Bound::Unbounded => Bound::Unbounded, }; - RwCursor::new(txn, self.dbi).map(|cursor| RwRange::new(cursor, start_bound, end_bound)) + RwCursor::open(txn, self.dbi).map(|cursor| RwRange::new(cursor, start_bound, end_bound)) } /// Return a reverse ordered iterator of a range of key-value pairs in this database. @@ -1443,7 +1443,7 @@ impl Database { Bound::Unbounded => Bound::Unbounded, }; - RoCursor::new(txn, self.dbi).map(|cursor| RoRevRange::new(cursor, start_bound, end_bound)) + RoCursor::open(txn, self.dbi).map(|cursor| RoRevRange::new(cursor, start_bound, end_bound)) } /// Return a mutable reverse ordered iterator of a range of key-value pairs in this database. @@ -1534,7 +1534,7 @@ impl Database { Bound::Unbounded => Bound::Unbounded, }; - RwCursor::new(txn, self.dbi).map(|cursor| RwRevRange::new(cursor, start_bound, end_bound)) + RwCursor::open(txn, self.dbi).map(|cursor| RwRevRange::new(cursor, start_bound, end_bound)) } /// Return a lexicographically ordered iterator of all key-value pairs @@ -1596,7 +1596,7 @@ impl Database { let prefix_bytes = KC::bytes_encode(prefix).map_err(Error::Encoding)?; let prefix_bytes = prefix_bytes.into_owned(); - RoCursor::new(txn, self.dbi).map(|cursor| RoPrefix::new(cursor, prefix_bytes)) + RoCursor::open(txn, self.dbi).map(|cursor| RoPrefix::new(cursor, prefix_bytes)) } /// Return a mutable lexicographically ordered iterator of all key-value pairs @@ -1667,7 +1667,7 @@ impl Database { let prefix_bytes = KC::bytes_encode(prefix).map_err(Error::Encoding)?; let prefix_bytes = prefix_bytes.into_owned(); - RwCursor::new(txn, self.dbi).map(|cursor| RwPrefix::new(cursor, prefix_bytes)) + RwCursor::open(txn, self.dbi).map(|cursor| RwPrefix::new(cursor, prefix_bytes)) } /// Return a reversed lexicographically ordered iterator of all key-value pairs @@ -1729,7 +1729,7 @@ impl Database { let prefix_bytes = KC::bytes_encode(prefix).map_err(Error::Encoding)?; let prefix_bytes = prefix_bytes.into_owned(); - RoCursor::new(txn, self.dbi).map(|cursor| RoRevPrefix::new(cursor, prefix_bytes)) + RoCursor::open(txn, self.dbi).map(|cursor| RoRevPrefix::new(cursor, prefix_bytes)) } /// Return a mutable reversed lexicographically ordered iterator of all key-value pairs @@ -1800,7 +1800,7 @@ impl Database { let prefix_bytes = KC::bytes_encode(prefix).map_err(Error::Encoding)?; let prefix_bytes = prefix_bytes.into_owned(); - RwCursor::new(txn, self.dbi).map(|cursor| RwRevPrefix::new(cursor, prefix_bytes)) + RwCursor::open(txn, self.dbi).map(|cursor| RwRevPrefix::new(cursor, prefix_bytes)) } /// Insert a key-value pair in this database, replacing any previous value. The entry is diff --git a/heed/src/envs/env.rs b/heed/src/envs/env.rs index 74858909..2104f665 100644 --- a/heed/src/envs/env.rs +++ b/heed/src/envs/env.rs @@ -170,7 +170,7 @@ impl Env { let dbi = self.raw_open_dbi::(rtxn.txn_ptr(), None, 0)?; // We're going to iterate on the unnamed database - let mut cursor = RoCursor::new(&rtxn, dbi)?; + let mut cursor = RoCursor::open(&rtxn, dbi)?; while let Some((key, _value)) = cursor.move_on_next(MoveOperation::NoDup)? { if key.contains(&0) { From a75a8c06bbdfd432065cf601761c6a0c15328200 Mon Sep 17 00:00:00 2001 From: Kerollmops Date: Tue, 4 Mar 2025 18:17:18 +0100 Subject: [PATCH 3/5] Expose the RwTxn from the RwIter type --- heed/src/cursor.rs | 388 ++++++++++++++++++--------------- heed/src/databases/database.rs | 31 +-- heed/src/iterator/iter.rs | 55 ++--- heed/src/iterator/prefix.rs | 58 ++--- heed/src/iterator/range.rs | 58 ++--- 5 files changed, 314 insertions(+), 276 deletions(-) diff --git a/heed/src/cursor.rs b/heed/src/cursor.rs index d43b77ce..54832130 100644 --- a/heed/src/cursor.rs +++ b/heed/src/cursor.rs @@ -1,22 +1,222 @@ use std::ops::{Deref, DerefMut}; -use std::{marker, mem, ptr}; +use std::{mem, ptr}; use crate::mdb::error::mdb_result; use crate::mdb::ffi; use crate::*; pub struct RoCursor<'txn> { - cursor: *mut ffi::MDB_cursor, - _marker: marker::PhantomData<&'txn ()>, + cursor: CursorInner<'txn>, } impl<'txn> RoCursor<'txn> { // TODO should I ask for a &mut RoTxn<'_, T>, here? pub(crate) fn open(txn: &'txn RoTxn<'_, T>, dbi: ffi::MDB_dbi) -> Result> { + let cursor = unsafe { CursorInner::open(txn.txn_ptr().as_mut(), dbi)? }; + Ok(RoCursor { cursor }) + } +} + +impl<'txn> Deref for RoCursor<'txn> { + type Target = CursorInner<'txn>; + + fn deref(&self) -> &Self::Target { + &self.cursor + } +} + +impl DerefMut for RoCursor<'_> { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.cursor + } +} + +pub struct RwCursor<'txn, 'p> { + cursor: CursorInner<'txn>, + pub(crate) txn: &'txn mut RwTxn<'p>, +} + +impl<'txn, 'p> RwCursor<'txn, 'p> { + pub(crate) fn open(txn: &'txn mut RwTxn<'p>, dbi: ffi::MDB_dbi) -> Result> { + let cursor = unsafe { CursorInner::open(txn.txn_ptr().as_mut(), dbi)? }; + Ok(RwCursor { cursor, txn }) + } + + /// Delete the entry the cursor is currently pointing to. + /// + /// Returns `true` if the entry was successfully deleted. + /// + /// # Safety + /// + /// It is _[undefined behavior]_ to keep a reference of a value from this database + /// while modifying it. + /// + /// > [Values returned from the database are valid only until a subsequent update operation, + /// > or the end of the transaction.](http://www.lmdb.tech/doc/group__mdb.html#structMDB__val) + /// + /// [undefined behavior]: https://doc.rust-lang.org/reference/behavior-considered-undefined.html + pub unsafe fn del_current(&mut self) -> Result { + // Delete the current entry + let result = mdb_result(ffi::mdb_cursor_del(self.cursor.cursor, 0)); + + match result { + Ok(()) => Ok(true), + Err(e) if e.not_found() => Ok(false), + Err(e) => Err(e.into()), + } + } + + /// Write a new value to the current entry. + /// + /// The given key **must** be equal to the one this cursor is pointing otherwise the database + /// can be put into an inconsistent state. + /// + /// Returns `true` if the entry was successfully written. + /// + /// > This is intended to be used when the new data is the same size as the old. + /// > Otherwise it will simply perform a delete of the old record followed by an insert. + /// + /// # Safety + /// + /// It is _[undefined behavior]_ to keep a reference of a value from this database while + /// modifying it, so you can't use the key/value that comes from the cursor to feed + /// this function. + /// + /// In other words: Transform the key and value that you borrow from this database into an owned + /// version of them (e.g. `&str` into `String`). + /// + /// > [Values returned from the database are valid only until a subsequent update operation, + /// > or the end of the transaction.](http://www.lmdb.tech/doc/group__mdb.html#structMDB__val) + /// + /// [undefined behavior]: https://doc.rust-lang.org/reference/behavior-considered-undefined.html + pub unsafe fn put_current(&mut self, key: &[u8], data: &[u8]) -> Result { + let mut key_val = crate::into_val(key); + let mut data_val = crate::into_val(data); + + // Modify the pointed data + let result = mdb_result(ffi::mdb_cursor_put( + self.cursor.cursor, + &mut key_val, + &mut data_val, + ffi::MDB_CURRENT, + )); + + match result { + Ok(()) => Ok(true), + Err(e) if e.not_found() => Ok(false), + Err(e) => Err(e.into()), + } + } + + /// Write a new value to the current entry. + /// + /// The given key **must** be equal to the one this cursor is pointing otherwise the database + /// can be put into an inconsistent state. + /// + /// Returns `true` if the entry was successfully written. + /// + /// > This is intended to be used when the new data is the same size as the old. + /// > Otherwise it will simply perform a delete of the old record followed by an insert. + /// + /// # Safety + /// + /// Please read the safety notes of the [`Self::put_current`] method. + pub unsafe fn put_current_reserved_with_flags( + &mut self, + flags: PutFlags, + key: &[u8], + data_size: usize, + write_func: F, + ) -> Result + where + F: FnOnce(&mut ReservedSpace) -> io::Result<()>, + { + let mut key_val = crate::into_val(key); + let mut reserved = ffi::reserve_size_val(data_size); + let flags = ffi::MDB_RESERVE | flags.bits(); + + let result = + mdb_result(ffi::mdb_cursor_put(self.cursor.cursor, &mut key_val, &mut reserved, flags)); + + let found = match result { + Ok(()) => true, + Err(e) if e.not_found() => false, + Err(e) => return Err(e.into()), + }; + + let mut reserved = ReservedSpace::from_val(reserved); + write_func(&mut reserved)?; + + if reserved.remaining() == 0 { + Ok(found) + } else { + Err(io::Error::from(io::ErrorKind::UnexpectedEof).into()) + } + } + + /// Append the given key/value pair to the end of the database. + /// + /// If a key is inserted that is less than any previous key a `KeyExist` error + /// is returned and the key is not inserted into the database. + /// + /// # Safety + /// + /// It is _[undefined behavior]_ to keep a reference of a value from this database while + /// modifying it, so you can't use the key/value that comes from the cursor to feed + /// this function. + /// + /// In other words: Transform the key and value that you borrow from this database into an owned + /// version of them (e.g. `&str` into `String`). + /// + /// > [Values returned from the database are valid only until a subsequent update operation, + /// > or the end of the transaction.](http://www.lmdb.tech/doc/group__mdb.html#structMDB__val) + /// + /// [undefined behavior]: https://doc.rust-lang.org/reference/behavior-considered-undefined.html + pub unsafe fn put_current_with_flags( + &mut self, + flags: PutFlags, + key: &[u8], + data: &[u8], + ) -> Result<()> { + let mut key_val = crate::into_val(key); + let mut data_val = crate::into_val(data); + + // Modify the pointed data + let result = mdb_result(ffi::mdb_cursor_put( + self.cursor.cursor, + &mut key_val, + &mut data_val, + flags.bits(), + )); + + result.map_err(Into::into) + } +} + +impl<'txn> Deref for RwCursor<'txn, '_> { + type Target = CursorInner<'txn>; + + fn deref(&self) -> &Self::Target { + &self.cursor + } +} + +impl DerefMut for RwCursor<'_, '_> { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.cursor + } +} + +pub struct CursorInner<'txn> { + cursor: *mut ffi::MDB_cursor, + _marker: std::marker::PhantomData<&'txn ()>, +} + +impl<'txn> CursorInner<'txn> { + pub fn open(txn: *mut ffi::MDB_txn, dbi: ffi::MDB_dbi) -> Result> { let mut cursor: *mut ffi::MDB_cursor = ptr::null_mut(); - let mut txn = txn.txn_ptr(); - unsafe { mdb_result(ffi::mdb_cursor_open(txn.as_mut(), dbi, &mut cursor))? } - Ok(RoCursor { cursor, _marker: marker::PhantomData }) + unsafe { mdb_result(ffi::mdb_cursor_open(txn, dbi, &mut cursor))? }; + Ok(Self { cursor, _marker: std::marker::PhantomData }) } pub fn current(&mut self) -> Result> { @@ -237,186 +437,12 @@ impl<'txn> RoCursor<'txn> { } } -impl Drop for RoCursor<'_> { +impl Drop for CursorInner<'_> { fn drop(&mut self) { unsafe { ffi::mdb_cursor_close(self.cursor) } } } -pub struct RwCursor<'txn> { - cursor: RoCursor<'txn>, -} - -impl<'txn> RwCursor<'txn> { - pub(crate) fn open(txn: &'txn RwTxn, dbi: ffi::MDB_dbi) -> Result> { - Ok(RwCursor { cursor: RoCursor::open(txn, dbi)? }) - } - - /// Delete the entry the cursor is currently pointing to. - /// - /// Returns `true` if the entry was successfully deleted. - /// - /// # Safety - /// - /// It is _[undefined behavior]_ to keep a reference of a value from this database - /// while modifying it. - /// - /// > [Values returned from the database are valid only until a subsequent update operation, - /// > or the end of the transaction.](http://www.lmdb.tech/doc/group__mdb.html#structMDB__val) - /// - /// [undefined behavior]: https://doc.rust-lang.org/reference/behavior-considered-undefined.html - pub unsafe fn del_current(&mut self) -> Result { - // Delete the current entry - let result = mdb_result(ffi::mdb_cursor_del(self.cursor.cursor, 0)); - - match result { - Ok(()) => Ok(true), - Err(e) if e.not_found() => Ok(false), - Err(e) => Err(e.into()), - } - } - - /// Write a new value to the current entry. - /// - /// The given key **must** be equal to the one this cursor is pointing otherwise the database - /// can be put into an inconsistent state. - /// - /// Returns `true` if the entry was successfully written. - /// - /// > This is intended to be used when the new data is the same size as the old. - /// > Otherwise it will simply perform a delete of the old record followed by an insert. - /// - /// # Safety - /// - /// It is _[undefined behavior]_ to keep a reference of a value from this database while - /// modifying it, so you can't use the key/value that comes from the cursor to feed - /// this function. - /// - /// In other words: Transform the key and value that you borrow from this database into an owned - /// version of them (e.g. `&str` into `String`). - /// - /// > [Values returned from the database are valid only until a subsequent update operation, - /// > or the end of the transaction.](http://www.lmdb.tech/doc/group__mdb.html#structMDB__val) - /// - /// [undefined behavior]: https://doc.rust-lang.org/reference/behavior-considered-undefined.html - pub unsafe fn put_current(&mut self, key: &[u8], data: &[u8]) -> Result { - let mut key_val = crate::into_val(key); - let mut data_val = crate::into_val(data); - - // Modify the pointed data - let result = mdb_result(ffi::mdb_cursor_put( - self.cursor.cursor, - &mut key_val, - &mut data_val, - ffi::MDB_CURRENT, - )); - - match result { - Ok(()) => Ok(true), - Err(e) if e.not_found() => Ok(false), - Err(e) => Err(e.into()), - } - } - - /// Write a new value to the current entry. - /// - /// The given key **must** be equal to the one this cursor is pointing otherwise the database - /// can be put into an inconsistent state. - /// - /// Returns `true` if the entry was successfully written. - /// - /// > This is intended to be used when the new data is the same size as the old. - /// > Otherwise it will simply perform a delete of the old record followed by an insert. - /// - /// # Safety - /// - /// Please read the safety notes of the [`Self::put_current`] method. - pub unsafe fn put_current_reserved_with_flags( - &mut self, - flags: PutFlags, - key: &[u8], - data_size: usize, - write_func: F, - ) -> Result - where - F: FnOnce(&mut ReservedSpace) -> io::Result<()>, - { - let mut key_val = crate::into_val(key); - let mut reserved = ffi::reserve_size_val(data_size); - let flags = ffi::MDB_RESERVE | flags.bits(); - - let result = - mdb_result(ffi::mdb_cursor_put(self.cursor.cursor, &mut key_val, &mut reserved, flags)); - - let found = match result { - Ok(()) => true, - Err(e) if e.not_found() => false, - Err(e) => return Err(e.into()), - }; - - let mut reserved = ReservedSpace::from_val(reserved); - write_func(&mut reserved)?; - - if reserved.remaining() == 0 { - Ok(found) - } else { - Err(io::Error::from(io::ErrorKind::UnexpectedEof).into()) - } - } - - /// Append the given key/value pair to the end of the database. - /// - /// If a key is inserted that is less than any previous key a `KeyExist` error - /// is returned and the key is not inserted into the database. - /// - /// # Safety - /// - /// It is _[undefined behavior]_ to keep a reference of a value from this database while - /// modifying it, so you can't use the key/value that comes from the cursor to feed - /// this function. - /// - /// In other words: Transform the key and value that you borrow from this database into an owned - /// version of them (e.g. `&str` into `String`). - /// - /// > [Values returned from the database are valid only until a subsequent update operation, - /// > or the end of the transaction.](http://www.lmdb.tech/doc/group__mdb.html#structMDB__val) - /// - /// [undefined behavior]: https://doc.rust-lang.org/reference/behavior-considered-undefined.html - pub unsafe fn put_current_with_flags( - &mut self, - flags: PutFlags, - key: &[u8], - data: &[u8], - ) -> Result<()> { - let mut key_val = crate::into_val(key); - let mut data_val = crate::into_val(data); - - // Modify the pointed data - let result = mdb_result(ffi::mdb_cursor_put( - self.cursor.cursor, - &mut key_val, - &mut data_val, - flags.bits(), - )); - - result.map_err(Into::into) - } -} - -impl<'txn> Deref for RwCursor<'txn> { - type Target = RoCursor<'txn>; - - fn deref(&self) -> &Self::Target { - &self.cursor - } -} - -impl DerefMut for RwCursor<'_> { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.cursor - } -} - /// The way the `Iterator::next/prev` method behaves towards DUP data. #[derive(Debug, Clone, Copy)] pub enum MoveOperation { diff --git a/heed/src/databases/database.rs b/heed/src/databases/database.rs index b96c1f1c..fb4dfc58 100644 --- a/heed/src/databases/database.rs +++ b/heed/src/databases/database.rs @@ -1081,7 +1081,7 @@ impl Database { /// wtxn.commit()?; /// # Ok(()) } /// ``` - pub fn iter_mut<'txn>(&self, txn: &'txn mut RwTxn) -> Result> { + pub fn iter_mut<'txn, 'p>(&self, txn: &'txn mut RwTxn<'p>) -> Result> { assert_eq_env_db_txn!(self, txn); RwCursor::open(txn, self.dbi).map(|cursor| RwIter::new(cursor)) @@ -1185,7 +1185,10 @@ impl Database { /// wtxn.commit()?; /// # Ok(()) } /// ``` - pub fn rev_iter_mut<'txn>(&self, txn: &'txn mut RwTxn) -> Result> { + pub fn rev_iter_mut<'txn, 'p>( + &self, + txn: &'txn mut RwTxn<'p>, + ) -> Result> { assert_eq_env_db_txn!(self, txn); RwCursor::open(txn, self.dbi).map(|cursor| RwRevIter::new(cursor)) @@ -1326,11 +1329,11 @@ impl Database { /// wtxn.commit()?; /// # Ok(()) } /// ``` - pub fn range_mut<'a, 'txn, R>( + pub fn range_mut<'a, 'txn, 'p, R>( &self, - txn: &'txn mut RwTxn, + txn: &'txn mut RwTxn<'p>, range: &'a R, - ) -> Result> + ) -> Result> where KC: BytesEncode<'a>, R: RangeBounds, @@ -1499,11 +1502,11 @@ impl Database { /// wtxn.commit()?; /// # Ok(()) } /// ``` - pub fn rev_range_mut<'a, 'txn, R>( + pub fn rev_range_mut<'a, 'txn, 'p, R>( &self, - txn: &'txn mut RwTxn, + txn: &'txn mut RwTxn<'p>, range: &'a R, - ) -> Result> + ) -> Result> where KC: BytesEncode<'a>, R: RangeBounds, @@ -1654,11 +1657,11 @@ impl Database { /// wtxn.commit()?; /// # Ok(()) } /// ``` - pub fn prefix_iter_mut<'a, 'txn>( + pub fn prefix_iter_mut<'a, 'txn, 'p>( &self, - txn: &'txn mut RwTxn, + txn: &'txn mut RwTxn<'p>, prefix: &'a KC::EItem, - ) -> Result> + ) -> Result> where KC: BytesEncode<'a>, C: LexicographicComparator, @@ -1787,11 +1790,11 @@ impl Database { /// wtxn.commit()?; /// # Ok(()) } /// ``` - pub fn rev_prefix_iter_mut<'a, 'txn>( + pub fn rev_prefix_iter_mut<'a, 'txn, 'p>( &self, - txn: &'txn mut RwTxn, + txn: &'txn mut RwTxn<'p>, prefix: &'a KC::EItem, - ) -> Result> + ) -> Result> where KC: BytesEncode<'a>, C: LexicographicComparator, diff --git a/heed/src/iterator/iter.rs b/heed/src/iterator/iter.rs index ee4dd1d7..fb09b84b 100644 --- a/heed/src/iterator/iter.rs +++ b/heed/src/iterator/iter.rs @@ -212,19 +212,20 @@ impl fmt::Debug for RoIter<'_, KC, DC, IM> { unsafe impl Send for RoIter<'_, KC, DC, IM> {} /// A read-write iterator structure. -pub struct RwIter<'txn, KC, DC, IM = MoveThroughDuplicateValues> { - cursor: RwCursor<'txn>, +pub struct RwIter<'txn, 'p, KC, DC, IM = MoveThroughDuplicateValues> { + pub(crate) cursor: RwCursor<'txn, 'p>, move_on_first: bool, _phantom: marker::PhantomData<(KC, DC, IM)>, } -impl<'txn, KC, DC, IM> RwIter<'txn, KC, DC, IM> { - pub(crate) fn new(cursor: RwCursor<'txn>) -> RwIter<'txn, KC, DC, IM> { +impl<'txn, 'p, KC, DC, IM> RwIter<'txn, 'p, KC, DC, IM> { + pub(crate) fn new(cursor: RwCursor<'txn, 'p>) -> RwIter<'txn, 'p, KC, DC, IM> { RwIter { cursor, move_on_first: true, _phantom: marker::PhantomData } } - pub fn as_wtxn<'p>(&mut self) -> &mut RwTxn<'p> { - todo!() + pub fn as_wtxn(&mut self) -> &mut RwTxn<'p> { + /// TODO should the output lifetime be 'txn? + self.cursor.txn } /// Delete the entry the cursor is currently pointing to. @@ -344,7 +345,7 @@ impl<'txn, KC, DC, IM> RwIter<'txn, KC, DC, IM> { /// Move on the first value of keys, ignoring duplicate values. /// /// For more info, see [`RoIter::move_between_keys`]. - pub fn move_between_keys(self) -> RwIter<'txn, KC, DC, MoveBetweenKeys> { + pub fn move_between_keys(self) -> RwIter<'txn, 'p, KC, DC, MoveBetweenKeys> { RwIter { cursor: self.cursor, move_on_first: self.move_on_first, @@ -355,7 +356,9 @@ impl<'txn, KC, DC, IM> RwIter<'txn, KC, DC, IM> { /// Move through key/values entries and output duplicate values. /// /// For more info, see [`RoIter::move_through_duplicate_values`]. - pub fn move_through_duplicate_values(self) -> RwIter<'txn, KC, DC, MoveThroughDuplicateValues> { + pub fn move_through_duplicate_values( + self, + ) -> RwIter<'txn, 'p, KC, DC, MoveThroughDuplicateValues> { RwIter { cursor: self.cursor, move_on_first: self.move_on_first, @@ -364,7 +367,7 @@ impl<'txn, KC, DC, IM> RwIter<'txn, KC, DC, IM> { } /// Change the codec types of this iterator, specifying the codecs. - pub fn remap_types(self) -> RwIter<'txn, KC2, DC2, IM> { + pub fn remap_types(self) -> RwIter<'txn, 'p, KC2, DC2, IM> { RwIter { cursor: self.cursor, move_on_first: self.move_on_first, @@ -373,22 +376,22 @@ impl<'txn, KC, DC, IM> RwIter<'txn, KC, DC, IM> { } /// Change the key codec type of this iterator, specifying the new codec. - pub fn remap_key_type(self) -> RwIter<'txn, KC2, DC, IM> { + pub fn remap_key_type(self) -> RwIter<'txn, 'p, KC2, DC, IM> { self.remap_types::() } /// Change the data codec type of this iterator, specifying the new codec. - pub fn remap_data_type(self) -> RwIter<'txn, KC, DC2, IM> { + pub fn remap_data_type(self) -> RwIter<'txn, 'p, KC, DC2, IM> { self.remap_types::() } /// Wrap the data bytes into a lazy decoder. - pub fn lazily_decode_data(self) -> RwIter<'txn, KC, LazyDecode, IM> { + pub fn lazily_decode_data(self) -> RwIter<'txn, 'p, KC, LazyDecode, IM> { self.remap_types::>() } } -impl<'txn, KC, DC, IM> Iterator for RwIter<'txn, KC, DC, IM> +impl<'txn, KC, DC, IM> Iterator for RwIter<'txn, '_, KC, DC, IM> where KC: BytesDecode<'txn>, DC: BytesDecode<'txn>, @@ -438,7 +441,7 @@ where } } -impl fmt::Debug for RwIter<'_, KC, DC, IM> { +impl fmt::Debug for RwIter<'_, '_, KC, DC, IM> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("RwIter").finish() } @@ -564,14 +567,14 @@ impl fmt::Debug for RoRevIter<'_, KC, DC, IM> { unsafe impl Send for RoRevIter<'_, KC, DC, IM> {} /// A reverse read-write iterator structure. -pub struct RwRevIter<'txn, KC, DC, IM = MoveThroughDuplicateValues> { - cursor: RwCursor<'txn>, +pub struct RwRevIter<'txn, 'p, KC, DC, IM = MoveThroughDuplicateValues> { + cursor: RwCursor<'txn, 'p>, move_on_last: bool, _phantom: marker::PhantomData<(KC, DC, IM)>, } -impl<'txn, KC, DC, IM> RwRevIter<'txn, KC, DC, IM> { - pub(crate) fn new(cursor: RwCursor<'txn>) -> RwRevIter<'txn, KC, DC, IM> { +impl<'txn, 'p, KC, DC, IM> RwRevIter<'txn, 'p, KC, DC, IM> { + pub(crate) fn new(cursor: RwCursor<'txn, 'p>) -> RwRevIter<'txn, 'p, KC, DC, IM> { RwRevIter { cursor, move_on_last: true, _phantom: marker::PhantomData } } @@ -692,7 +695,7 @@ impl<'txn, KC, DC, IM> RwRevIter<'txn, KC, DC, IM> { /// Move on the first value of keys, ignoring duplicate values. /// /// For more info, see [`RoIter::move_between_keys`]. - pub fn move_between_keys(self) -> RwRevIter<'txn, KC, DC, MoveBetweenKeys> { + pub fn move_between_keys(self) -> RwRevIter<'txn, 'p, KC, DC, MoveBetweenKeys> { RwRevIter { cursor: self.cursor, move_on_last: self.move_on_last, @@ -705,7 +708,7 @@ impl<'txn, KC, DC, IM> RwRevIter<'txn, KC, DC, IM> { /// For more info, see [`RoIter::move_through_duplicate_values`]. pub fn move_through_duplicate_values( self, - ) -> RwRevIter<'txn, KC, DC, MoveThroughDuplicateValues> { + ) -> RwRevIter<'txn, 'p, KC, DC, MoveThroughDuplicateValues> { RwRevIter { cursor: self.cursor, move_on_last: self.move_on_last, @@ -714,7 +717,7 @@ impl<'txn, KC, DC, IM> RwRevIter<'txn, KC, DC, IM> { } /// Change the codec types of this iterator, specifying the codecs. - pub fn remap_types(self) -> RwRevIter<'txn, KC2, DC2, IM> { + pub fn remap_types(self) -> RwRevIter<'txn, 'p, KC2, DC2, IM> { RwRevIter { cursor: self.cursor, move_on_last: self.move_on_last, @@ -723,22 +726,22 @@ impl<'txn, KC, DC, IM> RwRevIter<'txn, KC, DC, IM> { } /// Change the key codec type of this iterator, specifying the new codec. - pub fn remap_key_type(self) -> RwRevIter<'txn, KC2, DC, IM> { + pub fn remap_key_type(self) -> RwRevIter<'txn, 'p, KC2, DC, IM> { self.remap_types::() } /// Change the data codec type of this iterator, specifying the new codec. - pub fn remap_data_type(self) -> RwRevIter<'txn, KC, DC2, IM> { + pub fn remap_data_type(self) -> RwRevIter<'txn, 'p, KC, DC2, IM> { self.remap_types::() } /// Wrap the data bytes into a lazy decoder. - pub fn lazily_decode_data(self) -> RwRevIter<'txn, KC, LazyDecode, IM> { + pub fn lazily_decode_data(self) -> RwRevIter<'txn, 'p, KC, LazyDecode, IM> { self.remap_types::>() } } -impl<'txn, KC, DC, IM> Iterator for RwRevIter<'txn, KC, DC, IM> +impl<'txn, KC, DC, IM> Iterator for RwRevIter<'txn, '_, KC, DC, IM> where KC: BytesDecode<'txn>, DC: BytesDecode<'txn>, @@ -788,7 +791,7 @@ where } } -impl fmt::Debug for RwRevIter<'_, KC, DC, IM> { +impl fmt::Debug for RwRevIter<'_, '_, KC, DC, IM> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("RwRevIter").finish() } diff --git a/heed/src/iterator/prefix.rs b/heed/src/iterator/prefix.rs index 61239a26..ced93e39 100644 --- a/heed/src/iterator/prefix.rs +++ b/heed/src/iterator/prefix.rs @@ -4,7 +4,7 @@ use std::marker; use heed_traits::LexicographicComparator; use types::LazyDecode; -use crate::cursor::MoveOperation; +use crate::cursor::{CursorInner, MoveOperation}; use crate::envs::DefaultComparator; use crate::iteration_method::{IterationMethod, MoveBetweenKeys, MoveThroughDuplicateValues}; use crate::*; @@ -48,7 +48,7 @@ fn retreat_prefix(bytes: &mut [u8]) -> bool { } fn move_on_prefix_end<'txn, C: LexicographicComparator>( - cursor: &mut RoCursor<'txn>, + cursor: &mut CursorInner<'txn>, prefix: &mut [u8], ) -> Result> { if advance_prefix::(prefix) { @@ -203,15 +203,18 @@ impl fmt::Debug for RoPrefix<'_, KC, DC, C, IM> { unsafe impl Send for RoPrefix<'_, WithoutTls, KC, DC, IM> {} /// A read-write prefix iterator structure. -pub struct RwPrefix<'txn, KC, DC, C = DefaultComparator, IM = MoveThroughDuplicateValues> { - cursor: RwCursor<'txn>, +pub struct RwPrefix<'txn, 'p, KC, DC, C = DefaultComparator, IM = MoveThroughDuplicateValues> { + cursor: RwCursor<'txn, 'p>, prefix: Vec, move_on_first: bool, _phantom: marker::PhantomData<(KC, DC, C, IM)>, } -impl<'txn, KC, DC, C, IM> RwPrefix<'txn, KC, DC, C, IM> { - pub(crate) fn new(cursor: RwCursor<'txn>, prefix: Vec) -> RwPrefix<'txn, KC, DC, C, IM> { +impl<'txn, 'p, KC, DC, C, IM> RwPrefix<'txn, 'p, KC, DC, C, IM> { + pub(crate) fn new( + cursor: RwCursor<'txn, 'p>, + prefix: Vec, + ) -> RwPrefix<'txn, 'p, KC, DC, C, IM> { RwPrefix { cursor, prefix, move_on_first: true, _phantom: marker::PhantomData } } @@ -332,7 +335,7 @@ impl<'txn, KC, DC, C, IM> RwPrefix<'txn, KC, DC, C, IM> { /// Move on the first value of keys, ignoring duplicate values. /// /// For more info, see [`RoIter::move_between_keys`]. - pub fn move_between_keys(self) -> RwPrefix<'txn, KC, DC, C, MoveBetweenKeys> { + pub fn move_between_keys(self) -> RwPrefix<'txn, 'p, KC, DC, C, MoveBetweenKeys> { RwPrefix { cursor: self.cursor, prefix: self.prefix, @@ -346,7 +349,7 @@ impl<'txn, KC, DC, C, IM> RwPrefix<'txn, KC, DC, C, IM> { /// For more info, see [`RoIter::move_through_duplicate_values`]. pub fn move_through_duplicate_values( self, - ) -> RwPrefix<'txn, KC, DC, C, MoveThroughDuplicateValues> { + ) -> RwPrefix<'txn, 'p, KC, DC, C, MoveThroughDuplicateValues> { RwPrefix { cursor: self.cursor, prefix: self.prefix, @@ -356,7 +359,7 @@ impl<'txn, KC, DC, C, IM> RwPrefix<'txn, KC, DC, C, IM> { } /// Change the codec types of this iterator, specifying the codecs. - pub fn remap_types(self) -> RwPrefix<'txn, KC2, DC2, C, IM> { + pub fn remap_types(self) -> RwPrefix<'txn, 'p, KC2, DC2, C, IM> { RwPrefix { cursor: self.cursor, prefix: self.prefix, @@ -366,22 +369,22 @@ impl<'txn, KC, DC, C, IM> RwPrefix<'txn, KC, DC, C, IM> { } /// Change the key codec type of this iterator, specifying the new codec. - pub fn remap_key_type(self) -> RwPrefix<'txn, KC2, DC, C, IM> { + pub fn remap_key_type(self) -> RwPrefix<'txn, 'p, KC2, DC, C, IM> { self.remap_types::() } /// Change the data codec type of this iterator, specifying the new codec. - pub fn remap_data_type(self) -> RwPrefix<'txn, KC, DC2, C, IM> { + pub fn remap_data_type(self) -> RwPrefix<'txn, 'p, KC, DC2, C, IM> { self.remap_types::() } /// Wrap the data bytes into a lazy decoder. - pub fn lazily_decode_data(self) -> RwPrefix<'txn, KC, LazyDecode, C, IM> { + pub fn lazily_decode_data(self) -> RwPrefix<'txn, 'p, KC, LazyDecode, C, IM> { self.remap_types::>() } } -impl<'txn, KC, DC, C, IM> Iterator for RwPrefix<'txn, KC, DC, C, IM> +impl<'txn, KC, DC, C, IM> Iterator for RwPrefix<'txn, '_, KC, DC, C, IM> where KC: BytesDecode<'txn>, DC: BytesDecode<'txn>, @@ -447,7 +450,7 @@ where } } -impl fmt::Debug for RwPrefix<'_, KC, DC, C, IM> { +impl fmt::Debug for RwPrefix<'_, '_, KC, DC, C, IM> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("RwPrefix").finish() } @@ -592,15 +595,18 @@ impl fmt::Debug for RoRevPrefix<'_, KC, DC, C, IM> { unsafe impl Send for RoRevPrefix<'_, WithoutTls, KC, DC, IM> {} /// A reverse read-write prefix iterator structure. -pub struct RwRevPrefix<'txn, KC, DC, C = DefaultComparator, IM = MoveThroughDuplicateValues> { - cursor: RwCursor<'txn>, +pub struct RwRevPrefix<'txn, 'p, KC, DC, C = DefaultComparator, IM = MoveThroughDuplicateValues> { + cursor: RwCursor<'txn, 'p>, prefix: Vec, move_on_last: bool, _phantom: marker::PhantomData<(KC, DC, C, IM)>, } -impl<'txn, KC, DC, C, IM> RwRevPrefix<'txn, KC, DC, C, IM> { - pub(crate) fn new(cursor: RwCursor<'txn>, prefix: Vec) -> RwRevPrefix<'txn, KC, DC, C, IM> { +impl<'txn, 'p, KC, DC, C, IM> RwRevPrefix<'txn, 'p, KC, DC, C, IM> { + pub(crate) fn new( + cursor: RwCursor<'txn, 'p>, + prefix: Vec, + ) -> RwRevPrefix<'txn, 'p, KC, DC, C, IM> { RwRevPrefix { cursor, prefix, move_on_last: true, _phantom: marker::PhantomData } } @@ -721,7 +727,7 @@ impl<'txn, KC, DC, C, IM> RwRevPrefix<'txn, KC, DC, C, IM> { /// Move on the first value of keys, ignoring duplicate values. /// /// For more info, see [`RoIter::move_between_keys`]. - pub fn move_between_keys(self) -> RwRevPrefix<'txn, KC, DC, C, MoveBetweenKeys> { + pub fn move_between_keys(self) -> RwRevPrefix<'txn, 'p, KC, DC, C, MoveBetweenKeys> { RwRevPrefix { cursor: self.cursor, prefix: self.prefix, @@ -735,7 +741,7 @@ impl<'txn, KC, DC, C, IM> RwRevPrefix<'txn, KC, DC, C, IM> { /// For more info, see [`RoIter::move_through_duplicate_values`]. pub fn move_through_duplicate_values( self, - ) -> RwRevPrefix<'txn, KC, DC, C, MoveThroughDuplicateValues> { + ) -> RwRevPrefix<'txn, 'p, KC, DC, C, MoveThroughDuplicateValues> { RwRevPrefix { cursor: self.cursor, prefix: self.prefix, @@ -745,7 +751,7 @@ impl<'txn, KC, DC, C, IM> RwRevPrefix<'txn, KC, DC, C, IM> { } /// Change the codec types of this iterator, specifying the codecs. - pub fn remap_types(self) -> RwRevPrefix<'txn, KC2, DC2, C, IM> { + pub fn remap_types(self) -> RwRevPrefix<'txn, 'p, KC2, DC2, C, IM> { RwRevPrefix { cursor: self.cursor, prefix: self.prefix, @@ -755,22 +761,22 @@ impl<'txn, KC, DC, C, IM> RwRevPrefix<'txn, KC, DC, C, IM> { } /// Change the key codec type of this iterator, specifying the new codec. - pub fn remap_key_type(self) -> RwRevPrefix<'txn, KC2, DC, C, IM> { + pub fn remap_key_type(self) -> RwRevPrefix<'txn, 'p, KC2, DC, C, IM> { self.remap_types::() } /// Change the data codec type of this iterator, specifying the new codec. - pub fn remap_data_type(self) -> RwRevPrefix<'txn, KC, DC2, C, IM> { + pub fn remap_data_type(self) -> RwRevPrefix<'txn, 'p, KC, DC2, C, IM> { self.remap_types::() } /// Wrap the data bytes into a lazy decoder. - pub fn lazily_decode_data(self) -> RwRevPrefix<'txn, KC, LazyDecode, C, IM> { + pub fn lazily_decode_data(self) -> RwRevPrefix<'txn, 'p, KC, LazyDecode, C, IM> { self.remap_types::>() } } -impl<'txn, KC, DC, C, IM> Iterator for RwRevPrefix<'txn, KC, DC, C, IM> +impl<'txn, KC, DC, C, IM> Iterator for RwRevPrefix<'txn, '_, KC, DC, C, IM> where KC: BytesDecode<'txn>, DC: BytesDecode<'txn>, @@ -835,7 +841,7 @@ where } } -impl fmt::Debug for RwRevPrefix<'_, KC, DC, C, IM> { +impl fmt::Debug for RwRevPrefix<'_, '_, KC, DC, C, IM> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("RwRevPrefix").finish() } diff --git a/heed/src/iterator/range.rs b/heed/src/iterator/range.rs index d0524a9d..f19de213 100644 --- a/heed/src/iterator/range.rs +++ b/heed/src/iterator/range.rs @@ -4,12 +4,12 @@ use std::ops::Bound; use types::LazyDecode; -use crate::cursor::MoveOperation; +use crate::cursor::{CursorInner, MoveOperation}; use crate::iteration_method::{IterationMethod, MoveBetweenKeys, MoveThroughDuplicateValues}; use crate::*; fn move_on_range_end<'txn>( - cursor: &mut RoCursor<'txn>, + cursor: &mut CursorInner<'txn>, end_bound: &Bound>, ) -> Result> { match end_bound { @@ -26,7 +26,7 @@ fn move_on_range_end<'txn>( } fn move_on_range_start<'txn>( - cursor: &mut RoCursor<'txn>, + cursor: &mut CursorInner<'txn>, start_bound: &mut Bound>, ) -> Result> { match start_bound { @@ -202,20 +202,20 @@ impl fmt::Debug for RoRange<'_, KC, DC, C, IM> { unsafe impl Send for RoRange<'_, KC, DC, C, IM> {} /// A read-write range iterator structure. -pub struct RwRange<'txn, KC, DC, C = DefaultComparator, IM = MoveThroughDuplicateValues> { - cursor: RwCursor<'txn>, +pub struct RwRange<'txn, 'p, KC, DC, C = DefaultComparator, IM = MoveThroughDuplicateValues> { + cursor: RwCursor<'txn, 'p>, move_on_start: bool, start_bound: Bound>, end_bound: Bound>, _phantom: marker::PhantomData<(KC, DC, C, IM)>, } -impl<'txn, KC, DC, C, IM> RwRange<'txn, KC, DC, C, IM> { +impl<'txn, 'p, KC, DC, C, IM> RwRange<'txn, 'p, KC, DC, C, IM> { pub(crate) fn new( - cursor: RwCursor<'txn>, + cursor: RwCursor<'txn, 'p>, start_bound: Bound>, end_bound: Bound>, - ) -> RwRange<'txn, KC, DC, C, IM> { + ) -> RwRange<'txn, 'p, KC, DC, C, IM> { RwRange { cursor, move_on_start: true, @@ -342,7 +342,7 @@ impl<'txn, KC, DC, C, IM> RwRange<'txn, KC, DC, C, IM> { /// Move on the first value of keys, ignoring duplicate values. /// /// For more info, see [`RoIter::move_between_keys`]. - pub fn move_between_keys(self) -> RwRange<'txn, KC, DC, C, MoveBetweenKeys> { + pub fn move_between_keys(self) -> RwRange<'txn, 'p, KC, DC, C, MoveBetweenKeys> { RwRange { cursor: self.cursor, move_on_start: self.move_on_start, @@ -357,7 +357,7 @@ impl<'txn, KC, DC, C, IM> RwRange<'txn, KC, DC, C, IM> { /// For more info, see [`RoIter::move_through_duplicate_values`]. pub fn move_through_duplicate_values( self, - ) -> RwRange<'txn, KC, DC, C, MoveThroughDuplicateValues> { + ) -> RwRange<'txn, 'p, KC, DC, C, MoveThroughDuplicateValues> { RwRange { cursor: self.cursor, move_on_start: self.move_on_start, @@ -368,7 +368,7 @@ impl<'txn, KC, DC, C, IM> RwRange<'txn, KC, DC, C, IM> { } /// Change the codec types of this iterator, specifying the codecs. - pub fn remap_types(self) -> RwRange<'txn, KC2, DC2, C, IM> { + pub fn remap_types(self) -> RwRange<'txn, 'p, KC2, DC2, C, IM> { RwRange { cursor: self.cursor, move_on_start: self.move_on_start, @@ -379,22 +379,22 @@ impl<'txn, KC, DC, C, IM> RwRange<'txn, KC, DC, C, IM> { } /// Change the key codec type of this iterator, specifying the new codec. - pub fn remap_key_type(self) -> RwRange<'txn, KC2, DC, C, IM> { + pub fn remap_key_type(self) -> RwRange<'txn, 'p, KC2, DC, C, IM> { self.remap_types::() } /// Change the data codec type of this iterator, specifying the new codec. - pub fn remap_data_type(self) -> RwRange<'txn, KC, DC2, C, IM> { + pub fn remap_data_type(self) -> RwRange<'txn, 'p, KC, DC2, C, IM> { self.remap_types::() } /// Wrap the data bytes into a lazy decoder. - pub fn lazily_decode_data(self) -> RwRange<'txn, KC, LazyDecode, C, IM> { + pub fn lazily_decode_data(self) -> RwRange<'txn, 'p, KC, LazyDecode, C, IM> { self.remap_types::>() } } -impl<'txn, KC, DC, C, IM> Iterator for RwRange<'txn, KC, DC, C, IM> +impl<'txn, KC, DC, C, IM> Iterator for RwRange<'txn, '_, KC, DC, C, IM> where KC: BytesDecode<'txn>, DC: BytesDecode<'txn>, @@ -469,7 +469,7 @@ where } } -impl fmt::Debug for RwRange<'_, KC, DC, C, IM> { +impl fmt::Debug for RwRange<'_, '_, KC, DC, C, IM> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("RwRange").finish() } @@ -640,20 +640,20 @@ impl fmt::Debug for RoRevRange<'_, KC, DC, C, IM> { unsafe impl Send for RoRevRange<'_, KC, DC, C, IM> {} /// A reverse read-write range iterator structure. -pub struct RwRevRange<'txn, KC, DC, C = DefaultComparator, IM = MoveThroughDuplicateValues> { - cursor: RwCursor<'txn>, +pub struct RwRevRange<'txn, 'p, KC, DC, C = DefaultComparator, IM = MoveThroughDuplicateValues> { + cursor: RwCursor<'txn, 'p>, move_on_end: bool, start_bound: Bound>, end_bound: Bound>, _phantom: marker::PhantomData<(KC, DC, C, IM)>, } -impl<'txn, KC, DC, C, IM> RwRevRange<'txn, KC, DC, C, IM> { +impl<'txn, 'p, KC, DC, C, IM> RwRevRange<'txn, 'p, KC, DC, C, IM> { pub(crate) fn new( - cursor: RwCursor<'txn>, + cursor: RwCursor<'txn, 'p>, start_bound: Bound>, end_bound: Bound>, - ) -> RwRevRange<'txn, KC, DC, C, IM> { + ) -> RwRevRange<'txn, 'p, KC, DC, C, IM> { RwRevRange { cursor, move_on_end: true, @@ -780,7 +780,7 @@ impl<'txn, KC, DC, C, IM> RwRevRange<'txn, KC, DC, C, IM> { /// Move on the first value of keys, ignoring duplicate values. /// /// For more info, see [`RoIter::move_between_keys`]. - pub fn move_between_keys(self) -> RwRevRange<'txn, KC, DC, C, MoveBetweenKeys> { + pub fn move_between_keys(self) -> RwRevRange<'txn, 'p, KC, DC, C, MoveBetweenKeys> { RwRevRange { cursor: self.cursor, move_on_end: self.move_on_end, @@ -795,7 +795,7 @@ impl<'txn, KC, DC, C, IM> RwRevRange<'txn, KC, DC, C, IM> { /// For more info, see [`RoIter::move_through_duplicate_values`]. pub fn move_through_duplicate_values( self, - ) -> RwRevRange<'txn, KC, DC, C, MoveThroughDuplicateValues> { + ) -> RwRevRange<'txn, 'p, KC, DC, C, MoveThroughDuplicateValues> { RwRevRange { cursor: self.cursor, move_on_end: self.move_on_end, @@ -806,7 +806,7 @@ impl<'txn, KC, DC, C, IM> RwRevRange<'txn, KC, DC, C, IM> { } /// Change the codec types of this iterator, specifying the codecs. - pub fn remap_types(self) -> RwRevRange<'txn, KC2, DC2, C, IM> { + pub fn remap_types(self) -> RwRevRange<'txn, 'p, KC2, DC2, C, IM> { RwRevRange { cursor: self.cursor, move_on_end: self.move_on_end, @@ -817,22 +817,22 @@ impl<'txn, KC, DC, C, IM> RwRevRange<'txn, KC, DC, C, IM> { } /// Change the key codec type of this iterator, specifying the new codec. - pub fn remap_key_type(self) -> RwRevRange<'txn, KC2, DC, C, IM> { + pub fn remap_key_type(self) -> RwRevRange<'txn, 'p, KC2, DC, C, IM> { self.remap_types::() } /// Change the data codec type of this iterator, specifying the new codec. - pub fn remap_data_type(self) -> RwRevRange<'txn, KC, DC2, C, IM> { + pub fn remap_data_type(self) -> RwRevRange<'txn, 'p, KC, DC2, C, IM> { self.remap_types::() } /// Wrap the data bytes into a lazy decoder. - pub fn lazily_decode_data(self) -> RwRevRange<'txn, KC, LazyDecode, C, IM> { + pub fn lazily_decode_data(self) -> RwRevRange<'txn, 'p, KC, LazyDecode, C, IM> { self.remap_types::>() } } -impl<'txn, KC, DC, C, IM> Iterator for RwRevRange<'txn, KC, DC, C, IM> +impl<'txn, KC, DC, C, IM> Iterator for RwRevRange<'txn, '_, KC, DC, C, IM> where KC: BytesDecode<'txn>, DC: BytesDecode<'txn>, @@ -909,7 +909,7 @@ where } } -impl fmt::Debug for RwRevRange<'_, KC, DC, C, IM> { +impl fmt::Debug for RwRevRange<'_, '_, KC, DC, C, IM> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("RwRevRange").finish() } From dec6a50f1e344041072a3c71db754e0adcbf1b69 Mon Sep 17 00:00:00 2001 From: Kerollmops Date: Wed, 5 Mar 2025 10:09:27 +0100 Subject: [PATCH 4/5] Introduce a test to check that transvasing entries works --- heed/src/cookbook.rs | 68 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) diff --git a/heed/src/cookbook.rs b/heed/src/cookbook.rs index ac0d3d9d..9843f079 100644 --- a/heed/src/cookbook.rs +++ b/heed/src/cookbook.rs @@ -2,6 +2,7 @@ //! //! - [Decode Values on Demand](#decode-values-on-demand) //! - [Listing and Opening the Named Databases](#listing-and-opening-the-named-databases) +//! - [Moving entries from one database to another](#moving-entries-from-one-database-to-another) //! - [Create Custom and Prefix Codecs](#create-custom-and-prefix-codecs) //! - [Change the Environment Size Dynamically](#change-the-environment-size-dynamically) //! - [Advanced Multithreaded Access of Entries](#advanced-multithreaded-access-of-entries) @@ -129,6 +130,73 @@ //! } //! ``` //! +//! # Moving entries from one database to another +//! +//! TBD +//! +//! ```rust,compile_fail +//! # use std::error::Error; +//! # use heed::types::*; +//! # use heed::{Database, EnvOpenOptions}; +//! # fn main() -> Result<(), Box> { +//! # let path = tempfile::tempdir()?; +//! # let env = unsafe { +//! # EnvOpenOptions::new() +//! # .map_size(1024 * 1024 * 100) // 100 MiB +//! # .open(&path)? +//! # }; +//! let mut wtxn = env.write_txn()?; +//! # let database1: Database = env.create_database(&mut wtxn, Some("database1"))?; +//! # let database2: Database = env.create_database(&mut wtxn, Some("database2"))?; +//! # for i in 0..10 { +//! # let key = format!("key{}", i); +//! # let value = format!("value{}", i); +//! # database1.put(&mut wtxn, &key, &value)?; +//! # } +//! for result in database1.iter(&wtxn)? { +//! let (k, v) = result?; +//! // compilation error: cannot use &mut and & of RwTxn simultaneously +//! database2.put(&mut wtxn, k, v)?; +//! } +//! # wtxn.commit()?; +//! # Ok(()) +//! # } +//! ``` +//! +//! TBD we use an iter_mut + as_wtxn and owned strings. Explain why this is necessary. +//! +//! ``` +//! # use std::error::Error; +//! # use heed::types::*; +//! # use heed::{Database, EnvOpenOptions}; +//! # fn main() -> Result<(), Box> { +//! # let path = tempfile::tempdir()?; +//! # let env = unsafe { +//! # EnvOpenOptions::new() +//! # .max_dbs(10) +//! # .map_size(1024 * 1024 * 100) // 100 MiB +//! # .open(&path)? +//! # }; +//! let mut wtxn = env.write_txn()?; +//! # let database1: Database = env.create_database(&mut wtxn, Some("database1"))?; +//! # let database2: Database = env.create_database(&mut wtxn, Some("database2"))?; +//! # for i in 0..10 { +//! # let key = format!("key{}", i); +//! # let value = format!("value{}", i); +//! # database1.put(&mut wtxn, &key, &value)?; +//! # } +//! let mut iter = database1.iter_mut(&mut wtxn)?; +//! while let Some((k, v)) = iter.next().transpose()? { +//! let ck = k.to_owned(); +//! let cv = v.to_owned(); +//! database2.put(iter.as_wtxn(), &ck, &cv)?; +//! } +//! # drop(iter); +//! # wtxn.commit()?; +//! # Ok(()) +//! # } +//! ``` +//! //! # Create Custom and Prefix Codecs //! //! With heed you can store any kind of data and serialize it the way you want. From 713d2bc4080bf86c4ea054674064cba3ee2d3bb7 Mon Sep 17 00:00:00 2001 From: Kerollmops Date: Wed, 5 Mar 2025 10:53:02 +0100 Subject: [PATCH 5/5] Introduce a cookbook test --- heed/src/iterator/iter.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/heed/src/iterator/iter.rs b/heed/src/iterator/iter.rs index fb09b84b..2c828b03 100644 --- a/heed/src/iterator/iter.rs +++ b/heed/src/iterator/iter.rs @@ -223,7 +223,7 @@ impl<'txn, 'p, KC, DC, IM> RwIter<'txn, 'p, KC, DC, IM> { RwIter { cursor, move_on_first: true, _phantom: marker::PhantomData } } - pub fn as_wtxn(&mut self) -> &mut RwTxn<'p> { + pub fn as_wtxn<'a: 'txn>(&'a mut self) -> &'txn mut RwTxn<'p> { /// TODO should the output lifetime be 'txn? self.cursor.txn }