Skip to main content

reth_db_api/
table.rs

1use crate::{
2    cursor::{DbCursorRO, DbCursorRW, DbDupCursorRO, DbDupCursorRW},
3    transaction::{DbTx, DbTxMut},
4    DatabaseError, Tables,
5};
6use serde::{Deserialize, Serialize};
7use std::fmt::Debug;
8
9pub use reth_codecs::{Compress, Decompress};
10
11/// Trait for converting encoded types to `Vec<u8>`.
12///
13/// This is implemented for all `AsRef<[u8]>` types. For `Vec<u8>` this is a no-op,
14/// for other types like `ArrayVec` or fixed arrays it performs a copy.
15pub trait IntoVec: AsRef<[u8]> {
16    /// Convert to a `Vec<u8>`.
17    fn into_vec(self) -> Vec<u8>;
18}
19
20impl IntoVec for Vec<u8> {
21    #[inline]
22    fn into_vec(self) -> Vec<u8> {
23        self
24    }
25}
26
27impl<const N: usize> IntoVec for [u8; N] {
28    #[inline]
29    fn into_vec(self) -> Vec<u8> {
30        self.to_vec()
31    }
32}
33
34impl<const N: usize> IntoVec for arrayvec::ArrayVec<u8, N> {
35    #[inline]
36    fn into_vec(self) -> Vec<u8> {
37        self.to_vec()
38    }
39}
40
41/// Trait that will transform the data to be saved in the DB.
42pub trait Encode: Send + Sync + Sized + Debug {
43    /// Encoded type.
44    type Encoded: IntoVec + Send + Sync + Ord + Debug;
45
46    /// Encodes data going into the database.
47    fn encode(self) -> Self::Encoded;
48}
49
50/// Trait that will transform the data to be read from the DB.
51pub trait Decode: Send + Sync + Sized + Debug {
52    /// Decodes data coming from the database.
53    fn decode(value: &[u8]) -> Result<Self, DatabaseError>;
54
55    /// Decodes owned data coming from the database.
56    fn decode_owned(value: Vec<u8>) -> Result<Self, DatabaseError> {
57        Self::decode(&value)
58    }
59}
60
61/// Generic trait that enforces the database key to implement [`Encode`] and [`Decode`].
62pub trait Key: Encode + Decode + Ord + Clone + Serialize + for<'a> Deserialize<'a> {}
63
64impl<T> Key for T where T: Encode + Decode + Ord + Clone + Serialize + for<'a> Deserialize<'a> {}
65
66/// Generic trait that enforces the database value to implement [`Compress`] and [`Decompress`].
67pub trait Value: Compress + Decompress + Serialize {}
68
69impl<T> Value for T where T: Compress + Decompress + Serialize {}
70
71/// Generic trait that a database table should follow.
72///
73/// The [`Table::Key`] and [`Table::Value`] types should implement [`Encode`] and
74/// [`Decode`] when appropriate. These traits define how the data is stored and read from the
75/// database.
76///
77/// It allows for the use of codecs. See [`crate::models::ShardedKey`] for a custom
78/// implementation.
79pub trait Table: Send + Sync + Debug + 'static {
80    /// The table's name.
81    const NAME: &'static str;
82
83    /// Whether the table is also a `DUPSORT` table.
84    const DUPSORT: bool;
85
86    /// The [`Tables`] entry this table is stored in, or `None` for tables defined outside of
87    /// [`Tables`].
88    ///
89    /// Database implementations use it to find per-table state by position instead of by
90    /// [`Self::NAME`]. A table that reuses the name of a [`Tables`] entry must return that entry.
91    const TABLE: Option<Tables> = None;
92
93    /// Key element of `Table`.
94    ///
95    /// Sorting should be taken into account when encoding this.
96    type Key: Key;
97
98    /// Value element of `Table`.
99    type Value: Value;
100}
101
102/// Trait that provides object-safe access to the table's metadata.
103pub trait TableInfo: Send + Sync + Debug + 'static {
104    /// The table's name.
105    fn name(&self) -> &'static str;
106
107    /// Whether the table is a `DUPSORT` table.
108    fn is_dupsort(&self) -> bool;
109}
110
111/// Tuple with `T::Key` and `T::Value`.
112pub type TableRow<T> = (<T as Table>::Key, <T as Table>::Value);
113
114/// `DupSort` allows for keys to be repeated in the database.
115///
116/// Upstream docs: <https://libmdbx.dqdkfa.ru/usage.html#autotoc_md48>
117pub trait DupSort: Table {
118    /// The table subkey. This type must implement [`Encode`] and [`Decode`].
119    ///
120    /// Sorting should be taken into account when encoding this.
121    ///
122    /// Upstream docs: <https://libmdbx.dqdkfa.ru/usage.html#autotoc_md48>
123    type SubKey: Key;
124}
125
126/// Allows duplicating tables across databases
127pub trait TableImporter: DbTxMut {
128    /// Imports all table data from another transaction.
129    fn import_table<T: Table, R: DbTx>(&self, source_tx: &R) -> Result<(), DatabaseError> {
130        let mut destination_cursor = self.cursor_write::<T>()?;
131
132        for kv in source_tx.cursor_read::<T>()?.walk(None)? {
133            let (k, v) = kv?;
134            destination_cursor.append(k, &v)?;
135        }
136
137        Ok(())
138    }
139
140    /// Imports table data from another transaction within a range.
141    ///
142    /// This method works correctly with both regular and `DupSort` tables. For `DupSort` tables,
143    /// all duplicate entries within the range are preserved during import.
144    fn import_table_with_range<T: Table, R: DbTx>(
145        &self,
146        source_tx: &R,
147        from: Option<<T as Table>::Key>,
148        to: <T as Table>::Key,
149    ) -> Result<(), DatabaseError>
150    where
151        T::Key: Default,
152    {
153        let mut destination_cursor = self.cursor_write::<T>()?;
154        let mut source_cursor = source_tx.cursor_read::<T>()?;
155
156        let source_range = match from {
157            Some(from) => source_cursor.walk_range(from..=to),
158            None => source_cursor.walk_range(..=to),
159        };
160        for row in source_range? {
161            let (key, value) = row?;
162            destination_cursor.append(key, &value)?;
163        }
164
165        Ok(())
166    }
167
168    /// Imports all dupsort data from another transaction.
169    fn import_dupsort<T: DupSort, R: DbTx>(&self, source_tx: &R) -> Result<(), DatabaseError> {
170        let mut destination_cursor = self.cursor_dup_write::<T>()?;
171        let mut cursor = source_tx.cursor_dup_read::<T>()?;
172
173        while let Some((k, _)) = cursor.next_no_dup()? {
174            for kv in cursor.walk_dup(Some(k), None)? {
175                let (k, v) = kv?;
176                destination_cursor.append_dup(k, v)?;
177            }
178        }
179
180        Ok(())
181    }
182}