Skip to content

Commit

Permalink
Add static lifetime versions of range() and multimap table get()
Browse files Browse the repository at this point in the history
These methods return reference counted iterators from read only
transactions
  • Loading branch information
cberner committed Jan 20, 2024
1 parent 4b8fc3e commit a6acb67
Show file tree
Hide file tree
Showing 5 changed files with 123 additions and 2 deletions.
3 changes: 1 addition & 2 deletions src/db.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,7 @@ use crate::tree_store::{
};
use crate::types::{RedbKey, RedbValue};
use crate::{
CompactionError, DatabaseError, Durability, ReadOnlyTable, ReadableTable, SavepointError,
StorageError,
CompactionError, DatabaseError, Durability, ReadOnlyTable, SavepointError, StorageError,
};
use crate::{ReadTransaction, Result, WriteTransaction};
use std::fmt::{Debug, Display, Formatter};
Expand Down
29 changes: 29 additions & 0 deletions src/multimap_table.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1296,6 +1296,35 @@ impl<'txn, K: RedbKey + 'static, V: RedbKey + 'static> ReadOnlyMultimapTable<'tx
_lifetime: Default::default(),
})
}

/// This method is like [`ReadableMultimapTable::get()`], but the iterator is reference counted and keeps the transaction
/// alive until it is dropped.
pub fn get<'a>(&self, key: impl Borrow<K::SelfType<'a>>) -> Result<MultimapValue<'static, V>> {
let iter = if let Some(collection) = self.tree.get(key.borrow())? {
DynamicCollection::iter(collection, self.transaction_guard.clone(), self.mem.clone())?
} else {
MultimapValue::new_subtree(
BtreeRangeIter::new::<RangeFull, &V::SelfType<'_>>(&(..), None, self.mem.clone())?,
self.transaction_guard.clone(),
)
};

Ok(iter)
}

/// This method is like [`ReadableMultimapTable::range()`], but the iterator is reference counted and keeps the transaction
/// alive until it is dropped.
pub fn range<'a, KR>(&self, range: impl RangeBounds<KR>) -> Result<MultimapRange<'static, K, V>>
where
KR: Borrow<K::SelfType<'a>>,
{
let inner = self.tree.range(&range)?;
Ok(MultimapRange::new(
inner,
self.transaction_guard.clone(),
self.mem.clone(),
))
}
}

impl<'txn, K: RedbKey + 'static, V: RedbKey + 'static> ReadableMultimapTable<K, V>
Expand Down
11 changes: 11 additions & 0 deletions src/table.rs
Original file line number Diff line number Diff line change
Expand Up @@ -439,6 +439,17 @@ impl<'txn, K: RedbKey + 'static, V: RedbValue + 'static> ReadOnlyTable<'txn, K,
_lifetime: Default::default(),
})
}

/// This method is like [`ReadableTable::range()`], but the iterator is reference counted and keeps the transaction
/// alive until it is dropped.
pub fn range<'a, KR>(&self, range: impl RangeBounds<KR>) -> Result<Range<'static, K, V>>
where
KR: Borrow<K::SelfType<'a>>,
{
self.tree
.range(&range)
.map(|x| Range::new(x, self.transaction_guard.clone()))
}
}

impl<'txn, K: RedbKey + 'static, V: RedbValue + 'static> ReadableTable<K, V>
Expand Down
24 changes: 24 additions & 0 deletions tests/basic_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1246,6 +1246,30 @@ fn range_lifetime() {
assert!(iter.next().is_none());
}

#[test]
fn range_arc() {
let tmpfile = create_tempfile();
let db = Database::create(tmpfile.path()).unwrap();

let definition: TableDefinition<&str, &str> = TableDefinition::new("x");

let write_txn = db.begin_write().unwrap();
{
let mut table = write_txn.open_table(definition).unwrap();
table.insert("hello", "world").unwrap();
}
write_txn.commit().unwrap();

let mut iter = {
let read_txn = db.begin_read().unwrap();
let table = read_txn.open_table(definition).unwrap();
let start = "hello".to_string();
table.range::<&str>(start.as_str()..).unwrap()
};
assert_eq!(iter.next().unwrap().unwrap().1.value(), "world");
assert!(iter.next().is_none());
}

#[test]
fn drain_lifetime() {
let tmpfile = create_tempfile();
Expand Down
58 changes: 58 additions & 0 deletions tests/multimap_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,64 @@ fn range_lifetime() {
assert!(iter.next().is_none());
}

#[test]
fn range_arc_lifetime() {
let tmpfile = create_tempfile();
let db = Database::create(tmpfile.path()).unwrap();

let definition: MultimapTableDefinition<&str, &str> = MultimapTableDefinition::new("x");

let write_txn = db.begin_write().unwrap();
{
let mut table = write_txn.open_multimap_table(definition).unwrap();
table.insert("hello", "world").unwrap();
}
write_txn.commit().unwrap();

let mut iter = {
let read_txn = db.begin_read().unwrap();
let table = read_txn.open_multimap_table(definition).unwrap();
let start = "hello".to_string();
table.range::<&str>(start.as_str()..).unwrap()
};
assert_eq!(
iter.next()
.unwrap()
.unwrap()
.1
.next()
.unwrap()
.unwrap()
.value(),
"world"
);
assert!(iter.next().is_none());
}

#[test]
fn get_arc_lifetime() {
let tmpfile = create_tempfile();
let db = Database::create(tmpfile.path()).unwrap();

let definition: MultimapTableDefinition<&str, &str> = MultimapTableDefinition::new("x");

let write_txn = db.begin_write().unwrap();
{
let mut table = write_txn.open_multimap_table(definition).unwrap();
table.insert("hello", "world").unwrap();
}
write_txn.commit().unwrap();

let mut iter = {
let read_txn = db.begin_read().unwrap();
let table = read_txn.open_multimap_table(definition).unwrap();
let start = "hello".to_string();
table.get(start.as_str()).unwrap()
};
assert_eq!(iter.next().unwrap().unwrap().value(), "world");
assert!(iter.next().is_none());
}

#[test]
fn delete() {
let tmpfile = create_tempfile();
Expand Down

0 comments on commit a6acb67

Please sign in to comment.