mirror of
https://github.com/Cuprate/cuprate.git
synced 2024-11-16 15:58:17 +00:00
storage: move table generator macro blockchain
-> database
(#222)
Some checks failed
Audit / audit (push) Has been cancelled
CI / fmt (push) Has been cancelled
CI / typo (push) Has been cancelled
CI / ci (macos-latest, stable, bash) (push) Has been cancelled
CI / ci (ubuntu-latest, stable, bash) (push) Has been cancelled
CI / ci (windows-latest, stable-x86_64-pc-windows-gnu, msys2 {0}) (push) Has been cancelled
Deny / audit (push) Has been cancelled
Doc / build (push) Has been cancelled
Doc / deploy (push) Has been cancelled
Some checks failed
Audit / audit (push) Has been cancelled
CI / fmt (push) Has been cancelled
CI / typo (push) Has been cancelled
CI / ci (macos-latest, stable, bash) (push) Has been cancelled
CI / ci (ubuntu-latest, stable, bash) (push) Has been cancelled
CI / ci (windows-latest, stable-x86_64-pc-windows-gnu, msys2 {0}) (push) Has been cancelled
Deny / audit (push) Has been cancelled
Doc / build (push) Has been cancelled
Doc / deploy (push) Has been cancelled
* 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",
|
||||||
"hex-literal",
|
"hex-literal",
|
||||||
"monero-serai",
|
"monero-serai",
|
||||||
"paste",
|
|
||||||
"pretty_assertions",
|
"pretty_assertions",
|
||||||
"proptest",
|
"proptest",
|
||||||
"rayon",
|
"rayon",
|
||||||
|
@ -601,6 +600,7 @@ dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"heed",
|
"heed",
|
||||||
"page_size",
|
"page_size",
|
||||||
|
"paste",
|
||||||
"redb",
|
"redb",
|
||||||
"serde",
|
"serde",
|
||||||
"tempfile",
|
"tempfile",
|
||||||
|
|
|
@ -30,7 +30,6 @@ bytemuck = { version = "1.14.3", features = ["must_cast", "derive", "min
|
||||||
curve25519-dalek = { workspace = true }
|
curve25519-dalek = { workspace = true }
|
||||||
cuprate-pruning = { path = "../../pruning" }
|
cuprate-pruning = { path = "../../pruning" }
|
||||||
monero-serai = { workspace = true, features = ["std"] }
|
monero-serai = { workspace = true, features = ["std"] }
|
||||||
paste = { workspace = true }
|
|
||||||
serde = { workspace = true, optional = true }
|
serde = { workspace = true, optional = true }
|
||||||
|
|
||||||
# `service` feature.
|
# `service` feature.
|
||||||
|
|
|
@ -67,8 +67,7 @@ use cuprate_blockchain::{
|
||||||
DatabaseRo, DatabaseRw, TxRo, TxRw,
|
DatabaseRo, DatabaseRw, TxRo, TxRw,
|
||||||
},
|
},
|
||||||
config::ConfigBuilder,
|
config::ConfigBuilder,
|
||||||
tables::{Tables, TablesMut},
|
tables::{Tables, TablesMut, OpenTables},
|
||||||
OpenTables,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
# fn main() -> Result<(), Box<dyn std::error::Error>> {
|
# fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
//---------------------------------------------------------------------------------------------------- Import
|
//---------------------------------------------------------------------------------------------------- Import
|
||||||
use cuprate_database::{ConcreteEnv, Env, EnvInner, InitError, RuntimeError, TxRw};
|
use cuprate_database::{ConcreteEnv, Env, EnvInner, InitError, RuntimeError, TxRw};
|
||||||
|
|
||||||
use crate::{config::Config, open_tables::OpenTables};
|
use crate::{config::Config, tables::OpenTables};
|
||||||
|
|
||||||
//---------------------------------------------------------------------------------------------------- Free functions
|
//---------------------------------------------------------------------------------------------------- Free functions
|
||||||
/// Open the blockchain database, using the passed [`Config`].
|
/// 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.
|
// Documentation for each module is located in the respective file.
|
||||||
|
|
||||||
pub mod config;
|
|
||||||
|
|
||||||
mod constants;
|
mod constants;
|
||||||
pub use constants::{DATABASE_CORRUPT_MSG, DATABASE_VERSION};
|
|
||||||
|
|
||||||
mod open_tables;
|
|
||||||
pub use open_tables::OpenTables;
|
|
||||||
|
|
||||||
mod free;
|
mod free;
|
||||||
|
|
||||||
|
pub use constants::{DATABASE_CORRUPT_MSG, DATABASE_VERSION};
|
||||||
|
pub use cuprate_database;
|
||||||
pub use free::open;
|
pub use free::open;
|
||||||
|
|
||||||
|
pub mod config;
|
||||||
pub mod ops;
|
pub mod ops;
|
||||||
pub mod tables;
|
pub mod tables;
|
||||||
pub mod types;
|
pub mod types;
|
||||||
|
|
||||||
pub use cuprate_database;
|
|
||||||
|
|
||||||
//---------------------------------------------------------------------------------------------------- Feature-gated
|
//---------------------------------------------------------------------------------------------------- Feature-gated
|
||||||
#[cfg(feature = "service")]
|
#[cfg(feature = "service")]
|
||||||
pub mod 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 super::*;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
open_tables::OpenTables,
|
|
||||||
ops::tx::{get_tx, tx_exists},
|
ops::tx::{get_tx, tx_exists},
|
||||||
|
tables::OpenTables,
|
||||||
tests::{assert_all_tables_are_empty, tmp_concrete_env, AssertTableLen},
|
tests::{assert_all_tables_are_empty, tmp_concrete_env, AssertTableLen},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -87,9 +87,8 @@ mod test {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
open_tables::OpenTables,
|
|
||||||
ops::block::add_block,
|
ops::block::add_block,
|
||||||
tables::Tables,
|
tables::{OpenTables, Tables},
|
||||||
tests::{assert_all_tables_are_empty, tmp_concrete_env, AssertTableLen},
|
tests::{assert_all_tables_are_empty, tmp_concrete_env, AssertTableLen},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -52,8 +52,7 @@ mod test {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
open_tables::OpenTables,
|
tables::{OpenTables, Tables, TablesMut},
|
||||||
tables::{Tables, TablesMut},
|
|
||||||
tests::{assert_all_tables_are_empty, tmp_concrete_env, AssertTableLen},
|
tests::{assert_all_tables_are_empty, tmp_concrete_env, AssertTableLen},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -61,9 +61,8 @@
|
||||||
//! Env, EnvInner,
|
//! Env, EnvInner,
|
||||||
//! DatabaseRo, DatabaseRw, TxRo, TxRw,
|
//! DatabaseRo, DatabaseRw, TxRo, TxRw,
|
||||||
//! },
|
//! },
|
||||||
//! OpenTables,
|
|
||||||
//! config::ConfigBuilder,
|
//! config::ConfigBuilder,
|
||||||
//! tables::{Tables, TablesMut},
|
//! tables::{Tables, TablesMut, OpenTables},
|
||||||
//! ops::block::{add_block, pop_block},
|
//! ops::block::{add_block, pop_block},
|
||||||
//! };
|
//! };
|
||||||
//!
|
//!
|
||||||
|
|
|
@ -254,8 +254,7 @@ mod test {
|
||||||
use cuprate_database::{Env, EnvInner};
|
use cuprate_database::{Env, EnvInner};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
open_tables::OpenTables,
|
tables::{OpenTables, Tables, TablesMut},
|
||||||
tables::{Tables, TablesMut},
|
|
||||||
tests::{assert_all_tables_are_empty, tmp_concrete_env, AssertTableLen},
|
tests::{assert_all_tables_are_empty, tmp_concrete_env, AssertTableLen},
|
||||||
types::OutputFlags,
|
types::OutputFlags,
|
||||||
};
|
};
|
||||||
|
|
|
@ -331,8 +331,7 @@ mod test {
|
||||||
use cuprate_test_utils::data::{tx_v1_sig0, tx_v1_sig2, tx_v2_rct3};
|
use cuprate_test_utils::data::{tx_v1_sig0, tx_v1_sig2, tx_v2_rct3};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
open_tables::OpenTables,
|
tables::{OpenTables, Tables},
|
||||||
tables::Tables,
|
|
||||||
tests::{assert_all_tables_are_empty, tmp_concrete_env, AssertTableLen},
|
tests::{assert_all_tables_are_empty, tmp_concrete_env, AssertTableLen},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -22,7 +22,6 @@ use cuprate_types::{
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
config::ReaderThreads,
|
config::ReaderThreads,
|
||||||
open_tables::OpenTables,
|
|
||||||
ops::{
|
ops::{
|
||||||
block::{
|
block::{
|
||||||
block_exists, get_block_extended_header_from_height, get_block_height, get_block_info,
|
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},
|
free::{compact_history_genesis_not_included, compact_history_index_to_height_offset},
|
||||||
types::{ResponseReceiver, ResponseResult, ResponseSender},
|
types::{ResponseReceiver, ResponseResult, ResponseSender},
|
||||||
},
|
},
|
||||||
|
tables::OpenTables,
|
||||||
tables::{BlockHeights, BlockInfos, Tables},
|
tables::{BlockHeights, BlockInfos, Tables},
|
||||||
types::{Amount, AmountIndex, BlockHash, BlockHeight, KeyImage, PreRctOutputId},
|
types::{Amount, AmountIndex, BlockHash, BlockHeight, KeyImage, PreRctOutputId},
|
||||||
};
|
};
|
||||||
|
|
|
@ -24,14 +24,13 @@ use cuprate_types::{
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
config::ConfigBuilder,
|
config::ConfigBuilder,
|
||||||
open_tables::OpenTables,
|
|
||||||
ops::{
|
ops::{
|
||||||
block::{get_block_extended_header_from_height, get_block_info},
|
block::{get_block_extended_header_from_height, get_block_info},
|
||||||
blockchain::chain_height,
|
blockchain::chain_height,
|
||||||
output::id_to_output_on_chain,
|
output::id_to_output_on_chain,
|
||||||
},
|
},
|
||||||
service::{init, DatabaseReadHandle, DatabaseWriteHandle},
|
service::{init, DatabaseReadHandle, DatabaseWriteHandle},
|
||||||
tables::{Tables, TablesIter},
|
tables::{OpenTables, Tables, TablesIter},
|
||||||
tests::AssertTableLen,
|
tests::AssertTableLen,
|
||||||
types::{Amount, AmountIndex, PreRctOutputId},
|
types::{Amount, AmountIndex, PreRctOutputId},
|
||||||
};
|
};
|
||||||
|
|
|
@ -16,8 +16,8 @@ use cuprate_types::{
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
open_tables::OpenTables,
|
|
||||||
service::types::{ResponseReceiver, ResponseResult, ResponseSender},
|
service::types::{ResponseReceiver, ResponseResult, ResponseSender},
|
||||||
|
tables::OpenTables,
|
||||||
};
|
};
|
||||||
|
|
||||||
//---------------------------------------------------------------------------------------------------- Constants
|
//---------------------------------------------------------------------------------------------------- Constants
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
//! This module contains all the table definitions used by `cuprate_blockchain`.
|
//! This module contains all the table definitions used by `cuprate_blockchain`.
|
||||||
//!
|
//!
|
||||||
//! The zero-sized structs here represents the table type;
|
//! 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
|
//! Table structs are `CamelCase`, and their static string
|
||||||
//! names used by the actual database backend are `snake_case`.
|
//! names used by the actual database backend are `snake_case`.
|
||||||
|
@ -14,311 +14,14 @@
|
||||||
//! # Traits
|
//! # Traits
|
||||||
//! This module also contains a set of traits for
|
//! This module also contains a set of traits for
|
||||||
//! accessing _all_ tables defined here at once.
|
//! accessing _all_ tables defined here at once.
|
||||||
//!
|
|
||||||
//! For example, this is the object returned by [`OpenTables::open_tables`](crate::OpenTables::open_tables).
|
|
||||||
|
|
||||||
//---------------------------------------------------------------------------------------------------- Import
|
//---------------------------------------------------------------------------------------------------- Import
|
||||||
use cuprate_database::{DatabaseIter, DatabaseRo, DatabaseRw, Table};
|
|
||||||
|
|
||||||
use crate::types::{
|
use crate::types::{
|
||||||
Amount, AmountIndex, AmountIndices, BlockBlob, BlockHash, BlockHeight, BlockInfo, KeyImage,
|
Amount, AmountIndex, AmountIndices, BlockBlob, BlockHash, BlockHeight, BlockInfo, KeyImage,
|
||||||
Output, PreRctOutputId, PrunableBlob, PrunableHash, PrunedBlob, RctOutput, TxBlob, TxHash,
|
Output, PreRctOutputId, PrunableBlob, PrunableHash, PrunedBlob, RctOutput, TxBlob, TxHash,
|
||||||
TxId, UnlockTime,
|
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
|
//---------------------------------------------------------------------------------------------------- Tables
|
||||||
// Notes:
|
// Notes:
|
||||||
// - Keep this sorted A-Z (by table name)
|
// - Keep this sorted A-Z (by table name)
|
||||||
|
@ -326,23 +29,23 @@ macro_rules! tables {
|
||||||
// - If adding/changing a table also edit:
|
// - If adding/changing a table also edit:
|
||||||
// - the tests in `src/backend/tests.rs`
|
// - the tests in `src/backend/tests.rs`
|
||||||
// - `call_fn_on_all_tables_or_early_return!()` macro in `src/open_tables.rs`
|
// - `call_fn_on_all_tables_or_early_return!()` macro in `src/open_tables.rs`
|
||||||
tables! {
|
cuprate_database::define_tables! {
|
||||||
/// Serialized block blobs (bytes).
|
/// Serialized block blobs (bytes).
|
||||||
///
|
///
|
||||||
/// Contains the serialized version of all blocks.
|
/// Contains the serialized version of all blocks.
|
||||||
BlockBlobs,
|
0 => BlockBlobs,
|
||||||
BlockHeight => BlockBlob,
|
BlockHeight => BlockBlob,
|
||||||
|
|
||||||
/// Block heights.
|
/// Block heights.
|
||||||
///
|
///
|
||||||
/// Contains the height of all blocks.
|
/// Contains the height of all blocks.
|
||||||
BlockHeights,
|
1 => BlockHeights,
|
||||||
BlockHash => BlockHeight,
|
BlockHash => BlockHeight,
|
||||||
|
|
||||||
/// Block information.
|
/// Block information.
|
||||||
///
|
///
|
||||||
/// Contains metadata of all blocks.
|
/// Contains metadata of all blocks.
|
||||||
BlockInfos,
|
2 => BlockInfos,
|
||||||
BlockHeight => BlockInfo,
|
BlockHeight => BlockInfo,
|
||||||
|
|
||||||
/// Set of key images.
|
/// Set of key images.
|
||||||
|
@ -351,38 +54,38 @@ tables! {
|
||||||
///
|
///
|
||||||
/// This table has `()` as the value type, as in,
|
/// This table has `()` as the value type, as in,
|
||||||
/// it is a set of key images.
|
/// it is a set of key images.
|
||||||
KeyImages,
|
3 => KeyImages,
|
||||||
KeyImage => (),
|
KeyImage => (),
|
||||||
|
|
||||||
/// Maps an output's amount to the number of outputs with that amount.
|
/// Maps an output's amount to the number of outputs with that amount.
|
||||||
///
|
///
|
||||||
/// For example, if there are 5 outputs with `amount = 123`
|
/// For example, if there are 5 outputs with `amount = 123`
|
||||||
/// then calling `get(123)` on this table will return 5.
|
/// then calling `get(123)` on this table will return 5.
|
||||||
NumOutputs,
|
4 => NumOutputs,
|
||||||
Amount => u64,
|
Amount => u64,
|
||||||
|
|
||||||
/// Pre-RCT output data.
|
/// Pre-RCT output data.
|
||||||
Outputs,
|
5 => Outputs,
|
||||||
PreRctOutputId => Output,
|
PreRctOutputId => Output,
|
||||||
|
|
||||||
/// Pruned transaction blobs (bytes).
|
/// Pruned transaction blobs (bytes).
|
||||||
///
|
///
|
||||||
/// Contains the pruned portion of serialized transaction data.
|
/// Contains the pruned portion of serialized transaction data.
|
||||||
PrunedTxBlobs,
|
6 => PrunedTxBlobs,
|
||||||
TxId => PrunedBlob,
|
TxId => PrunedBlob,
|
||||||
|
|
||||||
/// Prunable transaction blobs (bytes).
|
/// Prunable transaction blobs (bytes).
|
||||||
///
|
///
|
||||||
/// Contains the prunable portion of serialized transaction data.
|
/// Contains the prunable portion of serialized transaction data.
|
||||||
// SOMEDAY: impl when `monero-serai` supports pruning
|
// SOMEDAY: impl when `monero-serai` supports pruning
|
||||||
PrunableTxBlobs,
|
7 => PrunableTxBlobs,
|
||||||
TxId => PrunableBlob,
|
TxId => PrunableBlob,
|
||||||
|
|
||||||
/// Prunable transaction hashes.
|
/// Prunable transaction hashes.
|
||||||
///
|
///
|
||||||
/// Contains the prunable portion of transaction hashes.
|
/// Contains the prunable portion of transaction hashes.
|
||||||
// SOMEDAY: impl when `monero-serai` supports pruning
|
// SOMEDAY: impl when `monero-serai` supports pruning
|
||||||
PrunableHashes,
|
8 => PrunableHashes,
|
||||||
TxId => PrunableHash,
|
TxId => PrunableHash,
|
||||||
|
|
||||||
// SOMEDAY: impl a properties table:
|
// SOMEDAY: impl a properties table:
|
||||||
|
@ -392,40 +95,40 @@ tables! {
|
||||||
// StorableString => StorableVec,
|
// StorableString => StorableVec,
|
||||||
|
|
||||||
/// RCT output data.
|
/// RCT output data.
|
||||||
RctOutputs,
|
9 => RctOutputs,
|
||||||
AmountIndex => RctOutput,
|
AmountIndex => RctOutput,
|
||||||
|
|
||||||
/// Transaction blobs (bytes).
|
/// Transaction blobs (bytes).
|
||||||
///
|
///
|
||||||
/// Contains the serialized version of all transactions.
|
/// Contains the serialized version of all transactions.
|
||||||
// SOMEDAY: remove when `monero-serai` supports pruning
|
// SOMEDAY: remove when `monero-serai` supports pruning
|
||||||
TxBlobs,
|
10 => TxBlobs,
|
||||||
TxId => TxBlob,
|
TxId => TxBlob,
|
||||||
|
|
||||||
/// Transaction indices.
|
/// Transaction indices.
|
||||||
///
|
///
|
||||||
/// Contains the indices all transactions.
|
/// Contains the indices all transactions.
|
||||||
TxIds,
|
11 => TxIds,
|
||||||
TxHash => TxId,
|
TxHash => TxId,
|
||||||
|
|
||||||
/// Transaction heights.
|
/// Transaction heights.
|
||||||
///
|
///
|
||||||
/// Contains the block height associated with all transactions.
|
/// Contains the block height associated with all transactions.
|
||||||
TxHeights,
|
12 => TxHeights,
|
||||||
TxId => BlockHeight,
|
TxId => BlockHeight,
|
||||||
|
|
||||||
/// Transaction outputs.
|
/// Transaction outputs.
|
||||||
///
|
///
|
||||||
/// Contains the list of `AmountIndex`'s of the
|
/// Contains the list of `AmountIndex`'s of the
|
||||||
/// outputs associated with all transactions.
|
/// outputs associated with all transactions.
|
||||||
TxOutputs,
|
13 => TxOutputs,
|
||||||
TxId => AmountIndices,
|
TxId => AmountIndices,
|
||||||
|
|
||||||
/// Transaction unlock time.
|
/// Transaction unlock time.
|
||||||
///
|
///
|
||||||
/// Contains the unlock time of transactions IF they have one.
|
/// Contains the unlock time of transactions IF they have one.
|
||||||
/// Transactions without unlock times will not exist in this table.
|
/// Transactions without unlock times will not exist in this table.
|
||||||
TxUnlockTime,
|
14 => TxUnlockTime,
|
||||||
TxId => UnlockTime,
|
TxId => UnlockTime,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -11,7 +11,10 @@ use pretty_assertions::assert_eq;
|
||||||
|
|
||||||
use cuprate_database::{ConcreteEnv, DatabaseRo, Env, EnvInner};
|
use cuprate_database::{ConcreteEnv, DatabaseRo, Env, EnvInner};
|
||||||
|
|
||||||
use crate::{config::ConfigBuilder, open_tables::OpenTables, tables::Tables};
|
use crate::{
|
||||||
|
config::ConfigBuilder,
|
||||||
|
tables::{OpenTables, Tables},
|
||||||
|
};
|
||||||
|
|
||||||
//---------------------------------------------------------------------------------------------------- Struct
|
//---------------------------------------------------------------------------------------------------- Struct
|
||||||
/// Named struct to assert the length of all tables.
|
/// 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 }
|
bytes = { workspace = true }
|
||||||
cfg-if = { 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.
|
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 }
|
thiserror = { workspace = true }
|
||||||
|
|
||||||
# Optional features.
|
# 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.
|
generic-backed dynamic runtime selection of the database backend, i.e.
|
||||||
the user can select which database backend they use. -->
|
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
|
# Feature flags
|
||||||
Different database backends are enabled by the feature flags:
|
Different database backends are enabled by the feature flags:
|
||||||
- `heed` (LMDB)
|
- `heed` (LMDB)
|
||||||
|
|
|
@ -78,6 +78,8 @@
|
||||||
clippy::module_inception,
|
clippy::module_inception,
|
||||||
clippy::redundant_pub_crate,
|
clippy::redundant_pub_crate,
|
||||||
clippy::option_if_let_else,
|
clippy::option_if_let_else,
|
||||||
|
|
||||||
|
// unused_crate_dependencies, // false-positive with `paste`
|
||||||
)]
|
)]
|
||||||
// Allow some lints when running in debug mode.
|
// Allow some lints when running in debug mode.
|
||||||
#![cfg_attr(
|
#![cfg_attr(
|
||||||
|
@ -105,42 +107,39 @@
|
||||||
// Documentation for each module is located in the respective file.
|
// Documentation for each module is located in the respective file.
|
||||||
|
|
||||||
mod backend;
|
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 config;
|
||||||
|
pub mod resize;
|
||||||
|
|
||||||
mod constants;
|
pub use backend::ConcreteEnv;
|
||||||
pub use constants::{
|
pub use constants::{
|
||||||
DATABASE_BACKEND, DATABASE_CORRUPT_MSG, DATABASE_DATA_FILENAME, DATABASE_LOCK_FILENAME,
|
DATABASE_BACKEND, DATABASE_CORRUPT_MSG, DATABASE_DATA_FILENAME, DATABASE_LOCK_FILENAME,
|
||||||
};
|
};
|
||||||
|
|
||||||
mod database;
|
|
||||||
pub use database::{DatabaseIter, DatabaseRo, DatabaseRw};
|
pub use database::{DatabaseIter, DatabaseRo, DatabaseRw};
|
||||||
|
|
||||||
mod env;
|
|
||||||
pub use env::{Env, EnvInner};
|
pub use env::{Env, EnvInner};
|
||||||
|
|
||||||
mod error;
|
|
||||||
pub use error::{InitError, RuntimeError};
|
pub use error::{InitError, RuntimeError};
|
||||||
|
|
||||||
pub mod resize;
|
|
||||||
|
|
||||||
mod key;
|
|
||||||
pub use key::{Key, KeyCompare};
|
pub use key::{Key, KeyCompare};
|
||||||
|
|
||||||
mod storable;
|
|
||||||
pub use storable::{Storable, StorableBytes, StorableStr, StorableVec};
|
pub use storable::{Storable, StorableBytes, StorableStr, StorableVec};
|
||||||
|
|
||||||
mod table;
|
|
||||||
pub use table::Table;
|
pub use table::Table;
|
||||||
|
|
||||||
mod transaction;
|
|
||||||
pub use transaction::{TxRo, TxRw};
|
pub use transaction::{TxRo, TxRw};
|
||||||
|
|
||||||
//---------------------------------------------------------------------------------------------------- Private
|
//---------------------------------------------------------------------------------------------------- Private
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
pub(crate) mod tests;
|
pub(crate) mod tests;
|
||||||
|
|
||||||
|
// Used inside public facing macros.
|
||||||
|
#[doc(hidden)]
|
||||||
|
pub use paste;
|
||||||
|
|
||||||
//----------------------------------------------------------------------------------------------------
|
//----------------------------------------------------------------------------------------------------
|
||||||
// HACK: needed to satisfy the `unused_crate_dependencies` lint.
|
// HACK: needed to satisfy the `unused_crate_dependencies` lint.
|
||||||
cfg_if::cfg_if! {
|
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