summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorMathias Stearn <mathias@10gen.com>2015-03-25 12:25:04 -0400
committerMathias Stearn <mathias@10gen.com>2015-04-09 11:35:56 -0400
commitdb59e0f31b12966f127bf39df5674e3239c57350 (patch)
tree70699492e09cb72c3d7a0654e74107e512a990ad /src
parent8b31673665dd2a9d38e4b65ea880fc49acc0bd81 (diff)
downloadmongo-db59e0f31b12966f127bf39df5674e3239c57350.tar.gz
SERVER-17635 Improve SortedDataInterface::Cursor API
Major changes: * Implementation now responsible for simple end point checking. * No way to ask for current position. Relocating methods now return position. * Simplified seeking methods so they have clear uses. * Callers can use saveUnpositioned to indicate they don't care about position.
Diffstat (limited to 'src')
-rw-r--r--src/mongo/SConscript1
-rw-r--r--src/mongo/db/exec/count_scan.cpp166
-rw-r--r--src/mongo/db/exec/count_scan.h21
-rw-r--r--src/mongo/db/exec/distinct_scan.cpp189
-rw-r--r--src/mongo/db/exec/distinct_scan.h43
-rw-r--r--src/mongo/db/exec/index_scan.cpp406
-rw-r--r--src/mongo/db/exec/index_scan.h60
-rw-r--r--src/mongo/db/exec/plan_stats.h2
-rw-r--r--src/mongo/db/index/2d_access_method.h1
-rw-r--r--src/mongo/db/index/btree_access_method.h1
-rw-r--r--src/mongo/db/index/index_access_method.cpp36
-rw-r--r--src/mongo/db/index/index_access_method.h8
-rw-r--r--src/mongo/db/index/index_cursor.cpp123
-rw-r--r--src/mongo/db/index/index_cursor.h188
-rw-r--r--src/mongo/db/index/s2_access_method.h3
-rw-r--r--src/mongo/db/query/index_bounds.cpp46
-rw-r--r--src/mongo/db/query/index_bounds.h37
-rw-r--r--src/mongo/db/query/index_bounds_test.cpp306
-rw-r--r--src/mongo/db/storage/SConscript9
-rw-r--r--src/mongo/db/storage/devnull/devnull_kv_engine.cpp5
-rw-r--r--src/mongo/db/storage/in_memory/in_memory_btree_impl.cpp340
-rw-r--r--src/mongo/db/storage/index_entry_comparison.cpp6
-rw-r--r--src/mongo/db/storage/index_entry_comparison.h100
-rw-r--r--src/mongo/db/storage/key_string.cpp38
-rw-r--r--src/mongo/db/storage/key_string.h15
-rw-r--r--src/mongo/db/storage/mmap_v1/btree/btree_interface.cpp259
-rw-r--r--src/mongo/db/storage/mmap_v1/btree/btree_logic.cpp179
-rw-r--r--src/mongo/db/storage/mmap_v1/btree/btree_logic.h48
-rw-r--r--src/mongo/db/storage/sorted_data_interface.h237
-rw-r--r--src/mongo/db/storage/sorted_data_interface_test_cursor.cpp112
-rw-r--r--src/mongo/db/storage/sorted_data_interface_test_cursor_advanceto.cpp743
-rw-r--r--src/mongo/db/storage/sorted_data_interface_test_cursor_locate.cpp593
-rw-r--r--src/mongo/db/storage/sorted_data_interface_test_cursor_position.cpp481
-rw-r--r--src/mongo/db/storage/sorted_data_interface_test_cursor_saverestore.cpp164
-rw-r--r--src/mongo/db/storage/sorted_data_interface_test_harness.cpp339
-rw-r--r--src/mongo/db/storage/sorted_data_interface_test_insert.cpp112
-rw-r--r--src/mongo/db/storage/wiredtiger/wiredtiger_index.cpp408
-rw-r--r--src/mongo/db/storage/wiredtiger/wiredtiger_index.h50
-rw-r--r--src/mongo/dbtests/query_stage_ixscan.cpp26
-rw-r--r--src/mongo/dbtests/rollbacktests.cpp12
40 files changed, 2026 insertions, 3887 deletions
diff --git a/src/mongo/SConscript b/src/mongo/SConscript
index 40bfc918512..5698fbceab4 100644
--- a/src/mongo/SConscript
+++ b/src/mongo/SConscript
@@ -791,7 +791,6 @@ serverOnlyFiles = [ "db/background.cpp",
"db/index/hash_access_method.cpp",
"db/index/haystack_access_method.cpp",
"db/index/index_access_method.cpp",
- "db/index/index_cursor.cpp",
"db/index/s2_access_method.cpp",
"db/index_builder.cpp",
"db/index_legacy.cpp",
diff --git a/src/mongo/db/exec/count_scan.cpp b/src/mongo/db/exec/count_scan.cpp
index 13a1652f54b..5ab657704f3 100644
--- a/src/mongo/db/exec/count_scan.cpp
+++ b/src/mongo/db/exec/count_scan.cpp
@@ -30,7 +30,6 @@
#include "mongo/db/concurrency/write_conflict_exception.h"
#include "mongo/db/exec/scoped_timer.h"
-#include "mongo/db/index/index_cursor.h"
#include "mongo/db/index/index_descriptor.h"
namespace mongo {
@@ -48,109 +47,66 @@ namespace mongo {
_workingSet(workingSet),
_descriptor(params.descriptor),
_iam(params.descriptor->getIndexCatalog()->getIndex(params.descriptor)),
- _params(params),
- _hitEnd(false),
_shouldDedup(params.descriptor->isMultikey(txn)),
+ _params(params),
_commonStats(kStageType) {
_specificStats.keyPattern = _params.descriptor->keyPattern();
_specificStats.indexName = _params.descriptor->indexName();
_specificStats.isMultiKey = _params.descriptor->isMultikey(txn);
_specificStats.indexVersion = _params.descriptor->version();
- }
-
- void CountScan::initIndexCursor() {
- CursorOptions cursorOptions;
- cursorOptions.direction = CursorOptions::INCREASING;
-
- IndexCursor *cursor;
- Status s = _iam->newCursor(_txn, cursorOptions, &cursor);
- verify(s.isOK());
- verify(cursor);
- _cursor.reset(cursor);
-
- // _cursor points at our start position. We move it forward until it hits a cursor
- // that points at the end.
- _cursor->seek(_params.startKey, !_params.startKeyInclusive);
-
- ++_specificStats.keysExamined;
-
- // Create the cursor that points at our end position.
- IndexCursor* endCursor;
- verify(_iam->newCursor(_txn, cursorOptions, &endCursor).isOK());
- verify(endCursor);
- _endCursor.reset(endCursor);
- // If the end key is inclusive we want to point *past* it since that's the end.
- _endCursor->seek(_params.endKey, _params.endKeyInclusive);
-
- ++_specificStats.keysExamined;
-
- // See if we've hit the end already.
- checkEnd();
+ // endKey must be after startKey in index order since we only do forward scans.
+ dassert(_params.startKey.woCompare(_params.endKey,
+ Ordering::make(params.descriptor->keyPattern()),
+ /*compareFieldNames*/false) <= 0);
}
- void CountScan::checkEnd() {
- if (isEOF()) { return; }
-
- if (_endCursor->isEOF()) {
- // If the endCursor is EOF we're only done when our 'current count position' hits EOF.
- _hitEnd = _cursor->isEOF();
- }
- else {
- // If not, we're only done when we hit the end cursor's (valid) position.
- _hitEnd = _cursor->pointsAt(*_endCursor.get());
- }
- }
PlanStage::StageState CountScan::work(WorkingSetID* out) {
++_commonStats.works;
+ if (_commonStats.isEOF) return PlanStage::IS_EOF;
// Adds the amount of time taken by work() to executionTimeMillis.
ScopedTimer timer(&_commonStats.executionTimeMillis);
- if (NULL == _cursor.get()) {
- // First call to work(). Perform cursor init.
- try {
- initIndexCursor();
- checkEnd();
- }
- catch (const WriteConflictException& wce) {
- // Release our owned cursors and try again next time.
- _cursor.reset();
- _endCursor.reset();
- *out = WorkingSet::INVALID_ID;
- return PlanStage::NEED_YIELD;
- }
- ++_commonStats.needTime;
- return PlanStage::NEED_TIME;
- }
-
- if (isEOF()) { return PlanStage::IS_EOF; }
+ boost::optional<IndexKeyEntry> entry;
+ const bool needInit = !_cursor;
+ try {
+ // We don't care about the keys.
+ const auto kWantLoc = SortedDataInterface::Cursor::kWantLoc;
- RecordId loc = _cursor->getValue();
+ if (needInit) {
+ // First call to work(). Perform cursor init.
+ _cursor = _iam->newCursor(_txn);
+ _cursor->setEndPosition(_params.endKey, _params.endKeyInclusive);
- try {
- _cursor->next();
+ entry = _cursor->seek(_params.startKey, _params.startKeyInclusive, kWantLoc);
+ }
+ else {
+ entry = _cursor->next(kWantLoc);
+ }
}
catch (const WriteConflictException& wce) {
- // The cursor shouldn't have moved.
- invariant(_cursor->getValue() == loc);
+ if (needInit) {
+ // Release our cursor and try again next time.
+ _cursor.reset();
+ }
*out = WorkingSet::INVALID_ID;
return PlanStage::NEED_YIELD;
}
- checkEnd();
-
++_specificStats.keysExamined;
- if (_shouldDedup) {
- if (_returned.end() != _returned.find(loc)) {
- ++_commonStats.needTime;
- return PlanStage::NEED_TIME;
- }
- else {
- _returned.insert(loc);
- }
+ if (!entry) {
+ _commonStats.isEOF = true;
+ _cursor.reset();
+ return PlanStage::IS_EOF;
+ }
+
+ if (_shouldDedup && !_returned.insert(entry->loc).second) {
+ // *loc was already in _returned.
+ ++_commonStats.needTime;
+ return PlanStage::NEED_TIME;
}
*out = WorkingSet::INVALID_ID;
@@ -159,68 +115,25 @@ namespace mongo {
}
bool CountScan::isEOF() {
- if (NULL == _cursor.get()) {
- // Have to call work() at least once.
- return false;
- }
-
- return _hitEnd || _cursor->isEOF();
+ return _commonStats.isEOF;
}
void CountScan::saveState() {
_txn = NULL;
++_commonStats.yields;
- if (_hitEnd || (NULL == _cursor.get())) { return; }
-
- _cursor->savePosition();
- _endCursor->savePosition();
+ if (_cursor) _cursor->savePositioned();
}
void CountScan::restoreState(OperationContext* opCtx) {
invariant(_txn == NULL);
_txn = opCtx;
++_commonStats.unyields;
- if (_hitEnd || (NULL == _cursor.get())) { return; }
-
- if (!_cursor->restorePosition( opCtx ).isOK()) {
- _hitEnd = true;
- return;
- }
-
- if (_cursor->isEOF()) {
- _hitEnd = true;
- return;
- }
-
- // See if we're somehow already past our end key (maybe the thing we were pointing at got
- // deleted...)
- int cmp = _cursor->getKey().woCompare(_params.endKey, _descriptor->keyPattern(), false);
- if (cmp > 0 || (cmp == 0 && !_params.endKeyInclusive)) {
- _hitEnd = true;
- return;
- }
-
- if (!_endCursor->restorePosition( opCtx ).isOK()) {
- _hitEnd = true;
- return;
- }
-
- // If we were EOF when we yielded we don't always want to have _cursor run until
- // EOF. New documents may have been inserted after our endKey and our end marker
- // may be before them.
- //
- // As an example, say we're counting from 5 to 10 and the index only has keys
- // for 6, 7, 8, and 9. btreeCursor will point at a 6 key at the start and the
- // endCursor will be EOF. If we insert documents with keys 11 during a yield we
- // need to relocate the endCursor to point at them as the "end key" of our count.
- //
- // If we weren't EOF our end position might have moved around. Relocate it.
- _endCursor->seek(_params.endKey, _params.endKeyInclusive);
+
+ if (_cursor) _cursor->restore(opCtx);
// This can change during yielding.
+ // TODO this isn't sufficient. See SERVER-17678.
_shouldDedup = _descriptor->isMultikey(_txn);
-
- checkEnd();
}
void CountScan::invalidate(OperationContext* txn, const RecordId& dl, InvalidationType type) {
@@ -246,7 +159,6 @@ namespace mongo {
}
PlanStageStats* CountScan::getStats() {
- _commonStats.isEOF = isEOF();
auto_ptr<PlanStageStats> ret(new PlanStageStats(_commonStats, STAGE_COUNT_SCAN));
CountScanStats* countStats = new CountScanStats(_specificStats);
diff --git a/src/mongo/db/exec/count_scan.h b/src/mongo/db/exec/count_scan.h
index 54bdaf8db2f..e1b3966a22e 100644
--- a/src/mongo/db/exec/count_scan.h
+++ b/src/mongo/db/exec/count_scan.h
@@ -89,16 +89,6 @@ namespace mongo {
static const char* kStageType;
private:
- /**
- * Initialize the underlying IndexCursor
- */
- void initIndexCursor();
-
- /**
- * See if we've hit the end yet.
- */
- void checkEnd();
-
// transactional context for read locks. Not owned by us
OperationContext* _txn;
@@ -109,21 +99,14 @@ namespace mongo {
const IndexDescriptor* _descriptor;
const IndexAccessMethod* _iam;
- // Our start cursor.
- boost::scoped_ptr<IndexCursor> _cursor;
-
- // Our end marker.
- boost::scoped_ptr<IndexCursor> _endCursor;
+ std::unique_ptr<SortedDataInterface::Cursor> _cursor;
// Could our index have duplicates? If so, we use _returned to dedup.
+ bool _shouldDedup;
unordered_set<RecordId, RecordId::Hasher> _returned;
CountScanParams _params;
- bool _hitEnd;
-
- bool _shouldDedup;
-
CommonStats _commonStats;
CountScanStats _specificStats;
};
diff --git a/src/mongo/db/exec/distinct_scan.cpp b/src/mongo/db/exec/distinct_scan.cpp
index db3eae5dceb..9a38f7d675a 100644
--- a/src/mongo/db/exec/distinct_scan.cpp
+++ b/src/mongo/db/exec/distinct_scan.cpp
@@ -28,11 +28,11 @@
#include "mongo/db/exec/distinct_scan.h"
+#include "mongo/db/concurrency/write_conflict_exception.h"
#include "mongo/db/exec/filter.h"
#include "mongo/db/exec/scoped_timer.h"
#include "mongo/db/exec/working_set_computed_data.h"
#include "mongo/db/index/index_access_method.h"
-#include "mongo/db/index/index_cursor.h"
#include "mongo/db/index/index_descriptor.h"
namespace mongo {
@@ -48,142 +48,87 @@ namespace mongo {
_workingSet(workingSet),
_descriptor(params.descriptor),
_iam(params.descriptor->getIndexCatalog()->getIndex(params.descriptor)),
- _scanState(INITIALIZING),
_params(params),
+ _checker(&_params.bounds, _descriptor->keyPattern(), _params.direction),
_commonStats(kStageType) {
+
_specificStats.keyPattern = _params.descriptor->keyPattern();
_specificStats.indexName = _params.descriptor->indexName();
_specificStats.indexVersion = _params.descriptor->version();
- }
-
- void DistinctScan::initIndexCursor() {
- // This function transitions from the initializing state to CHECKING_END. If
- // the initialization fails, however, then the state transitions to HIT_END.
- invariant(INITIALIZING == _scanState);
-
- // Create an IndexCursor over the btree we're distinct-ing over.
- CursorOptions cursorOptions;
-
- if (1 == _params.direction) {
- cursorOptions.direction = CursorOptions::INCREASING;
- }
- else {
- cursorOptions.direction = CursorOptions::DECREASING;
- }
-
- IndexCursor *cursor;
- Status s = _iam->newCursor(_txn, cursorOptions, &cursor);
- verify(s.isOK());
- verify(cursor);
- _cursor.reset(cursor);
-
- // Create a new bounds checker. The bounds checker gets our start key and assists in
- // executing the scan and staying within the required bounds.
- _checker.reset(new IndexBoundsChecker(&_params.bounds,
- _descriptor->keyPattern(),
- _params.direction));
-
- int nFields = _descriptor->keyPattern().nFields();
- // The start key is dumped into these two.
- vector<const BSONElement*> key;
- vector<bool> inc;
- key.resize(nFields);
- inc.resize(nFields);
- if (_checker->getStartKey(&key, &inc)) {
- _cursor->seek(key, inc);
- _keyElts.resize(nFields);
- _keyEltsInc.resize(nFields);
- }
- else {
- _scanState = HIT_END;
- }
- // This method may throw an exception while it's doing initialization. If we've gotten
- // here, then we've done all the initialization without an exception being thrown. This
- // means it is safe to transition to the CHECKING_END state. In error cases, we transition
- // to HIT_END, so we should not change state again here.
- if (HIT_END != _scanState) {
- _scanState = CHECKING_END;
- }
+ // Set up our initial seek. If there is no valid data, just mark as EOF.
+ _commonStats.isEOF = !_checker.getStartSeekPoint(&_seekPoint);
}
PlanStage::StageState DistinctScan::work(WorkingSetID* out) {
++_commonStats.works;
+ if (_commonStats.isEOF) return PlanStage::IS_EOF;
// Adds the amount of time taken by work() to executionTimeMillis.
ScopedTimer timer(&_commonStats.executionTimeMillis);
- if (INITIALIZING == _scanState) {
- invariant(NULL == _cursor.get());
- initIndexCursor();
+ boost::optional<IndexKeyEntry> kv;
+ try {
+ if (!_cursor) _cursor = _iam->newCursor(_txn, _params.direction == 1);
+ kv = _cursor->seek(_seekPoint);
}
-
- if (CHECKING_END == _scanState) {
- checkEnd();
+ catch (const WriteConflictException& wce) {
+ *out = WorkingSet::INVALID_ID;
+ return PlanStage::NEED_YIELD;
}
-
- if (isEOF()) {
+
+ if (!kv) {
_commonStats.isEOF = true;
return PlanStage::IS_EOF;
}
- if (GETTING_NEXT == _scanState) {
- // Grab the next (key, value) from the index.
- BSONObj ownedKeyObj = _cursor->getKey().getOwned();
- RecordId loc = _cursor->getValue();
-
- // The underlying IndexCursor points at the *next* thing we want to return. We do this
- // so that if we're scanning an index looking for docs to delete we don't continually
- // clobber the thing we're pointing at.
+ ++_specificStats.keysExamined;
- // We skip to the next value of the _params.fieldNo-th field in the index key pattern.
- // This is the field we're distinct-ing over.
- _cursor->skip(_cursor->getKey(),
- _params.fieldNo + 1,
- true,
- _keyElts,
- _keyEltsInc);
+ switch (_checker.checkKey(kv->key, &_seekPoint)) {
+ case IndexBoundsChecker::MUST_ADVANCE:
+ // Try again next time. The checker has adjusted the _seekPoint.
+ ++_commonStats.needTime;
+ return PlanStage::NEED_TIME;
- // On the next call to work, make sure that the cursor is still within the bounds.
- _scanState = CHECKING_END;
+ case IndexBoundsChecker::DONE:
+ // There won't be a next time.
+ _commonStats.isEOF = true;
+ _cursor.reset();
+ return IS_EOF;
+
+ case IndexBoundsChecker::VALID:
+ // Return this key. Adjust the _seekPoint so that it is exclusive on the field we
+ // are using.
+
+ if (!kv->key.isOwned()) kv->key = kv->key.getOwned();
+ _seekPoint.keyPrefix = kv->key;
+ _seekPoint.prefixLen = _params.fieldNo + 1;
+ _seekPoint.prefixExclusive = true;
// Package up the result for the caller.
WorkingSetID id = _workingSet->allocate();
WorkingSetMember* member = _workingSet->get(id);
- member->loc = loc;
- member->keyData.push_back(IndexKeyDatum(_descriptor->keyPattern(), ownedKeyObj, _iam));
+ member->loc = kv->loc;
+ member->keyData.push_back(IndexKeyDatum(_descriptor->keyPattern(), kv->key, _iam));
member->state = WorkingSetMember::LOC_AND_IDX;
*out = id;
++_commonStats.advanced;
return PlanStage::ADVANCED;
}
-
- ++_commonStats.needTime;
- return PlanStage::NEED_TIME;
+ invariant(false);
}
bool DistinctScan::isEOF() {
- if (INITIALIZING == _scanState) {
- // Have to call work() at least once.
- return false;
- }
-
- return HIT_END == _scanState || _cursor->isEOF();
+ return _commonStats.isEOF;
}
void DistinctScan::saveState() {
_txn = NULL;
++_commonStats.yields;
- if (HIT_END == _scanState || INITIALIZING == _scanState) { return; }
- // We save these so that we know if the cursor moves during the yield. If it moves, we have
- // to make sure its ending position is valid w.r.t. our bounds.
- if (!_cursor->isEOF()) {
- _savedKey = _cursor->getKey().getOwned();
- _savedLoc = _cursor->getValue();
- }
- _cursor->savePosition();
+ // We always seek, so we don't care where the cursor is.
+ if (_cursor) _cursor->saveUnpositioned();
}
void DistinctScan::restoreState(OperationContext* opCtx) {
@@ -191,69 +136,19 @@ namespace mongo {
_txn = opCtx;
++_commonStats.unyields;
- if (HIT_END == _scanState || INITIALIZING == _scanState) { return; }
-
- // We can have a valid position before we check isEOF(), restore the position, and then be
- // EOF upon restore.
- if (!_cursor->restorePosition( opCtx ).isOK() || _cursor->isEOF()) {
- _scanState = HIT_END;
- return;
- }
-
- if (!_savedKey.binaryEqual(_cursor->getKey()) || _savedLoc != _cursor->getValue()) {
- // Our restored position might be past endKey, see if we've hit the end.
- _scanState = CHECKING_END;
- }
+ if (_cursor) _cursor->restore(opCtx);
}
void DistinctScan::invalidate(OperationContext* txn, const RecordId& dl, InvalidationType type) {
++_commonStats.invalidates;
}
- void DistinctScan::checkEnd() {
- if (isEOF()) {
- _commonStats.isEOF = true;
- return;
- }
-
- // Use _checker to see how things are.
- IndexBoundsChecker::KeyState keyState;
- keyState = _checker->checkKey(_cursor->getKey(),
- &_keyEltsToUse,
- &_movePastKeyElts,
- &_keyElts,
- &_keyEltsInc);
-
- if (IndexBoundsChecker::DONE == keyState) {
- _scanState = HIT_END;
- return;
- }
-
- // This seems weird but it's the old definition of nscanned.
- ++_specificStats.keysExamined;
-
- if (IndexBoundsChecker::VALID == keyState) {
- _scanState = GETTING_NEXT;
- return;
- }
-
- verify(IndexBoundsChecker::MUST_ADVANCE == keyState);
- _cursor->skip(_cursor->getKey(), _keyEltsToUse, _movePastKeyElts,
- _keyElts, _keyEltsInc);
-
- // Must check underlying cursor EOF after every cursor movement.
- if (_cursor->isEOF()) {
- _scanState = HIT_END;
- }
- }
-
vector<PlanStage*> DistinctScan::getChildren() const {
vector<PlanStage*> empty;
return empty;
}
PlanStageStats* DistinctScan::getStats() {
- _commonStats.isEOF = isEOF();
auto_ptr<PlanStageStats> ret(new PlanStageStats(_commonStats, STAGE_DISTINCT_SCAN));
ret->specific.reset(new DistinctScanStats(_specificStats));
return ret.release();
diff --git a/src/mongo/db/exec/distinct_scan.h b/src/mongo/db/exec/distinct_scan.h
index 5d42c338ac7..bfc18104cea 100644
--- a/src/mongo/db/exec/distinct_scan.h
+++ b/src/mongo/db/exec/distinct_scan.h
@@ -41,7 +41,6 @@
namespace mongo {
class IndexAccessMethod;
- class IndexCursor;
class IndexDescriptor;
class WorkingSet;
@@ -78,24 +77,6 @@ namespace mongo {
*/
class DistinctScan : public PlanStage {
public:
- /**
- * Keeps track of what this distinct scan is currently doing so that it
- * can do the right thing on the next call to work().
- */
- enum ScanState {
- // Need to initialize the underlying index traversal machinery.
- INITIALIZING,
-
- // Skipping keys in order to check whether we have reached the end.
- CHECKING_END,
-
- // Retrieving the next key, and applying the filter if necessary.
- GETTING_NEXT,
-
- // The index scan is finished.
- HIT_END
- };
-
DistinctScan(OperationContext* txn, const DistinctParams& params, WorkingSet* workingSet);
virtual ~DistinctScan() { }
@@ -118,14 +99,6 @@ namespace mongo {
static const char* kStageType;
private:
- /**
- * Initialize the underlying IndexCursor
- */
- void initIndexCursor();
-
- /** See if the cursor is pointing at or past _endKey, if _endKey is non-empty. */
- void checkEnd();
-
// transactional context for read locks. Not owned by us
OperationContext* _txn;
@@ -137,23 +110,13 @@ namespace mongo {
const IndexAccessMethod* _iam; // owned by Collection -> IndexCatalog
// The cursor we use to navigate the tree.
- boost::scoped_ptr<IndexCursor> _cursor;
-
- // Keeps track of what work we need to do next.
- ScanState _scanState;
-
- // For yielding.
- BSONObj _savedKey;
- RecordId _savedLoc;
+ std::unique_ptr<SortedDataInterface::Cursor> _cursor;
DistinctParams _params;
// _checker gives us our start key and ensures we stay in bounds.
- boost::scoped_ptr<IndexBoundsChecker> _checker;
- int _keyEltsToUse;
- bool _movePastKeyElts;
- std::vector<const BSONElement*> _keyElts;
- std::vector<bool> _keyEltsInc;
+ IndexBoundsChecker _checker;
+ IndexSeekPoint _seekPoint;
// Stats
CommonStats _commonStats;
diff --git a/src/mongo/db/exec/index_scan.cpp b/src/mongo/db/exec/index_scan.cpp
index 8df75d2bdda..e8f59c07496 100644
--- a/src/mongo/db/exec/index_scan.cpp
+++ b/src/mongo/db/exec/index_scan.cpp
@@ -37,7 +37,6 @@
#include "mongo/db/exec/scoped_timer.h"
#include "mongo/db/exec/working_set_computed_data.h"
#include "mongo/db/index/index_access_method.h"
-#include "mongo/db/index/index_cursor.h"
#include "mongo/db/index/index_descriptor.h"
#include "mongo/db/query/index_bounds_builder.h"
#include "mongo/util/log.h"
@@ -55,9 +54,6 @@ namespace {
namespace mongo {
- using std::auto_ptr;
- using std::vector;
-
// static
const char* IndexScan::kStageType = "IXSCAN";
@@ -67,16 +63,15 @@ namespace mongo {
const MatchExpression* filter)
: _txn(txn),
_workingSet(workingSet),
+ _iam(params.descriptor->getIndexCatalog()->getIndex(params.descriptor)),
+ _keyPattern(params.descriptor->keyPattern().getOwned()),
_scanState(INITIALIZING),
_filter(filter),
_shouldDedup(true),
+ _forward(params.direction == 1),
_params(params),
_commonStats(kStageType),
- _keyEltsToUse(0),
- _movePastKeyElts(false),
_endKeyInclusive(false) {
- _iam = _params.descriptor->getIndexCatalog()->getIndex(_params.descriptor);
- _keyPattern = _params.descriptor->keyPattern().getOwned();
// We can't always access the descriptor in the call to getStats() so we pull
// any info we need for stats reporting out here.
@@ -86,44 +81,24 @@ namespace mongo {
_specificStats.indexVersion = _params.descriptor->version();
}
- void IndexScan::initIndexScan() {
- // This function transitions from the initializing state to CHECKING_END. If
- // the initialization fails, however, then the state transitions to HIT_END.
- invariant(INITIALIZING == _scanState);
-
- // Perform the possibly heavy-duty initialization of the underlying index cursor.
+ boost::optional<IndexKeyEntry> IndexScan::initIndexScan() {
if (_params.doNotDedup) {
_shouldDedup = false;
}
else {
+ // TODO it is incorrect to rely on this not changing. SERVER-17678
_shouldDedup = _params.descriptor->isMultikey(_txn);
}
- // Set up the index cursor.
- CursorOptions cursorOptions;
-
- if (1 == _params.direction) {
- cursorOptions.direction = CursorOptions::INCREASING;
- }
- else {
- cursorOptions.direction = CursorOptions::DECREASING;
- }
-
- IndexCursor *cursor;
- Status s = _iam->newCursor(_txn, cursorOptions, &cursor);
- verify(s.isOK());
- _indexCursor.reset(cursor);
+ // Perform the possibly heavy-duty initialization of the underlying index cursor.
+ _indexCursor = _iam->newCursor(_txn, _forward);
if (_params.bounds.isSimpleRange) {
// Start at one key, end at another.
- Status status = _indexCursor->seek(_params.bounds.startKey);
- if (!status.isOK()) {
- warning() << "IndexCursor seek failed: " << status.toString();
- _scanState = HIT_END;
- }
- if (!isEOF()) {
- _specificStats.keysExamined = 1;
- }
+ _endKey = _params.bounds.endKey;
+ _endKeyInclusive = _params.bounds.endKeyInclusive;
+ _indexCursor->setEndPosition(_endKey, _endKeyInclusive);
+ return _indexCursor->seek(_params.bounds.startKey, /*inclusive*/true);
}
else {
// For single intervals, we can use an optimized scan which checks against the position
@@ -136,45 +111,20 @@ namespace mongo {
&startKeyInclusive,
&_endKey,
&_endKeyInclusive)) {
- // We want to point at the start key if it's inclusive, and we want to point past
- // the start key if it's exclusive.
- _indexCursor->seek(startKey, !startKeyInclusive);
-
- IndexCursor* endCursor;
- invariant(_iam->newCursor(_txn, cursorOptions, &endCursor).isOK());
- invariant(endCursor);
- _endCursor.reset(endCursor);
- // If the end key is inclusive, we want to point *past* it since that's the end.
- _endCursor->seek(_endKey, _endKeyInclusive);
+ _indexCursor->setEndPosition(_endKey, _endKeyInclusive);
+ return _indexCursor->seek(startKey, startKeyInclusive);
}
else {
_checker.reset(new IndexBoundsChecker(&_params.bounds,
_keyPattern,
_params.direction));
- int nFields = _keyPattern.nFields();
- vector<const BSONElement*> key;
- vector<bool> inc;
- key.resize(nFields);
- inc.resize(nFields);
- if (_checker->getStartKey(&key, &inc)) {
- _indexCursor->seek(key, inc);
- _keyElts.resize(nFields);
- _keyEltsInc.resize(nFields);
- }
- else {
- _scanState = HIT_END;
- }
- }
- }
+ if (!_checker->getStartSeekPoint(&_seekPoint))
+ return boost::none;
- // This method may throw an exception while it's doing initialization. If we've gotten
- // here, then we've done all the initialization without an exception being thrown. This
- // means it is safe to transition to the CHECKING_END state. In error cases, we transition
- // to HIT_END, so we should not change state again here.
- if (HIT_END != _scanState) {
- _scanState = CHECKING_END;
+ return _indexCursor->seek(_seekPoint);
+ }
}
}
@@ -184,120 +134,105 @@ namespace mongo {
// Adds the amount of time taken by work() to executionTimeMillis.
ScopedTimer timer(&_commonStats.executionTimeMillis);
- if (INITIALIZING == _scanState) {
- invariant(NULL == _indexCursor.get());
- try {
- initIndexScan();
- }
- catch (const WriteConflictException& wce) {
- // Release our owned cursors and try again next time.
- _scanState = INITIALIZING;
- _indexCursor.reset();
- _endCursor.reset();
- *out = WorkingSet::INVALID_ID;
- return PlanStage::NEED_YIELD;
+ // Get the next kv pair from the index, if any.
+ boost::optional<IndexKeyEntry> kv;
+ try {
+ switch (_scanState) {
+ case INITIALIZING: kv = initIndexScan(); break;
+ case GETTING_NEXT: kv = _indexCursor->next(); break;
+ case NEED_SEEK: kv = _indexCursor->seek(_seekPoint); break;
+ case HIT_END: return PlanStage::IS_EOF;
}
}
+ catch (const WriteConflictException& wce) {
+ *out = WorkingSet::INVALID_ID;
+ return PlanStage::NEED_YIELD;
+ }
- if (CHECKING_END == _scanState) {
- try {
- checkEnd();
+ if (kv) {
+ // In debug mode, check that the cursor isn't lying to us.
+ if (kDebugBuild && !_endKey.isEmpty()) {
+ int cmp = kv->key.woCompare(_endKey,
+ Ordering::make(_params.descriptor->keyPattern()),
+ /*compareFieldNames*/false);
+ if (cmp == 0) dassert(_endKeyInclusive);
+ dassert(_forward ? cmp <= 0 : cmp >= 0);
+ }
+
+ ++_specificStats.keysExamined;
+ if (_params.maxScan && _specificStats.keysExamined >= _params.maxScan) {
+ kv = boost::none;
}
- catch (const WriteConflictException& wce) {
- // checkEnd only fails in ways that is safe to call again after yielding.
- _scanState = CHECKING_END;
- *out = WorkingSet::INVALID_ID;
- return PlanStage::NEED_YIELD;
+ }
+
+ if (kv && _checker) {
+ switch (_checker->checkKey(kv->key, &_seekPoint)) {
+ case IndexBoundsChecker::VALID:
+ break;
+
+ case IndexBoundsChecker::DONE:
+ // This seems weird but it's the old definition of nscanned.
+ --_specificStats.keysExamined;
+ kv = boost::none;
+ break;
+
+ case IndexBoundsChecker::MUST_ADVANCE:
+ _scanState = NEED_SEEK;
+ _commonStats.needTime++;
+ return PlanStage::NEED_TIME;
}
}
- if (isEOF()) {
+ if (!kv) {
+ _scanState = HIT_END;
_commonStats.isEOF = true;
+ _indexCursor.reset();
return PlanStage::IS_EOF;
}
- if (GETTING_NEXT == _scanState) {
- // Grab the next (key, value) from the index.
- BSONObj keyObj = _indexCursor->getKey();
- RecordId loc = _indexCursor->getValue();
-
- bool filterPasses = Filter::passes(keyObj, _keyPattern, _filter);
- if ( filterPasses ) {
- // We must make a copy of the on-disk data since it can mutate during the execution
- // of this query.
- keyObj = keyObj.getOwned();
- }
-
- _scanState = CHECKING_END;
+ _scanState = GETTING_NEXT;
- // Move to the next result.
- // The underlying IndexCursor points at the *next* thing we want to return. We do this
- // so that if we're scanning an index looking for docs to delete we don't continually
- // clobber the thing we're pointing at.
- try {
- _indexCursor->next();
- }
- catch (const WriteConflictException& wce) {
- // If next throws, it leaves us at the original position.
- invariant(_indexCursor->getValue() == loc);
- *out = WorkingSet::INVALID_ID;
- return PlanStage::NEED_YIELD;
+ if (_shouldDedup) {
+ ++_specificStats.dupsTested;
+ if (!_returned.insert(kv->loc).second) {
+ // We've seen this RecordId before. Skip it this time.
+ ++_specificStats.dupsDropped;
+ ++_commonStats.needTime;
+ return PlanStage::NEED_TIME;
}
+ }
- if (_shouldDedup) {
- ++_specificStats.dupsTested;
- if (_returned.end() != _returned.find(loc)) {
- ++_specificStats.dupsDropped;
- ++_commonStats.needTime;
- return PlanStage::NEED_TIME;
- }
- else {
- _returned.insert(loc);
- }
+ if (_filter) {
+ if (!Filter::passes(kv->key, _keyPattern, _filter)) {
+ ++_commonStats.needTime;
+ return PlanStage::NEED_TIME;
}
- if (filterPasses) {
- if (NULL != _filter) {
- ++_specificStats.matchTested;
- }
-
- // Fill out the WSM.
- WorkingSetID id = _workingSet->allocate();
- WorkingSetMember* member = _workingSet->get(id);
- member->loc = loc;
- member->keyData.push_back(IndexKeyDatum(_keyPattern, keyObj, _iam));
- member->state = WorkingSetMember::LOC_AND_IDX;
-
- if (_params.addKeyMetadata) {
- BSONObjBuilder bob;
- bob.appendKeys(_keyPattern, keyObj);
- member->addComputed(new IndexKeyComputedData(bob.obj()));
- }
-
- *out = id;
- ++_commonStats.advanced;
- return PlanStage::ADVANCED;
- }
+ ++_specificStats.matchTested;
}
+
+ if (!kv->key.isOwned()) kv->key = kv->key.getOwned();
- ++_commonStats.needTime;
- return PlanStage::NEED_TIME;
- }
+ // We found something to return, so fill out the WSM.
+ WorkingSetID id = _workingSet->allocate();
+ WorkingSetMember* member = _workingSet->get(id);
+ member->loc = kv->loc;
+ member->keyData.push_back(IndexKeyDatum(_keyPattern, kv->key, _iam));
+ member->state = WorkingSetMember::LOC_AND_IDX;
- bool IndexScan::isEOF() {
- if (INITIALIZING == _scanState) {
- // Have to call work() at least once.
- return false;
+ if (_params.addKeyMetadata) {
+ BSONObjBuilder bob;
+ bob.appendKeys(_keyPattern, kv->key);
+ member->addComputed(new IndexKeyComputedData(bob.obj()));
}
- // If there's a limit on how many keys we can scan, we may be EOF when we hit that.
- if (0 != _params.maxScan) {
- if (_specificStats.keysExamined >= _params.maxScan) {
- return true;
- }
- }
+ *out = id;
+ ++_commonStats.advanced;
+ return PlanStage::ADVANCED;
+ }
- return HIT_END == _scanState || _indexCursor->isEOF();
+ bool IndexScan::isEOF() {
+ return _commonStats.isEOF;
}
void IndexScan::saveState() {
@@ -308,17 +243,14 @@ namespace mongo {
_txn = NULL;
++_commonStats.yields;
+ if (!_indexCursor) return;
- if (HIT_END == _scanState || INITIALIZING == _scanState) { return; }
- if (!_indexCursor->isEOF()) {
- _savedKey = _indexCursor->getKey().getOwned();
- _savedLoc = _indexCursor->getValue();
+ if (_scanState == NEED_SEEK) {
+ _indexCursor->saveUnpositioned();
+ return;
}
- _indexCursor->savePosition();
- if (_endCursor) {
- _endCursor->savePosition();
- }
+ _indexCursor->savePositioned();
}
void IndexScan::restoreState(OperationContext* opCtx) {
@@ -326,61 +258,7 @@ namespace mongo {
_txn = opCtx;
++_commonStats.unyields;
- if (HIT_END == _scanState || INITIALIZING == _scanState) { return; }
-
- // We can have a valid position before we check isEOF(), restore the position, and then be
- // EOF upon restore.
- if (!_indexCursor->restorePosition( opCtx ).isOK() || _indexCursor->isEOF()) {
- _scanState = HIT_END;
- return;
- }
-
- if (_endCursor) {
- // Single interval case.
- if (!_endCursor->restorePosition(opCtx).isOK()) {
- _scanState = HIT_END;
- return;
- }
-
- // If we were EOF when we yielded, we don't always want to have '_indexCursor' run until
- // EOF. New documents may have been inserted after our end key, and our end marker may
- // be before them.
- //
- // As an example, say we're counting from 5 to 10 and the index only has keys for 6, 7,
- // 8, and 9. '_indexCursor' will point at key 6 at the start and '_endCursor' will be
- // EOF. If we insert a document with key 11 during a yield, we need to relocate
- // '_endCursor' to point at the new key as the end key of our scan.
- _endCursor->seek(_endKey, _endKeyInclusive);
-
- // It is possible that the re-positioning of the end cursor above will move the end
- // cursor so that it is on the other side of the scanning cursor. If this happens, the
- // scan is over, so we transition to HIT_END state.
- //
- // Example:
- // Suppose we're counting from 5 to 10 and the index has keys 6 and 15. The end
- // cursor will initially point at 15. Say that the scanning cursor advances, returning
- // key 6 and now points at 15. Then the index scan state is saved. While saved,
- // key 11 is inserted. The end cursor will seek to point at key 11. If we didn't have
- // the check below, then the scan could erroneously return key 15, which is not in the
- // desired range of [5, 10].
- int cmp = _endKey.woCompare(_indexCursor->getKey(), _keyPattern);
- const bool cursorPastEndKey = (_params.direction == 1 ? cmp < 0 : cmp > 0);
- const bool cursorAtExclusiveEndKey = (cmp == 0 && !_endKeyInclusive);
- if (cursorPastEndKey || cursorAtExclusiveEndKey) {
- _scanState = HIT_END;
- return;
- }
- }
-
- if (!_savedKey.binaryEqual(_indexCursor->getKey())
- || _savedLoc != _indexCursor->getValue()) {
- // Our restored position isn't the same as the saved position. When we call work()
- // again we want to return where we currently point, not past it.
- ++_specificStats.yieldMovedCursor;
-
- // Our restored position might be past endKey, see if we've hit the end.
- _scanState = CHECKING_END;
- }
+ if (_indexCursor) _indexCursor->restore(opCtx);
}
void IndexScan::invalidate(OperationContext* txn, const RecordId& dl, InvalidationType type) {
@@ -401,91 +279,13 @@ namespace mongo {
}
}
- void IndexScan::checkEnd() {
- if (isEOF()) {
- _commonStats.isEOF = true;
- return;
- }
-
- if (_params.bounds.isSimpleRange) {
- _scanState = GETTING_NEXT;
-
- // "Normal" start -> end scanning.
- verify(NULL == _endCursor);
- verify(NULL == _checker.get());
-
- // If there is an empty endKey we will scan until we run out of index to scan over.
- if (_params.bounds.endKey.isEmpty()) { return; }
-
- int cmp = sgn(_params.bounds.endKey.woCompare(_indexCursor->getKey(), _keyPattern));
-
- if ((cmp != 0 && cmp != _params.direction)
- || (cmp == 0 && !_params.bounds.endKeyInclusive)) {
- _scanState = HIT_END;
- }
- else {
- ++_specificStats.keysExamined;
- }
- }
- else if (_endCursor) {
- // We're in the single interval case, and we have a cursor pointing to the end position.
- // We can check whether the scan is over by seeing if our cursor points at the same
- // thing as the end cursor.
- _scanState = GETTING_NEXT;
- invariant(!_checker);
-
- if (_endCursor->pointsAt(*_indexCursor)) {
- _scanState = HIT_END;
- }
- else {
- ++_specificStats.keysExamined;
- }
- }
- else {
- verify(NULL != _indexCursor);
- verify(NULL != _checker.get());
-
- IndexBoundsChecker::KeyState keyState;
- keyState = _checker->checkKey(_indexCursor->getKey(),
- &_keyEltsToUse,
- &_movePastKeyElts,
- &_keyElts,
- &_keyEltsInc);
-
- if (IndexBoundsChecker::DONE == keyState) {
- _scanState = HIT_END;
- return;
- }
-
- // This seems weird but it's the old definition of nscanned.
- ++_specificStats.keysExamined;
-
- if (IndexBoundsChecker::VALID == keyState) {
- _scanState = GETTING_NEXT;
- return;
- }
-
- verify(IndexBoundsChecker::MUST_ADVANCE == keyState);
- _indexCursor->skip(_indexCursor->getKey(), _keyEltsToUse, _movePastKeyElts,
- _keyElts, _keyEltsInc);
-
- // Must check underlying cursor EOF after every cursor movement.
- if (_indexCursor->isEOF()) {
- _scanState = HIT_END;
- return;
- }
- }
- }
-
- vector<PlanStage*> IndexScan::getChildren() const {
- vector<PlanStage*> empty;
- return empty;
+ std::vector<PlanStage*> IndexScan::getChildren() const {
+ return {};
}
PlanStageStats* IndexScan::getStats() {
// WARNING: this could be called even if the collection was dropped. Do not access any
// catalog information here.
- _commonStats.isEOF = isEOF();
// Add a BSON representation of the filter to the stats tree, if there is one.
if (NULL != _filter) {
@@ -503,7 +303,7 @@ namespace mongo {
_specificStats.direction = _params.direction;
}
- auto_ptr<PlanStageStats> ret(new PlanStageStats(_commonStats, STAGE_IXSCAN));
+ std::unique_ptr<PlanStageStats> ret(new PlanStageStats(_commonStats, STAGE_IXSCAN));
ret->specific.reset(new IndexScanStats(_specificStats));
return ret.release();
}
diff --git a/src/mongo/db/exec/index_scan.h b/src/mongo/db/exec/index_scan.h
index db15d43a0bd..a2cfd4bad45 100644
--- a/src/mongo/db/exec/index_scan.h
+++ b/src/mongo/db/exec/index_scan.h
@@ -36,12 +36,13 @@
#include "mongo/db/matcher/expression.h"
#include "mongo/db/query/index_bounds.h"
#include "mongo/db/record_id.h"
+#include "mongo/db/storage/index_entry_comparison.h"
+#include "mongo/db/storage/sorted_data_interface.h"
#include "mongo/platform/unordered_set.h"
namespace mongo {
class IndexAccessMethod;
- class IndexCursor;
class IndexDescriptor;
class WorkingSet;
@@ -84,8 +85,8 @@ namespace mongo {
// Need to initialize the underlying index traversal machinery.
INITIALIZING,
- // Skipping keys in order to check whether we have reached the end.
- CHECKING_END,
+ // Skipping keys as directed by the _checker.
+ NEED_SEEK,
// Retrieving the next key, and applying the filter if necessary.
GETTING_NEXT,
@@ -121,23 +122,20 @@ namespace mongo {
private:
/**
- * Initialize the underlying IndexCursor, grab information from the catalog for stats.
+ * Initialize the underlying index Cursor, returning first result if any.
*/
- void initIndexScan();
-
- /** See if the cursor is pointing at or past _endKey, if _endKey is non-empty. */
- void checkEnd();
+ boost::optional<IndexKeyEntry> initIndexScan();
// transactional context for read locks. Not owned by us
OperationContext* _txn;
- // The WorkingSet we annotate with results. Not owned by us.
- WorkingSet* _workingSet;
+ // The WorkingSet we fill with results. Not owned by us.
+ WorkingSet* const _workingSet;
// Index access.
- const IndexAccessMethod* _iam; // owned by Collection -> IndexCatalog
- boost::scoped_ptr<IndexCursor> _indexCursor;
- BSONObj _keyPattern;
+ const IndexAccessMethod* const _iam; // owned by Collection -> IndexCatalog
+ std::unique_ptr<SortedDataInterface::Cursor> _indexCursor;
+ const BSONObj _keyPattern;
// Keeps track of what work we need to do next.
ScanState _scanState;
@@ -145,50 +143,40 @@ namespace mongo {
// Contains expressions only over fields in the index key. We assume this is built
// correctly by whomever creates this class.
// The filter is not owned by us.
- const MatchExpression* _filter;
+ const MatchExpression* const _filter;
// Could our index have duplicates? If so, we use _returned to dedup.
bool _shouldDedup;
unordered_set<RecordId, RecordId::Hasher> _returned;
- // For yielding.
- BSONObj _savedKey;
- RecordId _savedLoc;
-
- IndexScanParams _params;
+ const bool _forward;
+ const IndexScanParams _params;
// Stats
CommonStats _commonStats;
IndexScanStats _specificStats;
//
- // If we aren't doing a "simple" index scan, we make a decision to employ one of two
- // different algorithms for determining when the index scan has reached the end:
+ // This class employs one of two different algorithms for determining when the index scan
+ // has reached the end:
//
//
- // 1) If the index scan is not a single interval, then we use an IndexBoundsChecker to
- // determine when the index scan has reached the end. In this case, _checker will be
- // non-NULL (and _endCursor will be NULL).
+ // 1) If the index scan is not a single contiguous interval, then we use an
+ // IndexBoundsChecker to determine which keys to return and when to stop scanning.
+ // In this case, _checker will be non-NULL.
//
boost::scoped_ptr<IndexBoundsChecker> _checker;
- int _keyEltsToUse;
- bool _movePastKeyElts;
- std::vector<const BSONElement*> _keyElts;
- std::vector<bool> _keyEltsInc;
+ IndexSeekPoint _seekPoint;
//
- // 2) If the index scan is a single interval, then the scan can execute faster by
- // checking for the end via comparison against an end cursor, rather than repeatedly
- // doing BSON compares against scanned keys. In this case, _endCursor will be non-NULL
- // (and _checker will be NULL).
+ // 2) If the index scan is a single contiguous interval, then the scan can execute faster by
+ // letting the index cursor tell us when it hits the end, rather than repeatedly doing
+ // BSON compares against scanned keys. In this case _checker will be NULL.
//
- // The end cursor.
- boost::scoped_ptr<IndexCursor> _endCursor;
-
- // The key that the end cursor should point to.
+ // The key that the index cursor should stop on/after.
BSONObj _endKey;
// Is the end key included in the range?
diff --git a/src/mongo/db/exec/plan_stats.h b/src/mongo/db/exec/plan_stats.h
index 6e1069f1609..d823c9ee4d2 100644
--- a/src/mongo/db/exec/plan_stats.h
+++ b/src/mongo/db/exec/plan_stats.h
@@ -364,7 +364,6 @@ namespace mongo {
IndexScanStats() : indexVersion(0),
direction(1),
isMultiKey(false),
- yieldMovedCursor(0),
dupsTested(0),
dupsDropped(0),
seenInvalidated(0),
@@ -402,7 +401,6 @@ namespace mongo {
// Whether this index is over a field that contain array values.
bool isMultiKey;
- size_t yieldMovedCursor;
size_t dupsTested;
size_t dupsDropped;
diff --git a/src/mongo/db/index/2d_access_method.h b/src/mongo/db/index/2d_access_method.h
index 6a97ab720c5..208df9235cd 100644
--- a/src/mongo/db/index/2d_access_method.h
+++ b/src/mongo/db/index/2d_access_method.h
@@ -36,7 +36,6 @@
namespace mongo {
class IndexCatalogEntry;
- class IndexCursor;
class IndexDescriptor;
struct TwoDIndexingParams;
diff --git a/src/mongo/db/index/btree_access_method.h b/src/mongo/db/index/btree_access_method.h
index 4278471d782..a19858ca2b4 100644
--- a/src/mongo/db/index/btree_access_method.h
+++ b/src/mongo/db/index/btree_access_method.h
@@ -38,7 +38,6 @@
namespace mongo {
- class IndexCursor;
class IndexDescriptor;
/**
diff --git a/src/mongo/db/index/index_access_method.cpp b/src/mongo/db/index/index_access_method.cpp
index b01a1eafded..1f373090288 100644
--- a/src/mongo/db/index/index_access_method.cpp
+++ b/src/mongo/db/index/index_access_method.cpp
@@ -166,10 +166,10 @@ namespace mongo {
}
}
- Status IndexAccessMethod::newCursor(OperationContext* txn, const CursorOptions& opts,
- IndexCursor** out) const {
- *out = new IndexCursor(_newInterface->newCursor(txn, opts.direction));
- return Status::OK();
+ std::unique_ptr<SortedDataInterface::Cursor> IndexAccessMethod::newCursor(
+ OperationContext* txn,
+ bool isForward) const {
+ return _newInterface->newCursor(txn, isForward);
}
// Remove the provided doc from the index.
@@ -219,9 +219,9 @@ namespace mongo {
BSONObjSet keys;
getKeys(obj, &keys);
- boost::scoped_ptr<SortedDataInterface::Cursor> cursor(_newInterface->newCursor(txn, 1));
+ std::unique_ptr<SortedDataInterface::Cursor> cursor(_newInterface->newCursor(txn));
for (BSONObjSet::const_iterator i = keys.begin(); i != keys.end(); ++i) {
- cursor->locate(*i, RecordId());
+ cursor->seekExact(*i);
}
return Status::OK();
@@ -233,22 +233,18 @@ namespace mongo {
}
RecordId IndexAccessMethod::findSingle(OperationContext* txn, const BSONObj& key) const {
- boost::scoped_ptr<SortedDataInterface::Cursor> cursor(_newInterface->newCursor(txn, 1));
- cursor->locate(key, RecordId::min());
-
- // A null bucket means the key wasn't found (nor was anything found after it).
- if (cursor->isEOF()) {
- return RecordId();
- }
-
- // We found something but it could be a key after 'key'. Examine what we're pointing at.
- if (0 != key.woCompare(cursor->getKey(), BSONObj(), false)) {
- // If the keys don't match, return "not found."
- return RecordId();
+ std::unique_ptr<SortedDataInterface::Cursor> cursor(_newInterface->newCursor(txn));
+ const auto requestedInfo = kDebugBuild ? SortedDataInterface::Cursor::kKeyAndLoc
+ : SortedDataInterface::Cursor::kWantLoc;
+ if (auto kv = cursor->seekExact(key, requestedInfo)) {
+ // StorageEngine should guarantee these.
+ dassert(!kv->loc.isNull());
+ dassert(kv->key.woCompare(key, /*order*/BSONObj(), /*considerFieldNames*/false) == 0);
+
+ return kv->loc;
}
- // Return the RecordId found.
- return cursor->getRecordId();
+ return RecordId();
}
Status IndexAccessMethod::validate(OperationContext* txn, bool full, int64_t* numKeys,
diff --git a/src/mongo/db/index/index_access_method.h b/src/mongo/db/index/index_access_method.h
index fea0c8e4a10..3f311128a2e 100644
--- a/src/mongo/db/index/index_access_method.h
+++ b/src/mongo/db/index/index_access_method.h
@@ -31,7 +31,6 @@
#include <memory>
#include "mongo/base/disallow_copying.h"
-#include "mongo/db/index/index_cursor.h"
#include "mongo/db/index/index_descriptor.h"
#include "mongo/db/jsobj.h"
#include "mongo/db/operation_context.h"
@@ -120,10 +119,10 @@ namespace mongo {
Status update(OperationContext* txn, const UpdateTicket& ticket, int64_t* numUpdated);
/**
- * Fills in '*out' with an IndexCursor. Return a status indicating success or reason of
- * failure. If the latter, '*out' contains NULL. See index_cursor.h for IndexCursor usage.
+ * Returns an unpositioned cursor over 'this' index.
*/
- Status newCursor(OperationContext* txn, const CursorOptions& opts, IndexCursor** out) const;
+ std::unique_ptr<SortedDataInterface::Cursor> newCursor(OperationContext* txn,
+ bool isForward = true) const;
// ------ index level operations ------
@@ -177,7 +176,6 @@ namespace mongo {
*/
long long getSpaceUsedBytes( OperationContext* txn ) const;
- // XXX: consider migrating callers to use IndexCursor instead
RecordId findSingle( OperationContext* txn, const BSONObj& key ) const;
//
diff --git a/src/mongo/db/index/index_cursor.cpp b/src/mongo/db/index/index_cursor.cpp
deleted file mode 100644
index de09e7c1d3d..00000000000
--- a/src/mongo/db/index/index_cursor.cpp
+++ /dev/null
@@ -1,123 +0,0 @@
-/**
-* Copyright (C) 2013 10gen Inc.
-*
-* This program is free software: you can redistribute it and/or modify
-* it under the terms of the GNU Affero General Public License, version 3,
-* as published by the Free Software Foundation.
-*
-* This program is distributed in the hope that it will be useful,
-* but WITHOUT ANY WARRANTY; without even the implied warranty of
-* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-* GNU Affero General Public License for more details.
-*
-* You should have received a copy of the GNU Affero General Public License
-* along with this program. If not, see <http://www.gnu.org/licenses/>.
-*
-* As a special exception, the copyright holders give permission to link the
-* code of portions of this program with the OpenSSL library under certain
-* conditions as described in each individual source file and distribute
-* linked combinations including the program with the OpenSSL library. You
-* must comply with the GNU Affero General Public License in all respects for
-* all of the code used other than as permitted herein. If you modify file(s)
-* with this exception, you may extend this exception to your version of the
-* file(s), but you are not obligated to do so. If you do not wish to do so,
-* delete this exception statement from your version. If you delete this
-* exception statement from all source files in the program, then also delete
-* it in the license file.
-*/
-
-#include "mongo/db/index/index_cursor.h"
-
-#include <vector>
-
-#include "mongo/base/status.h"
-#include "mongo/db/index/index_cursor.h"
-#include "mongo/db/index/index_descriptor.h"
-#include "mongo/db/jsobj.h"
-#include "mongo/db/record_id.h"
-#include "mongo/platform/unordered_set.h"
-
-namespace mongo {
-
- using std::string;
- using std::vector;
-
- IndexCursor::IndexCursor(SortedDataInterface::Cursor* cursor) : _cursor(cursor) { }
-
- bool IndexCursor::isEOF() const { return _cursor->isEOF(); }
-
- Status IndexCursor::seek(const BSONObj& position) {
- _cursor->locate(position,
- 1 == _cursor->getDirection() ? RecordId::min() : RecordId::max());
- return Status::OK();
- }
-
- void IndexCursor::seek(const BSONObj& position, bool afterKey) {
- const bool forward = (1 == _cursor->getDirection());
- _cursor->locate(position, (afterKey == forward) ? RecordId::max() : RecordId::min());
- }
-
- bool IndexCursor::pointsAt(const IndexCursor& other) {
- return _cursor->pointsToSamePlaceAs(*other._cursor);
- }
-
- Status IndexCursor::seek(const vector<const BSONElement*>& position,
- const vector<bool>& inclusive) {
-
- BSONObj emptyObj;
-
- _cursor->customLocate(emptyObj,
- 0,
- false,
- position,
- inclusive);
- return Status::OK();
- }
-
- Status IndexCursor::skip(const BSONObj &keyBegin,
- int keyBeginLen,
- bool afterKey,
- const vector<const BSONElement*>& keyEnd,
- const vector<bool>& keyEndInclusive) {
-
- _cursor->advanceTo(keyBegin,
- keyBeginLen,
- afterKey,
- keyEnd,
- keyEndInclusive);
- return Status::OK();
- }
-
- BSONObj IndexCursor::getKey() const {
- return _cursor->getKey();
- }
-
- RecordId IndexCursor::getValue() const {
- return _cursor->getRecordId();
- }
-
- void IndexCursor::next() {
- advance();
- }
-
- Status IndexCursor::savePosition() {
- _cursor->savePosition();
- return Status::OK();
- }
-
- Status IndexCursor::restorePosition(OperationContext* txn) {
- _cursor->restorePosition(txn);
- return Status::OK();
- }
-
- string IndexCursor::toString() {
- // TODO: is this ever called?
- return "I AM A BTREE INDEX CURSOR!\n";
- }
-
- // Move to the next/prev. key. Used by normal getNext and also skipping unused keys.
- void IndexCursor::advance() {
- _cursor->advance();
- }
-
-} // namespace mongo
diff --git a/src/mongo/db/index/index_cursor.h b/src/mongo/db/index/index_cursor.h
deleted file mode 100644
index deb8969e409..00000000000
--- a/src/mongo/db/index/index_cursor.h
+++ /dev/null
@@ -1,188 +0,0 @@
-/**
-* Copyright (C) 2013 10gen Inc.
-*
-* This program is free software: you can redistribute it and/or modify
-* it under the terms of the GNU Affero General Public License, version 3,
-* as published by the Free Software Foundation.
-*
-* This program is distributed in the hope that it will be useful,
-* but WITHOUT ANY WARRANTY; without even the implied warranty of
-* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-* GNU Affero General Public License for more details.
-*
-* You should have received a copy of the GNU Affero General Public License
-* along with this program. If not, see <http://www.gnu.org/licenses/>.
-*
-* As a special exception, the copyright holders give permission to link the
-* code of portions of this program with the OpenSSL library under certain
-* conditions as described in each individual source file and distribute
-* linked combinations including the program with the OpenSSL library. You
-* must comply with the GNU Affero General Public License in all respects for
-* all of the code used other than as permitted herein. If you modify file(s)
-* with this exception, you may extend this exception to your version of the
-* file(s), but you are not obligated to do so. If you do not wish to do so,
-* delete this exception statement from your version. If you delete this
-* exception statement from all source files in the program, then also delete
-* it in the license file.
-*/
-
-#pragma once
-
-#include <memory>
-#include <vector>
-
-#include "mongo/base/status.h"
-#include "mongo/db/index/index_cursor.h"
-#include "mongo/db/index/index_descriptor.h"
-#include "mongo/db/jsobj.h"
-#include "mongo/db/record_id.h"
-#include "mongo/db/storage/sorted_data_interface.h"
-
-namespace mongo {
-
- struct CursorOptions;
-
- /**
- * TODO remove this class in favor of direct usage of SortedDataInterface::Cursor.
- *
- * An IndexCursor is the interface through which one traverses the entries of a given
- * index. The internal structure of an index is kept isolated.
- *
- * The cursor must be initialized by seek()ing to a given entry in the index. The index is
- * traversed by calling next() or skip()-ping ahead.
- *
- * The set of predicates a given index can understand is known a priori. These predicates may
- * be simple (a key location for a Btree index) or rich ($within for a geo index).
- *
- * Locking is the responsibility of the caller. The IndexCursor keeps state. If the caller
- * wishes to yield or unlock, it must call savePosition() first. When it decides to unyield it
- * must call restorePosition(). The cursor may be EOF after a restorePosition().
- */
- class IndexCursor {
- public:
-
- /**
- * A cursor doesn't point anywhere by default. You must seek to the start position.
- * The provided position must be a predicate that the index understands. The
- * predicate must describe one value, though there may be several instances
- *
- * Possible return values:
- * 1. Success: seeked to the position.
- * 2. Success: seeked to 'closest' key oriented according to the cursor's direction.
- * 3. Error: can't seek to the position.
- */
- Status seek(const BSONObj& position);
-
- Status seek(const std::vector<const BSONElement*>& position,
- const std::vector<bool>& inclusive);
-
- /**
- * Seek to the key 'position'. If 'afterKey' is true, seeks to the first
- * key that is oriented after 'position'.
- *
- * Btree-specific.
- */
- void seek(const BSONObj& position, bool afterKey);
-
- Status skip(const BSONObj& keyBegin,
- int keyBeginLen,
- bool afterKey,
- const std::vector<const BSONElement*>& keyEnd,
- const std::vector<bool>& keyEndInclusive);
-
- /**
- * Returns true if 'this' points at the same exact key as 'other'.
- * Returns false otherwise.
- */
- bool pointsAt(const IndexCursor& other);
-
- //
- // Iteration support
- //
-
- // Are we out of documents?
- bool isEOF() const;
-
- // Move to the next key/value pair. Assumes !isEOF().
- void next();
-
- //
- // Accessors
- //
-
- // Current key we point at. Assumes !isEOF().
- BSONObj getKey() const;
-
- // Current value we point at. Assumes !isEOF().
- RecordId getValue() const;
-
- //
- // Yielding support
- //
-
- /**
- * Yielding semantics:
- * If the entry that a cursor points at is not deleted during a yield, the cursor will
- * point at that entry after a restore.
- * An entry inserted during a yield may or may not be returned by an in-progress scan.
- * An entry deleted during a yield may or may not be returned by an in-progress scan.
- * An entry modified during a yield may or may not be returned by an in-progress scan.
- * An entry that is not inserted or deleted during a yield will be returned, and only once.
- * If the index returns entries in a given order (Btree), this order will be mantained even
- * if the entry corresponding to a saved position is deleted during a yield.
- */
-
- /**
- * Save our current position in the index.
- */
- Status savePosition();
-
- /**
- * Restore the saved position. Errors if there is no saved position.
- * The cursor may be EOF after a restore.
- */
- Status restorePosition(OperationContext* txn);
-
- /**
- * Return a std::string describing the cursor.
- */
- std::string toString();
-
- private:
- // We keep the constructor private and only allow the AM to create us.
- friend class IndexAccessMethod;
-
- /**
- * interface is an abstraction to hide the fact that we have two types of Btrees.
- *
- * Intentionally private, we're friends with the only class allowed to call it.
- */
- IndexCursor(SortedDataInterface::Cursor* cursor);
-
- bool isSavedPositionValid();
-
- /**
- * Move to the next (or previous depending on the direction) key. Used by normal getNext
- * and also skipping unused keys.
- */
- void advance();
-
- const std::unique_ptr<SortedDataInterface::Cursor> _cursor;
- };
-
- // All the options we might want to set on a cursor.
- struct CursorOptions {
- // Set the direction of the scan. Ignored if the cursor doesn't have directions (geo).
- enum Direction {
- DECREASING = -1,
- INCREASING = 1,
- };
-
- Direction direction;
-
- // 2d indices need to know exactly how many results you want beforehand.
- // Ignored by every other index.
- int numWanted;
- };
-
-} // namespace mongo
diff --git a/src/mongo/db/index/s2_access_method.h b/src/mongo/db/index/s2_access_method.h
index 85f4af8e5b0..9ce655e47f7 100644
--- a/src/mongo/db/index/s2_access_method.h
+++ b/src/mongo/db/index/s2_access_method.h
@@ -36,9 +36,6 @@
namespace mongo {
- class IndexCursor;
- struct S2IndexingParams;
-
class S2AccessMethod : public IndexAccessMethod {
public:
S2AccessMethod(IndexCatalogEntry* btreeState, SortedDataInterface* btree);
diff --git a/src/mongo/db/query/index_bounds.cpp b/src/mongo/db/query/index_bounds.cpp
index b88bbe67f42..f7592994e29 100644
--- a/src/mongo/db/query/index_bounds.cpp
+++ b/src/mongo/db/query/index_bounds.cpp
@@ -291,17 +291,18 @@ namespace mongo {
}
}
- bool IndexBoundsChecker::getStartKey(vector<const BSONElement*>* valueOut,
- vector<bool>* inclusiveOut) {
- verify(valueOut->size() == _bounds->fields.size());
- verify(inclusiveOut->size() == _bounds->fields.size());
+ bool IndexBoundsChecker::getStartSeekPoint(IndexSeekPoint* out) {
+ out->prefixLen = 0;
+ out->prefixExclusive = false;
+ out->keySuffix.resize(_bounds->fields.size());
+ out->suffixInclusive.resize(_bounds->fields.size());
for (size_t i = 0; i < _bounds->fields.size(); ++i) {
if (0 == _bounds->fields[i].intervals.size()) {
return false;
}
- (*valueOut)[i] = &_bounds->fields[i].intervals[0].start;
- (*inclusiveOut)[i] = _bounds->fields[i].intervals[0].startInclusive;
+ out->keySuffix[i] = &_bounds->fields[i].intervals[0].start;
+ out->suffixInclusive[i] = _bounds->fields[i].intervals[0].startInclusive;
}
return true;
@@ -374,13 +375,10 @@ namespace mongo {
}
IndexBoundsChecker::KeyState IndexBoundsChecker::checkKey(const BSONObj& key,
- int* keyEltsToUse,
- bool* movePastKeyElts,
- vector<const BSONElement*>* out,
- vector<bool>* incOut) {
+ IndexSeekPoint* out) {
verify(_curInterval.size() > 0);
- verify(out->size() == _curInterval.size());
- verify(incOut->size() == _curInterval.size());
+ out->keySuffix.resize(_curInterval.size());
+ out->suffixInclusive.resize(_curInterval.size());
// It's useful later to go from a field number to the value for that field. Store these.
// TODO: on optimization pass, populate the vector as-needed and keep the vector around as a
@@ -419,13 +417,14 @@ namespace mongo {
// Field number 'firstNonContainedField' of the index key is before all current intervals.
if (BEHIND == orientation) {
// Tell the caller to move forward to the start of the current interval.
- *keyEltsToUse = firstNonContainedField;
- *movePastKeyElts = false;
+ out->keyPrefix = key.getOwned();
+ out->prefixLen = firstNonContainedField;
+ out->prefixExclusive = false;
for (size_t j = firstNonContainedField; j < _curInterval.size(); ++j) {
const OrderedIntervalList& oil = _bounds->fields[j];
- (*out)[j] = &oil.intervals[_curInterval[j]].start;
- (*incOut)[j] = oil.intervals[_curInterval[j]].startInclusive;
+ out->keySuffix[j] = &oil.intervals[_curInterval[j]].start;
+ out->suffixInclusive[j] = oil.intervals[_curInterval[j]].startInclusive;
}
return MUST_ADVANCE;
@@ -463,13 +462,13 @@ namespace mongo {
_curInterval[i] = 0;
}
- *keyEltsToUse = firstNonContainedField;
- *movePastKeyElts = false;
-
+ out->keyPrefix = key.getOwned();
+ out->prefixLen = firstNonContainedField;
+ out->prefixExclusive = false;
for (size_t i = firstNonContainedField; i < _curInterval.size(); ++i) {
const OrderedIntervalList& oil = _bounds->fields[i];
- (*out)[i] = &oil.intervals[_curInterval[i]].start;
- (*incOut)[i] = oil.intervals[_curInterval[i]].startInclusive;
+ out->keySuffix[i] = &oil.intervals[_curInterval[i]].start;
+ out->suffixInclusive[i] = oil.intervals[_curInterval[i]].startInclusive;
}
return MUST_ADVANCE;
@@ -486,8 +485,9 @@ namespace mongo {
return DONE;
}
- *keyEltsToUse = firstNonContainedField;
- *movePastKeyElts = true;
+ out->keyPrefix = key.getOwned();
+ out->prefixLen = firstNonContainedField;
+ out->prefixExclusive = true;
for (size_t i = firstNonContainedField; i < _curInterval.size(); ++i) {
_curInterval[i] = 0;
diff --git a/src/mongo/db/query/index_bounds.h b/src/mongo/db/query/index_bounds.h
index e99754680b9..d1e140f3932 100644
--- a/src/mongo/db/query/index_bounds.h
+++ b/src/mongo/db/query/index_bounds.h
@@ -33,6 +33,7 @@
#include "mongo/db/jsobj.h"
#include "mongo/db/query/interval.h"
+#include "mongo/db/storage/index_entry_comparison.h"
namespace mongo {
@@ -125,12 +126,15 @@ namespace mongo {
*/
IndexBoundsChecker(const IndexBounds* bounds, const BSONObj& keyPattern, int direction);
+
/**
- * Get the key that we should with.
+ * Get the IndexSeekPoint that we should with.
*
- * Returns true if there is a valid start key. Returns false otherwise.
+ * Returns false if there are no possible index entries that match the bounds. In this case
+ * there is no valid start point to seek to so out will not be filled out and the caller
+ * should emit no results.
*/
- bool getStartKey(std::vector<const BSONElement*>* valueOut, std::vector<bool>* inclusiveOut);
+ bool getStartSeekPoint(IndexSeekPoint* out);
/**
* The states of a key from an index scan. See checkKey below.
@@ -157,8 +161,8 @@ namespace mongo {
* key.
*
* 2. The key is not in our bounds but has not exceeded the maximum value in our bounds.
- * Returns MUST_ADVANCE. Caller must advance to the key provided in the out parameters and
- * call checkKey again.
+ * Returns MUST_ADVANCE. Caller must advance to the query provided in the out parameters
+ * and call checkKey again.
*
* 3. The key is past our bounds. Returns DONE. No further keys will satisfy the bounds
* and the caller should stop.
@@ -167,28 +171,11 @@ namespace mongo {
* out and incOut must already be resized to have as many elements as the key has fields.
*
* In parameters:
- * key is the index key.
- *
- * Out parameters, only valid if we return MUST_ADVANCE:
- *
- * keyEltsToUse: The key that the caller should advance to is made up of the first
- * 'keyEltsToUse' of the key that was provided.
- *
- * movePastKeyElts: If true, the caller must only use the first 'keyEltsToUse' of the
- * provided key to form its key. It moves to the first key that is after
- * the key formed by only using those elements.
- *
- * out: If keyEltsToUse is less than the number of indexed fields in the key, the remaining
- * fields are taken from here. out is not filled from the start but from the position
- * that the key corresponds to. An example: If keyEltsToUse is 1, movePastKeyElts is
- * false, and the index we're iterating over has two fields, out[1] will have the value
- * for the second field.
+ * currentKey is the index key.
*
- * incOut: If the i-th element is false, seek to the key *after* the i-th element of out.
- * If the i-th element is true, seek to the i-th element of out.
+ * Out parameter only valid if we return MUST_ADVANCE.
*/
- KeyState checkKey(const BSONObj& key, int* keyEltsToUse, bool* movePastKeyElts,
- std::vector<const BSONElement*>* out, std::vector<bool>* incOut);
+ KeyState checkKey(const BSONObj& currentKey, IndexSeekPoint* query);
/**
* Relative position of a key to an interval.
diff --git a/src/mongo/db/query/index_bounds_test.cpp b/src/mongo/db/query/index_bounds_test.cpp
index 2e285f227ce..d5cc470b3af 100644
--- a/src/mongo/db/query/index_bounds_test.cpp
+++ b/src/mongo/db/query/index_bounds_test.cpp
@@ -42,9 +42,6 @@ using namespace mongo;
namespace {
- using std::string;
- using std::vector;
-
//
// Validation
//
@@ -268,15 +265,13 @@ namespace {
bounds.fields.push_back(barList);
IndexBoundsChecker it(&bounds, BSON("foo" << 1 << "bar" << 1), 1);
- vector<const BSONElement*> elt(2);
- vector<bool> inc(2);
-
- it.getStartKey(&elt, &inc);
+ IndexSeekPoint seekPoint;
+ it.getStartSeekPoint(&seekPoint);
- ASSERT_EQUALS(elt[0]->numberInt(), 7);
- ASSERT_EQUALS(inc[0], true);
- ASSERT_EQUALS(elt[1]->numberInt(), 0);
- ASSERT_EQUALS(inc[1], false);
+ ASSERT_EQUALS(seekPoint.keySuffix[0]->numberInt(), 7);
+ ASSERT_EQUALS(seekPoint.suffixInclusive[0], true);
+ ASSERT_EQUALS(seekPoint.keySuffix[1]->numberInt(), 0);
+ ASSERT_EQUALS(seekPoint.suffixInclusive[1], false);
}
TEST(IndexBoundsCheckerTest, CheckEnd) {
@@ -292,52 +287,32 @@ namespace {
bounds.fields.push_back(barList);
IndexBoundsChecker it(&bounds, BSON("foo" << 1 << "bar" << 1), 1);
- int keyEltsToUse;
- bool movePastKeyElts;
- vector<const BSONElement*> elt(2);
- vector<bool> inc(2);
-
+ IndexSeekPoint seekPoint;
IndexBoundsChecker::KeyState state;
// Start at something in our range.
- state = it.checkKey(BSON("" << 7 << "" << 1),
- &keyEltsToUse,
- &movePastKeyElts,
- &elt,
- &inc);
+ state = it.checkKey(BSON("" << 7 << "" << 1), &seekPoint);
ASSERT_EQUALS(state, IndexBoundsChecker::VALID);
// Second field moves past the end, but we're not done, since there's still an interval in
// the previous field that the key hasn't advanced to.
- state = it.checkKey(BSON("" << 20 << "" << 5),
- &keyEltsToUse,
- &movePastKeyElts,
- &elt,
- &inc);
+ state = it.checkKey(BSON("" << 20 << "" << 5), &seekPoint);
ASSERT_EQUALS(state, IndexBoundsChecker::MUST_ADVANCE);
- ASSERT_EQUALS(keyEltsToUse, 1);
- ASSERT(movePastKeyElts);
+ ASSERT_EQUALS(seekPoint.prefixLen, 1);
+ ASSERT(seekPoint.prefixExclusive);
// The next index key is in the second interval for 'foo' and there is a valid interval for
// 'bar'.
- state = it.checkKey(BSON("" << 22 << "" << 1),
- &keyEltsToUse,
- &movePastKeyElts,
- &elt,
- &inc);
+ state = it.checkKey(BSON("" << 22 << "" << 1), &seekPoint);
ASSERT_EQUALS(state, IndexBoundsChecker::VALID);
// The next index key is very close to the end of the open interval for foo, and it's past
// the interval for 'bar'. Since the interval for foo is open, we are asked to move
// forward, since we possibly could.
- state = it.checkKey(BSON("" << 29.9 << "" << 5),
- &keyEltsToUse,
- &movePastKeyElts,
- &elt,
- &inc);
+ state = it.checkKey(BSON("" << 29.9 << "" << 5), &seekPoint);
ASSERT_EQUALS(state, IndexBoundsChecker::MUST_ADVANCE);
- ASSERT_EQUALS(keyEltsToUse, 1);
- ASSERT(movePastKeyElts);
+ ASSERT_EQUALS(seekPoint.prefixLen, 1);
+ ASSERT(seekPoint.prefixExclusive);
}
TEST(IndexBoundsCheckerTest, MoveIntervalForwardToNextInterval) {
@@ -353,35 +328,23 @@ namespace {
bounds.fields.push_back(barList);
IndexBoundsChecker it(&bounds, BSON("foo" << 1 << "bar" << 1), 1);
- int keyEltsToUse;
- bool movePastKeyElts;
- vector<const BSONElement*> elt(2);
- vector<bool> inc(2);
-
+ IndexSeekPoint seekPoint;
IndexBoundsChecker::KeyState state;
// Start at something in our range.
- state = it.checkKey(BSON("" << 7 << "" << 1),
- &keyEltsToUse,
- &movePastKeyElts,
- &elt,
- &inc);
+ state = it.checkKey(BSON("" << 7 << "" << 1), &seekPoint);
ASSERT_EQUALS(state, IndexBoundsChecker::VALID);
// "foo" moves between two intervals.
- state = it.checkKey(BSON("" << 20.5 << "" << 1),
- &keyEltsToUse,
- &movePastKeyElts,
- &elt,
- &inc);
+ state = it.checkKey(BSON("" << 20.5 << "" << 1), &seekPoint);
ASSERT_EQUALS(state, IndexBoundsChecker::MUST_ADVANCE);
- ASSERT_EQUALS(keyEltsToUse, 0);
+ ASSERT_EQUALS(seekPoint.prefixLen, 0);
// Should be told to move exactly to the next interval's beginning.
- ASSERT_EQUALS(movePastKeyElts, false);
- ASSERT_EQUALS(elt[0]->numberInt(), 21);
- ASSERT_EQUALS(inc[0], true);
- ASSERT_EQUALS(elt[1]->numberInt(), 0);
- ASSERT_EQUALS(inc[1], false);
+ ASSERT_EQUALS(seekPoint.prefixExclusive, false);
+ ASSERT_EQUALS(seekPoint.keySuffix[0]->numberInt(), 21);
+ ASSERT_EQUALS(seekPoint.suffixInclusive[0], true);
+ ASSERT_EQUALS(seekPoint.keySuffix[1]->numberInt(), 0);
+ ASSERT_EQUALS(seekPoint.suffixInclusive[1], false);
}
TEST(IndexBoundsCheckerTest, MoveIntervalForwardManyIntervals) {
@@ -395,27 +358,15 @@ namespace {
bounds.fields.push_back(fooList);
IndexBoundsChecker it(&bounds, BSON("foo" << 1), 1);
- int keyEltsToUse;
- bool movePastKeyElts;
- vector<const BSONElement*> elt(1);
- vector<bool> inc(1);
-
+ IndexSeekPoint seekPoint;
IndexBoundsChecker::KeyState state;
// Start at something in our range.
- state = it.checkKey(BSON("" << 7),
- &keyEltsToUse,
- &movePastKeyElts,
- &elt,
- &inc);
+ state = it.checkKey(BSON("" << 7), &seekPoint);
ASSERT_EQUALS(state, IndexBoundsChecker::VALID);
// "foo" moves forward a few intervals.
- state = it.checkKey(BSON("" << 42),
- &keyEltsToUse,
- &movePastKeyElts,
- &elt,
- &inc);
+ state = it.checkKey(BSON("" << 42), &seekPoint);
ASSERT_EQUALS(state, IndexBoundsChecker::VALID);
}
@@ -431,58 +382,34 @@ namespace {
bounds.fields.push_back(barList);
IndexBoundsChecker it(&bounds, BSON("foo" << 1 << "bar" << 1), 1);
- int keyEltsToUse;
- bool movePastKeyElts;
- vector<const BSONElement*> elt(2);
- vector<bool> inc(2);
-
+ IndexSeekPoint seekPoint;
IndexBoundsChecker::KeyState state;
// Start at something in our range.
- state = it.checkKey(BSON("" << 7 << "" << 1),
- &keyEltsToUse,
- &movePastKeyElts,
- &elt,
- &inc);
+ state = it.checkKey(BSON("" << 7 << "" << 1), &seekPoint);
ASSERT_EQUALS(state, IndexBoundsChecker::VALID);
// The rightmost key is past the range. We should be told to move past the key before the
// one whose interval we exhausted.
- state = it.checkKey(BSON("" << 7 << "" << 5.00001),
- &keyEltsToUse,
- &movePastKeyElts,
- &elt,
- &inc);
+ state = it.checkKey(BSON("" << 7 << "" << 5.00001), &seekPoint);
ASSERT_EQUALS(state, IndexBoundsChecker::MUST_ADVANCE);
- ASSERT_EQUALS(keyEltsToUse, 1);
- ASSERT_EQUALS(movePastKeyElts, true);
+ ASSERT_EQUALS(seekPoint.prefixLen, 1);
+ ASSERT_EQUALS(seekPoint.prefixExclusive, true);
// Move a little forward, but note that the rightmost key isn't in the interval yet.
- state = it.checkKey(BSON("" << 7.2 << "" << 0),
- &keyEltsToUse,
- &movePastKeyElts,
- &elt,
- &inc);
+ state = it.checkKey(BSON("" << 7.2 << "" << 0), &seekPoint);
ASSERT_EQUALS(state, IndexBoundsChecker::MUST_ADVANCE);
- ASSERT_EQUALS(keyEltsToUse, 1);
- ASSERT_EQUALS(movePastKeyElts, false);
- ASSERT_EQUALS(elt[1]->numberInt(), 0);
- ASSERT_EQUALS(inc[1], false);
+ ASSERT_EQUALS(seekPoint.prefixLen, 1);
+ ASSERT_EQUALS(seekPoint.prefixExclusive, false);
+ ASSERT_EQUALS(seekPoint.keySuffix[1]->numberInt(), 0);
+ ASSERT_EQUALS(seekPoint.suffixInclusive[1], false);
// Move to the edge of both intervals, 20,5
- state = it.checkKey(BSON("" << 20 << "" << 5),
- &keyEltsToUse,
- &movePastKeyElts,
- &elt,
- &inc);
+ state = it.checkKey(BSON("" << 20 << "" << 5), &seekPoint);
ASSERT_EQUALS(state, IndexBoundsChecker::VALID);
// And a little beyond.
- state = it.checkKey(BSON("" << 20 << "" << 5.1),
- &keyEltsToUse,
- &movePastKeyElts,
- &elt,
- &inc);
+ state = it.checkKey(BSON("" << 20 << "" << 5.1), &seekPoint);
ASSERT_EQUALS(state, IndexBoundsChecker::DONE);
}
@@ -499,32 +426,20 @@ namespace {
bounds.fields.push_back(barList);
IndexBoundsChecker it(&bounds, BSON("foo" << 1 << "bar" << 1), 1);
- int keyEltsToUse;
- bool movePastKeyElts;
- vector<const BSONElement*> elt(2);
- vector<bool> inc(2);
-
+ IndexSeekPoint seekPoint;
IndexBoundsChecker::KeyState state;
// Start at something in our range.
- state = it.checkKey(BSON("" << 0 << "" << 1),
- &keyEltsToUse,
- &movePastKeyElts,
- &elt,
- &inc);
+ state = it.checkKey(BSON("" << 0 << "" << 1), &seekPoint);
ASSERT_EQUALS(state, IndexBoundsChecker::VALID);
// First key moves to next interval, second key needs to be advanced.
- state = it.checkKey(BSON("" << 10 << "" << -1),
- &keyEltsToUse,
- &movePastKeyElts,
- &elt,
- &inc);
+ state = it.checkKey(BSON("" << 10 << "" << -1), &seekPoint);
ASSERT_EQUALS(state, IndexBoundsChecker::MUST_ADVANCE);
- ASSERT_EQUALS(keyEltsToUse, 1);
- ASSERT_EQUALS(movePastKeyElts, false);
- ASSERT_EQUALS(elt[1]->numberInt(), 0);
- ASSERT_EQUALS(inc[1], false);
+ ASSERT_EQUALS(seekPoint.prefixLen, 1);
+ ASSERT_EQUALS(seekPoint.prefixExclusive, false);
+ ASSERT_EQUALS(seekPoint.keySuffix[1]->numberInt(), 0);
+ ASSERT_EQUALS(seekPoint.suffixInclusive[1], false);
}
TEST(IndexBoundsCheckerTest, SecondIntervalMustRewind) {
@@ -543,46 +458,25 @@ namespace {
ASSERT(bounds.isValidFor(idx, 1));
IndexBoundsChecker it(&bounds, idx, 1);
- int keyEltsToUse;
- bool movePastKeyElts;
-
- vector<const BSONElement*> elt(2);
- vector<bool> inc(2);
-
+ IndexSeekPoint seekPoint;
IndexBoundsChecker::KeyState state;
- state = it.checkKey(BSON("" << 25 << "" << 0),
- &keyEltsToUse,
- &movePastKeyElts,
- &elt,
- &inc);
+ state = it.checkKey(BSON("" << 25 << "" << 0), &seekPoint);
ASSERT_EQUALS(state, IndexBoundsChecker::VALID);
- state = it.checkKey(BSON("" << 25 << "" << 1),
- &keyEltsToUse,
- &movePastKeyElts,
- &elt,
- &inc);
+ state = it.checkKey(BSON("" << 25 << "" << 1), &seekPoint);
ASSERT_EQUALS(state, IndexBoundsChecker::MUST_ADVANCE);
- ASSERT_EQUALS(keyEltsToUse, 1);
- ASSERT_EQUALS(movePastKeyElts, false);
- ASSERT_EQUALS(elt[1]->numberInt(), 9);
- ASSERT_EQUALS(inc[1], true);
-
- state = it.checkKey(BSON("" << 25 << "" << 9),
- &keyEltsToUse,
- &movePastKeyElts,
- &elt,
- &inc);
+ ASSERT_EQUALS(seekPoint.prefixLen, 1);
+ ASSERT_EQUALS(seekPoint.prefixExclusive, false);
+ ASSERT_EQUALS(seekPoint.keySuffix[1]->numberInt(), 9);
+ ASSERT_EQUALS(seekPoint.suffixInclusive[1], true);
+
+ state = it.checkKey(BSON("" << 25 << "" << 9), &seekPoint);
ASSERT_EQUALS(state, IndexBoundsChecker::VALID);
// First key moved forward. The second key moved back to a valid state but it's behind
// the interval that the checker thought it was in.
- state = it.checkKey(BSON("" << 26 << "" << 0),
- &keyEltsToUse,
- &movePastKeyElts,
- &elt,
- &inc);
+ state = it.checkKey(BSON("" << 26 << "" << 0), &seekPoint);
ASSERT_EQUALS(state, IndexBoundsChecker::VALID);
}
@@ -601,58 +495,34 @@ namespace {
ASSERT(bounds.isValidFor(idx, 1));
IndexBoundsChecker it(&bounds, idx, 1);
- int keyEltsToUse;
- bool movePastKeyElts;
- vector<const BSONElement*> elt(2);
- vector<bool> inc(2);
-
+ IndexSeekPoint seekPoint;
IndexBoundsChecker::KeyState state;
// Start at something in our range.
- state = it.checkKey(BSON("" << 20 << "" << 5),
- &keyEltsToUse,
- &movePastKeyElts,
- &elt,
- &inc);
+ state = it.checkKey(BSON("" << 20 << "" << 5), &seekPoint);
ASSERT_EQUALS(state, IndexBoundsChecker::VALID);
// The rightmost key is past the range. We should be told to move past the key before the
// one whose interval we exhausted.
- state = it.checkKey(BSON("" << 20 << "" << 0),
- &keyEltsToUse,
- &movePastKeyElts,
- &elt,
- &inc);
+ state = it.checkKey(BSON("" << 20 << "" << 0), &seekPoint);
ASSERT_EQUALS(state, IndexBoundsChecker::MUST_ADVANCE);
- ASSERT_EQUALS(keyEltsToUse, 1);
- ASSERT_EQUALS(movePastKeyElts, true);
+ ASSERT_EQUALS(seekPoint.prefixLen, 1);
+ ASSERT_EQUALS(seekPoint.prefixExclusive, true);
// Move a little forward, but note that the rightmost key isn't in the interval yet.
- state = it.checkKey(BSON("" << 19 << "" << 6),
- &keyEltsToUse,
- &movePastKeyElts,
- &elt,
- &inc);
+ state = it.checkKey(BSON("" << 19 << "" << 6), &seekPoint);
ASSERT_EQUALS(state, IndexBoundsChecker::MUST_ADVANCE);
- ASSERT_EQUALS(keyEltsToUse, 1);
- ASSERT_EQUALS(movePastKeyElts, false);
- ASSERT_EQUALS(elt[1]->numberInt(), 5);
- ASSERT_EQUALS(inc[1], true);
+ ASSERT_EQUALS(seekPoint.prefixLen, 1);
+ ASSERT_EQUALS(seekPoint.prefixExclusive, false);
+ ASSERT_EQUALS(seekPoint.keySuffix[1]->numberInt(), 5);
+ ASSERT_EQUALS(seekPoint.suffixInclusive[1], true);
// Move to the edge of both intervals
- state = it.checkKey(BSON("" << 7 << "" << 0.01),
- &keyEltsToUse,
- &movePastKeyElts,
- &elt,
- &inc);
+ state = it.checkKey(BSON("" << 7 << "" << 0.01), &seekPoint);
ASSERT_EQUALS(state, IndexBoundsChecker::VALID);
// And a little beyond.
- state = it.checkKey(BSON("" << 7 << "" << 0),
- &keyEltsToUse,
- &movePastKeyElts,
- &elt,
- &inc);
+ state = it.checkKey(BSON("" << 7 << "" << 0), &seekPoint);
ASSERT_EQUALS(state, IndexBoundsChecker::DONE);
}
@@ -672,52 +542,32 @@ namespace {
ASSERT(bounds.isValidFor(idx, -1));
IndexBoundsChecker it(&bounds, idx, -1);
- int keyEltsToUse;
- bool movePastKeyElts;
- vector<const BSONElement*> elt(2);
- vector<bool> inc(2);
-
+ IndexSeekPoint seekPoint;
IndexBoundsChecker::KeyState state;
// Start at something in our range.
- state = it.checkKey(BSON("" << 30 << "" << 1),
- &keyEltsToUse,
- &movePastKeyElts,
- &elt,
- &inc);
+ state = it.checkKey(BSON("" << 30 << "" << 1), &seekPoint);
ASSERT_EQUALS(state, IndexBoundsChecker::VALID);
// Second field moves past the end, but we're not done, since there's still an interval in
// the previous field that the key hasn't advanced to.
- state = it.checkKey(BSON("" << 30 << "" << 5),
- &keyEltsToUse,
- &movePastKeyElts,
- &elt,
- &inc);
+ state = it.checkKey(BSON("" << 30 << "" << 5), &seekPoint);
ASSERT_EQUALS(state, IndexBoundsChecker::MUST_ADVANCE);
- ASSERT_EQUALS(keyEltsToUse, 1);
- ASSERT(movePastKeyElts);
+ ASSERT_EQUALS(seekPoint.prefixLen, 1);
+ ASSERT(seekPoint.prefixExclusive);
// The next index key is in the second interval for 'foo' and there is a valid interval for
// 'bar'.
- state = it.checkKey(BSON("" << 20 << "" << 1),
- &keyEltsToUse,
- &movePastKeyElts,
- &elt,
- &inc);
+ state = it.checkKey(BSON("" << 20 << "" << 1), &seekPoint);
ASSERT_EQUALS(state, IndexBoundsChecker::VALID);
// The next index key is very close to the end of the open interval for foo, and it's past
// the interval for 'bar'. Since the interval for foo is open, we are asked to move
// forward, since we possibly could.
- state = it.checkKey(BSON("" << 7.001 << "" << 5),
- &keyEltsToUse,
- &movePastKeyElts,
- &elt,
- &inc);
+ state = it.checkKey(BSON("" << 7.001 << "" << 5), &seekPoint);
ASSERT_EQUALS(state, IndexBoundsChecker::MUST_ADVANCE);
- ASSERT_EQUALS(keyEltsToUse, 1);
- ASSERT(movePastKeyElts);
+ ASSERT_EQUALS(seekPoint.prefixLen, 1);
+ ASSERT(seekPoint.prefixExclusive);
}
//
@@ -727,7 +577,7 @@ namespace {
/**
* Returns string representation of IndexBoundsChecker::Location.
*/
- string toString(IndexBoundsChecker::Location location) {
+ std::string toString(IndexBoundsChecker::Location location) {
switch(location) {
case IndexBoundsChecker::BEHIND: return "BEHIND";
case IndexBoundsChecker::WITHIN: return "WITHIN";
diff --git a/src/mongo/db/storage/SConscript b/src/mongo/db/storage/SConscript
index cefbcf752f5..2627532afbd 100644
--- a/src/mongo/db/storage/SConscript
+++ b/src/mongo/db/storage/SConscript
@@ -42,7 +42,6 @@ env.Library(
'sorted_data_interface_test_cursor.cpp',
'sorted_data_interface_test_cursor_advanceto.cpp',
'sorted_data_interface_test_cursor_locate.cpp',
- 'sorted_data_interface_test_cursor_position.cpp',
'sorted_data_interface_test_cursor_saverestore.cpp',
'sorted_data_interface_test_dupkeycheck.cpp',
'sorted_data_interface_test_fullvalidate.cpp',
@@ -53,9 +52,11 @@ env.Library(
'sorted_data_interface_test_spaceused.cpp',
'sorted_data_interface_test_touch.cpp',
'sorted_data_interface_test_unindex.cpp',
- ],
- LIBDEPS=[]
- )
+ ],
+ LIBDEPS=[
+ 'index_entry_comparison',
+ ],
+)
env.Library(
target='record_store_test_harness',
diff --git a/src/mongo/db/storage/devnull/devnull_kv_engine.cpp b/src/mongo/db/storage/devnull/devnull_kv_engine.cpp
index 074637d147a..9ef68a68dbf 100644
--- a/src/mongo/db/storage/devnull/devnull_kv_engine.cpp
+++ b/src/mongo/db/storage/devnull/devnull_kv_engine.cpp
@@ -216,8 +216,9 @@ namespace mongo {
virtual bool isEmpty(OperationContext* txn) { return true; }
- virtual SortedDataInterface::Cursor* newCursor(OperationContext* txn, int direction) const {
- return NULL;
+ virtual std::unique_ptr<SortedDataInterface::Cursor> newCursor(OperationContext* txn,
+ bool isForward) const {
+ return {};
}
virtual Status initAsEmpty(OperationContext* txn) { return Status::OK(); }
diff --git a/src/mongo/db/storage/in_memory/in_memory_btree_impl.cpp b/src/mongo/db/storage/in_memory/in_memory_btree_impl.cpp
index ea303575bf5..1b937118bb3 100644
--- a/src/mongo/db/storage/in_memory/in_memory_btree_impl.cpp
+++ b/src/mongo/db/storage/in_memory/in_memory_btree_impl.cpp
@@ -39,6 +39,7 @@
#include "mongo/db/catalog/index_catalog_entry.h"
#include "mongo/db/storage/index_entry_comparison.h"
#include "mongo/db/storage/in_memory/in_memory_recovery_unit.h"
+#include "mongo/stdx/memory.h"
#include "mongo/util/mongoutils/str.h"
namespace mongo {
@@ -224,238 +225,225 @@ namespace {
return Status::OK();
}
- class ForwardCursor : public SortedDataInterface::Cursor {
+ class Cursor final : public SortedDataInterface::Cursor {
public:
- ForwardCursor(const IndexSet& data, OperationContext* txn)
+ Cursor(OperationContext* txn, const IndexSet& data, bool isForward)
: _txn(txn),
_data(data),
+ _forward(isForward),
_it(data.end())
{}
-
- virtual int getDirection() const { return 1; }
-
- virtual bool isEOF() const {
- return _it == _data.end();
- }
-
- virtual bool pointsToSamePlaceAs(const SortedDataInterface::Cursor& otherBase) const {
- const ForwardCursor& other = static_cast<const ForwardCursor&>(otherBase);
- invariant(&_data == &other._data); // iterators over same index
- return _it == other._it;
- }
-
- virtual bool locate(const BSONObj& keyRaw, const RecordId& loc) {
- const BSONObj key = stripFieldNames(keyRaw);
- _it = _data.lower_bound(IndexKeyEntry(key, loc)); // lower_bound is >= key
- if ( _it == _data.end() ) {
- return false;
+
+ boost::optional<IndexKeyEntry> next(RequestedInfo parts) override {
+ if (_lastMoveWasRestore) {
+ // Return current position rather than advancing.
+ _lastMoveWasRestore = false;
}
-
- if ( _it->key != key ) {
- return false;
+ else {
+ advance();
+ if (atEndPoint()) _isEOF = true;
}
- return _it->loc == loc;
+ if (_isEOF) return {};
+ return *_it;
}
-
- virtual void customLocate(const BSONObj& keyBegin,
- int keyBeginLen,
- bool afterKey,
- const vector<const BSONElement*>& keyEnd,
- const vector<bool>& keyEndInclusive) {
- // makeQueryObject handles stripping of fieldnames for us.
- _it = _data.lower_bound(IndexKeyEntry(IndexEntryComparison::makeQueryObject(
- keyBegin,
- keyBeginLen,
- afterKey,
- keyEnd,
- keyEndInclusive,
- 1), // forward
- RecordId()));
- }
-
- void advanceTo(const BSONObj &keyBegin,
- int keyBeginLen,
- bool afterKey,
- const vector<const BSONElement*>& keyEnd,
- const vector<bool>& keyEndInclusive) {
- // XXX I think these do the same thing????
- customLocate(keyBegin, keyBeginLen, afterKey, keyEnd, keyEndInclusive);
+
+ void setEndPosition(const BSONObj& key, bool inclusive) override {
+ if (key.isEmpty()) {
+ // This means scan to end of index.
+ _endState = {};
+ return;
+ }
+
+ // NOTE: this uses the opposite min/max rules as a normal seek because a forward
+ // scan should land after the key if inclusive and before if exclusive.
+ _endState = EndState(stripFieldNames(key),
+ _forward == inclusive ? RecordId::max() : RecordId::min());
+ seekEndCursor();
}
- virtual BSONObj getKey() const {
- return _it->key;
+ boost::optional<IndexKeyEntry> seek(const BSONObj& key, bool inclusive,
+ RequestedInfo parts) override {
+ const BSONObj query = stripFieldNames(key);
+ locate(query, _forward == inclusive ? RecordId::min() : RecordId::max());
+ _lastMoveWasRestore = false;
+ if (_isEOF) return {};
+ dassert(inclusive ? compareKeys(_it->key, query) >= 0
+ : compareKeys(_it->key, query) > 0);
+ return *_it;
}
- virtual RecordId getRecordId() const {
- return _it->loc;
+ boost::optional<IndexKeyEntry> seek(const IndexSeekPoint& seekPoint,
+ RequestedInfo parts) override {
+ // Query encodes exclusive case so it can be treated as an inclusive query.
+ const BSONObj query = IndexEntryComparison::makeQueryObject(seekPoint, _forward);
+ locate(query, _forward ? RecordId::min() : RecordId::max());
+ _lastMoveWasRestore = false;
+ if (_isEOF) return {};
+ dassert(compareKeys(_it->key, query) >= 0);
+ return *_it;
}
- virtual void advance() {
- if (_it != _data.end())
- ++_it;
- }
+ void savePositioned() override {
+ // Keep original position if we haven't moved since the last restore.
+ _txn = nullptr;
+ if (_lastMoveWasRestore) return;
- virtual void savePosition() {
- if (_it == _data.end()) {
- _savedAtEnd = true;
+ if (_isEOF) {
+ saveUnpositioned();
return;
}
_savedAtEnd = false;
_savedKey = _it->key.getOwned();
_savedLoc = _it->loc;
+ // Doing nothing with end cursor since it will do full reseek on restore.
}
- virtual void restorePosition(OperationContext* txn) {
- if (_savedAtEnd) {
- _it = _data.end();
- }
- else {
- locate(_savedKey, _savedLoc);
- }
+ void saveUnpositioned() override {
+ _txn = nullptr;
+ _savedAtEnd = true;
+ // Doing nothing with end cursor since it will do full reseek on restore.
}
- private:
-
- OperationContext* _txn; // not owned
- const IndexSet& _data;
- IndexSet::const_iterator _it;
-
- // For save/restorePosition since _it may be invalidated durring a yield.
- bool _savedAtEnd;
- BSONObj _savedKey;
- RecordId _savedLoc;
-
- };
+ void restore(OperationContext* txn) override {
+ _txn = txn;
- // TODO see if this can share any code with ForwardIterator
- class ReverseCursor : public SortedDataInterface::Cursor {
- public:
- ReverseCursor(const IndexSet& data, OperationContext* txn)
- : _txn(txn),
- _data(data),
- _it(data.rend())
- {}
+ // Always do a full seek on restore. We cannot use our last position since index
+ // entries may have been inserted closer to our endpoint and we would need to move
+ // over them.
+ seekEndCursor();
- virtual int getDirection() const { return -1; }
+ if (_savedAtEnd) {
+ _isEOF = true;
+ return;
+ }
+
+ // Need to find our position from the root.
+ locate(_savedKey, _savedLoc);
- virtual bool isEOF() const {
- return _it == _data.rend();
+ _lastMoveWasRestore = _isEOF // We weren't EOF but now are.
+ || _data.value_comp().compare(*_it, {_savedKey, _savedLoc}) != 0;
}
- virtual bool pointsToSamePlaceAs(const SortedDataInterface::Cursor& otherBase) const {
- const ReverseCursor& other = static_cast<const ReverseCursor&>(otherBase);
- invariant(&_data == &other._data); // iterators over same index
- return _it == other._it;
+ private:
+ bool atEndPoint() const {
+ return _endState && _it == _endState->it;
}
- virtual bool locate(const BSONObj& keyRaw, const RecordId& loc) {
- const BSONObj key = stripFieldNames(keyRaw);
- _it = lower_bound(IndexKeyEntry(key, loc)); // lower_bound is <= query
-
- if ( _it == _data.rend() ) {
- return false;
+ // Advances once in the direction of the scan, updating _isEOF as needed.
+ // Does nothing if already _isEOF.
+ void advance() {
+ if (_isEOF) return;
+ if (_forward) {
+ if (_it != _data.end()) ++_it;
+ if (_it == _data.end() || atEndPoint()) _isEOF = true;
}
-
-
- if ( _it->key != key ) {
- return false;
+ else {
+ if (_it == _data.begin() || _data.empty()) {
+ _isEOF = true;
+ }
+ else {
+ --_it;
+ }
+ if (atEndPoint()) _isEOF = true;
}
-
- return _it->loc == loc;
}
- virtual void customLocate(const BSONObj& keyBegin,
- int keyBeginLen,
- bool afterKey,
- const vector<const BSONElement*>& keyEnd,
- const vector<bool>& keyEndInclusive) {
- // makeQueryObject handles stripping of fieldnames for us.
- _it = lower_bound(IndexKeyEntry(IndexEntryComparison::makeQueryObject(
- keyBegin,
- keyBeginLen,
- afterKey,
- keyEnd,
- keyEndInclusive,
- -1), // reverse
- RecordId()));
- }
+ bool atOrPastEndPointAfterSeeking() const {
+ if (_isEOF) return true;
+ if (!_endState) return false;
+
+ const int cmp = _data.value_comp().compare(*_it, _endState->query);
- void advanceTo(const BSONObj &keyBegin,
- int keyBeginLen,
- bool afterKey,
- const vector<const BSONElement*>& keyEnd,
- const vector<bool>& keyEndInclusive) {
- // XXX I think these do the same thing????
- customLocate(keyBegin, keyBeginLen, afterKey, keyEnd, keyEndInclusive);
- }
+ // We set up _endState->query to be in between the last in-range value and the first
+ // out-of-range value. In particular, it is constructed to never equal any legal
+ // index key.
+ dassert(cmp != 0);
- virtual BSONObj getKey() const {
- return _it->key;
+ if (_forward) {
+ // We may have landed after the end point.
+ return cmp > 0;
+ }
+ else {
+ // We may have landed before the end point.
+ return cmp < 0;
+ }
}
- virtual RecordId getRecordId() const {
- return _it->loc;
- }
+ void locate(const BSONObj& key, const RecordId& loc) {
+ _isEOF = false;
+ const auto query = IndexKeyEntry(key, loc);
+ _it = _data.lower_bound(query);
+ if (_forward) {
+ if (_it == _data.end()) _isEOF = true;
+ }
+ else {
+ // lower_bound lands us on or after query. Reverse cursors must be on or before.
+ if (_it == _data.end() || _data.value_comp().compare(*_it, query) > 0)
+ advance(); // sets _isEOF if there is nothing more to return.
+ }
- virtual void advance() {
- if (_it != _data.rend())
- ++_it;
+ if (atOrPastEndPointAfterSeeking()) _isEOF = true;
}
- virtual void savePosition() {
- if (_it == _data.rend()) {
- _savedAtEnd = true;
- return;
- }
-
- _savedAtEnd = false;
- _savedKey = _it->key.getOwned();
- _savedLoc = _it->loc;
+ // Returns comparison relative to direction of scan. If rhs would be seen later, returns
+ // a positive value.
+ int compareKeys(const BSONObj& lhs, const BSONObj& rhs) const {
+ int cmp = _data.value_comp().compare({lhs, RecordId()}, {rhs, RecordId()});
+ return _forward ? cmp : -cmp;
}
- virtual void restorePosition(OperationContext* txn) {
- if (_savedAtEnd) {
- _it = _data.rend();
- }
- else {
- locate(_savedKey, _savedLoc);
+ void seekEndCursor() {
+ if (!_endState || _data.empty()) return;
+
+ auto it = _data.lower_bound(_endState->query);
+ if (!_forward) {
+ // lower_bound lands us on or after query. Reverse cursors must be on or before.
+ if (it == _data.end() || _data.value_comp().compare(*it,
+ _endState->query) > 0) {
+ if (it == _data.begin()) {
+ it = _data.end(); // all existing data in range.
+ }
+ else {
+ --it;
+ }
+ }
}
- }
- private:
- /**
- * Returns the first entry <= query. This is equivalent to ForwardCursors use of
- * _data.lower_bound which returns the first entry >= query.
- */
- IndexSet::const_reverse_iterator lower_bound(const IndexKeyEntry& query) const {
- // using upper_bound since we want to the right-most entry matching the query.
- IndexSet::const_iterator it = _data.upper_bound(query);
-
- // upper_bound returns the entry to the right of the one we want. Helpfully,
- // converting to a reverse_iterator moves one to the left. This also correctly
- // handles the case where upper_bound returns end() by converting to rbegin(),
- // meaning that all data is to the right of the query.
- return IndexSet::const_reverse_iterator(it);
+ if (it != _data.end()) dassert(compareKeys(it->key, _endState->query.key) >= 0);
+ _endState->it = it;
}
OperationContext* _txn; // not owned
const IndexSet& _data;
- IndexSet::const_reverse_iterator _it;
-
- // For save/restorePosition since _it may be invalidated durring a yield.
- bool _savedAtEnd;
+ const bool _forward;
+ bool _isEOF = true;
+ IndexSet::const_iterator _it;
+
+ struct EndState {
+ EndState(BSONObj key, RecordId loc) : query(std::move(key), loc) {}
+
+ IndexKeyEntry query;
+ IndexSet::const_iterator it;
+ };
+ boost::optional<EndState> _endState;
+
+ // Used by next to decide to return current position rather than moving. Should be reset
+ // to false by any operation that moves the cursor, other than subsequent save/restore
+ // pairs.
+ bool _lastMoveWasRestore = false;
+
+ // For save/restore since _it may be invalidated during a yield.
+ bool _savedAtEnd = false;
BSONObj _savedKey;
RecordId _savedLoc;
};
- virtual SortedDataInterface::Cursor* newCursor(OperationContext* txn, int direction) const {
- if (direction == 1)
- return new ForwardCursor(*_data, txn);
-
- invariant(direction == -1);
- return new ReverseCursor(*_data, txn);
+ virtual std::unique_ptr<SortedDataInterface::Cursor> newCursor(
+ OperationContext* txn,
+ bool isForward) const {
+ return stdx::make_unique<Cursor>(txn, *_data, isForward);
}
virtual Status initAsEmpty(OperationContext* txn) {
diff --git a/src/mongo/db/storage/index_entry_comparison.cpp b/src/mongo/db/storage/index_entry_comparison.cpp
index c609933eb38..4a5d4fdab1a 100644
--- a/src/mongo/db/storage/index_entry_comparison.cpp
+++ b/src/mongo/db/storage/index_entry_comparison.cpp
@@ -27,11 +27,17 @@
*/
#include "mongo/platform/basic.h"
+#include <ostream>
+
#include "mongo/db/jsobj.h"
#include "mongo/db/storage/index_entry_comparison.h"
namespace mongo {
+ std::ostream& operator<<(std::ostream& stream, const IndexKeyEntry& entry) {
+ return stream << entry.key << '@' << entry.loc;
+ }
+
// Due to the limitations of various APIs, we need to use the same type (IndexKeyEntry)
// for both the stored data and the "query". We cheat and encode extra information in the
// first byte of the field names in the query. This works because all stored objects should
diff --git a/src/mongo/db/storage/index_entry_comparison.h b/src/mongo/db/storage/index_entry_comparison.h
index c1415c2fba2..dbdd15e0368 100644
--- a/src/mongo/db/storage/index_entry_comparison.h
+++ b/src/mongo/db/storage/index_entry_comparison.h
@@ -28,6 +28,8 @@
#pragma once
+#include <iosfwd>
+#include <tuple>
#include <vector>
#include "mongo/db/jsobj.h"
@@ -40,17 +42,98 @@ namespace mongo {
* and a disk location.
*/
struct IndexKeyEntry {
- IndexKeyEntry(const BSONObj& key, RecordId loc) :key(key), loc(loc) {}
+ IndexKeyEntry(BSONObj key, RecordId loc) :key(std::move(key)), loc(std::move(loc)) {}
BSONObj key;
RecordId loc;
};
+ std::ostream& operator<<(std::ostream& stream, const IndexKeyEntry& entry);
+
+ inline bool operator==(const IndexKeyEntry& lhs, const IndexKeyEntry& rhs) {
+ return std::tie(lhs.key, lhs.loc) == std::tie(rhs.key, rhs.loc);
+ }
+
+ inline bool operator!=(const IndexKeyEntry& lhs, const IndexKeyEntry& rhs) {
+ return std::tie(lhs.key, lhs.loc) != std::tie(rhs.key, rhs.loc);
+ }
+
+ /**
+ * Describes a query that can be compared against an IndexKeyEntry in a way that allows
+ * expressing exclusiveness on a prefix of the key. This is mostly used to express a location to
+ * seek to in an index that may not be representable as a valid key.
+ *
+ * The "key" used for comparison is the concatenation of the first 'prefixLen' elements of
+ * 'keyPrefix' followed by the last 'keySuffix.size() - prefixLen' elements of
+ * 'keySuffix'.
+ *
+ * The comparison is exclusive if either 'prefixExclusive' is true or if there are any false
+ * values in 'suffixInclusive' that are false at index >= 'prefixLen'.
+ *
+ * Portions of the key following the first exclusive part may be ignored.
+ *
+ * e.g.
+ *
+ * Suppose that
+ *
+ * keyPrefix = { "" : 1, "" : 2 }
+ * prefixLen = 1
+ * prefixExclusive = false
+ * keySuffix = [ IGNORED, { "" : 5 } ]
+ * suffixInclusive = [ IGNORED, false ]
+ *
+ * ==> key is { "" : 1, "" : 5 }
+ * with the comparison being done exclusively
+ *
+ * Suppose that
+ *
+ * keyPrefix = { "" : 1, "" : 2 }
+ * prefixLen = 1
+ * prefixExclusive = true
+ * keySuffix = IGNORED
+ * suffixInclusive = IGNORED
+ *
+ * ==> represented key is { "" : 1 }
+ * with the comparison being done exclusively
+ *
+ * 'prefixLen = 0' and 'prefixExclusive = true' are mutually incompatible.
+ *
+ * @see IndexEntryComparison::makeQueryObject
+ */
+ struct IndexSeekPoint {
+ BSONObj keyPrefix;
+
+ /**
+ * Use this many fields in 'keyPrefix'.
+ */
+ int prefixLen = 0;
+
+ /**
+ * If true, compare exclusively on just the fields on keyPrefix and ignore the suffix.
+ */
+ bool prefixExclusive = false;
+
+ /**
+ * Elements starting at index 'prefixLen' are logically appended to the prefix.
+ * The elements before index 'prefixLen' should be ignored.
+ */
+ std::vector<const BSONElement*> keySuffix;
+
+ /**
+ * If the ith element is false, ignore indexes > i in keySuffix and treat the
+ * concatenated key as exclusive.
+ * The elements before index 'prefixLen' should be ignored.
+ *
+ * Must have identical size as keySuffix.
+ */
+ std::vector<bool> suffixInclusive;
+ };
+
/**
* Compares two different IndexKeyEntry instances.
- * The existense of compound indexes necessitates some complicated logic. This is meant to
- * support the implementation of the SortedDataInterface::customLocate() and
- * SortedDataInterface::advanceTo() methods, which require fine-grained control over whether the
+ * The existence of compound indexes necessitates some complicated logic. This is meant to
+ * support the comparisons of IndexKeyEntries (that are stored in an index) with IndexSeekPoints
+ * (that were encoded with makeQueryObject) to support fine-grained control over whether the
* ranges of various keys comprising a compound index are inclusive or exclusive.
*/
class IndexEntryComparison {
@@ -105,6 +188,15 @@ namespace mongo {
const std::vector<bool>& suffixInclusive,
const int cursorDirection);
+ static BSONObj makeQueryObject(const IndexSeekPoint& seekPoint, bool isForward) {
+ return makeQueryObject(seekPoint.keyPrefix,
+ seekPoint.prefixLen,
+ seekPoint.prefixExclusive,
+ seekPoint.keySuffix,
+ seekPoint.suffixInclusive,
+ isForward ? 1 : -1);
+ }
+
private:
// Ordering is used in comparison() to compare BSONElements
const Ordering _order;
diff --git a/src/mongo/db/storage/key_string.cpp b/src/mongo/db/storage/key_string.cpp
index 2b3391e1d42..2cd548aadf7 100644
--- a/src/mongo/db/storage/key_string.cpp
+++ b/src/mongo/db/storage/key_string.cpp
@@ -262,22 +262,24 @@ namespace mongo {
void KeyString::resetToKey(const BSONObj& obj, Ordering ord, RecordId recordId) {
resetToEmpty();
- _appendAllElementsForIndexing(obj, ord);
+ _appendAllElementsForIndexing(obj, ord, kInclusive);
appendRecordId(recordId);
}
- void KeyString::resetToKey(const BSONObj& obj, Ordering ord) {
+ void KeyString::resetToKey(const BSONObj& obj, Ordering ord, Discriminator discriminator) {
resetToEmpty();
- _appendAllElementsForIndexing(obj, ord);
+ _appendAllElementsForIndexing(obj, ord, discriminator);
}
// ----------------------------------------------------------------------
// ----------- APPEND CODE -------------------------------------------
// ----------------------------------------------------------------------
- void KeyString::_appendAllElementsForIndexing(const BSONObj& obj, Ordering ord) {
+ void KeyString::_appendAllElementsForIndexing(const BSONObj& obj, Ordering ord,
+ Discriminator discriminator) {
int elemCount = 0;
- BSONForEach(elem, obj) {
+ BSONObjIterator it(obj);
+ while (auto elem = it.next()) {
const int elemIdx = elemCount++;
const bool invert = (ord.get(elemIdx) == -1);
@@ -285,12 +287,30 @@ namespace mongo {
dassert(elem.fieldNameSize() < 3); // fieldNameSize includes the NUL
- // These are used in IndexEntryComparison::makeQueryObject()
- switch (*elem.fieldName()) {
- case 'l': _append(kLess, false); break;
- case 'g': _append(kGreater, false); break;
+ // IndexEntryComparison::makeQueryObject() encodes a discriminator in the first byte of
+ // the field name. This discriminator overrides the passed in one. Normal elements only
+ // have the NUL byte terminator. Entries stored in an index are not allowed to have a
+ // discriminator.
+ if (char ch = *elem.fieldName()) {
+ // l for less / g for greater.
+ invariant(ch == 'l' || ch == 'g');
+ discriminator = ch == 'l' ? kExclusiveBefore : kExclusiveAfter;
+ invariant(!it.more());
}
}
+
+ // The discriminator forces this KeyString to compare Less/Greater than any KeyString with
+ // the same prefix of keys. As an example, this can be used to land on the first key in the
+ // index with the value "a" regardless of the RecordId. In compound indexes it can use a
+ // prefix of the full key to ignore the later keys.
+ switch (discriminator) {
+ case kExclusiveBefore: _append(kLess, false); break;
+ case kExclusiveAfter: _append(kGreater, false); break;
+ case kInclusive: break; // No discriminator byte.
+ }
+
+ // TODO consider omitting kEnd when using a discriminator byte. It is not a storage format
+ // change since keystrings with discriminators are not allowed to be stored.
_append(kEnd, false);
}
diff --git a/src/mongo/db/storage/key_string.h b/src/mongo/db/storage/key_string.h
index 5bd9cf7c8a9..0d895941f9e 100644
--- a/src/mongo/db/storage/key_string.h
+++ b/src/mongo/db/storage/key_string.h
@@ -181,14 +181,20 @@ namespace mongo {
uint8_t _buf[1/*size*/ + kMaxBytesNeeded];
};
+ enum Discriminator {
+ kInclusive, // Anything to be stored in an index must use this.
+ kExclusiveBefore,
+ kExclusiveAfter,
+ };
+
KeyString() {}
KeyString(const BSONObj& obj, Ordering ord, RecordId recordId) {
resetToKey(obj, ord, recordId);
}
- KeyString(const BSONObj& obj, Ordering ord) {
- resetToKey(obj, ord);
+ KeyString(const BSONObj& obj, Ordering ord, Discriminator discriminator = kInclusive) {
+ resetToKey(obj, ord, discriminator);
}
explicit KeyString(RecordId rid) {
@@ -222,7 +228,7 @@ namespace mongo {
}
void resetToKey(const BSONObj& obj, Ordering ord, RecordId recordId);
- void resetToKey(const BSONObj& obj, Ordering ord);
+ void resetToKey(const BSONObj& obj, Ordering ord, Discriminator discriminator = kInclusive);
void resetFromBuffer(const void* buffer, size_t size) {
_buffer.reset();
memcpy(_buffer.skip(size), buffer, size);
@@ -243,7 +249,8 @@ namespace mongo {
private:
- void _appendAllElementsForIndexing(const BSONObj& obj, Ordering ord);
+ void _appendAllElementsForIndexing(const BSONObj& obj, Ordering ord,
+ Discriminator discriminator);
void _appendBool(bool val, bool invert);
void _appendDate(Date_t val, bool invert);
diff --git a/src/mongo/db/storage/mmap_v1/btree/btree_interface.cpp b/src/mongo/db/storage/mmap_v1/btree/btree_interface.cpp
index fbd1e6ad063..4c0a436d27c 100644
--- a/src/mongo/db/storage/mmap_v1/btree/btree_interface.cpp
+++ b/src/mongo/db/storage/mmap_v1/btree/btree_interface.cpp
@@ -35,15 +35,17 @@
#include "mongo/db/operation_context.h"
#include "mongo/db/storage/mmap_v1/btree/btree_logic.h"
#include "mongo/db/storage/mmap_v1/record_store_v1_base.h"
+#include "mongo/stdx/memory.h"
namespace mongo {
+namespace {
using boost::scoped_ptr;
using std::string;
using std::vector;
template <class OnDiskFormat>
- class BtreeBuilderInterfaceImpl : public SortedDataBuilderInterface {
+ class BtreeBuilderInterfaceImpl final : public SortedDataBuilderInterface {
public:
BtreeBuilderInterfaceImpl(OperationContext* trans,
typename BtreeLogic<OnDiskFormat>::Builder* builder)
@@ -61,7 +63,7 @@ namespace mongo {
};
template <class OnDiskFormat>
- class BtreeInterfaceImpl : public SortedDataInterface {
+ class BtreeInterfaceImpl final : public SortedDataInterface {
public:
BtreeInterfaceImpl(HeadManager* headManager,
RecordStore* recordStore,
@@ -128,135 +130,213 @@ namespace mongo {
return _btree->touch(txn);
}
- class Cursor : public SortedDataInterface::Cursor {
+ class Cursor final : public SortedDataInterface::Cursor {
public:
Cursor(OperationContext* txn,
const BtreeLogic<OnDiskFormat>* btree,
- int direction)
+ bool forward)
: _txn(txn),
_btree(btree),
- _direction(direction),
- _bucket(btree->getHead(txn)), // XXX this shouldn't be nessisary, but is.
- _ofs(0) {
- }
+ _direction(forward ? 1 : -1),
+ _ofs(0)
+ {}
+
+ boost::optional<IndexKeyEntry> next(RequestedInfo parts) override {
+ if (isEOF()) return {};
+ if (_lastMoveWasRestore) {
+ // Return current position rather than advancing.
+ _lastMoveWasRestore = false;
+ }
+ else {
+ _btree->advance(_txn, &_bucket, &_ofs, _direction);
+ }
- virtual int getDirection() const { return _direction; }
+ if (atEndPoint()) markEOF();
+ return curr(parts);
+ }
- virtual bool isEOF() const { return _bucket.isNull(); }
+ void setEndPosition(const BSONObj& key, bool inclusive) override {
+ if (key.isEmpty()) {
+ // This means scan to end of index.
+ _endState = {};
+ return;
+ }
- virtual bool pointsToSamePlaceAs(const SortedDataInterface::Cursor& otherBase) const {
- const Cursor& other = static_cast<const Cursor&>(otherBase);
- if (isEOF())
- return other.isEOF();
+ _endState = {{key, inclusive}};
+ seekEndCursor(); // Completes initialization of _endState.
+ }
- return _bucket == other._bucket && _ofs == other._ofs;
+ boost::optional<IndexKeyEntry> seek(const BSONObj& key, bool inclusive,
+ RequestedInfo parts) override {
+ locate(key, inclusive == forward() ? RecordId::min() : RecordId::max());
+ _lastMoveWasRestore = false;
+ if (isEOF()) return {};
+ dassert(inclusive ? compareKeys(getKey(), key) >= 0
+ : compareKeys(getKey(), key) > 0);
+ return curr(parts);
}
- virtual void aboutToDeleteBucket(const RecordId& bucket) {
- if (_bucket.toRecordId() == bucket)
- _ofs = -1;
- }
- virtual bool locate(const BSONObj& key, const RecordId& loc) {
- return _btree->locate(_txn, key, DiskLoc::fromRecordId(loc), _direction, &_ofs,
- &_bucket);
+ boost::optional<IndexKeyEntry> seek(const IndexSeekPoint& seekPoint,
+ RequestedInfo parts) override {
+ bool canUseAdvanceTo = false;
+ if (!isEOF()) {
+ int cmp = _btree->customBSONCmp(getKey(), seekPoint, _direction);
+
+ // advanceTo requires that we are positioned "earlier" in the index than the
+ // seek point, in scan order.
+ canUseAdvanceTo = forward() ? cmp < 0 : cmp > 0;
+ }
+
+
+ if (canUseAdvanceTo) {
+ // This takes advantage of current location.
+ _btree->advanceTo(_txn, &_bucket, &_ofs, seekPoint, _direction);
+ }
+ else {
+ // Start at root.
+ _bucket = _btree->getHead(_txn);
+ _ofs = 0;
+ _btree->customLocate(_txn, &_bucket, &_ofs, seekPoint, _direction);
+ }
+
+ _lastMoveWasRestore = false;
+
+ if (atOrPastEndPointAfterSeeking()) markEOF();
+ return curr(parts);
}
- virtual void customLocate(const BSONObj& keyBegin,
- int keyBeginLen,
- bool afterKey,
- const vector<const BSONElement*>& keyEnd,
- const vector<bool>& keyEndInclusive) {
-
- _btree->customLocate(_txn,
- &_bucket,
- &_ofs,
- keyBegin,
- keyBeginLen,
- afterKey,
- keyEnd,
- keyEndInclusive,
- _direction);
+ void savePositioned() override {
+ _txn = nullptr;
+ if (!isEOF()) {
+ _saved.bucket = _bucket;
+ _btree->savedCursors()->registerCursor(&_saved);
+ // Don't want to change saved position if we only moved during restore.
+ if (!_lastMoveWasRestore) {
+ _saved.key = getKey().getOwned();
+ _saved.loc = getDiskLoc();
+ }
+ }
+ // Doing nothing with end cursor since it will do full reseek on restore.
}
- void advanceTo(const BSONObj &keyBegin,
- int keyBeginLen,
- bool afterKey,
- const vector<const BSONElement*>& keyEnd,
- const vector<bool>& keyEndInclusive) {
-
- _btree->advanceTo(_txn,
- &_bucket,
- &_ofs,
- keyBegin,
- keyBeginLen,
- afterKey,
- keyEnd,
- keyEndInclusive,
- _direction);
+ void saveUnpositioned() override {
+ _txn = nullptr;
+ // Don't leak our registration if savePositioned() was previously called.
+ if (!_saved.bucket.isNull()) _btree->savedCursors()->unregisterCursor(&_saved);
+
+ _saved.bucket = DiskLoc();
+ markEOF();
}
- virtual BSONObj getKey() const {
- return _btree->getKey(_txn, _bucket, _ofs);
+ void restore(OperationContext* txn) override {
+ _txn = txn;
+
+ // Always do a full seek on restore. We cannot use our last position since index
+ // entries may have been inserted closer to our endpoint and we would need to move
+ // over them.
+ seekEndCursor();
+
+ if (isEOF()) return;
+
+ // guard against accidental double restore
+ invariant(!_saved.bucket.isNull());
+ _saved.bucket = DiskLoc();
+
+ if (_btree->savedCursors()->unregisterCursor(&_saved)) {
+ // We can use the fast restore mechanism.
+ _btree->restorePosition(_txn, _saved.key, _saved.loc, _direction,
+ &_bucket, &_ofs);
+ }
+ else {
+ // Need to find our position from the root.
+ locate(_saved.key, _saved.loc.toRecordId());
+ }
+
+ _lastMoveWasRestore = isEOF() // We weren't EOF but now are.
+ || getDiskLoc() != _saved.loc
+ || compareKeys(getKey(), _saved.key) != 0;
}
- DiskLoc getDiskLoc() const {
- return _btree->getDiskLoc(_txn, _bucket, _ofs);
+ private:
+ bool isEOF() const { return _bucket.isNull(); }
+ void markEOF() { _bucket = DiskLoc(); }
+
+ boost::optional<IndexKeyEntry> curr(RequestedInfo parts) {
+ if (isEOF()) return {};
+ return {{(parts & kWantKey) ? getKey() : BSONObj(),
+ (parts & kWantLoc) ? getDiskLoc().toRecordId() : RecordId()}};
}
- virtual RecordId getRecordId() const {
- return getDiskLoc().toRecordId();
+ bool atEndPoint() const {
+ return _endState
+ && _bucket == _endState->bucket
+ && (isEOF() || _ofs == _endState->ofs);
}
- virtual void advance() {
- if (!_bucket.isNull()) {
- _btree->advance(_txn, &_bucket, &_ofs, _direction);
- }
+ bool atOrPastEndPointAfterSeeking() const {
+ if (!_endState) return false;
+ if (isEOF()) return true;
+
+ int cmp = compareKeys(getKey(), _endState->key);
+ return _endState->inclusive ? cmp > 0 : cmp >= 0;
}
- virtual void savePosition() {
- if (!_bucket.isNull()) {
- _saved.bucket = _bucket;
- _saved.key = getKey().getOwned();
- _saved.loc = getDiskLoc();
- _btree->savedCursors()->registerCursor(&_saved);
- }
+ void locate(const BSONObj& key, const RecordId& loc) {
+ _btree->locate(_txn, key, DiskLoc::fromRecordId(loc), _direction, &_ofs, &_bucket);
+ if (atOrPastEndPointAfterSeeking()) markEOF();
}
- virtual void restorePosition(OperationContext* txn) {
- if (!_bucket.isNull()) {
- invariant(!_saved.bucket.isNull());
- _saved.bucket = DiskLoc(); // guard against accidental double restore
+ // Returns comparison relative to direction of scan. If rhs would be seen later, returns
+ // a positive value.
+ int compareKeys(const BSONObj& lhs, const BSONObj& rhs) const {
+ int cmp = lhs.woCompare(rhs, _btree->ordering(), /*considerFieldName*/false);
+ return forward() ? cmp : -cmp;
+ }
- if (!_btree->savedCursors()->unregisterCursor(&_saved)) {
- locate(_saved.key, _saved.loc.toRecordId());
- return;
- }
+ BSONObj getKey() const { return _btree->getKey(_txn, _bucket, _ofs); }
+ DiskLoc getDiskLoc() const { return _btree->getDiskLoc(_txn, _bucket, _ofs); }
- _btree->restorePosition(_txn,
- _saved.key,
- _saved.loc,
- _direction,
- &_bucket,
- &_ofs);
- }
+ void seekEndCursor() {
+ if (!_endState) return;
+ _btree->locate(_txn,
+ _endState->key,
+ forward() == _endState->inclusive ? DiskLoc::max() : DiskLoc::min(),
+ _direction,
+ &_endState->ofs, &_endState->bucket); // pure out params.
}
- private:
+ bool forward() const { return _direction == 1; }
+
OperationContext* _txn; // not owned
const BtreeLogic<OnDiskFormat>* const _btree;
const int _direction;
DiskLoc _bucket;
int _ofs;
-
- // Only used by save/restorePosition() if _bucket is non-Null.
+
+ struct EndState {
+ BSONObj key;
+ bool inclusive;
+ DiskLoc bucket;
+ int ofs;
+ };
+ boost::optional<EndState> _endState;
+
+ // Used by next to decide to return current position rather than moving. Should be reset
+ // to false by any operation that moves the cursor, other than subsequent save/restore
+ // pairs.
+ bool _lastMoveWasRestore = false;
+
+ // Only used by save/restore() if _bucket is non-Null.
SavedCursorRegistry::SavedCursor _saved;
};
- virtual Cursor* newCursor(OperationContext* txn, int direction) const {
- return new Cursor(txn, _btree.get(), direction);
+ virtual std::unique_ptr<SortedDataInterface::Cursor> newCursor(
+ OperationContext* txn,
+ bool isForward = true) const {
+ return stdx::make_unique<Cursor>(txn, _btree.get(), isForward);
}
virtual Status initAsEmpty(OperationContext* txn) {
@@ -266,6 +346,7 @@ namespace mongo {
private:
scoped_ptr<BtreeLogic<OnDiskFormat> > _btree;
};
+} // namespace
SortedDataInterface* getMMAPV1Interface(HeadManager* headManager,
RecordStore* recordStore,
diff --git a/src/mongo/db/storage/mmap_v1/btree/btree_logic.cpp b/src/mongo/db/storage/mmap_v1/btree/btree_logic.cpp
index 62f77773dd1..98f1d1497e9 100644
--- a/src/mongo/db/storage/mmap_v1/btree/btree_logic.cpp
+++ b/src/mongo/db/storage/mmap_v1/btree/btree_logic.cpp
@@ -678,25 +678,11 @@ namespace mongo {
void BtreeLogic<BtreeLayout>::customLocate(OperationContext* txn,
DiskLoc* locInOut,
int* keyOfsInOut,
- const BSONObj& keyBegin,
- int keyBeginLen,
- bool afterKey,
- const vector<const BSONElement*>& keyEnd,
- const vector<bool>& keyEndInclusive,
+ const IndexSeekPoint& seekPoint,
int direction) const {
pair<DiskLoc, int> unused;
- customLocate(txn,
- locInOut,
- keyOfsInOut,
- keyBegin,
- keyBeginLen,
- afterKey,
- keyEnd,
- keyEndInclusive,
- direction,
- unused);
-
+ customLocate(txn, locInOut, keyOfsInOut, seekPoint, direction, unused);
skipUnusedKeys(txn, locInOut, keyOfsInOut, direction);
}
@@ -724,23 +710,10 @@ namespace mongo {
void BtreeLogic<BtreeLayout>::advanceTo(OperationContext* txn,
DiskLoc* thisLocInOut,
int* keyOfsInOut,
- const BSONObj &keyBegin,
- int keyBeginLen,
- bool afterKey,
- const vector<const BSONElement*>& keyEnd,
- const vector<bool>& keyEndInclusive,
+ const IndexSeekPoint& seekPoint,
int direction) const {
- advanceToImpl(txn,
- thisLocInOut,
- keyOfsInOut,
- keyBegin,
- keyBeginLen,
- afterKey,
- keyEnd,
- keyEndInclusive,
- direction);
-
+ advanceToImpl(txn, thisLocInOut, keyOfsInOut, seekPoint, direction);
skipUnusedKeys(txn, thisLocInOut, keyOfsInOut, direction);
}
@@ -757,11 +730,7 @@ namespace mongo {
void BtreeLogic<BtreeLayout>::advanceToImpl(OperationContext* txn,
DiskLoc* thisLocInOut,
int* keyOfsInOut,
- const BSONObj &keyBegin,
- int keyBeginLen,
- bool afterKey,
- const vector<const BSONElement*>& keyEnd,
- const vector<bool>& keyEndInclusive,
+ const IndexSeekPoint& seekPoint,
int direction) const {
BucketType* bucket = getBucket(txn, *thisLocInOut);
@@ -773,12 +742,7 @@ namespace mongo {
l = *keyOfsInOut;
h = bucket->n - 1;
int cmpResult = customBSONCmp(getFullKey(bucket, h).data.toBson(),
- keyBegin,
- keyBeginLen,
- afterKey,
- keyEnd,
- keyEndInclusive,
- _ordering,
+ seekPoint,
direction);
dontGoUp = (cmpResult >= 0);
}
@@ -786,12 +750,7 @@ namespace mongo {
l = 0;
h = *keyOfsInOut;
int cmpResult = customBSONCmp(getFullKey(bucket, l).data.toBson(),
- keyBegin,
- keyBeginLen,
- afterKey,
- keyEnd,
- keyEndInclusive,
- _ordering,
+ seekPoint,
direction);
dontGoUp = (cmpResult <= 0);
}
@@ -803,12 +762,7 @@ namespace mongo {
if (!customFind(txn,
l,
h,
- keyBegin,
- keyBeginLen,
- afterKey,
- keyEnd,
- keyEndInclusive,
- _ordering,
+ seekPoint,
direction,
thisLocInOut,
keyOfsInOut,
@@ -825,24 +779,14 @@ namespace mongo {
if (direction > 0) {
if (customBSONCmp(getFullKey(bucket, bucket->n - 1).data.toBson(),
- keyBegin,
- keyBeginLen,
- afterKey,
- keyEnd,
- keyEndInclusive,
- _ordering,
+ seekPoint,
direction) >= 0 ) {
break;
}
}
else {
if (customBSONCmp(getFullKey(bucket, 0).data.toBson(),
- keyBegin,
- keyBeginLen,
- afterKey,
- keyEnd,
- keyEndInclusive,
- _ordering,
+ seekPoint,
direction) <= 0) {
break;
}
@@ -850,27 +794,14 @@ namespace mongo {
}
}
- customLocate(txn,
- thisLocInOut,
- keyOfsInOut,
- keyBegin,
- keyBeginLen,
- afterKey,
- keyEnd,
- keyEndInclusive,
- direction,
- bestParent);
+ customLocate(txn, thisLocInOut, keyOfsInOut, seekPoint, direction, bestParent);
}
template <class BtreeLayout>
void BtreeLogic<BtreeLayout>::customLocate(OperationContext* txn,
DiskLoc* locInOut,
int* keyOfsInOut,
- const BSONObj& keyBegin,
- int keyBeginLen,
- bool afterKey,
- const vector<const BSONElement*>& keyEnd,
- const vector<bool>& keyEndInclusive,
+ const IndexSeekPoint& seekPoint,
int direction,
pair<DiskLoc, int>& bestParent) const {
@@ -890,16 +821,7 @@ namespace mongo {
int z = (direction > 0) ? 0 : h;
// leftmost/rightmost key may possibly be >=/<= search key
- int res = customBSONCmp(getFullKey(bucket, z).data.toBson(),
- keyBegin,
- keyBeginLen,
- afterKey,
- keyEnd,
- keyEndInclusive,
- _ordering,
- direction);
-
-
+ int res = customBSONCmp(getFullKey(bucket, z).data.toBson(), seekPoint, direction);
if (direction * res >= 0) {
DiskLoc next;
*keyOfsInOut = z;
@@ -923,15 +845,7 @@ namespace mongo {
}
}
- res = customBSONCmp(getFullKey(bucket, h - z).data.toBson(),
- keyBegin,
- keyBeginLen,
- afterKey,
- keyEnd,
- keyEndInclusive,
- _ordering,
- direction);
-
+ res = customBSONCmp(getFullKey(bucket, h - z).data.toBson(), seekPoint, direction);
if (direction * res < 0) {
DiskLoc next;
if (direction > 0) {
@@ -957,12 +871,7 @@ namespace mongo {
if (!customFind(txn,
l,
h,
- keyBegin,
- keyBeginLen,
- afterKey,
- keyEnd,
- keyEndInclusive,
- _ordering,
+ seekPoint,
direction,
locInOut,
keyOfsInOut,
@@ -978,12 +887,7 @@ namespace mongo {
bool BtreeLogic<BtreeLayout>::customFind(OperationContext* txn,
int low,
int high,
- const BSONObj& keyBegin,
- int keyBeginLen,
- bool afterKey,
- const vector<const BSONElement*>& keyEnd,
- const vector<bool>& keyEndInclusive,
- const Ordering& order,
+ const IndexSeekPoint& seekPoint,
int direction,
DiskLoc* thisLocInOut,
int* keyOfsInOut,
@@ -1007,15 +911,7 @@ namespace mongo {
int middle = low + (high - low) / 2;
- int cmp = customBSONCmp(getFullKey(bucket, middle).data.toBson(),
- keyBegin,
- keyBeginLen,
- afterKey,
- keyEnd,
- keyEndInclusive,
- order,
- direction);
-
+ int cmp = customBSONCmp(getFullKey(bucket, middle).data.toBson(), seekPoint, direction);
if (cmp < 0) {
low = middle;
}
@@ -1047,50 +943,45 @@ namespace mongo {
*/
// static
template <class BtreeLayout>
- int BtreeLogic<BtreeLayout>::customBSONCmp(const BSONObj& l,
- const BSONObj& rBegin,
- int rBeginLen,
- bool rSup,
- const vector<const BSONElement*>& rEnd,
- const vector<bool>& rEndInclusive,
- const Ordering& o,
+ int BtreeLogic<BtreeLayout>::customBSONCmp(const BSONObj& left,
+ const IndexSeekPoint& right,
int direction) const {
// XXX: make this readable
- BSONObjIterator ll( l );
- BSONObjIterator rr( rBegin );
- vector< const BSONElement * >::const_iterator rr2 = rEnd.begin();
- vector< bool >::const_iterator inc = rEndInclusive.begin();
+ dassert(right.keySuffix.size() == right.suffixInclusive.size());
+
+ BSONObjIterator ll( left );
+ BSONObjIterator rr( right.keyPrefix );
unsigned mask = 1;
- for( int i = 0; i < rBeginLen; ++i, mask <<= 1 ) {
+ size_t i = 0;
+ for( ; i < size_t(right.prefixLen); ++i, mask <<= 1 ) {
BSONElement lll = ll.next();
BSONElement rrr = rr.next();
- ++rr2;
- ++inc;
int x = lll.woCompare( rrr, false );
- if ( o.descending( mask ) )
+ if ( _ordering.descending( mask ) )
x = -x;
if ( x != 0 )
return x;
}
- if ( rSup ) {
+ if (right.prefixExclusive) {
return -direction;
}
- for( ; ll.more(); mask <<= 1 ) {
+ for( ; i < right.keySuffix.size(); ++i, mask <<= 1 ) {
+ if (!ll.more())
+ return -direction;
+
BSONElement lll = ll.next();
- BSONElement rrr = **rr2;
- ++rr2;
+ BSONElement rrr = *right.keySuffix[i];
int x = lll.woCompare( rrr, false );
- if ( o.descending( mask ) )
+ if ( _ordering.descending( mask ) )
x = -x;
if ( x != 0 )
return x;
- if ( !*inc ) {
+ if ( !right.suffixInclusive[i] ) {
return -direction;
}
- ++inc;
}
- return 0;
+ return ll.more() ? direction : 0;
}
template <class BtreeLayout>
diff --git a/src/mongo/db/storage/mmap_v1/btree/btree_logic.h b/src/mongo/db/storage/mmap_v1/btree/btree_logic.h
index 942f2b41365..5b4fdc8205e 100644
--- a/src/mongo/db/storage/mmap_v1/btree/btree_logic.h
+++ b/src/mongo/db/storage/mmap_v1/btree/btree_logic.h
@@ -34,6 +34,7 @@
#include "mongo/db/catalog/index_catalog_entry.h"
#include "mongo/db/jsobj.h"
#include "mongo/db/operation_context.h"
+#include "mongo/db/storage/index_entry_comparison.h"
#include "mongo/db/storage/mmap_v1/btree/btree_ondisk.h"
#include "mongo/db/storage/mmap_v1/btree/key.h"
#include "mongo/db/storage/mmap_v1/diskloc.h"
@@ -196,21 +197,13 @@ namespace mongo {
void customLocate(OperationContext* txn,
DiskLoc* locInOut,
int* keyOfsInOut,
- const BSONObj& keyBegin,
- int keyBeginLen,
- bool afterKey,
- const std::vector<const BSONElement*>& keyEnd,
- const std::vector<bool>& keyEndInclusive,
+ const IndexSeekPoint& seekPoint,
int direction) const;
void advanceTo(OperationContext*,
DiskLoc* thisLocInOut,
int* keyOfsInOut,
- const BSONObj &keyBegin,
- int keyBeginLen,
- bool afterKey,
- const std::vector<const BSONElement*>& keyEnd,
- const std::vector<bool>& keyEndInclusive,
+ const IndexSeekPoint& seekPoint,
int direction) const;
void restorePosition(OperationContext* txn,
@@ -238,6 +231,12 @@ namespace mongo {
SavedCursorRegistry* savedCursors() const { return _cursorRegistry; }
static int lowWaterMark();
+
+ Ordering ordering() const { return _ordering; }
+
+ int customBSONCmp(const BSONObj& inIndex_left,
+ const IndexSeekPoint& seekPoint_right,
+ int direction) const;
private:
friend class BtreeLogic::Builder;
@@ -348,11 +347,7 @@ namespace mongo {
void customLocate(OperationContext* txn,
DiskLoc* locInOut,
int* keyOfsInOut,
- const BSONObj& keyBegin,
- int keyBeginLen,
- bool afterKey,
- const std::vector<const BSONElement*>& keyEnd,
- const std::vector<bool>& keyEndInclusive,
+ const IndexSeekPoint& seekPoint,
int direction,
std::pair<DiskLoc, int>& bestParent) const;
@@ -367,12 +362,7 @@ namespace mongo {
bool customFind(OperationContext* txn,
int low,
int high,
- const BSONObj& keyBegin,
- int keyBeginLen,
- bool afterKey,
- const std::vector<const BSONElement*>& keyEnd,
- const std::vector<bool>& keyEndInclusive,
- const Ordering& order,
+ const IndexSeekPoint& seekPoint,
int direction,
DiskLoc* thisLocInOut,
int* keyOfsInOut,
@@ -381,11 +371,7 @@ namespace mongo {
void advanceToImpl(OperationContext* txn,
DiskLoc* thisLocInOut,
int* keyOfsInOut,
- const BSONObj &keyBegin,
- int keyBeginLen,
- bool afterKey,
- const std::vector<const BSONElement*>& keyEnd,
- const std::vector<bool>& keyEndInclusive,
+ const IndexSeekPoint& seekPoint,
int direction) const;
bool wouldCreateDup(OperationContext* txn,
@@ -547,16 +533,6 @@ namespace mongo {
BucketType* bucket,
int keyPos) const;
- // TODO 'this' for _ordering(?)
- int customBSONCmp(const BSONObj& l,
- const BSONObj& rBegin,
- int rBeginLen,
- bool rSup,
- const std::vector<const BSONElement*>& rEnd,
- const std::vector<bool>& rEndInclusive,
- const Ordering& o,
- int direction) const;
-
/**
* Tries to push key into bucket. Return false if it can't because key doesn't fit.
*
diff --git a/src/mongo/db/storage/sorted_data_interface.h b/src/mongo/db/storage/sorted_data_interface.h
index 98bac8e5136..006fa7ff4dd 100644
--- a/src/mongo/db/storage/sorted_data_interface.h
+++ b/src/mongo/db/storage/sorted_data_interface.h
@@ -26,12 +26,14 @@
* it in the license file.
*/
-#include "mongo/bson/ordering.h"
-#include "mongo/db/catalog/head_manager.h"
+#include <boost/optional/optional.hpp>
+#include <boost/optional/optional_io.hpp>
+#include <memory>
+
#include "mongo/db/jsobj.h"
#include "mongo/db/operation_context.h"
#include "mongo/db/record_id.h"
-#include "mongo/db/storage/record_store.h"
+#include "mongo/db/storage/index_entry_comparison.h"
#pragma once
@@ -179,172 +181,161 @@ namespace mongo {
}
/**
- * Navigation
+ * Navigates over the sorted data.
+ *
+ * A cursor is constructed with a direction flag with the following effects:
+ * - The direction that next() moves.
+ * - If a seek method hits an exact match on key, forward cursors will be positioned on
+ * the first value for that key, reverse cursors on the last.
+ * - If a seek method or restore does not hit an exact match, cursors will be
+ * positioned on the closest position *after* the query in the direction of the
+ * search.
+ * - The end position is on the "far" side of the query. In a forward cursor that means
+ * that it is the lowest value for the key if the end is exclusive or the first entry
+ * past the key if the end is inclusive or there are no exact matches.
*
* A cursor is tied to a transaction, such as the OperationContext or a WriteUnitOfWork
* inside that context. Any cursor acquired inside a transaction is invalid outside
- * of that transaction, instead use the savePosition() and restorePosition() methods
- * reestablish the cursor.
+ * of that transaction, instead use the save and restore methods to reestablish the cursor.
+ *
+ * Any method other than the save methods may throw WriteConflict exception. If that
+ * happens, the cursor may not be used again until it has been saved and successfully
+ * restored. If next() or restore() throw a WCE the cursor's position will be the same as
+ * before the call (strong exception guarantee). All other methods leave the cursor in a
+ * valid state but with an unspecified position (basic exception guarantee). All methods
+ * only provide the basic guarantee for exceptions other than WCE.
+ *
+ * Any returned unowned BSON is only valid until the next call to any method on this
+ * interface. The implementations must assume that passed-in unowned BSON is only valid for
+ * the duration of the call.
+ *
+ * Implementations may override any default implementation if they can provide a more
+ * efficient implementation.
*/
class Cursor {
public:
- virtual ~Cursor() {}
/**
- * Return the direction of 'this' cursor.
+ * Tells methods that return an IndexKeyEntry what part of the data the caller is
+ * interested in.
+ *
+ * Methods returning an engaged optional<T> will only return null RecordIds or empty
+ * BSONObjs if they have been explicitly left out of the request.
*
- * @return +1 for a forward cursor or -1 for a reverse cursor
+ * Implementations are allowed to return more data than requested, but not less.
*/
- virtual int getDirection() const = 0;
+ enum RequestedInfo {
+ // Only usable part of the return is whether it is engaged or not.
+ kJustExistance = 0,
+ // Key must be filled in.
+ kWantKey = 1,
+ // Loc must be fulled in.
+ kWantLoc = 2,
+ // Both must be returned.
+ kKeyAndLoc = kWantKey | kWantLoc,
+ };
- /**
- * Return true if 'this' forward (reverse) cursor is positioned
- * past the end (before the beginning) of the index, and false otherwise.
- */
- virtual bool isEOF() const = 0;
+ virtual ~Cursor() = default;
- /**
- * Return true if 'this' cursor and the 'other' cursor are positioned at
- * the same key and RecordId, or if both cursors are at EOF. Otherwise,
- * this function returns false.
- *
- * Implementations should prohibit the comparison of cursors associated
- * with different indices.
- */
- virtual bool pointsToSamePlaceAs(const Cursor& other) const = 0;
/**
- * Position 'this' forward (reverse) cursor either at the entry or
- * immediately after (or immediately before) the specified key and RecordId.
- * The cursor should be positioned at EOF if no such entry exists.
+ * Sets the position to stop scanning. An empty key unsets the end position.
*
- * @return true if the entry (key, RecordId) exists within the index,
- * and false otherwise
- */
- virtual bool locate(const BSONObj& key, const RecordId& loc) = 0;
-
- /**
- * Position 'this' forward (reverse) cursor either at the next
- * (previous) occurrence of a particular key or immediately after
- * (or immediately before).
+ * If next() hits this position, or a seek method attempts to seek past it they
+ * unposition the cursor and return boost::none.
*
- * @see SortedDataInterface::customLocate
+ * Setting the end position should be done before seeking since the current position, if
+ * any, isn't checked.
*/
- virtual void advanceTo(const BSONObj &keyPrefix,
- int prefixLen,
- bool prefixExclusive,
- const std::vector<const BSONElement*>& keySuffix,
- const std::vector<bool>& suffixInclusive) = 0;
+ virtual void setEndPosition(const BSONObj& key, bool inclusive) = 0;
/**
- * Position 'this' forward (reverse) cursor either at the first
- * (last) occurrence of a particular key or immediately after
- * (or immediately before). The key is a typical BSONObj,
- * represented by the specified parameters in the following way:
- *
- * The first 'prefixLen' elements of 'keyPrefix' followed by
- * the last 'keySuffix.size() - prefixLen' elements of 'keySuffix'.
- *
- * e.g.
- *
- * Suppose that
- *
- * keyPrefix = { "" : 1, "" : 2 }
- * prefixLen = 1
- * prefixExclusive = false
- * keySuffix = [ IGNORED; { "" : 5 } ]
- * suffixInclusive = [ IGNORED; false ]
- *
- * ==> represented key is { "" : 1, "" : 5 }
- * with the exclusive byte set on the second field
- *
- * Suppose that
- *
- * keyPrefix = { "" : 1, "" : 2 }
- * prefixLen = 1
- * prefixExclusive = true
- * keySuffix = IGNORED
- * suffixInclusive = IGNORED
- *
- * ==> represented key is { "" : 1 }
- * with the exclusive byte set on the first field
- *
- * @param prefixExclusive true if 'this' forward (reverse) cursor
- * should be positioned immediately after (immediately
- * before) the represented key, and false otherwise
- *
- * @param suffixInclusive an element of the vector is false if
- * 'this' forward (reverse) cursor should be positioned
- * immediately after (immediately before) the represented
- * key, and true otherwise
- *
- * Implementations should prohibit callers from specifying
- * 'prefixLen = 0' when 'prefixExclusive = true'.
- *
- * @see IndexEntryComparison::makeQueryObject
+ * Moves forward and returns the new data or boost::none if there is no more data.
+ * If not positioned, returns boost::none.
*/
- virtual void customLocate(const BSONObj& keyPrefix,
- int prefixLen,
- bool prefixExclusive,
- const std::vector<const BSONElement*>& keySuffix,
- const std::vector<bool>& suffixInclusive) = 0;
+ virtual boost::optional<IndexKeyEntry> next(RequestedInfo parts = kKeyAndLoc) = 0;
+
+ //
+ // Seeking
+ //
/**
- * Return the key associated with the current position of 'this' cursor.
+ * Seeks to the provided key and returns current position.
+ *
+ * TODO consider removing once IndexSeekPoint has been cleaned up a bit. In particular,
+ * need a way to specify use whole keyPrefix and nothing else and to support the
+ * combination of empty and exclusive. Should also make it easier to construct for the
+ * common cases.
*/
- virtual BSONObj getKey() const = 0;
+ virtual boost::optional<IndexKeyEntry> seek(const BSONObj& key,
+ bool inclusive,
+ RequestedInfo parts = kKeyAndLoc) = 0;
/**
- * Return the RecordId associated with the current position of 'this' cursor.
+ * Seeks to the position described by seekPoint and returns the current position.
+ *
+ * NOTE: most implementations should just pass seekPoint to
+ * IndexEntryComparison::makeQueryObject().
*/
- virtual RecordId getRecordId() const = 0;
+ virtual boost::optional<IndexKeyEntry> seek(const IndexSeekPoint& seekPoint,
+ RequestedInfo parts = kKeyAndLoc) = 0;
/**
- * Position 'this' forward (reverse) cursor at the next (preceding) entry
- * in the index. A cursor positioned at EOF should remain at EOF when advanced.
+ * Seeks to a key with a hint to the implementation that you only want exact matches. If
+ * an exact match can't be found, boost::none will be returned and the resulting
+ * position of the cursor is unspecified.
*/
- virtual void advance() = 0;
+ virtual boost::optional<IndexKeyEntry> seekExact(const BSONObj& key,
+ RequestedInfo parts = kKeyAndLoc) {
+ auto kv = seek(key, true, kKeyAndLoc);
+ if (kv && kv->key.woCompare(key, BSONObj(), /*considerFieldNames*/false) == 0)
+ return kv;
+ return {};
+ }
//
// Saving and restoring state
//
/**
- * Save the entry in the index (i.e. its key and RecordId) of where
- * 'this' cursor is currently positioned.
+ * Prepares for state changes in underlying data in a way that allows the cursor's
+ * current position to be restored.
*
- * Implementations can assume that no operations other than delete
- * or restorePosition() will be called on 'this' cursor after its
- * position has been saved.
+ * It is safe to call savePositioned multiple times in a row.
+ * No other method (excluding destructor) may be called until successfully restored.
*/
- virtual void savePosition() = 0;
+ virtual void savePositioned() = 0;
/**
- * Restore 'this' cursor to the previously saved entry in the index.
+ * Prepares for state changes in underlying data without necessarily saving the current
+ * state.
*
- * Implementations should have the same behavior as calling locate()
- * with the saved key and RecordId.
+ * The cursor's position when restored is unspecified. Caller is expected to seek
+ * following the restore.
+ *
+ * It is safe to call saveUnpositioned multiple times in a row.
+ * No other method (excluding destructor) may be called until successfully restored.
+ */
+ virtual void saveUnpositioned() { savePositioned(); }
+
+ /**
+ * Recovers from potential state changes in underlying data.
+ *
+ * If the former position no longer exists, a following call to next() will return the
+ * next closest position in the direction of the scan, if any.
+ *
+ * This handles restoring after either savePositioned() or saveUnpositioned().
*/
- virtual void restorePosition(OperationContext* txn) = 0;
+ virtual void restore(OperationContext* txn) = 0;
};
/**
- * Return a cursor over 'this' index. The cursor need not be positioned
- * at any particular entry.
- *
- * Implementations can assume that locate() is called on the cursor
- * before it gets used.
- *
- * Implementations can assume that 'this' index outlives all cursors
- * it produces.
+ * Returns an unpositioned cursor over 'this' index.
*
- * @param txn the transaction to which the cursor is tied
- * @param direction the direction of the cursor.
- * +1 for a forward cursor or -1 for a reverse cursor.
- *
- * @return caller takes ownership
+ * Implementations can assume that 'this' index outlives all cursors it produces.
*/
- virtual Cursor* newCursor(OperationContext* txn, int direction) const = 0;
+ virtual std::unique_ptr<Cursor> newCursor(OperationContext* txn,
+ bool isForward = true) const = 0;
//
// Index creation
diff --git a/src/mongo/db/storage/sorted_data_interface_test_cursor.cpp b/src/mongo/db/storage/sorted_data_interface_test_cursor.cpp
index 069c93a4041..d4a99333fdc 100644
--- a/src/mongo/db/storage/sorted_data_interface_test_cursor.cpp
+++ b/src/mongo/db/storage/sorted_data_interface_test_cursor.cpp
@@ -30,99 +30,69 @@
#include "mongo/db/storage/sorted_data_interface_test_harness.h"
-#include <boost/scoped_ptr.hpp>
+#include <memory>
#include "mongo/db/storage/sorted_data_interface.h"
#include "mongo/unittest/unittest.h"
namespace mongo {
- using boost::scoped_ptr;
-
- // Call getDirection() on a forward cursor and verify the result equals +1.
- TEST( SortedDataInterface, GetCursorDirection ) {
- scoped_ptr<HarnessHelper> harnessHelper( newHarnessHelper() );
- scoped_ptr<SortedDataInterface> sorted( harnessHelper->newSortedDataInterface( false ) );
-
- {
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
- scoped_ptr<SortedDataInterface::Cursor> cursor( sorted->newCursor( opCtx.get(), 1 ) );
- ASSERT_EQUALS( 1, cursor->getDirection() );
- }
- }
-
- // Call getDirection() on a reverse cursor and verify the result equals -1.
- TEST( SortedDataInterface, GetCursorDirectionReversed ) {
- scoped_ptr<HarnessHelper> harnessHelper( newHarnessHelper() );
- scoped_ptr<SortedDataInterface> sorted( harnessHelper->newSortedDataInterface( false ) );
-
- {
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
- scoped_ptr<SortedDataInterface::Cursor> cursor( sorted->newCursor( opCtx.get(), -1 ) );
- ASSERT_EQUALS( -1, cursor->getDirection() );
- }
- }
-
// Verify that a forward cursor is positioned at EOF when the index is empty.
TEST( SortedDataInterface, CursorIsEOFWhenEmpty ) {
- scoped_ptr<HarnessHelper> harnessHelper( newHarnessHelper() );
- scoped_ptr<SortedDataInterface> sorted( harnessHelper->newSortedDataInterface( false ) );
+ const std::unique_ptr<HarnessHelper> harnessHelper( newHarnessHelper() );
+ const std::unique_ptr<SortedDataInterface> sorted( harnessHelper->newSortedDataInterface( false ) );
{
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
ASSERT( sorted->isEmpty( opCtx.get() ) );
}
{
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
- scoped_ptr<SortedDataInterface::Cursor> cursor( sorted->newCursor( opCtx.get(), 1 ) );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<SortedDataInterface::Cursor> cursor( sorted->newCursor(opCtx.get()) );
- ASSERT( !cursor->locate( minKey, RecordId::min() ) );
- ASSERT( cursor->isEOF() );
+ ASSERT( !cursor->seek(minKey, true) );
// Cursor at EOF should remain at EOF when advanced
- cursor->advance();
- ASSERT( cursor->isEOF() );
+ ASSERT( !cursor->next() );
}
}
// Verify that a reverse cursor is positioned at EOF when the index is empty.
TEST( SortedDataInterface, CursorIsEOFWhenEmptyReversed ) {
- scoped_ptr<HarnessHelper> harnessHelper( newHarnessHelper() );
- scoped_ptr<SortedDataInterface> sorted( harnessHelper->newSortedDataInterface( false ) );
+ const std::unique_ptr<HarnessHelper> harnessHelper( newHarnessHelper() );
+ const std::unique_ptr<SortedDataInterface> sorted( harnessHelper->newSortedDataInterface( false ) );
{
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
ASSERT( sorted->isEmpty( opCtx.get() ) );
}
{
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
- scoped_ptr<SortedDataInterface::Cursor> cursor( sorted->newCursor( opCtx.get(), -1 ) );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<SortedDataInterface::Cursor> cursor( sorted->newCursor(opCtx.get(), false) );
- ASSERT( !cursor->locate( maxKey, RecordId::max() ) );
- ASSERT( cursor->isEOF() );
+ ASSERT( !cursor->seek( maxKey, true ) );
// Cursor at EOF should remain at EOF when advanced
- cursor->advance();
- ASSERT( cursor->isEOF() );
+ ASSERT( !cursor->next() );
}
}
// Call advance() on a forward cursor until it is exhausted.
// When a cursor positioned at EOF is advanced, it stays at EOF.
TEST( SortedDataInterface, ExhaustCursor ) {
- scoped_ptr<HarnessHelper> harnessHelper( newHarnessHelper() );
- scoped_ptr<SortedDataInterface> sorted( harnessHelper->newSortedDataInterface( false ) );
+ const std::unique_ptr<HarnessHelper> harnessHelper( newHarnessHelper() );
+ const std::unique_ptr<SortedDataInterface> sorted( harnessHelper->newSortedDataInterface( false ) );
{
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
ASSERT( sorted->isEmpty( opCtx.get() ) );
}
int nToInsert = 10;
for ( int i = 0; i < nToInsert; i++ ) {
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
{
WriteUnitOfWork uow( opCtx.get() );
BSONObj key = BSON( "" << i );
@@ -133,42 +103,38 @@ namespace mongo {
}
{
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
ASSERT_EQUALS( nToInsert, sorted->numEntries( opCtx.get() ) );
}
{
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
- scoped_ptr<SortedDataInterface::Cursor> cursor( sorted->newCursor( opCtx.get(), 1 ) );
- ASSERT( !cursor->locate( minKey, RecordId::min() ) );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<SortedDataInterface::Cursor> cursor( sorted->newCursor(opCtx.get()) );
for ( int i = 0; i < nToInsert; i++ ) {
- ASSERT( !cursor->isEOF() );
- ASSERT_EQUALS( BSON( "" << i ), cursor->getKey() );
- ASSERT_EQUALS( RecordId( 42, i * 2 ), cursor->getRecordId() );
- cursor->advance();
+ auto entry = i == 0 ? cursor->seek(minKey, true) : cursor->next();
+ ASSERT_EQ(entry, IndexKeyEntry(BSON("" << i), RecordId(42, i * 2)));
}
- ASSERT( cursor->isEOF() );
+ ASSERT( !cursor->next() );
// Cursor at EOF should remain at EOF when advanced
- cursor->advance();
- ASSERT( cursor->isEOF() );
+ ASSERT( !cursor->next() );
}
}
// Call advance() on a reverse cursor until it is exhausted.
// When a cursor positioned at EOF is advanced, it stays at EOF.
TEST( SortedDataInterface, ExhaustCursorReversed ) {
- scoped_ptr<HarnessHelper> harnessHelper( newHarnessHelper() );
- scoped_ptr<SortedDataInterface> sorted( harnessHelper->newSortedDataInterface( false ) );
+ const std::unique_ptr<HarnessHelper> harnessHelper( newHarnessHelper() );
+ const std::unique_ptr<SortedDataInterface> sorted( harnessHelper->newSortedDataInterface( false ) );
{
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
ASSERT( sorted->isEmpty( opCtx.get() ) );
}
int nToInsert = 10;
for ( int i = 0; i < nToInsert; i++ ) {
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
{
WriteUnitOfWork uow( opCtx.get() );
BSONObj key = BSON( "" << i );
@@ -179,25 +145,21 @@ namespace mongo {
}
{
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
ASSERT_EQUALS( nToInsert, sorted->numEntries( opCtx.get() ) );
}
{
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
- scoped_ptr<SortedDataInterface::Cursor> cursor( sorted->newCursor( opCtx.get(), -1 ) );
- ASSERT( !cursor->locate( maxKey, RecordId::max() ) );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<SortedDataInterface::Cursor> cursor( sorted->newCursor(opCtx.get(), false) );
for ( int i = nToInsert - 1; i >= 0; i-- ) {
- ASSERT( !cursor->isEOF() );
- ASSERT_EQUALS( BSON( "" << i ), cursor->getKey() );
- ASSERT_EQUALS( RecordId( 42, i * 2 ), cursor->getRecordId() );
- cursor->advance();
+ auto entry = (i == nToInsert - 1) ? cursor->seek(maxKey, true) : cursor->next();
+ ASSERT_EQ(entry, IndexKeyEntry(BSON("" << i), RecordId(42, i * 2)));
}
- ASSERT( cursor->isEOF() );
+ ASSERT( !cursor->next() );
// Cursor at EOF should remain at EOF when advanced
- cursor->advance();
- ASSERT( cursor->isEOF() );
+ ASSERT( !cursor->next() );
}
}
diff --git a/src/mongo/db/storage/sorted_data_interface_test_cursor_advanceto.cpp b/src/mongo/db/storage/sorted_data_interface_test_cursor_advanceto.cpp
index 29f4122d539..2b094626ac6 100644
--- a/src/mongo/db/storage/sorted_data_interface_test_cursor_advanceto.cpp
+++ b/src/mongo/db/storage/sorted_data_interface_test_cursor_advanceto.cpp
@@ -30,32 +30,29 @@
#include "mongo/db/storage/sorted_data_interface_test_harness.h"
-#include <boost/scoped_ptr.hpp>
+#include <memory>
#include "mongo/db/storage/sorted_data_interface.h"
#include "mongo/unittest/unittest.h"
namespace mongo {
- using boost::scoped_ptr;
- using std::vector;
-
// Insert multiple single-field keys and advance to each of them
// using a forward cursor by specifying their exact key. When
// advanceTo() is called on a duplicate key, the cursor is
- // positioned at the next occurrence of that key in ascending
+ // positioned at the first occurrence of that key in ascending
// order by RecordId.
TEST( SortedDataInterface, AdvanceTo ) {
- scoped_ptr<HarnessHelper> harnessHelper( newHarnessHelper() );
- scoped_ptr<SortedDataInterface> sorted( harnessHelper->newSortedDataInterface( false ) );
+ const std::unique_ptr<HarnessHelper> harnessHelper( newHarnessHelper() );
+ const std::unique_ptr<SortedDataInterface> sorted( harnessHelper->newSortedDataInterface( false ) );
{
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
ASSERT( sorted->isEmpty( opCtx.get() ) );
}
{
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
{
WriteUnitOfWork uow( opCtx.get() );
ASSERT_OK( sorted->insert( opCtx.get(), key1, loc1, true ) );
@@ -68,73 +65,49 @@ namespace mongo {
}
{
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
ASSERT_EQUALS( 5, sorted->numEntries( opCtx.get() ) );
}
{
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
- scoped_ptr<SortedDataInterface::Cursor> cursor( sorted->newCursor( opCtx.get(), 1 ) );
-
- ASSERT( cursor->locate( key1, loc1 ) );
- ASSERT_EQUALS( key1, cursor->getKey() );
- ASSERT_EQUALS( loc1, cursor->getRecordId() );
-
- {
- vector<const BSONElement*> keyEnd( 1 );
- vector<bool> keyEndInclusive( 1 );
-
- cursor->advanceTo( key1, 1, false, keyEnd, keyEndInclusive );
- // SERVER-15489 forward cursor is positioned at first occurrence of key in index
- // when advanceTo() called on duplicate key
- // ASSERT_EQUALS( key1, cursor->getKey() );
- // ASSERT_EQUALS( loc2, cursor->getRecordId() );
- }
-
- {
- vector<const BSONElement*> keyEnd( 1 );
- vector<bool> keyEndInclusive( 1 );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<SortedDataInterface::Cursor> cursor( sorted->newCursor(opCtx.get()) );
- cursor->advanceTo( key2, 1, false, keyEnd, keyEndInclusive );
- ASSERT_EQUALS( key2, cursor->getKey() );
- ASSERT_EQUALS( loc4, cursor->getRecordId() );
- }
+ ASSERT_EQ(cursor->seek(key1, true), IndexKeyEntry(key1, loc1));
- {
- vector<const BSONElement*> keyEnd( 1 );
- vector<bool> keyEndInclusive( 1 );
+ IndexSeekPoint seekPoint;
+ seekPoint.keyPrefix = key1;
+ seekPoint.prefixLen = 1;
+ seekPoint.prefixExclusive = false;
+ ASSERT_EQ(cursor->seek(seekPoint), IndexKeyEntry(key1, loc1));
- cursor->advanceTo( key3, 1, false, keyEnd, keyEndInclusive );
- ASSERT_EQUALS( key3, cursor->getKey() );
- ASSERT_EQUALS( loc5, cursor->getRecordId() );
- }
+ seekPoint.keyPrefix = key2;
+ ASSERT_EQ(cursor->seek(seekPoint), IndexKeyEntry(key2, loc4));
- {
- vector<const BSONElement*> keyEnd( 1 );
- vector<bool> keyEndInclusive( 1 );
+ seekPoint.keyPrefix = key3;
+ ASSERT_EQ(cursor->seek(seekPoint), IndexKeyEntry(key3, loc5));
- cursor->advanceTo( key4, 1, false, keyEnd, keyEndInclusive );
- ASSERT( cursor->isEOF() );
- }
+ seekPoint.keyPrefix = key4;
+ ASSERT_EQ(cursor->seek(seekPoint), boost::none);
}
}
// Insert multiple single-field keys and advance to each of them
// using a reverse cursor by specifying their exact key. When
// advanceTo() is called on a duplicate key, the cursor is
- // positioned at the next occurrence of that key in descending
- // order by RecordId.
+ // positioned at the first occurrence of that key in descending
+ // order by RecordId (last occurrence in index order).
TEST( SortedDataInterface, AdvanceToReversed ) {
- scoped_ptr<HarnessHelper> harnessHelper( newHarnessHelper() );
- scoped_ptr<SortedDataInterface> sorted( harnessHelper->newSortedDataInterface( false ) );
+ const std::unique_ptr<HarnessHelper> harnessHelper( newHarnessHelper() );
+ const std::unique_ptr<SortedDataInterface> sorted( harnessHelper->newSortedDataInterface( false ) );
{
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
ASSERT( sorted->isEmpty( opCtx.get() ) );
}
{
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
{
WriteUnitOfWork uow( opCtx.get() );
ASSERT_OK( sorted->insert( opCtx.get(), key1, loc1, true ) );
@@ -147,71 +120,47 @@ namespace mongo {
}
{
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
ASSERT_EQUALS( 5, sorted->numEntries( opCtx.get() ) );
}
{
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
- scoped_ptr<SortedDataInterface::Cursor> cursor( sorted->newCursor( opCtx.get(), -1 ) );
-
- ASSERT( cursor->locate( key3, loc5 ) );
- ASSERT_EQUALS( key3, cursor->getKey() );
- ASSERT_EQUALS( loc5, cursor->getRecordId() );
-
- {
- vector<const BSONElement*> keyEnd( 1 );
- vector<bool> keyEndInclusive( 1 );
-
- cursor->advanceTo( key3, 1, false, keyEnd, keyEndInclusive );
- // SERVER-15490 reverse cursor is positioned at last occurrence of key in index
- // when advanceTo() called on duplicate key
- // ASSERT_EQUALS( key3, cursor->getKey() );
- // ASSERT_EQUALS( loc4, cursor->getRecordId() );
- }
-
- {
- vector<const BSONElement*> keyEnd( 1 );
- vector<bool> keyEndInclusive( 1 );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<SortedDataInterface::Cursor> cursor( sorted->newCursor(opCtx.get(), false) );
- cursor->advanceTo( key2, 1, false, keyEnd, keyEndInclusive );
- ASSERT_EQUALS( key2, cursor->getKey() );
- ASSERT_EQUALS( loc2, cursor->getRecordId() );
- }
+ ASSERT_EQ(cursor->seek(key3, true), IndexKeyEntry(key3, loc5));
- {
- vector<const BSONElement*> keyEnd( 1 );
- vector<bool> keyEndInclusive( 1 );
+ IndexSeekPoint seekPoint;
+ seekPoint.keyPrefix = key3;
+ seekPoint.prefixLen = 1;
+ seekPoint.prefixExclusive = false;
+ ASSERT_EQ(cursor->seek(seekPoint), IndexKeyEntry(key3, loc5));
- cursor->advanceTo( key1, 1, false, keyEnd, keyEndInclusive );
- ASSERT_EQUALS( key1, cursor->getKey() );
- ASSERT_EQUALS( loc1, cursor->getRecordId() );
- }
+ seekPoint.keyPrefix = key2;
+ ASSERT_EQ(cursor->seek(seekPoint), IndexKeyEntry(key2, loc2));
- {
- vector<const BSONElement*> keyEnd( 1 );
- vector<bool> keyEndInclusive( 1 );
+ seekPoint.keyPrefix = key1;
+ ASSERT_EQ(cursor->seek(seekPoint), IndexKeyEntry(key1, loc1));
- cursor->advanceTo( key0, 1, false, keyEnd, keyEndInclusive );
- ASSERT( cursor->isEOF() );
- }
+ seekPoint.keyPrefix = key0;
+ ASSERT_EQ(cursor->seek(seekPoint), boost::none);
}
}
- // Insert two single-field keys and advance to the larger one using
- // a forward cursor positioned at the smaller one, by specifying a key
- // before the current position of the cursor.
+ // Insert two single-field keys, then seek a forward cursor to the larger one then seek behind
+ // the smaller one. Ending position is on the smaller one since a seek describes where to go
+ // and should not be effected by current position.
TEST( SortedDataInterface, AdvanceToKeyBeforeCursorPosition ) {
- scoped_ptr<HarnessHelper> harnessHelper( newHarnessHelper() );
- scoped_ptr<SortedDataInterface> sorted( harnessHelper->newSortedDataInterface( false ) );
+ const std::unique_ptr<HarnessHelper> harnessHelper( newHarnessHelper() );
+ const std::unique_ptr<SortedDataInterface> sorted( harnessHelper->newSortedDataInterface( false ) );
{
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
ASSERT( sorted->isEmpty( opCtx.get() ) );
}
{
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
{
WriteUnitOfWork uow( opCtx.get() );
ASSERT_OK( sorted->insert( opCtx.get(), key1, loc1, true ) );
@@ -221,65 +170,41 @@ namespace mongo {
}
{
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
ASSERT_EQUALS( 2, sorted->numEntries( opCtx.get() ) );
}
{
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
- scoped_ptr<SortedDataInterface::Cursor> cursor( sorted->newCursor( opCtx.get(), 1 ) );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<SortedDataInterface::Cursor> cursor( sorted->newCursor(opCtx.get()) );
- ASSERT( cursor->locate( key1, loc1 ) );
- ASSERT_EQUALS( key1, cursor->getKey() );
- ASSERT_EQUALS( loc1, cursor->getRecordId() );
+ ASSERT_EQ(cursor->seek(key1, true), IndexKeyEntry(key1, loc1));
- {
- vector<const BSONElement*> keyEnd( 1 );
- vector<bool> keyEndInclusive( 1 );
-
- cursor->advanceTo( key0, 1, false, keyEnd, keyEndInclusive );
- // SERVER-15489 forward cursor is positioned at first key in index
- // when advanceTo() called with key smaller than any entry
- // ASSERT_EQUALS( key2, cursor->getKey() );
- // ASSERT_EQUALS( loc2, cursor->getRecordId() );
- }
- }
-
- {
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
- scoped_ptr<SortedDataInterface::Cursor> cursor( sorted->newCursor( opCtx.get(), 1 ) );
-
- ASSERT( cursor->locate( key1, loc1 ) );
- ASSERT_EQUALS( key1, cursor->getKey() );
- ASSERT_EQUALS( loc1, cursor->getRecordId() );
+ IndexSeekPoint seekPoint;
+ seekPoint.keyPrefix = key0;
+ seekPoint.prefixLen = 1;
+ seekPoint.prefixExclusive = false;
+ ASSERT_EQ(cursor->seek(seekPoint), IndexKeyEntry(key1, loc1));
- {
- vector<const BSONElement*> keyEnd( 1 );
- vector<bool> keyEndInclusive( 1 );
-
- cursor->advanceTo( key0, 1, true, keyEnd, keyEndInclusive );
- // SERVER-15489 forward cursor is positioned at first key in index
- // when advanceTo() called with key smaller than any entry
- // ASSERT_EQUALS( key2, cursor->getKey() );
- // ASSERT_EQUALS( loc2, cursor->getRecordId() );
- }
+ seekPoint.prefixExclusive = true;
+ ASSERT_EQ(cursor->seek(seekPoint), IndexKeyEntry(key1, loc1));
}
}
- // Insert two single-field keys and advance to the smaller one using
- // a reverse cursor positioned at the larger one, by specifying a key
- // after the current position of the cursor.
+ // Insert two single-field keys, then seek a reverse cursor to the smaller one then seek behind
+ // the larger one. Ending position is on the larger one since a seek describes where to go
+ // and should not be effected by current position.
TEST( SortedDataInterface, AdvanceToKeyAfterCursorPositionReversed ) {
- scoped_ptr<HarnessHelper> harnessHelper( newHarnessHelper() );
- scoped_ptr<SortedDataInterface> sorted( harnessHelper->newSortedDataInterface( false ) );
+ const std::unique_ptr<HarnessHelper> harnessHelper( newHarnessHelper() );
+ const std::unique_ptr<SortedDataInterface> sorted( harnessHelper->newSortedDataInterface( false ) );
{
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
ASSERT( sorted->isEmpty( opCtx.get() ) );
}
{
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
{
WriteUnitOfWork uow( opCtx.get() );
ASSERT_OK( sorted->insert( opCtx.get(), key1, loc1, true ) );
@@ -289,67 +214,43 @@ namespace mongo {
}
{
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
ASSERT_EQUALS( 2, sorted->numEntries( opCtx.get() ) );
}
{
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
- scoped_ptr<SortedDataInterface::Cursor> cursor( sorted->newCursor( opCtx.get(), -1 ) );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<SortedDataInterface::Cursor> cursor( sorted->newCursor(opCtx.get(), false) );
- ASSERT( cursor->locate( key2, loc2 ) );
- ASSERT_EQUALS( key2, cursor->getKey() );
- ASSERT_EQUALS( loc2, cursor->getRecordId() );
+ ASSERT_EQ(cursor->seek(key2, true), IndexKeyEntry(key2, loc2));
- {
- vector<const BSONElement*> keyEnd( 1 );
- vector<bool> keyEndInclusive( 1 );
-
- cursor->advanceTo( key3, 1, false, keyEnd, keyEndInclusive );
- // SERVER-15490 reverse cursor is positioned at last key in index
- // when advanceTo() called with key larger than any entry
- // ASSERT_EQUALS( key1, cursor->getKey() );
- // ASSERT_EQUALS( loc1, cursor->getRecordId() );
- }
- }
-
- {
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
- scoped_ptr<SortedDataInterface::Cursor> cursor( sorted->newCursor( opCtx.get(), -1 ) );
-
- ASSERT( cursor->locate( key2, loc2 ) );
- ASSERT_EQUALS( key2, cursor->getKey() );
- ASSERT_EQUALS( loc2, cursor->getRecordId() );
+ IndexSeekPoint seekPoint;
+ seekPoint.keyPrefix = key3;
+ seekPoint.prefixLen = 1;
+ seekPoint.prefixExclusive = false;
+ ASSERT_EQ(cursor->seek(seekPoint), IndexKeyEntry(key2, loc2));
- {
- vector<const BSONElement*> keyEnd( 1 );
- vector<bool> keyEndInclusive( 1 );
-
- cursor->advanceTo( key3, 1, true, keyEnd, keyEndInclusive );
- // SERVER-15490 reverse cursor is positioned at last key in index
- // when advanceTo() called with key larger than any entry
- // ASSERT_EQUALS( key1, cursor->getKey() );
- // ASSERT_EQUALS( loc1, cursor->getRecordId() );
- }
+ seekPoint.prefixExclusive = true;
+ ASSERT_EQ(cursor->seek(seekPoint), IndexKeyEntry(key2, loc2));
}
}
// Insert a single-field key and advance to EOF using a forward cursor
- // by specifying that exact key. When advanceTo() is called with the key
- // where the cursor is positioned (and it is the last entry for that key),
- // the cursor should advance to EOF regardless of whether it is in non-
- // or inclusive mode.
+ // by specifying that exact key. When seek() is called with the key
+ // where the cursor is positioned (and it is the first entry for that key),
+ // the cursor should remain at its current position. An exclusive seek will
+ // position the cursor on the next position, which may be EOF.
TEST( SortedDataInterface, AdvanceToKeyAtCursorPosition ) {
- scoped_ptr<HarnessHelper> harnessHelper( newHarnessHelper() );
- scoped_ptr<SortedDataInterface> sorted( harnessHelper->newSortedDataInterface( false ) );
+ const std::unique_ptr<HarnessHelper> harnessHelper( newHarnessHelper() );
+ const std::unique_ptr<SortedDataInterface> sorted( harnessHelper->newSortedDataInterface( false ) );
{
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
ASSERT( sorted->isEmpty( opCtx.get() ) );
}
{
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
{
WriteUnitOfWork uow( opCtx.get() );
ASSERT_OK( sorted->insert( opCtx.get(), key1, loc1, true ) );
@@ -358,63 +259,43 @@ namespace mongo {
}
{
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
ASSERT_EQUALS( 1, sorted->numEntries( opCtx.get() ) );
}
{
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
- scoped_ptr<SortedDataInterface::Cursor> cursor( sorted->newCursor( opCtx.get(), 1 ) );
-
- ASSERT( cursor->locate( key1, loc1 ) );
- ASSERT_EQUALS( key1, cursor->getKey() );
- ASSERT_EQUALS( loc1, cursor->getRecordId() );
-
- {
- vector<const BSONElement*> keyEnd( 1 );
- vector<bool> keyEndInclusive( 1 );
-
- // SERVER-15483 forward cursor positioned at last entry in index should move
- // to EOF when advanceTo() is called with that particular key
- // cursor->advanceTo( key1, 1, false, keyEnd, keyEndInclusive );
- // ASSERT( cursor->isEOF() );
- }
- }
-
- {
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
- scoped_ptr<SortedDataInterface::Cursor> cursor( sorted->newCursor( opCtx.get(), 1 ) );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<SortedDataInterface::Cursor> cursor( sorted->newCursor(opCtx.get()) );
- ASSERT( cursor->locate( key1, loc1 ) );
- ASSERT_EQUALS( key1, cursor->getKey() );
- ASSERT_EQUALS( loc1, cursor->getRecordId() );
+ ASSERT_EQ(cursor->seek(key1, true), IndexKeyEntry(key1, loc1));
- {
- vector<const BSONElement*> keyEnd( 1 );
- vector<bool> keyEndInclusive( 1 );
+ IndexSeekPoint seekPoint;
+ seekPoint.keyPrefix = key1;
+ seekPoint.prefixLen = 1;
+ seekPoint.prefixExclusive = false;
+ ASSERT_EQ(cursor->seek(seekPoint), IndexKeyEntry(key1, loc1));
- cursor->advanceTo( key1, 1, true, keyEnd, keyEndInclusive );
- ASSERT( cursor->isEOF() );
- }
+ seekPoint.prefixExclusive = true;
+ ASSERT_EQ(cursor->seek(seekPoint), boost::none);
}
}
// Insert a single-field key and advance to EOF using a reverse cursor
- // by specifying that exact key. When advanceTo() is called with the key
+ // by specifying that exact key. When seek() is called with the key
// where the cursor is positioned (and it is the first entry for that key),
- // the cursor should advance to EOF regardless of whether it is in non-
- // or inclusive mode.
+ // the cursor should remain at its current position. An exclusive seek will
+ // position the cursor on the next position, which may be EOF.
TEST( SortedDataInterface, AdvanceToKeyAtCursorPositionReversed ) {
- scoped_ptr<HarnessHelper> harnessHelper( newHarnessHelper() );
- scoped_ptr<SortedDataInterface> sorted( harnessHelper->newSortedDataInterface( false ) );
+ const std::unique_ptr<HarnessHelper> harnessHelper( newHarnessHelper() );
+ const std::unique_ptr<SortedDataInterface> sorted( harnessHelper->newSortedDataInterface( false ) );
{
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
ASSERT( sorted->isEmpty( opCtx.get() ) );
}
{
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
{
WriteUnitOfWork uow( opCtx.get() );
ASSERT_OK( sorted->insert( opCtx.get(), key1, loc1, true ) );
@@ -423,44 +304,24 @@ namespace mongo {
}
{
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
ASSERT_EQUALS( 1, sorted->numEntries( opCtx.get() ) );
}
{
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
- scoped_ptr<SortedDataInterface::Cursor> cursor( sorted->newCursor( opCtx.get(), -1 ) );
-
- ASSERT( cursor->locate( key1, loc1 ) );
- ASSERT_EQUALS( key1, cursor->getKey() );
- ASSERT_EQUALS( loc1, cursor->getRecordId() );
-
- {
- vector<const BSONElement*> keyEnd( 1 );
- vector<bool> keyEndInclusive( 1 );
-
- // SERVER-15483 reverse cursor positioned at first entry in index should move
- // to EOF when advanceTo() is called with that particular key
- // cursor->advanceTo( key1, 1, false, keyEnd, keyEndInclusive );
- // ASSERT( cursor->isEOF() );
- }
- }
-
- {
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
- scoped_ptr<SortedDataInterface::Cursor> cursor( sorted->newCursor( opCtx.get(), -1 ) );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<SortedDataInterface::Cursor> cursor( sorted->newCursor(opCtx.get(), false) );
- ASSERT( cursor->locate( key1, loc1 ) );
- ASSERT_EQUALS( key1, cursor->getKey() );
- ASSERT_EQUALS( loc1, cursor->getRecordId() );
+ ASSERT_EQ(cursor->seek(key1, true), IndexKeyEntry(key1, loc1));
- {
- vector<const BSONElement*> keyEnd( 1 );
- vector<bool> keyEndInclusive( 1 );
+ IndexSeekPoint seekPoint;
+ seekPoint.keyPrefix = key1;
+ seekPoint.prefixLen = 1;
+ seekPoint.prefixExclusive = false;
+ ASSERT_EQ(cursor->seek(seekPoint), IndexKeyEntry(key1, loc1));
- cursor->advanceTo( key1, 1, true, keyEnd, keyEndInclusive );
- ASSERT( cursor->isEOF() );
- }
+ seekPoint.prefixExclusive = true;
+ ASSERT_EQ(cursor->seek(seekPoint), boost::none);
}
}
@@ -469,16 +330,16 @@ namespace mongo {
// When advanceTo() is called in non-inclusive mode, the cursor is
// positioned at the key that comes after the one specified.
TEST( SortedDataInterface, AdvanceToExclusive ) {
- scoped_ptr<HarnessHelper> harnessHelper( newHarnessHelper() );
- scoped_ptr<SortedDataInterface> sorted( harnessHelper->newSortedDataInterface( false ) );
+ const std::unique_ptr<HarnessHelper> harnessHelper( newHarnessHelper() );
+ const std::unique_ptr<SortedDataInterface> sorted( harnessHelper->newSortedDataInterface( false ) );
{
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
ASSERT( sorted->isEmpty( opCtx.get() ) );
}
{
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
{
WriteUnitOfWork uow( opCtx.get() );
ASSERT_OK( sorted->insert( opCtx.get(), key1, loc1, true ) );
@@ -491,53 +352,30 @@ namespace mongo {
}
{
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
ASSERT_EQUALS( 5, sorted->numEntries( opCtx.get() ) );
}
{
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
- scoped_ptr<SortedDataInterface::Cursor> cursor( sorted->newCursor( opCtx.get(), 1 ) );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<SortedDataInterface::Cursor> cursor( sorted->newCursor(opCtx.get()) );
- ASSERT( cursor->locate( key1, loc1 ) );
- ASSERT_EQUALS( key1, cursor->getKey() );
- ASSERT_EQUALS( loc1, cursor->getRecordId() );
+ ASSERT_EQ(cursor->seek(key1, true), IndexKeyEntry(key1, loc1));
- {
- vector<const BSONElement*> keyEnd( 1 );
- vector<bool> keyEndInclusive( 1 );
+ IndexSeekPoint seekPoint;
+ seekPoint.keyPrefix = key1;
+ seekPoint.prefixLen = 1;
+ seekPoint.prefixExclusive = true;
+ ASSERT_EQ(cursor->seek(seekPoint), IndexKeyEntry(key2, loc4));
- cursor->advanceTo( key1, 1, true, keyEnd, keyEndInclusive );
- ASSERT_EQUALS( key2, cursor->getKey() );
- ASSERT_EQUALS( loc4, cursor->getRecordId() );
- }
+ seekPoint.keyPrefix = key2;
+ ASSERT_EQ(cursor->seek(seekPoint), IndexKeyEntry(key3, loc5));
- {
- vector<const BSONElement*> keyEnd( 1 );
- vector<bool> keyEndInclusive( 1 );
+ seekPoint.keyPrefix = key3;
+ ASSERT_EQ(cursor->seek(seekPoint), boost::none);
- cursor->advanceTo( key2, 1, true, keyEnd, keyEndInclusive );
- ASSERT_EQUALS( key3, cursor->getKey() );
- ASSERT_EQUALS( loc5, cursor->getRecordId() );
- }
-
- {
- vector<const BSONElement*> keyEnd( 1 );
- vector<bool> keyEndInclusive( 1 );
-
- cursor->advanceTo( key3, 1, true, keyEnd, keyEndInclusive );
- ASSERT( cursor->isEOF() );
- }
-
- {
- vector<const BSONElement*> keyEnd( 1 );
- vector<bool> keyEndInclusive( 1 );
-
- // SERVER-15449 forward cursor positioned at EOF should stay at EOF
- // when advanceTo() is called
- // cursor->advanceTo( key4, 1, true, keyEnd, keyEndInclusive );
- // ASSERT( cursor->isEOF() );
- }
+ seekPoint.keyPrefix = key4;
+ ASSERT_EQ(cursor->seek(seekPoint), boost::none);
}
}
@@ -546,16 +384,16 @@ namespace mongo {
// When advanceTo() is called in non-inclusive mode, the cursor is
// positioned at the key that comes before the one specified.
TEST( SortedDataInterface, AdvanceToExclusiveReversed ) {
- scoped_ptr<HarnessHelper> harnessHelper( newHarnessHelper() );
- scoped_ptr<SortedDataInterface> sorted( harnessHelper->newSortedDataInterface( false ) );
+ const std::unique_ptr<HarnessHelper> harnessHelper( newHarnessHelper() );
+ const std::unique_ptr<SortedDataInterface> sorted( harnessHelper->newSortedDataInterface( false ) );
{
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
ASSERT( sorted->isEmpty( opCtx.get() ) );
}
{
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
{
WriteUnitOfWork uow( opCtx.get() );
ASSERT_OK( sorted->insert( opCtx.get(), key1, loc1, true ) );
@@ -568,53 +406,30 @@ namespace mongo {
}
{
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
ASSERT_EQUALS( 5, sorted->numEntries( opCtx.get() ) );
}
{
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
- scoped_ptr<SortedDataInterface::Cursor> cursor( sorted->newCursor( opCtx.get(), -1 ) );
-
- ASSERT( cursor->locate( key3, loc5 ) );
- ASSERT_EQUALS( key3, cursor->getKey() );
- ASSERT_EQUALS( loc5, cursor->getRecordId() );
-
- {
- vector<const BSONElement*> keyEnd( 1 );
- vector<bool> keyEndInclusive( 1 );
-
- cursor->advanceTo( key3, 1, true, keyEnd, keyEndInclusive );
- ASSERT_EQUALS( key2, cursor->getKey() );
- ASSERT_EQUALS( loc2, cursor->getRecordId() );
- }
-
- {
- vector<const BSONElement*> keyEnd( 1 );
- vector<bool> keyEndInclusive( 1 );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<SortedDataInterface::Cursor> cursor( sorted->newCursor(opCtx.get(), false) );
- cursor->advanceTo( key2, 1, true, keyEnd, keyEndInclusive );
- ASSERT_EQUALS( key1, cursor->getKey() );
- ASSERT_EQUALS( loc1, cursor->getRecordId() );
- }
+ ASSERT_EQ(cursor->seek(key3, true), IndexKeyEntry(key3, loc5));
- {
- vector<const BSONElement*> keyEnd( 1 );
- vector<bool> keyEndInclusive( 1 );
+ IndexSeekPoint seekPoint;
+ seekPoint.keyPrefix = key3;
+ seekPoint.prefixLen = 1;
+ seekPoint.prefixExclusive = true;
+ ASSERT_EQ(cursor->seek(seekPoint), IndexKeyEntry(key2, loc2));
- cursor->advanceTo( key1, 1, true, keyEnd, keyEndInclusive );
- ASSERT( cursor->isEOF() );
- }
+ seekPoint.keyPrefix = key2;
+ ASSERT_EQ(cursor->seek(seekPoint), IndexKeyEntry(key1, loc1));
- {
- vector<const BSONElement*> keyEnd( 1 );
- vector<bool> keyEndInclusive( 1 );
+ seekPoint.keyPrefix = key1;
+ ASSERT_EQ(cursor->seek(seekPoint), boost::none);
- // SERVER-15449 reverse cursor positioned at EOF should stay at EOF
- // when advanceTo() is called
- // cursor->advanceTo( key0, 1, true, keyEnd, keyEndInclusive );
- // ASSERT( cursor->isEOF() );
- }
+ seekPoint.keyPrefix = key0;
+ ASSERT_EQ(cursor->seek(seekPoint), boost::none);
}
}
@@ -622,18 +437,18 @@ namespace mongo {
// each of them using a forward cursor by specifying a key between their
// exact key and the current position of the cursor.
TEST( SortedDataInterface, AdvanceToIndirect ) {
- scoped_ptr<HarnessHelper> harnessHelper( newHarnessHelper() );
- scoped_ptr<SortedDataInterface> sorted( harnessHelper->newSortedDataInterface( false ) );
+ const std::unique_ptr<HarnessHelper> harnessHelper( newHarnessHelper() );
+ const std::unique_ptr<SortedDataInterface> sorted( harnessHelper->newSortedDataInterface( false ) );
BSONObj unusedKey = key6; // larger than any inserted key
{
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
ASSERT( sorted->isEmpty( opCtx.get() ) );
}
{
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
{
WriteUnitOfWork uow( opCtx.get() );
ASSERT_OK( sorted->insert( opCtx.get(), key1, loc1, true ) );
@@ -644,43 +459,27 @@ namespace mongo {
}
{
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
ASSERT_EQUALS( 3, sorted->numEntries( opCtx.get() ) );
}
{
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
- scoped_ptr<SortedDataInterface::Cursor> cursor( sorted->newCursor( opCtx.get(), 1 ) );
-
- ASSERT( cursor->locate( key1, loc1 ) );
- ASSERT_EQUALS( key1, cursor->getKey() );
- ASSERT_EQUALS( loc1, cursor->getRecordId() );
-
- {
- vector<const BSONElement*> keyEnd( 1 );
- vector<bool> keyEndInclusive( 1 );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<SortedDataInterface::Cursor> cursor( sorted->newCursor(opCtx.get()) );
- const BSONElement end0 = key2.firstElement();
- keyEnd[0] = &end0;
- keyEndInclusive[0] = true;
+ ASSERT_EQ(cursor->seek(key1, true), IndexKeyEntry(key1, loc1));
- cursor->advanceTo( unusedKey, 0, false, keyEnd, keyEndInclusive );
- ASSERT_EQUALS( key3, cursor->getKey() );
- ASSERT_EQUALS( loc2, cursor->getRecordId() );
- }
-
- {
- vector<const BSONElement*> keyEnd( 1 );
- vector<bool> keyEndInclusive( 1 );
+ IndexSeekPoint seekPoint;
+ seekPoint.prefixLen = 0;
+ BSONElement suffix0;
+ seekPoint.keySuffix = {&suffix0};
+ seekPoint.suffixInclusive = {true};
- const BSONElement end0 = key4.firstElement();
- keyEnd[0] = &end0;
- keyEndInclusive[0] = true;
+ suffix0 = key2.firstElement();
+ ASSERT_EQ(cursor->seek(seekPoint), IndexKeyEntry(key3, loc2));
- cursor->advanceTo( unusedKey, 0, false, keyEnd, keyEndInclusive );
- ASSERT_EQUALS( key5, cursor->getKey() );
- ASSERT_EQUALS( loc3, cursor->getRecordId() );
- }
+ suffix0 = key4.firstElement();
+ ASSERT_EQ(cursor->seek(seekPoint), IndexKeyEntry(key5, loc3));
}
}
@@ -688,18 +487,18 @@ namespace mongo {
// each of them using a reverse cursor by specifying a key between their
// exact key and the current position of the cursor.
TEST( SortedDataInterface, AdvanceToIndirectReversed ) {
- scoped_ptr<HarnessHelper> harnessHelper( newHarnessHelper() );
- scoped_ptr<SortedDataInterface> sorted( harnessHelper->newSortedDataInterface( false ) );
+ const std::unique_ptr<HarnessHelper> harnessHelper( newHarnessHelper() );
+ const std::unique_ptr<SortedDataInterface> sorted( harnessHelper->newSortedDataInterface( false ) );
BSONObj unusedKey = key0; // smaller than any inserted key
{
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
ASSERT( sorted->isEmpty( opCtx.get() ) );
}
{
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
{
WriteUnitOfWork uow( opCtx.get() );
ASSERT_OK( sorted->insert( opCtx.get(), key1, loc1, true ) );
@@ -710,43 +509,27 @@ namespace mongo {
}
{
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
ASSERT_EQUALS( 3, sorted->numEntries( opCtx.get() ) );
}
{
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
- scoped_ptr<SortedDataInterface::Cursor> cursor( sorted->newCursor( opCtx.get(), -1 ) );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<SortedDataInterface::Cursor> cursor( sorted->newCursor(opCtx.get(), false) );
- ASSERT( cursor->locate( key5, loc3 ) );
- ASSERT_EQUALS( key5, cursor->getKey() );
- ASSERT_EQUALS( loc3, cursor->getRecordId() );
+ ASSERT_EQ(cursor->seek(key5, true), IndexKeyEntry(key5, loc3));
- {
- vector<const BSONElement*> keyEnd( 1 );
- vector<bool> keyEndInclusive( 1 );
-
- const BSONElement end0 = key4.firstElement();
- keyEnd[0] = &end0;
- keyEndInclusive[0] = true;
+ IndexSeekPoint seekPoint;
+ seekPoint.prefixLen = 0;
+ BSONElement suffix0;
+ seekPoint.keySuffix = {&suffix0};
+ seekPoint.suffixInclusive = {true};
- cursor->advanceTo( unusedKey, 0, false, keyEnd, keyEndInclusive );
- ASSERT_EQUALS( key3, cursor->getKey() );
- ASSERT_EQUALS( loc2, cursor->getRecordId() );
- }
+ suffix0 = key4.firstElement();
+ ASSERT_EQ(cursor->seek(seekPoint), IndexKeyEntry(key3, loc2));
- {
- vector<const BSONElement*> keyEnd( 1 );
- vector<bool> keyEndInclusive( 1 );
-
- const BSONElement end0 = key2.firstElement();
- keyEnd[0] = &end0;
- keyEndInclusive[0] = true;
-
- cursor->advanceTo( unusedKey, 0, false, keyEnd, keyEndInclusive );
- ASSERT_EQUALS( key1, cursor->getKey() );
- ASSERT_EQUALS( loc1, cursor->getRecordId() );
- }
+ suffix0 = key2.firstElement();
+ ASSERT_EQ(cursor->seek(seekPoint), IndexKeyEntry(key1, loc1));
}
}
@@ -756,18 +539,18 @@ namespace mongo {
// is called in non-inclusive mode, the cursor is positioned at the key
// that comes after the one specified.
TEST( SortedDataInterface, AdvanceToIndirectExclusive ) {
- scoped_ptr<HarnessHelper> harnessHelper( newHarnessHelper() );
- scoped_ptr<SortedDataInterface> sorted( harnessHelper->newSortedDataInterface( false ) );
+ const std::unique_ptr<HarnessHelper> harnessHelper( newHarnessHelper() );
+ const std::unique_ptr<SortedDataInterface> sorted( harnessHelper->newSortedDataInterface( false ) );
BSONObj unusedKey = key6; // larger than any inserted key
{
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
ASSERT( sorted->isEmpty( opCtx.get() ) );
}
{
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
{
WriteUnitOfWork uow( opCtx.get() );
ASSERT_OK( sorted->insert( opCtx.get(), key1, loc1, true ) );
@@ -778,68 +561,32 @@ namespace mongo {
}
{
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
ASSERT_EQUALS( 3, sorted->numEntries( opCtx.get() ) );
}
{
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
- scoped_ptr<SortedDataInterface::Cursor> cursor( sorted->newCursor( opCtx.get(), 1 ) );
-
- ASSERT( cursor->locate( key1, loc1 ) );
- ASSERT_EQUALS( key1, cursor->getKey() );
- ASSERT_EQUALS( loc1, cursor->getRecordId() );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<SortedDataInterface::Cursor> cursor( sorted->newCursor(opCtx.get()) );
- {
- vector<const BSONElement*> keyEnd( 1 );
- vector<bool> keyEndInclusive( 1 );
+ ASSERT_EQ(cursor->seek(key1, true), IndexKeyEntry(key1, loc1));
- const BSONElement end0 = key2.firstElement();
- keyEnd[0] = &end0;
- keyEndInclusive[0] = false;
+ IndexSeekPoint seekPoint;
+ seekPoint.prefixLen = 0;
+ BSONElement suffix0;
+ seekPoint.keySuffix = {&suffix0};
+ seekPoint.suffixInclusive = {false};
- cursor->advanceTo( unusedKey, 0, false, keyEnd, keyEndInclusive );
- ASSERT( !cursor->isEOF() );
- ASSERT_EQUALS( key3, cursor->getKey() );
- ASSERT_EQUALS( loc2, cursor->getRecordId() );
- }
+ suffix0 = key2.firstElement();
+ ASSERT_EQ(cursor->seek(seekPoint), IndexKeyEntry(key3, loc2));
- {
- vector<const BSONElement*> keyEnd( 1 );
- vector<bool> keyEndInclusive( 1 );
+ suffix0 = key4.firstElement();
+ ASSERT_EQ(cursor->seek(seekPoint), IndexKeyEntry(key5, loc3));
- const BSONElement end0 = key4.firstElement();
- keyEnd[0] = &end0;
- keyEndInclusive[0] = false;
+ ASSERT_EQ(cursor->seek(key1, true), IndexKeyEntry(key1, loc1));
- cursor->advanceTo( unusedKey, 0, false, keyEnd, keyEndInclusive );
- ASSERT( !cursor->isEOF() );
- ASSERT_EQUALS( key5, cursor->getKey() );
- ASSERT_EQUALS( loc3, cursor->getRecordId() );
- }
- }
-
- {
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
- scoped_ptr<SortedDataInterface::Cursor> cursor( sorted->newCursor( opCtx.get(), 1 ) );
-
- ASSERT( cursor->locate( key1, loc1 ) );
- ASSERT_EQUALS( key1, cursor->getKey() );
- ASSERT_EQUALS( loc1, cursor->getRecordId() );
-
- {
- vector<const BSONElement*> keyEnd( 1 );
- vector<bool> keyEndInclusive( 1 );
-
- const BSONElement end0 = key3.firstElement();
- keyEnd[0] = &end0;
- keyEndInclusive[0] = false;
-
- cursor->advanceTo( unusedKey, 0, false, keyEnd, keyEndInclusive );
- ASSERT( !cursor->isEOF() );
- ASSERT_EQUALS( key5, cursor->getKey() );
- ASSERT_EQUALS( loc3, cursor->getRecordId() );
- }
+ suffix0 = key3.firstElement();
+ ASSERT_EQ(cursor->seek(seekPoint), IndexKeyEntry(key5, loc3));
}
}
@@ -849,18 +596,18 @@ namespace mongo {
// is called in non-inclusive mode, the cursor is positioned at the key
// that comes before the one specified.
TEST( SortedDataInterface, AdvanceToIndirectExclusiveReversed ) {
- scoped_ptr<HarnessHelper> harnessHelper( newHarnessHelper() );
- scoped_ptr<SortedDataInterface> sorted( harnessHelper->newSortedDataInterface( false ) );
+ const std::unique_ptr<HarnessHelper> harnessHelper( newHarnessHelper() );
+ const std::unique_ptr<SortedDataInterface> sorted( harnessHelper->newSortedDataInterface( false ) );
BSONObj unusedKey = key0; // smaller than any inserted key
{
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
ASSERT( sorted->isEmpty( opCtx.get() ) );
}
{
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
{
WriteUnitOfWork uow( opCtx.get() );
ASSERT_OK( sorted->insert( opCtx.get(), key1, loc1, true ) );
@@ -871,68 +618,32 @@ namespace mongo {
}
{
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
ASSERT_EQUALS( 3, sorted->numEntries( opCtx.get() ) );
}
{
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
- scoped_ptr<SortedDataInterface::Cursor> cursor( sorted->newCursor( opCtx.get(), -1 ) );
-
- ASSERT( cursor->locate( key5, loc3 ) );
- ASSERT_EQUALS( key5, cursor->getKey() );
- ASSERT_EQUALS( loc3, cursor->getRecordId() );
-
- {
- vector<const BSONElement*> keyEnd( 1 );
- vector<bool> keyEndInclusive( 1 );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<SortedDataInterface::Cursor> cursor( sorted->newCursor(opCtx.get(), false) );
- const BSONElement end0 = key4.firstElement();
- keyEnd[0] = &end0;
- keyEndInclusive[0] = false;
+ ASSERT_EQ(cursor->seek(key5, true), IndexKeyEntry(key5, loc3));
- cursor->advanceTo( unusedKey, 0, false, keyEnd, keyEndInclusive );
- ASSERT( !cursor->isEOF() );
- ASSERT_EQUALS( key3, cursor->getKey() );
- ASSERT_EQUALS( loc2, cursor->getRecordId() );
- }
+ IndexSeekPoint seekPoint;
+ seekPoint.prefixLen = 0;
+ BSONElement suffix0;
+ seekPoint.keySuffix = {&suffix0};
+ seekPoint.suffixInclusive = {false};
- {
- vector<const BSONElement*> keyEnd( 1 );
- vector<bool> keyEndInclusive( 1 );
+ suffix0 = key4.firstElement();
+ ASSERT_EQ(cursor->seek(seekPoint), IndexKeyEntry(key3, loc2));
- const BSONElement end0 = key2.firstElement();
- keyEnd[0] = &end0;
- keyEndInclusive[0] = false;
+ suffix0 = key2.firstElement();
+ ASSERT_EQ(cursor->seek(seekPoint), IndexKeyEntry(key1, loc1));
- cursor->advanceTo( unusedKey, 0, false, keyEnd, keyEndInclusive );
- ASSERT( !cursor->isEOF() );
- ASSERT_EQUALS( key1, cursor->getKey() );
- ASSERT_EQUALS( loc1, cursor->getRecordId() );
- }
- }
+ ASSERT_EQ(cursor->seek(key5, true), IndexKeyEntry(key5, loc3));
- {
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
- scoped_ptr<SortedDataInterface::Cursor> cursor( sorted->newCursor( opCtx.get(), -1 ) );
-
- ASSERT( cursor->locate( key5, loc3 ) );
- ASSERT_EQUALS( key5, cursor->getKey() );
- ASSERT_EQUALS( loc3, cursor->getRecordId() );
-
- {
- vector<const BSONElement*> keyEnd( 1 );
- vector<bool> keyEndInclusive( 1 );
-
- const BSONElement end0 = key3.firstElement();
- keyEnd[0] = &end0;
- keyEndInclusive[0] = false;
-
- cursor->advanceTo( unusedKey, 0, false, keyEnd, keyEndInclusive );
- ASSERT( !cursor->isEOF() );
- ASSERT_EQUALS( key1, cursor->getKey() );
- ASSERT_EQUALS( loc1, cursor->getRecordId() );
- }
+ suffix0 = key3.firstElement();
+ ASSERT_EQ(cursor->seek(seekPoint), IndexKeyEntry(key1, loc1));
}
}
diff --git a/src/mongo/db/storage/sorted_data_interface_test_cursor_locate.cpp b/src/mongo/db/storage/sorted_data_interface_test_cursor_locate.cpp
index 3d0a95e99a6..b69a39a15fd 100644
--- a/src/mongo/db/storage/sorted_data_interface_test_cursor_locate.cpp
+++ b/src/mongo/db/storage/sorted_data_interface_test_cursor_locate.cpp
@@ -30,29 +30,27 @@
#include "mongo/db/storage/sorted_data_interface_test_harness.h"
-#include <boost/scoped_ptr.hpp>
+#include <memory>
#include "mongo/db/storage/sorted_data_interface.h"
#include "mongo/unittest/unittest.h"
namespace mongo {
- using boost::scoped_ptr;
-
// Insert a key and try to locate it using a forward cursor
// by specifying its exact key and RecordId.
TEST( SortedDataInterface, Locate ) {
- scoped_ptr<HarnessHelper> harnessHelper( newHarnessHelper() );
- scoped_ptr<SortedDataInterface> sorted( harnessHelper->newSortedDataInterface( false ) );
+ const std::unique_ptr<HarnessHelper> harnessHelper( newHarnessHelper() );
+ const std::unique_ptr<SortedDataInterface> sorted( harnessHelper->newSortedDataInterface( false ) );
{
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
- scoped_ptr<SortedDataInterface::Cursor> cursor( sorted->newCursor( opCtx.get(), 1 ) );
- ASSERT( !cursor->locate( key1, loc1 ) );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<SortedDataInterface::Cursor> cursor( sorted->newCursor(opCtx.get()) );
+ ASSERT( !cursor->seek( key1, true ) );
}
{
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
{
WriteUnitOfWork uow( opCtx.get() );
ASSERT_OK( sorted->insert( opCtx.get(), key1, loc1, true ) );
@@ -61,32 +59,28 @@ namespace mongo {
}
{
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
- scoped_ptr<SortedDataInterface::Cursor> cursor( sorted->newCursor( opCtx.get(), 1 ) );
-
- ASSERT( cursor->locate( key1, loc1 ) );
- ASSERT_EQUALS( key1, cursor->getKey() );
- ASSERT_EQUALS( loc1, cursor->getRecordId() );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<SortedDataInterface::Cursor> cursor( sorted->newCursor(opCtx.get()) );
- cursor->advance();
- ASSERT( cursor->isEOF() );
+ ASSERT_EQ(cursor->seek(key1, true), IndexKeyEntry(key1, loc1));
+ ASSERT_EQ(cursor->next(), boost::none);
}
}
// Insert a key and try to locate it using a reverse cursor
// by specifying its exact key and RecordId.
TEST( SortedDataInterface, LocateReversed ) {
- scoped_ptr<HarnessHelper> harnessHelper( newHarnessHelper() );
- scoped_ptr<SortedDataInterface> sorted( harnessHelper->newSortedDataInterface( false ) );
+ const std::unique_ptr<HarnessHelper> harnessHelper( newHarnessHelper() );
+ const std::unique_ptr<SortedDataInterface> sorted( harnessHelper->newSortedDataInterface( false ) );
{
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
- scoped_ptr<SortedDataInterface::Cursor> cursor( sorted->newCursor( opCtx.get(), -1 ) );
- ASSERT( !cursor->locate( key1, loc1 ) );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<SortedDataInterface::Cursor> cursor( sorted->newCursor(opCtx.get(), false) );
+ ASSERT( !cursor->seek( key1, true ) );
}
{
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
{
WriteUnitOfWork uow( opCtx.get() );
ASSERT_OK( sorted->insert( opCtx.get(), key1, loc1, true ) );
@@ -95,32 +89,28 @@ namespace mongo {
}
{
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
- scoped_ptr<SortedDataInterface::Cursor> cursor( sorted->newCursor( opCtx.get(), -1 ) );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<SortedDataInterface::Cursor> cursor( sorted->newCursor(opCtx.get(), false) );
- ASSERT( cursor->locate( key1, loc1 ) );
- ASSERT_EQUALS( key1, cursor->getKey() );
- ASSERT_EQUALS( loc1, cursor->getRecordId() );
-
- cursor->advance();
- ASSERT( cursor->isEOF() );
+ ASSERT_EQ(cursor->seek(key1, true), IndexKeyEntry(key1, loc1));
+ ASSERT_EQ(cursor->next(), boost::none);
}
}
// Insert a compound key and try to locate it using a forward cursor
// by specifying its exact key and RecordId.
TEST( SortedDataInterface, LocateCompoundKey ) {
- scoped_ptr<HarnessHelper> harnessHelper( newHarnessHelper() );
- scoped_ptr<SortedDataInterface> sorted( harnessHelper->newSortedDataInterface( false ) );
+ const std::unique_ptr<HarnessHelper> harnessHelper( newHarnessHelper() );
+ const std::unique_ptr<SortedDataInterface> sorted( harnessHelper->newSortedDataInterface( false ) );
{
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
- scoped_ptr<SortedDataInterface::Cursor> cursor( sorted->newCursor( opCtx.get(), 1 ) );
- ASSERT( !cursor->locate( compoundKey1a, loc1 ) );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<SortedDataInterface::Cursor> cursor( sorted->newCursor(opCtx.get()) );
+ ASSERT( !cursor->seek( compoundKey1a, true ) );
}
{
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
{
WriteUnitOfWork uow( opCtx.get() );
ASSERT_OK( sorted->insert( opCtx.get(), compoundKey1a, loc1, true ) );
@@ -129,32 +119,28 @@ namespace mongo {
}
{
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
- scoped_ptr<SortedDataInterface::Cursor> cursor( sorted->newCursor( opCtx.get(), 1 ) );
-
- ASSERT( cursor->locate( compoundKey1a, loc1 ) );
- ASSERT_EQUALS( compoundKey1a, cursor->getKey() );
- ASSERT_EQUALS( loc1, cursor->getRecordId() );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<SortedDataInterface::Cursor> cursor( sorted->newCursor(opCtx.get()) );
- cursor->advance();
- ASSERT( cursor->isEOF() );
+ ASSERT_EQ(cursor->seek(compoundKey1a, true), IndexKeyEntry(compoundKey1a, loc1));
+ ASSERT_EQ(cursor->next(), boost::none);
}
}
// Insert a compound key and try to locate it using a reverse cursor
// by specifying its exact key and RecordId.
TEST( SortedDataInterface, LocateCompoundKeyReversed ) {
- scoped_ptr<HarnessHelper> harnessHelper( newHarnessHelper() );
- scoped_ptr<SortedDataInterface> sorted( harnessHelper->newSortedDataInterface( false ) );
+ const std::unique_ptr<HarnessHelper> harnessHelper( newHarnessHelper() );
+ const std::unique_ptr<SortedDataInterface> sorted( harnessHelper->newSortedDataInterface( false ) );
{
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
- scoped_ptr<SortedDataInterface::Cursor> cursor( sorted->newCursor( opCtx.get(), -1 ) );
- ASSERT( !cursor->locate( compoundKey1a, loc1 ) );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<SortedDataInterface::Cursor> cursor( sorted->newCursor(opCtx.get(), false) );
+ ASSERT( !cursor->seek( compoundKey1a, true ) );
}
{
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
{
WriteUnitOfWork uow( opCtx.get() );
ASSERT_OK( sorted->insert( opCtx.get(), compoundKey1a, loc1, true ) );
@@ -163,32 +149,28 @@ namespace mongo {
}
{
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
- scoped_ptr<SortedDataInterface::Cursor> cursor( sorted->newCursor( opCtx.get(), -1 ) );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<SortedDataInterface::Cursor> cursor( sorted->newCursor(opCtx.get(), false) );
- ASSERT( cursor->locate( compoundKey1a, loc1 ) );
- ASSERT_EQUALS( compoundKey1a, cursor->getKey() );
- ASSERT_EQUALS( loc1, cursor->getRecordId() );
-
- cursor->advance();
- ASSERT( cursor->isEOF() );
+ ASSERT_EQ(cursor->seek(compoundKey1a, true), IndexKeyEntry(compoundKey1a, loc1));
+ ASSERT_EQ(cursor->next(), boost::none);
}
}
// Insert multiple keys and try to locate them using a forward cursor
// by specifying their exact key and RecordId.
TEST( SortedDataInterface, LocateMultiple ) {
- scoped_ptr<HarnessHelper> harnessHelper( newHarnessHelper() );
- scoped_ptr<SortedDataInterface> sorted( harnessHelper->newSortedDataInterface( false ) );
+ const std::unique_ptr<HarnessHelper> harnessHelper( newHarnessHelper() );
+ const std::unique_ptr<SortedDataInterface> sorted( harnessHelper->newSortedDataInterface( false ) );
{
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
- scoped_ptr<SortedDataInterface::Cursor> cursor( sorted->newCursor( opCtx.get(), 1 ) );
- ASSERT( !cursor->locate( key1, loc1 ) );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<SortedDataInterface::Cursor> cursor( sorted->newCursor(opCtx.get()) );
+ ASSERT( !cursor->seek( key1, true ) );
}
{
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
{
WriteUnitOfWork uow( opCtx.get() );
ASSERT_OK( sorted->insert( opCtx.get(), key1, loc1, true ) );
@@ -198,23 +180,16 @@ namespace mongo {
}
{
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
- scoped_ptr<SortedDataInterface::Cursor> cursor( sorted->newCursor( opCtx.get(), 1 ) );
-
- ASSERT( cursor->locate( key1, loc1 ) );
- ASSERT_EQUALS( key1, cursor->getKey() );
- ASSERT_EQUALS( loc1, cursor->getRecordId() );
-
- cursor->advance();
- ASSERT_EQUALS( key2, cursor->getKey() );
- ASSERT_EQUALS( loc2, cursor->getRecordId() );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<SortedDataInterface::Cursor> cursor( sorted->newCursor(opCtx.get()) );
- cursor->advance();
- ASSERT( cursor->isEOF() );
+ ASSERT_EQ(cursor->seek(key1, true), IndexKeyEntry(key1, loc1));
+ ASSERT_EQ(cursor->next(), IndexKeyEntry(key2, loc2));
+ ASSERT_EQ(cursor->next(), boost::none);
}
{
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
{
WriteUnitOfWork uow( opCtx.get() );
ASSERT_OK( sorted->insert( opCtx.get(), key3, loc3, true ) );
@@ -223,51 +198,34 @@ namespace mongo {
}
{
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
- scoped_ptr<SortedDataInterface::Cursor> cursor( sorted->newCursor( opCtx.get(), 1 ) );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<SortedDataInterface::Cursor> cursor( sorted->newCursor(opCtx.get()) );
- ASSERT( cursor->locate( key2, loc2 ) );
- ASSERT_EQUALS( key2, cursor->getKey() );
- ASSERT_EQUALS( loc2, cursor->getRecordId() );
+ ASSERT_EQ(cursor->seek(key2, true), IndexKeyEntry(key2, loc2));
+ ASSERT_EQ(cursor->next(), IndexKeyEntry(key3, loc3));
+ ASSERT_EQ(cursor->next(), boost::none);
- cursor->advance();
- ASSERT_EQUALS( key3, cursor->getKey() );
- ASSERT_EQUALS( loc3, cursor->getRecordId() );
-
- cursor->advance();
- ASSERT( cursor->isEOF() );
-
- ASSERT( cursor->locate( key1, loc1 ) );
- ASSERT_EQUALS( key1, cursor->getKey() );
- ASSERT_EQUALS( loc1, cursor->getRecordId() );
-
- cursor->advance();
- ASSERT_EQUALS( key2, cursor->getKey() );
- ASSERT_EQUALS( loc2, cursor->getRecordId() );
-
- cursor->advance();
- ASSERT_EQUALS( key3, cursor->getKey() );
- ASSERT_EQUALS( loc3, cursor->getRecordId() );
-
- cursor->advance();
- ASSERT( cursor->isEOF() );
+ ASSERT_EQ(cursor->seek(key1, true), IndexKeyEntry(key1, loc1));
+ ASSERT_EQ(cursor->next(), IndexKeyEntry(key2, loc2));
+ ASSERT_EQ(cursor->next(), IndexKeyEntry(key3, loc3));
+ ASSERT_EQ(cursor->next(), boost::none);
}
}
// Insert multiple keys and try to locate them using a reverse cursor
// by specifying their exact key and RecordId.
TEST( SortedDataInterface, LocateMultipleReversed ) {
- scoped_ptr<HarnessHelper> harnessHelper( newHarnessHelper() );
- scoped_ptr<SortedDataInterface> sorted( harnessHelper->newSortedDataInterface( false ) );
+ const std::unique_ptr<HarnessHelper> harnessHelper( newHarnessHelper() );
+ const std::unique_ptr<SortedDataInterface> sorted( harnessHelper->newSortedDataInterface( false ) );
{
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
- scoped_ptr<SortedDataInterface::Cursor> cursor( sorted->newCursor( opCtx.get(), -1 ) );
- ASSERT( !cursor->locate( key3, loc1 ) );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<SortedDataInterface::Cursor> cursor( sorted->newCursor(opCtx.get(), false) );
+ ASSERT( !cursor->seek( key3, true ) );
}
{
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
{
WriteUnitOfWork uow( opCtx.get() );
ASSERT_OK( sorted->insert( opCtx.get(), key1, loc1, true ) );
@@ -277,23 +235,16 @@ namespace mongo {
}
{
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
- scoped_ptr<SortedDataInterface::Cursor> cursor( sorted->newCursor( opCtx.get(), -1 ) );
-
- ASSERT( cursor->locate( key2, loc2 ) );
- ASSERT_EQUALS( key2, cursor->getKey() );
- ASSERT_EQUALS( loc2, cursor->getRecordId() );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<SortedDataInterface::Cursor> cursor( sorted->newCursor(opCtx.get(), false) );
- cursor->advance();
- ASSERT_EQUALS( key1, cursor->getKey() );
- ASSERT_EQUALS( loc1, cursor->getRecordId() );
-
- cursor->advance();
- ASSERT( cursor->isEOF() );
+ ASSERT_EQ(cursor->seek(key2, true), IndexKeyEntry(key2, loc2));
+ ASSERT_EQ(cursor->next(), IndexKeyEntry(key1, loc1));
+ ASSERT_EQ(cursor->next(), boost::none);
}
{
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
{
WriteUnitOfWork uow( opCtx.get() );
ASSERT_OK( sorted->insert( opCtx.get(), key3, loc3, true ) );
@@ -302,51 +253,34 @@ namespace mongo {
}
{
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
- scoped_ptr<SortedDataInterface::Cursor> cursor( sorted->newCursor( opCtx.get(), -1 ) );
-
- ASSERT( cursor->locate( key2, loc2 ) );
- ASSERT_EQUALS( key2, cursor->getKey() );
- ASSERT_EQUALS( loc2, cursor->getRecordId() );
-
- cursor->advance();
- ASSERT_EQUALS( key1, cursor->getKey() );
- ASSERT_EQUALS( loc1, cursor->getRecordId() );
-
- cursor->advance();
- ASSERT( cursor->isEOF() );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<SortedDataInterface::Cursor> cursor( sorted->newCursor(opCtx.get(), false) );
- ASSERT( cursor->locate( key3, loc3 ) );
- ASSERT_EQUALS( key3, cursor->getKey() );
- ASSERT_EQUALS( loc3, cursor->getRecordId() );
+ ASSERT_EQ(cursor->seek(key2, true), IndexKeyEntry(key2, loc2));
+ ASSERT_EQ(cursor->next(), IndexKeyEntry(key1, loc1));
+ ASSERT_EQ(cursor->next(), boost::none);
- cursor->advance();
- ASSERT_EQUALS( key2, cursor->getKey() );
- ASSERT_EQUALS( loc2, cursor->getRecordId() );
-
- cursor->advance();
- ASSERT_EQUALS( key1, cursor->getKey() );
- ASSERT_EQUALS( loc1, cursor->getRecordId() );
-
- cursor->advance();
- ASSERT( cursor->isEOF() );
+ ASSERT_EQ(cursor->seek(key3, true), IndexKeyEntry(key3, loc3));
+ ASSERT_EQ(cursor->next(), IndexKeyEntry(key2, loc2));
+ ASSERT_EQ(cursor->next(), IndexKeyEntry(key1, loc1));
+ ASSERT_EQ(cursor->next(), boost::none);
}
}
// Insert multiple compound keys and try to locate them using a forward cursor
// by specifying their exact key and RecordId.
TEST( SortedDataInterface, LocateMultipleCompoundKeys ) {
- scoped_ptr<HarnessHelper> harnessHelper( newHarnessHelper() );
- scoped_ptr<SortedDataInterface> sorted( harnessHelper->newSortedDataInterface( false ) );
+ const std::unique_ptr<HarnessHelper> harnessHelper( newHarnessHelper() );
+ const std::unique_ptr<SortedDataInterface> sorted( harnessHelper->newSortedDataInterface( false ) );
{
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
- scoped_ptr<SortedDataInterface::Cursor> cursor( sorted->newCursor( opCtx.get(), 1 ) );
- ASSERT( !cursor->locate( compoundKey1a, loc1 ) );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<SortedDataInterface::Cursor> cursor( sorted->newCursor(opCtx.get()) );
+ ASSERT( !cursor->seek( compoundKey1a, true ) );
}
{
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
{
WriteUnitOfWork uow( opCtx.get() );
ASSERT_OK( sorted->insert( opCtx.get(), compoundKey1a, loc1, true ) );
@@ -357,27 +291,17 @@ namespace mongo {
}
{
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
- scoped_ptr<SortedDataInterface::Cursor> cursor( sorted->newCursor( opCtx.get(), 1 ) );
-
- ASSERT( cursor->locate( compoundKey1a, loc1 ) );
- ASSERT_EQUALS( compoundKey1a, cursor->getKey() );
- ASSERT_EQUALS( loc1, cursor->getRecordId() );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<SortedDataInterface::Cursor> cursor( sorted->newCursor(opCtx.get()) );
- cursor->advance();
- ASSERT_EQUALS( compoundKey1b, cursor->getKey() );
- ASSERT_EQUALS( loc2, cursor->getRecordId() );
-
- cursor->advance();
- ASSERT_EQUALS( compoundKey2b, cursor->getKey() );
- ASSERT_EQUALS( loc3, cursor->getRecordId() );
-
- cursor->advance();
- ASSERT( cursor->isEOF() );
+ ASSERT_EQ(cursor->seek(compoundKey1a, true), IndexKeyEntry(compoundKey1a, loc1));
+ ASSERT_EQ(cursor->next(), IndexKeyEntry(compoundKey1b, loc2));
+ ASSERT_EQ(cursor->next(), IndexKeyEntry(compoundKey2b, loc3));
+ ASSERT_EQ(cursor->next(), boost::none);
}
{
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
{
WriteUnitOfWork uow( opCtx.get() );
ASSERT_OK( sorted->insert( opCtx.get(), compoundKey1c, loc4, true ) );
@@ -387,48 +311,32 @@ namespace mongo {
}
{
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
- scoped_ptr<SortedDataInterface::Cursor> cursor( sorted->newCursor( opCtx.get(), 1 ) );
-
- ASSERT( cursor->locate( compoundKey1a, loc1 ) );
- ASSERT_EQUALS( compoundKey1a, cursor->getKey() );
- ASSERT_EQUALS( loc1, cursor->getRecordId() );
-
- cursor->advance();
- ASSERT_EQUALS( compoundKey1b, cursor->getKey() );
- ASSERT_EQUALS( loc2, cursor->getRecordId() );
-
- cursor->advance();
- ASSERT_EQUALS( compoundKey1c, cursor->getKey() );
- ASSERT_EQUALS( loc4, cursor->getRecordId() );
-
- cursor->advance();
- ASSERT_EQUALS( compoundKey2b, cursor->getKey() );
- ASSERT_EQUALS( loc3, cursor->getRecordId() );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<SortedDataInterface::Cursor> cursor( sorted->newCursor(opCtx.get()) );
- cursor->advance();
- ASSERT_EQUALS( compoundKey3a, cursor->getKey() );
- ASSERT_EQUALS( loc5, cursor->getRecordId() );
-
- cursor->advance();
- ASSERT( cursor->isEOF() );
+ ASSERT_EQ(cursor->seek(compoundKey1a, true), IndexKeyEntry(compoundKey1a, loc1));
+ ASSERT_EQ(cursor->next(), IndexKeyEntry(compoundKey1b, loc2));
+ ASSERT_EQ(cursor->next(), IndexKeyEntry(compoundKey1c, loc4));
+ ASSERT_EQ(cursor->next(), IndexKeyEntry(compoundKey2b, loc3));
+ ASSERT_EQ(cursor->next(), IndexKeyEntry(compoundKey3a, loc5));
+ ASSERT_EQ(cursor->next(), boost::none);
}
}
// Insert multiple compound keys and try to locate them using a reverse cursor
// by specifying their exact key and RecordId.
TEST( SortedDataInterface, LocateMultipleCompoundKeysReversed ) {
- scoped_ptr<HarnessHelper> harnessHelper( newHarnessHelper() );
- scoped_ptr<SortedDataInterface> sorted( harnessHelper->newSortedDataInterface( false ) );
+ const std::unique_ptr<HarnessHelper> harnessHelper( newHarnessHelper() );
+ const std::unique_ptr<SortedDataInterface> sorted( harnessHelper->newSortedDataInterface( false ) );
{
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
- scoped_ptr<SortedDataInterface::Cursor> cursor( sorted->newCursor( opCtx.get(), -1 ) );
- ASSERT( !cursor->locate( compoundKey3a, loc1 ) );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<SortedDataInterface::Cursor> cursor( sorted->newCursor(opCtx.get(), false) );
+ ASSERT( !cursor->seek( compoundKey3a, true ) );
}
{
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
{
WriteUnitOfWork uow( opCtx.get() );
ASSERT_OK( sorted->insert( opCtx.get(), compoundKey1a, loc1, true ) );
@@ -439,27 +347,17 @@ namespace mongo {
}
{
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
- scoped_ptr<SortedDataInterface::Cursor> cursor( sorted->newCursor( opCtx.get(), -1 ) );
-
- ASSERT( cursor->locate( compoundKey2b, loc3 ) );
- ASSERT_EQUALS( compoundKey2b, cursor->getKey() );
- ASSERT_EQUALS( loc3, cursor->getRecordId() );
-
- cursor->advance();
- ASSERT_EQUALS( compoundKey1b, cursor->getKey() );
- ASSERT_EQUALS( loc2, cursor->getRecordId() );
-
- cursor->advance();
- ASSERT_EQUALS( compoundKey1a, cursor->getKey() );
- ASSERT_EQUALS( loc1, cursor->getRecordId() );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<SortedDataInterface::Cursor> cursor( sorted->newCursor(opCtx.get(), false) );
- cursor->advance();
- ASSERT( cursor->isEOF() );
+ ASSERT_EQ(cursor->seek(compoundKey2b, true), IndexKeyEntry(compoundKey2b, loc3));
+ ASSERT_EQ(cursor->next(), IndexKeyEntry(compoundKey1b, loc2));
+ ASSERT_EQ(cursor->next(), IndexKeyEntry(compoundKey1a, loc1));
+ ASSERT_EQ(cursor->next(), boost::none);
}
{
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
{
WriteUnitOfWork uow( opCtx.get() );
ASSERT_OK( sorted->insert( opCtx.get(), compoundKey1c, loc4, true ) );
@@ -469,48 +367,32 @@ namespace mongo {
}
{
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
- scoped_ptr<SortedDataInterface::Cursor> cursor( sorted->newCursor( opCtx.get(), -1 ) );
-
- ASSERT( cursor->locate( compoundKey3a, loc5 ) );
- ASSERT_EQUALS( compoundKey3a, cursor->getKey() );
- ASSERT_EQUALS( loc5, cursor->getRecordId() );
-
- cursor->advance();
- ASSERT_EQUALS( compoundKey2b, cursor->getKey() );
- ASSERT_EQUALS( loc3, cursor->getRecordId() );
-
- cursor->advance();
- ASSERT_EQUALS( compoundKey1c, cursor->getKey() );
- ASSERT_EQUALS( loc4, cursor->getRecordId() );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<SortedDataInterface::Cursor> cursor( sorted->newCursor(opCtx.get(), false) );
- cursor->advance();
- ASSERT_EQUALS( compoundKey1b, cursor->getKey() );
- ASSERT_EQUALS( loc2, cursor->getRecordId() );
-
- cursor->advance();
- ASSERT_EQUALS( compoundKey1a, cursor->getKey() );
- ASSERT_EQUALS( loc1, cursor->getRecordId() );
-
- cursor->advance();
- ASSERT( cursor->isEOF() );
+ ASSERT_EQ(cursor->seek(compoundKey3a, true), IndexKeyEntry(compoundKey3a, loc5));
+ ASSERT_EQ(cursor->next(), IndexKeyEntry(compoundKey2b, loc3));
+ ASSERT_EQ(cursor->next(), IndexKeyEntry(compoundKey1c, loc4));
+ ASSERT_EQ(cursor->next(), IndexKeyEntry(compoundKey1b, loc2));
+ ASSERT_EQ(cursor->next(), IndexKeyEntry(compoundKey1a, loc1));
+ ASSERT_EQ(cursor->next(), boost::none);
}
}
// Insert multiple keys and try to locate them using a forward cursor
// by specifying either a smaller key or RecordId.
TEST( SortedDataInterface, LocateIndirect ) {
- scoped_ptr<HarnessHelper> harnessHelper( newHarnessHelper() );
- scoped_ptr<SortedDataInterface> sorted( harnessHelper->newSortedDataInterface( false ) );
+ const std::unique_ptr<HarnessHelper> harnessHelper( newHarnessHelper() );
+ const std::unique_ptr<SortedDataInterface> sorted( harnessHelper->newSortedDataInterface( false ) );
{
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
- scoped_ptr<SortedDataInterface::Cursor> cursor( sorted->newCursor( opCtx.get(), 1 ) );
- ASSERT( !cursor->locate( key1, loc1 ) );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<SortedDataInterface::Cursor> cursor( sorted->newCursor(opCtx.get()) );
+ ASSERT( !cursor->seek( key1, true ) );
}
{
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
{
WriteUnitOfWork uow( opCtx.get() );
ASSERT_OK( sorted->insert( opCtx.get(), key1, loc1, true ) );
@@ -520,19 +402,15 @@ namespace mongo {
}
{
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
- scoped_ptr<SortedDataInterface::Cursor> cursor( sorted->newCursor( opCtx.get(), 1 ) );
-
- ASSERT( !cursor->locate( key1, RecordId::max() ) );
- ASSERT_EQUALS( key2, cursor->getKey() );
- ASSERT_EQUALS( loc2, cursor->getRecordId() );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<SortedDataInterface::Cursor> cursor( sorted->newCursor(opCtx.get()) );
- cursor->advance();
- ASSERT( cursor->isEOF() );
+ ASSERT_EQ(cursor->seek(key1, false), IndexKeyEntry(key2, loc2));
+ ASSERT_EQ(cursor->next(), boost::none);
}
{
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
{
WriteUnitOfWork uow( opCtx.get() );
ASSERT_OK( sorted->insert( opCtx.get(), key3, loc3, true ) );
@@ -541,40 +419,30 @@ namespace mongo {
}
{
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
- scoped_ptr<SortedDataInterface::Cursor> cursor( sorted->newCursor( opCtx.get(), 1 ) );
-
- ASSERT( !cursor->locate( key1, RecordId::min() ) );
- ASSERT_EQUALS( key1, cursor->getKey() );
- ASSERT_EQUALS( loc1, cursor->getRecordId() );
-
- cursor->advance();
- ASSERT_EQUALS( key2, cursor->getKey() );
- ASSERT_EQUALS( loc2, cursor->getRecordId() );
-
- cursor->advance();
- ASSERT_EQUALS( key3, cursor->getKey() );
- ASSERT_EQUALS( loc3, cursor->getRecordId() );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<SortedDataInterface::Cursor> cursor( sorted->newCursor(opCtx.get()) );
- cursor->advance();
- ASSERT( cursor->isEOF() );
+ ASSERT_EQ(cursor->seek(key1, true), IndexKeyEntry(key1, loc1));
+ ASSERT_EQ(cursor->next(), IndexKeyEntry(key2, loc2));
+ ASSERT_EQ(cursor->next(), IndexKeyEntry(key3, loc3));
+ ASSERT_EQ(cursor->next(), boost::none);
}
}
// Insert multiple keys and try to locate them using a reverse cursor
// by specifying either a larger key or RecordId.
TEST( SortedDataInterface, LocateIndirectReversed ) {
- scoped_ptr<HarnessHelper> harnessHelper( newHarnessHelper() );
- scoped_ptr<SortedDataInterface> sorted( harnessHelper->newSortedDataInterface( false ) );
+ const std::unique_ptr<HarnessHelper> harnessHelper( newHarnessHelper() );
+ const std::unique_ptr<SortedDataInterface> sorted( harnessHelper->newSortedDataInterface( false ) );
{
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
- scoped_ptr<SortedDataInterface::Cursor> cursor( sorted->newCursor( opCtx.get(), -1 ) );
- ASSERT( !cursor->locate( key3, loc1 ) );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<SortedDataInterface::Cursor> cursor( sorted->newCursor(opCtx.get(), false) );
+ ASSERT( !cursor->seek( key3, true ) );
}
{
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
{
WriteUnitOfWork uow( opCtx.get() );
ASSERT_OK( sorted->insert( opCtx.get(), key1, loc1, true ) );
@@ -584,19 +452,15 @@ namespace mongo {
}
{
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
- scoped_ptr<SortedDataInterface::Cursor> cursor( sorted->newCursor( opCtx.get(), -1 ) );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<SortedDataInterface::Cursor> cursor( sorted->newCursor(opCtx.get(), false) );
- ASSERT( !cursor->locate( key2, RecordId::min() ) );
- ASSERT_EQUALS( key1, cursor->getKey() );
- ASSERT_EQUALS( loc1, cursor->getRecordId() );
-
- cursor->advance();
- ASSERT( cursor->isEOF() );
+ ASSERT_EQ(cursor->seek(key2, false), IndexKeyEntry(key1, loc1));
+ ASSERT_EQ(cursor->next(), boost::none);
}
{
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
{
WriteUnitOfWork uow( opCtx.get() );
ASSERT_OK( sorted->insert( opCtx.get(), key3, loc3, true ) );
@@ -605,40 +469,30 @@ namespace mongo {
}
{
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
- scoped_ptr<SortedDataInterface::Cursor> cursor( sorted->newCursor( opCtx.get(), -1 ) );
-
- ASSERT( !cursor->locate( key3, RecordId::max() ) );
- ASSERT_EQUALS( key3, cursor->getKey() );
- ASSERT_EQUALS( loc3, cursor->getRecordId() );
-
- cursor->advance();
- ASSERT_EQUALS( key2, cursor->getKey() );
- ASSERT_EQUALS( loc2, cursor->getRecordId() );
-
- cursor->advance();
- ASSERT_EQUALS( key1, cursor->getKey() );
- ASSERT_EQUALS( loc1, cursor->getRecordId() );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<SortedDataInterface::Cursor> cursor( sorted->newCursor(opCtx.get(), false) );
- cursor->advance();
- ASSERT( cursor->isEOF() );
+ ASSERT_EQ(cursor->seek(key3, true), IndexKeyEntry(key3, loc3));
+ ASSERT_EQ(cursor->next(), IndexKeyEntry(key2, loc2));
+ ASSERT_EQ(cursor->next(), IndexKeyEntry(key1, loc1));
+ ASSERT_EQ(cursor->next(), boost::none);
}
}
// Insert multiple compound keys and try to locate them using a forward cursor
// by specifying either a smaller key or RecordId.
TEST( SortedDataInterface, LocateIndirectCompoundKeys ) {
- scoped_ptr<HarnessHelper> harnessHelper( newHarnessHelper() );
- scoped_ptr<SortedDataInterface> sorted( harnessHelper->newSortedDataInterface( false ) );
+ const std::unique_ptr<HarnessHelper> harnessHelper( newHarnessHelper() );
+ const std::unique_ptr<SortedDataInterface> sorted( harnessHelper->newSortedDataInterface( false ) );
{
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
- scoped_ptr<SortedDataInterface::Cursor> cursor( sorted->newCursor( opCtx.get(), 1 ) );
- ASSERT( !cursor->locate( compoundKey1a, loc1 ) );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<SortedDataInterface::Cursor> cursor( sorted->newCursor(opCtx.get()) );
+ ASSERT( !cursor->seek( compoundKey1a, true ) );
}
{
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
{
WriteUnitOfWork uow( opCtx.get() );
ASSERT_OK( sorted->insert( opCtx.get(), compoundKey1a, loc1, true ) );
@@ -649,23 +503,16 @@ namespace mongo {
}
{
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
- scoped_ptr<SortedDataInterface::Cursor> cursor( sorted->newCursor( opCtx.get(), 1 ) );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<SortedDataInterface::Cursor> cursor( sorted->newCursor(opCtx.get()) );
- ASSERT( !cursor->locate( compoundKey1a, RecordId::max() ) );
- ASSERT_EQUALS( compoundKey1b, cursor->getKey() );
- ASSERT_EQUALS( loc2, cursor->getRecordId() );
-
- cursor->advance();
- ASSERT_EQUALS( compoundKey2b, cursor->getKey() );
- ASSERT_EQUALS( loc3, cursor->getRecordId() );
-
- cursor->advance();
- ASSERT( cursor->isEOF() );
+ ASSERT_EQ(cursor->seek(compoundKey1a, false), IndexKeyEntry(compoundKey1b, loc2));
+ ASSERT_EQ(cursor->next(), IndexKeyEntry(compoundKey2b, loc3));
+ ASSERT_EQ(cursor->next(), boost::none);
}
{
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
{
WriteUnitOfWork uow( opCtx.get() );
ASSERT_OK( sorted->insert( opCtx.get(), compoundKey1c, loc4, true ) );
@@ -675,36 +522,29 @@ namespace mongo {
}
{
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
- scoped_ptr<SortedDataInterface::Cursor> cursor( sorted->newCursor( opCtx.get(), 1 ) );
-
- ASSERT( !cursor->locate( compoundKey2a, loc1 ) );
- ASSERT_EQUALS( compoundKey2b, cursor->getKey() );
- ASSERT_EQUALS( loc3, cursor->getRecordId() );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<SortedDataInterface::Cursor> cursor( sorted->newCursor(opCtx.get()) );
- cursor->advance();
- ASSERT_EQUALS( compoundKey3a, cursor->getKey() );
- ASSERT_EQUALS( loc5, cursor->getRecordId() );
-
- cursor->advance();
- ASSERT( cursor->isEOF() );
+ ASSERT_EQ(cursor->seek(compoundKey2a, true), IndexKeyEntry(compoundKey2b, loc3));
+ ASSERT_EQ(cursor->next(), IndexKeyEntry(compoundKey3a, loc5));
+ ASSERT_EQ(cursor->next(), boost::none);
}
}
// Insert multiple compound keys and try to locate them using a reverse cursor
// by specifying either a larger key or RecordId.
TEST( SortedDataInterface, LocateIndirectCompoundKeysReversed ) {
- scoped_ptr<HarnessHelper> harnessHelper( newHarnessHelper() );
- scoped_ptr<SortedDataInterface> sorted( harnessHelper->newSortedDataInterface( false ) );
+ const std::unique_ptr<HarnessHelper> harnessHelper( newHarnessHelper() );
+ const std::unique_ptr<SortedDataInterface> sorted( harnessHelper->newSortedDataInterface( false ) );
{
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
- scoped_ptr<SortedDataInterface::Cursor> cursor( sorted->newCursor( opCtx.get(), -1 ) );
- ASSERT( !cursor->locate( compoundKey3a, loc1 ) );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<SortedDataInterface::Cursor> cursor( sorted->newCursor(opCtx.get(), false) );
+ ASSERT( !cursor->seek( compoundKey3a, true ) );
}
{
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
{
WriteUnitOfWork uow( opCtx.get() );
ASSERT_OK( sorted->insert( opCtx.get(), compoundKey1a, loc1, true ) );
@@ -715,23 +555,16 @@ namespace mongo {
}
{
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
- scoped_ptr<SortedDataInterface::Cursor> cursor( sorted->newCursor( opCtx.get(), -1 ) );
-
- ASSERT( !cursor->locate( compoundKey2b, RecordId::min() ) );
- ASSERT_EQUALS( compoundKey1b, cursor->getKey() );
- ASSERT_EQUALS( loc2, cursor->getRecordId() );
-
- cursor->advance();
- ASSERT_EQUALS( compoundKey1a, cursor->getKey() );
- ASSERT_EQUALS( loc1, cursor->getRecordId() );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<SortedDataInterface::Cursor> cursor( sorted->newCursor(opCtx.get(), false) );
- cursor->advance();
- ASSERT( cursor->isEOF() );
+ ASSERT_EQ(cursor->seek(compoundKey2b, false), IndexKeyEntry(compoundKey1b, loc2));
+ ASSERT_EQ(cursor->next(), IndexKeyEntry(compoundKey1a, loc1));
+ ASSERT_EQ(cursor->next(), boost::none);
}
{
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
{
WriteUnitOfWork uow( opCtx.get() );
ASSERT_OK( sorted->insert( opCtx.get(), compoundKey1c, loc4, true ) );
@@ -741,63 +574,53 @@ namespace mongo {
}
{
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
- scoped_ptr<SortedDataInterface::Cursor> cursor( sorted->newCursor( opCtx.get(), -1 ) );
-
- ASSERT( !cursor->locate( compoundKey1d, loc1 ) );
- ASSERT_EQUALS( compoundKey1c, cursor->getKey() );
- ASSERT_EQUALS( loc4, cursor->getRecordId() );
-
- cursor->advance();
- ASSERT_EQUALS( compoundKey1b, cursor->getKey() );
- ASSERT_EQUALS( loc2, cursor->getRecordId() );
-
- cursor->advance();
- ASSERT_EQUALS( compoundKey1a, cursor->getKey() );
- ASSERT_EQUALS( loc1, cursor->getRecordId() );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<SortedDataInterface::Cursor> cursor( sorted->newCursor(opCtx.get(), false) );
- cursor->advance();
- ASSERT( cursor->isEOF() );
+ ASSERT_EQ(cursor->seek(compoundKey1d, true), IndexKeyEntry(compoundKey1c, loc4));
+ ASSERT_EQ(cursor->next(), IndexKeyEntry(compoundKey1b, loc2));
+ ASSERT_EQ(cursor->next(), IndexKeyEntry(compoundKey1a, loc1));
+ ASSERT_EQ(cursor->next(), boost::none);
}
}
// Call locate on a forward cursor of an empty index and verify that the cursor
// is positioned at EOF.
TEST( SortedDataInterface, LocateEmpty ) {
- scoped_ptr<HarnessHelper> harnessHelper( newHarnessHelper() );
- scoped_ptr<SortedDataInterface> sorted( harnessHelper->newSortedDataInterface( false ) );
+ const std::unique_ptr<HarnessHelper> harnessHelper( newHarnessHelper() );
+ const std::unique_ptr<SortedDataInterface> sorted( harnessHelper->newSortedDataInterface( false ) );
{
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
ASSERT( sorted->isEmpty( opCtx.get() ) );
}
{
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
- scoped_ptr<SortedDataInterface::Cursor> cursor( sorted->newCursor( opCtx.get(), 1 ) );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<SortedDataInterface::Cursor> cursor( sorted->newCursor(opCtx.get()) );
- ASSERT( !cursor->locate( BSONObj(), RecordId::min() ) );
- ASSERT( cursor->isEOF() );
+ ASSERT( !cursor->seek( BSONObj(), true ) );
+ ASSERT( !cursor->next() );
}
}
// Call locate on a reverse cursor of an empty index and verify that the cursor
// is positioned at EOF.
TEST( SortedDataInterface, LocateEmptyReversed ) {
- scoped_ptr<HarnessHelper> harnessHelper( newHarnessHelper() );
- scoped_ptr<SortedDataInterface> sorted( harnessHelper->newSortedDataInterface( false ) );
+ const std::unique_ptr<HarnessHelper> harnessHelper( newHarnessHelper() );
+ const std::unique_ptr<SortedDataInterface> sorted( harnessHelper->newSortedDataInterface( false ) );
{
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
ASSERT( sorted->isEmpty( opCtx.get() ) );
}
{
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
- scoped_ptr<SortedDataInterface::Cursor> cursor( sorted->newCursor( opCtx.get(), -1 ) );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<SortedDataInterface::Cursor> cursor( sorted->newCursor(opCtx.get(), false) );
- ASSERT( !cursor->locate( BSONObj(), RecordId::max() ) );
- ASSERT( cursor->isEOF() );
+ ASSERT( !cursor->seek( BSONObj(), true ) );
+ ASSERT( !cursor->next() );
}
}
diff --git a/src/mongo/db/storage/sorted_data_interface_test_cursor_position.cpp b/src/mongo/db/storage/sorted_data_interface_test_cursor_position.cpp
deleted file mode 100644
index 41e74cddf4e..00000000000
--- a/src/mongo/db/storage/sorted_data_interface_test_cursor_position.cpp
+++ /dev/null
@@ -1,481 +0,0 @@
-// sorted_data_interface_test_cursor_position.cpp
-
-/**
- * Copyright (C) 2014 MongoDB Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License, version 3,
- * as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- * As a special exception, the copyright holders give permission to link the
- * code of portions of this program with the OpenSSL library under certain
- * conditions as described in each individual source file and distribute
- * linked combinations including the program with the OpenSSL library. You
- * must comply with the GNU Affero General Public License in all respects for
- * all of the code used other than as permitted herein. If you modify file(s)
- * with this exception, you may extend this exception to your version of the
- * file(s), but you are not obligated to do so. If you do not wish to do so,
- * delete this exception statement from your version. If you delete this
- * exception statement from all source files in the program, then also delete
- * it in the license file.
- */
-
-#include "mongo/db/storage/sorted_data_interface_test_harness.h"
-
-#include <boost/scoped_ptr.hpp>
-
-#include "mongo/db/storage/sorted_data_interface.h"
-#include "mongo/unittest/unittest.h"
-
-namespace mongo {
-
- using boost::scoped_ptr;
-
- // Verify that two forward cursors positioned at EOF are considered
- // to point to the same place.
- TEST( SortedDataInterface, CursorsPointToSamePlaceIfEOF ) {
- scoped_ptr<HarnessHelper> harnessHelper( newHarnessHelper() );
- scoped_ptr<SortedDataInterface> sorted( harnessHelper->newSortedDataInterface( false ) );
-
- {
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
- ASSERT( sorted->isEmpty( opCtx.get() ) );
- }
-
- {
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
- scoped_ptr<SortedDataInterface::Cursor> cursor1( sorted->newCursor( opCtx.get(), 1 ) );
- scoped_ptr<SortedDataInterface::Cursor> cursor2( sorted->newCursor( opCtx.get(), 1 ) );
-
- ASSERT( !cursor1->locate( minKey, RecordId::min() ) );
- ASSERT( !cursor2->locate( minKey, RecordId::min() ) );
- ASSERT( cursor1->isEOF() );
- ASSERT( cursor2->isEOF() );
- ASSERT( cursor1->pointsToSamePlaceAs( *cursor2 ) );
- ASSERT( cursor2->pointsToSamePlaceAs( *cursor1 ) );
- }
- }
-
- // Verify that two reverse cursors positioned at EOF are considered
- // to point to the same place.
- TEST( SortedDataInterface, CursorsPointToSamePlaceIfEOFReversed ) {
- scoped_ptr<HarnessHelper> harnessHelper( newHarnessHelper() );
- scoped_ptr<SortedDataInterface> sorted( harnessHelper->newSortedDataInterface( false ) );
-
- {
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
- ASSERT( sorted->isEmpty( opCtx.get() ) );
- }
-
- {
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
- scoped_ptr<SortedDataInterface::Cursor> cursor1( sorted->newCursor( opCtx.get(), -1 ) );
- scoped_ptr<SortedDataInterface::Cursor> cursor2( sorted->newCursor( opCtx.get(), -1 ) );
-
- ASSERT( !cursor1->locate( maxKey, RecordId::max() ) );
- ASSERT( !cursor2->locate( maxKey, RecordId::max() ) );
- ASSERT( cursor1->isEOF() );
- ASSERT( cursor2->isEOF() );
- ASSERT( cursor1->pointsToSamePlaceAs( *cursor2 ) );
- ASSERT( cursor2->pointsToSamePlaceAs( *cursor1 ) );
- }
- }
-
- // Iterate two forward cursors simultaneously and verify they are considered
- // to point to the same place.
- TEST( SortedDataInterface, CursorsPointToSamePlace ) {
- scoped_ptr<HarnessHelper> harnessHelper( newHarnessHelper() );
- scoped_ptr<SortedDataInterface> sorted( harnessHelper->newSortedDataInterface( true ) );
-
- {
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
- ASSERT( sorted->isEmpty( opCtx.get() ) );
- }
-
- {
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
- {
- WriteUnitOfWork uow( opCtx.get() );
- ASSERT_OK( sorted->insert( opCtx.get(), key1, loc1, false ) );
- ASSERT_OK( sorted->insert( opCtx.get(), key2, loc2, false ) );
- uow.commit();
- }
- }
-
- {
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
- ASSERT_EQUALS( 2, sorted->numEntries( opCtx.get() ) );
- }
-
- {
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
- scoped_ptr<SortedDataInterface::Cursor> cursor1( sorted->newCursor( opCtx.get(), 1 ) );
- scoped_ptr<SortedDataInterface::Cursor> cursor2( sorted->newCursor( opCtx.get(), 1 ) );
-
- ASSERT( cursor1->locate( key1, loc1 ) );
- ASSERT( cursor2->locate( key1, loc1 ) );
- ASSERT( cursor1->pointsToSamePlaceAs( *cursor2 ) );
- ASSERT( cursor2->pointsToSamePlaceAs( *cursor1 ) );
-
- cursor1->advance();
- cursor2->advance();
- ASSERT( cursor1->pointsToSamePlaceAs( *cursor2 ) );
- ASSERT( cursor2->pointsToSamePlaceAs( *cursor1 ) );
-
- cursor1->advance();
- cursor2->advance();
- ASSERT( cursor1->pointsToSamePlaceAs( *cursor2 ) );
- ASSERT( cursor2->pointsToSamePlaceAs( *cursor1 ) );
- }
- }
-
- // Iterate two reverse cursors simultaneously and verify they are considered
- // to point to the same place.
- TEST( SortedDataInterface, CursorsPointToSamePlaceReversed ) {
- scoped_ptr<HarnessHelper> harnessHelper( newHarnessHelper() );
- scoped_ptr<SortedDataInterface> sorted( harnessHelper->newSortedDataInterface( false ) );
-
- {
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
- ASSERT( sorted->isEmpty( opCtx.get() ) );
- }
-
- {
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
- {
- WriteUnitOfWork uow( opCtx.get() );
- ASSERT_OK( sorted->insert( opCtx.get(), key1, loc1, true ) );
- ASSERT_OK( sorted->insert( opCtx.get(), key2, loc2, true ) );
- uow.commit();
- }
- }
-
- {
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
- ASSERT_EQUALS( 2, sorted->numEntries( opCtx.get() ) );
- }
-
- {
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
- scoped_ptr<SortedDataInterface::Cursor> cursor1( sorted->newCursor( opCtx.get(), -1 ) );
- scoped_ptr<SortedDataInterface::Cursor> cursor2( sorted->newCursor( opCtx.get(), -1 ) );
-
- ASSERT( cursor1->locate( key2, loc2 ) );
- ASSERT( cursor2->locate( key2, loc2 ) );
- ASSERT( cursor1->pointsToSamePlaceAs( *cursor2 ) );
- ASSERT( cursor2->pointsToSamePlaceAs( *cursor1 ) );
-
- cursor1->advance();
- cursor2->advance();
- ASSERT( cursor1->pointsToSamePlaceAs( *cursor2 ) );
- ASSERT( cursor2->pointsToSamePlaceAs( *cursor1 ) );
-
- cursor1->advance();
- cursor2->advance();
- ASSERT( cursor1->pointsToSamePlaceAs( *cursor2 ) );
- ASSERT( cursor2->pointsToSamePlaceAs( *cursor1 ) );
- }
- }
-
- // Verify that two forward cursors positioned at different keys are not considered
- // to point to the same place.
- TEST( SortedDataInterface, CursorsPointToDifferentKeys ) {
- scoped_ptr<HarnessHelper> harnessHelper( newHarnessHelper() );
- scoped_ptr<SortedDataInterface> sorted( harnessHelper->newSortedDataInterface( false ) );
-
- {
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
- ASSERT( sorted->isEmpty( opCtx.get() ) );
- }
-
- {
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
- {
- WriteUnitOfWork uow( opCtx.get() );
- ASSERT_OK( sorted->insert( opCtx.get(), key1, loc1, true ) );
- ASSERT_OK( sorted->insert( opCtx.get(), key2, loc2, true ) );
- uow.commit();
- }
- }
-
- {
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
- ASSERT_EQUALS( 2, sorted->numEntries( opCtx.get() ) );
- }
-
- {
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
- scoped_ptr<SortedDataInterface::Cursor> cursor1( sorted->newCursor( opCtx.get(), 1 ) );
- scoped_ptr<SortedDataInterface::Cursor> cursor2( sorted->newCursor( opCtx.get(), 1 ) );
-
- ASSERT( cursor1->locate( key1, loc1 ) );
- ASSERT( cursor2->locate( key2, loc2 ) );
- ASSERT( !cursor1->pointsToSamePlaceAs( *cursor2 ) );
- ASSERT( !cursor2->pointsToSamePlaceAs( *cursor1 ) );
- }
- }
-
- // Verify that two reverse cursors positioned at different keys are not considered
- // to point to the same place.
- TEST( SortedDataInterface, CursorsPointToDifferentKeysReversed ) {
- scoped_ptr<HarnessHelper> harnessHelper( newHarnessHelper() );
- scoped_ptr<SortedDataInterface> sorted( harnessHelper->newSortedDataInterface( false ) );
-
- {
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
- ASSERT( sorted->isEmpty( opCtx.get() ) );
- }
-
- {
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
- {
- WriteUnitOfWork uow( opCtx.get() );
- ASSERT_OK( sorted->insert( opCtx.get(), key1, loc1, true ) );
- ASSERT_OK( sorted->insert( opCtx.get(), key2, loc2, true ) );
- uow.commit();
- }
- }
-
- {
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
- ASSERT_EQUALS( 2, sorted->numEntries( opCtx.get() ) );
- }
-
- {
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
- scoped_ptr<SortedDataInterface::Cursor> cursor1( sorted->newCursor( opCtx.get(), -1 ) );
- scoped_ptr<SortedDataInterface::Cursor> cursor2( sorted->newCursor( opCtx.get(), -1 ) );
-
- ASSERT( cursor1->locate( key1, loc1 ) );
- ASSERT( cursor2->locate( key2, loc2 ) );
- ASSERT( !cursor1->pointsToSamePlaceAs( *cursor2 ) );
- ASSERT( !cursor2->pointsToSamePlaceAs( *cursor1 ) );
- }
- }
-
- // Verify that two forward cursors positioned at a duplicate key, but with
- // different RecordIds are not considered to point to the same place.
- TEST( SortedDataInterface, CursorsPointToDifferentDiskLocs ) {
- scoped_ptr<HarnessHelper> harnessHelper( newHarnessHelper() );
- scoped_ptr<SortedDataInterface> sorted( harnessHelper->newSortedDataInterface( false ) );
-
- {
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
- ASSERT( sorted->isEmpty( opCtx.get() ) );
- }
-
- {
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
- {
- WriteUnitOfWork uow( opCtx.get() );
- ASSERT_OK( sorted->insert( opCtx.get(), key1, loc1, true ) );
- ASSERT_OK( sorted->insert( opCtx.get(), key1, loc2, true ) );
- uow.commit();
- }
- }
-
- {
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
- ASSERT_EQUALS( 2, sorted->numEntries( opCtx.get() ) );
- }
-
- {
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
- scoped_ptr<SortedDataInterface::Cursor> cursor1( sorted->newCursor( opCtx.get(), 1 ) );
- scoped_ptr<SortedDataInterface::Cursor> cursor2( sorted->newCursor( opCtx.get(), 1 ) );
-
- ASSERT( cursor1->locate( key1, loc1 ) );
- ASSERT( cursor2->locate( key1, loc2 ) );
- ASSERT( !cursor1->pointsToSamePlaceAs( *cursor2 ) );
- ASSERT( !cursor2->pointsToSamePlaceAs( *cursor1 ) );
- }
- }
-
- // Verify that two reverse cursors positioned at a duplicate key, but with
- // different RecordIds are not considered to point to the same place.
- TEST( SortedDataInterface, CursorsPointToDifferentDiskLocsReversed ) {
- scoped_ptr<HarnessHelper> harnessHelper( newHarnessHelper() );
- scoped_ptr<SortedDataInterface> sorted( harnessHelper->newSortedDataInterface( false ) );
-
- {
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
- ASSERT( sorted->isEmpty( opCtx.get() ) );
- }
-
- {
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
- {
- WriteUnitOfWork uow( opCtx.get() );
- ASSERT_OK( sorted->insert( opCtx.get(), key1, loc1, true ) );
- ASSERT_OK( sorted->insert( opCtx.get(), key1, loc2, true /* allow duplicates */ ) );
- uow.commit();
- }
- }
-
- {
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
- ASSERT_EQUALS( 2, sorted->numEntries( opCtx.get() ) );
- }
-
- {
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
- scoped_ptr<SortedDataInterface::Cursor> cursor1( sorted->newCursor( opCtx.get(), -1 ) );
- scoped_ptr<SortedDataInterface::Cursor> cursor2( sorted->newCursor( opCtx.get(), -1 ) );
-
- ASSERT( cursor1->locate( key1, loc1 ) );
- ASSERT( cursor2->locate( key1, loc2 ) );
- ASSERT( !cursor1->pointsToSamePlaceAs( *cursor2 ) );
- ASSERT( !cursor2->pointsToSamePlaceAs( *cursor1 ) );
- }
- }
-
- // Verify that a forward cursor and a reverse cursor positioned at the same key
- // are considered to point to the same place.
- TEST( SortedDataInterface, CursorPointsToSamePlaceRegardlessOfDirection ) {
- scoped_ptr<HarnessHelper> harnessHelper( newHarnessHelper() );
- scoped_ptr<SortedDataInterface> sorted( harnessHelper->newSortedDataInterface( true ) );
-
- {
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
- ASSERT( sorted->isEmpty( opCtx.get() ) );
- }
-
- {
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
- {
- WriteUnitOfWork uow( opCtx.get() );
- ASSERT_OK( sorted->insert( opCtx.get(), key1, loc1, false ) );
- ASSERT_OK( sorted->insert( opCtx.get(), key2, loc2, false ) );
- ASSERT_OK( sorted->insert( opCtx.get(), key3, loc3, false ) );
- uow.commit();
- }
- }
-
- {
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
- ASSERT_EQUALS( 3, sorted->numEntries( opCtx.get() ) );
- }
-
- {
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
- scoped_ptr<SortedDataInterface::Cursor> cursor1( sorted->newCursor( opCtx.get(), 1 ) );
- scoped_ptr<SortedDataInterface::Cursor> cursor2( sorted->newCursor( opCtx.get(), -1 ) );
-
- ASSERT( cursor1->locate( key1, loc1 ) );
- ASSERT( cursor2->locate( key3, loc3 ) );
- // SERVER-15480 the reverse cursor is incorrectly casted to a
- // cursor of type InMemoryBtreeImpl::ForwardCursor
- // ASSERT( !cursor1->pointsToSamePlaceAs( *cursor2 ) );
- // SERVER-15480 the forward cursor is incorrectly casted to a
- // cursor of type InMemoryBtreeImpl::ReverseCursor
- // ASSERT( !cursor2->pointsToSamePlaceAs( *cursor1 ) );
-
- cursor1->advance();
- cursor2->advance();
- // SERVER-15480 the reverse cursor is incorrectly casted to a
- // cursor of type InMemoryBtreeImpl::ForwardCursor
- // ASSERT( cursor1->pointsToSamePlaceAs( *cursor2 ) );
- // SERVER-15480 the forward cursor is incorrectly casted to a
- // cursor of type InMemoryBtreeImpl::ReverseCursor
- // ASSERT( cursor2->pointsToSamePlaceAs( *cursor1 ) );
-
- cursor1->advance();
- cursor2->advance();
- // SERVER-15480 the reverse cursor is incorrectly casted to a
- // cursor of type InMemoryBtreeImpl::ForwardCursor
- // ASSERT( !cursor1->pointsToSamePlaceAs( *cursor2 ) );
- // SERVER-15480 the forward cursor is incorrectly casted to a
- // cursor of type InMemoryBtreeImpl::ReverseCursor
- // ASSERT( !cursor2->pointsToSamePlaceAs( *cursor1 ) );
- }
- }
-
- // Verify that a forward cursor always points to the same place as itself.
- TEST( SortedDataInterface, CursorPointsToSamePlaceAsItself ) {
- scoped_ptr<HarnessHelper> harnessHelper( newHarnessHelper() );
- scoped_ptr<SortedDataInterface> sorted( harnessHelper->newSortedDataInterface( false ) );
-
- {
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
- ASSERT( sorted->isEmpty( opCtx.get() ) );
- }
-
- int nToInsert = 10;
- for ( int i = 0; i < nToInsert; i++ ) {
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
- {
- WriteUnitOfWork uow( opCtx.get() );
- BSONObj key = BSON( "" << i );
- RecordId loc( 42, i * 2 );
- ASSERT_OK( sorted->insert( opCtx.get(), key, loc, true ) );
- uow.commit();
- }
- }
-
- {
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
- ASSERT_EQUALS( nToInsert, sorted->numEntries( opCtx.get() ) );
- }
-
- {
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
- scoped_ptr<SortedDataInterface::Cursor> cursor( sorted->newCursor( opCtx.get(), 1 ) );
- ASSERT( !cursor->locate( minKey, RecordId::min() ) );
- for ( int i = 0; i < nToInsert; i++ ) {
- ASSERT( !cursor->isEOF() );
- ASSERT( cursor->pointsToSamePlaceAs( *cursor ) );
- cursor->advance();
- }
- ASSERT( cursor->isEOF() );
- }
- }
-
- // Verify that a reverse cursor always points to the same place as itself.
- TEST( SortedDataInterface, CursorPointsToSamePlaceAsItselfReversed ) {
- scoped_ptr<HarnessHelper> harnessHelper( newHarnessHelper() );
- scoped_ptr<SortedDataInterface> sorted( harnessHelper->newSortedDataInterface( false ) );
-
- {
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
- ASSERT( sorted->isEmpty( opCtx.get() ) );
- }
-
- int nToInsert = 10;
- for ( int i = 0; i < nToInsert; i++ ) {
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
- {
- WriteUnitOfWork uow( opCtx.get() );
- BSONObj key = BSON( "" << i );
- RecordId loc( 42, i * 2 );
- ASSERT_OK( sorted->insert( opCtx.get(), key, loc, true ) );
- uow.commit();
- }
- }
-
- {
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
- ASSERT_EQUALS( nToInsert, sorted->numEntries( opCtx.get() ) );
- }
-
- {
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
- scoped_ptr<SortedDataInterface::Cursor> cursor( sorted->newCursor( opCtx.get(), -1 ) );
- ASSERT( !cursor->locate( maxKey, RecordId::max() ) );
- for ( int i = nToInsert - 1; i >= 0; i-- ) {
- ASSERT( !cursor->isEOF() );
- ASSERT( cursor->pointsToSamePlaceAs( *cursor ) );
- cursor->advance();
- }
- ASSERT( cursor->isEOF() );
- }
- }
-
-} // namespace mongo
diff --git a/src/mongo/db/storage/sorted_data_interface_test_cursor_saverestore.cpp b/src/mongo/db/storage/sorted_data_interface_test_cursor_saverestore.cpp
index 2e71e60b145..2518d96490e 100644
--- a/src/mongo/db/storage/sorted_data_interface_test_cursor_saverestore.cpp
+++ b/src/mongo/db/storage/sorted_data_interface_test_cursor_saverestore.cpp
@@ -30,30 +30,28 @@
#include "mongo/db/storage/sorted_data_interface_test_harness.h"
-#include <boost/scoped_ptr.hpp>
+#include <memory>
#include "mongo/db/storage/sorted_data_interface.h"
#include "mongo/unittest/unittest.h"
namespace mongo {
- using boost::scoped_ptr;
-
// Insert multiple keys and try to iterate through all of them
// using a forward cursor while calling savePosition() and
// restorePosition() in succession.
TEST( SortedDataInterface, SaveAndRestorePositionWhileIterateCursor ) {
- scoped_ptr<HarnessHelper> harnessHelper( newHarnessHelper() );
- scoped_ptr<SortedDataInterface> sorted( harnessHelper->newSortedDataInterface( false ) );
+ const std::unique_ptr<HarnessHelper> harnessHelper( newHarnessHelper() );
+ const std::unique_ptr<SortedDataInterface> sorted( harnessHelper->newSortedDataInterface( false ) );
{
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
ASSERT( sorted->isEmpty( opCtx.get() ) );
}
int nToInsert = 10;
for ( int i = 0; i < nToInsert; i++ ) {
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
{
WriteUnitOfWork uow( opCtx.get() );
BSONObj key = BSON( "" << i );
@@ -64,23 +62,23 @@ namespace mongo {
}
{
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
ASSERT_EQUALS( nToInsert, sorted->numEntries( opCtx.get() ) );
}
{
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
- scoped_ptr<SortedDataInterface::Cursor> cursor( sorted->newCursor( opCtx.get(), 1 ) );
- ASSERT( !cursor->locate( minKey, RecordId::min() ) );
- for ( int i = 0; i < nToInsert; i++ ) {
- ASSERT( !cursor->isEOF() );
- ASSERT_EQUALS( BSON( "" << i ), cursor->getKey() );
- ASSERT_EQUALS( RecordId( 42, i * 2 ), cursor->getRecordId() );
- cursor->advance();
- cursor->savePosition();
- cursor->restorePosition( opCtx.get() );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<SortedDataInterface::Cursor> cursor( sorted->newCursor(opCtx.get()) );
+ int i = 0;
+ for (auto entry = cursor->seek(minKey, true); entry; i++, entry = cursor->next()) {
+ ASSERT_LT(i, nToInsert);
+ ASSERT_EQ(entry, IndexKeyEntry(BSON( "" << i), RecordId(42, i * 2)));
+
+ cursor->savePositioned();
+ cursor->restore( opCtx.get() );
}
- ASSERT( cursor->isEOF() );
+ ASSERT( !cursor->next() );
+ ASSERT_EQ(i, nToInsert);
}
}
@@ -88,17 +86,17 @@ namespace mongo {
// using a reverse cursor while calling savePosition() and
// restorePosition() in succession.
TEST( SortedDataInterface, SaveAndRestorePositionWhileIterateCursorReversed ) {
- scoped_ptr<HarnessHelper> harnessHelper( newHarnessHelper() );
- scoped_ptr<SortedDataInterface> sorted( harnessHelper->newSortedDataInterface( false ) );
+ const std::unique_ptr<HarnessHelper> harnessHelper( newHarnessHelper() );
+ const std::unique_ptr<SortedDataInterface> sorted( harnessHelper->newSortedDataInterface( false ) );
{
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
ASSERT( sorted->isEmpty( opCtx.get() ) );
}
int nToInsert = 10;
for ( int i = 0; i < nToInsert; i++ ) {
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
{
WriteUnitOfWork uow( opCtx.get() );
BSONObj key = BSON( "" << i );
@@ -109,23 +107,23 @@ namespace mongo {
}
{
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
ASSERT_EQUALS( nToInsert, sorted->numEntries( opCtx.get() ) );
}
{
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
- scoped_ptr<SortedDataInterface::Cursor> cursor( sorted->newCursor( opCtx.get(), -1 ) );
- ASSERT( !cursor->locate( maxKey, RecordId::max() ) );
- for ( int i = nToInsert - 1; i >= 0; i-- ) {
- ASSERT( !cursor->isEOF() );
- ASSERT_EQUALS( BSON( "" << i ), cursor->getKey() );
- ASSERT_EQUALS( RecordId( 42, i * 2 ), cursor->getRecordId() );
- cursor->advance();
- cursor->savePosition();
- cursor->restorePosition( opCtx.get() );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<SortedDataInterface::Cursor> cursor( sorted->newCursor(opCtx.get(), false) );
+ int i = nToInsert - 1;
+ for (auto entry = cursor->seek(maxKey, true); entry; i--, entry = cursor->next()) {
+ ASSERT_GTE(i, 0);
+ ASSERT_EQ(entry, IndexKeyEntry(BSON( "" << i), RecordId(42, i * 2)));
+
+ cursor->savePositioned();
+ cursor->restore( opCtx.get() );
}
- ASSERT( cursor->isEOF() );
+ ASSERT( !cursor->next() );
+ ASSERT_EQ(i, -1);
}
}
@@ -134,17 +132,17 @@ namespace mongo {
// restorePosition() in succession. Verify that the RecordId is saved
// as part of the current position of the cursor.
TEST( SortedDataInterface, SaveAndRestorePositionWhileIterateCursorWithDupKeys ) {
- scoped_ptr<HarnessHelper> harnessHelper( newHarnessHelper() );
- scoped_ptr<SortedDataInterface> sorted( harnessHelper->newSortedDataInterface( false ) );
+ const std::unique_ptr<HarnessHelper> harnessHelper( newHarnessHelper() );
+ const std::unique_ptr<SortedDataInterface> sorted( harnessHelper->newSortedDataInterface( false ) );
{
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
ASSERT( sorted->isEmpty( opCtx.get() ) );
}
int nToInsert = 10;
for ( int i = 0; i < nToInsert; i++ ) {
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
{
WriteUnitOfWork uow( opCtx.get() );
RecordId loc( 42, i * 2 );
@@ -154,23 +152,23 @@ namespace mongo {
}
{
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
ASSERT_EQUALS( nToInsert, sorted->numEntries( opCtx.get() ) );
}
{
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
- scoped_ptr<SortedDataInterface::Cursor> cursor( sorted->newCursor( opCtx.get(), 1 ) );
- ASSERT( !cursor->locate( minKey, RecordId::min() ) );
- for ( int i = 0; i < nToInsert; i++ ) {
- ASSERT( !cursor->isEOF() );
- ASSERT_EQUALS( key1, cursor->getKey() );
- ASSERT_EQUALS( RecordId( 42, i * 2 ), cursor->getRecordId() );
- cursor->advance();
- cursor->savePosition();
- cursor->restorePosition( opCtx.get() );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<SortedDataInterface::Cursor> cursor( sorted->newCursor(opCtx.get()) );
+ int i = 0;
+ for (auto entry = cursor->seek(minKey, true); entry; i++, entry = cursor->next()) {
+ ASSERT_LT(i, nToInsert);
+ ASSERT_EQ(entry, IndexKeyEntry(key1, RecordId(42, i * 2)));
+
+ cursor->savePositioned();
+ cursor->restore( opCtx.get() );
}
- ASSERT( cursor->isEOF() );
+ ASSERT( !cursor->next() );
+ ASSERT_EQ(i, nToInsert);
}
}
@@ -179,17 +177,17 @@ namespace mongo {
// restorePosition() in succession. Verify that the RecordId is saved
// as part of the current position of the cursor.
TEST( SortedDataInterface, SaveAndRestorePositionWhileIterateCursorWithDupKeysReversed ) {
- scoped_ptr<HarnessHelper> harnessHelper( newHarnessHelper() );
- scoped_ptr<SortedDataInterface> sorted( harnessHelper->newSortedDataInterface( false ) );
+ const std::unique_ptr<HarnessHelper> harnessHelper( newHarnessHelper() );
+ const std::unique_ptr<SortedDataInterface> sorted( harnessHelper->newSortedDataInterface( false ) );
{
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
ASSERT( sorted->isEmpty( opCtx.get() ) );
}
int nToInsert = 10;
for ( int i = 0; i < nToInsert; i++ ) {
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
{
WriteUnitOfWork uow( opCtx.get() );
RecordId loc( 42, i * 2 );
@@ -199,39 +197,39 @@ namespace mongo {
}
{
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
ASSERT_EQUALS( nToInsert, sorted->numEntries( opCtx.get() ) );
}
{
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
- scoped_ptr<SortedDataInterface::Cursor> cursor( sorted->newCursor( opCtx.get(), -1 ) );
- ASSERT( !cursor->locate( maxKey, RecordId::max() ) );
- for ( int i = nToInsert - 1; i >= 0; i-- ) {
- ASSERT( !cursor->isEOF() );
- ASSERT_EQUALS( key1, cursor->getKey() );
- ASSERT_EQUALS( RecordId( 42, i * 2 ), cursor->getRecordId() );
- cursor->advance();
- cursor->savePosition();
- cursor->restorePosition( opCtx.get() );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<SortedDataInterface::Cursor> cursor( sorted->newCursor(opCtx.get(), false) );
+ int i = nToInsert - 1;
+ for (auto entry = cursor->seek(maxKey, true); entry; i--, entry = cursor->next()) {
+ ASSERT_GTE(i, 0);
+ ASSERT_EQ(entry, IndexKeyEntry(key1, RecordId(42, i * 2)));
+
+ cursor->savePositioned();
+ cursor->restore( opCtx.get() );
}
- ASSERT( cursor->isEOF() );
+ ASSERT( !cursor->next() );
+ ASSERT_EQ(i, -1);
}
}
// Call savePosition() on a forward cursor without ever calling restorePosition().
// May be useful to run this test under valgrind to verify there are no leaks.
TEST( SortedDataInterface, SavePositionWithoutRestore ) {
- scoped_ptr<HarnessHelper> harnessHelper( newHarnessHelper() );
- scoped_ptr<SortedDataInterface> sorted( harnessHelper->newSortedDataInterface( true ) );
+ const std::unique_ptr<HarnessHelper> harnessHelper( newHarnessHelper() );
+ const std::unique_ptr<SortedDataInterface> sorted( harnessHelper->newSortedDataInterface( true ) );
{
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
ASSERT( sorted->isEmpty( opCtx.get() ) );
}
{
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
{
WriteUnitOfWork uow( opCtx.get() );
ASSERT_OK( sorted->insert( opCtx.get(), key1, loc1, false ) );
@@ -240,30 +238,30 @@ namespace mongo {
}
{
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
ASSERT_EQUALS( 1, sorted->numEntries( opCtx.get() ) );
}
{
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
- scoped_ptr<SortedDataInterface::Cursor> cursor( sorted->newCursor( opCtx.get(), 1 ) );
- cursor->savePosition();
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<SortedDataInterface::Cursor> cursor( sorted->newCursor(opCtx.get()) );
+ cursor->savePositioned();
}
}
// Call savePosition() on a reverse cursor without ever calling restorePosition().
// May be useful to run this test under valgrind to verify there are no leaks.
TEST( SortedDataInterface, SavePositionWithoutRestoreReversed ) {
- scoped_ptr<HarnessHelper> harnessHelper( newHarnessHelper() );
- scoped_ptr<SortedDataInterface> sorted( harnessHelper->newSortedDataInterface( false ) );
+ const std::unique_ptr<HarnessHelper> harnessHelper( newHarnessHelper() );
+ const std::unique_ptr<SortedDataInterface> sorted( harnessHelper->newSortedDataInterface( false ) );
{
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
ASSERT( sorted->isEmpty( opCtx.get() ) );
}
{
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
{
WriteUnitOfWork uow( opCtx.get() );
ASSERT_OK( sorted->insert( opCtx.get(), key1, loc1, true ) );
@@ -272,14 +270,14 @@ namespace mongo {
}
{
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
ASSERT_EQUALS( 1, sorted->numEntries( opCtx.get() ) );
}
{
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
- scoped_ptr<SortedDataInterface::Cursor> cursor( sorted->newCursor( opCtx.get(), -1 ) );
- cursor->savePosition();
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<SortedDataInterface::Cursor> cursor( sorted->newCursor(opCtx.get(), false) );
+ cursor->savePositioned();
}
}
diff --git a/src/mongo/db/storage/sorted_data_interface_test_harness.cpp b/src/mongo/db/storage/sorted_data_interface_test_harness.cpp
index f26ac1ef494..066b6a0b1a9 100644
--- a/src/mongo/db/storage/sorted_data_interface_test_harness.cpp
+++ b/src/mongo/db/storage/sorted_data_interface_test_harness.cpp
@@ -30,21 +30,19 @@
#include "mongo/db/storage/sorted_data_interface_test_harness.h"
-#include <boost/scoped_ptr.hpp>
+#include <memory>
#include "mongo/db/storage/sorted_data_interface.h"
#include "mongo/unittest/unittest.h"
namespace mongo {
- using boost::scoped_ptr;
-
TEST( SortedDataInterface, InsertWithDups1 ) {
- scoped_ptr<HarnessHelper> harnessHelper( newHarnessHelper() );
- scoped_ptr<SortedDataInterface> sorted( harnessHelper->newSortedDataInterface( false ) );
+ const std::unique_ptr<HarnessHelper> harnessHelper( newHarnessHelper() );
+ const std::unique_ptr<SortedDataInterface> sorted( harnessHelper->newSortedDataInterface( false ) );
{
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
{
WriteUnitOfWork uow( opCtx.get() );
sorted->insert( opCtx.get(), BSON( "" << 1 ), RecordId( 5, 2 ), true );
@@ -53,7 +51,7 @@ namespace mongo {
}
{
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
{
WriteUnitOfWork uow( opCtx.get() );
sorted->insert( opCtx.get(), BSON( "" << 1 ), RecordId( 6, 2 ), true );
@@ -62,7 +60,7 @@ namespace mongo {
}
{
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
ASSERT_EQUALS( 2, sorted->numEntries( opCtx.get() ) );
long long x = 0;
@@ -72,11 +70,11 @@ namespace mongo {
}
TEST( SortedDataInterface, InsertWithDups2 ) {
- scoped_ptr<HarnessHelper> harnessHelper( newHarnessHelper() );
- scoped_ptr<SortedDataInterface> sorted( harnessHelper->newSortedDataInterface( false ) );
+ const std::unique_ptr<HarnessHelper> harnessHelper( newHarnessHelper() );
+ const std::unique_ptr<SortedDataInterface> sorted( harnessHelper->newSortedDataInterface( false ) );
{
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
{
WriteUnitOfWork uow( opCtx.get() );
sorted->insert( opCtx.get(), BSON( "" << 1 ), RecordId( 5, 18 ), true );
@@ -85,7 +83,7 @@ namespace mongo {
}
{
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
{
WriteUnitOfWork uow( opCtx.get() );
sorted->insert( opCtx.get(), BSON( "" << 1 ), RecordId( 5, 20 ), true );
@@ -94,17 +92,17 @@ namespace mongo {
}
{
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
ASSERT_EQUALS( 2, sorted->numEntries( opCtx.get() ) );
}
}
TEST( SortedDataInterface, InsertWithDups3AndRollback ) {
- scoped_ptr<HarnessHelper> harnessHelper( newHarnessHelper() );
- scoped_ptr<SortedDataInterface> sorted( harnessHelper->newSortedDataInterface( false ) );
+ const std::unique_ptr<HarnessHelper> harnessHelper( newHarnessHelper() );
+ const std::unique_ptr<SortedDataInterface> sorted( harnessHelper->newSortedDataInterface( false ) );
{
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
{
WriteUnitOfWork uow( opCtx.get() );
sorted->insert( opCtx.get(), BSON( "" << 1 ), RecordId( 5, 18 ), true );
@@ -113,7 +111,7 @@ namespace mongo {
}
{
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
{
WriteUnitOfWork uow( opCtx.get() );
sorted->insert( opCtx.get(), BSON( "" << 1 ), RecordId( 5, 20 ), true );
@@ -122,17 +120,17 @@ namespace mongo {
}
{
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
ASSERT_EQUALS( 1, sorted->numEntries( opCtx.get() ) );
}
}
TEST( SortedDataInterface, InsertNoDups1 ) {
- scoped_ptr<HarnessHelper> harnessHelper( newHarnessHelper() );
- scoped_ptr<SortedDataInterface> sorted( harnessHelper->newSortedDataInterface( true ) );
+ const std::unique_ptr<HarnessHelper> harnessHelper( newHarnessHelper() );
+ const std::unique_ptr<SortedDataInterface> sorted( harnessHelper->newSortedDataInterface( true ) );
{
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
{
WriteUnitOfWork uow( opCtx.get() );
sorted->insert( opCtx.get(), BSON( "" << 1 ), RecordId( 5, 18 ), false );
@@ -141,7 +139,7 @@ namespace mongo {
}
{
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
{
WriteUnitOfWork uow( opCtx.get() );
sorted->insert( opCtx.get(), BSON( "" << 2 ), RecordId( 5, 20 ), false );
@@ -150,18 +148,18 @@ namespace mongo {
}
{
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
ASSERT_EQUALS( 2, sorted->numEntries( opCtx.get() ) );
}
}
TEST( SortedDataInterface, InsertNoDups2 ) {
- scoped_ptr<HarnessHelper> harnessHelper( newHarnessHelper() );
- scoped_ptr<SortedDataInterface> sorted( harnessHelper->newSortedDataInterface( true ) );
+ const std::unique_ptr<HarnessHelper> harnessHelper( newHarnessHelper() );
+ const std::unique_ptr<SortedDataInterface> sorted( harnessHelper->newSortedDataInterface( true ) );
{
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
{
WriteUnitOfWork uow( opCtx.get() );
sorted->insert( opCtx.get(), BSON( "" << 1 ), RecordId( 5, 2 ), false );
@@ -170,7 +168,7 @@ namespace mongo {
}
{
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
{
WriteUnitOfWork uow( opCtx.get() );
sorted->insert( opCtx.get(), BSON( "" << 1 ), RecordId( 5, 4 ), false );
@@ -179,18 +177,18 @@ namespace mongo {
}
{
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
ASSERT_EQUALS( 1, sorted->numEntries( opCtx.get() ) );
}
}
TEST( SortedDataInterface, Unindex1 ) {
- scoped_ptr<HarnessHelper> harnessHelper( newHarnessHelper() );
- scoped_ptr<SortedDataInterface> sorted( harnessHelper->newSortedDataInterface( false ) );
+ const std::unique_ptr<HarnessHelper> harnessHelper( newHarnessHelper() );
+ const std::unique_ptr<SortedDataInterface> sorted( harnessHelper->newSortedDataInterface( false ) );
{
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
{
WriteUnitOfWork uow( opCtx.get() );
sorted->insert( opCtx.get(), BSON( "" << 1 ), RecordId( 5, 18 ), true );
@@ -199,12 +197,12 @@ namespace mongo {
}
{
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
ASSERT_EQUALS( 1, sorted->numEntries( opCtx.get() ) );
}
{
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
{
WriteUnitOfWork uow( opCtx.get() );
sorted->unindex( opCtx.get(), BSON( "" << 1 ), RecordId( 5, 20 ), true );
@@ -214,12 +212,12 @@ namespace mongo {
}
{
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
ASSERT_EQUALS( 1, sorted->numEntries( opCtx.get() ) );
}
{
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
{
WriteUnitOfWork uow( opCtx.get() );
sorted->unindex( opCtx.get(), BSON( "" << 2 ), RecordId( 5, 18 ), true );
@@ -229,13 +227,13 @@ namespace mongo {
}
{
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
ASSERT_EQUALS( 1, sorted->numEntries( opCtx.get() ) );
}
{
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
{
WriteUnitOfWork uow( opCtx.get() );
sorted->unindex( opCtx.get(), BSON( "" << 1 ), RecordId( 5, 18 ), true );
@@ -245,18 +243,18 @@ namespace mongo {
}
{
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
ASSERT( sorted->isEmpty( opCtx.get() ) );
}
}
TEST( SortedDataInterface, Unindex2Rollback ) {
- scoped_ptr<HarnessHelper> harnessHelper( newHarnessHelper() );
- scoped_ptr<SortedDataInterface> sorted( harnessHelper->newSortedDataInterface( false ) );
+ const std::unique_ptr<HarnessHelper> harnessHelper( newHarnessHelper() );
+ const std::unique_ptr<SortedDataInterface> sorted( harnessHelper->newSortedDataInterface( false ) );
{
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
{
WriteUnitOfWork uow( opCtx.get() );
sorted->insert( opCtx.get(), BSON( "" << 1 ), RecordId( 5, 18 ), true );
@@ -265,12 +263,12 @@ namespace mongo {
}
{
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
ASSERT_EQUALS( 1, sorted->numEntries( opCtx.get() ) );
}
{
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
{
WriteUnitOfWork uow( opCtx.get() );
sorted->unindex( opCtx.get(), BSON( "" << 1 ), RecordId( 5, 18 ), true );
@@ -280,7 +278,7 @@ namespace mongo {
}
{
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
ASSERT_EQUALS( 1, sorted->numEntries( opCtx.get() ) );
}
@@ -288,12 +286,12 @@ namespace mongo {
TEST( SortedDataInterface, CursorIterate1 ) {
- scoped_ptr<HarnessHelper> harnessHelper( newHarnessHelper() );
- scoped_ptr<SortedDataInterface> sorted( harnessHelper->newSortedDataInterface( false ) );
+ const std::unique_ptr<HarnessHelper> harnessHelper( newHarnessHelper() );
+ const std::unique_ptr<SortedDataInterface> sorted( harnessHelper->newSortedDataInterface( false ) );
int N = 5;
for ( int i = 0; i < N; i++ ) {
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
{
WriteUnitOfWork uow( opCtx.get() );
ASSERT_OK( sorted->insert( opCtx.get(), BSON( "" << i ), RecordId( 5, i * 2 ), true ) );
@@ -302,16 +300,12 @@ namespace mongo {
}
{
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
- scoped_ptr<SortedDataInterface::Cursor> cursor( sorted->newCursor( opCtx.get(), 1 ) );
- cursor->locate( BSONObj(), RecordId::min() );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<SortedDataInterface::Cursor> cursor( sorted->newCursor(opCtx.get()) );
int n = 0;
- while ( !cursor->isEOF() ) {
- RecordId loc = cursor->getRecordId();
- ASSERT_EQUALS( RecordId(5, n * 2), loc );
- ASSERT_EQUALS( BSON( "" << n ), cursor->getKey() );
+ for (auto entry = cursor->seek(BSONObj(), true); entry; entry = cursor->next()) {
+ ASSERT_EQ(entry, IndexKeyEntry(BSON("" << n), RecordId(5, n * 2)));
n++;
- cursor->advance();
}
ASSERT_EQUALS( N, n );
}
@@ -320,12 +314,12 @@ namespace mongo {
}
TEST( SortedDataInterface, CursorIterate1WithSaveRestore ) {
- scoped_ptr<HarnessHelper> harnessHelper( newHarnessHelper() );
- scoped_ptr<SortedDataInterface> sorted( harnessHelper->newSortedDataInterface( false ) );
+ const std::unique_ptr<HarnessHelper> harnessHelper( newHarnessHelper() );
+ const std::unique_ptr<SortedDataInterface> sorted( harnessHelper->newSortedDataInterface( false ) );
int N = 5;
for ( int i = 0; i < N; i++ ) {
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
{
WriteUnitOfWork uow( opCtx.get() );
sorted->insert( opCtx.get(), BSON( "" << i ), RecordId( 5, i * 2 ), true );
@@ -334,18 +328,14 @@ namespace mongo {
}
{
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
- scoped_ptr<SortedDataInterface::Cursor> cursor( sorted->newCursor( opCtx.get(), 1 ) );
- cursor->locate( BSONObj(), RecordId::min() );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<SortedDataInterface::Cursor> cursor( sorted->newCursor(opCtx.get()) );
int n = 0;
- while ( !cursor->isEOF() ) {
- RecordId loc = cursor->getRecordId();
- ASSERT_EQUALS( RecordId(5, n * 2), loc );
- ASSERT_EQUALS( BSON( "" << n ), cursor->getKey() );
+ for (auto entry = cursor->seek(BSONObj(), true); entry; entry = cursor->next()) {
+ ASSERT_EQ(entry, IndexKeyEntry(BSON("" << n), RecordId(5, n * 2)));
n++;
- cursor->advance();
- cursor->savePosition();
- cursor->restorePosition( opCtx.get() );
+ cursor->savePositioned();
+ cursor->restore( opCtx.get() );
}
ASSERT_EQUALS( N, n );
}
@@ -353,13 +343,13 @@ namespace mongo {
}
- TEST( SortedDataInterface, CursorIterate2WithSaveRestore ) {
- scoped_ptr<HarnessHelper> harnessHelper( newHarnessHelper() );
- scoped_ptr<SortedDataInterface> sorted( harnessHelper->newSortedDataInterface( false ) );
+ TEST( SortedDataInterface, CursorIterateAllDupKeysWithSaveRestore ) {
+ const std::unique_ptr<HarnessHelper> harnessHelper( newHarnessHelper() );
+ const std::unique_ptr<SortedDataInterface> sorted( harnessHelper->newSortedDataInterface( false ) );
int N = 5;
for ( int i = 0; i < N; i++ ) {
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
{
WriteUnitOfWork uow( opCtx.get() );
sorted->insert( opCtx.get(), BSON( "" << 5 ), RecordId( 5, i * 2 ), true );
@@ -368,17 +358,14 @@ namespace mongo {
}
{
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
- scoped_ptr<SortedDataInterface::Cursor> cursor( sorted->newCursor( opCtx.get(), 1 ) );
- cursor->locate( BSONObj(), RecordId::min() );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<SortedDataInterface::Cursor> cursor( sorted->newCursor(opCtx.get()) );
int n = 0;
- while ( !cursor->isEOF() ) {
- RecordId loc = cursor->getRecordId();
- ASSERT_EQUALS( RecordId(5, n * 2), loc );
+ for (auto entry = cursor->seek(BSONObj(), true); entry; entry = cursor->next()) {
+ ASSERT_EQ(entry, IndexKeyEntry(BSON("" << 5), RecordId(5, n * 2)));
n++;
- cursor->advance();
- cursor->savePosition();
- cursor->restorePosition( opCtx.get() );
+ cursor->savePositioned();
+ cursor->restore( opCtx.get() );
}
ASSERT_EQUALS( N, n );
}
@@ -387,20 +374,20 @@ namespace mongo {
TEST( SortedDataInterface, Locate1 ) {
- scoped_ptr<HarnessHelper> harnessHelper( newHarnessHelper() );
- scoped_ptr<SortedDataInterface> sorted( harnessHelper->newSortedDataInterface( false ) );
+ const std::unique_ptr<HarnessHelper> harnessHelper( newHarnessHelper() );
+ const std::unique_ptr<SortedDataInterface> sorted( harnessHelper->newSortedDataInterface( false ) );
BSONObj key = BSON( "" << 1 );
RecordId loc( 5, 16 );
{
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
- scoped_ptr<SortedDataInterface::Cursor> cursor( sorted->newCursor( opCtx.get(), 1 ) );
- ASSERT( !cursor->locate( key, loc ) );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<SortedDataInterface::Cursor> cursor( sorted->newCursor(opCtx.get()) );
+ ASSERT( !cursor->seek( key, true ) );
}
{
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
{
WriteUnitOfWork uow( opCtx.get() );
Status res = sorted->insert( opCtx.get(), key, loc, true );
@@ -410,20 +397,18 @@ namespace mongo {
}
{
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
- scoped_ptr<SortedDataInterface::Cursor> cursor( sorted->newCursor( opCtx.get(), 1 ) );
- ASSERT( cursor->locate( key, loc ) );
- ASSERT_EQUALS( key, cursor->getKey() );
- ASSERT_EQUALS( loc, cursor->getRecordId() );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<SortedDataInterface::Cursor> cursor( sorted->newCursor(opCtx.get()) );
+ ASSERT_EQ(cursor->seek(key, true), IndexKeyEntry(key, loc));
}
}
TEST( SortedDataInterface, Locate2 ) {
- scoped_ptr<HarnessHelper> harnessHelper( newHarnessHelper() );
- scoped_ptr<SortedDataInterface> sorted( harnessHelper->newSortedDataInterface( false ) );
+ const std::unique_ptr<HarnessHelper> harnessHelper( newHarnessHelper() );
+ const std::unique_ptr<SortedDataInterface> sorted( harnessHelper->newSortedDataInterface( false ) );
{
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
{
WriteUnitOfWork uow( opCtx.get() );
@@ -435,28 +420,22 @@ namespace mongo {
}
{
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
- scoped_ptr<SortedDataInterface::Cursor> cursor( sorted->newCursor( opCtx.get(), 1 ) );
- ASSERT( !cursor->locate( BSON( "a" << 2 ), RecordId::min() ) );
- ASSERT( !cursor->isEOF() );
- ASSERT_EQUALS( BSON( "" << 2 ), cursor->getKey() );
- ASSERT_EQUALS( RecordId(1,4), cursor->getRecordId() );
-
- cursor->advance();
- ASSERT_EQUALS( BSON( "" << 3 ), cursor->getKey() );
- ASSERT_EQUALS( RecordId(1,6), cursor->getRecordId() );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<SortedDataInterface::Cursor> cursor( sorted->newCursor(opCtx.get()) );
+ ASSERT_EQ(cursor->seek(BSON("a" << 2), true),
+ IndexKeyEntry(BSON("" << 2), RecordId(1, 4)));
- cursor->advance();
- ASSERT( cursor->isEOF() );
+ ASSERT_EQ(cursor->next(), IndexKeyEntry(BSON("" << 3), RecordId(1, 6)));
+ ASSERT_EQ(cursor->next(), boost::none);
}
}
TEST( SortedDataInterface, Locate2Empty ) {
- scoped_ptr<HarnessHelper> harnessHelper( newHarnessHelper() );
- scoped_ptr<SortedDataInterface> sorted( harnessHelper->newSortedDataInterface( false ) );
+ const std::unique_ptr<HarnessHelper> harnessHelper( newHarnessHelper() );
+ const std::unique_ptr<SortedDataInterface> sorted( harnessHelper->newSortedDataInterface( false ) );
{
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
{
WriteUnitOfWork uow( opCtx.get() );
@@ -468,88 +447,69 @@ namespace mongo {
}
{
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
- scoped_ptr<SortedDataInterface::Cursor> cursor( sorted->newCursor( opCtx.get(), 1 ) );
- ASSERT( !cursor->locate( BSONObj(), RecordId::min() ) );
- ASSERT( !cursor->isEOF() );
- ASSERT_EQUALS( BSON( "" << 1 ), cursor->getKey() );
- ASSERT_EQUALS( RecordId(1,2), cursor->getRecordId() );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<SortedDataInterface::Cursor> cursor( sorted->newCursor(opCtx.get()) );
+ ASSERT_EQ(cursor->seek(BSONObj(), true), IndexKeyEntry(BSON("" << 1), RecordId(1, 2)));
}
{
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
- scoped_ptr<SortedDataInterface::Cursor> cursor( sorted->newCursor( opCtx.get(), -1 ) );
- ASSERT( !cursor->locate( BSONObj(), RecordId::min() ) );
- ASSERT( cursor->isEOF() );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<SortedDataInterface::Cursor> cursor( sorted->newCursor(opCtx.get(), false) );
+ ASSERT_EQ(cursor->seek(BSONObj(), false), boost::none);
}
}
TEST( SortedDataInterface, Locate3Descending ) {
- scoped_ptr<HarnessHelper> harnessHelper( newHarnessHelper() );
- scoped_ptr<SortedDataInterface> sorted( harnessHelper->newSortedDataInterface( false ) );
+ const std::unique_ptr<HarnessHelper> harnessHelper( newHarnessHelper() );
+ const std::unique_ptr<SortedDataInterface> sorted( harnessHelper->newSortedDataInterface( false ) );
+
+ auto buildEntry = [](int i) { return IndexKeyEntry(BSON("" << i), RecordId(1, i*2)); };
{
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
for ( int i = 0; i < 10; i++ ) {
if ( i == 6 )
continue;
WriteUnitOfWork uow( opCtx.get() );
- ASSERT_OK( sorted->insert( opCtx.get(), BSON( "" << i ), RecordId(1,i*2), true ) );
+ auto entry = buildEntry(i);
+ ASSERT_OK( sorted->insert( opCtx.get(), entry.key, entry.loc, true ) );
uow.commit();
}
}
- scoped_ptr<OperationContext> opCtx(harnessHelper->newOperationContext());
- scoped_ptr<SortedDataInterface::Cursor> cursor( sorted->newCursor( opCtx.get(), 1 ) );
- ASSERT( !cursor->locate( BSON( "" << 5 ), RecordId::min() ) );
- ASSERT( !cursor->isEOF() );
- ASSERT_EQUALS( BSON( "" << 5 ), cursor->getKey() );
- cursor->advance();
- ASSERT_EQUALS( BSON( "" << 7 ), cursor->getKey() );
-
- cursor.reset( sorted->newCursor( opCtx.get(), -1 ) );
- ASSERT( !cursor->locate( BSON( "" << 5 ), RecordId::min() ) );
- ASSERT( !cursor->isEOF() );
- ASSERT_EQUALS( BSON( "" << 4 ), cursor->getKey() );
-
- cursor.reset( sorted->newCursor( opCtx.get(), -1 ) );
- ASSERT( !cursor->locate( BSON( "" << 5 ), RecordId::max() ) );
- ASSERT( !cursor->isEOF() );
- ASSERT_EQUALS( BSON( "" << 5 ), cursor->getKey() );
- cursor->advance();
- ASSERT_EQUALS( BSON( "" << 4 ), cursor->getKey() );
-
- cursor.reset( sorted->newCursor( opCtx.get(), -1 ) );
- ASSERT( !cursor->locate( BSON( "" << 5 ), RecordId::min() ) );
- ASSERT( !cursor->isEOF() );
- ASSERT_EQUALS( BSON( "" << 4 ), cursor->getKey() );
- cursor->advance();
- ASSERT_EQUALS( BSON( "" << 3 ), cursor->getKey() );
-
- cursor.reset( sorted->newCursor( opCtx.get(), -1 ) );
- cursor->locate( BSON( "" << 6 ), RecordId::max() );
- ASSERT( !cursor->isEOF() );
- ASSERT_EQUALS( BSON( "" << 5 ), cursor->getKey() );
- cursor->advance();
- ASSERT_EQUALS( BSON( "" << 4 ), cursor->getKey() );
-
- cursor.reset( sorted->newCursor( opCtx.get(), -1 ) );
- cursor->locate( BSON( "" << 500 ), RecordId::max() );
- ASSERT( !cursor->isEOF() );
- ASSERT_EQUALS( BSON( "" << 9 ), cursor->getKey() );
- cursor->advance();
- ASSERT_EQUALS( BSON( "" << 8 ), cursor->getKey() );
+ const std::unique_ptr<OperationContext> opCtx(harnessHelper->newOperationContext());
+ std::unique_ptr<SortedDataInterface::Cursor> cursor(sorted->newCursor(opCtx.get(), true));
+ ASSERT_EQ(cursor->seek(BSON("" << 5), true), buildEntry(5));
+ ASSERT_EQ(cursor->next(), buildEntry(7));
+
+ cursor = sorted->newCursor(opCtx.get(), /*forward*/false);
+ ASSERT_EQ(cursor->seek(BSON("" << 5), /*inclusive*/false), buildEntry(4));
+
+ cursor = sorted->newCursor(opCtx.get(), /*forward*/false);
+ ASSERT_EQ(cursor->seek(BSON("" << 5), /*inclusive*/true), buildEntry(5));
+ ASSERT_EQ(cursor->next(), buildEntry(4));
+
+ cursor = sorted->newCursor(opCtx.get(), /*forward*/false);
+ ASSERT_EQ(cursor->seek(BSON("" << 5), /*inclusive*/false), buildEntry(4));
+ ASSERT_EQ(cursor->next(), buildEntry(3));
+ cursor = sorted->newCursor(opCtx.get(), /*forward*/false);
+ ASSERT_EQ(cursor->seek(BSON("" << 6), /*inclusive*/true), buildEntry(5));
+ ASSERT_EQ(cursor->next(), buildEntry(4));
+
+ cursor = sorted->newCursor(opCtx.get(), /*forward*/false);
+ ASSERT_EQ(cursor->seek(BSON("" << 500), /*inclusive*/true), buildEntry(9));
+ ASSERT_EQ(cursor->next(), buildEntry(8));
}
TEST( SortedDataInterface, Locate4 ) {
- scoped_ptr<HarnessHelper> harnessHelper( newHarnessHelper() );
- scoped_ptr<SortedDataInterface> sorted( harnessHelper->newSortedDataInterface( false ) );
+ const std::unique_ptr<HarnessHelper> harnessHelper( newHarnessHelper() );
+ const std::unique_ptr<SortedDataInterface> sorted( harnessHelper->newSortedDataInterface( false ) );
{
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
{
WriteUnitOfWork uow( opCtx.get() );
@@ -562,41 +522,26 @@ namespace mongo {
}
{
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
- scoped_ptr<SortedDataInterface::Cursor> cursor( sorted->newCursor( opCtx.get(), 1 ) );
- ASSERT( !cursor->locate( BSON( "a" << 1 ), RecordId::min() ) );
- ASSERT( !cursor->isEOF() );
- ASSERT_EQUALS( RecordId(1,2), cursor->getRecordId() );
-
- cursor->advance();
- ASSERT_EQUALS( RecordId(1,4), cursor->getRecordId() );
-
- cursor->advance();
- ASSERT_EQUALS( RecordId(1,6), cursor->getRecordId() );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<SortedDataInterface::Cursor> cursor( sorted->newCursor(opCtx.get()) );
+ ASSERT_EQ(cursor->seek(BSON("a" << 1), true),
+ IndexKeyEntry(BSON("" << 1), RecordId(1, 2)));
- cursor->advance();
- ASSERT_EQUALS( RecordId(1,8), cursor->getRecordId() );
-
- cursor->advance();
- ASSERT( cursor->isEOF() );
+ ASSERT_EQ(cursor->next(), IndexKeyEntry(BSON("" << 1), RecordId(1, 4)));
+ ASSERT_EQ(cursor->next(), IndexKeyEntry(BSON("" << 1), RecordId(1, 6)));
+ ASSERT_EQ(cursor->next(), IndexKeyEntry(BSON("" << 2), RecordId(1, 8)));
+ ASSERT_EQ(cursor->next(), boost::none);
}
{
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
- scoped_ptr<SortedDataInterface::Cursor> cursor( sorted->newCursor( opCtx.get(), -1 ) );
- ASSERT( !cursor->locate( BSON( "a" << 1 ), RecordId::max() ) );
- ASSERT( !cursor->isEOF() );
- ASSERT( cursor->getDirection() == -1 );
- ASSERT_EQUALS( RecordId(1,6), cursor->getRecordId() );
-
- cursor->advance();
- ASSERT_EQUALS( RecordId(1,4), cursor->getRecordId() );
-
- cursor->advance();
- ASSERT_EQUALS( RecordId(1,2), cursor->getRecordId() );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<SortedDataInterface::Cursor> cursor( sorted->newCursor(opCtx.get(), false) );
+ ASSERT_EQ(cursor->seek(BSON("a" << 1), true),
+ IndexKeyEntry(BSON("" << 1), RecordId(1, 6)));
- cursor->advance();
- ASSERT( cursor->isEOF() );
+ ASSERT_EQ(cursor->next(), IndexKeyEntry(BSON("" << 1), RecordId(1, 4)));
+ ASSERT_EQ(cursor->next(), IndexKeyEntry(BSON("" << 1), RecordId(1, 2)));
+ ASSERT_EQ(cursor->next(), boost::none);
}
}
diff --git a/src/mongo/db/storage/sorted_data_interface_test_insert.cpp b/src/mongo/db/storage/sorted_data_interface_test_insert.cpp
index 6158aa0ed60..2aa254f2f3f 100644
--- a/src/mongo/db/storage/sorted_data_interface_test_insert.cpp
+++ b/src/mongo/db/storage/sorted_data_interface_test_insert.cpp
@@ -30,27 +30,25 @@
#include "mongo/db/storage/sorted_data_interface_test_harness.h"
-#include <boost/scoped_ptr.hpp>
+#include <memory>
#include "mongo/db/storage/sorted_data_interface.h"
#include "mongo/unittest/unittest.h"
namespace mongo {
- using boost::scoped_ptr;
-
// Insert a key and verify that the number of entries in the index equals 1.
TEST( SortedDataInterface, Insert ) {
- scoped_ptr<HarnessHelper> harnessHelper( newHarnessHelper() );
- scoped_ptr<SortedDataInterface> sorted( harnessHelper->newSortedDataInterface( false ) );
+ const std::unique_ptr<HarnessHelper> harnessHelper( newHarnessHelper() );
+ const std::unique_ptr<SortedDataInterface> sorted( harnessHelper->newSortedDataInterface( false ) );
{
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
ASSERT( sorted->isEmpty( opCtx.get() ) );
}
{
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
{
WriteUnitOfWork uow( opCtx.get() );
ASSERT_OK( sorted->insert( opCtx.get(), key1, loc1, true ) );
@@ -59,23 +57,23 @@ namespace mongo {
}
{
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
ASSERT_EQUALS( 1, sorted->numEntries( opCtx.get() ) );
}
}
// Insert a compound key and verify that the number of entries in the index equals 1.
TEST( SortedDataInterface, InsertCompoundKey ) {
- scoped_ptr<HarnessHelper> harnessHelper( newHarnessHelper() );
- scoped_ptr<SortedDataInterface> sorted( harnessHelper->newSortedDataInterface( false ) );
+ const std::unique_ptr<HarnessHelper> harnessHelper( newHarnessHelper() );
+ const std::unique_ptr<SortedDataInterface> sorted( harnessHelper->newSortedDataInterface( false ) );
{
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
ASSERT( sorted->isEmpty( opCtx.get() ) );
}
{
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
{
WriteUnitOfWork uow( opCtx.get() );
ASSERT_OK( sorted->insert( opCtx.get(), compoundKey1a, loc1, true ) );
@@ -84,7 +82,7 @@ namespace mongo {
}
{
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
ASSERT_EQUALS( 1, sorted->numEntries( opCtx.get() ) );
}
}
@@ -93,16 +91,16 @@ namespace mongo {
// number of entries in the index equals the number that were inserted, even
// when duplicates are not allowed.
TEST( SortedDataInterface, InsertSameDiskLoc ) {
- scoped_ptr<HarnessHelper> harnessHelper( newHarnessHelper() );
- scoped_ptr<SortedDataInterface> sorted( harnessHelper->newSortedDataInterface( false ) );
+ const std::unique_ptr<HarnessHelper> harnessHelper( newHarnessHelper() );
+ const std::unique_ptr<SortedDataInterface> sorted( harnessHelper->newSortedDataInterface( false ) );
{
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
ASSERT( sorted->isEmpty( opCtx.get() ) );
}
{
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
{
WriteUnitOfWork uow( opCtx.get() );
ASSERT_OK( sorted->insert( opCtx.get(), key1, loc1, true ) );
@@ -112,12 +110,12 @@ namespace mongo {
}
{
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
ASSERT_EQUALS( 2, sorted->numEntries( opCtx.get() ) );
}
{
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
{
WriteUnitOfWork uow( opCtx.get() );
ASSERT_OK( sorted->insert( opCtx.get(), key3, loc1, true ) );
@@ -126,7 +124,7 @@ namespace mongo {
}
{
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
ASSERT_EQUALS( 3, sorted->numEntries( opCtx.get() ) );
}
}
@@ -135,16 +133,16 @@ namespace mongo {
// number of entries in the index equals the number that were inserted, even
// when duplicates are allowed.
TEST( SortedDataInterface, InsertSameDiskLocWithDupsAllowed ) {
- scoped_ptr<HarnessHelper> harnessHelper( newHarnessHelper() );
- scoped_ptr<SortedDataInterface> sorted( harnessHelper->newSortedDataInterface( true ) );
+ const std::unique_ptr<HarnessHelper> harnessHelper( newHarnessHelper() );
+ const std::unique_ptr<SortedDataInterface> sorted( harnessHelper->newSortedDataInterface( true ) );
{
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
ASSERT( sorted->isEmpty( opCtx.get() ) );
}
{
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
{
WriteUnitOfWork uow( opCtx.get() );
ASSERT_OK( sorted->insert( opCtx.get(), key1, loc1, false ) );
@@ -154,12 +152,12 @@ namespace mongo {
}
{
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
ASSERT_EQUALS( 2, sorted->numEntries( opCtx.get() ) );
}
{
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
{
WriteUnitOfWork uow( opCtx.get() );
ASSERT_OK( sorted->insert( opCtx.get(), key3, loc1, true /* allow duplicates */ ) );
@@ -168,7 +166,7 @@ namespace mongo {
}
{
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
ASSERT_EQUALS( 3, sorted->numEntries( opCtx.get() ) );
}
}
@@ -176,16 +174,16 @@ namespace mongo {
// Insert the same key multiple times and verify that only 1 entry exists
// in the index when duplicates are not allowed.
TEST( SortedDataInterface, InsertSameKey ) {
- scoped_ptr<HarnessHelper> harnessHelper( newHarnessHelper() );
- scoped_ptr<SortedDataInterface> sorted( harnessHelper->newSortedDataInterface( true ) );
+ const std::unique_ptr<HarnessHelper> harnessHelper( newHarnessHelper() );
+ const std::unique_ptr<SortedDataInterface> sorted( harnessHelper->newSortedDataInterface( true ) );
{
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
ASSERT( sorted->isEmpty( opCtx.get() ) );
}
{
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
{
WriteUnitOfWork uow( opCtx.get() );
ASSERT_OK( sorted->insert( opCtx.get(), key1, loc1, false ) );
@@ -195,12 +193,12 @@ namespace mongo {
}
{
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
ASSERT_EQUALS( 1, sorted->numEntries( opCtx.get() ) );
}
{
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
{
WriteUnitOfWork uow( opCtx.get() );
ASSERT_NOT_OK( sorted->insert( opCtx.get(), key1, loc2, false ) );
@@ -209,7 +207,7 @@ namespace mongo {
}
{
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
ASSERT_EQUALS( 1, sorted->numEntries( opCtx.get() ) );
}
}
@@ -222,16 +220,16 @@ namespace {
// removing all but one loc each time and verifying the correct loc remains.
void _testInsertSameKeyWithDupsAllowed(const RecordId locs[3]) {
for (int keeper = 0; keeper < 3; keeper++) {
- scoped_ptr<HarnessHelper> harnessHelper( newHarnessHelper() );
- scoped_ptr<SortedDataInterface> sorted( harnessHelper->newSortedDataInterface( true ) );
+ const std::unique_ptr<HarnessHelper> harnessHelper( newHarnessHelper() );
+ const std::unique_ptr<SortedDataInterface> sorted( harnessHelper->newSortedDataInterface( true ) );
{
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
ASSERT( sorted->isEmpty( opCtx.get() ) );
}
{
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
{
WriteUnitOfWork uow( opCtx.get() );
ASSERT_OK(sorted->insert(opCtx.get(), key1, locs[0], false));
@@ -242,7 +240,7 @@ namespace {
}
{
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
{
WriteUnitOfWork uow( opCtx.get() );
for (int i = 0; i < 3; i++) {
@@ -255,13 +253,11 @@ namespace {
}
{
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
ASSERT_EQUALS( 1, sorted->numEntries( opCtx.get() ) );
- scoped_ptr<SortedDataInterface::Cursor> cursor(sorted->newCursor(opCtx.get(), 1));
- cursor->locate(key1, RecordId::min());
- ASSERT_EQUALS(key1, cursor->getKey());
- ASSERT_EQUALS(locs[keeper], cursor->getRecordId());
+ const std::unique_ptr<SortedDataInterface::Cursor> cursor(sorted->newCursor(opCtx.get()));
+ ASSERT_EQ(cursor->seek(key1, true), IndexKeyEntry(key1, locs[keeper]));
}
}
}
@@ -281,16 +277,16 @@ namespace {
// Insert multiple keys and verify that the number of entries
// in the index equals the number that were inserted.
TEST( SortedDataInterface, InsertMultiple ) {
- scoped_ptr<HarnessHelper> harnessHelper( newHarnessHelper() );
- scoped_ptr<SortedDataInterface> sorted( harnessHelper->newSortedDataInterface( true ) );
+ const std::unique_ptr<HarnessHelper> harnessHelper( newHarnessHelper() );
+ const std::unique_ptr<SortedDataInterface> sorted( harnessHelper->newSortedDataInterface( true ) );
{
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
ASSERT( sorted->isEmpty( opCtx.get() ) );
}
{
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
{
WriteUnitOfWork uow( opCtx.get() );
ASSERT_OK( sorted->insert( opCtx.get(), key1, loc1, false ) );
@@ -300,12 +296,12 @@ namespace {
}
{
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
ASSERT_EQUALS( 2, sorted->numEntries( opCtx.get() ) );
}
{
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
{
WriteUnitOfWork uow( opCtx.get() );
ASSERT_OK( sorted->insert( opCtx.get(), key3, loc3, false ) );
@@ -314,7 +310,7 @@ namespace {
}
{
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
ASSERT_EQUALS( 3, sorted->numEntries( opCtx.get() ) );
}
}
@@ -322,16 +318,16 @@ namespace {
// Insert multiple compound keys and verify that the number of entries
// in the index equals the number that were inserted.
TEST( SortedDataInterface, InsertMultipleCompoundKeys ) {
- scoped_ptr<HarnessHelper> harnessHelper( newHarnessHelper() );
- scoped_ptr<SortedDataInterface> sorted( harnessHelper->newSortedDataInterface( true ) );
+ const std::unique_ptr<HarnessHelper> harnessHelper( newHarnessHelper() );
+ const std::unique_ptr<SortedDataInterface> sorted( harnessHelper->newSortedDataInterface( true ) );
{
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
ASSERT( sorted->isEmpty( opCtx.get() ) );
}
{
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
{
WriteUnitOfWork uow( opCtx.get() );
ASSERT_OK( sorted->insert( opCtx.get(), compoundKey1a, loc1, false ) );
@@ -342,12 +338,12 @@ namespace {
}
{
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
ASSERT_EQUALS( 3, sorted->numEntries( opCtx.get() ) );
}
{
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
{
WriteUnitOfWork uow( opCtx.get() );
ASSERT_OK( sorted->insert( opCtx.get(), compoundKey1c, loc4, false ) );
@@ -357,7 +353,7 @@ namespace {
}
{
- scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
ASSERT_EQUALS( 5, sorted->numEntries( opCtx.get() ) );
}
}
diff --git a/src/mongo/db/storage/wiredtiger/wiredtiger_index.cpp b/src/mongo/db/storage/wiredtiger/wiredtiger_index.cpp
index 70d9f4cd768..ce1f10cde29 100644
--- a/src/mongo/db/storage/wiredtiger/wiredtiger_index.cpp
+++ b/src/mongo/db/storage/wiredtiger/wiredtiger_index.cpp
@@ -47,12 +47,15 @@
#include "mongo/db/storage/wiredtiger/wiredtiger_session_cache.h"
#include "mongo/db/storage/wiredtiger/wiredtiger_util.h"
#include "mongo/db/storage_options.h"
+#include "mongo/stdx/memory.h"
#include "mongo/util/assert_util.h"
#include "mongo/util/fail_point.h"
#include "mongo/util/log.h"
#include "mongo/util/mongoutils/str.h"
-#if 0
+#define TRACING_ENABLED 0
+
+#if TRACING_ENABLED
#define TRACE_CURSOR log() << "WT index (" << (const void*)&_idx << ") "
#define TRACE_INDEX log() << "WT index (" << (const void*)this << ") "
#else
@@ -281,15 +284,16 @@ namespace {
if (output) *output << "valid" << true;
- boost::scoped_ptr<SortedDataInterface::Cursor> cursor(newCursor(txn, 1));
- cursor->locate( minKey, RecordId::min() );
+ auto cursor = newCursor(txn);
long long count = 0;
TRACE_INDEX << " fullValidate";
- while ( !cursor->isEOF() ) {
- TRACE_INDEX << "\t" << cursor->getKey();
- cursor->advance();
+
+ const auto requestedInfo = TRACING_ENABLED ? Cursor::kKeyAndLoc : Cursor::kJustExistance;
+ for (auto kv = cursor->seek(minKey, true, requestedInfo); kv; kv = cursor->next()) {
+ TRACE_INDEX << "\t" << kv->key << ' ' << kv->loc;
count++;
}
+
if ( numKeysOut ) {
*numKeysOut = count;
}
@@ -598,111 +602,86 @@ namespace {
: _txn(txn),
_cursor(idx.uri(), idx.instanceId(), false, txn),
_idx(idx),
- _forward(forward),
- _eof(true),
- _cursorAtEof(true) {
+ _forward(forward) {
}
- virtual int getDirection() const { return _forward ? 1 : -1; }
- virtual bool isEOF() const { return _eof; }
-
- virtual bool pointsToSamePlaceAs(const SortedDataInterface::Cursor& genOther) const {
- const WiredTigerIndexCursorBase& other =
- checked_cast<const WiredTigerIndexCursorBase&>(genOther);
-
- if ( _eof && other._eof )
- return true;
- else if ( _eof || other._eof )
- return false;
+ boost::optional<IndexKeyEntry> next(RequestedInfo parts) override {
+ // Advance on a cursor at the end is a no-op
+ if (_eof) return {};
- // First try WT_CURSOR equals(), as this should be cheap.
- int equal;
- invariantWTOK(_cursor.get()->equals(_cursor.get(), other._cursor.get(), &equal));
- if (!equal)
- return false;
+ if (_lastMoveWasRestore) {
+ // Return current position rather than advancing.
+ updatePosition();
+ }
+ else {
+ advanceWTCursor();
+ updatePosition(/*checkEndPosition*/false);
+ if (!_eof && atEndPoint()) _eof = true;
+ }
- // WT says cursors are equal, but need to double-check that the RecordIds match.
- return getRecordId() == other.getRecordId();
+ return curr(parts);
}
- virtual void advance() {
- // Advance on a cursor at the end is a no-op
- if (_eof)
+ void setEndPosition(const BSONObj& key, bool inclusive) override {
+ TRACE_CURSOR << "setEndPosition inclusive: " << inclusive << ' ' << key;
+ if (key.isEmpty()) {
+ // This means scan to end of index.
+ _endState.reset();
return;
- advanceWTCursor();
- updatePosition();
+ }
+
+ // NOTE: this uses the opposite rules as a normal seek because a forward scan should
+ // end after the key if inclusive and before if exclusive.
+ const auto discriminator = _forward == inclusive ? KeyString::kExclusiveAfter
+ : KeyString::kExclusiveBefore;
+ _endState = stdx::make_unique<EndState>();
+ _endState->query.resetToKey(stripFieldNames(key), _idx.ordering(), discriminator);
+ seekEndCursor();
}
- bool locate(const BSONObj &key, const RecordId& loc) {
+ boost::optional<IndexKeyEntry> seek(const BSONObj& key, bool inclusive,
+ RequestedInfo parts) override {
const BSONObj finalKey = stripFieldNames(key);
- fillQuery(finalKey, loc, &_query);
- bool result = _locate(_query, loc);
+ const auto discriminator = _forward == inclusive ? KeyString::kExclusiveBefore
+ : KeyString::kExclusiveAfter;
+ // By using a discriminator other than kInclusive, there is no need to distinguish
+ // unique vs non-unique key formats since both start with the key.
+ _query.resetToKey(finalKey, _idx.ordering(), discriminator);
+ seekWTCursor(_query);
updatePosition();
+ return curr(parts);
+ }
- // An explicit search at the start of the range should always return false
- if (loc == RecordId::min() || loc == RecordId::max() )
- return false;
- return result;
- }
-
- void advanceTo(const BSONObj &keyBegin,
- int keyBeginLen,
- bool afterKey,
- const vector<const BSONElement*>& keyEnd,
- const vector<bool>& keyEndInclusive) {
+ boost::optional<IndexKeyEntry> seek(const IndexSeekPoint& seekPoint,
+ RequestedInfo parts) override {
// TODO: don't go to a bson obj then to a KeyString, go straight
- BSONObj key = IndexEntryComparison::makeQueryObject(
- keyBegin, keyBeginLen,
- afterKey, keyEnd, keyEndInclusive, getDirection() );
+ BSONObj key = IndexEntryComparison::makeQueryObject(seekPoint, _forward);
- fillQuery(key, RecordId(), &_query);
- _locate(_query, RecordId());
+ // makeQueryObject handles the discriminator in the real exclusive cases.
+ const auto discriminator = _forward ? KeyString::kExclusiveBefore
+ : KeyString::kExclusiveAfter;
+ _query.resetToKey(key, _idx.ordering(), discriminator);
+ seekWTCursor(_query);
updatePosition();
+ return curr(parts);
}
- void customLocate(const BSONObj& keyBegin,
- int keyBeginLen,
- bool afterKey,
- const vector<const BSONElement*>& keyEnd,
- const vector<bool>& keyEndInclusive) {
- advanceTo(keyBegin, keyBeginLen, afterKey, keyEnd, keyEndInclusive);
- }
-
-
- BSONObj getKey() const {
- if (_eof)
- return BSONObj();
-
- if (!_keyBsonCache.isEmpty())
- return _keyBsonCache;
-
- _keyBsonCache = KeyString::toBson(_key.getBuffer(), _key.getSize(), _idx.ordering(),
- _typeBits);
-
- TRACE_INDEX << " returning key: " << _keyBsonCache;
- return _keyBsonCache;
- }
-
- RecordId getRecordId() const { return _loc; }
-
- void savePosition() {
- if (!_txn)
- return; // still saved
+ void savePositioned() override {
+ if (!_txn) return; // still saved
_savedForCheck = _txn->recoveryUnit();
- if ( !wt_keeptxnopen() && !_eof ) {
+ if (!wt_keeptxnopen()) {
try {
_cursor.reset();
+ if (_endState && _endState->cursor) _endState->cursor->reset();
}
catch (const WriteConflictException& wce) {
// Ignore since this is only called when we are about to kill our transaction
// anyway.
}
- _cursorAtEof = true;
-
// Our saved position is wherever we were when we last called updatePosition().
// Any partially completed repositions should not effect our saved position.
}
@@ -710,28 +689,120 @@ namespace {
_txn = NULL;
}
- void restorePosition( OperationContext *txn ) {
+ void saveUnpositioned() override {
+ savePositioned();
+ _eof = true;
+ }
+
+ void restore(OperationContext *txn) override {
// Update the session handle with our new operation context.
invariant( _savedForCheck == txn->recoveryUnit() );
+ _txn = txn;
- if ( !wt_keeptxnopen() && !_eof ) {
- // Ensure an active session exists, so any restored cursors will bind to it
- WiredTigerRecoveryUnit::get(txn)->getSession(txn);
-
- _locate(_key, _loc);
- updatePosition();
+ if (!wt_keeptxnopen()) {
+ seekEndCursor();
+ if (!_eof) {
+ // Ensure an active session exists, so any restored cursors will bind to it
+ WiredTigerRecoveryUnit::get(txn)->getSession(txn);
+ _lastMoveWasRestore = !seekWTCursor(_key);
+ TRACE_CURSOR << "restore _lastMoveWasRestore:" << _lastMoveWasRestore;
+ }
}
- _txn = txn;
}
protected:
- virtual bool _locate(const KeyString& query, RecordId loc) = 0;
-
- virtual void fillQuery(const BSONObj& key, RecordId loc, KeyString* query) const = 0;
-
// Called after _key has been filled in. Must not throw WriteConflictException.
virtual void updateLocAndTypeBits() = 0;
+ boost::optional<IndexKeyEntry> curr(RequestedInfo parts) const {
+ if (_eof) return {};
+
+ dassert(!atOrPastEndPointAfterSeeking());
+ dassert(!_loc.isNull());
+
+ BSONObj bson;
+ if (TRACING_ENABLED || (parts & kWantKey)) {
+ bson = KeyString::toBson(_key.getBuffer(), _key.getSize(), _idx.ordering(),
+ _typeBits);
+
+ TRACE_CURSOR << " returning " << bson << ' ' << _loc;
+ }
+
+ return {{std::move(bson), _loc}};
+ }
+
+ bool atEndPoint() const {
+ if (_cursorAtEof || !_endState || !_endState->cursor) return false;
+
+ // TODO verify that _cursor->equals is actually faster now that we are using KeyString.
+ // In particular is it fast enough to make up for overhead of maintaining an extra
+ // cursor.
+ int equal;
+ invariantWTOK(_cursor->equals(_cursor.get(), _endState->cursor->get(), &equal));
+ dassert(bool(equal) == atOrPastEndPointAfterSeeking());
+ return equal;
+ }
+
+ bool atOrPastEndPointAfterSeeking() const {
+ if (_eof) return true;
+ if (!_endState) return false;
+
+ const int cmp = _key.compare(_endState->query);
+
+ // We set up _endState->query to be in between the last in-range value and the first
+ // out-of-range value. In particular, it is constructed to never equal any legal index
+ // key.
+ dassert(cmp != 0);
+
+ if (_forward) {
+ // We may have landed after the end point.
+ return cmp > 0;
+ }
+ else {
+ // We may have landed before the end point.
+ return cmp < 0;
+ }
+ }
+
+ void seekEndCursor() {
+ if (!_endState) return;
+
+ if (!_endState->cursor) {
+ _endState->cursor.reset(new WiredTigerCursor(_idx.uri(),
+ _idx.instanceId(),
+ false,
+ _txn));
+ }
+
+ WT_CURSOR* c = _endState->cursor->get();
+ WiredTigerItem keyItem(_endState->query.getBuffer(), _endState->query.getSize());
+ c->set_key(c, keyItem.Get());
+
+ int cmp = 0;
+ int ret = WT_OP_CHECK(c->search_near(c, &cmp));
+ TRACE_CURSOR << "seekEndCursor() search_near"
+ << " fwd: " << _forward
+ << " ret:" << ret
+ << " cmp:" << cmp;
+
+ if (ret != WT_NOTFOUND) {
+ invariantWTOK(ret);
+
+ // Need to land after/before query for forward/reverse cursors
+ if ( _forward && cmp < 0) ret = WT_OP_CHECK(c->next(c));
+ if (!_forward && cmp > 0) ret = WT_OP_CHECK(c->prev(c));
+
+ TRACE_CURSOR << "seekEndCursor() ret:" << ret;
+ }
+
+ if (ret == WT_NOTFOUND) {
+ _endState->cursor.reset();
+ return;
+ }
+
+ invariantWTOK(ret);
+ }
+
void advanceWTCursor() {
WT_CURSOR *c = _cursor.get();
int ret = WT_OP_CHECK(_forward ? c->next(c) : c->prev(c));
@@ -767,35 +838,21 @@ namespace {
return true;
}
- // Make sure we land on a matching key
- if (_forward) {
- // We need to be >=
- if (cmp < 0) {
- ret = WT_OP_CHECK(c->next(c));
- }
- }
- else {
- // We need to be <=
- if (cmp > 0) {
- ret = WT_OP_CHECK(c->prev(c));
- }
- }
-
- if (ret == WT_NOTFOUND) {
- _cursorAtEof = true;
- TRACE_CURSOR << "\t eof " << ret << " _forward: " << _forward;
- }
- else {
- invariantWTOK(ret);
+ // Make sure we land on a matching key (after/before for forward/reverse).
+ if (_forward ? cmp < 0 : cmp > 0) {
+ advanceWTCursor();
}
return false;
}
- // This must be called after every successful public method that repositions the cursor. If
- // a public reposition method partially completes, the key and value should be left at their
- // original position. This is why it is not called from the internal reposition methods.
- void updatePosition() {
+ /**
+ * This must be called after moving the cursor to update our cached position. It should not
+ * be called after a restore that did not restore to original state since that does not
+ * logically move the cursor until the following call to next().
+ */
+ void updatePosition(bool checkEndPosition = true) {
+ _lastMoveWasRestore = false;
if (_cursorAtEof) {
_eof = true;
_loc = RecordId();
@@ -808,7 +865,11 @@ namespace {
WT_ITEM item;
invariantWTOK(c->get_key(c, &item));
_key.resetFromBuffer(item.data, item.size);
- _keyBsonCache = BSONObj(); // Invalidate cached BSONObj.
+
+ if (checkEndPosition && atOrPastEndPointAfterSeeking()) {
+ _eof = true;
+ return;
+ }
updateLocAndTypeBits();
}
@@ -818,50 +879,41 @@ namespace {
const WiredTigerIndex& _idx; // not owned
const bool _forward;
- // For save/restorePosition
+ // Ensures we have the same RU at restore time.
RecoveryUnit* _savedForCheck;
// These are where this cursor instance is. They are not changed in the face of a failing
- // reposition operation.
+ // next().
KeyString _key;
KeyString::TypeBits _typeBits;
RecordId _loc;
- bool _eof;
- mutable BSONObj _keyBsonCache; // if isEmpty, cache invalid and must be loaded from _key.
+ bool _eof = false;
// This differs from _eof in that it always reflects the result of the most recent call to
// reposition _cursor.
- bool _cursorAtEof;
+ bool _cursorAtEof = false;
+
+ // Used by next to decide to return current position rather than moving. Should be reset to
+ // false by any operation that moves the cursor, other than subsequent save/restore pairs.
+ bool _lastMoveWasRestore = false;
KeyString _query;
+
+ struct EndState {
+ KeyString query;
+ std::unique_ptr<WiredTigerCursor> cursor;
+ };
+ std::unique_ptr<EndState> _endState;
};
- class WiredTigerIndexStandardCursor : public WiredTigerIndexCursorBase {
+ class WiredTigerIndexStandardCursor final : public WiredTigerIndexCursorBase {
public:
WiredTigerIndexStandardCursor(const WiredTigerIndex& idx, OperationContext *txn,
bool forward)
: WiredTigerIndexCursorBase(idx, txn, forward) {
}
- virtual void fillQuery(const BSONObj& key, RecordId loc, KeyString* query) const {
- TRACE_CURSOR << " fillQuery " << key << " " << loc
- << (_forward ? " forward" : " backward");
-
- // Null cursors should start at the zero key to maintain search ordering in the
- // collator.
- // Reverse cursors should start on the last matching key.
- if (loc.isNull())
- loc = _forward ? RecordId::min() : RecordId::max();
-
- query->resetToKey(key, _idx.ordering(), loc);
- }
-
- virtual bool _locate(const KeyString& query, RecordId loc) {
- // loc already encoded in query
- return seekWTCursor(query);
- }
-
- virtual void updateLocAndTypeBits() {
+ void updateLocAndTypeBits() override {
_loc = KeyString::decodeRecordIdAtEnd(_key.getBuffer(), _key.getSize());
WT_CURSOR *c = _cursor.get();
@@ -872,25 +924,19 @@ namespace {
}
};
- class WiredTigerIndexUniqueCursor : public WiredTigerIndexCursorBase {
+ class WiredTigerIndexUniqueCursor final : public WiredTigerIndexCursorBase {
public:
WiredTigerIndexUniqueCursor(const WiredTigerIndex& idx, OperationContext *txn, bool forward)
: WiredTigerIndexCursorBase(idx, txn, forward) {
}
- virtual void fillQuery(const BSONObj& key, RecordId loc, KeyString* query) const {
- TRACE_CURSOR << " fillQuery " << key << " " << loc
- << (_forward ? " forward" : " backward");
+ void restore(OperationContext *txn) override {
+ WiredTigerIndexCursorBase::restore(txn);
- query->resetToKey(key, _idx.ordering()); // loc doesn't go in _query for unique indexes
- }
-
- virtual bool _locate(const KeyString& query, RecordId loc) {
- if (!seekWTCursor(query)) {
- // If didn't seek to exact key, start at beginning of wherever we ended up.
- return false;
- }
- dassert(!_cursorAtEof);
+ // In addition to seeking to the correct key, we also need to make sure that the loc is
+ // on the correct side of _loc.
+ if (_lastMoveWasRestore) return; // We are on a different key so no need to check loc.
+ if (_eof) return;
// If we get here we need to look at the actual RecordId for this key and make sure we
// are supposed to see it.
@@ -901,13 +947,18 @@ namespace {
BufReader br(item.data, item.size);
RecordId locInIndex = KeyString::decodeRecordId(&br);
- if ( _forward && (locInIndex < loc)) advanceWTCursor();
- if (!_forward && (locInIndex > loc)) advanceWTCursor();
+ TRACE_CURSOR << "restore"
+ << " _loc:" << _loc
+ << " locInIndex:" << locInIndex;
- return true;
+ if (locInIndex == _loc) return;
+
+ _lastMoveWasRestore = true;
+ if ( _forward && (locInIndex < _loc)) advanceWTCursor();
+ if (!_forward && (locInIndex > _loc)) advanceWTCursor();
}
- void updateLocAndTypeBits() {
+ void updateLocAndTypeBits() override {
// We assume that cursors can only ever see unique indexes in their "pristine" state,
// where no duplicates are possible. The cases where dups are allowed should hold
// sufficient locks to ensure that no cursor ever sees them.
@@ -920,10 +971,27 @@ namespace {
_typeBits.resetFromBuffer(&br);
if (!br.atEof()) {
- severe() << "Unique index cursor seeing multiple records for key " << getKey();
+ severe() << "Unique index cursor seeing multiple records for key "
+ << curr(kWantKey)->key;
fassertFailed(28608);
}
}
+
+ boost::optional<IndexKeyEntry> seekExact(const BSONObj& key, RequestedInfo parts) override {
+ _query.resetToKey(stripFieldNames(key), _idx.ordering());
+ const WiredTigerItem keyItem(_query.getBuffer(), _query.getSize());
+
+ WT_CURSOR* c = _cursor.get();
+ c->set_key(c, keyItem.Get());
+
+ // Using search rather than search_near.
+ int ret = WT_OP_CHECK(c->search(c));
+ if (ret != WT_NOTFOUND) invariantWTOK(ret);
+ _cursorAtEof = ret == WT_NOTFOUND;
+ updatePosition();
+ dassert(_eof || _key.compare(_query) == 0);
+ return curr(parts);
+ }
};
} // namespace
@@ -934,10 +1002,10 @@ namespace {
: WiredTigerIndex( ctx, uri, desc ) {
}
- SortedDataInterface::Cursor* WiredTigerIndexUnique::newCursor(OperationContext* txn,
- int direction) const {
- invariant((direction == 1) || (direction == -1));
- return new WiredTigerIndexUniqueCursor(*this, txn, direction == 1);
+ std::unique_ptr<SortedDataInterface::Cursor> WiredTigerIndexUnique::newCursor(
+ OperationContext* txn,
+ bool forward) const {
+ return stdx::make_unique<WiredTigerIndexUniqueCursor>(*this, txn, forward);
}
SortedDataBuilderInterface* WiredTigerIndexUnique::getBulkBuilder(OperationContext* txn,
@@ -1091,10 +1159,10 @@ namespace {
: WiredTigerIndex( ctx, uri, desc ) {
}
- SortedDataInterface::Cursor* WiredTigerIndexStandard::newCursor(OperationContext* txn,
- int direction) const {
- invariant((direction == 1) || (direction == -1));
- return new WiredTigerIndexStandardCursor(*this, txn, direction == 1);
+ std::unique_ptr<SortedDataInterface::Cursor> WiredTigerIndexStandard::newCursor(
+ OperationContext* txn,
+ bool forward) const {
+ return stdx::make_unique<WiredTigerIndexStandardCursor>(*this, txn, forward);
}
SortedDataBuilderInterface* WiredTigerIndexStandard::getBulkBuilder(OperationContext* txn,
diff --git a/src/mongo/db/storage/wiredtiger/wiredtiger_index.h b/src/mongo/db/storage/wiredtiger/wiredtiger_index.h
index 169c3789471..4d3c50c09f7 100644
--- a/src/mongo/db/storage/wiredtiger/wiredtiger_index.h
+++ b/src/mongo/db/storage/wiredtiger/wiredtiger_index.h
@@ -145,20 +145,23 @@ namespace mongo {
const std::string& uri,
const IndexDescriptor* desc );
- virtual SortedDataInterface::Cursor* newCursor(OperationContext* txn, int direction) const;
- SortedDataBuilderInterface* getBulkBuilder(OperationContext* txn, bool dupsAllowed);
+ std::unique_ptr<SortedDataInterface::Cursor> newCursor(OperationContext* txn,
+ bool forward) const override;
- virtual bool unique() const { return true; }
+ SortedDataBuilderInterface* getBulkBuilder(OperationContext* txn,
+ bool dupsAllowed) override;
- virtual Status _insert( WT_CURSOR* c,
- const BSONObj& key,
- const RecordId& loc,
- bool dupsAllowed );
+ bool unique() const override { return true; }
- virtual void _unindex( WT_CURSOR* c,
- const BSONObj& key,
- const RecordId& loc,
- bool dupsAllowed );
+ Status _insert(WT_CURSOR* c,
+ const BSONObj& key,
+ const RecordId& loc,
+ bool dupsAllowed) override;
+
+ void _unindex(WT_CURSOR* c,
+ const BSONObj& key,
+ const RecordId& loc,
+ bool dupsAllowed) override;
};
class WiredTigerIndexStandard : public WiredTigerIndex {
@@ -167,20 +170,23 @@ namespace mongo {
const std::string& uri,
const IndexDescriptor* desc );
- virtual SortedDataInterface::Cursor* newCursor(OperationContext* txn, int direction) const;
- SortedDataBuilderInterface* getBulkBuilder(OperationContext* txn, bool dupsAllowed);
+ std::unique_ptr<SortedDataInterface::Cursor> newCursor(OperationContext* txn,
+ bool forward) const override;
- virtual bool unique() const { return false; }
+ SortedDataBuilderInterface* getBulkBuilder(OperationContext* txn,
+ bool dupsAllowed) override;
- virtual Status _insert( WT_CURSOR* c,
- const BSONObj& key,
- const RecordId& loc,
- bool dupsAllowed );
+ bool unique() const override { return false; }
- virtual void _unindex( WT_CURSOR* c,
- const BSONObj& key,
- const RecordId& loc,
- bool dupsAllowed );
+ Status _insert(WT_CURSOR* c,
+ const BSONObj& key,
+ const RecordId& loc,
+ bool dupsAllowed) override;
+
+ void _unindex(WT_CURSOR* c,
+ const BSONObj& key,
+ const RecordId& loc,
+ bool dupsAllowed) override;
};
diff --git a/src/mongo/dbtests/query_stage_ixscan.cpp b/src/mongo/dbtests/query_stage_ixscan.cpp
index b5aadc755d2..e3f5f8877cc 100644
--- a/src/mongo/dbtests/query_stage_ixscan.cpp
+++ b/src/mongo/dbtests/query_stage_ixscan.cpp
@@ -200,10 +200,13 @@ namespace QueryStageIxscan {
insert(fromjson("{_id: 5, x: 11}"));
ixscan->restoreState(&_txn);
- // Expect EOF: we miss {'': 10} because it is inserted behind the cursor.
- ASSERT(ixscan->isEOF());
+ member = getNext(ixscan.get());
+ ASSERT_EQ(WorkingSetMember::LOC_AND_IDX, member->state);
+ ASSERT_EQ(member->keyData[0].keyData, BSON("" << 10));
+
WorkingSetID id;
ASSERT_EQ(PlanStage::IS_EOF, ixscan->work(&id));
+ ASSERT(ixscan->isEOF());
}
};
@@ -232,11 +235,13 @@ namespace QueryStageIxscan {
insert(fromjson("{_id: 4, x: 7}"));
ixscan->restoreState(&_txn);
- // Expect EOF: we miss {'': 7} because it is inserted behind the cursor, and
- // {'': 10} is not in the range (5, 10)
- ASSERT(ixscan->isEOF());
+ member = getNext(ixscan.get());
+ ASSERT_EQ(WorkingSetMember::LOC_AND_IDX, member->state);
+ ASSERT_EQ(member->keyData[0].keyData, BSON("" << 7));
+
WorkingSetID id;
ASSERT_EQ(PlanStage::IS_EOF, ixscan->work(&id));
+ ASSERT(ixscan->isEOF());
}
};
@@ -266,9 +271,9 @@ namespace QueryStageIxscan {
ixscan->restoreState(&_txn);
// Ensure that we're EOF and we don't erroneously return {'': 12}.
- ASSERT(ixscan->isEOF());
WorkingSetID id;
ASSERT_EQ(PlanStage::IS_EOF, ixscan->work(&id));
+ ASSERT(ixscan->isEOF());
}
};
@@ -299,12 +304,17 @@ namespace QueryStageIxscan {
// Save state and insert an indexed doc.
ixscan->saveState();
insert(fromjson("{_id: 4, x: 6}"));
+ insert(fromjson("{_id: 5, x: 9}"));
ixscan->restoreState(&_txn);
- // Ensure that we're EOF and we don't erroneously return {'': 6}.
- ASSERT(ixscan->isEOF());
+ // Ensure that we don't erroneously return {'': 9} or {'':3}.
+ member = getNext(ixscan.get());
+ ASSERT_EQ(WorkingSetMember::LOC_AND_IDX, member->state);
+ ASSERT_EQ(member->keyData[0].keyData, BSON("" << 6));
+
WorkingSetID id;
ASSERT_EQ(PlanStage::IS_EOF, ixscan->work(&id));
+ ASSERT(ixscan->isEOF());
}
};
diff --git a/src/mongo/dbtests/rollbacktests.cpp b/src/mongo/dbtests/rollbacktests.cpp
index 2f3180f749a..8abfe02ff43 100644
--- a/src/mongo/dbtests/rollbacktests.cpp
+++ b/src/mongo/dbtests/rollbacktests.cpp
@@ -128,22 +128,16 @@ namespace {
IndexDescriptor* desc = catalog->findIndexByName( txn, idxName, false );
if ( desc ) {
- CursorOptions cursorOptions;
- cursorOptions.direction = CursorOptions::INCREASING;
+ auto cursor = catalog->getIndex(desc)->newCursor(txn);
- IndexCursor *cursor;
- ASSERT_OK( catalog->getIndex( desc )->newCursor( txn, cursorOptions, &cursor ) );
- ASSERT_OK( cursor->seek( minKey ) );
-
- while ( !cursor->isEOF() ) {
+ for (auto kv = cursor->seek(minKey, true); kv; kv = cursor->next()) {
numEntries++;
- cursor->next();
}
- delete cursor;
}
return numEntries;
}
+
void dropIndex( OperationContext* txn, const NamespaceString& nss, const string& idxName ) {
Collection* coll = dbHolder().get( txn, nss.db() )->getCollection(nss.ns() );
IndexDescriptor* desc = coll->getIndexCatalog()->findIndexByName( txn, idxName );