redb 2.0.0

Rust Embedded DataBase
Documentation
use redb::{DatabaseError, ReadableTableMetadata};
use redb1::ReadableTable as ReadableTable1;

const ELEMENTS: usize = 3;

trait TestData: redb::Value + redb1::RedbValue {
    fn gen1() -> [<Self as redb1::RedbValue>::SelfType<'static>; ELEMENTS];

    fn gen() -> [<Self as redb::Value>::SelfType<'static>; ELEMENTS];
}

impl TestData for u8 {
    fn gen1() -> [<Self as redb1::RedbValue>::SelfType<'static>; ELEMENTS] {
        [0, 1, 2]
    }

    fn gen() -> [<Self as redb::Value>::SelfType<'static>; ELEMENTS] {
        [0, 1, 2]
    }
}

impl TestData for u16 {
    fn gen1() -> [<Self as redb1::RedbValue>::SelfType<'static>; ELEMENTS] {
        [0, 1, 2]
    }

    fn gen() -> [<Self as redb::Value>::SelfType<'static>; ELEMENTS] {
        [0, 1, 2]
    }
}

impl TestData for u32 {
    fn gen1() -> [<Self as redb1::RedbValue>::SelfType<'static>; ELEMENTS] {
        [0, 1, 2]
    }

    fn gen() -> [<Self as redb::Value>::SelfType<'static>; ELEMENTS] {
        [0, 1, 2]
    }
}

impl TestData for u64 {
    fn gen1() -> [<Self as redb1::RedbValue>::SelfType<'static>; ELEMENTS] {
        [0, 1, 2]
    }

    fn gen() -> [<Self as redb::Value>::SelfType<'static>; ELEMENTS] {
        [0, 1, 2]
    }
}

impl TestData for u128 {
    fn gen1() -> [<Self as redb1::RedbValue>::SelfType<'static>; ELEMENTS] {
        [0, 1, 2]
    }

    fn gen() -> [<Self as redb::Value>::SelfType<'static>; ELEMENTS] {
        [0, 1, 2]
    }
}

impl TestData for i8 {
    fn gen1() -> [<Self as redb1::RedbValue>::SelfType<'static>; ELEMENTS] {
        [-1, 1, 2]
    }

    fn gen() -> [<Self as redb::Value>::SelfType<'static>; ELEMENTS] {
        [-1, 1, 2]
    }
}

impl TestData for i16 {
    fn gen1() -> [<Self as redb1::RedbValue>::SelfType<'static>; ELEMENTS] {
        [-1, 1, 2]
    }

    fn gen() -> [<Self as redb::Value>::SelfType<'static>; ELEMENTS] {
        [-1, 1, 2]
    }
}

impl TestData for i32 {
    fn gen1() -> [<Self as redb1::RedbValue>::SelfType<'static>; ELEMENTS] {
        [-1, 1, 2]
    }

    fn gen() -> [<Self as redb::Value>::SelfType<'static>; ELEMENTS] {
        [-1, 1, 2]
    }
}

impl TestData for i64 {
    fn gen1() -> [<Self as redb1::RedbValue>::SelfType<'static>; ELEMENTS] {
        [-1, 1, 2]
    }

    fn gen() -> [<Self as redb::Value>::SelfType<'static>; ELEMENTS] {
        [-1, 1, 2]
    }
}

impl TestData for i128 {
    fn gen1() -> [<Self as redb1::RedbValue>::SelfType<'static>; ELEMENTS] {
        [-1, 1, 2]
    }

    fn gen() -> [<Self as redb::Value>::SelfType<'static>; ELEMENTS] {
        [-1, 1, 2]
    }
}

impl TestData for f32 {
    fn gen1() -> [<Self as redb1::RedbValue>::SelfType<'static>; ELEMENTS] {
        [f32::NAN, f32::INFINITY, f32::MIN_POSITIVE]
    }

    fn gen() -> [<Self as redb::Value>::SelfType<'static>; ELEMENTS] {
        [f32::NAN, f32::INFINITY, f32::MIN_POSITIVE]
    }
}

impl TestData for f64 {
    fn gen1() -> [<Self as redb1::RedbValue>::SelfType<'static>; ELEMENTS] {
        [f64::MIN, f64::NEG_INFINITY, f64::MAX]
    }

    fn gen() -> [<Self as redb::Value>::SelfType<'static>; ELEMENTS] {
        [f64::MIN, f64::NEG_INFINITY, f64::MAX]
    }
}

impl TestData for () {
    fn gen1() -> [<Self as redb1::RedbValue>::SelfType<'static>; ELEMENTS] {
        [(), (), ()]
    }

    fn gen() -> [<Self as redb::Value>::SelfType<'static>; ELEMENTS] {
        [(), (), ()]
    }
}

impl TestData for &'static str {
    fn gen1() -> [<Self as redb1::RedbValue>::SelfType<'static>; ELEMENTS] {
        ["hello", "world1", "hi"]
    }

    fn gen() -> [<Self as redb::Value>::SelfType<'static>; ELEMENTS] {
        ["hello", "world1", "hi"]
    }
}

impl TestData for &'static [u8] {
    fn gen1() -> [<Self as redb1::RedbValue>::SelfType<'static>; ELEMENTS] {
        [b"test", b"bytes", b"now"]
    }

    fn gen() -> [<Self as redb::Value>::SelfType<'static>; ELEMENTS] {
        [b"test", b"bytes", b"now"]
    }
}

