33// COPYING file in the root directory) and Apache 2.0 License
44// (found in the LICENSE.Apache file in the root directory).
55
6+ #include < string>
7+ #include < vector>
8+
69#include " file/file_util.h"
710#include " rocksdb/db.h"
811#include " rocksdb/file_system.h"
12+ #include " rocksdb/iterator.h"
913
1014namespace ROCKSDB_NAMESPACE {
1115
1216using MultiScanIterator = MultiScan::MultiScanIterator;
1317
18+ namespace {
19+
20+ Status ValidateScanOptionsForMultiScan (const MultiScanArgs& multiscan_opts) {
21+ if (multiscan_opts.empty ()) {
22+ return Status::InvalidArgument (" Empty MultiScanArgs" );
23+ }
24+
25+ const Comparator* comparator = multiscan_opts.GetComparator ();
26+ if (comparator == nullptr ) {
27+ return Status::InvalidArgument (" MultiScanArgs requires comparator" );
28+ }
29+
30+ const std::vector<ScanOptions>& scan_opts = multiscan_opts.GetScanRanges ();
31+ for (size_t i = 0 ; i < scan_opts.size (); ++i) {
32+ const auto & scan_range = scan_opts[i].range ;
33+ if (!scan_range.start .has_value ()) {
34+ return Status::InvalidArgument (" Scan has no start key at index " +
35+ std::to_string (i));
36+ }
37+ if (multiscan_opts.reverse && !scan_range.limit .has_value ()) {
38+ return Status::InvalidArgument (
39+ " Reverse MultiScan requires upper bound at index " +
40+ std::to_string (i));
41+ }
42+ if (scan_range.limit .has_value () &&
43+ comparator->CompareWithoutTimestamp (
44+ scan_range.start .value (), /* a_has_ts=*/ false ,
45+ scan_range.limit .value (), /* b_has_ts=*/ false ) >= 0 ) {
46+ return Status::InvalidArgument (
47+ " Scan start key is large or equal than limit at index " +
48+ std::to_string (i));
49+ }
50+ }
51+
52+ return Status::OK ();
53+ }
54+
55+ } // namespace
56+
1457MultiScan::MultiScan (const ReadOptions& read_options,
1558 const MultiScanArgs& scan_opts, DB * db,
1659 ColumnFamilyHandle* cfh)
@@ -28,14 +71,34 @@ MultiScan::MultiScan(const ReadOptions& read_options,
2871 }()),
2972 db_(db),
3073 cfh_(cfh) {
74+ Status validate_status = ValidateScanOptionsForMultiScan (scan_opts_);
75+ if (!validate_status.ok ()) {
76+ db_iter_.reset (NewErrorIterator (validate_status));
77+ return ;
78+ }
79+
3180 bool slow_path = false ;
32- // Setup read_options with iterate_uuper_bound based on the first scan.
33- // Subsequent scans will update and allocate a new DB iterator as necessary
34- if (scan_opts_.GetScanRanges ()[0 ].range .limit ) {
35- upper_bound_ = *scan_opts_.GetScanRanges ()[0 ].range .limit ;
36- read_options_.iterate_upper_bound = &upper_bound_;
37- } else {
38- read_options_.iterate_upper_bound = nullptr ;
81+ // Setup read_options bounds based on the first scan. Subsequent scans will
82+ // update the bounds and allocate a new DB iterator as necessary.
83+ if (!scan_opts_.GetScanRanges ().empty ()) {
84+ const size_t first_scan_idx =
85+ scan_opts_.reverse ? scan_opts_.GetScanRanges ().size () - 1 : 0 ;
86+ const ScanOptions& first_scan = scan_opts_.GetScanRanges ()[first_scan_idx];
87+ if (scan_opts_.reverse ) {
88+ lower_bound_ = *first_scan.range .start ;
89+ read_options_.iterate_lower_bound = &lower_bound_;
90+ if (first_scan.range .limit ) {
91+ upper_bound_ = *first_scan.range .limit ;
92+ read_options_.iterate_upper_bound = &upper_bound_;
93+ } else {
94+ read_options_.iterate_upper_bound = nullptr ;
95+ }
96+ } else if (first_scan.range .limit ) {
97+ upper_bound_ = *first_scan.range .limit ;
98+ read_options_.iterate_upper_bound = &upper_bound_;
99+ } else {
100+ read_options_.iterate_upper_bound = nullptr ;
101+ }
39102 }
40103 for (const auto & opts : scan_opts_.GetScanRanges ()) {
41104 // Check that all the ScanOptions either specify an upper bound or not. If
@@ -54,17 +117,49 @@ MultiScan::MultiScan(const ReadOptions& read_options,
54117 }
55118}
56119
120+ void MultiScanIterator::SeekCurrentRange () {
121+ const ScanOptions& scan_opt = scan_opts_[idx_];
122+ if (scan_opt.range .limit ) {
123+ *upper_bound_ = *scan_opt.range .limit ;
124+ read_options_.iterate_upper_bound = upper_bound_;
125+ } else {
126+ read_options_.iterate_upper_bound = nullptr ;
127+ }
128+
129+ if (reverse_) {
130+ *lower_bound_ = *scan_opt.range .start ;
131+ read_options_.iterate_lower_bound = lower_bound_;
132+ assert (scan_opt.range .limit .has_value ());
133+ db_iter_->SeekForPrev (*scan_opt.range .limit );
134+ } else {
135+ db_iter_->Seek (*scan_opt.range .start );
136+ }
137+ }
138+
57139MultiScanIterator& MultiScanIterator::operator ++() {
58140 status_ = db_iter_->status ();
59141 if (!status_.ok ()) {
60142 throw MultiScanException (status_);
61143 }
62144
63- if (idx_ >= scan_opts_.size ()) {
64- throw std::logic_error (" Index out of range" );
145+ if (!valid_) {
146+ throw std::logic_error (" Incrementing exhausted MultiScan iterator" );
147+ }
148+ if (reverse_) {
149+ if (idx_ == 0 ) {
150+ valid_ = false ;
151+ return *this ;
152+ }
153+ --idx_;
154+ } else {
155+ ++idx_;
156+ if (idx_ >= scan_opts_.size ()) {
157+ valid_ = false ;
158+ return *this ;
159+ }
65160 }
66- idx_++;
67- if (idx_ < scan_opts_. size () ) {
161+
162+ if (valid_ ) {
68163 // Check if we need to update read_options_
69164 if (scan_opts_[idx_].range .limit .has_value () !=
70165 (read_options_.iterate_upper_bound != nullptr )) {
@@ -75,11 +170,11 @@ MultiScanIterator& MultiScanIterator::operator++() {
75170 read_options_.iterate_upper_bound = nullptr ;
76171 }
77172 db_iter_.reset (db_->NewIterator (read_options_, cfh_));
78- scan_.Reset (db_iter_.get ());
173+ scan_.Reset (db_iter_.get (), reverse_ );
79174 } else if (scan_opts_[idx_].range .limit ) {
80175 *upper_bound_ = *scan_opts_[idx_].range .limit ;
81176 }
82- db_iter_-> Seek (*scan_opts_[idx_]. range . start );
177+ SeekCurrentRange ( );
83178 status_ = db_iter_->status ();
84179 if (!status_.ok ()) {
85180 throw MultiScanException (status_);
0 commit comments