summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/mongo/SConscript13
-rw-r--r--src/mongo/db/btree.cpp3
-rw-r--r--src/mongo/db/btreecursor.cpp310
-rw-r--r--src/mongo/db/btreecursor.h226
-rw-r--r--src/mongo/db/btreeposition.cpp109
-rw-r--r--src/mongo/db/btreeposition.h117
-rw-r--r--src/mongo/db/clientcursor.cpp331
-rw-r--r--src/mongo/db/clientcursor.h101
-rw-r--r--src/mongo/db/commands/mr.cpp1
-rw-r--r--src/mongo/db/commands/test_commands.cpp14
-rw-r--r--src/mongo/db/curop.cpp1
-rw-r--r--src/mongo/db/cursor.cpp190
-rw-r--r--src/mongo/db/cursor.h336
-rw-r--r--src/mongo/db/dbcommands.cpp1
-rw-r--r--src/mongo/db/dbhelpers.cpp1
-rw-r--r--src/mongo/db/dbhelpers.h1
-rw-r--r--src/mongo/db/exec/2d.h1
-rw-r--r--src/mongo/db/explain.cpp277
-rw-r--r--src/mongo/db/explain.h188
-rw-r--r--src/mongo/db/index/emulated_cursor.h266
-rw-r--r--src/mongo/db/intervalbtreecursor.cpp232
-rw-r--r--src/mongo/db/intervalbtreecursor.h163
-rw-r--r--src/mongo/db/matcher.h1
-rw-r--r--src/mongo/db/matcher_covered.cpp145
-rw-r--r--src/mongo/db/matcher_covered.h88
-rw-r--r--src/mongo/db/namespace_details.h1
-rw-r--r--src/mongo/db/ops/update.h1
-rw-r--r--src/mongo/db/ops/update_request.h11
-rw-r--r--src/mongo/db/ops/update_result.h1
-rw-r--r--src/mongo/db/pdfile.cpp80
-rw-r--r--src/mongo/db/pdfile.h4
-rw-r--r--src/mongo/db/pipeline/pipeline_d.cpp1
-rw-r--r--src/mongo/db/query_optimizer.cpp84
-rw-r--r--src/mongo/db/query_optimizer.h96
-rw-r--r--src/mongo/db/query_optimizer_internal.cpp1645
-rw-r--r--src/mongo/db/query_optimizer_internal.h822
-rw-r--r--src/mongo/db/query_plan.cpp492
-rw-r--r--src/mongo/db/query_plan.h199
-rw-r--r--src/mongo/db/query_plan_selection_policy.cpp59
-rw-r--r--src/mongo/db/query_plan_selection_policy.h110
-rw-r--r--src/mongo/db/query_plan_summary.h64
-rw-r--r--src/mongo/db/queryoptimizercursor.h81
-rw-r--r--src/mongo/db/queryoptimizercursorimpl.cpp513
-rw-r--r--src/mongo/db/queryoptimizercursorimpl.h307
-rw-r--r--src/mongo/db/querypattern.cpp117
-rw-r--r--src/mongo/db/querypattern.h118
-rw-r--r--src/mongo/db/queryutil.cpp5
-rw-r--r--src/mongo/db/queryutil.h4
-rw-r--r--src/mongo/db/repl/oplog.cpp6
-rw-r--r--src/mongo/db/repl/rs_rollback.cpp24
-rw-r--r--src/mongo/db/scanandorder.cpp138
-rw-r--r--src/mongo/db/scanandorder.h139
-rw-r--r--src/mongo/db/structure/collection_info_cache.cpp30
-rw-r--r--src/mongo/db/structure/collection_info_cache.h16
-rw-r--r--src/mongo/dbtests/btreebuildertests.cpp41
-rw-r--r--src/mongo/dbtests/btreepositiontests.cpp339
-rw-r--r--src/mongo/dbtests/btreetests.cpp1
-rw-r--r--src/mongo/dbtests/btreetests.inl8
-rw-r--r--src/mongo/dbtests/counttests.cpp1
-rw-r--r--src/mongo/dbtests/cursortests.cpp814
-rw-r--r--src/mongo/dbtests/indexupdatetests.cpp8
-rw-r--r--src/mongo/dbtests/intervalbtreecursortests.cpp672
-rw-r--r--src/mongo/dbtests/matchertests.cpp86
-rw-r--r--src/mongo/dbtests/namespacetests.cpp88
-rw-r--r--src/mongo/dbtests/pdfiletests.cpp255
-rw-r--r--src/mongo/dbtests/perf/perftest.cpp4
-rw-r--r--src/mongo/dbtests/query_stage_fetch.cpp1
-rw-r--r--src/mongo/dbtests/queryoptimizercursortests.cpp4984
-rw-r--r--src/mongo/dbtests/queryoptimizertests2.cpp829
-rw-r--r--src/mongo/dbtests/querytests.cpp1
-rw-r--r--src/mongo/dbtests/queryutiltests.cpp146
-rw-r--r--src/mongo/dbtests/repltests.cpp1
72 files changed, 69 insertions, 16494 deletions
diff --git a/src/mongo/SConscript b/src/mongo/SConscript
index 53cadc7417f..1157e56ac21 100644
--- a/src/mongo/SConscript
+++ b/src/mongo/SConscript
@@ -426,7 +426,6 @@ env.Library("coredb", [
"db/pipeline/field_path.cpp",
"db/pipeline/value.cpp",
"db/projection.cpp",
- "db/querypattern.cpp",
"db/queryutil.cpp",
"db/stats/timer_stats.cpp",
"s/shardconnection.cpp",
@@ -555,7 +554,6 @@ serverOnlyFiles = [ "db/curop.cpp",
"db/repl/oplog.cpp",
"db/prefetch.cpp",
"db/repl/write_concern.cpp",
- "db/btreecursor.cpp",
"db/index_legacy.cpp",
"db/index_selection.cpp",
"db/index/2d_access_method.cpp",
@@ -567,13 +565,10 @@ serverOnlyFiles = [ "db/curop.cpp",
"db/index/hash_access_method.cpp",
"db/index/haystack_access_method.cpp",
"db/index/s2_access_method.cpp",
- "db/intervalbtreecursor.cpp",
- "db/btreeposition.cpp",
"db/cloner.cpp",
"db/namespace_details.cpp",
"db/catalog/ondisk/namespace_index.cpp",
"db/cap.cpp",
- "db/matcher_covered.cpp",
"db/dbeval.cpp",
"db/dbhelpers.cpp",
"db/instance.cpp",
@@ -592,18 +587,10 @@ serverOnlyFiles = [ "db/curop.cpp",
"db/storage/extent_manager.cpp",
"db/storage/index_details.cpp",
"db/structure/record_store.cpp",
- "db/cursor.cpp",
- "db/query_optimizer.cpp",
- "db/query_optimizer_internal.cpp",
- "db/queryoptimizercursorimpl.cpp",
- "db/query_plan.cpp",
- "db/query_plan_selection_policy.cpp",
"db/extsort.cpp",
"db/index_builder.cpp",
"db/index_rebuilder.cpp",
"db/storage/record.cpp",
- "db/scanandorder.cpp",
- "db/explain.cpp",
"db/commands/geonear.cpp",
"db/geo/haystack.cpp",
"db/geo/s2common.cpp",
diff --git a/src/mongo/db/btree.cpp b/src/mongo/db/btree.cpp
index 24151dbfbec..5b59d292625 100644
--- a/src/mongo/db/btree.cpp
+++ b/src/mongo/db/btree.cpp
@@ -41,7 +41,6 @@
#include "mongo/db/dbhelpers.h"
#include "mongo/db/dur_commitjob.h"
#include "mongo/db/index/btree_index_cursor.h" // for aboutToDeleteBucket
-#include "mongo/db/intervalbtreecursor.h" // also for aboutToDeleteBucket
#include "mongo/db/json.h"
#include "mongo/db/kill_current_op.h"
#include "mongo/db/pdfile.h"
@@ -834,7 +833,6 @@ namespace mongo {
template< class V >
void BtreeBucket<V>::delBucket(const DiskLoc thisLoc, const IndexDetails& id) {
BtreeIndexCursor::aboutToDeleteBucket(thisLoc);
- IntervalBtreeCursor::aboutToDeleteBucket(thisLoc);
verify( !isHead() );
DiskLoc ll = this->parent;
@@ -967,7 +965,6 @@ namespace mongo {
}
BTREE(this->nextChild)->parent.writing() = this->parent;
BtreeIndexCursor::aboutToDeleteBucket(thisLoc);
- IntervalBtreeCursor::aboutToDeleteBucket(thisLoc);
deallocBucket( thisLoc, id );
}
diff --git a/src/mongo/db/btreecursor.cpp b/src/mongo/db/btreecursor.cpp
deleted file mode 100644
index 8e9ea001994..00000000000
--- a/src/mongo/db/btreecursor.cpp
+++ /dev/null
@@ -1,310 +0,0 @@
-// DEPRECATED
-/**
-* Copyright (C) 2008 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/btreecursor.h"
-
-#include "mongo/db/kill_current_op.h"
-#include "mongo/db/pdfile.h"
-#include "mongo/db/queryutil.h"
-#include "mongo/db/index/index_access_method.h"
-#include "mongo/db/index/btree_access_method.h"
-#include "mongo/db/index/catalog_hack.h"
-
-namespace mongo {
-
- BtreeCursor* BtreeCursor::make( NamespaceDetails * nsd , int idxNo , const IndexDetails& indexDetails ) {
- return new BtreeCursor( nsd , idxNo , indexDetails );
- }
-
- BtreeCursor* BtreeCursor::make( NamespaceDetails* namespaceDetails,
- const IndexDetails& id,
- const BSONObj& startKey,
- const BSONObj& endKey,
- bool endKeyInclusive,
- int direction ) {
- auto_ptr<BtreeCursor> c( make( namespaceDetails, namespaceDetails->idxNo( id ), id ) );
- c->init(startKey,endKey,endKeyInclusive,direction);
- c->initWithoutIndependentFieldRanges();
- dassert( c->_dups.size() == 0 );
- return c.release();
- }
-
- BtreeCursor* BtreeCursor::make( NamespaceDetails* namespaceDetails,
- const IndexDetails& id,
- const shared_ptr<FieldRangeVector>& bounds,
- int singleIntervalLimit,
- int direction )
- {
- auto_ptr<BtreeCursor> c( make( namespaceDetails, namespaceDetails->idxNo( id ), id ) );
- c->init(bounds,singleIntervalLimit,direction);
- return c.release();
- }
-
- BtreeCursor::BtreeCursor( NamespaceDetails* nsd, int theIndexNo, const IndexDetails& id ) :
- d( nsd ),
- idxNo( theIndexNo ),
- indexDetails( id ),
- _boundsMustMatch( true ),
- _nscanned() {
- }
-
- void BtreeCursor::_finishConstructorInit() {
- _multikey = d->isMultikey( idxNo );
- _order = indexDetails.keyPattern();
- }
-
- void BtreeCursor::init(const BSONObj& sk, const BSONObj& ek, bool endKeyInclusive,
- int direction ) {
- _finishConstructorInit();
- startKey = sk;
- endKey = ek;
- _endKeyInclusive = endKeyInclusive;
- _direction = direction;
- _independentFieldRanges = false;
- dassert( d->idxNo((IndexDetails&) indexDetails) == idxNo );
-
- _indexDescriptor.reset(CatalogHack::getDescriptor(d, idxNo));
- _indexAM.reset(CatalogHack::getBtreeIndex(_indexDescriptor.get()));
-
- IndexCursor *cursor;
- _indexAM->newCursor(&cursor);
- _indexCursor.reset(static_cast<BtreeIndexCursor*>(cursor));
-
- CursorOptions opts;
- opts.direction = _direction == 1 ? CursorOptions::INCREASING : CursorOptions::DECREASING;
- cursor->setOptions(opts);
-
- _hitEnd = false;
- }
-
- void BtreeCursor::init( const shared_ptr< FieldRangeVector > &bounds, int singleIntervalLimit, int direction ) {
- _finishConstructorInit();
- _bounds = bounds;
- verify( _bounds );
- _direction = direction;
- _endKeyInclusive = true;
- _boundsIterator.reset( new FieldRangeVectorIterator( *_bounds , singleIntervalLimit ) );
- _independentFieldRanges = true;
- dassert( d->idxNo((IndexDetails&) indexDetails) == idxNo );
- startKey = _bounds->startKey();
- _boundsIterator->advance( startKey ); // handles initialization
- _boundsIterator->prepDive();
-
- _indexDescriptor.reset(CatalogHack::getDescriptor(d, idxNo));
- _indexAM.reset(CatalogHack::getBtreeIndex(_indexDescriptor.get()));
-
- IndexCursor *cursor;
- _indexAM->newCursor(&cursor);
- _indexCursor.reset(static_cast<BtreeIndexCursor*>(cursor));
-
- CursorOptions opts;
- opts.direction = _direction == 1 ? CursorOptions::INCREASING : CursorOptions::DECREASING;
- _indexCursor->setOptions(opts);
-
- _indexCursor->seek(_boundsIterator->cmp(), _boundsIterator->inc());
- _hitEnd = false;
- skipAndCheck();
- dassert( _dups.size() == 0 );
- }
-
- /** Properly destroy forward declared class members. */
- BtreeCursor::~BtreeCursor() {}
-
- DiskLoc BtreeCursor::currLoc() {
- if (!ok()) {
- return DiskLoc();
- } else {
- return _indexCursor->getValue();
- }
- }
-
- const DiskLoc BtreeCursor::getBucket() const {
- return _indexCursor->getBucket();
- }
-
- int BtreeCursor::getKeyOfs() const {
- return _indexCursor->getKeyOfs();
- }
-
- BSONObj BtreeCursor::currKey() const {
- return _indexCursor->getKey();
- }
-
- /* Since the last noteLocation(), our key may have moved around, and that old cached
- information may thus be stale and wrong (although often it is right). We check
- that here; if we have moved, we have to search back for where we were at.
-
- i.e., after operations on the index, the BtreeCursor's cached location info may
- be invalid. This function ensures validity, so you should call it before using
- the cursor if other writers have used the database since the last noteLocation
- call.
- */
- void BtreeCursor::checkLocation() {
- if (eof()) return;
- _indexCursor->restorePosition();
- _multikey = d->isMultikey(idxNo);
- }
-
- void BtreeCursor::initWithoutIndependentFieldRanges() {
- _indexCursor->seek(startKey);
- if (ok()) { _nscanned = 1; }
- checkEnd();
- }
-
- void BtreeCursor::skipAndCheck() {
- long long startNscanned = _nscanned;
- if ( !skipOutOfRangeKeysAndCheckEnd() ) {
- return;
- }
- do {
- // If nscanned is increased by more than 20 before a matching key is found, abort
- // skipping through the btree to find a matching key. This iteration cutoff
- // prevents unbounded internal iteration within BtreeCursor::init() and
- // BtreeCursor::advance() (the callers of skipAndCheck()). See SERVER-3448.
- if ( _nscanned > startNscanned + 20 ) {
- //skipUnusedKeys();
- // If iteration is aborted before a key matching _bounds is identified, the
- // cursor may be left pointing at a key that is not within bounds
- // (_bounds->matchesKey( currKey() ) may be false). Set _boundsMustMatch to
- // false accordingly.
- _boundsMustMatch = false;
- return;
- }
- } while( skipOutOfRangeKeysAndCheckEnd() );
- }
-
- bool BtreeCursor::skipOutOfRangeKeysAndCheckEnd() {
- if ( !ok() ) {
- return false;
- }
- int ret = _boundsIterator->advance( currKey() );
- if ( ret == -2 ) {
- _hitEnd = true;
- return false;
- }
- else if ( ret == -1 ) {
- ++_nscanned;
- return false;
- }
- ++_nscanned;
- advanceTo( currKey(), ret, _boundsIterator->after(), _boundsIterator->cmp(), _boundsIterator->inc() );
- return true;
- }
-
- // Return a value in the set {-1, 0, 1} to represent the sign of parameter i.
- int sgn( int i ) {
- if ( i == 0 )
- return 0;
- return i > 0 ? 1 : -1;
- }
-
- // Check if the current key is beyond endKey.
- void BtreeCursor::checkEnd() {
- if (!ok()) { return; }
-
- if ( !endKey.isEmpty() ) {
- int cmp = sgn( endKey.woCompare( currKey(), _order ) );
- if ( ( cmp != 0 && cmp != _direction ) || ( cmp == 0 && !_endKeyInclusive ) ) {
- _hitEnd = true;
- }
- }
- }
-
- bool BtreeCursor::ok() {
- return !_indexCursor->isEOF() && !_hitEnd;
- }
-
- void BtreeCursor::advanceTo( const BSONObj &keyBegin, int keyBeginLen, bool afterKey, const vector< const BSONElement * > &keyEnd, const vector< bool > &keyEndInclusive) {
- _indexCursor->skip(keyBegin, keyBeginLen, afterKey, keyEnd, keyEndInclusive);
- }
-
- bool BtreeCursor::advance() {
- // Reset this flag at the start of a new iteration.
- _boundsMustMatch = true;
-
- killCurrentOp.checkForInterrupt();
- if (!ok()) {
- return false;
- }
-
- _indexCursor->next();
-
- if ( !_independentFieldRanges ) {
- checkEnd();
- if ( ok() ) {
- ++_nscanned;
- }
- }
- else {
- skipAndCheck();
- }
-
- return ok();
- }
-
- void BtreeCursor::noteLocation() {
- if (!eof()) { _indexCursor->savePosition(); }
- }
-
- string BtreeCursor::toString() {
- string s = string("BtreeCursor ") + indexDetails.indexName();
- if ( _direction < 0 ) s += " reverse";
- if ( _bounds.get() && _bounds->size() > 1 ) s += " multi";
- return s;
- }
-
- BSONObj BtreeCursor::prettyIndexBounds() const {
- if ( !_independentFieldRanges ) {
- return BSON( "start" << prettyKey( startKey ) << "end" << prettyKey( endKey ) );
- }
- else {
- return _bounds->obj();
- }
- }
-
- bool BtreeCursor::currentMatches( MatchDetails* details ) {
- // If currKey() might not match the specified _bounds, check whether or not it does.
- if ( !_boundsMustMatch && _bounds && !_bounds->matchesKey( currKey() ) ) {
- // If the key does not match _bounds, it does not match the query.
- return false;
- }
- // Forward to the base class implementation, which may utilize a Matcher.
- return Cursor::currentMatches( details );
- }
-
- /* -------------------------- tests below -------------------------------------- */
- /* ----------------------------------------------------------------------------- */
-
- struct BtreeCursorUnitTest {
- BtreeCursorUnitTest() {
- verify( minDiskLoc.compare(maxDiskLoc) < 0 );
- }
- } btut;
-
-} // namespace mongo
diff --git a/src/mongo/db/btreecursor.h b/src/mongo/db/btreecursor.h
deleted file mode 100644
index 783a00d4a2d..00000000000
--- a/src/mongo/db/btreecursor.h
+++ /dev/null
@@ -1,226 +0,0 @@
-// DEPRECATED
-/**
- * Copyright (C) 2008 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 <set>
-#include <vector>
-
-#include "mongo/db/cursor.h"
-#include "mongo/db/diskloc.h"
-#include "mongo/db/jsobj.h"
-#include "mongo/db/namespace_details.h"
-#include "mongo/db/index/btree_index_cursor.h"
-#include "mongo/db/index/index_access_method.h"
-
-namespace mongo {
-
- class FieldRangeVector;
- class FieldRangeVectorIterator;
-
- /**
- * A Cursor class for btree iteration.
- *
- * A BtreeCursor iterates over all keys of a specified btree that are within the provided
- * btree bounds, using the specified direction of traversal.
- *
- * Notes on specifying btree bounds:
- *
- * A BtreeCursor may be initialized with the 'startKey' and 'endKey' bounds of an interval of
- * keys within a btree. A BtreeCursor may alternatively be initialized with a FieldRangeVector
- * describing a list of intervals for every field of the btree index.
- *
- * Notes on the yielding implementation:
- *
- * When an operation using a BtreeCursor yields the database mutex that locks the btree data
- * structure, the btree may be modified. When the operation regains the database mutex, the
- * BtreeCursor can relocate its position in the modified btree and continue iteration from that
- * point.
- *
- * Before the database mutex is yielded, a BtreeCursor records its position (noteLoc()). A
- * recorded btree position consists of a btree bucket, bucket key offset, and unique btree key.
- *
- * After the database mutex is regained, a BtreeCursor relocates its position (checkLoc()). To
- * relocate a unique btree key, a BtreeCursor first checks the btree key at its recorded btree
- * bucket and bucket key offset. If the key at that location does not match the recorded btree
- * key, and a preceding offset also fails to match, the recorded key (or the next existing key
- * following it) is located in the btree using binary search. If the recorded btree bucket is
- * invalidated, the initial recorded bucket check is not attempted (see SERVER-4575).
- */
- class BtreeCursor : public Cursor {
- public:
- virtual ~BtreeCursor();
-
- /** Makes an appropriate subclass depending on the index version. */
-
- static BtreeCursor* make( NamespaceDetails* namespaceDetails,
- const IndexDetails& id,
- const BSONObj& startKey,
- const BSONObj& endKey,
- bool endKeyInclusive,
- int direction );
-
- static BtreeCursor* make( NamespaceDetails* namespaceDetails,
- const IndexDetails& id,
- const shared_ptr<FieldRangeVector>& bounds,
- int singleIntervalLimit,
- int direction );
-
- virtual bool ok();
- virtual bool advance();
- virtual void noteLocation();
- virtual void checkLocation();
- virtual bool supportGetMore() { return true; }
- virtual bool supportYields() { return true; }
-
- /**
- * used for multikey index traversal to avoid sending back dups. see Matcher::matches().
- * if a multikey index traversal:
- * if loc has already been sent, returns true.
- * otherwise, marks loc as sent.
- * @return false if the loc has not been seen
- */
- virtual bool getsetdup(DiskLoc loc) {
- if( _multikey ) {
- pair<set<DiskLoc>::iterator, bool> p = _dups.insert(loc);
- return !p.second;
- }
- return false;
- }
-
- virtual bool modifiedKeys() const { return _multikey; }
- virtual bool isMultiKey() const { return _multikey; }
-
- virtual BSONObj currKey() const;
- virtual BSONObj indexKeyPattern() { return _order; }
-
- virtual DiskLoc currLoc();
- virtual DiskLoc refLoc() { return currLoc(); }
- virtual Record* _current() { return currLoc().rec(); }
- virtual BSONObj current() { return BSONObj::make(_current()); }
- virtual string toString();
-
- BSONObj prettyKey( const BSONObj& key ) const {
- return key.replaceFieldNames( indexDetails.keyPattern() ).clientReadable();
- }
-
- virtual BSONObj prettyIndexBounds() const;
-
- virtual CoveredIndexMatcher* matcher() const { return _matcher.get(); }
-
- virtual bool currentMatches( MatchDetails* details = 0 );
-
- virtual void setMatcher( shared_ptr<CoveredIndexMatcher> matcher ) { _matcher = matcher; }
-
- virtual const Projection::KeyOnly* keyFieldsOnly() const { return _keyFieldsOnly.get(); }
-
- virtual void setKeyFieldsOnly( const shared_ptr<Projection::KeyOnly>& keyFieldsOnly ) {
- _keyFieldsOnly = keyFieldsOnly;
- }
-
- virtual long long nscanned() { return _nscanned; }
-
- // XXX(hk): geo uses this for restoring state...for now.
- virtual const DiskLoc getBucket() const;
- // XXX(hk): geo uses this too. :(
- virtual int getKeyOfs() const;
-
- private:
- BtreeCursor( NamespaceDetails* nsd, int theIndexNo, const IndexDetails& idxDetails );
-
- virtual void init( const BSONObj& startKey,
- const BSONObj& endKey,
- bool endKeyInclusive,
- int direction );
-
- virtual void init( const shared_ptr<FieldRangeVector>& bounds,
- int singleIntervalLimit,
- int direction );
-
- bool skipOutOfRangeKeysAndCheckEnd();
-
- /**
- * Attempt to locate the next btree key matching _bounds. This may mean advancing to the
- * next successive key in the btree, or skipping to a new position in the btree. If an
- * internal iteration cutoff is reached before a matching key is found, then the search for
- * a matching key will be aborted, leaving the cursor pointing at a key that is not within
- * bounds.
- */
- void skipAndCheck();
-
- void checkEnd();
-
- /** set initial bucket */
- void initWithoutIndependentFieldRanges();
-
- /** if afterKey is true, we want the first key with values of the keyBegin fields greater than keyBegin */
- void advanceTo( const BSONObj& keyBegin,
- int keyBeginLen,
- bool afterKey,
- const vector<const BSONElement*>& keyEnd,
- const vector<bool>& keyEndInclusive );
-
- void _finishConstructorInit();
- static BtreeCursor* make( NamespaceDetails* nsd,
- int idxNo,
- const IndexDetails& indexDetails );
-
- // these are set in the construtor
- NamespaceDetails* const d;
- const int idxNo;
- const IndexDetails& indexDetails;
-
- // These variables are for query-level index scanning.
- // these are all set in init()
- set<DiskLoc> _dups;
- BSONObj startKey;
- BSONObj endKey;
- bool _endKeyInclusive;
- bool _multikey; // this must be updated every getmore batch in case someone added a multikey
- BSONObj _order; // this is the same as indexDetails.keyPattern()
- int _direction;
- shared_ptr<FieldRangeVector> _bounds;
- auto_ptr<FieldRangeVectorIterator> _boundsIterator;
- bool _boundsMustMatch; // If iteration is aborted before a key matching _bounds is
- // identified, the cursor may be left pointing at a key that is not
- // within bounds (_bounds->matchesKey( currKey() ) may be false).
- // _boundsMustMatch will be set to false accordingly.
- shared_ptr<CoveredIndexMatcher> _matcher;
- shared_ptr<Projection::KeyOnly> _keyFieldsOnly;
- bool _independentFieldRanges;
- long long _nscanned;
-
- // These variables are for index traversal.
- scoped_ptr<BtreeIndexCursor> _indexCursor;
- scoped_ptr<IndexAccessMethod> _indexAM;
- scoped_ptr<IndexDescriptor> _indexDescriptor;
- bool _hitEnd;
- };
-
-} // namespace mongo
diff --git a/src/mongo/db/btreeposition.cpp b/src/mongo/db/btreeposition.cpp
deleted file mode 100644
index 290396f9bab..00000000000
--- a/src/mongo/db/btreeposition.cpp
+++ /dev/null
@@ -1,109 +0,0 @@
-/**
- * Copyright (C) 2012 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/btreeposition.h"
-
-#include "mongo/db/btree.h"
-#include "mongo/db/storage/index_details.h"
-#include "mongo/db/pdfile.h"
-
-namespace mongo {
-
- std::ostream& operator<<( std::ostream& stream, const BtreeKeyLocation& loc ) {
- return stream << BSON( "bucket" << loc.bucket.toString() <<
- "index" << loc.pos ).jsonString();
- }
-
- LogicalBtreePosition::LogicalBtreePosition( const IndexDetails& indexDetails,
- Ordering ordering,
- const BtreeKeyLocation& initialLocation ) :
- _indexDetails( &indexDetails ),
- _ordering( ordering ),
- _initialLocation( initialLocation ),
- _initialLocationValid() {
- fassert( 16494, _indexDetails->version() == 1 );
- }
-
- void LogicalBtreePosition::init() {
- if ( _initialLocation.bucket.isNull() ) {
- // Abort if the initial location is not a valid bucket.
- return;
- }
-
- // Store the key and record referenced at the supplied initial location.
- BucketBasics<V1>::KeyNode keyNode =
- _initialLocation.bucket.btree<V1>()->keyNode( _initialLocation.pos );
- _key = keyNode.key.toBson().getOwned();
- _record = keyNode.recordLoc;
- _initialLocationValid = true;
- }
-
- BtreeKeyLocation LogicalBtreePosition::currentLocation() const {
- if ( _initialLocation.bucket.isNull() ) {
- // Abort if the initial location is not a valid bucket.
- return BtreeKeyLocation();
- }
-
- // If the initial location has not been invalidated ...
- if ( _initialLocationValid ) {
-
- const BtreeBucket<V1>* bucket = _initialLocation.bucket.btree<V1>();
- if ( // ... and the bucket was not marked as invalid ...
- bucket->getN() != bucket->INVALID_N_SENTINEL &&
- // ... and the initial location index is valid for the bucket ...
- _initialLocation.pos < bucket->getN() ) {
-
- BucketBasics<V1>::KeyNode keyNode = bucket->keyNode( _initialLocation.pos );
- if ( // ... and the record location equals the initial record location ...
- keyNode.recordLoc == _record &&
- // ... and the key equals the initial key ...
- keyNode.key.toBson().binaryEqual( _key ) ) {
- // ... then the initial location is the current location, so return it.
- return _initialLocation;
- }
- }
- }
-
- // Relocate the key and record location retrieved from _initialLocation.
- BtreeKeyLocation ret;
- bool found;
- ret.bucket = _indexDetails->head.btree<V1>()->locate( *_indexDetails,
- _indexDetails->head,
- _key,
- _ordering,
- ret.pos,
- found,
- _record,
- 1 // Forward direction means the next
- // ordered key will be returned if
- // the requested key is missing.
- );
- return ret;
- }
-
-} // namespace mongo
diff --git a/src/mongo/db/btreeposition.h b/src/mongo/db/btreeposition.h
deleted file mode 100644
index 21e947eaa38..00000000000
--- a/src/mongo/db/btreeposition.h
+++ /dev/null
@@ -1,117 +0,0 @@
-/**
- * Copyright (C) 2012 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 "mongo/db/diskloc.h"
-#include "mongo/db/jsobj.h"
-#include "mongo/platform/cstdint.h"
-
-namespace mongo {
-
- class IndexDetails;
-
- /**
- * Physical location of a key within a btree. Comprised of the DiskLoc address of a btree
- * bucket and the index of a key within that bucket.
- */
- struct BtreeKeyLocation {
-
- BtreeKeyLocation() :
- pos() {
- }
-
- BtreeKeyLocation( DiskLoc initialBucket, int32_t initialPos ) :
- bucket( initialBucket ),
- pos( initialPos ) {
- }
-
- bool operator==( const BtreeKeyLocation& other ) const {
- return bucket == other.bucket && pos == other.pos;
- }
-
- DiskLoc bucket; // Bucket within btree.
- int32_t pos; // Index within bucket.
- };
-
- std::ostream& operator<<( std::ostream& stream, const BtreeKeyLocation& loc );
-
- /**
- * Logical btree position independent of the physical structure of a btree. This is used to
- * track a position within a btree while the structure of the btree is changing.
- *
- * For example, a btree containing keys 'a', 'b', and 'c' might have all three keys in one
- * bucket or alternatively 'b' within a left child of 'c'. The same LogicalBtreePosition can
- * represent the position of 'b' in both cases and can retrieve the physical BtreeKeyLocation of
- * 'b' in each case. If the btree is changed so that it lacks a 'b' key, the position will
- * reference the lowest key greater than 'b'. This is desirable behavior when the logical btree
- * position is used to implement a forward direction iterator.
- *
- * The class is seeded with a BtreeKeyLocation identifying a btree key. This initial physical
- * location is cached in order to quickly check if the physical location corresponding to the
- * logical position is unchanged and can be returned as is.
- *
- * NOTE Only supports V1 indexes.
- */
- class LogicalBtreePosition {
- public:
-
- /**
- * Create a position with the @param 'indexDetails', @param 'ordering', and initial key
- * location @param 'initialLocation' specified.
- * @fasserts if 'indexDetails' is not a V1 index.
- */
- LogicalBtreePosition( const IndexDetails& indexDetails,
- Ordering ordering,
- const BtreeKeyLocation& initialLocation );
-
- /** Initialize the position by reading the key at the supplied initial location. */
- void init();
-
- /**
- * Invalidate the supplied initial location. This may be called when bucket containing the
- * supplied location is deleted.
- */
- void invalidateInitialLocation() { _initialLocationValid = false; }
-
- /**
- * Retrieve the current physical location in the btree corresponding to this logical
- * position.
- */
- BtreeKeyLocation currentLocation() const;
-
- private:
- const IndexDetails* _indexDetails;
- Ordering _ordering;
- BtreeKeyLocation _initialLocation;
- bool _initialLocationValid;
- BSONObj _key;
- DiskLoc _record;
- };
-
-} // namespace mongo
diff --git a/src/mongo/db/clientcursor.cpp b/src/mongo/db/clientcursor.cpp
index 79af41825bf..e3d2c2cdfc9 100644
--- a/src/mongo/db/clientcursor.cpp
+++ b/src/mongo/db/clientcursor.cpp
@@ -50,7 +50,6 @@
#include "mongo/db/parsed_query.h"
#include "mongo/db/repl/rs.h"
#include "mongo/db/repl/write_concern.h"
-#include "mongo/db/scanandorder.h"
#include "mongo/platform/random.h"
#include "mongo/util/processinfo.h"
#include "mongo/util/timer.h"
@@ -67,18 +66,7 @@ namespace mongo {
const NamespaceDetails* nsd,
const DiskLoc& dl ); // from s/d_logic.h
- ClientCursor::ClientCursor(int qopts, const shared_ptr<Cursor>& c, const StringData& ns,
- BSONObj query)
- : _ns(ns.toString()), _query(query), _runner(NULL), _c(c), _yieldSometimesTracker(128, 10) {
-
- _queryOptions = qopts;
- _doingDeletes = false;
- init();
- }
-
- ClientCursor::ClientCursor(Runner* runner, int qopts, const BSONObj query)
- : _yieldSometimesTracker(128, 10) {
-
+ ClientCursor::ClientCursor(Runner* runner, int qopts, const BSONObj query) {
_runner.reset(runner);
_ns = runner->ns();
_query = query;
@@ -88,8 +76,7 @@ namespace mongo {
ClientCursor::ClientCursor(const string& ns)
: _ns(ns),
- _queryOptions(QueryOption_NoCursorTimeout),
- _yieldSometimesTracker(128, 10) {
+ _queryOptions(QueryOption_NoCursorTimeout) {
init();
}
@@ -128,11 +115,6 @@ namespace mongo {
{
recursive_scoped_lock lock(ccmutex);
- if (NULL != _c.get()) {
- // Removes 'this' from bylocation map
- setLastLoc_inlock( DiskLoc() );
- }
-
clientCursorsById.erase(_cursorid);
// defensive:
@@ -202,7 +184,7 @@ namespace mongo {
// Note that a valid ClientCursor state is "no cursor no runner." This is because
// the set of active cursor IDs in ClientCursor is used as representation of query
// state. See sharding_block.h. TODO(greg,hk): Move this out.
- if (NULL == cc->c() && NULL == cc->_runner.get()) {
+ if (NULL == cc->_runner.get()) {
++it;
continue;
}
@@ -212,8 +194,6 @@ namespace mongo {
// We will only delete CCs with runners that are not actively in use. The runners that
// are actively in use are instead kill()-ed.
if (NULL != cc->_runner.get()) {
- verify(NULL == cc->c());
-
if (isDB || cc->_runner->ns() == ns) {
// If there is a pinValue >= 100, somebody is actively using the CC and we do
// not delete it. Instead we notify the holder that we killed it. The holder
@@ -228,22 +208,6 @@ namespace mongo {
}
}
}
- // Begin cursor-only DEPRECATED
- else if (cc->c()->shouldDestroyOnNSDeletion()) {
- verify(NULL == cc->_runner.get());
-
- if (isDB) {
- // already checked that db matched above
- dassert( StringData(cc->_ns).startsWith( ns ) );
- shouldDelete = true;
- }
- else {
- if ( ns == cc->_ns ) {
- shouldDelete = true;
- }
- }
- }
- // End cursor-only DEPRECATED
if (shouldDelete) {
ClientCursor* toDelete = it->second;
@@ -301,80 +265,6 @@ namespace mongo {
if (NULL == cc->_runner.get()) { continue; }
cc->_runner->invalidate(dl);
}
-
- // Begin cursor-only. Only cursors that are in ccByLoc are processed here.
- CCByLoc& bl = db->ccByLoc();
- CCByLoc::iterator j = bl.lower_bound(ByLocKey::min(dl));
- CCByLoc::iterator stop = bl.upper_bound(ByLocKey::max(dl));
- if ( j == stop )
- return;
-
- vector<ClientCursor*> toAdvance;
-
- while ( 1 ) {
- toAdvance.push_back(j->second);
- DEV verify( j->first.loc == dl );
- ++j;
- if ( j == stop )
- break;
- }
-
- if( toAdvance.size() >= 3000 ) {
- log() << "perf warning MPW101: " << toAdvance.size() << " cursors for one diskloc "
- << dl.toString()
- << ' ' << toAdvance[1000]->_ns
- << ' ' << toAdvance[2000]->_ns
- << ' ' << toAdvance[1000]->_pinValue
- << ' ' << toAdvance[2000]->_pinValue
- << ' ' << toAdvance[1000]->_pos
- << ' ' << toAdvance[2000]->_pos
- << ' ' << toAdvance[1000]->_idleAgeMillis
- << ' ' << toAdvance[2000]->_idleAgeMillis
- << ' ' << toAdvance[1000]->_doingDeletes
- << ' ' << toAdvance[2000]->_doingDeletes
- << endl;
- //wassert( toAdvance.size() < 5000 );
- }
-
- for ( vector<ClientCursor*>::iterator i = toAdvance.begin(); i != toAdvance.end(); ++i ) {
- ClientCursor* cc = *i;
- wassert(cc->_db == db);
-
- if ( cc->_doingDeletes ) continue;
-
- Cursor *c = cc->_c.get();
- if ( c->capped() ) {
- /* note we cannot advance here. if this condition occurs, writes to the oplog
- have "caught" the reader. skipping ahead, the reader would miss postentially
- important data.
- */
- delete cc;
- continue;
- }
-
- c->recoverFromYield();
- DiskLoc tmp1 = c->refLoc();
- if ( tmp1 != dl ) {
- // This might indicate a failure to call ClientCursor::prepareToYield() but it can
- // also happen during correct operation, see SERVER-2009.
- problem() << "warning: cursor loc " << tmp1 << " does not match byLoc position " << dl << " !" << endl;
- }
- else {
- c->advance();
- }
- while (!c->eof() && c->refLoc() == dl) {
- /* We don't delete at EOF because we want to return "no more results" rather than "no such cursor".
- * The loop is to handle MultiKey indexes where the deleted record is pointed to by multiple adjacent keys.
- * In that case we need to advance until we get to the next distinct record or EOF.
- * SERVER-4154
- * SERVER-5198
- * But see SERVER-5725.
- */
- c->advance();
- }
- cc->updateLocation();
- }
- // End cursor-only
}
void ClientCursor::registerRunner(Runner* runner) {
@@ -519,20 +409,6 @@ namespace mongo {
}
}
- // DEPRECATED only used by Cursor.
- void ClientCursor::storeOpForSlave( DiskLoc last ) {
- verify(NULL == _runner.get());
- if ( ! ( _queryOptions & QueryOption_OplogReplay ))
- return;
-
- if ( last.isNull() )
- return;
-
- BSONElement e = last.obj()["ts"];
- if ( e.type() == Date || e.type() == Timestamp )
- _slaveReadTill = e._opTime();
- }
-
void ClientCursor::updateSlaveLocation( CurOp& curop ) {
if ( _slaveReadTill.isNull() )
return;
@@ -715,139 +591,6 @@ namespace mongo {
return found;
}
- //
- // Yielding that is DEPRECATED. Will be removed when we use runners and they yield internally.
- //
-
- Record* ClientCursor::_recordForYield( ClientCursor::RecordNeeds need ) {
- if ( ! ok() )
- return 0;
-
- if ( need == DontNeed ) {
- return 0;
- }
- else if ( need == MaybeCovered ) {
- // TODO
- return 0;
- }
- else if ( need == WillNeed ) {
- // no-op
- }
- else {
- warning() << "don't understand RecordNeeds: " << (int)need << endl;
- return 0;
- }
-
- DiskLoc l = currLoc();
- if ( l.isNull() )
- return 0;
-
- Record * rec = l.rec();
- if ( rec->likelyInPhysicalMemory() )
- return 0;
-
- return rec;
- }
-
- void ClientCursor::updateLocation() {
- verify( _cursorid );
- _idleAgeMillis = 0;
- // Cursor-specific
- _c->prepareToYield();
- DiskLoc cl = _c->refLoc();
- if ( lastLoc() == cl ) {
- //log() << "info: lastloc==curloc " << ns << endl;
- }
- else {
- recursive_scoped_lock lock(ccmutex);
- setLastLoc_inlock(cl);
- }
- }
-
- void ClientCursor::setLastLoc_inlock(DiskLoc L) {
- verify(NULL == _runner.get());
- verify( _pos != -2 ); // defensive - see ~ClientCursor
-
- if (L == _lastLoc) { return; }
- CCByLoc& bl = _db->ccByLoc();
-
- if (!_lastLoc.isNull()) {
- bl.erase(ByLocKey(_lastLoc, _cursorid));
- }
-
- if (!L.isNull()) {
- bl[ByLocKey(L,_cursorid)] = this;
- }
-
- _lastLoc = L;
- }
-
- bool ClientCursor::yield( int micros , Record * recordToLoad ) {
- // some cursors (geo@oct2011) don't support yielding
- if (!_c->supportYields()) { return true; }
-
- YieldData data;
- prepareToYield( data );
- staticYield( micros , _ns , recordToLoad );
- return ClientCursor::recoverFromYield( data );
- }
-
- bool ClientCursor::yieldSometimes(RecordNeeds need, bool* yielded) {
- if (yielded) { *yielded = false; }
-
- if ( ! _yieldSometimesTracker.intervalHasElapsed() ) {
- Record* rec = _recordForYield( need );
- if ( rec ) {
- // yield for page fault
- if ( yielded ) {
- *yielded = true;
- }
- bool res = yield( suggestYieldMicros() , rec );
- if ( res )
- _yieldSometimesTracker.resetLastTime();
- return res;
- }
- return true;
- }
-
- int micros = suggestYieldMicros();
- if ( micros > 0 ) {
- if ( yielded ) {
- *yielded = true;
- }
- bool res = yield( micros , _recordForYield( need ) );
- if ( res )
- _yieldSometimesTracker.resetLastTime();
- return res;
- }
- return true;
- }
-
- bool ClientCursor::prepareToYield( YieldData &data ) {
- if (!_c->supportYields()) { return false; }
-
- // need to store in case 'this' gets deleted
- data._id = _cursorid;
- data._doingDeletes = _doingDeletes;
- _doingDeletes = false;
-
- updateLocation();
-
- return true;
- }
-
- bool ClientCursor::recoverFromYield( const YieldData &data ) {
- ClientCursor *cc = ClientCursor::find( data._id , false );
- if ( cc == 0 ) {
- // id was deleted
- return false;
- }
-
- cc->_doingDeletes = data._doingDeletes;
- cc->_c->recoverFromYield();
- return true;
- }
-
int ClientCursor::suggestYieldMicros() {
int writers = 0;
int readers = 0;
@@ -909,41 +652,6 @@ namespace mongo {
}
//
- // Holder methods DEPRECATED
- //
- ClientCursorHolder::ClientCursorHolder(ClientCursor *c) : _c(0), _id(INVALID_CURSOR_ID) {
- reset(c);
- }
-
- ClientCursorHolder::~ClientCursorHolder() {
- DESTRUCTOR_GUARD(reset(););
- }
-
- void ClientCursorHolder::reset(ClientCursor *c) {
- if ( c == _c )
- return;
- if ( _c ) {
- // be careful in case cursor was deleted by someone else
- ClientCursor::erase( _id );
- }
- if ( c ) {
- _c = c;
- _id = c->_cursorid;
- }
- else {
- _c = 0;
- _id = INVALID_CURSOR_ID;
- }
- }
- ClientCursor* ClientCursorHolder::get() { return _c; }
- ClientCursor * ClientCursorHolder::operator-> () { return _c; }
- const ClientCursor * ClientCursorHolder::operator-> () const { return _c; }
- void ClientCursorHolder::release() {
- _c = 0;
- _id = INVALID_CURSOR_ID;
- }
-
- //
// ClientCursorMonitor
//
@@ -1062,37 +770,4 @@ namespace mongo {
}
} cursorServerStats;
- //
- // YieldLock
- //
-
- ClientCursorYieldLock::ClientCursorYieldLock( ptr<ClientCursor> cc )
- : _canYield(cc->_c->supportYields()) {
-
- if ( _canYield ) {
- cc->prepareToYield( _data );
- _unlock.reset(new dbtempreleasecond());
- }
-
- }
-
- ClientCursorYieldLock::~ClientCursorYieldLock() {
- if ( _unlock ) {
- warning() << "ClientCursorYieldLock not closed properly" << endl;
- relock();
- }
- }
-
- bool ClientCursorYieldLock::stillOk() {
- if ( ! _canYield )
- return true;
- relock();
- return ClientCursor::recoverFromYield( _data );
- }
-
- void ClientCursorYieldLock::relock() {
- _unlock.reset();
- }
-
-
} // namespace mongo
diff --git a/src/mongo/db/clientcursor.h b/src/mongo/db/clientcursor.h
index 7d108c521e7..41414978d12 100644
--- a/src/mongo/db/clientcursor.h
+++ b/src/mongo/db/clientcursor.h
@@ -33,7 +33,6 @@
#include <boost/thread/recursive_mutex.hpp>
#include "mongo/db/cc_by_loc.h"
-#include "mongo/db/cursor.h"
#include "mongo/db/diskloc.h"
#include "mongo/db/dbhelpers.h"
#include "mongo/db/jsobj.h"
@@ -58,9 +57,6 @@ namespace mongo {
*/
class ClientCursor : private boost::noncopyable {
public:
- ClientCursor(int qopts, const shared_ptr<Cursor>& c, const StringData& ns,
- BSONObj query = BSONObj());
-
ClientCursor(Runner* runner, int qopts = 0, const BSONObj query = BSONObj());
ClientCursor(const string& ns);
@@ -248,36 +244,6 @@ namespace mongo {
*/
bool isAggCursor;
- //
- // Cursor-only DEPRECATED methods.
- //
-
- void storeOpForSlave( DiskLoc last );
-
- // Only used by ops/query.cpp, which will stop using them when queries are answered only by
- // a runner.
- const BSONObj& query() const { return _query; }
- shared_ptr<ParsedQuery> pq;
- // This one is used also by pipeline/document_source_cursor.cpp
- shared_ptr<Projection> fields; // which fields query wants returned
-
- DiskLoc lastLoc() const { return _lastLoc; }
- Cursor* c() const { return _c.get(); }
- bool ok() { return _c->ok(); }
- bool advance() { return _c->advance(); }
- BSONObj current() { return _c->current(); }
- DiskLoc currLoc() { return _c->currLoc(); }
- BSONObj currKey() const { return _c->currKey(); }
-
- bool currentIsDup() { return _c->getsetdup( _c->currLoc() ); }
- bool currentMatches() {
- if ( ! _c->matcher() )
- return true;
- return _c->matcher()->matchesCurrent( _c.get() );
- }
-
- void setDoingDeletes( bool doingDeletes ) {_doingDeletes = doingDeletes; }
-
private:
friend class ClientCursorHolder;
friend class ClientCursorPin;
@@ -376,28 +342,6 @@ namespace mongo {
// The new world: a runner.
scoped_ptr<Runner> _runner;
-
- //
- // Cursor-only private data and methods. DEPRECATED.
- //
-
- // The old world: a cursor. DEPRECATED.
- const shared_ptr<Cursor> _c;
-
- /**
- * call when cursor's location changes so that we can update the cursorsbylocation map. if
- * you are locked and internally iterating, only need to call when you are ready to
- * "unlock".
- */
- void updateLocation();
- void setLastLoc_inlock(DiskLoc);
- Record* _recordForYield( RecordNeeds need );
-
- DiskLoc _lastLoc; // use getter and setter not this (important)
- bool _doingDeletes; // when true we are the delete and aboutToDelete shouldn't manipulate us
-
- // TODO: This will be moved into the runner.
- ElapsedTracker _yieldSometimesTracker;
};
/**
@@ -420,56 +364,11 @@ namespace mongo {
CursorId _cursorid;
};
- /** Assures safe and reliable cleanup of a ClientCursor. */
- class ClientCursorHolder : boost::noncopyable {
- public:
- ClientCursorHolder( ClientCursor *c = 0 );
- ~ClientCursorHolder();
- void reset( ClientCursor *c = 0 );
- ClientCursor* get();
- operator bool() { return _c; }
- ClientCursor * operator-> ();
- const ClientCursor * operator-> () const;
- /** Release ownership of the ClientCursor. */
- void release();
- private:
- ClientCursor *_c;
- CursorId _id;
- };
-
/** thread for timing out old cursors */
class ClientCursorMonitor : public BackgroundJob {
public:
string name() const { return "ClientCursorMonitor"; }
void run();
};
-
- struct ClientCursorYieldLock : boost::noncopyable {
- explicit ClientCursorYieldLock( ptr<ClientCursor> cc );
- ~ClientCursorYieldLock();
-
- /**
- * @return if the cursor is still ok
- * if it is, we also relock
- */
- bool stillOk();
- void relock();
-
- private:
- const bool _canYield;
- ClientCursor::YieldData _data;
- scoped_ptr<dbtempreleasecond> _unlock;
- };
} // namespace mongo
-
-// ClientCursor should only be used with auto_ptr because it needs to be
-// release()ed after a yield if stillOk() returns false and these pointer types
-// do not support releasing. This will prevent them from being used accidentally
-// Instead of auto_ptr<>, which still requires some degree of manual management
-// of this, consider using ClientCursor::Holder which handles ClientCursor's
-// unusual self-deletion mechanics.
-namespace boost{
- template<> class scoped_ptr<mongo::ClientCursor> {};
- template<> class shared_ptr<mongo::ClientCursor> {};
-}
diff --git a/src/mongo/db/commands/mr.cpp b/src/mongo/db/commands/mr.cpp
index e24dc93515f..bd0328ffd51 100644
--- a/src/mongo/db/commands/mr.cpp
+++ b/src/mongo/db/commands/mr.cpp
@@ -41,7 +41,6 @@
#include "mongo/db/instance.h"
#include "mongo/db/kill_current_op.h"
#include "mongo/db/matcher.h"
-#include "mongo/db/query_optimizer.h"
#include "mongo/db/query/get_runner.h"
#include "mongo/db/query/query_planner.h"
#include "mongo/db/repl/is_master.h"
diff --git a/src/mongo/db/commands/test_commands.cpp b/src/mongo/db/commands/test_commands.cpp
index 595dcf63fb0..3b3924561bd 100644
--- a/src/mongo/db/commands/test_commands.cpp
+++ b/src/mongo/db/commands/test_commands.cpp
@@ -35,6 +35,7 @@
#include "mongo/db/index_builder.h"
#include "mongo/db/kill_current_op.h"
#include "mongo/db/pdfile.h"
+#include "mongo/db/query/internal_plans.h"
#include "mongo/db/structure/collection.h"
namespace mongo {
@@ -142,12 +143,15 @@ namespace mongo {
// inclusive range?
bool inc = cmdObj.getBoolField( "inc" );
NamespaceDetails *nsd = nsdetails( ns );
- ReverseCappedCursor c( nsd );
- massert( 13417, "captrunc collection not found or empty", c.ok() );
- for( int i = 0; i < n; ++i ) {
- massert( 13418, "captrunc invalid n", c.advance() );
+ massert( 13417, "captrunc collection not found or empty", nsd);
+
+ boost::scoped_ptr<Runner> runner(InternalPlanner::collectionScan(ns, InternalPlanner::BACKWARD));
+ DiskLoc end;
+ // We remove 'n' elements so the start is one past that
+ for( int i = 0; i < n + 1; ++i ) {
+ Runner::RunnerState state = runner->getNext(NULL, &end);
+ massert( 13418, "captrunc invalid n", Runner::RUNNER_ADVANCED == state);
}
- DiskLoc end = c.currLoc();
nsd->cappedTruncateAfter( ns.c_str(), end, inc );
return true;
}
diff --git a/src/mongo/db/curop.cpp b/src/mongo/db/curop.cpp
index 01e33330111..b7581d3c33a 100644
--- a/src/mongo/db/curop.cpp
+++ b/src/mongo/db/curop.cpp
@@ -33,6 +33,7 @@
#include "mongo/db/curop.h"
#include "mongo/db/database.h"
#include "mongo/db/kill_current_op.h"
+#include "mongo/db/matcher.h"
#include "mongo/util/fail_point_service.h"
namespace mongo {
diff --git a/src/mongo/db/cursor.cpp b/src/mongo/db/cursor.cpp
deleted file mode 100644
index 5f4131924a8..00000000000
--- a/src/mongo/db/cursor.cpp
+++ /dev/null
@@ -1,190 +0,0 @@
-/**
- * Copyright (C) 2008 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/pch.h"
-
-#include "mongo/db/curop-inl.h"
-#include "mongo/db/kill_current_op.h"
-#include "mongo/db/pdfile.h"
-
-namespace mongo {
-
- bool BasicCursor::advance() {
- killCurrentOp.checkForInterrupt();
- if ( eof() ) {
- if ( tailable_ && !last.isNull() ) {
- curr = s->next( last );
- }
- else {
- return false;
- }
- }
- else {
- last = curr;
- curr = s->next( curr );
- }
- incNscanned();
- return ok();
- }
-
- /* these will be used outside of mutexes - really functors - thus the const */
- class Forward : public AdvanceStrategy {
- virtual DiskLoc next( const DiskLoc &prev ) const {
- return prev.rec()->getNext( prev );
- }
- } _forward;
-
- class Reverse : public AdvanceStrategy {
- virtual DiskLoc next( const DiskLoc &prev ) const {
- return prev.rec()->getPrev( prev );
- }
- } _reverse;
-
- const AdvanceStrategy *forward() {
- return &_forward;
- }
- const AdvanceStrategy *reverse() {
- return &_reverse;
- }
-
- DiskLoc nextLoop( NamespaceDetails *nsd, const DiskLoc &prev ) {
- verify( nsd->capLooped() );
- DiskLoc next = forward()->next( prev );
- if ( !next.isNull() )
- return next;
- return nsd->firstRecord();
- }
-
- DiskLoc prevLoop( NamespaceDetails *nsd, const DiskLoc &curr ) {
- verify( nsd->capLooped() );
- DiskLoc prev = reverse()->next( curr );
- if ( !prev.isNull() )
- return prev;
- return nsd->lastRecord();
- }
-
- ForwardCappedCursor* ForwardCappedCursor::make( NamespaceDetails* nsd,
- const DiskLoc& startLoc ) {
- auto_ptr<ForwardCappedCursor> ret( new ForwardCappedCursor( nsd ) );
- ret->init( startLoc );
- return ret.release();
- }
-
- ForwardCappedCursor::ForwardCappedCursor( NamespaceDetails* _nsd ) :
- nsd( _nsd ) {
- }
-
- void ForwardCappedCursor::init( const DiskLoc& startLoc ) {
- if ( !nsd )
- return;
- DiskLoc start = startLoc;
- if ( start.isNull() ) {
- if ( !nsd->capLooped() )
- start = nsd->firstRecord();
- else {
- start = nsd->capExtent().ext()->firstRecord;
- if ( !start.isNull() && start == nsd->capFirstNewRecord() ) {
- start = nsd->capExtent().ext()->lastRecord;
- start = nextLoop( nsd, start );
- }
- }
- }
- curr = start;
- s = this;
- incNscanned();
- }
-
- DiskLoc ForwardCappedCursor::next( const DiskLoc &prev ) const {
- verify( nsd );
- if ( !nsd->capLooped() )
- return forward()->next( prev );
-
- DiskLoc i = prev;
- // Last record
- if ( i == nsd->capExtent().ext()->lastRecord )
- return DiskLoc();
- i = nextLoop( nsd, i );
- // If we become capFirstNewRecord from same extent, advance to next extent.
- if ( i == nsd->capFirstNewRecord() &&
- i != nsd->capExtent().ext()->firstRecord )
- i = nextLoop( nsd, nsd->capExtent().ext()->lastRecord );
- // If we have just gotten to beginning of capExtent, skip to capFirstNewRecord
- if ( i == nsd->capExtent().ext()->firstRecord )
- i = nsd->capFirstNewRecord();
- return i;
- }
-
- ReverseCappedCursor::ReverseCappedCursor( NamespaceDetails *_nsd, const DiskLoc &startLoc ) :
- nsd( _nsd ) {
- if ( !nsd )
- return;
- DiskLoc start = startLoc;
- if ( start.isNull() ) {
- if ( !nsd->capLooped() ) {
- start = nsd->lastRecord();
- }
- else {
- start = nsd->capExtent().ext()->lastRecord;
- }
- }
- curr = start;
- s = this;
- incNscanned();
- }
-
- DiskLoc ReverseCappedCursor::next( const DiskLoc &prev ) const {
- verify( nsd );
- if ( !nsd->capLooped() )
- return reverse()->next( prev );
-
- DiskLoc i = prev;
- // Last record
- if ( nsd->capFirstNewRecord() == nsd->capExtent().ext()->firstRecord ) {
- if ( i == nextLoop( nsd, nsd->capExtent().ext()->lastRecord ) ) {
- return DiskLoc();
- }
- }
- else {
- if ( i == nsd->capExtent().ext()->firstRecord ) {
- return DiskLoc();
- }
- }
- // If we are capFirstNewRecord, advance to prev extent, otherwise just get prev.
- if ( i == nsd->capFirstNewRecord() )
- i = prevLoop( nsd, nsd->capExtent().ext()->firstRecord );
- else
- i = prevLoop( nsd, i );
- // If we just became last in cap extent, advance past capFirstNewRecord
- // (We know capExtent.ext()->firstRecord != capFirstNewRecord, since would
- // have returned DiskLoc() earlier otherwise.)
- if ( i == nsd->capExtent().ext()->lastRecord )
- i = reverse()->next( nsd->capFirstNewRecord() );
-
- return i;
- }
-} // namespace mongo
diff --git a/src/mongo/db/cursor.h b/src/mongo/db/cursor.h
deleted file mode 100644
index e3cd0ede83c..00000000000
--- a/src/mongo/db/cursor.h
+++ /dev/null
@@ -1,336 +0,0 @@
-/**
-* Copyright (C) 2008 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 "mongo/pch.h"
-
-#include "mongo/db/diskloc.h"
-#include "mongo/db/jsobj.h"
-#include "mongo/db/matcher.h"
-#include "mongo/db/matcher_covered.h"
-#include "mongo/db/projection.h"
-
-namespace mongo {
-
- class NamespaceDetails;
- class Record;
- class CoveredIndexMatcher;
-
- /**
- * Query cursors, base class. This is for our internal cursors. "ClientCursor" is a separate
- * concept and is for the user's cursor.
- *
- * WARNING concurrency: the vfunctions below are called back from within a
- * ClientCursor::ccmutex. Don't cause a deadlock, you've been warned.
- *
- * Two general techniques may be used to ensure a Cursor is in a consistent state after a write.
- * - The Cursor may be advanced before the document at its current position is deleted.
- * - The Cursor may record its position and then relocate this position.
- * A particular Cursor may potentially utilize only one of the above techniques, but a client
- * that is Cursor subclass agnostic must implement a pattern handling both techniques.
- *
- * When the document at a Cursor's current position is deleted (or moved to a new location) the
- * following pattern is used:
- * DiskLoc toDelete = cursor->currLoc();
- * while( cursor->ok() && cursor->currLoc() == toDelete ) {
- * cursor->advance();
- * }
- * cursor->prepareToTouchEarlierIterate();
- * delete( toDelete );
- * cursor->recoverFromTouchingEarlierIterate();
- *
- * When a cursor yields, the following pattern is used:
- * cursor->prepareToYield();
- * while( Op theOp = nextOp() ) {
- * if ( theOp.type() == INSERT || theOp.type() == UPDATE_IN_PLACE ) {
- * theOp.run();
- * }
- * else if ( theOp.type() == DELETE ) {
- * if ( cursor->refLoc() == theOp.toDelete() ) {
- * cursor->recoverFromYield();
- * while ( cursor->ok() && cursor->refLoc() == theOp.toDelete() ) {
- * cursor->advance();
- * }
- * cursor->prepareToYield();
- * }
- * theOp.run();
- * }
- * }
- * cursor->recoverFromYield();
- *
- * The break before a getMore request is typically treated as a yield, but if a Cursor supports
- * getMore but not yield the following pattern is currently used:
- * cursor->noteLocation();
- * runOtherOps();
- * cursor->checkLocation();
- *
- * But see SERVER-5725.
- *
- * A Cursor may rely on additional callbacks not listed above to relocate its position after a
- * write.
- */
- class Cursor : boost::noncopyable {
- public:
- virtual ~Cursor() {}
- virtual bool ok() = 0;
- bool eof() { return !ok(); }
- virtual Record* _current() = 0;
- virtual BSONObj current() = 0;
- virtual DiskLoc currLoc() = 0;
- virtual bool advance() = 0; /*true=ok*/
- virtual BSONObj currKey() const { return BSONObj(); }
-
- // DiskLoc the cursor requires for continued operation. Before this
- // DiskLoc is deleted, the cursor must be incremented or destroyed.
- virtual DiskLoc refLoc() = 0;
-
- /* Implement these if you want the cursor to be "tailable" */
-
- /* Request that the cursor starts tailing after advancing past last record. */
- /* The implementation may or may not honor this request. */
- virtual void setTailable() {}
- /* indicates if tailing is enabled. */
- virtual bool tailable() {
- return false;
- }
-
- /* optional to implement. if implemented, means 'this' is a prototype */
- virtual Cursor* clone() {
- return 0;
- }
-
- virtual BSONObj indexKeyPattern() {
- return BSONObj();
- }
-
- virtual bool supportGetMore() = 0;
-
- /* called after every query block is iterated -- i.e. between getMore() blocks
- so you can note where we are, if necessary.
- */
- virtual void noteLocation() { }
-
- /* called before query getmore block is iterated */
- virtual void checkLocation() { }
-
- /**
- * Called before a document pointed at by an earlier iterate of this cursor is to be
- * modified. It is ok if the current iterate also points to the document to be modified.
- */
- virtual void prepareToTouchEarlierIterate() { noteLocation(); }
-
- /** Recover from a previous call to prepareToTouchEarlierIterate(). */
- virtual void recoverFromTouchingEarlierIterate() { checkLocation(); }
-
- virtual bool supportYields() = 0;
-
- /** Called before a ClientCursor yield. */
- virtual void prepareToYield() { noteLocation(); }
-
- /** Called after a ClientCursor yield. Recovers from a previous call to prepareToYield(). */
- virtual void recoverFromYield() { checkLocation(); }
-
- virtual string toString() { return "abstract?"; }
-
- /* used for multikey index traversal to avoid sending back dups. see Matcher::matches().
- if a multikey index traversal:
- if loc has already been sent, returns true.
- otherwise, marks loc as sent.
- */
- virtual bool getsetdup(DiskLoc loc) = 0;
-
- virtual bool isMultiKey() const = 0;
-
- virtual bool autoDedup() const { return true; }
-
- /**
- * return true if the keys in the index have been modified from the main doc
- * if you have { a : 1 , b : [ 1 , 2 ] }
- * an index on { a : 1 } would not be modified
- * an index on { b : 1 } would be since the values of the array are put in the index
- * not the array
- */
- virtual bool modifiedKeys() const = 0;
-
- virtual BSONObj prettyIndexBounds() const { return BSONArray(); }
-
- /**
- * If true, this is an unindexed cursor over a capped collection. Currently such cursors must
- * not own a delegate ClientCursor, due to the implementation of ClientCursor::aboutToDelete(). - SERVER-4563
- */
- virtual bool capped() const { return false; }
-
- virtual long long nscanned() = 0;
-
- // The implementation may return different matchers depending on the
- // position of the cursor. If matcher() is nonzero at the start,
- // matcher() should be checked each time advance() is called.
- // Implementations which generate their own matcher should return this
- // to avoid a matcher being set manually.
- // Note that the return values differ subtly here
-
- // Used when we want fast matcher lookup
- virtual CoveredIndexMatcher *matcher() const { return 0; }
-
- virtual bool currentMatches( MatchDetails *details = 0 ) {
- return !matcher() || matcher()->matchesCurrent( this, details );
- }
-
- // A convenience function for setting the value of matcher() manually
- // so it may be accessed later. Implementations which must generate
- // their own matcher() should assert here.
- virtual void setMatcher( shared_ptr< CoveredIndexMatcher > matcher ) {
- massert( 13285, "manual matcher config not allowed", false );
- }
-
- /** @return the covered index projector for the current iterate, if any. */
- virtual const Projection::KeyOnly *keyFieldsOnly() const { return 0; }
-
- /**
- * Manually set the value of keyFieldsOnly() so it may be accessed later. Implementations
- * that generate their own keyFieldsOnly() must assert.
- */
- virtual void setKeyFieldsOnly( const shared_ptr<Projection::KeyOnly> &keyFieldsOnly ) {
- massert( 16159, "manual keyFieldsOnly config not allowed", false );
- }
-
- virtual void explainDetails( BSONObjBuilder& b ) { return; }
-
- /// Should getmore handle locking for you
- virtual bool requiresLock() { return true; }
-
- /// Should this cursor be destroyed when it's namespace is deleted
- virtual bool shouldDestroyOnNSDeletion() { return true; }
- };
-
- // strategy object implementing direction of traversal.
- class AdvanceStrategy {
- public:
- virtual ~AdvanceStrategy() { }
- virtual DiskLoc next( const DiskLoc &prev ) const = 0;
- };
-
- const AdvanceStrategy *forward();
- const AdvanceStrategy *reverse();
-
- /**
- * table-scan style cursor
- *
- * A BasicCursor relies on advance() to ensure it is in a consistent state after a write. If
- * the document at a BasicCursor's current position will be deleted or relocated, the cursor
- * must first be advanced. The same is true of BasicCursor subclasses.
- */
- class BasicCursor : public Cursor {
- public:
- BasicCursor(DiskLoc dl, const AdvanceStrategy *_s = forward()) : curr(dl), s( _s ), _nscanned() {
- incNscanned();
- init();
- }
- BasicCursor(const AdvanceStrategy *_s = forward()) : s( _s ), _nscanned() {
- init();
- }
- bool ok() { return !curr.isNull(); }
- Record* _current() {
- verify( ok() );
- return curr.rec();
- }
- BSONObj current() {
- Record *r = _current();
- return BSONObj::make(r);
- }
- virtual DiskLoc currLoc() { return curr; }
- virtual DiskLoc refLoc() { return curr.isNull() ? last : curr; }
- bool advance();
- virtual string toString() { return "BasicCursor"; }
- virtual void setTailable() {
- if ( !curr.isNull() || !last.isNull() )
- tailable_ = true;
- }
- virtual bool tailable() { return tailable_; }
- virtual bool getsetdup(DiskLoc loc) { return false; }
- virtual bool isMultiKey() const { return false; }
- virtual bool modifiedKeys() const { return false; }
- virtual bool supportGetMore() { return true; }
- virtual bool supportYields() { return true; }
- virtual CoveredIndexMatcher *matcher() const { return _matcher.get(); }
- virtual void setMatcher( shared_ptr< CoveredIndexMatcher > matcher ) { _matcher = matcher; }
- virtual const Projection::KeyOnly *keyFieldsOnly() const { return _keyFieldsOnly.get(); }
- virtual void setKeyFieldsOnly( const shared_ptr<Projection::KeyOnly> &keyFieldsOnly ) {
- _keyFieldsOnly = keyFieldsOnly;
- }
- virtual long long nscanned() { return _nscanned; }
-
- protected:
- DiskLoc curr, last;
- const AdvanceStrategy *s;
- void incNscanned() { if ( !curr.isNull() ) { ++_nscanned; } }
- private:
- bool tailable_;
- shared_ptr< CoveredIndexMatcher > _matcher;
- shared_ptr<Projection::KeyOnly> _keyFieldsOnly;
- long long _nscanned;
- void init() { tailable_ = false; }
- };
-
- /* used for order { $natural: -1 } */
- class ReverseCursor : public BasicCursor {
- public:
- ReverseCursor(DiskLoc dl) : BasicCursor( dl, reverse() ) { }
- ReverseCursor() : BasicCursor( reverse() ) { }
- virtual string toString() { return "ReverseCursor"; }
- };
-
- class ForwardCappedCursor : public BasicCursor, public AdvanceStrategy {
- public:
- static ForwardCappedCursor* make( NamespaceDetails* nsd = 0,
- const DiskLoc& startLoc = DiskLoc() );
- virtual string toString() {
- return "ForwardCappedCursor";
- }
- virtual DiskLoc next( const DiskLoc &prev ) const;
- virtual bool capped() const { return true; }
- private:
- ForwardCappedCursor( NamespaceDetails* nsd );
- void init( const DiskLoc& startLoc );
- NamespaceDetails *nsd;
- };
-
- class ReverseCappedCursor : public BasicCursor, public AdvanceStrategy {
- public:
- ReverseCappedCursor( NamespaceDetails *nsd = 0, const DiskLoc &startLoc = DiskLoc() );
- virtual string toString() {
- return "ReverseCappedCursor";
- }
- virtual DiskLoc next( const DiskLoc &prev ) const;
- virtual bool capped() const { return true; }
- private:
- NamespaceDetails *nsd;
- };
-
-} // namespace mongo
diff --git a/src/mongo/db/dbcommands.cpp b/src/mongo/db/dbcommands.cpp
index 269144cbbdb..ed82e5bdad6 100644
--- a/src/mongo/db/dbcommands.cpp
+++ b/src/mongo/db/dbcommands.cpp
@@ -60,7 +60,6 @@
#include "mongo/db/query/get_runner.h"
#include "mongo/db/query/internal_plans.h"
#include "mongo/db/query/query_planner.h"
-#include "mongo/db/query_optimizer.h"
#include "mongo/db/repl/is_master.h"
#include "mongo/db/repl/oplog.h"
#include "mongo/db/write_concern.h"
diff --git a/src/mongo/db/dbhelpers.cpp b/src/mongo/db/dbhelpers.cpp
index d015ff4d319..d6750f53d85 100644
--- a/src/mongo/db/dbhelpers.cpp
+++ b/src/mongo/db/dbhelpers.cpp
@@ -45,7 +45,6 @@
#include "mongo/db/ops/update_request.h"
#include "mongo/db/ops/update_result.h"
#include "mongo/db/pagefault.h"
-#include "mongo/db/query_optimizer.h"
#include "mongo/db/query_runner.h"
#include "mongo/db/query/get_runner.h"
#include "mongo/db/query/internal_plans.h"
diff --git a/src/mongo/db/dbhelpers.h b/src/mongo/db/dbhelpers.h
index 19226cc8aec..58bb1881796 100644
--- a/src/mongo/db/dbhelpers.h
+++ b/src/mongo/db/dbhelpers.h
@@ -41,7 +41,6 @@ namespace mongo {
extern const BSONObj reverseNaturalObj; // {"$natural": -1 }
class Cursor;
- class CoveredIndexMatcher;
/**
* db helpers are helper functions and classes that let us easily manipulate the local
diff --git a/src/mongo/db/exec/2d.h b/src/mongo/db/exec/2d.h
index fe0aaf7c55a..4a84f695c44 100644
--- a/src/mongo/db/exec/2d.h
+++ b/src/mongo/db/exec/2d.h
@@ -28,6 +28,7 @@
#include "mongo/db/exec/2dcommon.h"
#include "mongo/db/exec/plan_stage.h"
+#include "mongo/db/geo/geoquery.h"
#pragma once
diff --git a/src/mongo/db/explain.cpp b/src/mongo/db/explain.cpp
deleted file mode 100644
index b73f3081696..00000000000
--- a/src/mongo/db/explain.cpp
+++ /dev/null
@@ -1,277 +0,0 @@
-// @file explain.cpp - Helper classes for generating query explain output.
-
-/* Copyright 2012 10gen Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "mongo/db/explain.h"
-
-#include "mongo/db/server_options.h"
-#include "mongo/util/mongoutils/str.h"
-#include "mongo/util/net/sock.h"
-
-namespace mongo {
-
- // !!! TODO get rid of const casts
-
- ExplainPlanInfo::ExplainPlanInfo() :
- _isMultiKey(),
- _n(),
- _nscannedObjects(),
- _nscanned(),
- _scanAndOrder(),
- _indexOnly(),
- _picked(),
- _done() {
- }
-
- void ExplainPlanInfo::notePlan( const Cursor &cursor, bool scanAndOrder, bool indexOnly ) {
- _cursorName = const_cast<Cursor&>(cursor).toString();
- _indexBounds = cursor.prettyIndexBounds().getOwned();
- _scanAndOrder = scanAndOrder;
- _indexOnly = indexOnly;
- noteCursorUpdate( cursor );
- }
-
- void ExplainPlanInfo::noteIterate( bool match, bool loadedRecord, const Cursor &cursor ) {
- if ( match ) {
- ++_n;
- }
- if ( loadedRecord ) {
- ++_nscannedObjects;
- }
- noteCursorUpdate( cursor );
- }
-
- void ExplainPlanInfo::noteDone( const Cursor &cursor ) {
- _done = true;
- noteCursorUpdate( cursor );
- BSONObjBuilder bob;
- const_cast<Cursor&>(cursor).explainDetails( bob );
- _details = bob.obj();
- }
-
- void ExplainPlanInfo::notePicked() {
- _picked = true;
- }
-
- BSONObj ExplainPlanInfo::bson() const {
- BSONObjBuilder bob;
- bob.append( "cursor", _cursorName );
- bob.appendNumber( "n", _n );
- bob.appendNumber( "nscannedObjects", _nscannedObjects );
- bob.appendNumber( "nscanned", _nscanned );
- bob.append( "indexBounds", _indexBounds );
- return bob.obj();
- }
-
- BSONObj ExplainPlanInfo::pickedPlanBson( const ExplainClauseInfo &clauseInfo ) const {
- BSONObjBuilder bob;
- bob.append( "cursor", _cursorName );
- bob.append( "isMultiKey", _isMultiKey );
- bob.appendNumber( "n", clauseInfo.n() );
- bob.appendNumber( "nscannedObjects", clauseInfo.nscannedObjects() );
- bob.appendNumber( "nscanned", clauseInfo.nscanned() );
- bob.appendNumber( "nscannedObjectsAllPlans", clauseInfo.nscannedObjectsAllPlans() );
- bob.appendNumber( "nscannedAllPlans", clauseInfo.nscannedAllPlans() );
- bob.append( "scanAndOrder", _scanAndOrder );
- bob.append( "indexOnly", _indexOnly );
- bob.appendNumber( "nYields", clauseInfo.nYields() );
- bob.appendNumber( "nChunkSkips", clauseInfo.nChunkSkips() );
- bob.appendNumber( "millis", clauseInfo.millis() );
- bob.append( "indexBounds", _indexBounds );
- bob.appendElements( _details );
- return bob.obj();
- }
-
- void ExplainPlanInfo::noteCursorUpdate( const Cursor &cursor ) {
- _isMultiKey = cursor.isMultiKey();
- _nscanned = const_cast<Cursor&>(cursor).nscanned();
- }
-
- ExplainClauseInfo::ExplainClauseInfo() :
- _n(),
- _nscannedObjects(),
- _nChunkSkips(),
- _nYields() {
- }
-
- BSONObj ExplainClauseInfo::bson() const {
- BSONObjBuilder bb;
- bb.appendElements( virtualPickedPlan().pickedPlanBson( *this ) );
- BSONArrayBuilder allPlans( bb.subarrayStart( "allPlans" ) );
- for( list<shared_ptr<const ExplainPlanInfo> >::const_iterator i = _plans.begin();
- i != _plans.end(); ++i ) {
- allPlans << (*i)->bson();
- }
- allPlans.done();
- return bb.obj();
- }
-
- void ExplainClauseInfo::addPlanInfo( const shared_ptr<ExplainPlanInfo> &info ) {
- _plans.push_back( info );
- }
-
- void ExplainClauseInfo::noteYield() { ++_nYields; }
-
- void ExplainClauseInfo::noteIterate( bool match, bool loadedRecord, bool chunkSkip ) {
- if ( match ) {
- ++_n;
- }
- if ( loadedRecord ) {
- ++_nscannedObjects;
- }
- if ( chunkSkip ) {
- ++_nChunkSkips;
- }
- }
-
- void ExplainClauseInfo::reviseN( long long n ) {
- _n = n;
- }
-
- void ExplainClauseInfo::stopTimer() {
- _timer.stop();
- }
-
- long long ExplainClauseInfo::nscannedObjects() const {
- if ( _plans.empty() ) {
- return 0;
- }
- return virtualPickedPlan().nscannedObjects();
- }
-
- long long ExplainClauseInfo::nscanned() const {
- if ( _plans.empty() ) {
- return 0;
- }
- return virtualPickedPlan().nscanned();
- }
-
- long long ExplainClauseInfo::nscannedAllPlans() const {
- long long ret = 0;
- for( list<shared_ptr<const ExplainPlanInfo> >::const_iterator i = _plans.begin();
- i != _plans.end(); ++i ) {
- ret += (*i)->nscanned();
- }
- return ret;
- }
-
- const ExplainPlanInfo &ExplainClauseInfo::virtualPickedPlan() const {
- // Return a picked plan if possible.
- for( list<shared_ptr<const ExplainPlanInfo> >::const_iterator i = _plans.begin();
- i != _plans.end(); ++i ) {
- if ( (*i)->picked() ) {
- return **i;
- }
- }
- // Return a done plan if possible.
- for( list<shared_ptr<const ExplainPlanInfo> >::const_iterator i = _plans.begin();
- i != _plans.end(); ++i ) {
- if ( (*i)->done() ) {
- return **i;
- }
- }
- // Return a plan with the highest match count.
- long long maxN = -1;
- shared_ptr<const ExplainPlanInfo> ret;
- for( list<shared_ptr<const ExplainPlanInfo> >::const_iterator i = _plans.begin();
- i != _plans.end(); ++i ) {
- long long n = ( *i )->n();
- if ( n > maxN ) {
- maxN = n;
- ret = *i;
- }
- }
- verify( ret );
- return *ret;
- }
-
- void ExplainQueryInfo::noteIterate( bool match, bool loadedRecord, bool chunkSkip ) {
- verify( !_clauses.empty() );
- _clauses.back()->noteIterate( match, loadedRecord, chunkSkip );
- }
-
- void ExplainQueryInfo::noteYield() {
- verify( !_clauses.empty() );
- _clauses.back()->noteYield();
- }
-
- void ExplainQueryInfo::reviseN( long long n ) {
- verify( !_clauses.empty() );
- _clauses.back()->reviseN( n );
- }
-
- void ExplainQueryInfo::setAncillaryInfo( const AncillaryInfo &ancillaryInfo ) {
- _ancillaryInfo = ancillaryInfo;
- }
-
- BSONObj ExplainQueryInfo::bson() const {
- BSONObjBuilder bob;
- if ( _clauses.size() == 1 ) {
- bob.appendElements( _clauses.front()->bson() );
- }
- else {
- long long n = 0;
- long long nscannedObjects = 0;
- long long nscanned = 0;
- long long nscannedObjectsAllPlans = 0;
- long long nscannedAllPlans = 0;
- BSONArrayBuilder clauseArray( bob.subarrayStart( "clauses" ) );
- for( list<shared_ptr<ExplainClauseInfo> >::const_iterator i = _clauses.begin();
- i != _clauses.end(); ++i ) {
- clauseArray << (*i)->bson();
- n += (*i)->n();
- nscannedObjects += (*i)->nscannedObjects();
- nscanned += (*i)->nscanned();
- nscannedObjectsAllPlans += (*i)->nscannedObjectsAllPlans();
- nscannedAllPlans += (*i)->nscannedAllPlans();
- }
- clauseArray.done();
- bob.appendNumber( "n", n );
- bob.appendNumber( "nscannedObjects", nscannedObjects );
- bob.appendNumber( "nscanned", nscanned );
- bob.appendNumber( "nscannedObjectsAllPlans", nscannedObjectsAllPlans );
- bob.appendNumber( "nscannedAllPlans", nscannedAllPlans );
- bob.appendNumber( "millis", _timer.duration() );
- }
-
- if ( !_ancillaryInfo._oldPlan.isEmpty() ) {
- bob.append( "oldPlan", _ancillaryInfo._oldPlan );
- }
- bob.append( "server", server() );
-
- return bob.obj();
- }
-
- void ExplainQueryInfo::addClauseInfo( const shared_ptr<ExplainClauseInfo> &info ) {
- if ( !_clauses.empty() ) {
- _clauses.back()->stopTimer();
- }
- _clauses.push_back( info );
- }
-
- string ExplainQueryInfo::server() {
- return mongoutils::str::stream() << getHostNameCached() << ":" << serverGlobalParams.port;
- }
-
- ExplainSinglePlanQueryInfo::ExplainSinglePlanQueryInfo() :
- _planInfo( new ExplainPlanInfo() ),
- _queryInfo( new ExplainQueryInfo() ) {
- shared_ptr<ExplainClauseInfo> clauseInfo( new ExplainClauseInfo() );
- clauseInfo->addPlanInfo( _planInfo );
- _queryInfo->addClauseInfo( clauseInfo );
- }
-
-} // namespace mongo
diff --git a/src/mongo/db/explain.h b/src/mongo/db/explain.h
deleted file mode 100644
index 02c9bbf6c76..00000000000
--- a/src/mongo/db/explain.h
+++ /dev/null
@@ -1,188 +0,0 @@
-// @file explain.h - Helper classes for generating query explain output.
-
-/* Copyright 2012 10gen Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include "mongo/db/cursor.h"
-#include "mongo/util/timer.h"
-
-namespace mongo {
-
- /**
- * Note: by default we filter out allPlans and oldPlan in the shell's
- * explain() function. If you add any recursive structures, make sure to
- * edit the JS to make sure everything gets filtered.
- */
-
- /** The timer starts on construction and provides the duration since then or until stopped. */
- class DurationTimer {
- public:
- DurationTimer() : _running( true ), _duration() {}
- void stop() { _running = false; _duration = _timer.millis(); }
- int duration() const { return _running ? _timer.millis() : _duration; }
- private:
- Timer _timer;
- bool _running;
- int _duration;
- };
-
- class ExplainClauseInfo;
-
- /** Data describing execution of a query plan. */
- class ExplainPlanInfo {
- public:
- ExplainPlanInfo();
-
- /** Note information about the plan. */
- void notePlan( const Cursor &cursor, bool scanAndOrder, bool indexOnly );
- /** Note an iteration of the plan. */
- void noteIterate( bool match, bool loadedRecord, const Cursor &cursor );
- /** Note that the plan finished execution. */
- void noteDone( const Cursor &cursor );
- /** Note that the plan was chosen over others by the query optimizer. */
- void notePicked();
-
- /** BSON summary of the plan. */
- BSONObj bson() const;
- /** Combined details of both the plan and its clause. */
- BSONObj pickedPlanBson( const ExplainClauseInfo &clauseInfo ) const;
-
- bool picked() const { return _picked; }
- bool done() const { return _done; }
- long long n() const { return _n; }
- long long nscannedObjects() const { return _nscannedObjects; }
- long long nscanned() const { return _nscanned; }
-
- private:
- void noteCursorUpdate( const Cursor &cursor );
- string _cursorName;
- bool _isMultiKey;
- long long _n;
- long long _nscannedObjects;
- long long _nscanned;
- bool _scanAndOrder;
- bool _indexOnly;
- BSONObj _indexBounds;
- bool _picked;
- bool _done;
- BSONObj _details;
- };
-
- /** Data describing execution of a query clause. */
- class ExplainClauseInfo {
- public:
- ExplainClauseInfo();
-
- /** Note an iteration of the clause. */
- void noteIterate( bool match, bool loadedRecord, bool chunkSkip );
- /** Note a yield for the clause. */
- void noteYield();
- /** Revise the total number of documents returned to match an external count. */
- void reviseN( long long n );
- /** Stop the clauses's timer. */
- void stopTimer();
-
- /** Add information about a plan to this clause. */
- void addPlanInfo( const shared_ptr<ExplainPlanInfo> &info );
- BSONObj bson() const;
-
- long long n() const { return _n; }
- long long nscannedObjects() const;
- long long nscanned() const;
- long long nscannedObjectsAllPlans() const { return _nscannedObjects; }
- long long nscannedAllPlans() const;
- long long nChunkSkips() const { return _nChunkSkips; }
- int nYields() const { return _nYields; }
- int millis() const { return _timer.duration(); }
-
- private:
- /**
- * @return Plan explain information to be displayed at the top of the explain output. A
- * picked() plan will be returned if one is available, otherwise a successful non picked()
- * plan will be returned.
- */
- const ExplainPlanInfo &virtualPickedPlan() const;
- list<shared_ptr<const ExplainPlanInfo> > _plans;
- long long _n;
- long long _nscannedObjects;
- long long _nChunkSkips;
- int _nYields;
- DurationTimer _timer;
- };
-
- /** Data describing execution of a query. */
- class ExplainQueryInfo {
- public:
- /** Note an iteration of the query's current clause. */
- void noteIterate( bool match, bool loadedRecord, bool chunkSkip );
- /** Note a yield of the query's current clause. */
- void noteYield();
- /** Revise the number of documents returned by the current clause. */
- void reviseN( long long n );
-
- /* Additional information describing the query. */
- struct AncillaryInfo {
- BSONObj _oldPlan;
- };
- void setAncillaryInfo( const AncillaryInfo &ancillaryInfo );
-
- /* Add information about a clause to this query. */
- void addClauseInfo( const shared_ptr<ExplainClauseInfo> &info );
- BSONObj bson() const;
-
- private:
- static string server();
-
- list<shared_ptr<ExplainClauseInfo> > _clauses;
- AncillaryInfo _ancillaryInfo;
- DurationTimer _timer;
- };
-
- /** Data describing execution of a query with a single clause and plan. */
- class ExplainSinglePlanQueryInfo {
- public:
- ExplainSinglePlanQueryInfo();
-
- /** Note information about the plan. */
- void notePlan( const Cursor &cursor, bool scanAndOrder, bool indexOnly ) {
- _planInfo->notePlan( cursor, scanAndOrder, indexOnly );
- }
- /** Note an iteration of the plan and the clause. */
- void noteIterate( bool match, bool loadedRecord, bool chunkSkip, const Cursor &cursor ) {
- _planInfo->noteIterate( match, loadedRecord, cursor );
- _queryInfo->noteIterate( match, loadedRecord, chunkSkip );
- }
- /** Note a yield for the clause. */
- void noteYield() {
- _queryInfo->noteYield();
- }
- /** Note that the plan finished execution. */
- void noteDone( const Cursor &cursor ) {
- _planInfo->noteDone( cursor );
- }
-
- /** Return the corresponding ExplainQueryInfo for further use. */
- shared_ptr<ExplainQueryInfo> queryInfo() const {
- return _queryInfo;
- }
-
- private:
- shared_ptr<ExplainPlanInfo> _planInfo;
- shared_ptr<ExplainQueryInfo> _queryInfo;
- };
-
-} // namespace mongo
diff --git a/src/mongo/db/index/emulated_cursor.h b/src/mongo/db/index/emulated_cursor.h
deleted file mode 100644
index 86c82072603..00000000000
--- a/src/mongo/db/index/emulated_cursor.h
+++ /dev/null
@@ -1,266 +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 <boost/scoped_ptr.hpp>
-#include <set>
-
-#include "mongo/db/cursor.h"
-#include "mongo/db/index_names.h"
-#include "mongo/db/index/index_access_method.h"
-#include "mongo/db/index/index_descriptor.h"
-#include "mongo/db/index/index_cursor.h"
-#include "mongo/db/jsobj.h"
-#include "mongo/platform/unordered_set.h"
-
-namespace mongo {
-
- /**
- * This class is a crutch to help migrate from the old everything-is-a-Cursor world to the new
- * index API world. It wraps a new IndexCursor in the old Cursor. We only use this for special
- * (2d, 2dsphere, text, haystack, hash) indices.
- */
- class EmulatedCursor : public Cursor {
- public:
- /**
- * Create a new EmulatedCursor.
- * Takes ownership of the provided indexAccessMethod indexCursor.
- * Takes ownership of the IndexDescriptor inside the indexAccessMethod.
- *
- * Full semantics of numWanted:
- * numWanted == 0 : Return any number of results, but try to return in batches of 101.
- * numWanted == 1 : Return exactly one result.
- * numWanted > 1 : Return any number of results, but try to return in batches of numWanted.
- *
- * In practice, your cursor can ignore numWanted, as enforcement of limits is done
- * by the caller.
- */
- static EmulatedCursor* make(IndexDescriptor* descriptor,
- IndexAccessMethod* indexAccessMethod,
- const BSONObj& query,
- const BSONObj& order,
- int numWanted,
- const BSONObj& keyPattern) {
-
- verify(descriptor);
- verify(indexAccessMethod);
-
- auto_ptr<EmulatedCursor> ret(new EmulatedCursor(descriptor, indexAccessMethod,
- order, numWanted, keyPattern));
- // Why do we have to do this? No reading from disk is allowed in constructors,
- // and seeking involves reading from disk.
- ret->seek(query);
- return ret.release();
- }
-
- // We defer everything we can to the underlying cursor.
- virtual bool ok() { return !_indexCursor->isEOF(); }
- virtual Record* _current() { return currLoc().rec(); }
- virtual BSONObj current() { return BSONObj::make(_current()); }
- virtual DiskLoc currLoc() { return _indexCursor->getValue(); }
- virtual BSONObj currKey() const { return _indexCursor->getKey(); }
- virtual DiskLoc refLoc() {
- // This will sometimes get called even if we're not OK.
- // See ClientCursor::prepareToYield/updateLocation.
- if (!ok()) {
- return DiskLoc();
- } else {
- return currLoc();
- }
- }
- virtual long long nscanned() { return _nscanned; }
- virtual string toString() { return _indexCursor->toString(); }
-
- virtual void explainDetails(BSONObjBuilder& b) {
- _indexCursor->explainDetails(&b);
- return;
- }
-
- virtual bool advance() {
- _indexCursor->next();
- if (ok()) {
- ++_nscanned;
- }
- return ok();
- }
-
- virtual void noteLocation() {
- verify(_supportYields || _supportGetMore);
- _indexCursor->savePosition();
- }
-
- virtual void checkLocation() {
- verify(_supportYields || _supportGetMore);
- _indexCursor->restorePosition();
- // Somebody might have inserted a multikey during a yield.
- checkMultiKeyProperties();
- }
-
- // Below this is where the Cursor <---> IndexCursor mapping breaks down.
- virtual CoveredIndexMatcher* matcher() const {
- return _matcher.get();
- }
-
- virtual BSONObj indexKeyPattern() {
- return _descriptor->keyPattern();
- }
-
- virtual bool supportGetMore() { return _supportGetMore; }
- virtual bool supportYields() { return _supportYields; }
- virtual bool isMultiKey() const { return _isMultiKey; }
- virtual bool modifiedKeys() const { return _modifiedKeys; }
- virtual bool autoDedup() const { return _autoDedup; }
-
- virtual bool getsetdup(DiskLoc loc) {
- if (_shouldGetSetDup) {
- pair<unordered_set<DiskLoc, DiskLoc::Hasher>::iterator, bool> p = _dups.insert(loc);
- return !p.second;
- } else {
- return false;
- }
- }
-
- private:
- EmulatedCursor(IndexDescriptor* descriptor, IndexAccessMethod* indexAccessMethod,
- const BSONObj& order, int numWanted, const BSONObj& keyPattern)
- : _descriptor(descriptor), _indexAccessMethod(indexAccessMethod),
- _keyPattern(keyPattern), _pluginName(IndexNames::findPluginName(keyPattern)) {
-
- IndexCursor *cursor;
- indexAccessMethod->newCursor(&cursor);
- _indexCursor.reset(cursor);
-
- CursorOptions options;
- options.numWanted = numWanted;
- cursor->setOptions(options);
-
- if (IndexNames::HASHED == _pluginName) {
- _supportYields = true;
- _supportGetMore = true;
- _modifiedKeys = true;
- _shouldGetSetDup = false;
- _autoDedup = true;
- } else if (IndexNames::GEO_2DSPHERE == _pluginName) {
- _supportYields = true;
- _supportGetMore = true;
- _modifiedKeys = true;
- // Note: this duplicates the de-duplication in near cursors. Fix near cursors
- // to not de-dup themselves.
- _shouldGetSetDup = true;
- _autoDedup = false;
- } else if (IndexNames::GEO_2D == _pluginName) {
- _supportYields = false;
- _supportGetMore = true;
- _modifiedKeys = true;
- _isMultiKey = false;
- _shouldGetSetDup = false;
- _autoDedup = false;
- } else {
- verify(0);
- }
-
- // _isMultiKey and _shouldGetSetDup are set in this unless it's 2d.
- checkMultiKeyProperties();
- }
-
- void seek(const BSONObj& query) {
- Status seekStatus = _indexCursor->seek(query);
-
- // Our seek could be malformed. Code above us expects an exception if so.
- if (Status::OK() != seekStatus) {
- uasserted(seekStatus.location(), seekStatus.reason());
- }
-
- if (!_indexCursor->isEOF()) {
- _nscanned = 1;
- } else {
- _nscanned = 0;
- }
-
- if (IndexNames::HASHED == _pluginName) {
- // Quoted from hashindex.cpp:
- // Force a match of the query against the actual document by giving
- // the cursor a matcher with an empty indexKeyPattern. This ensures the
- // index is not used as a covered index.
- // NOTE: this forcing is necessary due to potential hash collisions
- _matcher = shared_ptr<CoveredIndexMatcher>(
- new CoveredIndexMatcher(query, BSONObj()));
- } else if (IndexNames::GEO_2DSPHERE == _pluginName) {
- // Technically, the non-geo indexed fields are in the key, though perhaps not in the
- // exact format the matcher expects (arrays). So, we match against all non-geo
- // fields. This could possibly be relaxed in some fashion in the future? Requires
- // query work.
- BSONObjBuilder fieldsToNuke;
- BSONObjIterator keyIt(_keyPattern);
-
- while (keyIt.more()) {
- BSONElement e = keyIt.next();
- if (e.type() == String && IndexNames::GEO_2DSPHERE == e.valuestr()) {
- fieldsToNuke.append(e.fieldName(), "");
- }
- }
-
- BSONObj filteredQuery = query.filterFieldsUndotted(fieldsToNuke.obj(), false);
-
- _matcher = shared_ptr<CoveredIndexMatcher>(
- new CoveredIndexMatcher(filteredQuery, _keyPattern));
- } else if (IndexNames::GEO_2D == _pluginName) {
- // No-op matcher.
- _matcher = shared_ptr<CoveredIndexMatcher>(
- new CoveredIndexMatcher(BSONObj(), BSONObj()));
- }
- }
-
- void checkMultiKeyProperties() {
- if (IndexNames::GEO_2D != _pluginName) {
- _isMultiKey = _shouldGetSetDup = _descriptor->isMultikey();
- }
- }
-
- unordered_set<DiskLoc, DiskLoc::Hasher> _dups;
-
- scoped_ptr<IndexDescriptor> _descriptor;
- scoped_ptr<IndexAccessMethod> _indexAccessMethod;
- scoped_ptr<IndexCursor> _indexCursor;
-
- long long _nscanned;
- shared_ptr<CoveredIndexMatcher> _matcher;
-
- bool _supportYields;
- bool _supportGetMore;
- bool _isMultiKey;
- bool _modifiedKeys;
- bool _shouldGetSetDup;
- bool _autoDedup;
-
- BSONObj _keyPattern;
- string _pluginName;
- };
-
-} // namespace mongo
diff --git a/src/mongo/db/intervalbtreecursor.cpp b/src/mongo/db/intervalbtreecursor.cpp
deleted file mode 100644
index b5620691a80..00000000000
--- a/src/mongo/db/intervalbtreecursor.cpp
+++ /dev/null
@@ -1,232 +0,0 @@
-/**
- * Copyright (C) 2012 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/intervalbtreecursor.h"
-
-#include "mongo/db/btree.h"
-#include "mongo/db/kill_current_op.h"
-#include "mongo/db/namespace_details-inl.h"
-#include "mongo/db/pdfile.h"
-
-namespace mongo {
-
- unordered_set<IntervalBtreeCursor*> IntervalBtreeCursor::_activeCursors;
- SimpleMutex IntervalBtreeCursor::_activeCursorsMutex("active_interval_btree_cursors");
-
- /**
- * Advance 'loc' until it does not reference an unused key, or the end of the btree is reached.
- */
- static void skipUnused( BtreeKeyLocation* loc ) {
-
- // While loc points to an unused key ...
- while( !loc->bucket.isNull() &&
- loc->bucket.btree<V1>()->k( loc->pos ).isUnused() ) {
-
- // ... advance loc to the next key in the btree.
- loc->bucket = loc->bucket.btree<V1>()->advance( loc->bucket,
- loc->pos,
- 1,
- __FUNCTION__ );
- }
- }
-
- IntervalBtreeCursor* IntervalBtreeCursor::make( NamespaceDetails* namespaceDetails,
- const IndexDetails& indexDetails,
- const BSONObj& lowerBound,
- bool lowerBoundInclusive,
- const BSONObj& upperBound,
- bool upperBoundInclusive ) {
- if ( indexDetails.version() != 1 ) {
- // Only v1 indexes are supported.
- return NULL;
- }
- auto_ptr<IntervalBtreeCursor> ret( new IntervalBtreeCursor( namespaceDetails,
- indexDetails,
- lowerBound,
- lowerBoundInclusive,
- upperBound,
- upperBoundInclusive ) );
- ret->init();
- return ret.release();
- }
-
- IntervalBtreeCursor::IntervalBtreeCursor( NamespaceDetails* namespaceDetails,
- const IndexDetails& indexDetails,
- const BSONObj& lowerBound,
- bool lowerBoundInclusive,
- const BSONObj& upperBound,
- bool upperBoundInclusive ) :
- _namespaceDetails( *namespaceDetails ),
- _indexNo( namespaceDetails->idxNo( indexDetails ) ),
- _indexDetails( indexDetails ),
- _ordering( Ordering::make( _indexDetails.keyPattern() ) ),
- _lowerBound( lowerBound ),
- _lowerBoundInclusive( lowerBoundInclusive ),
- _upperBound( upperBound ),
- _upperBoundInclusive( upperBoundInclusive ),
- _currRecoverable( _indexDetails, _ordering, _curr ),
- _nscanned(),
- _multikeyFlag() {
-
- SimpleMutex::scoped_lock lock(_activeCursorsMutex);
- _activeCursors.insert(this);
- }
-
- IntervalBtreeCursor::~IntervalBtreeCursor() {
- SimpleMutex::scoped_lock lock(_activeCursorsMutex);
- _activeCursors.erase(this);
- }
-
- void IntervalBtreeCursor::aboutToDeleteBucket(const DiskLoc& bucket) {
- SimpleMutex::scoped_lock lock(_activeCursorsMutex);
- for (unordered_set<IntervalBtreeCursor*>::iterator i = _activeCursors.begin();
- i != _activeCursors.end(); ++i) {
-
- IntervalBtreeCursor* ic = *i;
- if (bucket == ic->_curr.bucket) {
- ic->_currRecoverable.invalidateInitialLocation();
- }
- }
- }
-
- void IntervalBtreeCursor::init() {
- _multikeyFlag = _namespaceDetails.isMultikey( _indexNo );
- _curr = locateKey( _lowerBound, !_lowerBoundInclusive );
- skipUnused( &_curr );
- relocateEnd();
- if ( ok() ) {
- _nscanned = 1;
- }
- }
-
- bool IntervalBtreeCursor::ok() {
- return !_curr.bucket.isNull();
- }
-
- DiskLoc IntervalBtreeCursor::currLoc() {
- if ( eof() ) {
- return DiskLoc();
- }
- return _curr.bucket.btree<V1>()->keyNode( _curr.pos ).recordLoc;
- }
-
- bool IntervalBtreeCursor::advance() {
- RARELY killCurrentOp.checkForInterrupt();
- if ( eof() ) {
- return false;
- }
- // Advance _curr to the next key in the btree.
- _curr.bucket = _curr.bucket.btree<V1>()->advance( _curr.bucket,
- _curr.pos,
- 1,
- __FUNCTION__ );
- skipUnused( &_curr );
- if ( _curr == _end ) {
- // _curr has reached _end, so iteration is complete.
- _curr.bucket.Null();
- }
- else {
- ++_nscanned;
- }
- return ok();
- }
-
- BSONObj IntervalBtreeCursor::currKey() const {
- if ( _curr.bucket.isNull() ) {
- return BSONObj();
- }
- return _curr.bucket.btree<V1>()->keyNode( _curr.pos ).key.toBson();
- }
-
-
- void IntervalBtreeCursor::noteLocation() {
- _currRecoverable = LogicalBtreePosition( _indexDetails, _ordering, _curr );
- _currRecoverable.init();
- }
-
- void IntervalBtreeCursor::checkLocation() {
- _multikeyFlag = _namespaceDetails.isMultikey( _indexNo );
- _curr = _currRecoverable.currentLocation();
- skipUnused( &_curr );
- relocateEnd();
- }
-
- bool IntervalBtreeCursor::getsetdup( DiskLoc loc ) {
- // TODO _multikeyFlag may be set part way through an iteration by checkLocation(). In this
- // case results returned earlier, when _multikeyFlag was false, will not be deduped. This
- // is an old issue with all mongo btree cursor implementations.
- return _multikeyFlag && !_dups.insert( loc ).second;
- }
-
- BSONObj IntervalBtreeCursor::prettyIndexBounds() const {
- return BSON( "lower" << _lowerBound.replaceFieldNames( _indexDetails.keyPattern() ) <<
- "upper" << _upperBound.replaceFieldNames( _indexDetails.keyPattern() ) );
- }
-
- BtreeKeyLocation IntervalBtreeCursor::locateKey( const BSONObj& key, bool afterKey ) {
- bool found;
- BtreeKeyLocation ret;
-
- // To find the first btree location equal to the specified key, specify a record location of
- // minDiskLoc, which is below any actual Record location. To find the first btree location
- // greater than the specified key, specify a record location of maxDiskLoc, which is above
- // any actual Record location.
- DiskLoc targetRecord = afterKey ? maxDiskLoc : minDiskLoc;
-
- // Find the requested location in the btree.
- ret.bucket = _indexDetails.head.btree<V1>()->locate( _indexDetails,
- _indexDetails.head,
- key,
- _ordering,
- ret.pos,
- found,
- targetRecord,
- 1 );
- return ret;
- }
-
- void IntervalBtreeCursor::relocateEnd() {
- if ( eof() ) {
- return;
- }
-
- // If the current key is above the upper bound ...
- int32_t cmp = currKey().woCompare( _upperBound, _ordering, false );
- if ( cmp > 0 || ( cmp == 0 && !_upperBoundInclusive ) ) {
-
- // ... then iteration is complete.
- _curr.bucket.Null();
- return;
- }
-
- // Otherwise, relocate _end.
- _end = locateKey( _upperBound, _upperBoundInclusive );
- skipUnused( &_end );
- }
-
-} // namespace mongo
diff --git a/src/mongo/db/intervalbtreecursor.h b/src/mongo/db/intervalbtreecursor.h
deleted file mode 100644
index 0f9770204f7..00000000000
--- a/src/mongo/db/intervalbtreecursor.h
+++ /dev/null
@@ -1,163 +0,0 @@
-/**
- * Copyright (C) 2012 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 "mongo/db/btreeposition.h"
-#include "mongo/db/cursor.h"
-#include "mongo/db/namespace_details.h"
-#include "mongo/platform/cstdint.h"
-#include "mongo/platform/unordered_set.h"
-
-namespace mongo {
-
- /**
- * An optimized btree cursor that iterates through all btree keys between a lower bound and an
- * upper bound. The contents of the individual keys are not examined by the implementation,
- * which simply advances through the tree until reaching a predetermined end location. The goal
- * is to optimize count operations where the keys must only be counted, not tested for matching.
- *
- * Limitations compared to a standard BtreeCursor (partial list):
- * - Only supports index constraints consisting of a single interval within an index.
- * - Only supports forward direction iteration.
- * - Does not support covered index projections.
- * - Does not support get more.
- * - Only supports V1 indexes (not V0).
- */
- class IntervalBtreeCursor : public Cursor {
- public:
-
- /**
- * @return a cursor, or NULL if no cursor can be created.
- * @param namespaceDetails - Collection metadata that will not be modified.
- * @param indexDetails - Index metadata, if not a v1 index then make() will return NULL.
- * @param lowerBound - Lower bound of the key range to iterate, according to the index's
- * native ordering.
- * @param lowerBoundInclusive - If true, the lower bound includes the endpoint.
- * @param upperBound - Upper bound of the key range to iterate.
- * @param upperBoundInclusive - If true, the upper bound includes the endpoint.
- */
- static IntervalBtreeCursor* make( /* const */ NamespaceDetails* namespaceDetails,
- const IndexDetails& indexDetails,
- const BSONObj& lowerBound,
- bool lowerBoundInclusive,
- const BSONObj& upperBound,
- bool upperBoundInclusive );
-
- /** Virtuals from Cursor. */
-
- virtual bool ok();
-
- virtual Record* _current() { return currLoc().rec(); }
-
- virtual BSONObj current() { return currLoc().obj(); }
-
- virtual DiskLoc currLoc();
-
- virtual bool advance();
-
- virtual BSONObj currKey() const;
-
- virtual DiskLoc refLoc() { return currLoc(); }
-
- static void aboutToDeleteBucket( const DiskLoc& b );
-
- virtual BSONObj indexKeyPattern() { return _indexDetails.keyPattern(); }
-
- virtual bool supportGetMore() { return false; }
-
- virtual void noteLocation();
-
- virtual void checkLocation();
-
- virtual bool supportYields() { return true; }
-
- virtual string toString() { return "IntervalBtreeCursor"; }
-
- virtual bool getsetdup( DiskLoc loc );
-
- virtual bool isMultiKey() const { return _multikeyFlag; }
-
- virtual bool modifiedKeys() const { return _multikeyFlag; }
-
- virtual BSONObj prettyIndexBounds() const;
-
- virtual long long nscanned() { return _nscanned; }
-
- virtual CoveredIndexMatcher* matcher() const { return _matcher.get(); }
-
- virtual void setMatcher( shared_ptr<CoveredIndexMatcher> matcher ) { _matcher = matcher; }
-
- virtual ~IntervalBtreeCursor();
-
- private:
- IntervalBtreeCursor( NamespaceDetails* namespaceDetails,
- const IndexDetails& indexDetails,
- const BSONObj& lowerBound,
- bool lowerBoundInclusive,
- const BSONObj& upperBound,
- bool upperBoundInclusive );
-
- // For handling bucket deletion.
- static unordered_set<IntervalBtreeCursor*> _activeCursors;
- static SimpleMutex _activeCursorsMutex;
-
- void init();
-
- /**
- * @return a location in the btree, determined by the parameters specified.
- * @param key - The key to search for.
- * @param afterKey - If true, return the first btree key greater than the supplied 'key'.
- * If false, return the first key equal to the supplied 'key'.
- */
- BtreeKeyLocation locateKey( const BSONObj& key, bool afterKey );
-
- /** Find the iteration end location and set _end to it. */
- void relocateEnd();
-
- const NamespaceDetails& _namespaceDetails;
- const int32_t _indexNo;
- const IndexDetails& _indexDetails;
- const Ordering _ordering;
- const BSONObj _lowerBound;
- const bool _lowerBoundInclusive;
- const BSONObj _upperBound;
- const bool _upperBoundInclusive;
-
- BtreeKeyLocation _curr; // Current position in the btree.
- LogicalBtreePosition _currRecoverable; // Helper to track the position of _curr if the
- // btree is modified during a mutex yield.
- BtreeKeyLocation _end; // Exclusive end location in the btree.
- int64_t _nscanned;
-
- shared_ptr<CoveredIndexMatcher> _matcher;
- bool _multikeyFlag;
- unordered_set<DiskLoc,DiskLoc::Hasher> _dups;
- };
-
-} // namespace mongo
diff --git a/src/mongo/db/matcher.h b/src/mongo/db/matcher.h
index 8d2b1df8a81..f4ac8757a32 100644
--- a/src/mongo/db/matcher.h
+++ b/src/mongo/db/matcher.h
@@ -37,7 +37,6 @@
namespace mongo {
class Cursor;
- class CoveredIndexMatcher;
class FieldRangeVector;
struct element_lt {
diff --git a/src/mongo/db/matcher_covered.cpp b/src/mongo/db/matcher_covered.cpp
deleted file mode 100644
index f44ae264a85..00000000000
--- a/src/mongo/db/matcher_covered.cpp
+++ /dev/null
@@ -1,145 +0,0 @@
-// matcher_covered.cpp
-
-/* Matcher is our boolean expression evaluator for "where" clauses */
-
-/**
-* Copyright (C) 2008 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/pch.h"
-
-#include "mongo/db/cursor.h"
-#include "mongo/db/matcher.h"
-#include "mongo/db/matcher_covered.h"
-#include "mongo/db/pdfile.h"
-#include "mongo/db/queryutil.h"
-
-namespace mongo {
-
- CoveredIndexMatcher::CoveredIndexMatcher( const BSONObj &jsobj,
- const BSONObj &indexKeyPattern ) :
- _docMatcher( new Matcher( jsobj ) ),
- _keyMatcher( *_docMatcher, indexKeyPattern ) {
- init();
- }
-
- CoveredIndexMatcher::CoveredIndexMatcher( const CoveredIndexMatcher &prevClauseMatcher,
- const shared_ptr<FieldRangeVector> &prevClauseFrv,
- const BSONObj &nextClauseIndexKeyPattern ) :
- _docMatcher( prevClauseMatcher._docMatcher ),
- _keyMatcher( *_docMatcher, nextClauseIndexKeyPattern ),
- _orDedupConstraints( prevClauseMatcher._orDedupConstraints ) {
- if ( prevClauseFrv ) {
- _orDedupConstraints.push_back( prevClauseFrv );
- }
- init();
- }
-
- void CoveredIndexMatcher::init() {
- _needRecord =
- !_keyMatcher.keyMatch( *_docMatcher ) ||
- !_orDedupConstraints.empty();
- }
-
- bool CoveredIndexMatcher::matchesCurrent( Cursor * cursor , MatchDetails * details ) const {
- // bool keyUsable = ! cursor->isMultiKey() && check for $orish like conditions in matcher SERVER-1264
-
- bool keyUsable = true;
- if ( cursor->indexKeyPattern().isEmpty() ) { // unindexed cursor
- keyUsable = false;
- }
- else if ( cursor->isMultiKey() ) {
- keyUsable =
- _keyMatcher.singleSimpleCriterion() &&
- ( ! _docMatcher || _docMatcher->singleSimpleCriterion() );
- }
- return matches( cursor->currKey(),
- cursor->currLoc(),
- details,
- keyUsable );
- }
-
- bool CoveredIndexMatcher::matches( const BSONObj& key, const DiskLoc& recLoc,
- MatchDetails* details, bool keyUsable ) const {
-
- LOG(5) << "CoveredIndexMatcher::matches() " << key.toString() << ' ' << recLoc.toString() << ' ' << keyUsable << endl;
-
- dassert( key.isValid() );
-
- if ( details )
- details->resetOutput();
-
- if ( keyUsable ) {
- if ( !_keyMatcher.matches(key, details ) ) {
- return false;
- }
- bool needRecordForDetails = details && details->needRecord();
- if ( !_needRecord && !needRecordForDetails ) {
- return true;
- }
- }
-
- BSONObj obj = recLoc.obj();
- bool res =
- _docMatcher->matches( obj, details ) &&
- !isOrClauseDup( obj );
-
- if ( details )
- details->setLoadedRecord( true );
-
- LOG(5) << "CoveredIndexMatcher _docMatcher->matches() returns " << res << endl;
- return res;
- }
-
- bool CoveredIndexMatcher::isOrClauseDup( const BSONObj &obj ) const {
- for( vector<shared_ptr<FieldRangeVector> >::const_iterator i = _orDedupConstraints.begin();
- i != _orDedupConstraints.end(); ++i ) {
- if ( (*i)->matches( obj ) ) {
- // If a document matches a prior $or clause index range, generally it would have
- // been returned while scanning that range and so is reported as a dup.
- return true;
- }
- }
- return false;
- }
-
- string CoveredIndexMatcher::toString() const {
- StringBuilder buf;
- buf << "(CoveredIndexMatcher ";
-
- if ( _needRecord )
- buf << "needRecord ";
-
- buf << "keyMatcher: " << _keyMatcher.toString() << " ";
-
- if ( _docMatcher )
- buf << "docMatcher: " << _docMatcher->toString() << " ";
-
- buf << ")";
- return buf.str();
- }
-}
diff --git a/src/mongo/db/matcher_covered.h b/src/mongo/db/matcher_covered.h
deleted file mode 100644
index 02181ad1753..00000000000
--- a/src/mongo/db/matcher_covered.h
+++ /dev/null
@@ -1,88 +0,0 @@
-// matcher_covered.h
-
-/* Matcher is our boolean expression evaluator for "where" clauses */
-
-/**
-* Copyright (C) 2008 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 "mongo/db/diskloc.h"
-#include "mongo/db/geo/geoquery.h"
-#include "mongo/db/jsobj.h"
-#include "mongo/db/matcher.h"
-#include "mongo/db/matcher/match_details.h"
-
-namespace mongo {
-
- // If match succeeds on index key, then attempt to match full document.
- class CoveredIndexMatcher : boost::noncopyable {
- public:
- CoveredIndexMatcher(const BSONObj &pattern, const BSONObj &indexKeyPattern);
- bool matchesWithSingleKeyIndex( const BSONObj& key, const DiskLoc& recLoc,
- MatchDetails* details = 0 ) const {
- return matches( key, recLoc, details, true );
- }
- /**
- * This is the preferred method for matching against a cursor, as it
- * can handle both multi and single key cursors.
- */
- bool matchesCurrent( Cursor * cursor , MatchDetails * details = 0 ) const;
- bool needRecord() const { return _needRecord; }
-
- const Matcher &docMatcher() const { return *_docMatcher; }
-
- /**
- * @return a matcher for a following $or clause.
- * @param prevClauseFrs The index range scanned by the previous $or clause. May be empty.
- * @param nextClauseIndexKeyPattern The index key of the following $or clause.
- */
- CoveredIndexMatcher *nextClauseMatcher( const shared_ptr<FieldRangeVector>& prevClauseFrv,
- const BSONObj& nextClauseIndexKeyPattern ) const {
- return new CoveredIndexMatcher( *this, prevClauseFrv, nextClauseIndexKeyPattern );
- }
-
- string toString() const;
-
- private:
- bool matches( const BSONObj& key, const DiskLoc& recLoc, MatchDetails* details = 0,
- bool keyUsable = true ) const;
- bool isOrClauseDup( const BSONObj &obj ) const;
- CoveredIndexMatcher( const CoveredIndexMatcher &prevClauseMatcher,
- const shared_ptr<FieldRangeVector> &prevClauseFrv,
- const BSONObj &nextClauseIndexKeyPattern );
- void init();
- shared_ptr< Matcher > _docMatcher;
- Matcher _keyMatcher;
- vector<shared_ptr<FieldRangeVector> > _orDedupConstraints;
-
- bool _needRecord; // if the key itself isn't good enough to determine a positive match
- };
-
-} // namespace mongo
-
diff --git a/src/mongo/db/namespace_details.h b/src/mongo/db/namespace_details.h
index c201903e2c4..bfaea894017 100644
--- a/src/mongo/db/namespace_details.h
+++ b/src/mongo/db/namespace_details.h
@@ -37,7 +37,6 @@
#include "mongo/db/jsobj.h"
#include "mongo/db/storage/durable_mapped_file.h"
#include "mongo/db/namespace_string.h"
-#include "mongo/db/querypattern.h"
#include "mongo/db/catalog/ondisk/namespace.h"
#include "mongo/db/catalog/ondisk/namespace_index.h"
#include "mongo/platform/unordered_map.h"
diff --git a/src/mongo/db/ops/update.h b/src/mongo/db/ops/update.h
index ba3cc9fa40c..ac00f82b57d 100644
--- a/src/mongo/db/ops/update.h
+++ b/src/mongo/db/ops/update.h
@@ -34,7 +34,6 @@
#include "mongo/db/curop.h"
#include "mongo/db/ops/update_request.h"
#include "mongo/db/ops/update_result.h"
-#include "mongo/db/query_plan_selection_policy.h"
namespace mongo {
diff --git a/src/mongo/db/ops/update_request.h b/src/mongo/db/ops/update_request.h
index 36e1b20bde1..4d4139a1601 100644
--- a/src/mongo/db/ops/update_request.h
+++ b/src/mongo/db/ops/update_request.h
@@ -31,7 +31,6 @@
#include "mongo/db/jsobj.h"
#include "mongo/db/curop.h"
#include "mongo/db/namespace_string.h"
-#include "mongo/db/query_plan_selection_policy.h"
#include "mongo/util/mongoutils/str.h"
namespace mongo {
@@ -43,11 +42,8 @@ namespace mongo {
class UpdateRequest {
public:
- inline UpdateRequest(
- const NamespaceString& nsString,
- const QueryPlanSelectionPolicy& policy = QueryPlanSelectionPolicy::any() )
+ inline UpdateRequest(const NamespaceString& nsString)
: _nsString(nsString)
- , _queryPlanPolicy(policy)
, _god(false)
, _upsert(false)
, _multi(false)
@@ -60,10 +56,6 @@ namespace mongo {
return _nsString;
}
- const QueryPlanSelectionPolicy& getQueryPlanSelectionPolicy() const {
- return _queryPlanPolicy;
- }
-
inline void setQuery(const BSONObj& query) {
_query = query;
}
@@ -153,7 +145,6 @@ namespace mongo {
private:
const NamespaceString& _nsString;
- const QueryPlanSelectionPolicy& _queryPlanPolicy;
// Contains the query that selects documents to update.
BSONObj _query;
diff --git a/src/mongo/db/ops/update_result.h b/src/mongo/db/ops/update_result.h
index 9d42af10db3..3d1b3019eb3 100644
--- a/src/mongo/db/ops/update_result.h
+++ b/src/mongo/db/ops/update_result.h
@@ -31,7 +31,6 @@
#include "mongo/db/jsobj.h"
#include "mongo/db/curop.h"
#include "mongo/db/namespace_string.h"
-#include "mongo/db/query_plan_selection_policy.h"
#include "mongo/util/mongoutils/str.h"
namespace mongo {
diff --git a/src/mongo/db/pdfile.cpp b/src/mongo/db/pdfile.cpp
index 34f55a24d3d..8cdd74e6aef 100644
--- a/src/mongo/db/pdfile.cpp
+++ b/src/mongo/db/pdfile.cpp
@@ -337,86 +337,6 @@ namespace mongo {
DataFileMgr::DataFileMgr(){}
- shared_ptr<Cursor> DataFileMgr::findAll(const StringData& ns, const DiskLoc &startLoc) {
- Database* db = cc().database();
- Collection* collection = db->getCollection( ns );
- if ( !collection )
- return shared_ptr<Cursor>(new BasicCursor(DiskLoc()));
- NamespaceDetails* d = collection->details();
- DiskLoc loc = d->firstExtent();
- if ( loc.isNull() )
- return shared_ptr<Cursor>(new BasicCursor(DiskLoc()));
- Extent *e = getExtent(loc);
-
- DEBUGGING {
- out() << "listing extents for " << ns << endl;
- DiskLoc tmp = loc;
- set<DiskLoc> extents;
-
- while ( 1 ) {
- Extent *f = db->getExtentManager().getExtent(tmp);
- out() << "extent: " << tmp.toString() << endl;
- extents.insert(tmp);
- tmp = f->xnext;
- if ( tmp.isNull() )
- break;
- f = db->getExtentManager().getNextExtent( f );
- }
-
- out() << endl;
- d->dumpDeleted(&extents);
- }
-
- if ( d->isCapped() )
- return shared_ptr<Cursor>( ForwardCappedCursor::make( d , startLoc ) );
-
- if ( !startLoc.isNull() )
- return shared_ptr<Cursor>(new BasicCursor( startLoc ));
-
- while ( e->firstRecord.isNull() && !e->xnext.isNull() ) {
- /* todo: if extent is empty, free it for reuse elsewhere.
- that is a bit complicated have to clean up the freelists.
- */
- RARELY out() << "info DFM::findAll(): extent " << loc.toString() << " was empty, skipping ahead. ns:" << ns << endl;
- // find a nonempty extent
- // it might be nice to free the whole extent here! but have to clean up free recs then.
- e = db->getExtentManager().getNextExtent( e );
- }
- return shared_ptr<Cursor>(new BasicCursor( e->firstRecord ));
- }
-
- /* get a table scan cursor, but can be forward or reverse direction.
- order.$natural - if set, > 0 means forward (asc), < 0 backward (desc).
- */
- shared_ptr<Cursor> findTableScan(const char *ns, const BSONObj& order, const DiskLoc &startLoc) {
- BSONElement el = order.getField("$natural"); // e.g., { $natural : -1 }
-
- if ( el.number() >= 0 )
- return DataFileMgr::findAll(ns, startLoc);
-
- // "reverse natural order"
- Database* db = cc().database();
- Collection* collection = db->getCollection( ns );
- if ( !collection )
- return shared_ptr<Cursor>(new BasicCursor(DiskLoc()));
-
- NamespaceDetails* d = collection->details();
-
- if ( !d->isCapped() ) {
- if ( !startLoc.isNull() )
- return shared_ptr<Cursor>(new ReverseCursor( startLoc ));
- Extent *e = d->lastExtent().ext();
- while ( e->lastRecord.isNull() && !e->xprev.isNull() ) {
- OCCASIONALLY out() << " findTableScan: extent empty, skipping ahead" << endl;
- e = db->getExtentManager().getPrevExtent(e);
- }
- return shared_ptr<Cursor>(new ReverseCursor( e->lastRecord ));
- }
- else {
- return shared_ptr<Cursor>( new ReverseCappedCursor( d, startLoc ) );
- }
- }
-
/* deletes a record, just the pdfile portion -- no index cleanup, no cursor cleanup, etc.
caller must check if capped
*/
diff --git a/src/mongo/db/pdfile.h b/src/mongo/db/pdfile.h
index 5968e4e9e7c..98aaa154b18 100644
--- a/src/mongo/db/pdfile.h
+++ b/src/mongo/db/pdfile.h
@@ -38,7 +38,6 @@
#pragma once
#include "mongo/db/client.h"
-#include "mongo/db/cursor.h"
#include "mongo/db/database.h"
#include "mongo/db/diskloc.h"
#include "mongo/db/jsobjmanipulator.h"
@@ -55,7 +54,6 @@
namespace mongo {
- class Cursor;
class DataFileHeader;
class Extent;
class OpDebug;
@@ -66,7 +64,6 @@ namespace mongo {
bool repairDatabase(string db, string &errmsg, bool preserveClonedFilesOnFailure = false, bool backupOriginalFiles = false);
bool userCreateNS(const char *ns, BSONObj j, string& err, bool logForReplication, bool *deferIdIndex = 0);
- shared_ptr<Cursor> findTableScan(const char *ns, const BSONObj& order, const DiskLoc &startLoc=DiskLoc());
bool isValidNS( const StringData& ns );
@@ -109,7 +106,6 @@ namespace mongo {
bool god = false,
bool mayAddIndex = true,
bool* addedID = 0);
- static shared_ptr<Cursor> findAll(const StringData& ns, const DiskLoc &startLoc = DiskLoc());
/* special version of insert for transaction logging -- streamlined a bit.
assumes ns is capped and no indexes
diff --git a/src/mongo/db/pipeline/pipeline_d.cpp b/src/mongo/db/pipeline/pipeline_d.cpp
index e633edcd398..30ce9c29962 100644
--- a/src/mongo/db/pipeline/pipeline_d.cpp
+++ b/src/mongo/db/pipeline/pipeline_d.cpp
@@ -31,7 +31,6 @@
#include "mongo/db/pipeline/pipeline_d.h"
#include "mongo/client/dbclientinterface.h"
-#include "mongo/db/cursor.h"
#include "mongo/db/instance.h"
#include "mongo/db/parsed_query.h"
#include "mongo/db/pipeline/document_source.h"
diff --git a/src/mongo/db/query_optimizer.cpp b/src/mongo/db/query_optimizer.cpp
deleted file mode 100644
index f514f77f3f3..00000000000
--- a/src/mongo/db/query_optimizer.cpp
+++ /dev/null
@@ -1,84 +0,0 @@
-/**
- * Copyright (C) 2011 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/query_optimizer.h"
-
-#include "mongo/db/query_optimizer_internal.h"
-#include "mongo/db/queryoptimizercursorimpl.h"
-#include "mongo/db/queryutil.h"
-
-namespace mongo {
-
- shared_ptr<Cursor> getOptimizedCursor( const StringData& ns,
- const BSONObj& query,
- const BSONObj& order,
- const QueryPlanSelectionPolicy& planPolicy,
- const shared_ptr<const ParsedQuery>& parsedQuery,
- bool requireOrder,
- QueryPlanSummary* singlePlanSummary ) {
- CursorGenerator generator( ns,
- query,
- order,
- planPolicy,
- parsedQuery,
- requireOrder,
- singlePlanSummary );
- return generator.generate();
- }
-
- shared_ptr<Cursor> getBestGuessCursor( const char* ns,
- const BSONObj& query,
- const BSONObj& sort ) {
-
- auto_ptr<FieldRangeSetPair> frsp( new FieldRangeSetPair( ns, query, true ) );
- auto_ptr<FieldRangeSetPair> origFrsp( new FieldRangeSetPair( *frsp ) );
-
- scoped_ptr<QueryPlanSet> qps( QueryPlanSet::make( ns,
- frsp,
- origFrsp,
- query,
- sort,
- shared_ptr<const ParsedQuery>(),
- BSONObj(),
- QueryPlanGenerator::UseIfInOrder,
- BSONObj(),
- BSONObj(),
- true ) );
- QueryPlanSet::QueryPlanPtr qpp = qps->getBestGuess();
- if( ! qpp.get() ) return shared_ptr<Cursor>();
-
- shared_ptr<Cursor> ret = qpp->newCursor();
-
- // If we don't already have a matcher, supply one.
- if ( !query.isEmpty() && ! ret->matcher() ) {
- ret->setMatcher( qpp->matcher() );
- }
- return ret;
- }
-
-} // namespace mongo;
diff --git a/src/mongo/db/query_optimizer.h b/src/mongo/db/query_optimizer.h
deleted file mode 100644
index 1a1c0a24afe..00000000000
--- a/src/mongo/db/query_optimizer.h
+++ /dev/null
@@ -1,96 +0,0 @@
-/**
- * Copyright (C) 2008 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 "mongo/db/query_plan_selection_policy.h"
-
-namespace mongo {
-
- class Cursor;
- class ParsedQuery;
- struct QueryPlanSummary;
-
- /**
- * @return a cursor interface to the query optimizer. The implementation may utilize a
- * single query plan or interleave results from multiple query plans before settling on a
- * single query plan. Note that the schema of currKey() documents, indexKeyPattern(), the
- * matcher(), and the isMultiKey() nature of the cursor may change over the course of
- * iteration.
- *
- * @param query - Query used to select indexes and populate matchers; not copied if unowned
- * (see bsonobj.h).
- *
- * @param order - Required ordering spec for documents produced by this cursor, empty object
- * default indicates no order requirement. If no index exists that satisfies the required
- * sort order, an empty shared_ptr is returned unless parsedQuery is also provided. This is
- * not copied if unowned.
- *
- * @param planPolicy - A policy for selecting query plans - see queryoptimizercursor.h
- *
- * @param parsedQuery - Additional query parameters, as from a client query request.
- *
- * @param requireOrder - If false, the resulting cursor may return results in an order
- * inconsistent with the @param order spec. See queryoptimizercursor.h for information on
- * handling these results properly.
- *
- * @param singlePlanSummary - Query plan summary information that may be provided when a
- * cursor running a single plan is returned.
- *
- * The returned cursor may @throw inside of advance() or recoverFromYield() in certain error
- * cases, for example if a capped overrun occurred during a yield. This indicates that the
- * cursor was unable to perform a complete scan.
- *
- * This is a work in progress. Partial list of features not yet implemented through this
- * interface:
- *
- * - covered indexes
- * - in memory sorting
- */
- shared_ptr<Cursor> getOptimizedCursor( const StringData& ns,
- const BSONObj& query,
- const BSONObj& order = BSONObj(),
- const QueryPlanSelectionPolicy& planPolicy =
- QueryPlanSelectionPolicy::any(),
- const shared_ptr<const ParsedQuery>& parsedQuery =
- shared_ptr<const ParsedQuery>(),
- bool requireOrder = true,
- QueryPlanSummary* singlePlanSummary = NULL );
-
- /**
- * @return a single cursor that may work well for the given query. A $or style query will
- * produce a single cursor, not a MultiCursor.
- * It is possible no cursor is returned if the sort is not supported by an index. Clients
- * are responsible for checking this if they are not sure an index for a sort exists, and
- * defaulting to a non-sort if no suitable indices exist.
- */
- shared_ptr<Cursor> getBestGuessCursor( const char* ns,
- const BSONObj& query,
- const BSONObj& sort );
-
-} // namespace mongo
diff --git a/src/mongo/db/query_optimizer_internal.cpp b/src/mongo/db/query_optimizer_internal.cpp
deleted file mode 100644
index df1cf1aaecf..00000000000
--- a/src/mongo/db/query_optimizer_internal.cpp
+++ /dev/null
@@ -1,1645 +0,0 @@
-/**
- * Copyright (C) 2008 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/pch.h"
-
-#include "mongo/db/query_optimizer_internal.h"
-
-#include "mongo/client/dbclientinterface.h"
-#include "mongo/db/db.h"
-#include "mongo/db/index/catalog_hack.h"
-#include "mongo/db/index_selection.h"
-#include "mongo/db/pagefault.h"
-#include "mongo/db/parsed_query.h"
-#include "mongo/db/query_plan_selection_policy.h"
-#include "mongo/db/queryutil.h"
-#include "mongo/db/structure/collection.h"
-
-//#define DEBUGQO(x) cout << x << endl;
-#define DEBUGQO(x)
-
-namespace mongo {
-
- // returns an IndexDetails* for a hint, 0 if hint is $natural.
- // hint must not be eoo()
- IndexDetails* parseHint( const BSONElement& hint, NamespaceDetails* d ) {
- massert( 13292, "hint eoo", !hint.eoo() );
- if ( hint.type() == String ) {
- string hintstr = hint.valuestr();
- NamespaceDetails::IndexIterator i = d->ii();
- while( i.more() ) {
- IndexDetails& ii = i.next();
- if ( ii.indexName() == hintstr ) {
- return &ii;
- }
- }
- }
- else if ( hint.type() == Object ) {
- BSONObj hintobj = hint.embeddedObject();
- uassert( 10112, "bad hint", !hintobj.isEmpty() );
- if ( !strcmp( hintobj.firstElementFieldName(), "$natural" ) ) {
- return 0;
- }
- NamespaceDetails::IndexIterator i = d->ii();
- while( i.more() ) {
- IndexDetails& ii = i.next();
- if( ii.keyPattern().woCompare(hintobj) == 0 ) {
- return &ii;
- }
- }
- }
- uassert( 10113, "bad hint", false );
- return 0;
- }
-
- CachedMatchCounter::CachedMatchCounter( long long& aggregateNscanned,
- int cumulativeCount ) :
- _aggregateNscanned( aggregateNscanned ),
- _nscanned(),
- _cumulativeCount( cumulativeCount ),
- _count(),
- _checkDups(),
- _match( Unknown ),
- _counted() {
- }
-
- void CachedMatchCounter::resetMatch() {
- _match = Unknown;
- _counted = false;
- }
-
- bool CachedMatchCounter::setMatch( bool match ) {
- MatchState oldMatch = _match;
- _match = match ? True : False;
- return _match == True && oldMatch != True;
- }
-
- void CachedMatchCounter::incMatch( const DiskLoc& loc ) {
- if ( !_counted && _match == True && !getsetdup( loc ) ) {
- ++_cumulativeCount;
- ++_count;
- _counted = true;
- }
- }
-
- bool CachedMatchCounter::wouldIncMatch( const DiskLoc& loc ) const {
- return !_counted && _match == True && !getdup( loc );
- }
-
- bool CachedMatchCounter::enoughCumulativeMatchesToChooseAPlan() const {
- // This is equivalent to the default condition for switching from
- // a query to a getMore, which was the historical default match count for
- // choosing a plan.
- return _cumulativeCount >= 101;
- }
-
- bool CachedMatchCounter::enoughMatchesToRecordPlan() const {
- // Recording after 50 matches is a historical default (101 default limit / 2).
- return _count > 50;
- }
-
- void CachedMatchCounter::updateNscanned( long long nscanned ) {
- _aggregateNscanned += ( nscanned - _nscanned );
- _nscanned = nscanned;
- }
-
- bool CachedMatchCounter::getsetdup( const DiskLoc& loc ) {
- if ( !_checkDups ) {
- return false;
- }
- pair<set<DiskLoc>::iterator, bool> p = _dups.insert( loc );
- return !p.second;
- }
-
- bool CachedMatchCounter::getdup( const DiskLoc& loc ) const {
- if ( !_checkDups ) {
- return false;
- }
- return _dups.find( loc ) != _dups.end();
- }
-
- QueryPlanRunner::QueryPlanRunner( long long& aggregateNscanned,
- const QueryPlanSelectionPolicy& selectionPolicy,
- const bool& requireOrder,
- bool alwaysCountMatches,
- int cumulativeCount ) :
- _complete(),
- _stopRequested(),
- _queryPlan(),
- _error(),
- _matchCounter( aggregateNscanned, cumulativeCount ),
- _countingMatches(),
- _mustAdvance(),
- _capped(),
- _selectionPolicy( selectionPolicy ),
- _requireOrder( requireOrder ),
- _alwaysCountMatches( alwaysCountMatches ) {
- }
-
- void QueryPlanRunner::next() {
- checkCursorOrdering();
-
- mayAdvance();
-
- if ( countMatches() && _matchCounter.enoughCumulativeMatchesToChooseAPlan() ) {
- setStop();
- if ( _explainPlanInfo ) _explainPlanInfo->notePicked();
- return;
- }
- if ( !_c || !_c->ok() ) {
- if ( _explainPlanInfo && _c ) _explainPlanInfo->noteDone( *_c );
- setComplete();
- return;
- }
-
- _mustAdvance = true;
- }
-
- long long QueryPlanRunner::nscanned() const {
- return _c ? _c->nscanned() : _matchCounter.nscanned();
- }
-
- void QueryPlanRunner::prepareToYield() {
- if ( _c && !_cc ) {
- _cc.reset( new ClientCursor( QueryOption_NoCursorTimeout, _c, queryPlan().ns() ) );
- // Set 'doing deletes' as deletes may occur; if there are no deletes this has no
- // effect.
- _cc->setDoingDeletes( true );
- }
- if ( _cc ) {
- recordCursorLocation();
- _cc->prepareToYield( _yieldData );
- }
- }
-
- void QueryPlanRunner::recoverFromYield() {
- if ( _cc && !ClientCursor::recoverFromYield( _yieldData ) ) {
- // !!! The collection may be gone, and any namespace or index specific memory may
- // have become invalid.
- _c.reset();
- _cc.reset();
-
- if ( _capped ) {
- msgassertedNoTrace( 13338,
- str::stream() << "capped cursor overrun: "
- << queryPlan().ns() );
- }
- msgassertedNoTrace( 15892,
- str::stream() <<
- "QueryPlanRunner::recoverFromYield() failed to recover" );
- }
- else {
- checkCursorAdvanced();
- }
- }
-
- void QueryPlanRunner::prepareToTouchEarlierIterate() {
- recordCursorLocation();
- if ( _c ) {
- _c->prepareToTouchEarlierIterate();
- }
- }
-
- void QueryPlanRunner::recoverFromTouchingEarlierIterate() {
- if ( _c ) {
- _c->recoverFromTouchingEarlierIterate();
- }
- checkCursorAdvanced();
- }
-
- bool QueryPlanRunner::currentMatches( MatchDetails* details ) {
- if ( !_c || !_c->ok() ) {
- _matchCounter.setMatch( false );
- return false;
- }
-
- MatchDetails myDetails;
- if ( !details && _explainPlanInfo ) {
- details = &myDetails;
- }
-
- bool match = queryPlan().matcher()->matchesCurrent( _c.get(), details );
- // Cache the match, so we can count it in mayAdvance().
- bool newMatch = _matchCounter.setMatch( match );
-
- if ( _explainPlanInfo ) {
- // Note iterate results as if this is the only query plan running. But do not account
- // for query parameters that may be appled to the whole result set (results from
- // interleaved plans), for example the 'skip' parameter.
- bool countableMatch = newMatch && _matchCounter.wouldIncMatch( _c->currLoc() );
- bool matchWouldBeLoadedForReturn = countableMatch && hasDocumentLoadingQueryPlan();
- _explainPlanInfo->noteIterate( countableMatch,
- details->hasLoadedRecord() ||
- matchWouldBeLoadedForReturn,
- *_c );
- }
-
- return match;
- }
-
- bool QueryPlanRunner::mayRecordPlan() const {
- return complete() && ( !stopRequested() || _matchCounter.enoughMatchesToRecordPlan() );
- }
-
- QueryPlanRunner* QueryPlanRunner::createChild() const {
- return new QueryPlanRunner( _matchCounter.aggregateNscanned(),
- _selectionPolicy,
- _requireOrder,
- _alwaysCountMatches,
- _matchCounter.cumulativeCount() );
- }
-
- void QueryPlanRunner::setQueryPlan( const QueryPlan* queryPlan ) {
- _queryPlan = queryPlan;
- verify( _queryPlan != NULL );
- }
-
- void QueryPlanRunner::init() {
- checkCursorOrdering();
- if ( !_selectionPolicy.permitPlan( queryPlan() ) ) {
- throw MsgAssertionException( 9011,
- str::stream()
- << "Plan not permitted by query plan selection policy '"
- << _selectionPolicy.name()
- << "'" );
- }
-
- _c = queryPlan().newCursor();
- // The basic and btree cursors used by this implementation do not supply their own
- // matchers, and a matcher from a query plan will be used instead.
- verify( !_c->matcher() );
- // Such cursors all support deduplication.
- verify( _c->autoDedup() );
-
- // The query plan must have a matcher. The matcher's constructor performs some aspects
- // of query validation that should occur as part of this class's init() if not handled
- // already.
- fassert( 16249, queryPlan().matcher() );
-
- // All candidate cursors must support yields for QueryOptimizerCursorImpl's
- // prepareToYield() and prepareToTouchEarlierIterate() to work.
- verify( _c->supportYields() );
- _capped = _c->capped();
-
- // TODO This violates the current Cursor interface abstraction, but for now it's simpler to keep our own set of
- // dups rather than avoid poisoning the cursor's dup set with unreturned documents. Deduping documents
- // matched in this QueryOptimizerCursorOp will run against the takeover cursor.
- _matchCounter.setCheckDups( countMatches() && _c->isMultiKey() );
- // TODO ok if cursor becomes multikey later?
-
- _matchCounter.updateNscanned( _c->nscanned() );
- }
-
- void QueryPlanRunner::setException( const DBException& e ) {
- _error = true;
- _exception = e.getInfo();
- }
-
- shared_ptr<ExplainPlanInfo> QueryPlanRunner::generateExplainInfo() {
- if ( !_c ) {
- return shared_ptr<ExplainPlanInfo>( new ExplainPlanInfo() );
- }
- _explainPlanInfo.reset( new ExplainPlanInfo() );
- _explainPlanInfo->notePlan( *_c,
- queryPlan().scanAndOrderRequired(),
- queryPlan().keyFieldsOnly() );
- return _explainPlanInfo;
- }
-
- void QueryPlanRunner::mayAdvance() {
- if ( !_c ) {
- return;
- }
- if ( countingMatches() ) {
- // Check match if not yet known.
- if ( !_matchCounter.knowMatch() ) {
- currentMatches( 0 );
- }
- _matchCounter.incMatch( currLoc() );
- }
- if ( _mustAdvance ) {
- _c->advance();
- handleCursorAdvanced();
- }
- _matchCounter.updateNscanned( _c->nscanned() );
- }
-
- bool QueryPlanRunner::countingMatches() {
- if ( _countingMatches ) {
- return true;
- }
- if ( countMatches() ) {
- // Only count matches after the first call to next(), which occurs before the first
- // result is returned.
- _countingMatches = true;
- }
- return false;
- }
-
- bool QueryPlanRunner::countMatches() const {
- return _alwaysCountMatches || !queryPlan().scanAndOrderRequired();
- }
-
- bool QueryPlanRunner::hasDocumentLoadingQueryPlan() const {
- if ( queryPlan().parsedQuery() && queryPlan().parsedQuery()->returnKey() ) {
- // Index keys will be returned using $returnKey.
- return false;
- }
- if ( queryPlan().scanAndOrderRequired() ) {
- // The in memory sort implementation operates on full documents.
- return true;
- }
- if ( keyFieldsOnly() ) {
- // A covered index projection will be used.
- return false;
- }
- // Documents will be loaded for a standard query.
- return true;
- }
-
- void QueryPlanRunner::recordCursorLocation() {
- _posBeforeYield = currLoc();
- }
-
- void QueryPlanRunner::checkCursorAdvanced() {
- // This check will not correctly determine if we are looking at a different document in
- // all cases, but it is adequate for updating the query plan's match count (just used to pick
- // plans, not returned to the client) and adjust iteration via _mustAdvance.
- if ( _posBeforeYield != currLoc() ) {
- // If the yield advanced our position, the next next() will be a no op.
- handleCursorAdvanced();
- }
- }
-
- void QueryPlanRunner::handleCursorAdvanced() {
- _mustAdvance = false;
- _matchCounter.resetMatch();
- }
-
- void QueryPlanRunner::checkCursorOrdering() {
- if ( _requireOrder && queryPlan().scanAndOrderRequired() ) {
- throw MsgAssertionException( OutOfOrderDocumentsAssertionCode, "order spec cannot be satisfied with index" );
- }
- }
-
- QueryPlanGenerator::QueryPlanGenerator( QueryPlanSet& qps,
- auto_ptr<FieldRangeSetPair> originalFrsp,
- const shared_ptr<const ParsedQuery>& parsedQuery,
- const BSONObj& hint,
- RecordedPlanPolicy recordedPlanPolicy,
- const BSONObj& min,
- const BSONObj& max,
- bool allowSpecial ) :
- _qps( qps ),
- _originalFrsp( originalFrsp ),
- _parsedQuery( parsedQuery ),
- _hint( hint.getOwned() ),
- _recordedPlanPolicy( recordedPlanPolicy ),
- _min( min.getOwned() ),
- _max( max.getOwned() ),
- _allowSpecial( allowSpecial ) {
- }
-
- void QueryPlanGenerator::addInitialPlans() {
- const char* ns = _qps.frsp().ns();
- NamespaceDetails* d = nsdetails( ns );
-
- if ( addShortCircuitPlan( d ) ) {
- return;
- }
-
- addStandardPlans( d );
- warnOnCappedIdTableScan();
- }
-
- void QueryPlanGenerator::addFallbackPlans() {
- const char* ns = _qps.frsp().ns();
- NamespaceDetails* d = nsdetails( ns );
- verify( d );
-
- vector<shared_ptr<QueryPlan> > plans;
- shared_ptr<QueryPlan> optimalPlan;
- shared_ptr<QueryPlan> specialPlan;
- for( int i = 0; i < d->getCompletedIndexCount(); ++i ) {
-
- if ( !QueryUtilIndexed::indexUseful( _qps.frsp(), d, i, _qps.order() ) ) {
- continue;
- }
-
- shared_ptr<QueryPlan> p = newPlan( d, i );
- switch( p->utility() ) {
- case QueryPlan::Impossible:
- _qps.setSinglePlan( p );
- return;
- case QueryPlan::Optimal:
- if ( !optimalPlan ) {
- optimalPlan = p;
- }
- break;
- case QueryPlan::Helpful:
- if ( p->special().empty() ) {
- // Not a 'special' plan.
- plans.push_back( p );
- }
- else if ( _allowSpecial ) {
- specialPlan = p;
- }
- break;
- default:
- break;
- }
- }
-
- if ( optimalPlan ) {
- _qps.setSinglePlan( optimalPlan );
- // Record an optimal plan in the query cache immediately, with a small nscanned value
- // that will be ignored.
- optimalPlan->registerSelf
- ( 0, CandidatePlanCharacter( !optimalPlan->scanAndOrderRequired(),
- optimalPlan->scanAndOrderRequired() ) );
- return;
- }
-
- // Only add a special plan if no standard btree plans have been added. SERVER-4531
- if ( plans.empty() && specialPlan ) {
- _qps.setSinglePlan( specialPlan );
- return;
- }
-
- for( vector<shared_ptr<QueryPlan> >::const_iterator i = plans.begin(); i != plans.end();
- ++i ) {
- _qps.addCandidatePlan( *i );
- }
-
- _qps.addCandidatePlan( newPlan( d, -1 ) );
- }
-
- bool QueryPlanGenerator::addShortCircuitPlan( NamespaceDetails* d ) {
- return
- // The collection is missing.
- setUnindexedPlanIf( !d, d ) ||
- // No match is possible.
- setUnindexedPlanIf( !_qps.frsp().matchPossible(), d ) ||
- // The hint, min, or max parameters are specified.
- addHintPlan( d ) ||
- // A special index operation is requested.
- addSpecialPlan( d ) ||
- // No indexable ranges or ordering are specified.
- setUnindexedPlanIf( _qps.frsp().noNonUniversalRanges() && _qps.order().isEmpty(), d ) ||
- // $natural sort is requested.
- setUnindexedPlanIf( !_qps.order().isEmpty() &&
- str::equals( _qps.order().firstElementFieldName(), "$natural" ),
- d );
- }
-
- bool QueryPlanGenerator::addHintPlan( NamespaceDetails* d ) {
- BSONElement hint = _hint.firstElement();
- if ( !hint.eoo() ) {
- IndexDetails* id = parseHint( hint, d );
- if ( id ) {
- setHintedPlanForIndex( *id );
- }
- else {
- uassert( 10366, "natural order cannot be specified with $min/$max",
- _min.isEmpty() && _max.isEmpty() );
- setSingleUnindexedPlan( d );
- }
- return true;
- }
-
- if ( !_min.isEmpty() || !_max.isEmpty() ) {
- string errmsg;
- BSONObj keyPattern;
- IndexDetails *idx = indexDetailsForRange( _qps.frsp().ns(),
- errmsg,
- _min,
- _max,
- keyPattern );
- uassert( 10367, errmsg, idx );
- validateAndSetHintedPlan( newPlan( d, d->idxNo( *idx ), _min, _max ) );
- return true;
- }
-
- return false;
- }
-
- bool QueryPlanGenerator::addSpecialPlan( NamespaceDetails* d ) {
- DEBUGQO( "\t special : " << _qps.frsp().getSpecial().toString() );
- SpecialIndices special = _qps.frsp().getSpecial();
- if (!special.empty()) {
- // Try to handle the special part of the query with an index
- NamespaceDetails::IndexIterator i = d->ii();
- while( i.more() ) {
- int j = i.pos();
- IndexDetails& ii = i.next();
- BSONObj keyPattern = ii.keyPattern();
- string pluginName = CatalogHack::getAccessMethodName(keyPattern);
- if (special.has(pluginName) &&
- (USELESS != IndexSelection::isSuitableFor(keyPattern,
- _qps.frsp().frsForIndex(d, j), _qps.order()))) {
- uassert( 16330, "'special' query operator not allowed", _allowSpecial );
- _qps.setSinglePlan( newPlan( d, j, BSONObj(), BSONObj(), pluginName));
- return true;
- }
- }
- // If all possible special indices require an index and we don't have one,
- // error.
- if (special.allRequireIndex()) {
- uassert(13038, "can't find any special indices: " + special.toString()
- + " for: " + _qps.originalQuery().toString(), false );
- }
- // Otherwise, we can get the same functionality from the matcher.
- }
- return false;
- }
-
- void QueryPlanGenerator::addStandardPlans( NamespaceDetails* d ) {
- if ( !addCachedPlan( d ) ) {
- addFallbackPlans();
- }
- }
-
- bool QueryPlanGenerator::addCachedPlan( NamespaceDetails* d ) {
- if ( _recordedPlanPolicy == Ignore ) {
- return false;
- }
-
- CachedQueryPlan best = QueryUtilIndexed::bestIndexForPatterns( _qps.frsp(), _qps.order() );
- BSONObj bestIndex = best.indexKey();
- if ( bestIndex.isEmpty() ) {
- return false;
- }
-
- shared_ptr<QueryPlan> p;
- if ( str::equals( bestIndex.firstElementFieldName(), "$natural" ) ) {
- p = newPlan( d, -1 );
- }
-
- NamespaceDetails::IndexIterator i = d->ii();
- while( i.more() ) {
- int j = i.pos();
- IndexDetails& ii = i.next();
- if( ii.keyPattern().woCompare(bestIndex) == 0 ) {
- p = newPlan( d, j );
- }
- }
-
- massert( 10368, "Unable to locate previously recorded index", p );
-
- if ( p->utility() == QueryPlan::Unhelpful ||
- p->utility() == QueryPlan::Disallowed ) {
- return false;
- }
-
- if ( _recordedPlanPolicy == UseIfInOrder && p->scanAndOrderRequired() ) {
- return false;
- }
-
- if ( !_allowSpecial && !p->special().empty() ) {
- return false;
- }
-
- _qps.setCachedPlan( p, best );
- return true;
- }
-
- shared_ptr<QueryPlan> QueryPlanGenerator::newPlan( NamespaceDetails* d,
- int idxNo,
- const BSONObj& min,
- const BSONObj& max,
- const string& special ) const {
- shared_ptr<QueryPlan> ret( QueryPlan::make( d,
- idxNo,
- _qps.frsp(),
- _originalFrsp.get(),
- _qps.originalQuery(),
- _qps.order(),
- _parsedQuery,
- min,
- max,
- special ) );
- return ret;
- }
-
- bool QueryPlanGenerator::setUnindexedPlanIf( bool set, NamespaceDetails* d ) {
- if ( set ) {
- setSingleUnindexedPlan( d );
- }
- return set;
- }
-
- void QueryPlanGenerator::setSingleUnindexedPlan( NamespaceDetails* d ) {
- _qps.setSinglePlan( newPlan( d, -1 ) );
- }
-
- void QueryPlanGenerator::setHintedPlanForIndex( IndexDetails& id ) {
- if ( !_min.isEmpty() || !_max.isEmpty() ) {
- string errmsg;
- BSONObj keyPattern = id.keyPattern();
- // This reformats _min and _max to be used for index lookup.
- massert( 10365 , errmsg, indexDetailsForRange( _qps.frsp().ns(),
- errmsg,
- _min,
- _max,
- keyPattern ) );
- }
- NamespaceDetails* d = nsdetails( _qps.frsp().ns() );
- validateAndSetHintedPlan( newPlan( d, d->idxNo( id ), _min, _max ) );
- }
-
- void QueryPlanGenerator::validateAndSetHintedPlan( const shared_ptr<QueryPlan>& plan ) {
- uassert( 16331, "'special' plan hint not allowed",
- _allowSpecial || plan->special().empty() );
- _qps.setSinglePlan( plan );
- }
-
- void QueryPlanGenerator::warnOnCappedIdTableScan() const {
- // if we are doing a table scan on _id
- // and it's a capped collection
- // we warn as it's a common user error
- // .system. and local collections are exempt
- const char* ns = _qps.frsp().ns();
- NamespaceDetails* d = nsdetails( ns );
- if ( d &&
- d->isCapped() &&
- _qps.nPlans() == 1 &&
- ( _qps.firstPlan()->utility() != QueryPlan::Impossible ) &&
- !_qps.firstPlan()->indexed() &&
- !_qps.firstPlan()->multikeyFrs().range( "_id" ).universal() ) {
- if ( !str::contains( ns , ".system." ) && !str::startsWith( ns , "local." ) ) {
- warning() << "unindexed _id query on capped collection, "
- << "performance will be poor collection: " << ns << endl;
- }
- }
- }
-
- QueryPlanSet* QueryPlanSet::make( const char* ns,
- auto_ptr<FieldRangeSetPair> frsp,
- auto_ptr<FieldRangeSetPair> originalFrsp,
- const BSONObj& originalQuery,
- const BSONObj& order,
- const shared_ptr<const ParsedQuery>& parsedQuery,
- const BSONObj& hint,
- QueryPlanGenerator::RecordedPlanPolicy recordedPlanPolicy,
- const BSONObj& min,
- const BSONObj& max,
- bool allowSpecial ) {
- auto_ptr<QueryPlanSet> ret( new QueryPlanSet( ns,
- frsp,
- originalFrsp,
- originalQuery,
- order,
- parsedQuery,
- hint,
- recordedPlanPolicy,
- min,
- max,
- allowSpecial ) );
- ret->init();
- return ret.release();
- }
-
-
- QueryPlanSet::QueryPlanSet( const char* ns,
- auto_ptr<FieldRangeSetPair> frsp,
- auto_ptr<FieldRangeSetPair> originalFrsp,
- const BSONObj& originalQuery,
- const BSONObj& order,
- const shared_ptr<const ParsedQuery>& parsedQuery,
- const BSONObj& hint,
- QueryPlanGenerator::RecordedPlanPolicy recordedPlanPolicy,
- const BSONObj& min,
- const BSONObj& max,
- bool allowSpecial ) :
- _generator( *this,
- originalFrsp,
- parsedQuery,
- hint,
- recordedPlanPolicy,
- min,
- max,
- allowSpecial ),
- _originalQuery( originalQuery ),
- _frsp( frsp ),
- _mayRecordPlan(),
- _usingCachedPlan(),
- _order( order.getOwned() ),
- _oldNScanned( 0 ),
- _yieldSometimesTracker( 256, 20 ),
- _allowSpecial( allowSpecial ) {
- }
-
- bool QueryPlanSet::hasMultiKey() const {
- for( PlanVector::const_iterator i = _plans.begin(); i != _plans.end(); ++i )
- if ( (*i)->isMultiKey() )
- return true;
- return false;
- }
-
- void QueryPlanSet::init() {
- DEBUGQO( "QueryPlanSet::init " << ns << "\t" << _originalQuery );
- _plans.clear();
- _usingCachedPlan = false;
-
- _generator.addInitialPlans();
- }
-
- void QueryPlanSet::setSinglePlan( const QueryPlanPtr& plan ) {
- if ( nPlans() == 0 ) {
- pushPlan( plan );
- }
- }
-
- void QueryPlanSet::setCachedPlan( const QueryPlanPtr& plan,
- const CachedQueryPlan& cachedPlan ) {
- verify( nPlans() == 0 );
- _usingCachedPlan = true;
- _oldNScanned = cachedPlan.nScanned();
- _cachedPlanCharacter = cachedPlan.planCharacter();
- pushPlan( plan );
- }
-
- void QueryPlanSet::addCandidatePlan( const QueryPlanPtr& plan ) {
- // If _plans is nonempty, the new plan may be supplementing a recorded plan at the first
- // position of _plans. It must not duplicate the first plan.
- if ( nPlans() > 0 && plan->indexKey() == firstPlan()->indexKey() ) {
- return;
- }
- pushPlan( plan );
- _mayRecordPlan = true;
- }
-
- void QueryPlanSet::addFallbackPlans() {
- _generator.addFallbackPlans();
- _mayRecordPlan = true;
- }
-
- void QueryPlanSet::pushPlan( const QueryPlanSet::QueryPlanPtr& plan ) {
- verify( _allowSpecial || plan->special().empty() );
- _plans.push_back( plan );
- }
-
- bool QueryPlanSet::hasPossiblyExcludedPlans() const {
- return
- _usingCachedPlan &&
- ( nPlans() == 1 ) &&
- ( firstPlan()->utility() != QueryPlan::Optimal );
- }
-
- QueryPlanSet::QueryPlanPtr QueryPlanSet::getBestGuess() const {
- verify( _plans.size() );
- if ( _plans[ 0 ]->scanAndOrderRequired() ) {
- for ( unsigned i=1; i<_plans.size(); i++ ) {
- if ( ! _plans[i]->scanAndOrderRequired() )
- return _plans[i];
- }
-
- warning() << "best guess query plan requested, but scan and order are required for all "
- "plans "
- << " query: " << _originalQuery
- << " order: " << _order
- << " choices: ";
-
- for ( unsigned i=0; i<_plans.size(); i++ )
- warning() << _plans[i]->indexKey() << " ";
- warning() << endl;
-
- return QueryPlanPtr();
- }
- return _plans[0];
- }
-
- bool QueryPlanSet::haveInOrderPlan() const {
- for( PlanVector::const_iterator i = _plans.begin(); i != _plans.end(); ++i ) {
- if ( !(*i)->scanAndOrderRequired() ) {
- return true;
- }
- }
- return false;
- }
-
- bool QueryPlanSet::possibleInOrderPlan() const {
- if ( haveInOrderPlan() ) {
- return true;
- }
- return _cachedPlanCharacter.mayRunInOrderPlan();
- }
-
- bool QueryPlanSet::possibleOutOfOrderPlan() const {
- for( PlanVector::const_iterator i = _plans.begin(); i != _plans.end(); ++i ) {
- if ( (*i)->scanAndOrderRequired() ) {
- return true;
- }
- }
- return _cachedPlanCharacter.mayRunOutOfOrderPlan();
- }
-
- CandidatePlanCharacter QueryPlanSet::characterizeCandidatePlans() const {
- return CandidatePlanCharacter( possibleInOrderPlan(), possibleOutOfOrderPlan() );
- }
-
- bool QueryPlanSet::prepareToRetryQuery() {
- if ( !hasPossiblyExcludedPlans() || _plans.size() > 1 ) {
- return false;
- }
-
- // A cached plan was used, so clear the plan for this query pattern so the query may be
- // retried without a cached plan.
- QueryUtilIndexed::clearIndexesForPatterns( *_frsp, _order );
- init();
- return true;
- }
-
- string QueryPlanSet::toString() const {
- BSONArrayBuilder bab;
- for( PlanVector::const_iterator i = _plans.begin(); i != _plans.end(); ++i ) {
- bab << (*i)->toString();
- }
- return bab.arr().jsonString();
- }
-
- MultiPlanScanner *MultiPlanScanner::make( const StringData& ns,
- const BSONObj& query,
- const BSONObj& order,
- const shared_ptr<const ParsedQuery>& parsedQuery,
- const BSONObj& hint,
- QueryPlanGenerator::RecordedPlanPolicy
- recordedPlanPolicy,
- const BSONObj& min,
- const BSONObj& max ) {
- auto_ptr<MultiPlanScanner> ret( new MultiPlanScanner( ns,
- query,
- parsedQuery,
- hint,
- recordedPlanPolicy ) );
- ret->init( order, min, max );
- return ret.release();
- }
-
- shared_ptr<QueryPlanRunner> MultiPlanScanner::iterateRunnerQueue
- ( QueryPlanRunner& originalRunner, bool retried ) {
-
- if ( _runnerQueue ) {
- return _runnerQueue->next();
- }
-
- _runnerQueue.reset( new QueryPlanRunnerQueue( *_currentQps, originalRunner ) );
- shared_ptr<ExplainClauseInfo> explainClause;
- if ( _explainQueryInfo ) {
- explainClause = _runnerQueue->generateExplainInfo();
- }
-
- shared_ptr<QueryPlanRunner> runner = _runnerQueue->next();
- if ( runner->error() &&
- _currentQps->prepareToRetryQuery() ) {
-
- // Avoid an infinite loop here - this should never occur.
- verify( !retried );
- _runnerQueue.reset();
- return iterateRunnerQueue( originalRunner, true );
- }
-
- if ( _explainQueryInfo ) {
- _explainQueryInfo->addClauseInfo( explainClause );
- }
- return runner;
- }
-
- void MultiPlanScanner::updateCurrentQps( QueryPlanSet* qps ) {
- _currentQps.reset( qps );
- _runnerQueue.reset();
- }
-
- QueryPlanRunnerQueue::QueryPlanRunnerQueue( QueryPlanSet& plans,
- const QueryPlanRunner& prototypeRunner ) :
- _prototypeRunner( prototypeRunner ),
- _plans( plans ),
- _done() {
- }
-
- void QueryPlanRunnerQueue::prepareToYield() {
- for( vector<shared_ptr<QueryPlanRunner> >::const_iterator i = _runners.begin();
- i != _runners.end(); ++i ) {
- prepareToYieldRunner( **i );
- }
- }
-
- void QueryPlanRunnerQueue::recoverFromYield() {
- for( vector<shared_ptr<QueryPlanRunner> >::const_iterator i = _runners.begin();
- i != _runners.end(); ++i ) {
- recoverFromYieldRunner( **i );
- }
- }
-
- shared_ptr<QueryPlanRunner> QueryPlanRunnerQueue::init() {
- massert( 10369, "no plans", _plans.plans().size() > 0 );
-
- if ( _plans.plans().size() > 1 ) {
- LOG(1) << " running multiple plans" << endl;
- }
- for( QueryPlanSet::PlanVector::const_iterator i = _plans.plans().begin();
- i != _plans.plans().end(); ++i ) {
- shared_ptr<QueryPlanRunner> runner( _prototypeRunner.createChild() );
- runner->setQueryPlan( i->get() );
- _runners.push_back( runner );
- }
-
- // Initialize runners.
- for( vector<shared_ptr<QueryPlanRunner> >::iterator i = _runners.begin();
- i != _runners.end(); ++i ) {
- initRunner( **i );
- if ( _explainClauseInfo ) {
- _explainClauseInfo->addPlanInfo( (*i)->generateExplainInfo() );
- }
- }
-
- // See if an op has completed.
- for( vector<shared_ptr<QueryPlanRunner> >::iterator i = _runners.begin();
- i != _runners.end(); ++i ) {
- if ( (*i)->complete() ) {
- return *i;
- }
- }
-
- // Put runnable ops in the priority queue.
- for( vector<shared_ptr<QueryPlanRunner> >::iterator i = _runners.begin();
- i != _runners.end(); ++i ) {
- if ( !(*i)->error() ) {
- _queue.push( *i );
- }
- }
-
- if ( _queue.empty() ) {
- return _runners.front();
- }
-
- return shared_ptr<QueryPlanRunner>();
- }
-
- shared_ptr<QueryPlanRunner> QueryPlanRunnerQueue::next() {
- verify( !done() );
-
- if ( _runners.empty() ) {
- shared_ptr<QueryPlanRunner> initialRet = init();
- if ( initialRet ) {
- _done = true;
- return initialRet;
- }
- }
-
- shared_ptr<QueryPlanRunner> ret;
- do {
- ret = _next();
- } while( ret->error() && !_queue.empty() );
-
- if ( _queue.empty() ) {
- _done = true;
- }
-
- return ret;
- }
-
- shared_ptr<QueryPlanRunner> QueryPlanRunnerQueue::_next() {
- verify( !_queue.empty() );
- RunnerHolder holder = _queue.pop();
- QueryPlanRunner& runner = *holder._runner;
- nextRunner( runner );
- if ( runner.complete() ) {
- if ( _plans.mayRecordPlan() && runner.mayRecordPlan() ) {
- runner.queryPlan().registerSelf( runner.nscanned(),
- _plans.characterizeCandidatePlans() );
- }
- _done = true;
- return holder._runner;
- }
- if ( runner.error() ) {
- return holder._runner;
- }
- if ( _plans.hasPossiblyExcludedPlans() &&
- runner.nscanned() > _plans.oldNScanned() * 10 ) {
- verify( _plans.nPlans() == 1 && _plans.firstPlan()->special().empty() );
- holder._offset = -runner.nscanned();
- _plans.addFallbackPlans();
- QueryPlanSet::PlanVector::const_iterator i = _plans.plans().begin();
- ++i;
- for( ; i != _plans.plans().end(); ++i ) {
- shared_ptr<QueryPlanRunner> runner( _prototypeRunner.createChild() );
- runner->setQueryPlan( i->get() );
- _runners.push_back( runner );
- initRunner( *runner );
- if ( runner->complete() )
- return runner;
- _queue.push( runner );
- }
- _plans.setUsingCachedPlan( false );
- }
- _queue.push( holder );
- return holder._runner;
- }
-
-#define GUARD_RUNNER_EXCEPTION( runner, expression ) \
- try { \
- expression; \
- } \
- catch ( DBException& e ) { \
- runner.setException( e.getInfo() ); \
- } \
- catch ( const std::exception &e ) { \
- runner.setException( ExceptionInfo( e.what(), 0 ) ); \
- } \
- catch ( PageFaultException& pfe ) { \
- throw pfe; \
- } \
- catch ( ... ) { \
- runner.setException( ExceptionInfo( "Caught unknown exception", 0 ) ); \
- }
-
-
- void QueryPlanRunnerQueue::initRunner( QueryPlanRunner &runner ) {
- GUARD_RUNNER_EXCEPTION( runner, runner.init() );
- }
-
- void QueryPlanRunnerQueue::nextRunner( QueryPlanRunner& runner ) {
- GUARD_RUNNER_EXCEPTION( runner, if ( !runner.error() ) { runner.next(); } );
- }
-
- void QueryPlanRunnerQueue::prepareToYieldRunner( QueryPlanRunner& runner ) {
- GUARD_RUNNER_EXCEPTION( runner, if ( !runner.error() ) { runner.prepareToYield(); } );
- }
-
- void QueryPlanRunnerQueue::recoverFromYieldRunner( QueryPlanRunner& runner ) {
- GUARD_RUNNER_EXCEPTION( runner, if ( !runner.error() ) { runner.recoverFromYield(); } );
- }
-
- /**
- * NOTE on our $or implementation: In our current qo implementation we don't
- * keep statistics on our data, but we can conceptualize the problem of
- * selecting an index when statistics exist for all index ranges. The
- * d-hitting set problem on k sets and n elements can be reduced to the
- * problem of index selection on k $or clauses and n index ranges (where
- * d is the max number of indexes, and the number of ranges n is unbounded).
- * In light of the fact that d-hitting set is np complete, and we don't even
- * track statistics (so cost calculations are expensive) our first
- * implementation uses the following greedy approach: We take one $or clause
- * at a time and treat each as a separate query for index selection purposes.
- * But if an index range is scanned for a particular $or clause, we eliminate
- * that range from all subsequent clauses. One could imagine an opposite
- * implementation where we select indexes based on the union of index ranges
- * for all $or clauses, but this can have much poorer worst case behavior.
- * (An index range that suits one $or clause may not suit another, and this
- * is worse than the typical case of index range choice staleness because
- * with $or the clauses may likely be logically distinct.) The greedy
- * implementation won't do any worse than all the $or clauses individually,
- * and it can often do better. In the first cut we are intentionally using
- * QueryPattern tracking to record successful plans on $or clauses for use by
- * subsequent $or clauses, even though there may be a significant aggregate
- * $nor component that would not be represented in QueryPattern.
- */
-
- MultiPlanScanner::MultiPlanScanner( const StringData& ns,
- const BSONObj& query,
- const shared_ptr<const ParsedQuery>& parsedQuery,
- const BSONObj& hint,
- QueryPlanGenerator::RecordedPlanPolicy recordedPlanPolicy ) :
- _ns( ns.toString() ),
- _or( !query.getField( "$or" ).eoo() ),
- _query( query.getOwned() ),
- _parsedQuery( parsedQuery ),
- _i(),
- _recordedPlanPolicy( recordedPlanPolicy ),
- _hint( hint.getOwned() ),
- _tableScanned(),
- _doneRunners() {
- }
-
- void MultiPlanScanner::init( const BSONObj& order, const BSONObj& min, const BSONObj& max ) {
- if ( !order.isEmpty() || !min.isEmpty() || !max.isEmpty() ) {
- _or = false;
- }
- if ( _or ) {
- // Only construct an OrRangeGenerator if we may handle $or clauses.
- _org.reset( new OrRangeGenerator( _ns.c_str(), _query ) );
- if ( !_org->getSpecial().empty() ) {
- _or = false;
- }
- else if ( haveUselessOr() ) {
- _or = false;
- }
- }
-
- // if _or == false, don't use or clauses for index selection
- if ( !_or ) {
- ++_i;
- auto_ptr<FieldRangeSetPair> frsp( new FieldRangeSetPair( _ns.c_str(), _query, true ) );
- updateCurrentQps( QueryPlanSet::make( _ns.c_str(),
- frsp,
- auto_ptr<FieldRangeSetPair>(),
- _query,
- order,
- _parsedQuery,
- _hint,
- _recordedPlanPolicy,
- min,
- max,
- true ) );
- }
- else {
- BSONElement e = _query.getField( "$or" );
- massert( 13268,
- "invalid $or spec",
- e.type() == Array && e.embeddedObject().nFields() > 0 );
- handleBeginningOfClause();
- }
- }
-
- void MultiPlanScanner::handleEndOfClause( const QueryPlan& clausePlan ) {
- if ( clausePlan.willScanTable() ) {
- _tableScanned = true;
- }
- else {
- _org->popOrClause( clausePlan.nsd(),
- clausePlan.idxNo(),
- clausePlan.indexed() ? clausePlan.indexKey() : BSONObj() );
- }
- }
-
- void MultiPlanScanner::handleBeginningOfClause() {
- assertHasMoreClauses();
- ++_i;
- auto_ptr<FieldRangeSetPair> frsp( _org->topFrsp() );
- auto_ptr<FieldRangeSetPair> originalFrsp( _org->topFrspOriginal() );
- updateCurrentQps( QueryPlanSet::make( _ns.c_str(),
- frsp,
- originalFrsp,
- _query,
- BSONObj(),
- _parsedQuery,
- _hint,
- _recordedPlanPolicy,
- BSONObj(),
- BSONObj(),
- // 'Special' plans are not supported within $or.
- false ) );
- }
-
- bool MultiPlanScanner::mayHandleBeginningOfClause() {
- if ( hasMoreClauses() ) {
- handleBeginningOfClause();
- return true;
- }
- return false;
- }
-
- shared_ptr<QueryPlanRunner> MultiPlanScanner::nextRunner() {
- verify( !doneRunners() );
- shared_ptr<QueryPlanRunner> ret = _or ? nextRunnerOr() : nextRunnerSimple();
- if ( ret->error() || ret->complete() ) {
- _doneRunners = true;
- }
- return ret;
- }
-
- shared_ptr<QueryPlanRunner> MultiPlanScanner::nextRunnerSimple() {
- return iterateRunnerQueue( *_baseRunner );
- }
-
- shared_ptr<QueryPlanRunner> MultiPlanScanner::nextRunnerOr() {
- shared_ptr<QueryPlanRunner> runner;
- do {
- runner = nextRunnerSimple();
- if ( !runner->completeWithoutStop() ) {
- return runner;
- }
- handleEndOfClause( runner->queryPlan() );
- _baseRunner = runner;
- } while( mayHandleBeginningOfClause() );
- return runner;
- }
-
- const QueryPlan *MultiPlanScanner::nextClauseBestGuessPlan( const QueryPlan& currentPlan ) {
- assertHasMoreClauses();
- handleEndOfClause( currentPlan );
- if ( !hasMoreClauses() ) {
- return 0;
- }
- handleBeginningOfClause();
- shared_ptr<QueryPlan> bestGuess = _currentQps->getBestGuess();
- verify( bestGuess );
- return bestGuess.get();
- }
-
- void MultiPlanScanner::prepareToYield() {
- if ( _runnerQueue ) {
- _runnerQueue->prepareToYield();
- }
- }
-
- void MultiPlanScanner::recoverFromYield() {
- if ( _runnerQueue ) {
- _runnerQueue->recoverFromYield();
- }
- }
-
- void MultiPlanScanner::clearRunnerQueue() {
- if ( _runnerQueue ) {
- _runnerQueue.reset();
- }
- }
-
- int MultiPlanScanner::currentNPlans() const {
- return _currentQps->nPlans();
- }
-
- const QueryPlan *MultiPlanScanner::singlePlan() const {
- if ( _or ||
- _currentQps->nPlans() != 1 ||
- _currentQps->hasPossiblyExcludedPlans() ) {
- return 0;
- }
- return _currentQps->firstPlan().get();
- }
-
- bool MultiPlanScanner::hasMoreClauses() const {
- return _or ? ( !_tableScanned && !_org->orRangesExhausted() ) : _i == 0;
- }
-
- bool MultiPlanScanner::haveUselessOr() const {
- NamespaceDetails* nsd = nsdetails( _ns );
- if ( !nsd ) {
- return true;
- }
- BSONElement hintElt = _hint.firstElement();
- if ( !hintElt.eoo() ) {
- IndexDetails* id = parseHint( hintElt, nsd );
- if ( !id ) {
- return true;
- }
- return QueryUtilIndexed::uselessOr( *_org, nsd, nsd->idxNo( *id ) );
- }
- return QueryUtilIndexed::uselessOr( *_org, nsd, -1 );
- }
-
- BSONObj MultiPlanScanner::cachedPlanExplainSummary() const {
- if ( _or || !_currentQps->usingCachedPlan() ) {
- return BSONObj();
- }
- QueryPlanSet::QueryPlanPtr plan = _currentQps->firstPlan();
- shared_ptr<Cursor> cursor = plan->newCursor();
- return BSON( "cursor" << cursor->toString() <<
- "indexBounds" << cursor->prettyIndexBounds() );
- }
-
- void MultiPlanScanner::clearIndexesForPatterns() const {
- QueryUtilIndexed::clearIndexesForPatterns( _currentQps->frsp(), _currentQps->order() );
- }
-
- bool MultiPlanScanner::haveInOrderPlan() const {
- return _or ? true : _currentQps->haveInOrderPlan();
- }
-
- bool MultiPlanScanner::possibleInOrderPlan() const {
- return _or ? true : _currentQps->possibleInOrderPlan();
- }
-
- bool MultiPlanScanner::possibleOutOfOrderPlan() const {
- return _or ? false : _currentQps->possibleOutOfOrderPlan();
- }
-
- string MultiPlanScanner::toString() const {
- return BSON(
- "or" << _or <<
- "currentQps" << _currentQps->toString()
- ).jsonString();
- }
-
- MultiCursor::MultiCursor( auto_ptr<MultiPlanScanner> mps,
- const shared_ptr<Cursor>& c,
- const shared_ptr<CoveredIndexMatcher>& matcher,
- const shared_ptr<ExplainPlanInfo>& explainPlanInfo,
- const QueryPlanRunner& runner,
- long long nscanned ) :
- _mps( mps ),
- _c( c ),
- _matcher( matcher ),
- _queryPlan( &runner.queryPlan() ),
- _nscanned( nscanned ),
- _explainPlanInfo( explainPlanInfo ) {
- _mps->clearRunnerQueue();
- _mps->setRecordedPlanPolicy( QueryPlanGenerator::UseIfInOrder );
- if ( !ok() ) {
- // If the supplied cursor is exhausted, try to advance it.
- advance();
- }
- }
-
- bool MultiCursor::advance() {
- _c->advance();
- advanceExhaustedClauses();
- return ok();
- }
-
- void MultiCursor::recoverFromYield() {
- Cursor::recoverFromYield();
- advanceExhaustedClauses();
- }
-
- void MultiCursor::advanceClause() {
- _nscanned += _c->nscanned();
- if ( _explainPlanInfo ) _explainPlanInfo->noteDone( *_c );
- shared_ptr<FieldRangeVector> oldClauseFrv = _queryPlan->originalFrv();
- _queryPlan = _mps->nextClauseBestGuessPlan( *_queryPlan );
- if ( _queryPlan ) {
- _matcher.reset( _matcher->nextClauseMatcher( oldClauseFrv, _queryPlan->indexKey() ) );
- _c = _queryPlan->newCursor();
- // The basic and btree cursors used by this implementation support deduplication.
- verify( _c->autoDedup() );
- // All sub cursors must support yields.
- verify( _c->supportYields() );
- if ( _explainPlanInfo ) {
- _explainPlanInfo.reset( new ExplainPlanInfo() );
- _explainPlanInfo->notePlan( *_c,
- _queryPlan->scanAndOrderRequired(),
- _queryPlan->keyFieldsOnly() );
- shared_ptr<ExplainClauseInfo> clauseInfo( new ExplainClauseInfo() );
- clauseInfo->addPlanInfo( _explainPlanInfo );
- _mps->addClauseInfo( clauseInfo );
- }
- }
- }
-
- void MultiCursor::advanceExhaustedClauses() {
- while( !ok() && _mps->hasMoreClauses() ) {
- advanceClause();
- }
- }
-
- void MultiCursor::noteIterate( bool match, bool loadedRecord ) {
- if ( _explainPlanInfo ) _explainPlanInfo->noteIterate( match, loadedRecord, *_c );
- }
-
- bool indexWorks( const BSONObj& idxPattern,
- const BSONObj& sampleKey,
- int direction,
- int firstSignificantField ) {
- BSONObjIterator p( idxPattern );
- BSONObjIterator k( sampleKey );
- int i = 0;
- while( 1 ) {
- BSONElement pe = p.next();
- BSONElement ke = k.next();
- if ( pe.eoo() && ke.eoo() )
- return true;
- if ( pe.eoo() || ke.eoo() )
- return false;
- if ( strcmp( pe.fieldName(), ke.fieldName() ) != 0 )
- return false;
- if ( ( i == firstSignificantField ) && !( ( direction > 0 ) == ( pe.number() > 0 ) ) )
- return false;
- ++i;
- }
- return false;
- }
-
- BSONObj extremeKeyForIndex( const BSONObj& idxPattern, int baseDirection ) {
- BSONObjIterator i( idxPattern );
- BSONObjBuilder b;
- while( i.moreWithEOO() ) {
- BSONElement e = i.next();
- if ( e.eoo() )
- break;
- int idxDirection = e.number() >= 0 ? 1 : -1;
- int direction = idxDirection * baseDirection;
- switch( direction ) {
- case 1:
- b.appendMaxKey( e.fieldName() );
- break;
- case -1:
- b.appendMinKey( e.fieldName() );
- break;
- default:
- verify( false );
- }
- }
- return b.obj();
- }
-
- pair<int,int> keyAudit( const BSONObj& min, const BSONObj& max ) {
- int direction = 0;
- int firstSignificantField = 0;
- BSONObjIterator i( min );
- BSONObjIterator a( max );
- while( 1 ) {
- BSONElement ie = i.next();
- BSONElement ae = a.next();
- if ( ie.eoo() && ae.eoo() )
- break;
- if ( ie.eoo() || ae.eoo() || strcmp( ie.fieldName(), ae.fieldName() ) != 0 ) {
- return make_pair( -1, -1 );
- }
- int cmp = ie.woCompare( ae );
- if ( cmp < 0 )
- direction = 1;
- if ( cmp > 0 )
- direction = -1;
- if ( direction != 0 )
- break;
- ++firstSignificantField;
- }
- return make_pair( direction, firstSignificantField );
- }
-
- pair<int,int> flexibleKeyAudit( const BSONObj& min, const BSONObj& max ) {
- if ( min.isEmpty() || max.isEmpty() ) {
- return make_pair( 1, -1 );
- }
- else {
- return keyAudit( min, max );
- }
- }
-
- // NOTE min, max, and keyPattern will be updated to be consistent with the selected index.
- IndexDetails* indexDetailsForRange( const char* ns,
- string& errmsg,
- BSONObj& min,
- BSONObj& max,
- BSONObj& keyPattern ) {
- if ( min.isEmpty() && max.isEmpty() ) {
- errmsg = "one of min or max must be specified";
- return 0;
- }
-
- Client::Context ctx( ns );
- IndexDetails* id = 0;
- NamespaceDetails* d = nsdetails( ns );
- if ( !d ) {
- errmsg = "ns not found";
- return 0;
- }
-
- pair<int,int> ret = flexibleKeyAudit( min, max );
- if ( ret == make_pair( -1, -1 ) ) {
- errmsg = "min and max keys do not share pattern";
- return 0;
- }
- if ( keyPattern.isEmpty() ) {
- NamespaceDetails::IndexIterator i = d->ii();
- while( i.more() ) {
- IndexDetails& ii = i.next();
- if ( indexWorks( ii.keyPattern(), min.isEmpty() ? max : min, ret.first, ret.second ) ) {
- if (CatalogHack::getAccessMethodName(ii.keyPattern()).empty()) {
- id = &ii;
- keyPattern = ii.keyPattern();
- break;
- }
- }
- }
-
- }
- else {
- if ( !indexWorks( keyPattern, min.isEmpty() ? max : min, ret.first, ret.second ) ) {
- errmsg = "requested keyPattern does not match specified keys";
- return 0;
- }
- NamespaceDetails::IndexIterator i = d->ii();
- while( i.more() ) {
- IndexDetails& ii = i.next();
- if( ii.keyPattern().woCompare(keyPattern) == 0 ) {
- id = &ii;
- break;
- }
- if ( keyPattern.nFields() == 1 && ii.keyPattern().nFields() == 1 &&
- IndexDetails::isIdIndexPattern( keyPattern ) &&
- ii.isIdIndex() ) {
- id = &ii;
- break;
- }
-
- }
- }
-
- if ( min.isEmpty() ) {
- min = extremeKeyForIndex( keyPattern, -1 );
- }
- else if ( max.isEmpty() ) {
- max = extremeKeyForIndex( keyPattern, 1 );
- }
-
- if ( !id ) {
- errmsg = str::stream() << "no index found for specified keyPattern: "
- << keyPattern.toString()
- << " min: " << min << " max: " << max;
- return 0;
- }
-
- min = min.extractFieldsUnDotted( keyPattern );
- max = max.extractFieldsUnDotted( keyPattern );
-
- return id;
- }
-
- bool QueryUtilIndexed::indexUseful( const FieldRangeSetPair& frsp,
- NamespaceDetails* d,
- int idxNo,
- const BSONObj& order ) {
- DEV frsp.assertValidIndex( d, idxNo );
- BSONObj keyPattern = d->idx( idxNo ).keyPattern();
- if ( !frsp.matchPossibleForIndex( d, idxNo, keyPattern ) ) {
- // No matches are possible in the index so the index may be useful.
- return true;
- }
- return USELESS != IndexSelection::isSuitableFor(keyPattern, frsp.frsForIndex( d , idxNo ) ,
- order );
- }
-
- void QueryUtilIndexed::clearIndexesForPatterns( const FieldRangeSetPair& frsp,
- const BSONObj& order ) {
- CachedQueryPlan noCachedPlan;
-
- Collection* collection = cc().database()->getCollection( frsp.ns() );
- if ( !collection )
- return;
-
- collection->infoCache()->registerCachedQueryPlanForPattern( frsp._singleKey.pattern( order ),
- noCachedPlan );
- collection->infoCache()->registerCachedQueryPlanForPattern( frsp._multiKey.pattern( order ),
- noCachedPlan );
- }
-
- CachedQueryPlan QueryUtilIndexed::bestIndexForPatterns( const FieldRangeSetPair& frsp,
- const BSONObj& order ) {
-
- Collection* collection = cc().database()->getCollection( frsp.ns() );
- verify( collection );
-
- // TODO Maybe it would make sense to return the index with the lowest
- // nscanned if there are two possibilities.
- {
- QueryPattern pattern = frsp._singleKey.pattern( order );
- CachedQueryPlan cachedQueryPlan = collection->infoCache()->cachedQueryPlanForPattern( pattern );
- if ( !cachedQueryPlan.indexKey().isEmpty() ) {
- return cachedQueryPlan;
- }
- }
- {
- QueryPattern pattern = frsp._multiKey.pattern( order );
- CachedQueryPlan cachedQueryPlan = collection->infoCache()->cachedQueryPlanForPattern( pattern );
- if ( !cachedQueryPlan.indexKey().isEmpty() ) {
- return cachedQueryPlan;
- }
- }
- return CachedQueryPlan();
- }
-
- bool QueryUtilIndexed::uselessOr( const OrRangeGenerator& org,
- NamespaceDetails* d,
- int hintIdx ) {
- for( list<FieldRangeSetPair>::const_iterator i = org._originalOrSets.begin();
- i != org._originalOrSets.end();
- ++i ) {
- if ( hintIdx != -1 ) {
- if ( !indexUseful( *i, d, hintIdx, BSONObj() ) ) {
- return true;
- }
- }
- else {
- bool useful = false;
- for( int j = 0; j < d->getCompletedIndexCount(); ++j ) {
- if ( indexUseful( *i, d, j, BSONObj() ) ) {
- useful = true;
- break;
- }
- }
- if ( !useful ) {
- return true;
- }
- }
- }
- return false;
- }
-
-} // namespace mongo
diff --git a/src/mongo/db/query_optimizer_internal.h b/src/mongo/db/query_optimizer_internal.h
deleted file mode 100644
index b48f69774d2..00000000000
--- a/src/mongo/db/query_optimizer_internal.h
+++ /dev/null
@@ -1,822 +0,0 @@
-/**
- * Copyright (C) 2008 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/base/disallow_copying.h"
-#include "mongo/db/clientcursor.h"
-#include "mongo/db/cursor.h"
-#include "mongo/db/explain.h"
-#include "mongo/db/matcher.h"
-#include "mongo/db/query_plan.h"
-#include "mongo/db/queryutil.h"
-#include "mongo/util/elapsed_tracker.h"
-
-#pragma once
-
-namespace mongo {
-
- static const int OutOfOrderDocumentsAssertionCode = 14810;
-
- class FieldRangeSetPair;
- class QueryPlanSelectionPolicy;
- class OrRangeGenerator;
- class ParsedQuery;
-
- /**
- * Helper class for a QueryPlanRunner to cache and count matches. One object of this type is
- * used per candidate QueryPlan (as there is one QueryPlanRunner per QueryPlan).
- *
- * Typical usage:
- * 1) resetMatch() - reset stored match value to Unkonwn.
- * 2) setMatch() - set match value to a definite true/false value.
- * 3) knowMatch() - check if setMatch() has been called.
- * 4) incMatch() - increment count if match is true.
- */
- class CachedMatchCounter {
- public:
- /**
- * @param aggregateNscanned - shared count of nscanned for this and other plans.
- * @param cumulativeCount - starting point for accumulated count over a series of plans.
- */
- CachedMatchCounter( long long& aggregateNscanned, int cumulativeCount );
-
- /** Set whether dup checking is enabled when counting. */
- void setCheckDups( bool checkDups ) { _checkDups = checkDups; }
-
- void resetMatch();
-
- /** @return true if the match was not previously recorded. */
- bool setMatch( bool match );
-
- bool knowMatch() const { return _match != Unknown; }
-
- void incMatch( const DiskLoc& loc );
-
- bool wouldIncMatch( const DiskLoc& loc ) const;
-
- bool enoughCumulativeMatchesToChooseAPlan() const;
-
- bool enoughMatchesToRecordPlan() const;
-
- int cumulativeCount() const { return _cumulativeCount; }
-
- int count() const { return _count; }
-
- /** Update local and aggregate nscanned counts. */
- void updateNscanned( long long nscanned );
-
- long long nscanned() const { return _nscanned; }
-
- long long& aggregateNscanned() const { return _aggregateNscanned; }
-
- private:
- bool getsetdup( const DiskLoc& loc );
-
- bool getdup( const DiskLoc& loc ) const;
-
- long long& _aggregateNscanned;
- long long _nscanned;
- int _cumulativeCount;
- int _count;
- bool _checkDups;
- enum MatchState { Unknown, False, True };
- MatchState _match;
- bool _counted;
- set<DiskLoc> _dups;
- };
-
- /**
- * Iterates through a QueryPlan's candidate matches, keeping track of accumulated nscanned.
- * Generally used along with runners for other QueryPlans in a QueryPlanRunnerQueue priority
- * queue. Eg if there are three candidate QueryPlans evaluated in parallel, there are three
- * QueryPlanRunners, one checking for matches on each query.
- *
- * Typical usage:
- * 1) A new QueryPlanRunner is generated using createChild().
- * 2) A QueryPlan is assigned using setQueryPlan().
- * 3) init() is called to initialize the runner.
- * 4) next() is called repeatedly, with nscanned() checked after each call.
- * 5) In one of these calls to next(), setComplete() is called internally.
- * 6) The QueryPattern for the QueryPlan may be recorded as a winning plan.
- */
- class QueryPlanRunner {
- MONGO_DISALLOW_COPYING( QueryPlanRunner );
- public:
- /**
- * @param aggregateNscanned Shared long long counting total nscanned for runners for all
- * cursors.
- * @param selectionPolicy Characterizes the set of QueryPlans allowed for this operation.
- * See queryoptimizercursor.h for more information.
- * @param requireOrder Whether only ordered plans are allowed.
- * @param alwaysCountMatches Whether matches are to be counted regardless of ordering.
- * @param cumulativeCount Total count.
- */
- QueryPlanRunner( long long& aggregateNscanned,
- const QueryPlanSelectionPolicy& selectionPolicy,
- const bool& requireOrder,
- bool alwaysCountMatches,
- int cumulativeCount = 0 );
-
- /** @return QueryPlan assigned to this runner by the query optimizer. */
- const QueryPlan& queryPlan() const { return *_queryPlan; }
-
- /** Advance to the next potential matching document (eg using a cursor). */
- void next();
-
- /**
- * @return current 'nscanned' metric for this runner. Used to compare cost to other
- * runners.
- */
- long long nscanned() const;
-
- /** Take any steps necessary before the db mutex is yielded. */
- void prepareToYield();
-
- /** Recover once the db mutex is regained. */
- void recoverFromYield();
-
- /** Take any steps necessary before an earlier iterate of the cursor is modified. */
- void prepareToTouchEarlierIterate();
-
- /** Recover after the earlier iterate is modified. */
- void recoverFromTouchingEarlierIterate();
-
- DiskLoc currLoc() const { return _c ? _c->currLoc() : DiskLoc(); }
-
- BSONObj currKey() const { return _c ? _c->currKey() : BSONObj(); }
-
- bool currentMatches( MatchDetails* details );
-
- /**
- * @return true iff the QueryPlan for this runner may be registered
- * as a winning plan.
- */
- bool mayRecordPlan() const;
-
- shared_ptr<Cursor> cursor() const { return _c; }
-
- /** @return true iff the implementation called setComplete() or setStop(). */
- bool complete() const { return _complete; }
-
- /** @return true iff the implementation called setStop(). */
- bool stopRequested() const { return _stopRequested; }
-
- bool completeWithoutStop() const { return complete() && !stopRequested(); }
-
- /** @return true iff the implementation errored out. */
- bool error() const { return _error; }
-
- /** @return the error information. */
- ExceptionInfo exception() const { return _exception; }
-
- /** To be called by QueryPlanSet::Runner only. */
-
- /**
- * @return a copy of the inheriting class, which will be run with its own query plan. The
- * child runner will assume its parent runner has completed execution.
- */
- QueryPlanRunner* createChild() const;
-
- void setQueryPlan( const QueryPlan* queryPlan );
-
- /** Handle initialization after a QueryPlan has been set. */
- void init();
-
- void setException( const DBException& e );
-
- /** @return an ExplainPlanInfo object that will be updated as the query runs. */
- shared_ptr<ExplainPlanInfo> generateExplainInfo();
-
- shared_ptr<ExplainPlanInfo> explainInfo() const { return _explainPlanInfo; }
-
- const Projection::KeyOnly* keyFieldsOnly() const {
- return queryPlan().keyFieldsOnly().get();
- }
-
- private:
- /** Call if all results have been found. */
- void setComplete() { _complete = true; }
-
- /** Call if the scan is complete even if not all results have been found. */
- void setStop() { setComplete(); _stopRequested = true; }
-
- void mayAdvance();
-
- bool countingMatches();
-
- bool countMatches() const;
-
- /**
- * @return true if the results generated by this query plan will be loaded from the record
- * store (not built from an index entry).
- */
- bool hasDocumentLoadingQueryPlan() const;
-
- void recordCursorLocation();
-
- void checkCursorAdvanced();
-
- void handleCursorAdvanced();
-
- void checkCursorOrdering();
-
- bool _complete;
- bool _stopRequested;
- ExceptionInfo _exception;
- const QueryPlan* _queryPlan;
- bool _error;
- CachedMatchCounter _matchCounter;
- bool _countingMatches;
- bool _mustAdvance;
- bool _capped;
- shared_ptr<Cursor> _c;
- ClientCursorHolder _cc;
- DiskLoc _posBeforeYield;
- ClientCursor::YieldData _yieldData;
- const QueryPlanSelectionPolicy& _selectionPolicy;
- const bool& _requireOrder; // TODO don't use a ref for this, but signal change explicitly
- shared_ptr<ExplainPlanInfo> _explainPlanInfo;
- bool _alwaysCountMatches;
- };
-
- /**
- * This class works if T::operator< is variant unlike a regular stl priority queue, but it's
- * very slow. However if _vec.size() is always very small, it would be fine, maybe even faster
- * than a smart impl that does more memory allocations.
- * TODO Clean up this temporary code.
- */
- template<class T>
- class PriorityQueue {
- MONGO_DISALLOW_COPYING( PriorityQueue );
- public:
- PriorityQueue() {
- _vec.reserve(4);
- }
- int size() const { return _vec.size(); }
- bool empty() const { return _vec.empty(); }
- void push(const T& x) {
- _vec.push_back(x);
- }
- T pop() {
- size_t t = 0;
- for( size_t i = 1; i < _vec.size(); i++ ) {
- if( _vec[t] < _vec[i] )
- t = i;
- }
- T ret = _vec[t];
- _vec.erase(_vec.begin()+t);
- return ret;
- }
- private:
- vector<T> _vec;
- };
-
- class QueryPlanSet;
-
- /** Populates a provided QueryPlanSet with candidate query plans, when requested. */
- class QueryPlanGenerator {
- public:
-
- /** Policies for utilizing recorded plans. */
- typedef enum {
- Ignore, // Ignore the recorded plan and try all candidate plans.
- UseIfInOrder, // Use the recorded plan if it is properly ordered.
- Use // Always use the recorded plan.
- } RecordedPlanPolicy;
-
- /** @param qps The QueryPlanSet to which plans will be provided. */
- QueryPlanGenerator( QueryPlanSet& qps,
- auto_ptr<FieldRangeSetPair> originalFrsp,
- const shared_ptr<const ParsedQuery>& parsedQuery,
- const BSONObj& hint,
- RecordedPlanPolicy recordedPlanPolicy,
- const BSONObj& min,
- const BSONObj& max,
- bool allowSpecial );
-
- /** Populate the provided QueryPlanSet with an initial set of plans. */
- void addInitialPlans();
-
- /** Supplement a cached plan provided earlier by adding additional query plans. */
- void addFallbackPlans();
-
- private:
-
- bool addShortCircuitPlan( NamespaceDetails* d );
-
- bool addHintPlan( NamespaceDetails* d );
-
- bool addSpecialPlan( NamespaceDetails* d );
-
- void addStandardPlans( NamespaceDetails* d );
-
- bool addCachedPlan( NamespaceDetails* d );
-
- shared_ptr<QueryPlan> newPlan( NamespaceDetails* d,
- int idxNo,
- const BSONObj& min = BSONObj(),
- const BSONObj& max = BSONObj(),
- const string& special = "" ) const;
-
- bool setUnindexedPlanIf( bool set, NamespaceDetails* d );
-
- void setSingleUnindexedPlan( NamespaceDetails* d );
-
- void setHintedPlanForIndex( IndexDetails& id );
-
- void validateAndSetHintedPlan( const shared_ptr<QueryPlan>& plan );
-
- void warnOnCappedIdTableScan() const;
-
- QueryPlanSet& _qps;
- auto_ptr<FieldRangeSetPair> _originalFrsp;
- shared_ptr<const ParsedQuery> _parsedQuery;
- BSONObj _hint;
- RecordedPlanPolicy _recordedPlanPolicy;
- BSONObj _min;
- BSONObj _max;
- bool _allowSpecial;
- };
-
- /** A set of candidate query plans for a query. */
- class QueryPlanSet {
- public:
- typedef boost::shared_ptr<QueryPlan> QueryPlanPtr;
- typedef vector<QueryPlanPtr> PlanVector;
-
- /**
- * @param originalFrsp - original constraints for this query clause; if null, frsp will be
- * used.
- */
- static QueryPlanSet* make( const char* ns,
- auto_ptr<FieldRangeSetPair> frsp,
- auto_ptr<FieldRangeSetPair> originalFrsp,
- const BSONObj& originalQuery,
- const BSONObj& order,
- const shared_ptr<const ParsedQuery>& parsedQuery,
- const BSONObj& hint,
- QueryPlanGenerator::RecordedPlanPolicy recordedPlanPolicy,
- const BSONObj& min,
- const BSONObj& max,
- bool allowSpecial );
-
- /** @return number of candidate plans. */
- int nPlans() const { return _plans.size(); }
-
- QueryPlanPtr firstPlan() const { return _plans[ 0 ]; }
-
- /** @return true if a plan is selected based on previous success of this plan. */
- bool usingCachedPlan() const { return _usingCachedPlan; }
-
- /** @return true if some candidate plans may have been excluded due to plan caching. */
- bool hasPossiblyExcludedPlans() const;
-
- /** @return a single plan that may work well for the specified query. */
- QueryPlanPtr getBestGuess() const;
-
- const FieldRangeSetPair& frsp() const { return *_frsp; }
-
- BSONObj originalQuery() const { return _originalQuery; }
-
- BSONObj order() const { return _order; }
-
- /** @return true if an active plan is in order. */
- bool haveInOrderPlan() const;
-
- /** @return true if an active or fallback plan is in order. */
- bool possibleInOrderPlan() const;
-
- /** @return true if an active or fallback plan is out of order. */
- bool possibleOutOfOrderPlan() const;
-
- CandidatePlanCharacter characterizeCandidatePlans() const;
-
- bool prepareToRetryQuery();
-
- string toString() const;
-
- /** Configure a single query plan if one has not already been provided. */
- void setSinglePlan( const QueryPlanPtr& plan );
-
- /** Configure a query plan from the plan cache. */
- void setCachedPlan( const QueryPlanPtr& plan, const CachedQueryPlan& cachedPlan );
-
- /** Add a candidate query plan, potentially one of many. */
- void addCandidatePlan( const QueryPlanPtr& plan );
-
- const PlanVector& plans() const { return _plans; }
-
- bool mayRecordPlan() const { return _mayRecordPlan; }
-
- int oldNScanned() const { return _oldNScanned; }
-
- void addFallbackPlans();
-
- void setUsingCachedPlan( bool usingCachedPlan ) { _usingCachedPlan = usingCachedPlan; }
-
- //for testing
-
- bool modifiedKeys() const;
-
- bool hasMultiKey() const;
-
- private:
-
- QueryPlanSet( const char* ns,
- auto_ptr<FieldRangeSetPair> frsp,
- auto_ptr<FieldRangeSetPair> originalFrsp,
- const BSONObj& originalQuery,
- const BSONObj& order,
- const shared_ptr<const ParsedQuery>& parsedQuery,
- const BSONObj& hint,
- QueryPlanGenerator::RecordedPlanPolicy recordedPlanPolicy,
- const BSONObj& min,
- const BSONObj& max,
- bool allowSpecial );
-
- void init();
-
- void pushPlan( const QueryPlanPtr& plan );
-
- QueryPlanGenerator _generator;
- BSONObj _originalQuery;
- auto_ptr<FieldRangeSetPair> _frsp;
- PlanVector _plans;
- bool _mayRecordPlan;
- bool _usingCachedPlan;
- CandidatePlanCharacter _cachedPlanCharacter;
- BSONObj _order;
- long long _oldNScanned;
- ElapsedTracker _yieldSometimesTracker;
- bool _allowSpecial;
- };
-
- /**
- * A priority queue of QueryPlanRunners ordered by their nscanned values. The QueryPlanRunners
- * are iterated sequentially and reinserted into the queue until one runner completes or all
- * runners error out.
- */
- class QueryPlanRunnerQueue {
- public:
- QueryPlanRunnerQueue( QueryPlanSet& plans, const QueryPlanRunner& prototypeRunner );
-
- /**
- * Pull a runner from the priority queue, advance it if possible, re-insert it into the
- * queue if it is not done, and return it. But if this runner errors out, retry with
- * another runner until a non error runner is found or all runners have errored out.
- * @return the next non error runner if there is one, otherwise an error runner.
- * If the returned runner is complete() or error(), this queue becomes done().
- */
- shared_ptr<QueryPlanRunner> next();
-
- /** @return true if done iterating. */
- bool done() const { return _done; }
-
- /** Prepare all runners for a database mutex yield. */
- void prepareToYield();
-
- /** Restore all runners after a database mutex yield. */
- void recoverFromYield();
-
- /** @return an ExplainClauseInfo object that will be updated as the query runs. */
- shared_ptr<ExplainClauseInfo> generateExplainInfo() {
- _explainClauseInfo.reset( new ExplainClauseInfo() );
- return _explainClauseInfo;
- }
-
- private:
- const QueryPlanRunner& _prototypeRunner;
- QueryPlanSet& _plans;
-
- static void initRunner( QueryPlanRunner& runner );
-
- static void nextRunner( QueryPlanRunner& runner );
-
- static void prepareToYieldRunner( QueryPlanRunner& runner );
-
- static void recoverFromYieldRunner( QueryPlanRunner& runner );
-
- /** Initialize the Runner. */
- shared_ptr<QueryPlanRunner> init();
-
- /** Move the Runner forward one iteration, and @return the plan for the iteration. */
- shared_ptr<QueryPlanRunner> _next();
-
- vector<shared_ptr<QueryPlanRunner> > _runners;
- struct RunnerHolder {
- RunnerHolder( const shared_ptr<QueryPlanRunner>& runner ) :
- _runner( runner ),
- _offset() {
- }
- shared_ptr<QueryPlanRunner> _runner;
- long long _offset;
- bool operator<( const RunnerHolder& other ) const {
- return _runner->nscanned() + _offset > other._runner->nscanned() + other._offset;
- }
- };
- PriorityQueue<RunnerHolder> _queue;
- shared_ptr<ExplainClauseInfo> _explainClauseInfo;
- bool _done;
- };
-
- /** Handles $or type queries by generating a QueryPlanSet for each $or clause. */
- class MultiPlanScanner {
- public:
-
- static MultiPlanScanner* make( const StringData& ns,
- const BSONObj& query,
- const BSONObj& order,
- const shared_ptr<const ParsedQuery>& parsedQuery =
- shared_ptr<const ParsedQuery>(),
- const BSONObj& hint = BSONObj(),
- QueryPlanGenerator::RecordedPlanPolicy recordedPlanPolicy =
- QueryPlanGenerator::Use,
- const BSONObj& min = BSONObj(),
- const BSONObj& max = BSONObj() );
-
- /** Set the originalRunner for QueryPlanSet iteration. */
- void initialRunner( const shared_ptr<QueryPlanRunner>& originalRunner ) {
- _baseRunner = originalRunner;
- }
-
- /**
- * Advance to the next runner, if not doneRunners().
- * @return the next non error runner if there is one, otherwise an error runner.
- * If the returned runner is complete() or error(), the MultiPlanScanner becomes
- * doneRunners() and no further runner iteration is possible.
- */
- shared_ptr<QueryPlanRunner> nextRunner();
-
- /** @return true if done with runner iteration. */
- bool doneRunners() const { return _doneRunners; }
-
- /**
- * Advance to the next $or clause; hasMoreClauses() must be true.
- * @param currentPlan QueryPlan of the current $or clause
- * @return best guess query plan of the next $or clause, 0 if there is no such plan.
- */
- const QueryPlan* nextClauseBestGuessPlan( const QueryPlan& currentPlan );
-
- /** Add explain information for a new clause. */
- void addClauseInfo( const shared_ptr<ExplainClauseInfo>& clauseInfo ) {
- verify( _explainQueryInfo );
- _explainQueryInfo->addClauseInfo( clauseInfo );
- }
-
- /** @return an ExplainQueryInfo object that will be updated as the query runs. */
- shared_ptr<ExplainQueryInfo> generateExplainInfo() {
- _explainQueryInfo.reset( new ExplainQueryInfo() );
- return _explainQueryInfo;
- }
-
- /** Yield the runner member. */
-
- void prepareToYield();
-
- void recoverFromYield();
-
- /** Clear the runner member. */
- void clearRunnerQueue();
-
- void setRecordedPlanPolicy( QueryPlanGenerator::RecordedPlanPolicy recordedPlanPolicy ) {
- _recordedPlanPolicy = recordedPlanPolicy;
- }
-
- int currentNPlans() const;
-
- /**
- * @return the query plan that would be used if the scanner would run a single
- * cursor for this query, otherwise 0. The returned plan is invalid if this
- * MultiPlanScanner is destroyed, hence we return a raw pointer.
- */
- const QueryPlan* singlePlan() const;
-
- /** @return true if more $or clauses need to be scanned. */
- bool hasMoreClauses() const;
-
- /**
- * @return plan information if there is a cached plan for a non $or query, otherwise an
- * empty object.
- */
- BSONObj cachedPlanExplainSummary() const;
-
- /**
- * @return true if this is not a $or query and some candidate plans may have been excluded
- * due to plan caching.
- */
- bool hasPossiblyExcludedPlans() const {
- return !_or && _currentQps->hasPossiblyExcludedPlans();
- }
-
- bool hasMultiKey() const { return _currentQps->hasMultiKey(); }
-
- /** Clear recorded indexes for the current QueryPlanSet's patterns. */
- void clearIndexesForPatterns() const;
-
- /** @return true if an active plan of _currentQps is in order. */
- bool haveInOrderPlan() const;
-
- /** @return true if an active or fallback plan of _currentQps is in order. */
- bool possibleInOrderPlan() const;
-
- /** @return true if an active or fallback plan of _currentQps is out of order. */
- bool possibleOutOfOrderPlan() const;
-
- int i() const { return _i; }
-
- string toString() const;
-
- private:
-
- MultiPlanScanner( const StringData& ns,
- const BSONObj& query,
- const shared_ptr<const ParsedQuery>& parsedQuery,
- const BSONObj& hint,
- QueryPlanGenerator::RecordedPlanPolicy recordedPlanPolicy );
-
- void init( const BSONObj& order,
- const BSONObj& min,
- const BSONObj& max );
-
- /** Initialize or iterate a runner generated from @param originalOp. */
- shared_ptr<QueryPlanRunner> iterateRunnerQueue( QueryPlanRunner& originalRunner,
- bool retried = false );
-
- shared_ptr<QueryPlanRunner> nextRunnerSimple();
-
- shared_ptr<QueryPlanRunner> nextRunnerOr();
-
- void updateCurrentQps( QueryPlanSet* qps );
-
- void assertNotOr() const {
- massert( 13266, "not implemented for $or query", !_or );
- }
-
- void assertHasMoreClauses() const {
- massert( 13271, "no more clauses", hasMoreClauses() );
- }
-
- void handleEndOfClause( const QueryPlan& clausePlan );
-
- void handleBeginningOfClause();
-
- bool mayHandleBeginningOfClause();
-
- bool haveUselessOr() const;
-
- const string _ns;
- bool _or;
- BSONObj _query;
- shared_ptr<const ParsedQuery> _parsedQuery;
- scoped_ptr<OrRangeGenerator> _org; // May be null in certain non $or query cases.
- scoped_ptr<QueryPlanSet> _currentQps;
- int _i;
- QueryPlanGenerator::RecordedPlanPolicy _recordedPlanPolicy;
- BSONObj _hint;
- bool _tableScanned;
- shared_ptr<QueryPlanRunner> _baseRunner;
- scoped_ptr<QueryPlanRunnerQueue> _runnerQueue;
- shared_ptr<ExplainQueryInfo> _explainQueryInfo;
- bool _doneRunners;
- };
-
- /**
- * Provides a cursor interface for serial single Cursor iteration using a MultiPlanScanner.
- * Currently used internally by a QueryOptimizerCursor.
- *
- * A MultiCursor is backed by one BasicCursor or BtreeCursor at a time and forwards calls for
- * ensuring a consistent state after a write to its backing Cursor.
- */
- class MultiCursor : public Cursor {
- public:
- /** @param nscanned is the initial nscanned value. */
- MultiCursor( auto_ptr<MultiPlanScanner> mps,
- const shared_ptr<Cursor>& c,
- const shared_ptr<CoveredIndexMatcher>& matcher,
- const shared_ptr<ExplainPlanInfo>& explainPlanInfo,
- const QueryPlanRunner& runner,
- long long nscanned );
-
- virtual bool ok() { return _c->ok(); }
-
- virtual Record* _current() { return _c->_current(); }
-
- virtual BSONObj current() { return _c->current(); }
-
- virtual DiskLoc currLoc() { return _c->currLoc(); }
-
- virtual bool advance();
-
- virtual BSONObj currKey() const { return _c->currKey(); }
-
- virtual DiskLoc refLoc() { return _c->refLoc(); }
-
- virtual void noteLocation() { _c->noteLocation(); }
-
- virtual void checkLocation() { _c->checkLocation(); }
-
- virtual void recoverFromYield();
-
- virtual bool supportGetMore() { return true; }
-
- virtual bool supportYields() { return true; }
-
- virtual BSONObj indexKeyPattern() { return _c->indexKeyPattern(); }
-
- /** Deduping documents from a prior cursor is handled by the matcher. */
- virtual bool getsetdup(DiskLoc loc) { return _c->getsetdup( loc ); }
-
- virtual bool modifiedKeys() const { return true; }
-
- virtual bool isMultiKey() const { return _mps->hasMultiKey(); }
-
- virtual CoveredIndexMatcher* matcher() const { return _matcher.get(); }
-
- virtual bool capped() const { return _c->capped(); }
-
- virtual long long nscanned() { return _nscanned + _c->nscanned(); }
-
- void noteIterate( bool match, bool loadedRecord );
-
- const QueryPlan& queryPlan() const {
- verify( _c->ok() && _queryPlan );
- return *_queryPlan;
- }
-
- const Projection::KeyOnly* keyFieldsOnly() const {
- verify( _c->ok() && _queryPlan );
- return _queryPlan->keyFieldsOnly().get();
- }
-
- private:
- void advanceClause();
-
- void advanceExhaustedClauses();
-
- auto_ptr<MultiPlanScanner> _mps;
- shared_ptr<Cursor> _c;
- shared_ptr<CoveredIndexMatcher> _matcher;
- const QueryPlan* _queryPlan;
- long long _nscanned;
- shared_ptr<ExplainPlanInfo> _explainPlanInfo;
- };
-
- /** NOTE min, max, and keyPattern will be updated to be consistent with the selected index. */
- IndexDetails* indexDetailsForRange( const char* ns,
- string& errmsg,
- BSONObj& min,
- BSONObj& max,
- BSONObj& keyPattern );
-
- class CachedQueryPlan;
-
- /**
- * Add-on functionality for queryutil classes requiring access to indexing
- * functionality not currently linked to mongos.
- * TODO Clean this up a bit, possibly with separate sharded and non sharded
- * implementations for the appropriate queryutil classes or by pulling index
- * related functionality into separate wrapper classes.
- */
- struct QueryUtilIndexed {
-
- /** @return true if the index may be useful according to its KeySpec. */
- static bool indexUseful( const FieldRangeSetPair& frsp,
- NamespaceDetails* d,
- int idxNo,
- const BSONObj& order );
-
- /** Clear any indexes recorded as the best for either the single or multi key pattern. */
- static void clearIndexesForPatterns( const FieldRangeSetPair& frsp, const BSONObj& order );
-
- /** Return a recorded best index for the single or multi key pattern. */
- static CachedQueryPlan bestIndexForPatterns( const FieldRangeSetPair& frsp,
- const BSONObj& order );
-
- static bool uselessOr( const OrRangeGenerator& org, NamespaceDetails* d, int hintIdx );
- };
-
-} // namespace mongo
diff --git a/src/mongo/db/query_plan.cpp b/src/mongo/db/query_plan.cpp
deleted file mode 100644
index ea954a92b38..00000000000
--- a/src/mongo/db/query_plan.cpp
+++ /dev/null
@@ -1,492 +0,0 @@
-/**
- * Copyright (C) 2008 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/query_plan.h"
-
-#include "mongo/db/btreecursor.h"
-#include "mongo/db/index_selection.h"
-#include "mongo/db/index/catalog_hack.h"
-#include "mongo/db/index/emulated_cursor.h"
-#include "mongo/db/index/index_access_method.h"
-#include "mongo/db/index/index_descriptor.h"
-#include "mongo/db/intervalbtreecursor.h"
-#include "mongo/db/pdfile.h"
-#include "mongo/db/parsed_query.h"
-#include "mongo/db/query_plan_summary.h"
-#include "mongo/db/queryutil.h"
-#include "mongo/db/structure/collection.h"
-#include "mongo/server.h"
-
-namespace mongo {
-
- QueryPlanSummary QueryPlan::summary() const {
- QueryPlanSummary summary;
- summary.fieldRangeSetMulti.reset( new FieldRangeSet( multikeyFrs() ) );
- summary.keyFieldsOnly = keyFieldsOnly();
- summary.scanAndOrderRequired = scanAndOrderRequired();
- return summary;
- }
-
- double elementDirection( const BSONElement& e ) {
- if ( e.isNumber() )
- return e.number();
- return 1;
- }
-
- QueryPlan* QueryPlan::make( NamespaceDetails* d,
- int idxNo,
- const FieldRangeSetPair& frsp,
- const FieldRangeSetPair* originalFrsp,
- const BSONObj& originalQuery,
- const BSONObj& order,
- const shared_ptr<const ParsedQuery>& parsedQuery,
- const BSONObj& startKey,
- const BSONObj& endKey,
- const std::string& special ) {
- auto_ptr<QueryPlan> ret( new QueryPlan( d,
- idxNo,
- frsp,
- originalQuery,
- order,
- parsedQuery,
- special ) );
- ret->init( originalFrsp, startKey, endKey );
- return ret.release();
- }
-
- QueryPlan::QueryPlan( NamespaceDetails* d,
- int idxNo,
- const FieldRangeSetPair& frsp,
- const BSONObj& originalQuery,
- const BSONObj& order,
- const shared_ptr<const ParsedQuery>& parsedQuery,
- const std::string& special ) :
- _d( d ),
- _idxNo( idxNo ),
- _frs( frsp.frsForIndex( _d, _idxNo ) ),
- _frsMulti( frsp.frsForIndex( _d, -1 ) ),
- _originalQuery( originalQuery ),
- _order( order ),
- _parsedQuery( parsedQuery ),
- _index( 0 ),
- _scanAndOrderRequired( true ),
- _matcherNecessary( true ),
- _direction( 0 ),
- _endKeyInclusive(),
- _utility( Helpful ),
- _special( special ),
- _startOrEndSpec() {
- }
-
- void QueryPlan::init( const FieldRangeSetPair* originalFrsp,
- const BSONObj& startKey,
- const BSONObj& endKey ) {
- _endKeyInclusive = endKey.isEmpty();
- _startOrEndSpec = !startKey.isEmpty() || !endKey.isEmpty();
-
- BSONObj idxKey = _idxNo < 0 ? BSONObj() : _d->idx( _idxNo ).keyPattern();
-
- if ( !_frs.matchPossibleForIndex( idxKey ) ) {
- _utility = Impossible;
- _scanAndOrderRequired = false;
- return;
- }
-
- if ( willScanTable() ) {
- if ( _order.isEmpty() || !strcmp( _order.firstElementFieldName(), "$natural" ) )
- _scanAndOrderRequired = false;
- return;
- }
-
- _descriptor.reset(CatalogHack::getDescriptor(_d, _idxNo));
- _index = &_d->idx(_idxNo);
-
- // If the parsing or index indicates this is a special query, don't continue the processing
- if (!_special.empty() ||
- ( ("" != CatalogHack::getAccessMethodName(_descriptor->keyPattern())) && (USELESS !=
- IndexSelection::isSuitableFor(_descriptor->keyPattern(), _frs, _order)))) {
-
- _specialIndexName = CatalogHack::getAccessMethodName(_descriptor->keyPattern());
- if (_special.empty()) _special = _specialIndexName;
-
- massert( 13040 , (string)"no type for special: " + _special , "" != _specialIndexName);
- // hopefully safe to use original query in these contexts;
- // don't think we can mix special with $or clause separation yet
- _scanAndOrderRequired = !_order.isEmpty();
- return;
- }
-
- BSONObjIterator o( _order );
- BSONObjIterator k( idxKey );
- if ( !o.moreWithEOO() )
- _scanAndOrderRequired = false;
- while( o.moreWithEOO() ) {
- BSONElement oe = o.next();
- if ( oe.eoo() ) {
- _scanAndOrderRequired = false;
- break;
- }
- if ( !k.moreWithEOO() )
- break;
- BSONElement ke;
- while( 1 ) {
- ke = k.next();
- if ( ke.eoo() )
- goto doneCheckOrder;
- if ( strcmp( oe.fieldName(), ke.fieldName() ) == 0 )
- break;
- if ( !_frs.range( ke.fieldName() ).equality() )
- goto doneCheckOrder;
- }
- int d = elementDirection( oe ) == elementDirection( ke ) ? 1 : -1;
- if ( _direction == 0 )
- _direction = d;
- else if ( _direction != d )
- break;
- }
-doneCheckOrder:
- if ( _scanAndOrderRequired )
- _direction = 0;
- BSONObjIterator i( idxKey );
- int exactIndexedQueryCount = 0;
- int optimalIndexedQueryCount = 0;
- bool awaitingLastOptimalField = true;
- set<string> orderFieldsUnindexed;
- _order.getFieldNames( orderFieldsUnindexed );
- while( i.moreWithEOO() ) {
- BSONElement e = i.next();
- if ( e.eoo() )
- break;
- const FieldRange& fr = _frs.range( e.fieldName() );
- if ( awaitingLastOptimalField ) {
- if ( !fr.universal() )
- ++optimalIndexedQueryCount;
- if ( !fr.equality() )
- awaitingLastOptimalField = false;
- }
- else {
- if ( !fr.universal() )
- optimalIndexedQueryCount = -1;
- }
- if ( fr.equality() ) {
- BSONElement e = fr.max();
- if ( !e.isNumber() && !e.mayEncapsulate() && e.type() != RegEx )
- ++exactIndexedQueryCount;
- }
- orderFieldsUnindexed.erase( e.fieldName() );
- }
- if ( !_scanAndOrderRequired &&
- ( optimalIndexedQueryCount == _frs.numNonUniversalRanges() ) )
- _utility = Optimal;
- _frv.reset( new FieldRangeVector( _frs, _descriptor->keyPattern(), _direction ) );
-
- if ( // If all field range constraints are on indexed fields and ...
- _utility == Optimal &&
- // ... the field ranges exactly represent the query and ...
- _frs.mustBeExactMatchRepresentation() &&
- // ... all indexed ranges are represented in the field range vector ...
- _frv->hasAllIndexedRanges() ) {
-
- // ... then the field range vector is sufficient to perform query matching against index
- // keys. No matcher is required.
- _matcherNecessary = false;
- }
-
- if ( originalFrsp ) {
- _originalFrv.reset( new FieldRangeVector( originalFrsp->frsForIndex( _d, _idxNo ),
- _descriptor->keyPattern(),
- _direction ) );
- }
- else {
- _originalFrv = _frv;
- }
- if ( _startOrEndSpec ) {
- BSONObj newStart, newEnd;
- if ( !startKey.isEmpty() )
- _startKey = startKey;
- else
- _startKey = _frv->startKey();
- if ( !endKey.isEmpty() )
- _endKey = endKey;
- else
- _endKey = _frv->endKey();
- }
-
- if ( ( _scanAndOrderRequired || _order.isEmpty() ) &&
- _frs.range( idxKey.firstElementFieldName() ).universal() ) { // NOTE SERVER-2140
- _utility = Unhelpful;
- }
-
- if ( _descriptor->isSparse() && hasPossibleExistsFalsePredicate() ) {
- _utility = Disallowed;
- }
-
- if ( _parsedQuery && _parsedQuery->getFields() && !_d->isMultikey( _idxNo ) ) {
- // Does not check modifiedKeys()
- _keyFieldsOnly.reset( _parsedQuery->getFields()->checkKey( _index->keyPattern() ) );
- }
- }
-
- shared_ptr<Cursor> QueryPlan::newCursor( const DiskLoc& startLoc,
- bool requestIntervalCursor ) const {
-
- if ("" != _specialIndexName) {
- // hopefully safe to use original query in these contexts - don't think we can mix type
- // with $or clause separation yet
- int numWanted = 0;
- if ( _parsedQuery ) {
- // SERVER-5390
- numWanted = _parsedQuery->getSkip() + _parsedQuery->getNumToReturn();
- }
-
- // Why do we get new objects here? Because EmulatedCursor takes ownership of them.
- IndexDescriptor* descriptor = CatalogHack::getDescriptor(_d, _idxNo);
- IndexAccessMethod* iam = CatalogHack::getIndex(descriptor);
- return shared_ptr<Cursor>(EmulatedCursor::make(descriptor, iam, _originalQuery,
- _order, numWanted,
- descriptor->keyPattern()));
- }
-
- if ( _utility == Impossible ) {
- // Dummy table scan cursor returning no results. Allowed in --notablescan mode.
- return shared_ptr<Cursor>( new BasicCursor( DiskLoc() ) );
- }
-
- if ( willScanTable() ) {
- checkTableScanAllowed();
- return findTableScan( _frs.ns(), _order, startLoc );
- }
-
- massert( 10363,
- "newCursor() with start location not implemented for indexed plans",
- startLoc.isNull() );
-
- if ( _startOrEndSpec ) {
- // we are sure to spec _endKeyInclusive
- return shared_ptr<Cursor>( BtreeCursor::make( _d,
- *_index,
- _startKey,
- _endKey,
- _endKeyInclusive,
- _direction >= 0 ? 1 : -1 ) );
- }
-
- if ( "" != CatalogHack::getAccessMethodName(_descriptor->keyPattern())) {
- return shared_ptr<Cursor>( BtreeCursor::make( _d,
- *_index,
- _frv->startKey(),
- _frv->endKey(),
- true,
- _direction >= 0 ? 1 : -1 ) );
- }
-
- // An IntervalBtreeCursor is returned if explicitly requested AND _frv is exactly
- // represented by a single interval within the btree.
- if ( // If an interval cursor is requested and ...
- requestIntervalCursor &&
- // ... equalities come before ranges (a requirement of Optimal) and ...
- _utility == Optimal &&
- // ... the field range vector exactly represents a single interval ...
- _frv->isSingleInterval() ) {
- // ... and an interval cursor can be created ...
- shared_ptr<Cursor> ret( IntervalBtreeCursor::make( _d,
- *_index,
- _frv->startKey(),
- _frv->startKeyInclusive(),
- _frv->endKey(),
- _frv->endKeyInclusive() ) );
- if ( ret ) {
- // ... then return the interval cursor.
- return ret;
- }
- }
-
- return shared_ptr<Cursor>( BtreeCursor::make( _d,
- *_index,
- _frv,
- independentRangesSingleIntervalLimit(),
- _direction >= 0 ? 1 : -1 ) );
- }
-
- shared_ptr<Cursor> QueryPlan::newReverseCursor() const {
- if ( willScanTable() ) {
- int orderSpec = _order.getIntField( "$natural" );
- if ( orderSpec == INT_MIN )
- orderSpec = 1;
- return findTableScan( _frs.ns(), BSON( "$natural" << -orderSpec ) );
- }
- massert( 10364, "newReverseCursor() not implemented for indexed plans", false );
- return shared_ptr<Cursor>();
- }
-
- BSONObj QueryPlan::indexKey() const {
- if ( !_index )
- return BSON( "$natural" << 1 );
- return _index->keyPattern();
- }
-
- const char* QueryPlan::ns() const {
- return _frs.ns();
- }
-
- void QueryPlan::registerSelf( long long nScanned,
- CandidatePlanCharacter candidatePlans ) const {
- // Impossible query constraints can be detected before scanning and historically could not
- // generate a QueryPattern.
- if ( _utility == Impossible ) {
- return;
- }
-
- QueryPattern queryPattern = _frs.pattern( _order );
- CachedQueryPlan queryPlanToCache( indexKey(), nScanned, candidatePlans );
-
- Collection* collection = cc().database()->getCollection( ns() );
- verify( collection );
- collection->infoCache()->registerCachedQueryPlanForPattern( queryPattern, queryPlanToCache );
- }
-
- void QueryPlan::checkTableScanAllowed() const {
- if (likely(!storageGlobalParams.noTableScan))
- return;
-
- // TODO - is this desirable? See SERVER-2222.
- if ( _frs.numNonUniversalRanges() == 0 )
- return;
-
- if ( strstr( ns(), ".system." ) )
- return;
-
- if( str::startsWith( ns(), "local." ) )
- return;
-
- if ( !nsdetails( ns() ) )
- return;
-
- uassert(10111, (string)"table scans not allowed:" + ns(),
- !storageGlobalParams.noTableScan);
- }
-
- int QueryPlan::independentRangesSingleIntervalLimit() const {
- if ( _scanAndOrderRequired &&
- _parsedQuery &&
- !_parsedQuery->wantMore() &&
- !isMultiKey() &&
- queryBoundsExactOrderSuffix() ) {
- verify( _direction == 0 );
- // Limit the results for each compound interval. SERVER-5063
- return _parsedQuery->getSkip() + _parsedQuery->getNumToReturn();
- }
- return 0;
- }
-
-
-
- bool QueryPlan::hasPossibleExistsFalsePredicate() const {
- return matcher()->docMatcher().hasExistsFalse();
- }
-
- bool QueryPlan::queryBoundsExactOrderSuffix() const {
- if ( !indexed() ||
- !_frs.matchPossible() ||
- !_frs.mustBeExactMatchRepresentation() ) {
- return false;
- }
- BSONObj idxKey = indexKey();
- BSONObjIterator index( idxKey );
- BSONObjIterator order( _order );
- int coveredNonUniversalRanges = 0;
- while( index.more() ) {
- const FieldRange& indexFieldRange = _frs.range( (*index).fieldName() );
- if ( !indexFieldRange.isPointIntervalSet() ) {
- if ( !indexFieldRange.universal() ) {
- // The last indexed range may be a non point set containing a single interval.
- // SERVER-5777
- if ( indexFieldRange.intervals().size() > 1 ) {
- return false;
- }
- ++coveredNonUniversalRanges;
- }
- break;
- }
- ++coveredNonUniversalRanges;
- if ( order.more() && str::equals( (*index).fieldName(), (*order).fieldName() ) ) {
- ++order;
- }
- ++index;
- }
- if ( coveredNonUniversalRanges != _frs.numNonUniversalRanges() ) {
- return false;
- }
- while( index.more() && order.more() ) {
- if ( !str::equals( (*index).fieldName(), (*order).fieldName() ) ) {
- return false;
- }
- if ( ( elementDirection( *index ) < 0 ) != ( elementDirection( *order ) < 0 ) ) {
- return false;
- }
- ++order;
- ++index;
- }
- return !order.more();
- }
-
- string QueryPlan::toString() const {
- return BSON(
- "index" << indexKey() <<
- "frv" << ( _frv ? _frv->toString() : "" ) <<
- "order" << _order
- ).jsonString();
- }
-
- shared_ptr<CoveredIndexMatcher> QueryPlan::matcher() const {
- if ( !_matcher ) {
- _matcher.reset( new CoveredIndexMatcher( originalQuery(), indexKey() ) );
- }
- return _matcher;
- }
-
- bool QueryPlan::isMultiKey() const {
- if ( _idxNo < 0 )
- return false;
- return _d->isMultikey( _idxNo );
- }
-
- std::ostream& operator<< ( std::ostream& out, const QueryPlan::Utility& utility ) {
- out << "QueryPlan::";
- switch( utility ) {
- case QueryPlan::Impossible: return out << "Impossible";
- case QueryPlan::Optimal: return out << "Optimal";
- case QueryPlan::Helpful: return out << "Helpful";
- case QueryPlan::Unhelpful: return out << "Unhelpful";
- case QueryPlan::Disallowed: return out << "Disallowed";
- default:
- return out << "UNKNOWN(" << utility << ")";
- }
- }
-
-} // namespace mongo
diff --git a/src/mongo/db/query_plan.h b/src/mongo/db/query_plan.h
deleted file mode 100644
index d2891ce4a2d..00000000000
--- a/src/mongo/db/query_plan.h
+++ /dev/null
@@ -1,199 +0,0 @@
-/**
- * Copyright (C) 2008 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 "mongo/db/diskloc.h"
-#include "mongo/db/index/index_descriptor.h"
-#include "mongo/db/jsobj.h"
-#include "mongo/db/matcher.h"
-#include "mongo/db/projection.h"
-#include "mongo/db/querypattern.h"
-
-namespace mongo {
-
- class Cursor;
- class FieldRangeSet;
- class FieldRangeSetPair;
- class IndexDetails;
- class NamespaceDetails;
- class ParsedQuery;
- struct QueryPlanSummary;
-
- /**
- * A plan for executing a query using the given index spec and FieldRangeSet. An object of this
- * class may only be used by one thread at a time.
- */
- class QueryPlan : boost::noncopyable {
- public:
-
- /**
- * @param originalFrsp - original constraints for this query clause. If null, frsp will be
- * used instead.
- */
- static QueryPlan* make( NamespaceDetails* d,
- int idxNo, // -1 = no index
- const FieldRangeSetPair& frsp,
- const FieldRangeSetPair* originalFrsp,
- const BSONObj& originalQuery,
- const BSONObj& order,
- const shared_ptr<const ParsedQuery>& parsedQuery =
- shared_ptr<const ParsedQuery>(),
- const BSONObj& startKey = BSONObj(),
- const BSONObj& endKey = BSONObj(),
- const std::string& special = "" );
-
- /** Categorical classification of a QueryPlan's utility. */
- enum Utility {
- Impossible, // Cannot produce any matches, so the query must have an empty result set.
- // No other plans need to be considered.
- Optimal, // Should run as the only candidate plan in the absence of an Impossible
- // plan.
- Helpful, // Should be considered.
- Unhelpful, // Should not be considered.
- Disallowed // Must not be considered unless explicitly hinted. May produce a
- // semantically incorrect result set.
- };
-
- Utility utility() const { return _utility; }
-
- /** @return true if ScanAndOrder processing will be required for result set. */
- bool scanAndOrderRequired() const { return _scanAndOrderRequired; }
-
- /**
- * @return false if document matching can be determined entirely using index keys and the
- * FieldRangeSetPair generated for the query, without using a Matcher. This function may
- * return false positives but not false negatives. For example, if the field range set's
- * mustBeExactMatchRepresentation() returns a false negative, this function will return a
- * false positive.
- */
- bool mayBeMatcherNecessary() const { return _matcherNecessary; }
-
- /** @return true if this QueryPlan would perform an unindexed scan. */
- bool willScanTable() const { return _idxNo < 0 && ( _utility != Impossible ); }
-
- /**
- * @return 'special' attribute of the plan, which was either set explicitly or generated
- * from the index.
- */
- const string& special() const { return _special; }
-
- /** @return a new cursor based on this QueryPlan's index and FieldRangeSet. */
- shared_ptr<Cursor> newCursor( const DiskLoc& startLoc = DiskLoc(),
- bool requestIntervalCursor = false ) const;
-
- /** @return a new reverse cursor if this is an unindexed plan. */
- shared_ptr<Cursor> newReverseCursor() const;
-
- /** Register this plan as a winner for its QueryPattern, with specified 'nscanned'. */
- void registerSelf( long long nScanned, CandidatePlanCharacter candidatePlans ) const;
-
- int direction() const { return _direction; }
-
- BSONObj indexKey() const;
-
- bool indexed() const { return _index != 0; }
-
- const IndexDetails* index() const { return _index; }
-
- int idxNo() const { return _idxNo; }
-
- const char* ns() const;
-
- NamespaceDetails* nsd() const { return _d; }
-
- BSONObj originalQuery() const { return _originalQuery; }
-
- shared_ptr<FieldRangeVector> originalFrv() const { return _originalFrv; }
-
- const FieldRangeSet& multikeyFrs() const { return _frsMulti; }
-
- shared_ptr<Projection::KeyOnly> keyFieldsOnly() const { return _keyFieldsOnly; }
-
- const ParsedQuery* parsedQuery() const { return _parsedQuery.get(); }
-
- /** @return a shared, lazily initialized matcher for the query plan. */
- shared_ptr<CoveredIndexMatcher> matcher() const;
-
- QueryPlanSummary summary() const;
-
- // The following member functions are for testing, or public for testing.
-
- shared_ptr<FieldRangeVector> frv() const { return _frv; }
- bool isMultiKey() const;
- string toString() const;
- bool queryBoundsExactOrderSuffix() const;
-
- private:
-
- QueryPlan( NamespaceDetails* d,
- int idxNo,
- const FieldRangeSetPair& frsp,
- const BSONObj& originalQuery,
- const BSONObj& order,
- const shared_ptr<const ParsedQuery>& parsedQuery,
- const std::string& special );
- void init( const FieldRangeSetPair* originalFrsp,
- const BSONObj& startKey,
- const BSONObj& endKey );
-
- void checkTableScanAllowed() const;
-
- int independentRangesSingleIntervalLimit() const;
-
- /** @return true when the plan's query may contains an $exists:false predicate. */
- bool hasPossibleExistsFalsePredicate() const;
-
- NamespaceDetails* _d;
- int _idxNo;
- const FieldRangeSet& _frs;
- const FieldRangeSet& _frsMulti;
- const BSONObj _originalQuery;
- const BSONObj _order;
- shared_ptr<const ParsedQuery> _parsedQuery;
- const IndexDetails* _index;
- bool _scanAndOrderRequired;
- bool _matcherNecessary;
- int _direction;
- shared_ptr<FieldRangeVector> _frv;
- shared_ptr<FieldRangeVector> _originalFrv;
- BSONObj _startKey;
- BSONObj _endKey;
- bool _endKeyInclusive;
- Utility _utility;
- string _special;
- bool _startOrEndSpec;
- shared_ptr<Projection::KeyOnly> _keyFieldsOnly;
- mutable shared_ptr<CoveredIndexMatcher> _matcher; // Lazy initialization.
- auto_ptr<IndexDescriptor> _descriptor;
- string _specialIndexName;
- };
-
- std::ostream &operator<< ( std::ostream& out, const QueryPlan::Utility& utility );
-
-} // namespace mongo
diff --git a/src/mongo/db/query_plan_selection_policy.cpp b/src/mongo/db/query_plan_selection_policy.cpp
deleted file mode 100644
index ee653637e79..00000000000
--- a/src/mongo/db/query_plan_selection_policy.cpp
+++ /dev/null
@@ -1,59 +0,0 @@
-/**
- * Copyright (C) 2011 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/query_plan_selection_policy.h"
-
-#include "mongo/db/query_optimizer_internal.h"
-
-namespace mongo {
-
- QueryPlanSelectionPolicy::Any QueryPlanSelectionPolicy::__any;
- const QueryPlanSelectionPolicy& QueryPlanSelectionPolicy::any() { return __any; }
-
- bool QueryPlanSelectionPolicy::IndexOnly::permitPlan( const QueryPlan& plan ) const {
- return !plan.willScanTable();
- }
- QueryPlanSelectionPolicy::IndexOnly QueryPlanSelectionPolicy::__indexOnly;
- const QueryPlanSelectionPolicy& QueryPlanSelectionPolicy::indexOnly() { return __indexOnly; }
-
- bool QueryPlanSelectionPolicy::IdElseNatural::permitPlan( const QueryPlan& plan ) const {
- return !plan.indexed() || plan.index()->isIdIndex();
- }
- BSONObj QueryPlanSelectionPolicy::IdElseNatural::planHint( const StringData& ns ) const {
- NamespaceDetails* nsd = nsdetails( ns );
- if ( !nsd || !nsd->haveIdIndex() ) {
- return BSON( "$hint" << BSON( "$natural" << 1 ) );
- }
- return BSON( "$hint" << nsd->idx( nsd->findIdIndex() ).indexName() );
- }
- QueryPlanSelectionPolicy::IdElseNatural QueryPlanSelectionPolicy::__idElseNatural;
- const QueryPlanSelectionPolicy& QueryPlanSelectionPolicy::idElseNatural() {
- return __idElseNatural;
- }
-
-} // namespace mongo
diff --git a/src/mongo/db/query_plan_selection_policy.h b/src/mongo/db/query_plan_selection_policy.h
deleted file mode 100644
index 2ae1ccdc45e..00000000000
--- a/src/mongo/db/query_plan_selection_policy.h
+++ /dev/null
@@ -1,110 +0,0 @@
-/**
- * Copyright (C) 2011 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 "mongo/db/jsobj.h"
-
-namespace mongo {
-
- class QueryPlan;
-
- /**
- * An interface for policies overriding the query optimizer's default behavior for selecting
- * query plans and creating cursors.
- */
- class QueryPlanSelectionPolicy {
- public:
- virtual ~QueryPlanSelectionPolicy() {}
- virtual string name() const = 0;
- virtual bool permitOptimalNaturalPlan() const { return true; }
- virtual bool permitOptimalIdPlan() const { return true; }
- virtual bool permitPlan( const QueryPlan& plan ) const { return true; }
- virtual BSONObj planHint( const StringData& ns ) const { return BSONObj(); }
-
- /**
- * @return true to request that a created Cursor provide a matcher(). If false, the
- * Cursor's matcher() may be NULL if the Cursor can perform accurate query matching
- * internally using a non Matcher mechanism. One case where a Matcher might be requested
- * even though not strictly necessary to select matching documents is if metadata about
- * matches may be requested using MatchDetails. NOTE This is a hint that the Cursor use a
- * Matcher, but the hint may be ignored. In some cases the Cursor may not provide
- * a Matcher even if 'requestMatcher' is true.
- */
- virtual bool requestMatcher() const { return true; }
-
- /**
- * @return true to request creating an IntervalBtreeCursor rather than a BtreeCursor when
- * possible. An IntervalBtreeCursor is optimized for counting the number of documents
- * between two endpoints in a btree. NOTE This is a hint to create an interval cursor, but
- * the hint may be ignored. In some cases a different cursor type may be created even if
- * 'requestIntervalCursor' is true.
- */
- virtual bool requestIntervalCursor() const { return false; }
-
- /** Allow any query plan selection, permitting the query optimizer's default behavior. */
- static const QueryPlanSelectionPolicy& any();
-
- /** Prevent unindexed collection scans. */
- static const QueryPlanSelectionPolicy& indexOnly();
-
- /**
- * Generally hints to use the _id plan, falling back to the $natural plan. However, the
- * $natural plan will always be used if optimal for the query.
- */
- static const QueryPlanSelectionPolicy& idElseNatural();
-
- private:
- class Any;
- static Any __any;
- class IndexOnly;
- static IndexOnly __indexOnly;
- class IdElseNatural;
- static IdElseNatural __idElseNatural;
- };
-
- class QueryPlanSelectionPolicy::Any : public QueryPlanSelectionPolicy {
- public:
- virtual string name() const { return "any"; }
- };
-
- class QueryPlanSelectionPolicy::IndexOnly : public QueryPlanSelectionPolicy {
- public:
- virtual string name() const { return "indexOnly"; }
- virtual bool permitOptimalNaturalPlan() const { return false; }
- virtual bool permitPlan( const QueryPlan& plan ) const;
- };
-
- class QueryPlanSelectionPolicy::IdElseNatural : public QueryPlanSelectionPolicy {
- public:
- virtual string name() const { return "idElseNatural"; }
- virtual bool permitPlan( const QueryPlan& plan ) const;
- virtual BSONObj planHint( const StringData& ns ) const;
- };
-
-} // namespace mongo
diff --git a/src/mongo/db/query_plan_summary.h b/src/mongo/db/query_plan_summary.h
deleted file mode 100644
index 30a9fd14408..00000000000
--- a/src/mongo/db/query_plan_summary.h
+++ /dev/null
@@ -1,64 +0,0 @@
-/**
-* Copyright (C) 2008 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 "mongo/db/projection.h"
-
-namespace mongo {
-
- class FieldRangeSet;
-
- /**
- * A partial description of a QueryPlan that provides access to relevant plan attributes outside
- * of query optimizer internal code.
- */
- struct QueryPlanSummary {
- QueryPlanSummary() :
- scanAndOrderRequired() {
- }
-
- /**
- * The 'fieldRangeMulti' attribute is required, and its presence indicates the object has
- * been configured with a query plan.
- */
- bool valid() const { return fieldRangeSetMulti; }
-
- // A description of the valid values for the fields of a query, in the context of a multikey
- // index or in memory sort.
- shared_ptr<FieldRangeSet> fieldRangeSetMulti;
-
- // A helper object used to implement covered index queries. This attribute is only non-NULL
- // if the query plan supports covered index queries.
- shared_ptr<Projection::KeyOnly> keyFieldsOnly;
-
- // True if the query plan results must be reordered to match a requested ordering.
- bool scanAndOrderRequired;
- };
-
-} // namespace mongo
diff --git a/src/mongo/db/queryoptimizercursor.h b/src/mongo/db/queryoptimizercursor.h
deleted file mode 100644
index dbddee8b02f..00000000000
--- a/src/mongo/db/queryoptimizercursor.h
+++ /dev/null
@@ -1,81 +0,0 @@
-/**
- * Copyright (C) 2011 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 "cursor.h"
-
-namespace mongo {
-
- class CandidatePlanCharacter;
- class ExplainQueryInfo;
- class FieldRangeSet;
-
- /**
- * Adds functionality to Cursor for running multiple plans, running out of order plans,
- * utilizing covered indexes, and generating explain output.
- */
- class QueryOptimizerCursor : public Cursor {
- public:
-
- /** Candidate plans for the query before it begins running. */
- virtual CandidatePlanCharacter initialCandidatePlans() const = 0;
- /** FieldRangeSet for the query before it begins running. */
- virtual const FieldRangeSet *initialFieldRangeSet() const = 0;
-
- /** @return true if the plan for the current iterate is out of order. */
- virtual bool currentPlanScanAndOrderRequired() const = 0;
-
- /** @return true when there may be multiple plans running and some are in order. */
- virtual bool runningInitialInOrderPlan() const = 0;
- /**
- * @return true when some query plans may have been excluded due to plan caching, for a
- * non-$or query.
- */
- virtual bool hasPossiblyExcludedPlans() const = 0;
-
- /**
- * @return true when both in order and out of order candidate plans were available, and
- * an out of order candidate plan completed iteration.
- */
- virtual bool completePlanOfHybridSetScanAndOrderRequired() const = 0;
-
- /** Clear recorded indexes for the current clause's query patterns. */
- virtual void clearIndexesForPatterns() = 0;
- /** Stop returning results from out of order plans and do not allow them to complete. */
- virtual void abortOutOfOrderPlans() = 0;
-
- /** Note match information for the current iterate, to generate explain output. */
- virtual void noteIterate( bool match, bool loadedDocument, bool chunkSkip ) = 0;
- /** Note a lock yield for explain output reporting. */
- virtual void noteYield() = 0;
- /** @return explain output for the query run by this cursor. */
- virtual shared_ptr<ExplainQueryInfo> explainQueryInfo() const = 0;
- };
-
-} // namespace mongo
diff --git a/src/mongo/db/queryoptimizercursorimpl.cpp b/src/mongo/db/queryoptimizercursorimpl.cpp
deleted file mode 100644
index 0545aa7ea9f..00000000000
--- a/src/mongo/db/queryoptimizercursorimpl.cpp
+++ /dev/null
@@ -1,513 +0,0 @@
-/**
- * Copyright (C) 2011 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/pch.h"
-
-#include "mongo/db/queryoptimizercursorimpl.h"
-
-#include "mongo/db/btreecursor.h"
-#include "mongo/db/query_plan_selection_policy.h"
-#include "mongo/db/query_plan_summary.h"
-#include "mongo/db/query_optimizer_internal.h"
-#include "mongo/db/queryutil.h"
-
-namespace mongo {
-
- QueryOptimizerCursorImpl* QueryOptimizerCursorImpl::make
- ( auto_ptr<MultiPlanScanner>& mps,
- const QueryPlanSelectionPolicy& planPolicy,
- bool requireOrder,
- bool explain ) {
- auto_ptr<QueryOptimizerCursorImpl> ret( new QueryOptimizerCursorImpl( mps, planPolicy,
- requireOrder ) );
- ret->init( explain );
- return ret.release();
- }
-
- bool QueryOptimizerCursorImpl::ok() {
- return _takeover ? _takeover->ok() : !currLoc().isNull();
- }
-
- Record* QueryOptimizerCursorImpl::_current() {
- if ( _takeover ) {
- return _takeover->_current();
- }
- assertOk();
- return currLoc().rec();
- }
-
- BSONObj QueryOptimizerCursorImpl::current() {
- if ( _takeover ) {
- return _takeover->current();
- }
- assertOk();
- return currLoc().obj();
- }
-
- DiskLoc QueryOptimizerCursorImpl::currLoc() {
- return _takeover ? _takeover->currLoc() : _currLoc();
- }
-
- DiskLoc QueryOptimizerCursorImpl::_currLoc() const {
- dassert( !_takeover );
- return _currRunner ? _currRunner->currLoc() : DiskLoc();
- }
-
- bool QueryOptimizerCursorImpl::advance() {
- return _advance( false );
- }
-
- BSONObj QueryOptimizerCursorImpl::currKey() const {
- if ( _takeover ) {
- return _takeover->currKey();
- }
- assertOk();
- return _currRunner->currKey();
- }
-
- DiskLoc QueryOptimizerCursorImpl::refLoc() {
- return _takeover ? _takeover->refLoc() : DiskLoc();
- }
-
- BSONObj QueryOptimizerCursorImpl::indexKeyPattern() {
- if ( _takeover ) {
- return _takeover->indexKeyPattern();
- }
- assertOk();
- return _currRunner->cursor()->indexKeyPattern();
- }
-
- void QueryOptimizerCursorImpl::prepareToTouchEarlierIterate() {
- if ( _takeover ) {
- _takeover->prepareToTouchEarlierIterate();
- }
- else if ( _currRunner ) {
- if ( _mps->currentNPlans() == 1 ) {
- // This single plan version is a bit more performant, so we use it when possible.
- _currRunner->prepareToTouchEarlierIterate();
- }
- else {
- // With multiple plans, the 'earlier iterate' could be the current iterate of one of
- // the component plans. We do a full yield of all plans, using ClientCursors.
- _mps->prepareToYield();
- }
- }
- }
-
- void QueryOptimizerCursorImpl::recoverFromTouchingEarlierIterate() {
- if ( _takeover ) {
- _takeover->recoverFromTouchingEarlierIterate();
- }
- else if ( _currRunner ) {
- if ( _mps->currentNPlans() == 1 ) {
- _currRunner->recoverFromTouchingEarlierIterate();
- }
- else {
- recoverFromYield();
- }
- }
- }
-
- void QueryOptimizerCursorImpl::prepareToYield() {
- if ( _takeover ) {
- _takeover->prepareToYield();
- }
- else if ( _currRunner ) {
- _mps->prepareToYield();
- }
- }
-
- void QueryOptimizerCursorImpl::recoverFromYield() {
- if ( _takeover ) {
- _takeover->recoverFromYield();
- return;
- }
- if ( _currRunner ) {
- _mps->recoverFromYield();
- if ( _currRunner->error() || !ok() ) {
- // Advance to a non error op if one of the ops errored out.
- // Advance to a following $or clause if the $or clause returned all results.
- verify( !_mps->doneRunners() );
- _advance( true );
- }
- }
- }
-
- bool QueryOptimizerCursorImpl::getsetdup(DiskLoc loc) {
- if ( _takeover ) {
- if ( getdupInternal( loc ) ) {
- return true;
- }
- return _takeover->getsetdup( loc );
- }
- assertOk();
- return getsetdupInternal( loc );
- }
-
- bool QueryOptimizerCursorImpl::isMultiKey() const {
- if ( _takeover ) {
- return _takeover->isMultiKey();
- }
- assertOk();
- return _currRunner->cursor()->isMultiKey();
- }
-
- bool QueryOptimizerCursorImpl::capped() const {
- // Initial capped wrapping cases (before takeover) are handled internally by a component
- // ClientCursor.
- return _takeover ? _takeover->capped() : false;
- }
-
- long long QueryOptimizerCursorImpl::nscanned() {
- return _takeover ? _takeover->nscanned() : _nscanned;
- }
-
- CoveredIndexMatcher* QueryOptimizerCursorImpl::matcher() const {
- if ( _takeover ) {
- return _takeover->matcher();
- }
- assertOk();
- return _currRunner->queryPlan().matcher().get();
- }
-
- bool QueryOptimizerCursorImpl::currentMatches( MatchDetails* details ) {
- if ( _takeover ) {
- return _takeover->currentMatches( details );
- }
- assertOk();
- return _currRunner->currentMatches( details );
- }
-
- const FieldRangeSet* QueryOptimizerCursorImpl::initialFieldRangeSet() const {
- if ( _takeover ) {
- return 0;
- }
- assertOk();
- return &_currRunner->queryPlan().multikeyFrs();
- }
-
- bool QueryOptimizerCursorImpl::currentPlanScanAndOrderRequired() const {
- if ( _takeover ) {
- return _takeover->queryPlan().scanAndOrderRequired();
- }
- assertOk();
- return _currRunner->queryPlan().scanAndOrderRequired();
- }
-
- const Projection::KeyOnly* QueryOptimizerCursorImpl::keyFieldsOnly() const {
- if ( _takeover ) {
- return _takeover->keyFieldsOnly();
- }
- assertOk();
- return _currRunner->keyFieldsOnly();
- }
-
- bool QueryOptimizerCursorImpl::runningInitialInOrderPlan() const {
- if ( _takeover ) {
- return false;
- }
- assertOk();
- return _mps->haveInOrderPlan();
- }
-
- bool QueryOptimizerCursorImpl::hasPossiblyExcludedPlans() const {
- if ( _takeover ) {
- return false;
- }
- assertOk();
- return _mps->hasPossiblyExcludedPlans();
- }
-
- void QueryOptimizerCursorImpl::clearIndexesForPatterns() {
- if ( !_takeover ) {
- _mps->clearIndexesForPatterns();
- }
- }
-
- void QueryOptimizerCursorImpl::abortOutOfOrderPlans() {
- _requireOrder = true;
- }
-
- void QueryOptimizerCursorImpl::noteIterate( bool match, bool loadedDocument, bool chunkSkip ) {
- if ( _explainQueryInfo ) {
- _explainQueryInfo->noteIterate( match, loadedDocument, chunkSkip );
- }
- if ( _takeover ) {
- _takeover->noteIterate( match, loadedDocument );
- }
- }
-
- void QueryOptimizerCursorImpl::noteYield() {
- if ( _explainQueryInfo ) {
- _explainQueryInfo->noteYield();
- }
- }
-
- QueryOptimizerCursorImpl::QueryOptimizerCursorImpl( auto_ptr<MultiPlanScanner>& mps,
- const QueryPlanSelectionPolicy& planPolicy,
- bool requireOrder ) :
- _requireOrder( requireOrder ),
- _mps( mps ),
- _initialCandidatePlans( _mps->possibleInOrderPlan(), _mps->possibleOutOfOrderPlan() ),
- _originalRunner( new QueryPlanRunner( _nscanned,
- planPolicy,
- _requireOrder,
- !_initialCandidatePlans.hybridPlanSet() ) ),
- _currRunner(),
- _completePlanOfHybridSetScanAndOrderRequired(),
- _nscanned() {
- }
-
- void QueryOptimizerCursorImpl::init( bool explain ) {
- _mps->initialRunner( _originalRunner );
- if ( explain ) {
- _explainQueryInfo = _mps->generateExplainInfo();
- }
- shared_ptr<QueryPlanRunner> runner = _mps->nextRunner();
- rethrowOnError( runner );
- if ( !runner->complete() ) {
- _currRunner = runner.get();
- }
- }
-
- bool QueryOptimizerCursorImpl::_advance( bool force ) {
- if ( _takeover ) {
- return _takeover->advance();
- }
-
- if ( !force && !ok() ) {
- return false;
- }
-
- _currRunner = 0;
- shared_ptr<QueryPlanRunner> runner = _mps->nextRunner();
- rethrowOnError( runner );
-
- if ( !runner->complete() ) {
- // The 'runner' will be valid until we call _mps->nextOp() again. We return 'current'
- // values from this op.
- _currRunner = runner.get();
- }
- else if ( runner->stopRequested() ) {
- if ( runner->cursor() ) {
- _takeover.reset( new MultiCursor( _mps,
- runner->cursor(),
- runner->queryPlan().matcher(),
- runner->explainInfo(),
- *runner,
- _nscanned - runner->cursor()->nscanned() ) );
- }
- }
- else {
- if ( _initialCandidatePlans.hybridPlanSet() ) {
- _completePlanOfHybridSetScanAndOrderRequired =
- runner->queryPlan().scanAndOrderRequired();
- }
- }
-
- return ok();
- }
-
- /** Forward an exception when the runner errs out. */
- void QueryOptimizerCursorImpl::rethrowOnError( const shared_ptr< QueryPlanRunner > &runner ) {
- if ( runner->error() ) {
- throw MsgAssertionException( runner->exception() );
- }
- }
-
- bool QueryOptimizerCursorImpl::getsetdupInternal(const DiskLoc &loc) {
- return _dups.getsetdup( loc );
- }
-
- bool QueryOptimizerCursorImpl::getdupInternal(const DiskLoc &loc) {
- dassert( _takeover );
- return _dups.getdup( loc );
- }
-
- shared_ptr<Cursor> newQueryOptimizerCursor( auto_ptr<MultiPlanScanner> mps,
- const QueryPlanSelectionPolicy &planPolicy,
- bool requireOrder, bool explain ) {
- try {
- shared_ptr<QueryOptimizerCursorImpl> ret
- ( QueryOptimizerCursorImpl::make( mps, planPolicy, requireOrder, explain ) );
- return ret;
- } catch( const AssertionException &e ) {
- if ( e.getCode() == OutOfOrderDocumentsAssertionCode ) {
- // If no indexes follow the requested sort order, return an
- // empty pointer. This is legacy behavior based on bestGuessCursor().
- return shared_ptr<Cursor>();
- }
- throw;
- }
- return shared_ptr<Cursor>();
- }
-
- CursorGenerator::CursorGenerator( const StringData &ns,
- const BSONObj &query,
- const BSONObj &order,
- const QueryPlanSelectionPolicy &planPolicy,
- const shared_ptr<const ParsedQuery> &parsedQuery,
- bool requireOrder,
- QueryPlanSummary *singlePlanSummary ) :
- _ns( ns ),
- _query( query ),
- _order( order ),
- _planPolicy( planPolicy ),
- _parsedQuery( parsedQuery ),
- _requireOrder( requireOrder ),
- _singlePlanSummary( singlePlanSummary ) {
- // Initialize optional return variables.
- if ( _singlePlanSummary ) {
- *_singlePlanSummary = QueryPlanSummary();
- }
- }
-
- BSONObj CursorGenerator::hint() const {
- return _argumentsHint.isEmpty() ? _planPolicy.planHint( _ns ) : _argumentsHint;
- }
-
- void CursorGenerator::setArgumentsHint() {
- if (storageGlobalParams.useHints && _parsedQuery) {
- _argumentsHint = _parsedQuery->getHint();
- }
-
- if ( snapshot() ) {
- NamespaceDetails *d = nsdetails( _ns );
- if ( d ) {
- int i = d->findIdIndex();
- if( i < 0 ) {
- if ( _ns.find( ".system." ) == string::npos )
- log() << "warning: no _id index on $snapshot query, ns:" << _ns << endl;
- }
- else {
- /* [dm] the name of an _id index tends to vary, so we build the hint the hard
- way here. probably need a better way to specify "use the _id index" as a hint.
- if someone is in the query optimizer please fix this then!
- */
- _argumentsHint = BSON( "$hint" << d->idx(i).indexName() );
- }
- }
- }
- }
-
- shared_ptr<Cursor> CursorGenerator::shortcutCursor() const {
- if ( !mayShortcutQueryOptimizer() ) {
- return shared_ptr<Cursor>();
- }
-
- if ( _planPolicy.permitOptimalNaturalPlan() && _query.isEmpty() && _order.isEmpty() ) {
- return theDataFileMgr.findAll( _ns );
- }
- if ( _planPolicy.permitOptimalIdPlan() && isSimpleIdQuery( _query ) ) {
- Database *database = cc().database();
- verify( database );
- NamespaceDetails *d = database->namespaceIndex().details( _ns );
- if ( d ) {
- int idxNo = d->findIdIndex();
- if ( idxNo >= 0 ) {
- IndexDetails& i = d->idx( idxNo );
- BSONObj key = i.getKeyFromQuery( _query );
- return shared_ptr<Cursor>( BtreeCursor::make( d, i, key, key, true, 1 ) );
- }
- }
- }
-
- return shared_ptr<Cursor>();
- }
-
- void CursorGenerator::setMultiPlanScanner() {
- _mps.reset( MultiPlanScanner::make( _ns, _query, _order, _parsedQuery, hint(),
- explain() ? QueryPlanGenerator::Ignore :
- QueryPlanGenerator::Use,
- min(), max() ) );
- }
-
- shared_ptr<Cursor> CursorGenerator::singlePlanCursor() {
- const QueryPlan *singlePlan = _mps->singlePlan();
- if ( !singlePlan || ( isOrderRequired() && singlePlan->scanAndOrderRequired() ) ) {
- return shared_ptr<Cursor>();
- }
- if ( !_planPolicy.permitPlan( *singlePlan ) ) {
- return shared_ptr<Cursor>();
- }
-
- if ( _singlePlanSummary ) {
- *_singlePlanSummary = singlePlan->summary();
- }
- shared_ptr<Cursor> single = singlePlan->newCursor( DiskLoc(),
- _planPolicy.requestIntervalCursor() );
- if ( !_query.isEmpty() && !single->matcher() ) {
-
- // The query plan must have a matcher. The matcher's constructor performs some aspects
- // of query validation that should occur before a cursor is returned.
- fassert( 16449, singlePlan->matcher() );
-
- if ( // If a matcher is requested or ...
- _planPolicy.requestMatcher() ||
- // ... the index ranges do not exactly match the query or ...
- singlePlan->mayBeMatcherNecessary() ||
- // ... the matcher must look at the full record ...
- singlePlan->matcher()->needRecord() ) {
-
- // ... then set the cursor's matcher to the query plan's matcher.
- single->setMatcher( singlePlan->matcher() );
- }
- }
- if ( singlePlan->keyFieldsOnly() ) {
- single->setKeyFieldsOnly( singlePlan->keyFieldsOnly() );
- }
- return single;
- }
-
- shared_ptr<Cursor> CursorGenerator::generate() {
-
- setArgumentsHint();
- shared_ptr<Cursor> cursor = shortcutCursor();
- if ( cursor ) {
- return cursor;
- }
-
- setMultiPlanScanner();
- cursor = singlePlanCursor();
- if ( cursor ) {
- return cursor;
- }
-
- return newQueryOptimizerCursor( _mps, _planPolicy, isOrderRequired(), explain() );
- }
-
- /** This interface is just available for testing. */
- shared_ptr<Cursor> newQueryOptimizerCursor
- ( const char *ns, const BSONObj &query, const BSONObj &order,
- const QueryPlanSelectionPolicy &planPolicy, bool requireOrder,
- const shared_ptr<const ParsedQuery> &parsedQuery ) {
- auto_ptr<MultiPlanScanner> mps( MultiPlanScanner::make( ns, query, order, parsedQuery ) );
- return newQueryOptimizerCursor( mps, planPolicy, requireOrder, false );
- }
-
-} // namespace mongo;
diff --git a/src/mongo/db/queryoptimizercursorimpl.h b/src/mongo/db/queryoptimizercursorimpl.h
deleted file mode 100644
index ddd24f19940..00000000000
--- a/src/mongo/db/queryoptimizercursorimpl.h
+++ /dev/null
@@ -1,307 +0,0 @@
-/**
- * Copyright (C) 2011 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 "mongo/db/parsed_query.h"
-#include "mongo/db/queryoptimizercursor.h"
-#include "mongo/db/querypattern.h"
-
-namespace mongo {
-
- class MultiCursor;
- class MultiPlanScanner;
- class QueryPlanRunner;
- class QueryPlanSelectionPolicy;
- struct QueryPlanSummary;
-
- /** Dup tracking class, optimizing one common case with small set and few initial reads. */
- class SmallDupSet {
- public:
- SmallDupSet() : _accesses() {
- _vec.reserve( 250 );
- }
- /** @return true if @param 'loc' already added to the set, false if adding to the set in this call. */
- bool getsetdup( const DiskLoc &loc ) {
- access();
- return vec() ? getsetdupVec( loc ) : getsetdupSet( loc );
- }
- /** @return true when @param loc in the set. */
- bool getdup( const DiskLoc &loc ) {
- access();
- return vec() ? getdupVec( loc ) : getdupSet( loc );
- }
- private:
- void access() {
- ++_accesses;
- mayUpgrade();
- }
- void mayUpgrade() {
- if ( vec() && _accesses > 500 ) {
- _set.insert( _vec.begin(), _vec.end() );
- }
- }
- bool vec() const {
- return _set.size() == 0;
- }
- bool getsetdupVec( const DiskLoc &loc ) {
- if ( getdupVec( loc ) ) {
- return true;
- }
- _vec.push_back( loc );
- return false;
- }
- bool getdupVec( const DiskLoc &loc ) const {
- for( vector<DiskLoc>::const_iterator i = _vec.begin(); i != _vec.end(); ++i ) {
- if ( *i == loc ) {
- return true;
- }
- }
- return false;
- }
- bool getsetdupSet( const DiskLoc &loc ) {
- pair<set<DiskLoc>::iterator, bool> p = _set.insert(loc);
- return !p.second;
- }
- bool getdupSet( const DiskLoc &loc ) {
- return _set.count( loc ) > 0;
- }
- vector<DiskLoc> _vec;
- set<DiskLoc> _set;
- long long _accesses;
- };
-
- /**
- * This cursor runs a MultiPlanScanner iteratively and returns results from
- * the scanner's cursors as they become available. Once the scanner chooses
- * a single plan, this cursor becomes a simple wrapper around that single
- * plan's cursor (called the 'takeover' cursor).
- *
- * A QueryOptimizerCursor employs a delegation strategy to ensure consistency after writes
- * during its initial phase when multiple delegate Cursors may be active (before _takeover is
- * set).
- *
- * Before takeover, the return value of refLoc() will be isNull(), causing ClientCursor to
- * ignore a QueryOptimizerCursor (though not its delegate Cursors) when a delete occurs.
- * Requests to prepareToYield() or recoverFromYield() will be forwarded to
- * prepareToYield()/recoverFromYield() on ClientCursors of delegate Cursors. If a delegate
- * Cursor becomes eof() or invalid after a yield recovery,
- * QueryOptimizerCursor::recoverFromYield() may advance _currRunner to another delegate Cursor.
- *
- * Requests to prepareToTouchEarlierIterate() or recoverFromTouchingEarlierIterate() are
- * forwarded as prepareToTouchEarlierIterate()/recoverFromTouchingEarlierIterate() to the
- * delegate Cursor when a single delegate Cursor is active. If multiple delegate Cursors are
- * active, the advance() call preceeding prepareToTouchEarlierIterate() may not properly advance
- * all delegate Cursors, so the calls are forwarded as prepareToYield()/recoverFromYield() to a
- * ClientCursor for each delegate Cursor.
- *
- * After _takeover is set, consistency after writes is ensured by delegation to the _takeover
- * MultiCursor.
- */
- class QueryOptimizerCursorImpl : public QueryOptimizerCursor {
- public:
- static QueryOptimizerCursorImpl* make( auto_ptr<MultiPlanScanner>& mps,
- const QueryPlanSelectionPolicy& planPolicy,
- bool requireOrder,
- bool explain );
-
- virtual bool ok();
-
- virtual Record* _current();
-
- virtual BSONObj current();
-
- virtual DiskLoc currLoc();
-
- DiskLoc _currLoc() const;
-
- virtual bool advance();
-
- virtual BSONObj currKey() const;
-
- /**
- * When return value isNull(), our cursor will be ignored for deletions by the ClientCursor
- * implementation. In such cases, internal ClientCursors will update the positions of
- * component Cursors when necessary.
- * !!! Use care if changing this behavior, as some ClientCursor functionality may not work
- * recursively.
- */
- virtual DiskLoc refLoc();
-
- virtual BSONObj indexKeyPattern();
-
- virtual bool supportGetMore() { return true; }
-
- virtual bool supportYields() { return true; }
-
- virtual void prepareToTouchEarlierIterate();
-
- virtual void recoverFromTouchingEarlierIterate();
-
- virtual void prepareToYield();
-
- virtual void recoverFromYield();
-
- virtual string toString() { return "QueryOptimizerCursor"; }
-
- virtual bool getsetdup(DiskLoc loc);
-
- /** Matcher needs to know if the the cursor being forwarded to is multikey. */
- virtual bool isMultiKey() const;
-
- // TODO fix
- virtual bool modifiedKeys() const { return true; }
-
- virtual bool capped() const;
-
- virtual long long nscanned();
-
- virtual CoveredIndexMatcher *matcher() const;
-
- virtual bool currentMatches( MatchDetails* details = 0 );
-
- virtual CandidatePlanCharacter initialCandidatePlans() const {
- return _initialCandidatePlans;
- }
-
- virtual const FieldRangeSet* initialFieldRangeSet() const;
-
- virtual bool currentPlanScanAndOrderRequired() const;
-
- virtual const Projection::KeyOnly* keyFieldsOnly() const;
-
- virtual bool runningInitialInOrderPlan() const;
-
- virtual bool hasPossiblyExcludedPlans() const;
-
- virtual bool completePlanOfHybridSetScanAndOrderRequired() const {
- return _completePlanOfHybridSetScanAndOrderRequired;
- }
-
- virtual void clearIndexesForPatterns();
-
- virtual void abortOutOfOrderPlans();
-
- virtual void noteIterate( bool match, bool loadedDocument, bool chunkSkip );
-
- virtual void noteYield();
-
- virtual shared_ptr<ExplainQueryInfo> explainQueryInfo() const {
- return _explainQueryInfo;
- }
-
- private:
-
- QueryOptimizerCursorImpl( auto_ptr<MultiPlanScanner>& mps,
- const QueryPlanSelectionPolicy& planPolicy,
- bool requireOrder );
-
- void init( bool explain );
-
- /**
- * Advances the QueryPlanSet::Runner.
- * @param force - advance even if the current query op is not valid. The 'force' param should only be specified
- * when there are plans left in the runner.
- */
- bool _advance( bool force );
-
- /** Forward an exception when the runner errs out. */
- void rethrowOnError( const shared_ptr< QueryPlanRunner >& runner );
-
- void assertOk() const {
- massert( 14809, "Invalid access for cursor that is not ok()", !_currLoc().isNull() );
- }
-
- /** Insert and check for dups before takeover occurs */
- bool getsetdupInternal(const DiskLoc& loc);
-
- /** Just check for dups - after takeover occurs */
- bool getdupInternal(const DiskLoc& loc);
-
- bool _requireOrder;
- auto_ptr<MultiPlanScanner> _mps;
- CandidatePlanCharacter _initialCandidatePlans;
- shared_ptr<QueryPlanRunner> _originalRunner;
- QueryPlanRunner* _currRunner;
- bool _completePlanOfHybridSetScanAndOrderRequired;
- shared_ptr<MultiCursor> _takeover;
- long long _nscanned;
- // Using a SmallDupSet seems a bit hokey, but I've measured a 5% performance improvement
- // with ~100 document non multi key scans.
- SmallDupSet _dups;
- shared_ptr<ExplainQueryInfo> _explainQueryInfo;
- };
-
- /**
- * Helper class for generating a simple Cursor or QueryOptimizerCursor from a set of query
- * parameters. This class was refactored from a single function call and is not expected to
- * outlive its constructor arguments.
- */
- class CursorGenerator {
- public:
- CursorGenerator( const StringData& ns,
- const BSONObj &query,
- const BSONObj &order,
- const QueryPlanSelectionPolicy &planPolicy,
- const shared_ptr<const ParsedQuery> &parsedQuery,
- bool requireOrder,
- QueryPlanSummary *singlePlanSummary );
-
- shared_ptr<Cursor> generate();
-
- private:
- bool snapshot() const { return _parsedQuery && _parsedQuery->isSnapshot(); }
- bool explain() const { return _parsedQuery && _parsedQuery->isExplain(); }
- BSONObj min() const { return _parsedQuery ? _parsedQuery->getMin() : BSONObj(); }
- BSONObj max() const { return _parsedQuery ? _parsedQuery->getMax() : BSONObj(); }
- bool hasFields() const { return _parsedQuery && _parsedQuery->getFieldPtr(); }
-
- bool isOrderRequired() const { return _requireOrder; }
- bool mayShortcutQueryOptimizer() const {
- return min().isEmpty() && max().isEmpty() && !hasFields() && _argumentsHint.isEmpty();
- }
- BSONObj hint() const;
-
- void setArgumentsHint();
- shared_ptr<Cursor> shortcutCursor() const;
- void setMultiPlanScanner();
- shared_ptr<Cursor> singlePlanCursor();
-
- const StringData _ns;
- BSONObj _query;
- BSONObj _order;
- const QueryPlanSelectionPolicy &_planPolicy;
- shared_ptr<const ParsedQuery> _parsedQuery;
- bool _requireOrder;
- QueryPlanSummary *_singlePlanSummary;
-
- BSONObj _argumentsHint;
- auto_ptr<MultiPlanScanner> _mps;
- };
-
-} // namespace mongo
diff --git a/src/mongo/db/querypattern.cpp b/src/mongo/db/querypattern.cpp
deleted file mode 100644
index 8abdc8c281a..00000000000
--- a/src/mongo/db/querypattern.cpp
+++ /dev/null
@@ -1,117 +0,0 @@
-// @file querypattern.cpp - Query pattern matching for selecting similar plans given similar queries.
-
-/* Copyright 2011 10gen Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "querypattern.h"
-#include "mongo/db/queryutil.h"
-
-namespace mongo {
-
- QueryPattern::QueryPattern( const FieldRangeSet &frs, const BSONObj &sort ) {
- for( map<string,FieldRange>::const_iterator i = frs.ranges().begin(); i != frs.ranges().end(); ++i ) {
- if ( i->second.equality() ) {
- _fieldTypes[ i->first ] = QueryPattern::Equality;
- }
- else if ( i->second.empty() ) {
- _fieldTypes[ i->first ] = QueryPattern::Empty;
- }
- else if ( !i->second.universal() ) {
- bool upper = i->second.max().type() != MaxKey;
- bool lower = i->second.min().type() != MinKey;
- if ( upper && lower ) {
- _fieldTypes[ i->first ] = QueryPattern::UpperAndLowerBound;
- }
- else if ( upper ) {
- _fieldTypes[ i->first ] = QueryPattern::UpperBound;
- }
- else if ( lower ) {
- _fieldTypes[ i->first ] = QueryPattern::LowerBound;
- }
- else {
- _fieldTypes[ i->first ] = QueryPattern::ConstraintPresent;
- }
- }
- }
- setSort( sort );
- }
-
- /** for testing only - speed unimportant */
- bool QueryPattern::operator==( const QueryPattern &other ) const {
- bool less = operator<( other );
- bool more = other.operator<( *this );
- verify( !( less && more ) );
- return !( less || more );
- }
-
- /** for testing only - speed unimportant */
- bool QueryPattern::operator!=( const QueryPattern &other ) const {
- return !operator==( other );
- }
-
- string typeToString( enum QueryPattern::Type t ) {
- switch (t) {
- case QueryPattern::Empty:
- return "Empty";
- case QueryPattern::Equality:
- return "Equality";
- case QueryPattern::LowerBound:
- return "LowerBound";
- case QueryPattern::UpperBound:
- return "UpperBound";
- case QueryPattern::UpperAndLowerBound:
- return "UpperAndLowerBound";
- case QueryPattern::ConstraintPresent:
- return "ConstraintPresent";
- }
- return "";
- }
-
- string QueryPattern::toString() const {
- BSONObjBuilder b;
- for( map<string,Type>::const_iterator i = _fieldTypes.begin(); i != _fieldTypes.end(); ++i ) {
- b << i->first << typeToString( i->second );
- }
- return BSON( "query" << b.done() << "sort" << _sort ).toString();
- }
-
- void QueryPattern::setSort( const BSONObj sort ) {
- _sort = normalizeSort( sort );
- }
-
- BSONObj QueryPattern::normalizeSort( const BSONObj &spec ) {
- if ( spec.isEmpty() )
- return spec;
- int direction = ( spec.firstElement().number() >= 0 ) ? 1 : -1;
- BSONObjIterator i( spec );
- BSONObjBuilder b;
- while( i.moreWithEOO() ) {
- BSONElement e = i.next();
- if ( e.eoo() )
- break;
- b.append( e.fieldName(), direction * ( ( e.number() >= 0 ) ? -1 : 1 ) );
- }
- return b.obj();
- }
-
- CachedQueryPlan::CachedQueryPlan( const BSONObj &indexKey, long long nScanned,
- CandidatePlanCharacter planCharacter ) :
- _indexKey( indexKey ),
- _nScanned( nScanned ),
- _planCharacter( planCharacter ) {
- }
-
-
-} // namespace mongo
diff --git a/src/mongo/db/querypattern.h b/src/mongo/db/querypattern.h
deleted file mode 100644
index 3c7ad07fa9b..00000000000
--- a/src/mongo/db/querypattern.h
+++ /dev/null
@@ -1,118 +0,0 @@
-// @file querypattern.h - Query pattern matching for selecting similar plans given similar queries.
-
-/* Copyright 2011 10gen Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include "jsobj.h"
-
-namespace mongo {
-
- class FieldRangeSet;
-
- /**
- * Implements query pattern matching, used to determine if a query is
- * similar to an earlier query and should use the same plan.
- *
- * Two queries will generate the same QueryPattern, and therefore match each
- * other, if their fields have the same Types and they have the same sort
- * spec.
- */
- class QueryPattern {
- public:
- QueryPattern( const FieldRangeSet &frs, const BSONObj &sort );
- enum Type {
- Empty,
- Equality,
- LowerBound,
- UpperBound,
- UpperAndLowerBound,
- ConstraintPresent
- };
- bool operator<( const QueryPattern &other ) const;
- /** for testing only */
- bool operator==( const QueryPattern &other ) const;
- /** for testing only */
- bool operator!=( const QueryPattern &other ) const;
- /** for development / debugging */
- string toString() const;
- private:
- void setSort( const BSONObj sort );
- static BSONObj normalizeSort( const BSONObj &spec );
- map<string,Type> _fieldTypes;
- BSONObj _sort;
- };
-
- /** Summarizes the candidate plans that may run for a query. */
- class CandidatePlanCharacter {
- public:
- CandidatePlanCharacter( bool mayRunInOrderPlan, bool mayRunOutOfOrderPlan ) :
- _mayRunInOrderPlan( mayRunInOrderPlan ),
- _mayRunOutOfOrderPlan( mayRunOutOfOrderPlan ) {
- }
- CandidatePlanCharacter() :
- _mayRunInOrderPlan(),
- _mayRunOutOfOrderPlan() {
- }
- bool mayRunInOrderPlan() const { return _mayRunInOrderPlan; }
- bool mayRunOutOfOrderPlan() const { return _mayRunOutOfOrderPlan; }
- bool valid() const { return mayRunInOrderPlan() || mayRunOutOfOrderPlan(); }
- bool hybridPlanSet() const { return mayRunInOrderPlan() && mayRunOutOfOrderPlan(); }
- private:
- bool _mayRunInOrderPlan;
- bool _mayRunOutOfOrderPlan;
- };
-
- /** Information about a query plan that ran successfully for a QueryPattern. */
- class CachedQueryPlan {
- public:
- CachedQueryPlan() :
- _nScanned() {
- }
- CachedQueryPlan( const BSONObj &indexKey, long long nScanned,
- CandidatePlanCharacter planCharacter );
- BSONObj indexKey() const { return _indexKey; }
- long long nScanned() const { return _nScanned; }
- CandidatePlanCharacter planCharacter() const { return _planCharacter; }
- private:
- BSONObj _indexKey;
- long long _nScanned;
- CandidatePlanCharacter _planCharacter;
- };
-
- inline bool QueryPattern::operator<( const QueryPattern &other ) const {
- map<string,Type>::const_iterator i = _fieldTypes.begin();
- map<string,Type>::const_iterator j = other._fieldTypes.begin();
- while( i != _fieldTypes.end() ) {
- if ( j == other._fieldTypes.end() )
- return false;
- if ( i->first < j->first )
- return true;
- else if ( i->first > j->first )
- return false;
- if ( i->second < j->second )
- return true;
- else if ( i->second > j->second )
- return false;
- ++i;
- ++j;
- }
- if ( j != other._fieldTypes.end() )
- return true;
- return _sort.woCompare( other._sort ) < 0;
- }
-
-} // namespace mongo
diff --git a/src/mongo/db/queryutil.cpp b/src/mongo/db/queryutil.cpp
index 33fb3040449..773833d78ae 100644
--- a/src/mongo/db/queryutil.cpp
+++ b/src/mongo/db/queryutil.cpp
@@ -18,6 +18,7 @@
#include "mongo/db/queryutil.h"
#include "mongo/db/index_names.h"
+#include "mongo/db/matcher.h"
#include "mongo/db/pdfile.h"
#include "mongo/util/mongoutils/str.h"
@@ -1388,10 +1389,6 @@ namespace mongo {
return true;
}
- QueryPattern FieldRangeSet::pattern( const BSONObj &sort ) const {
- return QueryPattern( *this, sort );
- }
-
int FieldRangeSet::numNonUniversalRanges() const {
int count = 0;
for( map<string,FieldRange>::const_iterator i = _ranges.begin(); i != _ranges.end(); ++i ) {
diff --git a/src/mongo/db/queryutil.h b/src/mongo/db/queryutil.h
index a1eaf78bcf8..9dfc7a0cf3d 100644
--- a/src/mongo/db/queryutil.h
+++ b/src/mongo/db/queryutil.h
@@ -212,8 +212,6 @@ namespace mongo {
// element having field name '$elemMatch'.
};
- class QueryPattern;
-
/**
* A set of FieldRanges determined from constraints on the fields of a query,
* that may be used to determine index bounds.
@@ -279,7 +277,7 @@ namespace mongo {
const char *ns() const { return _ns.c_str(); }
- QueryPattern pattern( const BSONObj &sort = BSONObj() ) const;
+ // QueryPattern pattern( const BSONObj &sort = BSONObj() ) const;
SpecialIndices getSpecial() const;
/**
diff --git a/src/mongo/db/repl/oplog.cpp b/src/mongo/db/repl/oplog.cpp
index 2560b0944f3..b6ed66e9a65 100644
--- a/src/mongo/db/repl/oplog.cpp
+++ b/src/mongo/db/repl/oplog.cpp
@@ -516,7 +516,7 @@ namespace mongo {
Timer t;
const NamespaceString requestNs(ns);
- UpdateRequest request(requestNs, QueryPlanSelectionPolicy::idElseNatural());
+ UpdateRequest request(requestNs);
request.setQuery(o);
request.setUpdates(o);
@@ -545,7 +545,7 @@ namespace mongo {
b.append(_id);
const NamespaceString requestNs(ns);
- UpdateRequest request(requestNs, QueryPlanSelectionPolicy::idElseNatural());
+ UpdateRequest request(requestNs);
request.setQuery(b.done());
request.setUpdates(o);
@@ -572,7 +572,7 @@ namespace mongo {
const bool upsert = valueB || convertUpdateToUpsert;
const NamespaceString requestNs(ns);
- UpdateRequest request(requestNs, QueryPlanSelectionPolicy::idElseNatural());
+ UpdateRequest request(requestNs);
request.setQuery(updateCriteria);
request.setUpdates(o);
diff --git a/src/mongo/db/repl/rs_rollback.cpp b/src/mongo/db/repl/rs_rollback.cpp
index e5419aaa6b4..8017c965b53 100644
--- a/src/mongo/db/repl/rs_rollback.cpp
+++ b/src/mongo/db/repl/rs_rollback.cpp
@@ -37,6 +37,7 @@
#include "mongo/db/ops/update_request.h"
#include "mongo/db/ops/update_lifecycle_impl.h"
#include "mongo/db/ops/delete.h"
+#include "mongo/db/query/internal_plans.h"
#include "mongo/db/repl/oplog.h"
#include "mongo/db/repl/rs.h"
@@ -225,11 +226,19 @@ namespace mongo {
static void syncRollbackFindCommonPoint(DBClientConnection *them, HowToFixUp& h) {
verify( Lock::isLocked() );
Client::Context c(rsoplog);
+
NamespaceDetails *nsd = nsdetails(rsoplog);
verify(nsd);
- ReverseCappedCursor u(nsd);
- if( !u.ok() )
+
+ boost::scoped_ptr<Runner> runner(
+ InternalPlanner::collectionScan(rsoplog, InternalPlanner::BACKWARD));
+
+ BSONObj ourObj;
+ DiskLoc ourLoc;
+
+ if (Runner::RUNNER_ADVANCED != runner->getNext(&ourObj, &ourLoc)) {
throw rsfatal("our oplog empty or unreadable");
+ }
const Query q = Query().sort(reverseNaturalObj);
const bo fields = BSON( "ts" << 1 << "h" << 1 );
@@ -241,7 +250,6 @@ namespace mongo {
if( t.get() == 0 || !t->more() ) throw rsfatal("remote oplog empty or unreadable");
- BSONObj ourObj = u.current();
OpTime ourTime = ourObj["ts"]._opTime();
BSONObj theirObj = t->nextSafe();
OpTime theirTime = theirObj["ts"]._opTime();
@@ -270,7 +278,7 @@ namespace mongo {
log() << "replSet rollback found matching events at " << ourTime.toStringPretty() << rsLog;
log() << "replSet rollback findcommonpoint scanned : " << scanned << rsLog;
h.commonPoint = ourTime;
- h.commonPointOurDiskloc = u.currLoc();
+ h.commonPointOurDiskloc = ourLoc;
return;
}
@@ -286,15 +294,13 @@ namespace mongo {
theirObj = t->nextSafe();
theirTime = theirObj["ts"]._opTime();
- u.advance();
- if( !u.ok() ) {
+ if (Runner::RUNNER_ADVANCED != runner->getNext(&ourObj, &ourLoc)) {
log() << "replSet rollback error RS101 reached beginning of local oplog" << rsLog;
log() << "replSet them: " << them->toString() << " scanned: " << scanned << rsLog;
log() << "replSet theirTime: " << theirTime.toStringLong() << rsLog;
log() << "replSet ourTime: " << ourTime.toStringLong() << rsLog;
throw rsfatal("RS101 reached beginning of local oplog [1]");
}
- ourObj = u.current();
ourTime = ourObj["ts"]._opTime();
}
else if( theirTime > ourTime ) {
@@ -311,15 +317,13 @@ namespace mongo {
else {
// theirTime < ourTime
refetch(h, ourObj);
- u.advance();
- if( !u.ok() ) {
+ if (Runner::RUNNER_ADVANCED != runner->getNext(&ourObj, &ourLoc)) {
log() << "replSet rollback error RS101 reached beginning of local oplog" << rsLog;
log() << "replSet them: " << them->toString() << " scanned: " << scanned << rsLog;
log() << "replSet theirTime: " << theirTime.toStringLong() << rsLog;
log() << "replSet ourTime: " << ourTime.toStringLong() << rsLog;
throw rsfatal("RS101 reached beginning of local oplog [2]");
}
- ourObj = u.current();
ourTime = ourObj["ts"]._opTime();
}
}
diff --git a/src/mongo/db/scanandorder.cpp b/src/mongo/db/scanandorder.cpp
deleted file mode 100644
index 40b5eb9edaf..00000000000
--- a/src/mongo/db/scanandorder.cpp
+++ /dev/null
@@ -1,138 +0,0 @@
-/* scanandorder.cpp
- Order results (that aren't already indexes and in order.)
-*/
-
-/**
- * Copyright (C) 2008 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/pch.h"
-
-#include "mongo/db/scanandorder.h"
-
-#include "mongo/db/index/btree_key_generator.h"
-#include "mongo/db/matcher.h"
-#include "mongo/db/parsed_query.h"
-#include "mongo/util/mongoutils/str.h"
-
-namespace mongo {
-
- const unsigned ScanAndOrder::MaxScanAndOrderBytes = 32 * 1024 * 1024;
-
- void ScanAndOrder::add(const BSONObj& o, const DiskLoc* loc) {
- verify( o.isValid() );
- BSONObj k;
- try {
- k = _order.getKeyFromObject(o);
- }
- catch (UserException &e) {
- if ( e.getCode() == BtreeKeyGenerator::ParallelArraysCode) { // cannot get keys for parallel arrays
- // fix lasterror text to be more accurate.
- uasserted( 15925, "cannot sort with keys that are parallel arrays" );
- }
- else
- throw;
- }
-
- if ( k.isEmpty() ) {
- return;
- }
- if ( (int) _best.size() < _limit ) {
- _add(k, o, loc);
- return;
- }
- BestMap::iterator i;
- verify( _best.end() != _best.begin() );
- i = _best.end();
- i--;
- _addIfBetter(k, o, i, loc);
- }
-
-
- void ScanAndOrder::fill( BufBuilder& b, const ParsedQuery *parsedQuery, int& nout ) const {
- int n = 0;
- int nFilled = 0;
- Projection *projection = parsedQuery ? parsedQuery->getFields() : NULL;
- scoped_ptr<Matcher> arrayMatcher;
- scoped_ptr<MatchDetails> details;
- if ( projection && projection->getArrayOpType() == Projection::ARRAY_OP_POSITIONAL ) {
- // the projection specified an array positional match operator; create a new matcher
- // for the projected array
- arrayMatcher.reset( new Matcher( parsedQuery->getFilter() ) );
- details.reset( new MatchDetails );
- details->requestElemMatchKey();
- }
- for ( BestMap::const_iterator i = _best.begin(); i != _best.end(); i++ ) {
- n++;
- if ( n <= _startFrom )
- continue;
- const BSONObj& o = i->second;
- massert( 16355, "positional operator specified, but no array match",
- ! arrayMatcher || arrayMatcher->matches( o, details.get() ) );
- fillQueryResultFromObj( b, projection, o, details.get() );
- nFilled++;
- if ( nFilled >= _limit )
- break;
- }
- nout = nFilled;
- }
-
- void ScanAndOrder::_add(const BSONObj& k, const BSONObj& o, const DiskLoc* loc) {
- BSONObj docToReturn = o;
- if ( loc ) {
- BSONObjBuilder b;
- b.appendElements(o);
- b.append("$diskLoc", loc->toBSONObj());
- docToReturn = b.obj();
- }
- _validateAndUpdateApproxSize( k.objsize() + docToReturn.objsize() );
- _best.insert(make_pair(k.getOwned(),docToReturn.getOwned()));
- }
-
- void ScanAndOrder::_addIfBetter(const BSONObj& k, const BSONObj& o, const BestMap::iterator& i,
- const DiskLoc* loc) {
- const BSONObj& worstBestKey = i->first;
- int cmp = worstBestKey.woCompare(k, _order._keyPattern);
- if ( cmp > 0 ) {
- // k is better, 'upgrade'
- _validateAndUpdateApproxSize( -i->first.objsize() + -i->second.objsize() );
- _best.erase(i);
- _add(k, o, loc);
- }
- }
-
- void ScanAndOrder::_validateAndUpdateApproxSize( const int approxSizeDelta ) {
- // note : adjust when bson return limit adjusts. note this limit should be a bit higher.
- int newApproxSize = _approxSize + approxSizeDelta;
- verify( newApproxSize >= 0 );
- uassert( ScanAndOrderMemoryLimitExceededAssertionCode,
- "too much data for sort() with no index. add an index or specify a smaller limit",
- (unsigned)newApproxSize < MaxScanAndOrderBytes );
- _approxSize = newApproxSize;
- }
-
-} // namespace mongo
diff --git a/src/mongo/db/scanandorder.h b/src/mongo/db/scanandorder.h
deleted file mode 100644
index 1b34b4f2808..00000000000
--- a/src/mongo/db/scanandorder.h
+++ /dev/null
@@ -1,139 +0,0 @@
-/* scanandorder.h
- Order results (that aren't already indexes and in order.)
-*/
-
-/**
-* Copyright (C) 2008 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 "mongo/db/diskloc.h"
-#include "mongo/db/projection.h"
-#include "mongo/db/queryutil.h"
-
-namespace mongo {
-
- class ParsedQuery;
-
- static const int ScanAndOrderMemoryLimitExceededAssertionCode = 10128;
-
- class KeyType : boost::noncopyable {
- public:
- BSONObj _keyPattern;
- FieldRangeVector _keyCutter;
- public:
- KeyType(const BSONObj &pattern, const FieldRangeSet &frs)
- : _keyPattern(pattern), _keyCutter(frs, pattern, 1) {
- verify(!pattern.isEmpty());
- }
-
- /**
- * @return first key of the object that would be encountered while
- * scanning an index with keySpec 'pattern' using constraints 'frs', or
- * BSONObj() if no such key.
- */
- BSONObj getKeyFromObject(const BSONObj &o) const {
- return _keyCutter.firstMatch(o);
- }
- };
-
- /* todo:
- _ response size limit from runquery; push it up a bit.
- */
-
- inline void fillQueryResultFromObj(BufBuilder& bb, const Projection *filter, const BSONObj& js,
- const MatchDetails* details = NULL,
- const DiskLoc* loc=NULL) {
- if ( filter ) {
- BSONObjBuilder b( bb );
- filter->transform( js , b, details );
- if (loc)
- b.append("$diskLoc", loc->toBSONObj());
- b.done();
- }
- else if (loc) {
- BSONObjBuilder b( bb );
- b.appendElements(js);
- b.append("$diskLoc", loc->toBSONObj());
- b.done();
- }
- else {
- bb.appendBuf((void*) js.objdata(), js.objsize());
- }
- }
-
- typedef multimap<BSONObj,BSONObj,BSONObjCmp> BestMap;
- class ScanAndOrder {
- public:
- static const unsigned MaxScanAndOrderBytes;
-
- ScanAndOrder(int startFrom, int limit, const BSONObj &order, const FieldRangeSet &frs) :
- _best( BSONObjCmp( order ) ),
- _startFrom(startFrom), _order(order, frs) {
- _limit = limit > 0 ? limit + _startFrom : 0x7fffffff;
- _approxSize = 0;
- }
-
- int size() const { return _best.size(); }
-
- /**
- * @throw ScanAndOrderMemoryLimitExceededAssertionCode if adding would grow memory usage
- * to ScanAndOrder::MaxScanAndOrderBytes.
- */
- void add(const BSONObj &o, const DiskLoc* loc);
-
- /* scanning complete. stick the query result in b for n objects. */
- void fill(BufBuilder& b, const ParsedQuery *query, int& nout) const;
-
- /** Functions for testing. */
- protected:
-
- unsigned approxSize() const { return _approxSize; }
-
- private:
-
- void _add(const BSONObj& k, const BSONObj& o, const DiskLoc* loc);
-
- void _addIfBetter(const BSONObj& k, const BSONObj& o, const BestMap::iterator& i,
- const DiskLoc* loc);
-
- /**
- * @throw ScanAndOrderMemoryLimitExceededAssertionCode if approxSize would grow too high,
- * otherwise update _approxSize.
- */
- void _validateAndUpdateApproxSize( const int approxSizeDelta );
-
- BestMap _best; // key -> full object
- int _startFrom;
- int _limit; // max to send back.
- KeyType _order;
- unsigned _approxSize;
-
- };
-
-} // namespace mongo
diff --git a/src/mongo/db/structure/collection_info_cache.cpp b/src/mongo/db/structure/collection_info_cache.cpp
index 8a2bf7ec0ef..39921acd4ac 100644
--- a/src/mongo/db/structure/collection_info_cache.cpp
+++ b/src/mongo/db/structure/collection_info_cache.cpp
@@ -43,9 +43,7 @@ namespace mongo {
CollectionInfoCache::CollectionInfoCache( Collection* collection )
: _collection( collection ),
- _keysComputed( false ),
- _qcCacheMutex( "_qcCacheMutex" ),
- _qcWriteCount( 0 ) {}
+ _keysComputed( false ) { }
void CollectionInfoCache::reset() {
Lock::assertWriteLocked( _collection->ns().ns() );
@@ -73,33 +71,11 @@ namespace mongo {
}
void CollectionInfoCache::notifyOfWriteOp() {
- scoped_lock lk( _qcCacheMutex );
- if ( _qcCache.empty() )
- return;
- if ( ++_qcWriteCount >= 100 )
- _clearQueryCache_inlock();
+ // TODO: hook up w/new cache when impl
}
void CollectionInfoCache::clearQueryCache() {
- scoped_lock lk( _qcCacheMutex );
- _clearQueryCache_inlock();
- }
-
- void CollectionInfoCache::_clearQueryCache_inlock() {
- _qcCache.clear();
- _qcWriteCount = 0;
- }
-
- CachedQueryPlan CollectionInfoCache::cachedQueryPlanForPattern( const QueryPattern &pattern ) {
- scoped_lock lk( _qcCacheMutex );
- return _qcCache[ pattern ];
- }
-
-
- void CollectionInfoCache::registerCachedQueryPlanForPattern( const QueryPattern &pattern,
- const CachedQueryPlan &cachedQueryPlan ) {
- scoped_lock lk( _qcCacheMutex );
- _qcCache[ pattern ] = cachedQueryPlan;
+ // TODO: hook up w/new cache when impl
}
}
diff --git a/src/mongo/db/structure/collection_info_cache.h b/src/mongo/db/structure/collection_info_cache.h
index be88b246559..32a87696394 100644
--- a/src/mongo/db/structure/collection_info_cache.h
+++ b/src/mongo/db/structure/collection_info_cache.h
@@ -31,8 +31,6 @@
#pragma once
#include "mongo/db/index_set.h"
-#include "mongo/db/querypattern.h"
-
namespace mongo {
@@ -72,11 +70,6 @@ namespace mongo {
/* you must notify the cache if you are doing writes, as query plan utility will change */
void notifyOfWriteOp();
- CachedQueryPlan cachedQueryPlanForPattern( const QueryPattern &pattern );
-
- void registerCachedQueryPlanForPattern( const QueryPattern &pattern,
- const CachedQueryPlan &cachedQueryPlan );
-
private:
Collection* _collection; // not owned
@@ -86,15 +79,6 @@ namespace mongo {
IndexPathSet _indexedPaths;
void computeIndexKeys();
-
- // --- for old query optimizer
-
- void _clearQueryCache_inlock();
-
- mutex _qcCacheMutex;
- int _qcWriteCount;
- std::map<QueryPattern,CachedQueryPlan> _qcCache;
-
};
}
diff --git a/src/mongo/dbtests/btreebuildertests.cpp b/src/mongo/dbtests/btreebuildertests.cpp
index bd668c6c45f..5e23f6b4c3f 100644
--- a/src/mongo/dbtests/btreebuildertests.cpp
+++ b/src/mongo/dbtests/btreebuildertests.cpp
@@ -30,7 +30,6 @@
#include "mongo/db/btreebuilder.h"
-#include "mongo/db/btreecursor.h"
#include "mongo/db/catalog/index_catalog.h"
#include "mongo/db/pdfile.h"
#include "mongo/db/structure/collection.h"
@@ -88,45 +87,6 @@ namespace BtreeBuilderTests {
};
/**
- * BtreeBuilder::commit() constructs a btree from the keys provided to BtreeBuilder::addKey().
- */
- class Commit : public IndexBuildBase {
- public:
- void run() {
- IndexDetails& id = addIndexWithInfo();
- // Create a btree builder.
- BtreeBuilder<V1> builder( false, id );
- // Add some keys to the builder, in order.
- int32_t nKeys = 1000;
- for( int32_t i = 0; i < nKeys; ++i ) {
- BSONObj key = BSON( "a" << i );
- builder.addKey( key, /* dummy location */ DiskLoc() );
- }
- // The root of the index has not yet been set.
- ASSERT( id.head.isNull() );
- // Call commit on the builder to finish building the btree.
- builder.commit( true );
- // The root of the index is now set.
- ASSERT( !id.head.isNull() );
- // Create a cursor over the index.
- scoped_ptr<BtreeCursor> cursor(
- BtreeCursor::make( nsdetails( _ns ),
- id,
- BSON( "" << -1 ), // startKey below minimum key value.
- BSON( "" << nKeys ), // endKey above maximum key value.
- true, // endKeyInclusive true.
- 1 // direction forward.
- ) );
- // Check that the keys in the index are the expected ones.
- int32_t expectedKey = 0;
- for( ; cursor->ok(); cursor->advance(), ++expectedKey ) {
- ASSERT_EQUALS( expectedKey, cursor->currKey().firstElement().number() );
- }
- ASSERT_EQUALS( nKeys, expectedKey );
- }
- };
-
- /**
* BtreeBuilder::commit() is interrupted if there is a request to kill the current operation.
*/
class InterruptCommit : public IndexBuildBase {
@@ -174,7 +134,6 @@ namespace BtreeBuilderTests {
}
void setupTests() {
- add<Commit>();
add<InterruptCommit>( false );
add<InterruptCommit>( true );
}
diff --git a/src/mongo/dbtests/btreepositiontests.cpp b/src/mongo/dbtests/btreepositiontests.cpp
deleted file mode 100644
index 34681507fee..00000000000
--- a/src/mongo/dbtests/btreepositiontests.cpp
+++ /dev/null
@@ -1,339 +0,0 @@
-/**
- * Copyright (C) 2012 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/btreeposition.h"
-
-#include "mongo/db/btree.h"
-#include "mongo/db/btreecursor.h"
-#include "mongo/db/pdfile.h"
-#include "mongo/dbtests/dbtests.h"
-#include "mongo/platform/cstdint.h"
-
-namespace BtreePositionTests {
-
- DBDirectClient _client;
- const char* _ns = "unittests.btreeposition";
-
- namespace BtreeKeyLocation {
- using mongo::BtreeKeyLocation;
-
- /** Check equality comparison performed by BtreeKeyLocation::operator==. */
- class Equality {
- public:
- void run() {
- // Equal initially.
- BtreeKeyLocation one, two;
- ASSERT_EQUALS( one, two );
-
- // Unequal with equal indexes but unequal buckets.
- one.bucket = DiskLoc( 1, 2 );
- ASSERT( !( one == two ) );
-
- // Unequal with equal buckets but unequal indexes.
- one.pos = 1;
- two.bucket = DiskLoc( 1, 2 );
- ASSERT( !( one == two ) );
-
- // Equal with both bucket and index equal.
- two.pos = 1;
- ASSERT_EQUALS( one, two );
- }
- };
-
- } // namespace BtreeKeyLocation
-
- namespace LogicalBtreePosition {
- using mongo::LogicalBtreePosition;
- using mongo::BtreeKeyLocation;
-
- /** Helper to construct custom btrees for tests. */
- class TestableBtree : public BtreeBucket<V1> {
- public:
-
- /**
- * Create a btree structure based on the json structure in @param 'spec', and set
- * @param 'id' to this btree.
- * @return the btree.
- *
- * For example the spec { b:{ a:null }, d:{ c:null }, _:{ e:null } } would create the
- * btree
- *
- * [ b, d ]
- * / | \
- * [ a ] [ c ] [ e ]
- *
- * Dummy record locations are populated based on the string values. The first character
- * of each string value must be a hex digit. See dummyRecordForKey().
- */
- static TestableBtree* set( const string& spec, IndexDetails& id ) {
- DiskLoc btree = make( spec, id );
- id.head = btree;
- return cast( btree );
- }
-
- /** Cast a disk location to a TestableBtree. */
- static TestableBtree* cast( const DiskLoc& l ) {
- return static_cast<TestableBtree*>( l.btreemod<V1>() );
- }
-
- /** Push a new key to this bucket. */
- void push( const BSONObj& key, DiskLoc child ) {
- KeyOwned k(key);
- pushBack( dummyRecordForKey( key ),
- k,
- Ordering::make( BSON( "a" << 1 ) ),
- child );
- }
-
- /** Delete a key from this bucket. */
- void delKey( int index ) { _delKeyAtPos( index ); }
-
- /** Reset the number of keys for this bucket. */
- void setN( int newN ) { n = newN; }
-
- /** Set the right child for this bucket. */
- void setNext( const DiskLoc &child ) { nextChild = child; }
-
- /** A dummy record DiskLoc generated from a key's string value. */
- static DiskLoc dummyRecordForKey( const BSONObj& key ) {
- return DiskLoc( 0, fromHex( key.firstElement().String()[ 0 ] ) );
- }
-
- private:
- static DiskLoc make( const string& specString, IndexDetails& id ) {
- BSONObj spec = fromjson( specString );
- DiskLoc bucket = addBucket( id );
- cast( bucket )->init();
- TestableBtree* btree = TestableBtree::cast( bucket );
- BSONObjIterator i( spec );
- while( i.more() ) {
- BSONElement e = i.next();
- DiskLoc child;
- if ( e.type() == Object ) {
- child = make( e.embeddedObject().jsonString(), id );
- }
- if ( e.fieldName() == string( "_" ) ) {
- btree->setNext( child );
- }
- else {
- btree->push( BSON( "" << e.fieldName() ), child );
- }
- }
- btree->fixParentPtrs( bucket );
- return bucket;
- }
- };
-
- /**
- * Helper to check that the expected key and its corresponding dummy record are located at
- * the supplied key location.
- */
- void assertKeyForPosition( const string& expectedKey,
- const BtreeKeyLocation& location ) {
- BucketBasics<V1>::KeyNode keyNode =
- location.bucket.btree<V1>()->keyNode( location.pos );
- BSONObj expectedKeyObj = BSON( "" << expectedKey );
- ASSERT_EQUALS( expectedKeyObj, keyNode.key.toBson() );
- ASSERT_EQUALS( TestableBtree::dummyRecordForKey( expectedKeyObj ),
- keyNode.recordLoc );
- }
-
- /** A btree position is recovered when the btree bucket is unmodified. */
- class RecoverPositionBucketUnchanged {
- public:
- void run() {
- Client::WriteContext ctx( _ns );
- _client.dropCollection( _ns );
-
- // Add an index and populate it with dummy keys.
- _client.ensureIndex( _ns, BSON( "a" << 1 ) );
- IndexDetails& idx = nsdetails( _ns )->idx( 1 );
- TestableBtree* btree = TestableBtree::set( "{b:{a:null},_:{c:null}}", idx );
-
- // Locate the 'a' key.
- BtreeKeyLocation aLocation( btree->keyNode( 0 ).prevChildBucket, 0 );
-
- // Try to recover the key location.
- Ordering ordering = Ordering::make( nsdetails( _ns )->idx( 1 ).keyPattern() );
- LogicalBtreePosition logical( idx, ordering, aLocation );
- logical.init();
-
- // Check that the original location is recovered.
- ASSERT_EQUALS( aLocation, logical.currentLocation() );
-
- // Invalidate the original location.
- logical.invalidateInitialLocation();
-
- // Check that the original location is still recovered.
- ASSERT_EQUALS( aLocation, logical.currentLocation() );
- assertKeyForPosition( "a", logical.currentLocation() );
- }
- };
-
- /** A btree position is recovered after its initial bucket is deallocated. */
- class RecoverPositionBucketDeallocated {
- public:
- void run() {
- Client::WriteContext ctx( _ns );
- _client.dropCollection( _ns );
-
- // Add an index and populate it with dummy keys.
- _client.ensureIndex( _ns, BSON( "a" << 1 ) );
- IndexDetails& idx = nsdetails( _ns )->idx( 1 );
- TestableBtree* btree = TestableBtree::set( "{b:{a:null},_:{c:null}}", idx );
-
- // Locate the 'c' key.
- BtreeKeyLocation cLocation( btree->getNextChild(), 0 );
-
- // Identify the key position.
- Ordering ordering = Ordering::make( nsdetails( _ns )->idx( 1 ).keyPattern() );
- LogicalBtreePosition logical( idx, ordering, cLocation );
- logical.init();
-
- // Invalidate the 'c' key's btree bucket.
- TestableBtree::cast( cLocation.bucket )->deallocBucket( cLocation.bucket, idx );
-
- // Add the 'c' key back to the tree, in the root bucket.
- btree->push( BSON( "" << "c" ),
- TestableBtree::dummyRecordForKey( BSON( "" << "c" ) ) );
-
- // Check that the new location of 'c' is recovered.
- ASSERT_EQUALS( BtreeKeyLocation( idx.head, 1 ), logical.currentLocation() );
- assertKeyForPosition( "c", logical.currentLocation() );
- }
- };
-
- /** A btree position is recovered after its initial bucket shrinks. */
- class RecoverPositionKeyIndexInvalid {
- public:
- void run() {
- Client::WriteContext ctx( _ns );
- _client.dropCollection( _ns );
-
- // Add an index and populate it with dummy keys.
- _client.ensureIndex( _ns, BSON( "a" << 1 ) );
- IndexDetails& idx = nsdetails( _ns )->idx( 1 );
- TestableBtree::set( "{b:{a:null},c:null,_:{d:null}}", idx );
-
- // Locate the 'c' key.
- BtreeKeyLocation cLocation( idx.head, 1 );
-
- // Identify the key position.
- Ordering ordering = Ordering::make( nsdetails( _ns )->idx( 1 ).keyPattern() );
- LogicalBtreePosition logical( idx, ordering, cLocation );
- logical.init();
-
- // Remove the 'c' key by resizing the root bucket.
- TestableBtree::cast( cLocation.bucket )->setN( 1 );
-
- // Check that the location of 'd' is recovered.
- assertKeyForPosition( "d", logical.currentLocation() );
- }
- };
-
- /** A btree position is recovered after the key it refers to is removed. */
- class RecoverPositionKeyRemoved {
- public:
- void run() {
- Client::WriteContext ctx( _ns );
- _client.dropCollection( _ns );
-
- // Add an index and populate it with dummy keys.
- _client.ensureIndex( _ns, BSON( "a" << 1 ) );
- IndexDetails& idx = nsdetails( _ns )->idx( 1 );
- TestableBtree::set( "{b:{a:null},c:null,e:{d:null}}", idx );
-
- // Locate the 'c' key.
- BtreeKeyLocation cLocation( idx.head, 1 );
-
- // Identify the key position.
- Ordering ordering = Ordering::make( nsdetails( _ns )->idx( 1 ).keyPattern() );
- LogicalBtreePosition logical( idx, ordering, cLocation );
- logical.init();
-
- // Remove the 'c' key.
- TestableBtree::cast( cLocation.bucket )->delKey( 1 );
-
- // Check that the location of 'd' is recovered.
- assertKeyForPosition( "d", logical.currentLocation() );
- }
- };
-
- /**
- * A btree position is recovered after the key it refers to is removed, and a subsequent key
- * has the same record location.
- */
- class RecoverPositionKeyRemovedWithMatchingRecord {
- public:
- void run() {
- Client::WriteContext ctx( _ns );
- _client.dropCollection( _ns );
-
- // Add an index and populate it with dummy keys.
- _client.ensureIndex( _ns, BSON( "a" << 1 ) );
- IndexDetails& idx = nsdetails( _ns )->idx( 1 );
- TestableBtree* btree =
- TestableBtree::set( "{b:{a:null},c:null,ccc:{cc:null}}", idx );
-
- // Verify that the 'c' key has the the same record location as the 'ccc' key, which
- // is a requirement of this test.
- ASSERT_EQUALS( btree->keyNode( 1 ).recordLoc, btree->keyNode( 2 ).recordLoc );
-
- // Locate the 'c' key.
- BtreeKeyLocation cLocation( idx.head, 1 );
-
- // Identify the key position.
- Ordering ordering = Ordering::make( nsdetails( _ns )->idx( 1 ).keyPattern() );
- LogicalBtreePosition logical( idx, ordering, cLocation );
- logical.init();
-
- // Remove the 'c' key.
- TestableBtree::cast( cLocation.bucket )->delKey( 1 );
-
- // Check that the location of 'cc' is recovered.
- assertKeyForPosition( "cc", logical.currentLocation() );
- }
- };
-
- } // namespace LogicalBtreePosition
-
- class All : public Suite {
- public:
- All() : Suite( "btreeposition" ) {
- }
- void setupTests() {
- add<BtreeKeyLocation::Equality>();
- add<LogicalBtreePosition::RecoverPositionBucketUnchanged>();
- add<LogicalBtreePosition::RecoverPositionBucketDeallocated>();
- add<LogicalBtreePosition::RecoverPositionKeyIndexInvalid>();
- add<LogicalBtreePosition::RecoverPositionKeyRemoved>();
- add<LogicalBtreePosition::RecoverPositionKeyRemovedWithMatchingRecord>();
- }
- } myall;
-
-} // BtreePositionTests
diff --git a/src/mongo/dbtests/btreetests.cpp b/src/mongo/dbtests/btreetests.cpp
index bf982a6f4d5..f04406e49a3 100644
--- a/src/mongo/dbtests/btreetests.cpp
+++ b/src/mongo/dbtests/btreetests.cpp
@@ -32,7 +32,6 @@
#include "mongo/pch.h"
#include "mongo/db/btree.h"
-#include "mongo/db/btreecursor.h"
#include "mongo/db/db.h"
#include "mongo/db/json.h"
#include "mongo/dbtests/dbtests.h"
diff --git a/src/mongo/dbtests/btreetests.inl b/src/mongo/dbtests/btreetests.inl
index d06b8c7b12a..200aa19997c 100644
--- a/src/mongo/dbtests/btreetests.inl
+++ b/src/mongo/dbtests/btreetests.inl
@@ -308,6 +308,8 @@
}
};
+/*
+// QUERY_MIGRATION: port later
class PackUnused : public Base {
public:
void run() {
@@ -413,6 +415,7 @@
Base::insert( k );
}
};
+ */
class MergeBuckets : public Base {
public:
@@ -1642,8 +1645,9 @@
add< MissingLocateMultiBucket >();
add< SERVER983 >();
add< DontReuseUnused >();
- add< PackUnused >();
- add< DontDropReferenceKey >();
+ // QUERY_MIGRATION
+ // add< PackUnused >();
+ // add< DontDropReferenceKey >();
add< MergeBucketsLeft >();
add< MergeBucketsRight >();
// add< MergeBucketsHead >();
diff --git a/src/mongo/dbtests/counttests.cpp b/src/mongo/dbtests/counttests.cpp
index cdf778eac88..5528093916f 100644
--- a/src/mongo/dbtests/counttests.cpp
+++ b/src/mongo/dbtests/counttests.cpp
@@ -30,7 +30,6 @@
#include <boost/thread/thread.hpp>
-#include "mongo/db/cursor.h"
#include "mongo/db/db.h"
#include "mongo/db/json.h"
#include "mongo/db/ops/count.h"
diff --git a/src/mongo/dbtests/cursortests.cpp b/src/mongo/dbtests/cursortests.cpp
deleted file mode 100644
index 1c4de2102ff..00000000000
--- a/src/mongo/dbtests/cursortests.cpp
+++ /dev/null
@@ -1,814 +0,0 @@
-// cusrortests.cpp // cursor related unit tests
-//
-
-/**
- * Copyright (C) 2009 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/pch.h"
-
-#include "mongo/db/btreecursor.h"
-#include "mongo/db/clientcursor.h"
-#include "mongo/db/instance.h"
-#include "mongo/db/json.h"
-#include "mongo/db/query_optimizer.h"
-#include "mongo/db/queryutil.h"
-#include "mongo/dbtests/dbtests.h"
-
-namespace CursorTests {
-
- namespace BtreeCursor {
-
- using mongo::BtreeCursor;
-
- // The ranges expressed in these tests are impossible given our query
- // syntax, so going to do them a hacky way.
-
- class Base {
- protected:
- static const char *ns() { return "unittests.cursortests.Base"; }
- FieldRangeVector *vec( int *vals, int len, int direction = 1 ) {
- FieldRangeSet s( "", BSON( "a" << 1 ), true, true );
- for( int i = 0; i < len; i += 2 ) {
- _objs.push_back( BSON( "a" << BSON( "$gte" << vals[ i ] << "$lte" << vals[ i + 1 ] ) ) );
- FieldRangeSet s2( "", _objs.back(), true, true );
- if ( i == 0 ) {
- s.range( "a" ) = s2.range( "a" );
- }
- else {
- s.range( "a" ) |= s2.range( "a" );
- }
- }
- // orphan idxSpec for this test
- BSONObj kp = BSON( "a" << 1 );
- return new FieldRangeVector( s, kp, direction );
- }
- DBDirectClient _c;
- private:
- vector< BSONObj > _objs;
- };
-
- class MultiRangeForward : public Base {
- public:
- void run() {
- const char *ns = "unittests.cursortests.BtreeCursorTests.MultiRange";
- {
- DBDirectClient c;
- for( int i = 0; i < 10; ++i )
- c.insert( ns, BSON( "a" << i ) );
- ASSERT( c.ensureIndex( ns, BSON( "a" << 1 ) ) );
- }
- int v[] = { 1, 2, 4, 6 };
- boost::shared_ptr< FieldRangeVector > frv( vec( v, 4 ) );
- Client::WriteContext ctx( ns );
- scoped_ptr<BtreeCursor> _c( BtreeCursor::make( nsdetails( ns ),
- nsdetails( ns )->idx(1),
- frv,
- 0,
- 1 ) );
- BtreeCursor &c = *_c.get();
- ASSERT_EQUALS( "BtreeCursor a_1 multi", c.toString() );
- double expected[] = { 1, 2, 4, 5, 6 };
- for( int i = 0; i < 5; ++i ) {
- ASSERT( c.ok() );
- ASSERT_EQUALS( expected[ i ], c.currKey().firstElement().number() );
- c.advance();
- }
- ASSERT( !c.ok() );
- }
- };
-
- class MultiRangeGap : public Base {
- public:
- void run() {
- const char *ns = "unittests.cursortests.BtreeCursorTests.MultiRangeGap";
- {
- DBDirectClient c;
- for( int i = 0; i < 10; ++i )
- c.insert( ns, BSON( "a" << i ) );
- for( int i = 100; i < 110; ++i )
- c.insert( ns, BSON( "a" << i ) );
- ASSERT( c.ensureIndex( ns, BSON( "a" << 1 ) ) );
- }
- int v[] = { -50, 2, 40, 60, 109, 200 };
- boost::shared_ptr< FieldRangeVector > frv( vec( v, 6 ) );
- Client::WriteContext ctx( ns );
- scoped_ptr<BtreeCursor> _c( BtreeCursor::make( nsdetails( ns ),
- nsdetails( ns )->idx(1),
- frv,
- 0,
- 1 ) );
- BtreeCursor &c = *_c.get();
- ASSERT_EQUALS( "BtreeCursor a_1 multi", c.toString() );
- double expected[] = { 0, 1, 2, 109 };
- for( int i = 0; i < 4; ++i ) {
- ASSERT( c.ok() );
- ASSERT_EQUALS( expected[ i ], c.currKey().firstElement().number() );
- c.advance();
- }
- ASSERT( !c.ok() );
- }
- };
-
- class MultiRangeReverse : public Base {
- public:
- void run() {
- const char *ns = "unittests.cursortests.BtreeCursorTests.MultiRangeReverse";
- {
- DBDirectClient c;
- for( int i = 0; i < 10; ++i )
- c.insert( ns, BSON( "a" << i ) );
- ASSERT( c.ensureIndex( ns, BSON( "a" << 1 ) ) );
- }
- int v[] = { 1, 2, 4, 6 };
- boost::shared_ptr< FieldRangeVector > frv( vec( v, 4, -1 ) );
- Client::WriteContext ctx( ns );
- scoped_ptr<BtreeCursor> _c( BtreeCursor::make( nsdetails( ns ),
- nsdetails( ns )->idx(1),
- frv,
- 0,
- -1 ) );
- BtreeCursor& c = *_c.get();
- ASSERT_EQUALS( "BtreeCursor a_1 reverse multi", c.toString() );
- double expected[] = { 6, 5, 4, 2, 1 };
- for( int i = 0; i < 5; ++i ) {
- ASSERT( c.ok() );
- ASSERT_EQUALS( expected[ i ], c.currKey().firstElement().number() );
- c.advance();
- }
- ASSERT( !c.ok() );
- }
- };
-
- class Base2 {
- public:
- virtual ~Base2() { _c.dropCollection( ns() ); }
- protected:
- static const char *ns() { return "unittests.cursortests.Base2"; }
- DBDirectClient _c;
- virtual BSONObj idx() const = 0;
- virtual int direction() const { return 1; }
- void insert( const BSONObj &o ) {
- _objs.push_back( o );
- _c.insert( ns(), o );
- }
- void check( const BSONObj &spec ) {
- {
- BSONObj keypat = idx();
- //cout << keypat.toString() << endl;
- _c.ensureIndex( ns(), idx() );
- }
-
- Client::WriteContext ctx( ns() );
- FieldRangeSet frs( ns(), spec, true, true );
- boost::shared_ptr< FieldRangeVector > frv( new FieldRangeVector( frs, idx(), direction() ) );
- scoped_ptr<BtreeCursor> c( BtreeCursor::make( nsdetails( ns() ),
- nsdetails( ns() )->idx( 1 ),
- frv,
- 0,
- direction() ) );
- Matcher m( spec );
- int count = 0;
- while( c->ok() ) {
- ASSERT( m.matches( c->current() ) );
- c->advance();
- ++count;
- }
- int expectedCount = 0;
- for( vector< BSONObj >::const_iterator i = _objs.begin(); i != _objs.end(); ++i ) {
- if ( m.matches( *i ) ) {
- ++expectedCount;
- }
- }
- ASSERT_EQUALS( expectedCount, count );
- }
- private:
- vector< BSONObj > _objs;
- };
-
- class EqEq : public Base2 {
- public:
- void run() {
- insert( BSON( "a" << 4 << "b" << 5 ) );
- insert( BSON( "a" << 4 << "b" << 5 ) );
- insert( BSON( "a" << 4 << "b" << 4 ) );
- insert( BSON( "a" << 5 << "b" << 4 ) );
- check( BSON( "a" << 4 << "b" << 5 ) );
- }
- virtual BSONObj idx() const { return BSON( "a" << 1 << "b" << 1 ); }
- };
-
- class EqRange : public Base2 {
- public:
- void run() {
- insert( BSON( "a" << 3 << "b" << 5 ) );
- insert( BSON( "a" << 4 << "b" << 0 ) );
- insert( BSON( "a" << 4 << "b" << 5 ) );
- insert( BSON( "a" << 4 << "b" << 6 ) );
- insert( BSON( "a" << 4 << "b" << 6 ) );
- insert( BSON( "a" << 4 << "b" << 10 ) );
- insert( BSON( "a" << 4 << "b" << 11 ) );
- insert( BSON( "a" << 5 << "b" << 5 ) );
- check( BSON( "a" << 4 << "b" << BSON( "$gte" << 1 << "$lte" << 10 ) ) );
- }
- virtual BSONObj idx() const { return BSON( "a" << 1 << "b" << 1 ); }
- };
-
- class EqIn : public Base2 {
- public:
- void run() {
- insert( BSON( "a" << 3 << "b" << 5 ) );
- insert( BSON( "a" << 4 << "b" << 0 ) );
- insert( BSON( "a" << 4 << "b" << 5 ) );
- insert( BSON( "a" << 4 << "b" << 6 ) );
- insert( BSON( "a" << 4 << "b" << 6 ) );
- insert( BSON( "a" << 4 << "b" << 10 ) );
- insert( BSON( "a" << 4 << "b" << 11 ) );
- insert( BSON( "a" << 5 << "b" << 5 ) );
- check( BSON( "a" << 4 << "b" << BSON( "$in" << BSON_ARRAY( 5 << 6 << 11 ) ) ) );
- }
- virtual BSONObj idx() const { return BSON( "a" << 1 << "b" << 1 ); }
- };
-
- class RangeEq : public Base2 {
- public:
- void run() {
- insert( BSON( "a" << 0 << "b" << 4 ) );
- insert( BSON( "a" << 1 << "b" << 4 ) );
- insert( BSON( "a" << 4 << "b" << 3 ) );
- insert( BSON( "a" << 5 << "b" << 4 ) );
- insert( BSON( "a" << 7 << "b" << 4 ) );
- insert( BSON( "a" << 4 << "b" << 4 ) );
- insert( BSON( "a" << 9 << "b" << 6 ) );
- insert( BSON( "a" << 11 << "b" << 1 ) );
- insert( BSON( "a" << 11 << "b" << 4 ) );
- check( BSON( "a" << BSON( "$gte" << 1 << "$lte" << 10 ) << "b" << 4 ) );
- }
- virtual BSONObj idx() const { return BSON( "a" << 1 << "b" << 1 ); }
- };
-
- class RangeIn : public Base2 {
- public:
- void run() {
- insert( BSON( "a" << 0 << "b" << 4 ) );
- insert( BSON( "a" << 1 << "b" << 5 ) );
- insert( BSON( "a" << 4 << "b" << 3 ) );
- insert( BSON( "a" << 5 << "b" << 4 ) );
- insert( BSON( "a" << 7 << "b" << 5 ) );
- insert( BSON( "a" << 4 << "b" << 4 ) );
- insert( BSON( "a" << 9 << "b" << 6 ) );
- insert( BSON( "a" << 11 << "b" << 1 ) );
- insert( BSON( "a" << 11 << "b" << 4 ) );
- check( BSON( "a" << BSON( "$gte" << 1 << "$lte" << 10 ) << "b" << BSON( "$in" << BSON_ARRAY( 4 << 6 ) ) ) );
- }
- virtual BSONObj idx() const { return BSON( "a" << 1 << "b" << 1 ); }
- };
-
- /**
- * BtreeCursor::advance() may skip to new btree positions multiple times. A cutoff (tested
- * here) has been implemented to avoid excessive iteration in such cases. See SERVER-3448.
- */
- class AbortImplicitScan : public Base {
- public:
- void run() {
- _c.dropCollection( ns() );
- // Set up a compound index with some data.
- BSONObj kp = BSON( "a" << 1 << "b" << 1 );
- _c.ensureIndex( ns(), kp);
- for( int i = 0; i < 300; ++i ) {
- _c.insert( ns(), BSON( "a" << i << "b" << i ) );
- }
- _c.insert( ns(), BSON( "a" << 300 << "b" << 30 ) );
-
- // Set up a cursor on the { a:1, b:1 } index, the same cursor that would be created
- // for the query { b:30 }. Because this query has no constraint on 'a' (the
- // first field of the compound index), the cursor will examine every distinct value
- // of 'a' in the index and check for an index key with that value for 'a' and 'b'
- // equal to 30.
- FieldRangeSet frs( ns(), BSON( "b" << 30 ), true, true );
- boost::shared_ptr<FieldRangeVector> frv( new FieldRangeVector( frs, kp, 1 ) );
- Client::WriteContext ctx( ns() );
- scoped_ptr<BtreeCursor> c( BtreeCursor::make( nsdetails( ns() ),
- nsdetails( ns() )->idx(1),
- frv,
- 0,
- 1 ) );
-
- // BtreeCursor::init() and BtreeCursor::advance() attempt to advance the cursor to
- // the next matching key, which may entail examining many successive distinct values
- // of 'a' having no index key where b equals 30. To prevent excessive iteration
- // within init() and advance(), examining distinct 'a' values is aborted once an
- // nscanned cutoff is reached. We test here that this cutoff is applied, and that
- // if it is applied before a matching key is found, then
- // BtreeCursor::currentMatches() returns false appropriately.
-
- ASSERT( c->ok() );
- // The starting iterate found by BtreeCursor::init() does not match. This is a key
- // before the {'':30,'':30} key, because init() is aborted prematurely.
- ASSERT( !c->currentMatches() );
- // And init() stopped iterating before scanning the whole btree (with ~300 keys).
- ASSERT( c->nscanned() < 200 );
-
- ASSERT( c->advance() );
- // The next iterate matches (this is the {'':30,'':30} key).
- ASSERT( c->currentMatches() );
-
- int oldNscanned = c->nscanned();
- ASSERT( c->advance() );
- // Check that nscanned has increased ...
- ASSERT( c->nscanned() > oldNscanned );
- // ... but that advance() stopped iterating before the whole btree (with ~300 keys)
- // was scanned.
- ASSERT( c->nscanned() < 200 );
- // Because advance() is aborted prematurely, the current iterate does not match.
- ASSERT( !c->currentMatches() );
-
- // Iterate through the remainder of the btree.
- bool foundLastMatch = false;
- while( c->advance() ) {
- bool bMatches = ( c->current()[ "b" ].number() == 30 );
- // The current iterate only matches if it has the proper 'b' value.
- ASSERT_EQUALS( bMatches, c->currentMatches() );
- if ( bMatches ) {
- foundLastMatch = true;
- }
- }
- // Check that the final match, on key {'':300,'':30}, is found.
- ASSERT( foundLastMatch );
- }
- };
-
- class RequestMatcherFalse : public QueryPlanSelectionPolicy {
- virtual string name() const { return "RequestMatcherFalse"; }
- virtual bool requestMatcher() const { return false; }
- } _requestMatcherFalse;
-
- /**
- * A BtreeCursor typically moves from one index match to another when its advance() method
- * is called. However, to prevent excessive iteration advance() may bail out early before
- * the next index match is identified (SERVER-3448). The BtreeCursor must indicate that
- * these iterates are not matches in matchesCurrent() to prevent them from being matched
- * when requestMatcher == false.
- */
- class DontMatchOutOfIndexBoundsDocuments : public Base {
- public:
- void run() {
- _c.dropCollection( ns() );
- _c.ensureIndex( ns(), BSON( "a" << 1 ) );
- // Save 'a' values 0, 0.5, 1.5, 2.5 ... 97.5, 98.5, 99.
- _c.insert( ns(), BSON( "a" << 0 ) );
- _c.insert( ns(), BSON( "a" << 99 ) );
- for( int i = 0; i < 99; ++i ) {
- _c.insert( ns(), BSON( "a" << ( i + 0.5 ) ) );
- }
- // Query 'a' values $in 0, 1, 2, ..., 99.
- BSONArrayBuilder inVals;
- for( int i = 0; i < 100; ++i ) {
- inVals << i;
- }
- BSONObj query = BSON( "a" << BSON( "$in" << inVals.arr() ) );
- int matchCount = 0;
- Client::ReadContext ctx( ns() );
- boost::shared_ptr<Cursor> c = getOptimizedCursor( ns(),
- query,
- BSONObj(),
- _requestMatcherFalse );
- // The BtreeCursor attempts to find each of the values 0, 1, 2, ... etc in the
- // btree. Because the values 0.5, 1.5, etc are present in the btree, the
- // BtreeCursor will explicitly look for all the values in the $in list during
- // successive calls to advance(). Because there are a large number of $in values to
- // iterate over, BtreeCursor::advance() will bail out on intermediate values of 'a'
- // (for example 20.5) that do not match the query if nscanned increases by more than
- // 20. We test here that these intermediate results are not matched. Only the two
- // correct matches a:0 and a:99 are matched.
- while( c->ok() ) {
- ASSERT( !c->matcher() );
- if ( c->currentMatches() ) {
- double aVal = c->current()[ "a" ].number();
- // Only the expected values of a are matched.
- ASSERT( aVal == 0 || aVal == 99 );
- ++matchCount;
- }
- c->advance();
- }
- // Only the two expected documents a:0 and a:99 are matched.
- ASSERT_EQUALS( 2, matchCount );
- }
- };
-
- /**
- * When using a multikey index, two constraints on the same field cannot be intersected for
- * a non $elemMatch query (SERVER-958). For example, using a single key index on { a:1 }
- * the query { a:{ $gt:0, $lt:5 } } would generate the field range [[ 0, 5 ]]. But for a
- * multikey index the field range is [[ 0, max_number ]]. In this case, the field range
- * does not exactly represent the query, so a Matcher is required.
- */
- class MatcherRequiredTwoConstraintsSameField : public Base {
- public:
- void run() {
- _c.dropCollection( ns() );
- _c.ensureIndex( ns(), BSON( "a" << 1 ) );
- _c.insert( ns(), BSON( "_id" << 0 << "a" << BSON_ARRAY( 1 << 2 ) ) );
- _c.insert( ns(), BSON( "_id" << 1 << "a" << 9 ) );
- Client::ReadContext ctx( ns() );
- boost::shared_ptr<Cursor> c = getOptimizedCursor( ns(),
- BSON( "a" << GT << 0 << LT << 5 ),
- BSONObj(),
- _requestMatcherFalse );
- while( c->ok() ) {
- // A Matcher is provided even though 'requestMatcher' is false.
- ASSERT( c->matcher() );
- if ( c->currentMatches() ) {
- // Even though a:9 is in the field range [[ 0, max_number ]], that result
- // does not match because the Matcher rejects it. Only the _id:0 document
- // matches.
- ASSERT_EQUALS( 0, c->current()[ "_id" ].number() );
- }
- c->advance();
- }
- }
- };
-
- /**
- * When using a multikey index, two constraints on fields with a shared parent cannot be
- * intersected for a non $elemMatch query (SERVER-958). For example, using a single key
- * compound index on { 'a.b':1, 'a.c':1 } the query { 'a.b':2, 'a.c':2 } would generate the
- * field range vector [ [[ 2, 2 ]], [[ 2, 2 ]] ]. But for a multikey index the field range
- * vector is [ [[ 2, 2 ]], [[ minkey, maxkey ]] ]. In this case, the field range does not
- * exactly represent the query, so a Matcher is required.
- */
- class MatcherRequiredTwoConstraintsDifferentFields : public Base {
- public:
- void run() {
- _c.dropCollection( ns() );
- _c.ensureIndex( ns(), BSON( "a.b" << 1 << "a.c" << 1 ) );
- _c.insert( ns(), BSON( "a" << BSON_ARRAY( BSON( "b" << 2 << "c" << 3 ) <<
- BSONObj() ) ) );
- Client::ReadContext ctx( ns() );
- boost::shared_ptr<Cursor> c = getOptimizedCursor( ns(),
- BSON( "a.b" << 2 << "a.c" << 2 ),
- BSONObj(),
- _requestMatcherFalse );
- while( c->ok() ) {
- // A Matcher is provided even though 'requestMatcher' is false.
- ASSERT( c->matcher() );
- // Even though { a:[ { b:2, c:3 } ] } is matched by the field range vector
- // [ [[ 2, 2 ]], [[ minkey, maxkey ]] ], that resut is not matched because the
- // Matcher rejects the document.
- ASSERT( !c->currentMatches() );
- c->advance();
- }
- }
- };
-
- /**
- * The upper bound of a $gt:string query is the empty object. This upper bound must be
- * exclusive so that empty objects do not match without a Matcher.
- */
- class TypeBracketedUpperBoundWithoutMatcher : public Base {
- public:
- void run() {
- _c.dropCollection( ns() );
- _c.ensureIndex( ns(), BSON( "a" << 1 ) );
- _c.insert( ns(), BSON( "_id" << 0 << "a" << "a" ) );
- _c.insert( ns(), BSON( "_id" << 1 << "a" << BSONObj() ) );
- Client::ReadContext ctx( ns() );
- boost::shared_ptr<Cursor> c = getOptimizedCursor( ns(),
- BSON( "a" << GTE << "" ),
- BSONObj(),
- _requestMatcherFalse );
- while( c->ok() ) {
- ASSERT( !c->matcher() );
- if ( c->currentMatches() ) {
- // Only a:'a' matches, not a:{}.
- ASSERT_EQUALS( 0, c->current()[ "_id" ].number() );
- }
- c->advance();
- }
- }
- };
-
- /**
- * The lower bound of a $lt:date query is the bson value 'true'. This lower bound must be
- * exclusive so that 'true' values do not match without a Matcher.
- */
- class TypeBracketedLowerBoundWithoutMatcher : public Base {
- public:
- void run() {
- _c.dropCollection( ns() );
- _c.ensureIndex( ns(), BSON( "a" << 1 ) );
- _c.insert( ns(), BSON( "_id" << 0 << "a" << Date_t( 1 ) ) );
- _c.insert( ns(), BSON( "_id" << 1 << "a" << true ) );
- Client::ReadContext ctx( ns() );
- boost::shared_ptr<Cursor> c = getOptimizedCursor( ns(),
- BSON( "a" << LTE << Date_t( 1 ) ),
- BSONObj(),
- _requestMatcherFalse );
- while( c->ok() ) {
- ASSERT( !c->matcher() );
- if ( c->currentMatches() ) {
- // Only a:Date_t( 1 ) matches, not a:true.
- ASSERT_EQUALS( 0, c->current()[ "_id" ].number() );
- }
- c->advance();
- }
- }
- };
-
- /** Test iteration of a reverse direction btree cursor between start and end keys. */
- class ReverseDirectionStartEndKeys : public Base {
- public:
- void run() {
- _c.dropCollection( ns() );
- _c.ensureIndex( ns(), BSON( "a" << 1 ) );
- // Add documents a:4 and a:5
- _c.insert( ns(), BSON( "a" << 4 ) );
- _c.insert( ns(), BSON( "a" << 5 ) );
- Client::ReadContext ctx( ns() );
- scoped_ptr<Cursor> cursor( BtreeCursor::make( nsdetails( ns() ),
- nsdetails( ns() )->idx( 1 ),
- /* startKey */ BSON( "" << 5 ),
- /* endKey */ BSON( "" << 4 ),
- /* endKeyInclusive */ true,
- /* direction */ -1 ) );
- // Check that the iterator produces the expected results, in the expected order.
- ASSERT( cursor->ok() );
- ASSERT_EQUALS( 5, cursor->current()[ "a" ].Int() );
- ASSERT( cursor->advance() );
- ASSERT_EQUALS( 4, cursor->current()[ "a" ].Int() );
- ASSERT( !cursor->advance() );
- }
- };
-
- } // namespace BtreeCursor
-
- namespace ClientCursor {
-
- using mongo::ClientCursor;
-
- static const char * const ns() { return "unittests.cursortests.clientcursor"; }
- DBDirectClient client;
-
- class Base {
- public:
- virtual ~Base() {
- client.dropCollection( ns() );
- }
- };
-
- /**
- * A cursor is advanced when the document at its current iterate is removed.
- */
- class HandleDelete : public Base {
- public:
- void run() {
- for( int i = 0; i < 150; ++i ) {
- client.insert( ns(), BSON( "_id" << i ) );
- }
-
- boost::shared_ptr<Cursor> cursor;
- ClientCursorHolder clientCursor;
- ClientCursor::YieldData yieldData;
-
- {
- Client::ReadContext ctx( ns() );
- // The query will utilize the _id index for both the first and second clauses.
- cursor = getOptimizedCursor( ns(),
- fromjson( "{$or:[{_id:{$gte:0,$lte:148}},"
- "{_id:149}]}" ) );
- clientCursor.reset( new ClientCursor( QueryOption_NoCursorTimeout, cursor,
- ns() ) );
- // Advance to the last iterate of the first clause.
- ASSERT( cursor->ok() );
- while( cursor->current()[ "_id" ].number() != 148 ) {
- ASSERT( cursor->advance() );
- }
- clientCursor->prepareToYield( yieldData );
- }
-
- // Remove the document at the cursor's current position, which will cause the
- // cursor to be advanced.
- client.remove( ns(), BSON( "_id" << 148 ) );
-
- {
- Client::ReadContext ctx( ns() );
- clientCursor->recoverFromYield( yieldData );
- // Verify that the cursor has another iterate, _id:149, after it is advanced due
- // to _id:148's removal.
- ASSERT( cursor->ok() );
- ASSERT_EQUALS( 149, cursor->current()[ "_id" ].number() );
- }
- }
- };
-
- /**
- * ClientCursor::aboutToDelete() advances a ClientCursor with a refLoc() matching the
- * document to be deleted.
- */
- class AboutToDelete : public Base {
- public:
- void run() {
- populateData();
- Client::WriteContext ctx( ns() );
-
- // Generate a cursor from the supplied query and advance it to the iterate to be
- // deleted.
- boost::shared_ptr<Cursor> cursor = getOptimizedCursor( ns(), query() );
- while( !isExpectedIterate( cursor->current() ) ) {
- ASSERT( cursor->advance() );
- }
- ClientCursorHolder clientCursor( new ClientCursor( QueryOption_NoCursorTimeout,
- cursor, ns() ) );
- DiskLoc loc = clientCursor->currLoc();
- ASSERT( !loc.isNull() );
-
- // Yield the cursor.
- ClientCursor::YieldData data;
- clientCursor->prepareToYield( data );
- // The cursor will be advanced in aboutToDelete().
- ClientCursor::aboutToDelete( ns(), nsdetails( ns() ), loc );
- clientCursor->recoverFromYield( data );
- ASSERT( clientCursor->ok() );
-
- // Validate the expected cursor advancement.
- validateIterateAfterYield( clientCursor->current() );
- }
- protected:
- virtual void populateData() const {
- client.insert( ns(), BSON( "a" << 1 ) );
- client.insert( ns(), BSON( "a" << 2 ) );
- }
- virtual BSONObj query() const { return BSONObj(); }
- virtual bool isExpectedIterate( const BSONObj &current ) const {
- return 1 == current[ "a" ].number();
- }
- virtual void validateIterateAfterYield( const BSONObj &current ) const {
- ASSERT_EQUALS( 2, current[ "a" ].number() );
- }
- };
-
- /** aboutToDelete() advances past a document referenced by adjacent cursor iterates. */
- class AboutToDeleteDuplicate : public AboutToDelete {
- virtual void populateData() const {
- client.insert( ns(), BSON( "a" << BSON_ARRAY( 1 << 2 ) ) );
- client.insert( ns(), BSON( "a" << 3 ) );
- client.ensureIndex( ns(), BSON( "a" << 1 ) );
- }
- virtual BSONObj query() const { return BSON( "a" << GT << 0 ); }
- virtual bool isExpectedIterate( const BSONObj &current ) const {
- return BSON_ARRAY( 1 << 2 ) == current[ "a" ].embeddedObject();
- }
- virtual void validateIterateAfterYield( const BSONObj &current ) const {
- ASSERT_EQUALS( 3, current[ "a" ].number() );
- }
- };
-
- /** aboutToDelete() advances past a document referenced by adjacent cursor clauses. */
- class AboutToDeleteDuplicateNextClause : public AboutToDelete {
- virtual void populateData() const {
- for( int i = 119; i >= 0; --i ) {
- client.insert( ns(), BSON( "a" << i ) );
- }
- client.ensureIndex( ns(), BSON( "a" << 1 ) );
- client.ensureIndex( ns(), BSON( "a" << 1 << "b" << 1 ) );
- }
- virtual BSONObj query() const { return fromjson( "{$or:[{a:{$gte:0}},{b:1}]}" ); }
- virtual bool isExpectedIterate( const BSONObj &current ) const {
- // In the absence of the aboutToDelete() call, the next iterate will be a:119 from
- // an unindexed cursor over the second clause.
- return 119 == current[ "a" ].number();
- }
- virtual void validateIterateAfterYield( const BSONObj &current ) const {
- // After the aboutToDelete() call, the a:119 iterate of the unindexed cursor is
- // skipped, advancing to the a:118 iterate.
- ASSERT_EQUALS( 118, current[ "a" ].number() );
- }
- };
-
- namespace Pin {
-
- class Base {
- public:
- Base() :
- _ctx( ns() ),
- _cursor( theDataFileMgr.findAll( ns() ) ) {
- ASSERT( _cursor );
- _clientCursor.reset( new ClientCursor( 0, _cursor, ns() ) );
- }
- protected:
- CursorId cursorid() const { return _clientCursor->cursorid(); }
- private:
- Client::WriteContext _ctx;
- boost::shared_ptr<Cursor> _cursor;
- ClientCursorHolder _clientCursor;
- };
-
- /** Pin pins a ClientCursor over its lifetime. */
- class PinCursor : public Base {
- public:
- void run() {
- assertNotPinned();
- {
- ClientCursorPin pin( cursorid() );
- assertPinned();
- ASSERT_THROWS( erase(), AssertionException );
- }
- assertNotPinned();
- ASSERT( erase() );
- }
- private:
- void assertPinned() const {
- ASSERT( ClientCursor::find( cursorid() ) );
- }
- void assertNotPinned() const {
- ASSERT_THROWS( ClientCursor::find( cursorid() ), AssertionException );
- }
- bool erase() const {
- return ClientCursor::erase( cursorid() );
- }
- };
-
- /** A ClientCursor cannot be pinned twice. */
- class PinTwice : public Base {
- public:
- void run() {
- ClientCursorPin pin( cursorid() );
- ASSERT_THROWS( pinCursor(), AssertionException );
- }
- private:
- void pinCursor() const {
- ClientCursorPin pin( cursorid() );
- }
- };
-
- /** Pin behaves properly if its ClientCursor is destroyed early. */
- class CursorDeleted : public Base {
- public:
- void run() {
- ClientCursorPin pin( cursorid() );
- ASSERT( pin.c() );
- // Delete the pinned cursor.
- ClientCursor::invalidate( ns() );
- ASSERT( !pin.c() );
- // pin is destroyed safely, even though its ClientCursor was already destroyed.
- }
- };
-
- } // namespace Pin
-
- } // namespace ClientCursor
-
- class All : public Suite {
- public:
- All() : Suite( "cursor" ) {}
-
- void setupTests() {
- add<BtreeCursor::MultiRangeForward>();
- add<BtreeCursor::MultiRangeGap>();
- add<BtreeCursor::MultiRangeReverse>();
- add<BtreeCursor::EqEq>();
- add<BtreeCursor::EqRange>();
- add<BtreeCursor::EqIn>();
- add<BtreeCursor::RangeEq>();
- add<BtreeCursor::RangeIn>();
- add<BtreeCursor::AbortImplicitScan>();
- add<BtreeCursor::DontMatchOutOfIndexBoundsDocuments>();
- add<BtreeCursor::MatcherRequiredTwoConstraintsSameField>();
- add<BtreeCursor::MatcherRequiredTwoConstraintsDifferentFields>();
- add<BtreeCursor::TypeBracketedUpperBoundWithoutMatcher>();
- add<BtreeCursor::TypeBracketedLowerBoundWithoutMatcher>();
- add<BtreeCursor::ReverseDirectionStartEndKeys>();
- add<ClientCursor::HandleDelete>();
- add<ClientCursor::AboutToDelete>();
- add<ClientCursor::AboutToDeleteDuplicate>();
- add<ClientCursor::AboutToDeleteDuplicateNextClause>();
- add<ClientCursor::Pin::PinCursor>();
- add<ClientCursor::Pin::PinTwice>();
- add<ClientCursor::Pin::CursorDeleted>();
- }
- } myall;
-} // namespace CursorTests
diff --git a/src/mongo/dbtests/indexupdatetests.cpp b/src/mongo/dbtests/indexupdatetests.cpp
index ab754608a31..8caf6f6c7da 100644
--- a/src/mongo/dbtests/indexupdatetests.cpp
+++ b/src/mongo/dbtests/indexupdatetests.cpp
@@ -29,10 +29,10 @@
*/
#include "mongo/db/btree.h"
-#include "mongo/db/btreecursor.h"
#include "mongo/db/catalog/index_catalog.h"
#include "mongo/db/dbhelpers.h"
#include "mongo/db/index/btree_based_builder.h"
+#include "mongo/db/index/index_descriptor.h"
#include "mongo/db/kill_current_op.h"
#include "mongo/db/sort_phase_one.h"
#include "mongo/db/structure/collection.h"
@@ -171,6 +171,8 @@ namespace IndexUpdateTests {
bool _mayInterrupt;
};
+ // QUERY_MIGRATION
+#if 0
/** buildBottomUpPhases2And3() builds a btree from the keys in an external sorter. */
class BuildBottomUp : public IndexBuildBase {
public:
@@ -227,6 +229,7 @@ namespace IndexUpdateTests {
ASSERT_EQUALS( nKeys, expectedKey );
}
};
+#endif
/** buildBottomUpPhases2And3() aborts if the current operation is interrupted. */
class InterruptBuildBottomUp : public IndexBuildBase {
@@ -771,7 +774,8 @@ namespace IndexUpdateTests {
add<AddKeysToPhaseOne>();
add<InterruptAddKeysToPhaseOne>( false );
add<InterruptAddKeysToPhaseOne>( true );
- add<BuildBottomUp>();
+ // QUERY_MIGRATION
+ // add<BuildBottomUp>();
add<InterruptBuildBottomUp>( false );
add<InterruptBuildBottomUp>( true );
add<DoDropDups>();
diff --git a/src/mongo/dbtests/intervalbtreecursortests.cpp b/src/mongo/dbtests/intervalbtreecursortests.cpp
deleted file mode 100644
index c02bd22315c..00000000000
--- a/src/mongo/dbtests/intervalbtreecursortests.cpp
+++ /dev/null
@@ -1,672 +0,0 @@
-/**
- * Copyright (C) 2012 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/intervalbtreecursor.h"
-
-#include "mongo/db/btree.h"
-#include "mongo/db/pdfile.h"
-#include "mongo/dbtests/dbtests.h"
-#include "mongo/platform/cstdint.h"
-
-namespace IntervalBtreeCursorTests {
-
- DBDirectClient _client;
- const char* _ns = "unittests.intervalbtreecursor";
-
- /** An IntervalBtreeCursor can only be created for a version 1 index. */
- class WrongIndexVersion {
- public:
- void run() {
- Client::WriteContext ctx( _ns );
- _client.dropCollection( _ns );
-
- // Create a version 0 index.
- _client.ensureIndex( _ns, BSON( "a" << 1 ), false, "", true, false, /* version */ 0 );
-
- // Attempt to create a cursor on the a:1 index.
- scoped_ptr<Cursor> cursor( IntervalBtreeCursor::make( nsdetails( _ns ),
- nsdetails( _ns )->idx( 1 ),
- BSON( "" << 5 ),
- true,
- BSON( "" << 6 ),
- true ) );
-
- // No cursor was created because the index was not of version 1.
- ASSERT( !cursor );
- }
- };
-
- class BasicAccessors {
- public:
- void run() {
- Client::WriteContext ctx( _ns );
- _client.dropCollection( _ns );
- _client.insert( _ns, BSON( "a" << 5 ) );
- _client.ensureIndex( _ns, BSON( "a" << 1 ) );
- scoped_ptr<Cursor> cursor( IntervalBtreeCursor::make( nsdetails( _ns ),
- nsdetails( _ns )->idx( 1 ),
- BSON( "" << 5 ),
- true,
- BSON( "" << 6 ),
- true ) );
-
- // Create a reference BasicCursor, which will return identical values from certain
- // accessors.
- boost::shared_ptr<Cursor> reference = theDataFileMgr.findAll( _ns );
-
- ASSERT( cursor->ok() );
- ASSERT( reference->_current() == cursor->_current() );
- ASSERT_EQUALS( reference->current(), cursor->current() );
- ASSERT_EQUALS( reference->currLoc(), cursor->currLoc() );
- ASSERT_EQUALS( BSON( "" << 5 ), cursor->currKey() );
- ASSERT_EQUALS( cursor->currLoc(), cursor->refLoc() );
- ASSERT_EQUALS( BSON( "a" << 1 ), cursor->indexKeyPattern() );
- ASSERT( !cursor->supportGetMore() );
- ASSERT( cursor->supportYields() );
- ASSERT_EQUALS( "IntervalBtreeCursor", cursor->toString() );
- ASSERT( !cursor->isMultiKey() );
- ASSERT( !cursor->modifiedKeys() );
- ASSERT_EQUALS( BSON( "lower" << BSON( "a" << 5 ) << "upper" << BSON( "a" << 6 ) ),
- cursor->prettyIndexBounds() );
-
- // Advance the cursor to the end.
- ASSERT( !cursor->advance() );
- ASSERT( !cursor->ok() );
- ASSERT( cursor->currLoc().isNull() );
- ASSERT( cursor->currKey().isEmpty() );
- ASSERT( cursor->refLoc().isNull() );
- }
- };
-
- /**
- * Check nscanned counting semantics. The expectation is to match the behavior of BtreeCursor,
- * as described in the test comments.
- */
- class Nscanned {
- public:
- void run() {
- Client::WriteContext ctx( _ns );
- _client.dropCollection( _ns );
- for( int32_t i = 0; i < 10; ++i ) {
- _client.insert( _ns, BSON( "a" << 5 ) );
- }
- _client.insert( _ns, BSON( "a" << 7 ) );
- _client.ensureIndex( _ns, BSON( "a" << 1 ) );
- scoped_ptr<Cursor> cursor( IntervalBtreeCursor::make( nsdetails( _ns ),
- nsdetails( _ns )->idx( 1 ),
- BSON( "" << 5 ),
- true,
- BSON( "" << 6 ),
- true ) );
- // nscanned is 1 for the first match.
- ASSERT_EQUALS( 1, cursor->nscanned() );
- for( int32_t i = 1; i < 10; ++i ) {
- ASSERT( cursor->ok() );
-
- // nscanned is incremented by 1 for intermediate matches.
- ASSERT_EQUALS( i, cursor->nscanned() );
- ASSERT( cursor->advance() );
- }
- ASSERT( cursor->ok() );
- ASSERT_EQUALS( 10, cursor->nscanned() );
- ASSERT( !cursor->advance() );
- ASSERT( !cursor->ok() );
-
- // nscanned is not incremented when reaching the end of the cursor.
- ASSERT_EQUALS( 10, cursor->nscanned() );
- }
- };
-
- /** Check that a CoveredIndexMatcher can be set and used properly by the cursor. */
- class Matcher {
- public:
- void run() {
- Client::WriteContext ctx( _ns );
- _client.dropCollection( _ns );
-
- // Insert a document that will match.
- _client.insert( _ns, BSON( "a" << 5 << "b" << 1 ) );
-
- // Insert a document that will not match.
- _client.insert( _ns, BSON( "a" << 5 << "b" << 2 ) );
- _client.ensureIndex( _ns, BSON( "a" << 1 ) );
- scoped_ptr<Cursor> cursor( IntervalBtreeCursor::make( nsdetails( _ns ),
- nsdetails( _ns )->idx( 1 ),
- BSON( "" << 5 ),
- true,
- BSON( "" << 5 ),
- true ) );
-
- // No matcher is set yet.
- ASSERT( !cursor->matcher() );
- ASSERT( cursor->currentMatches() );
-
- // Create a matcher and set it on the cursor.
- boost::shared_ptr<CoveredIndexMatcher> matcher
- ( new CoveredIndexMatcher( BSON( "a" << 5 << "b" << 1 ), BSON( "a" << 1 ) ) );
- cursor->setMatcher( matcher );
-
- // The document with b:1 matches.
- ASSERT_EQUALS( 1, cursor->current()[ "b" ].Int() );
- ASSERT( cursor->matcher()->matchesCurrent( cursor.get() ) );
- ASSERT( cursor->currentMatches() );
- cursor->advance();
-
- // The document with b:2 does not match.
- ASSERT_EQUALS( 2, cursor->current()[ "b" ].Int() );
- ASSERT( !cursor->matcher()->matchesCurrent( cursor.get() ) );
- ASSERT( !cursor->currentMatches() );
- }
- };
-
- /** Check that dups are properly identified by the cursor. */
- class Dups {
- public:
- void run() {
- Client::WriteContext ctx( _ns );
- _client.dropCollection( _ns );
- _client.insert( _ns, BSON( "a" << BSON_ARRAY( 5 << 7 ) ) );
- _client.insert( _ns, BSON( "a" << BSON_ARRAY( 6 << 8 ) ) );
- _client.ensureIndex( _ns, BSON( "a" << 1 ) );
- scoped_ptr<Cursor> cursor( IntervalBtreeCursor::make( nsdetails( _ns ),
- nsdetails( _ns )->idx( 1 ),
- BSON( "" << 5 ),
- true,
- BSON( "" << 10 ),
- true ) );
- ASSERT( cursor->isMultiKey() );
- ASSERT( cursor->modifiedKeys() );
-
- // This is the 5,7 document, first time seen. Not a dup.
- DiskLoc firstLoc = cursor->currLoc();
- ASSERT( !cursor->getsetdup( cursor->currLoc() ) );
- cursor->advance();
-
- // This is the 6,8 document, first time seen. Not a dup.
- DiskLoc secondLoc = cursor->currLoc();
- ASSERT( !cursor->getsetdup( cursor->currLoc() ) );
- cursor->advance();
-
- // This is the 5,7 document, second time seen. A dup.
- ASSERT_EQUALS( firstLoc, cursor->currLoc() );
- ASSERT( cursor->getsetdup( cursor->currLoc() ) );
- cursor->advance();
-
- // This is the 6,8 document, second time seen. A dup.
- ASSERT_EQUALS( secondLoc, cursor->currLoc() );
- ASSERT( cursor->getsetdup( cursor->currLoc() ) );
- }
- };
-
- /** Check that expected results are returned with inclusive bounds. */
- class InclusiveBounds {
- public:
- void run() {
- Client::WriteContext ctx( _ns );
- _client.dropCollection( _ns );
-
- // Save 'a' values 1-10.
- for( int32_t i = 0; i < 10; ++i ) {
- _client.insert( _ns, BSON( "a" << i ) );
- }
- _client.ensureIndex( _ns, BSON( "a" << 1 ) );
-
- // Iterate over 'a' values 3-7 inclusive.
- scoped_ptr<Cursor> cursor( IntervalBtreeCursor::make( nsdetails( _ns ),
- nsdetails( _ns )->idx( 1 ),
- BSON( "" << 3 ),
- true,
- BSON( "" << 7 ),
- true ) );
-
- // Check that the expected 'a' values are returned.
- for( int32_t i = 3; i < 8; ++i, cursor->advance() ) {
- ASSERT_EQUALS( i, cursor->current()[ "a" ].Int() );
- }
- ASSERT( !cursor->ok() );
- }
- };
-
- /** Check that expected results are returned with exclusive bounds. */
- class ExclusiveBounds {
- public:
- void run() {
- Client::WriteContext ctx( _ns );
- _client.dropCollection( _ns );
-
- // Save 'a' values 1-10.
- for( int32_t i = 0; i < 10; ++i ) {
- _client.insert( _ns, BSON( "a" << i ) );
- }
- _client.ensureIndex( _ns, BSON( "a" << 1 ) );
-
- // Iterate over 'a' values 3-7 exclusive.
- scoped_ptr<Cursor> cursor( IntervalBtreeCursor::make( nsdetails( _ns ),
- nsdetails( _ns )->idx( 1 ),
- BSON( "" << 3 ),
- false,
- BSON( "" << 7 ),
- false ) );
-
- // Check that the expected 'a' values are returned.
- for( int32_t i = 4; i < 7; ++i, cursor->advance() ) {
- ASSERT_EQUALS( i, cursor->current()[ "a" ].Int() );
- }
- ASSERT( !cursor->ok() );
- }
- };
-
- /** Check that killOp will interrupt cursor iteration. */
- class Interrupt {
- public:
- ~Interrupt() {
- // Reset curop's kill flag.
- cc().curop()->reset();
- }
- void run() {
- _client.dropCollection( _ns );
- for( int32_t i = 0; i < 150; ++i ) {
- _client.insert( _ns, BSON( "a" << 5 ) );
- }
- _client.ensureIndex( _ns, BSON( "a" << 1 ) );
- Client::ReadContext ctx( _ns );
- scoped_ptr<Cursor> cursor( IntervalBtreeCursor::make( nsdetails( _ns ),
- nsdetails( _ns )->idx( 1 ),
- BSON( "" << 5 ),
- true,
- BSON( "" << 5 ),
- true ) );
-
- // Register a request to kill the current operation.
- cc().curop()->kill();
-
- // Check that an exception is thrown when iterating the cursor.
- ASSERT_THROWS( exhaustCursor( cursor.get() ), UserException );
- }
- private:
- void exhaustCursor( Cursor* cursor ) {
- while( cursor->advance() );
- }
- };
-
- /** Check that a cursor returns no results if all documents are below the lower bound. */
- class NothingAboveLowerBound {
- public:
- void run() {
- Client::WriteContext ctx( _ns );
- _client.dropCollection( _ns );
- _client.insert( _ns, BSON( "a" << 2 ) );
- _client.ensureIndex( _ns, BSON( "a" << 1 ) );
- scoped_ptr<Cursor> cursor( IntervalBtreeCursor::make( nsdetails( _ns ),
- nsdetails( _ns )->idx( 1 ),
- BSON( "" << 2 ),
- false,
- BSON( "" << 3 ),
- false ) );
-
- // The cursor returns no results.
- ASSERT( !cursor->ok() );
- }
- };
-
- /** Check that a cursor returns no results if there are no documents within the interval. */
- class NothingInInterval {
- public:
- void run() {
- Client::WriteContext ctx( _ns );
- _client.dropCollection( _ns );
- _client.insert( _ns, BSON( "a" << 2 ) );
- _client.insert( _ns, BSON( "a" << 3 ) );
- _client.ensureIndex( _ns, BSON( "a" << 1 ) );
- scoped_ptr<Cursor> cursor( IntervalBtreeCursor::make( nsdetails( _ns ),
- nsdetails( _ns )->idx( 1 ),
- BSON( "" << 2 ),
- false,
- BSON( "" << 3 ),
- false ) );
-
- // The cursor returns no results.
- ASSERT( !cursor->ok() );
- }
- };
-
- /**
- * Check that a cursor returns no results if there are no documents within the interval and
- * the first key located during initialization is above the upper bound.
- */
- class NothingInIntervalFirstMatchBeyondUpperBound {
- public:
- void run() {
- Client::WriteContext ctx( _ns );
- _client.dropCollection( _ns );
- _client.insert( _ns, BSON( "a" << 2 ) );
- _client.insert( _ns, BSON( "a" << 4 ) );
- _client.ensureIndex( _ns, BSON( "a" << 1 ) );
-
- // Iterate over 'a' values ( 2, 3 ].
- scoped_ptr<Cursor> cursor( IntervalBtreeCursor::make( nsdetails( _ns ),
- nsdetails( _ns )->idx( 1 ),
- BSON( "" << 2 ),
- false,
- BSON( "" << 3 ),
- true ) );
- ASSERT( !cursor->ok() );
- }
- };
-
- /** Check that a cursor recovers its position properly if there is no change during a yield. */
- class NoChangeDuringYield {
- public:
- void run() {
- Client::WriteContext ctx( _ns );
- _client.dropCollection( _ns );
- for( int32_t i = 0; i < 10; ++i ) {
- _client.insert( _ns, BSON( "a" << i ) );
- }
- _client.ensureIndex( _ns, BSON( "a" << 1 ) );
- scoped_ptr<Cursor> cursor( IntervalBtreeCursor::make( nsdetails( _ns ),
- nsdetails( _ns )->idx( 1 ),
- BSON( "" << 2 ),
- false,
- BSON( "" << 6 ),
- true ) );
- while( cursor->current()[ "a" ].Int() != 5 ) {
- cursor->advance();
- }
-
- // Prepare the cursor to yield.
- cursor->prepareToYield();
-
- // Recover the cursor.
- cursor->recoverFromYield();
-
- // The cursor is still at its original position.
- ASSERT_EQUALS( 5, cursor->current()[ "a" ].Int() );
-
- // The cursor can be advanced from this position.
- ASSERT( cursor->advance() );
- ASSERT_EQUALS( 6, cursor->current()[ "a" ].Int() );
- }
- };
-
- /**
- * Check that a cursor recovers its position properly if its current location is deleted
- * during a yield.
- */
- class DeleteDuringYield {
- public:
- void run() {
- Client::WriteContext ctx( _ns );
- _client.dropCollection( _ns );
- for( int32_t i = 0; i < 10; ++i ) {
- _client.insert( _ns, BSON( "a" << i ) );
- }
- _client.ensureIndex( _ns, BSON( "a" << 1 ) );
- scoped_ptr<Cursor> cursor( IntervalBtreeCursor::make( nsdetails( _ns ),
- nsdetails( _ns )->idx( 1 ),
- BSON( "" << 2 ),
- false,
- BSON( "" << 6 ),
- true ) );
- while( cursor->current()[ "a" ].Int() != 5 ) {
- cursor->advance();
- }
-
- // Prepare the cursor to yield.
- cursor->prepareToYield();
-
- // Remove the current iterate and all remaining iterates.
- _client.remove( _ns, BSON( "a" << GTE << 5 ) );
-
- // Recover the cursor.
- cursor->recoverFromYield();
-
- // The cursor is exhausted.
- ASSERT( !cursor->ok() );
- }
- };
-
- /**
- * Check that a cursor relocates its end location properly if the end location changes during a
- * yield.
- */
- class InsertNewDocsDuringYield {
- public:
- void run() {
- Client::WriteContext ctx( _ns );
- _client.dropCollection( _ns );
- for( int32_t i = 0; i < 10; ++i ) {
- _client.insert( _ns, BSON( "a" << i ) );
- }
- _client.ensureIndex( _ns, BSON( "a" << 1 ) );
- scoped_ptr<Cursor> cursor( IntervalBtreeCursor::make( nsdetails( _ns ),
- nsdetails( _ns )->idx( 1 ),
- BSON( "" << 2 ),
- false,
- BSON( "" << 6 ),
- true ) );
- while( cursor->current()[ "a" ].Int() != 4 ) {
- cursor->advance();
- }
-
- // Prepare the cursor to yield.
- cursor->prepareToYield();
-
- // Insert one doc before the end.
- _client.insert( _ns, BSON( "a" << 5.5 ) );
-
- // Insert one doc after the end.
- _client.insert( _ns, BSON( "a" << 6.5 ) );
-
- // Recover the cursor.
- cursor->recoverFromYield();
-
- // Check that the cursor returns the expected remaining documents.
- ASSERT_EQUALS( 4, cursor->current()[ "a" ].Int() );
- ASSERT( cursor->advance() );
- ASSERT_EQUALS( 5, cursor->current()[ "a" ].Int() );
- ASSERT( cursor->advance() );
- ASSERT_EQUALS( 5.5, cursor->current()[ "a" ].number() );
- ASSERT( cursor->advance() );
- ASSERT_EQUALS( 6, cursor->current()[ "a" ].Int() );
- ASSERT( !cursor->advance() );
- }
- };
-
- /** Check that isMultiKey() is updated correctly if an index becomes multikey during a yield. */
- class BecomesMultikeyDuringYield {
- public:
- void run() {
- Client::WriteContext ctx( _ns );
- _client.dropCollection( _ns );
- for( int32_t i = 0; i < 10; ++i ) {
- _client.insert( _ns, BSON( "a" << i ) );
- }
- _client.ensureIndex( _ns, BSON( "a" << 1 ) );
- scoped_ptr<Cursor> cursor( IntervalBtreeCursor::make( nsdetails( _ns ),
- nsdetails( _ns )->idx( 1 ),
- BSON( "" << 2 ),
- false,
- BSON( "" << 50 ),
- true ) );
- while( cursor->current()[ "a" ].Int() != 4 ) {
- cursor->advance();
- }
-
- // Check that the cursor is not multikey.
- ASSERT( !cursor->isMultiKey() );
-
- // Prepare the cursor to yield.
- cursor->prepareToYield();
-
- // Insert a document with two values of 'a'.
- _client.insert( _ns, BSON( "a" << BSON_ARRAY( 10 << 11 ) ) );
-
- // Recover the cursor.
- cursor->recoverFromYield();
-
- // Check that the cursor becomes multikey.
- ASSERT( cursor->isMultiKey() );
-
- // Check that keys 10 and 11 are detected as duplicates.
- while( cursor->currKey().firstElement().Int() != 10 ) {
- ASSERT( cursor->advance() );
- }
- ASSERT( !cursor->getsetdup( cursor->currLoc() ) );
- ASSERT( cursor->advance() );
- ASSERT_EQUALS( 11, cursor->currKey().firstElement().Int() );
- ASSERT( cursor->getsetdup( cursor->currLoc() ) );
- }
- };
-
- /** Unused keys are not returned during iteration. */
- class UnusedKeys {
- public:
- void run() {
- Client::WriteContext ctx( _ns );
- _client.dropCollection( _ns );
- for( int32_t i = 0; i < 10; ++i ) {
- _client.insert( _ns, BSON( "a" << i ) );
- }
- _client.ensureIndex( _ns, BSON( "a" << 1 ) );
-
- // Mark keys at position 0, 3, and 4 as unused.
- nsdetails( _ns )->idx( 1 ).head.btreemod<V1>()->_k( 0 ).setUnused();
- nsdetails( _ns )->idx( 1 ).head.btreemod<V1>()->_k( 3 ).setUnused();
- nsdetails( _ns )->idx( 1 ).head.btreemod<V1>()->_k( 4 ).setUnused();
-
- scoped_ptr<Cursor> cursor( IntervalBtreeCursor::make( nsdetails( _ns ),
- nsdetails( _ns )->idx( 1 ),
- BSON( "" << 0 ),
- true,
- BSON( "" << 6 ),
- true ) );
-
- // Check that the unused keys are not returned but the other keys are returned.
- ASSERT_EQUALS( 1, cursor->current()[ "a" ].Int() );
- ASSERT( cursor->advance() );
- ASSERT_EQUALS( 2, cursor->current()[ "a" ].Int() );
- ASSERT( cursor->advance() );
- ASSERT_EQUALS( 5, cursor->current()[ "a" ].Int() );
- ASSERT( cursor->advance() );
- ASSERT_EQUALS( 6, cursor->current()[ "a" ].Int() );
- ASSERT( !cursor->advance() );
- }
- };
-
- /** Iteration is properly terminated when the end location is an unused key. */
- class UnusedEndKey {
- public:
- void run() {
- Client::WriteContext ctx( _ns );
- _client.dropCollection( _ns );
- for( int32_t i = 0; i < 10; ++i ) {
- _client.insert( _ns, BSON( "a" << i ) );
- }
- _client.ensureIndex( _ns, BSON( "a" << 1 ) );
-
- // Mark the key at position 5 as unused.
- nsdetails( _ns )->idx( 1 ).head.btreemod<V1>()->_k( 5 ).setUnused();
-
- scoped_ptr<Cursor> cursor( IntervalBtreeCursor::make( nsdetails( _ns ),
- nsdetails( _ns )->idx( 1 ),
- BSON( "" << 4 ),
- true,
- BSON( "" << 5 ),
- false ) );
-
- // Check the documents produced by the cursor.
- ASSERT_EQUALS( 4, cursor->current()[ "a" ].Int() );
- ASSERT( !cursor->advance() );
- }
- };
-
- /** Advances past a key that becomes unused during a yield. */
- class KeyBecomesUnusedDuringYield {
- public:
- void run() {
- Client::WriteContext ctx( _ns );
- _client.dropCollection( _ns );
- for( int32_t i = 0; i < 10; ++i ) {
- _client.insert( _ns, BSON( "a" << i ) );
- }
- _client.ensureIndex( _ns, BSON( "a" << 1 ) );
-
- scoped_ptr<Cursor> cursor( IntervalBtreeCursor::make( nsdetails( _ns ),
- nsdetails( _ns )->idx( 1 ),
- BSON( "" << 3 ),
- true,
- BSON( "" << 9 ),
- true ) );
-
- // Advance the cursor to key a:5.
- while( cursor->current()[ "a" ].Int() != 5 ) {
- cursor->advance();
- }
-
- // Backup the cursor position.
- cursor->prepareToYield();
-
- // Mark the key at position 5 as unused.
- nsdetails( _ns )->idx( 1 ).head.btreemod<V1>()->_k( 5 ).setUnused();
-
- // Restore the cursor position.
- cursor->recoverFromYield();
-
- // The cursor advanced from 5, now unused, to 6.
- ASSERT_EQUALS( 6, cursor->current()[ "a" ].Int() );
- }
- };
-
- class All : public Suite {
- public:
- All() : Suite( "intervalbtreecursor" ) {
- }
- void setupTests() {
- add<WrongIndexVersion>();
- add<BasicAccessors>();
- add<Nscanned>();
- add<Matcher>();
- add<Dups>();
- add<InclusiveBounds>();
- add<ExclusiveBounds>();
- add<Interrupt>();
- add<NothingAboveLowerBound>();
- add<NothingInInterval>();
- add<NothingInIntervalFirstMatchBeyondUpperBound>();
- add<NoChangeDuringYield>();
- add<DeleteDuringYield>();
- add<InsertNewDocsDuringYield>();
- add<BecomesMultikeyDuringYield>();
- add<UnusedKeys>();
- add<UnusedEndKey>();
- add<KeyBecomesUnusedDuringYield>();
- }
- } myall;
-
-} // namespace IntervalBtreeCursorTests
diff --git a/src/mongo/dbtests/matchertests.cpp b/src/mongo/dbtests/matchertests.cpp
index e94b9e345f9..a42986bddf2 100644
--- a/src/mongo/dbtests/matchertests.cpp
+++ b/src/mongo/dbtests/matchertests.cpp
@@ -31,12 +31,10 @@
#include "mongo/pch.h"
-#include "mongo/db/cursor.h"
#include "mongo/db/json.h"
#include "mongo/db/matcher.h"
#include "mongo/db/matcher/matcher.h"
#include "mongo/db/namespace_details.h"
-#include "mongo/db/query_optimizer.h"
#include "mongo/dbtests/dbtests.h"
#include "mongo/util/timer.h"
@@ -223,87 +221,6 @@ namespace MatcherTests {
}
};
- namespace Covered { // Tests for CoveredIndexMatcher.
-
- /**
- * Test that MatchDetails::elemMatchKey() is set correctly after an unindexed cursor match.
- */
- class ElemMatchKeyUnindexed : public CollectionBase {
- public:
- void run() {
- client().insert( ns(), fromjson( "{ a:[ {}, { b:1 } ] }" ) );
-
- Client::ReadContext context( ns() );
-
- CoveredIndexMatcher matcher( BSON( "a.b" << 1 ), BSON( "$natural" << 1 ) );
- MatchDetails details;
- details.requestElemMatchKey();
- boost::shared_ptr<Cursor> cursor = getOptimizedCursor( ns(), BSONObj() );
- // Verify that the cursor is unindexed.
- ASSERT_EQUALS( "BasicCursor", cursor->toString() );
- ASSERT( matcher.matchesCurrent( cursor.get(), &details ) );
- // The '1' entry of the 'a' array is matched.
- ASSERT( details.hasElemMatchKey() );
- ASSERT_EQUALS( string( "1" ), details.elemMatchKey() );
- }
- };
-
- /**
- * Test that MatchDetails::elemMatchKey() is set correctly after an indexed cursor match.
- */
- class ElemMatchKeyIndexed : public CollectionBase {
- public:
- void run() {
- client().ensureIndex( ns(), BSON( "a.b" << 1 ) );
- client().insert( ns(), fromjson( "{ a:[ {}, { b:9 }, { b:1 } ] }" ) );
-
- Client::ReadContext context( ns() );
-
- BSONObj query = BSON( "a.b" << 1 );
- CoveredIndexMatcher matcher( query, BSON( "a.b" << 1 ) );
- MatchDetails details;
- details.requestElemMatchKey();
- boost::shared_ptr<Cursor> cursor = getOptimizedCursor( ns(), query );
- // Verify that the cursor is indexed.
- ASSERT_EQUALS( "BtreeCursor a.b_1", cursor->toString() );
- ASSERT( matcher.matchesCurrent( cursor.get(), &details ) );
- // The '2' entry of the 'a' array is matched.
- ASSERT( details.hasElemMatchKey() );
- ASSERT_EQUALS( string( "2" ), details.elemMatchKey() );
- }
- };
-
- /**
- * Test that MatchDetails::elemMatchKey() is set correctly after an indexed cursor match
- * on a non multikey index.
- */
- class ElemMatchKeyIndexedSingleKey : public CollectionBase {
- public:
- void run() {
- client().ensureIndex( ns(), BSON( "a.b" << 1 ) );
- client().insert( ns(), fromjson( "{ a:[ { b:1 } ] }" ) );
-
- Client::ReadContext context( ns() );
-
- BSONObj query = BSON( "a.b" << 1 );
- CoveredIndexMatcher matcher( query, BSON( "a.b" << 1 ) );
- MatchDetails details;
- details.requestElemMatchKey();
- boost::shared_ptr<Cursor> cursor = getOptimizedCursor( ns(), query );
- // Verify that the cursor is indexed.
- ASSERT_EQUALS( "BtreeCursor a.b_1", cursor->toString() );
- // Verify that the cursor is not multikey.
- ASSERT( !cursor->isMultiKey() );
- ASSERT( matcher.matchesCurrent( cursor.get(), &details ) );
- // The '0' entry of the 'a' array is matched.
- ASSERT( details.hasElemMatchKey() );
- ASSERT_EQUALS( string( "0" ), details.elemMatchKey() );
- }
- };
-
- } // namespace Covered
-
-
template< typename M >
class TimingBase {
public:
@@ -472,9 +389,6 @@ namespace MatcherTests {
ADD_BOTH(MixedNumericEmbedded);
ADD_BOTH(ElemMatchKey);
ADD_BOTH(WhereSimple1);
- add<Covered::ElemMatchKeyUnindexed>();
- add<Covered::ElemMatchKeyIndexed>();
- add<Covered::ElemMatchKeyIndexedSingleKey>();
ADD_BOTH(AllTiming);
ADD_BOTH(WithinBox);
ADD_BOTH(WithinCenter);
diff --git a/src/mongo/dbtests/namespacetests.cpp b/src/mongo/dbtests/namespacetests.cpp
index 5768f4427a6..b1ee4a469e7 100644
--- a/src/mongo/dbtests/namespacetests.cpp
+++ b/src/mongo/dbtests/namespacetests.cpp
@@ -38,6 +38,7 @@
#include "mongo/db/index_legacy.h"
#include "mongo/db/index_selection.h"
#include "mongo/db/json.h"
+#include "mongo/db/query/internal_plans.h"
#include "mongo/db/queryutil.h"
#include "mongo/db/catalog/ondisk/namespace.h"
#include "mongo/db/structure/collection.h"
@@ -2150,13 +2151,15 @@ namespace NamespaceTests {
DiskLoc last, first;
{
- ReverseCappedCursor c(nsd);
- last = c.currLoc();
+ auto_ptr<Runner> runner(
+ InternalPlanner::collectionScan(ns(), InternalPlanner::BACKWARD));
+ runner->getNext(NULL, &last);
ASSERT( !last.isNull() );
}
{
- scoped_ptr<ForwardCappedCursor> c( ForwardCappedCursor::make( nsd ) );
- first = c->currLoc();
+ auto_ptr<Runner> runner(
+ InternalPlanner::collectionScan(ns(), InternalPlanner::FORWARD));
+ runner->getNext(NULL, &first);
ASSERT( !first.isNull() );
ASSERT( first != last ) ;
}
@@ -2165,12 +2168,18 @@ namespace NamespaceTests {
ASSERT_EQUALS( nsd->numRecords() , 28 );
{
- scoped_ptr<ForwardCappedCursor> c( ForwardCappedCursor::make( nsd ) );
- ASSERT( first == c->currLoc() );
+ DiskLoc loc;
+ auto_ptr<Runner> runner(
+ InternalPlanner::collectionScan(ns(), InternalPlanner::FORWARD));
+ runner->getNext(NULL, &loc);
+ ASSERT( first == loc);
}
{
- ReverseCappedCursor c(nsd);
- ASSERT( last != c.currLoc() ); // old last should be deleted
+ auto_ptr<Runner> runner(
+ InternalPlanner::collectionScan(ns(), InternalPlanner::BACKWARD));
+ DiskLoc loc;
+ runner->getNext(NULL, &loc);
+ ASSERT( last != loc );
ASSERT( !last.isNull() );
}
@@ -2246,49 +2255,6 @@ namespace NamespaceTests {
}
};
- class CachedPlanBase : public Base {
- public:
- CachedPlanBase() :
- _fieldRangeSet( ns(), BSON( "a" << 1 ), true, true ),
- _pattern( _fieldRangeSet, BSONObj() ) {
- create();
- }
- protected:
- void assertCachedIndexKey( const BSONObj &indexKey ) const {
- ASSERT_EQUALS( indexKey,
- infoCache()->cachedQueryPlanForPattern( _pattern ).indexKey() );
- }
- void registerIndexKey( const BSONObj &indexKey ) {
- infoCache()->registerCachedQueryPlanForPattern( _pattern,
- CachedQueryPlan( indexKey, 1, CandidatePlanCharacter( true, false ) ) );
- }
- FieldRangeSet _fieldRangeSet;
- QueryPattern _pattern;
- };
-
- /**
- * setIndexIsMultikey() sets the multikey flag for an index and clears the query plan
- * cache.
- */
- class SetIndexIsMultikey : public CachedPlanBase {
- public:
- void run() {
- DBDirectClient client;
- client.ensureIndex( ns(), BSON( "a" << 1 ) );
- registerIndexKey( BSON( "a" << 1 ) );
-
- ASSERT( !nsd()->isMultikey( 1 ) );
-
- indexCatalog()->markMultikey( indexCatalog()->getDescriptor( 1 ) );
- ASSERT( nsd()->isMultikey( 1 ) );
- assertCachedIndexKey( BSONObj() );
-
- registerIndexKey( BSON( "a" << 1 ) );
- indexCatalog()->markMultikey( indexCatalog()->getDescriptor( 1 ) );
- assertCachedIndexKey( BSON( "a" << 1 ) );
- }
- };
-
class SwapIndexEntriesTest : public Base {
public:
void run() {
@@ -2325,24 +2291,6 @@ namespace NamespaceTests {
} // namespace NamespaceDetailsTests
- namespace CollectionInfoCacheTests {
-
- /** clearQueryCache() clears the query plan cache. */
- class ClearQueryCache : public NamespaceDetailsTests::CachedPlanBase {
- public:
- void run() {
- // Register a query plan in the query plan cache.
- registerIndexKey( BSON( "a" << 1 ) );
- assertCachedIndexKey( BSON( "a" << 1 ) );
-
- // The query plan is cleared.
- infoCache()->clearQueryCache();
- assertCachedIndexKey( BSONObj() );
- }
- };
-
- } // namespace CollectionInfoCacheTests
-
class All : public Suite {
public:
All() : Suite( "namespace" ) {
@@ -2455,8 +2403,6 @@ namespace NamespaceTests {
add< NamespaceDetailsTests::SwapIndexEntriesTest >();
// add< NamespaceDetailsTests::BigCollection >();
add< NamespaceDetailsTests::Size >();
- add< NamespaceDetailsTests::SetIndexIsMultikey >();
- add< CollectionInfoCacheTests::ClearQueryCache >();
add< MissingFieldTests::BtreeIndexMissingField >();
add< MissingFieldTests::TwoDIndexMissingField >();
add< MissingFieldTests::HashedIndexMissingField >();
diff --git a/src/mongo/dbtests/pdfiletests.cpp b/src/mongo/dbtests/pdfiletests.cpp
index 442b87021d4..dd1283b14ad 100644
--- a/src/mongo/dbtests/pdfiletests.cpp
+++ b/src/mongo/dbtests/pdfiletests.cpp
@@ -38,248 +38,6 @@
namespace PdfileTests {
- // XXX: These tests have been ported to query_stage_collscan.cpp and are deprecated here.
- namespace ScanCapped {
-
- class Base {
- public:
- Base() : _context( ns() ) {
- }
- virtual ~Base() {
- if ( !nsd() )
- return;
- _context.db()->dropCollection( ns() );
- }
- void run() {
- stringstream spec;
- spec << "{\"capped\":true,\"size\":2000,\"$nExtents\":" << nExtents() << "}";
- string err;
- ASSERT( userCreateNS( ns(), fromjson( spec.str() ), err, false ) );
- prepare();
- int j = 0;
- for ( boost::shared_ptr<Cursor> i = theDataFileMgr.findAll( ns() );
- i->ok(); i->advance(), ++j )
- ASSERT_EQUALS( j, i->current().firstElement().number() );
- ASSERT_EQUALS( count(), j );
-
- j = count() - 1;
- for ( boost::shared_ptr<Cursor> i =
- findTableScan( ns(), fromjson( "{\"$natural\":-1}" ) );
- i->ok(); i->advance(), --j )
- ASSERT_EQUALS( j, i->current().firstElement().number() );
- ASSERT_EQUALS( -1, j );
- }
- protected:
- virtual void prepare() = 0;
- virtual int count() const = 0;
- virtual int nExtents() const {
- return 0;
- }
- // bypass standard alloc/insert routines to use the extent we want.
- static DiskLoc insert( const DiskLoc& ext, int i ) {
- BSONObjBuilder b;
- b.append( "a", i );
- BSONObj o = b.done();
- int len = o.objsize();
- Extent *e = ext.ext();
- e = getDur().writing(e);
- int ofs;
- if ( e->lastRecord.isNull() )
- ofs = ext.getOfs() + ( e->_extentData - (char *)e );
- else
- ofs = e->lastRecord.getOfs() + e->lastRecord.rec()->lengthWithHeaders();
- DiskLoc dl( ext.a(), ofs );
- Record *r = dl.rec();
- r = (Record*) getDur().writingPtr(r, Record::HeaderSize + len);
- r->lengthWithHeaders() = Record::HeaderSize + len;
- r->extentOfs() = e->myLoc.getOfs();
- r->nextOfs() = DiskLoc::NullOfs;
- r->prevOfs() = e->lastRecord.isNull() ? DiskLoc::NullOfs : e->lastRecord.getOfs();
- memcpy( r->data(), o.objdata(), len );
- if ( e->firstRecord.isNull() )
- e->firstRecord = dl;
- else
- getDur().writingInt(e->lastRecord.rec()->nextOfs()) = ofs;
- e->lastRecord = dl;
- return dl;
- }
- static const char *ns() {
- return "unittests.ScanCapped";
- }
- static NamespaceDetails *nsd() {
- return nsdetails( ns() );
- }
- private:
- Lock::GlobalWrite lk_;
- Client::Context _context;
- };
-
- class Empty : public Base {
- virtual void prepare() {}
- virtual int count() const {
- return 0;
- }
- };
-
- class EmptyLooped : public Base {
- virtual void prepare() {
- nsd()->writingWithExtra()->capFirstNewRecord() = DiskLoc();
- }
- virtual int count() const {
- return 0;
- }
- };
-
- class EmptyMultiExtentLooped : public Base {
- virtual void prepare() {
- nsd()->writingWithExtra()->capFirstNewRecord() = DiskLoc();
- }
- virtual int count() const {
- return 0;
- }
- virtual int nExtents() const {
- return 3;
- }
- };
-
- class Single : public Base {
- virtual void prepare() {
- nsd()->writingWithExtra()->capFirstNewRecord() = insert( nsd()->capExtent(), 0 );
- }
- virtual int count() const {
- return 1;
- }
- };
-
- class NewCapFirst : public Base {
- virtual void prepare() {
- DiskLoc x = insert( nsd()->capExtent(), 0 );
- nsd()->writingWithExtra()->capFirstNewRecord() = x;
- insert( nsd()->capExtent(), 1 );
- }
- virtual int count() const {
- return 2;
- }
- };
-
- class NewCapLast : public Base {
- virtual void prepare() {
- insert( nsd()->capExtent(), 0 );
- nsd()->capFirstNewRecord().writing() = insert( nsd()->capExtent(), 1 );
- }
- virtual int count() const {
- return 2;
- }
- };
-
- class NewCapMiddle : public Base {
- virtual void prepare() {
- insert( nsd()->capExtent(), 0 );
- nsd()->capFirstNewRecord().writing() = insert( nsd()->capExtent(), 1 );
- insert( nsd()->capExtent(), 2 );
- }
- virtual int count() const {
- return 3;
- }
- };
-
- class FirstExtent : public Base {
- virtual void prepare() {
- insert( nsd()->capExtent(), 0 );
- insert( nsd()->lastExtent(), 1 );
- nsd()->capFirstNewRecord().writing() = insert( nsd()->capExtent(), 2 );
- insert( nsd()->capExtent(), 3 );
- }
- virtual int count() const {
- return 4;
- }
- virtual int nExtents() const {
- return 2;
- }
- };
-
- class LastExtent : public Base {
- virtual void prepare() {
- nsd()->capExtent().writing() = nsd()->lastExtent();
- insert( nsd()->capExtent(), 0 );
- insert( nsd()->firstExtent(), 1 );
- nsd()->capFirstNewRecord().writing() = insert( nsd()->capExtent(), 2 );
- insert( nsd()->capExtent(), 3 );
- }
- virtual int count() const {
- return 4;
- }
- virtual int nExtents() const {
- return 2;
- }
- };
-
- class MidExtent : public Base {
- virtual void prepare() {
- nsd()->capExtent().writing() = nsd()->firstExtent().ext()->xnext;
- insert( nsd()->capExtent(), 0 );
- insert( nsd()->lastExtent(), 1 );
- insert( nsd()->firstExtent(), 2 );
- nsd()->capFirstNewRecord().writing() = insert( nsd()->capExtent(), 3 );
- insert( nsd()->capExtent(), 4 );
- }
- virtual int count() const {
- return 5;
- }
- virtual int nExtents() const {
- return 3;
- }
- };
-
- class AloneInExtent : public Base {
- virtual void prepare() {
- nsd()->capExtent().writing() = nsd()->firstExtent().ext()->xnext;
- insert( nsd()->lastExtent(), 0 );
- insert( nsd()->firstExtent(), 1 );
- nsd()->capFirstNewRecord().writing() = insert( nsd()->capExtent(), 2 );
- }
- virtual int count() const {
- return 3;
- }
- virtual int nExtents() const {
- return 3;
- }
- };
-
- class FirstInExtent : public Base {
- virtual void prepare() {
- nsd()->capExtent().writing() = nsd()->firstExtent().ext()->xnext;
- insert( nsd()->lastExtent(), 0 );
- insert( nsd()->firstExtent(), 1 );
- nsd()->capFirstNewRecord().writing() = insert( nsd()->capExtent(), 2 );
- insert( nsd()->capExtent(), 3 );
- }
- virtual int count() const {
- return 4;
- }
- virtual int nExtents() const {
- return 3;
- }
- };
-
- class LastInExtent : public Base {
- virtual void prepare() {
- nsd()->capExtent().writing() = nsd()->firstExtent().ext()->xnext;
- insert( nsd()->capExtent(), 0 );
- insert( nsd()->lastExtent(), 1 );
- insert( nsd()->firstExtent(), 2 );
- nsd()->capFirstNewRecord().writing() = insert( nsd()->capExtent(), 3 );
- }
- virtual int count() const {
- return 4;
- }
- virtual int nExtents() const {
- return 3;
- }
- };
-
- } // namespace ScanCapped
-
namespace Insert {
class Base {
public:
@@ -385,19 +143,6 @@ namespace PdfileTests {
All() : Suite( "pdfile" ) {}
void setupTests() {
- add< ScanCapped::Empty >();
- add< ScanCapped::EmptyLooped >();
- add< ScanCapped::EmptyMultiExtentLooped >();
- add< ScanCapped::Single >();
- add< ScanCapped::NewCapFirst >();
- add< ScanCapped::NewCapLast >();
- add< ScanCapped::NewCapMiddle >();
- add< ScanCapped::FirstExtent >();
- add< ScanCapped::LastExtent >();
- add< ScanCapped::MidExtent >();
- add< ScanCapped::AloneInExtent >();
- add< ScanCapped::FirstInExtent >();
- add< ScanCapped::LastInExtent >();
add< Insert::InsertAddId >();
add< Insert::UpdateDate >();
add< ExtentSizing >();
diff --git a/src/mongo/dbtests/perf/perftest.cpp b/src/mongo/dbtests/perf/perftest.cpp
index f4a6c98ff39..2afa74f68fa 100644
--- a/src/mongo/dbtests/perf/perftest.cpp
+++ b/src/mongo/dbtests/perf/perftest.cpp
@@ -37,7 +37,6 @@
#include "mongo/client/dbclientcursor.h"
#include "mongo/db/instance.h"
#include "mongo/db/json.h"
-#include "mongo/db/query_optimizer_internal.h"
#include "mongo/dbtests/dbtests.h"
#include "mongo/dbtests/framework.h"
#include "mongo/util/file_allocator.h"
@@ -665,6 +664,8 @@ namespace Count {
namespace Plan {
+ // QUERY_MIGRATION: what is this really testing?
+#if 0
class Hint {
public:
Hint() : ns_( testNs( this ) ) {
@@ -741,6 +742,7 @@ namespace Plan {
add< Query >();
}
} all;
+#endif
} // namespace Plan
namespace Misc {
diff --git a/src/mongo/dbtests/query_stage_fetch.cpp b/src/mongo/dbtests/query_stage_fetch.cpp
index 5ef65be9688..23e6f7f0d0e 100644
--- a/src/mongo/dbtests/query_stage_fetch.cpp
+++ b/src/mongo/dbtests/query_stage_fetch.cpp
@@ -33,7 +33,6 @@
#include <boost/shared_ptr.hpp>
#include "mongo/client/dbclientcursor.h"
-#include "mongo/db/cursor.h"
#include "mongo/db/database.h"
#include "mongo/db/exec/fetch.h"
#include "mongo/db/exec/plan_stage.h"
diff --git a/src/mongo/dbtests/queryoptimizercursortests.cpp b/src/mongo/dbtests/queryoptimizercursortests.cpp
deleted file mode 100644
index f599549e32f..00000000000
--- a/src/mongo/dbtests/queryoptimizercursortests.cpp
+++ /dev/null
@@ -1,4984 +0,0 @@
-// queryoptimizertests.cpp : query optimizer unit tests
-//
-
-/**
- * Copyright (C) 2009 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/pch.h"
-
-
-#include "mongo/client/dbclientcursor.h"
-#include "mongo/db/clientcursor.h"
-#include "mongo/db/instance.h"
-#include "mongo/db/json.h"
-#include "mongo/db/pdfile.h"
-#include "mongo/db/query_optimizer.h"
-#include "mongo/db/query_optimizer_internal.h"
-#include "mongo/db/queryoptimizercursorimpl.h"
-#include "mongo/db/queryutil.h"
-#include "mongo/db/structure/collection.h"
-#include "mongo/dbtests/dbtests.h"
-
-namespace mongo {
- shared_ptr<Cursor> newQueryOptimizerCursor( const char *ns, const BSONObj &query,
- const BSONObj &order = BSONObj(),
- const QueryPlanSelectionPolicy &planPolicy =
- QueryPlanSelectionPolicy::any(),
- bool requireOrder = true,
- const shared_ptr<const ParsedQuery> &parsedQuery =
- shared_ptr<const ParsedQuery>() );
-} // namespace mongo
-
-namespace QueryOptimizerCursorTests {
-
- using boost::shared_ptr;
-
- namespace CachedMatchCounter {
-
- using mongo::CachedMatchCounter;
-
- class Count {
- public:
- void run() {
- long long aggregateNscanned;
- CachedMatchCounter c( aggregateNscanned, 0 );
- ASSERT_EQUALS( 0, c.count() );
- ASSERT_EQUALS( 0, c.cumulativeCount() );
-
- c.resetMatch();
- ASSERT( !c.knowMatch() );
-
- c.setMatch( false );
- ASSERT( c.knowMatch() );
-
- c.incMatch( DiskLoc() );
- ASSERT_EQUALS( 0, c.count() );
- ASSERT_EQUALS( 0, c.cumulativeCount() );
-
- c.resetMatch();
- ASSERT( !c.knowMatch() );
-
- c.setMatch( true );
- ASSERT( c.knowMatch() );
-
- c.incMatch( DiskLoc() );
- ASSERT_EQUALS( 1, c.count() );
- ASSERT_EQUALS( 1, c.cumulativeCount() );
-
- // Don't count the same match twice, without checking the document location.
- c.incMatch( DiskLoc( 1, 1 ) );
- ASSERT_EQUALS( 1, c.count() );
- ASSERT_EQUALS( 1, c.cumulativeCount() );
-
- // Reset and count another match.
- c.resetMatch();
- c.setMatch( true );
- c.incMatch( DiskLoc( 1, 1 ) );
- ASSERT_EQUALS( 2, c.count() );
- ASSERT_EQUALS( 2, c.cumulativeCount() );
- }
- };
-
- class Accumulate {
- public:
- void run() {
- long long aggregateNscanned;
- CachedMatchCounter c( aggregateNscanned, 10 );
- ASSERT_EQUALS( 0, c.count() );
- ASSERT_EQUALS( 10, c.cumulativeCount() );
-
- c.setMatch( true );
- c.incMatch( DiskLoc() );
- ASSERT_EQUALS( 1, c.count() );
- ASSERT_EQUALS( 11, c.cumulativeCount() );
- }
- };
-
- class Dedup {
- public:
- void run() {
- long long aggregateNscanned;
- CachedMatchCounter c( aggregateNscanned, 0 );
-
- c.setCheckDups( true );
- c.setMatch( true );
- c.incMatch( DiskLoc() );
- ASSERT_EQUALS( 1, c.count() );
-
- c.resetMatch();
- c.setMatch( true );
- c.incMatch( DiskLoc() );
- ASSERT_EQUALS( 1, c.count() );
- }
- };
-
- class Nscanned {
- public:
- void run() {
- long long aggregateNscanned = 5;
- CachedMatchCounter c( aggregateNscanned, 0 );
- ASSERT_EQUALS( 0, c.nscanned() );
- ASSERT_EQUALS( 5, c.aggregateNscanned() );
-
- c.updateNscanned( 4 );
- ASSERT_EQUALS( 4, c.nscanned() );
- ASSERT_EQUALS( 9, c.aggregateNscanned() );
- }
- };
-
- } // namespace CachedMatchCounter
-
- namespace SmallDupSet {
-
- using mongo::SmallDupSet;
-
- class Upgrade {
- public:
- void run() {
- SmallDupSet d;
- for( int i = 0; i < 100; ++i ) {
- ASSERT( !d.getsetdup( DiskLoc( 0, i ) ) );
- for( int j = 0; j <= i; ++j ) {
- ASSERT( d.getdup( DiskLoc( 0, j ) ) );
- }
- }
- }
- };
-
- class UpgradeRead {
- public:
- void run() {
- SmallDupSet d;
- d.getsetdup( DiskLoc( 0, 0 ) );
- for( int i = 0; i < 550; ++i ) {
- ASSERT( d.getdup( DiskLoc( 0, 0 ) ) );
- }
- ASSERT( d.getsetdup( DiskLoc( 0, 0 ) ) );
- }
- };
-
- class UpgradeWrite {
- public:
- void run() {
- SmallDupSet d;
- for( int i = 0; i < 550; ++i ) {
- ASSERT( !d.getsetdup( DiskLoc( 0, i ) ) );
- }
- for( int i = 0; i < 550; ++i ) {
- ASSERT( d.getsetdup( DiskLoc( 0, i ) ) );
- }
- }
- };
-
- } // namespace SmallDupSet
-
- class DurationTimerStop {
- public:
- void run() {
- DurationTimer t;
- while( t.duration() == 0 );
- ASSERT( t.duration() > 0 );
- t.stop();
- ASSERT( t.duration() > 0 );
- ASSERT( t.duration() > 0 );
- }
- };
-
- class Base {
- public:
- Base() {
- Lock::GlobalWrite lk;
- Client::Context ctx( ns() );
- string err;
- userCreateNS( ns(), BSONObj(), err, false );
- ctx.db()->dropCollection( ns() );
- }
- ~Base() {
- cc().curop()->reset();
- }
- protected:
- DBDirectClient _cli;
- static const char *ns() { return "unittests.QueryOptimizerTests"; }
- void setQueryOptimizerCursor( const BSONObj &query, const BSONObj &order = BSONObj() ) {
- setQueryOptimizerCursorWithoutAdvancing( query, order );
- if ( ok() && !mayReturnCurrent() ) {
- advance();
- }
- }
- void setQueryOptimizerCursorWithoutAdvancing( const BSONObj &query, const BSONObj &order = BSONObj() ) {
- _c = newQueryOptimizerCursor( ns(), query, order );
- }
- bool ok() const { return _c->ok(); }
- /** Handles matching and deduping. */
- bool advance() {
- while( _c->advance() && !mayReturnCurrent() );
- return ok();
- }
- int itcount() {
- int ret = 0;
- while( ok() ) {
- ++ret;
- advance();
- }
- return ret;
- }
- BSONObj current() const { return _c->current(); }
- DiskLoc currLoc() const { return _c->currLoc(); }
- void prepareToTouchEarlierIterate() { _c->prepareToTouchEarlierIterate(); }
- void recoverFromTouchingEarlierIterate() { _c->recoverFromTouchingEarlierIterate(); }
- bool mayReturnCurrent() {
-// return _c->currentMatches() && !_c->getsetdup( _c->currLoc() );
- return ( !_c->matcher() || _c->matcher()->matchesCurrent( _c.get() ) ) && !_c->getsetdup( _c->currLoc() );
- }
- void prepareToYield() const { _c->prepareToYield(); }
- void recoverFromYield() {
- _c->recoverFromYield();
- if ( ok() && !mayReturnCurrent() ) {
- advance();
- }
- }
- shared_ptr<Cursor> c() { return _c; }
- long long nscanned() const { return _c->nscanned(); }
- unsigned nNsCursors() const {
- set<CursorId> nsCursors;
- ClientCursor::find( ns(), nsCursors );
- return nsCursors.size();
- }
- BSONObj cachedIndexForQuery( const BSONObj &query, const BSONObj &order = BSONObj() ) {
- QueryPattern queryPattern = FieldRangeSet( ns(), query, true, true ).pattern( order );
-
- Client* client = currentClient.get();
- verify( client );
-
- Collection* collection = client->database()->getCollection( ns() );
- if ( !collection )
- return BSONObj();
-
- return collection->infoCache()->cachedQueryPlanForPattern( queryPattern ).indexKey();
- }
- private:
- shared_ptr<Cursor> _c;
- };
-
- /** No results for empty collection. */
- class Empty : public Base {
- public:
- void run() {
- Lock::GlobalWrite lk;
- Client::Context ctx( ns() );
- shared_ptr<QueryOptimizerCursor> c =
- dynamic_pointer_cast<QueryOptimizerCursor>
- ( newQueryOptimizerCursor( ns(), BSONObj() ) );
- ASSERT( !c->ok() );
- ASSERT_THROWS( c->_current(), AssertionException );
- ASSERT_THROWS( c->current(), AssertionException );
- ASSERT( c->currLoc().isNull() );
- ASSERT( !c->advance() );
- ASSERT_THROWS( c->currKey(), AssertionException );
- ASSERT_THROWS( c->getsetdup( DiskLoc() ), AssertionException );
- ASSERT_THROWS( c->isMultiKey(), AssertionException );
- ASSERT_THROWS( c->matcher(), AssertionException );
-
- ASSERT_THROWS( c->initialFieldRangeSet(), AssertionException );
- ASSERT_THROWS( c->currentPlanScanAndOrderRequired(), AssertionException );
- ASSERT_THROWS( c->keyFieldsOnly(), AssertionException );
- ASSERT_THROWS( c->runningInitialInOrderPlan(), AssertionException );
- ASSERT_THROWS( c->hasPossiblyExcludedPlans(), AssertionException );
-
- // ok
- c->initialCandidatePlans();
- c->completePlanOfHybridSetScanAndOrderRequired();
- c->clearIndexesForPatterns();
- c->abortOutOfOrderPlans();
- c->noteIterate( false, false, false );
- c->explainQueryInfo();
- }
- };
-
- /** Simple table scan. */
- class Unindexed : public Base {
- public:
- void run() {
- _cli.insert( ns(), BSON( "_id" << 1 ) );
- _cli.insert( ns(), BSON( "_id" << 2 ) );
-
- Lock::GlobalWrite lk;
- Client::Context ctx( ns() );
- setQueryOptimizerCursor( BSONObj() );
- ASSERT_EQUALS( 2, itcount() );
- }
- };
-
- /** Basic test with two indexes and deduping requirement. */
- class Basic : public Base {
- public:
- void run() {
- _cli.insert( ns(), BSON( "_id" << 1 << "a" << 2 ) );
- _cli.insert( ns(), BSON( "_id" << 2 << "a" << 1 ) );
- _cli.ensureIndex( ns(), BSON( "a" << 1 ) );
-
- Lock::GlobalWrite lk;
- Client::Context ctx( ns() );
- setQueryOptimizerCursor( BSON( "_id" << GT << 0 << "a" << GT << 0 ) );
- ASSERT( ok() );
- ASSERT_EQUALS( BSON( "_id" << 1 << "a" << 2 ), current() );
- ASSERT( advance() );
- ASSERT_EQUALS( BSON( "_id" << 2 << "a" << 1 ), current() );
- ASSERT( !advance() );
- ASSERT( !ok() );
- }
- };
-
- class NoMatch : public Base {
- public:
- void run() {
- _cli.ensureIndex( ns(), BSON( "a" << 1 ) );
-
- Lock::GlobalWrite lk;
- Client::Context ctx( ns() );
- setQueryOptimizerCursor( BSON( "_id" << GT << 5 << LT << 4 << "a" << GT << 0 ) );
- ASSERT( !ok() );
- }
- };
-
- /** Order of results indicates that interleaving is occurring. */
- class Interleaved : public Base {
- public:
- void run() {
- _cli.insert( ns(), BSON( "_id" << 1 << "a" << 2 ) );
- _cli.insert( ns(), BSON( "_id" << 3 << "a" << 1 ) );
- _cli.insert( ns(), BSON( "_id" << 2 << "a" << 2 ) );
- _cli.ensureIndex( ns(), BSON( "a" << 1 ) );
-
- Lock::GlobalWrite lk;
- Client::Context ctx( ns() );
- setQueryOptimizerCursor( BSON( "_id" << GT << 0 << "a" << GT << 0 ) );
- ASSERT( ok() );
- ASSERT_EQUALS( BSON( "_id" << 1 << "a" << 2 ), current() );
- ASSERT( advance() );
- ASSERT_EQUALS( BSON( "_id" << 3 << "a" << 1 ), current() );
- ASSERT( advance() );
- ASSERT_EQUALS( BSON( "_id" << 2 << "a" << 2 ), current() );
- ASSERT( !advance() );
- ASSERT( !ok() );
- }
- };
-
- /** Some values on each index do not match. */
- class NotMatch : public Base {
- public:
- void run() {
- _cli.insert( ns(), BSON( "_id" << 0 << "a" << 10 ) );
- _cli.insert( ns(), BSON( "_id" << 10 << "a" << 0 ) );
- _cli.insert( ns(), BSON( "_id" << 11 << "a" << 12 ) );
- _cli.insert( ns(), BSON( "_id" << 12 << "a" << 11 ) );
- _cli.ensureIndex( ns(), BSON( "a" << 1 ) );
-
- Lock::GlobalWrite lk;
- Client::Context ctx( ns() );
- setQueryOptimizerCursor( BSON( "_id" << GT << 5 << "a" << GT << 5 ) );
- ASSERT( ok() );
- ASSERT_EQUALS( BSON( "_id" << 11 << "a" << 12 ), current() );
- ASSERT( advance() );
- ASSERT_EQUALS( BSON( "_id" << 12 << "a" << 11 ), current() );
- ASSERT( !advance() );
- ASSERT( !ok() );
- }
- };
-
- /** After the first 101 matches for a plan, we stop interleaving the plans. */
- class StopInterleaving : public Base {
- public:
- void run() {
- for( int i = 0; i < 101; ++i ) {
- _cli.insert( ns(), BSON( "_id" << i << "a" << i ) );
- }
- for( int i = 101; i < 200; ++i ) {
- _cli.insert( ns(), BSON( "_id" << i << "a" << (301-i) ) );
- }
- _cli.ensureIndex( ns(), BSON( "a" << 1 ) );
-
- Lock::GlobalWrite lk;
- Client::Context ctx( ns() );
- setQueryOptimizerCursor( BSON( "_id" << GT << -1 << "a" << GT << -1 ) );
- for( int i = 0; i < 200; ++i ) {
- ASSERT( ok() );
- ASSERT_EQUALS( i, current().getIntField( "_id" ) );
- advance();
- }
- ASSERT( !advance() );
- ASSERT( !ok() );
- }
- };
-
- /** Test correct deduping with the takeover cursor. */
- class TakeoverWithDup : public Base {
- public:
- void run() {
- for( int i = 0; i < 101; ++i ) {
- _cli.insert( ns(), BSON( "_id" << i << "a" << i ) );
- }
- _cli.insert( ns(), BSON( "_id" << 500 << "a" << BSON_ARRAY( 0 << 300 ) ) );
- _cli.ensureIndex( ns(), BSON( "a" << 1 ) );
- Lock::GlobalWrite lk;
- Client::Context ctx( ns() );
- setQueryOptimizerCursor( BSON( "_id" << GT << -1 << "a" << GT << -1 ) );
- ASSERT_EQUALS( 102, itcount() );
- }
- };
-
- /** Test usage of matcher with takeover cursor. */
- class TakeoverWithNonMatches : public Base {
- public:
- void run() {
- for( int i = 0; i < 101; ++i ) {
- _cli.insert( ns(), BSON( "_id" << i << "a" << i ) );
- }
- _cli.insert( ns(), BSON( "_id" << 101 << "a" << 600 ) );
- _cli.ensureIndex( ns(), BSON( "a" << 1 ) );
- Lock::GlobalWrite lk;
- Client::Context ctx( ns() );
- setQueryOptimizerCursor( BSON( "_id" << GT << -1 << "a" << LT << 500 ) );
- ASSERT_EQUALS( 101, itcount() );
- }
- };
-
- /** Check deduping of dups within just the takeover cursor. */
- class TakeoverWithTakeoverDup : public Base {
- public:
- void run() {
- for( int i = 0; i < 101; ++i ) {
- _cli.insert( ns(), BSON( "_id" << i*2 << "a" << 0 ) );
- _cli.insert( ns(), BSON( "_id" << i*2+1 << "a" << 1 ) );
- }
- _cli.insert( ns(), BSON( "_id" << 202 << "a" << BSON_ARRAY( 2 << 3 ) ) );
- _cli.ensureIndex( ns(), BSON( "a" << 1 ) );
- Lock::GlobalWrite lk;
- Client::Context ctx( ns() );
- setQueryOptimizerCursor( BSON( "_id" << GT << -1 << "a" << GT << 0) );
- ASSERT_EQUALS( 102, itcount() );
- }
- };
-
- /** Basic test with $or query. */
- class BasicOr : public Base {
- public:
- void run() {
- _cli.insert( ns(), BSON( "_id" << 0 << "a" << 0 ) );
- _cli.insert( ns(), BSON( "_id" << 1 << "a" << 1 ) );
- _cli.ensureIndex( ns(), BSON( "a" << 1 ) );
- Lock::GlobalWrite lk;
- Client::Context ctx( ns() );
- setQueryOptimizerCursor( BSON( "$or" << BSON_ARRAY( BSON( "_id" << 0 ) << BSON( "a" << 1 ) ) ) );
- ASSERT_EQUALS( BSON( "_id" << 0 << "a" << 0 ), current() );
- ASSERT( advance() );
- ASSERT_EQUALS( BSON( "_id" << 1 << "a" << 1 ), current() );
- ASSERT( !advance() );
- }
- };
-
- /** $or first clause empty. */
- class OrFirstClauseEmpty : public Base {
- public:
- void run() {
- _cli.insert( ns(), BSON( "_id" << 0 << "a" << 1 ) );
- _cli.insert( ns(), BSON( "_id" << 1 << "a" << 1 ) );
- _cli.ensureIndex( ns(), BSON( "a" << 1 ) );
- Lock::GlobalWrite lk;
- Client::Context ctx( ns() );
- setQueryOptimizerCursor( BSON( "$or" << BSON_ARRAY( BSON( "_id" << -1 ) << BSON( "a" << 1 ) ) ) );
- ASSERT_EQUALS( BSON( "_id" << 0 << "a" << 1 ), current() );
- ASSERT( advance() );
- ASSERT_EQUALS( BSON( "_id" << 1 << "a" << 1 ), current() );
- ASSERT( !advance() );
- }
- };
-
- /** $or second clause empty. */
- class OrSecondClauseEmpty : public Base {
- public:
- void run() {
- _cli.insert( ns(), BSON( "_id" << 0 << "a" << 1 ) );
- _cli.insert( ns(), BSON( "_id" << 1 << "a" << 1 ) );
- _cli.ensureIndex( ns(), BSON( "a" << 1 ) );
- Lock::GlobalWrite lk;
- Client::Context ctx( ns() );
- setQueryOptimizerCursor( BSON( "$or" << BSON_ARRAY( BSON( "_id" << 0 ) << BSON( "_id" << -1 ) << BSON( "a" << 1 ) ) ) );
- ASSERT_EQUALS( BSON( "_id" << 0 << "a" << 1 ), current() );
- ASSERT( advance() );
- ASSERT_EQUALS( BSON( "_id" << 1 << "a" << 1 ), current() );
- ASSERT( !advance() );
- }
- };
-
- /** $or multiple clauses empty empty. */
- class OrMultipleClausesEmpty : public Base {
- public:
- void run() {
- _cli.insert( ns(), BSON( "_id" << 0 << "a" << 1 ) );
- _cli.insert( ns(), BSON( "_id" << 1 << "a" << 1 ) );
- _cli.ensureIndex( ns(), BSON( "a" << 1 ) );
- Lock::GlobalWrite lk;
- Client::Context ctx( ns() );
- setQueryOptimizerCursor( BSON( "$or" << BSON_ARRAY( BSON( "_id" << 2 ) << BSON( "_id" << 4 ) << BSON( "_id" << 0 ) << BSON( "_id" << -1 ) << BSON( "_id" << 6 ) << BSON( "a" << 1 ) << BSON( "_id" << 9 ) ) ) );
- ASSERT_EQUALS( BSON( "_id" << 0 << "a" << 1 ), current() );
- ASSERT( advance() );
- ASSERT_EQUALS( BSON( "_id" << 1 << "a" << 1 ), current() );
- ASSERT( !advance() );
- }
- };
-
- /** Check that takeover occurs at proper match count with $or clauses */
- class TakeoverCountOr : public Base {
- public:
- void run() {
- for( int i = 0; i < 60; ++i ) {
- _cli.insert( ns(), BSON( "_id" << i << "a" << 0 ) );
- }
- for( int i = 60; i < 120; ++i ) {
- _cli.insert( ns(), BSON( "_id" << i << "a" << 1 ) );
- }
- for( int i = 120; i < 150; ++i ) {
- _cli.insert( ns(), BSON( "_id" << i << "a" << (200-i) ) );
- }
- _cli.ensureIndex( ns(), BSON( "a" << 1 ) );
- Lock::GlobalWrite lk;
- Client::Context ctx( ns() );
- setQueryOptimizerCursor( BSON( "$or" << BSON_ARRAY( BSON( "a" << 0 ) << BSON( "a" << 1 ) << BSON( "_id" << GTE << 120 << "a" << GT << 1 ) ) ) );
- for( int i = 0; i < 120; ++i ) {
- ASSERT( ok() );
- advance();
- }
- // Expect to be scanning on _id index only.
- for( int i = 120; i < 150; ++i ) {
- ASSERT_EQUALS( i, current().getIntField( "_id" ) );
- advance();
- }
- ASSERT( !ok() );
- }
- };
-
- /** Takeover just at end of clause. */
- class TakeoverEndOfOrClause : public Base {
- public:
- void run() {
- for( int i = 0; i < 102; ++i ) {
- _cli.insert( ns(), BSON( "_id" << i ) );
- }
- Lock::GlobalWrite lk;
- Client::Context ctx( ns() );
- setQueryOptimizerCursor( BSON( "$or" << BSON_ARRAY( BSON( "_id" << LT << 101 ) << BSON( "_id" << 101 ) ) ) );
- for( int i = 0; i < 102; ++i ) {
- ASSERT_EQUALS( i, current().getIntField( "_id" ) );
- advance();
- }
- ASSERT( !ok() );
- }
- };
-
- class TakeoverBeforeEndOfOrClause : public Base {
- public:
- void run() {
- for( int i = 0; i < 101; ++i ) {
- _cli.insert( ns(), BSON( "_id" << i ) );
- }
- Lock::GlobalWrite lk;
- Client::Context ctx( ns() );
- setQueryOptimizerCursor( BSON( "$or" << BSON_ARRAY( BSON( "_id" << LT << 100 ) << BSON( "_id" << 100 ) ) ) );
- for( int i = 0; i < 101; ++i ) {
- ASSERT_EQUALS( i, current().getIntField( "_id" ) );
- advance();
- }
- ASSERT( !ok() );
- }
- };
-
- class TakeoverAfterEndOfOrClause : public Base {
- public:
- void run() {
- for( int i = 0; i < 103; ++i ) {
- _cli.insert( ns(), BSON( "_id" << i ) );
- }
- Lock::GlobalWrite lk;
- Client::Context ctx( ns() );
- setQueryOptimizerCursor( BSON( "$or" << BSON_ARRAY( BSON( "_id" << LT << 102 ) << BSON( "_id" << 102 ) ) ) );
- for( int i = 0; i < 103; ++i ) {
- ASSERT_EQUALS( i, current().getIntField( "_id" ) );
- advance();
- }
- ASSERT( !ok() );
- }
- };
-
- /** Test matching and deduping done manually by cursor client. */
- class ManualMatchingDeduping : public Base {
- public:
- void run() {
- _cli.insert( ns(), BSON( "_id" << 0 << "a" << 10 ) );
- _cli.insert( ns(), BSON( "_id" << 10 << "a" << 0 ) );
- _cli.insert( ns(), BSON( "_id" << 11 << "a" << 12 ) );
- _cli.insert( ns(), BSON( "_id" << 12 << "a" << 11 ) );
- _cli.ensureIndex( ns(), BSON( "a" << 1 ) );
-
- Lock::GlobalWrite lk;
- Client::Context ctx( ns() );
- shared_ptr< Cursor > c = newQueryOptimizerCursor( ns(), BSON( "_id" << GT << 5 << "a" << GT << 5 ) );
- ASSERT( c->ok() );
-
- // _id 10 {_id:1}
- ASSERT_EQUALS( 10, c->current().getIntField( "_id" ) );
- ASSERT( !c->matcher()->matchesCurrent( c.get() ) );
- ASSERT( c->advance() );
-
- // _id 0 {a:1}
- ASSERT_EQUALS( 0, c->current().getIntField( "_id" ) );
- ASSERT( !c->matcher()->matchesCurrent( c.get() ) );
- ASSERT( c->advance() );
-
- // _id 0 {$natural:1}
- ASSERT_EQUALS( 0, c->current().getIntField( "_id" ) );
- ASSERT( !c->matcher()->matchesCurrent( c.get() ) );
- ASSERT( c->advance() );
-
- // _id 11 {_id:1}
- ASSERT_EQUALS( BSON( "_id" << 11 << "a" << 12 ), c->current() );
- ASSERT( c->matcher()->matchesCurrent( c.get() ) );
- ASSERT( !c->getsetdup( c->currLoc() ) );
- ASSERT( c->advance() );
-
- // _id 12 {a:1}
- ASSERT_EQUALS( BSON( "_id" << 12 << "a" << 11 ), c->current() );
- ASSERT( c->matcher()->matchesCurrent( c.get() ) );
- ASSERT( !c->getsetdup( c->currLoc() ) );
- ASSERT( c->advance() );
-
- // _id 10 {$natural:1}
- ASSERT_EQUALS( 10, c->current().getIntField( "_id" ) );
- ASSERT( !c->matcher()->matchesCurrent( c.get() ) );
- ASSERT( c->advance() );
-
- // _id 12 {_id:1}
- ASSERT_EQUALS( BSON( "_id" << 12 << "a" << 11 ), c->current() );
- ASSERT( c->matcher()->matchesCurrent( c.get() ) );
- ASSERT( c->getsetdup( c->currLoc() ) );
- ASSERT( c->advance() );
-
- // _id 11 {a:1}
- ASSERT_EQUALS( BSON( "_id" << 11 << "a" << 12 ), c->current() );
- ASSERT( c->matcher()->matchesCurrent( c.get() ) );
- ASSERT( c->getsetdup( c->currLoc() ) );
- ASSERT( c->advance() );
-
- // _id 11 {$natural:1}
- ASSERT_EQUALS( 11, c->current().getIntField( "_id" ) );
- ASSERT( c->matcher()->matchesCurrent( c.get() ) );
- ASSERT( c->getsetdup( c->currLoc() ) );
-
- // {_id:1} scan is complete.
- ASSERT( !c->advance() );
- ASSERT( !c->ok() );
-
- // Scan the results again - this time the winning plan has been
- // recorded.
- c = newQueryOptimizerCursor( ns(), BSON( "_id" << GT << 5 << "a" << GT << 5 ) );
- ASSERT( c->ok() );
-
- // _id 10 {_id:1}
- ASSERT_EQUALS( 10, c->current().getIntField( "_id" ) );
- ASSERT( !c->matcher()->matchesCurrent( c.get() ) );
- ASSERT( c->advance() );
-
- // _id 11 {_id:1}
- ASSERT_EQUALS( BSON( "_id" << 11 << "a" << 12 ), c->current() );
- ASSERT( c->matcher()->matchesCurrent( c.get() ) );
- ASSERT( !c->getsetdup( c->currLoc() ) );
- ASSERT( c->advance() );
-
- // _id 12 {_id:1}
- ASSERT_EQUALS( BSON( "_id" << 12 << "a" << 11 ), c->current() );
- ASSERT( c->matcher()->matchesCurrent( c.get() ) );
- ASSERT( !c->getsetdup( c->currLoc() ) );
-
- // {_id:1} scan complete
- ASSERT( !c->advance() );
- ASSERT( !c->ok() );
- }
- };
-
- /** Curr key must be correct for currLoc for correct matching. */
- class ManualMatchingUsingCurrKey : public Base {
- public:
- void run() {
- _cli.insert( ns(), BSON( "_id" << "a" ) );
- _cli.insert( ns(), BSON( "_id" << "b" ) );
- _cli.insert( ns(), BSON( "_id" << "ba" ) );
-
- Lock::GlobalWrite lk;
- Client::Context ctx( ns() );
- shared_ptr< Cursor > c = newQueryOptimizerCursor( ns(), fromjson( "{_id:/a/}" ) );
- ASSERT( c->ok() );
- // "a"
- ASSERT( c->matcher()->matchesCurrent( c.get() ) );
- ASSERT( !c->getsetdup( c->currLoc() ) );
- ASSERT( c->advance() );
- ASSERT( c->ok() );
-
- // "b"
- ASSERT( !c->matcher()->matchesCurrent( c.get() ) );
- ASSERT( c->advance() );
- ASSERT( c->ok() );
-
- // "ba"
- ASSERT( c->matcher()->matchesCurrent( c.get() ) );
- ASSERT( !c->getsetdup( c->currLoc() ) );
- ASSERT( !c->advance() );
- }
- };
-
- /** Test matching and deduping done manually by cursor client. */
- class ManualMatchingDedupingTakeover : public Base {
- public:
- void run() {
- for( int i = 0; i < 150; ++i ) {
- _cli.insert( ns(), BSON( "_id" << i << "a" << 0 ) );
- }
- _cli.insert( ns(), BSON( "_id" << 300 << "a" << 1 ) );
- _cli.ensureIndex( ns(), BSON( "a" << 1 ) );
-
- Lock::GlobalWrite lk;
- Client::Context ctx( ns() );
- shared_ptr< Cursor > c = newQueryOptimizerCursor( ns(), BSON( "$or" << BSON_ARRAY( BSON( "_id" << LT << 300 ) << BSON( "a" << 1 ) ) ) );
- for( int i = 0; i < 151; ++i ) {
- ASSERT( c->ok() );
- ASSERT( c->matcher()->matchesCurrent( c.get() ) );
- ASSERT( !c->getsetdup( c->currLoc() ) );
- c->advance();
- }
- ASSERT( !c->ok() );
- }
- };
-
- /** Test single key matching bounds. */
- class Singlekey : public Base {
- public:
- void run() {
- _cli.insert( ns(), BSON( "a" << "10" ) );
- _cli.ensureIndex( ns(), BSON( "a" << 1 ) );
-
- Lock::GlobalWrite lk;
- Client::Context ctx( ns() );
- shared_ptr< Cursor > c = newQueryOptimizerCursor( ns(), BSON( "a" << GT << 1 << LT << 5 ) );
- // Two sided bounds work.
- ASSERT( !c->ok() );
- }
- };
-
- /** Test multi key matching bounds. */
- class Multikey : public Base {
- public:
- void run() {
- _cli.insert( ns(), BSON( "a" << BSON_ARRAY( 1 << 10 ) ) );
- _cli.ensureIndex( ns(), BSON( "a" << 1 ) );
-
- Lock::GlobalWrite lk;
- Client::Context ctx( ns() );
- setQueryOptimizerCursor( BSON( "a" << GT << 5 << LT << 3 ) );
- // Multi key bounds work.
- ASSERT( ok() );
- }
- };
-
- /** Add other plans when the recorded one is doing more poorly than expected. */
- class AddOtherPlans : public Base {
- public:
- void run() {
- _cli.insert( ns(), BSON( "_id" << 0 << "a" << 0 << "b" << 0 ) );
- _cli.insert( ns(), BSON( "_id" << 1 << "a" << 1 << "b" << 0 ) );
- for( int i = 100; i < 150; ++i ) {
- _cli.insert( ns(), BSON( "_id" << i << "a" << 100 << "b" << i ) );
- }
- _cli.ensureIndex( ns(), BSON( "a" << 1 ) );
- _cli.ensureIndex( ns(), BSON( "b" << 1 ) );
-
- Lock::GlobalWrite lk;
- Client::Context ctx( ns() );
- shared_ptr<Cursor> c = newQueryOptimizerCursor( ns(), BSON( "a" << 0 << "b" << 0 ) );
-
- ASSERT_EQUALS( BSON( "_id" << 0 << "a" << 0 << "b" << 0 ), c->current() );
- ASSERT_EQUALS( BSON( "a" << 1 ), c->indexKeyPattern() );
-
- ASSERT( c->advance() );
- ASSERT_EQUALS( BSON( "_id" << 0 << "a" << 0 << "b" << 0 ), c->current() );
- ASSERT_EQUALS( BSON( "b" << 1 ), c->indexKeyPattern() );
-
- ASSERT( c->advance() );
- ASSERT_EQUALS( BSON( "_id" << 0 << "a" << 0 << "b" << 0 ), c->current() );
- // Unindexed plan
- ASSERT_EQUALS( BSONObj(), c->indexKeyPattern() );
- ASSERT( !c->advance() );
-
- c = newQueryOptimizerCursor( ns(), BSON( "a" << 100 << "b" << 149 ) );
- // Try {a:1}, which was successful previously.
- for( int i = 0; i < 12; ++i ) {
- ASSERT( 149 != c->current().getIntField( "b" ) );
- ASSERT( c->advance() );
- }
- bool sawB1Index = false;
- do {
- if ( c->indexKeyPattern() == BSON( "b" << 1 ) ) {
- ASSERT_EQUALS( 149, c->current().getIntField( "b" ) );
- // We should try the {b:1} index and only see one result from it.
- ASSERT( !sawB1Index );
- sawB1Index = true;
- }
- } while ( c->advance() );
- ASSERT( sawB1Index );
- }
- };
-
- /** Add other plans when the recorded one is doing more poorly than expected, with deletion. */
- class AddOtherPlansDelete : public Base {
- public:
- void run() {
- _cli.insert( ns(), BSON( "_id" << 0 << "a" << 0 << "b" << 0 ) );
- _cli.insert( ns(), BSON( "_id" << 1 << "a" << 1 << "b" << 0 ) );
- for( int i = 100; i < 120; ++i ) {
- _cli.insert( ns(), BSON( "_id" << i << "a" << 100 << "b" << i ) );
- }
- for( int i = 199; i >= 150; --i ) {
- _cli.insert( ns(), BSON( "_id" << i << "a" << 100 << "b" << 150 ) );
- }
- _cli.ensureIndex( ns(), BSON( "a" << 1 ) );
- _cli.ensureIndex( ns(), BSON( "b" << 1 ) );
-
- Lock::GlobalWrite lk;
- Client::Context ctx( ns() );
- shared_ptr<Cursor> c = newQueryOptimizerCursor( ns(), BSON( "a" << 0 << "b" << 0 ) );
-
- ASSERT_EQUALS( BSON( "_id" << 0 << "a" << 0 << "b" << 0 ), c->current() );
- ASSERT_EQUALS( BSON( "a" << 1 ), c->indexKeyPattern() );
-
- ASSERT( c->advance() );
- ASSERT_EQUALS( BSON( "_id" << 0 << "a" << 0 << "b" << 0 ), c->current() );
- ASSERT_EQUALS( BSON( "b" << 1 ), c->indexKeyPattern() );
-
- ASSERT( c->advance() );
- ASSERT_EQUALS( BSON( "_id" << 0 << "a" << 0 << "b" << 0 ), c->current() );
- // Unindexed plan
- ASSERT_EQUALS( BSONObj(), c->indexKeyPattern() );
- ASSERT( !c->advance() );
-
- c = newQueryOptimizerCursor( ns(), BSON( "a" << 100 << "b" << 150 ) );
- // Try {a:1}, which was successful previously.
- for( int i = 0; i < 12; ++i ) {
- ASSERT( 150 != c->current().getIntField( "b" ) );
- ASSERT_EQUALS( BSON( "a" << 1 ), c->indexKeyPattern() );
- ASSERT( c->advance() );
- }
- // Now try {b:1} plan.
- ASSERT_EQUALS( BSON( "b" << 1 ), c->indexKeyPattern() );
- ASSERT_EQUALS( 150, c->current().getIntField( "b" ) );
- ASSERT( c->currentMatches() );
- int id = c->current().getIntField( "_id" );
- c->advance();
- c->prepareToTouchEarlierIterate();
- _cli.remove( ns(), BSON( "_id" << id ) );
- c->recoverFromTouchingEarlierIterate();
- int count = 1;
- while( c->ok() ) {
- if ( c->currentMatches() ) {
- ++count;
- int id = c->current().getIntField( "_id" );
- c->advance();
- c->prepareToTouchEarlierIterate();
- _cli.remove( ns(), BSON( "_id" << id ) );
- c->recoverFromTouchingEarlierIterate();
- }
- else {
- c->advance();
- }
- }
- ASSERT_EQUALS( 50, count );
- }
- };
-
- /**
- * Add other plans when the recorded one is doing more poorly than expected, with deletion before
- * and after adding the additional plans.
- */
- class AddOtherPlansContinuousDelete : public Base {
- public:
- void run() {
- _cli.insert( ns(), BSON( "_id" << 0 << "a" << 0 << "b" << 0 ) );
- _cli.insert( ns(), BSON( "_id" << 1 << "a" << 1 << "b" << 0 ) );
- for( int i = 100; i < 400; ++i ) {
- _cli.insert( ns(), BSON( "_id" << i << "a" << i << "b" << ( 499 - i ) ) );
- }
- _cli.ensureIndex( ns(), BSON( "a" << 1 ) );
- _cli.ensureIndex( ns(), BSON( "b" << 1 ) );
-
- Lock::GlobalWrite lk;
- Client::Context ctx( ns() );
- shared_ptr<Cursor> c = newQueryOptimizerCursor( ns(), BSON( "a" << GTE << -1 << LTE << 0 << "b" << GTE << -1 << LTE << 0 ) );
- while( c->advance() );
- // {a:1} plan should be recorded now.
-
- c = newQueryOptimizerCursor( ns(), BSON( "a" << GTE << 100 << LTE << 400 << "b" << GTE << 100 << LTE << 400 ) );
- int count = 0;
- while( c->ok() ) {
- if ( c->currentMatches() ) {
- ASSERT( !c->getsetdup( c->currLoc() ) );
- ++count;
- int id = c->current().getIntField( "_id" );
- c->advance();
- c->prepareToTouchEarlierIterate();
- _cli.remove( ns(), BSON( "_id" << id ) );
- c->recoverFromTouchingEarlierIterate();
- } else {
- c->advance();
- }
- }
- ASSERT_EQUALS( 300, count );
- ASSERT_EQUALS( 2U, _cli.count( ns(), BSONObj() ) );
- }
- };
-
- /**
- * When an index becomes multikey and ceases to be optimal for a query, attempt other plans
- * quickly.
- */
- class AddOtherPlansWhenOptimalBecomesNonOptimal : public Base {
- public:
- void run() {
- _cli.ensureIndex( ns(), BSON( "a" << 1 << "b" << 1 ) );
-
- {
- // Create a btree cursor on an optimal a:1,b:1 plan.
- Client::ReadContext ctx( ns() );
- shared_ptr<Cursor> cursor = getCursor();
- ASSERT_EQUALS( "BtreeCursor a_1_b_1", cursor->toString() );
-
- // The optimal a:1,b:1 plan is recorded.
- ASSERT_EQUALS( BSON( "a" << 1 << "b" << 1 ),
- cachedIndexForQuery( BSON( "a" << 1 ), BSON( "b" << 1 ) ) );
- }
-
- // Make the a:1,b:1 index multikey.
- _cli.insert( ns(), BSON( "a" << 1 << "b" << BSON_ARRAY( 1 << 2 ) ) );
-
- // Create a QueryOptimizerCursor, without an optimal plan.
- Client::ReadContext ctx( ns() );
- shared_ptr<Cursor> cursor = getCursor();
- ASSERT_EQUALS( "QueryOptimizerCursor", cursor->toString() );
- ASSERT_EQUALS( BSON( "a" << 1 << "b" << 1 ), cursor->indexKeyPattern() );
- ASSERT( cursor->advance() );
- // An alternative plan is quickly attempted.
- ASSERT_EQUALS( BSONObj(), cursor->indexKeyPattern() );
- }
- private:
- static shared_ptr<Cursor> getCursor() {
- // The a:1,b:1 index will be optimal for this query and sort if single key, but if
- // the index is multi key only one of the upper or lower constraints will be applied and
- // the index will not be optimal.
- BSONObj query = BSON( "a" << GTE << 1 << LTE << 1 );
- BSONObj order = BSON( "b" << 1 );
- shared_ptr<ParsedQuery> parsedQuery
- ( new ParsedQuery( ns(), 0, 0, 0,
- BSON( "$query" << query << "$orderby" << order ),
- BSONObj() ) );
- return getOptimizedCursor( ns(),
- query,
- order,
- QueryPlanSelectionPolicy::any(),
- parsedQuery,
- false );
- }
- };
-
- /** Check $or clause range elimination. */
- class OrRangeElimination : public Base {
- public:
- void run() {
- _cli.insert( ns(), BSON( "_id" << 1 ) );
-
- Lock::GlobalWrite lk;
- Client::Context ctx( ns() );
- shared_ptr<Cursor> c = newQueryOptimizerCursor( ns(), BSON( "$or" << BSON_ARRAY( BSON( "_id" << GT << 0 ) << BSON( "_id" << 1 ) ) ) );
- ASSERT( c->ok() );
- ASSERT( !c->advance() );
- }
- };
-
- /** Check $or match deduping - in takeover cursor. */
- class OrDedup : public Base {
- public:
- void run() {
- for( int i = 0; i < 150; ++i ) {
- _cli.insert( ns(), BSON( "_id" << i << "a" << i ) );
- }
- _cli.ensureIndex( ns(), BSON( "a" << 1 ) );
-
- Lock::GlobalWrite lk;
- Client::Context ctx( ns() );
- shared_ptr<Cursor> c = newQueryOptimizerCursor( ns(), BSON( "$or" << BSON_ARRAY( BSON( "_id" << LT << 140 ) << BSON( "_id" << 145 ) << BSON( "a" << 145 ) ) ) );
-
- while( c->current().getIntField( "_id" ) < 140 ) {
- ASSERT( c->advance() );
- }
- // Match from second $or clause.
- ASSERT_EQUALS( 145, c->current().getIntField( "_id" ) );
- ASSERT( c->matcher()->matchesCurrent( c.get() ) );
- ASSERT( c->advance() );
- // Match from third $or clause.
- ASSERT_EQUALS( 145, c->current().getIntField( "_id" ) );
- // $or deduping is handled by the matcher.
- ASSERT( !c->matcher()->matchesCurrent( c.get() ) );
- ASSERT( !c->advance() );
- }
- };
-
- /** Standard dups with a multikey cursor. */
- class EarlyDups : public Base {
- public:
- void run() {
- _cli.insert( ns(), BSON( "a" << BSON_ARRAY( 0 << 1 << 200 ) ) );
- for( int i = 2; i < 150; ++i ) {
- _cli.insert( ns(), BSON( "a" << i ) );
- }
- _cli.ensureIndex( ns(), BSON( "a" << 1 ) );
-
- Lock::GlobalWrite lk;
- Client::Context ctx( ns() );
- setQueryOptimizerCursor( BSON( "a" << GT << -1 ) );
- ASSERT_EQUALS( 149, itcount() );
- }
- };
-
- /** Pop or clause in takeover cursor. */
- class OrPopInTakeover : public Base {
- public:
- void run() {
- for( int i = 0; i < 150; ++i ) {
- _cli.insert( ns(), BSON( "_id" << i ) );
- }
-
- Lock::GlobalWrite lk;
- Client::Context ctx( ns() );
- shared_ptr<Cursor> c = newQueryOptimizerCursor( ns(), BSON( "$or" << BSON_ARRAY( BSON( "_id" << LTE << 147 ) << BSON( "_id" << 148 ) << BSON( "_id" << 149 ) ) ) );
- for( int i = 0; i < 150; ++i ) {
- ASSERT( c->ok() );
- ASSERT_EQUALS( i, c->current().getIntField( "_id" ) );
- c->advance();
- }
- ASSERT( !c->ok() );
- }
- };
-
- /** Or clause iteration abandoned once full collection scan is performed. */
- class OrCollectionScanAbort : public Base {
- public:
- void run() {
- _cli.insert( ns(), BSON( "_id" << 0 << "a" << BSON_ARRAY( 1 << 2 << 3 << 4 << 5 ) << "b" << 4 ) );
- _cli.insert( ns(), BSON( "_id" << 1 << "a" << BSON_ARRAY( 6 << 7 << 8 << 9 << 10 ) << "b" << 4 ) );
- _cli.ensureIndex( ns(), BSON( "a" << 1 ) );
-
- Lock::GlobalWrite lk;
- Client::Context ctx( ns() );
- shared_ptr<Cursor> c = newQueryOptimizerCursor( ns(), BSON( "$or" << BSON_ARRAY( BSON( "a" << LT << 6 << "b" << 4 ) << BSON( "a" << GTE << 6 << "b" << 4 ) ) ) );
-
- ASSERT( c->ok() );
-
- // _id 0 on {a:1}
- ASSERT_EQUALS( 0, c->current().getIntField( "_id" ) );
- ASSERT( c->matcher()->matchesCurrent( c.get() ) );
- ASSERT( !c->getsetdup( c->currLoc() ) );
- c->advance();
-
- // _id 0 on {$natural:1}
- ASSERT_EQUALS( 0, c->current().getIntField( "_id" ) );
- ASSERT( c->matcher()->matchesCurrent( c.get() ) );
- ASSERT( c->getsetdup( c->currLoc() ) );
- c->advance();
-
- // _id 0 on {a:1}
- ASSERT_EQUALS( 0, c->current().getIntField( "_id" ) );
- ASSERT( c->matcher()->matchesCurrent( c.get() ) );
- ASSERT( c->getsetdup( c->currLoc() ) );
- c->advance();
-
- // _id 1 on {$natural:1}
- ASSERT_EQUALS( 1, c->current().getIntField( "_id" ) );
- ASSERT( c->matcher()->matchesCurrent( c.get() ) );
- ASSERT( !c->getsetdup( c->currLoc() ) );
- c->advance();
-
- // _id 0 on {a:1}
- ASSERT_EQUALS( 0, c->current().getIntField( "_id" ) );
- ASSERT( c->matcher()->matchesCurrent( c.get() ) );
- ASSERT( c->getsetdup( c->currLoc() ) );
- c->advance();
-
- // {$natural:1} finished
- ASSERT( !c->ok() );
- }
- };
-
- namespace Yield {
-
- /** Yield cursor and delete current entry, then continue iteration. */
- class NoOp : public Base {
- public:
- void run() {
- _cli.insert( ns(), BSON( "_id" << 1 ) );
- _cli.insert( ns(), BSON( "_id" << 2 ) );
-
- {
- Lock::GlobalWrite lk;
-
- Client::Context ctx( ns() );
- setQueryOptimizerCursor( BSON( "_id" << GT << 0 ) );
- ASSERT_EQUALS( 1, current().getIntField( "_id" ) );
- prepareToYield();
- }
-
- {
- Lock::GlobalWrite lk;
-
- Client::Context ctx( ns() );
- recoverFromYield();
- ASSERT( ok() );
- ASSERT_EQUALS( 2, current().getIntField( "_id" ) );
- ASSERT( !advance() );
- ASSERT( !ok() );
- prepareToYield();
- recoverFromYield();
- }
- }
- };
-
- /** Yield cursor and delete current entry. */
- class Delete : public Base {
- public:
- void run() {
- _cli.insert( ns(), BSON( "_id" << 1 ) );
-
- {
- Lock::GlobalWrite lk;
-
- Client::Context ctx( ns() );
- setQueryOptimizerCursor( BSON( "_id" << 1 ) );
- ASSERT_EQUALS( 1, current().getIntField( "_id" ) );
- prepareToYield();
- }
-
- _cli.remove( ns(), BSON( "_id" << 1 ) );
-
- {
- Lock::GlobalWrite lk;
-
- Client::Context ctx( ns() );
- recoverFromYield();
- ASSERT( !ok() );
- ASSERT( !advance() );
- }
- }
- };
-
- /** Yield cursor and delete current entry, then continue iteration. */
- class DeleteContinue : public Base {
- public:
- void run() {
- _cli.insert( ns(), BSON( "_id" << 1 ) );
- _cli.insert( ns(), BSON( "_id" << 2 ) );
-
- {
- Lock::GlobalWrite lk;
-
- Client::Context ctx( ns() );
- setQueryOptimizerCursor( BSON( "_id" << GT << 0 ) );
- ASSERT_EQUALS( 1, current().getIntField( "_id" ) );
- prepareToYield();
- }
-
- _cli.remove( ns(), BSON( "_id" << 1 ) );
-
- {
- Lock::GlobalWrite lk;
-
- Client::Context ctx( ns() );
- recoverFromYield();
- ASSERT( ok() );
- ASSERT_EQUALS( 2, current().getIntField( "_id" ) );
- ASSERT( !advance() );
- ASSERT( !ok() );
- }
- }
- };
-
- /** Yield cursor and delete current entry, then continue iteration. */
- class DeleteContinueFurther : public Base {
- public:
- void run() {
- _cli.insert( ns(), BSON( "_id" << 1 ) );
- _cli.insert( ns(), BSON( "_id" << 2 ) );
- _cli.insert( ns(), BSON( "_id" << 3 ) );
-
- {
- Lock::GlobalWrite lk;
-
- Client::Context ctx( ns() );
- setQueryOptimizerCursor( BSON( "_id" << GT << 0 ) );
- ASSERT_EQUALS( 1, current().getIntField( "_id" ) );
- prepareToYield();
- }
-
- _cli.remove( ns(), BSON( "_id" << 1 ) );
-
- {
- Lock::GlobalWrite lk;
-
- Client::Context ctx( ns() );
- recoverFromYield();
- ASSERT( ok() );
- ASSERT_EQUALS( 2, current().getIntField( "_id" ) );
- ASSERT( advance() );
- ASSERT_EQUALS( 3, current().getIntField( "_id" ) );
- ASSERT( !advance() );
- ASSERT( !ok() );
- }
- }
- };
-
- /** Yield and update current. */
- class Update : public Base {
- public:
- void run() {
- _cli.insert( ns(), BSON( "a" << 1 ) );
- _cli.insert( ns(), BSON( "a" << 2 ) );
- _cli.ensureIndex( ns(), BSON( "a" << 1 ) );
-
- {
- Lock::GlobalWrite lk;
-
- Client::Context ctx( ns() );
- setQueryOptimizerCursor( BSON( "a" << GT << 0 ) );
- ASSERT_EQUALS( 1, current().getIntField( "a" ) );
- prepareToYield();
- }
-
- _cli.update( ns(), BSON( "a" << 1 ), BSON( "$set" << BSON( "a" << 3 ) ) );
-
- {
- Lock::GlobalWrite lk;
-
- Client::Context ctx( ns() );
- recoverFromYield();
- ASSERT( ok() );
- ASSERT_EQUALS( 2, current().getIntField( "a" ) );
- ASSERT( !advance() );
- ASSERT( !ok() );
- }
- }
- };
-
- /** Yield and drop collection. */
- class Drop : public Base {
- public:
- void run() {
- _cli.insert( ns(), BSON( "_id" << 1 ) );
- _cli.insert( ns(), BSON( "_id" << 2 ) );
-
- {
- Lock::GlobalWrite lk;
-
- Client::Context ctx( ns() );
- setQueryOptimizerCursor( BSON( "_id" << GT << 0 ) );
- ASSERT_EQUALS( 1, current().getIntField( "_id" ) );
- prepareToYield();
- }
-
- _cli.dropCollection( ns() );
-
- {
- Lock::GlobalWrite lk;
-
- Client::Context ctx( ns() );
- ASSERT_THROWS( recoverFromYield(), MsgAssertionException );
- ASSERT( !ok() );
- }
- }
- };
-
- /** Yield and drop collection with $or query. */
- class DropOr : public Base {
- public:
- void run() {
- _cli.insert( ns(), BSON( "_id" << 1 ) );
- _cli.insert( ns(), BSON( "_id" << 2 ) );
-
- {
- Lock::GlobalWrite lk;
-
- Client::Context ctx( ns() );
- setQueryOptimizerCursor( BSON( "$or" << BSON_ARRAY( BSON( "_id" << 1 ) << BSON( "_id" << 2 ) ) ) );
- ASSERT_EQUALS( 1, current().getIntField( "_id" ) );
- prepareToYield();
- }
-
- _cli.dropCollection( ns() );
-
- {
- Lock::GlobalWrite lk;
-
- Client::Context ctx( ns() );
- ASSERT_THROWS( recoverFromYield(), MsgAssertionException );
- ASSERT( !ok() );
- }
- }
- };
-
- /** Yield and remove document with $or query. */
- class RemoveOr : public Base {
- public:
- void run() {
- _cli.insert( ns(), BSON( "_id" << 1 ) );
- _cli.insert( ns(), BSON( "_id" << 2 ) );
-
- {
- Lock::GlobalWrite lk;
-
- Client::Context ctx( ns() );
- setQueryOptimizerCursor( BSON( "$or" << BSON_ARRAY( BSON( "_id" << 1 ) << BSON( "_id" << 2 ) ) ) );
- ASSERT_EQUALS( 1, current().getIntField( "_id" ) );
- prepareToYield();
- }
-
- _cli.remove( ns(), BSON( "_id" << 1 ) );
-
- {
- Lock::GlobalWrite lk;
-
- Client::Context ctx( ns() );
- recoverFromYield();
- ASSERT( ok() );
- ASSERT_EQUALS( 2, current().getIntField( "_id" ) );
- }
- }
- };
-
- /** Yield and overwrite current in capped collection. */
- class CappedOverwrite : public Base {
- public:
- void run() {
- _cli.createCollection( ns(), 1000, true );
- _cli.insert( ns(), BSON( "x" << 1 ) );
-
- {
- Lock::GlobalWrite lk;
-
- Client::Context ctx( ns() );
- setQueryOptimizerCursor( BSON( "x" << GT << 0 ) );
- ASSERT_EQUALS( 1, current().getIntField( "x" ) );
- prepareToYield();
- }
-
- int x = 2;
- while( _cli.count( ns(), BSON( "x" << 1 ) ) > 0 ) {
- _cli.insert( ns(), BSON( "x" << x++ ) );
- }
-
- {
- Lock::GlobalWrite lk;
-
- Client::Context ctx( ns() );
- ASSERT_THROWS( recoverFromYield(), MsgAssertionException );
- ASSERT( !ok() );
- }
- }
- };
-
- /** Yield and drop unrelated index - see SERVER-2454. */
- class DropIndex : public Base {
- public:
- void run() {
- _cli.insert( ns(), BSON( "_id" << 1 ) );
- _cli.ensureIndex( ns(), BSON( "a" << 1 ) );
-
- {
- Lock::GlobalWrite lk;
-
- Client::Context ctx( ns() );
- setQueryOptimizerCursor( BSON( "_id" << 1 ) );
- ASSERT_EQUALS( 1, current().getIntField( "_id" ) );
- prepareToYield();
- }
-
- _cli.dropIndex( ns(), BSON( "a" << 1 ) );
-
- {
- Lock::GlobalWrite lk;
-
- Client::Context ctx( ns() );
- ASSERT_THROWS( recoverFromYield(), MsgAssertionException );
- ASSERT( !ok() );
- }
- }
- };
-
- /** Yielding with multiple plans active. */
- class MultiplePlansNoOp : public Base {
- public:
- void run() {
- _cli.insert( ns(), BSON( "_id" << 1 << "a" << 2 ) );
- _cli.insert( ns(), BSON( "_id" << 2 << "a" << 1 ) );
- _cli.ensureIndex( ns(), BSON( "a" << 1 ) );
-
- {
- Lock::GlobalWrite lk;
-
- Client::Context ctx( ns() );
- setQueryOptimizerCursor( BSON( "_id" << GT << 0 << "a" << GT << 0 ) );
- ASSERT_EQUALS( 1, current().getIntField( "_id" ) );
- prepareToYield();
- }
-
- {
- Lock::GlobalWrite lk;
-
- Client::Context ctx( ns() );
- recoverFromYield();
- ASSERT( ok() );
- ASSERT_EQUALS( 2, current().getIntField( "_id" ) );
- ASSERT( !advance() );
- ASSERT( !ok() );
- }
- }
- };
-
- /** Yielding with advance and multiple plans active. */
- class MultiplePlansAdvanceNoOp : public Base {
- public:
- void run() {
- _cli.insert( ns(), BSON( "_id" << 1 << "a" << 2 ) );
- _cli.insert( ns(), BSON( "_id" << 2 << "a" << 1 ) );
- _cli.insert( ns(), BSON( "_id" << 3 << "a" << 3 ) );
- _cli.ensureIndex( ns(), BSON( "a" << 1 ) );
-
- {
- Lock::GlobalWrite lk;
-
- Client::Context ctx( ns() );
- setQueryOptimizerCursor( BSON( "_id" << GT << 0 << "a" << GT << 0 ) );
- ASSERT_EQUALS( 1, current().getIntField( "_id" ) );
- advance();
- ASSERT_EQUALS( 2, current().getIntField( "_id" ) );
- prepareToYield();
- }
-
- {
- Lock::GlobalWrite lk;
-
- Client::Context ctx( ns() );
- recoverFromYield();
- ASSERT( ok() );
- ASSERT_EQUALS( 3, current().getIntField( "_id" ) );
- ASSERT( !advance() );
- ASSERT( !ok() );
- }
- }
- };
-
- /** Yielding with delete and multiple plans active. */
- class MultiplePlansDelete : public Base {
- public:
- void run() {
- _cli.insert( ns(), BSON( "_id" << 1 << "a" << 2 ) );
- _cli.insert( ns(), BSON( "_id" << 2 << "a" << 1 ) );
- _cli.insert( ns(), BSON( "_id" << 3 << "a" << 4 ) );
- _cli.insert( ns(), BSON( "_id" << 4 << "a" << 3 ) );
- _cli.ensureIndex( ns(), BSON( "a" << 1 ) );
-
- {
- Lock::GlobalWrite lk;
-
- Client::Context ctx( ns() );
- setQueryOptimizerCursor( BSON( "_id" << GT << 0 << "a" << GT << 0 ) );
- ASSERT_EQUALS( 1, current().getIntField( "_id" ) );
- advance();
- ASSERT_EQUALS( 2, current().getIntField( "_id" ) );
- prepareToYield();
- }
-
- _cli.remove( ns(), BSON( "_id" << 2 ) );
-
- {
- Lock::GlobalWrite lk;
-
- Client::Context ctx( ns() );
- c()->recoverFromYield();
- ASSERT( ok() );
- // index {a:1} active during yield
- ASSERT_EQUALS( 1, current().getIntField( "_id" ) );
- ASSERT( advance() );
- ASSERT_EQUALS( 3, current().getIntField( "_id" ) );
- ASSERT( advance() );
- ASSERT_EQUALS( 4, current().getIntField( "_id" ) );
- ASSERT( !advance() );
- ASSERT( !ok() );
- }
- }
- };
-
- /** Yielding with delete, multiple plans active, and $or clause. */
- class MultiplePlansDeleteOr : public Base {
- public:
- void run() {
- _cli.insert( ns(), BSON( "_id" << 1 << "a" << 2 ) );
- _cli.insert( ns(), BSON( "_id" << 2 << "a" << 1 ) );
- _cli.ensureIndex( ns(), BSON( "a" << 1 ) );
-
- {
- Lock::GlobalWrite lk;
-
- Client::Context ctx( ns() );
- setQueryOptimizerCursor( BSON( "$or" << BSON_ARRAY( BSON( "_id" << 1 << "a" << 2 ) << BSON( "_id" << 2 << "a" << 1 ) ) ) );
- ASSERT_EQUALS( 1, current().getIntField( "_id" ) );
- prepareToYield();
- }
-
- _cli.remove( ns(), BSON( "_id" << 1 ) );
-
- {
- Lock::GlobalWrite lk;
-
- Client::Context ctx( ns() );
- c()->recoverFromYield();
- ASSERT( ok() );
- ASSERT_EQUALS( 2, current().getIntField( "_id" ) );
- ASSERT( !advance() );
- ASSERT( !ok() );
- }
- }
- };
-
- /** Yielding with delete, multiple plans active with advancement to the second, and $or clause. */
- class MultiplePlansDeleteOrAdvance : public Base {
- public:
- void run() {
- _cli.insert( ns(), BSON( "_id" << 1 << "a" << 2 ) );
- _cli.insert( ns(), BSON( "_id" << 2 << "a" << 1 ) );
- _cli.ensureIndex( ns(), BSON( "a" << 1 ) );
-
- {
- Lock::GlobalWrite lk;
-
- Client::Context ctx( ns() );
- setQueryOptimizerCursor( BSON( "$or" << BSON_ARRAY( BSON( "_id" << 1 << "a" << 2 ) << BSON( "_id" << 2 << "a" << 1 ) ) ) );
- ASSERT_EQUALS( 1, current().getIntField( "_id" ) );
- prepareToYield();
- c()->advance();
- ASSERT_EQUALS( 1, current().getIntField( "_id" ) );
- }
-
- _cli.remove( ns(), BSON( "_id" << 1 ) );
-
- {
- Lock::GlobalWrite lk;
-
- Client::Context ctx( ns() );
- c()->recoverFromYield();
- ASSERT( ok() );
- ASSERT_EQUALS( 2, current().getIntField( "_id" ) );
- ASSERT( !advance() );
- ASSERT( !ok() );
- }
- }
- };
-
- /** Yielding with multiple plans and capped overwrite. */
- class MultiplePlansCappedOverwrite : public Base {
- public:
- void run() {
- _cli.createCollection( ns(), 1000, true );
- _cli.insert( ns(), BSON( "_id" << 1 << "a" << 1 ) );
- _cli.ensureIndex( ns(), BSON( "_id" << 1 ) );
-
- {
- Lock::GlobalWrite lk;
-
- Client::Context ctx( ns() );
- setQueryOptimizerCursor( BSON( "_id" << GT << 0 << "a" << GT << 0 ) );
- ASSERT_EQUALS( 1, current().getIntField( "_id" ) );
- prepareToYield();
- }
-
- int i = 1;
- while( _cli.count( ns(), BSON( "_id" << 1 ) ) > 0 ) {
- ++i;
- _cli.insert( ns(), BSON( "_id" << i << "a" << i ) );
- }
-
- {
- Lock::GlobalWrite lk;
-
- Client::Context ctx( ns() );
- recoverFromYield();
- ASSERT( ok() );
- // {$natural:1} plan does not recover, {_id:1} plan does.
- ASSERT( 1 < current().getIntField( "_id" ) );
- }
- }
- };
-
- /**
- * Yielding with multiple plans and capped overwrite with unrecoverable cursor
- * active at time of yield.
- */
- class MultiplePlansCappedOverwriteManual : public Base {
- public:
- void run() {
- _cli.createCollection( ns(), 1000, true );
- _cli.insert( ns(), BSON( "a" << 1 << "b" << 1 ) );
- _cli.ensureIndex( ns(), BSON( "a" << 1 ) );
-
- shared_ptr<Cursor> c;
- {
- Lock::GlobalWrite lk;
-
- Client::Context ctx( ns() );
- c = newQueryOptimizerCursor( ns(), BSON( "a" << GT << 0 << "b" << GT << 0 ) );
- ASSERT_EQUALS( 1, c->current().getIntField( "a" ) );
- ASSERT( !c->getsetdup( c->currLoc() ) );
- c->advance();
- ASSERT_EQUALS( 1, c->current().getIntField( "a" ) );
- ASSERT( c->getsetdup( c->currLoc() ) );
- c->prepareToYield();
- }
-
- int i = 1;
- while( _cli.count( ns(), BSON( "a" << 1 ) ) > 0 ) {
- ++i;
- _cli.insert( ns(), BSON( "a" << i << "b" << i ) );
- }
-
- {
- Lock::GlobalWrite lk;
-
- Client::Context ctx( ns() );
- c->recoverFromYield();
- ASSERT( c->ok() );
- // {$natural:1} plan does not recover, {_id:1} plan does.
- ASSERT( 1 < c->current().getIntField( "a" ) );
- }
- }
- };
-
- /**
- * Yielding with multiple plans and capped overwrite with unrecoverable cursor
- * inctive at time of yield.
- */
- class MultiplePlansCappedOverwriteManual2 : public Base {
- public:
- void run() {
- _cli.createCollection( ns(), 1000, true );
- _cli.insert( ns(), BSON( "_id" << 1 << "a" << 1 ) );
- _cli.ensureIndex( ns(), BSON( "_id" << 1 ) );
-
- shared_ptr<Cursor> c;
- {
- Lock::GlobalWrite lk;
-
- Client::Context ctx( ns() );
- c = newQueryOptimizerCursor( ns(), BSON( "_id" << GT << 0 << "a" << GT << 0 ) );
- ASSERT_EQUALS( 1, c->current().getIntField( "_id" ) );
- ASSERT( !c->getsetdup( c->currLoc() ) );
- c->prepareToYield();
- }
-
- int n = 1;
- while( _cli.count( ns(), BSON( "_id" << 1 ) ) > 0 ) {
- ++n;
- _cli.insert( ns(), BSON( "_id" << n << "a" << n ) );
- }
-
- {
- Lock::GlobalWrite lk;
-
- Client::Context ctx( ns() );
- c->recoverFromYield();
- ASSERT( c->ok() );
- // {$natural:1} plan does not recover, {_id:1} plan does.
- ASSERT( 1 < c->current().getIntField( "_id" ) );
- ASSERT( !c->getsetdup( c->currLoc() ) );
- int i = c->current().getIntField( "_id" );
- ASSERT( c->advance() );
- ASSERT( c->getsetdup( c->currLoc() ) );
- while( i < n ) {
- ASSERT( c->advance() );
- ++i;
- ASSERT_EQUALS( i, c->current().getIntField( "_id" ) );
- }
- }
- }
- };
-
- /** Yield with takeover cursor. */
- class Takeover : public Base {
- public:
- void run() {
- for( int i = 0; i < 150; ++i ) {
- _cli.insert( ns(), BSON( "_id" << i << "a" << i ) );
- }
- _cli.ensureIndex( ns(), BSON( "a" << 1 ) );
-
- {
- Lock::GlobalWrite lk;
-
- Client::Context ctx( ns() );
- setQueryOptimizerCursor( BSON( "_id" << GTE << 0 << "a" << GTE << 0 ) );
- for( int i = 0; i < 120; ++i ) {
- ASSERT( advance() );
- }
- ASSERT( ok() );
- ASSERT_EQUALS( 120, current().getIntField( "_id" ) );
- prepareToYield();
- }
-
- _cli.remove( ns(), BSON( "_id" << 120 ) );
-
- {
- Lock::GlobalWrite lk;
-
- Client::Context ctx( ns() );
- recoverFromYield();
- ASSERT( ok() );
- ASSERT_EQUALS( 121, current().getIntField( "_id" ) );
- ASSERT( advance() );
- ASSERT_EQUALS( 122, current().getIntField( "_id" ) );
- }
- }
- };
-
- /** Yield with BasicCursor takeover cursor. */
- class TakeoverBasic : public Base {
- public:
- void run() {
- for( int i = 0; i < 150; ++i ) {
- _cli.insert( ns(), BSON( "_id" << i << "a" << BSON_ARRAY( i << i+1 ) ) );
- }
- _cli.ensureIndex( ns(), BSON( "a" << 1 ) );
-
- auto_ptr<ClientCursor> cc;
- auto_ptr<ClientCursor::YieldData> data( new ClientCursor::YieldData() );
- {
- Lock::GlobalWrite lk;
-
- Client::Context ctx( ns() );
- setQueryOptimizerCursor( BSON( "b" << NE << 0 << "a" << GTE << 0 ) );
- cc.reset( new ClientCursor( QueryOption_NoCursorTimeout, c(), ns() ) );
- for( int i = 0; i < 120; ++i ) {
- ASSERT( advance() );
- }
- ASSERT( ok() );
- ASSERT_EQUALS( 120, current().getIntField( "_id" ) );
- cc->prepareToYield( *data );
- }
- _cli.remove( ns(), BSON( "_id" << 120 ) );
-
- {
- Lock::GlobalWrite lk;
-
- Client::Context ctx( ns() );
- ASSERT( ClientCursor::recoverFromYield( *data ) );
- ASSERT( ok() );
- ASSERT_EQUALS( 121, current().getIntField( "_id" ) );
- ASSERT( advance() );
- ASSERT_EQUALS( 122, current().getIntField( "_id" ) );
- }
- }
- };
-
- /** Yield with advance of inactive cursor. */
- class InactiveCursorAdvance : public Base {
- public:
- void run() {
- for( int i = 0; i < 10; ++i ) {
- _cli.insert( ns(), BSON( "_id" << i << "a" << 10 - i ) );
- }
- _cli.ensureIndex( ns(), BSON( "a" << 1 ) );
-
- {
- Lock::GlobalWrite lk;
-
- Client::Context ctx( ns() );
- setQueryOptimizerCursor( BSON( "_id" << GT << 0 << "a" << GT << 0 ) );
- ASSERT( ok() );
- ASSERT_EQUALS( 1, current().getIntField( "_id" ) );
- ASSERT( advance() );
- ASSERT_EQUALS( 9, current().getIntField( "_id" ) );
- ASSERT( advance() );
- ASSERT_EQUALS( 2, current().getIntField( "_id" ) );
- prepareToYield();
- }
-
- _cli.remove( ns(), BSON( "_id" << 9 ) );
-
- {
- Lock::GlobalWrite lk;
-
- Client::Context ctx( ns() );
- recoverFromYield();
- ASSERT( ok() );
- ASSERT_EQUALS( 8, current().getIntField( "_id" ) );
- ASSERT( advance() );
- ASSERT_EQUALS( 3, current().getIntField( "_id" ) );
- ASSERT( advance() );
- ASSERT_EQUALS( 7, current().getIntField( "_id" ) );
- }
- }
- };
-
- /**
- * This test, and every other test that assumes that you get documents back in the order you
- * inserted them in, is unreliable.
- *
- * Documents in an index are ordered by (extracted keypattern fields in doc, diskloc). We
- * use the diskloc to break ties. In this test, all the "extracted keypattern fields in
- * doc" values are the same, so the documents are in fact ordered in the Btree by their
- * diskloc.
- *
- * This test expects to retrieve docs in the order in which it inserts them. To get the
- * docs back in the order you insert them in, DiskLoc_of_insert_N < DiskLoc_of_insert_N+1
- * must always hold. This just happens to be true "by default" (when you run ./test) but
- * it's not true in general. So, this test (and other similar tests) will fail if you run a
- * subset of the dbtests, or really anything that modifies the free list such that the
- * DiskLocs are not strictly sequential.
- */
- class TakeoverUpdateBase : public Base {
- public:
- TakeoverUpdateBase() :
- _lastId( -1 ) {
- populateWithKey( "a" );
- populateWithKey( "b" );
- _cli.ensureIndex( ns(), BSON( "c" << 1 ) );
- }
- virtual ~TakeoverUpdateBase() {}
- void run() {
- advanceToEndOfARangeAndUpdate();
- advanceThroughBRange();
- }
- protected:
- virtual BSONObj query() const = 0;
- void advanceToEndOfARangeAndUpdate() {
- {
- Lock::GlobalWrite lk;
-
- Client::Context ctx( ns() );
- setQueryOptimizerCursor( query() );
- for( int i = 0; i < 149; ++i ) {
- advance();
- }
- ASSERT( ok() );
- // The current iterate corresponds to the last 'a' document.
- ASSERT_EQUALS( BSON( "_id" << 149 << "a" << 1 ), current() );
- ASSERT_EQUALS( BSON( "a" << 1 ), c()->indexKeyPattern() );
- prepareToYield();
- }
-
- _cli.update( ns(), BSON( "_id" << 149 ), BSON( "$set" << BSON( "a" << -1 ) ) );
- }
- void advanceThroughBRange() {
- Lock::GlobalWrite lk;
-
- Client::Context ctx( ns() );
- recoverFromYield();
- ASSERT( ok() );
- // The current iterate corresponds to the first 'b' document.
- ASSERT_EQUALS( BSON( "_id" << 150 << "b" << 1 ), current() );
- ASSERT_EQUALS( BSON( "b" << 1 ), c()->indexKeyPattern() );
- // Eventually the last 'b' document is reached.
- while( current() != BSON( "_id" << 299 << "b" << 1 ) ) {
- ASSERT( advance() );
- }
- ASSERT( !advance() );
- }
- private:
- void populateWithKey( const string &key ) {
- for( int i = 0; i < 150; ++i ) {
- _cli.insert( ns(), BSON( "_id" << nextId() << key << 1 ) );
- }
- _cli.ensureIndex( ns(), BSON( key << 1 ) );
- }
- int nextId() {
- return ++_lastId;
- }
- int _lastId;
- };
-
- /** An update causing an index key change advances the cursor past the last iterate. */
- class TakeoverUpdateKeyAtEndOfIteration : public TakeoverUpdateBase {
- public:
- virtual BSONObj query() const { return BSON( "a" << 1 ); }
- void run() {
- advanceToEndOfARangeAndUpdate();
-
- {
- Lock::GlobalWrite lk;
-
- Client::Context ctx( ns() );
- recoverFromYield();
- ASSERT( !ok() );
- }
- }
- };
-
- /**
- * An update causing an index key change advances the cursor past the last iterate of a $or
- * clause.
- */
- class TakeoverUpdateKeyAtEndOfClause : public TakeoverUpdateBase {
- virtual BSONObj query() const { return fromjson( "{$or:[{a:1},{b:1}]}" ); }
- };
-
- /**
- * An update causing an index key change advances the cursor past the last iterate of a $or
- * clause, and also past an empty clause.
- */
- class TakeoverUpdateKeyPrecedingEmptyClause : public TakeoverUpdateBase {
- virtual BSONObj query() const { return fromjson( "{$or:[{a:1},{c:1},{b:1}]}" ); }
- };
-
- } // namespace Yield
-
- class OrderId : public Base {
- public:
- void run() {
- for( int i = 0; i < 10; ++i ) {
- _cli.insert( ns(), BSON( "_id" << i ) );
- }
-
- Lock::GlobalWrite lk;
- Client::Context ctx( ns() );
- setQueryOptimizerCursor( BSONObj(), BSON( "_id" << 1 ) );
-
- for( int i = 0; i < 10; ++i, advance() ) {
- ASSERT( ok() );
- ASSERT_EQUALS( i, current().getIntField( "_id" ) );
- }
- }
- };
-
- class OrderMultiIndex : public Base {
- public:
- void run() {
- for( int i = 0; i < 10; ++i ) {
- _cli.insert( ns(), BSON( "_id" << i << "a" << 1 ) );
- }
- _cli.ensureIndex( ns(), BSON( "_id" << 1 << "a" << 1 ) );
-
- Lock::GlobalWrite lk;
- Client::Context ctx( ns() );
- setQueryOptimizerCursor( BSON( "_id" << GTE << 0 << "a" << GTE << 0 ), BSON( "_id" << 1 ) );
-
- for( int i = 0; i < 10; ++i, advance() ) {
- ASSERT( ok() );
- ASSERT_EQUALS( i, current().getIntField( "_id" ) );
- }
- }
- };
-
- class OrderReject : public Base {
- public:
- void run() {
- for( int i = 0; i < 10; ++i ) {
- _cli.insert( ns(), BSON( "_id" << i << "a" << i % 5 ) );
- }
- _cli.ensureIndex( ns(), BSON( "a" << 1 ) );
-
- Lock::GlobalWrite lk;
- Client::Context ctx( ns() );
- setQueryOptimizerCursor( BSON( "a" << GTE << 3 ), BSON( "_id" << 1 ) );
-
- ASSERT( ok() );
- ASSERT_EQUALS( 3, current().getIntField( "_id" ) );
- ASSERT( advance() );
- ASSERT_EQUALS( 4, current().getIntField( "_id" ) );
- ASSERT( advance() );
- ASSERT_EQUALS( 8, current().getIntField( "_id" ) );
- ASSERT( advance() );
- ASSERT_EQUALS( 9, current().getIntField( "_id" ) );
- ASSERT( !advance() );
- }
- };
-
- class OrderNatural : public Base {
- public:
- void run() {
- _cli.insert( ns(), BSON( "_id" << 5 ) );
- _cli.insert( ns(), BSON( "_id" << 4 ) );
- _cli.insert( ns(), BSON( "_id" << 6 ) );
- _cli.ensureIndex( ns(), BSON( "a" << 1 ) );
-
- Lock::GlobalWrite lk;
- Client::Context ctx( ns() );
- setQueryOptimizerCursor( BSON( "_id" << GT << 0 ), BSON( "$natural" << 1 ) );
-
- ASSERT( ok() );
- ASSERT_EQUALS( 5, current().getIntField( "_id" ) );
- ASSERT( advance() );
- ASSERT_EQUALS( 4, current().getIntField( "_id" ) );
- ASSERT( advance() );
- ASSERT_EQUALS( 6, current().getIntField( "_id" ) );
- ASSERT( !advance() );
- }
- };
-
- class OrderUnindexed : public Base {
- public:
- void run() {
- Lock::GlobalWrite lk;
- Client::Context ctx( ns() );
- ASSERT( !newQueryOptimizerCursor( ns(), BSONObj(), BSON( "a" << 1 ) ).get() );
- }
- };
-
- class RecordedOrderInvalid : public Base {
- public:
- void run() {
- _cli.insert( ns(), BSON( "a" << 1 << "b" << 1 ) );
- _cli.insert( ns(), BSON( "a" << 2 << "b" << 2 ) );
- _cli.insert( ns(), BSON( "a" << 3 << "b" << 3 ) );
- _cli.ensureIndex( ns(), BSON( "a" << 1 ) );
- _cli.ensureIndex( ns(), BSON( "b" << 1 ) );
- // Plan {a:1} will be chosen and recorded.
- ASSERT( _cli.query( ns(), QUERY( "a" << 2 ).sort( "b" ) )->more() );
-
- Lock::GlobalWrite lk;
- Client::Context ctx( ns() );
- shared_ptr<Cursor> c = newQueryOptimizerCursor( ns(), BSON( "a" << 2 ),
- BSON( "b" << 1 ) );
- // Check that we are scanning {b:1} not {a:1}, since {a:1} is not properly ordered.
- for( int i = 0; i < 3; ++i ) {
- ASSERT( c->ok() );
- c->advance();
- }
- ASSERT( !c->ok() );
- }
- };
-
- class KillOp : public Base {
- public:
- void run() {
- _cli.insert( ns(), BSON( "_id" << 1 << "b" << 1 ) );
- _cli.insert( ns(), BSON( "_id" << 2 << "b" << 2 ) );
- _cli.ensureIndex( ns(), BSON( "b" << 1 ) );
-
- Client::ReadContext ctx( ns() );
- setQueryOptimizerCursor( BSON( "_id" << GT << 0 << "b" << GT << 0 ) );
- ASSERT( ok() );
- cc().curop()->kill();
- // First advance() call throws, subsequent calls just fail.
- ASSERT_THROWS( advance(), MsgAssertionException );
- ASSERT( !advance() );
- }
- };
-
- class KillOpFirstClause : public Base {
- public:
- void run() {
- _cli.insert( ns(), BSON( "_id" << 1 << "b" << 1 ) );
- _cli.insert( ns(), BSON( "_id" << 2 << "b" << 2 ) );
- _cli.ensureIndex( ns(), BSON( "b" << 1 ) );
-
- Client::ReadContext ctx( ns() );
- shared_ptr<Cursor> c = newQueryOptimizerCursor( ns(), BSON( "$or" << BSON_ARRAY( BSON( "_id" << GT << 0 ) << BSON( "b" << GT << 0 ) ) ) );
- ASSERT( c->ok() );
- cc().curop()->kill();
- // First advance() call throws, subsequent calls just fail.
- ASSERT_THROWS( c->advance(), MsgAssertionException );
- ASSERT( !c->advance() );
- }
- };
-
- class Nscanned : public Base {
- public:
- void run() {
- for( int i = 0; i < 120; ++i ) {
- _cli.insert( ns(), BSON( "_id" << i << "a" << i ) );
- }
-
- Lock::GlobalWrite lk;
- Client::Context ctx( ns() );
- shared_ptr<Cursor> c = newQueryOptimizerCursor( ns(), BSON( "_id" << GTE << 0 << "a" << GTE << 0 ) );
- ASSERT( c->ok() );
- ASSERT_EQUALS( 2, c->nscanned() );
- c->advance();
- ASSERT( c->ok() );
- ASSERT_EQUALS( 2, c->nscanned() );
- c->advance();
- for( int i = 3; i < 222; ++i ) {
- ASSERT( c->ok() );
- c->advance();
- }
- ASSERT( !c->ok() );
- }
- };
-
- namespace TouchEarlierIterate {
-
- /* Test 'touching earlier iterate' without doc modifications. */
- class Basic : public Base {
- public:
- void run() {
- _cli.insert( ns(), BSON( "_id" << 1 << "b" << 1 ) );
- _cli.insert( ns(), BSON( "_id" << 2 << "b" << 2 ) );
- _cli.ensureIndex( ns(), BSON( "b" << 1 ) );
-
- Client::ReadContext ctx( ns() );
- shared_ptr<Cursor> c = newQueryOptimizerCursor( ns(), BSON( "_id" << GT << 0 << "b" << GT << 0 ) );
-
- ASSERT( c->ok() );
- while( c->ok() ) {
- DiskLoc loc = c->currLoc();
- BSONObj obj = c->current();
- c->prepareToTouchEarlierIterate();
- c->recoverFromTouchingEarlierIterate();
- ASSERT( loc == c->currLoc() );
- ASSERT_EQUALS( obj, c->current() );
- c->advance();
- }
- }
- };
-
- /* Test 'touching earlier iterate' with doc modifications. */
- class Delete : public Base {
- public:
- void run() {
- _cli.insert( ns(), BSON( "_id" << 1 << "b" << 1 ) );
- _cli.insert( ns(), BSON( "_id" << 2 << "b" << 2 ) );
- _cli.ensureIndex( ns(), BSON( "b" << 1 ) );
-
- DiskLoc firstLoc;
- Lock::GlobalWrite lk;
-
- Client::Context ctx( ns() );
- setQueryOptimizerCursor( BSON( "_id" << GT << 0 << "b" << GT << 0 ) );
- ASSERT( ok() );
- firstLoc = currLoc();
- ASSERT( c()->advance() );
- prepareToTouchEarlierIterate();
-
- _cli.remove( ns(), BSON( "_id" << 1 ), true );
-
- recoverFromTouchingEarlierIterate();
- ASSERT( ok() );
- while( ok() ) {
- ASSERT( firstLoc != currLoc() );
- c()->advance();
- }
- }
- };
-
- /* Test 'touch earlier iterate' with several doc modifications. */
- class DeleteMultiple : public Base {
- public:
- void run() {
- for( int i = 1; i < 10; ++i ) {
- _cli.insert( ns(), BSON( "_id" << i << "b" << i ) );
- }
- _cli.ensureIndex( ns(), BSON( "b" << 1 ) );
-
- set<DiskLoc> deleted;
- int id = 0;
- Lock::GlobalWrite lk;
-
- Client::Context ctx( ns() );
- setQueryOptimizerCursor( BSON( "_id" << GT << 0 << "b" << GT << 0 ) );
- while( 1 ) {
- if ( !ok() ) {
- break;
- }
- ASSERT( deleted.count( currLoc() ) == 0 );
- id = current()["_id"].Int();
- deleted.insert( currLoc() );
- c()->advance();
- prepareToTouchEarlierIterate();
-
- _cli.remove( ns(), BSON( "_id" << id ), true );
-
- recoverFromTouchingEarlierIterate();
- }
- ASSERT_EQUALS( 9U, deleted.size() );
- }
- };
-
- /* Test 'touch earlier iterate' after an earlier yield. */
- class DeleteAfterYield : public Base {
- public:
- void run() {
- for( int i = 0; i < 3; ++i ) {
- _cli.insert( ns(), BSON( "b" << i ) );
- }
-
- Lock::GlobalWrite lk;
-
- Client::Context ctx( ns() );
- setQueryOptimizerCursor( BSONObj() );
- ASSERT( ok() );
- ASSERT_EQUALS( 0, c()->current()[ "b" ].Int() );
-
- // Record the position of document b:0 in the cursor's component ClientCursor.
- c()->prepareToYield();
- c()->recoverFromYield();
- ASSERT( ok() );
-
- // Advance the cursor past document b:0.
- ASSERT( c()->advance() );
- ASSERT_EQUALS( 1, current()[ "b" ].Int() );
-
- // Remove document b:0.
- c()->prepareToTouchEarlierIterate();
- // A warning message will be logged for the component ClientCursor if it is not
- // configured with 'doing deletes'.
- _cli.remove( ns(), BSON( "b" << 0 ), true );
- c()->recoverFromTouchingEarlierIterate();
-
- // Check that the cursor recovers properly after b:0 is deleted.
- ASSERT( ok() );
- ASSERT_EQUALS( 1, current()[ "b" ].Int() );
- ASSERT( c()->advance() );
- ASSERT_EQUALS( 2, current()[ "b" ].Int() );
- ASSERT( !c()->advance() );
- }
- };
-
- /* Test 'touch earlier iterate' with takeover. */
- class Takeover : public Base {
- public:
- void run() {
- for( int i = 1; i < 600; ++i ) {
- _cli.insert( ns(), BSON( "_id" << i << "b" << i ) );
- }
- _cli.ensureIndex( ns(), BSON( "b" << 1 ) );
-
- Client::ReadContext ctx( ns() );
- setQueryOptimizerCursor( BSON( "_id" << GT << 0 << "b" << GT << 0 ) );
-
- ASSERT( ok() );
- int count = 1;
- while( ok() ) {
- DiskLoc loc = currLoc();
- BSONObj obj = current();
- prepareToTouchEarlierIterate();
- recoverFromTouchingEarlierIterate();
- ASSERT( loc == currLoc() );
- ASSERT_EQUALS( obj, current() );
- count += mayReturnCurrent();
- c()->advance();
- }
- ASSERT_EQUALS( 599, count );
- }
- };
-
- /* Test 'touch earlier iterate' with takeover and deletes. */
- class TakeoverDeleteMultiple : public Base {
- public:
- void run() {
- for( int i = 1; i < 600; ++i ) {
- _cli.insert( ns(), BSON( "_id" << i << "b" << i ) );
- }
- _cli.ensureIndex( ns(), BSON( "b" << 1 ) );
-
- set<DiskLoc> deleted;
- int id = 0;
-
- Lock::GlobalWrite lk;
-
- Client::Context ctx( ns() );
- setQueryOptimizerCursorWithoutAdvancing( BSON( "_id" << GT << 0 << "b" << GT << 0 ) );
- while( 1 ) {
- if ( !ok() ) {
- break;
- }
- ASSERT( deleted.count( currLoc() ) == 0 );
- id = current()["_id"].Int();
- ASSERT( c()->currentMatches() );
- ASSERT( !c()->getsetdup( currLoc() ) );
- deleted.insert( currLoc() );
- c()->advance();
- prepareToTouchEarlierIterate();
-
- _cli.remove( ns(), BSON( "_id" << id ), true );
-
- recoverFromTouchingEarlierIterate();
- }
- ASSERT_EQUALS( 599U, deleted.size() );
- }
- };
-
- /* Test 'touch earlier iterate' with unindexed cursor takeover and deletes. */
- class UnindexedTakeoverDeleteMultiple : public Base {
- public:
- void run() {
- for( int i = 1; i < 600; ++i ) {
- _cli.insert( ns(), BSON( "a" << BSON_ARRAY( i << i+1 ) << "b" << BSON_ARRAY( i << i+1 ) << "_id" << i ) );
- }
- _cli.ensureIndex( ns(), BSON( "a" << 1 ) );
- _cli.ensureIndex( ns(), BSON( "b" << 1 ) );
-
- set<DiskLoc> deleted;
- int id = 0;
-
- Lock::GlobalWrite lk;
-
- Client::Context ctx( ns() );
- setQueryOptimizerCursorWithoutAdvancing( BSON( "a" << GT << 0 << "b" << GT << 0 ) );
- while( 1 ) {
- if ( !ok() ) {
- break;
- }
- ASSERT( deleted.count( currLoc() ) == 0 );
- id = current()["_id"].Int();
- ASSERT( c()->currentMatches() );
- ASSERT( !c()->getsetdup( currLoc() ) );
- deleted.insert( currLoc() );
- // Advance past the document before deleting it.
- DiskLoc loc = currLoc();
- while( ok() && loc == currLoc() ) {
- c()->advance();
- }
- prepareToTouchEarlierIterate();
-
- _cli.remove( ns(), BSON( "_id" << id ), true );
-
- recoverFromTouchingEarlierIterate();
- }
- ASSERT_EQUALS( 599U, deleted.size() );
- }
- };
-
- /* Test 'touch earlier iterate' with takeover and deletes, with multiple advances in a row. */
- class TakeoverDeleteMultipleMultiAdvance : public Base {
- public:
- void run() {
- for( int i = 1; i < 600; ++i ) {
- _cli.insert( ns(), BSON( "_id" << i << "b" << i ) );
- }
- _cli.ensureIndex( ns(), BSON( "b" << 1 ) );
-
- set<DiskLoc> deleted;
- int id = 0;
-
- Lock::GlobalWrite lk;
-
- Client::Context ctx( ns() );
- setQueryOptimizerCursor( BSON( "_id" << GT << 0 << "b" << GT << 0 ) );
- while( 1 ) {
- if ( !ok() ) {
- break;
- }
- ASSERT( deleted.count( currLoc() ) == 0 );
- id = current()["_id"].Int();
- ASSERT( c()->currentMatches() );
- deleted.insert( currLoc() );
- advance();
- prepareToTouchEarlierIterate();
-
- _cli.remove( ns(), BSON( "_id" << id ), true );
-
- recoverFromTouchingEarlierIterate();
- }
- ASSERT_EQUALS( 599U, deleted.size() );
- }
- };
-
- } // namespace TouchEarlierIterate
-
- /* Test yield recovery failure of component capped cursor. */
- class InitialCappedWrapYieldRecoveryFailure : public Base {
- public:
- void run() {
- _cli.createCollection( ns(), 1000, true );
- _cli.insert( ns(), BSON( "_id" << 1 << "x" << 1 ) );
-
- Lock::GlobalWrite lk;
- Client::Context ctx( ns() );
- setQueryOptimizerCursor( BSON( "x" << GT << 0 ) );
- ASSERT_EQUALS( 1, current().getIntField( "x" ) );
-
- ClientCursorHolder p( new ClientCursor( QueryOption_NoCursorTimeout, c(), ns() ) );
- ClientCursor::YieldData yieldData;
- p->prepareToYield( yieldData );
-
- int x = 2;
- while( _cli.count( ns(), BSON( "x" << 1 ) ) > 0 ) {
- _cli.insert( ns(), BSON( "_id" << x << "x" << x ) );
- ++x;
- }
-
- // TODO - Might be preferable to return false rather than assert here.
- ASSERT_THROWS( ClientCursor::recoverFromYield( yieldData ), AssertionException );
- }
- };
-
- /* Test yield recovery failure of takeover capped cursor. */
- class TakeoverCappedWrapYieldRecoveryFailure : public Base {
- public:
- void run() {
- _cli.createCollection( ns(), 10000, true );
- for( int i = 0; i < 300; ++i ) {
- _cli.insert( ns(), BSON( "_id" << i << "x" << i ) );
- }
-
- ClientCursorHolder p;
- ClientCursor::YieldData yieldData;
- {
- Lock::GlobalWrite lk;
- Client::Context ctx( ns() );
- setQueryOptimizerCursor( BSON( "x" << GTE << 0 ) );
- for( int i = 0; i < 299; ++i ) {
- advance();
- }
- ASSERT_EQUALS( 299, current().getIntField( "x" ) );
-
- p.reset( new ClientCursor( QueryOption_NoCursorTimeout, c(), ns() ) );
- p->prepareToYield( yieldData );
- }
-
- int i = 300;
- while( _cli.count( ns(), BSON( "x" << 299 ) ) > 0 ) {
- _cli.insert( ns(), BSON( "_id" << i << "x" << i ) );
- ++i;
- }
-
- Lock::GlobalWrite lk;
- Client::Context ctx( ns() );
- ASSERT( !ClientCursor::recoverFromYield( yieldData ) );
- }
- };
-
- namespace ClientCursor {
-
- using mongo::ClientCursor;
-
- /** Test that a ClientCursor holding a QueryOptimizerCursor may be safely invalidated. */
- class Invalidate : public Base {
- public:
- void run() {
- _cli.ensureIndex( ns(), BSON( "a" << 1 ) );
- _cli.ensureIndex( ns(), BSON( "b" << 1 ) );
- _cli.insert( ns(), BSON( "a" << 1 << "b" << 1 ) );
- Lock::GlobalWrite lk;
-
- Client::Context ctx( ns() );
- ClientCursorHolder p
- ( new ClientCursor
- ( QueryOption_NoCursorTimeout,
- getOptimizedCursor
- ( ns(), BSON( "a" << GTE << 0 << "b" << GTE << 0 ) ),
- ns() ) );
- ClientCursor::invalidate( ns() );
- ASSERT_EQUALS( 0U, nNsCursors() );
- }
- };
-
- /** Test that a ClientCursor holding a QueryOptimizerCursor may be safely timed out. */
- class TimeoutClientCursorHolder : public Base {
- public:
- void run() {
- _cli.ensureIndex( ns(), BSON( "a" << 1 ) );
- _cli.ensureIndex( ns(), BSON( "b" << 1 ) );
- _cli.insert( ns(), BSON( "a" << 1 << "b" << 1 ) );
- Lock::GlobalWrite lk;
- Client::Context ctx( ns() );
- ClientCursorHolder p
- ( new ClientCursor
- ( 0,
- getOptimizedCursor
- ( ns(), BSON( "a" << GTE << 0 << "b" << GTE << 0 ) ),
- ns() ) );
-
- // Construct component client cursors.
- ClientCursor::YieldData yieldData;
- p->prepareToYield( yieldData );
- ASSERT( nNsCursors() > 1 );
-
- ClientCursor::invalidate( ns() );
- ASSERT_EQUALS( 0U, nNsCursors() );
- }
- };
-
- /** Test that a ClientCursor holding a QueryOptimizerCursor may be safely timed out. */
- class Timeout : public Base {
- public:
- void run() {
- _cli.ensureIndex( ns(), BSON( "a" << 1 ) );
- _cli.ensureIndex( ns(), BSON( "b" << 1 ) );
- _cli.insert( ns(), BSON( "a" << 1 << "b" << 1 ) );
- Lock::GlobalWrite lk;
-
- Client::Context ctx( ns() );
- ClientCursorHolder p
- ( new ClientCursor
- ( 0,
- getOptimizedCursor
- ( ns(), BSON( "a" << GTE << 0 << "b" << GTE << 0 ) ),
- ns() ) );
-
- // Construct component client cursors.
- ClientCursor::YieldData yieldData;
- p->prepareToYield( yieldData );
- ASSERT( nNsCursors() > 1 );
-
- ClientCursor::idleTimeReport( 600001 );
- ASSERT_EQUALS( 0U, nNsCursors() );
- }
- };
-
- /**
- * Test that a ClientCursor properly recovers a QueryOptimizerCursor after a btree
- * modification in preparation for a pre delete advance.
- */
- class AboutToDeleteRecoverFromYield : public Base {
- public:
- void run() {
- // Create a sparse index, so we can easily remove entries from it with an update.
- _cli.insert( NamespaceString( ns() ).getSystemIndexesCollection(),
- BSON( "ns" << ns() << "key" << BSON( "a" << 1 ) << "name" << "idx" <<
- "sparse" << true ) );
-
- _cli.ensureIndex( ns(), BSON( "a" << 1 ) );
- for( int i = 0; i < 150; ++i ) {
- _cli.insert( ns(), BSON( "a" << i << "b" << 0 ) );
- }
- Lock::GlobalWrite lk;
-
- Client::Context ctx( ns() );
- ClientCursorHolder p
- ( new ClientCursor
- ( QueryOption_NoCursorTimeout,
- getOptimizedCursor
- ( ns(), BSON( "a" << GTE << 0 << "b" << 0 ) ),
- ns() ) );
-
- // Iterate until after MultiCursor takes over.
- int readTo = 110;
- while( p->current()[ "a" ].number() < readTo ) {
- p->advance();
- }
-
- // Check that the btree plan was picked.
- ASSERT_EQUALS( BSON( "a" << 1 ), p->c()->indexKeyPattern() );
-
- // Yield the cursor.
- ClientCursor::YieldData yieldData;
- ASSERT( p->prepareToYield( yieldData ) );
-
- // Remove keys from the a:1 index, invalidating the cursor's position.
- _cli.update( ns(), BSON( "a" << LT << 100 ), BSON( "$unset" << BSON( "a" << 1 ) ),
- false, true );
-
- // Delete the cursor's current document. If the cursor's position is recovered
- // improperly in preparation for deleting the document, this will cause an
- // assertion.
- _cli.remove( ns(), BSON( "a" << readTo ) );
-
- // Check that the document was deleted.
- ASSERT_EQUALS( BSONObj(), _cli.findOne( ns(), BSON( "a" << readTo ) ) );
-
- // Recover the cursor.
- ASSERT( p->recoverFromYield( yieldData ) );
-
- // Check that the remaining documents are iterated as expected.
- for( int i = 111; i < 150; ++i ) {
- ASSERT_EQUALS( i, p->current()[ "a" ].number() );
- p->advance();
- }
- ASSERT( !p->ok() );
- }
- };
-
- /**
- * Test that a ClientCursor properly prepares a QueryOptimizerCursor to yield after a pre
- * delete advance.
- */
- class AboutToDeletePrepareToYield : public Base {
- public:
- void run() {
- // Create two indexes for serial $or clause traversal.
- _cli.ensureIndex( ns(), BSON( "a" << 1 ) );
- _cli.ensureIndex( ns(), BSON( "b" << 1 ) );
- for( int i = 0; i < 110; ++i ) {
- _cli.insert( ns(), BSON( "a" << i ) );
- }
- _cli.insert( ns(), BSON( "b" << 1 ) );
- Lock::GlobalWrite lk;
-
- Client::Context ctx( ns() );
- ClientCursorHolder p
- ( new ClientCursor
- ( QueryOption_NoCursorTimeout,
- getOptimizedCursor
- ( ns(), OR( BSON( "a" << GTE << 0 ), BSON( "b" << 1 ) ) ),
- ns() ) );
-
- // Iterate until after MultiCursor takes over.
- int readTo = 109;
- while( p->current()[ "a" ].number() < readTo ) {
- p->advance();
- }
-
- // Check the key pattern.
- ASSERT_EQUALS( BSON( "a" << 1 ), p->c()->indexKeyPattern() );
-
- // Yield the cursor.
- ClientCursor::YieldData yieldData;
- ASSERT( p->prepareToYield( yieldData ) );
-
- // Delete the cursor's current document. The cursor should advance to the b:1
- // index.
- _cli.remove( ns(), BSON( "a" << readTo ) );
-
- // Check that the document was deleted.
- ASSERT_EQUALS( BSONObj(), _cli.findOne( ns(), BSON( "a" << readTo ) ) );
-
- // Recover the cursor. If the cursor was not properly prepared for yielding
- // after the pre deletion advance, this will assert.
- ASSERT( p->recoverFromYield( yieldData ) );
-
- // Check that the remaining documents are as expected.
- ASSERT_EQUALS( 1, p->current()[ "b" ].number() );
- ASSERT( !p->advance() );
- }
- };
-
- /** The collection of a QueryOptimizerCursor stored in a ClientCursor is dropped. */
- class Drop : public Base {
- public:
- void run() {
- _cli.insert( ns(), BSON( "_id" << 1 ) );
- _cli.insert( ns(), BSON( "_id" << 2 ) );
-
- ClientCursor::YieldData yieldData;
- {
- Lock::DBWrite lk(ns());
- Client::Context ctx( ns() );
- ClientCursorHolder p
- ( new ClientCursor
- ( QueryOption_NoCursorTimeout,
- getOptimizedCursor
- ( ns(), BSON( "_id" << GT << 0 << "z" << 0 ) ),
- ns() ) );
-
- ASSERT_EQUALS( "QueryOptimizerCursor", p->c()->toString() );
- ASSERT_EQUALS( 1, p->c()->current().getIntField( "_id" ) );
- ASSERT( p->prepareToYield( yieldData ) );
- }
-
- // No assertion is expected when the collection is dropped and the cursor cannot be
- // recovered.
-
- _cli.dropCollection( ns() );
-
- {
- Lock::DBWrite lk(ns());
- Client::Context ctx( ns() );
- ASSERT( !ClientCursor::recoverFromYield( yieldData ) );
- }
- }
- };
-
- } // namespace ClientCursor
-
- class AllowOutOfOrderPlan : public Base {
- public:
- void run() {
- Lock::DBWrite lk(ns());
- Client::Context ctx( ns() );
- shared_ptr<Cursor> c =
- newQueryOptimizerCursor( ns(), BSONObj(), BSON( "a" << 1 ),
- QueryPlanSelectionPolicy::any(), false );
- ASSERT( c );
- }
- };
-
- class NoTakeoverByOutOfOrderPlan : public Base {
- public:
- void run() {
- _cli.ensureIndex( ns(), BSON( "a" << 1 ) );
- // Add enough early matches that the {$natural:1} plan would be chosen if it did not
- // require scan and order.
- for( int i = 0; i < 150; ++i ) {
- _cli.insert( ns(), BSON( "a" << 2 << "b" << 1 ) );
- }
- // Add non matches early on the {a:1} plan.
- for( int i = 0; i < 150; ++i ) {
- _cli.insert( ns(), BSON( "a" << 1 << "b" << 5 ) );
- }
- // Add enough matches outside the {a:1} index range that the {$natural:1} scan will not
- // complete before the {a:1} plan records 101 matches and is selected for takeover.
- for( int i = 0; i < 150; ++i ) {
- _cli.insert( ns(), BSON( "a" << 3 << "b" << 10 ) );
- }
- Lock::DBWrite lk(ns());
- Client::Context ctx( ns() );
- shared_ptr<Cursor> c =
- newQueryOptimizerCursor( ns(), BSON( "a" << LT << 3 << "b" << 1 ), BSON( "a" << 1 ),
- QueryPlanSelectionPolicy::any(), false );
- ASSERT( c );
- BSONObj idxKey;
- while( c->ok() ) {
- idxKey = c->indexKeyPattern();
- c->advance();
- }
- // Check that the ordered plan {a:1} took over, despite the unordered plan {$natural:1}
- // seeing > 101 matches.
- ASSERT_EQUALS( BSON( "a" << 1 ), idxKey );
- }
- };
-
- /** If no in order plans are possible, an out of order plan may take over. */
- class OutOfOrderOnlyTakeover : public Base {
- public:
- void run() {
- _cli.ensureIndex( ns(), BSON( "a" << 1 ) );
- for( int i = 0; i < 300; ++i ) {
- _cli.insert( ns(), BSON( "a" << 1 ) );
- _cli.insert( ns(), BSON( "a" << 2 ) );
- }
-
- Lock::DBWrite lk(ns());
- Client::Context ctx( ns() );
- shared_ptr<Cursor> c =
- newQueryOptimizerCursor( ns(), BSON( "a" << 1 ), BSON( "b" << 1 ),
- QueryPlanSelectionPolicy::any(), false );
- ASSERT( c );
- while( c->advance() );
- // Check that one of the plans took over, and we didn't scan both plans until the a:1
- // index completed (which would yield an nscanned near 600).
- ASSERT( c->nscanned() < 500 );
- }
- };
-
- class CoveredIndex : public Base {
- public:
- void run() {
- _cli.ensureIndex( ns(), BSON( "a" << 1 ) );
- _cli.ensureIndex( ns(), BSON( "b" << 1 ) );
- _cli.insert( ns(), BSON( "a" << 1 << "b" << 10 ) );
-
- Lock::DBWrite lk(ns());
- Client::Context ctx( ns() );
- shared_ptr<ParsedQuery> parsedQuery
- ( new ParsedQuery( ns(), 0, 0, 0, BSONObj(), BSON( "_id" << 0 << "a" << 1 ) ) );
- shared_ptr<QueryOptimizerCursor> c =
- dynamic_pointer_cast<QueryOptimizerCursor>
- ( newQueryOptimizerCursor( ns(), BSON( "a" << GTE << 0 << "b" << GTE << 0 ),
- BSON( "a" << 1 ), QueryPlanSelectionPolicy::any(), false,
- parsedQuery ) );
- bool foundA = false;
- bool foundB = false;
- while( c->ok() ) {
- if ( c->indexKeyPattern() == BSON( "a" << 1 ) ) {
- foundA = true;
- ASSERT( c->keyFieldsOnly() );
- ASSERT_EQUALS( BSON( "a" << 1 ), c->keyFieldsOnly()->hydrate( c->currKey() ) );
- }
- if ( c->indexKeyPattern() == BSON( "b" << 1 ) ) {
- foundB = true;
- ASSERT( !c->keyFieldsOnly() );
- }
- c->advance();
- }
- ASSERT( foundA );
- ASSERT( foundB );
- }
- };
-
- class CoveredIndexTakeover : public Base {
- public:
- void run() {
- _cli.ensureIndex( ns(), BSON( "a" << 1 ) );
- _cli.ensureIndex( ns(), BSON( "b" << 1 ) );
- for( int i = 0; i < 150; ++i ) {
- _cli.insert( ns(), BSON( "a" << 1 << "b" << 1 ) );
- }
- _cli.insert( ns(), BSON( "a" << 2 ) );
-
- Lock::DBWrite lk(ns());
- Client::Context ctx( ns() );
- shared_ptr<ParsedQuery> parsedQuery
- ( new ParsedQuery( ns(), 0, 0, 0, BSONObj(), BSON( "_id" << 0 << "a" << 1 ) ) );
- shared_ptr<QueryOptimizerCursor> c =
- dynamic_pointer_cast<QueryOptimizerCursor>
- ( newQueryOptimizerCursor( ns(), fromjson( "{$or:[{a:1},{b:1},{a:2}]}" ), BSONObj(),
- QueryPlanSelectionPolicy::any(), false, parsedQuery ) );
- bool foundA = false;
- bool foundB = false;
- while( c->ok() ) {
- if ( c->indexKeyPattern() == BSON( "a" << 1 ) ) {
- foundA = true;
- ASSERT( c->keyFieldsOnly() );
- ASSERT( BSON( "a" << 1 ) == c->keyFieldsOnly()->hydrate( c->currKey() ) ||
- BSON( "a" << 2 ) == c->keyFieldsOnly()->hydrate( c->currKey() ) );
- }
- if ( c->indexKeyPattern() == BSON( "b" << 1 ) ) {
- foundB = true;
- ASSERT( !c->keyFieldsOnly() );
- }
- c->advance();
- }
- ASSERT( foundA );
- ASSERT( foundB );
- }
- };
-
- class PlanChecking : public Base {
- public:
- virtual ~PlanChecking() {}
- protected:
- void nPlans( int n, const BSONObj &query, const BSONObj &order ) {
- auto_ptr< FieldRangeSetPair > frsp( new FieldRangeSetPair( ns(), query ) );
- auto_ptr< FieldRangeSetPair > frspOrig( new FieldRangeSetPair( *frsp ) );
- scoped_ptr<QueryPlanSet> s( QueryPlanSet::make( ns(), frsp, frspOrig, query, order,
- shared_ptr<const ParsedQuery>(),
- BSONObj(), QueryPlanGenerator::Use,
- BSONObj(), BSONObj(), true ) );
- ASSERT_EQUALS( n, s->nPlans() );
- }
- static shared_ptr<QueryOptimizerCursor> getCursor( const BSONObj &query,
- const BSONObj &order ) {
- shared_ptr<ParsedQuery> parsedQuery
- ( new ParsedQuery( ns(), 0, 0, 0,
- BSON( "$query" << query << "$orderby" << order ),
- BSONObj() ) );
- shared_ptr<Cursor> cursor = getOptimizedCursor( ns(),
- query,
- order,
- QueryPlanSelectionPolicy::any(),
- parsedQuery,
- false );
- shared_ptr<QueryOptimizerCursor> ret =
- dynamic_pointer_cast<QueryOptimizerCursor>( cursor );
- ASSERT( ret );
- return ret;
- }
- void runQuery( const BSONObj &query, const BSONObj &order ) {
- shared_ptr<QueryOptimizerCursor> cursor = getCursor( query, order );
- while( cursor->advance() );
- }
- };
-
- class SaveGoodIndex : public PlanChecking {
- public:
- void run() {
- _cli.ensureIndex( ns(), BSON( "a" << 1 ) );
- _cli.ensureIndex( ns(), BSON( "b" << 1 ) );
-
- Lock::DBWrite lk(ns());
- Client::Context ctx( ns() );
-
- // No best plan - all must be tried.
- nPlans( 3 );
- runQuery();
- // Best plan selected by query.
- nPlans( 1 );
- nPlans( 1 );
- Helpers::ensureIndex( ns(), BSON( "c" << 1 ), false, "c_1" );
- // Best plan cleared when new index added.
- nPlans( 3 );
- runQuery();
- // Best plan selected by query.
- nPlans( 1 );
-
- {
- DBDirectClient client;
- for( int i = 0; i < 334; ++i ) {
- client.insert( ns(), BSON( "i" << i ) );
- client.update( ns(), QUERY( "i" << i ), BSON( "i" << i + 1 ) );
- client.remove( ns(), BSON( "i" << i + 1 ) );
- }
- }
- // Best plan cleared by ~1000 writes.
- nPlans( 3 );
-
- shared_ptr<ParsedQuery> parsedQuery
- ( new ParsedQuery( ns(), 0, 0, 0,
- BSON( "$query" << BSON( "a" << 4 ) <<
- "$hint" << BSON( "$natural" << 1 ) ),
- BSON( "b" << 1 ) ) );
- shared_ptr<Cursor> cursor = getOptimizedCursor( ns(),
- BSON( "a" << 4 ),
- BSONObj(),
- QueryPlanSelectionPolicy::any(),
- parsedQuery,
- false );
- while( cursor->advance() );
- // No plan recorded when a hint is used.
- nPlans( 3 );
-
- shared_ptr<ParsedQuery> parsedQuery2
- ( new ParsedQuery( ns(), 0, 0, 0,
- BSON( "$query" << BSON( "a" << 4 ) <<
- "$orderby" << BSON( "b" << 1 << "c" << 1 ) ),
- BSONObj() ) );
- shared_ptr<Cursor> cursor2 = getOptimizedCursor( ns(),
- BSON( "a" << 4 ),
- BSON( "b" << 1 << "c" << 1 ),
- QueryPlanSelectionPolicy::any(),
- parsedQuery2,
- false );
- while( cursor2->advance() );
- // Plan recorded was for a different query pattern (different sort spec).
- nPlans( 3 );
-
- // Best plan still selected by query after all these other tests.
- runQuery();
- nPlans( 1 );
- }
- private:
- void nPlans( int n ) {
- return PlanChecking::nPlans( n, BSON( "a" << 4 ), BSON( "b" << 1 ) );
- }
- void runQuery() {
- return PlanChecking::runQuery( BSON( "a" << 4 ), BSON( "b" << 1 ) );
- }
- };
-
- class PossiblePlans : public PlanChecking {
- protected:
- void checkCursor( bool mayRunInOrderPlan, bool mayRunOutOfOrderPlan,
- bool runningInitialInOrderPlan, bool possiblyExcludedPlans ) {
- CandidatePlanCharacter plans = _cursor->initialCandidatePlans();
- ASSERT_EQUALS( mayRunInOrderPlan, plans.mayRunInOrderPlan() );
- ASSERT_EQUALS( mayRunOutOfOrderPlan, plans.mayRunOutOfOrderPlan() );
- ASSERT_EQUALS( runningInitialInOrderPlan, _cursor->runningInitialInOrderPlan() );
- ASSERT_EQUALS( possiblyExcludedPlans, _cursor->hasPossiblyExcludedPlans() );
- }
- void setCursor( const BSONObj &query, const BSONObj &order ) {
- _cursor = PlanChecking::getCursor( query, order );
- }
- void runCursor( bool completePlanOfHybridSetScanAndOrderRequired = false ) {
- while( _cursor->ok() ) {
- checkIterate( _cursor );
- _cursor->advance();
- }
- ASSERT_EQUALS( completePlanOfHybridSetScanAndOrderRequired,
- _cursor->completePlanOfHybridSetScanAndOrderRequired() );
- }
- void runCursorUntilTakeover() {
- // This is a bit of a hack, relying on initialFieldRangeSet() being nonzero before
- // takeover and zero after takeover.
- while( _cursor->ok() && _cursor->initialFieldRangeSet() ) {
- checkIterate( _cursor );
- _cursor->advance();
- }
- }
- void checkTakeoverCursor( bool currentPlanScanAndOrderRequired ) {
- ASSERT( !_cursor->initialFieldRangeSet() );
- ASSERT_EQUALS( currentPlanScanAndOrderRequired,
- _cursor->currentPlanScanAndOrderRequired() );
- ASSERT( !_cursor->completePlanOfHybridSetScanAndOrderRequired() );
- ASSERT( !_cursor->runningInitialInOrderPlan() );
- ASSERT( !_cursor->hasPossiblyExcludedPlans() );
- }
- virtual void checkIterate( const shared_ptr<QueryOptimizerCursor> &cursor ) const = 0;
- shared_ptr<QueryOptimizerCursor> _cursor;
- };
-
- class PossibleInOrderPlans : public PossiblePlans {
- public:
- void run() {
- _cli.ensureIndex( ns(), BSON( "a" << 1 ) );
- _cli.insert( ns(), BSON( "a" << 1 ) );
- for( int i = 0; i < 20; ++i ) {
- _cli.insert( ns(), BSON( "a" << 2 ) );
- }
-
- Lock::DBWrite lk(ns());
- Client::Context ctx( ns() );
- nPlans( 2, BSON( "a" << 1 << "x" << 1 ), BSONObj() );
- setCursor( BSON( "a" << 1 << "x" << 1 ), BSONObj() );
- checkCursor( false );
- ASSERT( _cursor->initialFieldRangeSet()->range( "a" ).equality() );
- ASSERT( !_cursor->initialFieldRangeSet()->range( "b" ).equality() );
- ASSERT( _cursor->initialFieldRangeSet()->range( "x" ).equality() );
-
- // Without running the (nonempty) cursor, no cached plan is recorded.
- setCursor( BSON( "a" << 1 << "x" << 1 ), BSONObj() );
- checkCursor( false );
-
- // Running the cursor records the plan.
- runCursor();
- nPlans( 1, BSON( "a" << 1 << "x" << 1 ), BSONObj() );
- setCursor( BSON( "a" << 1 << "x" << 1 ), BSONObj() );
- checkCursor( true );
-
- // Other plans may be added.
- setCursor( BSON( "a" << 2 << "x" << 1 ), BSONObj() );
- checkCursor( true );
- for( int i = 0; i < 10; ++i, _cursor->advance() );
- // The natural plan has been added in.
- checkCursor( false );
- nPlans( 1, BSON( "a" << 2 << "x" << 1 ), BSONObj() );
- runCursor();
-
- // The a:1 plan was recorded again.
- nPlans( 1, BSON( "a" << 2 << "x" << 1 ), BSONObj() );
- setCursor( BSON( "a" << 2 << "x" << 1 ), BSONObj() );
- checkCursor( true );
-
- // Clear the recorded plan manually.
- _cursor->clearIndexesForPatterns();
- nPlans( 2, BSON( "a" << 2 << "x" << 1 ), BSONObj() );
- setCursor( BSON( "a" << 2 << "x" << 1 ), BSONObj() );
- checkCursor( false );
-
- // Add more data, and run until takeover occurs.
- for( int i = 0; i < 120; ++i ) {
- _cli.insert( ns(), BSON( "a" << 3 << "x" << 1 ) );
- }
-
- setCursor( BSON( "a" << 3 << "x" << 1 ), BSONObj() );
- checkCursor( false );
- runCursorUntilTakeover();
- ASSERT( _cursor->ok() );
- checkTakeoverCursor( false );
-
- // Try again, with a cached plan this time.
- setCursor( BSON( "a" << 3 << "x" << 1 ), BSONObj() );
- checkCursor( true );
- runCursorUntilTakeover();
- checkTakeoverCursor( false );
- }
- private:
- void checkCursor( bool runningInitialCachedPlan ) {
- return PossiblePlans::checkCursor( true, false, true, runningInitialCachedPlan );
- }
- virtual void checkIterate( const shared_ptr<QueryOptimizerCursor> &cursor ) const {
- ASSERT( !cursor->currentPlanScanAndOrderRequired() );
- ASSERT( !cursor->completePlanOfHybridSetScanAndOrderRequired() );
- }
- };
-
- class PossibleOutOfOrderPlans : public PossiblePlans {
- public:
- void run() {
- _cli.ensureIndex( ns(), BSON( "a" << 1 ) );
- _cli.ensureIndex( ns(), BSON( "b" << 1 ) );
- _cli.insert( ns(), BSON( "a" << 1 << "b" << 1 ) );
- for( int i = 0; i < 20; ++i ) {
- _cli.insert( ns(), BSON( "a" << 2 ) );
- }
- _cli.insert( ns(), BSON( "b" << 2 ) );
-
- Lock::DBWrite lk(ns());
- Client::Context ctx( ns() );
- nPlans( 3, BSON( "a" << 1 << "b" << 1 ), BSON( "x" << 1 ) );
- setCursor( BSON( "a" << 1 << "b" << 1 ), BSON( "x" << 1 ) );
- checkCursor( false );
- ASSERT( _cursor->initialFieldRangeSet()->range( "a" ).equality() );
- ASSERT( _cursor->initialFieldRangeSet()->range( "b" ).equality() );
- ASSERT( !_cursor->initialFieldRangeSet()->range( "x" ).equality() );
-
- // Without running the (nonempty) cursor, no cached plan is recorded.
- setCursor( BSON( "a" << 1 << "b" << 1 ), BSON( "x" << 1 ) );
- checkCursor( false );
-
- // Running the cursor records the plan.
- runCursor();
- nPlans( 1, BSON( "a" << 1 << "b" << 1 ), BSON( "x" << 1 ) );
- setCursor( BSON( "a" << 1 << "b" << 1 ), BSON( "x" << 1 ) );
- checkCursor( true );
-
- // Other plans may be added.
- setCursor( BSON( "a" << 2 << "b" << 2 ), BSON( "x" << 1 ) );
- checkCursor( true );
- for( int i = 0; i < 10; ++i, _cursor->advance() );
- // The other plans have been added in.
- checkCursor( false );
- runCursor();
-
- // The b:1 plan was recorded.
- setCursor( BSON( "a" << 1 << "b" << 1 ), BSON( "x" << 1 ) );
- checkCursor( true );
-
- // Clear the recorded plan manually.
- _cursor->clearIndexesForPatterns();
- setCursor( BSON( "a" << 2 << "x" << 1 ), BSON( "x" << 1 ) );
- checkCursor( false );
-
- // Add more data, and run until takeover occurs.
- for( int i = 0; i < 120; ++i ) {
- _cli.insert( ns(), BSON( "a" << 3 << "b" << 3 ) );
- }
-
- setCursor( BSON( "a" << 3 << "b" << 3 ), BSON( "x" << 1 ) );
- checkCursor( false );
- runCursorUntilTakeover();
- ASSERT( _cursor->ok() );
- checkTakeoverCursor( true );
-
- // Try again, with a cached plan this time.
- setCursor( BSON( "a" << 3 << "b" << 3 ), BSON( "x" << 1 ) );
- checkCursor( true );
- runCursorUntilTakeover();
- checkTakeoverCursor( true );
- }
- private:
- void checkCursor( bool runningInitialCachedPlan ) {
- return PossiblePlans::checkCursor( false, true, false, runningInitialCachedPlan );
- }
- virtual void checkIterate( const shared_ptr<QueryOptimizerCursor> &cursor ) const {
- ASSERT( cursor->currentPlanScanAndOrderRequired() );
- ASSERT( !cursor->completePlanOfHybridSetScanAndOrderRequired() );
- }
- };
-
- class PossibleBothPlans : public PossiblePlans {
- public:
- void run() {
- _cli.ensureIndex( ns(), BSON( "a" << 1 ) );
- _cli.ensureIndex( ns(), BSON( "b" << 1 ) );
- _cli.insert( ns(), BSON( "a" << 1 << "b" << 1 ) );
- _cli.insert( ns(), BSON( "a" << 2 << "b" << 1 ) );
- for( int i = 0; i < 20; ++i ) {
- _cli.insert( ns(), BSON( "a" << 2 ) );
- if ( i % 10 == 0 ) {
- _cli.insert( ns(), BSON( "b" << 2 ) );
- }
- }
-
- Lock::DBWrite lk(ns());
- Client::Context ctx( ns() );
- nPlans( 3, BSON( "a" << 1 << "b" << 1 ), BSON( "b" << 1 ) );
- setCursor( BSON( "a" << 1 << "b" << 1 ), BSON( "b" << 1 ) );
- checkCursor( true, false );
- ASSERT( _cursor->initialFieldRangeSet()->range( "a" ).equality() );
- ASSERT( _cursor->initialFieldRangeSet()->range( "b" ).equality() );
- ASSERT( !_cursor->initialFieldRangeSet()->range( "x" ).equality() );
-
- // Without running the (nonempty) cursor, no cached plan is recorded.
- setCursor( BSON( "a" << 1 << "b" << 1 ), BSON( "b" << 1 ) );
- checkCursor( true, false );
-
- // Running the cursor records the a:1 plan.
- runCursor( true );
- nPlans( 1, BSON( "a" << 1 << "b" << 1 ), BSON( "b" << 1 ) );
- setCursor( BSON( "a" << 1 << "b" << 1 ), BSON( "b" << 1 ) );
- checkCursor( false, true );
-
- // Other plans may be added.
- setCursor( BSON( "a" << 2 << "b" << 2 ), BSON( "b" << 1 ) );
- checkCursor( false, true );
- for( int i = 0; i < 10; ++i, _cursor->advance() );
- // The other plans have been added in (including ordered b:1).
- checkCursor( true, false );
- runCursor( false );
-
- // The b:1 plan was recorded.
- setCursor( BSON( "a" << 1 << "b" << 1 ), BSON( "b" << 1 ) );
- checkCursor( true, true );
-
- // Clear the recorded plan manually.
- _cursor->clearIndexesForPatterns();
- setCursor( BSON( "a" << 2 << "b" << 1 ), BSON( "b" << 1 ) );
- checkCursor( true, false );
-
- // Add more data, and run until takeover occurs.
- for( int i = 0; i < 120; ++i ) {
- _cli.insert( ns(), BSON( "a" << 3 << "b" << 3 ) );
- }
-
- setCursor( BSON( "a" << 3 << "b" << 3 ), BSON( "b" << 1 ) );
- checkCursor( true, false );
- runCursorUntilTakeover();
- ASSERT( _cursor->ok() );
- checkTakeoverCursor( false );
- ASSERT_EQUALS( BSON( "b" << 1 ), _cursor->indexKeyPattern() );
-
- // Try again, with a cached plan this time.
- setCursor( BSON( "a" << 3 << "b" << 3 ), BSON( "b" << 1 ) );
- checkCursor( true, true );
- runCursorUntilTakeover();
- checkTakeoverCursor( false );
- ASSERT_EQUALS( BSON( "b" << 1 ), _cursor->indexKeyPattern() );
- }
- private:
- void checkCursor( bool runningInitialInOrderPlan, bool runningInitialCachedPlan ) {
- return PossiblePlans::checkCursor( true, true, runningInitialInOrderPlan,
- runningInitialCachedPlan );
- }
- virtual void checkIterate( const shared_ptr<QueryOptimizerCursor> &cursor ) const {
- if ( cursor->indexKeyPattern() == BSON( "b" << 1 ) ) {
- ASSERT( !cursor->currentPlanScanAndOrderRequired() );
- }
- else {
- ASSERT( cursor->currentPlanScanAndOrderRequired() );
- }
- ASSERT( !cursor->completePlanOfHybridSetScanAndOrderRequired() );
- }
- };
-
- class AbortOutOfOrderPlans : public PlanChecking {
- public:
- void run() {
- _cli.ensureIndex( ns(), BSON( "a" << 1 ) );
- for( int i = 0; i < 10; ++i ) {
- _cli.insert( ns(), BSON( "a" << 1 ) );
- }
-
- Lock::DBWrite lk(ns());
- Client::Context ctx( ns() );
-
- shared_ptr<QueryOptimizerCursor> c = getCursor( BSON( "a" << 1 << "b" << BSONNULL ),
- BSON( "a" << 1 ) );
- // Wait until a $natural plan result is returned.
- while( c->indexKeyPattern() != BSONObj() ) {
- c->advance();
- }
- // Abort the natural plan.
- c->abortOutOfOrderPlans();
- c->advance();
- // Check that no more results from the natural plan are returned.
- ASSERT( c->ok() );
- while( c->ok() ) {
- ASSERT_EQUALS( BSON( "a" << 1 ), c->indexKeyPattern() );
- c->advance();
- }
- ASSERT( !c->completePlanOfHybridSetScanAndOrderRequired() );
- }
- };
-
- class AbortOutOfOrderPlanOnLastMatch : public PlanChecking {
- public:
- void run() {
- _cli.ensureIndex( ns(), BSON( "a" << 1 ) );
- for( int i = 0; i < 10; ++i ) {
- _cli.insert( ns(), BSON( "a" << BSON_ARRAY( 1 << 2 ) ) );
- }
-
- Lock::DBWrite lk(ns());
- Client::Context ctx( ns() );
-
- shared_ptr<QueryOptimizerCursor> c =
- getCursor( BSON( "a" << GTE << 1 << "b" << BSONNULL ), BSON( "a" << 1 ) );
- // Wait until 10 (all) $natural plan results are returned.
- for( int i = 0; i < 10; ++i ) {
- while( c->indexKeyPattern() != BSONObj() ) {
- c->advance();
- }
- c->advance();
- }
- // Abort the natural plan.
- c->abortOutOfOrderPlans();
- c->advance();
- // Check that no more results from the natural plan are returned, and the cursor is not
- // done iterating.
- ASSERT( c->ok() );
- while( c->ok() ) {
- ASSERT_EQUALS( BSON( "a" << 1 ), c->indexKeyPattern() );
- c->advance();
- }
- ASSERT( !c->completePlanOfHybridSetScanAndOrderRequired() );
- }
- };
-
- /** Out of order plans are not added after abortOutOfOrderPlans() is called. */
- class AbortOutOfOrderPlansBeforeAddOtherPlans : public PlanChecking {
- public:
- void run() {
- _cli.ensureIndex( ns(), BSON( "a" << 1 ) );
- _cli.ensureIndex( ns(), BSON( "b" << 1 ) );
- _cli.insert( ns(), BSON( "a" << -1 << "b" << 0 ) );
- for( int b = 0; b < 2; ++b ) {
- for( int a = 0; a < 10; ++a ) {
- _cli.insert( ns(), BSON( "a" << a << "b" << b ) );
- }
- }
-
- // Selectivity is better for the a:1 index.
- _aPreferableQuery = BSON( "a" << GTE << -100 << LTE << -1 << "b" << 0 );
- // Selectivity is better for the b:1 index.
- _bPreferableQuery = BSON( "a" << GTE << 0 << LTE << 100 << "b" << 0 );
-
- Client::ReadContext ctx( ns() );
-
- // If abortOutOfOrderPlans() is not set, other plans will be attempted.
- recordAIndex();
- _cursor = getCursor( _bPreferableQuery, BSON( "a" << 1 ) );
- checkInitialIteratePlans();
- // The b:1 index is attempted later.
- checkBIndexUsed( true );
-
- // If abortOutOfOrderPlans() is set, other plans will not be attempted.
- recordAIndex();
- _cursor = getCursor( _bPreferableQuery, BSON( "a" << 1 ) );
- checkInitialIteratePlans();
- _cursor->abortOutOfOrderPlans();
- // The b:1 index is not attempted.
- checkBIndexUsed( false );
- }
- private:
- /** Record the a:1 index for the query pattern of interest. */
- void recordAIndex() const {
- Collection* collection = cc().database()->getCollection( ns() );
- CollectionInfoCache* cache = collection->infoCache();
- cache->clearQueryCache();
- shared_ptr<QueryOptimizerCursor> c = getCursor( _aPreferableQuery, BSON( "a" << 1 ) );
- while( c->advance() );
- FieldRangeSet aPreferableFields( ns(), _aPreferableQuery, true, true );
- ASSERT_EQUALS( BSON( "a" << 1 ),
- cache->cachedQueryPlanForPattern
- ( aPreferableFields.pattern( BSON( "a" << 1 ) ) ).indexKey() );
- }
- /** The first results come from the recorded index. */
- void checkInitialIteratePlans() const {
- for( int i = 0; i < 5; ++i ) {
- ASSERT_EQUALS( BSON( "a" << 1 ), _cursor->indexKeyPattern() );
- }
- }
- /** Check if the b:1 index is used during iteration. */
- void checkBIndexUsed( bool expected ) const {
- bool bIndexUsed = false;
- while( _cursor->advance() ) {
- if ( BSON( "b" << 1 ) == _cursor->indexKeyPattern() ) {
- bIndexUsed = true;
- }
- }
- ASSERT_EQUALS( expected, bIndexUsed );
- }
- BSONObj _aPreferableQuery;
- BSONObj _bPreferableQuery;
- shared_ptr<QueryOptimizerCursor> _cursor;
- };
-
- class TakeoverOrRangeElimination : public PlanChecking {
- public:
- void run() {
- _cli.ensureIndex( ns(), BSON( "a" << 1 ) );
- for( int i = 0; i < 120; ++i ) {
- _cli.insert( ns(), BSON( "a" << 1 ) );
- }
- for( int i = 0; i < 20; ++i ) {
- _cli.insert( ns(), BSON( "a" << 2 ) );
- }
- for( int i = 0; i < 20; ++i ) {
- _cli.insert( ns(), BSON( "a" << 3 ) );
- }
-
- Lock::DBWrite lk(ns());
- Client::Context ctx( ns() );
-
- shared_ptr<QueryOptimizerCursor> c =
- getCursor( fromjson( "{$or:[{a:{$lte:2}},{a:{$gte:2}},{a:9}]}" ), BSONObj() );
-
- int count = 0;
- while( c->ok() ) {
- c->advance();
- ++count;
- }
- ASSERT_EQUALS( 160, count );
- }
- };
-
- class TakeoverOrDedups : public PlanChecking {
- public:
- void run() {
- _cli.ensureIndex( ns(), BSON( "a" << 1 << "b" << 1 ) );
- for( int i = 0; i < 120; ++i ) {
- _cli.insert( ns(), BSON( "a" << 1 << "b" << 1 ) );
- }
- for( int i = 0; i < 20; ++i ) {
- _cli.insert( ns(), BSON( "a" << 2 << "b" << 2 ) );
- }
- for( int i = 0; i < 20; ++i ) {
- _cli.insert( ns(), BSON( "a" << 3 << "b" << 3 ) );
- }
-
- Lock::DBWrite lk(ns());
- Client::Context ctx( ns() );
-
- BSONObj query =
- BSON(
- "$or" << BSON_ARRAY(
- BSON(
- "a" << GTE << 0 << LTE << 2 <<
- "b" << GTE << 0 << LTE << 2
- ) <<
- BSON(
- "a" << GTE << 1 << LTE << 3 <<
- "b" << GTE << 1 << LTE << 3
- ) <<
- BSON(
- "a" << GTE << 1 << LTE << 4 <<
- "b" << GTE << 1 << LTE << 4
- )
- )
- );
-
- shared_ptr<QueryOptimizerCursor> c = getCursor( query, BSONObj() );
-
- int count = 0;
- while( c->ok() ) {
- if ( ( c->indexKeyPattern() == BSON( "a" << 1 << "b" << 1 ) ) &&
- c->currentMatches() ) {
- ++count;
- }
- c->advance();
- }
- ASSERT_EQUALS( 160, count );
- }
- };
-
- /** Proper index matching when transitioning between $or clauses after a takeover. */
- class TakeoverOrDifferentIndex : public PlanChecking {
- public:
- void run() {
- for( int i = 0; i < 120; ++i ) {
- _cli.insert( ns(), BSON( "a" << 1 << "b" << 2 ) );
- }
- _cli.ensureIndex( ns(), BSON( "a" << 1 << "b" << 1 ) );
- for( int i = 0; i < 130; ++i ) {
- _cli.insert( ns(), BSON( "b" << 3 << "a" << 4 ) );
- }
- _cli.ensureIndex( ns(), BSON( "b" << 1 << "a" << 1 ) );
-
- Lock::DBWrite lk(ns());
- Client::Context ctx( ns() );
-
- // This $or query will scan index a:1,b:1 then b:1,a:1. If the key pattern is specified
- // incorrectly for the second clause, matching will fail.
- setQueryOptimizerCursor( fromjson( "{$or:[{a:1,b:{$gte:0}},{b:3,a:{$gte:0}}]}" ) );
- // All documents match, and there are no dups.
- ASSERT_EQUALS( 250, itcount() );
- }
- };
-
- /**
- * An ordered plan returns all results, including when it takes over, even when it duplicates an
- * entry of an out of order plan.
- */
- class TakeoverOrderedPlanDupsOutOfOrderPlan : public PlanChecking {
- public:
- void run() {
- _cli.ensureIndex( ns(), BSON( "a" << 1 ) );
- for( int i = 1; i < 200; ++i ) {
- _cli.insert( ns(), BSON( "a" << i << "b" << 0 ) );
- }
- // Insert this document last, so that most documents are read from the $natural cursor
- // before the a:1 cursor.
- _cli.insert( ns(), BSON( "a" << 0 << "b" << 0 ) );
-
- Client::ReadContext ctx( ns() );
- shared_ptr<QueryOptimizerCursor> cursor =
- getCursor( BSON( "a" << GTE << 0 << "b" << 0 ), BSON( "a" << 1 ) );
- int nextA = 0;
- for( ; cursor->ok(); cursor->advance() ) {
- if ( cursor->indexKeyPattern() == BSON( "a" << 1 ) ) {
- // Check that the expected 'a' value is present and in order.
- ASSERT_EQUALS( nextA++, cursor->current()[ "a" ].number() );
- }
- }
- ASSERT_EQUALS( 200, nextA );
- }
- };
-
- /** Check that an elemMatchKey can be retrieved from MatchDetails using a qo cursor. */
- class ElemMatchKey : public Base {
- public:
- void run() {
- _cli.ensureIndex( ns(), BSON( "a.b" << 1 ) );
- _cli.insert( ns(), fromjson( "{ a:[ { b:1 } ] }" ) );
-
- Client::ReadContext ctx( ns() );
- setQueryOptimizerCursor( BSON( "a.b" << 1 ) );
- MatchDetails details;
- details.requestElemMatchKey();
- ASSERT( c()->currentMatches( &details ) );
- // The '0' entry of the 'a' array is matched.
- ASSERT_EQUALS( string( "0" ), details.elemMatchKey() );
- }
- };
-
- namespace GetCursor {
-
- class Base : public QueryOptimizerCursorTests::Base {
- public:
- Base() {
- // create collection
- _cli.insert( ns(), BSON( "_id" << 5 ) );
- }
- virtual ~Base() {}
- void run() {
- Lock::GlobalWrite lk;
- Client::Context ctx( ns() );
- if ( expectException() ) {
- ASSERT_THROWS
- ( getOptimizedCursor
- ( ns(), query(), order(), planPolicy() ),
- MsgAssertionException );
- return;
- }
- _query = query();
- _parsedQuery.reset( new ParsedQuery( ns(), skip(), limit(), 0, _query,
- BSONObj() ) );
- BSONObj extractedQuery = _query;
- if ( !_query["$query"].eoo() ) {
- extractedQuery = _query["$query"].Obj();
- }
- shared_ptr<Cursor> c = getOptimizedCursor( ns(),
- extractedQuery,
- order(),
- planPolicy(),
- _parsedQuery,
- false );
- string type = c->toString().substr( 0, expectedType().length() );
- ASSERT_EQUALS( expectedType(), type );
- check( c );
- }
- protected:
- virtual string expectedType() const { return "TESTDUMMY"; }
- virtual bool expectException() const { return false; }
- virtual BSONObj query() const { return BSONObj(); }
- virtual BSONObj order() const { return BSONObj(); }
- virtual int skip() const { return 0; }
- virtual int limit() const { return 0; }
- virtual const QueryPlanSelectionPolicy &planPolicy() const {
- return QueryPlanSelectionPolicy::any();
- }
- virtual void check( const shared_ptr<Cursor> &c ) {
- ASSERT( c->ok() );
- ASSERT( !c->matcher() );
- ASSERT_EQUALS( 5, c->current().getIntField( "_id" ) );
- ASSERT( !c->advance() );
- }
- private:
- BSONObj _query;
- shared_ptr<ParsedQuery> _parsedQuery;
- };
-
- class NoConstraints : public Base {
- string expectedType() const { return "BasicCursor"; }
- };
-
- class SimpleId : public Base {
- public:
- SimpleId() {
- _cli.insert( ns(), BSON( "_id" << 0 ) );
- _cli.insert( ns(), BSON( "_id" << 10 ) );
- }
- string expectedType() const { return "BtreeCursor _id_"; }
- BSONObj query() const { return BSON( "_id" << 5 ); }
- };
-
- class OptimalIndex : public Base {
- public:
- OptimalIndex() {
- _cli.ensureIndex( ns(), BSON( "a" << 1 ) );
- _cli.insert( ns(), BSON( "a" << 5 ) );
- _cli.insert( ns(), BSON( "a" << 6 ) );
- }
- string expectedType() const { return "BtreeCursor a_1"; }
- BSONObj query() const { return BSON( "a" << GTE << 5 ); }
- void check( const shared_ptr<Cursor> &c ) {
- ASSERT( c->ok() );
- ASSERT( c->matcher() );
- ASSERT_EQUALS( 5, c->current().getIntField( "a" ) );
- ASSERT( c->matcher()->matchesCurrent( c.get() ) );
- ASSERT( c->advance() );
- ASSERT_EQUALS( 6, c->current().getIntField( "a" ) );
- ASSERT( c->matcher()->matchesCurrent( c.get() ) );
- ASSERT( !c->advance() );
- }
- };
-
- class SimpleKeyMatch : public Base {
- public:
- SimpleKeyMatch() {
- _cli.ensureIndex( ns(), BSON( "a" << 1 ) );
- _cli.update( ns(), BSONObj(), BSON( "$set" << BSON( "a" << true ) ) );
- }
- string expectedType() const { return "BtreeCursor a_1"; }
- BSONObj query() const { return BSON( "a" << true ); }
- virtual void check( const shared_ptr<Cursor> &c ) {
- ASSERT( c->ok() );
- ASSERT_EQUALS( 5, c->current().getIntField( "_id" ) );
- ASSERT( !c->advance() );
- }
- };
-
- class PreventOutOfOrderPlan : public QueryOptimizerCursorTests::Base {
- public:
- void run() {
- _cli.insert( ns(), BSON( "_id" << 5 ) );
- Lock::GlobalWrite lk;
- Client::Context ctx( ns() );
- shared_ptr<Cursor> c = getOptimizedCursor( ns(), BSONObj(), BSON( "b" << 1 ) );
- ASSERT( !c );
- }
- };
-
- class AllowOutOfOrderPlan : public Base {
- public:
- void run() {
- Lock::DBWrite lk(ns());
- Client::Context ctx( ns() );
- shared_ptr<ParsedQuery> parsedQuery
- ( new ParsedQuery( ns(), 0, 0, 0,
- BSON( "$query" << BSONObj() <<
- "$orderby" << BSON( "a" << 1 ) ),
- BSONObj() ) );
- shared_ptr<Cursor> c = getOptimizedCursor( ns(),
- BSONObj(),
- BSON( "a" << 1 ),
- QueryPlanSelectionPolicy::any(),
- parsedQuery,
- false );
- ASSERT( c );
- }
- };
-
- class BestSavedOutOfOrder : public QueryOptimizerCursorTests::Base {
- public:
- void run() {
- _cli.insert( ns(), BSON( "_id" << 5 << "b" << BSON_ARRAY( 1 << 2 << 3 << 4 << 5 ) ) );
- _cli.insert( ns(), BSON( "_id" << 1 << "b" << 6 ) );
- _cli.ensureIndex( ns(), BSON( "b" << 1 ) );
- // record {_id:1} index for this query
- ASSERT( _cli.query( ns(), QUERY( "_id" << GT << 0 << "b" << GT << 0 ).sort( "b" ) )->more() );
- Lock::GlobalWrite lk;
- Client::Context ctx( ns() );
- shared_ptr<Cursor> c =
- getOptimizedCursor( ns(),
- BSON( "_id" << GT << 0 << "b" << GT << 0 ),
- BSON( "b" << 1 ) );
- // {_id:1} requires scan and order, so {b:1} must be chosen.
- ASSERT( c );
- ASSERT_EQUALS( 5, c->current().getIntField( "_id" ) );
- }
- };
-
- // QUERY MIGRATION
- // Cache is turned off
- /**
- * If an optimal plan is cached, return a Cursor for it rather than a QueryOptimizerCursor.
- */
- // class BestSavedOptimal : public QueryOptimizerCursorTests::Base {
- // public:
- // void run() {
- // _cli.insert( ns(), BSON( "_id" << 1 ) );
- // _cli.ensureIndex( ns(), BSON( "_id" << 1 << "q" << 1 ) );
- // ASSERT( _cli.query( ns(), QUERY( "_id" << GT << 0 ) )->more() );
- // Lock::GlobalWrite lk;
- // Client::Context ctx( ns() );
- // // Check the plan that was recorded for this query.
- // ASSERT_EQUALS( BSON( "_id" << 1 ), cachedIndexForQuery( BSON( "_id" << GT << 0 ) ) );
- // shared_ptr<Cursor> c = getOptimizedCursor( ns(), BSON( "_id" << GT << 0 ) );
- // // No need for query optimizer cursor since the plan is optimal.
- // ASSERT_EQUALS( "BtreeCursor _id_", c->toString() );
- // }
- // };
-
- // QUERY MIGRATIOM
- // Cache is turned off
- /** If a non optimal plan is a candidate a QueryOptimizerCursor should be returned, even if plan has been recorded. */
- // class BestSavedNotOptimal : public QueryOptimizerCursorTests::Base {
- // public:
- // void run() {
- // _cli.insert( ns(), BSON( "_id" << 1 << "q" << 1 ) );
- // _cli.ensureIndex( ns(), BSON( "q" << 1 ) );
- // // Record {_id:1} index for this query
- // ASSERT( _cli.query( ns(), QUERY( "q" << 1 << "_id" << 1 ) )->more() );
- // Lock::GlobalWrite lk;
- // Client::Context ctx( ns() );
- // ASSERT_EQUALS( BSON( "_id" << 1 ),
- // cachedIndexForQuery( BSON( "q" << 1 << "_id" << 1 ) ) );
- // shared_ptr<Cursor> c = getOptimizedCursor( ns(), BSON( "q" << 1 << "_id" << 1 ) );
- // // Need query optimizer cursor since the cached plan is not optimal.
- // ASSERT_EQUALS( "QueryOptimizerCursor", c->toString() );
- // }
- // };
-
- class MultiIndex : public Base {
- public:
- MultiIndex() {
- _cli.ensureIndex( ns(), BSON( "a" << 1 ) );
- }
- string expectedType() const { return "QueryOptimizerCursor"; }
- BSONObj query() const { return BSON( "_id" << GT << 0 << "a" << GT << 0 ); }
- void check( const shared_ptr<Cursor> &c ) {}
- };
-
- class Hint : public Base {
- public:
- Hint() {
- _cli.ensureIndex( ns(), BSON( "a" << 1 ) );
- }
- string expectedType() const { return "BtreeCursor a_1"; }
- BSONObj query() const {
- return BSON( "$query" << BSON( "_id" << 1 ) << "$hint" << BSON( "a" << 1 ) );
- }
- void check( const shared_ptr<Cursor> &c ) {}
- };
-
- class Snapshot : public Base {
- public:
- Snapshot() {
- _cli.ensureIndex( ns(), BSON( "a" << 1 ) );
- }
- string expectedType() const { return "BtreeCursor _id_"; }
- BSONObj query() const {
- return BSON( "$query" << BSON( "a" << 1 ) << "$snapshot" << true );
- }
- void check( const shared_ptr<Cursor> &c ) {}
- };
-
- class SnapshotCappedColl : public Base {
- public:
- SnapshotCappedColl() {
- _cli.dropCollection( ns() );
- _cli.createCollection( ns(), 1000, true );
- _cli.ensureIndex( ns(), BSON( "a" << 1 ) );
- }
- string expectedType() const { return "BtreeCursor _id_"; }
- BSONObj query() const {
- return BSON( "$query" << BSON( "a" << 1 ) << "$snapshot" << true );
- }
- void check( const shared_ptr<Cursor> &c ) {}
- };
-
- class Min : public Base {
- public:
- Min() {
- _cli.ensureIndex( ns(), BSON( "a" << 1 ) );
- }
- string expectedType() const { return "BtreeCursor a_1"; }
- BSONObj query() const {
- return BSON( "$query" << BSONObj() << "$min" << BSON( "a" << 1 ) );
- }
- void check( const shared_ptr<Cursor> &c ) {}
- };
-
- class Max : public Base {
- public:
- Max() {
- _cli.ensureIndex( ns(), BSON( "a" << 1 ) );
- }
- string expectedType() const { return "BtreeCursor a_1"; }
- BSONObj query() const {
- return BSON( "$query" << BSONObj() << "$max" << BSON( "a" << 1 ) );
- }
- void check( const shared_ptr<Cursor> &c ) {}
- };
-
- namespace RequireIndex {
-
- class Base : public GetCursor::Base {
- const QueryPlanSelectionPolicy &planPolicy() const {
- return QueryPlanSelectionPolicy::indexOnly();
- }
- };
-
- class NoConstraints : public Base {
- bool expectException() const { return true; }
- };
-
- class SimpleId : public Base {
- string expectedType() const { return "BtreeCursor _id_"; }
- BSONObj query() const { return BSON( "_id" << 5 ); }
- };
-
- class UnindexedQuery : public Base {
- bool expectException() const { return true; }
- BSONObj query() const { return BSON( "a" << GTE << 5 ); }
- };
-
- class IndexedQuery : public Base {
- public:
- IndexedQuery() {
- _cli.insert( ns(), BSON( "_id" << 6 << "a" << 6 << "c" << 4 ) );
- _cli.ensureIndex( ns(), BSON( "a" << 1 << "b" << 1 << "c" << 1 ) );
- }
- string expectedType() const { return "QueryOptimizerCursor"; }
- BSONObj query() const { return BSON( "a" << GTE << 5 << "c" << 4 ); }
- void check( const shared_ptr<Cursor> &c ) {
- ASSERT( c->ok() );
- ASSERT( c->matcher() );
- ASSERT_EQUALS( 6, c->current().getIntField( "_id" ) );
- ASSERT( !c->advance() );
- }
- };
-
- class SecondOrClauseIndexed : public Base {
- public:
- SecondOrClauseIndexed() {
- _cli.ensureIndex( ns(), BSON( "a" << 1 ) );
- _cli.ensureIndex( ns(), BSON( "b" << 1 ) );
- _cli.insert( ns(), BSON( "a" << 1 ) );
- _cli.insert( ns(), BSON( "b" << 1 ) );
- }
- string expectedType() const { return "QueryOptimizerCursor"; }
- BSONObj query() const { return fromjson( "{$or:[{a:1},{b:1}]}" ); }
- void check( const shared_ptr<Cursor> &c ) {
- ASSERT( c->ok() );
- ASSERT( c->matcher() );
- ASSERT( c->advance() );
- ASSERT( !c->advance() ); // 2 matches exactly
- }
- };
-
- class SecondOrClauseUnindexed : public Base {
- public:
- SecondOrClauseUnindexed() {
- _cli.ensureIndex( ns(), BSON( "a" << 1 ) );
- _cli.insert( ns(), BSON( "a" << 1 ) );
- }
- bool expectException() const { return true; }
- BSONObj query() const { return fromjson( "{$or:[{a:1},{b:1}]}" ); }
- };
-
- class SecondOrClauseUnindexedUndetected : public Base {
- public:
- SecondOrClauseUnindexedUndetected() {
- _cli.ensureIndex( ns(), BSON( "a" << 1 ) );
- _cli.ensureIndex( ns(), BSON( "a" << 1 << "b" << 1 ) );
- _cli.insert( ns(), BSON( "a" << 1 ) );
- _cli.insert( ns(), BSON( "b" << 1 ) );
- }
- string expectedType() const { return "QueryOptimizerCursor"; }
- BSONObj query() const { return fromjson( "{$or:[{a:1},{b:1}]}" ); }
- void check( const shared_ptr<Cursor> &c ) {
- ASSERT( c->ok() );
- ASSERT( c->matcher() );
- // An unindexed cursor is required for the second clause, but is not allowed.
- ASSERT_THROWS( c->advance(), MsgAssertionException );
- }
- };
-
- // QUERY MIGRATION
- // Cached is turned off
- // class RecordedUnindexedPlan : public Base {
- // public:
- // RecordedUnindexedPlan() {
- // _cli.ensureIndex( ns(), BSON( "a" << 1 ) );
- // _cli.insert( ns(), BSON( "a" << BSON_ARRAY( 1 << 2 << 3 ) << "b" << 1 ) );
- // auto_ptr<DBClientCursor> cursor =
- // _cli.query( ns(), QUERY( "a" << GT << 0 << "b" << 1 ).explain() );
- // BSONObj explain = cursor->next();
- // ASSERT_EQUALS( "BasicCursor", explain[ "cursor" ].String() );
- // }
- // string expectedType() const { return "QueryOptimizerCursor"; }
- // BSONObj query() const { return BSON( "a" << GT << 0 << "b" << 1 ); }
- // void check( const shared_ptr<Cursor> &c ) {
- // ASSERT( c->ok() );
- // ASSERT_EQUALS( BSON( "a" << 1 ), c->indexKeyPattern() );
- // while( c->advance() ) {
- // ASSERT_EQUALS( BSON( "a" << 1 ), c->indexKeyPattern() );
- // }
- // }
- // };
-
- } // namespace RequireIndex
-
- namespace IdElseNatural {
-
- class Base : public GetCursor::Base {
- const QueryPlanSelectionPolicy &planPolicy() const {
- return QueryPlanSelectionPolicy::idElseNatural();
- }
- };
-
- class AllowOptimalNaturalPlan : public Base {
- string expectedType() const { return "BasicCursor"; }
- void check( const shared_ptr<Cursor> &c ) {
- ASSERT( c->ok() );
- ASSERT( !c->matcher() );
- ASSERT_EQUALS( 5, c->current().getIntField( "_id" ) );
- ASSERT( !c->advance() );
- }
- };
-
- class AllowOptimalIdPlan : public Base {
- string expectedType() const { return "BtreeCursor _id_"; }
- BSONObj query() const { return BSON( "_id" << 5 ); }
- };
-
- class HintedIdForQuery : public Base {
- public:
- HintedIdForQuery( const BSONObj &query ) : _query( query ) {
- _cli.remove( ns(), BSONObj() );
- _cli.ensureIndex( ns(), BSON( "a" << 1 ) );
- _cli.insert( ns(), BSON( "_id" << 1 << "a" << 1 ) );
- }
- string expectedType() const { return "BtreeCursor _id_"; }
- BSONObj query() const { return _query; }
- void check( const shared_ptr<Cursor> &c ) {
- ASSERT( c->ok() );
- ASSERT( c->currentMatches() );
- ASSERT_EQUALS( 1, c->current().getIntField( "_id" ) );
- ASSERT( !c->advance() );
- }
- private:
- BSONObj _query;
- };
-
- class HintedNaturalForQuery : public Base {
- public:
- HintedNaturalForQuery( const BSONObj &query ) : _query( query ) {
- _cli.dropCollection( ns() );
- _cli.createCollection( ns(), 1024, true );
- _cli.ensureIndex( ns(), BSON( "a" << 1 ) );
- _cli.insert( ns(), BSON( "_id" << 1 << "a" << 1 ) );
- }
- ~HintedNaturalForQuery() {
- _cli.dropCollection( ns() );
- }
- string expectedType() const { return "ForwardCappedCursor"; }
- BSONObj query() const { return _query; }
- void check( const shared_ptr<Cursor> &c ) {
- ASSERT( c->ok() );
- ASSERT( c->currentMatches() );
- ASSERT_EQUALS( 1, c->current().getIntField( "_id" ) );
- ASSERT( !c->advance() );
- }
- private:
- BSONObj _query;
- };
-
- } // namespace IdElseNatural
-
- /**
- * Generating a cursor for an invalid query asserts, even if the collection is empty or
- * missing.
- */
- class MatcherValidation : public Base {
- public:
- void run() {
- // Matcher validation with an empty collection.
- _cli.remove( ns(), BSONObj() );
- checkInvalidQueryAssertions();
-
- // Matcher validation with a missing collection.
- _cli.dropCollection( ns() );
- checkInvalidQueryAssertions();
- }
- private:
- static void checkInvalidQueryAssertions() {
- Client::ReadContext ctx( ns() );
-
- // An invalid query generating a single query plan asserts.
- BSONObj invalidQuery = fromjson( "{$and:[{$atomic:true}]}" );
- assertInvalidQueryAssertion( invalidQuery );
-
- // An invalid query generating multiple query plans asserts.
- BSONObj invalidIdQuery = fromjson( "{_id:0,$and:[{$atomic:true}]}" );
- assertInvalidQueryAssertion( invalidIdQuery );
- }
- static void assertInvalidQueryAssertion( const BSONObj &query ) {
- ASSERT_THROWS( getOptimizedCursor( ns(), query, BSONObj() ),
- UserException );
- }
- };
-
- class RequestMatcherFalse : public QueryPlanSelectionPolicy {
- virtual string name() const { return "RequestMatcherFalse"; }
- virtual bool requestMatcher() const { return false; }
- } _requestMatcherFalse;
-
- /**
- * A Cursor returned by getOptimizedCursor() may or may not have a
- * matcher(). A Matcher will generally exist if required to match the provided query or
- * if specifically requested.
- */
- class MatcherSet : public Base {
- public:
- MatcherSet() {
- _cli.insert( ns(), BSON( "a" << 2 << "b" << 3 ) );
- _cli.ensureIndex( ns(), BSON( "a" << 1 ) );
- }
- void run() {
- // No matcher is set for an empty query.
- ASSERT( !hasMatcher( BSONObj(), false ) );
- // No matcher is set for an empty query, even if a matcher is requested.
- ASSERT( !hasMatcher( BSONObj(), true ) );
- // No matcher is set for an exact key match indexed query.
- ASSERT( !hasMatcher( BSON( "a" << 2 ), false ) );
- // No matcher is set for an exact key match indexed query, unless one is requested.
- ASSERT( hasMatcher( BSON( "a" << 2 ), true ) );
- // A matcher is set for a non exact key match indexed query.
- ASSERT( hasMatcher( BSON( "a" << 2 << "b" << 3 ), false ) );
- }
- private:
- bool hasMatcher( const BSONObj& query, bool requestMatcher ) {
- Client::ReadContext ctx( ns() );
- shared_ptr<Cursor> cursor = getOptimizedCursor( ns(),
- query,
- BSONObj(),
- requestMatcher ?
- QueryPlanSelectionPolicy::any():
- _requestMatcherFalse );
- return cursor->matcher();
- }
- };
-
- /**
- * Even though a Matcher may not be used to perform matching when requestMatcher == false, a
- * Matcher must be created because the Matcher's constructor performs query validation.
- */
- class MatcherValidate : public Base {
- public:
- void run() {
- Client::ReadContext ctx( ns() );
- // An assertion is triggered because { a:undefined } is an invalid query, even
- // though no matcher is required.
- ASSERT_THROWS( getOptimizedCursor( ns(),
- fromjson( "{a:undefined}" ),
- BSONObj(),
- _requestMatcherFalse ),
- UserException );
- }
- };
-
- class RequestIntervalCursorTrue : public QueryPlanSelectionPolicy {
- virtual string name() const { return "RequestIntervalCursorTrue"; }
- virtual bool requestIntervalCursor() const { return true; }
- } _requestIntervalCursorTrue;
-
- /** An IntervalBtreeCursor is selected when requested by a QueryPlanSelectionPolicy. */
- class IntervalCursor : public Base {
- public:
- IntervalCursor() {
- _cli.insert( ns(), BSON( "a" << 2 << "b" << 3 ) );
- _cli.ensureIndex( ns(), BSON( "a" << 1 ) );
- }
- void run() {
- Client::ReadContext ctx( ns() );
- shared_ptr<Cursor> cursor = getOptimizedCursor( ns(),
- BSON( "a" << 1 ),
- BSONObj(),
- _requestIntervalCursorTrue );
- ASSERT_EQUALS( "IntervalBtreeCursor", cursor->toString() );
- }
- };
-
- } // namespace GetCursor
-
- namespace Explain {
-
- class ClearRecordedIndex : public QueryOptimizerCursorTests::Base {
- public:
- void run() {
- _cli.ensureIndex( ns(), BSON( "a" << 1 ) );
- _cli.ensureIndex( ns(), BSON( "b" << 1 ) );
- _cli.insert( ns(), BSON( "a" << 1 << "b" << 1 ) );
- _cli.insert( ns(), BSON( "a" << 1 << "b" << 1 ) );
-
- Lock::DBWrite lk(ns());
- Client::Context ctx( ns() );
- BSONObj query = BSON( "a" << 1 << "b" << 1 );
- shared_ptr<Cursor> c = getOptimizedCursor( ns(), query );
- while( c->advance() );
- shared_ptr<ParsedQuery> parsedQuery
- ( new ParsedQuery( ns(), 0, 0, 0,
- BSON( "$query" << query << "$explain" << true ),
- BSONObj() ) );
- c = getOptimizedCursor( ns(),
- query,
- BSONObj(),
- QueryPlanSelectionPolicy::any(),
- parsedQuery,
- false );
- set<BSONObj> indexKeys;
- while( c->ok() ) {
- indexKeys.insert( c->indexKeyPattern() );
- c->advance();
- }
- ASSERT( indexKeys.size() > 1 );
- }
- };
-
- class Base : public QueryOptimizerCursorTests::Base {
- public:
- virtual ~Base() {}
- void run() {
- setupCollection();
-
- Lock::DBWrite lk(ns());
- Client::Context ctx( ns() );
- shared_ptr<ParsedQuery> parsedQuery
- ( new ParsedQuery( ns(), 0, 0, 0,
- BSON( "$query" << query() << "$explain" << true ),
- fields() ) );
- _cursor =
- dynamic_pointer_cast<QueryOptimizerCursor>
- ( getOptimizedCursor( ns(),
- query(),
- BSONObj(),
- QueryPlanSelectionPolicy::any(),
- parsedQuery,
- false ) );
- ASSERT( _cursor );
-
- handleCursor();
-
- _explainInfo = _cursor->explainQueryInfo();
- _explain = _explainInfo->bson();
-
- checkExplain();
- }
- protected:
- virtual void setupCollection() {
- _cli.ensureIndex( ns(), BSON( "a" << 1 ) );
- _cli.insert( ns(), BSON( "a" << 1 << "b" << 1 ) );
- _cli.insert( ns(), BSON( "a" << 2 << "b" << 1 ) );
- }
- virtual BSONObj query() const { return BSON( "a" << 1 << "b" << 1 ); }
- virtual BSONObj fields() const { return BSONObj(); }
- virtual void handleCursor() {
- }
- virtual void checkExplain() {
- }
- shared_ptr<QueryOptimizerCursor> _cursor;
- shared_ptr<ExplainQueryInfo> _explainInfo;
- BSONObj _explain;
- };
-
- class Initial : public Base {
- virtual void checkExplain() {
- ASSERT( !_explain[ "cursor" ].eoo() );
- ASSERT( !_explain[ "isMultiKey" ].Bool() );
- ASSERT_EQUALS( 0, _explain[ "n" ].number() );
- ASSERT_EQUALS( 0, _explain[ "nscannedObjectsAllPlans" ].number() );
- ASSERT_EQUALS( 2, _explain[ "nscannedAllPlans" ].number() );
- ASSERT( !_explain[ "scanAndOrder" ].Bool() );
- ASSERT( !_explain[ "indexOnly" ].Bool() );
- ASSERT_EQUALS( 0, _explain[ "nYields" ].Int() );
- ASSERT_EQUALS( 0, _explain[ "nChunkSkips" ].number() );
- ASSERT( !_explain[ "millis" ].eoo() );
- ASSERT( !_explain[ "indexBounds" ].eoo() );
- ASSERT_EQUALS( 2U, _explain[ "allPlans" ].Array().size() );
-
- BSONObj plan1 = _explain[ "allPlans" ].Array()[ 0 ].Obj();
- ASSERT_EQUALS( "BtreeCursor a_1", plan1[ "cursor" ].String() );
- ASSERT_EQUALS( 0, plan1[ "n" ].number() );
- ASSERT_EQUALS( 0, plan1[ "nscannedObjects" ].number() );
- ASSERT_EQUALS( 1, plan1[ "nscanned" ].number() );
- ASSERT_EQUALS( fromjson( "{a:[[1,1]]}" ), plan1[ "indexBounds" ].Obj() );
-
- BSONObj plan2 = _explain[ "allPlans" ].Array()[ 1 ].Obj();
- ASSERT_EQUALS( "BasicCursor", plan2[ "cursor" ].String() );
- ASSERT_EQUALS( 0, plan2[ "n" ].number() );
- ASSERT_EQUALS( 0, plan2[ "nscannedObjects" ].number() );
- ASSERT_EQUALS( 1, plan2[ "nscanned" ].number() );
- ASSERT_EQUALS( BSONObj(), plan2[ "indexBounds" ].Obj() );
- }
- };
-
- class Empty : public Base {
- virtual void setupCollection() {
- _cli.ensureIndex( ns(), BSON( "a" << 1 ) );
- }
- virtual void handleCursor() {
- ASSERT( !_cursor->ok() );
- }
- virtual void checkExplain() {
- ASSERT( !_explain[ "cursor" ].eoo() );
- ASSERT( !_explain[ "isMultiKey" ].Bool() );
- ASSERT_EQUALS( 0, _explain[ "n" ].number() );
- ASSERT_EQUALS( 0, _explain[ "nscannedObjectsAllPlans" ].number() );
- ASSERT_EQUALS( 0, _explain[ "nscannedAllPlans" ].number() );
- ASSERT( !_explain[ "scanAndOrder" ].Bool() );
- ASSERT( !_explain[ "indexOnly" ].Bool() );
- ASSERT_EQUALS( 0, _explain[ "nYields" ].Int() );
- ASSERT_EQUALS( 0, _explain[ "nChunkSkips" ].number() );
- ASSERT( !_explain[ "millis" ].eoo() );
- ASSERT( !_explain[ "indexBounds" ].eoo() );
- ASSERT_EQUALS( 2U, _explain[ "allPlans" ].Array().size() );
-
- BSONObj plan1 = _explain[ "allPlans" ].Array()[ 0 ].Obj();
- ASSERT_EQUALS( "BtreeCursor a_1", plan1[ "cursor" ].String() );
- ASSERT_EQUALS( 0, plan1[ "n" ].number() );
- ASSERT_EQUALS( 0, plan1[ "nscannedObjects" ].number() );
- ASSERT_EQUALS( 0, plan1[ "nscanned" ].number() );
- ASSERT_EQUALS( fromjson( "{a:[[1,1]]}" ), plan1[ "indexBounds" ].Obj() );
-
- BSONObj plan2 = _explain[ "allPlans" ].Array()[ 1 ].Obj();
- ASSERT_EQUALS( "BasicCursor", plan2[ "cursor" ].String() );
- ASSERT_EQUALS( 0, plan2[ "n" ].number() );
- ASSERT_EQUALS( 0, plan2[ "nscannedObjects" ].number() );
- ASSERT_EQUALS( 0, plan2[ "nscanned" ].number() );
- ASSERT_EQUALS( BSONObj(), plan2[ "indexBounds" ].Obj() );
- }
- };
-
- class SimpleCount : public Base {
- virtual void handleCursor() {
- while( _cursor->ok() ) {
- MatchDetails matchDetails;
- if ( _cursor->currentMatches( &matchDetails ) &&
- !_cursor->getsetdup( _cursor->currLoc() ) ) {
- _cursor->noteIterate( true, true, false );
- }
- else {
- _cursor->noteIterate( false, matchDetails.hasLoadedRecord(), false );
- }
- _cursor->advance();
- }
- }
- virtual void checkExplain() {
- ASSERT_EQUALS( "BtreeCursor a_1", _explain[ "cursor" ].String() );
- ASSERT_EQUALS( 1, _explain[ "n" ].number() );
- ASSERT_EQUALS( 2, _explain[ "nscannedObjectsAllPlans" ].number() );
- ASSERT_EQUALS( 2, _explain[ "nscannedAllPlans" ].number() );
-
- BSONObj plan1 = _explain[ "allPlans" ].Array()[ 0 ].Obj();
- ASSERT_EQUALS( 1, plan1[ "n" ].number() );
- ASSERT_EQUALS( 1, plan1[ "nscannedObjects" ].number() );
- ASSERT_EQUALS( 1, plan1[ "nscanned" ].number() );
-
- BSONObj plan2 = _explain[ "allPlans" ].Array()[ 1 ].Obj();
- ASSERT_EQUALS( 1, plan2[ "n" ].number() );
- ASSERT_EQUALS( 1, plan2[ "nscannedObjects" ].number() );
- ASSERT_EQUALS( 1, plan2[ "nscanned" ].number() );
- }
- };
-
- class IterateOnly : public Base {
- virtual BSONObj query() const { return BSON( "a" << GT << 0 << "b" << 1 ); }
- virtual void handleCursor() {
- while( _cursor->ok() ) {
- _cursor->advance();
- }
- }
- virtual void checkExplain() {
- ASSERT_EQUALS( "BtreeCursor a_1", _explain[ "cursor" ].String() );
- ASSERT_EQUALS( 0, _explain[ "n" ].number() ); // needs to be set with noteIterate()
- ASSERT_EQUALS( 0, _explain[ "nscannedObjectsAllPlans" ].number() );
- ASSERT_EQUALS( 3, _explain[ "nscannedAllPlans" ].number() );
-
- BSONObj plan1 = _explain[ "allPlans" ].Array()[ 0 ].Obj();
- ASSERT_EQUALS( 2, plan1[ "n" ].number() );
- ASSERT_EQUALS( 2, plan1[ "nscannedObjects" ].number() );
- ASSERT_EQUALS( 2, plan1[ "nscanned" ].number() );
-
- // Not fully incremented without checking for matches.
- BSONObj plan2 = _explain[ "allPlans" ].Array()[ 1 ].Obj();
- ASSERT_EQUALS( 1, plan2[ "n" ].number() );
- ASSERT_EQUALS( 1, plan2[ "nscannedObjects" ].number() );
- ASSERT_EQUALS( 1, plan2[ "nscanned" ].number() );
- }
- };
-
- class ExtraMatchChecks : public Base {
- virtual BSONObj query() const { return BSON( "a" << GT << 0 << "b" << 1 ); }
- virtual void handleCursor() {
- while( _cursor->ok() ) {
- _cursor->currentMatches();
- _cursor->currentMatches();
- _cursor->currentMatches();
- _cursor->advance();
- }
- }
- virtual void checkExplain() {
- ASSERT_EQUALS( "BtreeCursor a_1", _explain[ "cursor" ].String() );
- ASSERT_EQUALS( 0, _explain[ "n" ].number() ); // needs to be set with noteIterate()
- ASSERT_EQUALS( 0, _explain[ "nscannedObjectsAllPlans" ].number() );
- ASSERT_EQUALS( 4, _explain[ "nscannedAllPlans" ].number() );
-
- BSONObj plan1 = _explain[ "allPlans" ].Array()[ 0 ].Obj();
- ASSERT_EQUALS( 2, plan1[ "n" ].number() );
- // nscannedObjects are not deduped.
- ASSERT_EQUALS( 6, plan1[ "nscannedObjects" ].number() );
- ASSERT_EQUALS( 2, plan1[ "nscanned" ].number() );
-
- BSONObj plan2 = _explain[ "allPlans" ].Array()[ 1 ].Obj();
- ASSERT_EQUALS( 2, plan2[ "n" ].number() );
- ASSERT_EQUALS( 6, plan2[ "nscannedObjects" ].number() );
- ASSERT_EQUALS( 2, plan2[ "nscanned" ].number() );
- }
- };
-
- class PartialIteration : public Base {
- virtual void handleCursor() {
- _cursor->currentMatches();
- _cursor->advance();
- _cursor->noteIterate( true, true, false );
- }
- virtual void checkExplain() {
- ASSERT_EQUALS( "BtreeCursor a_1", _explain[ "cursor" ].String() );
- ASSERT_EQUALS( 1, _explain[ "n" ].number() );
- ASSERT_EQUALS( 1, _explain[ "nscannedObjectsAllPlans" ].number() );
- ASSERT_EQUALS( 2, _explain[ "nscannedAllPlans" ].number() );
-
- BSONObj plan1 = _explain[ "allPlans" ].Array()[ 0 ].Obj();
- ASSERT_EQUALS( 1, plan1[ "n" ].number() );
- ASSERT_EQUALS( 1, plan1[ "nscannedObjects" ].number() );
- ASSERT_EQUALS( 1, plan1[ "nscanned" ].number() );
-
- BSONObj plan2 = _explain[ "allPlans" ].Array()[ 1 ].Obj();
- ASSERT_EQUALS( 0, plan2[ "n" ].number() );
- ASSERT_EQUALS( 0, plan2[ "nscannedObjects" ].number() );
- ASSERT_EQUALS( 1, plan2[ "nscanned" ].number() );
- }
- };
-
- class Multikey : public Base {
- virtual void setupCollection() {
- _cli.ensureIndex( ns(), BSON( "a" << 1 ) );
- _cli.insert( ns(), BSON( "a" << BSON_ARRAY( 1 << 2 ) ) );
- }
- virtual void handleCursor() {
- while( _cursor->advance() );
- }
- virtual void checkExplain() {
- ASSERT( _explain[ "isMultiKey" ].Bool() );
- }
- };
-
- class MultikeyInitial : public Base {
- virtual void setupCollection() {
- _cli.ensureIndex( ns(), BSON( "a" << 1 ) );
- _cli.insert( ns(), BSON( "a" << BSON_ARRAY( 1 << 2 ) ) );
- }
- virtual void checkExplain() {
- ASSERT( _explain[ "isMultiKey" ].Bool() );
- }
- };
-
- class BecomesMultikey : public Base {
- virtual void setupCollection() {
- _cli.ensureIndex( ns(), BSON( "a" << 1 ) );
- _cli.insert( ns(), BSON( "a" << 1 ) );
- }
- virtual void checkExplain() {
- ASSERT( !_explain[ "isMultiKey" ].Bool() );
-
- _cursor->prepareToYield();
- {
- dbtemprelease t;
- _cli.insert( ns(), BSON( "a" << BSON_ARRAY( 1 << 2 ) ) );
- }
- _cursor->recoverFromYield();
- _cursor->currentMatches();
- ASSERT( _explainInfo->bson()[ "isMultiKey" ].Bool() );
- }
- };
-
- class CountAndYield : public Base {
- virtual void setupCollection() {
- _cli.ensureIndex( ns(), BSON( "a" << 1 ) );
- for( int i = 0; i < 5; ++i ) {
- _cli.insert( ns(), BSON( "a" << 1 << "b" << 1 ) );
- }
- }
- virtual void handleCursor() {
- _nYields = 0;
- while( _cursor->ok() ) {
- _cursor->prepareToYield();
- ++_nYields;
- _cursor->recoverFromYield();
- _cursor->noteYield();
- MatchDetails matchDetails;
- if ( _cursor->currentMatches( &matchDetails ) &&
- !_cursor->getsetdup( _cursor->currLoc() ) ) {
- _cursor->noteIterate( true, true, false );
- }
- else {
- _cursor->noteIterate( false, matchDetails.hasLoadedRecord(), false );
- }
- _cursor->advance();
- }
- }
- virtual void checkExplain() {
- ASSERT( _nYields > 0 );
- ASSERT_EQUALS( _nYields, _explain[ "nYields" ].Int() );
-
- ASSERT_EQUALS( "BtreeCursor a_1", _explain[ "cursor" ].String() );
- ASSERT_EQUALS( 5, _explain[ "n" ].number() );
- ASSERT_EQUALS( 10, _explain[ "nscannedObjectsAllPlans" ].number() );
- ASSERT_EQUALS( 10, _explain[ "nscannedAllPlans" ].number() );
-
- BSONObj plan1 = _explain[ "allPlans" ].Array()[ 0 ].Obj();
- ASSERT_EQUALS( 5, plan1[ "n" ].number() );
- ASSERT_EQUALS( 5, plan1[ "nscannedObjects" ].number() );
- ASSERT_EQUALS( 5, plan1[ "nscanned" ].number() );
-
- BSONObj plan2 = _explain[ "allPlans" ].Array()[ 1 ].Obj();
- ASSERT_EQUALS( 5, plan2[ "n" ].number() );
- ASSERT_EQUALS( 5, plan2[ "nscannedObjects" ].number() );
- ASSERT_EQUALS( 5, plan2[ "nscanned" ].number() );
- }
- protected:
- int _nYields;
- };
-
- class MultipleClauses : public CountAndYield {
- virtual void setupCollection() {
- _cli.ensureIndex( ns(), BSON( "a" << 1 ) );
- _cli.ensureIndex( ns(), BSON( "b" << 1 ) );
- for( int i = 0; i < 4; ++i ) {
- _cli.insert( ns(), BSON( "a" << 1 << "b" << 1 ) );
- }
- _cli.insert( ns(), BSON( "a" << 0 << "b" << 1 ) );
- }
- virtual BSONObj query() const { return fromjson( "{$or:[{a:1,c:null},{b:1,c:null}]}"); }
- virtual void checkExplain() {
- ASSERT_EQUALS( 18, _nYields );
-
- ASSERT_EQUALS( 5, _explain[ "n" ].number() );
- ASSERT_EQUALS( 18, _explain[ "nscannedObjectsAllPlans" ].number() );
- ASSERT_EQUALS( 18, _explain[ "nscannedAllPlans" ].number() );
-
- BSONObj clause1 = _explain[ "clauses" ].Array()[ 0 ].Obj();
- ASSERT_EQUALS( "BtreeCursor a_1", clause1[ "cursor" ].String() );
- ASSERT_EQUALS( 4, clause1[ "n" ].number() );
- ASSERT_EQUALS( 8, clause1[ "nscannedObjectsAllPlans" ].number() );
- ASSERT_EQUALS( 8, clause1[ "nscannedAllPlans" ].number() );
- ASSERT_EQUALS( 8, clause1[ "nYields" ].Int() );
-
- BSONObj c1plan1 = clause1[ "allPlans" ].Array()[ 0 ].Obj();
- ASSERT_EQUALS( "BtreeCursor a_1", c1plan1[ "cursor" ].String() );
- ASSERT_EQUALS( 4, c1plan1[ "n" ].number() );
- ASSERT_EQUALS( 4, c1plan1[ "nscannedObjects" ].number() );
- ASSERT_EQUALS( 4, c1plan1[ "nscanned" ].number() );
-
- BSONObj c1plan2 = clause1[ "allPlans" ].Array()[ 1 ].Obj();
- ASSERT_EQUALS( "BasicCursor", c1plan2[ "cursor" ].String() );
- ASSERT_EQUALS( 4, c1plan2[ "n" ].number() );
- ASSERT_EQUALS( 4, c1plan2[ "nscannedObjects" ].number() );
- ASSERT_EQUALS( 4, c1plan2[ "nscanned" ].number() );
-
- BSONObj clause2 = _explain[ "clauses" ].Array()[ 1 ].Obj();
- ASSERT_EQUALS( "BtreeCursor b_1", clause2[ "cursor" ].String() );
- ASSERT_EQUALS( 1, clause2[ "n" ].number() );
- ASSERT_EQUALS( 10, clause2[ "nscannedObjectsAllPlans" ].number() );
- ASSERT_EQUALS( 10, clause2[ "nscannedAllPlans" ].number() );
- ASSERT_EQUALS( 10, clause2[ "nYields" ].Int() );
-
- BSONObj c2plan1 = clause2[ "allPlans" ].Array()[ 0 ].Obj();
- ASSERT_EQUALS( "BtreeCursor b_1", c2plan1[ "cursor" ].String() );
- ASSERT_EQUALS( 5, c2plan1[ "n" ].number() );
- ASSERT_EQUALS( 5, c2plan1[ "nscannedObjects" ].number() );
- ASSERT_EQUALS( 5, c2plan1[ "nscanned" ].number() );
-
- BSONObj c2plan2 = clause2[ "allPlans" ].Array()[ 1 ].Obj();
- ASSERT_EQUALS( "BasicCursor", c2plan2[ "cursor" ].String() );
- ASSERT_EQUALS( 5, c2plan2[ "n" ].number() );
- ASSERT_EQUALS( 5, c2plan2[ "nscannedObjects" ].number() );
- ASSERT_EQUALS( 5, c2plan2[ "nscanned" ].number() );
- }
- };
-
- class MultiCursorTakeover : public CountAndYield {
- virtual void setupCollection() {
- _cli.ensureIndex( ns(), BSON( "a" << 1 ) );
- for( int i = 20; i >= 1; --i ) {
- for( int j = 0; j < i; ++j ) {
- _cli.insert( ns(), BSON( "a" << i ) );
- }
- }
- }
- virtual BSONObj query() const {
- BSONArrayBuilder bab;
- for( int i = 20; i >= 1; --i ) {
- bab << BSON( "a" << i );
- }
- return BSON( "$or" << bab.arr() );
- }
- virtual void checkExplain() {
- ASSERT_EQUALS( 20U, _explain[ "clauses" ].Array().size() );
- for( int i = 20; i >= 1; --i ) {
- BSONObj clause = _explain[ "clauses" ].Array()[ 20-i ].Obj();
- ASSERT_EQUALS( "BtreeCursor a_1", clause[ "cursor" ].String() );
- ASSERT_EQUALS( BSON( "a" << BSON_ARRAY( BSON_ARRAY( i << i ) ) ),
- clause[ "indexBounds" ].Obj() );
- ASSERT_EQUALS( i, clause[ "n" ].number() );
- ASSERT_EQUALS( i, clause[ "nscannedObjects" ].number() );
- ASSERT_EQUALS( i, clause[ "nscanned" ].number() );
- ASSERT_EQUALS( i, clause[ "nYields" ].Int() );
-
- ASSERT_EQUALS( 1U, clause[ "allPlans" ].Array().size() );
- BSONObj plan = clause[ "allPlans" ].Array()[ 0 ].Obj();
- ASSERT_EQUALS( i, plan[ "n" ].number() );
- ASSERT_EQUALS( i, plan[ "nscannedObjects" ].number() );
- ASSERT_EQUALS( i, plan[ "nscanned" ].number() );
- }
-
- ASSERT_EQUALS( 210, _explain[ "n" ].number() );
- ASSERT_EQUALS( 210, _explain[ "nscannedObjects" ].number() );
- ASSERT_EQUALS( 210, _explain[ "nscanned" ].number() );
- }
- };
-
- class NChunkSkipsTakeover : public Base {
- virtual void setupCollection() {
- _cli.ensureIndex( ns(), BSON( "a" << 1 ) );
- for( int i = 0; i < 200; ++i ) {
- _cli.insert( ns(), BSON( "a" << 1 << "b" << 1 ) );
- }
- for( int i = 0; i < 200; ++i ) {
- _cli.insert( ns(), BSON( "a" << 2 << "b" << 2 ) );
- }
- }
- virtual BSONObj query() const { return fromjson( "{$or:[{a:1,b:1},{a:2,b:2}]}" ); }
- virtual void handleCursor() {
- ASSERT_EQUALS( "QueryOptimizerCursor", _cursor->toString() );
- int i = 0;
- while( _cursor->ok() ) {
- if ( _cursor->currentMatches() && !_cursor->getsetdup( _cursor->currLoc() ) ) {
- _cursor->noteIterate( true, true, i++ %2 == 0 );
- }
- _cursor->advance();
- }
- }
- virtual void checkExplain() {
- // Historically, nChunkSkips has been excluded from the query summary.
- ASSERT( _explain[ "nChunkSkips" ].eoo() );
-
- BSONObj clause0 = _explain[ "clauses" ].Array()[ 0 ].Obj();
- ASSERT_EQUALS( 100, clause0[ "nChunkSkips" ].number() );
- BSONObj plan0 = clause0[ "allPlans" ].Array()[ 0 ].Obj();
- ASSERT( plan0[ "nChunkSkips" ].eoo() );
-
- BSONObj clause1 = _explain[ "clauses" ].Array()[ 1 ].Obj();
- ASSERT_EQUALS( 100, clause1[ "nChunkSkips" ].number() );
- BSONObj plan1 = clause1[ "allPlans" ].Array()[ 0 ].Obj();
- ASSERT( plan1[ "nChunkSkips" ].eoo() );
- }
- };
-
- class CoveredIndex : public Base {
- virtual BSONObj query() const { return fromjson( "{$or:[{a:1},{a:2}]}" ); }
- virtual BSONObj fields() const { return BSON( "_id" << 0 << "a" << 1 ); }
- virtual void handleCursor() {
- ASSERT_EQUALS( "QueryOptimizerCursor", _cursor->toString() );
- while( _cursor->advance() );
- }
- virtual void checkExplain() {
- BSONObj clause0 = _explain[ "clauses" ].Array()[ 0 ].Obj();
- ASSERT( clause0[ "indexOnly" ].Bool() );
-
- BSONObj clause1 = _explain[ "clauses" ].Array()[ 1 ].Obj();
- ASSERT( clause1[ "indexOnly" ].Bool() );
- }
- };
-
- class CoveredIndexTakeover : public Base {
- virtual void setupCollection() {
- _cli.ensureIndex( ns(), BSON( "a" << 1 ) );
- for( int i = 0; i < 150; ++i ) {
- _cli.insert( ns(), BSON( "a" << 1 ) );
- }
- _cli.insert( ns(), BSON( "a" << 2 ) );
- }
- virtual BSONObj query() const { return fromjson( "{$or:[{a:1},{a:2}]}" ); }
- virtual BSONObj fields() const { return BSON( "_id" << 0 << "a" << 1 ); }
- virtual void handleCursor() {
- ASSERT_EQUALS( "QueryOptimizerCursor", _cursor->toString() );
- while( _cursor->advance() );
- }
- virtual void checkExplain() {
- BSONObj clause0 = _explain[ "clauses" ].Array()[ 0 ].Obj();
- ASSERT( clause0[ "indexOnly" ].Bool() );
-
- BSONObj clause1 = _explain[ "clauses" ].Array()[ 1 ].Obj();
- ASSERT( clause1[ "indexOnly" ].Bool() );
- }
- };
-
- /**
- * Check that the plan with the most matches is reported at the top of the explain output
- * in the absence of a done or picked plan.
- */
- class VirtualPickedPlan : public Base {
- public:
- void run() {
- Client::WriteContext ctx( ns() );
-
- _cli.ensureIndex( ns(), BSON( "a" << 1 ) );
- _cli.ensureIndex( ns(), BSON( "b" << 1 ) );
- _cli.ensureIndex( ns(), BSON( "c" << 1 ) );
-
- shared_ptr<Cursor> aCursor( getOptimizedCursor( ns(), BSON( "a" << 1 ) ) );
- shared_ptr<Cursor> bCursor( getOptimizedCursor( ns(), BSON( "b" << 1 ) ) );
- shared_ptr<Cursor> cCursor( getOptimizedCursor( ns(), BSON( "c" << 1 ) ) );
-
- shared_ptr<ExplainPlanInfo> aPlan( new ExplainPlanInfo() );
- aPlan->notePlan( *aCursor, false, false );
- shared_ptr<ExplainPlanInfo> bPlan( new ExplainPlanInfo() );
- bPlan->notePlan( *bCursor, false, false );
- shared_ptr<ExplainPlanInfo> cPlan( new ExplainPlanInfo() );
- cPlan->notePlan( *cCursor, false, false );
-
- aPlan->noteIterate( true, false, *aCursor ); // one match a
- bPlan->noteIterate( true, false, *bCursor ); // two matches b
- bPlan->noteIterate( true, false, *bCursor );
- cPlan->noteIterate( true, false, *cCursor ); // one match c
-
- shared_ptr<ExplainClauseInfo> clause( new ExplainClauseInfo() );
- clause->addPlanInfo( aPlan );
- clause->addPlanInfo( bPlan );
- clause->addPlanInfo( cPlan );
-
- ASSERT_EQUALS( "BtreeCursor b_1", clause->bson()[ "cursor" ].String() );
- }
- };
-
- /** Simple check that an explain result can contain a large value of n. */
- class LargeN : public Base {
- public:
- void run() {
- Lock::GlobalWrite lk;
-
- Client::Context ctx( ns() );
-
- shared_ptr<Cursor> cursor( getOptimizedCursor( ns(), BSONObj() ) );
- ExplainSinglePlanQueryInfo explainHelper;
- explainHelper.notePlan( *cursor, false, false );
- explainHelper.noteIterate( false, false, false, *cursor );
-
- shared_ptr<ExplainQueryInfo> explain = explainHelper.queryInfo();
- explain->reviseN( 3000000000000LL );
-
- // large n could be stored either in bson as long or double
- // retrieve value using safeNumberLong() so that we don't have to guess
- // internal type
- BSONObj obj = explain->bson();
- BSONElement e = obj.getField( "n" );
- long long n = e.safeNumberLong();
- ASSERT_EQUALS( 3000000000000LL, n );
- }
- };
-
- /**
- * Base class to test yield count when a cursor yields and its current iterate is deleted.
- */
- class NYieldsAdvanceBase : public Base {
- protected:
- virtual int aValueToDelete() const = 0;
- virtual int numDocuments() const {
- // Above the takeover threshold at 101 matches.
- return 200;
- }
- private:
- virtual void setupCollection() {
- // Insert some matching documents.
- for( int i = 0; i < numDocuments(); ++i ) {
- _cli.insert( ns(), BSON( "a" << i << "b" << 0 ) );
- }
- _cli.ensureIndex( ns(), BSON( "a" << 1 ) );
- }
- virtual BSONObj query() const {
- // A query that will match all inserted documents and generate a
- // QueryOptimizerCursor with a single candidate plan, on the a:1 index.
- return BSON( "a" << GTE << 0 << "b" << 0 );
- }
- virtual void handleCursor() {
- _clientCursor.reset
- ( new mongo::ClientCursor( QueryOption_NoCursorTimeout, _cursor,
- ns() ) );
- // Advance to the document with the specified 'a' value.
- while( _cursor->current()[ "a" ].number() != aValueToDelete() ) {
- _cursor->advance();
- }
-
- // Yield the cursor.
- mongo::ClientCursor::YieldData yieldData;
- _clientCursor->prepareToYield( yieldData );
-
- // Remove the document with the specified 'a' value.
- _cli.remove( ns(), BSON( "a" << aValueToDelete() ) );
-
- // Recover the cursor and note that a yield occurred.
- _clientCursor->recoverFromYield( yieldData );
- _cursor->noteYield();
-
- // Exhaust the cursor (not strictly necessary).
- while( _cursor->advance() );
- }
- virtual void checkExplain() {
- // The cursor was yielded once.
- ASSERT_EQUALS( 1, _explain[ "nYields" ].number() );
- }
- mongo::ClientCursorHolder _clientCursor;
- };
-
- /** nYields reporting of a QueryOptimizerCursor before it enters takeover mode. */
- class NYieldsAdvanceBasic : public NYieldsAdvanceBase {
- virtual int aValueToDelete() const {
- // Before the MultiCursor takes over at 101 matches.
- return 50;
- }
- };
-
- /** nYields reporting of a QueryOptimizerCursor after it enters takeover mode. */
- class NYieldsAdvanceTakeover : public NYieldsAdvanceBase {
- virtual int aValueToDelete() const { return 150; }
- };
-
- /** nYields reporting when a cursor is exhausted during a yield. */
- class NYieldsAdvanceDeleteLast : public NYieldsAdvanceBase {
- virtual int aValueToDelete() const { return 0; }
- virtual int numDocuments() const { return 1; }
- };
-
- /** nYields reporting when a takeover cursor is exhausted during a yield. */
- class NYieldsAdvanceDeleteLastTakeover : public NYieldsAdvanceBase {
- virtual int aValueToDelete() const { return 199; }
- };
-
- /** Explain reporting on yield recovery failure. */
- class YieldRecoveryFailure : public Base {
- virtual void setupCollection() {
- // Insert some matching documents.
- for( int i = 0; i < 10; ++i ) {
- _cli.insert( ns(), BSON( "a" << 1 << "b" << 1 ) );
- }
- _cli.ensureIndex( ns(), BSON( "a" << 1 ) );
- }
- virtual void handleCursor() {
- _clientCursor.reset
- ( new mongo::ClientCursor( QueryOption_NoCursorTimeout, _cursor,
- ns() ) );
-
- // Scan 5 matches.
- int numMatches = 0;
- while( numMatches < 5 ) {
- if ( _cursor->currentMatches() && !_cursor->getsetdup( _cursor->currLoc() ) ) {
- _cursor->noteIterate( true, true, false );
- ++numMatches;
- }
- _cursor->noteIterate( false, true, false );
- _cursor->advance();
- }
-
- // Yield the cursor.
- mongo::ClientCursor::YieldData yieldData;
- _clientCursor->prepareToYield( yieldData );
-
- // Drop the collection and invalidate the ClientCursor.
- {
- dbtemprelease release;
- _cli.dropCollection( ns() );
- }
- ASSERT( !_clientCursor->recoverFromYield( yieldData ) );
-
- // Note a yield after the ClientCursor was invalidated.
- _cursor->noteYield();
- }
- virtual void checkExplain() {
- // The cursor was yielded once.
- ASSERT_EQUALS( 1, _explain[ "nYields" ].number() );
- // Five matches were scanned.
- ASSERT_EQUALS( 5, _explain[ "n" ].number() );
- // Matches were identified for each plan.
- BSONObjIterator plans( _explain[ "allPlans" ].embeddedObject() );
- while( plans.more() ) {
- ASSERT( 0 < plans.next().Obj()[ "n" ].number() );
- }
- }
- mongo::ClientCursorHolder _clientCursor;
- };
-
- } // namespace Explain
-
- class All : public Suite {
- public:
- All() : Suite( "queryoptimizercursor" ) {}
-
- void setupTests() {
- add<CachedMatchCounter::Count>();
- add<CachedMatchCounter::Accumulate>();
- add<CachedMatchCounter::Dedup>();
- add<CachedMatchCounter::Nscanned>();
- add<SmallDupSet::Upgrade>();
- add<SmallDupSet::UpgradeRead>();
- add<SmallDupSet::UpgradeWrite>();
- add<DurationTimerStop>();
- add<Empty>();
- add<Unindexed>();
- add<Basic>();
- add<NoMatch>();
- add<Interleaved>();
- add<NotMatch>();
- add<StopInterleaving>();
- add<TakeoverWithDup>();
- add<TakeoverWithNonMatches>();
- add<TakeoverWithTakeoverDup>();
- add<BasicOr>();
- add<OrFirstClauseEmpty>();
- add<OrSecondClauseEmpty>();
- add<OrMultipleClausesEmpty>();
- add<TakeoverCountOr>();
- add<TakeoverEndOfOrClause>();
- add<TakeoverBeforeEndOfOrClause>();
- add<TakeoverAfterEndOfOrClause>();
- add<ManualMatchingDeduping>();
- add<ManualMatchingUsingCurrKey>();
- add<ManualMatchingDedupingTakeover>();
- add<Singlekey>();
- add<Multikey>();
- add<AddOtherPlans>();
- add<AddOtherPlansDelete>();
- add<AddOtherPlansContinuousDelete>();
- add<AddOtherPlansWhenOptimalBecomesNonOptimal>();
- add<OrRangeElimination>();
- add<OrDedup>();
- add<EarlyDups>();
- add<OrPopInTakeover>();
- add<OrCollectionScanAbort>();
- add<Yield::NoOp>();
- add<Yield::Delete>();
- add<Yield::DeleteContinue>();
- add<Yield::DeleteContinueFurther>();
- add<Yield::Update>();
- add<Yield::Drop>();
- add<Yield::DropOr>();
- add<Yield::RemoveOr>();
- add<Yield::CappedOverwrite>();
- add<Yield::DropIndex>();
- add<Yield::MultiplePlansNoOp>();
- add<Yield::MultiplePlansAdvanceNoOp>();
- add<Yield::MultiplePlansDelete>();
- add<Yield::MultiplePlansDeleteOr>();
- add<Yield::MultiplePlansDeleteOrAdvance>();
- add<Yield::MultiplePlansCappedOverwrite>();
- add<Yield::MultiplePlansCappedOverwriteManual>();
- add<Yield::MultiplePlansCappedOverwriteManual2>();
- add<Yield::Takeover>();
- add<Yield::TakeoverBasic>();
- add<Yield::InactiveCursorAdvance>();
- add<Yield::TakeoverUpdateKeyAtEndOfIteration>();
- add<Yield::TakeoverUpdateKeyAtEndOfClause>();
- add<Yield::TakeoverUpdateKeyPrecedingEmptyClause>();
- add<OrderId>();
- add<OrderMultiIndex>();
- add<OrderReject>();
- add<OrderNatural>();
- add<OrderUnindexed>();
- add<RecordedOrderInvalid>();
- add<KillOp>();
- add<KillOpFirstClause>();
- add<Nscanned>();
- add<TouchEarlierIterate::Basic>();
- add<TouchEarlierIterate::Delete>();
- add<TouchEarlierIterate::DeleteMultiple>();
- add<TouchEarlierIterate::DeleteAfterYield>();
- add<TouchEarlierIterate::Takeover>();
- add<TouchEarlierIterate::TakeoverDeleteMultiple>();
- add<TouchEarlierIterate::UnindexedTakeoverDeleteMultiple>();
- add<TouchEarlierIterate::TakeoverDeleteMultipleMultiAdvance>();
- add<InitialCappedWrapYieldRecoveryFailure>();
- add<TakeoverCappedWrapYieldRecoveryFailure>();
- add<ClientCursor::Invalidate>();
- add<ClientCursor::Timeout>();
- add<ClientCursor::AboutToDeleteRecoverFromYield>();
- add<ClientCursor::AboutToDeletePrepareToYield>();
- add<ClientCursor::Drop>();
- add<AllowOutOfOrderPlan>();
- add<NoTakeoverByOutOfOrderPlan>();
- add<OutOfOrderOnlyTakeover>();
- add<CoveredIndex>();
- add<CoveredIndexTakeover>();
- add<SaveGoodIndex>();
- add<PossibleInOrderPlans>();
- add<PossibleOutOfOrderPlans>();
- add<PossibleBothPlans>();
- add<AbortOutOfOrderPlans>();
- add<AbortOutOfOrderPlanOnLastMatch>();
- add<AbortOutOfOrderPlansBeforeAddOtherPlans>();
- add<TakeoverOrRangeElimination>();
- add<TakeoverOrDedups>();
- add<TakeoverOrDifferentIndex>();
- add<TakeoverOrderedPlanDupsOutOfOrderPlan>();
- add<ElemMatchKey>();
- add<GetCursor::NoConstraints>();
- add<GetCursor::SimpleId>();
- add<GetCursor::OptimalIndex>();
- add<GetCursor::SimpleKeyMatch>();
- add<GetCursor::PreventOutOfOrderPlan>();
- add<GetCursor::AllowOutOfOrderPlan>();
- add<GetCursor::BestSavedOutOfOrder>();
- //add<GetCursor::BestSavedOptimal>();
- //add<GetCursor::BestSavedNotOptimal>();
- add<GetCursor::MultiIndex>();
- add<GetCursor::Hint>();
- add<GetCursor::Snapshot>();
- add<GetCursor::SnapshotCappedColl>();
- add<GetCursor::Min>();
- add<GetCursor::Max>();
- add<GetCursor::RequireIndex::NoConstraints>();
- add<GetCursor::RequireIndex::SimpleId>();
- add<GetCursor::RequireIndex::UnindexedQuery>();
- add<GetCursor::RequireIndex::IndexedQuery>();
- add<GetCursor::RequireIndex::SecondOrClauseIndexed>();
- add<GetCursor::RequireIndex::SecondOrClauseUnindexed>();
- add<GetCursor::RequireIndex::SecondOrClauseUnindexedUndetected>();
- //add<GetCursor::RequireIndex::RecordedUnindexedPlan>();
- add<GetCursor::IdElseNatural::AllowOptimalNaturalPlan>();
- add<GetCursor::IdElseNatural::AllowOptimalIdPlan>();
- add<GetCursor::IdElseNatural::HintedIdForQuery>( BSON( "_id" << 1 ) );
- add<GetCursor::IdElseNatural::HintedIdForQuery>( BSON( "a" << 1 ) );
- add<GetCursor::IdElseNatural::HintedIdForQuery>( BSON( "_id" << 1 << "a" << 1 ) );
- add<GetCursor::IdElseNatural::HintedNaturalForQuery>( BSONObj() );
- // now capped collections have _id index by default, so skip these
- //add<GetCursor::IdElseNatural::HintedNaturalForQuery>( BSON( "_id" << 1 ) );
- //add<GetCursor::IdElseNatural::HintedNaturalForQuery>( BSON( "a" << 1 ) );
- //add<GetCursor::IdElseNatural::HintedNaturalForQuery>( BSON( "_id" << 1 << "a" << 1 ) );
- add<GetCursor::MatcherValidation>();
- add<GetCursor::MatcherSet>();
- add<GetCursor::MatcherValidate>();
- add<GetCursor::IntervalCursor>();
- add<Explain::ClearRecordedIndex>();
- add<Explain::Initial>();
- add<Explain::Empty>();
- add<Explain::SimpleCount>();
- add<Explain::IterateOnly>();
- add<Explain::ExtraMatchChecks>();
- add<Explain::PartialIteration>();
- add<Explain::Multikey>();
- add<Explain::MultikeyInitial>();
- add<Explain::BecomesMultikey>();
- add<Explain::CountAndYield>();
- add<Explain::MultipleClauses>();
- add<Explain::MultiCursorTakeover>();
- add<Explain::NChunkSkipsTakeover>();
- add<Explain::CoveredIndex>();
- add<Explain::CoveredIndexTakeover>();
- add<Explain::VirtualPickedPlan>();
- add<Explain::LargeN>();
- add<Explain::NYieldsAdvanceBasic>();
- add<Explain::NYieldsAdvanceTakeover>();
- add<Explain::NYieldsAdvanceDeleteLast>();
- add<Explain::NYieldsAdvanceDeleteLastTakeover>();
- add<Explain::YieldRecoveryFailure>();
- }
- } myall;
-
-} // namespace QueryOptimizerTests
diff --git a/src/mongo/dbtests/queryoptimizertests2.cpp b/src/mongo/dbtests/queryoptimizertests2.cpp
deleted file mode 100644
index 45dca25cea9..00000000000
--- a/src/mongo/dbtests/queryoptimizertests2.cpp
+++ /dev/null
@@ -1,829 +0,0 @@
-/**
- * Copyright (C) 2009 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/pch.h"
-
-#include "mongo/db/query_optimizer_internal.h"
-
-#include "mongo/db/instance.h"
-#include "mongo/db/json.h"
-#include "mongo/db/ops/count.h"
-#include "mongo/db/ops/delete.h"
-#include "mongo/db/query_optimizer.h"
-#include "mongo/db/query/new_find.h"
-#include "mongo/db/queryutil.h"
-#include "mongo/db/structure/collection.h"
-#include "mongo/dbtests/dbtests.h"
-
-
-namespace mongo {
- extern void runQuery(Message& m, QueryMessage& q, Message &response );
-} // namespace mongo
-
-namespace {
-
- using boost::shared_ptr;
-
- namespace QueryPlanSetTests {
-
- class Base {
- public:
- Base() : _context( ns() ) {
- string err;
- userCreateNS( ns(), BSONObj(), err, false );
- }
- virtual ~Base() {
- if ( !nsd() )
- return;
- cc().database()->dropCollection( ns() );
- }
- protected:
- static void assembleRequest( const string &ns, BSONObj query, int nToReturn, int nToSkip, BSONObj *fieldsToReturn, int queryOptions, Message &toSend ) {
- // see query.h for the protocol we are using here.
- BufBuilder b;
- int opts = queryOptions;
- b.appendNum(opts);
- b.appendStr(ns);
- b.appendNum(nToSkip);
- b.appendNum(nToReturn);
- query.appendSelfToBufBuilder(b);
- if ( fieldsToReturn )
- fieldsToReturn->appendSelfToBufBuilder(b);
- toSend.setData(dbQuery, b.buf(), b.len());
- }
- QueryPattern makePattern( const BSONObj &query, const BSONObj &order ) {
- FieldRangeSet frs( ns(), query, true, true );
- return QueryPattern( frs, order );
- }
- shared_ptr<QueryPlanSet> makeQps( const BSONObj& query = BSONObj(),
- const BSONObj& order = BSONObj(),
- const BSONObj& hint = BSONObj(),
- bool allowSpecial = true ) {
- auto_ptr<FieldRangeSetPair> frsp( new FieldRangeSetPair( ns(), query ) );
- auto_ptr<FieldRangeSetPair> frspOrig( new FieldRangeSetPair( *frsp ) );
- return shared_ptr<QueryPlanSet>
- ( QueryPlanSet::make( ns(), frsp, frspOrig, query, order,
- shared_ptr<const ParsedQuery>(), hint,
- QueryPlanGenerator::Use, BSONObj(), BSONObj(),
- allowSpecial ) );
- }
- static const char *ns() { return "unittests.QueryPlanSetTests"; }
- static NamespaceDetails *nsd() { return nsdetails( ns() ); }
- DBDirectClient &client() { return _client; }
- Collection* collection() { return _context.db()->getCollection( ns() ); }
- CollectionInfoCache* infoCache() { return collection()->infoCache(); }
- private:
- Lock::GlobalWrite lk_;
- Client::Context _context;
- DBDirectClient _client;
- };
-
- class ToString : public Base {
- public:
- void run() {
- // Just test that we don't crash.
- makeQps( BSON( "a" << 1 ) )->toString();
- }
- };
-
- class NoIndexes : public Base {
- public:
- void run() {
- ASSERT_EQUALS( 1, makeQps( BSON( "a" << 4 ), BSON( "b" << 1 ) )->nPlans() );
- }
- };
-
- class Optimal : public Base {
- public:
- void run() {
- Helpers::ensureIndex( ns(), BSON( "a" << 1 ), false, "a_1" );
- Helpers::ensureIndex( ns(), BSON( "a" << 1 ), false, "b_2" );
- BSONObj query = BSON( "a" << 4 );
-
- // Only one optimal plan is added to the plan set.
- ASSERT_EQUALS( 1, makeQps( query )->nPlans() );
-
- // The optimal plan is recorded in the plan cache.
- FieldRangeSet frs( ns(), query, true, true );
- CachedQueryPlan cachedPlan =
- infoCache()->cachedQueryPlanForPattern( QueryPattern( frs, BSONObj() ) );
- ASSERT_EQUALS( BSON( "a" << 1 ), cachedPlan.indexKey() );
- CandidatePlanCharacter planCharacter = cachedPlan.planCharacter();
- ASSERT( planCharacter.mayRunInOrderPlan() );
- ASSERT( !planCharacter.mayRunOutOfOrderPlan() );
- }
- };
-
- class NoOptimal : public Base {
- public:
- void run() {
- Helpers::ensureIndex( ns(), BSON( "a" << 1 ), false, "a_1" );
- Helpers::ensureIndex( ns(), BSON( "b" << 1 ), false, "b_1" );
- ASSERT_EQUALS( 3, makeQps( BSON( "a" << 4 ), BSON( "b" << 1 ) )->nPlans() );
- }
- };
-
- class NoSpec : public Base {
- public:
- void run() {
- Helpers::ensureIndex( ns(), BSON( "a" << 1 ), false, "a_1" );
- Helpers::ensureIndex( ns(), BSON( "b" << 1 ), false, "b_1" );
- ASSERT_EQUALS( 1, makeQps()->nPlans() );
- }
- };
-
- class HintSpec : public Base {
- public:
- void run() {
- Helpers::ensureIndex( ns(), BSON( "a" << 1 ), false, "a_1" );
- Helpers::ensureIndex( ns(), BSON( "b" << 1 ), false, "b_1" );
- ASSERT_EQUALS( 1, makeQps( BSON( "a" << 1 ), BSON( "b" << 1 ),
- BSON( "hint" << BSON( "a" << 1 ) ) )->nPlans() );
- }
- };
-
- class HintName : public Base {
- public:
- void run() {
- Helpers::ensureIndex( ns(), BSON( "a" << 1 ), false, "a_1" );
- Helpers::ensureIndex( ns(), BSON( "b" << 1 ), false, "b_1" );
- ASSERT_EQUALS( 1, makeQps( BSON( "a" << 1 ), BSON( "b" << 1 ),
- BSON( "hint" << "a_1" ) )->nPlans() );
- }
- };
-
- class NaturalHint : public Base {
- public:
- void run() {
- Helpers::ensureIndex( ns(), BSON( "a" << 1 ), false, "a_1" );
- Helpers::ensureIndex( ns(), BSON( "b" << 1 ), false, "b_1" );
- ASSERT_EQUALS( 1, makeQps( BSON( "a" << 1 ), BSON( "b" << 1 ),
- BSON( "hint" << BSON( "$natural" << 1 ) ) )->nPlans() );
- }
- };
-
- class NaturalSort : public Base {
- public:
- void run() {
- Helpers::ensureIndex( ns(), BSON( "a" << 1 ), false, "a_1" );
- Helpers::ensureIndex( ns(), BSON( "a" << 1 ), false, "b_2" );
- ASSERT_EQUALS( 1, makeQps( BSON( "a" << 1 ), BSON( "$natural" << 1 ) )->nPlans() );
- }
- };
-
- class BadHint : public Base {
- public:
- void run() {
- ASSERT_THROWS( makeQps( BSON( "a" << 1 ), BSON( "b" << 1 ),
- BSON( "hint" << "a_1" ) ),
- AssertionException );
- }
- };
-
- class Count : public Base {
- public:
- void run() {
- Helpers::ensureIndex( ns(), BSON( "a" << 1 ), false, "a_1" );
- Helpers::ensureIndex( ns(), BSON( "b" << 1 ), false, "b_1" );
- string err;
- int errCode;
- ASSERT_EQUALS( 0, runCount( ns(), BSON( "query" << BSON( "a" << 4 ) ), err, errCode ) );
- BSONObj one = BSON( "a" << 1 );
- BSONObj fourA = BSON( "a" << 4 );
- BSONObj fourB = BSON( "a" << 4 );
- theDataFileMgr.insertWithObjMod( ns(), one );
- ASSERT_EQUALS( 0, runCount( ns(), BSON( "query" << BSON( "a" << 4 ) ), err, errCode ) );
- theDataFileMgr.insertWithObjMod( ns(), fourA );
- ASSERT_EQUALS( 1, runCount( ns(), BSON( "query" << BSON( "a" << 4 ) ), err, errCode ) );
- theDataFileMgr.insertWithObjMod( ns(), fourB );
- ASSERT_EQUALS( 2, runCount( ns(), BSON( "query" << BSON( "a" << 4 ) ), err, errCode ) );
- ASSERT_EQUALS( 3, runCount( ns(), BSON( "query" << BSONObj() ), err, errCode ) );
- ASSERT_EQUALS( 3, runCount( ns(), BSON( "query" << BSON( "a" << GT << 0 ) ), err, errCode ) );
- // missing ns
- ASSERT_EQUALS( -1, runCount( "unittests.missingNS", BSONObj(), err, errCode ) );
- // impossible match
- ASSERT_EQUALS( 0, runCount( ns(), BSON( "query" << BSON( "a" << GT << 0 << LT << -1 ) ), err, errCode ) );
- }
- };
-
- class QueryMissingNs : public Base {
- public:
- QueryMissingNs() { mongo::unittest::log() << "querymissingns starts" << endl; }
- ~QueryMissingNs() {
- mongo::unittest::log() << "end QueryMissingNs" << endl;
- }
- void run() {
- Message m;
- assembleRequest( "unittests.missingNS", BSONObj(), 0, 0, 0, 0, m );
- DbMessage d(m);
- QueryMessage q(d);
- Message ret;
- CurOp op(&cc());
- op.ensureStarted();
- newRunQuery( m, q, op, ret );
- ASSERT_EQUALS( 0, ((QueryResult*)ret.header())->nReturned );
- }
-
- };
-
- class UnhelpfulIndex : public Base {
- public:
- void run() {
- Helpers::ensureIndex( ns(), BSON( "a" << 1 ), false, "a_1" );
- Helpers::ensureIndex( ns(), BSON( "b" << 1 ), false, "b_1" );
- ASSERT_EQUALS( 2, makeQps( BSON( "a" << 1 << "c" << 2 ) )->nPlans() );
- }
- };
-
- class FindOne : public Base {
- public:
- void run() {
- BSONObj one = BSON( "a" << 1 );
- theDataFileMgr.insertWithObjMod( ns(), one );
- BSONObj result;
- ASSERT( Helpers::findOne( ns(), BSON( "a" << 1 ), result ) );
- ASSERT_THROWS( Helpers::findOne( ns(), BSON( "a" << 1 ), result, true ), AssertionException );
- Helpers::ensureIndex( ns(), BSON( "a" << 1 ), false, "a_1" );
- ASSERT( Helpers::findOne( ns(), BSON( "a" << 1 ), result, true ) );
- }
- };
-
- class Delete : public Base {
- public:
- void run() {
- Helpers::ensureIndex( ns(), BSON( "a" << 1 ), false, "a_1" );
- for( int i = 0; i < 200; ++i ) {
- BSONObj two = BSON( "a" << 2 );
- theDataFileMgr.insertWithObjMod( ns(), two );
- }
- BSONObj one = BSON( "a" << 1 );
- theDataFileMgr.insertWithObjMod( ns(), one );
- BSONObj delSpec = BSON( "a" << 1 << "_id" << NE << 0 );
- deleteObjects( ns(), delSpec, false );
-
- // QUERY_MIGRATION no plan caching.
- //QueryPattern queryPattern = FieldRangeSet( ns(), delSpec, true, true ).pattern();
- //CachedQueryPlan cachedQueryPlan = infoCache()->cachedQueryPlanForPattern( queryPattern );
- //ASSERT_EQUALS( BSON( "a" << 1 ), cachedQueryPlan.indexKey() );
- //ASSERT_EQUALS( 1, cachedQueryPlan.nScanned() );
- }
- };
-
- class DeleteOneScan : public Base {
- public:
- void run() {
- Helpers::ensureIndex( ns(), BSON( "_id" << 1 ), false, "_id_1" );
- BSONObj one = BSON( "_id" << 3 << "a" << 1 );
- BSONObj two = BSON( "_id" << 2 << "a" << 1 );
- BSONObj three = BSON( "_id" << 1 << "a" << -1 );
- theDataFileMgr.insertWithObjMod( ns(), one );
- theDataFileMgr.insertWithObjMod( ns(), two );
- theDataFileMgr.insertWithObjMod( ns(), three );
- deleteObjects( ns(), BSON( "_id" << GT << 0 << "a" << GT << 0 ), true );
- size_t numDocs = 0;
- for( boost::shared_ptr<Cursor> c = theDataFileMgr.findAll( ns() ); c->ok(); c->advance() ) {
- ++numDocs;
- }
- ASSERT_EQUALS(2U, numDocs);
- }
- };
-
- class DeleteOneIndex : public Base {
- public:
- void run() {
- Helpers::ensureIndex( ns(), BSON( "a" << 1 ), false, "a" );
- BSONObj one = BSON( "a" << 2 << "_id" << 0 );
- BSONObj two = BSON( "a" << 1 << "_id" << 1 );
- BSONObj three = BSON( "a" << 0 << "_id" << 2 );
- theDataFileMgr.insertWithObjMod( ns(), one );
- theDataFileMgr.insertWithObjMod( ns(), two );
- theDataFileMgr.insertWithObjMod( ns(), three );
- deleteObjects( ns(), BSON( "a" << GTE << 0 ), true );
- size_t numDocs = 0;
- for( boost::shared_ptr<Cursor> c = theDataFileMgr.findAll( ns() ); c->ok(); c->advance() ) {
- ++numDocs;
- }
- ASSERT_EQUALS(2U, numDocs);
- }
- };
-
- class InQueryIntervals : public Base {
- public:
- void run() {
- Helpers::ensureIndex( ns(), BSON( "a" << 1 ), false, "a_1" );
- for( int i = 0; i < 10; ++i ) {
- BSONObj temp = BSON( "a" << i );
- theDataFileMgr.insertWithObjMod( ns(), temp );
- }
- BSONObj query = fromjson( "{a:{$in:[2,3,6,9,11]}}" );
- BSONObj order;
- BSONObj hint = fromjson( "{$hint:{a:1}}" );
- auto_ptr< FieldRangeSetPair > frsp( new FieldRangeSetPair( ns(), query ) );
- shared_ptr<QueryPlanSet> s = makeQps( query, order, hint );
- scoped_ptr<QueryPlan> qp( QueryPlan::make( nsd(), 1, s->frsp(), frsp.get(),
- query, order ) );
- boost::shared_ptr<Cursor> c = qp->newCursor();
- double expected[] = { 2, 3, 6, 9 };
- for( int i = 0; i < 4; ++i, c->advance() ) {
- ASSERT_EQUALS( expected[ i ], c->current().getField( "a" ).number() );
- }
- ASSERT( !c->ok() );
-
- // now check reverse
- {
- order = BSON( "a" << -1 );
- auto_ptr< FieldRangeSetPair > frsp( new FieldRangeSetPair( ns(), query ) );
- shared_ptr<QueryPlanSet> s = makeQps( query, order, hint );
- scoped_ptr<QueryPlan> qp( QueryPlan::make( nsd(), 1, s->frsp(), frsp.get(),
- query, order ) );
- boost::shared_ptr<Cursor> c = qp->newCursor();
- double expected[] = { 9, 6, 3, 2 };
- for( int i = 0; i < 4; ++i, c->advance() ) {
- ASSERT_EQUALS( expected[ i ], c->current().getField( "a" ).number() );
- }
- ASSERT( !c->ok() );
- }
- }
- };
-
- class EqualityThenIn : public Base {
- public:
- void run() {
- Helpers::ensureIndex( ns(), BSON( "a" << 1 << "b" << 1 ), false, "a_1_b_1" );
- for( int i = 0; i < 10; ++i ) {
- BSONObj temp = BSON( "a" << 5 << "b" << i );
- theDataFileMgr.insertWithObjMod( ns(), temp );
- }
- auto_ptr< FieldRangeSetPair > frsp( new FieldRangeSetPair( ns(), fromjson( "{a:5,b:{$in:[2,3,6,9,11]}}" ) ) );
- scoped_ptr<QueryPlan> qp( QueryPlan::make( nsd(), 1, *frsp, frsp.get(),
- fromjson( "{a:5,b:{$in:[2,3,6,9,11]}}" ),
- BSONObj() ) );
- boost::shared_ptr<Cursor> c = qp->newCursor();
- double expected[] = { 2, 3, 6, 9 };
- ASSERT( c->ok() );
- for( int i = 0; i < 4; ++i, c->advance() ) {
- ASSERT( c->ok() );
- ASSERT_EQUALS( expected[ i ], c->current().getField( "b" ).number() );
- }
- ASSERT( !c->ok() );
- }
- };
-
- class NotEqualityThenIn : public Base {
- public:
- void run() {
- Helpers::ensureIndex( ns(), BSON( "a" << 1 << "b" << 1 ), false, "a_1_b_1" );
- for( int i = 0; i < 10; ++i ) {
- BSONObj temp = BSON( "a" << 5 << "b" << i );
- theDataFileMgr.insertWithObjMod( ns(), temp );
- }
- auto_ptr< FieldRangeSetPair > frsp( new FieldRangeSetPair( ns(), fromjson( "{a:{$gte:5},b:{$in:[2,3,6,9,11]}}" ) ) );
- scoped_ptr<QueryPlan> qp
- ( QueryPlan::make( nsd(), 1, *frsp, frsp.get(),
- fromjson( "{a:{$gte:5},b:{$in:[2,3,6,9,11]}}" ),
- BSONObj() ) );
- boost::shared_ptr<Cursor> c = qp->newCursor();
- int matches[] = { 2, 3, 6, 9 };
- for( int i = 0; i < 4; ++i, c->advance() ) {
- ASSERT_EQUALS( matches[ i ], c->current().getField( "b" ).number() );
- }
- ASSERT( !c->ok() );
- }
- };
-
- /** Exclude special plan candidate if there are btree plan candidates. SERVER-4531 */
- class ExcludeSpecialPlanWhenBtreePlan : public Base {
- public:
- void run() {
- Helpers::ensureIndex( ns(), BSON( "a" << "2d" ), false, "a_2d" );
- Helpers::ensureIndex( ns(), BSON( "a" << 1 ), false, "a_1" );
- shared_ptr<QueryPlanSet> s =
- makeQps( BSON( "a" << BSON_ARRAY( 0 << 0 ) << "b" << 1 ) );
- // Two query plans, btree and collection scan.
- ASSERT_EQUALS( 2, s->nPlans() );
- // Not the geo plan.
- ASSERT( s->firstPlan()->special().empty() );
- }
- };
-
- /** Exclude unindexed plan candidate if there is a special plan candidate. SERVER-4531 */
- class ExcludeUnindexedPlanWhenSpecialPlan : public Base {
- public:
- void run() {
- Helpers::ensureIndex( ns(), BSON( "a" << "2d" ), false, "a_2d" );
- shared_ptr<QueryPlanSet> s =
- makeQps( BSON( "a" << BSON_ARRAY( 0 << 0 ) << "b" << 1 ) );
- // Single query plan.
- ASSERT_EQUALS( 1, s->nPlans() );
- // It's the geo plan.
- ASSERT( !s->firstPlan()->special().empty() );
- }
- };
-
- class PossiblePlans : public Base {
- public:
- void run() {
- client().ensureIndex( ns(), BSON( "a" << 1 ) );
- client().ensureIndex( ns(), BSON( "b" << 1 ) );
-
- {
- shared_ptr<QueryPlanSet> qps = makeQps( BSON( "a" << 1 ), BSONObj() );
- ASSERT_EQUALS( 1, qps->nPlans() );
- ASSERT( qps->possibleInOrderPlan() );
- ASSERT( qps->haveInOrderPlan() );
- ASSERT( !qps->possibleOutOfOrderPlan() );
- ASSERT( !qps->hasPossiblyExcludedPlans() );
- ASSERT( !qps->usingCachedPlan() );
- }
-
- {
- shared_ptr<QueryPlanSet> qps = makeQps( BSON( "a" << 1 ), BSON( "b" << 1 ) );
- ASSERT_EQUALS( 3, qps->nPlans() );
- ASSERT( qps->possibleInOrderPlan() );
- ASSERT( qps->haveInOrderPlan() );
- ASSERT( qps->possibleOutOfOrderPlan() );
- ASSERT( !qps->hasPossiblyExcludedPlans() );
- ASSERT( !qps->usingCachedPlan() );
- }
-
- infoCache()->registerCachedQueryPlanForPattern( makePattern( BSON( "a" << 1 ), BSONObj() ),
- CachedQueryPlan( BSON( "a" << 1 ), 1,
- CandidatePlanCharacter( true, false ) ) );
- {
- shared_ptr<QueryPlanSet> qps = makeQps( BSON( "a" << 1 ), BSONObj() );
- ASSERT_EQUALS( 1, qps->nPlans() );
- ASSERT( qps->possibleInOrderPlan() );
- ASSERT( qps->haveInOrderPlan() );
- ASSERT( !qps->possibleOutOfOrderPlan() );
- ASSERT( !qps->hasPossiblyExcludedPlans() );
- ASSERT( qps->usingCachedPlan() );
- }
-
- infoCache()->registerCachedQueryPlanForPattern( makePattern( BSON( "a" << 1 ), BSON( "b" << 1 ) ),
- CachedQueryPlan( BSON( "a" << 1 ), 1,
- CandidatePlanCharacter( true, true ) ) );
-
- {
- shared_ptr<QueryPlanSet> qps = makeQps( BSON( "a" << 1 ), BSON( "b" << 1 ) );
- ASSERT_EQUALS( 1, qps->nPlans() );
- ASSERT( qps->possibleInOrderPlan() );
- ASSERT( !qps->haveInOrderPlan() );
- ASSERT( qps->possibleOutOfOrderPlan() );
- ASSERT( qps->hasPossiblyExcludedPlans() );
- ASSERT( qps->usingCachedPlan() );
- }
-
- infoCache()->registerCachedQueryPlanForPattern( makePattern( BSON( "a" << 1 ), BSON( "b" << 1 ) ),
- CachedQueryPlan( BSON( "b" << 1 ), 1,
- CandidatePlanCharacter( true, true ) ) );
- {
- shared_ptr<QueryPlanSet> qps = makeQps( BSON( "a" << 1 ), BSON( "b" << 1 ) );
- ASSERT_EQUALS( 1, qps->nPlans() );
- ASSERT( qps->possibleInOrderPlan() );
- ASSERT( qps->haveInOrderPlan() );
- ASSERT( qps->possibleOutOfOrderPlan() );
- ASSERT( qps->hasPossiblyExcludedPlans() );
- ASSERT( qps->usingCachedPlan() );
- }
-
- {
- shared_ptr<QueryPlanSet> qps = makeQps( BSON( "a" << 1 ), BSON( "c" << 1 ) );
- ASSERT_EQUALS( 2, qps->nPlans() );
- ASSERT( !qps->possibleInOrderPlan() );
- ASSERT( !qps->haveInOrderPlan() );
- ASSERT( qps->possibleOutOfOrderPlan() );
- ASSERT( !qps->hasPossiblyExcludedPlans() );
- ASSERT( !qps->usingCachedPlan() );
- }
- }
- };
-
- /** An unhelpful query plan will not be used if recorded in the query plan cache. */
- class AvoidUnhelpfulRecordedPlan : public Base {
- public:
- void run() {
- client().ensureIndex( ns(), BSON( "a" << 1 ) );
-
- // Record the {a:1} index for a {b:1} query.
- infoCache()->registerCachedQueryPlanForPattern( makePattern( BSON( "b" << 1 ), BSONObj() ),
- CachedQueryPlan( BSON( "a" << 1 ), 1,
- CandidatePlanCharacter( true, false ) ) );
-
- // The {a:1} index is not used for a {b:1} query because it generates an unhelpful
- // plan.
- shared_ptr<QueryPlanSet> qps = makeQps( BSON( "b" << 1 ), BSONObj() );
- ASSERT_EQUALS( 1, qps->nPlans() );
- ASSERT_EQUALS( BSON( "$natural" << 1 ), qps->firstPlan()->indexKey() );
- }
- };
-
- /** An unhelpful query plan will not be used if recorded in the query plan cache. */
- class AvoidDisallowedRecordedPlan : public Base {
- public:
- void run() {
- client().insert( "unittests.system.indexes",
- BSON( "ns" << ns() <<
- "key" << BSON( "a" << 1 ) <<
- "name" << client().genIndexName( BSON( "a" << 1 ) ) <<
- "sparse" << true ) );
-
- // Record the {a:1} index for a {a:null} query.
- infoCache()->registerCachedQueryPlanForPattern( makePattern( BSON( "a" << BSONNULL ), BSONObj() ),
- CachedQueryPlan( BSON( "a" << 1 ), 1,
- CandidatePlanCharacter( true, false ) ) );
-
- // The {a:1} index is not used for an {a:{$exists:false}} query because it generates
- // a disallowed plan.
- shared_ptr<QueryPlanSet> qps = makeQps( BSON( "a" << BSON( "$exists" << false ) ),
- BSONObj() );
- ASSERT_EQUALS( 1, qps->nPlans() );
- ASSERT_EQUALS( BSON( "$natural" << 1 ), qps->firstPlan()->indexKey() );
- }
- };
-
- /** Special plans are only selected when allowed. */
- class AllowSpecial : public Base {
- public:
- void run() {
- BSONObj naturalIndex = BSON( "$natural" << 1 );
- BSONObj specialIndex = BSON( "a" << "2d" );
- BSONObj query = BSON( "a" << BSON_ARRAY( 0 << 0 ) );
- client().ensureIndex( ns(), specialIndex );
-
- // The special plan is chosen if allowed.
- assertSingleIndex( specialIndex, makeQps( query ) );
-
- // The special plan is not chosen if not allowed
- assertSingleIndex( naturalIndex, makeQps( query, BSONObj(), BSONObj(), false ) );
-
- // Attempting to hint a special plan when not allowed triggers an assertion.
- ASSERT_THROWS( makeQps( query, BSONObj(), BSON( "$hint" << specialIndex ), false ),
- UserException );
-
- // Attempting to use a geo operator when special plans are not allowed triggers an
- // assertion.
- ASSERT_THROWS( makeQps( BSON( "a" << BSON( "$near" << BSON_ARRAY( 0 << 0 ) ) ),
- BSONObj(), BSONObj(), false ),
- UserException );
-
- // The special plan is not chosen if not allowed, even if cached.
- infoCache()->registerCachedQueryPlanForPattern( makePattern( query, BSONObj() ),
- CachedQueryPlan( specialIndex, 1,
- CandidatePlanCharacter( true, false ) ) );
- assertSingleIndex( naturalIndex, makeQps( query, BSONObj(), BSONObj(), false ) );
- }
- private:
- void assertSingleIndex( const BSONObj& index, const shared_ptr<QueryPlanSet>& set ) {
- ASSERT_EQUALS( 1, set->nPlans() );
- ASSERT_EQUALS( index, set->firstPlan()->indexKey() );
- }
- };
-
- } // namespace QueryPlanSetTests
-
- class Base {
- public:
- Base() : _ctx( ns() ) {
- string err;
- userCreateNS( ns(), BSONObj(), err, false );
- }
- ~Base() {
- if ( !nsd() )
- return;
- string s( ns() );
- cc().database()->dropCollection( ns() );
- }
- protected:
- static const char *ns() { return "unittests.QueryOptimizerTests"; }
- static NamespaceDetails *nsd() { return nsdetails( ns() ); }
- QueryPattern makePattern( const BSONObj &query, const BSONObj &order ) {
- FieldRangeSet frs( ns(), query, true, true );
- return QueryPattern( frs, order );
- }
- shared_ptr<MultiPlanScanner> makeMps( const BSONObj &query, const BSONObj &order ) {
- shared_ptr<MultiPlanScanner> ret( MultiPlanScanner::make( ns(), query, order ) );
- return ret;
- }
- DBDirectClient &client() { return _client; }
- Collection* collection() { return _ctx.db()->getCollection( ns() ); }
- CollectionInfoCache* infoCache() { return collection()->infoCache(); }
- private:
- Lock::GlobalWrite lk_;
- Client::Context _ctx;
- DBDirectClient _client;
- };
-
- namespace MultiPlanScannerTests {
- class ToString : public Base {
- public:
- void run() {
- scoped_ptr<MultiPlanScanner> multiPlanScanner
- ( MultiPlanScanner::make( ns(), BSON( "a" << 1 ), BSONObj() ) );
- multiPlanScanner->toString(); // Just test that we don't crash.
- }
- };
-
- class PossiblePlans : public Base {
- public:
- void run() {
- client().ensureIndex( ns(), BSON( "a" << 1 ) );
- client().ensureIndex( ns(), BSON( "b" << 1 ) );
-
- {
- shared_ptr<MultiPlanScanner> mps = makeMps( BSON( "a" << 1 ), BSONObj() );
- ASSERT_EQUALS( 1, mps->currentNPlans() );
- ASSERT( mps->possibleInOrderPlan() );
- ASSERT( mps->haveInOrderPlan() );
- ASSERT( !mps->possibleOutOfOrderPlan() );
- ASSERT( !mps->hasPossiblyExcludedPlans() );
- }
-
- {
- shared_ptr<MultiPlanScanner> mps =
- makeMps( BSON( "a" << 1 ), BSON( "b" << 1 ) );
- ASSERT_EQUALS( 3, mps->currentNPlans() );
- ASSERT( mps->possibleInOrderPlan() );
- ASSERT( mps->haveInOrderPlan() );
- ASSERT( mps->possibleOutOfOrderPlan() );
- ASSERT( !mps->hasPossiblyExcludedPlans() );
- }
-
- infoCache()->registerCachedQueryPlanForPattern( makePattern( BSON( "a" << 1 ), BSONObj() ),
- CachedQueryPlan( BSON( "a" << 1 ), 1,
- CandidatePlanCharacter( true, false ) ) );
- {
- shared_ptr<MultiPlanScanner> mps = makeMps( BSON( "a" << 1 ), BSONObj() );
- ASSERT_EQUALS( 1, mps->currentNPlans() );
- ASSERT( mps->possibleInOrderPlan() );
- ASSERT( mps->haveInOrderPlan() );
- ASSERT( !mps->possibleOutOfOrderPlan() );
- ASSERT( !mps->hasPossiblyExcludedPlans() );
- }
-
- infoCache()->registerCachedQueryPlanForPattern( makePattern( BSON( "a" << 1 ), BSON( "b" << 1 ) ),
- CachedQueryPlan( BSON( "a" << 1 ), 1,
- CandidatePlanCharacter( true, true ) ) );
-
- {
- shared_ptr<MultiPlanScanner> mps =
- makeMps( BSON( "a" << 1 ), BSON( "b" << 1 ) );
- ASSERT_EQUALS( 1, mps->currentNPlans() );
- ASSERT( mps->possibleInOrderPlan() );
- ASSERT( !mps->haveInOrderPlan() );
- ASSERT( mps->possibleOutOfOrderPlan() );
- ASSERT( mps->hasPossiblyExcludedPlans() );
- }
-
- infoCache()->registerCachedQueryPlanForPattern( makePattern( BSON( "a" << 1 ), BSON( "b" << 1 ) ),
- CachedQueryPlan( BSON( "b" << 1 ), 1,
- CandidatePlanCharacter( true, true ) ) );
-
- {
- shared_ptr<MultiPlanScanner> mps =
- makeMps( BSON( "a" << 1 ), BSON( "b" << 1 ) );
- ASSERT_EQUALS( 1, mps->currentNPlans() );
- ASSERT( mps->possibleInOrderPlan() );
- ASSERT( mps->haveInOrderPlan() );
- ASSERT( mps->possibleOutOfOrderPlan() );
- ASSERT( mps->hasPossiblyExcludedPlans() );
- }
-
- {
- shared_ptr<MultiPlanScanner> mps =
- makeMps( BSON( "a" << 1 ), BSON( "c" << 1 ) );
- ASSERT_EQUALS( 2, mps->currentNPlans() );
- ASSERT( !mps->possibleInOrderPlan() );
- ASSERT( !mps->haveInOrderPlan() );
- ASSERT( mps->possibleOutOfOrderPlan() );
- ASSERT( !mps->hasPossiblyExcludedPlans() );
- }
-
- {
- shared_ptr<MultiPlanScanner> mps =
- makeMps( fromjson( "{$or:[{a:1},{a:2}]}" ), BSON( "c" << 1 ) );
- ASSERT_EQUALS( 1, mps->currentNPlans() );
- ASSERT( !mps->possibleInOrderPlan() );
- ASSERT( !mps->haveInOrderPlan() );
- ASSERT( mps->possibleOutOfOrderPlan() );
- ASSERT( !mps->hasPossiblyExcludedPlans() );
- }
-
- {
- shared_ptr<MultiPlanScanner> mps =
- makeMps( fromjson( "{$or:[{a:1,b:1},{a:2,b:2}]}" ), BSONObj() );
- ASSERT_EQUALS( 3, mps->currentNPlans() );
- ASSERT( mps->possibleInOrderPlan() );
- ASSERT( mps->haveInOrderPlan() );
- ASSERT( !mps->possibleOutOfOrderPlan() );
- ASSERT( !mps->hasPossiblyExcludedPlans() );
- }
- }
- };
-
- } // namespace MultiPlanScannerTests
-
- class BestGuess : public Base {
- public:
- void run() {
- Helpers::ensureIndex( ns(), BSON( "a" << 1 ), false, "a_1" );
- Helpers::ensureIndex( ns(), BSON( "b" << 1 ), false, "b_1" );
- BSONObj temp = BSON( "a" << 1 );
- theDataFileMgr.insertWithObjMod( ns(), temp );
- temp = BSON( "b" << 1 );
- theDataFileMgr.insertWithObjMod( ns(), temp );
-
- boost::shared_ptr< Cursor > c = getBestGuessCursor( ns(),
- BSON( "b" << 1 ),
- BSON( "a" << 1 ) );
- ASSERT_EQUALS( string( "a" ), c->indexKeyPattern().firstElement().fieldName() );
-
- c = getBestGuessCursor( ns(), BSON( "a" << 1 ), BSON( "b" << 1 ) );
- ASSERT_EQUALS( string( "b" ), c->indexKeyPattern().firstElementFieldName() );
- ASSERT( c->matcher() );
- ASSERT( c->currentMatches() ); // { b:1 } document
- c->advance();
- ASSERT( !c->currentMatches() ); // { a:1 } document
-
- c = getBestGuessCursor( ns(), fromjson( "{b:1,$or:[{z:1}]}" ), BSON( "a" << 1 ) );
- ASSERT_EQUALS( string( "a" ), c->indexKeyPattern().firstElement().fieldName() );
-
- c = getBestGuessCursor( ns(), fromjson( "{a:1,$or:[{y:1}]}" ), BSON( "b" << 1 ) );
- ASSERT_EQUALS( string( "b" ), c->indexKeyPattern().firstElementFieldName() );
-
- FieldRangeSet frs( "ns", BSON( "a" << 1 ), true, true );
- {
- infoCache()->registerCachedQueryPlanForPattern( frs.pattern( BSON( "b" << 1 ) ),
- CachedQueryPlan( BSON( "a" << 1 ), 0,
- CandidatePlanCharacter( true, true ) ) );
- }
-
- c = getBestGuessCursor( ns(), fromjson( "{a:1,$or:[{y:1}]}" ), BSON( "b" << 1 ) );
- ASSERT_EQUALS( string( "b" ),
- c->indexKeyPattern().firstElement().fieldName() );
- }
- };
-
- class All : public Suite {
- public:
- All() : Suite( "queryoptimizer2" ) {}
-
- void setupTests() {
- add<QueryPlanSetTests::ToString>();
- add<QueryPlanSetTests::NoIndexes>();
- add<QueryPlanSetTests::Optimal>();
- add<QueryPlanSetTests::NoOptimal>();
- add<QueryPlanSetTests::NoSpec>();
- add<QueryPlanSetTests::HintSpec>();
- add<QueryPlanSetTests::HintName>();
- add<QueryPlanSetTests::NaturalHint>();
- add<QueryPlanSetTests::NaturalSort>();
- add<QueryPlanSetTests::BadHint>();
- add<QueryPlanSetTests::Count>();
- add<QueryPlanSetTests::QueryMissingNs>();
- add<QueryPlanSetTests::UnhelpfulIndex>();
- add<QueryPlanSetTests::FindOne>();
- add<QueryPlanSetTests::Delete>();
- add<QueryPlanSetTests::DeleteOneScan>();
- add<QueryPlanSetTests::DeleteOneIndex>();
- add<QueryPlanSetTests::InQueryIntervals>();
- add<QueryPlanSetTests::EqualityThenIn>();
- add<QueryPlanSetTests::NotEqualityThenIn>();
- add<QueryPlanSetTests::ExcludeSpecialPlanWhenBtreePlan>();
- add<QueryPlanSetTests::ExcludeUnindexedPlanWhenSpecialPlan>();
- add<QueryPlanSetTests::PossiblePlans>();
- add<QueryPlanSetTests::AvoidUnhelpfulRecordedPlan>();
- add<QueryPlanSetTests::AvoidDisallowedRecordedPlan>();
- add<QueryPlanSetTests::AllowSpecial>();
- add<MultiPlanScannerTests::ToString>();
- add<MultiPlanScannerTests::PossiblePlans>();
- add<BestGuess>();
- }
- } myall;
-
-} // namespace QueryOptimizerTests
-
diff --git a/src/mongo/dbtests/querytests.cpp b/src/mongo/dbtests/querytests.cpp
index 6115210b699..caf028698ff 100644
--- a/src/mongo/dbtests/querytests.cpp
+++ b/src/mongo/dbtests/querytests.cpp
@@ -40,7 +40,6 @@
#include "mongo/db/parsed_query.h"
#include "mongo/db/query/new_find.h"
#include "mongo/db/query/lite_parsed_query.h"
-#include "mongo/db/scanandorder.h"
#include "mongo/db/structure/collection.h"
#include "mongo/dbtests/dbtests.h"
#include "mongo/util/timer.h"
diff --git a/src/mongo/dbtests/queryutiltests.cpp b/src/mongo/dbtests/queryutiltests.cpp
index ca5a8463593..af1605c1692 100644
--- a/src/mongo/dbtests/queryutiltests.cpp
+++ b/src/mongo/dbtests/queryutiltests.cpp
@@ -34,8 +34,6 @@
#include "mongo/db/instance.h"
#include "mongo/db/json.h"
#include "mongo/db/pdfile.h"
-#include "mongo/db/query_optimizer_internal.h"
-#include "mongo/db/querypattern.h"
#include "mongo/db/queryutil.h"
#include "mongo/db/structure/collection.h"
#include "mongo/dbtests/dbtests.h"
@@ -430,68 +428,6 @@ namespace QueryUtilTests {
}
};
- class QueryPatternBase {
- protected:
- static QueryPattern p( const BSONObj &query, const BSONObj &sort = BSONObj() ) {
- return FieldRangeSet( "", query, true, true ).pattern( sort );
- }
- };
-
- class QueryPatternTest : public QueryPatternBase {
- public:
- void run() {
- ASSERT( p( BSON( "a" << 1 ) ) == p( BSON( "a" << 1 ) ) );
- ASSERT( p( BSON( "a" << 1 ) ) == p( BSON( "a" << 5 ) ) );
- ASSERT( p( BSON( "a" << 1 ) ) != p( BSON( "b" << 1 ) ) );
- ASSERT( p( BSON( "a" << 1 ) ) != p( BSON( "a" << LTE << 1 ) ) );
- ASSERT( p( BSON( "a" << 1 ) ) != p( BSON( "a" << 1 << "b" << 2 ) ) );
- ASSERT( p( BSON( "a" << 1 << "b" << 3 ) ) != p( BSON( "a" << 1 ) ) );
- ASSERT( p( BSON( "a" << LT << 1 ) ) == p( BSON( "a" << LTE << 5 ) ) );
- ASSERT( p( BSON( "a" << LT << 1 << GTE << 0 ) ) == p( BSON( "a" << LTE << 5 << GTE << 0 ) ) );
- ASSERT( p( BSON( "a" << 1 ) ) < p( BSON( "a" << 1 << "b" << 1 ) ) );
- ASSERT( !( p( BSON( "a" << 1 << "b" << 1 ) ) < p( BSON( "a" << 1 ) ) ) );
- ASSERT( p( BSON( "a" << 1 ), BSON( "b" << 1 ) ) == p( BSON( "a" << 4 ), BSON( "b" << "a" ) ) );
- ASSERT( p( BSON( "a" << 1 ), BSON( "b" << 1 ) ) == p( BSON( "a" << 4 ), BSON( "b" << -1 ) ) );
- ASSERT( p( BSON( "a" << 1 ), BSON( "b" << 1 ) ) != p( BSON( "a" << 4 ), BSON( "c" << 1 ) ) );
- ASSERT( p( BSON( "a" << 1 ), BSON( "b" << 1 << "c" << -1 ) ) == p( BSON( "a" << 4 ), BSON( "b" << -1 << "c" << 1 ) ) );
- ASSERT( p( BSON( "a" << 1 ), BSON( "b" << 1 << "c" << 1 ) ) != p( BSON( "a" << 4 ), BSON( "b" << 1 ) ) );
- ASSERT( p( BSON( "a" << 1 ), BSON( "b" << 1 ) ) != p( BSON( "a" << 4 ), BSON( "b" << 1 << "c" << 1 ) ) );
- }
- };
-
- class QueryPatternEmpty : public QueryPatternBase {
- public:
- void run() {
- ASSERT( p( BSON( "a" << GT << 5 << LT << 7 ) ) !=
- p( BSON( "a" << GT << 7 << LT << 5 ) ) );
- }
- };
-
- class QueryPatternNeConstraint : public QueryPatternBase {
- public:
- void run() {
- ASSERT( p( BSON( "a" << NE << 5 ) ) != p( BSON( "a" << GT << 1 ) ) );
- ASSERT( p( BSON( "a" << NE << 5 ) ) != p( BSONObj() ) );
- ASSERT( p( BSON( "a" << NE << 5 ) ) == p( BSON( "a" << NE << "a" ) ) );
- }
- };
-
- /** Check QueryPattern categories for optimized bounds. */
- class QueryPatternOptimizedBounds {
- public:
- void run() {
- // With unoptimized bounds, different inequalities yield different query patterns.
- ASSERT( p( BSON( "a" << GT << 1 ), false ) != p( BSON( "a" << LT << 1 ), false ) );
- // SERVER-4675 Descriptive test - With optimized bounds, different inequalities
- // yield different query patterns.
- ASSERT( p( BSON( "a" << GT << 1 ), true ) == p( BSON( "a" << LT << 1 ), true ) );
- }
- private:
- static QueryPattern p( const BSONObj &query, bool optimize ) {
- return FieldRangeSet( "", query, true, optimize ).pattern( BSONObj() );
- }
- };
-
class NoWhere {
public:
void run() {
@@ -1700,82 +1636,6 @@ namespace QueryUtilTests {
}
};
- /** Check that clearIndexesForPatterns() clears recorded query plans. */
- class ClearIndexesForPatterns : public IndexBase {
- public:
- void run() {
- index( BSON( "a" << 1 ) );
- BSONObj query = BSON( "a" << GT << 5 << LT << 5 );
- BSONObj sort = BSON( "a" << 1 );
-
- Collection* collection = ctx()->db()->getCollection( ns() );
- verify( collection );
- CollectionInfoCache* cache = collection->infoCache();
-
- // Record the a:1 index for the query's single and multi key query patterns.
- QueryPattern singleKey = FieldRangeSet( ns(), query, true, true ).pattern( sort );
- cache->registerCachedQueryPlanForPattern( singleKey,
- CachedQueryPlan( BSON( "a" << 1 ), 1,
- CandidatePlanCharacter( true, true ) ) );
- QueryPattern multiKey = FieldRangeSet( ns(), query, false, true ).pattern( sort );
- cache->registerCachedQueryPlanForPattern( multiKey,
- CachedQueryPlan( BSON( "a" << 1 ), 5,
- CandidatePlanCharacter( true, true ) ) );
-
- // The single and multi key fields for this query must differ for the test to be
- // valid.
- ASSERT( singleKey != multiKey );
-
- // Clear the recorded query plans using clearIndexesForPatterns.
- FieldRangeSetPair frsp( ns(), query );
- QueryUtilIndexed::clearIndexesForPatterns( frsp, sort );
-
- // Check that the recorded query plans were cleared.
- ASSERT_EQUALS( BSONObj(), cache->cachedQueryPlanForPattern( singleKey ).indexKey() );
- ASSERT_EQUALS( BSONObj(), cache->cachedQueryPlanForPattern( multiKey ).indexKey() );
- }
- };
-
- /** Check query plan returned by bestIndexForPatterns(). */
- class BestIndexForPatterns : public IndexBase {
- public:
- void run() {
- index( BSON( "a" << 1 ) );
- index( BSON( "b" << 1 ) );
- BSONObj query = BSON( "a" << GT << 5 << LT << 5 );
- BSONObj sort = BSON( "a" << 1 );
-
- Collection* collection = ctx()->db()->getCollection( ns() );
- verify( collection );
- CollectionInfoCache* cache = collection->infoCache();
-
- // No query plan is returned when none has been recorded.
- FieldRangeSetPair frsp( ns(), query );
- ASSERT_EQUALS( BSONObj(),
- QueryUtilIndexed::bestIndexForPatterns( frsp, sort ).indexKey() );
-
- // A multikey index query plan is returned if recorded.
- QueryPattern multiKey = FieldRangeSet( ns(), query, false, true ).pattern( sort );
- cache->registerCachedQueryPlanForPattern( multiKey,
- CachedQueryPlan( BSON( "a" << 1 ), 5,
- CandidatePlanCharacter( true, true ) ) );
- ASSERT_EQUALS( BSON( "a" << 1 ),
- QueryUtilIndexed::bestIndexForPatterns( frsp, sort ).indexKey() );
-
- // A non multikey index query plan is preferentially returned if recorded.
- QueryPattern singleKey = FieldRangeSet( ns(), query, true, true ).pattern( sort );
- cache->registerCachedQueryPlanForPattern( singleKey,
- CachedQueryPlan( BSON( "b" << 1 ), 5,
- CandidatePlanCharacter( true, true ) ) );
- ASSERT_EQUALS( BSON( "b" << 1 ),
- QueryUtilIndexed::bestIndexForPatterns( frsp, sort ).indexKey() );
-
- // The single and multi key fields for this query must differ for the test to be
- // valid.
- ASSERT( singleKey != multiKey );
- }
- };
-
} // namespace FieldRangeSetPairTests
namespace FieldRangeVectorTests {
@@ -2923,10 +2783,6 @@ namespace QueryUtilTests {
add<FieldRangeTests::NonSingletonOr>();
add<FieldRangeTests::Empty>();
add<FieldRangeTests::Equality>();
- add<FieldRangeTests::QueryPatternTest>();
- add<FieldRangeTests::QueryPatternEmpty>();
- add<FieldRangeTests::QueryPatternNeConstraint>();
- add<FieldRangeTests::QueryPatternOptimizedBounds>();
add<FieldRangeTests::NoWhere>();
add<FieldRangeTests::Numeric>();
add<FieldRangeTests::InLowerBound>();
@@ -3060,8 +2916,6 @@ namespace QueryUtilTests {
add<FieldRangeSetPairTests::NoNonUniversalRanges>();
add<FieldRangeSetPairTests::MatchPossible>();
add<FieldRangeSetPairTests::MatchPossibleForIndex>();
- add<FieldRangeSetPairTests::ClearIndexesForPatterns>();
- add<FieldRangeSetPairTests::BestIndexForPatterns>();
add<FieldRangeVectorTests::ToString>();
add<FieldRangeVectorTests::HasAllIndexedRanges>();
add<FieldRangeVectorTests::SingleInterval>();
diff --git a/src/mongo/dbtests/repltests.cpp b/src/mongo/dbtests/repltests.cpp
index 8ca970c17f2..5da329e645d 100644
--- a/src/mongo/dbtests/repltests.cpp
+++ b/src/mongo/dbtests/repltests.cpp
@@ -36,7 +36,6 @@
#include "mongo/db/db.h"
#include "mongo/db/instance.h"
#include "mongo/db/json.h"
-#include "mongo/db/query_plan.h"
#include "mongo/db/queryutil.h"
#include "mongo/db/repl/master_slave.h"
#include "mongo/db/repl/oplog.h"