diff options
Diffstat (limited to 'src/mongo/db/queryoptimizer.h')
| -rw-r--r-- | src/mongo/db/queryoptimizer.h | 729 |
1 files changed, 729 insertions, 0 deletions
diff --git a/src/mongo/db/queryoptimizer.h b/src/mongo/db/queryoptimizer.h new file mode 100644 index 00000000000..64a5e3a75a0 --- /dev/null +++ b/src/mongo/db/queryoptimizer.h @@ -0,0 +1,729 @@ +// @file queryoptimizer.h + +/** +* 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/>. +*/ + +#pragma once + +#include "cursor.h" +#include "queryutil.h" +#include "matcher.h" +#include "explain.h" +#include "../util/net/listen.h" +#include "mongo/db/querypattern.h" + +namespace mongo { + + class IndexDetails; + class IndexType; + class 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(), + 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 true if the index we are using has keys such that it can completely resolve the + * query expression to match by itself without ever checking the main object. + */ + bool exactKeyMatch() const { return _exactKeyMatch; } + /** @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() ) 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 { return _frs.ns(); } + 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; } + + /** @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, + 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 _exactKeyMatch; + int _direction; + shared_ptr<FieldRangeVector> _frv; + shared_ptr<FieldRangeVector> _originalFrv; + BSONObj _startKey; + BSONObj _endKey; + bool _endKeyInclusive; + Utility _utility; + string _special; + IndexType * _type; + bool _startOrEndSpec; + shared_ptr<Projection::KeyOnly> _keyFieldsOnly; + mutable shared_ptr<CoveredIndexMatcher> _matcher; // Lazy initialization. + }; + + std::ostream &operator<< ( std::ostream &out, const QueryPlan::Utility &utility ); + + /** + * A QueryPlanSummary owns its own attributes and may be shared. Currently a QueryPlan + * should only be owned by a QueryPlanSet. + */ + class QueryPlanSummary { + public: + QueryPlanSummary() : + _scanAndOrderRequired() { + } + QueryPlanSummary( const QueryPlan &queryPlan ) : + _fieldRangeSetMulti( new FieldRangeSet( queryPlan.multikeyFrs() ) ), + _keyFieldsOnly( queryPlan.keyFieldsOnly() ), + _scanAndOrderRequired( queryPlan.scanAndOrderRequired() ) { + } + bool valid() const { return _fieldRangeSetMulti; } + shared_ptr<FieldRangeSet> _fieldRangeSetMulti; + shared_ptr<Projection::KeyOnly> _keyFieldsOnly; + bool _scanAndOrderRequired; + }; + + /** + * NOTE This interface is deprecated and will be replaced by a special purpose delegation class + * for the query optimizer cursor (QueryOptimizerCursorOp). + * + * Inherit from this interface to implement a new query operation. + * The query optimizer will clone the QueryOp that is provided, giving + * each clone its own query plan. + * + * Normal sequence of events: + * 1) A new QueryOp is generated using createChild(). + * 2) A QueryPlan is assigned to this QueryOp with setQueryPlan(). + * 3) _init() is called on the QueryPlan. + * 4) next() is called repeatedly, with nscanned() checked after each call. + * 5) In one of these calls to next(), setComplete() is called. + * 6) The QueryPattern for the QueryPlan may be recorded as a winner. + */ + class QueryOp : private boost::noncopyable { + public: + QueryOp() : _complete(), _stopRequested(), _queryPlan(), _error() {} + + virtual ~QueryOp() {} + + /** @return QueryPlan assigned to this QueryOp by the query optimizer. */ + const QueryPlan &queryPlan() const { return *_queryPlan; } + + /** Advance to next potential matching document (eg using a cursor). */ + virtual void next() = 0; + /** + * @return current 'nscanned' metric for this QueryOp. Used to compare + * cost to other QueryOps. + */ + virtual long long nscanned() = 0; + /** Take any steps necessary before the db mutex is yielded. */ + virtual void prepareToYield() = 0; + /** Recover once the db mutex is regained. */ + virtual void recoverFromYield() = 0; + + /** + * @return true iff the QueryPlan for this QueryOp may be registered + * as a winning plan. + */ + virtual bool mayRecordPlan() const = 0; + + /** @return true iff the implementation called setComplete() or setStop(). */ + bool complete() const { return _complete; } + /** @return true iff the implementation called steStop(). */ + bool stopRequested() const { return _stopRequested; } + bool completeWithoutStop() const { return complete() && !stopRequested(); } + /** @return true iff the implementation threw an exception. */ + bool error() const { return _error; } + /** @return the exception thrown by implementation if one was thrown. */ + 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 QueryOp will assume its parent QueryOp has completed execution. + */ + virtual QueryOp *createChild() const = 0; + void setQueryPlan( const QueryPlan *queryPlan ) { + _queryPlan = queryPlan; + verify( _queryPlan != NULL ); + } + /** Handle initialization after a QueryPlan has been set. */ + virtual void init() = 0; + void setException( const DBException &e ) { + _error = true; + _exception = e.getInfo(); + } + + /** @return an ExplainPlanInfo object that will be updated as the query runs. */ + virtual shared_ptr<ExplainPlanInfo> generateExplainInfo() { + return shared_ptr<ExplainPlanInfo>( new ExplainPlanInfo() ); + } + + protected: + /** 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; } + + private: + bool _complete; + bool _stopRequested; + ExceptionInfo _exception; + const QueryPlan *_queryPlan; + bool _error; + }; + + // temp. this class works if T::operator< is variant unlike a regular stl priority queue. + // but it's very slow. however if v.size() is always very small, it would be fine, + // maybe even faster than a smart impl that does more memory allocations. + template<class T> + class our_priority_queue : boost::noncopyable { + vector<T> v; + public: + our_priority_queue() { + v.reserve(4); + } + int size() const { return v.size(); } + bool empty() const { return v.empty(); } + void push(const T & x) { + v.push_back(x); + } + T pop() { + size_t t = 0; + for( size_t i = 1; i < v.size(); i++ ) { + if( v[t] < v[i] ) + t = i; + } + T ret = v[t]; + v.erase(v.begin()+t); + return ret; + } + }; + + 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. This class can return a best guess plan or run a + * QueryOp on all the plans. + */ + class QueryPlanSet { + public: + typedef boost::shared_ptr<QueryPlan> QueryPlanPtr; + typedef vector<QueryPlanPtr> PlanSet; + + /** + * @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 ); + + //for testing + bool modifiedKeys() const; + bool hasMultiKey() const; + + class Runner { + public: + Runner( QueryPlanSet &plans, QueryOp &op ); + + /** + * Advance the runner, if it is not done(). + * @return the next non error op if there is one, otherwise an error op. + * If the returned op is complete() or error(), the Runner becomes done(). + */ + shared_ptr<QueryOp> next(); + /** @return true if done iterating. */ + bool done() const { return _done; } + + void prepareToYield(); + 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: + QueryOp &_op; + QueryPlanSet &_plans; + static void initOp( QueryOp &op ); + static void nextOp( QueryOp &op ); + static void prepareToYieldOp( QueryOp &op ); + static void recoverFromYieldOp( QueryOp &op ); + + /** Initialize the Runner. */ + shared_ptr<QueryOp> init(); + /** Move the Runner forward one iteration, and @return the plan for the iteration. */ + shared_ptr<QueryOp> _next(); + + vector<shared_ptr<QueryOp> > _ops; + struct OpHolder { + OpHolder( const shared_ptr<QueryOp> &op ) : _op( op ), _offset() {} + shared_ptr<QueryOp> _op; + long long _offset; + bool operator<( const OpHolder &other ) const { + return _op->nscanned() + _offset > other._op->nscanned() + other._offset; + } + }; + our_priority_queue<OpHolder> _queue; + shared_ptr<ExplainClauseInfo> _explainClauseInfo; + bool _done; + }; + + 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 addFallbackPlans(); + void pushPlan( const QueryPlanPtr& plan ); + + QueryPlanGenerator _generator; + BSONObj _originalQuery; + auto_ptr<FieldRangeSetPair> _frsp; + PlanSet _plans; + bool _mayRecordPlan; + bool _usingCachedPlan; + CandidatePlanCharacter _cachedPlanCharacter; + BSONObj _order; + long long _oldNScanned; + ElapsedTracker _yieldSometimesTracker; + bool _allowSpecial; + }; + + /** Handles $or type queries by generating a QueryPlanSet for each $or clause. */ + class MultiPlanScanner { + public: + + static MultiPlanScanner *make( const char *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 initial QueryOp for QueryPlanSet iteration. */ + void initialOp( const shared_ptr<QueryOp> &originalOp ) { _baseOp = originalOp; } + /** + * Advance to the next QueryOp, if not doneOps(). + * @return the next non error op if there is one, otherwise an error op. + * If the returned op is complete() or error(), the MultiPlanScanner becomes doneOps() and + * no further QueryOp iteration is possible. + */ + shared_ptr<QueryOp> nextOp(); + /** @return true if done with QueryOp iteration. */ + bool doneOps() const { return _doneOps; } + + /** + * 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 ¤tPlan ); + + /** 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 clearRunner(); + + 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 _or ? ( !_tableScanned && !_org->orRangesExhausted() ) : _i == 0; + } + /** + * @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 char *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<QueryOp> iterateRunner( QueryOp &originalOp, bool retried = false ); + + shared_ptr<QueryOp> nextOpSimple(); + shared_ptr<QueryOp> nextOpOr(); + + 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<QueryOp> _baseOp; + scoped_ptr<QueryPlanSet::Runner> _runner; + shared_ptr<ExplainQueryInfo> _explainQueryInfo; + bool _doneOps; + }; + + /** + * 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 QueryOp &op, 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 shared_ptr< CoveredIndexMatcher > matcherPtr() const { return _matcher; } + 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 |
