@@ -1565,11 +1565,115 @@ void DBIter::SetSavedKeyToSeekForPrevTarget(const Slice& target) {
15651565 }
15661566}
15671567
1568+ Status DBIter::ValidateScanOptions (const MultiScanArgs& multiscan_opts) const {
1569+ if (multiscan_opts.empty ()) {
1570+ return Status::InvalidArgument (" Empty MultiScanArgs" );
1571+ }
1572+
1573+ const std::vector<ScanOptions>& scan_opts = multiscan_opts.GetScanRanges ();
1574+ const bool has_limit = scan_opts.front ().range .limit .has_value ();
1575+ if (!has_limit && scan_opts.size () > 1 ) {
1576+ return Status::InvalidArgument (" Scan has no upper bound" );
1577+ }
1578+
1579+ for (size_t i = 0 ; i < scan_opts.size (); ++i) {
1580+ const auto & scan_range = scan_opts[i].range ;
1581+ if (!scan_range.start .has_value ()) {
1582+ return Status::InvalidArgument (" Scan has no start key at index " +
1583+ std::to_string (i));
1584+ }
1585+
1586+ if (scan_range.limit .has_value ()) {
1587+ if (user_comparator_.CompareWithoutTimestamp (
1588+ scan_range.start .value (), /* a_has_ts=*/ false ,
1589+ scan_range.limit .value (), /* b_has_ts=*/ false ) >= 0 ) {
1590+ return Status::InvalidArgument (
1591+ " Scan start key is large or equal than limit at index " +
1592+ std::to_string (i));
1593+ }
1594+ }
1595+
1596+ if (i > 0 ) {
1597+ if (!scan_range.limit .has_value ()) {
1598+ // multiple scan without limit scan ranges
1599+ return Status::InvalidArgument (" Scan has no upper bound at index " +
1600+ std::to_string (i));
1601+ }
1602+
1603+ const auto & last_end_key = scan_opts[i - 1 ].range .limit .value ();
1604+ if (user_comparator_.CompareWithoutTimestamp (
1605+ scan_range.start .value (), /* a_has_ts=*/ false , last_end_key,
1606+ /* b_has_ts=*/ false ) < 0 ) {
1607+ return Status::InvalidArgument (" Overlapping ranges at index " +
1608+ std::to_string (i));
1609+ }
1610+ }
1611+ }
1612+ return Status::OK ();
1613+ }
1614+
1615+ void DBIter::Prepare (const MultiScanArgs& scan_opts) {
1616+ status_ = ValidateScanOptions (scan_opts);
1617+ if (!status_.ok ()) {
1618+ return ;
1619+ }
1620+ std::optional<MultiScanArgs> new_scan_opts;
1621+ new_scan_opts.emplace (scan_opts);
1622+ scan_opts_.swap (new_scan_opts);
1623+ scan_index_ = 0 ;
1624+ if (!scan_opts.empty ()) {
1625+ iter_.Prepare (&scan_opts_.value ());
1626+ } else {
1627+ iter_.Prepare (nullptr );
1628+ }
1629+ }
1630+
15681631void DBIter::Seek (const Slice& target) {
15691632 PERF_COUNTER_ADD (iter_seek_count, 1 );
15701633 PERF_CPU_TIMER_GUARD (iter_seek_cpu_nanos, clock_);
15711634 StopWatch sw (clock_, statistics_, DB_SEEK );
15721635
1636+ if (scan_opts_.has_value ()) {
1637+ // Validate the seek target is as expected in the previously prepared range
1638+ auto const & scan_ranges = scan_opts_.value ().GetScanRanges ();
1639+ if (scan_index_ >= scan_ranges.size ()) {
1640+ status_ = Status::InvalidArgument (
1641+ " Seek called after exhausting all of the scan ranges" );
1642+ valid_ = false ;
1643+ return ;
1644+ }
1645+
1646+ // Validate start key of next prepare range matches the seek target
1647+ auto const & range = scan_ranges[scan_index_];
1648+ auto const & start = range.range .start ;
1649+ assert (start.has_value ());
1650+ if (user_comparator_.CompareWithoutTimestamp (target, *start) != 0 ) {
1651+ status_ = Status::InvalidArgument (
1652+ " Seek target does not match the start of the next prepared range at "
1653+ " index " +
1654+ std::to_string (scan_index_));
1655+ valid_ = false ;
1656+ return ;
1657+ }
1658+
1659+ // validate the upper bound is set to the same value of limit, if limit
1660+ // exists
1661+ auto const & limit = range.range .limit ;
1662+ if (limit.has_value ()) {
1663+ if (iterate_upper_bound_ == nullptr ||
1664+ user_comparator_.CompareWithoutTimestamp (
1665+ limit.value (), *iterate_upper_bound_) != 0 ) {
1666+ status_ = Status::InvalidArgument (
1667+ " Upper bound is not set to the same limit value of the next "
1668+ " prepared range at index " +
1669+ std::to_string (scan_index_));
1670+ valid_ = false ;
1671+ return ;
1672+ }
1673+ }
1674+ scan_index_++;
1675+ }
1676+
15731677 if (cfh_ != nullptr ) {
15741678 // TODO: What do we do if this returns an error?
15751679 Slice lower_bound, upper_bound;
0 commit comments