From 187d2128374307145e41dc52ba6db2c8de081c75 Mon Sep 17 00:00:00 2001 From: Christopher Berner Date: Sat, 14 Oct 2023 19:15:28 -0700 Subject: [PATCH] Implement Debug for Table and ReadOnlyTable --- src/db.rs | 23 ++++++++++++++---- src/table.rs | 59 +++++++++++++++++++++++++++++++++++++++++++++ src/transactions.rs | 1 + 3 files changed, 78 insertions(+), 5 deletions(-) diff --git a/src/db.rs b/src/db.rs index 96ef9ed3..de8b1226 100644 --- a/src/db.rs +++ b/src/db.rs @@ -408,7 +408,12 @@ impl Database { })? { let savepoint_table: ReadOnlyTable = - ReadOnlyTable::new(savepoint_table_def.get_root(), PageHint::None, mem)?; + ReadOnlyTable::new( + "internal savepoint table".to_string(), + savepoint_table_def.get_root(), + PageHint::None, + mem, + )?; for result in savepoint_table.range::(..)? { let (_, savepoint_data) = result?; let savepoint = savepoint_data @@ -443,8 +448,12 @@ impl Database { mem.mark_pages_allocated(freed_pages_iter, true)?; } - let freed_table: ReadOnlyTable> = - ReadOnlyTable::new(freed_root, PageHint::None, mem)?; + let freed_table: ReadOnlyTable> = ReadOnlyTable::new( + "internal freed table".to_string(), + freed_root, + PageHint::None, + mem, + )?; let lookup_key = FreedTableKey { transaction_id: oldest_unprocessed_free_transaction.0, pagination_id: 0, @@ -557,8 +566,12 @@ impl Database { let freed_root = mem.get_freed_root(); // Allow processing of all transactions, since this is the main freed tree Self::mark_freed_tree(freed_root, mem, TransactionId(0))?; - let freed_table: ReadOnlyTable> = - ReadOnlyTable::new(freed_root, PageHint::None, mem)?; + let freed_table: ReadOnlyTable> = ReadOnlyTable::new( + "internal freed table".to_string(), + freed_root, + PageHint::None, + mem, + )?; // The persistent savepoints might hold references to older freed trees that are partially processed. // Make sure we don't reprocess those frees, as that would result in a double-free let oldest_unprocessed_transaction = diff --git a/src/table.rs b/src/table.rs index 60505ce9..b5f1abee 100644 --- a/src/table.rs +++ b/src/table.rs @@ -7,6 +7,7 @@ use crate::types::{RedbKey, RedbValue, RedbValueMutInPlace}; use crate::Result; use crate::{AccessGuard, StorageError, WriteTransaction}; use std::borrow::Borrow; +use std::fmt::{Debug, Formatter}; use std::ops::RangeBounds; use std::sync::{Arc, Mutex}; @@ -249,6 +250,55 @@ impl<'db, 'txn, K: RedbKey + 'static, V: RedbValue + 'static> Drop for Table<'db } } +fn debug_helper( + f: &mut Formatter<'_>, + name: &str, + len: Result, + first: Result, AccessGuard)>>, + last: Result, AccessGuard)>>, +) -> std::fmt::Result { + write!(f, "Table [ name: \"{}\", ", name)?; + if let Ok(len) = len { + if len == 0 { + write!(f, "No entries")?; + } else if len == 1 { + if let Ok(first) = first { + let (key, value) = first.as_ref().unwrap(); + write!(f, "One key-value: {:?} = {:?}", key.value(), value.value())?; + } else { + write!(f, "I/O Error accessing table!")?; + } + } else { + if let Ok(first) = first { + let (key, value) = first.as_ref().unwrap(); + write!(f, "first: {:?} = {:?}, ", key.value(), value.value())?; + } else { + write!(f, "I/O Error accessing table!")?; + } + if len > 2 { + write!(f, "...{} more entries..., ", len - 2)?; + } + if let Ok(last) = last { + let (key, value) = last.as_ref().unwrap(); + write!(f, "last: {:?} = {:?}", key.value(), value.value())?; + } else { + write!(f, "I/O Error accessing table!")?; + } + } + } else { + write!(f, "I/O Error accessing table!")?; + } + write!(f, " ]")?; + + Ok(()) +} + +impl Debug for Table<'_, '_, K, V> { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + debug_helper(f, &self.name, self.len(), self.first(), self.last()) + } +} + pub trait ReadableTable: Sealed { /// Returns the value corresponding to the given key fn get<'a>(&self, key: impl Borrow>) -> Result>> @@ -319,16 +369,19 @@ pub trait ReadableTable: Sealed { /// A read-only table pub struct ReadOnlyTable<'txn, K: RedbKey + 'static, V: RedbValue + 'static> { + name: String, tree: Btree<'txn, K, V>, } impl<'txn, K: RedbKey + 'static, V: RedbValue + 'static> ReadOnlyTable<'txn, K, V> { pub(crate) fn new( + name: String, root_page: Option<(PageNumber, Checksum)>, hint: PageHint, mem: &'txn TransactionalMemory, ) -> Result> { Ok(ReadOnlyTable { + name, tree: Btree::new(root_page, hint, mem)?, }) } @@ -376,6 +429,12 @@ impl<'txn, K: RedbKey + 'static, V: RedbValue + 'static> ReadableTable impl Sealed for ReadOnlyTable<'_, K, V> {} +impl Debug for ReadOnlyTable<'_, K, V> { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + debug_helper(f, &self.name, self.len(), self.first(), self.last()) + } +} + pub struct Drain<'a, K: RedbKey + 'static, V: RedbValue + 'static> { inner: BtreeDrain<'a, K, V>, } diff --git a/src/transactions.rs b/src/transactions.rs index e0b6299d..5e50a223 100644 --- a/src/transactions.rs +++ b/src/transactions.rs @@ -1150,6 +1150,7 @@ impl<'db> ReadTransaction<'db> { .ok_or_else(|| TableError::TableDoesNotExist(definition.name().to_string()))?; Ok(ReadOnlyTable::new( + definition.name().to_string(), header.get_root(), PageHint::Clean, self.mem,