Merge pull request #200 from mozilla/migrator

Implement a simple migrator between multiple backends
without.crypto
Victor Porof 4 years ago committed by GitHub
commit a6a616ad3d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      examples/simple-store.rs
  2. 3
      src/backend.rs
  3. 7
      src/backend/impl_lmdb.rs
  4. 4
      src/backend/impl_lmdb/arch_migrator.rs
  5. 107
      src/backend/impl_lmdb/arch_migrator_error.rs
  6. 45
      src/backend/impl_lmdb/environment.rs
  7. 3
      src/backend/impl_lmdb/error.rs
  8. 15
      src/backend/impl_safe/environment.rs
  9. 3
      src/backend/impl_safe/error.rs
  10. 4
      src/backend/traits.rs
  11. 10
      src/bin/dump.rs
  12. 5
      src/env.rs
  13. 103
      src/error.rs
  14. 5
      src/helpers.rs
  15. 2
      src/lib.rs
  16. 91
      src/migrator.rs
  17. 10
      src/readwrite.rs
  18. 40
      src/store/integermulti.rs
  19. 2
      src/store/multi.rs
  20. 2
      src/store/single.rs
  21. 109
      tests/env-lmdb.rs
  22. 138
      tests/env-migration.rs
  23. 112
      tests/env-safe.rs
  24. 4
      tests/multi-integer-store.rs
  25. 2
      tests/test_txn.rs

