@@ -17,6 +17,24 @@ use crate::{
1717 versionstamp:: { generate_versionstamp, substitute_raw_versionstamp} ,
1818} ;
1919
20+ /// Copy bytes borrowed from a rocksdb iterator into an owned `Vec`.
21+ ///
22+ /// RocksDB hands back a null data pointer for zero-length keys and values.
23+ /// Copying from a null pointer (which `<[u8]>::to_vec` does internally via
24+ /// `ptr::copy_nonoverlapping`) violates its non-null precondition and aborts the
25+ /// process on builds compiled with UB checks (debug assertions) enabled, even
26+ /// though the copy length is zero. This is also why the boxing `DBIterator`
27+ /// adapter must be avoided here: its `Iterator::next` unconditionally boxes the
28+ /// value via `Box::<[u8]>::from(&[])`, hitting the same null-pointer copy. Guard
29+ /// the empty case so we never copy from the null pointer.
30+ fn iter_bytes_to_vec ( bytes : & [ u8 ] ) -> Vec < u8 > {
31+ if bytes. is_empty ( ) {
32+ Vec :: new ( )
33+ } else {
34+ bytes. to_vec ( )
35+ }
36+ }
37+
2038pub enum TransactionCommand {
2139 Get {
2240 key : Vec < u8 > ,
@@ -174,67 +192,62 @@ impl TransactionTask {
174192 match ( or_equal, offset) {
175193 ( false , 1 ) => {
176194 // first_greater_or_equal: find first key >= search_key
177- let iter = txn. iterator_opt (
178- rocksdb:: IteratorMode :: From ( key, rocksdb:: Direction :: Forward ) ,
179- read_opts,
180- ) ;
181- for item in iter {
182- let ( k, _v) =
183- item. context ( "failed to iterate rocksdb for first_greater_or_equal" ) ?;
184- return Ok ( Some ( k. to_vec ( ) . into ( ) ) ) ;
185- }
186- Ok ( None )
195+ let mut iter = txn. raw_iterator_opt ( read_opts) ;
196+ iter. seek ( key) ;
197+ let result = iter. key ( ) . map ( iter_bytes_to_vec) ;
198+ iter. status ( )
199+ . context ( "failed to iterate rocksdb for first_greater_or_equal" ) ?;
200+ Ok ( result. map ( Into :: into) )
187201 }
188202 ( true , 1 ) => {
189203 // first_greater_than: find first key > search_key
190- let iter = txn. iterator_opt (
191- rocksdb:: IteratorMode :: From ( key, rocksdb:: Direction :: Forward ) ,
192- read_opts,
193- ) ;
194- for item in iter {
195- let ( k, _v) =
196- item. context ( "failed to iterate rocksdb for first_greater_than" ) ?;
204+ let mut iter = txn. raw_iterator_opt ( read_opts) ;
205+ iter. seek ( key) ;
206+ while iter. valid ( ) {
207+ let k = iter. key ( ) . expect ( "iterator should be valid" ) ;
197208 // Skip if it's the exact key
198- if k. as_ref ( ) == key {
209+ if k == key {
210+ iter. next ( ) ;
199211 continue ;
200212 }
201- return Ok ( Some ( k . to_vec ( ) . into ( ) ) ) ;
213+ return Ok ( Some ( iter_bytes_to_vec ( k ) . into ( ) ) ) ;
202214 }
215+ iter. status ( )
216+ . context ( "failed to iterate rocksdb for first_greater_than" ) ?;
203217 Ok ( None )
204218 }
205219 ( false , 0 ) => {
206220 // last_less_than: find last key < search_key
207221 // Use reverse iterator starting just before the key
208- let iter = txn. iterator_opt (
209- rocksdb:: IteratorMode :: From ( key, rocksdb:: Direction :: Reverse ) ,
210- read_opts,
211- ) ;
212-
213- for item in iter {
214- let ( k, _v) = item. context ( "failed to iterate rocksdb for last_less_than" ) ?;
222+ let mut iter = txn. raw_iterator_opt ( read_opts) ;
223+ iter. seek_for_prev ( key) ;
224+ while iter. valid ( ) {
225+ let k = iter. key ( ) . expect ( "iterator should be valid" ) ;
215226 // We want strictly less than
216- if k. as_ref ( ) < key {
217- return Ok ( Some ( k . to_vec ( ) . into ( ) ) ) ;
227+ if k < key {
228+ return Ok ( Some ( iter_bytes_to_vec ( k ) . into ( ) ) ) ;
218229 }
230+ iter. prev ( ) ;
219231 }
232+ iter. status ( )
233+ . context ( "failed to iterate rocksdb for last_less_than" ) ?;
220234 Ok ( None )
221235 }
222236 ( true , 0 ) => {
223237 // last_less_or_equal: find last key <= search_key
224238 // Use reverse iterator starting from the key
225- let iter = txn. iterator_opt (
226- rocksdb:: IteratorMode :: From ( key, rocksdb:: Direction :: Reverse ) ,
227- read_opts,
228- ) ;
229-
230- for item in iter {
231- let ( k, _v) =
232- item. context ( "failed to iterate rocksdb for last_less_or_equal" ) ?;
239+ let mut iter = txn. raw_iterator_opt ( read_opts) ;
240+ iter. seek_for_prev ( key) ;
241+ while iter. valid ( ) {
242+ let k = iter. key ( ) . expect ( "iterator should be valid" ) ;
233243 // We want less than or equal
234- if k. as_ref ( ) <= key {
235- return Ok ( Some ( k . to_vec ( ) . into ( ) ) ) ;
244+ if k <= key {
245+ return Ok ( Some ( iter_bytes_to_vec ( k ) . into ( ) ) ) ;
236246 }
247+ iter. prev ( ) ;
237248 }
249+ iter. status ( )
250+ . context ( "failed to iterate rocksdb for last_less_or_equal" ) ?;
238251 Ok ( None )
239252 }
240253 _ => {
@@ -261,20 +274,21 @@ impl TransactionTask {
261274 }
262275
263276 // Create an iterator to find the key
264- let iter = txn. iterator_opt (
265- rocksdb:: IteratorMode :: From ( key, rocksdb:: Direction :: Forward ) ,
266- ReadOptions :: default ( ) ,
267- ) ;
277+ let mut iter = txn. raw_iterator_opt ( ReadOptions :: default ( ) ) ;
278+ iter. seek ( key) ;
268279
269280 let mut keys: Vec < Vec < u8 > > = Vec :: new ( ) ;
270281
271- for item in iter {
272- let ( k , _v ) = item . context ( "failed to iterate rocksdb for key selector" ) ? ;
273- keys. push ( k . to_vec ( ) ) ;
282+ while iter. valid ( ) {
283+ let k = iter . key ( ) . expect ( "iterator should be valid" ) ;
284+ keys. push ( iter_bytes_to_vec ( k ) ) ;
274285 if keys. len ( ) > ( offset. abs ( ) + 1 ) as usize {
275286 break ;
276287 }
288+ iter. next ( ) ;
277289 }
290+ iter. status ( )
291+ . context ( "failed to iterate rocksdb for key selector" ) ?;
278292
279293 // Apply the selector logic
280294 let idx = if or_equal {
@@ -328,19 +342,28 @@ impl TransactionTask {
328342 . context ( "failed to delete key from rocksdb" ) ?;
329343 }
330344 Operation :: ClearRange { begin, end } => {
331- // RocksDB doesn't have a native clear_range, so we need to iterate and delete
345+ // RocksDB doesn't have a native clear_range, so we need to iterate and delete.
346+ // Collect the in-range keys first, then delete after dropping the iterator, so
347+ // we never mutate the transaction's write batch while iterating it.
332348 let read_opts = ReadOptions :: default ( ) ;
333- let iter = txn. iterator_opt (
334- rocksdb:: IteratorMode :: From ( & begin, rocksdb:: Direction :: Forward ) ,
335- read_opts,
336- ) ;
337-
338- for item in iter {
339- let ( k, _v) = item. context ( "failed to iterate rocksdb for clear range" ) ?;
340- if k. as_ref ( ) >= end. as_slice ( ) {
349+ let mut iter = txn. raw_iterator_opt ( read_opts) ;
350+ iter. seek ( & begin) ;
351+
352+ let mut keys_to_delete: Vec < Vec < u8 > > = Vec :: new ( ) ;
353+ while iter. valid ( ) {
354+ let k = iter. key ( ) . expect ( "iterator should be valid" ) ;
355+ if k >= end. as_slice ( ) {
341356 break ;
342357 }
343- txn. delete ( & k)
358+ keys_to_delete. push ( iter_bytes_to_vec ( k) ) ;
359+ iter. next ( ) ;
360+ }
361+ iter. status ( )
362+ . context ( "failed to iterate rocksdb for clear range" ) ?;
363+ drop ( iter) ;
364+
365+ for key in keys_to_delete {
366+ txn. delete ( & key)
344367 . context ( "failed to delete key in range from rocksdb" ) ?;
345368 }
346369 }
@@ -447,47 +470,54 @@ impl TransactionTask {
447470 // during a forward scan and reversing afterward would instead return the
448471 // lowest keys, which is wrong for reverse range reads.
449472 if reverse {
450- let iter = txn. iterator_opt (
451- rocksdb:: IteratorMode :: From ( & resolved_end, rocksdb:: Direction :: Reverse ) ,
452- read_opts,
453- ) ;
473+ let mut iter = txn. raw_iterator_opt ( read_opts) ;
474+ iter. seek_for_prev ( & resolved_end) ;
454475
455- for item in iter {
456- let ( k , v ) = item . context ( "failed to iterate rocksdb for get range" ) ? ;
476+ while iter. valid ( ) {
477+ let k = iter . key ( ) . expect ( "iterator should be valid" ) ;
457478 // The end key is exclusive, so skip anything at or above it.
458- if k. as_ref ( ) >= resolved_end. as_slice ( ) {
479+ if k >= resolved_end. as_slice ( ) {
480+ iter. prev ( ) ;
459481 continue ;
460482 }
461483 // The begin key is inclusive; once we drop below it we are done.
462- if k. as_ref ( ) < resolved_begin. as_slice ( ) {
484+ if k < resolved_begin. as_slice ( ) {
463485 break ;
464486 }
465487
466- results. push ( KeyValue :: new ( k. to_vec ( ) , v. to_vec ( ) ) ) ;
488+ let key = iter_bytes_to_vec ( k) ;
489+ let value = iter. value ( ) . map ( iter_bytes_to_vec) . unwrap_or_default ( ) ;
490+ results. push ( KeyValue :: new ( key, value) ) ;
467491
468492 if results. len ( ) >= limit {
469493 break ;
470494 }
495+ iter. prev ( ) ;
471496 }
497+ iter. status ( )
498+ . context ( "failed to iterate rocksdb for get range" ) ?;
472499 } else {
473- let iter = txn. iterator_opt (
474- rocksdb:: IteratorMode :: From ( & resolved_begin, rocksdb:: Direction :: Forward ) ,
475- read_opts,
476- ) ;
500+ let mut iter = txn. raw_iterator_opt ( read_opts) ;
501+ iter. seek ( & resolved_begin) ;
477502
478- for item in iter {
479- let ( k , v ) = item . context ( "failed to iterate rocksdb for get range" ) ? ;
503+ while iter. valid ( ) {
504+ let k = iter . key ( ) . expect ( "iterator should be valid" ) ;
480505 // Check if we've reached the end key
481- if k. as_ref ( ) >= resolved_end. as_slice ( ) {
506+ if k >= resolved_end. as_slice ( ) {
482507 break ;
483508 }
484509
485- results. push ( KeyValue :: new ( k. to_vec ( ) , v. to_vec ( ) ) ) ;
510+ let key = iter_bytes_to_vec ( k) ;
511+ let value = iter. value ( ) . map ( iter_bytes_to_vec) . unwrap_or_default ( ) ;
512+ results. push ( KeyValue :: new ( key, value) ) ;
486513
487514 if results. len ( ) >= limit {
488515 break ;
489516 }
517+ iter. next ( ) ;
490518 }
519+ iter. status ( )
520+ . context ( "failed to iterate rocksdb for get range" ) ?;
491521 }
492522
493523 Ok ( Values :: new ( results) )
@@ -511,35 +541,31 @@ impl TransactionTask {
511541 match ( or_equal, offset) {
512542 ( false , 1 ) => {
513543 // first_greater_or_equal: find first key >= search_key
514- let iter = txn. iterator_opt (
515- rocksdb:: IteratorMode :: From ( key, rocksdb:: Direction :: Forward ) ,
516- read_opts,
517- ) ;
518- for item in iter {
519- let ( k, _v) = item. context (
520- "failed to iterate rocksdb for range selector first_greater_or_equal" ,
521- ) ?;
522- return Ok ( k. to_vec ( ) ) ;
523- }
544+ let mut iter = txn. raw_iterator_opt ( read_opts) ;
545+ iter. seek ( key) ;
546+ let result = iter. key ( ) . map ( iter_bytes_to_vec) ;
547+ iter. status ( ) . context (
548+ "failed to iterate rocksdb for range selector first_greater_or_equal" ,
549+ ) ?;
524550 // If no key found, return a key that will make the range empty
525- Ok ( vec ! [ 0xff ; 255 ] )
551+ Ok ( result . unwrap_or_else ( || vec ! [ 0xff ; 255 ] ) )
526552 }
527553 ( true , 1 ) => {
528554 // first_greater_than: find first key > search_key
529- let iter = txn. iterator_opt (
530- rocksdb:: IteratorMode :: From ( key, rocksdb:: Direction :: Forward ) ,
531- read_opts,
532- ) ;
533- for item in iter {
534- let ( k, _v) = item. context (
535- "failed to iterate rocksdb for range selector first_greater_than" ,
536- ) ?;
555+ let mut iter = txn. raw_iterator_opt ( read_opts) ;
556+ iter. seek ( key) ;
557+ while iter. valid ( ) {
558+ let k = iter. key ( ) . expect ( "iterator should be valid" ) ;
537559 // Skip if it's the exact key
538- if k. as_ref ( ) == key {
560+ if k == key {
561+ iter. next ( ) ;
539562 continue ;
540563 }
541- return Ok ( k . to_vec ( ) ) ;
564+ return Ok ( iter_bytes_to_vec ( k ) ) ;
542565 }
566+ iter. status ( ) . context (
567+ "failed to iterate rocksdb for range selector first_greater_than" ,
568+ ) ?;
543569 // If no key found, return a key that will make the range empty
544570 Ok ( vec ! [ 0xff ; 255 ] )
545571 }
0 commit comments