impl TestData for &'static [u8; 5] {
    fn gen1() -> [<Self as redb1::RedbValue>::SelfType<'static>; ELEMENTS] {
        [b"test1", b"bytes", b"now12"]
    }

    fn gen() -> [<Self as redb::Value>::SelfType<'static>; ELEMENTS] {
        [b"test1", b"bytes", b"now12"]
    }
}

impl TestData for Option<u64> {
    fn gen1() -> [<Self as redb1::RedbValue>::SelfType<'static>; ELEMENTS] {
        [None, Some(0), Some(7)]
    }

    fn gen() -> [<Self as redb::Value>::SelfType<'static>; ELEMENTS] {
        [None, Some(0), Some(7)]
    }
}

impl TestData for (u64, &'static str) {
    fn gen1() -> [<Self as redb1::RedbValue>::SelfType<'static>; ELEMENTS] {
        [(0, "hi"), (1, "bye"), (2, "byte")]
    }

    fn gen() -> [<Self as redb::Value>::SelfType<'static>; ELEMENTS] {
        [(0, "hi"), (1, "bye"), (2, "byte")]
    }
}

fn create_tempfile() -> tempfile::NamedTempFile {
    if cfg!(target_os = "wasi") {
        tempfile::NamedTempFile::new_in("/").unwrap()
    } else {
        tempfile::NamedTempFile::new().unwrap()
    }
}

fn test_helper<K: TestData + redb::Key + redb1::RedbKey + 'static, V: TestData + 'static>() {
    let tmpfile = create_tempfile();
    let db = redb1::Database::create(tmpfile.path()).unwrap();
    let table_def: redb1::TableDefinition<K, V> = redb1::TableDefinition::new("table");
    let write_txn = db.begin_write().unwrap();
    {
        let mut table = write_txn.open_table(table_def).unwrap();
        for i in 0..ELEMENTS {
            table.insert(&K::gen1()[i], &V::gen1()[i]).unwrap();
        }
    }
    write_txn.commit().unwrap();
    drop(db);

    let db = redb::Database::open(tmpfile.path()).unwrap();
    let read_txn = db.begin_read().unwrap();
    let table_def: redb::TableDefinition<K, V> = redb::TableDefinition::new("table");
    let table = read_txn.open_table(table_def).unwrap();
    assert_eq!(table.len().unwrap(), ELEMENTS as u64);
    for i in 0..ELEMENTS {
        let result = table.get(&K::gen()[i]).unwrap().unwrap();
        let value = result.value();
        let bytes = <V as redb::Value>::as_bytes(&value);
        let expected = &V::gen()[i];
        let expected_bytes = <V as redb::Value>::as_bytes(expected);
        assert_eq!(bytes.as_ref(), expected_bytes.as_ref());
    }
}

// TODO: re-enable
#[test]
#[ignore]
fn primitive_types() {
    test_helper::<u8, u8>();
    test_helper::<u16, u16>();
    test_helper::<u32, u32>();
    test_helper::<u64, u64>();
    test_helper::<u128, u128>();
    test_helper::<i8, i8>();
    test_helper::<i16, i16>();
    test_helper::<i32, i32>();
    test_helper::<i64, i64>();
    test_helper::<i128, i128>();
    test_helper::<i128, f32>();
    test_helper::<i128, f64>();
    test_helper::<&str, &str>();
    test_helper::<u8, ()>();
}

// TODO: re-enable
#[test]
#[ignore]
fn container_types() {
    test_helper::<&[u8], &[u8]>();
    test_helper::<&[u8; 5], &[u8; 5]>();
    test_helper::<u64, Option<u64>>();
    test_helper::<(u64, &str), &str>();
}

// TODO: re-enable
#[test]
#[ignore]
fn mixed_width() {
    test_helper::<u8, &[u8]>();
    test_helper::<&[u8; 5], &str>();
}

#[test]
fn upgrade_v1_to_v2() {
    let tmpfile1 = create_tempfile();
    let tmpfile2 = create_tempfile();
    let table_def1: redb1::TableDefinition<u64, u64> = redb1::TableDefinition::new("my_data");
    let db = redb1::Database::create(tmpfile1.path()).unwrap();
    let write_txn = db.begin_write().unwrap();
    {
        let mut table = write_txn.open_table(table_def1).unwrap();
        table.insert(0, 0).unwrap();
    }
    write_txn.commit().unwrap();
    drop(db);

    let table_def2: redb::TableDefinition<u64, u64> = redb::TableDefinition::new("my_data");
    match redb::Database::create(tmpfile1.path()).err().unwrap() {
        DatabaseError::UpgradeRequired(_) => {
            let db1 = redb1::Database::create(tmpfile1.path()).unwrap();
            let db2 = redb::Database::create(tmpfile2.path()).unwrap();
            let read_txn = db1.begin_read().unwrap();
            let table1 = read_txn.open_table(table_def1).unwrap();
            let write_txn = db2.begin_write().unwrap();
            {
                let mut table2 = write_txn.open_table(table_def2).unwrap();
                for r in table1.iter().unwrap() {
                    let (k, v) = r.unwrap();
                    table2.insert(k.value(), v.value()).unwrap();
                }
            }
            write_txn.commit().unwrap();
        }
        _ => unreachable!(),
    };

    let db = redb::Database::open(tmpfile2.path()).unwrap();
    let read_txn = db.begin_read().unwrap();
    let table = read_txn.open_table(table_def2).unwrap();
    assert_eq!(table.get(0).unwrap().unwrap().value(), 0);
}