@@ -8,12 +8,17 @@ use reth_db::{
88 RawDupSort ,
99} ;
1010use reth_db_api:: {
11- table:: { Decompress , DupSort , Table } ,
12- tables, RawKey , RawTable , Receipts , TableViewer , Transactions ,
11+ cursor:: { DbCursorRO , DbDupCursorRO } ,
12+ database:: Database ,
13+ table:: { Compress , Decompress , DupSort , Table } ,
14+ tables,
15+ transaction:: DbTx ,
16+ RawKey , RawTable , Receipts , TableViewer , Transactions ,
1317} ;
1418use reth_db_common:: DbTool ;
1519use reth_node_api:: { HeaderTy , ReceiptTy , TxTy } ;
1620use reth_node_builder:: NodeTypesWithDB ;
21+ use reth_primitives_traits:: ValueWithSubKey ;
1722use reth_provider:: { providers:: ProviderNodeTypes , StaticFileProviderFactory } ;
1823use reth_static_file_types:: StaticFileSegment ;
1924use tracing:: error;
@@ -39,6 +44,14 @@ enum Subcommand {
3944 #[ arg( value_parser = maybe_json_value_parser) ]
4045 subkey : Option < String > ,
4146
47+ /// Optional end key for range query (exclusive upper bound)
48+ #[ arg( value_parser = maybe_json_value_parser) ]
49+ end_key : Option < String > ,
50+
51+ /// Optional end subkey for range query (exclusive upper bound)
52+ #[ arg( value_parser = maybe_json_value_parser) ]
53+ end_subkey : Option < String > ,
54+
4255 /// Output bytes instead of human-readable decoded value
4356 #[ arg( long) ]
4457 raw : bool ,
@@ -61,8 +74,8 @@ impl Command {
6174 /// Execute `db get` command
6275 pub fn execute < N : ProviderNodeTypes > ( self , tool : & DbTool < N > ) -> eyre:: Result < ( ) > {
6376 match self . subcommand {
64- Subcommand :: Mdbx { table, key, subkey, raw } => {
65- table. view ( & GetValueViewer { tool, key, subkey, raw } ) ?
77+ Subcommand :: Mdbx { table, key, subkey, end_key , end_subkey , raw } => {
78+ table. view ( & GetValueViewer { tool, key, subkey, end_key , end_subkey , raw } ) ?
6679 }
6780 Subcommand :: StaticFile { segment, key, raw } => {
6881 let ( key, mask) : ( u64 , _ ) = match segment {
@@ -154,6 +167,8 @@ struct GetValueViewer<'a, N: NodeTypesWithDB> {
154167 tool : & ' a DbTool < N > ,
155168 key : String ,
156169 subkey : Option < String > ,
170+ end_key : Option < String > ,
171+ end_subkey : Option < String > ,
157172 raw : bool ,
158173}
159174
@@ -163,53 +178,158 @@ impl<N: ProviderNodeTypes> TableViewer<()> for GetValueViewer<'_, N> {
163178 fn view < T : Table > ( & self ) -> Result < ( ) , Self :: Error > {
164179 let key = table_key :: < T > ( & self . key ) ?;
165180
166- let content = if self . raw {
167- self . tool
168- . get :: < RawTable < T > > ( RawKey :: from ( key) ) ?
169- . map ( |content| hex:: encode_prefixed ( content. raw_value ( ) ) )
181+ // A non-dupsort table cannot have subkeys. The `subkey` arg becomes the `end_key`. First we
182+ // check that `end_key` and `end_subkey` weren't previously given, as that wouldn't be
183+ // valid.
184+ if self . end_key . is_some ( ) || self . end_subkey . is_some ( ) {
185+ return Err ( eyre:: eyre!( "Only END_KEY can be given for non-DUPSORT tables" ) ) ;
186+ }
187+
188+ let end_key = self . subkey . clone ( ) ;
189+
190+ // Check if we're doing a range query
191+ if let Some ( ref end_key_str) = end_key {
192+ let end_key = table_key :: < T > ( end_key_str) ?;
193+
194+ // Use walk_range to iterate over the range
195+ self . tool . provider_factory . db_ref ( ) . view ( |tx| {
196+ let mut cursor = tx. cursor_read :: < T > ( ) ?;
197+ let walker = cursor. walk_range ( key..end_key) ?;
198+
199+ for result in walker {
200+ let ( k, v) = result?;
201+ let json_val = if self . raw {
202+ let raw_key = RawKey :: from ( k) ;
203+ serde_json:: json!( {
204+ "key" : hex:: encode_prefixed( raw_key. raw_key( ) ) ,
205+ "val" : hex:: encode_prefixed( v. compress( ) . as_ref( ) ) ,
206+ } )
207+ } else {
208+ serde_json:: json!( {
209+ "key" : & k,
210+ "val" : & v,
211+ } )
212+ } ;
213+
214+ println ! ( "{}" , serde_json:: to_string_pretty( & json_val) ?) ;
215+ }
216+
217+ Ok :: < _ , eyre:: Report > ( ( ) )
218+ } ) ??;
170219 } else {
171- self . tool . get :: < T > ( key) ?. as_ref ( ) . map ( serde_json:: to_string_pretty) . transpose ( ) ?
172- } ;
220+ // Single key lookup
221+ let content = if self . raw {
222+ self . tool
223+ . get :: < RawTable < T > > ( RawKey :: from ( key) ) ?
224+ . map ( |content| hex:: encode_prefixed ( content. raw_value ( ) ) )
225+ } else {
226+ self . tool . get :: < T > ( key) ?. as_ref ( ) . map ( serde_json:: to_string_pretty) . transpose ( ) ?
227+ } ;
173228
174- match content {
175- Some ( content) => {
176- println ! ( "{content}" ) ;
177- }
178- None => {
179- error ! ( target: "reth::cli" , "No content for the given table key." ) ;
180- }
181- } ;
229+ match content {
230+ Some ( content) => {
231+ println ! ( "{content}" ) ;
232+ }
233+ None => {
234+ error ! ( target: "reth::cli" , "No content for the given table key." ) ;
235+ }
236+ } ;
237+ }
182238
183239 Ok ( ( ) )
184240 }
185241
186- fn view_dupsort < T : DupSort > ( & self ) -> Result < ( ) , Self :: Error > {
242+ fn view_dupsort < T : DupSort > ( & self ) -> Result < ( ) , Self :: Error >
243+ where
244+ T :: Value : reth_primitives_traits:: ValueWithSubKey < SubKey = T :: SubKey > ,
245+ {
187246 // get a key for given table
188247 let key = table_key :: < T > ( & self . key ) ?;
189248
190- // process dupsort table
191- let subkey = table_subkey :: < T > ( self . subkey . as_deref ( ) ) ?;
249+ // Check if we're doing a range query
250+ if let Some ( ref end_key_str) = self . end_key {
251+ let end_key = table_key :: < T > ( end_key_str) ?;
252+ let start_subkey = table_subkey :: < T > ( Some (
253+ self . subkey . as_ref ( ) . expect ( "must have been given if end_key is given" ) . as_str ( ) ,
254+ ) ) ?;
255+ let end_subkey_parsed = self
256+ . end_subkey
257+ . as_ref ( )
258+ . map ( |s| table_subkey :: < T > ( Some ( s. as_str ( ) ) ) )
259+ . transpose ( ) ?;
192260
193- let content = if self . raw {
194- self . tool
195- . get_dup :: < RawDupSort < T > > ( RawKey :: from ( key) , RawKey :: from ( subkey) ) ?
196- . map ( |content| hex:: encode_prefixed ( content. raw_value ( ) ) )
261+ self . tool . provider_factory . db_ref ( ) . view ( |tx| {
262+ let mut cursor = tx. cursor_dup_read :: < T > ( ) ?;
263+
264+ // Seek to the starting key. If there is actually a key at the starting key then
265+ // seek to the subkey within it.
266+ if let Some ( ( decoded_key, _) ) = cursor. seek ( key. clone ( ) ) ? &&
267+ decoded_key == key
268+ {
269+ cursor. seek_by_key_subkey ( key. clone ( ) , start_subkey. clone ( ) ) ?;
270+ }
271+
272+ // Get the current position to start iteration
273+ let mut current = cursor. current ( ) ?;
274+
275+ while let Some ( ( decoded_key, decoded_value) ) = current {
276+ // Extract the subkey using the ValueWithSubKey trait
277+ let decoded_subkey = decoded_value. get_subkey ( ) ;
278+
279+ // Check if we've reached the end (exclusive)
280+ if ( & decoded_key, Some ( & decoded_subkey) ) >=
281+ ( & end_key, end_subkey_parsed. as_ref ( ) )
282+ {
283+ break ;
284+ }
285+
286+ // Output the entry with both key and subkey
287+ let json_val = if self . raw {
288+ let raw_key = RawKey :: from ( decoded_key. clone ( ) ) ;
289+ serde_json:: json!( {
290+ "key" : hex:: encode_prefixed( raw_key. raw_key( ) ) ,
291+ "val" : hex:: encode_prefixed( decoded_value. compress( ) . as_ref( ) ) ,
292+ } )
293+ } else {
294+ serde_json:: json!( {
295+ "key" : & decoded_key,
296+ "val" : & decoded_value,
297+ } )
298+ } ;
299+
300+ println ! ( "{}" , serde_json:: to_string_pretty( & json_val) ?) ;
301+
302+ // Move to next entry
303+ current = cursor. next ( ) ?;
304+ }
305+
306+ Ok :: < _ , eyre:: Report > ( ( ) )
307+ } ) ??;
197308 } else {
198- self . tool
199- . get_dup :: < T > ( key, subkey) ?
200- . as_ref ( )
201- . map ( serde_json:: to_string_pretty)
202- . transpose ( ) ?
203- } ;
309+ // Single key/subkey lookup
310+ let subkey = table_subkey :: < T > ( self . subkey . as_deref ( ) ) ?;
204311
205- match content {
206- Some ( content) => {
207- println ! ( "{content}" ) ;
208- }
209- None => {
210- error ! ( target: "reth::cli" , "No content for the given table subkey." ) ;
211- }
212- } ;
312+ let content = if self . raw {
313+ self . tool
314+ . get_dup :: < RawDupSort < T > > ( RawKey :: from ( key) , RawKey :: from ( subkey) ) ?
315+ . map ( |content| hex:: encode_prefixed ( content. raw_value ( ) ) )
316+ } else {
317+ self . tool
318+ . get_dup :: < T > ( key, subkey) ?
319+ . as_ref ( )
320+ . map ( serde_json:: to_string_pretty)
321+ . transpose ( ) ?
322+ } ;
323+
324+ match content {
325+ Some ( content) => {
326+ println ! ( "{content}" ) ;
327+ }
328+ None => {
329+ error ! ( target: "reth::cli" , "No content for the given table subkey." ) ;
330+ }
331+ } ;
332+ }
213333 Ok ( ( ) )
214334 }
215335}
0 commit comments