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