@ -35,7 +35,7 @@ fn getput<'w, 's>(store: MultiStore, writer: &'w mut Writer, ids: &'s mut Vec<St
// this is a multi-valued database, so get returns an iterator
let mut iter = store.get(writer, k).unwrap();
while let Some(Ok((_key, val))) = iter.next() {
if let Value::Str(s) = val.unwrap() {
if let Value::Str(s) = val {
ids.push(s.to_owned());
} else {
panic!("didn't get a string back!");

@ -17,6 +17,9 @@ pub use common::*;
pub use traits::*;
pub use impl_lmdb::{
ArchMigrateError as LmdbArchMigrateError,
ArchMigrateResult as LmdbArchMigrateResult,
ArchMigrator as LmdbArchMigrator,
DatabaseFlagsImpl as LmdbDatabaseFlags,
DatabaseImpl as LmdbDatabase,
EnvironmentBuilderImpl as Lmdb,

@ -8,6 +8,8 @@
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
// specific language governing permissions and limitations under the License.
mod arch_migrator;
mod arch_migrator_error;
mod cursor;
mod database;
mod environment;
@ -18,6 +20,11 @@ mod iter;
mod stat;
mod transaction;
pub use arch_migrator::{
MigrateError as ArchMigrateError,
MigrateResult as ArchMigrateResult,
Migrator as ArchMigrator,
};
pub use cursor::{
RoCursorImpl,
RwCursorImpl,

@ -44,7 +44,7 @@
//! new environment in a temporary directory:
//!
//! ```
//! use rkv::migrate::Migrator;
//! use rkv::migrator::LmdbArchMigrator as Migrator;
//! use std::path::Path;
//! use tempfile::tempdir;
//! let mut migrator = Migrator::new(Path::new("tests/envs/ref_env_32")).unwrap();
@ -89,7 +89,7 @@ use lmdb::{
WriteFlags,
};
pub use crate::error::MigrateError;
pub use super::arch_migrator_error::MigrateError;
const PAGESIZE: u16 = 4096;

@ -0,0 +1,107 @@
// Copyright 2018-2019 Mozilla
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use
// this file except in compliance with the License. You may obtain a copy of the
// License at http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software distributed
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
// specific language governing permissions and limitations under the License.
use std::{
io,
num,
str,
};
use failure::Fail;
#[derive(Debug, Fail)]
pub enum MigrateError {
#[fail(display = "database not found: {:?}", _0)]
DatabaseNotFound(String),
#[fail(display = "{}", _0)]
FromString(String),
#[fail(display = "couldn't determine bit depth")]
IndeterminateBitDepth,
#[fail(display = "I/O error: {:?}", _0)]
IoError(io::Error),
#[fail(display = "invalid DatabaseFlags bits")]
InvalidDatabaseBits,
#[fail(display = "invalid data version")]
InvalidDataVersion,
#[fail(display = "invalid magic number")]
InvalidMagicNum,
#[fail(display = "invalid NodeFlags bits")]
InvalidNodeBits,
#[fail(display = "invalid PageFlags bits")]
InvalidPageBits,
#[fail(display = "invalid page number")]
InvalidPageNum,
#[fail(display = "lmdb backend error: {}", _0)]
LmdbError(lmdb::Error),
#[fail(display = "string conversion error")]
StringConversionError,
#[fail(display = "TryFromInt error: {:?}", _0)]
TryFromIntError(num::TryFromIntError),
#[fail(display = "unexpected Page variant")]
UnexpectedPageVariant,
#[fail(display = "unexpected PageHeader variant")]
UnexpectedPageHeaderVariant,
#[fail(display = "unsupported PageHeader variant")]
UnsupportedPageHeaderVariant,
#[fail(display = "UTF8 error: {:?}", _0)]
Utf8Error(str::Utf8Error),
}
impl From<io::Error> for MigrateError {
fn from(e: io::Error) -> MigrateError {
MigrateError::IoError(e)
}
}
impl From<str::Utf8Error> for MigrateError {
fn from(e: str::Utf8Error) -> MigrateError {
MigrateError::Utf8Error(e)
}
}
impl From<num::TryFromIntError> for MigrateError {
fn from(e: num::TryFromIntError) -> MigrateError {
MigrateError::TryFromIntError(e)
}
}
impl From<&str> for MigrateError {
fn from(e: &str) -> MigrateError {
MigrateError::FromString(e.to_string())
}
}
impl From<String> for MigrateError {
fn from(e: String) -> MigrateError {
MigrateError::FromString(e)
}
}
impl From<lmdb::Error> for MigrateError {
fn from(e: lmdb::Error) -> MigrateError {
MigrateError::LmdbError(e)
}
}

@ -29,13 +29,18 @@ use crate::backend::traits::{
BackendEnvironment,
BackendEnvironmentBuilder,
BackendInfo,
BackendIter,
BackendRoCursor,
BackendRoCursorTransaction,
BackendStat,
};
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
pub struct EnvironmentBuilderImpl {
builder: lmdb::EnvironmentBuilder,
envtype: EnvironmentType,
make_dir: bool,
check_env_exists: bool,
}
impl<'b> BackendEnvironmentBuilder<'b> for EnvironmentBuilderImpl {
@ -46,7 +51,9 @@ impl<'b> BackendEnvironmentBuilder<'b> for EnvironmentBuilderImpl {
fn new() -> EnvironmentBuilderImpl {
EnvironmentBuilderImpl {
builder: lmdb::Environment::new(),
envtype: EnvironmentType::SingleDatabase,
make_dir: false,
check_env_exists: false,
}
}
@ -65,6 +72,9 @@ impl<'b> BackendEnvironmentBuilder<'b> for EnvironmentBuilderImpl {
fn set_max_dbs(&mut self, max_dbs: u32) -> &mut Self {
self.builder.set_max_dbs(max_dbs);
if max_dbs > 0 {
self.envtype = EnvironmentType::MultipleNamedDatabases
}
self
}
@ -78,19 +88,33 @@ impl<'b> BackendEnvironmentBuilder<'b> for EnvironmentBuilderImpl {
self
}
fn set_check_if_env_exists(&mut self, check_env_exists: bool) -> &mut Self {
self.check_env_exists = check_env_exists;
self
}
fn open(&self, path: &Path) -> Result<Self::Environment, Self::Error> {
if self.check_env_exists && !path.join("data.mdb").exists() {
return Err(ErrorImpl::EnvironmentDoesNotExistError(path.into()));
}
if !path.is_dir() {
if !self.make_dir {
return Err(ErrorImpl::DirectoryDoesNotExistError(path.into()));
}
fs::create_dir_all(path).map_err(ErrorImpl::IoError)?;
}
self.builder.open(path).map(EnvironmentImpl).map_err(ErrorImpl::LmdbError)
self.builder.open(path).map(|env| EnvironmentImpl(env, self.envtype)).map_err(ErrorImpl::LmdbError)
}
}
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
enum EnvironmentType {
SingleDatabase,
MultipleNamedDatabases,
}
#[derive(Debug)]
pub struct EnvironmentImpl(lmdb::Environment);
pub struct EnvironmentImpl(lmdb::Environment, EnvironmentType);
impl<'e> BackendEnvironment<'e> for EnvironmentImpl {
type Database = DatabaseImpl;
@ -101,6 +125,23 @@ impl<'e> BackendEnvironment<'e> for EnvironmentImpl {
type RwTransaction = RwTransactionImpl<'e>;
type Stat = StatImpl;
fn get_dbs(&self) -> Result<Vec<Option<String>>, Self::Error> {
if self.1 == EnvironmentType::SingleDatabase {
return Ok(vec![None]);
}
let db = self.0.open_db(None).map(DatabaseImpl).map_err(ErrorImpl::LmdbError)?;
let reader = self.begin_ro_txn()?;
let cursor = reader.open_ro_cursor(&db)?;
let mut iter = cursor.into_iter();
let mut store = vec![];
while let Some(result) = iter.next() {
let (key, _) = result?;
let name = String::from_utf8(key.to_owned()).map_err(|_| ErrorImpl::LmdbError(lmdb::Error::Corrupted))?;
store.push(Some(name));
}
Ok(store)
}
fn open_db(&self, name: Option<&str>) -> Result<Self::Database, Self::Error> {
self.0.open_db(name).map(DatabaseImpl).map_err(ErrorImpl::LmdbError)
}

@ -23,6 +23,7 @@ use crate::{
pub enum ErrorImpl {
LmdbError(lmdb::Error),
DirectoryDoesNotExistError(PathBuf),
EnvironmentDoesNotExistError(PathBuf),
IoError(io::Error),
}
@ -33,6 +34,7 @@ impl fmt::Display for ErrorImpl {
match self {
ErrorImpl::LmdbError(e) => e.fmt(fmt),
ErrorImpl::DirectoryDoesNotExistError(_) => write!(fmt, "DirectoryDoesNotExistError"),
ErrorImpl::EnvironmentDoesNotExistError(_) => write!(fmt, "EnvironmentDoesNotExistError"),
ErrorImpl::IoError(e) => e.fmt(fmt),
}
}
@ -50,6 +52,7 @@ impl Into<StoreError> for ErrorImpl {
ErrorImpl::LmdbError(lmdb::Error::ReadersFull) => StoreError::ReadersFull,
ErrorImpl::LmdbError(error) => StoreError::LmdbError(error),
ErrorImpl::DirectoryDoesNotExistError(path) => StoreError::DirectoryDoesNotExistError(path),
ErrorImpl::EnvironmentDoesNotExistError(path) => StoreError::EnvironmentDoesNotExistError(path),
ErrorImpl::IoError(error) => StoreError::IoError(error),
}
}

@ -55,6 +55,7 @@ pub struct EnvironmentBuilderImpl {
max_dbs: Option<usize>,
map_size: Option<usize>,
make_dir: bool,
check_env_exists: bool,
}
impl<'b> BackendEnvironmentBuilder<'b> for EnvironmentBuilderImpl {
@ -69,6 +70,7 @@ impl<'b> BackendEnvironmentBuilder<'b> for EnvironmentBuilderImpl {
max_dbs: None,
map_size: None,
make_dir: false,
check_env_exists: false,
}
}
@ -100,7 +102,15 @@ impl<'b> BackendEnvironmentBuilder<'b> for EnvironmentBuilderImpl {
self
}
fn set_check_if_env_exists(&mut self, check_env_exists: bool) -> &mut Self {
self.check_env_exists = check_env_exists;
self
}
fn open(&self, path: &Path) -> Result<Self::Environment, Self::Error> {
if self.check_env_exists && !path.join(DEFAULT_DB_FILENAME).exists() {
return Err(ErrorImpl::EnvironmentDoesNotExistError(path.into()));
}
if !path.is_dir() {
if !self.make_dir {
return Err(ErrorImpl::DirectoryDoesNotExistError(path.into()));
@ -211,6 +221,11 @@ impl<'e> BackendEnvironment<'e> for EnvironmentImpl {
type RwTransaction = RwTransactionImpl<'e>;
type Stat = StatImpl;
fn get_dbs(&self) -> Result<Vec<Option<String>>, Self::Error> {
let dbs = self.dbs.read().map_err(|_| ErrorImpl::EnvPoisonError)?;
Ok(dbs.keys().map(|key| key.to_owned()).collect())
}
fn open_db(&self, name: Option<&str>) -> Result<Self::Database, Self::Error> {
if Arc::strong_count(&self.ro_txns) > 1 {
return Err(ErrorImpl::DbsIllegalOpen);

@ -30,6 +30,7 @@ pub enum ErrorImpl {
DbNotFoundError,
DbIsForeignError,
DirectoryDoesNotExistError(PathBuf),
EnvironmentDoesNotExistError(PathBuf),
IoError(io::Error),
BincodeError(BincodeError),
}
@ -46,6 +47,7 @@ impl fmt::Display for ErrorImpl {
ErrorImpl::DbNotFoundError => write!(fmt, "DbNotFoundError (safe mode)"),
ErrorImpl::DbIsForeignError => write!(fmt, "DbIsForeignError (safe mode)"),
ErrorImpl::DirectoryDoesNotExistError(_) => write!(fmt, "DirectoryDoesNotExistError (safe mode)"),
ErrorImpl::EnvironmentDoesNotExistError(_) => write!(fmt, "EnvironmentDoesNotExistError (safe mode)"),
ErrorImpl::IoError(e) => e.fmt(fmt),
ErrorImpl::BincodeError(e) => e.fmt(fmt),
}
@ -63,6 +65,7 @@ impl Into<StoreError> for ErrorImpl {
ErrorImpl::BincodeError(_) => StoreError::FileInvalid,
ErrorImpl::DbsFull => StoreError::DbsFull,
ErrorImpl::DirectoryDoesNotExistError(path) => StoreError::DirectoryDoesNotExistError(path),
ErrorImpl::EnvironmentDoesNotExistError(path) => StoreError::EnvironmentDoesNotExistError(path),
ErrorImpl::IoError(error) => StoreError::IoError(error),
_ => StoreError::SafeModeError(self),
}

@ -90,6 +90,8 @@ pub trait BackendEnvironmentBuilder<'b>: Debug + Eq + PartialEq + Copy + Clone {
fn set_make_dir_if_needed(&mut self, make_dir: bool) -> &mut Self;
fn set_check_if_env_exists(&mut self, check_env: bool) -> &mut Self;
fn open(&self, path: &Path) -> Result<Self::Environment, Self::Error>;
}
@ -102,6 +104,8 @@ pub trait BackendEnvironment<'e>: Debug {
type RoTransaction: BackendRoCursorTransaction<'e, Database = Self::Database>;
type RwTransaction: BackendRwCursorTransaction<'e, Database = Self::Database>;
fn get_dbs(&self) -> Result<Vec<Option<String>>, Self::Error>;
fn open_db(&self, name: Option<&str>) -> Result<Self::Database, Self::Error>;
fn create_db(&self, name: Option<&str>, flags: Self::Flags) -> Result<Self::Database, Self::Error>;

@ -14,12 +14,12 @@ use std::{
path::Path,
};
use rkv::{
migrate::Migrator,
MigrateError,
use rkv::migrator::{
LmdbArchMigrateError,
LmdbArchMigrator,
};
fn main() -> Result<(), MigrateError> {
fn main() -> Result<(), LmdbArchMigrateError> {
let mut cli_args = args();
let mut db_name = None;
let mut env_path = None;
@ -47,7 +47,7 @@ fn main() -> Result<(), MigrateError> {
}
let env_path = env_path.ok_or("must provide a path to the LMDB environment")?;
let mut migrator: Migrator = Migrator::new(Path::new(&env_path))?;
let mut migrator = LmdbArchMigrator::new(Path::new(&env_path))?;
migrator.dump(db_name.as_deref(), io::stdout()).unwrap();
Ok(())

@ -110,6 +110,11 @@ impl<'e, E> Rkv<E>
where
E: BackendEnvironment<'e>,
{
/// Return all created databases.
pub fn get_dbs(&self) -> Result<Vec<Option<String>>, StoreError> {
self.env.get_dbs().map_err(|e| e.into())
}
/// Create or Open an existing database in (&[u8] -> Single Value) mode.
/// Note: that create=true cannot be called concurrently with other operations so if
/// you are sure that the database exists, call this with create=false.

@ -10,7 +10,6 @@
use std::{
io,
num,
path::PathBuf,
str,
thread,
@ -84,6 +83,9 @@ pub enum StoreError {
#[fail(display = "directory does not exist or not a directory: {:?}", _0)]
DirectoryDoesNotExistError(PathBuf),
#[fail(display = "environment does not exist in directory: {:?}", _0)]
EnvironmentDoesNotExistError(PathBuf),
#[fail(display = "data error: {:?}", _0)]
DataError(DataError),
@ -124,99 +126,18 @@ impl From<io::Error> for StoreError {
#[derive(Debug, Fail)]
pub enum MigrateError {
#[fail(display = "database not found: {:?}", _0)]
DatabaseNotFound(String),
#[fail(display = "{}", _0)]
FromString(String),
#[fail(display = "couldn't determine bit depth")]
IndeterminateBitDepth,
#[fail(display = "I/O error: {:?}", _0)]
IoError(io::Error),
#[fail(display = "invalid DatabaseFlags bits")]
InvalidDatabaseBits,
#[fail(display = "invalid data version")]
InvalidDataVersion,
#[fail(display = "invalid magic number")]
InvalidMagicNum,
#[fail(display = "invalid NodeFlags bits")]
InvalidNodeBits,
#[fail(display = "invalid PageFlags bits")]
InvalidPageBits,
#[fail(display = "invalid page number")]
InvalidPageNum,
#[fail(display = "lmdb backend error: {}", _0)]
LmdbError(lmdb::Error),
#[fail(display = "safe mode backend error: {}", _0)]
SafeModeError(SafeModeError),
#[fail(display = "string conversion error")]
StringConversionError,
#[fail(display = "TryFromInt error: {:?}", _0)]
TryFromIntError(num::TryFromIntError),
#[fail(display = "store error: {}", _0)]
StoreError(StoreError),
#[fail(display = "unexpected Page variant")]
UnexpectedPageVariant,
#[fail(display = "source is empty")]
SourceEmpty,
#[fail(display = "unexpected PageHeader variant")]
UnexpectedPageHeaderVariant,
#[fail(display = "unsupported PageHeader variant")]
UnsupportedPageHeaderVariant,
#[fail(display = "UTF8 error: {:?}", _0)]
Utf8Error(str::Utf8Error),
}
impl From<io::Error> for MigrateError {
fn from(e: io::Error) -> MigrateError {
MigrateError::IoError(e)
}
}
impl From<str::Utf8Error> for MigrateError {
fn from(e: str::Utf8Error) -> MigrateError {
MigrateError::Utf8Error(e)
}
}
impl From<num::TryFromIntError> for MigrateError {
fn from(e: num::TryFromIntError) -> MigrateError {
MigrateError::TryFromIntError(e)
}
}
impl From<&str> for MigrateError {
fn from(e: &str) -> MigrateError {
MigrateError::FromString(e.to_string())
}
}
impl From<String> for MigrateError {
fn from(e: String) -> MigrateError {
MigrateError::FromString(e)
}
}
impl From<lmdb::Error> for MigrateError {
fn from(e: lmdb::Error) -> MigrateError {
MigrateError::LmdbError(e)
}
#[fail(display = "destination is not empty")]
DestinationNotEmpty,
}
impl From<SafeModeError> for MigrateError {
fn from(e: SafeModeError) -> MigrateError {
MigrateError::SafeModeError(e)
impl From<StoreError> for MigrateError {
fn from(e: StoreError) -> MigrateError {
MigrateError::StoreError(e)
}
}

@ -23,10 +23,9 @@ use crate::{
value::Value,
};
pub(crate) fn read_transform(value: Result<&[u8], StoreError>) -> Result<Option<Value>, StoreError> {
pub(crate) fn read_transform(value: Result<&[u8], StoreError>) -> Result<Value, StoreError> {
match value {
Ok(bytes) => Value::from_tagged_slice(bytes).map(Some).map_err(StoreError::DataError),
Err(StoreError::KeyValuePairNotFound) => Ok(None),
Ok(bytes) => Value::from_tagged_slice(bytes).map_err(StoreError::DataError),
Err(e) => Err(e),
}
}

@ -209,7 +209,7 @@ mod manager;
mod readwrite;
pub mod backend;
pub mod migrate;
pub mod migrator;
pub mod store;
pub mod value;

@ -0,0 +1,91 @@
// Copyright 2018-2019 Mozilla
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use
// this file except in compliance with the License. You may obtain a copy of the
// License at http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software distributed
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
// specific language governing permissions and limitations under the License.
//! A simple utility for migrating data from one RVK environment to another. Notably, this
//! tool can migrate data from an enviroment created with a different backend than the
//! current RKV consumer (e.g from Lmdb to SafeMode).
//!
//! The utility doesn't support migrating between 32-bit and 64-bit LMDB environments yet,
//! see `arch_migrator` if this is needed. However, this utility is ultimately intended to
//! handle all possible migrations.
//!
//! The destination environment should be empty of data, otherwise an error is returned.
//!
//! The tool currently has these limitations:
//!
//! 1. It doesn't support migration from environments created with
//! `EnvironmentFlags::NO_SUB_DIR`. To migrate such an environment, create a temporary
//! directory, copy the environment's data files in the temporary directory, then
//! migrate the temporary directory as the source environment.
//! 2. It doesn't support migration from databases created with DatabaseFlags::DUP_SORT`
//! (with or without `DatabaseFlags::DUP_FIXED`) nor with `DatabaseFlags::INTEGER_KEY`.
//! This effectively means that migration is limited to `SingleStore`s.
//! 3. It doesn't allow for existing data in the destination environment, which means that
//! it cannot overwrite nor append data.
use crate::{
backend::{
LmdbEnvironment,
SafeModeEnvironment,
},
error::MigrateError,
Rkv,
StoreOptions,
};
pub use crate::backend::{
LmdbArchMigrateError,
LmdbArchMigrateResult,
LmdbArchMigrator,
};
// FIXME: should parametrize this instead.
macro_rules! fn_migrator {
($name:tt, $src:ty, $dst:ty) => {
/// Migrate all data in all of databases from the source environment to the destination
/// environment. This includes all key/value pairs in the main database that aren't
/// metadata about subdatabases and all key/value pairs in all subdatabases.
///
/// Other backend-specific metadata such as map size or maximum databases left intact on
/// the given environments.
///
/// The destination environment should be empty of data, otherwise an error is returned.
pub fn $name(src_env: &Rkv<$src>, dst_env: &Rkv<$dst>) -> Result<(), MigrateError> {
let src_dbs = src_env.get_dbs().unwrap();
if src_dbs.is_empty() {
return Err(MigrateError::SourceEmpty);
}
let dst_dbs = dst_env.get_dbs().unwrap();
if !dst_dbs.is_empty() {
return Err(MigrateError::DestinationNotEmpty);
}
for name in src_dbs {
let src_store = src_env.open_single(name.as_deref(), StoreOptions::default())?;
let dst_store = dst_env.open_single(name.as_deref(), StoreOptions::create())?;
let reader = src_env.read()?;
let mut writer = dst_env.write()?;
let mut iter = src_store.iter_start(&reader)?;
while let Some(Ok((key, value))) = iter.next() {
dst_store.put(&mut writer, key, &value).expect("wrote");
}
writer.commit()?;
}
Ok(())
}
};
}
pub struct Migrator;
impl Migrator {
fn_migrator!(migrate_lmdb_to_safe_mode, LmdbEnvironment, SafeModeEnvironment);
fn_migrator!(migrate_safe_mode_to_lmdb, SafeModeEnvironment, LmdbEnvironment);
}

@ -48,7 +48,10 @@ where
K: AsRef<[u8]>,
{
let bytes = self.0.get(db, k.as_ref()).map_err(|e| e.into());
read_transform(bytes)
match read_transform(bytes).map(Some) {
Err(StoreError::KeyValuePairNotFound) => Ok(None),
result => result,
}
}
fn open_ro_cursor(&'r self, db: &T::Database) -> Result<T::RoCursor, StoreError> {
@ -83,7 +86,10 @@ where
K: AsRef<[u8]>,
{
let bytes = self.0.get(db, k.as_ref()).map_err(|e| e.into());
read_transform(bytes)
match read_transform(bytes).map(Some) {
Err(StoreError::KeyValuePairNotFound) => Ok(None),
result => result,
}
}
fn open_ro_cursor(&'r self, db: &T::Database) -> Result<T::RoCursor, StoreError> {

@ -219,8 +219,8 @@ mod tests {
s.put(&mut writer, 1, &Value::Str("hello1!")).expect("write");
let mut iter = s.get(&writer, 1).expect("read");
assert_eq!(iter.next().expect("first").expect("ok").1, Some(Value::Str("hello!")));
assert_eq!(iter.next().expect("second").expect("ok").1, Some(Value::Str("hello1!")));
assert_eq!(iter.next().expect("first").expect("ok").1, Value::Str("hello!"));
assert_eq!(iter.next().expect("second").expect("ok").1, Value::Str("hello1!"));
assert!(iter.next().is_none());
}
}
@ -240,8 +240,8 @@ mod tests {
s.put(&mut writer, 1, &Value::Str("hello1!")).expect("write");
{
let mut iter = s.get(&writer, 1).expect("read");
assert_eq!(iter.next().expect("first").expect("ok").1, Some(Value::Str("hello!")));
assert_eq!(iter.next().expect("second").expect("ok").1, Some(Value::Str("hello1!")));
assert_eq!(iter.next().expect("first").expect("ok").1, Value::Str("hello!"));
assert_eq!(iter.next().expect("second").expect("ok").1, Value::Str("hello1!"));
assert!(iter.next().is_none());
}
writer.commit().expect("committed");
@ -254,7 +254,7 @@ mod tests {
let reader = k.read().expect("reader");
let mut iter = s.get(&reader, 1).expect("read");
assert_eq!(iter.next().expect("first").expect("ok").1, Some(Value::Str("hello1!")));
assert_eq!(iter.next().expect("first").expect("ok").1, Value::Str("hello1!"));
assert!(iter.next().is_none());
}
@ -265,7 +265,7 @@ mod tests {
let reader = k.read().expect("reader");
let mut iter = s.get(&reader, 1).expect("read");
assert_eq!(iter.next().expect("first").expect("ok").1, Some(Value::Str("hello1!")));
assert_eq!(iter.next().expect("first").expect("ok").1, Value::Str("hello1!"));
assert!(iter.next().is_none());
}
@ -305,8 +305,8 @@ mod tests {
s.put(&mut writer, 2, &Value::Str("hello!")).expect("write");
{
let mut iter = s.get(&writer, 1).expect("read");
assert_eq!(iter.next().expect("first").expect("ok").1, Some(Value::Str("hello!")));
assert_eq!(iter.next().expect("second").expect("ok").1, Some(Value::Str("hello1!")));
assert_eq!(iter.next().expect("first").expect("ok").1, Value::Str("hello!"));
assert_eq!(iter.next().expect("second").expect("ok").1, Value::Str("hello1!"));
assert!(iter.next().is_none());
}
writer.commit().expect("committed");
@ -318,8 +318,8 @@ mod tests {
let reader = k.read().expect("reader");
let mut iter = s.get(&reader, 1).expect("read");
assert_eq!(iter.next().expect("first").expect("ok").1, Some(Value::Str("hello!")));
assert_eq!(iter.next().expect("second").expect("ok").1, Some(Value::Str("hello1!")));
assert_eq!(iter.next().expect("first").expect("ok").1, Value::Str("hello!"));
assert_eq!(iter.next().expect("second").expect("ok").1, Value::Str("hello1!"));
assert!(iter.next().is_none());
}
}
@ -436,8 +436,8 @@ mod tests_safe {
s.put(&mut writer, 1, &Value::Str("hello1!")).expect("write");
let mut iter = s.get(&writer, 1).expect("read");
assert_eq!(iter.next().expect("first").expect("ok").1, Some(Value::Str("hello!")));
assert_eq!(iter.next().expect("second").expect("ok").1, Some(Value::Str("hello1!")));
assert_eq!(iter.next().expect("first").expect("ok").1, Value::Str("hello!"));
assert_eq!(iter.next().expect("second").expect("ok").1, Value::Str("hello1!"));
assert!(iter.next().is_none());
}
}
@ -457,8 +457,8 @@ mod tests_safe {
s.put(&mut writer, 1, &Value::Str("hello1!")).expect("write");
{
let mut iter = s.get(&writer, 1).expect("read");
assert_eq!(iter.next().expect("first").expect("ok").1, Some(Value::Str("hello!")));
assert_eq!(iter.next().expect("second").expect("ok").1, Some(Value::Str("hello1!")));
assert_eq!(iter.next().expect("first").expect("ok").1, Value::Str("hello!"));
assert_eq!(iter.next().expect("second").expect("ok").1, Value::Str("hello1!"));
assert!(iter.next().is_none());
}
writer.commit().expect("committed");
@ -471,7 +471,7 @@ mod tests_safe {
let reader = k.read().expect("reader");
let mut iter = s.get(&reader, 1).expect("read");
assert_eq!(iter.next().expect("first").expect("ok").1, Some(Value::Str("hello1!")));
assert_eq!(iter.next().expect("first").expect("ok").1, Value::Str("hello1!"));
assert!(iter.next().is_none());
}
@ -482,7 +482,7 @@ mod tests_safe {
let reader = k.read().expect("reader");
let mut iter = s.get(&reader, 1).expect("read");
assert_eq!(iter.next().expect("first").expect("ok").1, Some(Value::Str("hello1!")));
assert_eq!(iter.next().expect("first").expect("ok").1, Value::Str("hello1!"));
assert!(iter.next().is_none());
}
@ -522,8 +522,8 @@ mod tests_safe {
s.put(&mut writer, 2, &Value::Str("hello!")).expect("write");
{
let mut iter = s.get(&writer, 1).expect("read");
assert_eq!(iter.next().expect("first").expect("ok").1, Some(Value::Str("hello!")));
assert_eq!(iter.next().expect("second").expect("ok").1, Some(Value::Str("hello1!")));
assert_eq!(iter.next().expect("first").expect("ok").1, Value::Str("hello!"));
assert_eq!(iter.next().expect("second").expect("ok").1, Value::Str("hello1!"));
assert!(iter.next().is_none());
}
writer.commit().expect("committed");
@ -535,8 +535,8 @@ mod tests_safe {
let reader = k.read().expect("reader");
let mut iter = s.get(&reader, 1).expect("read");
assert_eq!(iter.next().expect("first").expect("ok").1, Some(Value::Str("hello!")));
assert_eq!(iter.next().expect("second").expect("ok").1, Some(Value::Str("hello1!")));
assert_eq!(iter.next().expect("first").expect("ok").1, Value::Str("hello!"));
assert_eq!(iter.next().expect("second").expect("ok").1, Value::Str("hello1!"));
assert!(iter.next().is_none());
}
}

@ -123,7 +123,7 @@ impl<'i, I> Iterator for Iter<'i, I>
where
I: BackendIter<'i>,
{
type Item = Result<(&'i [u8], Option<Value<'i>>), StoreError>;
type Item = Result<(&'i [u8], Value<'i>), StoreError>;
fn next(&mut self) -> Option<Self::Item> {
match self.iter.next() {

@ -128,7 +128,7 @@ impl<'i, I> Iterator for Iter<'i, I>
where
I: BackendIter<'i>,
{
type Item = Result<(&'i [u8], Option<Value<'i>>), StoreError>;
type Item = Result<(&'i [u8], Value<'i>), StoreError>;
fn next(&mut self) -> Option<Self::Item> {
match self.iter.next() {

@ -194,6 +194,51 @@ fn test_open_with_capacity_2() {
let _zzz = k.open_single(None, StoreOptions::default()).expect("opened");
}
#[test]
fn test_list_dbs_1() {
let root = Builder::new().prefix("test_list_dbs").tempdir().expect("tempdir");
println!("Root path: {:?}", root.path());
fs::create_dir_all(root.path()).expect("dir created");
assert!(root.path().is_dir());
let k = Rkv::with_capacity::<Lmdb>(root.path(), 1).expect("rkv");
check_rkv(&k);
let dbs = k.get_dbs().unwrap();
assert_eq!(dbs, vec![Some("s".to_owned())]);
}
#[test]
fn test_list_dbs_2() {
let root = Builder::new().prefix("test_list_dbs").tempdir().expect("tempdir");
println!("Root path: {:?}", root.path());
fs::create_dir_all(root.path()).expect("dir created");
assert!(root.path().is_dir());
let k = Rkv::with_capacity::<Lmdb>(root.path(), 2).expect("rkv");
check_rkv(&k);
let _ = k.open_single("zzz", StoreOptions::create()).expect("opened");
let dbs = k.get_dbs().unwrap();
assert_eq!(dbs, vec![Some("s".to_owned()), Some("zzz".to_owned())]);
}
#[test]
fn test_list_dbs_3() {
let root = Builder::new().prefix("test_list_dbs").tempdir().expect("tempdir");
println!("Root path: {:?}", root.path());
fs::create_dir_all(root.path()).expect("dir created");
assert!(root.path().is_dir());
let k = Rkv::with_capacity::<Lmdb>(root.path(), 0).expect("rkv");
let _ = k.open_single(None, StoreOptions::create()).expect("opened");
let dbs = k.get_dbs().unwrap();
assert_eq!(dbs, vec![None]);
}
fn get_larger_than_default_map_size_value() -> usize {
// The LMDB C library and lmdb Rust crate docs for setting the map size
// <http://www.lmdb.tech/doc/group__mdb.html#gaa2506ec8dab3d969b0e609cd82e619e5>
@ -432,9 +477,9 @@ fn test_multi_put_get_del() {
{
let mut iter = multistore.get(&writer, "str1").unwrap();
let (id, val) = iter.next().unwrap().unwrap();
assert_eq!((id, val), (&b"str1"[..], Some(Value::Str("str1 bar"))));
assert_eq!((id, val), (&b"str1"[..], Value::Str("str1 bar")));
let (id, val) = iter.next().unwrap().unwrap();
assert_eq!((id, val), (&b"str1"[..], Some(Value::Str("str1 foo"))));
assert_eq!((id, val), (&b"str1"[..], Value::Str("str1 foo")));
}
writer.commit().unwrap();
@ -866,22 +911,22 @@ fn test_iter() {
let mut iter = sk.iter_start(&reader).unwrap();
let (key, val) = iter.next().unwrap().unwrap();
assert_eq!(str::from_utf8(key).expect("key"), "bar");
assert_eq!(val, Some(Value::Bool(true)));
assert_eq!(val, Value::Bool(true));
let (key, val) = iter.next().unwrap().unwrap();
assert_eq!(str::from_utf8(key).expect("key"), "baz");
assert_eq!(val, Some(Value::Str("héllo, yöu")));
assert_eq!(val, Value::Str("héllo, yöu"));
let (key, val) = iter.next().unwrap().unwrap();
assert_eq!(str::from_utf8(key).expect("key"), "foo");
assert_eq!(val, Some(Value::I64(1234)));
assert_eq!(val, Value::I64(1234));
let (key, val) = iter.next().unwrap().unwrap();
assert_eq!(str::from_utf8(key).expect("key"), "héllò, töűrîst");
assert_eq!(val, Some(Value::Str("Emil.RuleZ!")));
assert_eq!(val, Value::Str("Emil.RuleZ!"));
let (key, val) = iter.next().unwrap().unwrap();
assert_eq!(str::from_utf8(key).expect("key"), "noo");
assert_eq!(val, Some(Value::F64(1234.0.into())));
assert_eq!(val, Value::F64(1234.0.into()));
let (key, val) = iter.next().unwrap().unwrap();
assert_eq!(str::from_utf8(key).expect("key"), "你好,遊客");
assert_eq!(val, Some(Value::Str("米克規則")));
assert_eq!(val, Value::Str("米克規則"));
assert!(iter.next().is_none());
// Iterators don't loop. Once one returns None, additional calls
@ -893,10 +938,10 @@ fn test_iter() {
let mut iter = sk.iter_from(&reader, "moo").unwrap();
let (key, val) = iter.next().unwrap().unwrap();
assert_eq!(str::from_utf8(key).expect("key"), "noo");
assert_eq!(val, Some(Value::F64(1234.0.into())));
assert_eq!(val, Value::F64(1234.0.into()));
let (key, val) = iter.next().unwrap().unwrap();
assert_eq!(str::from_utf8(key).expect("key"), "你好,遊客");
assert_eq!(val, Some(Value::Str("米克規則")));
assert_eq!(val, Value::Str("米克規則"));
assert!(iter.next().is_none());
// Reader.iter_from() works as expected when the given key is a prefix
@ -904,10 +949,10 @@ fn test_iter() {
let mut iter = sk.iter_from(&reader, "no").unwrap();
let (key, val) = iter.next().unwrap().unwrap();
assert_eq!(str::from_utf8(key).expect("key"), "noo");
assert_eq!(val, Some(Value::F64(1234.0.into())));
assert_eq!(val, Value::F64(1234.0.into()));
let (key, val) = iter.next().unwrap().unwrap();
assert_eq!(str::from_utf8(key).expect("key"), "你好,遊客");
assert_eq!(val, Some(Value::Str("米克規則")));
assert_eq!(val, Value::Str("米克規則"));
assert!(iter.next().is_none());
}
@ -1002,84 +1047,84 @@ fn test_multiple_store_iter() {
let mut iter = s1.iter_start(&reader).unwrap();
let (key, val) = iter.next().unwrap().unwrap();
assert_eq!(str::from_utf8(key).expect("key"), "bar");
assert_eq!(val, Some(Value::Bool(true)));
assert_eq!(val, Value::Bool(true));
let (key, val) = iter.next().unwrap().unwrap();
assert_eq!(str::from_utf8(key).expect("key"), "baz");
assert_eq!(val, Some(Value::Str("héllo, yöu")));
assert_eq!(val, Value::Str("héllo, yöu"));
let (key, val) = iter.next().unwrap().unwrap();
assert_eq!(str::from_utf8(key).expect("key"), "foo");
assert_eq!(val, Some(Value::I64(1234)));
assert_eq!(val, Value::I64(1234));
let (key, val) = iter.next().unwrap().unwrap();
assert_eq!(str::from_utf8(key).expect("key"), "héllò, töűrîst");
assert_eq!(val, Some(Value::Str("Emil.RuleZ!")));
assert_eq!(val, Value::Str("Emil.RuleZ!"));
let (key, val) = iter.next().unwrap().unwrap();
assert_eq!(str::from_utf8(key).expect("key"), "noo");
assert_eq!(val, Some(Value::F64(1234.0.into())));
assert_eq!(val, Value::F64(1234.0.into()));
let (key, val) = iter.next().unwrap().unwrap();
assert_eq!(str::from_utf8(key).expect("key"), "你好,遊客");
assert_eq!(val, Some(Value::Str("米克規則")));
assert_eq!(val, Value::Str("米克規則"));
assert!(iter.next().is_none());
// Iterate through the whole store in "s2"
let mut iter = s2.iter_start(&reader).unwrap();
let (key, val) = iter.next().unwrap().unwrap();
assert_eq!(str::from_utf8(key).expect("key"), "bar");
assert_eq!(val, Some(Value::Bool(true)));
assert_eq!(val, Value::Bool(true));
let (key, val) = iter.next().unwrap().unwrap();
assert_eq!(str::from_utf8(key).expect("key"), "baz");
assert_eq!(val, Some(Value::Str("héllo, yöu")));
assert_eq!(val, Value::Str("héllo, yöu"));
let (key, val) = iter.next().unwrap().unwrap();
assert_eq!(str::from_utf8(key).expect("key"), "foo");
assert_eq!(val, Some(Value::I64(1234)));
assert_eq!(val, Value::I64(1234));
let (key, val) = iter.next().unwrap().unwrap();
assert_eq!(str::from_utf8(key).expect("key"), "héllò, töűrîst");
assert_eq!(val, Some(Value::Str("Emil.RuleZ!")));
assert_eq!(val, Value::Str("Emil.RuleZ!"));
let (key, val) = iter.next().unwrap().unwrap();
assert_eq!(str::from_utf8(key).expect("key"), "noo");
assert_eq!(val, Some(Value::F64(1234.0.into())));
assert_eq!(val, Value::F64(1234.0.into()));
let (key, val) = iter.next().unwrap().unwrap();
assert_eq!(str::from_utf8(key).expect("key"), "你好,遊客");
assert_eq!(val, Some(Value::Str("米克規則")));
assert_eq!(val, Value::Str("米克規則"));
assert!(iter.next().is_none());
// Iterate from a given key in "s1"
let mut iter = s1.iter_from(&reader, "moo").unwrap();
let (key, val) = iter.next().unwrap().unwrap();
assert_eq!(str::from_utf8(key).expect("key"), "noo");
assert_eq!(val, Some(Value::F64(1234.0.into())));
assert_eq!(val, Value::F64(1234.0.into()));
let (key, val) = iter.next().unwrap().unwrap();
assert_eq!(str::from_utf8(key).expect("key"), "你好,遊客");
assert_eq!(val, Some(Value::Str("米克規則")));
assert_eq!(val, Value::Str("米克規則"));
assert!(iter.next().is_none());
// Iterate from a given key in "s2"
let mut iter = s2.iter_from(&reader, "moo").unwrap();
let (key, val) = iter.next().unwrap().unwrap();
assert_eq!(str::from_utf8(key).expect("key"), "noo");
assert_eq!(val, Some(Value::F64(1234.0.into())));
assert_eq!(val, Value::F64(1234.0.into()));
let (key, val) = iter.next().unwrap().unwrap();
assert_eq!(str::from_utf8(key).expect("key"), "你好,遊客");
assert_eq!(val, Some(Value::Str("米克規則")));
assert_eq!(val, Value::Str("米克規則"));
assert!(iter.next().is_none());
// Iterate from a given prefix in "s1"
let mut iter = s1.iter_from(&reader, "no").unwrap();
let (key, val) = iter.next().unwrap().unwrap();
assert_eq!(str::from_utf8(key).expect("key"), "noo");
assert_eq!(val, Some(Value::F64(1234.0.into())));
assert_eq!(val, Value::F64(1234.0.into()));
let (key, val) = iter.next().unwrap().unwrap();
assert_eq!(str::from_utf8(key).expect("key"), "你好,遊客");
assert_eq!(val, Some(Value::Str("米克規則")));
assert_eq!(val, Value::Str("米克規則"));
assert!(iter.next().is_none());
// Iterate from a given prefix in "s2"
let mut iter = s2.iter_from(&reader, "no").unwrap();
let (key, val) = iter.next().unwrap().unwrap();
assert_eq!(str::from_utf8(key).expect("key"), "noo");
assert_eq!(val, Some(Value::F64(1234.0.into())));
assert_eq!(val, Value::F64(1234.0.into()));
let (key, val) = iter.next().unwrap().unwrap();
assert_eq!(str::from_utf8(key).expect("key"), "你好,遊客");
assert_eq!(val, Some(Value::Str("米克規則")));
assert_eq!(val, Value::Str("米克規則"));
assert!(iter.next().is_none());
}

@ -0,0 +1,138 @@
// Copyright 2018-2019 Mozilla
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use
// this file except in compliance with the License. You may obtain a copy of the
// License at http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software distributed
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
// specific language governing permissions and limitations under the License.
use std::fs;
use tempfile::Builder;
use rkv::{
backend::{
BackendEnvironmentBuilder,
Lmdb,
SafeMode,
},
migrator::Migrator,
Rkv,
StoreOptions,
Value,
};
macro_rules! populate_store {
($env:expr) => {
let store = $env.open_single("store", StoreOptions::create()).expect("opened");
let mut writer = $env.write().expect("writer");
store.put(&mut writer, "foo", &Value::I64(1234)).expect("wrote");
store.put(&mut writer, "bar", &Value::Bool(true)).expect("wrote");
store.put(&mut writer, "baz", &Value::Str("héllo, yöu")).expect("wrote");
writer.commit().expect("committed");
};
}
#[test]
#[should_panic(expected = "new succeeded: EnvironmentDoesNotExistError")]
fn test_migrator_lmdb_to_safe_0() {
let mut builder = Lmdb::new();
builder.set_check_if_env_exists(true);
let root = Builder::new().prefix("test_migrate_lmdb_to_safe").tempdir().expect("tempdir");
let _ = Rkv::from_builder::<Lmdb>(root.path(), builder).expect("new succeeded");
}
#[test]
#[should_panic(expected = "migrated: SourceEmpty")]
fn test_migrator_lmdb_to_safe_1() {
let root = Builder::new().prefix("test_migrate_lmdb_to_safe").tempdir().expect("tempdir");
fs::create_dir_all(root.path()).expect("dir created");
let src_env = Rkv::new::<Lmdb>(root.path()).expect("new succeeded");
let dst_env = Rkv::new::<SafeMode>(root.path()).expect("new succeeded");
Migrator::migrate_lmdb_to_safe_mode(&src_env, &dst_env).expect("migrated");
}
#[test]
#[should_panic(expected = "migrated: DestinationNotEmpty")]
fn test_migrator_lmdb_to_safe_2() {
let root = Builder::new().prefix("test_migrate_lmdb_to_safe").tempdir().expect("tempdir");
fs::create_dir_all(root.path()).expect("dir created");
let src_env = Rkv::new::<Lmdb>(root.path()).expect("new succeeded");
populate_store!(&src_env);
let dst_env = Rkv::new::<SafeMode>(root.path()).expect("new succeeded");
populate_store!(&dst_env);
Migrator::migrate_lmdb_to_safe_mode(&src_env, &dst_env).expect("migrated");
}
#[test]
fn test_migrator_lmdb_to_safe_3() {
let root = Builder::new().prefix("test_migrate_lmdb_to_safe").tempdir().expect("tempdir");
fs::create_dir_all(root.path()).expect("dir created");
let src_env = Rkv::new::<Lmdb>(root.path()).expect("new succeeded");
populate_store!(&src_env);
let dst_env = Rkv::new::<SafeMode>(root.path()).expect("new succeeded");
Migrator::migrate_lmdb_to_safe_mode(&src_env, &dst_env).expect("migrated");
let store = dst_env.open_single("store", StoreOptions::default()).expect("opened");
let reader = dst_env.read().expect("reader");
assert_eq!(store.get(&reader, "foo").expect("read"), Some(Value::I64(1234)));
assert_eq!(store.get(&reader, "bar").expect("read"), Some(Value::Bool(true)));
assert_eq!(store.get(&reader, "baz").expect("read"), Some(Value::Str("héllo, yöu")));
}
#[test]
#[should_panic(expected = "new succeeded: EnvironmentDoesNotExistError")]
fn test_migrator_safe_to_lmdb_0() {
let mut builder = SafeMode::new();
builder.set_check_if_env_exists(true);
let root = Builder::new().prefix("test_migrate_safe_to_lmdb").tempdir().expect("tempdir");
let _ = Rkv::from_builder::<SafeMode>(root.path(), builder).expect("new succeeded");
}
#[test]
#[should_panic(expected = "migrated: SourceEmpty")]
fn test_migrator_safe_to_lmdb_1() {
let root = Builder::new().prefix("test_migrate_safe_to_lmdb").tempdir().expect("tempdir");
fs::create_dir_all(root.path()).expect("dir created");
let src_env = Rkv::new::<SafeMode>(root.path()).expect("new succeeded");
let dst_env = Rkv::new::<Lmdb>(root.path()).expect("new succeeded");
Migrator::migrate_safe_mode_to_lmdb(&src_env, &dst_env).expect("migrated");
}
#[test]
#[should_panic(expected = "migrated: DestinationNotEmpty")]
fn test_migrator_safe_to_lmdb_2() {
let root = Builder::new().prefix("test_migrate_safe_to_lmdb").tempdir().expect("tempdir");
fs::create_dir_all(root.path()).expect("dir created");
let src_env = Rkv::new::<SafeMode>(root.path()).expect("new succeeded");
populate_store!(&src_env);
let dst_env = Rkv::new::<Lmdb>(root.path()).expect("new succeeded");
populate_store!(&dst_env);
Migrator::migrate_safe_mode_to_lmdb(&src_env, &dst_env).expect("migrated");
}
#[test]
fn test_migrator_safe_to_lmdb_3() {
let root = Builder::new().prefix("test_migrate_safe_to_lmdb").tempdir().expect("tempdir");
fs::create_dir_all(root.path()).expect("dir created");
let src_env = Rkv::new::<SafeMode>(root.path()).expect("new succeeded");
populate_store!(&src_env);
let dst_env = Rkv::new::<Lmdb>(root.path()).expect("new succeeded");
Migrator::migrate_safe_mode_to_lmdb(&src_env, &dst_env).expect("migrated");
let store = dst_env.open_single("store", StoreOptions::default()).expect("opened");
let reader = dst_env.read().expect("reader");
assert_eq!(store.get(&reader, "foo").expect("read"), Some(Value::I64(1234)));
assert_eq!(store.get(&reader, "bar").expect("read"), Some(Value::Bool(true)));
assert_eq!(store.get(&reader, "baz").expect("read"), Some(Value::Str("héllo, yöu")));
}

@ -185,6 +185,54 @@ fn test_open_with_capacity_safe_2() {
let _zzz = k.open_single(None, StoreOptions::default()).expect("opened");
}
#[test]
fn test_list_dbs_safe_1() {
let root = Builder::new().prefix("test_list_dbs_safe").tempdir().expect("tempdir");
println!("Root path: {:?}", root.path());
fs::create_dir_all(root.path()).expect("dir created");
assert!(root.path().is_dir());
let k = Rkv::with_capacity::<SafeMode>(root.path(), 1).expect("rkv");
check_rkv(&k);
let mut dbs = k.get_dbs().unwrap();
dbs.sort();
assert_eq!(dbs, vec![None, Some("s".to_owned())]);
}
#[test]
fn test_list_dbs_safe_2() {
let root = Builder::new().prefix("test_list_dbs_safe").tempdir().expect("tempdir");
println!("Root path: {:?}", root.path());
fs::create_dir_all(root.path()).expect("dir created");
assert!(root.path().is_dir());
let k = Rkv::with_capacity::<SafeMode>(root.path(), 2).expect("rkv");
check_rkv(&k);
let _ = k.open_single("zzz", StoreOptions::create()).expect("opened");
let mut dbs = k.get_dbs().unwrap();
dbs.sort();
assert_eq!(dbs, vec![None, Some("s".to_owned()), Some("zzz".to_owned())]);
}
#[test]
fn test_list_dbs_safe_3() {
let root = Builder::new().prefix("test_list_dbs_safe").tempdir().expect("tempdir");
println!("Root path: {:?}", root.path());
fs::create_dir_all(root.path()).expect("dir created");
assert!(root.path().is_dir());
let k = Rkv::with_capacity::<SafeMode>(root.path(), 0).expect("rkv");
let _ = k.open_single(None, StoreOptions::create()).expect("opened");
let mut dbs = k.get_dbs().unwrap();
dbs.sort();
assert_eq!(dbs, vec![None]);
}
#[test]
fn test_round_trip_and_transactions_safe() {
let root = Builder::new().prefix("test_round_trip_and_transactions_safe").tempdir().expect("tempdir");
@ -352,9 +400,9 @@ fn test_multi_put_get_del_safe() {
{
let mut iter = multistore.get(&writer, "str1").unwrap();
let (id, val) = iter.next().unwrap().unwrap();
assert_eq!((id, val), (&b"str1"[..], Some(Value::Str("str1 bar"))));
assert_eq!((id, val), (&b"str1"[..], Value::Str("str1 bar")));
let (id, val) = iter.next().unwrap().unwrap();
assert_eq!((id, val), (&b"str1"[..], Some(Value::Str("str1 foo"))));
assert_eq!((id, val), (&b"str1"[..], Value::Str("str1 foo")));
}
writer.commit().unwrap();
@ -685,22 +733,22 @@ fn test_iter_safe() {
let mut iter = sk.iter_start(&reader).unwrap();
let (key, val) = iter.next().unwrap().unwrap();
assert_eq!(str::from_utf8(key).expect("key"), "bar");
assert_eq!(val, Some(Value::Bool(true)));
assert_eq!(val, Value::Bool(true));
let (key, val) = iter.next().unwrap().unwrap();
assert_eq!(str::from_utf8(key).expect("key"), "baz");
assert_eq!(val, Some(Value::Str("héllo, yöu")));
assert_eq!(val, Value::Str("héllo, yöu"));
let (key, val) = iter.next().unwrap().unwrap();
assert_eq!(str::from_utf8(key).expect("key"), "foo");
assert_eq!(val, Some(Value::I64(1234)));
assert_eq!(val, Value::I64(1234));
let (key, val) = iter.next().unwrap().unwrap();
assert_eq!(str::from_utf8(key).expect("key"), "héllò, töűrîst");
assert_eq!(val, Some(Value::Str("Emil.RuleZ!")));
assert_eq!(val, Value::Str("Emil.RuleZ!"));
let (key, val) = iter.next().unwrap().unwrap();
assert_eq!(str::from_utf8(key).expect("key"), "noo");
assert_eq!(val, Some(Value::F64(1234.0.into())));
assert_eq!(val, Value::F64(1234.0.into()));
let (key, val) = iter.next().unwrap().unwrap();
assert_eq!(str::from_utf8(key).expect("key"), "你好,遊客");
assert_eq!(val, Some(Value::Str("米克規則")));
assert_eq!(val, Value::Str("米克規則"));
assert!(iter.next().is_none());
// Iterators don't loop. Once one returns None, additional calls
@ -712,10 +760,10 @@ fn test_iter_safe() {
let mut iter = sk.iter_from(&reader, "moo").unwrap();
let (key, val) = iter.next().unwrap().unwrap();
assert_eq!(str::from_utf8(key).expect("key"), "noo");
assert_eq!(val, Some(Value::F64(1234.0.into())));
assert_eq!(val, Value::F64(1234.0.into()));
let (key, val) = iter.next().unwrap().unwrap();
assert_eq!(str::from_utf8(key).expect("key"), "你好,遊客");
assert_eq!(val, Some(Value::Str("米克規則")));
assert_eq!(val, Value::Str("米克規則"));
assert!(iter.next().is_none());
// Reader.iter_from() works as expected when the given key is a prefix
@ -723,10 +771,10 @@ fn test_iter_safe() {
let mut iter = sk.iter_from(&reader, "no").unwrap();
let (key, val) = iter.next().unwrap().unwrap();
assert_eq!(str::from_utf8(key).expect("key"), "noo");
assert_eq!(val, Some(Value::F64(1234.0.into())));
assert_eq!(val, Value::F64(1234.0.into()));
let (key, val) = iter.next().unwrap().unwrap();
assert_eq!(str::from_utf8(key).expect("key"), "你好,遊客");
assert_eq!(val, Some(Value::Str("米克規則")));
assert_eq!(val, Value::Str("米克規則"));
assert!(iter.next().is_none());
}
@ -822,84 +870,84 @@ fn test_multiple_store_iter_safe() {
let mut iter = s1.iter_start(&reader).unwrap();
let (key, val) = iter.next().unwrap().unwrap();
assert_eq!(str::from_utf8(key).expect("key"), "bar");
assert_eq!(val, Some(Value::Bool(true)));
assert_eq!(val, Value::Bool(true));
let (key, val) = iter.next().unwrap().unwrap();
assert_eq!(str::from_utf8(key).expect("key"), "baz");
assert_eq!(val, Some(Value::Str("héllo, yöu")));
assert_eq!(val, Value::Str("héllo, yöu"));
let (key, val) = iter.next().unwrap().unwrap();
assert_eq!(str::from_utf8(key).expect("key"), "foo");
assert_eq!(val, Some(Value::I64(1234)));
assert_eq!(val, Value::I64(1234));
let (key, val) = iter.next().unwrap().unwrap();
assert_eq!(str::from_utf8(key).expect("key"), "héllò, töűrîst");
assert_eq!(val, Some(Value::Str("Emil.RuleZ!")));
assert_eq!(val, Value::Str("Emil.RuleZ!"));
let (key, val) = iter.next().unwrap().unwrap();
assert_eq!(str::from_utf8(key).expect("key"), "noo");
assert_eq!(val, Some(Value::F64(1234.0.into())));
assert_eq!(val, Value::F64(1234.0.into()));
let (key, val) = iter.next().unwrap().unwrap();
assert_eq!(str::from_utf8(key).expect("key"), "你好,遊客");
assert_eq!(val, Some(Value::Str("米克規則")));
assert_eq!(val, Value::Str("米克規則"));
assert!(iter.next().is_none());
// Iterate through the whole store in "s2"
let mut iter = s2.iter_start(&reader).unwrap();
let (key, val) = iter.next().unwrap().unwrap();
assert_eq!(str::from_utf8(key).expect("key"), "bar");
assert_eq!(val, Some(Value::Bool(true)));
assert_eq!(val, Value::Bool(true));
let (key, val) = iter.next().unwrap().unwrap();
assert_eq!(str::from_utf8(key).expect("key"), "baz");
assert_eq!(val, Some(Value::Str("héllo, yöu")));
assert_eq!(val, Value::Str("héllo, yöu"));
let (key, val) = iter.next().unwrap().unwrap();
assert_eq!(str::from_utf8(key).expect("key"), "foo");
assert_eq!(val, Some(Value::I64(1234)));
assert_eq!(val, Value::I64(1234));
let (key, val) = iter.next().unwrap().unwrap();
assert_eq!(str::from_utf8(key).expect("key"), "héllò, töűrîst");
assert_eq!(val, Some(Value::Str("Emil.RuleZ!")));
assert_eq!(val, Value::Str("Emil.RuleZ!"));
let (key, val) = iter.next().unwrap().unwrap();
assert_eq!(str::from_utf8(key).expect("key"), "noo");
assert_eq!(val, Some(Value::F64(1234.0.into())));
assert_eq!(val, Value::F64(1234.0.into()));
let (key, val) = iter.next().unwrap().unwrap();
assert_eq!(str::from_utf8(key).expect("key"), "你好,遊客");
assert_eq!(val, Some(Value::Str("米克規則")));
assert_eq!(val, Value::Str("米克規則"));
assert!(iter.next().is_none());
// Iterate from a given key in "s1"
let mut iter = s1.iter_from(&reader, "moo").unwrap();
let (key, val) = iter.next().unwrap().unwrap();
assert_eq!(str::from_utf8(key).expect("key"), "noo");
assert_eq!(val, Some(Value::F64(1234.0.into())));
assert_eq!(val, Value::F64(1234.0.into()));
let (key, val) = iter.next().unwrap().unwrap();
assert_eq!(str::from_utf8(key).expect("key"), "你好,遊客");
assert_eq!(val, Some(Value::Str("米克規則")));
assert_eq!(val, Value::Str("米克規則"));
assert!(iter.next().is_none());
// Iterate from a given key in "s2"
let mut iter = s2.iter_from(&reader, "moo").unwrap();
let (key, val) = iter.next().unwrap().unwrap();
assert_eq!(str::from_utf8(key).expect("key"), "noo");
assert_eq!(val, Some(Value::F64(1234.0.into())));
assert_eq!(val, Value::F64(1234.0.into()));
let (key, val) = iter.next().unwrap().unwrap();
assert_eq!(str::from_utf8(key).expect("key"), "你好,遊客");
assert_eq!(val, Some(Value::Str("米克規則")));
assert_eq!(val, Value::Str("米克規則"));
assert!(iter.next().is_none());
// Iterate from a given prefix in "s1"
let mut iter = s1.iter_from(&reader, "no").unwrap();
let (key, val) = iter.next().unwrap().unwrap();
assert_eq!(str::from_utf8(key).expect("key"), "noo");
assert_eq!(val, Some(Value::F64(1234.0.into())));
assert_eq!(val, Value::F64(1234.0.into()));
let (key, val) = iter.next().unwrap().unwrap();
assert_eq!(str::from_utf8(key).expect("key"), "你好,遊客");
assert_eq!(val, Some(Value::Str("米克規則")));
assert_eq!(val, Value::Str("米克規則"));
assert!(iter.next().is_none());
// Iterate from a given prefix in "s2"
let mut iter = s2.iter_from(&reader, "no").unwrap();
let (key, val) = iter.next().unwrap().unwrap();
assert_eq!(str::from_utf8(key).expect("key"), "noo");
assert_eq!(val, Some(Value::F64(1234.0.into())));
assert_eq!(val, Value::F64(1234.0.into()));
let (key, val) = iter.next().unwrap().unwrap();
assert_eq!(str::from_utf8(key).expect("key"), "你好,遊客");
assert_eq!(val, Some(Value::Str("米克規則")));
assert_eq!(val, Value::Str("米克規則"));
assert!(iter.next().is_none());
}

@ -42,7 +42,7 @@ fn test_multi_integer_keys() {
.get(&writer, $key)
.expect("read")
.map(|result| result.expect("ok"))
.map(|(_, v)| v.expect("multi read"))
.map(|(_, v)| v)
.collect::<Vec<Value>>();
assert_eq!(vals, vec![Value::Str("hello1"), Value::Str("hello2"), Value::Str("hello3")]);
writer.commit().expect("committed");
@ -52,7 +52,7 @@ fn test_multi_integer_keys() {
.get(&reader, $key)
.expect("read")
.map(|result| result.expect("ok"))
.map(|(_, v)| v.expect("multi read"))
.map(|(_, v)| v)
.collect::<Vec<Value>>();
assert_eq!(vals, vec![Value::Str("hello1"), Value::Str("hello2"), Value::Str("hello3")]);
}};

@ -99,7 +99,7 @@ where
.expect("get iterator")
.map(|id| {
match id.expect("field") {
(_, Some(Value::U64(id))) => id,
(_, Value::U64(id)) => id,
_ => panic!("getting value in iter"),
}
})

Loading…
Cancel
Save