reth_cli_commands/db/
list.rsuse super::tui::DbListTUI;
use alloy_primitives::hex;
use clap::Parser;
use eyre::WrapErr;
use reth_chainspec::EthereumHardforks;
use reth_db::{DatabaseEnv, RawValue, TableViewer, Tables};
use reth_db_api::{database::Database, table::Table};
use reth_db_common::{DbTool, ListFilter};
use reth_node_builder::{NodeTypesWithDBAdapter, NodeTypesWithEngine};
use std::{cell::RefCell, sync::Arc};
use tracing::error;
#[derive(Parser, Debug)]
pub struct Command {
table: Tables,
#[arg(long, short, default_value_t = 0)]
skip: usize,
#[arg(long, short, default_value_t = false)]
reverse: bool,
#[arg(long, short, default_value_t = 5)]
len: usize,
#[arg(long)]
search: Option<String>,
#[arg(long, default_value_t = 0)]
min_row_size: usize,
#[arg(long, default_value_t = 0)]
min_key_size: usize,
#[arg(long, default_value_t = 0)]
min_value_size: usize,
#[arg(long, short)]
count: bool,
#[arg(long, short)]
json: bool,
#[arg(long)]
raw: bool,
}
impl Command {
pub fn execute<N: NodeTypesWithEngine<ChainSpec: EthereumHardforks>>(
self,
tool: &DbTool<NodeTypesWithDBAdapter<N, Arc<DatabaseEnv>>>,
) -> eyre::Result<()> {
self.table.view(&ListTableViewer { tool, args: &self })
}
pub fn list_filter(&self) -> ListFilter {
let search = self
.search
.as_ref()
.map(|search| {
if let Some(search) = search.strip_prefix("0x") {
return hex::decode(search).unwrap()
}
search.as_bytes().to_vec()
})
.unwrap_or_default();
ListFilter {
skip: self.skip,
len: self.len,
search,
min_row_size: self.min_row_size,
min_key_size: self.min_key_size,
min_value_size: self.min_value_size,
reverse: self.reverse,
only_count: self.count,
}
}
}
struct ListTableViewer<'a, N: NodeTypesWithEngine> {
tool: &'a DbTool<NodeTypesWithDBAdapter<N, Arc<DatabaseEnv>>>,
args: &'a Command,
}
impl<N: NodeTypesWithEngine> TableViewer<()> for ListTableViewer<'_, N> {
type Error = eyre::Report;
fn view<T: Table>(&self) -> Result<(), Self::Error> {
self.tool.provider_factory.db_ref().view(|tx| {
let table_db = tx.inner.open_db(Some(self.args.table.name())).wrap_err("Could not open db.")?;
let stats = tx.inner.db_stat(&table_db).wrap_err(format!("Could not find table: {}", stringify!($table)))?;
let total_entries = stats.entries();
let final_entry_idx = total_entries.saturating_sub(1);
if self.args.skip > final_entry_idx {
error!(
target: "reth::cli",
"Start index {start} is greater than the final entry index ({final_entry_idx}) in the table {table}",
start = self.args.skip,
final_entry_idx = final_entry_idx,
table = self.args.table.name()
);
return Ok(())
}
let list_filter = self.args.list_filter();
if self.args.json || self.args.count {
let (list, count) = self.tool.list::<T>(&list_filter)?;
if self.args.count {
println!("{count} entries found.")
} else if self.args.raw {
let list = list.into_iter().map(|row| (row.0, RawValue::new(row.1).into_value())).collect::<Vec<_>>();
println!("{}", serde_json::to_string_pretty(&list)?);
} else {
println!("{}", serde_json::to_string_pretty(&list)?);
}
Ok(())
} else {
let list_filter = RefCell::new(list_filter);
DbListTUI::<_, T>::new(|skip, len| {
list_filter.borrow_mut().update_page(skip, len);
self.tool.list::<T>(&list_filter.borrow()).unwrap().0
}, self.args.skip, self.args.len, total_entries, self.args.raw).run()
}
})??;
Ok(())
}
}