reth_db_common/db_tool/
mod.rs
1use boyer_moore_magiclen::BMByte;
4use eyre::Result;
5use reth_db_api::{
6 cursor::{DbCursorRO, DbDupCursorRO},
7 database::Database,
8 table::{Decode, Decompress, DupSort, Table, TableRow},
9 transaction::{DbTx, DbTxMut},
10 DatabaseError, RawTable, TableRawRow,
11};
12use reth_fs_util as fs;
13use reth_node_types::NodeTypesWithDB;
14use reth_provider::{providers::ProviderNodeTypes, ChainSpecProvider, DBProvider, ProviderFactory};
15use std::{path::Path, rc::Rc, sync::Arc};
16use tracing::info;
17
18#[derive(Debug)]
20pub struct DbTool<N: NodeTypesWithDB> {
21 pub provider_factory: ProviderFactory<N>,
23}
24
25impl<N: NodeTypesWithDB> DbTool<N> {
26 pub fn chain(&self) -> Arc<N::ChainSpec> {
28 self.provider_factory.chain_spec()
29 }
30
31 pub fn list<T: Table>(&self, filter: &ListFilter) -> Result<(Vec<TableRow<T>>, usize)> {
37 let bmb = Rc::new(BMByte::from(&filter.search));
38 if bmb.is_none() && filter.has_search() {
39 eyre::bail!("Invalid search.")
40 }
41
42 let mut hits = 0;
43
44 let data = self.provider_factory.db_ref().view(|tx| {
45 let mut cursor =
46 tx.cursor_read::<RawTable<T>>().expect("Was not able to obtain a cursor.");
47
48 let map_filter = |row: Result<TableRawRow<T>, _>| {
49 if let Ok((k, v)) = row {
50 let (key, value) = (k.into_key(), v.into_value());
51
52 if key.len() + value.len() < filter.min_row_size {
53 return None
54 }
55 if key.len() < filter.min_key_size {
56 return None
57 }
58 if value.len() < filter.min_value_size {
59 return None
60 }
61
62 let result = || {
63 if filter.only_count {
64 return None
65 }
66 Some((
67 <T as Table>::Key::decode(&key).unwrap(),
68 <T as Table>::Value::decompress(&value).unwrap(),
69 ))
70 };
71
72 match &*bmb {
73 Some(searcher) => {
74 if searcher.find_first_in(&value).is_some() ||
75 searcher.find_first_in(&key).is_some()
76 {
77 hits += 1;
78 return result()
79 }
80 }
81 None => {
82 hits += 1;
83 return result()
84 }
85 }
86 }
87 None
88 };
89
90 if filter.reverse {
91 Ok(cursor
92 .walk_back(None)?
93 .skip(filter.skip)
94 .filter_map(map_filter)
95 .take(filter.len)
96 .collect::<Vec<(_, _)>>())
97 } else {
98 Ok(cursor
99 .walk(None)?
100 .skip(filter.skip)
101 .filter_map(map_filter)
102 .take(filter.len)
103 .collect::<Vec<(_, _)>>())
104 }
105 })?;
106
107 Ok((data.map_err(|e: DatabaseError| eyre::eyre!(e))?, hits))
108 }
109}
110
111impl<N: ProviderNodeTypes> DbTool<N> {
112 pub fn new(provider_factory: ProviderFactory<N>) -> eyre::Result<Self> {
114 provider_factory.provider()?.disable_long_read_transaction_safety();
117 Ok(Self { provider_factory })
118 }
119
120 pub fn get<T: Table>(&self, key: T::Key) -> Result<Option<T::Value>> {
122 self.provider_factory.db_ref().view(|tx| tx.get::<T>(key))?.map_err(|e| eyre::eyre!(e))
123 }
124
125 pub fn get_dup<T: DupSort>(&self, key: T::Key, subkey: T::SubKey) -> Result<Option<T::Value>> {
127 self.provider_factory
128 .db_ref()
129 .view(|tx| tx.cursor_dup_read::<T>()?.seek_by_key_subkey(key, subkey))?
130 .map_err(|e| eyre::eyre!(e))
131 }
132
133 pub fn drop<P: AsRef<Path>>(
135 &self,
136 db_path: P,
137 static_files_path: P,
138 exex_wal_path: P,
139 ) -> Result<()> {
140 let db_path = db_path.as_ref();
141 info!(target: "reth::cli", "Dropping database at {:?}", db_path);
142 fs::remove_dir_all(db_path)?;
143
144 let static_files_path = static_files_path.as_ref();
145 info!(target: "reth::cli", "Dropping static files at {:?}", static_files_path);
146 fs::remove_dir_all(static_files_path)?;
147 fs::create_dir_all(static_files_path)?;
148
149 if exex_wal_path.as_ref().exists() {
150 let exex_wal_path = exex_wal_path.as_ref();
151 info!(target: "reth::cli", "Dropping ExEx WAL at {:?}", exex_wal_path);
152 fs::remove_dir_all(exex_wal_path)?;
153 }
154
155 Ok(())
156 }
157
158 pub fn drop_table<T: Table>(&self) -> Result<()> {
160 self.provider_factory.db_ref().update(|tx| tx.clear::<T>())??;
161 Ok(())
162 }
163}
164
165#[derive(Debug)]
167pub struct ListFilter {
168 pub skip: usize,
170 pub len: usize,
172 pub search: Vec<u8>,
174 pub min_row_size: usize,
176 pub min_key_size: usize,
178 pub min_value_size: usize,
180 pub reverse: bool,
182 pub only_count: bool,
184}
185
186impl ListFilter {
187 pub fn has_search(&self) -> bool {
189 !self.search.is_empty()
190 }
191
192 pub const fn update_page(&mut self, skip: usize, len: usize) {
194 self.skip = skip;
195 self.len = len;
196 }
197}