mirror of
https://github.com/hinto-janai/cuprate.git
synced 2024-11-16 15:58:14 +00:00
storage: move table generator macro blockchain
-> database
(#222)
Some checks are pending
Audit / audit (push) Waiting to run
CI / fmt (push) Waiting to run
CI / typo (push) Waiting to run
CI / ci (macos-latest, stable, bash) (push) Waiting to run
CI / ci (ubuntu-latest, stable, bash) (push) Waiting to run
CI / ci (windows-latest, stable-x86_64-pc-windows-gnu, msys2 {0}) (push) Waiting to run
Deny / audit (push) Waiting to run
Doc / build (push) Waiting to run
Doc / deploy (push) Blocked by required conditions
Some checks are pending
Audit / audit (push) Waiting to run
CI / fmt (push) Waiting to run
CI / typo (push) Waiting to run
CI / ci (macos-latest, stable, bash) (push) Waiting to run
CI / ci (ubuntu-latest, stable, bash) (push) Waiting to run
CI / ci (windows-latest, stable-x86_64-pc-windows-gnu, msys2 {0}) (push) Waiting to run
Deny / audit (push) Waiting to run
Doc / build (push) Waiting to run
Doc / deploy (push) Blocked by required conditions
* move table generator macro `blockchain` -> `database` * blockchain: fix imports * docs * fix import ordering
This commit is contained in:
parent
824651c8cf
commit
fbae3df203
21 changed files with 491 additions and 552 deletions
2
Cargo.lock
generated
2
Cargo.lock
generated
|
@ -507,7 +507,6 @@ dependencies = [
|
|||
"hex",
|
||||
"hex-literal",
|
||||
"monero-serai",
|
||||
"paste",
|
||||
"pretty_assertions",
|
||||
"proptest",
|
||||
"rayon",
|
||||
|
@ -601,6 +600,7 @@ dependencies = [
|
|||
"cfg-if",
|
||||
"heed",
|
||||
"page_size",
|
||||
"paste",
|
||||
"redb",
|
||||
"serde",
|
||||
"tempfile",
|
||||
|
|
|
@ -30,7 +30,6 @@ bytemuck = { version = "1.14.3", features = ["must_cast", "derive", "min
|
|||
curve25519-dalek = { workspace = true }
|
||||
cuprate-pruning = { path = "../../pruning" }
|
||||
monero-serai = { workspace = true, features = ["std"] }
|
||||
paste = { workspace = true }
|
||||
serde = { workspace = true, optional = true }
|
||||
|
||||
# `service` feature.
|
||||
|
|
|
@ -67,8 +67,7 @@ use cuprate_blockchain::{
|
|||
DatabaseRo, DatabaseRw, TxRo, TxRw,
|
||||
},
|
||||
config::ConfigBuilder,
|
||||
tables::{Tables, TablesMut},
|
||||
OpenTables,
|
||||
tables::{Tables, TablesMut, OpenTables},
|
||||
};
|
||||
|
||||
# fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
//---------------------------------------------------------------------------------------------------- Import
|
||||
use cuprate_database::{ConcreteEnv, Env, EnvInner, InitError, RuntimeError, TxRw};
|
||||
|
||||
use crate::{config::Config, open_tables::OpenTables};
|
||||
use crate::{config::Config, tables::OpenTables};
|
||||
|
||||
//---------------------------------------------------------------------------------------------------- Free functions
|
||||
/// Open the blockchain database, using the passed [`Config`].
|
||||
|
|
|
@ -114,23 +114,18 @@ compile_error!("Cuprate is only compatible with 64-bit CPUs");
|
|||
//
|
||||
// Documentation for each module is located in the respective file.
|
||||
|
||||
pub mod config;
|
||||
|
||||
mod constants;
|
||||
pub use constants::{DATABASE_CORRUPT_MSG, DATABASE_VERSION};
|
||||
|
||||
mod open_tables;
|
||||
pub use open_tables::OpenTables;
|
||||
|
||||
mod free;
|
||||
|
||||
pub use constants::{DATABASE_CORRUPT_MSG, DATABASE_VERSION};
|
||||
pub use cuprate_database;
|
||||
pub use free::open;
|
||||
|
||||
pub mod config;
|
||||
pub mod ops;
|
||||
pub mod tables;
|
||||
pub mod types;
|
||||
|
||||
pub use cuprate_database;
|
||||
|
||||
//---------------------------------------------------------------------------------------------------- Feature-gated
|
||||
#[cfg(feature = "service")]
|
||||
pub mod service;
|
||||
|
|
|
@ -1,190 +0,0 @@
|
|||
//! TODO
|
||||
|
||||
//---------------------------------------------------------------------------------------------------- Import
|
||||
use cuprate_database::{EnvInner, RuntimeError};
|
||||
|
||||
use crate::tables::{TablesIter, TablesMut};
|
||||
|
||||
//---------------------------------------------------------------------------------------------------- Table function macro
|
||||
/// `crate`-private macro for callings functions on all tables.
|
||||
///
|
||||
/// This calls the function `$fn` with the optional
|
||||
/// arguments `$args` on all tables - returning early
|
||||
/// (within whatever scope this is called) if any
|
||||
/// of the function calls error.
|
||||
///
|
||||
/// Else, it evaluates to an `Ok((tuple, of, all, table, types, ...))`,
|
||||
/// i.e., an `impl Table[Mut]` wrapped in `Ok`.
|
||||
macro_rules! call_fn_on_all_tables_or_early_return {
|
||||
(
|
||||
$($fn:ident $(::)?)*
|
||||
(
|
||||
$($arg:ident),* $(,)?
|
||||
)
|
||||
) => {{
|
||||
Ok((
|
||||
$($fn ::)*<$crate::tables::BlockInfos>($($arg),*)?,
|
||||
$($fn ::)*<$crate::tables::BlockBlobs>($($arg),*)?,
|
||||
$($fn ::)*<$crate::tables::BlockHeights>($($arg),*)?,
|
||||
$($fn ::)*<$crate::tables::KeyImages>($($arg),*)?,
|
||||
$($fn ::)*<$crate::tables::NumOutputs>($($arg),*)?,
|
||||
$($fn ::)*<$crate::tables::PrunedTxBlobs>($($arg),*)?,
|
||||
$($fn ::)*<$crate::tables::PrunableHashes>($($arg),*)?,
|
||||
$($fn ::)*<$crate::tables::Outputs>($($arg),*)?,
|
||||
$($fn ::)*<$crate::tables::PrunableTxBlobs>($($arg),*)?,
|
||||
$($fn ::)*<$crate::tables::RctOutputs>($($arg),*)?,
|
||||
$($fn ::)*<$crate::tables::TxBlobs>($($arg),*)?,
|
||||
$($fn ::)*<$crate::tables::TxIds>($($arg),*)?,
|
||||
$($fn ::)*<$crate::tables::TxHeights>($($arg),*)?,
|
||||
$($fn ::)*<$crate::tables::TxOutputs>($($arg),*)?,
|
||||
$($fn ::)*<$crate::tables::TxUnlockTime>($($arg),*)?,
|
||||
))
|
||||
}};
|
||||
}
|
||||
pub(crate) use call_fn_on_all_tables_or_early_return;
|
||||
|
||||
//---------------------------------------------------------------------------------------------------- OpenTables
|
||||
/// 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`](crate::tables::Tables) object.
|
||||
///
|
||||
/// Note that this is already implemented on [`cuprate_database::EnvInner`], thus:
|
||||
/// - You don't need to implement this
|
||||
/// - It can be called using `env_inner.open_tables()` notation
|
||||
///
|
||||
/// # Example
|
||||
/// ```rust
|
||||
/// use cuprate_blockchain::{
|
||||
/// cuprate_database::{Env, EnvInner},
|
||||
/// config::ConfigBuilder,
|
||||
/// tables::{Tables, TablesMut},
|
||||
/// OpenTables,
|
||||
/// };
|
||||
///
|
||||
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
/// // Create a configuration for the database environment.
|
||||
/// let tmp_dir = tempfile::tempdir()?;
|
||||
/// let db_dir = tmp_dir.path().to_owned();
|
||||
/// let config = ConfigBuilder::new()
|
||||
/// .db_directory(db_dir.into())
|
||||
/// .build();
|
||||
///
|
||||
/// // Initialize the database environment.
|
||||
/// let env = cuprate_blockchain::open(config)?;
|
||||
///
|
||||
/// // Open up a transaction.
|
||||
/// let env_inner = env.env_inner();
|
||||
/// let tx_rw = env_inner.tx_rw()?;
|
||||
///
|
||||
/// // Open _all_ tables in write mode using [`OpenTables::open_tables_mut`].
|
||||
/// // Note how this is being called on `env_inner`.
|
||||
/// // |
|
||||
/// // v
|
||||
/// let mut tables = env_inner.open_tables_mut(&tx_rw)?;
|
||||
/// # Ok(()) }
|
||||
/// ```
|
||||
pub trait OpenTables<'env> {
|
||||
/// The read-only transaction type of the backend.
|
||||
type Ro<'a>;
|
||||
/// The read-write transaction type of the backend.
|
||||
type Rw<'a>;
|
||||
|
||||
/// Open all tables in read/iter mode.
|
||||
///
|
||||
/// This calls [`EnvInner::open_db_ro`] on all database tables
|
||||
/// and returns a structure that allows access to all tables.
|
||||
///
|
||||
/// # Errors
|
||||
/// This will only return [`RuntimeError::Io`] if it errors.
|
||||
///
|
||||
/// As all tables are created upon [`crate::open`],
|
||||
/// this function will never error because a table doesn't exist.
|
||||
fn open_tables(&self, tx_ro: &Self::Ro<'_>) -> Result<impl TablesIter, RuntimeError>;
|
||||
|
||||
/// Open all tables in read-write mode.
|
||||
///
|
||||
/// This calls [`EnvInner::open_db_rw`] on all database tables
|
||||
/// and returns a structure that allows access to all tables.
|
||||
///
|
||||
/// # Errors
|
||||
/// This will only return [`RuntimeError::Io`] on errors.
|
||||
fn open_tables_mut(&self, tx_rw: &Self::Rw<'_>) -> Result<impl TablesMut, RuntimeError>;
|
||||
|
||||
/// Create all database tables.
|
||||
///
|
||||
/// This will create all the [`Table`](cuprate_database::Table)s
|
||||
/// found in [`tables`](crate::tables).
|
||||
///
|
||||
/// # Errors
|
||||
/// This will only return [`RuntimeError::Io`] on errors.
|
||||
fn create_tables(&self, tx_rw: &Self::Rw<'_>) -> Result<(), RuntimeError>;
|
||||
}
|
||||
|
||||
impl<'env, Ei> OpenTables<'env> for Ei
|
||||
where
|
||||
Ei: EnvInner<'env>,
|
||||
{
|
||||
type Ro<'a> = <Ei as EnvInner<'env>>::Ro<'a>;
|
||||
type Rw<'a> = <Ei as EnvInner<'env>>::Rw<'a>;
|
||||
|
||||
fn open_tables(&self, tx_ro: &Self::Ro<'_>) -> Result<impl TablesIter, RuntimeError> {
|
||||
call_fn_on_all_tables_or_early_return! {
|
||||
Self::open_db_ro(self, tx_ro)
|
||||
}
|
||||
}
|
||||
|
||||
fn open_tables_mut(&self, tx_rw: &Self::Rw<'_>) -> Result<impl TablesMut, RuntimeError> {
|
||||
call_fn_on_all_tables_or_early_return! {
|
||||
Self::open_db_rw(self, tx_rw)
|
||||
}
|
||||
}
|
||||
|
||||
fn create_tables(&self, tx_rw: &Self::Rw<'_>) -> Result<(), RuntimeError> {
|
||||
match call_fn_on_all_tables_or_early_return! {
|
||||
Self::create_db(self, tx_rw)
|
||||
} {
|
||||
Ok(_) => Ok(()),
|
||||
Err(e) => Err(e),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------------------------------- Tests
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use std::borrow::Cow;
|
||||
|
||||
use cuprate_database::{Env, EnvInner};
|
||||
|
||||
use crate::{config::ConfigBuilder, tests::tmp_concrete_env};
|
||||
|
||||
use super::*;
|
||||
|
||||
/// Tests that [`crate::open`] creates all tables.
|
||||
#[test]
|
||||
fn test_all_tables_are_created() {
|
||||
let (env, _tmp) = tmp_concrete_env();
|
||||
let env_inner = env.env_inner();
|
||||
let tx_ro = env_inner.tx_ro().unwrap();
|
||||
env_inner.open_tables(&tx_ro).unwrap();
|
||||
}
|
||||
|
||||
/// Tests that direct usage of
|
||||
/// [`cuprate_database::ConcreteEnv`]
|
||||
/// does NOT create all tables.
|
||||
#[test]
|
||||
#[should_panic(expected = "`Result::unwrap()` on an `Err` value: TableNotFound")]
|
||||
fn test_no_tables_are_created() {
|
||||
let tempdir = tempfile::tempdir().unwrap();
|
||||
let config = ConfigBuilder::new()
|
||||
.db_directory(Cow::Owned(tempdir.path().into()))
|
||||
.low_power()
|
||||
.build();
|
||||
let env = cuprate_database::ConcreteEnv::open(config.db_config).unwrap();
|
||||
|
||||
let env_inner = env.env_inner();
|
||||
let tx_ro = env_inner.tx_ro().unwrap();
|
||||
env_inner.open_tables(&tx_ro).unwrap();
|
||||
}
|
||||
}
|
|
@ -271,8 +271,8 @@ mod test {
|
|||
use super::*;
|
||||
|
||||
use crate::{
|
||||
open_tables::OpenTables,
|
||||
ops::tx::{get_tx, tx_exists},
|
||||
tables::OpenTables,
|
||||
tests::{assert_all_tables_are_empty, tmp_concrete_env, AssertTableLen},
|
||||
};
|
||||
|
||||
|
|
|
@ -87,9 +87,8 @@ mod test {
|
|||
use super::*;
|
||||
|
||||
use crate::{
|
||||
open_tables::OpenTables,
|
||||
ops::block::add_block,
|
||||
tables::Tables,
|
||||
tables::{OpenTables, Tables},
|
||||
tests::{assert_all_tables_are_empty, tmp_concrete_env, AssertTableLen},
|
||||
};
|
||||
|
||||
|
|
|
@ -52,8 +52,7 @@ mod test {
|
|||
use super::*;
|
||||
|
||||
use crate::{
|
||||
open_tables::OpenTables,
|
||||
tables::{Tables, TablesMut},
|
||||
tables::{OpenTables, Tables, TablesMut},
|
||||
tests::{assert_all_tables_are_empty, tmp_concrete_env, AssertTableLen},
|
||||
};
|
||||
|
||||
|
|
|
@ -61,9 +61,8 @@
|
|||
//! Env, EnvInner,
|
||||
//! DatabaseRo, DatabaseRw, TxRo, TxRw,
|
||||
//! },
|
||||
//! OpenTables,
|
||||
//! config::ConfigBuilder,
|
||||
//! tables::{Tables, TablesMut},
|
||||
//! tables::{Tables, TablesMut, OpenTables},
|
||||
//! ops::block::{add_block, pop_block},
|
||||
//! };
|
||||
//!
|
||||
|
|
|
@ -254,8 +254,7 @@ mod test {
|
|||
use cuprate_database::{Env, EnvInner};
|
||||
|
||||
use crate::{
|
||||
open_tables::OpenTables,
|
||||
tables::{Tables, TablesMut},
|
||||
tables::{OpenTables, Tables, TablesMut},
|
||||
tests::{assert_all_tables_are_empty, tmp_concrete_env, AssertTableLen},
|
||||
types::OutputFlags,
|
||||
};
|
||||
|
|
|
@ -331,8 +331,7 @@ mod test {
|
|||
use cuprate_test_utils::data::{tx_v1_sig0, tx_v1_sig2, tx_v2_rct3};
|
||||
|
||||
use crate::{
|
||||
open_tables::OpenTables,
|
||||
tables::Tables,
|
||||
tables::{OpenTables, Tables},
|
||||
tests::{assert_all_tables_are_empty, tmp_concrete_env, AssertTableLen},
|
||||
};
|
||||
|
||||
|
|
|
@ -22,7 +22,6 @@ use cuprate_types::{
|
|||
|
||||
use crate::{
|
||||
config::ReaderThreads,
|
||||
open_tables::OpenTables,
|
||||
ops::{
|
||||
block::{
|
||||
block_exists, get_block_extended_header_from_height, get_block_height, get_block_info,
|
||||
|
@ -35,6 +34,7 @@ use crate::{
|
|||
free::{compact_history_genesis_not_included, compact_history_index_to_height_offset},
|
||||
types::{ResponseReceiver, ResponseResult, ResponseSender},
|
||||
},
|
||||
tables::OpenTables,
|
||||
tables::{BlockHeights, BlockInfos, Tables},
|
||||
types::{Amount, AmountIndex, BlockHash, BlockHeight, KeyImage, PreRctOutputId},
|
||||
};
|
||||
|
|
|
@ -24,14 +24,13 @@ use cuprate_types::{
|
|||
|
||||
use crate::{
|
||||
config::ConfigBuilder,
|
||||
open_tables::OpenTables,
|
||||
ops::{
|
||||
block::{get_block_extended_header_from_height, get_block_info},
|
||||
blockchain::chain_height,
|
||||
output::id_to_output_on_chain,
|
||||
},
|
||||
service::{init, DatabaseReadHandle, DatabaseWriteHandle},
|
||||
tables::{Tables, TablesIter},
|
||||
tables::{OpenTables, Tables, TablesIter},
|
||||
tests::AssertTableLen,
|
||||
types::{Amount, AmountIndex, PreRctOutputId},
|
||||
};
|
||||
|
|
|
@ -16,8 +16,8 @@ use cuprate_types::{
|
|||
};
|
||||
|
||||
use crate::{
|
||||
open_tables::OpenTables,
|
||||
service::types::{ResponseReceiver, ResponseResult, ResponseSender},
|
||||
tables::OpenTables,
|
||||
};
|
||||
|
||||
//---------------------------------------------------------------------------------------------------- Constants
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
//! 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`].
|
||||
//! they all are essentially marker types that implement [`cuprate_database::Table`].
|
||||
//!
|
||||
//! Table structs are `CamelCase`, and their static string
|
||||
//! names used by the actual database backend are `snake_case`.
|
||||
|
@ -14,311 +14,14 @@
|
|||
//! # 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`](crate::OpenTables::open_tables).
|
||||
|
||||
//---------------------------------------------------------------------------------------------------- Import
|
||||
use cuprate_database::{DatabaseIter, DatabaseRo, DatabaseRw, Table};
|
||||
|
||||
use crate::types::{
|
||||
Amount, AmountIndex, AmountIndices, BlockBlob, BlockHash, BlockHeight, BlockInfo, KeyImage,
|
||||
Output, PreRctOutputId, PrunableBlob, PrunableHash, PrunedBlob, RctOutput, TxBlob, TxHash,
|
||||
TxId, UnlockTime,
|
||||
};
|
||||
|
||||
//---------------------------------------------------------------------------------------------------- Sealed
|
||||
/// Private module, should not be accessible outside this crate.
|
||||
pub(super) mod private {
|
||||
/// Private sealed trait.
|
||||
///
|
||||
/// Cannot be implemented outside this crate.
|
||||
pub trait Sealed {}
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------------------------------- `trait Tables[Mut]`
|
||||
/// Creates:
|
||||
/// - `pub trait Tables`
|
||||
/// - `pub trait TablesIter`
|
||||
/// - `pub trait TablesMut`
|
||||
/// - Blanket implementation for `(tuples, containing, all, open, database, tables, ...)`
|
||||
///
|
||||
/// For why this exists, see: <https://github.com/Cuprate/cuprate/pull/102#pullrequestreview-1978348871>.
|
||||
macro_rules! define_trait_tables {
|
||||
($(
|
||||
// The `T: Table` type The index in a tuple
|
||||
// | containing all tables
|
||||
// v v
|
||||
$table:ident => $index:literal
|
||||
),* $(,)?) => { paste::paste! {
|
||||
/// Object containing all opened [`Table`]s in read-only mode.
|
||||
///
|
||||
/// This is an encapsulated object that contains all
|
||||
/// 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.
|
||||
///
|
||||
/// To replace `tuple.0` style indexing, `field_accessor_functions()`
|
||||
/// are provided on this trait, which essentially map the object to
|
||||
/// fields containing the particular database table, for example:
|
||||
/// ```rust,ignore
|
||||
/// let tables = open_tables();
|
||||
///
|
||||
/// // The accessor function `block_infos()` returns the field
|
||||
/// // containing an open database table for `BlockInfos`.
|
||||
/// let _ = tables.block_infos();
|
||||
/// ```
|
||||
///
|
||||
/// See also:
|
||||
/// - [`TablesMut`]
|
||||
/// - [`TablesIter`]
|
||||
pub trait Tables: private::Sealed {
|
||||
// This expands to creating `fn field_accessor_functions()`
|
||||
// for each passed `$table` type.
|
||||
//
|
||||
// It is essentially a mapping to the field
|
||||
// containing the proper opened database table.
|
||||
//
|
||||
// The function name of the function is
|
||||
// the table type in `snake_case`, e.g., `block_info_v1s()`.
|
||||
$(
|
||||
/// Access an opened
|
||||
#[doc = concat!("[`", stringify!($table), "`]")]
|
||||
/// database.
|
||||
fn [<$table:snake>](&self) -> &impl 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>;
|
||||
}
|
||||
|
||||
/// Object containing all opened [`Table`]s in read + iter mode.
|
||||
///
|
||||
/// This is the same as [`Tables`] but includes `_iter()` variants.
|
||||
///
|
||||
/// Note that this trait is a supertrait of `Tables`,
|
||||
/// as in it can use all of its functions as well.
|
||||
///
|
||||
/// See [`Tables`] for documentation - this trait exists for the same reasons.
|
||||
pub trait TablesIter: private::Sealed + Tables {
|
||||
$(
|
||||
/// Access an opened read-only + iterable
|
||||
#[doc = concat!("[`", stringify!($table), "`]")]
|
||||
/// database.
|
||||
fn [<$table:snake _iter>](&self) -> &(impl DatabaseRo<$table> + DatabaseIter<$table>);
|
||||
)*
|
||||
}
|
||||
|
||||
/// Object containing all opened [`Table`]s in write mode.
|
||||
///
|
||||
/// This is the same as [`Tables`] but for mutable accesses.
|
||||
///
|
||||
/// Note that this trait is a supertrait of `Tables`,
|
||||
/// as in it can use all of its functions as well.
|
||||
///
|
||||
/// See [`Tables`] for documentation - this trait exists for the same reasons.
|
||||
pub trait TablesMut: private::Sealed + Tables {
|
||||
$(
|
||||
/// Access an opened
|
||||
#[doc = concat!("[`", stringify!($table), "`]")]
|
||||
/// database.
|
||||
fn [<$table:snake _mut>](&mut self) -> &mut impl DatabaseRw<$table>;
|
||||
)*
|
||||
}
|
||||
|
||||
// Implement `Sealed` for all table types.
|
||||
impl<$([<$table:upper>]),*> private::Sealed for ($([<$table:upper>]),*) {}
|
||||
|
||||
// This creates a blanket-implementation for
|
||||
// `(tuple, containing, all, table, types)`.
|
||||
//
|
||||
// There is a generic defined here _for each_ `$table` input.
|
||||
// Specifically, the generic letters are just the table types in UPPERCASE.
|
||||
// Concretely, this expands to something like:
|
||||
// ```rust
|
||||
// impl<BLOCKINFOSV1S, BLOCKINFOSV2S, BLOCKINFOSV3S, [...]>
|
||||
// ```
|
||||
impl<$([<$table:upper>]),*> Tables
|
||||
// We are implementing `Tables` on a tuple that
|
||||
// contains all those generics specified, i.e.,
|
||||
// a tuple containing all open table types.
|
||||
//
|
||||
// Concretely, this expands to something like:
|
||||
// ```rust
|
||||
// (BLOCKINFOSV1S, BLOCKINFOSV2S, BLOCKINFOSV3S, [...])
|
||||
// ```
|
||||
// which is just a tuple of the generics defined above.
|
||||
for ($([<$table:upper>]),*)
|
||||
where
|
||||
// This expands to a where bound that asserts each element
|
||||
// in the tuple implements some database table type.
|
||||
//
|
||||
// Concretely, this expands to something like:
|
||||
// ```rust
|
||||
// BLOCKINFOSV1S: DatabaseRo<BlockInfoV1s> + DatabaseIter<BlockInfoV1s>,
|
||||
// BLOCKINFOSV2S: DatabaseRo<BlockInfoV2s> + DatabaseIter<BlockInfoV2s>,
|
||||
// [...]
|
||||
// ```
|
||||
$(
|
||||
[<$table:upper>]: 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> {
|
||||
// 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> {
|
||||
$(
|
||||
if !DatabaseRo::is_empty(&self.$index)? {
|
||||
return Ok(false);
|
||||
}
|
||||
)*
|
||||
Ok(true)
|
||||
}
|
||||
}
|
||||
|
||||
// This is the same as the above
|
||||
// `Tables`, but for `TablesIter`.
|
||||
impl<$([<$table:upper>]),*> TablesIter
|
||||
for ($([<$table:upper>]),*)
|
||||
where
|
||||
$(
|
||||
[<$table:upper>]: DatabaseRo<$table> + 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>) {
|
||||
&self.$index
|
||||
}
|
||||
)*
|
||||
}
|
||||
|
||||
// This is the same as the above
|
||||
// `Tables`, but for `TablesMut`.
|
||||
impl<$([<$table:upper>]),*> TablesMut
|
||||
for ($([<$table:upper>]),*)
|
||||
where
|
||||
$(
|
||||
[<$table:upper>]: 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> {
|
||||
&mut self.$index
|
||||
}
|
||||
)*
|
||||
}
|
||||
}};
|
||||
}
|
||||
|
||||
// Input format: $table_type => $index
|
||||
//
|
||||
// The $index:
|
||||
// - Simply increments by 1 for each table
|
||||
// - Must be 0..
|
||||
// - Must end at the total amount of table types - 1
|
||||
//
|
||||
// Compile errors will occur if these aren't satisfied.
|
||||
//
|
||||
// $index is just the `tuple.$index`, as the above [`define_trait_tables`]
|
||||
// macro has a blanket impl for `(all, table, types, ...)` and we must map
|
||||
// each type to a tuple index explicitly.
|
||||
//
|
||||
// FIXME: there's definitely an automatic way to this :)
|
||||
define_trait_tables! {
|
||||
BlockInfos => 0,
|
||||
BlockBlobs => 1,
|
||||
BlockHeights => 2,
|
||||
KeyImages => 3,
|
||||
NumOutputs => 4,
|
||||
PrunedTxBlobs => 5,
|
||||
PrunableHashes => 6,
|
||||
Outputs => 7,
|
||||
PrunableTxBlobs => 8,
|
||||
RctOutputs => 9,
|
||||
TxBlobs => 10,
|
||||
TxIds => 11,
|
||||
TxHeights => 12,
|
||||
TxOutputs => 13,
|
||||
TxUnlockTime => 14,
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------------------------------- Table macro
|
||||
/// Create all tables, should be used _once_.
|
||||
///
|
||||
/// Generating this macro once and using `$()*` is probably
|
||||
/// faster for compile times than calling the macro _per_ table.
|
||||
///
|
||||
/// All tables are zero-sized table structs, and implement the `Table` trait.
|
||||
///
|
||||
/// Table structs are automatically `CamelCase`,
|
||||
/// and their static string names are automatically `snake_case`.
|
||||
macro_rules! tables {
|
||||
(
|
||||
$(
|
||||
$(#[$attr:meta])* // Documentation and any `derive`'s.
|
||||
$table:ident, // The table name + doubles as the table struct name.
|
||||
$key:ty => // Key type.
|
||||
$value:ty // Value type.
|
||||
),* $(,)?
|
||||
) => {
|
||||
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), "`]")]
|
||||
///
|
||||
/// ## Table Name
|
||||
/// ```rust
|
||||
/// # use cuprate_blockchain::{*,tables::*};
|
||||
/// use cuprate_database::Table;
|
||||
#[doc = concat!(
|
||||
"assert_eq!(",
|
||||
stringify!([<$table:camel>]),
|
||||
"::NAME, \"",
|
||||
stringify!([<$table:snake>]),
|
||||
"\");",
|
||||
)]
|
||||
/// ```
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[derive(Copy, Clone, Debug, PartialEq, PartialOrd, Eq, Ord, Hash)]
|
||||
pub struct [<$table:camel>];
|
||||
|
||||
// Implement the `Sealed` in this file.
|
||||
// Required by `Table`.
|
||||
impl private::Sealed for [<$table:camel>] {}
|
||||
|
||||
// Table trait impl.
|
||||
impl Table for [<$table:camel>] {
|
||||
const NAME: &'static str = stringify!([<$table:snake>]);
|
||||
type Key = $key;
|
||||
type Value = $value;
|
||||
}
|
||||
)* }
|
||||
};
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------------------------------- Tables
|
||||
// Notes:
|
||||
// - Keep this sorted A-Z (by table name)
|
||||
|
@ -326,23 +29,23 @@ macro_rules! tables {
|
|||
// - If adding/changing a table also edit:
|
||||
// - the tests in `src/backend/tests.rs`
|
||||
// - `call_fn_on_all_tables_or_early_return!()` macro in `src/open_tables.rs`
|
||||
tables! {
|
||||
cuprate_database::define_tables! {
|
||||
/// Serialized block blobs (bytes).
|
||||
///
|
||||
/// Contains the serialized version of all blocks.
|
||||
BlockBlobs,
|
||||
0 => BlockBlobs,
|
||||
BlockHeight => BlockBlob,
|
||||
|
||||
/// Block heights.
|
||||
///
|
||||
/// Contains the height of all blocks.
|
||||
BlockHeights,
|
||||
1 => BlockHeights,
|
||||
BlockHash => BlockHeight,
|
||||
|
||||
/// Block information.
|
||||
///
|
||||
/// Contains metadata of all blocks.
|
||||
BlockInfos,
|
||||
2 => BlockInfos,
|
||||
BlockHeight => BlockInfo,
|
||||
|
||||
/// Set of key images.
|
||||
|
@ -351,38 +54,38 @@ tables! {
|
|||
///
|
||||
/// This table has `()` as the value type, as in,
|
||||
/// it is a set of key images.
|
||||
KeyImages,
|
||||
3 => KeyImages,
|
||||
KeyImage => (),
|
||||
|
||||
/// Maps an output's amount to the number of outputs with that amount.
|
||||
///
|
||||
/// For example, if there are 5 outputs with `amount = 123`
|
||||
/// then calling `get(123)` on this table will return 5.
|
||||
NumOutputs,
|
||||
4 => NumOutputs,
|
||||
Amount => u64,
|
||||
|
||||
/// Pre-RCT output data.
|
||||
Outputs,
|
||||
5 => Outputs,
|
||||
PreRctOutputId => Output,
|
||||
|
||||
/// Pruned transaction blobs (bytes).
|
||||
///
|
||||
/// Contains the pruned portion of serialized transaction data.
|
||||
PrunedTxBlobs,
|
||||
6 => PrunedTxBlobs,
|
||||
TxId => PrunedBlob,
|
||||
|
||||
/// Prunable transaction blobs (bytes).
|
||||
///
|
||||
/// Contains the prunable portion of serialized transaction data.
|
||||
// SOMEDAY: impl when `monero-serai` supports pruning
|
||||
PrunableTxBlobs,
|
||||
7 => PrunableTxBlobs,
|
||||
TxId => PrunableBlob,
|
||||
|
||||
/// Prunable transaction hashes.
|
||||
///
|
||||
/// Contains the prunable portion of transaction hashes.
|
||||
// SOMEDAY: impl when `monero-serai` supports pruning
|
||||
PrunableHashes,
|
||||
8 => PrunableHashes,
|
||||
TxId => PrunableHash,
|
||||
|
||||
// SOMEDAY: impl a properties table:
|
||||
|
@ -392,40 +95,40 @@ tables! {
|
|||
// StorableString => StorableVec,
|
||||
|
||||
/// RCT output data.
|
||||
RctOutputs,
|
||||
9 => RctOutputs,
|
||||
AmountIndex => RctOutput,
|
||||
|
||||
/// Transaction blobs (bytes).
|
||||
///
|
||||
/// Contains the serialized version of all transactions.
|
||||
// SOMEDAY: remove when `monero-serai` supports pruning
|
||||
TxBlobs,
|
||||
10 => TxBlobs,
|
||||
TxId => TxBlob,
|
||||
|
||||
/// Transaction indices.
|
||||
///
|
||||
/// Contains the indices all transactions.
|
||||
TxIds,
|
||||
11 => TxIds,
|
||||
TxHash => TxId,
|
||||
|
||||
/// Transaction heights.
|
||||
///
|
||||
/// Contains the block height associated with all transactions.
|
||||
TxHeights,
|
||||
12 => TxHeights,
|
||||
TxId => BlockHeight,
|
||||
|
||||
/// Transaction outputs.
|
||||
///
|
||||
/// Contains the list of `AmountIndex`'s of the
|
||||
/// outputs associated with all transactions.
|
||||
TxOutputs,
|
||||
13 => TxOutputs,
|
||||
TxId => AmountIndices,
|
||||
|
||||
/// Transaction unlock time.
|
||||
///
|
||||
/// Contains the unlock time of transactions IF they have one.
|
||||
/// Transactions without unlock times will not exist in this table.
|
||||
TxUnlockTime,
|
||||
14 => TxUnlockTime,
|
||||
TxId => UnlockTime,
|
||||
}
|
||||
|
||||
|
|
|
@ -11,7 +11,10 @@ use pretty_assertions::assert_eq;
|
|||
|
||||
use cuprate_database::{ConcreteEnv, DatabaseRo, Env, EnvInner};
|
||||
|
||||
use crate::{config::ConfigBuilder, open_tables::OpenTables, tables::Tables};
|
||||
use crate::{
|
||||
config::ConfigBuilder,
|
||||
tables::{OpenTables, Tables},
|
||||
};
|
||||
|
||||
//---------------------------------------------------------------------------------------------------- Struct
|
||||
/// Named struct to assert the length of all tables.
|
||||
|
|
|
@ -21,6 +21,7 @@ bytemuck = { version = "1.14.3", features = ["must_cast", "derive", "min_const_
|
|||
bytes = { workspace = true }
|
||||
cfg-if = { workspace = true }
|
||||
page_size = { version = "0.6.0" } # Needed for database resizes, they must be a multiple of the OS page size.
|
||||
paste = { workspace = true }
|
||||
thiserror = { workspace = true }
|
||||
|
||||
# Optional features.
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -78,6 +78,8 @@
|
|||
clippy::module_inception,
|
||||
clippy::redundant_pub_crate,
|
||||
clippy::option_if_let_else,
|
||||
|
||||
// unused_crate_dependencies, // false-positive with `paste`
|
||||
)]
|
||||
// Allow some lints when running in debug mode.
|
||||
#![cfg_attr(
|
||||
|
@ -105,42 +107,39 @@
|
|||
// Documentation for each module is located in the respective file.
|
||||
|
||||
mod backend;
|
||||
pub use backend::ConcreteEnv;
|
||||
mod constants;
|
||||
mod database;
|
||||
mod env;
|
||||
mod error;
|
||||
mod key;
|
||||
mod storable;
|
||||
mod table;
|
||||
mod tables;
|
||||
mod transaction;
|
||||
|
||||
pub mod config;
|
||||
pub mod resize;
|
||||
|
||||
mod constants;
|
||||
pub use backend::ConcreteEnv;
|
||||
pub use constants::{
|
||||
DATABASE_BACKEND, DATABASE_CORRUPT_MSG, DATABASE_DATA_FILENAME, DATABASE_LOCK_FILENAME,
|
||||
};
|
||||
|
||||
mod database;
|
||||
pub use database::{DatabaseIter, DatabaseRo, DatabaseRw};
|
||||
|
||||
mod env;
|
||||
pub use env::{Env, EnvInner};
|
||||
|
||||
mod error;
|
||||
pub use error::{InitError, RuntimeError};
|
||||
|
||||
pub mod resize;
|
||||
|
||||
mod key;
|
||||
pub use key::{Key, KeyCompare};
|
||||
|
||||
mod storable;
|
||||
pub use storable::{Storable, StorableBytes, StorableStr, StorableVec};
|
||||
|
||||
mod table;
|
||||
pub use table::Table;
|
||||
|
||||
mod transaction;
|
||||
pub use transaction::{TxRo, TxRw};
|
||||
|
||||
//---------------------------------------------------------------------------------------------------- Private
|
||||
#[cfg(test)]
|
||||
pub(crate) mod tests;
|
||||
|
||||
// Used inside public facing macros.
|
||||
#[doc(hidden)]
|
||||
pub use paste;
|
||||
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
// HACK: needed to satisfy the `unused_crate_dependencies` lint.
|
||||
cfg_if::cfg_if! {
|
||||
|
|
427
storage/database/src/tables.rs
Normal file
427
storage/database/src/tables.rs
Normal file
|
@ -0,0 +1,427 @@
|
|||
//! Database table definition macro.
|
||||
|
||||
//---------------------------------------------------------------------------------------------------- Import
|
||||
|
||||
//---------------------------------------------------------------------------------------------------- Table macro
|
||||
/// Define all table types.
|
||||
///
|
||||
/// # Purpose
|
||||
/// This macro allows you to define all database tables in one place.
|
||||
///
|
||||
/// A by-product of this macro is that it defines some
|
||||
/// convenient traits specific to _your_ tables
|
||||
/// (see [Output](#output)).
|
||||
///
|
||||
/// # Inputs
|
||||
/// This macro expects a list of tables, and their key/value types.
|
||||
///
|
||||
/// This syntax is as follows:
|
||||
///
|
||||
/// ```rust
|
||||
/// cuprate_database::define_tables! {
|
||||
/// /// Any extra attributes you'd like to add to
|
||||
/// /// this table type, e.g. docs or derives.
|
||||
///
|
||||
/// 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 {
|
||||
(
|
||||
$(
|
||||
// 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])*
|
||||
#[doc = concat!("- Key: [`", stringify!($key), "`]")]
|
||||
#[doc = concat!("- Value: [`", stringify!($value), "`]")]
|
||||
#[doc = concat!("- Name: `", stringify!([<$table:snake>]), "`")]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[derive(Copy, Clone, Debug, PartialEq, PartialOrd, Eq, Ord, Hash)]
|
||||
pub struct [<$table:camel>];
|
||||
|
||||
// Table trait impl.
|
||||
impl $crate::Table for [<$table:camel>] {
|
||||
const NAME: &'static str = stringify!([<$table:snake>]);
|
||||
type Key = $key;
|
||||
type Value = $value;
|
||||
}
|
||||
)*
|
||||
|
||||
/// 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.
|
||||
///
|
||||
/// 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`] 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
|
||||
/// fields containing the particular database table, for example:
|
||||
/// ```rust,ignore
|
||||
/// let tables = open_tables();
|
||||
///
|
||||
/// // The accessor function `block_infos()` returns the field
|
||||
/// // containing an open database table for `BlockInfos`.
|
||||
/// let _ = tables.block_infos();
|
||||
/// ```
|
||||
///
|
||||
/// See also:
|
||||
/// - [`TablesMut`]
|
||||
/// - [`TablesIter`]
|
||||
pub trait Tables {
|
||||
// This expands to creating `fn field_accessor_functions()`
|
||||
// for each passed `$table` type.
|
||||
//
|
||||
// It is essentially a mapping to the field
|
||||
// containing the proper opened database table.
|
||||
//
|
||||
// The function name of the function is
|
||||
// the table type in `snake_case`, e.g., `block_info_v1s()`.
|
||||
$(
|
||||
/// Access an opened
|
||||
#[doc = concat!("[`", stringify!($table), "`]")]
|
||||
/// database.
|
||||
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, $crate::RuntimeError>;
|
||||
}
|
||||
|
||||
/// Object containing all opened [`Table`](cuprate_database::Table)s in read + iter mode.
|
||||
///
|
||||
/// This is the same as [`Tables`] but includes `_iter()` variants.
|
||||
///
|
||||
/// Note that this trait is a supertrait of `Tables`,
|
||||
/// as in it can use all of its functions as well.
|
||||
///
|
||||
/// See [`Tables`] for documentation - this trait exists for the same reasons.
|
||||
pub trait TablesIter: Tables {
|
||||
$(
|
||||
/// Access an opened read-only + iterable
|
||||
#[doc = concat!("[`", stringify!($table), "`]")]
|
||||
/// database.
|
||||
fn [<$table:snake _iter>](&self) -> &(impl $crate::DatabaseRo<$table> + $crate::DatabaseIter<$table>);
|
||||
)*
|
||||
}
|
||||
|
||||
/// Object containing all opened [`Table`](cuprate_database::Table)s in write mode.
|
||||
///
|
||||
/// This is the same as [`Tables`] but for mutable accesses.
|
||||
///
|
||||
/// Note that this trait is a supertrait of `Tables`,
|
||||
/// as in it can use all of its functions as well.
|
||||
///
|
||||
/// See [`Tables`] for documentation - this trait exists for the same reasons.
|
||||
pub trait TablesMut: Tables {
|
||||
$(
|
||||
/// Access an opened
|
||||
#[doc = concat!("[`", stringify!($table), "`]")]
|
||||
/// database.
|
||||
fn [<$table:snake _mut>](&mut self) -> &mut impl $crate::DatabaseRw<$table>;
|
||||
)*
|
||||
}
|
||||
|
||||
// This creates a blanket-implementation for
|
||||
// `(tuple, containing, all, table, types)`.
|
||||
//
|
||||
// There is a generic defined here _for each_ `$table` input.
|
||||
// Specifically, the generic letters are just the table types in UPPERCASE.
|
||||
// Concretely, this expands to something like:
|
||||
// ```rust
|
||||
// impl<BLOCKINFOSV1S, BLOCKINFOSV2S, BLOCKINFOSV3S, [...]>
|
||||
// ```
|
||||
impl<$([<$table:upper>]),*> Tables
|
||||
// We are implementing `Tables` on a tuple that
|
||||
// contains all those generics specified, i.e.,
|
||||
// a tuple containing all open table types.
|
||||
//
|
||||
// Concretely, this expands to something like:
|
||||
// ```rust
|
||||
// (BLOCKINFOSV1S, BLOCKINFOSV2S, BLOCKINFOSV3S, [...])
|
||||
// ```
|
||||
// which is just a tuple of the generics defined above.
|
||||
for ($([<$table:upper>]),*)
|
||||
where
|
||||
// This expands to a where bound that asserts each element
|
||||
// in the tuple implements some database table type.
|
||||
//
|
||||
// Concretely, this expands to something like:
|
||||
// ```rust
|
||||
// BLOCKINFOSV1S: DatabaseRo<BlockInfoV1s> + DatabaseIter<BlockInfoV1s>,
|
||||
// BLOCKINFOSV2S: DatabaseRo<BlockInfoV2s> + DatabaseIter<BlockInfoV2s>,
|
||||
// [...]
|
||||
// ```
|
||||
$(
|
||||
[<$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 $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, $crate::RuntimeError> {
|
||||
$(
|
||||
if !$crate::DatabaseRo::is_empty(&self.$index)? {
|
||||
return Ok(false);
|
||||
}
|
||||
)*
|
||||
Ok(true)
|
||||
}
|
||||
}
|
||||
|
||||
// This is the same as the above
|
||||
// `Tables`, but for `TablesIter`.
|
||||
impl<$([<$table:upper>]),*> TablesIter
|
||||
for ($([<$table:upper>]),*)
|
||||
where
|
||||
$(
|
||||
[<$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 $crate::DatabaseRo<$table> + $crate::DatabaseIter<$table>) {
|
||||
&self.$index
|
||||
}
|
||||
)*
|
||||
}
|
||||
|
||||
// This is the same as the above
|
||||
// `Tables`, but for `TablesMut`.
|
||||
impl<$([<$table:upper>]),*> TablesMut
|
||||
for ($([<$table:upper>]),*)
|
||||
where
|
||||
$(
|
||||
[<$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 $crate::DatabaseRw<$table> {
|
||||
&mut self.$index
|
||||
}
|
||||
)*
|
||||
}
|
||||
|
||||
/// 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.
|
||||
///
|
||||
/// Note that this is already implemented on [`cuprate_database::EnvInner`], thus:
|
||||
/// - You don't need to implement this
|
||||
/// - It can be called using `env_inner.open_tables()` notation
|
||||
pub trait OpenTables<'env> {
|
||||
/// The read-only transaction type of the backend.
|
||||
type Ro<'a>;
|
||||
/// The read-write transaction type of the backend.
|
||||
type Rw<'a>;
|
||||
|
||||
/// Open all tables in read/iter mode.
|
||||
///
|
||||
/// This calls [`cuprate_database::EnvInner::open_db_ro`] on all database tables
|
||||
/// and returns a structure that allows access to all tables.
|
||||
///
|
||||
/// # Errors
|
||||
/// This will only return [`cuprate_database::RuntimeError::Io`] if it errors.
|
||||
///
|
||||
/// # Invariant
|
||||
/// All tables should be created with a crate-specific open function.
|
||||
///
|
||||
/// TODO: explain why
|
||||
fn open_tables(&self, tx_ro: &Self::Ro<'_>) -> Result<impl TablesIter, $crate::RuntimeError>;
|
||||
|
||||
/// Open all tables in read-write mode.
|
||||
///
|
||||
/// This calls [`cuprate_database::EnvInner::open_db_rw`] on all database tables
|
||||
/// and returns a structure that allows access to all tables.
|
||||
///
|
||||
/// # Errors
|
||||
/// This will only return [`cuprate_database::RuntimeError::Io`] on errors.
|
||||
fn open_tables_mut(&self, tx_rw: &Self::Rw<'_>) -> Result<impl TablesMut, $crate::RuntimeError>;
|
||||
|
||||
/// Create all database tables.
|
||||
///
|
||||
/// This will create all the defined [`Table`](cuprate_database::Table)s.
|
||||
///
|
||||
/// # Errors
|
||||
/// This will only return [`cuprate_database::RuntimeError::Io`] on errors.
|
||||
fn create_tables(&self, tx_rw: &Self::Rw<'_>) -> Result<(), $crate::RuntimeError>;
|
||||
}
|
||||
|
||||
impl<'env, Ei> OpenTables<'env> for Ei
|
||||
where
|
||||
Ei: $crate::EnvInner<'env>,
|
||||
{
|
||||
type Ro<'a> = <Ei as $crate::EnvInner<'env>>::Ro<'a>;
|
||||
type Rw<'a> = <Ei as $crate::EnvInner<'env>>::Rw<'a>;
|
||||
|
||||
fn open_tables(&self, tx_ro: &Self::Ro<'_>) -> Result<impl TablesIter, $crate::RuntimeError> {
|
||||
Ok(($(
|
||||
Self::open_db_ro::<[<$table:camel>]>(self, tx_ro)?,
|
||||
)*))
|
||||
}
|
||||
|
||||
fn open_tables_mut(&self, tx_rw: &Self::Rw<'_>) -> Result<impl TablesMut, $crate::RuntimeError> {
|
||||
Ok(($(
|
||||
Self::open_db_rw::<[<$table:camel>]>(self, tx_rw)?,
|
||||
)*))
|
||||
}
|
||||
|
||||
fn create_tables(&self, tx_rw: &Self::Rw<'_>) -> Result<(), $crate::RuntimeError> {
|
||||
let result = Ok(($(
|
||||
Self::create_db::<[<$table:camel>]>(self, tx_rw),
|
||||
)*));
|
||||
|
||||
match result {
|
||||
Ok(_) => Ok(()),
|
||||
Err(e) => Err(e),
|
||||
}
|
||||
}
|
||||
}
|
||||
}};
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------------------------------- Tests
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
// use super::*;
|
||||
}
|
Loading…
Reference in a new issue