This commit is contained in:
hinto.janai 2024-07-09 17:34:17 -04:00
parent c5bd729285
commit bf1b76fa28
No known key found for this signature in database
GPG key ID: D47CE05FA175A499
3 changed files with 162 additions and 60 deletions

View file

@ -14,12 +14,8 @@
//! # Traits
//! This module also contains a set of traits for
//! accessing _all_ tables defined here at once.
//!
//! For example, this is the object returned by [`OpenTables::open_tables`].
//---------------------------------------------------------------------------------------------------- Import
use cuprate_database::{DatabaseIter, DatabaseRo, DatabaseRw};
use crate::types::{
Amount, AmountIndex, AmountIndices, BlockBlob, BlockHash, BlockHeight, BlockInfo, KeyImage,
Output, PreRctOutputId, PrunableBlob, PrunableHash, PrunedBlob, RctOutput, TxBlob, TxHash,

View file

@ -80,6 +80,15 @@ and use `<E: Env>` everywhere it is stored instead. This would allow
generic-backed dynamic runtime selection of the database backend, i.e.
the user can select which database backend they use. -->
# Defining tables
Most likely, your crate building on-top of `cuprate_database` will
want to define all tables used at compile time.
If this is the case, consider using the [`define_tables`] macro
to bulk generate zero-sized marker types that implement [`Table`].
This macro also generates other convenient traits specific to _your_ tables.
# Feature flags
Different database backends are enabled by the feature flags:
- `heed` (LMDB)

View file

@ -1,58 +1,156 @@
//! Database tables.
//!
//! # Table marker structs
//! This module contains all the table definitions used by `cuprate_blockchain`.
//!
//! The zero-sized structs here represents the table type;
//! they all are essentially marker types that implement `Table`.
//!
//! Table structs are `CamelCase`, and their static string
//! names used by the actual database backend are `snake_case`.
//!
//! For example: `BlockBlobs` -> `block_blobs`.
//!
//! # Traits
//! This module also contains a set of traits for
//! accessing _all_ tables defined here at once.
//!
//! For example, this is the object returned by `OpenTables::open_tables`.
//! Database table definition macro.
//---------------------------------------------------------------------------------------------------- Import
//---------------------------------------------------------------------------------------------------- Table macro
/// Create all tables, should be used _once_.
/// Define all table types.
///
/// Generating this macro once and using `$()*` is probably
/// faster for compile times than calling the macro _per_ table.
/// # Purpose
/// This macro allows you to define all database tables in one place.
///
/// All tables are zero-sized table structs, and implement the `Table` trait.
/// A by-product of this macro is that it defines some
/// convenient traits specific to _your_ tables
/// (see [Output](#output)).
///
/// Table structs are automatically `CamelCase`,
/// and their static string names are automatically `snake_case`.
/// # Inputs
/// This macro expects a list of tables, and their key/value types.
///
/// // --- TODO
/// This syntax is as follows:
///
/// Creates:
/// - `pub trait Tables`
/// - `pub trait TablesIter`
/// - `pub trait TablesMut`
/// - Blanket implementation for `(tuples, containing, all, open, database, tables, ...)`
/// ```rust
/// cuprate_database::define_tables! {
/// /// Any extra attributes you'd like to add to
/// /// this table type, e.g. docs or derives.
///
/// For why this exists, see: <https://github.com/Cuprate/cuprate/pull/102#pullrequestreview-1978348871>.
/// 0 => TableName,
/// // ▲ ▲
/// // │ └─ Table struct name. The macro generates this for you.
/// // │
/// // Incrementing index. This must start at 0
/// // and increment by 1 per table added.
///
/// u8 => u64,
/// // ▲ ▲
/// // │ └─ Table value type.
/// // │
/// // Table key type.
///
/// // Another table.
/// 1 => TableName2,
/// i8 => i64,
/// }
/// ```
///
/// An example:
/// ```rust
/// use cuprate_database::{
/// ConcreteEnv, Table,
/// config::ConfigBuilder,
/// Env, EnvInner,
/// DatabaseRo, DatabaseRw, TxRo, TxRw,
/// };
///
/// // This generates `pub struct Table{1,2,3}`
/// // where all those implement `Table` with
/// // the defined name and key/value types.
/// //
/// // It also generate traits specific to our tables.
/// cuprate_database::define_tables! {
/// 0 => Table1,
/// u32 => i32,
///
/// /// This one has extra docs.
/// 1 => Table2,
/// u64 => (),
///
/// 2 => Table3,
/// i32 => i32,
/// }
///
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
/// # let tmp_dir = tempfile::tempdir()?;
/// # let db_dir = tmp_dir.path().to_owned();
/// # let config = ConfigBuilder::new(db_dir.into()).build();
/// // Open the database.
/// let env = ConcreteEnv::open(config)?;
/// let env_inner = env.env_inner();
///
/// // Open the table we just defined.
/// {
/// let tx_rw = env_inner.tx_rw()?;
/// env_inner.create_db::<Table1>(&tx_rw)?;
/// let mut table = env_inner.open_db_rw::<Table1>(&tx_rw)?;
///
/// // Write data to the table.
/// table.put(&0, &1)?;
///
/// drop(table);
/// TxRw::commit(tx_rw)?;
/// }
///
/// // Read the data, assert it is correct.
/// {
/// let tx_ro = env_inner.tx_ro()?;
/// let table = env_inner.open_db_ro::<Table1>(&tx_ro)?;
/// assert_eq!(table.first()?, (0, 1));
/// }
///
/// // Create all tables at once using the
/// // `OpenTables` trait generated with the
/// // macro above.
/// {
/// let tx_rw = env_inner.tx_rw()?;
/// env_inner.create_tables(&tx_rw)?;
/// TxRw::commit(tx_rw)?;
/// }
///
/// // Open all tables at once.
/// {
/// let tx_ro = env_inner.tx_ro()?;
/// let all_tables = env_inner.open_tables(&tx_ro)?;
/// }
/// # Ok(()) }
/// ```
///
/// # Output
/// This macro:
/// 1. Implements [`Table`](crate::Table) on all your table types
/// 1. Creates a `pub trait Tables` trait (in scope)
/// 1. Creates a `pub trait TablesIter` trait (in scope)
/// 1. Creates a `pub trait TablesMut` trait (in scope)
/// 1. Blanket implements a `(tuples, containing, all, open, database, tables, ...)` for the above traits
/// 1. Creates a `pub trait OpenTables` trait (in scope)
///
/// All table types are zero-sized structs that implement the `Table` trait.
///
/// Table structs are automatically `CamelCase`, and their
/// static string names are automatically `snake_case`.
///
/// For why the table traits + blanket implementation on the tuple exists, see:
/// <https://github.com/Cuprate/cuprate/pull/102#pullrequestreview-1978348871>.
///
/// The `OpenTables` trait lets you open all tables you've defined, at once.
///
/// # Example
/// For examples of usage & output, see
/// [`cuprate_blockchain::tables`](https://github.com/Cuprate/cuprate/blob/main/storage/blockchain/src/tables.rs).
#[macro_export]
macro_rules! define_tables {
(
$(
$(#[$attr:meta])* // Documentation and any `derive`'s.
$index:literal => $table:ident, // The table name + doubles as the table struct name.
$key:ty => // Key type.
$value:ty // Value type.
// Documentation and any `derive`'s.
$(#[$attr:meta])*
// The table name + doubles as the table struct name.
$index:literal => $table:ident,
// Key type => Value type.
$key:ty => $value:ty
),* $(,)?
) => { $crate::paste::paste! {
$(
// Table struct.
$(#[$attr])*
// The below test show the `snake_case` table name in cargo docs.
#[doc = concat!("- Key: [`", stringify!($key), "`]")]
#[doc = concat!("- Value: [`", stringify!($value), "`]")]
#[doc = concat!("- Name: `", stringify!([<$table:snake>]), "`")]
@ -68,17 +166,16 @@ macro_rules! define_tables {
}
)*
/// Object containing all opened [`Table`]s in read-only mode.
/// Object containing all opened [`Table`](cuprate_database::Table)s in read-only mode.
///
/// This is an encapsulated object that contains all
/// available [`Table`]'s in read-only mode.
/// available `Table`'s in read-only mode.
///
/// It is a `Sealed` trait and is only implemented on a
/// `(tuple, containing, all, table, types, ...)`.
///
/// This is used to return a _single_ object from functions like
/// [`OpenTables::open_tables`](crate::OpenTables::open_tables) rather
/// than the tuple containing the tables itself.
/// [`OpenTables::open_tables`] rather than the tuple containing the tables itself.
///
/// To replace `tuple.0` style indexing, `field_accessor_functions()`
/// are provided on this trait, which essentially map the object to
@ -107,17 +204,17 @@ macro_rules! define_tables {
/// Access an opened
#[doc = concat!("[`", stringify!($table), "`]")]
/// database.
fn [<$table:snake>](&self) -> &impl DatabaseRo<$table>;
fn [<$table:snake>](&self) -> &impl $crate::DatabaseRo<$table>;
)*
/// This returns `true` if all tables are empty.
///
/// # Errors
/// This returns errors on regular database errors.
fn all_tables_empty(&self) -> Result<bool, cuprate_database::RuntimeError>;
fn all_tables_empty(&self) -> Result<bool, $crate::RuntimeError>;
}
/// Object containing all opened [`Table`]s in read + iter mode.
/// Object containing all opened [`Table`](cuprate_database::Table)s in read + iter mode.
///
/// This is the same as [`Tables`] but includes `_iter()` variants.
///
@ -130,11 +227,11 @@ macro_rules! define_tables {
/// Access an opened read-only + iterable
#[doc = concat!("[`", stringify!($table), "`]")]
/// database.
fn [<$table:snake _iter>](&self) -> &(impl DatabaseRo<$table> + DatabaseIter<$table>);
fn [<$table:snake _iter>](&self) -> &(impl $crate::DatabaseRo<$table> + $crate::DatabaseIter<$table>);
)*
}
/// Object containing all opened [`Table`]s in write mode.
/// Object containing all opened [`Table`](cuprate_database::Table)s in write mode.
///
/// This is the same as [`Tables`] but for mutable accesses.
///
@ -147,7 +244,7 @@ macro_rules! define_tables {
/// Access an opened
#[doc = concat!("[`", stringify!($table), "`]")]
/// database.
fn [<$table:snake _mut>](&mut self) -> &mut impl DatabaseRw<$table>;
fn [<$table:snake _mut>](&mut self) -> &mut impl $crate::DatabaseRw<$table>;
)*
}
@ -182,23 +279,23 @@ macro_rules! define_tables {
// [...]
// ```
$(
[<$table:upper>]: DatabaseRo<$table>,
[<$table:upper>]: $crate::DatabaseRo<$table>,
)*
{
$(
// The function name of the accessor function is
// the table type in `snake_case`, e.g., `block_info_v1s()`.
#[inline]
fn [<$table:snake>](&self) -> &impl DatabaseRo<$table> {
fn [<$table:snake>](&self) -> &impl $crate::DatabaseRo<$table> {
// The index of the database table in
// the tuple implements the table trait.
&self.$index
}
)*
fn all_tables_empty(&self) -> Result<bool, cuprate_database::RuntimeError> {
fn all_tables_empty(&self) -> Result<bool, $crate::RuntimeError> {
$(
if !DatabaseRo::is_empty(&self.$index)? {
if !$crate::DatabaseRo::is_empty(&self.$index)? {
return Ok(false);
}
)*
@ -212,14 +309,14 @@ macro_rules! define_tables {
for ($([<$table:upper>]),*)
where
$(
[<$table:upper>]: DatabaseRo<$table> + DatabaseIter<$table>,
[<$table:upper>]: $crate::DatabaseRo<$table> + $crate::DatabaseIter<$table>,
)*
{
$(
// The function name of the accessor function is
// the table type in `snake_case` + `_iter`, e.g., `block_info_v1s_iter()`.
#[inline]
fn [<$table:snake _iter>](&self) -> &(impl DatabaseRo<$table> + DatabaseIter<$table>) {
fn [<$table:snake _iter>](&self) -> &(impl $crate::DatabaseRo<$table> + $crate::DatabaseIter<$table>) {
&self.$index
}
)*
@ -231,14 +328,14 @@ macro_rules! define_tables {
for ($([<$table:upper>]),*)
where
$(
[<$table:upper>]: DatabaseRw<$table>,
[<$table:upper>]: $crate::DatabaseRw<$table>,
)*
{
$(
// The function name of the mutable accessor function is
// the table type in `snake_case` + `_mut`, e.g., `block_info_v1s_mut()`.
#[inline]
fn [<$table:snake _mut>](&mut self) -> &mut impl DatabaseRw<$table> {
fn [<$table:snake _mut>](&mut self) -> &mut impl $crate::DatabaseRw<$table> {
&mut self.$index
}
)*
@ -247,7 +344,7 @@ macro_rules! define_tables {
/// Open all tables at once.
///
/// This trait encapsulates the functionality of opening all tables at once.
/// It can be seen as the "constructor" for the `Tables` object.
/// It can be seen as the "constructor" for the [`Tables`] object.
///
/// Note that this is already implemented on [`cuprate_database::EnvInner`], thus:
/// - You don't need to implement this
@ -260,7 +357,7 @@ macro_rules! define_tables {
/// Open all tables in read/iter mode.
///
/// This calls [`EnvInner::open_db_ro`] on all database tables
/// This calls [`cuprate_database::EnvInner::open_db_ro`] on all database tables
/// and returns a structure that allows access to all tables.
///
/// # Errors