#![allow(missing_docs)]
mod common;
use common::{TestRoTxn, TestRwTxn, V1Factory, V2Factory};
use signet_libmdbx::{
Cursor, DatabaseFlags, DupItem, Environment, MdbxError, MdbxResult, ObjectLength, ReadResult,
TransactionKind, WriteFlags,
};
use std::{borrow::Cow, hint::black_box};
use tempfile::tempdir;
type Result<T> = ReadResult<T>;
fn collect_dup_items<K: Clone, V>(
iter: impl Iterator<Item = ReadResult<DupItem<K, V>>>,
) -> ReadResult<Vec<(K, V)>> {
let mut current_key: Option<K> = None;
iter.map(|r| {
let item = r?;
match item {
DupItem::NewKey(k, v) => {
current_key = Some(k.clone());
Ok((k, v))
}
DupItem::SameKey(v) => {
let k = current_key.clone().expect("SameKey without prior NewKey");
Ok((k, v))
}
}
})
.collect()
}
fn test_get_impl<RwTx, RoTx>(
begin_rw: impl Fn(&Environment) -> MdbxResult<RwTx>,
_begin_ro: impl Fn(&Environment) -> MdbxResult<RoTx>,
) where
RwTx: TestRwTxn,
RoTx: TestRoTxn,
{
let dir = tempdir().unwrap();
let env = Environment::builder().open(dir.path()).unwrap();
let txn = begin_rw(&env).unwrap();
let db = txn.open_db(None).unwrap();
assert_eq!(None, txn.cursor(db).unwrap().first::<(), ()>().unwrap());
txn.put(db, b"key1", b"val1", WriteFlags::empty()).unwrap();
txn.put(db, b"key2", b"val2", WriteFlags::empty()).unwrap();
txn.put(db, b"key3", b"val3", WriteFlags::empty()).unwrap();
let mut cursor = txn.cursor(db).unwrap();
assert_eq!(cursor.first().unwrap(), Some((*b"key1", *b"val1")));
assert_eq!(cursor.get_current().unwrap(), Some((*b"key1", *b"val1")));
assert_eq!(cursor.next().unwrap(), Some((*b"key2", *b"val2")));
assert_eq!(cursor.prev().unwrap(), Some((*b"key1", *b"val1")));
assert_eq!(cursor.last().unwrap(), Some((*b"key3", *b"val3")));
assert_eq!(cursor.set(b"key1").unwrap(), Some(*b"val1"));
assert_eq!(cursor.set_key(b"key3").unwrap(), Some((*b"key3", *b"val3")));
assert_eq!(cursor.set_range(b"key2\0").unwrap(), Some((*b"key3", *b"val3")));
}
#[test]
fn test_get_v1() {
test_get_impl(V1Factory::begin_rw, V1Factory::begin_ro);
}
#[test]
fn test_get_v2() {
test_get_impl(V2Factory::begin_rw, V2Factory::begin_ro);
}
fn test_get_dup_impl<RwTx, RoTx>(
begin_rw: impl Fn(&Environment) -> MdbxResult<RwTx>,
_begin_ro: impl Fn(&Environment) -> MdbxResult<RoTx>,
) where
RwTx: TestRwTxn,
RoTx: TestRoTxn,
{
let dir = tempdir().unwrap();
let env = Environment::builder().open(dir.path()).unwrap();
let txn = begin_rw(&env).unwrap();
let db = txn.create_db(None, DatabaseFlags::DUP_SORT).unwrap();
txn.put(db, b"key1", b"val1", WriteFlags::empty()).unwrap();
txn.put(db, b"key1", b"val2", WriteFlags::empty()).unwrap();
txn.put(db, b"key1", b"val3", WriteFlags::empty()).unwrap();
txn.put(db, b"key2", b"val1", WriteFlags::empty()).unwrap();
txn.put(db, b"key2", b"val2", WriteFlags::empty()).unwrap();
txn.put(db, b"key2", b"val3", WriteFlags::empty()).unwrap();
let mut cursor = txn.cursor(db).unwrap();
assert_eq!(cursor.first().unwrap(), Some((*b"key1", *b"val1")));
assert_eq!(cursor.first_dup().unwrap(), Some(*b"val1"));
assert_eq!(cursor.get_current().unwrap(), Some((*b"key1", *b"val1")));
assert_eq!(cursor.next_nodup().unwrap(), Some((*b"key2", *b"val1")));
assert_eq!(cursor.next().unwrap(), Some((*b"key2", *b"val2")));
assert_eq!(cursor.prev().unwrap(), Some((*b"key2", *b"val1")));
assert_eq!(cursor.next_dup().unwrap(), Some((*b"key2", *b"val2")));
assert_eq!(cursor.next_dup().unwrap(), Some((*b"key2", *b"val3")));
assert_eq!(cursor.next_dup::<(), ()>().unwrap(), None);
assert_eq!(cursor.prev_dup().unwrap(), Some((*b"key2", *b"val2")));
assert_eq!(cursor.last_dup().unwrap(), Some(*b"val3"));
assert_eq!(cursor.prev_nodup().unwrap(), Some((*b"key1", *b"val3")));
assert_eq!(cursor.next_dup::<(), ()>().unwrap(), None);
assert_eq!(cursor.set(b"key1").unwrap(), Some(*b"val1"));
assert_eq!(cursor.set(b"key2").unwrap(), Some(*b"val1"));
assert_eq!(cursor.set_range(b"key1\0").unwrap(), Some((*b"key2", *b"val1")));
assert_eq!(cursor.get_both(b"key1", b"val3").unwrap(), Some(*b"val3"));
assert_eq!(cursor.get_both_range::<()>(b"key1", b"val4").unwrap(), None);
assert_eq!(cursor.get_both_range(b"key2", b"val").unwrap(), Some(*b"val1"));
assert_eq!(cursor.last().unwrap(), Some((*b"key2", *b"val3")));
cursor.del().unwrap();
assert_eq!(cursor.last().unwrap(), Some((*b"key2", *b"val2")));
cursor.del().unwrap();
assert_eq!(cursor.last().unwrap(), Some((*b"key2", *b"val1")));
cursor.del().unwrap();
assert_eq!(cursor.last().unwrap(), Some((*b"key1", *b"val3")));
}
#[test]
fn test_get_dup_v1() {
test_get_dup_impl(V1Factory::begin_rw, V1Factory::begin_ro);
}
#[test]
fn test_get_dup_v2() {
test_get_dup_impl(V2Factory::begin_rw, V2Factory::begin_ro);
}
fn test_get_dupfixed_impl<RwTx, RoTx>(
begin_rw: impl Fn(&Environment) -> MdbxResult<RwTx>,
_begin_ro: impl Fn(&Environment) -> MdbxResult<RoTx>,
) where
RwTx: TestRwTxn,
RoTx: TestRoTxn,
{
let dir = tempdir().unwrap();
let env = Environment::builder().open(dir.path()).unwrap();
let txn = begin_rw(&env).unwrap();
let db = txn.create_db(None, DatabaseFlags::DUP_SORT | DatabaseFlags::DUP_FIXED).unwrap();
txn.put(db, b"key1", b"val1", WriteFlags::empty()).unwrap();
txn.put(db, b"key1", b"val2", WriteFlags::empty()).unwrap();
txn.put(db, b"key1", b"val3", WriteFlags::empty()).unwrap();
txn.put(db, b"key2", b"val4", WriteFlags::empty()).unwrap();
txn.put(db, b"key2", b"val5", WriteFlags::empty()).unwrap();
txn.put(db, b"key2", b"val6", WriteFlags::empty()).unwrap();
let mut cursor = txn.cursor(db).unwrap();
assert_eq!(cursor.first().unwrap(), Some((*b"key1", *b"val1")));
assert_eq!(cursor.get_multiple().unwrap(), Some(*b"val1val2val3"));
assert_eq!(cursor.next_multiple::<(), ()>().unwrap(), None);
}
#[test]
fn test_get_dupfixed_v1() {
test_get_dupfixed_impl(V1Factory::begin_rw, V1Factory::begin_ro);
}
#[test]
fn test_get_dupfixed_v2() {
test_get_dupfixed_impl(V2Factory::begin_rw, V2Factory::begin_ro);
}
fn test_iter_impl<RwTx, RoTx>(
begin_rw: impl Fn(&Environment) -> MdbxResult<RwTx>,
begin_ro: impl Fn(&Environment) -> MdbxResult<RoTx>,
) where
RwTx: TestRwTxn,
RoTx: TestRoTxn,
{
let dir = tempdir().unwrap();
let env = Environment::builder().open(dir.path()).unwrap();
let items: Vec<(_, _)> = vec![
(*b"key1", *b"val1"),
(*b"key2", *b"val2"),
(*b"key3", *b"val3"),
(*b"key5", *b"val5"),
];
{
let txn = begin_rw(&env).unwrap();
let db = txn.open_db(None).unwrap();
for (key, data) in &items {
txn.put(db, key, data, WriteFlags::empty()).unwrap();
}
txn.commit().unwrap();
}
let txn = begin_ro(&env).unwrap();
let db = txn.open_db(None).unwrap();
let mut cursor = txn.cursor(db).unwrap();
assert_eq!(items, cursor.iter().collect::<Result<Vec<_>>>().unwrap());
let retr: Result<Vec<_>> = cursor.iter_start().unwrap().collect();
assert_eq!(items, retr.unwrap());
cursor.set::<()>(b"key2").unwrap();
assert_eq!(
items.clone().into_iter().skip(2).collect::<Vec<_>>(),
cursor.iter().collect::<Result<Vec<_>>>().unwrap()
);
assert_eq!(items, cursor.iter_start().unwrap().collect::<Result<Vec<_>>>().unwrap());
assert_eq!(
items.clone().into_iter().skip(1).collect::<Vec<_>>(),
cursor.iter_from(b"key2").unwrap().collect::<Result<Vec<_>>>().unwrap()
);
assert_eq!(
items.into_iter().skip(3).collect::<Vec<_>>(),
cursor.iter_from(b"key4").unwrap().collect::<Result<Vec<_>>>().unwrap()
);
assert_eq!(
Vec::<((), ())>::new(),
cursor.iter_from(b"key6").unwrap().collect::<Result<Vec<_>>>().unwrap()
);
}
#[test]
fn test_iter_v1() {
test_iter_impl(V1Factory::begin_rw, V1Factory::begin_ro);
}
#[test]
fn test_iter_v2() {
test_iter_impl(V2Factory::begin_rw, V2Factory::begin_ro);
}
fn test_iter_empty_database_impl<RwTx, RoTx>(
_begin_rw: impl Fn(&Environment) -> MdbxResult<RwTx>,
begin_ro: impl Fn(&Environment) -> MdbxResult<RoTx>,
) where
RwTx: TestRwTxn,
RoTx: TestRoTxn,
{
let dir = tempdir().unwrap();
let env = Environment::builder().open(dir.path()).unwrap();
let txn = begin_ro(&env).unwrap();
let db = txn.open_db(None).unwrap();
let mut cursor = txn.cursor(db).unwrap();
assert!(cursor.iter::<(), ()>().next().is_none());
assert!(cursor.iter_start::<(), ()>().unwrap().next().is_none());
assert!(cursor.iter_from::<(), ()>(b"foo").unwrap().next().is_none());
}
#[test]
fn test_iter_empty_database_v1() {
test_iter_empty_database_impl(V1Factory::begin_rw, V1Factory::begin_ro);
}
#[test]
fn test_iter_empty_database_v2() {
test_iter_empty_database_impl(V2Factory::begin_rw, V2Factory::begin_ro);
}
fn test_iter_empty_dup_database_impl<RwTx, RoTx>(
begin_rw: impl Fn(&Environment) -> MdbxResult<RwTx>,
begin_ro: impl Fn(&Environment) -> MdbxResult<RoTx>,
) where
RwTx: TestRwTxn,
RoTx: TestRoTxn,
{
let dir = tempdir().unwrap();
let env = Environment::builder().open(dir.path()).unwrap();
let txn = begin_rw(&env).unwrap();
txn.create_db(None, DatabaseFlags::DUP_SORT).unwrap();
txn.commit().unwrap();
let txn = begin_ro(&env).unwrap();
let db = txn.open_db(None).unwrap();
let mut cursor = txn.cursor(db).unwrap();
assert!(cursor.iter::<(), ()>().next().is_none());
assert!(cursor.iter_start::<(), ()>().unwrap().next().is_none());
assert!(cursor.iter_from::<(), ()>(b"foo").unwrap().next().is_none());
assert!(cursor.iter_from::<(), ()>(b"foo").unwrap().next().is_none());
assert!(cursor.iter_dup::<(), ()>().next().is_none());
assert!(cursor.iter_dup_start::<(), ()>().unwrap().next().is_none());
assert!(cursor.iter_dup_from::<(), ()>(b"foo").unwrap().next().is_none());
assert!(cursor.iter_dup_of::<()>(b"foo").unwrap().next().is_none());
}
#[test]
fn test_iter_empty_dup_database_v1() {
test_iter_empty_dup_database_impl(V1Factory::begin_rw, V1Factory::begin_ro);
}
#[test]
fn test_iter_empty_dup_database_v2() {
test_iter_empty_dup_database_impl(V2Factory::begin_rw, V2Factory::begin_ro);
}
fn test_iter_dup_impl<RwTx, RoTx>(
begin_rw: impl Fn(&Environment) -> MdbxResult<RwTx>,
begin_ro: impl Fn(&Environment) -> MdbxResult<RoTx>,
) where
RwTx: TestRwTxn,
RoTx: TestRoTxn,
{
let dir = tempdir().unwrap();
let env = Environment::builder().open(dir.path()).unwrap();
let txn = begin_rw(&env).unwrap();
txn.create_db(None, DatabaseFlags::DUP_SORT).unwrap();
txn.commit().unwrap();
let items: Vec<(_, _)> = [
(b"a", b"1"),
(b"a", b"2"),
(b"a", b"3"),
(b"b", b"1"),
(b"b", b"2"),
(b"b", b"3"),
(b"c", b"1"),
(b"c", b"2"),
(b"c", b"3"),
(b"e", b"1"),
(b"e", b"2"),
(b"e", b"3"),
]
.iter()
.map(|&(&k, &v)| (k, v))
.collect();
{
let txn = begin_rw(&env).unwrap();
for (key, data) in items.clone() {
let db = txn.open_db(None).unwrap();
txn.put(db, &key, &data, WriteFlags::empty()).unwrap();
}
txn.commit().unwrap();
}
let txn = begin_ro(&env).unwrap();
let db = txn.open_db(None).unwrap();
let mut cursor = txn.cursor(db).unwrap();
assert_eq!(items, collect_dup_items(cursor.iter_dup()).unwrap());
cursor.set::<()>(b"b").unwrap();
assert_eq!(
items.iter().copied().skip(4).collect::<Vec<_>>(),
collect_dup_items(cursor.iter_dup()).unwrap()
);
assert_eq!(items, collect_dup_items(cursor.iter_dup_start().unwrap()).unwrap());
assert_eq!(
items.iter().copied().skip(3).collect::<Vec<_>>(),
collect_dup_items(cursor.iter_dup_from(b"b").unwrap()).unwrap()
);
assert_eq!(
items.iter().copied().skip(3).collect::<Vec<_>>(),
collect_dup_items(cursor.iter_dup_from(b"ab").unwrap()).unwrap()
);
assert_eq!(
items.iter().copied().skip(9).collect::<Vec<_>>(),
collect_dup_items(cursor.iter_dup_from(b"d").unwrap()).unwrap()
);
assert_eq!(
Vec::<([u8; 1], [u8; 1])>::new(),
collect_dup_items(cursor.iter_dup_from(b"f").unwrap()).unwrap()
);
assert_eq!(
items.iter().copied().skip(3).take(3).map(|(_, v)| v).collect::<Vec<_>>(),
cursor.iter_dup_of::<[u8; 1]>(b"b").unwrap().collect::<Result<Vec<_>>>().unwrap()
);
assert_eq!(0, cursor.iter_dup_of::<()>(b"foo").unwrap().count());
}
#[test]
fn test_iter_dup_v1() {
test_iter_dup_impl(V1Factory::begin_rw, V1Factory::begin_ro);
}
#[test]
fn test_iter_dup_v2() {
test_iter_dup_impl(V2Factory::begin_rw, V2Factory::begin_ro);
}
fn test_iter_del_get_impl<RwTx, RoTx>(
begin_rw: impl Fn(&Environment) -> MdbxResult<RwTx>,
_begin_ro: impl Fn(&Environment) -> MdbxResult<RoTx>,
) where
RwTx: TestRwTxn,
RoTx: TestRoTxn,
{
let dir = tempdir().unwrap();
let env = Environment::builder().open(dir.path()).unwrap();
let items = vec![(*b"a", *b"1"), (*b"b", *b"2")];
{
let txn = begin_rw(&env).unwrap();
let db = txn.create_db(None, DatabaseFlags::DUP_SORT).unwrap();
assert_eq!(
txn.cursor(db)
.unwrap()
.iter_dup_of::<()>(b"a")
.unwrap()
.collect::<Result<Vec<_>>>()
.unwrap()
.len(),
0
);
txn.commit().unwrap();
}
{
let txn = begin_rw(&env).unwrap();
let db = txn.open_db(None).unwrap();
for (key, data) in &items {
txn.put(db, key, data, WriteFlags::empty()).unwrap();
}
txn.commit().unwrap();
}
let txn = begin_rw(&env).unwrap();
let db = txn.open_db(None).unwrap();
let mut cursor = txn.cursor(db).unwrap();
assert_eq!(items, collect_dup_items(cursor.iter_dup_start().unwrap()).unwrap());
assert_eq!(
items.iter().copied().take(1).map(|(_, v)| v).collect::<Vec<_>>(),
cursor.iter_dup_of::<[u8; 1]>(b"a").unwrap().collect::<Result<Vec<_>>>().unwrap()
);
assert_eq!(cursor.set(b"a").unwrap(), Some(*b"1"));
cursor.del().unwrap();
assert_eq!(
cursor.iter_dup_of::<[u8; 1]>(b"a").unwrap().collect::<Result<Vec<_>>>().unwrap().len(),
0
);
}
#[test]
fn test_iter_del_get_v1() {
test_iter_del_get_impl(V1Factory::begin_rw, V1Factory::begin_ro);
}
#[test]
fn test_iter_del_get_v2() {
test_iter_del_get_impl(V2Factory::begin_rw, V2Factory::begin_ro);
}
fn test_put_del_impl<RwTx, RoTx>(
begin_rw: impl Fn(&Environment) -> MdbxResult<RwTx>,
_begin_ro: impl Fn(&Environment) -> MdbxResult<RoTx>,
) where
RwTx: TestRwTxn,
RoTx: TestRoTxn,
{
let dir = tempdir().unwrap();
let env = Environment::builder().open(dir.path()).unwrap();
let txn = begin_rw(&env).unwrap();
let db = txn.open_db(None).unwrap();
let mut cursor = txn.cursor(db).unwrap();
cursor.put(b"key1", b"val1", WriteFlags::empty()).unwrap();
cursor.put(b"key2", b"val2", WriteFlags::empty()).unwrap();
cursor.put(b"key3", b"val3", WriteFlags::empty()).unwrap();
assert_eq!(
cursor.set_key(b"key2").unwrap(),
Some((Cow::Borrowed(b"key2" as &[u8]), Cow::Borrowed(b"val2" as &[u8])))
);
assert_eq!(
cursor.get_current().unwrap(),
Some((Cow::Borrowed(b"key2" as &[u8]), Cow::Borrowed(b"val2" as &[u8])))
);
cursor.del().unwrap();
assert_eq!(
cursor.get_current().unwrap(),
Some((Cow::Borrowed(b"key3" as &[u8]), Cow::Borrowed(b"val3" as &[u8])))
);
assert_eq!(
cursor.last().unwrap(),
Some((Cow::Borrowed(b"key3" as &[u8]), Cow::Borrowed(b"val3" as &[u8])))
);
}
#[test]
fn test_put_del_v1() {
test_put_del_impl(V1Factory::begin_rw, V1Factory::begin_ro);
}
#[test]
fn test_put_del_v2() {
test_put_del_impl(V2Factory::begin_rw, V2Factory::begin_ro);
}
fn test_dup_sort_methods_work_on_dupsort_db_impl<RwTx, RoTx>(
begin_rw: impl Fn(&Environment) -> MdbxResult<RwTx>,
_begin_ro: impl Fn(&Environment) -> MdbxResult<RoTx>,
) where
RwTx: TestRwTxn,
RoTx: TestRoTxn,
{
let dir = tempdir().unwrap();
let env = Environment::builder().open(dir.path()).unwrap();
let txn = begin_rw(&env).unwrap();
let db = txn.create_db(None, DatabaseFlags::DUP_SORT).unwrap();
txn.put(db, b"key1", b"val1", WriteFlags::empty()).unwrap();
txn.put(db, b"key1", b"val2", WriteFlags::empty()).unwrap();
let mut cursor = txn.cursor(db).unwrap();
cursor.first::<(), ()>().unwrap();
cursor.first_dup::<()>().unwrap();
cursor.last_dup::<()>().unwrap();
cursor.next_dup::<(), ()>().unwrap();
cursor.prev_dup::<(), ()>().unwrap();
cursor.get_both::<()>(b"key1", b"val1").unwrap();
cursor.get_both_range::<()>(b"key1", b"val").unwrap();
}
#[test]
fn test_dup_sort_methods_work_on_dupsort_db_v1() {
test_dup_sort_methods_work_on_dupsort_db_impl(V1Factory::begin_rw, V1Factory::begin_ro);
}
#[test]
fn test_dup_sort_methods_work_on_dupsort_db_v2() {
test_dup_sort_methods_work_on_dupsort_db_impl(V2Factory::begin_rw, V2Factory::begin_ro);
}
fn test_dup_fixed_methods_work_on_dupfixed_db_impl<RwTx, RoTx>(
begin_rw: impl Fn(&Environment) -> MdbxResult<RwTx>,
_begin_ro: impl Fn(&Environment) -> MdbxResult<RoTx>,
) where
RwTx: TestRwTxn,
RoTx: TestRoTxn,
{
let dir = tempdir().unwrap();
let env = Environment::builder().open(dir.path()).unwrap();
let txn = begin_rw(&env).unwrap();
let db = txn.create_db(None, DatabaseFlags::DUP_SORT | DatabaseFlags::DUP_FIXED).unwrap();
txn.put(db, b"key1", b"val1", WriteFlags::empty()).unwrap();
txn.put(db, b"key1", b"val2", WriteFlags::empty()).unwrap();
let mut cursor = txn.cursor(db).unwrap();
cursor.first::<(), ()>().unwrap();
cursor.get_multiple::<()>().unwrap();
cursor.next_multiple::<(), ()>().unwrap();
cursor.prev_multiple::<(), ()>().unwrap();
}
#[test]
fn test_dup_fixed_methods_work_on_dupfixed_db_v1() {
test_dup_fixed_methods_work_on_dupfixed_db_impl(V1Factory::begin_rw, V1Factory::begin_ro);
}
#[test]
fn test_dup_fixed_methods_work_on_dupfixed_db_v2() {
test_dup_fixed_methods_work_on_dupfixed_db_impl(V2Factory::begin_rw, V2Factory::begin_ro);
}
fn test_iter_exhausted_cursor_repositions_impl<RwTx, RoTx>(
begin_rw: impl Fn(&Environment) -> MdbxResult<RwTx>,
begin_ro: impl Fn(&Environment) -> MdbxResult<RoTx>,
) where
RwTx: TestRwTxn,
RoTx: TestRoTxn,
{
let dir = tempdir().unwrap();
let env = Environment::builder().open(dir.path()).unwrap();
let txn = begin_rw(&env).unwrap();
let db = txn.open_db(None).unwrap();
for i in 0u8..100 {
txn.put(db, &[i], &[i], WriteFlags::empty()).unwrap();
}
txn.commit().unwrap();
let txn = begin_ro(&env).unwrap();
let db = txn.open_db(None).unwrap();
let mut cursor = txn.cursor(db).unwrap();
let count1 = cursor.iter::<[u8; 1], [u8; 1]>().count();
assert_eq!(count1, 100);
assert!(cursor.is_eof());
let count2 = cursor.iter::<[u8; 1], [u8; 1]>().count();
assert_eq!(count2, 100);
assert_eq!(count1 + count2, 200);
}
#[test]
fn test_iter_exhausted_cursor_repositions_v1() {
test_iter_exhausted_cursor_repositions_impl(V1Factory::begin_rw, V1Factory::begin_ro);
}
#[test]
fn test_iter_exhausted_cursor_repositions_v2() {
test_iter_exhausted_cursor_repositions_impl(V2Factory::begin_rw, V2Factory::begin_ro);
}
fn test_iter_benchmark_pattern_impl<RwTx, RoTx>(
begin_rw: impl Fn(&Environment) -> MdbxResult<RwTx>,
begin_ro: impl Fn(&Environment) -> MdbxResult<RoTx>,
) where
RwTx: TestRwTxn,
RoTx: TestRoTxn,
{
let dir = tempdir().unwrap();
let env = Environment::builder().open(dir.path()).unwrap();
let n = 100u32;
let txn = begin_rw(&env).unwrap();
let db = txn.open_db(None).unwrap();
for i in 0..n {
let key = format!("key{i}");
let data = format!("data{i}");
txn.put(db, key.as_bytes(), data.as_bytes(), WriteFlags::empty()).unwrap();
}
txn.commit().unwrap();
let txn = begin_ro(&env).unwrap();
let db = txn.open_db(None).unwrap();
for _ in 0..3 {
let mut cursor = txn.cursor(db).unwrap();
let mut count = 0u32;
for (key_len, data_len) in cursor.iter::<ObjectLength, ObjectLength>().map(Result::unwrap) {
black_box(*key_len + *data_len);
count += 1;
}
for (key_len, data_len) in
cursor.iter::<ObjectLength, ObjectLength>().filter_map(Result::ok)
{
black_box(*key_len + *data_len);
count += 1;
}
fn iterate<K: TransactionKind>(cursor: &mut Cursor<K>) -> ReadResult<()> {
for result in cursor.iter::<ObjectLength, ObjectLength>() {
let (key_len, data_len) = result?;
black_box(*key_len + *data_len);
}
Ok(())
}
iterate(&mut cursor).unwrap();
assert_eq!(count, n * 2);
}
}
#[test]
fn test_iter_benchmark_pattern_v1() {
test_iter_benchmark_pattern_impl(V1Factory::begin_rw, V1Factory::begin_ro);
}
#[test]
fn test_iter_benchmark_pattern_v2() {
test_iter_benchmark_pattern_impl(V2Factory::begin_rw, V2Factory::begin_ro);
}
fn test_cursor_append_impl<RwTx, RoTx>(
begin_rw: impl Fn(&Environment) -> MdbxResult<RwTx>,
begin_ro: impl Fn(&Environment) -> MdbxResult<RoTx>,
) where
RwTx: TestRwTxn,
RoTx: TestRoTxn,
{
let dir = tempdir().unwrap();
let env = Environment::builder().open(dir.path()).unwrap();
let txn = begin_rw(&env).unwrap();
let db = txn.open_db(None).unwrap();
let mut cursor = txn.cursor(db).unwrap();
cursor.append(b"a", b"val_a").unwrap();
cursor.append(b"b", b"val_b").unwrap();
cursor.append(b"c", b"val_c").unwrap();
drop(cursor);
txn.commit().unwrap();
let txn = begin_ro(&env).unwrap();
let db = txn.open_db(None).unwrap();
let mut cursor = txn.cursor(db).unwrap();
assert_eq!(cursor.first().unwrap(), Some((*b"a", *b"val_a")));
assert_eq!(cursor.next().unwrap(), Some((*b"b", *b"val_b")));
assert_eq!(cursor.next().unwrap(), Some((*b"c", *b"val_c")));
assert_eq!(cursor.next::<(), ()>().unwrap(), None);
}
#[test]
fn test_cursor_append_v1() {
test_cursor_append_impl(V1Factory::begin_rw, V1Factory::begin_ro);
}
#[test]
fn test_cursor_append_v2() {
test_cursor_append_impl(V2Factory::begin_rw, V2Factory::begin_ro);
}
fn test_tx_append_impl<RwTx, RoTx>(
begin_rw: impl Fn(&Environment) -> MdbxResult<RwTx>,
begin_ro: impl Fn(&Environment) -> MdbxResult<RoTx>,
) where
RwTx: TestRwTxn,
RoTx: TestRoTxn,
{
let dir = tempdir().unwrap();
let env = Environment::builder().open(dir.path()).unwrap();
{
let txn = begin_rw(&env).unwrap();
let db = txn.open_db(None).unwrap();
txn.append(db, b"key1", b"val1").unwrap();
txn.append(db, b"key2", b"val2").unwrap();
txn.append(db, b"key3", b"val3").unwrap();
txn.commit().unwrap();
}
let txn = begin_ro(&env).unwrap();
let db = txn.open_db(None).unwrap();
let mut cursor = txn.cursor(db).unwrap();
assert_eq!(cursor.first().unwrap(), Some((*b"key1", *b"val1")));
assert_eq!(cursor.next().unwrap(), Some((*b"key2", *b"val2")));
assert_eq!(cursor.next().unwrap(), Some((*b"key3", *b"val3")));
}
#[test]
fn test_tx_append_v1() {
test_tx_append_impl(V1Factory::begin_rw, V1Factory::begin_ro);
}
#[test]
fn test_tx_append_v2() {
test_tx_append_impl(V2Factory::begin_rw, V2Factory::begin_ro);
}
fn test_append_dup_impl<RwTx, RoTx>(
begin_rw: impl Fn(&Environment) -> MdbxResult<RwTx>,
begin_ro: impl Fn(&Environment) -> MdbxResult<RoTx>,
) where
RwTx: TestRwTxn,
RoTx: TestRoTxn,
{
let dir = tempdir().unwrap();
let env = Environment::builder().open(dir.path()).unwrap();
let txn = begin_rw(&env).unwrap();
let db = txn.create_db(None, DatabaseFlags::DUP_SORT).unwrap();
let mut cursor = txn.cursor(db).unwrap();
cursor.append_dup(b"a", b"1").unwrap();
cursor.append_dup(b"a", b"2").unwrap();
cursor.append_dup(b"a", b"3").unwrap();
drop(cursor);
txn.commit().unwrap();
let txn = begin_ro(&env).unwrap();
let db = txn.open_db(None).unwrap();
let mut cursor = txn.cursor(db).unwrap();
assert_eq!(cursor.first().unwrap(), Some((*b"a", *b"1")));
assert_eq!(cursor.next_dup().unwrap(), Some((*b"a", *b"2")));
assert_eq!(cursor.next_dup().unwrap(), Some((*b"a", *b"3")));
assert_eq!(cursor.next_dup::<(), ()>().unwrap(), None);
}
#[test]
fn test_append_dup_v1() {
test_append_dup_impl(V1Factory::begin_rw, V1Factory::begin_ro);
}
#[test]
fn test_append_dup_v2() {
test_append_dup_impl(V2Factory::begin_rw, V2Factory::begin_ro);
}
fn test_iter_dupfixed_basic_impl<RwTx, RoTx>(
begin_rw: impl Fn(&Environment) -> MdbxResult<RwTx>,
begin_ro: impl Fn(&Environment) -> MdbxResult<RoTx>,
) where
RwTx: TestRwTxn,
RoTx: TestRoTxn,
{
let dir = tempdir().unwrap();
let env = Environment::builder().open(dir.path()).unwrap();
let txn = begin_rw(&env).unwrap();
let db = txn.create_db(None, DatabaseFlags::DUP_SORT | DatabaseFlags::DUP_FIXED).unwrap();
txn.put(db, b"key1", &1u32.to_le_bytes(), WriteFlags::empty()).unwrap();
txn.put(db, b"key1", &2u32.to_le_bytes(), WriteFlags::empty()).unwrap();
txn.put(db, b"key1", &3u32.to_le_bytes(), WriteFlags::empty()).unwrap();
txn.put(db, b"key2", &10u32.to_le_bytes(), WriteFlags::empty()).unwrap();
txn.put(db, b"key2", &20u32.to_le_bytes(), WriteFlags::empty()).unwrap();
txn.commit().unwrap();
let txn = begin_ro(&env).unwrap();
let db = txn.open_db(None).unwrap();
let mut cursor = txn.cursor(db).unwrap();
let results: Vec<(Vec<u8>, [u8; 4])> =
collect_dup_items(cursor.iter_dupfixed_start::<Vec<u8>, [u8; 4]>().unwrap()).unwrap();
assert_eq!(results.len(), 5);
assert_eq!(results[0], (b"key1".to_vec(), 1u32.to_le_bytes()));
assert_eq!(results[1], (b"key1".to_vec(), 2u32.to_le_bytes()));
assert_eq!(results[2], (b"key1".to_vec(), 3u32.to_le_bytes()));
assert_eq!(results[3], (b"key2".to_vec(), 10u32.to_le_bytes()));
assert_eq!(results[4], (b"key2".to_vec(), 20u32.to_le_bytes()));
}
#[test]
fn test_iter_dupfixed_basic_v1() {
test_iter_dupfixed_basic_impl(V1Factory::begin_rw, V1Factory::begin_ro);
}
#[test]
fn test_iter_dupfixed_basic_v2() {
test_iter_dupfixed_basic_impl(V2Factory::begin_rw, V2Factory::begin_ro);
}
fn test_iter_dupfixed_from_impl<RwTx, RoTx>(
begin_rw: impl Fn(&Environment) -> MdbxResult<RwTx>,
begin_ro: impl Fn(&Environment) -> MdbxResult<RoTx>,
) where
RwTx: TestRwTxn,
RoTx: TestRoTxn,
{
let dir = tempdir().unwrap();
let env = Environment::builder().open(dir.path()).unwrap();
let txn = begin_rw(&env).unwrap();
let db = txn.create_db(None, DatabaseFlags::DUP_SORT | DatabaseFlags::DUP_FIXED).unwrap();
txn.put(db, b"aaa", &1u32.to_le_bytes(), WriteFlags::empty()).unwrap();
txn.put(db, b"bbb", &2u32.to_le_bytes(), WriteFlags::empty()).unwrap();
txn.put(db, b"ccc", &3u32.to_le_bytes(), WriteFlags::empty()).unwrap();
txn.put(db, b"ddd", &4u32.to_le_bytes(), WriteFlags::empty()).unwrap();
txn.commit().unwrap();
let txn = begin_ro(&env).unwrap();
let db = txn.open_db(None).unwrap();
let mut cursor = txn.cursor(db).unwrap();
let results: Vec<(Vec<u8>, [u8; 4])> =
collect_dup_items(cursor.iter_dupfixed_from::<Vec<u8>, [u8; 4]>(b"bbb").unwrap()).unwrap();
assert_eq!(results.len(), 3);
assert_eq!(results[0], (b"bbb".to_vec(), 2u32.to_le_bytes()));
assert_eq!(results[1], (b"ccc".to_vec(), 3u32.to_le_bytes()));
assert_eq!(results[2], (b"ddd".to_vec(), 4u32.to_le_bytes()));
}
#[test]
fn test_iter_dupfixed_from_v1() {
test_iter_dupfixed_from_impl(V1Factory::begin_rw, V1Factory::begin_ro);
}
#[test]
fn test_iter_dupfixed_from_v2() {
test_iter_dupfixed_from_impl(V2Factory::begin_rw, V2Factory::begin_ro);
}
fn test_iter_dupfixed_empty_impl<RwTx, RoTx>(
begin_rw: impl Fn(&Environment) -> MdbxResult<RwTx>,
begin_ro: impl Fn(&Environment) -> MdbxResult<RoTx>,
) where
RwTx: TestRwTxn,
RoTx: TestRoTxn,
{
let dir = tempdir().unwrap();
let env = Environment::builder().open(dir.path()).unwrap();
let txn = begin_rw(&env).unwrap();
txn.create_db(None, DatabaseFlags::DUP_SORT | DatabaseFlags::DUP_FIXED).unwrap();
txn.commit().unwrap();
let txn = begin_ro(&env).unwrap();
let db = txn.open_db(None).unwrap();
let mut cursor = txn.cursor(db).unwrap();
let results: Vec<(Vec<u8>, [u8; 4])> =
collect_dup_items(cursor.iter_dupfixed_start::<Vec<u8>, [u8; 4]>().unwrap()).unwrap();
assert!(results.is_empty());
}
#[test]
fn test_iter_dupfixed_empty_v1() {
test_iter_dupfixed_empty_impl(V1Factory::begin_rw, V1Factory::begin_ro);
}
#[test]
fn test_iter_dupfixed_empty_v2() {
test_iter_dupfixed_empty_impl(V2Factory::begin_rw, V2Factory::begin_ro);
}
fn test_iter_dupfixed_many_values_impl<RwTx, RoTx>(
begin_rw: impl Fn(&Environment) -> MdbxResult<RwTx>,
begin_ro: impl Fn(&Environment) -> MdbxResult<RoTx>,
) where
RwTx: TestRwTxn,
RoTx: TestRoTxn,
{
use std::collections::HashSet;
let dir = tempdir().unwrap();
let env = Environment::builder().open(dir.path()).unwrap();
let txn = begin_rw(&env).unwrap();
let db = txn.create_db(None, DatabaseFlags::DUP_SORT | DatabaseFlags::DUP_FIXED).unwrap();
for i in 0u32..1000 {
txn.put(db, b"key", &i.to_le_bytes(), WriteFlags::empty()).unwrap();
}
txn.commit().unwrap();
let txn = begin_ro(&env).unwrap();
let db = txn.open_db(None).unwrap();
let mut cursor = txn.cursor(db).unwrap();
let results: Vec<(Vec<u8>, [u8; 4])> =
collect_dup_items(cursor.iter_dupfixed_start::<Vec<u8>, [u8; 4]>().unwrap()).unwrap();
assert_eq!(results.len(), 1000);
for (key, _) in &results {
assert_eq!(key, b"key");
}
let values: HashSet<u32> = results.iter().map(|(_, v)| u32::from_le_bytes(*v)).collect();
let expected: HashSet<u32> = (0u32..1000).collect();
assert_eq!(values, expected);
for window in results.windows(2) {
let v1 = &window[0].1;
let v2 = &window[1].1;
assert!(v1 <= v2, "values not in sorted order: {:?} > {:?}", v1, v2);
}
}
#[test]
fn test_iter_dupfixed_many_values_v1() {
test_iter_dupfixed_many_values_impl(V1Factory::begin_rw, V1Factory::begin_ro);
}
#[test]
fn test_iter_dupfixed_many_values_v2() {
test_iter_dupfixed_many_values_impl(V2Factory::begin_rw, V2Factory::begin_ro);
}
fn test_iter_dupfixed_from_nonexistent_key_impl<RwTx, RoTx>(
begin_rw: impl Fn(&Environment) -> MdbxResult<RwTx>,
begin_ro: impl Fn(&Environment) -> MdbxResult<RoTx>,
) where
RwTx: TestRwTxn,
RoTx: TestRoTxn,
{
let dir = tempdir().unwrap();
let env = Environment::builder().open(dir.path()).unwrap();
let txn = begin_rw(&env).unwrap();
let db = txn.create_db(None, DatabaseFlags::DUP_SORT | DatabaseFlags::DUP_FIXED).unwrap();
txn.put(db, b"aaa", &1u32.to_le_bytes(), WriteFlags::empty()).unwrap();
txn.put(db, b"ccc", &2u32.to_le_bytes(), WriteFlags::empty()).unwrap();
txn.commit().unwrap();
let txn = begin_ro(&env).unwrap();
let db = txn.open_db(None).unwrap();
let mut cursor = txn.cursor(db).unwrap();
let results: Vec<(Vec<u8>, [u8; 4])> =
collect_dup_items(cursor.iter_dupfixed_from::<Vec<u8>, [u8; 4]>(b"bbb").unwrap()).unwrap();
assert_eq!(results.len(), 1);
assert_eq!(results[0], (b"ccc".to_vec(), 2u32.to_le_bytes()));
}
#[test]
fn test_iter_dupfixed_from_nonexistent_key_v1() {
test_iter_dupfixed_from_nonexistent_key_impl(V1Factory::begin_rw, V1Factory::begin_ro);
}
#[test]
fn test_iter_dupfixed_from_nonexistent_key_v2() {
test_iter_dupfixed_from_nonexistent_key_impl(V2Factory::begin_rw, V2Factory::begin_ro);
}
fn test_iter_dupfixed_from_past_end_impl<RwTx, RoTx>(
begin_rw: impl Fn(&Environment) -> MdbxResult<RwTx>,
begin_ro: impl Fn(&Environment) -> MdbxResult<RoTx>,
) where
RwTx: TestRwTxn,
RoTx: TestRoTxn,
{
let dir = tempdir().unwrap();
let env = Environment::builder().open(dir.path()).unwrap();
let txn = begin_rw(&env).unwrap();
let db = txn.create_db(None, DatabaseFlags::DUP_SORT | DatabaseFlags::DUP_FIXED).unwrap();
txn.put(db, b"aaa", &1u32.to_le_bytes(), WriteFlags::empty()).unwrap();
txn.commit().unwrap();
let txn = begin_ro(&env).unwrap();
let db = txn.open_db(None).unwrap();
let mut cursor = txn.cursor(db).unwrap();
let results: Vec<(Vec<u8>, [u8; 4])> =
collect_dup_items(cursor.iter_dupfixed_from::<Vec<u8>, [u8; 4]>(b"zzz").unwrap()).unwrap();
assert!(results.is_empty());
}
#[test]
fn test_iter_dupfixed_from_past_end_v1() {
test_iter_dupfixed_from_past_end_impl(V1Factory::begin_rw, V1Factory::begin_ro);
}
#[test]
fn test_iter_dupfixed_from_past_end_v2() {
test_iter_dupfixed_from_past_end_impl(V2Factory::begin_rw, V2Factory::begin_ro);
}
fn test_iter_dupfixed_of_impl<RwTx, RoTx>(
begin_rw: impl Fn(&Environment) -> MdbxResult<RwTx>,
begin_ro: impl Fn(&Environment) -> MdbxResult<RoTx>,
) where
RwTx: TestRwTxn,
RoTx: TestRoTxn,
{
let dir = tempdir().unwrap();
let env = Environment::builder().open(dir.path()).unwrap();
let txn = begin_rw(&env).unwrap();
let db = txn.create_db(None, DatabaseFlags::DUP_SORT | DatabaseFlags::DUP_FIXED).unwrap();
txn.put(db, b"key1", &1u32.to_le_bytes(), WriteFlags::empty()).unwrap();
txn.put(db, b"key1", &2u32.to_le_bytes(), WriteFlags::empty()).unwrap();
txn.put(db, b"key1", &3u32.to_le_bytes(), WriteFlags::empty()).unwrap();
txn.put(db, b"key2", &10u32.to_le_bytes(), WriteFlags::empty()).unwrap();
txn.put(db, b"key2", &20u32.to_le_bytes(), WriteFlags::empty()).unwrap();
txn.put(db, b"key3", &100u32.to_le_bytes(), WriteFlags::empty()).unwrap();
txn.commit().unwrap();
let txn = begin_ro(&env).unwrap();
let db = txn.open_db(None).unwrap();
let mut cursor = txn.cursor(db).unwrap();
let results: Vec<[u8; 4]> =
cursor.iter_dupfixed_of::<[u8; 4]>(b"key2").unwrap().map(|r| r.unwrap()).collect();
assert_eq!(results.len(), 2);
assert_eq!(u32::from_le_bytes(results[0]), 10);
assert_eq!(u32::from_le_bytes(results[1]), 20);
}
#[test]
fn test_iter_dupfixed_of_v1() {
test_iter_dupfixed_of_impl(V1Factory::begin_rw, V1Factory::begin_ro);
}
#[test]
fn test_iter_dupfixed_of_v2() {
test_iter_dupfixed_of_impl(V2Factory::begin_rw, V2Factory::begin_ro);
}
fn test_iter_dupfixed_of_nonexistent_key_impl<RwTx, RoTx>(
begin_rw: impl Fn(&Environment) -> MdbxResult<RwTx>,
begin_ro: impl Fn(&Environment) -> MdbxResult<RoTx>,
) where
RwTx: TestRwTxn,
RoTx: TestRoTxn,
{
let dir = tempdir().unwrap();
let env = Environment::builder().open(dir.path()).unwrap();
let txn = begin_rw(&env).unwrap();
let db = txn.create_db(None, DatabaseFlags::DUP_SORT | DatabaseFlags::DUP_FIXED).unwrap();
txn.put(db, b"aaa", &1u32.to_le_bytes(), WriteFlags::empty()).unwrap();
txn.put(db, b"ccc", &2u32.to_le_bytes(), WriteFlags::empty()).unwrap();
txn.commit().unwrap();
let txn = begin_ro(&env).unwrap();
let db = txn.open_db(None).unwrap();
let mut cursor = txn.cursor(db).unwrap();
let results: Vec<[u8; 4]> =
cursor.iter_dupfixed_of::<[u8; 4]>(b"bbb").unwrap().map(|r| r.unwrap()).collect();
assert!(results.is_empty());
}
#[test]
fn test_iter_dupfixed_of_nonexistent_key_v1() {
test_iter_dupfixed_of_nonexistent_key_impl(V1Factory::begin_rw, V1Factory::begin_ro);
}
#[test]
fn test_iter_dupfixed_of_nonexistent_key_v2() {
test_iter_dupfixed_of_nonexistent_key_impl(V2Factory::begin_rw, V2Factory::begin_ro);
}
fn test_iter_dupfixed_of_many_values_impl<RwTx, RoTx>(
begin_rw: impl Fn(&Environment) -> MdbxResult<RwTx>,
begin_ro: impl Fn(&Environment) -> MdbxResult<RoTx>,
) where
RwTx: TestRwTxn,
RoTx: TestRoTxn,
{
use std::collections::HashSet;
let dir = tempdir().unwrap();
let env = Environment::builder().open(dir.path()).unwrap();
let txn = begin_rw(&env).unwrap();
let db = txn.create_db(None, DatabaseFlags::DUP_SORT | DatabaseFlags::DUP_FIXED).unwrap();
txn.put(db, b"before", &999u32.to_le_bytes(), WriteFlags::empty()).unwrap();
for i in 0u32..1000 {
txn.put(db, b"target", &i.to_le_bytes(), WriteFlags::empty()).unwrap();
}
txn.put(db, b"zafter", &888u32.to_le_bytes(), WriteFlags::empty()).unwrap();
txn.commit().unwrap();
let txn = begin_ro(&env).unwrap();
let db = txn.open_db(None).unwrap();
let mut cursor = txn.cursor(db).unwrap();
let results: Vec<[u8; 4]> =
cursor.iter_dupfixed_of::<[u8; 4]>(b"target").unwrap().map(|r| r.unwrap()).collect();
assert_eq!(results.len(), 1000);
let values: HashSet<u32> = results.iter().map(|v| u32::from_le_bytes(*v)).collect();
let expected: HashSet<u32> = (0u32..1000).collect();
assert_eq!(values, expected);
for v in &results {
let num = u32::from_le_bytes(*v);
assert!(num < 1000, "unexpected value {num} from other key");
}
}
#[test]
fn test_iter_dupfixed_of_many_values_v1() {
test_iter_dupfixed_of_many_values_impl(V1Factory::begin_rw, V1Factory::begin_ro);
}
#[test]
fn test_iter_dupfixed_of_many_values_v2() {
test_iter_dupfixed_of_many_values_impl(V2Factory::begin_rw, V2Factory::begin_ro);
}
#[cfg(debug_assertions)]
mod append_debug_tests {
use super::*;
#[test]
#[should_panic(expected = "Append key must be greater")]
fn test_cursor_append_wrong_order_panics() {
let dir = tempdir().unwrap();
let env = Environment::builder().open(dir.path()).unwrap();
let txn = env.begin_rw_sync().unwrap();
let db = txn.open_db(None).unwrap();
let mut cursor = txn.cursor(db).unwrap();
cursor.append(b"b", b"val_b").unwrap();
cursor.append(b"a", b"val_a").unwrap();
}
#[test]
#[should_panic(expected = "Append dup must be greater")]
fn test_cursor_append_dup_wrong_order_panics() {
let dir = tempdir().unwrap();
let env = Environment::builder().open(dir.path()).unwrap();
let txn = env.begin_rw_sync().unwrap();
let db = txn.create_db(None, DatabaseFlags::DUP_SORT).unwrap();
let mut cursor = txn.cursor(db).unwrap();
cursor.append_dup(b"key", b"2").unwrap();
cursor.append_dup(b"key", b"1").unwrap();
}
#[test]
#[should_panic(expected = "Append key must be greater")]
fn test_tx_append_wrong_order_panics() {
let dir = tempdir().unwrap();
let env = Environment::builder().open(dir.path()).unwrap();
let txn = env.begin_rw_sync().unwrap();
let db = txn.open_db(None).unwrap();
txn.append(db, b"b", b"val_b").unwrap();
txn.append(db, b"a", b"val_a").unwrap();
}
#[test]
#[should_panic(expected = "Append dup must be greater")]
fn test_tx_append_dup_wrong_order_panics() {
let dir = tempdir().unwrap();
let env = Environment::builder().open(dir.path()).unwrap();
let txn = env.begin_rw_sync().unwrap();
let db = txn.create_db(None, DatabaseFlags::DUP_SORT).unwrap();
txn.append_dup(db, b"key", b"2").unwrap();
txn.append_dup(db, b"key", b"1").unwrap();
}
#[test]
fn test_cursor_append_reverse_key() {
let dir = tempdir().unwrap();
let env = Environment::builder().open(dir.path()).unwrap();
let txn = env.begin_rw_sync().unwrap();
let db = txn.create_db(None, DatabaseFlags::REVERSE_KEY).unwrap();
let mut cursor = txn.cursor(db).unwrap();
cursor.append(b"a", b"val_a").unwrap();
cursor.append(b"b", b"val_b").unwrap();
cursor.append(b"c", b"val_c").unwrap();
drop(cursor);
txn.commit().unwrap();
let txn = env.begin_ro_sync().unwrap();
let db = txn.open_db(None).unwrap();
let mut cursor = txn.cursor(db).unwrap();
let first: Option<(Vec<u8>, Vec<u8>)> = cursor.first().unwrap();
assert!(first.is_some());
}
#[test]
fn test_cursor_append_reverse_key_wrong_order_returns_error() {
let dir = tempdir().unwrap();
let env = Environment::builder().open(dir.path()).unwrap();
let txn = env.begin_rw_sync().unwrap();
let db = txn.create_db(None, DatabaseFlags::REVERSE_KEY).unwrap();
let mut cursor = txn.cursor(db).unwrap();
cursor.append(b"b", b"val_b").unwrap();
let result = cursor.append(b"a", b"val_a");
assert!(result.is_err());
assert!(matches!(result, Err(MdbxError::KeyMismatch)));
}
#[test]
fn test_cursor_append_dup_reverse_dup() {
let dir = tempdir().unwrap();
let env = Environment::builder().open(dir.path()).unwrap();
let txn = env.begin_rw_sync().unwrap();
let db = txn.create_db(None, DatabaseFlags::DUP_SORT | DatabaseFlags::REVERSE_DUP).unwrap();
let mut cursor = txn.cursor(db).unwrap();
cursor.append_dup(b"key", b"1").unwrap();
cursor.append_dup(b"key", b"2").unwrap();
cursor.append_dup(b"key", b"3").unwrap();
drop(cursor);
txn.commit().unwrap();
let txn = env.begin_ro_sync().unwrap();
let db = txn.open_db(None).unwrap();
let mut cursor = txn.cursor(db).unwrap();
let first: Option<(Vec<u8>, Vec<u8>)> = cursor.first().unwrap();
assert!(first.is_some());
}
#[test]
fn test_cursor_append_dup_reverse_dup_wrong_order_returns_error() {
let dir = tempdir().unwrap();
let env = Environment::builder().open(dir.path()).unwrap();
let txn = env.begin_rw_sync().unwrap();
let db = txn.create_db(None, DatabaseFlags::DUP_SORT | DatabaseFlags::REVERSE_DUP).unwrap();
let mut cursor = txn.cursor(db).unwrap();
cursor.append_dup(b"key", b"2").unwrap();
let result = cursor.append_dup(b"key", b"1");
assert!(result.is_err());
assert!(matches!(result, Err(MdbxError::KeyMismatch)));
}
#[test]
#[should_panic(expected = "Operation requires DUP_SORT database flag")]
fn test_first_dup_on_non_dupsort_panics() {
let dir = tempdir().unwrap();
let env = Environment::builder().open(dir.path()).unwrap();
let txn = env.begin_rw_sync().unwrap();
let db = txn.open_db(None).unwrap();
txn.put(db, b"key1", b"val1", WriteFlags::empty()).unwrap();
let mut cursor = txn.cursor(db).unwrap();
cursor.first::<(), ()>().unwrap();
let _ = cursor.first_dup::<()>();
}
#[test]
#[should_panic(expected = "Operation requires DUP_SORT database flag")]
fn test_append_dup_on_non_dupsort_panics() {
let dir = tempdir().unwrap();
let env = Environment::builder().open(dir.path()).unwrap();
let txn = env.begin_rw_sync().unwrap();
let db = txn.open_db(None).unwrap();
let mut cursor = txn.cursor(db).unwrap();
let _ = cursor.append_dup(b"key", b"value");
}
#[test]
#[should_panic(expected = "Operation requires DUP_FIXED database flag")]
fn test_get_multiple_on_non_dupfixed_panics() {
let dir = tempdir().unwrap();
let env = Environment::builder().open(dir.path()).unwrap();
let txn = env.begin_rw_sync().unwrap();
let db = txn.create_db(None, DatabaseFlags::DUP_SORT).unwrap();
txn.put(db, b"key1", b"val1", WriteFlags::empty()).unwrap();
let mut cursor = txn.cursor(db).unwrap();
cursor.first::<(), ()>().unwrap();
let _ = cursor.get_multiple::<()>();
}
#[test]
#[should_panic(expected = "Operation requires DUP_FIXED database flag")]
fn test_iter_dupfixed_on_non_dupfixed_panics() {
let dir = tempdir().unwrap();
let env = Environment::builder().open(dir.path()).unwrap();
let txn = env.begin_rw_sync().unwrap();
let db = txn.create_db(None, DatabaseFlags::DUP_SORT).unwrap();
txn.put(db, b"key", b"value", WriteFlags::empty()).unwrap();
txn.commit().unwrap();
let txn = env.begin_ro_sync().unwrap();
let db = txn.open_db(None).unwrap();
let mut cursor = txn.cursor(db).unwrap();
let _ = cursor.iter_dupfixed_start::<Vec<u8>, [u8; 4]>();
}
#[test]
#[should_panic(expected = "Operation requires DUP_FIXED database flag")]
fn test_put_multiple_on_non_dupfixed_panics() {
let dir = tempdir().unwrap();
let env = Environment::builder().open(dir.path()).unwrap();
let txn = env.begin_rw_sync().unwrap();
let db = txn.create_db(None, DatabaseFlags::DUP_SORT).unwrap();
let mut cursor = txn.cursor(db).unwrap();
let values = [1u32.to_le_bytes(), 2u32.to_le_bytes()].concat();
let _ = cursor.put_multiple(b"key", &values, 4);
}
}
fn test_put_multiple_basic_impl<RwTx, RoTx>(
begin_rw: impl Fn(&Environment) -> MdbxResult<RwTx>,
begin_ro: impl Fn(&Environment) -> MdbxResult<RoTx>,
) where
RwTx: TestRwTxn,
RoTx: TestRoTxn,
{
let dir = tempdir().unwrap();
let env = Environment::builder().open(dir.path()).unwrap();
let txn = begin_rw(&env).unwrap();
let db = txn.create_db(None, DatabaseFlags::DUP_SORT | DatabaseFlags::DUP_FIXED).unwrap();
let values: Vec<u8> =
[1u32, 2u32, 3u32, 4u32, 5u32].iter().flat_map(|v| v.to_le_bytes()).collect();
let mut cursor = txn.cursor(db).unwrap();
let count = cursor.put_multiple(b"key1", &values, 4).unwrap();
assert_eq!(count, 5);
drop(cursor);
txn.commit().unwrap();
let txn = begin_ro(&env).unwrap();
let db = txn.open_db(None).unwrap();
let mut cursor = txn.cursor(db).unwrap();
cursor.first::<(), ()>().unwrap();
let data: Cow<'_, [u8]> = cursor.get_multiple().unwrap().unwrap();
assert_eq!(data.len(), 20);
let read_values: Vec<u32> =
data.chunks(4).map(|c| u32::from_le_bytes(c.try_into().unwrap())).collect();
assert_eq!(read_values, vec![1, 2, 3, 4, 5]);
}
#[test]
fn test_put_multiple_basic_v1() {
test_put_multiple_basic_impl(V1Factory::begin_rw, V1Factory::begin_ro);
}
#[test]
fn test_put_multiple_basic_v2() {
test_put_multiple_basic_impl(V2Factory::begin_rw, V2Factory::begin_ro);
}
fn test_put_multiple_overwrite_replaces_impl<RwTx, RoTx>(
begin_rw: impl Fn(&Environment) -> MdbxResult<RwTx>,
begin_ro: impl Fn(&Environment) -> MdbxResult<RoTx>,
) where
RwTx: TestRwTxn,
RoTx: TestRoTxn,
{
let dir = tempdir().unwrap();
let env = Environment::builder().open(dir.path()).unwrap();
{
let txn = begin_rw(&env).unwrap();
let db = txn.create_db(None, DatabaseFlags::DUP_SORT | DatabaseFlags::DUP_FIXED).unwrap();
let initial_values: Vec<u8> =
[10u32, 20u32, 30u32].iter().flat_map(|v| v.to_le_bytes()).collect();
let mut cursor = txn.cursor(db).unwrap();
cursor.put_multiple(b"key1", &initial_values, 4).unwrap();
drop(cursor);
txn.commit().unwrap();
}
{
let txn = begin_rw(&env).unwrap();
let db = txn.open_db(None).unwrap();
let new_values: Vec<u8> = [100u32, 200u32].iter().flat_map(|v| v.to_le_bytes()).collect();
let mut cursor = txn.cursor(db).unwrap();
let count = cursor.put_multiple_overwrite(b"key1", &new_values, 4).unwrap();
assert_eq!(count, 2);
drop(cursor);
txn.commit().unwrap();
}
let txn = begin_ro(&env).unwrap();
let db = txn.open_db(None).unwrap();
let mut cursor = txn.cursor(db).unwrap();
cursor.first::<(), ()>().unwrap();
let data: Cow<'_, [u8]> = cursor.get_multiple().unwrap().unwrap();
assert_eq!(data.len(), 8);
let read_values: Vec<u32> =
data.chunks(4).map(|c| u32::from_le_bytes(c.try_into().unwrap())).collect();
assert_eq!(read_values, vec![100, 200]);
}
#[test]
fn test_put_multiple_overwrite_replaces_v1() {
test_put_multiple_overwrite_replaces_impl(V1Factory::begin_rw, V1Factory::begin_ro);
}
#[test]
fn test_put_multiple_overwrite_replaces_v2() {
test_put_multiple_overwrite_replaces_impl(V2Factory::begin_rw, V2Factory::begin_ro);
}
fn test_put_multiple_bad_value_size_impl<RwTx, RoTx>(
begin_rw: impl Fn(&Environment) -> MdbxResult<RwTx>,
_begin_ro: impl Fn(&Environment) -> MdbxResult<RoTx>,
) where
RwTx: TestRwTxn,
RoTx: TestRoTxn,
{
let dir = tempdir().unwrap();
let env = Environment::builder().open(dir.path()).unwrap();
let txn = begin_rw(&env).unwrap();
let db = txn.create_db(None, DatabaseFlags::DUP_SORT | DatabaseFlags::DUP_FIXED).unwrap();
let mut cursor = txn.cursor(db).unwrap();
let result = cursor.put_multiple(b"key", b"somedata", 0);
assert!(matches!(result, Err(MdbxError::BadValSize)));
let result = cursor.put_multiple(b"key", b"12345", 4); assert!(matches!(result, Err(MdbxError::BadValSize)));
}
#[test]
fn test_put_multiple_bad_value_size_v1() {
test_put_multiple_bad_value_size_impl(V1Factory::begin_rw, V1Factory::begin_ro);
}
#[test]
fn test_put_multiple_bad_value_size_v2() {
test_put_multiple_bad_value_size_impl(V2Factory::begin_rw, V2Factory::begin_ro);
}
fn test_put_multiple_empty_values_impl<RwTx, RoTx>(
begin_rw: impl Fn(&Environment) -> MdbxResult<RwTx>,
_begin_ro: impl Fn(&Environment) -> MdbxResult<RoTx>,
) where
RwTx: TestRwTxn,
RoTx: TestRoTxn,
{
let dir = tempdir().unwrap();
let env = Environment::builder().open(dir.path()).unwrap();
let txn = begin_rw(&env).unwrap();
let db = txn.create_db(None, DatabaseFlags::DUP_SORT | DatabaseFlags::DUP_FIXED).unwrap();
let mut cursor = txn.cursor(db).unwrap();
let count = cursor.put_multiple(b"key", &[], 4).unwrap();
assert_eq!(count, 0);
}
#[test]
fn test_put_multiple_empty_values_v1() {
test_put_multiple_empty_values_impl(V1Factory::begin_rw, V1Factory::begin_ro);
}
#[test]
fn test_put_multiple_empty_values_v2() {
test_put_multiple_empty_values_impl(V2Factory::begin_rw, V2Factory::begin_ro);
}
#[cfg(not(debug_assertions))]
#[test]
fn test_put_multiple_requires_dupfixed_returns_error() {
let dir = tempdir().unwrap();
let env = Environment::builder().open(dir.path()).unwrap();
let txn = env.begin_rw_sync().unwrap();
let db = txn.create_db(None, DatabaseFlags::DUP_SORT).unwrap();
let mut cursor = txn.cursor(db).unwrap();
let values = [1u32.to_le_bytes(), 2u32.to_le_bytes()].concat();
let result = cursor.put_multiple(b"key", &values, 4);
assert!(matches!(result, Err(MdbxError::RequiresDupFixed)));
}