mirror of
https://github.com/paradigmxyz/reth.git
synced 2026-02-06 21:15:27 -05:00
168 lines
5.4 KiB
Rust
168 lines
5.4 KiB
Rust
use crate::common::{AccessRights, Environment, EnvironmentArgs};
|
|
use clap::{Parser, Subcommand};
|
|
use reth_db::version::{get_db_version, DatabaseVersionError, DB_VERSION};
|
|
use reth_db_common::DbTool;
|
|
use std::io::{self, Write};
|
|
|
|
mod checksum;
|
|
mod clear;
|
|
mod diff;
|
|
mod get;
|
|
mod list;
|
|
mod stats;
|
|
/// DB List TUI
|
|
mod tui;
|
|
|
|
/// `reth db` command
|
|
#[derive(Debug, Parser)]
|
|
pub struct Command {
|
|
#[command(flatten)]
|
|
env: EnvironmentArgs,
|
|
|
|
#[command(subcommand)]
|
|
command: Subcommands,
|
|
}
|
|
|
|
#[derive(Subcommand, Debug)]
|
|
/// `reth db` subcommands
|
|
pub enum Subcommands {
|
|
/// Lists all the tables, their entry count and their size
|
|
Stats(stats::Command),
|
|
/// Lists the contents of a table
|
|
List(list::Command),
|
|
/// Calculates the content checksum of a table
|
|
Checksum(checksum::Command),
|
|
/// Create a diff between two database tables or two entire databases.
|
|
Diff(diff::Command),
|
|
/// Gets the content of a table for the given key
|
|
Get(get::Command),
|
|
/// Deletes all database entries
|
|
Drop {
|
|
/// Bypasses the interactive confirmation and drops the database directly
|
|
#[arg(short, long)]
|
|
force: bool,
|
|
},
|
|
/// Deletes all table entries
|
|
Clear(clear::Command),
|
|
/// Lists current and local database versions
|
|
Version,
|
|
/// Returns the full database path
|
|
Path,
|
|
}
|
|
|
|
/// `db_ro_exec` opens a database in read-only mode, and then execute with the provided command
|
|
macro_rules! db_ro_exec {
|
|
($env:expr, $tool:ident, $command:block) => {
|
|
let Environment { provider_factory, .. } = $env.init(AccessRights::RO)?;
|
|
|
|
let $tool = DbTool::new(provider_factory.clone())?;
|
|
$command;
|
|
};
|
|
}
|
|
|
|
impl Command {
|
|
/// Execute `db` command
|
|
pub async fn execute(self) -> eyre::Result<()> {
|
|
let data_dir = self.env.datadir.clone().resolve_datadir(self.env.chain.chain);
|
|
let db_path = data_dir.db();
|
|
let static_files_path = data_dir.static_files();
|
|
|
|
// ensure the provided datadir exist
|
|
eyre::ensure!(
|
|
data_dir.data_dir().is_dir(),
|
|
"Datadir does not exist: {:?}",
|
|
data_dir.data_dir()
|
|
);
|
|
|
|
// ensure the provided database exist
|
|
eyre::ensure!(db_path.is_dir(), "Database does not exist: {:?}", db_path);
|
|
|
|
match self.command {
|
|
// TODO: We'll need to add this on the DB trait.
|
|
Subcommands::Stats(command) => {
|
|
db_ro_exec!(self.env, tool, {
|
|
command.execute(data_dir, &tool)?;
|
|
});
|
|
}
|
|
Subcommands::List(command) => {
|
|
db_ro_exec!(self.env, tool, {
|
|
command.execute(&tool)?;
|
|
});
|
|
}
|
|
Subcommands::Checksum(command) => {
|
|
db_ro_exec!(self.env, tool, {
|
|
command.execute(&tool)?;
|
|
});
|
|
}
|
|
Subcommands::Diff(command) => {
|
|
db_ro_exec!(self.env, tool, {
|
|
command.execute(&tool)?;
|
|
});
|
|
}
|
|
Subcommands::Get(command) => {
|
|
db_ro_exec!(self.env, tool, {
|
|
command.execute(&tool)?;
|
|
});
|
|
}
|
|
Subcommands::Drop { force } => {
|
|
if !force {
|
|
// Ask for confirmation
|
|
print!("Are you sure you want to drop the database at {data_dir}? This cannot be undone. (y/N): ");
|
|
// Flush the buffer to ensure the message is printed immediately
|
|
io::stdout().flush().unwrap();
|
|
|
|
let mut input = String::new();
|
|
io::stdin().read_line(&mut input).expect("Failed to read line");
|
|
|
|
if !input.trim().eq_ignore_ascii_case("y") {
|
|
println!("Database drop aborted!");
|
|
return Ok(())
|
|
}
|
|
}
|
|
|
|
let Environment { provider_factory, .. } = self.env.init(AccessRights::RW)?;
|
|
let tool = DbTool::new(provider_factory)?;
|
|
tool.drop(db_path, static_files_path)?;
|
|
}
|
|
Subcommands::Clear(command) => {
|
|
let Environment { provider_factory, .. } = self.env.init(AccessRights::RW)?;
|
|
command.execute(provider_factory)?;
|
|
}
|
|
Subcommands::Version => {
|
|
let local_db_version = match get_db_version(&db_path) {
|
|
Ok(version) => Some(version),
|
|
Err(DatabaseVersionError::MissingFile) => None,
|
|
Err(err) => return Err(err.into()),
|
|
};
|
|
|
|
println!("Current database version: {DB_VERSION}");
|
|
|
|
if let Some(version) = local_db_version {
|
|
println!("Local database version: {version}");
|
|
} else {
|
|
println!("Local database is uninitialized");
|
|
}
|
|
}
|
|
Subcommands::Path => {
|
|
println!("{}", db_path.display());
|
|
}
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
use reth_node_core::args::utils::SUPPORTED_CHAINS;
|
|
use std::path::Path;
|
|
|
|
#[test]
|
|
fn parse_stats_globals() {
|
|
let path = format!("../{}", SUPPORTED_CHAINS[0]);
|
|
let cmd = Command::try_parse_from(["reth", "--datadir", &path, "stats"]).unwrap();
|
|
assert_eq!(cmd.env.datadir.resolve_datadir(cmd.env.chain.chain).as_ref(), Path::new(&path));
|
|
}
|
|
}
|