/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 |