Coverage Report

Created: 2025-07-23 07:17

/src/rocksdb/include/rocksdb/multi_scan.h
Line
Count
Source (jump to first uncovered line)
1
//  Copyright (c) Meta Platforms, Inc. and affiliates.
2
//  This source code is licensed under both the GPLv2 (found in the
3
//  COPYING file in the root directory) and Apache 2.0 License
4
//  (found in the LICENSE.Apache file in the root directory).
5
6
#pragma once
7
8
#include "rocksdb/db.h"
9
#include "rocksdb/iterator.h"
10
#include "rocksdb/options.h"
11
12
namespace ROCKSDB_NAMESPACE {
13
14
// EXPERIMENTAL
15
//
16
// An iterator that returns results from multiple scan ranges. The ranges are
17
// expected to be in increasing sorted order.
18
// The results are returned in nested container objects that can be iterated
19
// using an std::input_iterator.
20
//
21
// MultiScan
22
//     |
23
//     ---
24
//       |
25
//  MultiScanIterator  <-- std::input_iterator (returns a Scan object for each
26
//         |                                    scan range)
27
//         ---
28
//           |
29
//          Scan
30
//            |
31
//            ---
32
//              |
33
//          ScanIterator <-- std::input_iterator (returns the KVs of a single
34
//                                                scan range)
35
//
36
// The application on top of RocksDB
37
// would use this as follows -
38
//
39
//  std::vector<ScanOptions> scans{{.start = Slice("bar")},
40
//                              {.start = Slice("foo")}};
41
//  std::unique_ptr<MultiScan> iter.reset(
42
//                                      db->NewMultiScan());
43
//  try {
44
//    for (auto scan : *iter) {
45
//      for (auto it : scan) {
46
//        // Do something with key - it.first
47
//        // Do something with value - it.second
48
//      }
49
//    }
50
//  } catch (MultiScanException& ex) {
51
//    // Check ex.status()
52
//  } catch (std::logic_error& ex) {
53
//    // Check ex.what()
54
//  }
55
56
class MultiScanException : public std::runtime_error {
57
 public:
58
  explicit MultiScanException(Status& s)
59
0
      : std::runtime_error(s.ToString()), s_(s) {}
60
61
0
  Status& status() { return s_; }
62
63
 private:
64
  Status s_;
65
};
66
67
// A container object encapsulating a single scan range. It supports an
68
// std::input_iterator for a single pass iteration of the KVs in the range.
69
// A Status exception is thrown if there is an error in scanning the range.
70
class Scan {
71
 public:
72
  class ScanIterator;
73
74
0
  explicit Scan(Iterator* db_iter) : db_iter_(db_iter) {}
75
76
0
  void Reset(Iterator* db_iter) { db_iter_ = db_iter; }
77
78
0
  ScanIterator begin() { return ScanIterator(db_iter_); }
79
80
0
  std::nullptr_t end() { return nullptr; }
81
82
  class ScanIterator {
83
   public:
84
    using self_type = ScanIterator;
85
    using value_type = std::pair<Slice, Slice>;
86
    using reference = std::pair<Slice, Slice>&;
87
    using pointer = std::pair<Slice, Slice>*;
88
    using difference_type = int;
89
    using iterator_category = std::input_iterator_tag;
90
91
0
    explicit ScanIterator(Iterator* db_iter) : db_iter_(db_iter) {
92
0
      valid_ = db_iter_->Valid();
93
0
      if (valid_) {
94
0
        result_ = value_type(db_iter_->key(), db_iter_->value());
95
0
      }
96
0
    }
97
98
0
    ScanIterator() : db_iter_(nullptr), valid_(false) {}
99
100
0
    ~ScanIterator() { assert(status_.ok()); }
101
102
0
    ScanIterator& operator++() {
103
0
      if (!valid_) {
104
0
        throw std::logic_error("Trying to advance invalid iterator");
105
0
      } else {
106
0
        db_iter_->Next();
107
0
        status_ = db_iter_->status();
108
0
        if (!status_.ok()) {
109
0
          throw MultiScanException(status_);
110
0
        } else {
111
0
          valid_ = db_iter_->Valid();
112
0
          if (valid_) {
113
0
            result_ = value_type(db_iter_->key(), db_iter_->value());
114
0
          }
115
0
        }
116
0
      }
117
0
      return *this;
118
0
    }
119
120
0
    bool operator==(std::nullptr_t /*other*/) const { return !valid_; }
121
122
0
    bool operator!=(std::nullptr_t /*other*/) const { return valid_; }
123
124
0
    reference operator*() {
125
0
      if (!valid_) {
126
0
        throw std::logic_error("Trying to deref invalid iterator");
127
0
      }
128
0
      return result_;
129
0
    }
130
0
    reference operator->() {
131
0
      if (!valid_) {
132
0
        throw std::logic_error("Trying to deref invalid iterator");
133
0
      }
134
0
      return result_;
135
0
    }
136
137
   private:
138
    Iterator* db_iter_;
139
    bool valid_;
140
    Status status_;
141
    value_type result_;
142
  };
143
144
 private:
145
  Iterator* db_iter_;
146
};
147
148
// A container object encapsulating the scan ranges for a multi scan.
149
// It supports an std::input_iterator for a single pass iteration of the
150
// ScanOptions in scan_opts, which can be dereferenced to get the container
151
// (Scan) for a single range.
152
// A Status exception is thrown if there is an error.
153
class MultiScan {
154
 public:
155
  MultiScan(const ReadOptions& read_options,
156
            const std::vector<ScanOptions>& scan_opts, DB* db,
157
            ColumnFamilyHandle* cfh);
158
159
  explicit MultiScan(std::unique_ptr<Iterator>&& db_iter)
160
0
      : db_iter_(std::move(db_iter)) {}
161
162
  class MultiScanIterator {
163
   public:
164
    MultiScanIterator(const MultiScanIterator&) = delete;
165
    MultiScanIterator operator=(MultiScanIterator&) = delete;
166
167
    using self_type = MultiScanIterator;
168
    using value_type = Scan;
169
    using reference = Scan&;
170
    using pointer = Scan*;
171
    using difference_type = int;
172
    using iterator_category = std::input_iterator_tag;
173
174
    MultiScanIterator(const std::vector<ScanOptions>& scan_opts, DB* db,
175
                      ColumnFamilyHandle* cfh, ReadOptions& read_options,
176
                      Slice* upper_bound, std::unique_ptr<Iterator>& db_iter)
177
        : scan_opts_(scan_opts),
178
          db_(db),
179
          cfh_(cfh),
180
          read_options_(read_options),
181
          upper_bound_(upper_bound),
182
          idx_(0),
183
          db_iter_(db_iter),
184
0
          scan_(db_iter_.get()) {
185
0
      if (scan_opts_.empty()) {
186
0
        throw std::logic_error("Zero scans in multi-scan");
187
0
      }
188
0
      db_iter_->Seek(*scan_opts_[idx_].range.start);
189
0
      status_ = db_iter_->status();
190
0
      if (!status_.ok()) {
191
0
        throw MultiScanException(status_);
192
0
      }
193
0
    }
194
195
0
    ~MultiScanIterator() { assert(status_.ok()); }
196
197
    MultiScanIterator& operator++();
198
199
0
    bool operator==(std::nullptr_t /*other*/) const {
200
0
      return idx_ >= scan_opts_.size();
201
0
    }
202
203
0
    bool operator!=(std::nullptr_t /*other*/) const {
204
0
      return idx_ < scan_opts_.size();
205
0
    }
206
207
0
    reference operator*() { return scan_; }
208
0
    reference operator->() { return scan_; }
209
210
   private:
211
    const std::vector<ScanOptions>& scan_opts_;
212
    DB* db_;
213
    ColumnFamilyHandle* cfh_;
214
    ReadOptions& read_options_;
215
    Slice* upper_bound_;
216
    size_t idx_;
217
    std::unique_ptr<Iterator>& db_iter_;
218
    Status status_;
219
    Scan scan_;
220
  };
221
222
0
  MultiScanIterator begin() {
223
0
    return MultiScanIterator(scan_opts_, db_, cfh_, read_options_,
224
0
                             &upper_bound_, db_iter_);
225
0
  }
226
227
0
  std::nullptr_t end() { return nullptr; }
228
229
 private:
230
  ReadOptions read_options_;
231
  const std::vector<ScanOptions> scan_opts_;
232
  DB* db_;
233
  ColumnFamilyHandle* cfh_;
234
  Slice upper_bound_;
235
  std::unique_ptr<Iterator> db_iter_;
236
};
237
238
}  // namespace ROCKSDB_NAMESPACE