reth_cli_commands/db/
mod.rs
1use crate::common::{AccessRights, CliNodeTypes, Environment, EnvironmentArgs};
2use clap::{Parser, Subcommand};
3use reth_chainspec::{EthChainSpec, EthereumHardforks};
4use reth_cli::chainspec::ChainSpecParser;
5use reth_db::version::{get_db_version, DatabaseVersionError, DB_VERSION};
6use reth_db_common::DbTool;
7use std::io::{self, Write};
8
9mod checksum;
10mod clear;
11mod diff;
12mod get;
13mod list;
14mod stats;
15mod tui;
17
18#[derive(Debug, Parser)]
20pub struct Command<C: ChainSpecParser> {
21 #[command(flatten)]
22 env: EnvironmentArgs<C>,
23
24 #[command(subcommand)]
25 command: Subcommands,
26}
27
28#[derive(Subcommand, Debug)]
29pub enum Subcommands {
31 Stats(stats::Command),
33 List(list::Command),
35 Checksum(checksum::Command),
37 Diff(diff::Command),
39 Get(get::Command),
41 Drop {
43 #[arg(short, long)]
45 force: bool,
46 },
47 Clear(clear::Command),
49 Version,
51 Path,
53}
54
55macro_rules! db_ro_exec {
57 ($env:expr, $tool:ident, $N:ident, $command:block) => {
58 let Environment { provider_factory, .. } = $env.init::<$N>(AccessRights::RO)?;
59
60 let $tool = DbTool::new(provider_factory.clone())?;
61 $command;
62 };
63}
64
65impl<C: ChainSpecParser<ChainSpec: EthChainSpec + EthereumHardforks>> Command<C> {
66 pub async fn execute<N: CliNodeTypes<ChainSpec = C::ChainSpec>>(self) -> eyre::Result<()> {
68 let data_dir = self.env.datadir.clone().resolve_datadir(self.env.chain.chain());
69 let db_path = data_dir.db();
70 let static_files_path = data_dir.static_files();
71 let exex_wal_path = data_dir.exex_wal();
72
73 eyre::ensure!(
75 data_dir.data_dir().is_dir(),
76 "Datadir does not exist: {:?}",
77 data_dir.data_dir()
78 );
79
80 eyre::ensure!(db_path.is_dir(), "Database does not exist: {:?}", db_path);
82
83 match self.command {
84 Subcommands::Stats(command) => {
86 db_ro_exec!(self.env, tool, N, {
87 command.execute(data_dir, &tool)?;
88 });
89 }
90 Subcommands::List(command) => {
91 db_ro_exec!(self.env, tool, N, {
92 command.execute(&tool)?;
93 });
94 }
95 Subcommands::Checksum(command) => {
96 db_ro_exec!(self.env, tool, N, {
97 command.execute(&tool)?;
98 });
99 }
100 Subcommands::Diff(command) => {
101 db_ro_exec!(self.env, tool, N, {
102 command.execute(&tool)?;
103 });
104 }
105 Subcommands::Get(command) => {
106 db_ro_exec!(self.env, tool, N, {
107 command.execute(&tool)?;
108 });
109 }
110 Subcommands::Drop { force } => {
111 if !force {
112 print!("Are you sure you want to drop the database at {data_dir}? This cannot be undone. (y/N): ");
114 io::stdout().flush().unwrap();
116
117 let mut input = String::new();
118 io::stdin().read_line(&mut input).expect("Failed to read line");
119
120 if !input.trim().eq_ignore_ascii_case("y") {
121 println!("Database drop aborted!");
122 return Ok(())
123 }
124 }
125
126 let Environment { provider_factory, .. } = self.env.init::<N>(AccessRights::RW)?;
127 let tool = DbTool::new(provider_factory)?;
128 tool.drop(db_path, static_files_path, exex_wal_path)?;
129 }
130 Subcommands::Clear(command) => {
131 let Environment { provider_factory, .. } = self.env.init::<N>(AccessRights::RW)?;
132 command.execute(provider_factory)?;
133 }
134 Subcommands::Version => {
135 let local_db_version = match get_db_version(&db_path) {
136 Ok(version) => Some(version),
137 Err(DatabaseVersionError::MissingFile) => None,
138 Err(err) => return Err(err.into()),
139 };
140
141 println!("Current database version: {DB_VERSION}");
142
143 if let Some(version) = local_db_version {
144 println!("Local database version: {version}");
145 } else {
146 println!("Local database is uninitialized");
147 }
148 }
149 Subcommands::Path => {
150 println!("{}", db_path.display());
151 }
152 }
153
154 Ok(())
155 }
156}
157
158#[cfg(test)]
159mod tests {
160 use super::*;
161 use reth_ethereum_cli::chainspec::{EthereumChainSpecParser, SUPPORTED_CHAINS};
162 use std::path::Path;
163
164 #[test]
165 fn parse_stats_globals() {
166 let path = format!("../{}", SUPPORTED_CHAINS[0]);
167 let cmd = Command::<EthereumChainSpecParser>::try_parse_from([
168 "reth",
169 "--datadir",
170 &path,
171 "stats",
172 ])
173 .unwrap();
174 assert_eq!(cmd.env.datadir.resolve_datadir(cmd.env.chain.chain).as_ref(), Path::new(&path));
175 }
176}