diff options
| author | Apollon Oikonomopoulos <apoikos@debian.org> | 2016-01-14 00:10:06 +0200 |
|---|---|---|
| committer | Apollon Oikonomopoulos <apollon@skroutz.gr> | 2016-01-14 00:10:06 +0200 |
| commit | 374e1947abcd3e127a2a613aff73ecffdb9199ea (patch) | |
| tree | d83973c3c9802450acd5b5e86fe0d4e8e60a3a1b /src/mongo/db/queryoptimizer.cpp | |
| parent | 65585c90b12d6523bea75a2aebaae2a2fdf9e641 (diff) | |
Imported Upstream version 2.6.11upstream/2.6.11
Diffstat (limited to 'src/mongo/db/queryoptimizer.cpp')
| -rw-r--r-- | src/mongo/db/queryoptimizer.cpp | 2038 |
1 files changed, 0 insertions, 2038 deletions
diff --git a/src/mongo/db/queryoptimizer.cpp b/src/mongo/db/queryoptimizer.cpp deleted file mode 100644 index 469d7bf1691..00000000000 --- a/src/mongo/db/queryoptimizer.cpp +++ /dev/null @@ -1,2038 +0,0 @@ -// @file queryoptimizer.cpp - -/** -* 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/>. -*/ - -#include "mongo/pch.h" - -#include "mongo/db/queryoptimizer.h" - -#include "mongo/client/dbclientinterface.h" -#include "mongo/db/btreecursor.h" -#include "mongo/db/cmdline.h" -#include "mongo/db/db.h" -#include "mongo/db/intervalbtreecursor.h" -#include "mongo/db/pagefault.h" -#include "mongo/server.h" - -//#define DEBUGQO(x) cout << x << endl; -#define DEBUGQO(x) - -namespace mongo { - - QueryPlanSummary QueryPlan::summary() const { return QueryPlanSummary( *this ); } - - double elementDirection( const BSONElement &e ) { - if ( e.isNumber() ) - return e.number(); - return 1; - } - - // 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 ⅈ - } - } - } - 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 ⅈ - } - } - } - uassert( 10113 , "bad hint", false ); - return 0; - } - - 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 ), - _type(0), - _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; - } - - _index = &_d->idx(_idxNo); - - // If the parsing or index indicates this is a special query, don't continue the processing - if (!_special.empty() || - ( _index->getSpec().getType() && - _index->getSpec().getType()->suitability( _frs, _order ) != USELESS ) ) { - - _type = _index->getSpec().getType(); - if (_special.empty()) _special = _index->getSpec().getType()->getPlugin()->getName(); - - massert( 13040 , (string)"no type for special: " + _special , _type ); - // hopefully safe to use original query in these contexts; - // don't think we can mix special with $or clause separation yet - _scanAndOrderRequired = _type->scanAndOrderRequired( _originalQuery , _order ); - return; - } - - const IndexSpec &idxSpec = _index->getSpec(); - 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, idxSpec, _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 ), - idxSpec, _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 ( idxSpec.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 ( _type ) { - // 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(); - } - return _type->newCursor( _originalQuery , _order , numWanted ); - } - - 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 ( _index->getSpec().getType() ) { - 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(); - } - - 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; - } - - SimpleMutex::scoped_lock lk(NamespaceDetailsTransient::_qcMutex); - QueryPattern queryPattern = _frs.pattern( _order ); - CachedQueryPlan queryPlanToCache( indexKey(), nScanned, candidatePlans ); - NamespaceDetailsTransient &nsdt = NamespaceDetailsTransient::get_inlock( ns() ); - nsdt.registerCachedQueryPlanForPattern( queryPattern, queryPlanToCache ); - } - - void QueryPlan::checkTableScanAllowed() const { - if ( likely( !cmdLine.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() , ! cmdLine.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; - } - - /** - * Detects $exists:false predicates in a matcher. All $exists:false predicates will be - * detected. Some $exists:true predicates may be incorrectly reported as $exists:false due to - * the approximate nature of the implementation. - */ - class ExistsFalseDetector : public MatcherVisitor { - public: - ExistsFalseDetector( const Matcher& originalMatcher ); - bool hasFoundExistsFalse() const { return _foundExistsFalse; } - void visitMatcher( const Matcher& matcher ) { _currentMatcher = &matcher; } - void visitElementMatcher( const ElementMatcher& elementMatcher ); - private: - const Matcher* _originalMatcher; - const Matcher* _currentMatcher; - bool _foundExistsFalse; - }; - - ExistsFalseDetector::ExistsFalseDetector( const Matcher& originalMatcher ) : - _originalMatcher( &originalMatcher ), - _currentMatcher( 0 ), - _foundExistsFalse() { - } - - /** Matches $exists:false and $not:{$exists:true} exactly. */ - static bool isExistsFalsePredicate( const ElementMatcher& elementMatcher ) { - bool hasTrueValue = elementMatcher._toMatch.trueValue(); - bool hasNotModifier = elementMatcher._isNot; - return hasNotModifier ? hasTrueValue : !hasTrueValue; - } - - void ExistsFalseDetector::visitElementMatcher( const ElementMatcher& elementMatcher ) { - if ( elementMatcher._compareOp != BSONObj::opEXISTS ) { - // Only consider $exists predicates. - return; - } - if ( _currentMatcher != _originalMatcher ) { - // Treat all $exists predicates nested below the original matcher as $exists:false. - // This approximation is used because a nesting operator may change the matching - // semantics of $exists:true. - _foundExistsFalse = true; - return; - } - if ( isExistsFalsePredicate( elementMatcher ) ) { - // Top level $exists operators are matched exactly. - _foundExistsFalse = true; - } - } - - bool QueryPlan::hasPossibleExistsFalsePredicate() const { - ExistsFalseDetector detector( matcher()->docMatcher() ); - matcher()->docMatcher().visit( detector ); - return detector.hasFoundExistsFalse(); - } - - 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 << ")"; - } - } - - 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->nIndexes; ++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(); - const IndexSpec& spec = ii.getSpec(); - if (special.has(spec.getTypeName()) && - spec.suitability( _qps.frsp().frsForIndex(d, j), _qps.order() ) != USELESS ) { - uassert( 16330, "'special' query operator not allowed", _allowSpecial ); - _qps.setSinglePlan( newPlan( d, j, BSONObj(), BSONObj(), spec.getTypeName())); - 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 ¤tPlan ) { - 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::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 ( ii.getSpec().getType() == 0 ) { - id = ⅈ - 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 = ⅈ - break; - } - if ( keyPattern.nFields() == 1 && ii.keyPattern().nFields() == 1 && - IndexDetails::isIdIndexPattern( keyPattern ) && - ii.isIdIndex() ) { - id = ⅈ - 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; - } - - shared_ptr<Cursor> NamespaceDetailsTransient::bestGuessCursor( 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; - } - - 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 d->idx( idxNo ).getSpec().suitability( frsp.frsForIndex( d , idxNo ) , order ) - != USELESS; - } - - void QueryUtilIndexed::clearIndexesForPatterns( const FieldRangeSetPair &frsp, const BSONObj &order ) { - SimpleMutex::scoped_lock lk(NamespaceDetailsTransient::_qcMutex); - NamespaceDetailsTransient &nsdt = NamespaceDetailsTransient::get_inlock( frsp.ns() ); - CachedQueryPlan noCachedPlan; - nsdt.registerCachedQueryPlanForPattern( frsp._singleKey.pattern( order ), noCachedPlan ); - nsdt.registerCachedQueryPlanForPattern( frsp._multiKey.pattern( order ), noCachedPlan ); - } - - CachedQueryPlan QueryUtilIndexed::bestIndexForPatterns( const FieldRangeSetPair &frsp, const BSONObj &order ) { - SimpleMutex::scoped_lock lk(NamespaceDetailsTransient::_qcMutex); - NamespaceDetailsTransient &nsdt = NamespaceDetailsTransient::get_inlock( frsp.ns() ); - // 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 = nsdt.cachedQueryPlanForPattern( pattern ); - if ( !cachedQueryPlan.indexKey().isEmpty() ) { - return cachedQueryPlan; - } - } - { - QueryPattern pattern = frsp._multiKey.pattern( order ); - CachedQueryPlan cachedQueryPlan = nsdt.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->nIndexes; ++j ) { - if ( indexUseful( *i, d, j, BSONObj() ) ) { - useful = true; - break; - } - } - if ( !useful ) { - return true; - } - } - } - return false; - } - -} // namespace mongo |
