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}