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;
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, DatabaseEnv>>,
59 ) -> eyre::Result<()> {
60 self.table.view(&ListTableViewer { tool, args: &self })
61 }
62
63 pub fn list_filter(&self) -> eyre::Result<ListFilter> {
65 let search = match self.search.as_deref() {
66 Some(search) => {
67 if let Some(search) = search.strip_prefix("0x") {
68 hex::decode(search).wrap_err(
69 "Invalid hex content after 0x prefix in --search (expected valid hex like 0xdeadbeef).",
70 )?
71 } else {
72 search.as_bytes().to_vec()
73 }
74 }
75 None => Vec::new(),
76 };
77
78 Ok(ListFilter {
79 skip: self.skip,
80 len: self.len,
81 search,
82 min_row_size: self.min_row_size,
83 min_key_size: self.min_key_size,
84 min_value_size: self.min_value_size,
85 reverse: self.reverse,
86 only_count: self.count,
87 })
88 }
89}
90
91struct ListTableViewer<'a, N: NodeTypes> {
92 tool: &'a DbTool<NodeTypesWithDBAdapter<N, DatabaseEnv>>,
93 args: &'a Command,
94}
95
96impl<N: NodeTypes> TableViewer<()> for ListTableViewer<'_, N> {
97 type Error = eyre::Report;
98
99 fn view<T: Table>(&self) -> Result<(), Self::Error> {
100 self.tool.provider_factory.db_ref().view(|tx| {
101 tx.disable_long_read_transaction_safety();
103
104 let table_db = tx.inner().open_db(Some(self.args.table.name())).wrap_err("Could not open db.")?;
105 let stats = tx.inner().db_stat(table_db.dbi()).wrap_err(format!("Could not find table: {}", self.args.table.name()))?;
106 let total_entries = stats.entries();
107 let final_entry_idx = total_entries.saturating_sub(1);
108 if self.args.skip > final_entry_idx {
109 error!(
110 target: "reth::cli",
111 "Start index {start} is greater than the final entry index ({final_entry_idx}) in the table {table}",
112 start = self.args.skip,
113 final_entry_idx = final_entry_idx,
114 table = self.args.table.name()
115 );
116 return Ok(())
117 }
118
119
120 let list_filter = self.args.list_filter()?;
121
122 if self.args.json || self.args.count {
123 let (list, count) = self.tool.list::<T>(&list_filter)?;
124
125 if self.args.count {
126 println!("{count} entries found.")
127 } else if self.args.raw {
128 let list = list.into_iter().map(|row| (row.0, RawValue::new(row.1).into_value())).collect::<Vec<_>>();
129 println!("{}", serde_json::to_string_pretty(&list)?);
130 } else {
131 println!("{}", serde_json::to_string_pretty(&list)?);
132 }
133 Ok(())
134 } else {
135 let list_filter = RefCell::new(list_filter);
136 DbListTUI::<_, T>::new(|skip, len| {
137 list_filter.borrow_mut().update_page(skip, len);
138 self.tool.list::<T>(&list_filter.borrow()).unwrap().0
139 }, self.args.skip, self.args.len, total_entries, self.args.raw).run()
140 }
141 })??;
142
143 Ok(())
144 }
145}