diff options
author | Judah Schvimer <judah@mongodb.com> | 2016-09-15 10:20:59 -0400 |
---|---|---|
committer | Judah Schvimer <judah@mongodb.com> | 2016-09-15 10:20:59 -0400 |
commit | 4dc41d135737dbda0a9ecc70af75687dd5df9099 (patch) | |
tree | 381dcdf024db4da65d20051a430a4f7515c122d6 | |
parent | 97dd4a8eebfb2a2dc2e3a5e0907c8fc6e859c0ac (diff) | |
download | mongo-4dc41d135737dbda0a9ecc70af75687dd5df9099.tar.gz |
SERVER-26033 Allow simple range to exclude start key
50 files changed, 508 insertions, 256 deletions
diff --git a/jstests/concurrency/fsm_workloads/yield_and_hashed.js b/jstests/concurrency/fsm_workloads/yield_and_hashed.js index f9267b9c020..60d73670a1f 100644 --- a/jstests/concurrency/fsm_workloads/yield_and_hashed.js +++ b/jstests/concurrency/fsm_workloads/yield_and_hashed.js @@ -27,6 +27,7 @@ var $config = extendWorkload($config, function($config, $super) { keyPattern: {c: 1}, startKey: {'': nMatches}, endKey: {}, + startKeyInclusive: true, endKeyInclusive: true, direction: -1 } @@ -41,6 +42,7 @@ var $config = extendWorkload($config, function($config, $super) { keyPattern: {d: 1}, startKey: {'': this.nDocs - nMatches}, endKey: {}, + startKeyInclusive: true, endKeyInclusive: true, direction: 1 } diff --git a/jstests/concurrency/fsm_workloads/yield_and_sorted.js b/jstests/concurrency/fsm_workloads/yield_and_sorted.js index aed15988b10..ea077aeed12 100644 --- a/jstests/concurrency/fsm_workloads/yield_and_sorted.js +++ b/jstests/concurrency/fsm_workloads/yield_and_sorted.js @@ -28,6 +28,7 @@ var $config = extendWorkload($config, function($config, $super) { keyPattern: {c: 1}, startKey: {'': 0}, endKey: {'': 0}, + startKeyInclusive: true, endKeyInclusive: false, direction: 1 } @@ -41,6 +42,7 @@ var $config = extendWorkload($config, function($config, $super) { keyPattern: {d: 1}, startKey: {'': this.nDocs}, endKey: {'': this.nDocs}, + startKeyInclusive: true, endKeyInclusive: false, direction: -1 } diff --git a/jstests/core/min_max_bounds.js b/jstests/core/min_max_bounds.js new file mode 100644 index 00000000000..0139ec39851 --- /dev/null +++ b/jstests/core/min_max_bounds.js @@ -0,0 +1,59 @@ +/** + * This test ensures that queries using simple ranges handle bound inclusion properly. + */ +(function() { + 'use strict'; + var coll = db.query_bound_inclusion; + coll.drop(); + assert.writeOK(coll.insert({a: 1, b: 1})); + assert.writeOK(coll.insert({a: 2, b: 2})); + assert.writeOK(coll.insert({a: 3, b: 3})); + + assert.commandWorked(coll.createIndex({a: 1})); + + var res = coll.find().sort({a: 1}).toArray(); + assert.eq(res.length, 3); + assert.eq(res[0].a, 1); + assert.eq(res[1].a, 2); + assert.eq(res[2].a, 3); + + res = coll.find().sort({a: -1}).toArray(); + assert.eq(res.length, 3); + assert.eq(res[0].a, 3); + assert.eq(res[1].a, 2); + assert.eq(res[2].a, 1); + + res = coll.find().min({a: 1}).max({a: 3}).toArray(); + assert.eq(res.length, 2); + assert.eq(res[0].a, 1); + assert.eq(res[1].a, 2); + + res = coll.find().min({a: 1}).max({a: 3}).sort({a: -1}).toArray(); + assert.eq(res.length, 2); + assert.eq(res[0].a, 2); + assert.eq(res[1].a, 1); + + assert.commandWorked(coll.createIndex({b: -1})); + + res = coll.find().sort({b: -1}).toArray(); + assert.eq(res.length, 3); + assert.eq(res[0].b, 3); + assert.eq(res[1].b, 2); + assert.eq(res[2].b, 1); + + res = coll.find().sort({b: 1}).toArray(); + assert.eq(res.length, 3); + assert.eq(res[0].b, 1); + assert.eq(res[1].b, 2); + assert.eq(res[2].b, 3); + + res = coll.find().min({b: 3}).max({b: 1}).toArray(); + assert.eq(res.length, 2); + assert.eq(res[0].b, 3); + assert.eq(res[1].b, 2); + + res = coll.find().min({b: 3}).max({b: 1}).sort({b: 1}).toArray(); + assert.eq(res.length, 2); + assert.eq(res[0].b, 2); + assert.eq(res[1].b, 3); +})();
\ No newline at end of file diff --git a/jstests/core/server9547.js b/jstests/core/server9547.js index 9717893cbfb..56f059d82b3 100644 --- a/jstests/core/server9547.js +++ b/jstests/core/server9547.js @@ -16,6 +16,5 @@ assert.eq(4, t.find({}).max({a: 4}).toArray().length, "no order"); // Ascending order is fine. assert.eq(4, t.find({}).max({a: 4}).sort({a: 1}).toArray().length, "ascending"); -// Descending order is still broken. -// This should really return the same # of results but doesn't. -assert.eq(5, t.find({}).max({a: 4}).sort({a: -1}).toArray().length, "descending"); +// Descending order is fine. +assert.eq(4, t.find({}).max({a: 4}).sort({a: -1}).toArray().length, "descending"); diff --git a/jstests/core/stages_and_hash.js b/jstests/core/stages_and_hash.js index 8dcc8cf1345..14b00a46e73 100644 --- a/jstests/core/stages_and_hash.js +++ b/jstests/core/stages_and_hash.js @@ -20,6 +20,7 @@ ixscan1 = { keyPattern: {foo: 1}, startKey: {"": 20}, endKey: {}, + startKeyInclusive: true, endKeyInclusive: true, direction: -1 } @@ -34,6 +35,7 @@ ixscan2 = { keyPattern: {bar: 1}, startKey: {"": 40}, endKey: {}, + startKeyInclusive: true, endKeyInclusive: true, direction: 1 } diff --git a/jstests/core/stages_and_sorted.js b/jstests/core/stages_and_sorted.js index c29db7ce2eb..50e79ce9ea8 100644 --- a/jstests/core/stages_and_sorted.js +++ b/jstests/core/stages_and_sorted.js @@ -30,6 +30,7 @@ ixscan1 = { keyPattern: {foo: 1}, startKey: {"": 1}, endKey: {"": 1}, + startKeyInclusive: true, endKeyInclusive: true, direction: 1 } @@ -44,6 +45,7 @@ ixscan2 = { keyPattern: {bar: 1}, startKey: {"": 1}, endKey: {"": 1}, + startKeyInclusive: true, endKeyInclusive: true, direction: 1 } @@ -58,6 +60,7 @@ ixscan3 = { keyPattern: {baz: 1}, startKey: {"": 12}, endKey: {"": 12}, + startKeyInclusive: true, endKeyInclusive: true, direction: 1 } diff --git a/jstests/core/stages_fetch.js b/jstests/core/stages_fetch.js index 7adc52c67c5..02dffa929f9 100644 --- a/jstests/core/stages_fetch.js +++ b/jstests/core/stages_fetch.js @@ -18,6 +18,7 @@ ixscan1 = { keyPattern: {foo: 1}, startKey: {"": 20}, endKey: {"": 30}, + startKeyInclusive: true, endKeyInclusive: true, direction: 1 }, @@ -34,6 +35,7 @@ ixscan2 = { keyPattern: {foo: 1}, startKey: {"": 20}, endKey: {"": 30}, + startKeyInclusive: true, endKeyInclusive: true, direction: 1 } diff --git a/jstests/core/stages_ixscan.js b/jstests/core/stages_ixscan.js index fb7e8fd54c9..09c5480bd67 100644 --- a/jstests/core/stages_ixscan.js +++ b/jstests/core/stages_ixscan.js @@ -16,8 +16,17 @@ assert.commandFailed(db.runCommand({ stageDebug: { collection: collname, plan: { - ixscan: - {args: {startKey: {"": 20, endKey: {}, endKeyInclusive: true, direction: -1}}} + ixscan: { + args: { + startKey: { + "": 20, + endKey: {}, + startKeyInclusive: true, + endKeyInclusive: true, + direction: -1 + } + } + } } } })); @@ -29,6 +38,7 @@ ixscan1 = { keyPattern: {foo: 1}, startKey: {"": 20}, endKey: {}, + startKeyInclusive: true, endKeyInclusive: true, direction: -1 } @@ -45,6 +55,7 @@ ixscan1 = { keyPattern: {foo: 1}, startKey: {"": 20}, endKey: {"": 30}, + startKeyInclusive: true, endKeyInclusive: false, direction: 1 } @@ -61,6 +72,7 @@ ixscan1 = { keyPattern: {foo: 1}, startKey: {"": 20}, endKey: {"": 30}, + startKeyInclusive: true, endKeyInclusive: true, direction: 1 } @@ -78,6 +90,7 @@ ixscan1 = { keyPattern: {foo: 1}, startKey: {"": 20}, endKey: {"": 30}, + startKeyInclusive: true, endKeyInclusive: true, direction: 1 }, @@ -96,6 +109,7 @@ ixscan1 = { keyPattern: {foo: 1, baz: 1}, startKey: {foo: 20, baz: MinKey}, endKey: {foo: 30, baz: MaxKey}, + startKeyInclusive: true, endKeyInclusive: true, direction: 1 }, @@ -114,6 +128,7 @@ ixscan1 = { keyPattern: {foo: 1, baz: 1}, startKey: {foo: 20, baz: MinKey}, endKey: {foo: 30, baz: MaxKey}, + startKeyInclusive: true, endKeyInclusive: true, direction: 1 }, @@ -137,6 +152,7 @@ var ixscanAmbiguous = { keyPattern: {a: 1}, startKey: {a: 1}, endKey: {a: 2}, + startKeyInclusive: true, endKeyInclusive: true, direction: 1 }, @@ -153,6 +169,7 @@ var ixscanName = { name: "numeric", startKey: {a: ""}, endKey: {a: {}}, // All strings + startKeyInclusive: true, endKeyInclusive: false, direction: 1 }, diff --git a/jstests/core/stages_limit_skip.js b/jstests/core/stages_limit_skip.js index c582cb6b1e4..7cd98c1c931 100644 --- a/jstests/core/stages_limit_skip.js +++ b/jstests/core/stages_limit_skip.js @@ -18,6 +18,7 @@ ixscan1 = { keyPattern: {foo: 1}, startKey: {"": 20}, endKey: {}, + startKeyInclusive: true, endKeyInclusive: true, direction: -1 } diff --git a/jstests/core/stages_mergesort.js b/jstests/core/stages_mergesort.js index 5156743078c..4ebda2547c4 100644 --- a/jstests/core/stages_mergesort.js +++ b/jstests/core/stages_mergesort.js @@ -20,6 +20,7 @@ ixscan1 = { keyPattern: {foo: 1, bar: 1}, startKey: {foo: 1, bar: 0}, endKey: {foo: 1, bar: 100000}, + startKeyInclusive: true, endKeyInclusive: true, direction: 1 } @@ -32,6 +33,7 @@ ixscan2 = { keyPattern: {baz: 1, bar: 1}, startKey: {baz: 1, bar: 0}, endKey: {baz: 1, bar: 100000}, + startKeyInclusive: true, endKeyInclusive: true, direction: 1 } diff --git a/jstests/core/stages_or.js b/jstests/core/stages_or.js index 6ea73efd1ed..e8616fcbcaa 100644 --- a/jstests/core/stages_or.js +++ b/jstests/core/stages_or.js @@ -19,6 +19,7 @@ ixscan1 = { keyPattern: {baz: 1}, startKey: {"": 40}, endKey: {}, + startKeyInclusive: true, endKeyInclusive: true, direction: 1 } @@ -31,6 +32,7 @@ ixscan2 = { keyPattern: {foo: 1}, startKey: {"": 40}, endKey: {}, + startKeyInclusive: true, endKeyInclusive: true, direction: 1 } diff --git a/jstests/core/stages_sort.js b/jstests/core/stages_sort.js index 6d25279fa27..46b81662100 100644 --- a/jstests/core/stages_sort.js +++ b/jstests/core/stages_sort.js @@ -18,6 +18,7 @@ if (false) { keyPattern: {foo: 1}, startKey: {"": 20}, endKey: {}, + startKeyInclusive: true, endKeyInclusive: true, direction: -1 } diff --git a/src/mongo/db/commands/dbcommands.cpp b/src/mongo/db/commands/dbcommands.cpp index 3e8ce384741..9720775b0cf 100644 --- a/src/mongo/db/commands/dbcommands.cpp +++ b/src/mongo/db/commands/dbcommands.cpp @@ -834,7 +834,7 @@ public: idx, min, max, - false, // endKeyInclusive + BoundInclusion::kIncludeStartKeyOnly, PlanExecutor::YIELD_MANUAL); } diff --git a/src/mongo/db/commands/dbhash.cpp b/src/mongo/db/commands/dbhash.cpp index 045376922a8..acfa7435c72 100644 --- a/src/mongo/db/commands/dbhash.cpp +++ b/src/mongo/db/commands/dbhash.cpp @@ -224,7 +224,7 @@ private: desc, BSONObj(), BSONObj(), - false, // endKeyInclusive + BoundInclusion::kIncludeStartKeyOnly, PlanExecutor::YIELD_MANUAL, InternalPlanner::FORWARD, InternalPlanner::IXSCAN_FETCH); diff --git a/src/mongo/db/dbhelpers.cpp b/src/mongo/db/dbhelpers.cpp index 53e73c11119..82fb612ddf8 100644 --- a/src/mongo/db/dbhelpers.cpp +++ b/src/mongo/db/dbhelpers.cpp @@ -295,7 +295,7 @@ BSONObj Helpers::inferKeyPattern(const BSONObj& o) { long long Helpers::removeRange(OperationContext* txn, const KeyRange& range, - bool maxInclusive, + BoundInclusion boundInclusion, const WriteConcernOptions& writeConcern, RemoveSaver* callback, bool fromMigrate, @@ -337,10 +337,12 @@ long long Helpers::removeRange(OperationContext* txn, // Extend bounds to match the index we found + invariant(IndexBounds::isStartIncludedInBound(boundInclusion)); // Extend min to get (min, MinKey, MinKey, ....) min = Helpers::toKeyFormat(indexKeyPattern.extendRangeBound(range.minKey, false)); // If upper bound is included, extend max to get (max, MaxKey, MaxKey, ...) // If not included, extend max to get (max, MinKey, MinKey, ....) + const bool maxInclusive = IndexBounds::isEndIncludedInBound(boundInclusion); max = Helpers::toKeyFormat(indexKeyPattern.extendRangeBound(range.maxKey, maxInclusive)); } @@ -369,7 +371,7 @@ long long Helpers::removeRange(OperationContext* txn, desc, min, max, - maxInclusive, + boundInclusion, PlanExecutor::YIELD_MANUAL, InternalPlanner::FORWARD, InternalPlanner::IXSCAN_FETCH)); diff --git a/src/mongo/db/dbhelpers.h b/src/mongo/db/dbhelpers.h index f3acb9f190c..bb303a681c4 100644 --- a/src/mongo/db/dbhelpers.h +++ b/src/mongo/db/dbhelpers.h @@ -173,7 +173,7 @@ struct Helpers { */ static long long removeRange(OperationContext* txn, const KeyRange& range, - bool maxInclusive, + BoundInclusion boundInclusion, const WriteConcernOptions& secondaryThrottle, RemoveSaver* callback = NULL, bool fromMigrate = false, diff --git a/src/mongo/db/exec/geo_near.cpp b/src/mongo/db/exec/geo_near.cpp index 7e7d00fc8ec..56b10994c3c 100644 --- a/src/mongo/db/exec/geo_near.cpp +++ b/src/mongo/db/exec/geo_near.cpp @@ -331,7 +331,8 @@ void GeoNear2DStage::DensityEstimator::buildIndexScan(OperationContext* txn, mongo::BSONObjBuilder builder; it->appendHashMin(&builder, ""); it->appendHashMax(&builder, ""); - oil.intervals.push_back(IndexBoundsBuilder::makeRangeInterval(builder.obj(), true, true)); + oil.intervals.push_back(IndexBoundsBuilder::makeRangeInterval( + builder.obj(), BoundInclusion::kIncludeBothStartAndEndKeys)); } invariant(oil.isValidFor(1)); diff --git a/src/mongo/db/exec/index_scan.cpp b/src/mongo/db/exec/index_scan.cpp index 7e0cf847dc3..767539e65b4 100644 --- a/src/mongo/db/exec/index_scan.cpp +++ b/src/mongo/db/exec/index_scan.cpp @@ -71,7 +71,8 @@ IndexScan::IndexScan(OperationContext* txn, _shouldDedup(true), _forward(params.direction == 1), _params(params), - _endKeyInclusive(false) { + _startKeyInclusive(IndexBounds::isStartIncludedInBound(params.bounds.boundInclusion)), + _endKeyInclusive(IndexBounds::isEndIncludedInBound(params.bounds.boundInclusion)) { // We can't always access the descriptor in the call to getStats() so we pull // any info we need for stats reporting out here. _specificStats.keyPattern = _keyPattern; @@ -104,20 +105,18 @@ boost::optional<IndexKeyEntry> IndexScan::initIndexScan() { if (_params.bounds.isSimpleRange) { // Start at one key, end at another. + _startKey = _params.bounds.startKey; _endKey = _params.bounds.endKey; - _endKeyInclusive = _params.bounds.endKeyInclusive; _indexCursor->setEndPosition(_endKey, _endKeyInclusive); - return _indexCursor->seek(_params.bounds.startKey, /*inclusive*/ true); + return _indexCursor->seek(_startKey, _startKeyInclusive); } else { // For single intervals, we can use an optimized scan which checks against the position // of an end cursor. For all other index scans, we fall back on using // IndexBoundsChecker to determine when we've finished the scan. - BSONObj startKey; - bool startKeyInclusive; if (IndexBoundsBuilder::isSingleInterval( - _params.bounds, &startKey, &startKeyInclusive, &_endKey, &_endKeyInclusive)) { + _params.bounds, &_startKey, &_startKeyInclusive, &_endKey, &_endKeyInclusive)) { _indexCursor->setEndPosition(_endKey, _endKeyInclusive); - return _indexCursor->seek(startKey, startKeyInclusive); + return _indexCursor->seek(_startKey, _startKeyInclusive); } else { _checker.reset(new IndexBoundsChecker(&_params.bounds, _keyPattern, _params.direction)); @@ -154,6 +153,15 @@ PlanStage::StageState IndexScan::doWork(WorkingSetID* out) { if (kv) { // In debug mode, check that the cursor isn't lying to us. + if (kDebugBuild && !_startKey.isEmpty()) { + int cmp = kv->key.woCompare(_startKey, + Ordering::make(_params.descriptor->keyPattern()), + /*compareFieldNames*/ false); + if (cmp == 0) + dassert(_startKeyInclusive); + dassert(_forward ? cmp >= 0 : cmp <= 0); + } + if (kDebugBuild && !_endKey.isEmpty()) { int cmp = kv->key.woCompare(_endKey, Ordering::make(_params.descriptor->keyPattern()), diff --git a/src/mongo/db/exec/index_scan.h b/src/mongo/db/exec/index_scan.h index c6f43992d47..1cf2780f24b 100644 --- a/src/mongo/db/exec/index_scan.h +++ b/src/mongo/db/exec/index_scan.h @@ -165,9 +165,13 @@ private: // BSON compares against scanned keys. In this case _checker will be NULL. // + // The key that the index cursor should start on/after. + BSONObj _startKey; // The key that the index cursor should stop on/after. BSONObj _endKey; + // Is the start key included in the range? + bool _startKeyInclusive; // Is the end key included in the range? bool _endKeyInclusive; }; diff --git a/src/mongo/db/exec/stagedebug_cmd.cpp b/src/mongo/db/exec/stagedebug_cmd.cpp index 13da0300c94..926cca4c485 100644 --- a/src/mongo/db/exec/stagedebug_cmd.cpp +++ b/src/mongo/db/exec/stagedebug_cmd.cpp @@ -306,7 +306,8 @@ public: params.bounds.isSimpleRange = true; params.bounds.startKey = stripFieldNames(nodeArgs["startKey"].Obj()); params.bounds.endKey = stripFieldNames(nodeArgs["endKey"].Obj()); - params.bounds.endKeyInclusive = nodeArgs["endKeyInclusive"].Bool(); + params.bounds.boundInclusion = IndexBounds::makeBoundInclusionFromBoundBools( + nodeArgs["startKeyInclusive"].Bool(), nodeArgs["endKeyInclusive"].Bool()); params.direction = nodeArgs["direction"].numberInt(); return new IndexScan(txn, params, workingSet, matcher); diff --git a/src/mongo/db/exec/text.cpp b/src/mongo/db/exec/text.cpp index ddbcc1d9a46..a290b8f3095 100644 --- a/src/mongo/db/exec/text.cpp +++ b/src/mongo/db/exec/text.cpp @@ -105,7 +105,7 @@ unique_ptr<PlanStage> TextStage::buildTextTree(OperationContext* txn, MAX_WEIGHT, term, _params.indexPrefix, _params.spec.getTextIndexVersion()); ixparams.bounds.endKey = FTSIndexFormat::getIndexKey( 0, term, _params.indexPrefix, _params.spec.getTextIndexVersion()); - ixparams.bounds.endKeyInclusive = true; + ixparams.bounds.boundInclusion = BoundInclusion::kIncludeBothStartAndEndKeys; ixparams.bounds.isSimpleRange = true; ixparams.descriptor = _params.index; ixparams.direction = -1; diff --git a/src/mongo/db/index/haystack_access_method.cpp b/src/mongo/db/index/haystack_access_method.cpp index 95822b21dc8..3322a6f0aa9 100644 --- a/src/mongo/db/index/haystack_access_method.cpp +++ b/src/mongo/db/index/haystack_access_method.cpp @@ -110,13 +110,14 @@ void HaystackAccessMethod::searchCommand(OperationContext* txn, unordered_set<RecordId, RecordId::Hasher> thisPass; - unique_ptr<PlanExecutor> exec(InternalPlanner::indexScan(txn, - collection, - _descriptor, - key, - key, - true, // endKeyInclusive - PlanExecutor::YIELD_MANUAL)); + unique_ptr<PlanExecutor> exec( + InternalPlanner::indexScan(txn, + collection, + _descriptor, + key, + key, + BoundInclusion::kIncludeBothStartAndEndKeys, + PlanExecutor::YIELD_MANUAL)); PlanExecutor::ExecState state; BSONObj obj; RecordId loc; diff --git a/src/mongo/db/query/expression_index.cpp b/src/mongo/db/query/expression_index.cpp index 45617f35a89..f2e1439610a 100644 --- a/src/mongo/db/query/expression_index.cpp +++ b/src/mongo/db/query/expression_index.cpp @@ -95,8 +95,8 @@ void ExpressionMapping::GeoHashsToIntervalsWithParents( geoHash.appendHashMin(&builder, ""); geoHash.appendHashMax(&builder, ""); - oilOut->intervals.push_back( - IndexBoundsBuilder::makeRangeInterval(builder.obj(), true, true)); + oilOut->intervals.push_back(IndexBoundsBuilder::makeRangeInterval( + builder.obj(), BoundInclusion::kIncludeBothStartAndEndKeys)); } } @@ -152,7 +152,8 @@ void S2CellIdsToIntervalsUnsorted(const std::vector<S2CellId>& intervalSet, b.append("start", start); b.append("end", end); invariant(start <= end); - oilOut->intervals.push_back(IndexBoundsBuilder::makeRangeInterval(b.obj(), true, true)); + oilOut->intervals.push_back(IndexBoundsBuilder::makeRangeInterval( + b.obj(), BoundInclusion::kIncludeBothStartAndEndKeys)); } else { // for backwards compatibility, use strings std::string start = interval.toString(); @@ -160,8 +161,8 @@ void S2CellIdsToIntervalsUnsorted(const std::vector<S2CellId>& intervalSet, end[start.size() - 1]++; b.append("start", start); b.append("end", end); - oilOut->intervals.push_back( - IndexBoundsBuilder::makeRangeInterval(b.obj(), true, false)); + oilOut->intervals.push_back(IndexBoundsBuilder::makeRangeInterval( + b.obj(), BoundInclusion::kIncludeStartKeyOnly)); } } } diff --git a/src/mongo/db/query/index_bounds.cpp b/src/mongo/db/query/index_bounds.cpp index 065d53ba510..ef8eeb6d8b8 100644 --- a/src/mongo/db/query/index_bounds.cpp +++ b/src/mongo/db/query/index_bounds.cpp @@ -104,7 +104,7 @@ bool IndexBounds::operator==(const IndexBounds& other) const { if (this->isSimpleRange) { return SimpleBSONObjComparator::kInstance.evaluate(this->startKey == other.startKey) && SimpleBSONObjComparator::kInstance.evaluate(this->endKey == other.endKey) && - (this->endKeyInclusive == other.endKeyInclusive); + (this->boundInclusion == other.boundInclusion); } if (this->fields.size() != other.fields.size()) { @@ -136,6 +136,34 @@ string OrderedIntervalList::toString() const { return ss; } +bool IndexBounds::isStartIncludedInBound(BoundInclusion boundInclusion) { + return boundInclusion == BoundInclusion::kIncludeBothStartAndEndKeys || + boundInclusion == BoundInclusion::kIncludeStartKeyOnly; +} + +bool IndexBounds::isEndIncludedInBound(BoundInclusion boundInclusion) { + return boundInclusion == BoundInclusion::kIncludeBothStartAndEndKeys || + boundInclusion == BoundInclusion::kIncludeEndKeyOnly; +} + +BoundInclusion IndexBounds::makeBoundInclusionFromBoundBools(bool startKeyInclusive, + bool endKeyInclusive) { + if (startKeyInclusive) { + if (endKeyInclusive) { + return BoundInclusion::kIncludeBothStartAndEndKeys; + } else { + return BoundInclusion::kIncludeStartKeyOnly; + } + } else { + if (endKeyInclusive) { + return BoundInclusion::kIncludeEndKeyOnly; + } else { + return BoundInclusion::kExcludeBothStartAndEndKeys; + } + } +} + + bool OrderedIntervalList::operator==(const OrderedIntervalList& other) const { if (this->name != other.name) { return false; @@ -216,12 +244,17 @@ void OrderedIntervalList::complement() { string IndexBounds::toString() const { mongoutils::str::stream ss; if (isSimpleRange) { - ss << "[" << startKey.toString() << ", "; + if (IndexBounds::isStartIncludedInBound(boundInclusion)) { + ss << "["; + } else { + ss << "("; + } + ss << startKey.toString() << ", "; if (endKey.isEmpty()) { ss << "]"; } else { ss << endKey.toString(); - if (endKeyInclusive) { + if (IndexBounds::isEndIncludedInBound(boundInclusion)) { ss << "]"; } else { ss << ")"; diff --git a/src/mongo/db/query/index_bounds.h b/src/mongo/db/query/index_bounds.h index e3fabde17b7..2dfcc122b26 100644 --- a/src/mongo/db/query/index_bounds.h +++ b/src/mongo/db/query/index_bounds.h @@ -37,6 +37,13 @@ namespace mongo { +enum class BoundInclusion { + kExcludeBothStartAndEndKeys, + kIncludeStartKeyOnly, + kIncludeEndKeyOnly, + kIncludeBothStartAndEndKeys +}; + /** * An ordered list of intervals for one field. */ @@ -74,7 +81,7 @@ struct OrderedIntervalList { * interpret. Previously known as FieldRangeVector. */ struct IndexBounds { - IndexBounds() : isSimpleRange(false), endKeyInclusive(false) {} + IndexBounds() : isSimpleRange(false), boundInclusion(BoundInclusion::kIncludeStartKeyOnly) {} // For each indexed field, the values that the field is allowed to take on. std::vector<OrderedIntervalList> fields; @@ -101,6 +108,26 @@ struct IndexBounds { bool operator!=(const IndexBounds& other) const; /** + * Returns if the start key should be included in the bounds specified by the given + * BoundInclusion. + */ + static bool isStartIncludedInBound(BoundInclusion boundInclusion); + + /** + * Returns if the end key should be included in the bounds specified by the given + * BoundInclusion. + */ + static bool isEndIncludedInBound(BoundInclusion boundInclusion); + + /** + * Returns a BoundInclusion given two booleans of whether to included the start key and the end + * key. + */ + static BoundInclusion makeBoundInclusionFromBoundBools(bool startKeyInclusive, + bool endKeyInclusive); + + + /** * BSON format for explain. The format is an array of strings for each field. * Each string represents an interval. The strings use "[" and "]" if the interval * bounds are inclusive, and "(" / ")" if exclusive. @@ -114,7 +141,7 @@ struct IndexBounds { bool isSimpleRange; BSONObj startKey; BSONObj endKey; - bool endKeyInclusive; + BoundInclusion boundInclusion; }; /** diff --git a/src/mongo/db/query/index_bounds_builder.cpp b/src/mongo/db/query/index_bounds_builder.cpp index 21e56b4f42b..2268e3ef30c 100644 --- a/src/mongo/db/query/index_bounds_builder.cpp +++ b/src/mongo/db/query/index_bounds_builder.cpp @@ -186,14 +186,15 @@ void IndexBoundsBuilder::allValuesForField(const BSONElement& elt, OrderedInterv bob.appendMinKey(""); bob.appendMaxKey(""); out->name = elt.fieldName(); - out->intervals.push_back(makeRangeInterval(bob.obj(), true, true)); + out->intervals.push_back( + makeRangeInterval(bob.obj(), BoundInclusion::kIncludeBothStartAndEndKeys)); } Interval IndexBoundsBuilder::allValues() { BSONObjBuilder bob; bob.appendMinKey(""); bob.appendMaxKey(""); - return makeRangeInterval(bob.obj(), true, true); + return makeRangeInterval(bob.obj(), BoundInclusion::kIncludeBothStartAndEndKeys); } bool IntervalComparison(const Interval& lhs, const Interval& rhs) { @@ -310,7 +311,8 @@ void IndexBoundsBuilder::translate(const MatchExpression* expr, bob.appendNull(""); bob.appendNull(""); BSONObj dataObj = bob.obj(); - oilOut->intervals.push_back(makeRangeInterval(dataObj, true, true)); + oilOut->intervals.push_back( + makeRangeInterval(dataObj, BoundInclusion::kIncludeBothStartAndEndKeys)); *tightnessOut = IndexBoundsBuilder::INEXACT_FETCH; return; @@ -393,7 +395,8 @@ void IndexBoundsBuilder::translate(const MatchExpression* expr, CollationIndexKey::collationAwareIndexKeyAppend(dataElt, index.collator, &bob); BSONObj dataObj = bob.obj(); verify(dataObj.isOwned()); - oilOut->intervals.push_back(makeRangeInterval(dataObj, typeMatch(dataObj), true)); + oilOut->intervals.push_back(makeRangeInterval( + dataObj, IndexBounds::makeBoundInclusionFromBoundBools(typeMatch(dataObj), true))); *tightnessOut = getInequalityPredicateTightness(dataElt, index); } else if (MatchExpression::LT == expr->matchType()) { @@ -424,7 +427,8 @@ void IndexBoundsBuilder::translate(const MatchExpression* expr, CollationIndexKey::collationAwareIndexKeyAppend(dataElt, index.collator, &bob); BSONObj dataObj = bob.obj(); verify(dataObj.isOwned()); - Interval interval = makeRangeInterval(dataObj, typeMatch(dataObj), false); + Interval interval = makeRangeInterval( + dataObj, IndexBounds::makeBoundInclusionFromBoundBools(typeMatch(dataObj), false)); // If the operand to LT is equal to the lower bound X, the interval [X, X) is invalid // and should not be added to the bounds. @@ -460,7 +464,8 @@ void IndexBoundsBuilder::translate(const MatchExpression* expr, } BSONObj dataObj = bob.obj(); verify(dataObj.isOwned()); - Interval interval = makeRangeInterval(dataObj, false, typeMatch(dataObj)); + Interval interval = makeRangeInterval( + dataObj, IndexBounds::makeBoundInclusionFromBoundBools(false, typeMatch(dataObj))); // If the operand to GT is equal to the upper bound X, the interval (X, X] is invalid // and should not be added to the bounds. @@ -499,7 +504,8 @@ void IndexBoundsBuilder::translate(const MatchExpression* expr, BSONObj dataObj = bob.obj(); verify(dataObj.isOwned()); - oilOut->intervals.push_back(makeRangeInterval(dataObj, true, typeMatch(dataObj))); + oilOut->intervals.push_back(makeRangeInterval( + dataObj, IndexBounds::makeBoundInclusionFromBoundBools(true, typeMatch(dataObj)))); *tightnessOut = getInequalityPredicateTightness(dataElt, index); } else if (MatchExpression::REGEX == expr->matchType()) { @@ -511,7 +517,8 @@ void IndexBoundsBuilder::translate(const MatchExpression* expr, bob.appendMaxForType("", NumberDouble); BSONObj dataObj = bob.obj(); verify(dataObj.isOwned()); - oilOut->intervals.push_back(makeRangeInterval(dataObj, true, true)); + oilOut->intervals.push_back( + makeRangeInterval(dataObj, BoundInclusion::kIncludeBothStartAndEndKeys)); *tightnessOut = IndexBoundsBuilder::INEXACT_COVERED; } else if (MatchExpression::TYPE_OPERATOR == expr->matchType()) { const TypeMatchExpression* tme = static_cast<const TypeMatchExpression*>(expr); @@ -524,7 +531,8 @@ void IndexBoundsBuilder::translate(const MatchExpression* expr, bob.appendMaxForType("", type); BSONObj dataObj = bob.obj(); verify(dataObj.isOwned()); - oilOut->intervals.push_back(makeRangeInterval(dataObj, true, true)); + oilOut->intervals.push_back( + makeRangeInterval(dataObj, BoundInclusion::kIncludeBothStartAndEndKeys)); *tightnessOut = tme->matchesAllNumbers() ? IndexBoundsBuilder::EXACT : IndexBoundsBuilder::INEXACT_FETCH; @@ -597,13 +605,11 @@ void IndexBoundsBuilder::translate(const MatchExpression* expr, } // static -Interval IndexBoundsBuilder::makeRangeInterval(const BSONObj& obj, - bool startInclusive, - bool endInclusive) { +Interval IndexBoundsBuilder::makeRangeInterval(const BSONObj& obj, BoundInclusion boundInclusion) { Interval ret; ret._intervalData = obj; - ret.startInclusive = startInclusive; - ret.endInclusive = endInclusive; + ret.startInclusive = IndexBounds::isStartIncludedInBound(boundInclusion); + ret.endInclusive = IndexBounds::isEndIncludedInBound(boundInclusion); BSONObjIterator it(obj); verify(it.more()); ret.start = it.next(); @@ -705,7 +711,8 @@ void IndexBoundsBuilder::unionize(OrderedIntervalList* oilOut) { bool endInclusive = iv[i + 1].endInclusive; iv.erase(iv.begin() + i); // iv[i] is now the former iv[i + 1] - iv[i] = makeRangeInterval(data, startInclusive, endInclusive); + iv[i] = makeRangeInterval( + data, IndexBounds::makeBoundInclusionFromBoundBools(startInclusive, endInclusive)); // Don't increment 'i'. } } @@ -714,12 +721,11 @@ void IndexBoundsBuilder::unionize(OrderedIntervalList* oilOut) { // static Interval IndexBoundsBuilder::makeRangeInterval(const string& start, const string& end, - bool startInclusive, - bool endInclusive) { + BoundInclusion boundInclusion) { BSONObjBuilder bob; bob.append("", start); bob.append("", end); - return makeRangeInterval(bob.obj(), startInclusive, endInclusive); + return makeRangeInterval(bob.obj(), boundInclusion); } // static @@ -776,14 +782,16 @@ void IndexBoundsBuilder::translateRegex(const RegexMatchExpression* rme, if (!start.empty()) { string end = start; end[end.size() - 1]++; - oilOut->intervals.push_back(makeRangeInterval(start, end, true, false)); + oilOut->intervals.push_back( + makeRangeInterval(start, end, BoundInclusion::kIncludeStartKeyOnly)); } else { BSONObjBuilder bob; bob.appendMinForType("", String); bob.appendMaxForType("", String); BSONObj dataObj = bob.obj(); verify(dataObj.isOwned()); - oilOut->intervals.push_back(makeRangeInterval(dataObj, true, false)); + oilOut->intervals.push_back( + makeRangeInterval(dataObj, BoundInclusion::kIncludeStartKeyOnly)); } // Regexes are after strings. diff --git a/src/mongo/db/query/index_bounds_builder.h b/src/mongo/db/query/index_bounds_builder.h index 5d3c02e029f..b397f81b295 100644 --- a/src/mongo/db/query/index_bounds_builder.h +++ b/src/mongo/db/query/index_bounds_builder.h @@ -108,15 +108,14 @@ public: * Make a range interval from the provided object. * The object must have exactly two fields. The first field is the start, the second the * end. - * The two inclusive flags indicate whether or not the start/end fields are included in the + * The BoundInclusion indicates whether or not the start/end fields are included in the * interval (closed interval if included, open if not). */ - static Interval makeRangeInterval(const BSONObj& obj, bool startInclusive, bool endInclusive); + static Interval makeRangeInterval(const BSONObj& obj, BoundInclusion boundInclusion); static Interval makeRangeInterval(const std::string& start, const std::string& end, - bool startInclusive, - bool endInclusive); + BoundInclusion boundInclusion); /** * Make a point interval from the provided object. diff --git a/src/mongo/db/query/index_bounds_test.cpp b/src/mongo/db/query/index_bounds_test.cpp index 250563b54ce..1fca089e584 100644 --- a/src/mongo/db/query/index_bounds_test.cpp +++ b/src/mongo/db/query/index_bounds_test.cpp @@ -298,13 +298,13 @@ TEST(IndexBoundsTest, SimpleRangeBoundsEqual) { bounds1.isSimpleRange = true; bounds1.startKey = BSON("" << 1 << "" << 3); bounds1.endKey = BSON("" << 2 << "" << 4); - bounds1.endKeyInclusive = false; + bounds1.boundInclusion = BoundInclusion::kIncludeStartKeyOnly; IndexBounds bounds2; bounds2.isSimpleRange = true; bounds2.startKey = BSON("" << 1 << "" << 3); bounds2.endKey = BSON("" << 2 << "" << 4); - bounds2.endKeyInclusive = false; + bounds2.boundInclusion = BoundInclusion::kIncludeStartKeyOnly; ASSERT_TRUE(bounds1 == bounds2); ASSERT_FALSE(bounds1 != bounds2); @@ -457,7 +457,7 @@ TEST(IndexBoundsTest, SimpleRangeBoundsNotEqualToRegularBounds) { bounds1.isSimpleRange = true; bounds1.startKey = BSON("" << 1 << "" << 3); bounds1.endKey = BSON("" << 2 << "" << 4); - bounds1.endKeyInclusive = false; + bounds1.boundInclusion = BoundInclusion::kIncludeStartKeyOnly; IndexBounds bounds2; OrderedIntervalList oil; @@ -473,13 +473,13 @@ TEST(IndexBoundsTest, SimpleRangeBoundsNotEqualDifferentStartKey) { bounds1.isSimpleRange = true; bounds1.startKey = BSON("" << 1 << "" << 3); bounds1.endKey = BSON("" << 2 << "" << 4); - bounds1.endKeyInclusive = false; + bounds1.boundInclusion = BoundInclusion::kIncludeStartKeyOnly; IndexBounds bounds2; bounds1.isSimpleRange = true; bounds1.startKey = BSON("" << 1 << "" << 1); bounds1.endKey = BSON("" << 2 << "" << 4); - bounds1.endKeyInclusive = false; + bounds1.boundInclusion = BoundInclusion::kIncludeStartKeyOnly; ASSERT_FALSE(bounds1 == bounds2); ASSERT_TRUE(bounds1 != bounds2); @@ -490,13 +490,13 @@ TEST(IndexBoundsTest, SimpleRangeBoundsNotEqualDifferentEndKey) { bounds1.isSimpleRange = true; bounds1.startKey = BSON("" << 1 << "" << 3); bounds1.endKey = BSON("" << 2 << "" << 4); - bounds1.endKeyInclusive = false; + bounds1.boundInclusion = BoundInclusion::kIncludeStartKeyOnly; IndexBounds bounds2; bounds1.isSimpleRange = true; bounds1.startKey = BSON("" << 1 << "" << 3); bounds1.endKey = BSON("" << 2 << "" << 99); - bounds1.endKeyInclusive = false; + bounds1.boundInclusion = BoundInclusion::kIncludeStartKeyOnly; ASSERT_FALSE(bounds1 == bounds2); ASSERT_TRUE(bounds1 != bounds2); @@ -507,13 +507,13 @@ TEST(IndexBoundsTest, SimpleRangeBoundsNotEqualDifferentEndKeyInclusive) { bounds1.isSimpleRange = true; bounds1.startKey = BSON("" << 1 << "" << 3); bounds1.endKey = BSON("" << 2 << "" << 4); - bounds1.endKeyInclusive = false; + bounds1.boundInclusion = BoundInclusion::kIncludeStartKeyOnly; IndexBounds bounds2; bounds1.isSimpleRange = true; bounds1.startKey = BSON("" << 1 << "" << 3); bounds1.endKey = BSON("" << 2 << "" << 4); - bounds1.endKeyInclusive = true; + bounds1.boundInclusion = BoundInclusion::kIncludeBothStartAndEndKeys; ASSERT_FALSE(bounds1 == bounds2); ASSERT_TRUE(bounds1 != bounds2); diff --git a/src/mongo/db/query/internal_plans.cpp b/src/mongo/db/query/internal_plans.cpp index b0ebdcd1096..3889e82031b 100644 --- a/src/mongo/db/query/internal_plans.cpp +++ b/src/mongo/db/query/internal_plans.cpp @@ -94,7 +94,7 @@ std::unique_ptr<PlanExecutor> InternalPlanner::indexScan(OperationContext* txn, const IndexDescriptor* descriptor, const BSONObj& startKey, const BSONObj& endKey, - bool endKeyInclusive, + BoundInclusion boundInclusion, PlanExecutor::YieldPolicy yieldPolicy, Direction direction, int options) { @@ -106,7 +106,7 @@ std::unique_ptr<PlanExecutor> InternalPlanner::indexScan(OperationContext* txn, descriptor, startKey, endKey, - endKeyInclusive, + boundInclusion, direction, options); @@ -123,7 +123,7 @@ std::unique_ptr<PlanExecutor> InternalPlanner::deleteWithIndexScan( const IndexDescriptor* descriptor, const BSONObj& startKey, const BSONObj& endKey, - bool endKeyInclusive, + BoundInclusion boundInclusion, PlanExecutor::YieldPolicy yieldPolicy, Direction direction) { auto ws = stdx::make_unique<WorkingSet>(); @@ -134,7 +134,7 @@ std::unique_ptr<PlanExecutor> InternalPlanner::deleteWithIndexScan( descriptor, startKey, endKey, - endKeyInclusive, + boundInclusion, direction, InternalPlanner::IXSCAN_FETCH); @@ -172,7 +172,7 @@ std::unique_ptr<PlanStage> InternalPlanner::_indexScan(OperationContext* txn, const IndexDescriptor* descriptor, const BSONObj& startKey, const BSONObj& endKey, - bool endKeyInclusive, + BoundInclusion boundInclusion, Direction direction, int options) { invariant(collection); @@ -184,7 +184,7 @@ std::unique_ptr<PlanStage> InternalPlanner::_indexScan(OperationContext* txn, params.bounds.isSimpleRange = true; params.bounds.startKey = startKey; params.bounds.endKey = endKey; - params.bounds.endKeyInclusive = endKeyInclusive; + params.bounds.boundInclusion = boundInclusion; std::unique_ptr<PlanStage> root = stdx::make_unique<IndexScan>(txn, params, ws, nullptr); diff --git a/src/mongo/db/query/internal_plans.h b/src/mongo/db/query/internal_plans.h index 7c98df487aa..40b42f75df1 100644 --- a/src/mongo/db/query/internal_plans.h +++ b/src/mongo/db/query/internal_plans.h @@ -92,7 +92,7 @@ public: const IndexDescriptor* descriptor, const BSONObj& startKey, const BSONObj& endKey, - bool endKeyInclusive, + BoundInclusion boundInclusion, PlanExecutor::YieldPolicy yieldPolicy, Direction direction = FORWARD, int options = IXSCAN_DEFAULT); @@ -106,7 +106,7 @@ public: const IndexDescriptor* descriptor, const BSONObj& startKey, const BSONObj& endKey, - bool endKeyInclusive, + BoundInclusion boundInclusion, PlanExecutor::YieldPolicy yieldPolicy, Direction direction = FORWARD); @@ -133,7 +133,7 @@ private: const IndexDescriptor* descriptor, const BSONObj& startKey, const BSONObj& endKey, - bool endKeyInclusive, + BoundInclusion boundInclusion, Direction direction = FORWARD, int options = IXSCAN_DEFAULT); }; diff --git a/src/mongo/db/query/planner_access.cpp b/src/mongo/db/query/planner_access.cpp index b221210c035..7867b7c0161 100644 --- a/src/mongo/db/query/planner_access.cpp +++ b/src/mongo/db/query/planner_access.cpp @@ -1384,7 +1384,7 @@ QuerySolutionNode* QueryPlannerAccess::makeIndexScan(const IndexEntry& index, isn->bounds.isSimpleRange = true; isn->bounds.startKey = startKey; isn->bounds.endKey = endKey; - isn->bounds.endKeyInclusive = false; + isn->bounds.boundInclusion = BoundInclusion::kIncludeStartKeyOnly; isn->queryCollator = query.getCollator(); unique_ptr<MatchExpression> filter = query.root()->shallowClone(); diff --git a/src/mongo/db/query/query_planner_common.cpp b/src/mongo/db/query/query_planner_common.cpp index 8f129e54e5b..33eb72100cc 100644 --- a/src/mongo/db/query/query_planner_common.cpp +++ b/src/mongo/db/query/query_planner_common.cpp @@ -45,9 +45,19 @@ void QueryPlannerCommon::reverseScans(QuerySolutionNode* node) { if (isn->bounds.isSimpleRange) { std::swap(isn->bounds.startKey, isn->bounds.endKey); - // XXX: Not having a startKeyInclusive means that if we reverse a max/min query - // we have different results with and without the reverse... - isn->bounds.endKeyInclusive = true; + // If only one bound is included, swap which one is included. + switch (isn->bounds.boundInclusion) { + case BoundInclusion::kIncludeStartKeyOnly: + isn->bounds.boundInclusion = BoundInclusion::kIncludeEndKeyOnly; + break; + case BoundInclusion::kIncludeEndKeyOnly: + isn->bounds.boundInclusion = BoundInclusion::kIncludeStartKeyOnly; + break; + case BoundInclusion::kIncludeBothStartAndEndKeys: + case BoundInclusion::kExcludeBothStartAndEndKeys: + // These are both symmetric so no change needed. + break; + } } else { for (size_t i = 0; i < isn->bounds.fields.size(); ++i) { std::vector<Interval>& iv = isn->bounds.fields[i].intervals; diff --git a/src/mongo/db/query/query_solution.cpp b/src/mongo/db/query/query_solution.cpp index ed9ab70b4cc..aa2426954c4 100644 --- a/src/mongo/db/query/query_solution.cpp +++ b/src/mongo/db/query/query_solution.cpp @@ -52,31 +52,33 @@ OrderedIntervalList buildStringBoundsOil(const std::string& keyName) { BSONObjBuilder strBob; strBob.appendMinForType("", BSONType::String); strBob.appendMaxForType("", BSONType::String); - ret.intervals.push_back(IndexBoundsBuilder::makeRangeInterval(strBob.obj(), true, false)); + ret.intervals.push_back( + IndexBoundsBuilder::makeRangeInterval(strBob.obj(), BoundInclusion::kIncludeStartKeyOnly)); BSONObjBuilder objBob; objBob.appendMinForType("", BSONType::Object); objBob.appendMaxForType("", BSONType::Object); - ret.intervals.push_back(IndexBoundsBuilder::makeRangeInterval(objBob.obj(), true, false)); + ret.intervals.push_back( + IndexBoundsBuilder::makeRangeInterval(objBob.obj(), BoundInclusion::kIncludeStartKeyOnly)); BSONObjBuilder arrBob; arrBob.appendMinForType("", BSONType::Array); arrBob.appendMaxForType("", BSONType::Array); - ret.intervals.push_back(IndexBoundsBuilder::makeRangeInterval(arrBob.obj(), true, false)); + ret.intervals.push_back( + IndexBoundsBuilder::makeRangeInterval(arrBob.obj(), BoundInclusion::kIncludeStartKeyOnly)); return ret; } bool rangeCanContainString(const BSONElement& startKey, const BSONElement& endKey, - bool endKeyInclusive) { + BoundInclusion boundInclusion) { OrderedIntervalList stringBoundsOil = buildStringBoundsOil(""); OrderedIntervalList rangeOil; BSONObjBuilder bob; bob.appendAs(startKey, ""); bob.appendAs(endKey, ""); - rangeOil.intervals.push_back( - IndexBoundsBuilder::makeRangeInterval(bob.obj(), true, endKeyInclusive)); + rangeOil.intervals.push_back(IndexBoundsBuilder::makeRangeInterval(bob.obj(), boundInclusion)); IndexBoundsBuilder::intersectize(rangeOil, &stringBoundsOil); return !stringBoundsOil.intervals.empty(); @@ -606,8 +608,11 @@ std::set<StringData> IndexScanNode::getFieldsWithStringBounds(const IndexBounds& BSONElement endKey = endKeyIterator.next(); if (SimpleBSONElementComparator::kInstance.evaluate(startKey != endKey) || CollationIndexKey::isCollatableType(startKey.type())) { - if (!rangeCanContainString( - startKey, endKey, (startKeyIterator.more() || bounds.endKeyInclusive))) { + BoundInclusion boundInclusion = bounds.boundInclusion; + if (startKeyIterator.more()) { + boundInclusion = BoundInclusion::kIncludeBothStartAndEndKeys; + } + if (!rangeCanContainString(startKey, endKey, boundInclusion)) { // If the first non-point range cannot contain strings, we don't need to // add it to the return set. keyPatternIterator.next(); diff --git a/src/mongo/db/query/query_solution_test.cpp b/src/mongo/db/query/query_solution_test.cpp index a116d497661..b1f8bc18e4b 100644 --- a/src/mongo/db/query/query_solution_test.cpp +++ b/src/mongo/db/query/query_solution_test.cpp @@ -160,32 +160,32 @@ TEST(QuerySolutionTest, IntervalListNoPoints) { OrderedIntervalList a{}; a.name = "a"; - a.intervals.push_back( - IndexBoundsBuilder::makeRangeInterval(BSON("" << 1 << "" << 2), true, true)); + a.intervals.push_back(IndexBoundsBuilder::makeRangeInterval( + BSON("" << 1 << "" << 2), BoundInclusion::kIncludeBothStartAndEndKeys)); node.bounds.fields.push_back(a); OrderedIntervalList b{}; b.name = "b"; - b.intervals.push_back( - IndexBoundsBuilder::makeRangeInterval(BSON("" << 1 << "" << 2), true, true)); + b.intervals.push_back(IndexBoundsBuilder::makeRangeInterval( + BSON("" << 1 << "" << 2), BoundInclusion::kIncludeBothStartAndEndKeys)); node.bounds.fields.push_back(b); OrderedIntervalList c{}; c.name = "c"; - c.intervals.push_back( - IndexBoundsBuilder::makeRangeInterval(BSON("" << 1 << "" << 2), true, true)); + c.intervals.push_back(IndexBoundsBuilder::makeRangeInterval( + BSON("" << 1 << "" << 2), BoundInclusion::kIncludeBothStartAndEndKeys)); node.bounds.fields.push_back(c); OrderedIntervalList d{}; d.name = "d"; - d.intervals.push_back( - IndexBoundsBuilder::makeRangeInterval(BSON("" << 1 << "" << 2), true, true)); + d.intervals.push_back(IndexBoundsBuilder::makeRangeInterval( + BSON("" << 1 << "" << 2), BoundInclusion::kIncludeBothStartAndEndKeys)); node.bounds.fields.push_back(d); OrderedIntervalList e{}; e.name = "e"; - e.intervals.push_back( - IndexBoundsBuilder::makeRangeInterval(BSON("" << 1 << "" << 2), true, true)); + e.intervals.push_back(IndexBoundsBuilder::makeRangeInterval( + BSON("" << 1 << "" << 2), BoundInclusion::kIncludeBothStartAndEndKeys)); node.bounds.fields.push_back(e); node.computeProperties(); @@ -216,20 +216,20 @@ TEST(QuerySolutionTest, IntervalListSomePoints) { OrderedIntervalList c{}; c.name = "c"; - c.intervals.push_back( - IndexBoundsBuilder::makeRangeInterval(BSON("" << 1 << "" << 2), true, true)); + c.intervals.push_back(IndexBoundsBuilder::makeRangeInterval( + BSON("" << 1 << "" << 2), BoundInclusion::kIncludeBothStartAndEndKeys)); node.bounds.fields.push_back(c); OrderedIntervalList d{}; d.name = "d"; - d.intervals.push_back( - IndexBoundsBuilder::makeRangeInterval(BSON("" << 1 << "" << 2), true, true)); + d.intervals.push_back(IndexBoundsBuilder::makeRangeInterval( + BSON("" << 1 << "" << 2), BoundInclusion::kIncludeBothStartAndEndKeys)); node.bounds.fields.push_back(d); OrderedIntervalList e{}; e.name = "e"; - e.intervals.push_back( - IndexBoundsBuilder::makeRangeInterval(BSON("" << 1 << "" << 2), true, true)); + e.intervals.push_back(IndexBoundsBuilder::makeRangeInterval( + BSON("" << 1 << "" << 2), BoundInclusion::kIncludeBothStartAndEndKeys)); node.bounds.fields.push_back(e); node.computeProperties(); @@ -253,36 +253,38 @@ TEST(QuerySolutionTest, GetFieldsWithStringBoundsIdentifiesFieldsContainingStrin OrderedIntervalList oilA{}; oilA.name = "a"; - oilA.intervals.push_back( - IndexBoundsBuilder::makeRangeInterval(BSON("" << 1 << "" << 1), true, true)); + oilA.intervals.push_back(IndexBoundsBuilder::makeRangeInterval( + BSON("" << 1 << "" << 1), BoundInclusion::kIncludeBothStartAndEndKeys)); bounds.fields.push_back(oilA); OrderedIntervalList oilB{}; oilB.name = "b"; - oilB.intervals.push_back( - IndexBoundsBuilder::makeRangeInterval(BSON("" << false << "" << true), true, true)); + oilB.intervals.push_back(IndexBoundsBuilder::makeRangeInterval( + BSON("" << false << "" << true), BoundInclusion::kIncludeBothStartAndEndKeys)); bounds.fields.push_back(oilB); OrderedIntervalList oilC{}; oilC.name = "c"; - oilC.intervals.push_back(IndexBoundsBuilder::makeRangeInterval(BSON("" - << "a" - << "" - << "b"), - true, - true)); + oilC.intervals.push_back( + IndexBoundsBuilder::makeRangeInterval(BSON("" + << "a" + << "" + << "b"), + BoundInclusion::kIncludeBothStartAndEndKeys)); bounds.fields.push_back(oilC); OrderedIntervalList oilD{}; oilD.name = "d"; oilD.intervals.push_back(IndexBoundsBuilder::makeRangeInterval( - BSON("" << BSON("foo" << 1) << "" << BSON("foo" << 2)), true, true)); + BSON("" << BSON("foo" << 1) << "" << BSON("foo" << 2)), + BoundInclusion::kIncludeBothStartAndEndKeys)); bounds.fields.push_back(oilD); OrderedIntervalList oilE{}; oilE.name = "e"; oilE.intervals.push_back(IndexBoundsBuilder::makeRangeInterval( - BSON("" << BSON_ARRAY(1 << 2 << 3) << "" << BSON_ARRAY(2 << 3 << 4)), true, true)); + BSON("" << BSON_ARRAY(1 << 2 << 3) << "" << BSON_ARRAY(2 << 3 << 4)), + BoundInclusion::kIncludeBothStartAndEndKeys)); bounds.fields.push_back(oilE); auto fields = IndexScanNode::getFieldsWithStringBounds(bounds, keyPattern); @@ -300,7 +302,7 @@ TEST(QuerySolutionTest, GetFieldsWithStringBoundsIdentifiesStringsFromNonPointBo bounds.isSimpleRange = true; bounds.startKey = BSON("a" << 1 << "b" << 2 << "c" << 3 << "d" << 4 << "e" << 5); bounds.endKey = BSON("a" << 1 << "b" << 2 << "c" << 3 << "d" << 5 << "e" << 5); - bounds.endKeyInclusive = true; + bounds.boundInclusion = BoundInclusion::kIncludeBothStartAndEndKeys; auto fields = IndexScanNode::getFieldsWithStringBounds(bounds, keyPattern); ASSERT_EQUALS(fields.size(), 1U); @@ -317,7 +319,7 @@ TEST(QuerySolutionTest, GetFieldsWithStringBoundsIdentifiesStringsFromStringType bounds.isSimpleRange = true; bounds.startKey = fromjson("{'a': 1, 'b': 'a', 'c': 3, 'd': 4, 'e': 5}"); bounds.endKey = bounds.startKey; - bounds.endKeyInclusive = true; + bounds.boundInclusion = BoundInclusion::kIncludeBothStartAndEndKeys; auto fields = IndexScanNode::getFieldsWithStringBounds(bounds, keyPattern); ASSERT_EQUALS(fields.size(), 4U); @@ -334,7 +336,7 @@ TEST(QuerySolutionTest, GetFieldsWithStringBoundsIdentifiesStringsFromArrayTypeB bounds.isSimpleRange = true; bounds.startKey = fromjson("{'a': 1, 'b': [1,2], 'c': 3, 'd': 4, 'e': 5}"); bounds.endKey = bounds.startKey; - bounds.endKeyInclusive = true; + bounds.boundInclusion = BoundInclusion::kIncludeBothStartAndEndKeys; auto fields = IndexScanNode::getFieldsWithStringBounds(bounds, keyPattern); ASSERT_EQUALS(fields.size(), 4U); @@ -351,7 +353,7 @@ TEST(QuerySolutionTest, GetFieldsWithStringBoundsIdentifiesStringsFromObjectType bounds.isSimpleRange = true; bounds.startKey = fromjson("{'a': 1, 'b': {'foo': 2}, 'c': 3, 'd': 4, 'e': 5}"); bounds.endKey = bounds.startKey; - bounds.endKeyInclusive = true; + bounds.boundInclusion = BoundInclusion::kIncludeBothStartAndEndKeys; auto fields = IndexScanNode::getFieldsWithStringBounds(bounds, keyPattern); ASSERT_EQUALS(fields.size(), 4U); @@ -362,6 +364,59 @@ TEST(QuerySolutionTest, GetFieldsWithStringBoundsIdentifiesStringsFromObjectType ASSERT_TRUE(fields.count("e")); } +TEST(QuerySolutionTest, GetFieldsWithStringBoundsIdentifiesStringsWithExclusiveBounds) { + IndexBounds bounds; + BSONObj keyPattern = BSON("a" << 1 << "b" << 1); + bounds.isSimpleRange = true; + bounds.startKey = fromjson("{'a': 1, 'b': 1}"); + bounds.endKey = fromjson("{'a': 2, 'b': 2}"); + bounds.boundInclusion = BoundInclusion::kExcludeBothStartAndEndKeys; + + auto fields = IndexScanNode::getFieldsWithStringBounds(bounds, keyPattern); + ASSERT_EQUALS(fields.size(), 1U); + ASSERT_FALSE(fields.count("a")); + ASSERT_TRUE(fields.count("b")); +} + +TEST(QuerySolutionTest, GetFieldsWithStringBoundsIdentifiesStringsWithExclusiveBoundsOnBoundary) { + IndexBounds bounds; + BSONObj keyPattern = BSON("a" << 1 << "b" << 1); + bounds.isSimpleRange = true; + bounds.startKey = fromjson("{'a': 1, 'b': 1}"); + bounds.endKey = fromjson("{'a': '', 'b': 1}"); + bounds.boundInclusion = BoundInclusion::kExcludeBothStartAndEndKeys; + + auto fields = IndexScanNode::getFieldsWithStringBounds(bounds, keyPattern); + ASSERT_EQUALS(fields.size(), 2U); + ASSERT_TRUE(fields.count("a")); + ASSERT_TRUE(fields.count("b")); +} + +TEST(QuerySolutionTest, GetFieldsWithStringBoundsIdentifiesNoStringsWithEmptyExclusiveBounds) { + IndexBounds bounds; + BSONObj keyPattern = BSON("a" << 1); + bounds.isSimpleRange = true; + bounds.startKey = fromjson("{'a': 1}"); + bounds.endKey = fromjson("{'a': ''}"); + bounds.boundInclusion = BoundInclusion::kExcludeBothStartAndEndKeys; + + auto fields = IndexScanNode::getFieldsWithStringBounds(bounds, keyPattern); + ASSERT_EQUALS(fields.size(), 0U); +} + +TEST(QuerySolutionTest, GetFieldsWithStringBoundsIdentifiesStringsWithInclusiveBounds) { + IndexBounds bounds; + BSONObj keyPattern = BSON("a" << 1); + bounds.isSimpleRange = true; + bounds.startKey = fromjson("{'a': 1}"); + bounds.endKey = fromjson("{'a': ''}"); + bounds.boundInclusion = BoundInclusion::kIncludeBothStartAndEndKeys; + + auto fields = IndexScanNode::getFieldsWithStringBounds(bounds, keyPattern); + ASSERT_EQUALS(fields.size(), 1U); + ASSERT_TRUE(fields.count("a")); +} + TEST(QuerySolutionTest, IndexScanNodeRemovesNonMatchingCollatedFieldsFromSortsOnSimpleBounds) { IndexScanNode node{IndexEntry(BSON("a" << 1 << "b" << 1))}; CollatorInterfaceMock queryCollator(CollatorInterfaceMock::MockType::kReverseString); @@ -370,7 +425,7 @@ TEST(QuerySolutionTest, IndexScanNodeRemovesNonMatchingCollatedFieldsFromSortsOn node.bounds.isSimpleRange = true; node.bounds.startKey = BSON("a" << 1 << "b" << 1); node.bounds.endKey = BSON("a" << 2 << "b" << 1); - node.bounds.endKeyInclusive = true; + node.bounds.boundInclusion = BoundInclusion::kIncludeBothStartAndEndKeys; node.computeProperties(); @@ -388,7 +443,7 @@ TEST(QuerySolutionTest, IndexScanNodeGetFieldsWithStringBoundsCorrectlyHandlesEn node.bounds.startKey = BSON("a" << 1 << "b" << 1); node.bounds.endKey = BSON("a" << 1 << "b" << ""); - node.bounds.endKeyInclusive = false; + node.bounds.boundInclusion = BoundInclusion::kIncludeStartKeyOnly; node.computeProperties(); @@ -398,7 +453,7 @@ TEST(QuerySolutionTest, IndexScanNodeGetFieldsWithStringBoundsCorrectlyHandlesEn ASSERT_TRUE(sorts.count(BSON("a" << 1 << "b" << 1))); ASSERT_TRUE(sorts.count(BSON("b" << 1))); - node.bounds.endKeyInclusive = true; + node.bounds.boundInclusion = BoundInclusion::kIncludeBothStartAndEndKeys; node.computeProperties(); @@ -416,8 +471,8 @@ TEST(QuerySolutionTest, IndexScanNodeRemovesCollatedFieldsFromSortsIfCollationDi OrderedIntervalList oilA{}; oilA.name = "a"; - oilA.intervals.push_back( - IndexBoundsBuilder::makeRangeInterval(BSON("" << MINKEY << "" << MAXKEY), true, true)); + oilA.intervals.push_back(IndexBoundsBuilder::makeRangeInterval( + BSON("" << MINKEY << "" << MAXKEY), BoundInclusion::kIncludeBothStartAndEndKeys)); node.bounds.fields.push_back(oilA); node.computeProperties(); @@ -432,8 +487,8 @@ TEST(QuerySolutionTest, IndexScanNodeDoesNotRemoveCollatedFieldsFromSortsIfColla OrderedIntervalList oilA{}; oilA.name = "a"; - oilA.intervals.push_back( - IndexBoundsBuilder::makeRangeInterval(BSON("" << MINKEY << "" << MAXKEY), true, true)); + oilA.intervals.push_back(IndexBoundsBuilder::makeRangeInterval( + BSON("" << MINKEY << "" << MAXKEY), BoundInclusion::kIncludeBothStartAndEndKeys)); node.bounds.fields.push_back(oilA); node.computeProperties(); @@ -464,20 +519,20 @@ TEST(QuerySolutionTest, CompoundIndexWithNonMatchingCollationFiltersAllSortsWith OrderedIntervalList c{}; c.name = "c"; - c.intervals.push_back( - IndexBoundsBuilder::makeRangeInterval(BSON("" << MINKEY << "" << MAXKEY), true, true)); + c.intervals.push_back(IndexBoundsBuilder::makeRangeInterval( + BSON("" << MINKEY << "" << MAXKEY), BoundInclusion::kIncludeBothStartAndEndKeys)); node.bounds.fields.push_back(c); OrderedIntervalList d{}; d.name = "d"; - d.intervals.push_back( - IndexBoundsBuilder::makeRangeInterval(BSON("" << 1 << "" << 2), true, true)); + d.intervals.push_back(IndexBoundsBuilder::makeRangeInterval( + BSON("" << 1 << "" << 2), BoundInclusion::kIncludeBothStartAndEndKeys)); node.bounds.fields.push_back(d); OrderedIntervalList e{}; e.name = "e"; - e.intervals.push_back( - IndexBoundsBuilder::makeRangeInterval(BSON("" << 1 << "" << 2), true, true)); + e.intervals.push_back(IndexBoundsBuilder::makeRangeInterval( + BSON("" << 1 << "" << 2), BoundInclusion::kIncludeBothStartAndEndKeys)); node.bounds.fields.push_back(e); node.computeProperties(); @@ -498,7 +553,8 @@ TEST(QuerySolutionTest, IndexScanNodeWithNonMatchingCollationFiltersObjectField) OrderedIntervalList oilA{}; oilA.name = "a"; oilA.intervals.push_back(IndexBoundsBuilder::makeRangeInterval( - BSON("" << BSON("foo" << 1) << "" << BSON("foo" << 2)), true, true)); + BSON("" << BSON("foo" << 1) << "" << BSON("foo" << 2)), + BoundInclusion::kIncludeBothStartAndEndKeys)); node.bounds.fields.push_back(oilA); node.computeProperties(); @@ -516,8 +572,9 @@ TEST(QuerySolutionTest, IndexScanNodeWithNonMatchingCollationFiltersArrayField) OrderedIntervalList oilA{}; oilA.name = "a"; - oilA.intervals.push_back(IndexBoundsBuilder::makeRangeInterval( - BSON("" << BSON_ARRAY(1) << "" << BSON_ARRAY(2)), true, true)); + oilA.intervals.push_back( + IndexBoundsBuilder::makeRangeInterval(BSON("" << BSON_ARRAY(1) << "" << BSON_ARRAY(2)), + BoundInclusion::kIncludeBothStartAndEndKeys)); node.bounds.fields.push_back(oilA); node.computeProperties(); @@ -533,18 +590,18 @@ TEST(QuerySolutionTest, WithNonMatchingCollatorAndNoEqualityPrefixSortsAreNotDup OrderedIntervalList oilA{}; oilA.name = "a"; - oilA.intervals.push_back( - IndexBoundsBuilder::makeRangeInterval(BSON("" << 1 << "" << 2), true, true)); + oilA.intervals.push_back(IndexBoundsBuilder::makeRangeInterval( + BSON("" << 1 << "" << 2), BoundInclusion::kIncludeBothStartAndEndKeys)); node.bounds.fields.push_back(oilA); OrderedIntervalList oilB{}; oilB.name = "b"; - oilB.intervals.push_back(IndexBoundsBuilder::makeRangeInterval(BSON("" - << "a" - << "" - << "b"), - true, - true)); + oilB.intervals.push_back( + IndexBoundsBuilder::makeRangeInterval(BSON("" + << "a" + << "" + << "b"), + BoundInclusion::kIncludeBothStartAndEndKeys)); node.bounds.fields.push_back(oilB); node.computeProperties(); @@ -559,12 +616,12 @@ TEST(QuerySolutionTest, IndexScanNodeHasFieldIncludesStringFieldWhenNoCollator) OrderedIntervalList oilA{}; oilA.name = "a"; - oilA.intervals.push_back(IndexBoundsBuilder::makeRangeInterval(BSON("" - << "str" - << "" - << "str"), - true, - true)); + oilA.intervals.push_back( + IndexBoundsBuilder::makeRangeInterval(BSON("" + << "str" + << "" + << "str"), + BoundInclusion::kIncludeBothStartAndEndKeys)); node.bounds.fields.push_back(oilA); OrderedIntervalList oilB{}; @@ -582,7 +639,7 @@ TEST(QuerySolutionTest, IndexScanNodeHasFieldIncludesSimpleBoundsStringFieldWhen node.bounds.isSimpleRange = true; node.bounds.startKey = BSON("a" << 1 << "b" << 2); node.bounds.endKey = BSON("a" << 2 << "b" << 1); - node.bounds.endKeyInclusive = false; + node.bounds.boundInclusion = BoundInclusion::kIncludeStartKeyOnly; ASSERT_TRUE(node.hasField("a")); ASSERT_TRUE(node.hasField("b")); @@ -595,18 +652,18 @@ TEST(QuerySolutionTest, IndexScanNodeHasFieldExcludesStringFieldWhenIndexHasColl OrderedIntervalList oilA{}; oilA.name = "a"; - oilA.intervals.push_back( - IndexBoundsBuilder::makeRangeInterval(BSON("" << 1 << "" << 2), true, true)); + oilA.intervals.push_back(IndexBoundsBuilder::makeRangeInterval( + BSON("" << 1 << "" << 2), BoundInclusion::kIncludeBothStartAndEndKeys)); node.bounds.fields.push_back(oilA); OrderedIntervalList oilB{}; oilB.name = "b"; - oilB.intervals.push_back(IndexBoundsBuilder::makeRangeInterval(BSON("" - << "bar" - << "" - << "foo"), - true, - false)); + oilB.intervals.push_back( + IndexBoundsBuilder::makeRangeInterval(BSON("" + << "bar" + << "" + << "foo"), + BoundInclusion::kIncludeStartKeyOnly)); node.bounds.fields.push_back(oilB); ASSERT_TRUE(node.hasField("a")); @@ -621,7 +678,7 @@ TEST(QuerySolutionTest, IndexScanNodeHasFieldExcludesSimpleBoundsStringFieldWhen node.bounds.isSimpleRange = true; node.bounds.startKey = BSON("a" << 1 << "b" << 2); node.bounds.endKey = BSON("a" << 2 << "b" << 1); - node.bounds.endKeyInclusive = false; + node.bounds.boundInclusion = BoundInclusion::kIncludeStartKeyOnly; ASSERT_TRUE(node.hasField("a")); ASSERT_FALSE(node.hasField("b")); diff --git a/src/mongo/db/range_deleter_db_env.cpp b/src/mongo/db/range_deleter_db_env.cpp index e064de33599..2f692111c0a 100644 --- a/src/mongo/db/range_deleter_db_env.cpp +++ b/src/mongo/db/range_deleter_db_env.cpp @@ -90,7 +90,7 @@ bool RangeDeleterDBEnv::deleteRange(OperationContext* txn, *deletedDocs = Helpers::removeRange(txn, KeyRange(ns, inclusiveLower, exclusiveUpper, keyPattern), - false, /*maxInclusive*/ + BoundInclusion::kIncludeStartKeyOnly, writeConcern, removeSaverPtr, fromMigrate, diff --git a/src/mongo/db/repl/storage_interface_impl.cpp b/src/mongo/db/repl/storage_interface_impl.cpp index 3a3c280fa7a..acabfe69eb9 100644 --- a/src/mongo/db/repl/storage_interface_impl.cpp +++ b/src/mongo/db/repl/storage_interface_impl.cpp @@ -506,7 +506,6 @@ StatusWith<BSONObj> _findOrDeleteOne(OperationContext* txn, auto maxKey = Helpers::toKeyFormat(keyPattern.extendRangeBound({}, true)); auto bounds = isForward ? std::make_pair(minKey, maxKey) : std::make_pair(maxKey, minKey); - bool endKeyInclusive = false; planExecutor = isFind ? InternalPlanner::indexScan(txn, @@ -514,7 +513,7 @@ StatusWith<BSONObj> _findOrDeleteOne(OperationContext* txn, indexDescriptor, bounds.first, bounds.second, - endKeyInclusive, + BoundInclusion::kIncludeStartKeyOnly, PlanExecutor::YIELD_MANUAL, direction, InternalPlanner::IXSCAN_FETCH) @@ -524,7 +523,7 @@ StatusWith<BSONObj> _findOrDeleteOne(OperationContext* txn, indexDescriptor, bounds.first, bounds.second, - endKeyInclusive, + BoundInclusion::kIncludeStartKeyOnly, PlanExecutor::YIELD_MANUAL, direction); } diff --git a/src/mongo/db/s/check_sharding_index_command.cpp b/src/mongo/db/s/check_sharding_index_command.cpp index 311ba971233..0d7ef33de31 100644 --- a/src/mongo/db/s/check_sharding_index_command.cpp +++ b/src/mongo/db/s/check_sharding_index_command.cpp @@ -135,14 +135,15 @@ public: max = Helpers::toKeyFormat(kp.extendRangeBound(max, false)); } - unique_ptr<PlanExecutor> exec(InternalPlanner::indexScan(txn, - collection, - idx, - min, - max, - false, // endKeyInclusive - PlanExecutor::YIELD_MANUAL, - InternalPlanner::FORWARD)); + unique_ptr<PlanExecutor> exec( + InternalPlanner::indexScan(txn, + collection, + idx, + min, + max, + BoundInclusion::kIncludeStartKeyOnly, + PlanExecutor::YIELD_MANUAL, + InternalPlanner::FORWARD)); exec->setYieldPolicy(PlanExecutor::YIELD_AUTO, collection); // Find the 'missingField' value used to represent a missing document field in a key of diff --git a/src/mongo/db/s/collection_range_deleter.cpp b/src/mongo/db/s/collection_range_deleter.cpp index 37f1899b8af..4feb104f748 100644 --- a/src/mongo/db/s/collection_range_deleter.cpp +++ b/src/mongo/db/s/collection_range_deleter.cpp @@ -169,15 +169,16 @@ int CollectionRangeDeleter::_doDeletion(OperationContext* txn, return -1; } - std::unique_ptr<PlanExecutor> exec(InternalPlanner::indexScan(txn, - collection, - desc, - min, - max, - false, - PlanExecutor::YIELD_MANUAL, - InternalPlanner::FORWARD, - InternalPlanner::IXSCAN_FETCH)); + std::unique_ptr<PlanExecutor> exec( + InternalPlanner::indexScan(txn, + collection, + desc, + min, + max, + BoundInclusion::kIncludeStartKeyOnly, + PlanExecutor::YIELD_MANUAL, + InternalPlanner::FORWARD, + InternalPlanner::IXSCAN_FETCH)); int numDeleted = 0; const int maxItersBeforeYield = std::max(static_cast<int>(internalQueryExecYieldIterations), 1); diff --git a/src/mongo/db/s/migration_chunk_cloner_source_legacy.cpp b/src/mongo/db/s/migration_chunk_cloner_source_legacy.cpp index d73e58cbfe4..a6f40042f1c 100644 --- a/src/mongo/db/s/migration_chunk_cloner_source_legacy.cpp +++ b/src/mongo/db/s/migration_chunk_cloner_source_legacy.cpp @@ -569,13 +569,14 @@ Status MigrationChunkClonerSourceLegacy::_storeCurrentLocs(OperationContext* txn BSONObj min = Helpers::toKeyFormat(kp.extendRangeBound(_args.getMinKey(), false)); BSONObj max = Helpers::toKeyFormat(kp.extendRangeBound(_args.getMaxKey(), false)); - std::unique_ptr<PlanExecutor> exec(InternalPlanner::indexScan(txn, - collection, - idx, - min, - max, - false, // endKeyInclusive - PlanExecutor::YIELD_MANUAL)); + std::unique_ptr<PlanExecutor> exec( + InternalPlanner::indexScan(txn, + collection, + idx, + min, + max, + BoundInclusion::kIncludeStartKeyOnly, + PlanExecutor::YIELD_MANUAL)); // We can afford to yield here because any change to the base data that we might miss is already // being queued and will migrate in the 'transferMods' stage. diff --git a/src/mongo/db/s/split_chunk_command.cpp b/src/mongo/db/s/split_chunk_command.cpp index 3be198a377d..c5719f9c7e1 100644 --- a/src/mongo/db/s/split_chunk_command.cpp +++ b/src/mongo/db/s/split_chunk_command.cpp @@ -78,7 +78,7 @@ bool checkIfSingleDoc(OperationContext* txn, idx, newmin, newmax, - false, // endKeyInclusive + BoundInclusion::kIncludeStartKeyOnly, PlanExecutor::YIELD_MANUAL)); // check if exactly one document found PlanExecutor::ExecState state; diff --git a/src/mongo/db/s/split_vector_command.cpp b/src/mongo/db/s/split_vector_command.cpp index 77503dda5f5..9b013430f3b 100644 --- a/src/mongo/db/s/split_vector_command.cpp +++ b/src/mongo/db/s/split_vector_command.cpp @@ -253,14 +253,15 @@ public: long long currCount = 0; long long numChunks = 0; - unique_ptr<PlanExecutor> exec(InternalPlanner::indexScan(txn, - collection, - idx, - min, - max, - false, // endKeyInclusive - PlanExecutor::YIELD_MANUAL, - InternalPlanner::FORWARD)); + unique_ptr<PlanExecutor> exec( + InternalPlanner::indexScan(txn, + collection, + idx, + min, + max, + BoundInclusion::kIncludeStartKeyOnly, + PlanExecutor::YIELD_MANUAL, + InternalPlanner::FORWARD)); BSONObj currKey; PlanExecutor::ExecState state = exec->getNext(&currKey, NULL); @@ -334,7 +335,7 @@ public: idx, min, max, - false, // endKeyInclusive + BoundInclusion::kIncludeStartKeyOnly, PlanExecutor::YIELD_MANUAL, InternalPlanner::FORWARD); diff --git a/src/mongo/db/ttl.cpp b/src/mongo/db/ttl.cpp index 09aea33b069..c2ee5ba1561 100644 --- a/src/mongo/db/ttl.cpp +++ b/src/mongo/db/ttl.cpp @@ -221,7 +221,6 @@ private: const Date_t expirationTime = Date_t::now() - Seconds(secondsExpireElt.numberLong()); const BSONObj startKey = BSON("" << kDawnOfTime); const BSONObj endKey = BSON("" << expirationTime); - const bool endKeyInclusive = true; // The canonical check as to whether a key pattern element is "ascending" or // "descending" is (elt.number() >= 0). This is defined by the Ordering class. const InternalPlanner::Direction direction = (key.firstElement().number() >= 0) @@ -251,7 +250,7 @@ private: desc, startKey, endKey, - endKeyInclusive, + BoundInclusion::kIncludeBothStartAndEndKeys, PlanExecutor::YIELD_AUTO, direction); diff --git a/src/mongo/dbtests/dbhelper_tests.cpp b/src/mongo/dbtests/dbhelper_tests.cpp index f7027bb8f91..1ed948092c7 100644 --- a/src/mongo/dbtests/dbhelper_tests.cpp +++ b/src/mongo/dbtests/dbhelper_tests.cpp @@ -74,7 +74,8 @@ public: KeyRange range(ns, BSON("_id" << _min), BSON("_id" << _max), BSON("_id" << 1)); mongo::WriteConcernOptions dummyWriteConcern; - Helpers::removeRange(&txn, range, false, dummyWriteConcern); + Helpers::removeRange( + &txn, range, BoundInclusion::kIncludeStartKeyOnly, dummyWriteConcern); } // Check that the expected documents remain. diff --git a/src/mongo/dbtests/query_plan_executor.cpp b/src/mongo/dbtests/query_plan_executor.cpp index 9cd8c8f4428..e1319cecf3f 100644 --- a/src/mongo/dbtests/query_plan_executor.cpp +++ b/src/mongo/dbtests/query_plan_executor.cpp @@ -141,7 +141,7 @@ public: ixparams.bounds.isSimpleRange = true; ixparams.bounds.startKey = BSON("" << start); ixparams.bounds.endKey = BSON("" << end); - ixparams.bounds.endKeyInclusive = true; + ixparams.bounds.boundInclusion = BoundInclusion::kIncludeBothStartAndEndKeys; ixparams.direction = 1; const Collection* coll = db->getCollection(nss.ns()); diff --git a/src/mongo/dbtests/query_stage_and.cpp b/src/mongo/dbtests/query_stage_and.cpp index 1939ce4c969..340101f66fa 100644 --- a/src/mongo/dbtests/query_stage_and.cpp +++ b/src/mongo/dbtests/query_stage_and.cpp @@ -193,7 +193,7 @@ public: params.bounds.isSimpleRange = true; params.bounds.startKey = BSON("" << 20); params.bounds.endKey = BSONObj(); - params.bounds.endKeyInclusive = true; + params.bounds.boundInclusion = BoundInclusion::kIncludeBothStartAndEndKeys; params.direction = -1; ah->addChild(new IndexScan(&_txn, params, &ws, NULL)); @@ -201,7 +201,7 @@ public: params.descriptor = getIndex(BSON("bar" << 1), coll); params.bounds.startKey = BSON("" << 10); params.bounds.endKey = BSONObj(); - params.bounds.endKeyInclusive = true; + params.bounds.boundInclusion = BoundInclusion::kIncludeBothStartAndEndKeys; params.direction = 1; ah->addChild(new IndexScan(&_txn, params, &ws, NULL)); @@ -299,7 +299,7 @@ public: params.bounds.isSimpleRange = true; params.bounds.startKey = BSON("" << 20); params.bounds.endKey = BSONObj(); - params.bounds.endKeyInclusive = true; + params.bounds.boundInclusion = BoundInclusion::kIncludeBothStartAndEndKeys; params.direction = -1; ah->addChild(new IndexScan(&_txn, params, &ws, NULL)); @@ -387,7 +387,7 @@ public: params.bounds.isSimpleRange = true; params.bounds.startKey = BSON("" << 20); params.bounds.endKey = BSONObj(); - params.bounds.endKeyInclusive = true; + params.bounds.boundInclusion = BoundInclusion::kIncludeBothStartAndEndKeys; params.direction = -1; ah->addChild(new IndexScan(&_txn, params, &ws, NULL)); @@ -395,7 +395,7 @@ public: params.descriptor = getIndex(BSON("bar" << 1), coll); params.bounds.startKey = BSON("" << 10); params.bounds.endKey = BSONObj(); - params.bounds.endKeyInclusive = true; + params.bounds.boundInclusion = BoundInclusion::kIncludeBothStartAndEndKeys; params.direction = 1; ah->addChild(new IndexScan(&_txn, params, &ws, NULL)); @@ -442,7 +442,7 @@ public: params.bounds.isSimpleRange = true; params.bounds.startKey = BSON("" << 20 << "" << big); params.bounds.endKey = BSONObj(); - params.bounds.endKeyInclusive = true; + params.bounds.boundInclusion = BoundInclusion::kIncludeBothStartAndEndKeys; params.direction = -1; ah->addChild(new IndexScan(&_txn, params, &ws, NULL)); @@ -450,7 +450,7 @@ public: params.descriptor = getIndex(BSON("bar" << 1), coll); params.bounds.startKey = BSON("" << 10); params.bounds.endKey = BSONObj(); - params.bounds.endKeyInclusive = true; + params.bounds.boundInclusion = BoundInclusion::kIncludeBothStartAndEndKeys; params.direction = 1; ah->addChild(new IndexScan(&_txn, params, &ws, NULL)); @@ -495,7 +495,7 @@ public: params.bounds.isSimpleRange = true; params.bounds.startKey = BSON("" << 20); params.bounds.endKey = BSONObj(); - params.bounds.endKeyInclusive = true; + params.bounds.boundInclusion = BoundInclusion::kIncludeBothStartAndEndKeys; params.direction = -1; ah->addChild(new IndexScan(&_txn, params, &ws, NULL)); @@ -503,7 +503,7 @@ public: params.descriptor = getIndex(BSON("bar" << 1 << "big" << 1), coll); params.bounds.startKey = BSON("" << 10 << "" << big); params.bounds.endKey = BSONObj(); - params.bounds.endKeyInclusive = true; + params.bounds.boundInclusion = BoundInclusion::kIncludeBothStartAndEndKeys; params.direction = 1; ah->addChild(new IndexScan(&_txn, params, &ws, NULL)); @@ -543,7 +543,7 @@ public: params.bounds.isSimpleRange = true; params.bounds.startKey = BSON("" << 20); params.bounds.endKey = BSONObj(); - params.bounds.endKeyInclusive = true; + params.bounds.boundInclusion = BoundInclusion::kIncludeBothStartAndEndKeys; params.direction = -1; ah->addChild(new IndexScan(&_txn, params, &ws, NULL)); @@ -551,7 +551,7 @@ public: params.descriptor = getIndex(BSON("bar" << 1), coll); params.bounds.startKey = BSON("" << 10); params.bounds.endKey = BSONObj(); - params.bounds.endKeyInclusive = true; + params.bounds.boundInclusion = BoundInclusion::kIncludeBothStartAndEndKeys; params.direction = 1; ah->addChild(new IndexScan(&_txn, params, &ws, NULL)); @@ -559,7 +559,7 @@ public: params.descriptor = getIndex(BSON("baz" << 1), coll); params.bounds.startKey = BSON("" << 5); params.bounds.endKey = BSON("" << 15); - params.bounds.endKeyInclusive = true; + params.bounds.boundInclusion = BoundInclusion::kIncludeBothStartAndEndKeys; params.direction = 1; ah->addChild(new IndexScan(&_txn, params, &ws, NULL)); @@ -610,7 +610,7 @@ public: params.bounds.isSimpleRange = true; params.bounds.startKey = BSON("" << 20); params.bounds.endKey = BSONObj(); - params.bounds.endKeyInclusive = true; + params.bounds.boundInclusion = BoundInclusion::kIncludeBothStartAndEndKeys; params.direction = -1; ah->addChild(new IndexScan(&_txn, params, &ws, NULL)); @@ -618,7 +618,7 @@ public: params.descriptor = getIndex(BSON("bar" << 1 << "big" << 1), coll); params.bounds.startKey = BSON("" << 10 << "" << big); params.bounds.endKey = BSONObj(); - params.bounds.endKeyInclusive = true; + params.bounds.boundInclusion = BoundInclusion::kIncludeBothStartAndEndKeys; params.direction = 1; ah->addChild(new IndexScan(&_txn, params, &ws, NULL)); @@ -626,7 +626,7 @@ public: params.descriptor = getIndex(BSON("baz" << 1), coll); params.bounds.startKey = BSON("" << 5); params.bounds.endKey = BSON("" << 15); - params.bounds.endKeyInclusive = true; + params.bounds.boundInclusion = BoundInclusion::kIncludeBothStartAndEndKeys; params.direction = 1; ah->addChild(new IndexScan(&_txn, params, &ws, NULL)); @@ -664,7 +664,7 @@ public: params.bounds.isSimpleRange = true; params.bounds.startKey = BSON("" << 20); params.bounds.endKey = BSONObj(); - params.bounds.endKeyInclusive = true; + params.bounds.boundInclusion = BoundInclusion::kIncludeBothStartAndEndKeys; params.direction = -1; ah->addChild(new IndexScan(&_txn, params, &ws, NULL)); @@ -672,7 +672,7 @@ public: params.descriptor = getIndex(BSON("bar" << 1), coll); params.bounds.startKey = BSON("" << 5); params.bounds.endKey = BSON("" << 5); - params.bounds.endKeyInclusive = true; + params.bounds.boundInclusion = BoundInclusion::kIncludeBothStartAndEndKeys; params.direction = 1; ah->addChild(new IndexScan(&_txn, params, &ws, NULL)); @@ -727,7 +727,7 @@ public: params.bounds.isSimpleRange = true; params.bounds.startKey = BSON("" << 100); params.bounds.endKey = BSONObj(); - params.bounds.endKeyInclusive = true; + params.bounds.boundInclusion = BoundInclusion::kIncludeBothStartAndEndKeys; params.direction = 1; ah->addChild(new IndexScan(&_txn, params, &ws, NULL)); @@ -739,7 +739,7 @@ public: // want to include that in our scan. params.bounds.endKey = BSON("" << ""); - params.bounds.endKeyInclusive = false; + params.bounds.boundInclusion = BoundInclusion::kIncludeStartKeyOnly; params.direction = -1; ah->addChild(new IndexScan(&_txn, params, &ws, NULL)); @@ -779,7 +779,7 @@ public: params.bounds.isSimpleRange = true; params.bounds.startKey = BSON("" << 20); params.bounds.endKey = BSONObj(); - params.bounds.endKeyInclusive = true; + params.bounds.boundInclusion = BoundInclusion::kIncludeBothStartAndEndKeys; params.direction = -1; IndexScan* firstScan = new IndexScan(&_txn, params, &ws, NULL); @@ -792,7 +792,7 @@ public: params.descriptor = getIndex(BSON("bar" << 1), coll); params.bounds.startKey = BSON("" << 10); params.bounds.endKey = BSONObj(); - params.bounds.endKeyInclusive = true; + params.bounds.boundInclusion = BoundInclusion::kIncludeBothStartAndEndKeys; params.direction = 1; ah->addChild(new IndexScan(&_txn, params, &ws, NULL)); @@ -838,7 +838,7 @@ public: params.bounds.isSimpleRange = true; params.bounds.startKey = BSON("" << 20); params.bounds.endKey = BSONObj(); - params.bounds.endKeyInclusive = true; + params.bounds.boundInclusion = BoundInclusion::kIncludeBothStartAndEndKeys; params.direction = -1; ah->addChild(new IndexScan(&_txn, params, &ws, NULL)); @@ -846,7 +846,7 @@ public: params.descriptor = getIndex(BSON("bar" << 1), coll); params.bounds.startKey = BSON("" << 10); params.bounds.endKey = BSONObj(); - params.bounds.endKeyInclusive = true; + params.bounds.boundInclusion = BoundInclusion::kIncludeBothStartAndEndKeys; params.direction = 1; IndexScan* secondScan = new IndexScan(&_txn, params, &ws, NULL); @@ -1032,7 +1032,7 @@ public: params.bounds.isSimpleRange = true; params.bounds.startKey = BSON("" << 1); params.bounds.endKey = BSON("" << 1); - params.bounds.endKeyInclusive = true; + params.bounds.boundInclusion = BoundInclusion::kIncludeBothStartAndEndKeys; params.direction = 1; ah->addChild(new IndexScan(&_txn, params, &ws, NULL)); @@ -1165,7 +1165,7 @@ public: params.bounds.isSimpleRange = true; params.bounds.startKey = BSON("" << 1); params.bounds.endKey = BSON("" << 1); - params.bounds.endKeyInclusive = true; + params.bounds.boundInclusion = BoundInclusion::kIncludeBothStartAndEndKeys; params.direction = 1; ah->addChild(new IndexScan(&_txn, params, &ws, NULL)); @@ -1210,7 +1210,7 @@ public: params.bounds.isSimpleRange = true; params.bounds.startKey = BSON("" << 7); params.bounds.endKey = BSON("" << 7); - params.bounds.endKeyInclusive = true; + params.bounds.boundInclusion = BoundInclusion::kIncludeBothStartAndEndKeys; params.direction = 1; ah->addChild(new IndexScan(&_txn, params, &ws, NULL)); @@ -1218,7 +1218,7 @@ public: params.descriptor = getIndex(BSON("bar" << 1), coll); params.bounds.startKey = BSON("" << 20); params.bounds.endKey = BSON("" << 20); - params.bounds.endKeyInclusive = true; + params.bounds.boundInclusion = BoundInclusion::kIncludeBothStartAndEndKeys; params.direction = 1; ah->addChild(new IndexScan(&_txn, params, &ws, NULL)); @@ -1259,7 +1259,7 @@ public: params.bounds.isSimpleRange = true; params.bounds.startKey = BSON("" << 7); params.bounds.endKey = BSON("" << 7); - params.bounds.endKeyInclusive = true; + params.bounds.boundInclusion = BoundInclusion::kIncludeBothStartAndEndKeys; params.direction = 1; ah->addChild(new IndexScan(&_txn, params, &ws, NULL)); @@ -1267,7 +1267,7 @@ public: params.descriptor = getIndex(BSON("bar" << 1), coll); params.bounds.startKey = BSON("" << 20); params.bounds.endKey = BSON("" << 20); - params.bounds.endKeyInclusive = true; + params.bounds.boundInclusion = BoundInclusion::kIncludeBothStartAndEndKeys; params.direction = 1; ah->addChild(new IndexScan(&_txn, params, &ws, NULL)); @@ -1304,7 +1304,7 @@ public: params.bounds.isSimpleRange = true; params.bounds.startKey = BSON("" << 1); params.bounds.endKey = BSON("" << 1); - params.bounds.endKeyInclusive = true; + params.bounds.boundInclusion = BoundInclusion::kIncludeBothStartAndEndKeys; params.direction = 1; ah->addChild(new IndexScan(&_txn, params, &ws, NULL)); @@ -1370,7 +1370,7 @@ public: params.bounds.isSimpleRange = true; params.bounds.startKey = BSON("" << 1); params.bounds.endKey = BSON("" << 1); - params.bounds.endKeyInclusive = true; + params.bounds.boundInclusion = BoundInclusion::kIncludeBothStartAndEndKeys; params.direction = 1; IndexScan* firstScan = new IndexScan(&_txn, params, &ws, NULL); @@ -1424,7 +1424,7 @@ public: params.bounds.isSimpleRange = true; params.bounds.startKey = BSON("" << 1); params.bounds.endKey = BSON("" << 1); - params.bounds.endKeyInclusive = true; + params.bounds.boundInclusion = BoundInclusion::kIncludeBothStartAndEndKeys; params.direction = 1; as->addChild(new IndexScan(&_txn, params, &ws, NULL)); diff --git a/src/mongo/dbtests/query_stage_count.cpp b/src/mongo/dbtests/query_stage_count.cpp index 4a642fb26d8..ccc08cab5a5 100644 --- a/src/mongo/dbtests/query_stage_count.cpp +++ b/src/mongo/dbtests/query_stage_count.cpp @@ -212,7 +212,7 @@ public: params.bounds.isSimpleRange = true; params.bounds.startKey = BSON("" << 0); params.bounds.endKey = BSON("" << kDocuments + 1); - params.bounds.endKeyInclusive = true; + params.bounds.boundInclusion = BoundInclusion::kIncludeBothStartAndEndKeys; params.direction = 1; // This child stage gets owned and freed by its parent CountStage diff --git a/src/mongo/dbtests/query_stage_ixscan.cpp b/src/mongo/dbtests/query_stage_ixscan.cpp index 7ad4e1a0b84..4f39d27588e 100644 --- a/src/mongo/dbtests/query_stage_ixscan.cpp +++ b/src/mongo/dbtests/query_stage_ixscan.cpp @@ -108,7 +108,7 @@ public: params.bounds.isSimpleRange = true; params.bounds.startKey = startKey; params.bounds.endKey = endKey; - params.bounds.endKeyInclusive = true; + params.bounds.boundInclusion = BoundInclusion::kIncludeBothStartAndEndKeys; params.direction = 1; // This child stage gets owned and freed by the caller. diff --git a/src/mongo/dbtests/query_stage_merge_sort.cpp b/src/mongo/dbtests/query_stage_merge_sort.cpp index d2a9139b2c6..e69c0e9d18b 100644 --- a/src/mongo/dbtests/query_stage_merge_sort.cpp +++ b/src/mongo/dbtests/query_stage_merge_sort.cpp @@ -154,7 +154,7 @@ public: params.bounds.isSimpleRange = true; params.bounds.startKey = objWithMinKey(1); params.bounds.endKey = objWithMaxKey(1); - params.bounds.endKeyInclusive = true; + params.bounds.boundInclusion = BoundInclusion::kIncludeBothStartAndEndKeys; params.direction = 1; ms->addChild(new IndexScan(&_txn, params, ws.get(), NULL)); @@ -225,7 +225,7 @@ public: params.bounds.isSimpleRange = true; params.bounds.startKey = objWithMinKey(1); params.bounds.endKey = objWithMaxKey(1); - params.bounds.endKeyInclusive = true; + params.bounds.boundInclusion = BoundInclusion::kIncludeBothStartAndEndKeys; params.direction = 1; ms->addChild(new IndexScan(&_txn, params, ws.get(), NULL)); @@ -295,7 +295,7 @@ public: params.bounds.isSimpleRange = true; params.bounds.startKey = objWithMinKey(1); params.bounds.endKey = objWithMaxKey(1); - params.bounds.endKeyInclusive = true; + params.bounds.boundInclusion = BoundInclusion::kIncludeBothStartAndEndKeys; params.direction = 1; ms->addChild(new IndexScan(&_txn, params, ws.get(), NULL)); @@ -367,7 +367,7 @@ public: params.bounds.isSimpleRange = true; params.bounds.startKey = objWithMaxKey(1); params.bounds.endKey = objWithMinKey(1); - params.bounds.endKeyInclusive = true; + params.bounds.boundInclusion = BoundInclusion::kIncludeBothStartAndEndKeys; // This is the direction along the index. params.direction = 1; ms->addChild(new IndexScan(&_txn, params, ws.get(), NULL)); @@ -438,7 +438,7 @@ public: params.bounds.isSimpleRange = true; params.bounds.startKey = objWithMinKey(1); params.bounds.endKey = objWithMaxKey(1); - params.bounds.endKeyInclusive = true; + params.bounds.boundInclusion = BoundInclusion::kIncludeBothStartAndEndKeys; params.direction = 1; ms->addChild(new IndexScan(&_txn, params, ws.get(), NULL)); @@ -492,7 +492,7 @@ public: params.bounds.isSimpleRange = true; params.bounds.startKey = objWithMinKey(1); params.bounds.endKey = objWithMaxKey(1); - params.bounds.endKeyInclusive = true; + params.bounds.boundInclusion = BoundInclusion::kIncludeBothStartAndEndKeys; params.direction = 1; int numIndices = 20; @@ -551,7 +551,7 @@ public: params.bounds.isSimpleRange = true; params.bounds.startKey = objWithMinKey(1); params.bounds.endKey = objWithMaxKey(1); - params.bounds.endKeyInclusive = true; + params.bounds.boundInclusion = BoundInclusion::kIncludeBothStartAndEndKeys; params.direction = 1; // Index 'a'+i has foo equal to 'i'. @@ -681,7 +681,7 @@ public: params.bounds.isSimpleRange = true; params.bounds.startKey = BSON("" << 5); params.bounds.endKey = BSON("" << 10); - params.bounds.endKeyInclusive = true; + params.bounds.boundInclusion = BoundInclusion::kIncludeBothStartAndEndKeys; params.direction = 1; auto fetchStage = stdx::make_unique<FetchStage>( &_txn, &ws, new IndexScan(&_txn, params, &ws, nullptr), nullptr, coll); @@ -695,7 +695,7 @@ public: params.bounds.isSimpleRange = true; params.bounds.startKey = BSON("" << 4); params.bounds.endKey = BSON("" << 10); - params.bounds.endKeyInclusive = true; + params.bounds.boundInclusion = BoundInclusion::kIncludeBothStartAndEndKeys; params.direction = 1; auto fetchStage = stdx::make_unique<FetchStage>( &_txn, &ws, new IndexScan(&_txn, params, &ws, nullptr), nullptr, coll); @@ -782,7 +782,7 @@ public: params.bounds.isSimpleRange = true; params.bounds.startKey = objWithMinKey(1); params.bounds.endKey = objWithMaxKey(1); - params.bounds.endKeyInclusive = true; + params.bounds.boundInclusion = BoundInclusion::kIncludeBothStartAndEndKeys; params.direction = 1; ms->addChild(new IndexScan(&_txn, params, ws.get(), NULL)); @@ -856,7 +856,7 @@ public: params.bounds.isSimpleRange = true; params.bounds.startKey = objWithMinKey(1); params.bounds.endKey = objWithMaxKey(1); - params.bounds.endKeyInclusive = true; + params.bounds.boundInclusion = BoundInclusion::kIncludeBothStartAndEndKeys; params.direction = 1; ms->addChild(new IndexScan(&_txn, params, ws.get(), NULL)); diff --git a/src/mongo/dbtests/query_stage_multiplan.cpp b/src/mongo/dbtests/query_stage_multiplan.cpp index 67f43043a46..2f1ea56d50e 100644 --- a/src/mongo/dbtests/query_stage_multiplan.cpp +++ b/src/mongo/dbtests/query_stage_multiplan.cpp @@ -147,7 +147,7 @@ public: ixparams.bounds.isSimpleRange = true; ixparams.bounds.startKey = BSON("" << 7); ixparams.bounds.endKey = BSON("" << 7); - ixparams.bounds.endKeyInclusive = true; + ixparams.bounds.boundInclusion = BoundInclusion::kIncludeBothStartAndEndKeys; ixparams.direction = 1; unique_ptr<WorkingSet> sharedWs(new WorkingSet()); diff --git a/src/mongo/dbtests/query_stage_tests.cpp b/src/mongo/dbtests/query_stage_tests.cpp index 70957466b2d..93ebb697d12 100644 --- a/src/mongo/dbtests/query_stage_tests.cpp +++ b/src/mongo/dbtests/query_stage_tests.cpp @@ -149,7 +149,7 @@ public: params.bounds.isSimpleRange = true; params.bounds.startKey = BSON("" << 20); params.bounds.endKey = BSONObj(); - params.bounds.endKeyInclusive = true; + params.bounds.boundInclusion = BoundInclusion::kIncludeBothStartAndEndKeys; params.direction = -1; ASSERT_EQUALS(countResults(params), 21); @@ -167,7 +167,7 @@ public: params.bounds.isSimpleRange = true; params.bounds.startKey = BSON("" << 20); params.bounds.endKey = BSON("" << 30); - params.bounds.endKeyInclusive = false; + params.bounds.boundInclusion = BoundInclusion::kIncludeStartKeyOnly; params.direction = 1; ASSERT_EQUALS(countResults(params), 10); @@ -185,7 +185,7 @@ public: params.bounds.isSimpleRange = true; params.bounds.startKey = BSON("" << 20); params.bounds.endKey = BSON("" << 30); - params.bounds.endKeyInclusive = true; + params.bounds.boundInclusion = BoundInclusion::kIncludeBothStartAndEndKeys; params.direction = 1; ASSERT_EQUALS(countResults(params), 11); @@ -204,7 +204,7 @@ public: params.bounds.isSimpleRange = true; params.bounds.startKey = BSON("" << 20); params.bounds.endKey = BSON("" << 30); - params.bounds.endKeyInclusive = true; + params.bounds.boundInclusion = BoundInclusion::kIncludeBothStartAndEndKeys; params.direction = 1; ASSERT_EQUALS(countResults(params, BSON("foo" << 25)), 1); @@ -223,7 +223,7 @@ public: params.bounds.isSimpleRange = true; params.bounds.startKey = BSON("" << 20); params.bounds.endKey = BSON("" << 30); - params.bounds.endKeyInclusive = true; + params.bounds.boundInclusion = BoundInclusion::kIncludeBothStartAndEndKeys; params.direction = 1; ASSERT_THROWS(countResults(params, BSON("baz" << 25)), MsgAssertionException); |