cuprate_database/
tables.rs

1//! Database table definition macro.
2
3//---------------------------------------------------------------------------------------------------- Import
4
5//---------------------------------------------------------------------------------------------------- Table macro
6/// Define all table types.
7///
8/// # Purpose
9/// This macro allows you to define all database tables in one place.
10///
11/// A by-product of this macro is that it defines some
12/// convenient traits specific to _your_ tables
13/// (see [Output](#output)).
14///
15/// # Inputs
16/// This macro expects a list of tables, and their key/value types.
17///
18/// This syntax is as follows:
19///
20/// ```rust
21/// cuprate_database::define_tables! {
22///     /// Any extra attributes you'd like to add to
23///     /// this table type, e.g. docs or derives.
24///
25///     0 => TableName,
26/// //  ▲    ▲
27/// //  │    └─ Table struct name. The macro generates this for you.
28/// //  │
29/// // Incrementing index. This must start at 0
30/// // and increment by 1 per table added.
31///
32///     u8 => u64,
33/// //  ▲    ▲
34/// //  │    └─ Table value type.
35/// //  │
36/// // Table key type.
37///
38///    // Another table.
39///    1 => TableName2,
40///    i8 => i64,
41/// }
42/// ```
43///
44/// An example:
45/// ```rust
46/// use cuprate_database::{
47///     ConcreteEnv, Table,
48///     config::ConfigBuilder,
49///     Env, EnvInner,
50///     DatabaseRo, DatabaseRw, TxRo, TxRw,
51/// };
52///
53/// // This generates `pub struct Table{1,2,3}`
54/// // where all those implement `Table` with
55/// // the defined name and key/value types.
56/// //
57/// // It also generate traits specific to our tables.
58/// cuprate_database::define_tables! {
59///     0 => Table1,
60///     u32 => i32,
61///
62///     /// This one has extra docs.
63///     1 => Table2,
64///     u64 => (),
65///
66///     2 => Table3,
67///     i32 => i32,
68/// }
69///
70/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
71/// # let tmp_dir = tempfile::tempdir()?;
72/// # let db_dir = tmp_dir.path().to_owned();
73/// # let config = ConfigBuilder::new(db_dir.into()).build();
74/// // Open the database.
75/// let env = ConcreteEnv::open(config)?;
76/// let env_inner = env.env_inner();
77///
78/// // Open the table we just defined.
79/// {
80///     let tx_rw = env_inner.tx_rw()?;
81///     env_inner.create_db::<Table1>(&tx_rw)?;
82///     let mut table = env_inner.open_db_rw::<Table1>(&tx_rw)?;
83///
84///     // Write data to the table.
85///     table.put(&0, &1)?;
86///
87///     drop(table);
88///     TxRw::commit(tx_rw)?;
89/// }
90///
91/// // Read the data, assert it is correct.
92/// {
93///     let tx_ro = env_inner.tx_ro()?;
94///     let table = env_inner.open_db_ro::<Table1>(&tx_ro)?;
95///     assert_eq!(table.first()?, (0, 1));
96/// }
97///
98/// // Create all tables at once using the
99/// // `OpenTables` trait generated with the
100/// // macro above.
101/// {
102///     let tx_rw = env_inner.tx_rw()?;
103///     env_inner.create_tables(&tx_rw)?;
104///     TxRw::commit(tx_rw)?;
105/// }
106///
107/// // Open all tables at once.
108/// {
109///     let tx_ro = env_inner.tx_ro()?;
110///     let all_tables = env_inner.open_tables(&tx_ro)?;
111/// }
112/// # Ok(()) }
113/// ```
114///
115/// # Output
116/// This macro:
117/// 1. Implements [`Table`](crate::Table) on all your table types
118/// 1. Creates a `pub trait Tables` trait (in scope)
119/// 1. Creates a `pub trait TablesIter` trait (in scope)
120/// 1. Creates a `pub trait TablesMut` trait (in scope)
121/// 1. Blanket implements a `(tuples, containing, all, open, database, tables, ...)` for the above traits
122/// 1. Creates a `pub trait OpenTables` trait (in scope)
123///
124/// All table types are zero-sized structs that implement the `Table` trait.
125///
126/// Table structs are automatically `CamelCase`, and their
127/// static string names are automatically `snake_case`.
128///
129/// For why the table traits + blanket implementation on the tuple exists, see:
130/// <https://github.com/Cuprate/cuprate/pull/102#pullrequestreview-1978348871>.
131///
132/// The `OpenTables` trait lets you open all tables you've defined, at once.
133///
134/// # Example
135/// For examples of usage & output, see
136/// [`cuprate_blockchain::tables`](https://github.com/Cuprate/cuprate/blob/main/storage/blockchain/src/tables.rs).
137#[macro_export]
138macro_rules! define_tables {
139    (
140        $(
141            // Documentation and any `derive`'s.
142            $(#[$attr:meta])*
143
144            // The table name + doubles as the table struct name.
145            $index:literal => $table:ident,
146
147            // Key type => Value type.
148            $key:ty => $value:ty
149        ),* $(,)?
150    ) => { $crate::paste::paste! {
151        $(
152            // Table struct.
153            $(#[$attr])*
154            #[doc = concat!("- Key: [`", stringify!($key), "`]")]
155            #[doc = concat!("- Value: [`", stringify!($value), "`]")]
156            #[doc = concat!("- Name: `", stringify!([<$table:snake>]), "`")]
157            #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
158            #[derive(Copy, Clone, Debug, PartialEq, PartialOrd, Eq, Ord, Hash)]
159            pub struct [<$table:camel>];
160
161            // Table trait impl.
162            impl $crate::Table for [<$table:camel>] {
163                const NAME: &'static str = stringify!([<$table:snake>]);
164                type Key = $key;
165                type Value = $value;
166            }
167        )*
168
169        /// Object containing all opened [`Table`](cuprate_database::Table)s in read-only mode.
170        ///
171        /// This is an encapsulated object that contains all
172        /// available `Table`'s in read-only mode.
173        ///
174        /// It is a `Sealed` trait and is only implemented on a
175        /// `(tuple, containing, all, table, types, ...)`.
176        ///
177        /// This is used to return a _single_ object from functions like
178        /// [`OpenTables::open_tables`] rather than the tuple containing the tables itself.
179        ///
180        /// To replace `tuple.0` style indexing, `field_accessor_functions()`
181        /// are provided on this trait, which essentially map the object to
182        /// fields containing the particular database table, for example:
183        /// ```rust,ignore
184        /// let tables = open_tables();
185        ///
186        /// // The accessor function `block_infos()` returns the field
187        /// // containing an open database table for `BlockInfos`.
188        /// let _ = tables.block_infos();
189        /// ```
190        ///
191        /// See also:
192        /// - [`TablesMut`]
193        /// - [`TablesIter`]
194        pub trait Tables {
195            // This expands to creating `fn field_accessor_functions()`
196            // for each passed `$table` type.
197            //
198            // It is essentially a mapping to the field
199            // containing the proper opened database table.
200            //
201            // The function name of the function is
202            // the table type in `snake_case`, e.g., `block_info_v1s()`.
203            $(
204                /// Access an opened
205                #[doc = concat!("[`", stringify!($table), "`]")]
206                /// database.
207                fn [<$table:snake>](&self) -> &impl $crate::DatabaseRo<$table>;
208            )*
209
210            /// This returns `true` if all tables are empty.
211            ///
212            /// # Errors
213            /// This returns errors on regular database errors.
214            fn all_tables_empty(&self) -> $crate::DbResult<bool>;
215        }
216
217        /// Object containing all opened [`Table`](cuprate_database::Table)s in read + iter mode.
218        ///
219        /// This is the same as [`Tables`] but includes `_iter()` variants.
220        ///
221        /// Note that this trait is a supertrait of `Tables`,
222        /// as in it can use all of its functions as well.
223        ///
224        /// See [`Tables`] for documentation - this trait exists for the same reasons.
225        pub trait TablesIter: Tables {
226            $(
227                /// Access an opened read-only + iterable
228                #[doc = concat!("[`", stringify!($table), "`]")]
229                /// database.
230                fn [<$table:snake _iter>](&self) -> &(impl $crate::DatabaseRo<$table> + $crate::DatabaseIter<$table>);
231            )*
232        }
233
234        /// Object containing all opened [`Table`](cuprate_database::Table)s in write mode.
235        ///
236        /// This is the same as [`Tables`] but for mutable accesses.
237        ///
238        /// Note that this trait is a supertrait of `Tables`,
239        /// as in it can use all of its functions as well.
240        ///
241        /// See [`Tables`] for documentation - this trait exists for the same reasons.
242        pub trait TablesMut: Tables {
243            $(
244                /// Access an opened
245                #[doc = concat!("[`", stringify!($table), "`]")]
246                /// database.
247                fn [<$table:snake _mut>](&mut self) -> &mut impl $crate::DatabaseRw<$table>;
248            )*
249        }
250
251        // This creates a blanket-implementation for
252        // `(tuple, containing, all, table, types)`.
253        //
254        // There is a generic defined here _for each_ `$table` input.
255        // Specifically, the generic letters are just the table types in UPPERCASE.
256        // Concretely, this expands to something like:
257        // ```rust
258        // impl<BLOCKINFOSV1S, BLOCKINFOSV2S, BLOCKINFOSV3S, [...]>
259        // ```
260        impl<$([<$table:upper>]),*> Tables
261            // We are implementing `Tables` on a tuple that
262            // contains all those generics specified, i.e.,
263            // a tuple containing all open table types.
264            //
265            // Concretely, this expands to something like:
266            // ```rust
267            // (BLOCKINFOSV1S, BLOCKINFOSV2S, BLOCKINFOSV3S, [...])
268            // ```
269            // which is just a tuple of the generics defined above.
270            for ($([<$table:upper>]),*)
271        where
272            // This expands to a where bound that asserts each element
273            // in the tuple implements some database table type.
274            //
275            // Concretely, this expands to something like:
276            // ```rust
277            // BLOCKINFOSV1S: DatabaseRo<BlockInfoV1s> + DatabaseIter<BlockInfoV1s>,
278            // BLOCKINFOSV2S: DatabaseRo<BlockInfoV2s> + DatabaseIter<BlockInfoV2s>,
279            // [...]
280            // ```
281            $(
282                [<$table:upper>]: $crate::DatabaseRo<$table>,
283            )*
284        {
285            $(
286                // The function name of the accessor function is
287                // the table type in `snake_case`, e.g., `block_info_v1s()`.
288                #[inline]
289                fn [<$table:snake>](&self) -> &impl $crate::DatabaseRo<$table> {
290                    // The index of the database table in
291                    // the tuple implements the table trait.
292                    &self.$index
293                }
294            )*
295
296            fn all_tables_empty(&self) -> $crate::DbResult<bool> {
297                $(
298                     if !$crate::DatabaseRo::is_empty(&self.$index)? {
299                        return Ok(false);
300                     }
301                )*
302                Ok(true)
303            }
304        }
305
306        // This is the same as the above
307        // `Tables`, but for `TablesIter`.
308        impl<$([<$table:upper>]),*> TablesIter
309            for ($([<$table:upper>]),*)
310        where
311            $(
312                [<$table:upper>]: $crate::DatabaseRo<$table> + $crate::DatabaseIter<$table>,
313            )*
314        {
315            $(
316                // The function name of the accessor function is
317                // the table type in `snake_case` + `_iter`, e.g., `block_info_v1s_iter()`.
318                #[inline]
319                fn [<$table:snake _iter>](&self) -> &(impl $crate::DatabaseRo<$table> + $crate::DatabaseIter<$table>) {
320                    &self.$index
321                }
322            )*
323        }
324
325        // This is the same as the above
326        // `Tables`, but for `TablesMut`.
327        impl<$([<$table:upper>]),*> TablesMut
328            for ($([<$table:upper>]),*)
329        where
330            $(
331                [<$table:upper>]: $crate::DatabaseRw<$table>,
332            )*
333        {
334            $(
335                // The function name of the mutable accessor function is
336                // the table type in `snake_case` + `_mut`, e.g., `block_info_v1s_mut()`.
337                #[inline]
338                fn [<$table:snake _mut>](&mut self) -> &mut impl $crate::DatabaseRw<$table> {
339                    &mut self.$index
340                }
341            )*
342        }
343
344        /// Open all tables at once.
345        ///
346        /// This trait encapsulates the functionality of opening all tables at once.
347        /// It can be seen as the "constructor" for the [`Tables`] object.
348        ///
349        /// Note that this is already implemented on [`cuprate_database::EnvInner`], thus:
350        /// - You don't need to implement this
351        /// - It can be called using `env_inner.open_tables()` notation
352        ///
353        /// # Creation before opening
354        /// As [`cuprate_database::EnvInner`] documentation states,
355        /// tables must be created before they are opened.
356        ///
357        /// I.e. [`OpenTables::create_tables`] must be called before
358        /// [`OpenTables::open_tables`] or else panics may occur.
359        pub trait OpenTables<'env> {
360            /// The read-only transaction type of the backend.
361            type Ro<'tx>;
362            /// The read-write transaction type of the backend.
363            type Rw<'tx>;
364
365            /// Open all tables in read/iter mode.
366            ///
367            /// This calls [`cuprate_database::EnvInner::open_db_ro`] on all database tables
368            /// and returns a structure that allows access to all tables.
369            ///
370            /// # Errors
371            /// This will only return [`cuprate_database::RuntimeError::Io`] if it errors.
372            fn open_tables(&self, tx_ro: &Self::Ro<'_>) -> $crate::DbResult<impl TablesIter>;
373
374            /// Open all tables in read-write mode.
375            ///
376            /// This calls [`cuprate_database::EnvInner::open_db_rw`] on all database tables
377            /// and returns a structure that allows access to all tables.
378            ///
379            /// # Errors
380            /// This will only return [`cuprate_database::RuntimeError::Io`] on errors.
381            fn open_tables_mut(&self, tx_rw: &Self::Rw<'_>) -> $crate::DbResult<impl TablesMut>;
382
383            /// Create all database tables.
384            ///
385            /// This will create all the defined [`Table`](cuprate_database::Table)s.
386            ///
387            /// # Errors
388            /// This will only return [`cuprate_database::RuntimeError::Io`] on errors.
389            fn create_tables(&self, tx_rw: &Self::Rw<'_>) -> $crate::DbResult<()>;
390        }
391
392        impl<'env, Ei> OpenTables<'env> for Ei
393        where
394            Ei: $crate::EnvInner<'env>,
395        {
396            type Ro<'tx> = <Ei as $crate::EnvInner<'env>>::Ro<'tx>;
397            type Rw<'tx> = <Ei as $crate::EnvInner<'env>>::Rw<'tx>;
398
399            fn open_tables(&self, tx_ro: &Self::Ro<'_>) -> $crate::DbResult<impl TablesIter> {
400                Ok(($(
401                    Self::open_db_ro::<[<$table:camel>]>(self, tx_ro)?,
402                )*))
403            }
404
405            fn open_tables_mut(&self, tx_rw: &Self::Rw<'_>) -> $crate::DbResult<impl TablesMut> {
406                Ok(($(
407                    Self::open_db_rw::<[<$table:camel>]>(self, tx_rw)?,
408                )*))
409            }
410
411            fn create_tables(&self, tx_rw: &Self::Rw<'_>) -> $crate::DbResult<()> {
412                let result = Ok(($(
413                    Self::create_db::<[<$table:camel>]>(self, tx_rw),
414                )*));
415
416                match result {
417                    Ok(_) => Ok(()),
418                    Err(e) => Err(e),
419                }
420            }
421        }
422    }};
423}
424
425//---------------------------------------------------------------------------------------------------- Tests
426#[cfg(test)]
427mod test {
428    // use super::*;
429}