|
|
|
@ -10,7 +10,10 @@ |
|
|
|
|
|
|
|
|
|
use std::{ |
|
|
|
|
fs, |
|
|
|
|
path::Path, |
|
|
|
|
path::{ |
|
|
|
|
Path, |
|
|
|
|
PathBuf, |
|
|
|
|
}, |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
use lmdb::Error as LmdbError; |
|
|
|
@ -38,9 +41,10 @@ use crate::backend::traits::{ |
|
|
|
|
#[derive(Debug, PartialEq, Eq, Copy, Clone)] |
|
|
|
|
pub struct EnvironmentBuilderImpl { |
|
|
|
|
builder: lmdb::EnvironmentBuilder, |
|
|
|
|
envtype: EnvironmentType, |
|
|
|
|
env_path_type: EnvironmentPathType, |
|
|
|
|
env_lock_type: EnvironmentLockType, |
|
|
|
|
env_db_type: EnvironmentDefaultDbType, |
|
|
|
|
make_dir: bool, |
|
|
|
|
check_env_exists: bool, |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
impl<'b> BackendEnvironmentBuilder<'b> for EnvironmentBuilderImpl { |
|
|
|
@ -51,9 +55,10 @@ impl<'b> BackendEnvironmentBuilder<'b> for EnvironmentBuilderImpl { |
|
|
|
|
fn new() -> EnvironmentBuilderImpl { |
|
|
|
|
EnvironmentBuilderImpl { |
|
|
|
|
builder: lmdb::Environment::new(), |
|
|
|
|
envtype: EnvironmentType::SingleDatabase, |
|
|
|
|
env_path_type: EnvironmentPathType::SubDir, |
|
|
|
|
env_lock_type: EnvironmentLockType::Lockfile, |
|
|
|
|
env_db_type: EnvironmentDefaultDbType::SingleDatabase, |
|
|
|
|
make_dir: false, |
|
|
|
|
check_env_exists: false, |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -61,7 +66,14 @@ impl<'b> BackendEnvironmentBuilder<'b> for EnvironmentBuilderImpl { |
|
|
|
|
where |
|
|
|
|
T: Into<Self::Flags>, |
|
|
|
|
{ |
|
|
|
|
self.builder.set_flags(flags.into().0); |
|
|
|
|
let flags = flags.into(); |
|
|
|
|
if flags.0 == lmdb::EnvironmentFlags::NO_SUB_DIR { |
|
|
|
|
self.env_path_type = EnvironmentPathType::NoSubDir; |
|
|
|
|
} |
|
|
|
|
if flags.0 == lmdb::EnvironmentFlags::NO_LOCK { |
|
|
|
|
self.env_lock_type = EnvironmentLockType::NoLockfile; |
|
|
|
|
} |
|
|
|
|
self.builder.set_flags(flags.0); |
|
|
|
|
self |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -71,10 +83,10 @@ 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.env_db_type = EnvironmentDefaultDbType::MultipleNamedDatabases |
|
|
|
|
} |
|
|
|
|
self.builder.set_max_dbs(max_dbs); |
|
|
|
|
self |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -88,33 +100,73 @@ 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())); |
|
|
|
|
match self.env_path_type { |
|
|
|
|
EnvironmentPathType::NoSubDir => { |
|
|
|
|
if !path.is_file() { |
|
|
|
|
return Err(ErrorImpl::UnsuitableEnvironmentPath(path.into())); |
|
|
|
|
} |
|
|
|
|
}, |
|
|
|
|
EnvironmentPathType::SubDir => { |
|
|
|
|
if !path.is_dir() { |
|
|
|
|
if !self.make_dir { |
|
|
|
|
return Err(ErrorImpl::UnsuitableEnvironmentPath(path.into())); |
|
|
|
|
} |
|
|
|
|
fs::create_dir_all(path)?; |
|
|
|
|
} |
|
|
|
|
}, |
|
|
|
|
} |
|
|
|
|
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(|env| EnvironmentImpl(env, self.envtype)).map_err(ErrorImpl::LmdbError) |
|
|
|
|
|
|
|
|
|
self.builder.open(path).map_err(ErrorImpl::LmdbError).and_then(|lmdbenv| { |
|
|
|
|
EnvironmentImpl::new(path, self.env_path_type, self.env_lock_type, self.env_db_type, lmdbenv) |
|
|
|
|
}) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
#[derive(Debug, PartialEq, Eq, Copy, Clone)] |
|
|
|
|
enum EnvironmentType { |
|
|
|
|
pub enum EnvironmentPathType { |
|
|
|
|
SubDir, |
|
|
|
|
NoSubDir, |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
#[derive(Debug, PartialEq, Eq, Copy, Clone)] |
|
|
|
|
pub enum EnvironmentLockType { |
|
|
|
|
Lockfile, |
|
|
|
|
NoLockfile, |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
#[derive(Debug, PartialEq, Eq, Copy, Clone)] |
|
|
|
|
pub enum EnvironmentDefaultDbType { |
|
|
|
|
SingleDatabase, |
|
|
|
|
MultipleNamedDatabases, |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
#[derive(Debug)] |
|
|
|
|
pub struct EnvironmentImpl(lmdb::Environment, EnvironmentType); |
|
|
|
|
pub struct EnvironmentImpl { |
|
|
|
|
path: PathBuf, |
|
|
|
|
env_path_type: EnvironmentPathType, |
|
|
|
|
env_lock_type: EnvironmentLockType, |
|
|
|
|
env_db_type: EnvironmentDefaultDbType, |
|
|
|
|
lmdbenv: lmdb::Environment, |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
impl EnvironmentImpl { |
|
|
|
|
pub(crate) fn new( |
|
|
|
|
path: &Path, |
|
|
|
|
env_path_type: EnvironmentPathType, |
|
|
|
|
env_lock_type: EnvironmentLockType, |
|
|
|
|
env_db_type: EnvironmentDefaultDbType, |
|
|
|
|
lmdbenv: lmdb::Environment, |
|
|
|
|
) -> Result<EnvironmentImpl, ErrorImpl> { |
|
|
|
|
Ok(EnvironmentImpl { |
|
|
|
|
path: path.to_path_buf(), |
|
|
|
|
env_path_type, |
|
|
|
|
env_lock_type, |
|
|
|
|
env_db_type, |
|
|
|
|
lmdbenv, |
|
|
|
|
}) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
impl<'e> BackendEnvironment<'e> for EnvironmentImpl { |
|
|
|
|
type Database = DatabaseImpl; |
|
|
|
@ -126,10 +178,10 @@ impl<'e> BackendEnvironment<'e> for EnvironmentImpl { |
|
|
|
|
type Stat = StatImpl; |
|
|
|
|
|
|
|
|
|
fn get_dbs(&self) -> Result<Vec<Option<String>>, Self::Error> { |
|
|
|
|
if self.1 == EnvironmentType::SingleDatabase { |
|
|
|
|
if self.env_db_type == EnvironmentDefaultDbType::SingleDatabase { |
|
|
|
|
return Ok(vec![None]); |
|
|
|
|
} |
|
|
|
|
let db = self.0.open_db(None).map(DatabaseImpl).map_err(ErrorImpl::LmdbError)?; |
|
|
|
|
let db = self.lmdbenv.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(); |
|
|
|
@ -143,35 +195,35 @@ impl<'e> BackendEnvironment<'e> for EnvironmentImpl { |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
fn open_db(&self, name: Option<&str>) -> Result<Self::Database, Self::Error> { |
|
|
|
|
self.0.open_db(name).map(DatabaseImpl).map_err(ErrorImpl::LmdbError) |
|
|
|
|
self.lmdbenv.open_db(name).map(DatabaseImpl).map_err(ErrorImpl::LmdbError) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
fn create_db(&self, name: Option<&str>, flags: Self::Flags) -> Result<Self::Database, Self::Error> { |
|
|
|
|
self.0.create_db(name, flags.0).map(DatabaseImpl).map_err(ErrorImpl::LmdbError) |
|
|
|
|
self.lmdbenv.create_db(name, flags.0).map(DatabaseImpl).map_err(ErrorImpl::LmdbError) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
fn begin_ro_txn(&'e self) -> Result<Self::RoTransaction, Self::Error> { |
|
|
|
|
self.0.begin_ro_txn().map(RoTransactionImpl).map_err(ErrorImpl::LmdbError) |
|
|
|
|
self.lmdbenv.begin_ro_txn().map(RoTransactionImpl).map_err(ErrorImpl::LmdbError) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
fn begin_rw_txn(&'e self) -> Result<Self::RwTransaction, Self::Error> { |
|
|
|
|
self.0.begin_rw_txn().map(RwTransactionImpl).map_err(ErrorImpl::LmdbError) |
|
|
|
|
self.lmdbenv.begin_rw_txn().map(RwTransactionImpl).map_err(ErrorImpl::LmdbError) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
fn sync(&self, force: bool) -> Result<(), Self::Error> { |
|
|
|
|
self.0.sync(force).map_err(ErrorImpl::LmdbError) |
|
|
|
|
self.lmdbenv.sync(force).map_err(ErrorImpl::LmdbError) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
fn stat(&self) -> Result<Self::Stat, Self::Error> { |
|
|
|
|
self.0.stat().map(StatImpl).map_err(ErrorImpl::LmdbError) |
|
|
|
|
self.lmdbenv.stat().map(StatImpl).map_err(ErrorImpl::LmdbError) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
fn info(&self) -> Result<Self::Info, Self::Error> { |
|
|
|
|
self.0.info().map(InfoImpl).map_err(ErrorImpl::LmdbError) |
|
|
|
|
self.lmdbenv.info().map(InfoImpl).map_err(ErrorImpl::LmdbError) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
fn freelist(&self) -> Result<usize, Self::Error> { |
|
|
|
|
self.0.freelist().map_err(ErrorImpl::LmdbError) |
|
|
|
|
self.lmdbenv.freelist().map_err(ErrorImpl::LmdbError) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
fn load_ratio(&self) -> Result<Option<f32>, Self::Error> { |
|
|
|
@ -189,6 +241,29 @@ impl<'e> BackendEnvironment<'e> for EnvironmentImpl { |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
fn set_map_size(&self, size: usize) -> Result<(), Self::Error> { |
|
|
|
|
self.0.set_map_size(size).map_err(ErrorImpl::LmdbError) |
|
|
|
|
self.lmdbenv.set_map_size(size).map_err(ErrorImpl::LmdbError) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
fn get_files_on_disk(&self) -> Vec<PathBuf> { |
|
|
|
|
let mut store = vec![]; |
|
|
|
|
|
|
|
|
|
if self.env_path_type == EnvironmentPathType::NoSubDir { |
|
|
|
|
// The option NO_SUB_DIR could change the default directory layout; therefore this should
|
|
|
|
|
// probably return the path used to create environment, along with the custom lockfile
|
|
|
|
|
// when available.
|
|
|
|
|
unimplemented!(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
let mut db_filename = self.path.clone(); |
|
|
|
|
db_filename.push("data.mdb"); |
|
|
|
|
store.push(db_filename); |
|
|
|
|
|
|
|
|
|
if self.env_lock_type == EnvironmentLockType::Lockfile { |
|
|
|
|
let mut lock_filename = self.path.clone(); |
|
|
|
|
lock_filename.push("lock.mdb"); |
|
|
|
|
store.push(lock_filename); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
store |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|