reth_cli_commands/db/
list.rs1use super::tui::DbListTUI;
2use alloy_primitives::hex;
3use clap::Parser;
4use eyre::WrapErr;
5use reth_chainspec::EthereumHardforks;
6use reth_db::{transaction::DbTx, DatabaseEnv};
7use reth_db_api::{database::Database, table::Table, RawValue, TableViewer, Tables};
8use reth_db_common::{DbTool, ListFilter};
9use reth_node_builder::{NodeTypes, NodeTypesWithDBAdapter};
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: NodeTypes<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: NodeTypes> {
90 tool: &'a DbTool<NodeTypesWithDBAdapter<N, Arc<DatabaseEnv>>>,
91 args: &'a Command,
92}
93
94impl<N: NodeTypes> 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 tx.disable_long_read_transaction_safety();
101
102 let table_db = tx.inner.open_db(Some(self.args.table.name())).wrap_err("Could not open db.")?;
103 let stats = tx.inner.db_stat(&table_db).wrap_err(format!("Could not find table: {}", self.args.table.name()))?;
104 let total_entries = stats.entries();
105 let final_entry_idx = total_entries.saturating_sub(1);
106 if self.args.skip > final_entry_idx {
107 error!(
108 target: "reth::cli",
109 "Start index {start} is greater than the final entry index ({final_entry_idx}) in the table {table}",
110 start = self.args.skip,
111 final_entry_idx = final_entry_idx,
112 table = self.args.table.name()
113 );
114 return Ok(())
115 }
116
117
118 let list_filter = self.args.list_filter();
119
120 if self.args.json || self.args.count {
121 let (list, count) = self.tool.list::<T>(&list_filter)?;
122
123 if self.args.count {
124 println!("{count} entries found.")
125 } else if self.args.raw {
126 let list = list.into_iter().map(|row| (row.0, RawValue::new(row.1).into_value())).collect::<Vec<_>>();
127 println!("{}", serde_json::to_string_pretty(&list)?);
128 } else {
129 println!("{}", serde_json::to_string_pretty(&list)?);
130 }
131 Ok(())
132 } else {
133 let list_filter = RefCell::new(list_filter);
134 DbListTUI::<_, T>::new(|skip, len| {
135 list_filter.borrow_mut().update_page(skip, len);
136 self.tool.list::<T>(&list_filter.borrow()).unwrap().0
137 }, self.args.skip, self.args.len, total_entries, self.args.raw).run()
138 }
139 })??;
140
141 Ok(())
142 }
143}