Skip to content

Commit

Permalink
Add range_arc() and get_arc()
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 Dec 22, 2023
1 parent 4080f98 commit 8677438
Show file tree
Hide file tree
Showing 5 changed files with 185 additions and 3 deletions.
6 changes: 3 additions & 3 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,11 +62,11 @@ pub use error::{
TransactionError,
};
pub use multimap_table::{
MultimapRange, MultimapTable, MultimapValue, ReadOnlyMultimapTable,
ReadOnlyUntypedMultimapTable, ReadableMultimapTable,
ArcMultimapRange, ArcMultimapValue, MultimapRange, MultimapTable, MultimapValue,
ReadOnlyMultimapTable, ReadOnlyUntypedMultimapTable, ReadableMultimapTable,
};
pub use table::{
Drain, DrainFilter, Range, ReadOnlyTable, ReadOnlyUntypedTable, ReadableTable, Table,
ArcRange, Drain, DrainFilter, Range, ReadOnlyTable, ReadOnlyUntypedTable, ReadableTable, Table,
TableStats,
};
pub use transactions::{DatabaseStats, Durability, ReadTransaction, WriteTransaction};
Expand Down
70 changes: 70 additions & 0 deletions src/multimap_table.rs
Original file line number Diff line number Diff line change
Expand Up @@ -566,6 +566,24 @@ enum ValueIterState<'a, V: RedbKey + 'static> {
InlineLeaf(LeafKeyIter<'a, V>),
}

pub struct ArcMultimapValue<V: RedbKey + 'static> {
inner: MultimapValue<'static, V>,
}

impl<V: RedbKey + 'static> Iterator for ArcMultimapValue<V> {
type Item = <MultimapValue<'static, V> as Iterator>::Item;

fn next(&mut self) -> Option<Self::Item> {
self.inner.next()
}
}

impl<V: RedbKey + 'static> DoubleEndedIterator for ArcMultimapValue<V> {
fn next_back(&mut self) -> Option<Self::Item> {
self.inner.next_back()
}
}

pub struct MultimapValue<'a, V: RedbKey + 'static> {
inner: Option<ValueIterState<'a, V>>,
freed_pages: Option<Arc<Mutex<Vec<PageNumber>>>>,
Expand Down Expand Up @@ -665,6 +683,24 @@ impl<'a, V: RedbKey + 'static> Drop for MultimapValue<'a, V> {
}
}

pub struct ArcMultimapRange<K: RedbKey + 'static, V: RedbKey + 'static> {
inner: MultimapRange<'static, K, V>,
}

impl<K: RedbKey + 'static, V: RedbKey + 'static> Iterator for ArcMultimapRange<K, V> {
type Item = <MultimapRange<'static, K, V> as Iterator>::Item;

fn next(&mut self) -> Option<Self::Item> {
self.inner.next()
}
}

impl<K: RedbKey + 'static, V: RedbKey + 'static> DoubleEndedIterator for ArcMultimapRange<K, V> {
fn next_back(&mut self) -> Option<Self::Item> {
self.inner.next_back()
}
}

pub struct MultimapRange<'a, K: RedbKey + 'static, V: RedbKey + 'static> {
inner: BtreeRangeIter<K, &'static DynamicCollection<V>>,
mem: Arc<TransactionalMemory>,
Expand Down Expand Up @@ -1296,6 +1332,40 @@ impl<'txn, K: RedbKey + 'static, V: RedbKey + 'static> ReadOnlyMultimapTable<'tx
_lifetime: Default::default(),
})
}

/// This method is like `get()`, but the iterator is reference counted and keeps the transaction
/// alive until it is dropped.
pub fn get_arc<'a>(&self, key: impl Borrow<K::SelfType<'a>>) -> Result<ArcMultimapValue<V>>
where
K: 'a,
{
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(ArcMultimapValue { inner: iter })
}

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

Ok(ArcMultimapRange { inner: range })
}
}

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

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

impl<'txn, K: RedbKey + 'static, V: RedbValue + 'static> ReadableTable<K, V>
Expand Down Expand Up @@ -594,6 +606,24 @@ impl<
}
}

pub struct ArcRange<K: RedbKey + 'static, V: RedbValue + 'static> {
inner: Range<'static, K, V>,
}

impl<K: RedbKey + 'static, V: RedbValue + 'static> Iterator for ArcRange<K, V> {
type Item = Result<(AccessGuard<'static, K>, AccessGuard<'static, V>)>;

fn next(&mut self) -> Option<Self::Item> {
self.inner.next()
}
}

impl<K: RedbKey + 'static, V: RedbValue + 'static> DoubleEndedIterator for ArcRange<K, V> {
fn next_back(&mut self) -> Option<Self::Item> {
self.inner.next_back()
}
}

pub struct Range<'a, K: RedbKey + 'static, V: RedbValue + 'static> {
inner: BtreeRangeIter<K, V>,
_transaction_guard: Arc<TransactionGuard>,
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_arc::<&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_arc::<&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_arc(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 8677438

Please sign in to comment.