Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introduce better support to acquire global database handles #154

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 9 additions & 9 deletions heed/src/db/uniform.rs → heed/src/db/database.rs
Original file line number Diff line number Diff line change
Expand Up @@ -114,13 +114,13 @@ use crate::*;
/// wtxn.commit()?;
/// # Ok(()) }
/// ```
pub struct Database<KC, DC> {
pub(crate) dyndb: PolyDatabase,
marker: marker::PhantomData<(KC, DC)>,
pub struct Database<'t, KC, DC> {
pub(crate) dyndb: PolyDatabase<'t>,
pub(crate) marker: marker::PhantomData<(KC, DC)>,
}

impl<KC, DC> Database<KC, DC> {
pub(crate) fn new(env_ident: usize, dbi: ffi::MDB_dbi) -> Database<KC, DC> {
impl<KC, DC> Database<'_, KC, DC> {
pub(crate) fn new<'t>(env_ident: usize, dbi: ffi::MDB_dbi) -> Database<'t, KC, DC> {
Database { dyndb: PolyDatabase::new(env_ident, dbi), marker: std::marker::PhantomData }
}

Expand Down Expand Up @@ -1604,15 +1604,15 @@ impl<KC, DC> Database<KC, DC> {
}
}

impl<KC, DC> Clone for Database<KC, DC> {
fn clone(&self) -> Database<KC, DC> {
impl<'t, KC, DC> Clone for Database<'t, KC, DC> {
fn clone(&self) -> Database<'t, KC, DC> {
Database { dyndb: self.dyndb, marker: marker::PhantomData }
}
}

impl<KC, DC> Copy for Database<KC, DC> {}
impl<KC, DC> Copy for Database<'_, KC, DC> {}

impl<KC, DC> fmt::Debug for Database<KC, DC> {
impl<KC, DC> fmt::Debug for Database<'_, KC, DC> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("Database")
.field("key_codec", &any::type_name::<KC>())
Expand Down
8 changes: 4 additions & 4 deletions heed/src/db/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
mod polymorph;
mod uniform;
mod database;
mod poly_database;

pub use self::polymorph::PolyDatabase;
pub use self::uniform::Database;
pub use self::database::Database;
pub use self::poly_database::PolyDatabase;
13 changes: 7 additions & 6 deletions heed/src/db/polymorph.rs → heed/src/db/poly_database.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use std::borrow::Cow;
use std::ops::{Bound, RangeBounds};
use std::{fmt, mem, ptr};
use std::{fmt, marker, mem, ptr};

use crate::mdb::error::mdb_result;
use crate::mdb::ffi;
Expand Down Expand Up @@ -103,14 +103,15 @@ use crate::*;
/// # Ok(()) }
/// ```
#[derive(Copy, Clone)]
pub struct PolyDatabase {
pub struct PolyDatabase<'t> {
pub(crate) env_ident: usize,
pub(crate) txn: marker::PhantomData<&'t ()>,
pub(crate) dbi: ffi::MDB_dbi,
}

impl PolyDatabase {
pub(crate) fn new(env_ident: usize, dbi: ffi::MDB_dbi) -> PolyDatabase {
PolyDatabase { env_ident, dbi }
impl PolyDatabase<'_> {
pub(crate) fn new<'t>(env_ident: usize, dbi: ffi::MDB_dbi) -> PolyDatabase<'t> {
PolyDatabase { env_ident, txn: marker::PhantomData, dbi }
}

/// Retrieves the value associated with a key.
Expand Down Expand Up @@ -1856,7 +1857,7 @@ impl PolyDatabase {
}
}

impl fmt::Debug for PolyDatabase {
impl fmt::Debug for PolyDatabase<'_> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("PolyDatabase").finish()
}
Expand Down
259 changes: 114 additions & 145 deletions heed/src/env.rs
Original file line number Diff line number Diff line change
Expand Up @@ -284,7 +284,7 @@ pub fn env_closing_event<P: AsRef<Path>>(path: P) -> Option<EnvClosingEvent> {

/// An environment handle constructed by using [`EnvOpenOptions`].
#[derive(Clone)]
pub struct Env(Arc<EnvInner>);
pub struct Env(pub(crate) Arc<EnvInner>);

impl fmt::Debug for Env {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
Expand All @@ -295,7 +295,9 @@ impl fmt::Debug for Env {

struct EnvInner {
env: *mut ffi::MDB_env,
dbi_open_mutex: sync::Mutex<HashMap<u32, Option<(TypeId, TypeId)>>>,
/// The first state of a database is `None` until an [`open_database`]
/// method is called to define the type for the first time.
pub(crate) dbi_open_mutex: sync::Mutex<HashMap<Option<String>, (u32, Option<DatabaseType>)>>,
path: PathBuf,
}

Expand All @@ -320,6 +322,16 @@ impl Drop for EnvInner {
}
}

/// The type of the database.
// TODO replace the UnknownYet by an Option<DatabaseType> in the HashMap
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum DatabaseType {
/// Defines the types of a [`Database`].
Typed { key_type: TypeId, data_type: TypeId },
/// Defines the types of a [`PolyDatabase`].
Untyped,
}

/// Whether to perform compaction while copying an environment.
#[derive(Debug, Copy, Clone)]
pub enum CompactionOption {
Expand Down Expand Up @@ -431,7 +443,7 @@ impl Env {
size += compute_size(stat);

// if the db wasn’t already opened
if !dbi_open.contains_key(&dbi) {
if !dbi_open.contains_key(&Some(key)) {
unsafe { ffi::mdb_dbi_close(self.env_mut_ptr(), dbi) }
}
}
Expand All @@ -440,148 +452,105 @@ impl Env {
Ok(size)
}

/// Opens a typed database that already exists in this environment.
///
/// If the database was previously opened in this program run, types will be checked.
///
/// ## Important Information
///
/// LMDB have an important restriction on the unnamed database when named ones are opened,
/// the names of the named databases are stored as keys in the unnamed one and are immutable,
/// these keys can only be read and not written.
pub fn open_database<KC, DC>(
&self,
rtxn: &RoTxn,
name: Option<&str>,
) -> Result<Option<Database<KC, DC>>>
where
KC: 'static,
DC: 'static,
{
assert_eq_env_txn!(self, rtxn);

let types = (TypeId::of::<KC>(), TypeId::of::<DC>());
match self.raw_init_database(rtxn.txn, name, Some(types), false) {
Ok(dbi) => Ok(Some(Database::new(self.env_mut_ptr() as _, dbi))),
Err(Error::Mdb(e)) if e.not_found() => Ok(None),
Err(e) => Err(e),
}
}

/// Opens an untyped database that already exists in this environment.
///
/// If the database was previously opened as a typed one, an error will be returned.
///
/// ## Important Information
///
/// LMDB have an important restriction on the unnamed database when named ones are opened,
/// the names of the named databases are stored as keys in the unnamed one and are immutable,
/// these keys can only be read and not written.
pub fn open_poly_database(
&self,
rtxn: &RoTxn,
name: Option<&str>,
) -> Result<Option<PolyDatabase>> {
assert_eq_env_txn!(self, rtxn);

match self.raw_init_database(rtxn.txn, name, None, false) {
Ok(dbi) => Ok(Some(PolyDatabase::new(self.env_mut_ptr() as _, dbi))),
Err(Error::Mdb(e)) if e.not_found() => Ok(None),
Err(e) => Err(e),
}
}

/// Creates a typed database that can already exist in this environment.
///
/// If the database was previously opened in this program run, types will be checked.
///
/// ## Important Information
///
/// LMDB have an important restriction on the unnamed database when named ones are opened,
/// the names of the named databases are stored as keys in the unnamed one and are immutable,
/// these keys can only be read and not written.
pub fn create_database<KC, DC>(
&self,
wtxn: &mut RwTxn,
name: Option<&str>,
) -> Result<Database<KC, DC>>
where
KC: 'static,
DC: 'static,
{
assert_eq_env_txn!(self, wtxn);

let types = (TypeId::of::<KC>(), TypeId::of::<DC>());
match self.raw_init_database(wtxn.txn.txn, name, Some(types), true) {
Ok(dbi) => Ok(Database::new(self.env_mut_ptr() as _, dbi)),
Err(e) => Err(e),
}
}

/// Creates a typed database that can already exist in this environment.
///
/// If the database was previously opened as a typed one, an error will be returned.
///
/// ## Important Information
///
/// LMDB have an important restriction on the unnamed database when named ones are opened,
/// the names of the named databases are stored as keys in the unnamed one and are immutable,
/// these keys can only be read and not written.
pub fn create_poly_database(
&self,
wtxn: &mut RwTxn,
name: Option<&str>,
) -> Result<PolyDatabase> {
assert_eq_env_txn!(self, wtxn);

match self.raw_init_database(wtxn.txn.txn, name, None, true) {
Ok(dbi) => Ok(PolyDatabase::new(self.env_mut_ptr() as _, dbi)),
Err(e) => Err(e),
}
}

fn raw_open_dbi(
&self,
raw_txn: *mut ffi::MDB_txn,
name: Option<&str>,
flags: u32,
) -> std::result::Result<u32, crate::mdb::lmdb_error::Error> {
let mut dbi = 0;
let name = name.map(|n| CString::new(n).unwrap());
let name_ptr = match name {
Some(ref name) => name.as_bytes_with_nul().as_ptr() as *const _,
None => ptr::null(),
};

// safety: The name cstring is cloned by LMDB, we can drop it after.
// If a read-only is used with the MDB_CREATE flag, LMDB will throw an error.
unsafe { mdb_result(ffi::mdb_dbi_open(raw_txn, name_ptr, flags, &mut dbi))? };

Ok(dbi)
}

fn raw_init_database(
&self,
raw_txn: *mut ffi::MDB_txn,
name: Option<&str>,
types: Option<(TypeId, TypeId)>,
create: bool,
) -> Result<u32> {
let mut lock = self.0.dbi_open_mutex.lock().unwrap();

let flags = if create { ffi::MDB_CREATE } else { 0 };
match self.raw_open_dbi(raw_txn, name, flags) {
Ok(dbi) => {
let old_types = lock.entry(dbi).or_insert(types);
if *old_types == types {
Ok(dbi)
} else {
Err(Error::InvalidDatabaseTyping)
}
}
Err(e) => Err(e.into()),
}
}
// /// Opens a typed database that already exists in this environment.
// ///
// /// If the database was previously opened in this program run, types will be checked.
// ///
// /// ## Important Information
// ///
// /// LMDB have an important restriction on the unnamed database when named ones are opened,
// /// the names of the named databases are stored as keys in the unnamed one and are immutable,
// /// these keys can only be read and not written.
// pub fn open_database<'t, KC, DC>(
// &self,
// rtxn: &'t RoTxn,
// name: Option<&str>,
// ) -> Result<Option<Database<'t, KC, DC>>>
// where
// KC: 'static,
// DC: 'static,
// {
// assert_eq_env_txn!(self, rtxn);

// let types = (TypeId::of::<KC>(), TypeId::of::<DC>());
// match self.raw_init_database(rtxn.txn, name, Some(types), false) {
// Ok(dbi) => Ok(Some(Database::new(self.env_mut_ptr() as _, dbi))),
// Err(Error::Mdb(e)) if e.not_found() => Ok(None),
// Err(e) => Err(e),
// }
// }

// /// Opens an untyped database that already exists in this environment.
// ///
// /// If the database was previously opened as a typed one, an error will be returned.
// ///
// /// ## Important Information
// ///
// /// LMDB have an important restriction on the unnamed database when named ones are opened,
// /// the names of the named databases are stored as keys in the unnamed one and are immutable,
// /// these keys can only be read and not written.
// pub fn open_poly_database<'t>(
// &self,
// rtxn: &'t RoTxn,
// name: Option<&str>,
// ) -> Result<Option<PolyDatabase<'t>>> {
// assert_eq_env_txn!(self, rtxn);

// match self.raw_init_database(rtxn.txn, name, None, false) {
// Ok(dbi) => Ok(Some(PolyDatabase::new(self.env_mut_ptr() as _, dbi))),
// Err(Error::Mdb(e)) if e.not_found() => Ok(None),
// Err(e) => Err(e),
// }
// }

// /// Creates a typed database that can already exist in this environment.
// ///
// /// If the database was previously opened in this program run, types will be checked.
// ///
// /// ## Important Information
// ///
// /// LMDB have an important restriction on the unnamed database when named ones are opened,
// /// the names of the named databases are stored as keys in the unnamed one and are immutable,
// /// these keys can only be read and not written.
// pub fn create_database<'t, KC, DC>(
// &self,
// wtxn: &'t mut RwTxn,
// name: Option<&str>,
// ) -> Result<Database<'t, KC, DC>>
// where
// KC: 'static,
// DC: 'static,
// {
// assert_eq_env_txn!(self, wtxn);

// let types = (TypeId::of::<KC>(), TypeId::of::<DC>());
// match self.raw_init_database(wtxn.txn.txn, name, Some(types), true) {
// Ok(dbi) => Ok(Database::new(self.env_mut_ptr() as _, dbi)),
// Err(e) => Err(e),
// }
// }

// /// Creates a typed database that can already exist in this environment.
// ///
// /// If the database was previously opened as a typed one, an error will be returned.
// ///
// /// ## Important Information
// ///
// /// LMDB have an important restriction on the unnamed database when named ones are opened,
// /// the names of the named databases are stored as keys in the unnamed one and are immutable,
// /// these keys can only be read and not written.
// pub fn create_poly_database<'t>(
// &self,
// wtxn: &'t mut RwTxn,
// name: Option<&str>,
// ) -> Result<PolyDatabase<'t>> {
// assert_eq_env_txn!(self, wtxn);

// match self.raw_init_database(wtxn.txn.txn, name, None, true) {
// Ok(dbi) => Ok(PolyDatabase::new(self.env_mut_ptr() as _, dbi)),
// Err(e) => Err(e),
// }
// }

/// Create a transaction with read and write access for use with the environment.
pub fn write_txn(&self) -> Result<RwTxn> {
Expand Down
Loading