rust_rocksdb/db_iterator.rs
1// Copyright 2020 Tyler Neely
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use crate::{
16 Error, ReadOptions, WriteBatch,
17 db::{DB, DBAccess},
18 ffi,
19};
20use libc::{c_char, c_uchar, size_t};
21use std::mem::ManuallyDrop;
22use std::sync::Arc;
23use std::{marker::PhantomData, ops::ControlFlow, slice};
24
25/// A type alias to keep compatibility. See [`DBRawIteratorWithThreadMode`] for details
26pub type DBRawIterator<'a> = DBRawIteratorWithThreadMode<'a, DB>;
27
28/// Keeps an iterator's [`ReadOptions`] alive for as long as the iterator is.
29///
30/// `rocksdb_create_iterators` builds N iterators from one options object, so
31/// that case needs a shared owner. Every other constructor makes one iterator
32/// from one options object and keeps it outright, which costs nothing.
33pub(crate) enum IterReadOptions {
34 Owned(ReadOptions),
35 Shared(Arc<ReadOptions>),
36}
37
38impl IterReadOptions {
39 #[inline]
40 pub(crate) fn as_ptr(&self) -> *mut ffi::rocksdb_readoptions_t {
41 match self {
42 Self::Owned(readopts) => readopts.inner,
43 Self::Shared(readopts) => readopts.inner,
44 }
45 }
46}
47
48impl From<ReadOptions> for IterReadOptions {
49 fn from(readopts: ReadOptions) -> Self {
50 Self::Owned(readopts)
51 }
52}
53
54impl From<Arc<ReadOptions>> for IterReadOptions {
55 fn from(readopts: Arc<ReadOptions>) -> Self {
56 Self::Shared(readopts)
57 }
58}
59
60/// A low-level iterator over a database or column family, created by [`DB::raw_iterator`]
61/// and other `raw_iterator_*` methods.
62///
63/// This iterator replicates RocksDB's API. It should provide better
64/// performance and more features than [`DBIteratorWithThreadMode`], which is a standard
65/// Rust [`std::iter::Iterator`].
66///
67/// ```
68/// use rust_rocksdb::{DB, Options};
69///
70/// let tempdir = tempfile::Builder::new()
71/// .prefix("_path_for_rocksdb_storage4")
72/// .tempdir()
73/// .expect("Failed to create temporary path for the _path_for_rocksdb_storage4.");
74/// let path = tempdir.path();
75/// {
76/// let db = DB::open_default(path).unwrap();
77/// let mut iter = db.raw_iterator();
78///
79/// // Forwards iteration
80/// iter.seek_to_first();
81/// while iter.valid() {
82/// println!("Saw {:?} {:?}", iter.key(), iter.value());
83/// iter.next();
84/// }
85///
86/// // Reverse iteration
87/// iter.seek_to_last();
88/// while iter.valid() {
89/// println!("Saw {:?} {:?}", iter.key(), iter.value());
90/// iter.prev();
91/// }
92///
93/// // Seeking
94/// iter.seek(b"my key");
95/// while iter.valid() {
96/// println!("Saw {:?} {:?}", iter.key(), iter.value());
97/// iter.next();
98/// }
99///
100/// // Reverse iteration from key
101/// // Note, use seek_for_prev when reversing because if this key doesn't exist,
102/// // this will make the iterator start from the previous key rather than the next.
103/// iter.seek_for_prev(b"my key");
104/// while iter.valid() {
105/// println!("Saw {:?} {:?}", iter.key(), iter.value());
106/// iter.prev();
107/// }
108/// }
109/// let _ = DB::destroy(&Options::default(), path);
110/// ```
111pub struct DBRawIteratorWithThreadMode<'a, D: DBAccess> {
112 inner: std::ptr::NonNull<ffi::rocksdb_iterator_t>,
113
114 /// When iterate_lower_bound or iterate_upper_bound are set, the inner
115 /// C iterator keeps a pointer to the upper bound inside `_readopts`.
116 /// Storing this makes sure the upper bound is always alive when the
117 /// iterator is being used.
118 ///
119 /// And yes, we need to store the entire ReadOptions structure since C++
120 /// ReadOptions keep reference to C rocksdb_readoptions_t wrapper which
121 /// point to vectors we own. See issue #660.
122 ///
123 /// This is deliberately not an `Option`. An iterator without its options
124 /// is the bug in issue #660, so the type does not let one be built.
125 readopts: IterReadOptions,
126
127 db: PhantomData<&'a D>,
128}
129
130impl<'a, D: DBAccess> DBRawIteratorWithThreadMode<'a, D> {
131 pub(crate) fn new(db: &D, readopts: ReadOptions) -> Self {
132 let inner = unsafe { db.create_iterator(&readopts) };
133 Self::from_inner(inner, readopts)
134 }
135
136 pub(crate) fn new_cf(
137 db: &'a D,
138 cf_handle: *mut ffi::rocksdb_column_family_handle_t,
139 readopts: ReadOptions,
140 ) -> Self {
141 let inner = unsafe { db.create_iterator_cf(cf_handle, &readopts) };
142 Self::from_inner(inner, readopts)
143 }
144
145 pub(crate) fn from_inner(
146 inner: *mut ffi::rocksdb_iterator_t,
147 readopts: impl Into<IterReadOptions>,
148 ) -> Self {
149 // This unwrap will never fail since rocksdb_create_iterator and
150 // rocksdb_create_iterator_cf functions always return non-null. They
151 // use new and deference the result so any nulls would end up with SIGSEGV
152 // there and we would have a bigger issue.
153 let inner = std::ptr::NonNull::new(inner).unwrap();
154 Self {
155 inner,
156 readopts: readopts.into(),
157 db: PhantomData,
158 }
159 }
160
161 pub(crate) fn into_inner(
162 self,
163 ) -> (std::ptr::NonNull<ffi::rocksdb_iterator_t>, IterReadOptions) {
164 let value = ManuallyDrop::new(self);
165 // SAFETY: value won't be used beyond this point
166 let inner = unsafe { std::ptr::read(&raw const value.inner) };
167 let readopts = unsafe { std::ptr::read(&raw const value.readopts) };
168
169 (inner, readopts)
170 }
171
172 /// The options object this iterator was created from, for tests that need
173 /// to assert which iterators share one owner.
174 #[cfg(test)]
175 pub(crate) fn readopts_ptr(&self) -> *mut ffi::rocksdb_readoptions_t {
176 self.readopts.as_ptr()
177 }
178
179 /// Returns `true` if the iterator is valid. An iterator is invalidated when
180 /// it reaches the end of its defined range, or when it encounters an error.
181 ///
182 /// To check whether the iterator encountered an error after `valid` has
183 /// returned `false`, use the [`status`](DBRawIteratorWithThreadMode::status) method. `status` will never
184 /// return an error when `valid` is `true`.
185 pub fn valid(&self) -> bool {
186 unsafe { ffi::rocksdb_iter_valid(self.inner.as_ptr()) != 0 }
187 }
188
189 /// Returns an error `Result` if the iterator has encountered an error
190 /// during operation. When an error is encountered, the iterator is
191 /// invalidated and [`valid`](DBRawIteratorWithThreadMode::valid) will return `false` when called.
192 ///
193 /// Performing a seek will discard the current status.
194 pub fn status(&self) -> Result<(), Error> {
195 unsafe {
196 ffi_try!(ffi::rocksdb_iter_get_error(self.inner.as_ptr()));
197 }
198 Ok(())
199 }
200
201 /// Refreshes the iterator to represent the latest state of the DB.
202 /// The iterator is invalidated after this call and must be re-sought
203 /// before use.
204 ///
205 /// If the iterator was created with a snapshot, the refreshed iterator
206 /// will no longer use that snapshot and will instead read the latest
207 /// DB state. The snapshot itself is not released; it remains valid and
208 /// will be released when the owning [`crate::SnapshotWithThreadMode`] is dropped.
209 pub fn refresh(&mut self) -> Result<(), Error> {
210 unsafe {
211 ffi_try!(ffi::rocksdb_iter_refresh(self.inner.as_ptr()));
212 }
213 Ok(())
214 }
215
216 /// Seeks to the first key in the database.
217 ///
218 /// # Examples
219 ///
220 /// ```rust
221 /// use rust_rocksdb::{DB, Options};
222 ///
223 /// let tempdir = tempfile::Builder::new()
224 /// .prefix("_path_for_rocksdb_storage5")
225 /// .tempdir()
226 /// .expect("Failed to create temporary path for the _path_for_rocksdb_storage5.");
227 /// let path = tempdir.path();
228 /// {
229 /// let db = DB::open_default(path).unwrap();
230 /// let mut iter = db.raw_iterator();
231 ///
232 /// // Iterate all keys from the start in lexicographic order
233 /// iter.seek_to_first();
234 ///
235 /// while iter.valid() {
236 /// println!("{:?} {:?}", iter.key(), iter.value());
237 /// iter.next();
238 /// }
239 ///
240 /// // Read just the first key
241 /// iter.seek_to_first();
242 ///
243 /// if iter.valid() {
244 /// println!("{:?} {:?}", iter.key(), iter.value());
245 /// } else {
246 /// // There are no keys in the database
247 /// }
248 /// }
249 /// let _ = DB::destroy(&Options::default(), path);
250 /// ```
251 pub fn seek_to_first(&mut self) {
252 unsafe {
253 ffi::rocksdb_iter_seek_to_first(self.inner.as_ptr());
254 }
255 }
256
257 /// Seeks to the last key in the database.
258 ///
259 /// # Examples
260 ///
261 /// ```rust
262 /// use rust_rocksdb::{DB, Options};
263 ///
264 /// let tempdir = tempfile::Builder::new()
265 /// .prefix("_path_for_rocksdb_storage6")
266 /// .tempdir()
267 /// .expect("Failed to create temporary path for the _path_for_rocksdb_storage6.");
268 /// let path = tempdir.path();
269 /// {
270 /// let db = DB::open_default(path).unwrap();
271 /// let mut iter = db.raw_iterator();
272 ///
273 /// // Iterate all keys from the end in reverse lexicographic order
274 /// iter.seek_to_last();
275 ///
276 /// while iter.valid() {
277 /// println!("{:?} {:?}", iter.key(), iter.value());
278 /// iter.prev();
279 /// }
280 ///
281 /// // Read just the last key
282 /// iter.seek_to_last();
283 ///
284 /// if iter.valid() {
285 /// println!("{:?} {:?}", iter.key(), iter.value());
286 /// } else {
287 /// // There are no keys in the database
288 /// }
289 /// }
290 /// let _ = DB::destroy(&Options::default(), path);
291 /// ```
292 pub fn seek_to_last(&mut self) {
293 unsafe {
294 ffi::rocksdb_iter_seek_to_last(self.inner.as_ptr());
295 }
296 }
297
298 /// Seeks to the specified key or the first key that lexicographically follows it.
299 ///
300 /// This method will attempt to seek to the specified key. If that key does not exist, it will
301 /// find and seek to the key that lexicographically follows it instead.
302 ///
303 /// # Examples
304 ///
305 /// ```rust
306 /// use rust_rocksdb::{DB, Options};
307 ///
308 /// let tempdir = tempfile::Builder::new()
309 /// .prefix("_path_for_rocksdb_storage7")
310 /// .tempdir()
311 /// .expect("Failed to create temporary path for the _path_for_rocksdb_storage7.");
312 /// let path = tempdir.path();
313 /// {
314 /// let db = DB::open_default(path).unwrap();
315 /// let mut iter = db.raw_iterator();
316 ///
317 /// // Read the first key that starts with 'a'
318 /// iter.seek(b"a");
319 ///
320 /// if iter.valid() {
321 /// println!("{:?} {:?}", iter.key(), iter.value());
322 /// } else {
323 /// // There are no keys in the database
324 /// }
325 /// }
326 /// let _ = DB::destroy(&Options::default(), path);
327 /// ```
328 pub fn seek<K: AsRef<[u8]>>(&mut self, key: K) {
329 let key = key.as_ref();
330
331 unsafe {
332 ffi::rocksdb_iter_seek(
333 self.inner.as_ptr(),
334 key.as_ptr() as *const c_char,
335 key.len() as size_t,
336 );
337 }
338 }
339
340 /// Seeks to the specified key, or the first key that lexicographically precedes it.
341 ///
342 /// Like ``.seek()`` this method will attempt to seek to the specified key.
343 /// The difference with ``.seek()`` is that if the specified key do not exist, this method will
344 /// seek to key that lexicographically precedes it instead.
345 ///
346 /// # Examples
347 ///
348 /// ```rust
349 /// use rust_rocksdb::{DB, Options};
350 ///
351 /// let tempdir = tempfile::Builder::new()
352 /// .prefix("_path_for_rocksdb_storage8")
353 /// .tempdir()
354 /// .expect("Failed to create temporary path for the _path_for_rocksdb_storage8.");
355 /// let path = tempdir.path();
356 /// {
357 /// let db = DB::open_default(path).unwrap();
358 /// let mut iter = db.raw_iterator();
359 ///
360 /// // Read the last key that starts with 'a'
361 /// iter.seek_for_prev(b"b");
362 ///
363 /// if iter.valid() {
364 /// println!("{:?} {:?}", iter.key(), iter.value());
365 /// } else {
366 /// // There are no keys in the database
367 /// }
368 /// }
369 /// let _ = DB::destroy(&Options::default(), path);
370 /// ```
371 pub fn seek_for_prev<K: AsRef<[u8]>>(&mut self, key: K) {
372 let key = key.as_ref();
373
374 unsafe {
375 ffi::rocksdb_iter_seek_for_prev(
376 self.inner.as_ptr(),
377 key.as_ptr() as *const c_char,
378 key.len() as size_t,
379 );
380 }
381 }
382
383 /// Seeks to the next key.
384 pub fn next(&mut self) {
385 if self.valid() {
386 // SAFETY: The validity check above guarantees that RocksDB permits
387 // advancing the iterator.
388 unsafe { self.next_unchecked() }
389 }
390 }
391
392 /// Advances to the next key without checking iterator validity.
393 ///
394 /// # Safety
395 ///
396 /// The iterator must be valid.
397 #[inline]
398 unsafe fn next_unchecked(&mut self) {
399 unsafe {
400 ffi::rocksdb_iter_next(self.inner.as_ptr());
401 }
402 }
403
404 /// Seeks to the previous key.
405 pub fn prev(&mut self) {
406 if self.valid() {
407 // SAFETY: The validity check above guarantees that RocksDB permits
408 // advancing the iterator.
409 unsafe { self.prev_unchecked() }
410 }
411 }
412
413 /// Advances to the previous key without checking iterator validity.
414 ///
415 /// # Safety
416 ///
417 /// The iterator must be valid.
418 #[inline]
419 unsafe fn prev_unchecked(&mut self) {
420 unsafe {
421 ffi::rocksdb_iter_prev(self.inner.as_ptr());
422 }
423 }
424
425 /// Returns a slice of the current key.
426 pub fn key(&self) -> Option<&[u8]> {
427 if self.valid() {
428 // SAFETY: We just checked that the iterator is valid.
429 Some(unsafe { self.key_impl() })
430 } else {
431 None
432 }
433 }
434
435 /// Returns a slice of the current value.
436 pub fn value(&self) -> Option<&[u8]> {
437 if self.valid() {
438 // SAFETY: We just checked that the iterator is valid.
439 Some(unsafe { self.value_impl() })
440 } else {
441 None
442 }
443 }
444
445 /// Returns pair with slice of the current key and current value.
446 pub fn item(&self) -> Option<(&[u8], &[u8])> {
447 if self.valid() {
448 // SAFETY: We just checked that the iterator is valid.
449 Some(unsafe { (self.key_impl(), self.value_impl()) })
450 } else {
451 None
452 }
453 }
454
455 /// Returns a slice of the current key.
456 ///
457 /// # Safety
458 ///
459 /// The iterator must be valid (i.e., `valid()` returns true). Calling this
460 /// method when the iterator is invalid is undefined behavior, as RocksDB
461 /// may return an invalid pointer.
462 ///
463 /// Uses `rocksdb_iter_key_slice` which returns a `rocksdb_slice_t` by value,
464 /// avoiding the overhead of output parameters compared to `rocksdb_iter_key`.
465 #[inline]
466 unsafe fn key_impl(&self) -> &[u8] {
467 unsafe {
468 let slice = ffi::rocksdb_iter_key_slice(self.inner.as_ptr());
469 if slice.size == 0 {
470 // Empty keys and values are legal in RocksDB, and
471 // `slice::from_raw_parts` requires a dereferenceable pointer
472 // even for a zero length, so do not build a slice from
473 // whatever `data` happens to be.
474 return &[];
475 }
476 slice::from_raw_parts(slice.data as *const c_uchar, slice.size)
477 }
478 }
479
480 /// Returns a slice of the current value.
481 ///
482 /// # Safety
483 ///
484 /// The iterator must be valid (i.e., `valid()` returns true). Calling this
485 /// method when the iterator is invalid is undefined behavior, as RocksDB
486 /// may return an invalid pointer.
487 ///
488 /// Uses `rocksdb_iter_value_slice` which returns a `rocksdb_slice_t` by value,
489 /// avoiding the overhead of output parameters compared to `rocksdb_iter_value`.
490 #[inline]
491 unsafe fn value_impl(&self) -> &[u8] {
492 unsafe {
493 let slice = ffi::rocksdb_iter_value_slice(self.inner.as_ptr());
494 if slice.size == 0 {
495 // Empty keys and values are legal in RocksDB, and
496 // `slice::from_raw_parts` requires a dereferenceable pointer
497 // even for a zero length, so do not build a slice from
498 // whatever `data` happens to be.
499 return &[];
500 }
501 slice::from_raw_parts(slice.data as *const c_uchar, slice.size)
502 }
503 }
504
505 /// Returns a slice of the current entry's timestamp.
506 pub fn timestamp(&self) -> Option<&[u8]> {
507 if self.valid() {
508 // SAFETY: We just checked that the iterator is valid.
509 Some(unsafe { self.timestamp_unchecked() })
510 } else {
511 None
512 }
513 }
514
515 /// Returns the timestamp of the current entry without checking iterator validity.
516 ///
517 /// # Safety
518 ///
519 /// The iterator must be valid (i.e., `valid()` returns true). Calling this
520 /// method when the iterator is invalid is undefined behavior, as RocksDB
521 /// may return an invalid pointer.
522 ///
523 /// Uses `rocksdb_iter_timestamp_slice` which returns a `rocksdb_slice_t` by value,
524 /// avoiding the overhead of output parameters compared to `rocksdb_iter_timestamp`.
525 pub unsafe fn timestamp_unchecked(&self) -> &[u8] {
526 unsafe {
527 let slice = ffi::rocksdb_iter_timestamp_slice(self.inner.as_ptr());
528 if slice.size == 0 {
529 return &[];
530 }
531 slice::from_raw_parts(slice.data as *const c_uchar, slice.size)
532 }
533 }
534}
535
536impl<D: DBAccess> Drop for DBRawIteratorWithThreadMode<'_, D> {
537 fn drop(&mut self) {
538 unsafe {
539 ffi::rocksdb_iter_destroy(self.inner.as_ptr());
540 }
541 }
542}
543
544unsafe impl<D: DBAccess> Send for DBRawIteratorWithThreadMode<'_, D> {}
545unsafe impl<D: DBAccess> Sync for DBRawIteratorWithThreadMode<'_, D> {}
546
547/// A type alias to keep compatibility. See [`DBIteratorWithThreadMode`] for details
548pub type DBIterator<'a> = DBIteratorWithThreadMode<'a, DB>;
549
550/// A standard Rust [`Iterator`] over a database or column family.
551///
552/// As an alternative, [`DBRawIteratorWithThreadMode`] is a low level wrapper around
553/// RocksDB's API, which can provide better performance and more features.
554///
555/// ```
556/// use rust_rocksdb::{DB, Direction, IteratorMode, Options};
557///
558/// let tempdir = tempfile::Builder::new()
559/// .prefix("_path_for_rocksdb_storage2")
560/// .tempdir()
561/// .expect("Failed to create temporary path for the _path_for_rocksdb_storage2.");
562/// let path = tempdir.path();
563/// {
564/// let db = DB::open_default(path).unwrap();
565/// let mut iter = db.iterator(IteratorMode::Start); // Always iterates forward
566/// for item in iter {
567/// let (key, value) = item.unwrap();
568/// println!("Saw {:?} {:?}", key, value);
569/// }
570/// iter = db.iterator(IteratorMode::End); // Always iterates backward
571/// for item in iter {
572/// let (key, value) = item.unwrap();
573/// println!("Saw {:?} {:?}", key, value);
574/// }
575/// iter = db.iterator(IteratorMode::From(b"my key", Direction::Forward)); // From a key in Direction::{forward,reverse}
576/// for item in iter {
577/// let (key, value) = item.unwrap();
578/// println!("Saw {:?} {:?}", key, value);
579/// }
580///
581/// // You can seek with an existing Iterator instance, too
582/// iter = db.iterator(IteratorMode::Start);
583/// iter.set_mode(IteratorMode::From(b"another key", Direction::Reverse));
584/// for item in iter {
585/// let (key, value) = item.unwrap();
586/// println!("Saw {:?} {:?}", key, value);
587/// }
588/// }
589/// let _ = DB::destroy(&Options::default(), path);
590/// ```
591///
592/// An iterator must not outlive the `DB` it iterates over:
593///
594/// ```compile_fail,E0597
595/// use rust_rocksdb::{IteratorMode, DB};
596///
597/// let _iter = {
598/// let db = DB::open_default("foo").unwrap();
599/// db.iterator(IteratorMode::Start)
600/// };
601/// ```
602pub struct DBIteratorWithThreadMode<'a, D: DBAccess> {
603 raw: DBRawIteratorWithThreadMode<'a, D>,
604 direction: Direction,
605 done: bool,
606}
607
608#[derive(Copy, Clone)]
609pub enum Direction {
610 Forward,
611 Reverse,
612}
613
614pub type KVBytes = (Box<[u8]>, Box<[u8]>);
615
616#[derive(Copy, Clone)]
617pub enum IteratorMode<'a> {
618 Start,
619 End,
620 From(&'a [u8], Direction),
621}
622
623impl<'a, D: DBAccess> DBIteratorWithThreadMode<'a, D> {
624 pub(crate) fn new(db: &D, readopts: ReadOptions, mode: IteratorMode) -> Self {
625 Self::from_raw(DBRawIteratorWithThreadMode::new(db, readopts), mode)
626 }
627
628 pub(crate) fn new_cf(
629 db: &'a D,
630 cf_handle: *mut ffi::rocksdb_column_family_handle_t,
631 readopts: ReadOptions,
632 mode: IteratorMode,
633 ) -> Self {
634 Self::from_raw(
635 DBRawIteratorWithThreadMode::new_cf(db, cf_handle, readopts),
636 mode,
637 )
638 }
639
640 fn from_raw(raw: DBRawIteratorWithThreadMode<'a, D>, mode: IteratorMode) -> Self {
641 let mut rv = DBIteratorWithThreadMode {
642 raw,
643 direction: Direction::Forward, // blown away by set_mode()
644 done: false,
645 };
646 rv.set_mode(mode);
647 rv
648 }
649
650 pub fn set_mode(&mut self, mode: IteratorMode) {
651 self.done = false;
652 self.direction = match mode {
653 IteratorMode::Start => {
654 self.raw.seek_to_first();
655 Direction::Forward
656 }
657 IteratorMode::End => {
658 self.raw.seek_to_last();
659 Direction::Reverse
660 }
661 IteratorMode::From(key, Direction::Forward) => {
662 self.raw.seek(key);
663 Direction::Forward
664 }
665 IteratorMode::From(key, Direction::Reverse) => {
666 self.raw.seek_for_prev(key);
667 Direction::Reverse
668 }
669 };
670 }
671
672 /// Refreshes the iterator, then re-seeks using the given mode.
673 ///
674 /// After a refresh the underlying iterator is invalidated, so a mode
675 /// must be provided to reposition it.
676 pub fn refresh(&mut self, mode: IteratorMode) -> Result<(), Error> {
677 self.raw.refresh()?;
678 self.set_mode(mode);
679 Ok(())
680 }
681
682 /// Visits the remaining entries without allocating key or value buffers.
683 ///
684 /// The key and value slices are valid only for the duration of each callback.
685 /// Returning [`ControlFlow::Break`] stops the scan after consuming the current
686 /// entry. The iterator can then resume at the following entry. Callback errors
687 /// and RocksDB iterator errors are returned through `E`.
688 ///
689 /// # Examples
690 ///
691 /// ```
692 /// use std::ops::ControlFlow;
693 ///
694 /// use rust_rocksdb::{DB, Error, IteratorMode};
695 ///
696 /// # let tempdir = tempfile::tempdir().unwrap();
697 /// let db = DB::open_default(tempdir.path()).unwrap();
698 /// db.put(b"k1", b"value").unwrap();
699 ///
700 /// let mut total_bytes = 0;
701 /// let mut iter = db.iterator(IteratorMode::Start);
702 /// let outcome: Result<ControlFlow<()>, Error> = iter.try_for_each_ref(|key, value| {
703 /// total_bytes += key.len() + value.len();
704 /// Ok(ControlFlow::Continue(()))
705 /// });
706 ///
707 /// assert!(matches!(outcome, Ok(ControlFlow::Continue(()))));
708 /// assert_eq!(total_bytes, 7);
709 /// ```
710 ///
711 /// Borrowed entries cannot escape the callback:
712 ///
713 /// ```compile_fail,E0521
714 /// use std::ops::ControlFlow;
715 ///
716 /// use rust_rocksdb::{DB, Error, IteratorMode};
717 ///
718 /// # let tempdir = tempfile::tempdir().unwrap();
719 /// let db = DB::open_default(tempdir.path()).unwrap();
720 /// let mut iter = db.iterator(IteratorMode::Start);
721 /// let mut saved_key = None;
722 ///
723 /// let _: Result<ControlFlow<()>, Error> = iter.try_for_each_ref(|key, _| {
724 /// saved_key = Some(key);
725 /// Ok(ControlFlow::Break(()))
726 /// });
727 /// ```
728 #[inline]
729 pub fn try_for_each_ref<B, E, F>(&mut self, mut visit: F) -> Result<ControlFlow<B>, E>
730 where
731 E: From<Error>,
732 F: FnMut(&[u8], &[u8]) -> Result<ControlFlow<B>, E>,
733 {
734 loop {
735 let item = self
736 .next_with(|key, value| visit(key, value))
737 .map_err(E::from)?;
738 match item {
739 Some(Ok(ControlFlow::Continue(()))) => {}
740 Some(Ok(ControlFlow::Break(value))) => return Ok(ControlFlow::Break(value)),
741 Some(Err(error)) => return Err(error),
742 None => return Ok(ControlFlow::Continue(())),
743 }
744 }
745 }
746
747 #[inline]
748 fn next_with<T>(&mut self, visit: impl FnOnce(&[u8], &[u8]) -> T) -> Result<Option<T>, Error> {
749 if self.done {
750 return Ok(None);
751 }
752
753 let Some((key, value)) = self.raw.item() else {
754 self.done = true;
755 self.raw.status()?;
756 return Ok(None);
757 };
758
759 let item = visit(key, value);
760 // SAFETY: `raw.item()` returned an entry, which proves that the
761 // iterator is valid until it is advanced.
762 unsafe { self.advance_unchecked() };
763 Ok(Some(item))
764 }
765
766 /// Advances in the configured direction without checking iterator validity.
767 ///
768 /// # Safety
769 ///
770 /// The raw iterator must be valid.
771 #[inline]
772 unsafe fn advance_unchecked(&mut self) {
773 match self.direction {
774 Direction::Forward => unsafe { self.raw.next_unchecked() },
775 Direction::Reverse => unsafe { self.raw.prev_unchecked() },
776 }
777 }
778}
779
780impl<D: DBAccess> Iterator for DBIteratorWithThreadMode<'_, D> {
781 type Item = Result<KVBytes, Error>;
782
783 fn next(&mut self) -> Option<Result<KVBytes, Error>> {
784 match self.next_with(|key, value| (Box::from(key), Box::from(value))) {
785 Ok(Some(item)) => Some(Ok(item)),
786 Ok(None) => None,
787 Err(error) => Some(Err(error)),
788 }
789 }
790}
791
792impl<D: DBAccess> std::iter::FusedIterator for DBIteratorWithThreadMode<'_, D> {}
793
794impl<'a, D: DBAccess> Into<DBRawIteratorWithThreadMode<'a, D>> for DBIteratorWithThreadMode<'a, D> {
795 fn into(self) -> DBRawIteratorWithThreadMode<'a, D> {
796 self.raw
797 }
798}
799
800/// Iterates the batches of writes since a given sequence number.
801///
802/// `DBWALIterator` is returned by `DB::get_updates_since()` and will return the
803/// batches of write operations that have occurred since a given sequence number
804/// (see `DB::latest_sequence_number()`). This iterator cannot be constructed by
805/// the application.
806///
807/// The iterator item type is a tuple of (`u64`, `WriteBatch`) where the first
808/// value is the sequence number of the associated write batch.
809///
810pub struct DBWALIterator {
811 pub(crate) inner: *mut ffi::rocksdb_wal_iterator_t,
812 pub(crate) start_seq_number: u64,
813}
814
815impl DBWALIterator {
816 /// Returns `true` if the iterator is valid. An iterator is invalidated when
817 /// it reaches the end of its defined range, or when it encounters an error.
818 ///
819 /// To check whether the iterator encountered an error after `valid` has
820 /// returned `false`, use the [`status`](DBWALIterator::status) method.
821 /// `status` will never return an error when `valid` is `true`.
822 pub fn valid(&self) -> bool {
823 unsafe { ffi::rocksdb_wal_iter_valid(self.inner) != 0 }
824 }
825
826 /// Returns an error `Result` if the iterator has encountered an error
827 /// during operation. When an error is encountered, the iterator is
828 /// invalidated and [`valid`](DBWALIterator::valid) will return `false` when
829 /// called.
830 pub fn status(&self) -> Result<(), Error> {
831 unsafe {
832 ffi_try!(ffi::rocksdb_wal_iter_status(self.inner));
833 }
834 Ok(())
835 }
836}
837
838impl Iterator for DBWALIterator {
839 type Item = Result<(u64, WriteBatch), Error>;
840
841 fn next(&mut self) -> Option<Self::Item> {
842 if !self.valid() {
843 return None;
844 }
845
846 let mut seq: u64 = 0;
847 let mut batch = WriteBatch {
848 inner: unsafe { ffi::rocksdb_wal_iter_get_batch(self.inner, &raw mut seq) },
849 };
850
851 // if the initial sequence number is what was requested we skip it to
852 // only provide changes *after* it
853 while seq <= self.start_seq_number {
854 unsafe {
855 ffi::rocksdb_wal_iter_next(self.inner);
856 }
857
858 if !self.valid() {
859 return None;
860 }
861
862 // this drops which in turn frees the skipped batch
863 batch = WriteBatch {
864 inner: unsafe { ffi::rocksdb_wal_iter_get_batch(self.inner, &raw mut seq) },
865 };
866 }
867
868 if !self.valid() {
869 return self.status().err().map(Result::Err);
870 }
871
872 // Seek to the next write batch.
873 // Note that WriteBatches live independently of the WAL iterator so this is safe to do
874 unsafe {
875 ffi::rocksdb_wal_iter_next(self.inner);
876 }
877
878 Some(Ok((seq, batch)))
879 }
880}
881
882impl Drop for DBWALIterator {
883 fn drop(&mut self) {
884 unsafe {
885 ffi::rocksdb_wal_iter_destroy(self.inner);
886 }
887 }
888}