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 limit at index " + std::to_string (i));
40+ }
41+ if (scan_range.limit .has_value () &&
42+ comparator->CompareWithoutTimestamp (
43+ scan_range.start .value (), /* a_has_ts=*/ false ,
44+ scan_range.limit .value (), /* b_has_ts=*/ false ) >= 0 ) {
45+ return Status::InvalidArgument (
46+ " Scan start key is large or equal than limit at index " +
47+ std::to_string (i));
48+ }
49+ }
50+
51+ return Status::OK ();
52+ }
53+
54+ } // namespace
55+
1456MultiScan::MultiScan (const ReadOptions& read_options,
1557 const MultiScanArgs& scan_opts, DB * db,
1658 ColumnFamilyHandle* cfh)
@@ -28,14 +70,28 @@ MultiScan::MultiScan(const ReadOptions& read_options,
2870 }()),
2971 db_(db),
3072 cfh_(cfh) {
73+ Status validate_status = ValidateScanOptionsForMultiScan (scan_opts_);
74+ if (!validate_status.ok ()) {
75+ db_iter_.reset (NewErrorIterator (validate_status));
76+ return ;
77+ }
78+
3179 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 ;
80+ // Setup read_options bounds based on the first scan. Subsequent scans will
81+ // update the bounds and allocate a new DB iterator as necessary.
82+ {
83+ const ScanOptions& first_scan = scan_opts_.GetScanRanges ()[0 ];
84+ if (scan_opts_.reverse ) {
85+ lower_bound_ = *first_scan.range .start ;
86+ read_options_.iterate_lower_bound = &lower_bound_;
87+ upper_bound_ = *first_scan.range .limit ;
88+ read_options_.iterate_upper_bound = &upper_bound_;
89+ } else if (first_scan.range .limit ) {
90+ upper_bound_ = *first_scan.range .limit ;
91+ read_options_.iterate_upper_bound = &upper_bound_;
92+ } else {
93+ read_options_.iterate_upper_bound = nullptr ;
94+ }
3995 }
4096 for (const auto & opts : scan_opts_.GetScanRanges ()) {
4197 // Check that all the ScanOptions either specify an upper bound or not. If
@@ -54,36 +110,55 @@ MultiScan::MultiScan(const ReadOptions& read_options,
54110 }
55111}
56112
113+ void MultiScanIterator::SeekCurrentRange () {
114+ const ScanOptions& scan_opt = scan_opts_[idx_];
115+ if (scan_opt.range .limit ) {
116+ *upper_bound_ = *scan_opt.range .limit ;
117+ read_options_.iterate_upper_bound = upper_bound_;
118+ } else {
119+ read_options_.iterate_upper_bound = nullptr ;
120+ }
121+
122+ if (reverse_) {
123+ *lower_bound_ = *scan_opt.range .start ;
124+ read_options_.iterate_lower_bound = lower_bound_;
125+ assert (scan_opt.range .limit .has_value ());
126+ db_iter_->SeekForPrev (*scan_opt.range .limit );
127+ } else {
128+ db_iter_->Seek (*scan_opt.range .start );
129+ }
130+ }
131+
57132MultiScanIterator& MultiScanIterator::operator ++() {
58133 status_ = db_iter_->status ();
59134 if (!status_.ok ()) {
60135 throw MultiScanException (status_);
61136 }
62137
138+ if (!valid_) {
139+ throw std::logic_error (" Incrementing exhausted MultiScan iterator" );
140+ }
141+ ++idx_;
63142 if (idx_ >= scan_opts_.size ()) {
64- throw std::logic_error (" Index out of range" );
143+ valid_ = false ;
144+ return *this ;
65145 }
66- idx_++;
67- if (idx_ < scan_opts_.size ()) {
68- // Check if we need to update read_options_
69- if (scan_opts_[idx_].range .limit .has_value () !=
70- (read_options_.iterate_upper_bound != nullptr )) {
71- if (scan_opts_[idx_].range .limit ) {
72- *upper_bound_ = *scan_opts_[idx_].range .limit ;
73- read_options_.iterate_upper_bound = upper_bound_;
74- } else {
75- read_options_.iterate_upper_bound = nullptr ;
76- }
77- db_iter_.reset (db_->NewIterator (read_options_, cfh_));
78- scan_.Reset (db_iter_.get ());
79- } else if (scan_opts_[idx_].range .limit ) {
80- *upper_bound_ = *scan_opts_[idx_].range .limit ;
81- }
82- db_iter_->Seek (*scan_opts_[idx_].range .start );
83- status_ = db_iter_->status ();
84- if (!status_.ok ()) {
85- throw MultiScanException (status_);
146+
147+ // Check if we need to update read_options_
148+ if (scan_opts_[idx_].range .limit .has_value () !=
149+ (read_options_.iterate_upper_bound != nullptr )) {
150+ if (scan_opts_[idx_].range .limit ) {
151+ read_options_.iterate_upper_bound = upper_bound_;
152+ } else {
153+ read_options_.iterate_upper_bound = nullptr ;
86154 }
155+ db_iter_.reset (db_->NewIterator (read_options_, cfh_));
156+ scan_.Reset (db_iter_.get (), reverse_);
157+ }
158+ SeekCurrentRange ();
159+ status_ = db_iter_->status ();
160+ if (!status_.ok ()) {
161+ throw MultiScanException (status_);
87162 }
88163 return *this ;
89164}
0 commit comments