refactor: make use of dbi consistent across mdbx interface (#21079)

This commit is contained in:
James Prestwich
2026-01-14 18:42:42 -05:00
committed by GitHub
parent b9ff5941eb
commit 1265a89c21
10 changed files with 103 additions and 96 deletions

View File

@@ -100,7 +100,7 @@ impl<N: NodeTypes> TableViewer<()> for ListTableViewer<'_, N> {
tx.disable_long_read_transaction_safety();
let table_db = tx.inner.open_db(Some(self.args.table.name())).wrap_err("Could not open db.")?;
let stats = tx.inner.db_stat(&table_db).wrap_err(format!("Could not find table: {}", self.args.table.name()))?;
let stats = tx.inner.db_stat(table_db.dbi()).wrap_err(format!("Could not find table: {}", self.args.table.name()))?;
let total_entries = stats.entries();
let final_entry_idx = total_entries.saturating_sub(1);
if self.args.skip > final_entry_idx {

View File

@@ -88,7 +88,7 @@ impl Command {
let stats = tx
.inner
.db_stat(&table_db)
.db_stat(table_db.dbi())
.wrap_err(format!("Could not find table: {db_table}"))?;
// Defaults to 16KB right now but we should
@@ -129,7 +129,8 @@ impl Command {
table.add_row(row);
let freelist = tx.inner.env().freelist()?;
let pagesize = tx.inner.db_stat(&mdbx::Database::freelist_db())?.page_size() as usize;
let pagesize =
tx.inner.db_stat(mdbx::Database::freelist_db().dbi())?.page_size() as usize;
let freelist_size = freelist * pagesize;
let mut row = Row::new();

View File

@@ -248,7 +248,7 @@ where
println!(
"{:?}\n",
tx.inner
.db_stat(&table_db)
.db_stat(table_db.dbi())
.map_err(|_| format!("Could not find table: {}", T::NAME))
.map(|stats| {
let num_pages =

View File

@@ -278,7 +278,7 @@ impl DatabaseMetrics for DatabaseEnv {
let stats = tx
.inner
.db_stat(&table_db)
.db_stat(table_db.dbi())
.wrap_err(format!("Could not find table: {table}"))?;
let page_size = stats.page_size() as usize;

View File

@@ -67,18 +67,25 @@ impl<K: TransactionKind> Tx<K> {
self.metrics_handler.as_ref().map_or_else(|| self.inner.id(), |handler| Ok(handler.txn_id))
}
/// Gets a table database handle if it exists, otherwise creates it.
pub fn get_dbi<T: Table>(&self) -> Result<MDBX_dbi, DatabaseError> {
if let Some(dbi) = self.dbis.get(T::NAME) {
/// Gets a table database handle by name if it exists, otherwise, check the
/// database, opening the DB if it exists.
pub fn get_dbi_raw(&self, name: &str) -> Result<MDBX_dbi, DatabaseError> {
if let Some(dbi) = self.dbis.get(name) {
Ok(*dbi)
} else {
self.inner
.open_db(Some(T::NAME))
.open_db(Some(name))
.map(|db| db.dbi())
.map_err(|e| DatabaseError::Open(e.into()))
}
}
/// Gets a table database handle by name if it exists, otherwise, check the
/// database, opening the DB if it exists.
pub fn get_dbi<T: Table>(&self) -> Result<MDBX_dbi, DatabaseError> {
self.get_dbi_raw(T::NAME)
}
/// Create db Cursor
pub fn new_cursor<T: Table>(&self) -> Result<Cursor<K, T>, DatabaseError> {
let inner = self

View File

@@ -12,10 +12,10 @@ fn bench_get_seq_iter(c: &mut Criterion) {
let (_dir, env) = setup_bench_db(n);
let txn = env.begin_ro_txn().unwrap();
let db = txn.open_db(None).unwrap();
let dbi = db.dbi();
c.bench_function("bench_get_seq_iter", |b| {
b.iter(|| {
let mut cursor = txn.cursor(&db).unwrap();
let mut cursor = txn.cursor(dbi).unwrap();
let mut i = 0;
let mut count = 0u32;
@@ -54,11 +54,11 @@ fn bench_get_seq_cursor(c: &mut Criterion) {
let (_dir, env) = setup_bench_db(n);
let txn = env.begin_ro_txn().unwrap();
let db = txn.open_db(None).unwrap();
let dbi = db.dbi();
c.bench_function("bench_get_seq_cursor", |b| {
b.iter(|| {
let (i, count) = txn
.cursor(&db)
.cursor(dbi)
.unwrap()
.iter::<ObjectLength, ObjectLength>()
.map(Result::unwrap)

View File

@@ -211,7 +211,7 @@ impl Environment {
let mut freelist: usize = 0;
let txn = self.begin_ro_txn()?;
let db = Database::freelist_db();
let cursor = txn.cursor(&db)?;
let cursor = txn.cursor(db.dbi())?;
for result in cursor.iter_slices() {
let (_key, value) = result?;

View File

@@ -208,11 +208,11 @@ where
}
/// Gets the option flags for the given database in the transaction.
pub fn db_flags(&self, db: &Database) -> Result<DatabaseFlags> {
pub fn db_flags(&self, dbi: ffi::MDBX_dbi) -> Result<DatabaseFlags> {
let mut flags: c_uint = 0;
unsafe {
self.txn_execute(|txn| {
mdbx_result(ffi::mdbx_dbi_flags_ex(txn, db.dbi(), &mut flags, ptr::null_mut()))
mdbx_result(ffi::mdbx_dbi_flags_ex(txn, dbi, &mut flags, ptr::null_mut()))
})??;
}
@@ -222,8 +222,8 @@ where
}
/// Retrieves database statistics.
pub fn db_stat(&self, db: &Database) -> Result<Stat> {
self.db_stat_with_dbi(db.dbi())
pub fn db_stat(&self, dbi: ffi::MDBX_dbi) -> Result<Stat> {
self.db_stat_with_dbi(dbi)
}
/// Retrieves database statistics by the given dbi.
@@ -238,8 +238,8 @@ where
}
/// Open a new cursor on the given database.
pub fn cursor(&self, db: &Database) -> Result<Cursor<K>> {
Cursor::new(self.clone(), db.dbi())
pub fn cursor(&self, dbi: ffi::MDBX_dbi) -> Result<Cursor<K>> {
Cursor::new(self.clone(), dbi)
}
/// Open a new cursor on the given dbi.
@@ -400,7 +400,7 @@ impl Transaction<RW> {
#[allow(clippy::mut_from_ref)]
pub fn reserve(
&self,
db: &Database,
dbi: ffi::MDBX_dbi,
key: impl AsRef<[u8]>,
len: usize,
flags: WriteFlags,
@@ -412,13 +412,7 @@ impl Transaction<RW> {
ffi::MDBX_val { iov_len: len, iov_base: ptr::null_mut::<c_void>() };
unsafe {
mdbx_result(self.txn_execute(|txn| {
ffi::mdbx_put(
txn,
db.dbi(),
&key_val,
&mut data_val,
flags.bits() | ffi::MDBX_RESERVE,
)
ffi::mdbx_put(txn, dbi, &key_val, &mut data_val, flags.bits() | ffi::MDBX_RESERVE)
})?)?;
Ok(slice::from_raw_parts_mut(data_val.iov_base as *mut u8, data_val.iov_len))
}
@@ -473,10 +467,10 @@ impl Transaction<RW> {
/// Drops the database from the environment.
///
/// # Safety
/// Caller must close ALL other [Database] and [Cursor] instances pointing to the same dbi
/// BEFORE calling this function.
pub unsafe fn drop_db(&self, db: Database) -> Result<()> {
mdbx_result(self.txn_execute(|txn| unsafe { ffi::mdbx_drop(txn, db.dbi(), true) })?)?;
/// Caller must close ALL other [Database] and [Cursor] instances pointing
/// to the same dbi BEFORE calling this function.
pub unsafe fn drop_db(&self, dbi: ffi::MDBX_dbi) -> Result<()> {
mdbx_result(self.txn_execute(|txn| unsafe { ffi::mdbx_drop(txn, dbi, true) })?)?;
Ok(())
}
@@ -488,8 +482,8 @@ impl Transaction<RO> {
/// # Safety
/// Caller must close ALL other [Database] and [Cursor] instances pointing to the same dbi
/// BEFORE calling this function.
pub unsafe fn close_db(&self, db: Database) -> Result<()> {
mdbx_result(unsafe { ffi::mdbx_dbi_close(self.env().env_ptr(), db.dbi()) })?;
pub unsafe fn close_db(&self, dbi: ffi::MDBX_dbi) -> Result<()> {
mdbx_result(unsafe { ffi::mdbx_dbi_close(self.env().env_ptr(), dbi) })?;
Ok(())
}

View File

@@ -9,15 +9,15 @@ fn test_get() {
let env = Environment::builder().open(dir.path()).unwrap();
let txn = env.begin_rw_txn().unwrap();
let db = txn.open_db(None).unwrap();
let dbi = txn.open_db(None).unwrap().dbi();
assert_eq!(None, txn.cursor(&db).unwrap().first::<(), ()>().unwrap());
assert_eq!(None, txn.cursor(dbi).unwrap().first::<(), ()>().unwrap());
txn.put(db.dbi(), b"key1", b"val1", WriteFlags::empty()).unwrap();
txn.put(db.dbi(), b"key2", b"val2", WriteFlags::empty()).unwrap();
txn.put(db.dbi(), b"key3", b"val3", WriteFlags::empty()).unwrap();
txn.put(dbi, b"key1", b"val1", WriteFlags::empty()).unwrap();
txn.put(dbi, b"key2", b"val2", WriteFlags::empty()).unwrap();
txn.put(dbi, b"key3", b"val3", WriteFlags::empty()).unwrap();
let mut cursor = txn.cursor(&db).unwrap();
let mut cursor = txn.cursor(dbi).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")));
@@ -34,15 +34,15 @@ fn test_get_dup() {
let env = Environment::builder().open(dir.path()).unwrap();
let txn = env.begin_rw_txn().unwrap();
let db = txn.create_db(None, DatabaseFlags::DUP_SORT).unwrap();
txn.put(db.dbi(), b"key1", b"val1", WriteFlags::empty()).unwrap();
txn.put(db.dbi(), b"key1", b"val2", WriteFlags::empty()).unwrap();
txn.put(db.dbi(), b"key1", b"val3", WriteFlags::empty()).unwrap();
txn.put(db.dbi(), b"key2", b"val1", WriteFlags::empty()).unwrap();
txn.put(db.dbi(), b"key2", b"val2", WriteFlags::empty()).unwrap();
txn.put(db.dbi(), b"key2", b"val3", WriteFlags::empty()).unwrap();
let dbi = txn.create_db(None, DatabaseFlags::DUP_SORT).unwrap().dbi();
txn.put(dbi, b"key1", b"val1", WriteFlags::empty()).unwrap();
txn.put(dbi, b"key1", b"val2", WriteFlags::empty()).unwrap();
txn.put(dbi, b"key1", b"val3", WriteFlags::empty()).unwrap();
txn.put(dbi, b"key2", b"val1", WriteFlags::empty()).unwrap();
txn.put(dbi, b"key2", b"val2", WriteFlags::empty()).unwrap();
txn.put(dbi, b"key2", b"val3", WriteFlags::empty()).unwrap();
let mut cursor = txn.cursor(&db).unwrap();
let mut cursor = txn.cursor(dbi).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")));
@@ -78,15 +78,16 @@ fn test_get_dupfixed() {
let env = Environment::builder().open(dir.path()).unwrap();
let txn = env.begin_rw_txn().unwrap();
let db = txn.create_db(None, DatabaseFlags::DUP_SORT | DatabaseFlags::DUP_FIXED).unwrap();
txn.put(db.dbi(), b"key1", b"val1", WriteFlags::empty()).unwrap();
txn.put(db.dbi(), b"key1", b"val2", WriteFlags::empty()).unwrap();
txn.put(db.dbi(), b"key1", b"val3", WriteFlags::empty()).unwrap();
txn.put(db.dbi(), b"key2", b"val4", WriteFlags::empty()).unwrap();
txn.put(db.dbi(), b"key2", b"val5", WriteFlags::empty()).unwrap();
txn.put(db.dbi(), b"key2", b"val6", WriteFlags::empty()).unwrap();
let dbi =
txn.create_db(None, DatabaseFlags::DUP_SORT | DatabaseFlags::DUP_FIXED).unwrap().dbi();
txn.put(dbi, b"key1", b"val1", WriteFlags::empty()).unwrap();
txn.put(dbi, b"key1", b"val2", WriteFlags::empty()).unwrap();
txn.put(dbi, b"key1", b"val3", WriteFlags::empty()).unwrap();
txn.put(dbi, b"key2", b"val4", WriteFlags::empty()).unwrap();
txn.put(dbi, b"key2", b"val5", WriteFlags::empty()).unwrap();
txn.put(dbi, b"key2", b"val6", WriteFlags::empty()).unwrap();
let mut cursor = txn.cursor(&db).unwrap();
let mut cursor = txn.cursor(dbi).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);
@@ -114,8 +115,8 @@ fn test_iter() {
}
let txn = env.begin_ro_txn().unwrap();
let db = txn.open_db(None).unwrap();
let mut cursor = txn.cursor(&db).unwrap();
let dbi = txn.open_db(None).unwrap().dbi();
let mut cursor = txn.cursor(dbi).unwrap();
// Because Result implements FromIterator, we can collect the iterator
// of items of type Result<_, E> into a Result<Vec<_, E>> by specifying
@@ -155,8 +156,8 @@ fn test_iter_empty_database() {
let dir = tempdir().unwrap();
let env = Environment::builder().open(dir.path()).unwrap();
let txn = env.begin_ro_txn().unwrap();
let db = txn.open_db(None).unwrap();
let mut cursor = txn.cursor(&db).unwrap();
let dbi = txn.open_db(None).unwrap().dbi();
let mut cursor = txn.cursor(dbi).unwrap();
assert!(cursor.iter::<(), ()>().next().is_none());
assert!(cursor.iter_start::<(), ()>().next().is_none());
@@ -173,8 +174,8 @@ fn test_iter_empty_dup_database() {
txn.commit().unwrap();
let txn = env.begin_ro_txn().unwrap();
let db = txn.open_db(None).unwrap();
let mut cursor = txn.cursor(&db).unwrap();
let dbi = txn.open_db(None).unwrap().dbi();
let mut cursor = txn.cursor(dbi).unwrap();
assert!(cursor.iter::<(), ()>().next().is_none());
assert!(cursor.iter_start::<(), ()>().next().is_none());
@@ -223,8 +224,8 @@ fn test_iter_dup() {
}
let txn = env.begin_ro_txn().unwrap();
let db = txn.open_db(None).unwrap();
let mut cursor = txn.cursor(&db).unwrap();
let dbi = txn.open_db(None).unwrap().dbi();
let mut cursor = txn.cursor(dbi).unwrap();
assert_eq!(items, cursor.iter_dup().flatten().collect::<Result<Vec<_>>>().unwrap());
cursor.set::<()>(b"b").unwrap();
@@ -271,9 +272,9 @@ fn test_iter_del_get() {
let items = vec![(*b"a", *b"1"), (*b"b", *b"2")];
{
let txn = env.begin_rw_txn().unwrap();
let db = txn.create_db(None, DatabaseFlags::DUP_SORT).unwrap();
let dbi = txn.create_db(None, DatabaseFlags::DUP_SORT).unwrap().dbi();
assert_eq!(
txn.cursor(&db)
txn.cursor(dbi)
.unwrap()
.iter_dup_of::<(), ()>(b"a")
.collect::<Result<Vec<_>>>()
@@ -294,8 +295,8 @@ fn test_iter_del_get() {
}
let txn = env.begin_rw_txn().unwrap();
let db = txn.open_db(None).unwrap();
let mut cursor = txn.cursor(&db).unwrap();
let dbi = txn.open_db(None).unwrap().dbi();
let mut cursor = txn.cursor(dbi).unwrap();
assert_eq!(items, cursor.iter_dup().flatten().collect::<Result<Vec<_>>>().unwrap());
assert_eq!(
@@ -316,8 +317,8 @@ fn test_put_del() {
let env = Environment::builder().open(dir.path()).unwrap();
let txn = env.begin_rw_txn().unwrap();
let db = txn.open_db(None).unwrap();
let mut cursor = txn.cursor(&db).unwrap();
let dbi = txn.open_db(None).unwrap().dbi();
let mut cursor = txn.cursor(dbi).unwrap();
cursor.put(b"key1", b"val1", WriteFlags::empty()).unwrap();
cursor.put(b"key2", b"val2", WriteFlags::empty()).unwrap();

View File

@@ -50,9 +50,9 @@ fn test_put_get_del_multi() {
txn.commit().unwrap();
let txn = env.begin_rw_txn().unwrap();
let db = txn.open_db(None).unwrap();
let dbi = txn.open_db(None).unwrap().dbi();
{
let mut cur = txn.cursor(&db).unwrap();
let mut cur = txn.cursor(dbi).unwrap();
let iter = cur.iter_dup_of::<(), [u8; 4]>(b"key1");
let vals = iter.map(|x| x.unwrap()).map(|(_, x)| x).collect::<Vec<_>>();
assert_eq!(vals, vec![*b"val1", *b"val2", *b"val3"]);
@@ -66,9 +66,9 @@ fn test_put_get_del_multi() {
txn.commit().unwrap();
let txn = env.begin_rw_txn().unwrap();
let db = txn.open_db(None).unwrap();
let dbi = txn.open_db(None).unwrap().dbi();
{
let mut cur = txn.cursor(&db).unwrap();
let mut cur = txn.cursor(dbi).unwrap();
let iter = cur.iter_dup_of::<(), [u8; 4]>(b"key1");
let vals = iter.map(|x| x.unwrap()).map(|(_, x)| x).collect::<Vec<_>>();
assert_eq!(vals, vec![*b"val1", *b"val3"]);
@@ -103,9 +103,9 @@ fn test_reserve() {
let env = Environment::builder().open(dir.path()).unwrap();
let txn = env.begin_rw_txn().unwrap();
let db = txn.open_db(None).unwrap();
let dbi = txn.open_db(None).unwrap().dbi();
{
let mut writer = txn.reserve(&db, b"key1", 4, WriteFlags::empty()).unwrap();
let mut writer = txn.reserve(dbi, b"key1", 4, WriteFlags::empty()).unwrap();
writer.write_all(b"val1").unwrap();
}
txn.commit().unwrap();
@@ -182,9 +182,9 @@ fn test_drop_db() {
}
{
let txn = env.begin_rw_txn().unwrap();
let db = txn.open_db(Some("test")).unwrap();
let dbi = txn.open_db(Some("test")).unwrap().dbi();
unsafe {
txn.drop_db(db).unwrap();
txn.drop_db(dbi).unwrap();
}
assert!(matches!(txn.open_db(Some("test")).unwrap_err(), Error::NotFound));
assert!(!txn.commit().unwrap().0);
@@ -291,8 +291,8 @@ fn test_stat() {
{
let txn = env.begin_ro_txn().unwrap();
let db = txn.open_db(None).unwrap();
let stat = txn.db_stat(&db).unwrap();
let dbi = txn.open_db(None).unwrap().dbi();
let stat = txn.db_stat(dbi).unwrap();
assert_eq!(stat.entries(), 3);
}
@@ -304,8 +304,8 @@ fn test_stat() {
{
let txn = env.begin_ro_txn().unwrap();
let db = txn.open_db(None).unwrap();
let stat = txn.db_stat(&db).unwrap();
let dbi = txn.open_db(None).unwrap().dbi();
let stat = txn.db_stat(dbi).unwrap();
assert_eq!(stat.entries(), 1);
}
@@ -318,8 +318,8 @@ fn test_stat() {
{
let txn = env.begin_ro_txn().unwrap();
let db = txn.open_db(None).unwrap();
let stat = txn.db_stat(&db).unwrap();
let dbi = txn.open_db(None).unwrap().dbi();
let stat = txn.db_stat(dbi).unwrap();
assert_eq!(stat.entries(), 4);
}
}
@@ -331,20 +331,22 @@ fn test_stat_dupsort() {
let txn = env.begin_rw_txn().unwrap();
let db = txn.create_db(None, DatabaseFlags::DUP_SORT).unwrap();
txn.put(db.dbi(), b"key1", b"val1", WriteFlags::empty()).unwrap();
txn.put(db.dbi(), b"key1", b"val2", WriteFlags::empty()).unwrap();
txn.put(db.dbi(), b"key1", b"val3", WriteFlags::empty()).unwrap();
txn.put(db.dbi(), b"key2", b"val1", WriteFlags::empty()).unwrap();
txn.put(db.dbi(), b"key2", b"val2", WriteFlags::empty()).unwrap();
txn.put(db.dbi(), b"key2", b"val3", WriteFlags::empty()).unwrap();
txn.put(db.dbi(), b"key3", b"val1", WriteFlags::empty()).unwrap();
txn.put(db.dbi(), b"key3", b"val2", WriteFlags::empty()).unwrap();
txn.put(db.dbi(), b"key3", b"val3", WriteFlags::empty()).unwrap();
let dbi = db.dbi();
txn.put(dbi, b"key1", b"val1", WriteFlags::empty()).unwrap();
txn.put(dbi, b"key1", b"val2", WriteFlags::empty()).unwrap();
txn.put(dbi, b"key1", b"val3", WriteFlags::empty()).unwrap();
txn.put(dbi, b"key2", b"val1", WriteFlags::empty()).unwrap();
txn.put(dbi, b"key2", b"val2", WriteFlags::empty()).unwrap();
txn.put(dbi, b"key2", b"val3", WriteFlags::empty()).unwrap();
txn.put(dbi, b"key3", b"val1", WriteFlags::empty()).unwrap();
txn.put(dbi, b"key3", b"val2", WriteFlags::empty()).unwrap();
txn.put(dbi, b"key3", b"val3", WriteFlags::empty()).unwrap();
txn.commit().unwrap();
{
let txn = env.begin_ro_txn().unwrap();
let stat = txn.db_stat(&txn.open_db(None).unwrap()).unwrap();
let dbi = txn.open_db(None).unwrap().dbi();
let stat = txn.db_stat(dbi).unwrap();
assert_eq!(stat.entries(), 9);
}
@@ -356,7 +358,8 @@ fn test_stat_dupsort() {
{
let txn = env.begin_ro_txn().unwrap();
let stat = txn.db_stat(&txn.open_db(None).unwrap()).unwrap();
let dbi = txn.open_db(None).unwrap().dbi();
let stat = txn.db_stat(dbi).unwrap();
assert_eq!(stat.entries(), 5);
}
@@ -369,7 +372,8 @@ fn test_stat_dupsort() {
{
let txn = env.begin_ro_txn().unwrap();
let stat = txn.db_stat(&txn.open_db(None).unwrap()).unwrap();
let dbi = txn.open_db(None).unwrap().dbi();
let stat = txn.db_stat(dbi).unwrap();
assert_eq!(stat.entries(), 8);
}
}