reth_cli_commands/db/
list.rs
1use super::tui::DbListTUI;
2use alloy_primitives::hex;
3use clap::Parser;
4use eyre::WrapErr;
5use reth_chainspec::EthereumHardforks;
6use reth_db::DatabaseEnv;
7use reth_db_api::{database::Database, table::Table, RawValue, TableViewer, Tables};
8use reth_db_common::{DbTool, ListFilter};
9use reth_node_builder::{NodeTypesWithDBAdapter, NodeTypesWithEngine};
10use std::{cell::RefCell, sync::Arc};
11use tracing::error;
12
13#[derive(Parser, Debug)]
14pub struct Command {
16 table: Tables,
18 #[arg(long, short, default_value_t = 0)]
20 skip: usize,
21 #[arg(long, short, default_value_t = false)]
23 reverse: bool,
24 #[arg(long, short, default_value_t = 5)]
26 len: usize,
27 #[arg(long)]
33 search: Option<String>,
34 #[arg(long, default_value_t = 0)]
36 min_row_size: usize,
37 #[arg(long, default_value_t = 0)]
39 min_key_size: usize,
40 #[arg(long, default_value_t = 0)]
42 min_value_size: usize,
43 #[arg(long, short)]
45 count: bool,
46 #[arg(long, short)]
48 json: bool,
49 #[arg(long)]
51 raw: bool,
52}
53
54impl Command {
55 pub fn execute<N: NodeTypesWithEngine<ChainSpec: EthereumHardforks>>(
57 self,
58 tool: &DbTool<NodeTypesWithDBAdapter<N, Arc<DatabaseEnv>>>,
59 ) -> eyre::Result<()> {
60 self.table.view(&ListTableViewer { tool, args: &self })
61 }
62
63 pub fn list_filter(&self) -> ListFilter {
65 let search = self
66 .search
67 .as_ref()
68 .map(|search| {
69 if let Some(search) = search.strip_prefix("0x") {
70 return hex::decode(search).unwrap()
71 }
72 search.as_bytes().to_vec()
73 })
74 .unwrap_or_default();
75
76 ListFilter {
77 skip: self.skip,
78 len: self.len,
79 search,
80 min_row_size: self.min_row_size,
81 min_key_size: self.min_key_size,
82 min_value_size: self.min_value_size,
83 reverse: self.reverse,
84 only_count: self.count,
85 }
86 }
87}
88
89struct ListTableViewer<'a, N: NodeTypesWithEngine> {
90 tool: &'a DbTool<NodeTypesWithDBAdapter<N, Arc<DatabaseEnv>>>,
91 args: &'a Command,
92}
93
94impl<N: NodeTypesWithEngine> TableViewer<()> for ListTableViewer<'_, N> {
95 type Error = eyre::Report;
96
97 fn view<T: Table>(&self) -> Result<(), Self::Error> {
98 self.tool.provider_factory.db_ref().view(|tx| {
99 let table_db = tx.inner.open_db(Some(self.args.table.name())).wrap_err("Could not open db.")?;
100 let stats = tx.inner.db_stat(&table_db).wrap_err(format!("Could not find table: {}", stringify!($table)))?;
101 let total_entries = stats.entries();
102 let final_entry_idx = total_entries.saturating_sub(1);
103 if self.args.skip > final_entry_idx {
104 error!(
105 target: "reth::cli",
106 "Start index {start} is greater than the final entry index ({final_entry_idx}) in the table {table}",
107 start = self.args.skip,
108 final_entry_idx = final_entry_idx,
109 table = self.args.table.name()
110 );
111 return Ok(())
112 }
113
114
115 let list_filter = self.args.list_filter();
116
117 if self.args.json || self.args.count {
118 let (list, count) = self.tool.list::<T>(&list_filter)?;
119
120 if self.args.count {
121 println!("{count} entries found.")
122 } else if self.args.raw {
123 let list = list.into_iter().map(|row| (row.0, RawValue::new(row.1).into_value())).collect::<Vec<_>>();
124 println!("{}", serde_json::to_string_pretty(&list)?);
125 } else {
126 println!("{}", serde_json::to_string_pretty(&list)?);
127 }
128 Ok(())
129 } else {
130 let list_filter = RefCell::new(list_filter);
131 DbListTUI::<_, T>::new(|skip, len| {
132 list_filter.borrow_mut().update_page(skip, len);
133 self.tool.list::<T>(&list_filter.borrow()).unwrap().0
134 }, self.args.skip, self.args.len, total_entries, self.args.raw).run()
135 }
136 })??;
137
138 Ok(())
139 }
140}