2008-11-15 19:04:01 -05:00
|
|
|
/* queryoptimizer.h */
|
|
|
|
|
|
2008-11-14 16:19:47 -05:00
|
|
|
/**
|
|
|
|
|
* Copyright (C) 2008 10gen Inc.
|
2008-12-28 20:28:49 -05:00
|
|
|
*
|
2008-11-14 16:19:47 -05:00
|
|
|
* 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.
|
2008-12-28 20:28:49 -05:00
|
|
|
*
|
2008-11-14 16:19:47 -05:00
|
|
|
* 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.
|
2008-12-28 20:28:49 -05:00
|
|
|
*
|
2008-11-14 16:19:47 -05:00
|
|
|
* 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/>.
|
|
|
|
|
*/
|
|
|
|
|
|
2008-12-28 20:28:49 -05:00
|
|
|
#pragma once
|
2008-11-14 16:19:47 -05:00
|
|
|
|
2009-02-17 15:53:19 -05:00
|
|
|
#include "cursor.h"
|
|
|
|
|
#include "jsobj.h"
|
2009-02-24 14:14:45 -05:00
|
|
|
#include "queryutil.h"
|
2010-05-24 22:00:25 -07:00
|
|
|
#include "matcher.h"
|
2010-07-07 19:35:26 -07:00
|
|
|
#include "../util/message.h"
|
2009-02-17 15:53:19 -05:00
|
|
|
|
2009-01-14 17:09:51 -05:00
|
|
|
namespace mongo {
|
2009-02-17 15:53:19 -05:00
|
|
|
|
2009-02-20 11:08:22 -05:00
|
|
|
class IndexDetails;
|
2010-03-10 22:44:51 -05:00
|
|
|
class IndexType;
|
|
|
|
|
|
2009-07-07 14:12:53 -04:00
|
|
|
class QueryPlan : boost::noncopyable {
|
2009-01-15 10:17:11 -05:00
|
|
|
public:
|
2009-07-07 14:12:53 -04:00
|
|
|
QueryPlan(NamespaceDetails *_d,
|
|
|
|
|
int _idxNo, // -1 = no index
|
2009-08-11 16:43:30 -04:00
|
|
|
const FieldRangeSet &fbs,
|
2010-06-02 17:10:34 -07:00
|
|
|
const BSONObj &originalQuery,
|
2009-04-30 16:25:36 -04:00
|
|
|
const BSONObj &order,
|
|
|
|
|
const BSONObj &startKey = BSONObj(),
|
2010-02-25 16:24:34 -05:00
|
|
|
const BSONObj &endKey = BSONObj() ,
|
|
|
|
|
string special="" );
|
2009-07-07 14:12:53 -04:00
|
|
|
|
2009-02-19 10:01:20 -05:00
|
|
|
/* If true, no other index can do better. */
|
2009-02-17 20:57:36 -05:00
|
|
|
bool optimal() const { return optimal_; }
|
2009-01-15 10:17:11 -05:00
|
|
|
/* ScanAndOrder processing will be required if true */
|
2009-02-17 20:57:36 -05:00
|
|
|
bool scanAndOrderRequired() const { return scanAndOrderRequired_; }
|
2009-01-15 10:17:11 -05:00
|
|
|
/* When true, the index we are using has keys such that it can completely resolve the
|
2009-02-17 20:57:36 -05:00
|
|
|
query expression to match by itself without ever checking the main object.
|
|
|
|
|
*/
|
|
|
|
|
bool exactKeyMatch() const { return exactKeyMatch_; }
|
2009-05-13 11:17:24 -04:00
|
|
|
/* If true, the startKey and endKey are unhelpful and the index order doesn't match the
|
|
|
|
|
requested sort order */
|
2009-02-24 10:27:07 -05:00
|
|
|
bool unhelpful() const { return unhelpful_; }
|
2009-02-19 13:58:22 -05:00
|
|
|
int direction() const { return direction_; }
|
2010-05-07 17:25:57 -04:00
|
|
|
shared_ptr<Cursor> newCursor( const DiskLoc &startLoc = DiskLoc() , int numWanted=0 ) const;
|
|
|
|
|
shared_ptr<Cursor> newReverseCursor() const;
|
2009-02-20 15:19:41 -05:00
|
|
|
BSONObj indexKey() const;
|
2010-07-20 13:58:58 -07:00
|
|
|
bool willScanTable() const { return !index_ && fbs_.matchPossible(); }
|
2009-02-20 15:19:41 -05:00
|
|
|
const char *ns() const { return fbs_.ns(); }
|
2010-02-02 11:46:02 -08:00
|
|
|
NamespaceDetails *nsd() const { return d; }
|
2010-06-02 17:10:34 -07:00
|
|
|
BSONObj originalQuery() const { return _originalQuery; }
|
2010-07-16 03:47:26 -07:00
|
|
|
BSONObj simplifiedQuery( const BSONObj& fields = BSONObj() ) const { return fbs_.simplifiedQuery( fields ); }
|
2009-08-11 16:43:30 -04:00
|
|
|
const FieldRange &range( const char *fieldName ) const { return fbs_.range( fieldName ); }
|
2009-03-24 12:12:04 -04:00
|
|
|
void registerSelf( long long nScanned ) const;
|
2010-07-16 03:47:26 -07:00
|
|
|
shared_ptr< FieldRangeVector > frv() const { return _frv; }
|
2009-02-17 20:57:36 -05:00
|
|
|
private:
|
2009-07-07 13:17:53 -04:00
|
|
|
NamespaceDetails *d;
|
|
|
|
|
int idxNo;
|
2009-08-11 16:43:30 -04:00
|
|
|
const FieldRangeSet &fbs_;
|
2010-06-02 17:10:34 -07:00
|
|
|
const BSONObj &_originalQuery;
|
2009-02-20 11:08:22 -05:00
|
|
|
const BSONObj &order_;
|
|
|
|
|
const IndexDetails *index_;
|
2009-02-17 20:57:36 -05:00
|
|
|
bool optimal_;
|
|
|
|
|
bool scanAndOrderRequired_;
|
|
|
|
|
bool exactKeyMatch_;
|
2009-02-19 13:58:22 -05:00
|
|
|
int direction_;
|
2010-07-16 01:49:15 -07:00
|
|
|
shared_ptr< FieldRangeVector > _frv;
|
|
|
|
|
BSONObj _startKey;
|
|
|
|
|
BSONObj _endKey;
|
2009-05-06 14:02:44 -04:00
|
|
|
bool endKeyInclusive_;
|
2009-02-24 10:27:07 -05:00
|
|
|
bool unhelpful_;
|
2010-02-25 16:24:34 -05:00
|
|
|
string _special;
|
2010-03-10 22:31:10 -05:00
|
|
|
IndexType * _type;
|
2010-07-07 12:26:20 -07:00
|
|
|
bool _startOrEndSpec;
|
2009-01-15 10:17:11 -05:00
|
|
|
};
|
|
|
|
|
|
2009-02-23 09:50:31 -05:00
|
|
|
// Inherit from this interface to implement a new query operation.
|
2009-12-21 09:26:16 -08:00
|
|
|
// The query optimizer will clone the QueryOp that is provided, giving
|
|
|
|
|
// each clone its own query plan.
|
2009-02-20 11:08:22 -05:00
|
|
|
class QueryOp {
|
|
|
|
|
public:
|
2010-07-16 03:47:26 -07:00
|
|
|
QueryOp() : _complete(), _stopRequested(), _qp(), _error() {}
|
2010-06-02 21:24:04 -07:00
|
|
|
|
|
|
|
|
// Used when handing off from one QueryOp type to another
|
|
|
|
|
QueryOp( const QueryOp &other ) :
|
|
|
|
|
_complete(), _stopRequested(), _qp(), _error(), _matcher( other._matcher ),
|
2010-07-16 03:47:26 -07:00
|
|
|
_orConstraint( other._orConstraint ) {}
|
2010-06-02 21:24:04 -07:00
|
|
|
|
2009-02-20 11:08:22 -05:00
|
|
|
virtual ~QueryOp() {}
|
2010-02-16 11:13:31 -05:00
|
|
|
|
2010-06-02 17:10:34 -07:00
|
|
|
/** these gets called after a query plan is set */
|
|
|
|
|
void init() {
|
|
|
|
|
if ( _oldMatcher.get() ) {
|
|
|
|
|
_matcher.reset( _oldMatcher->nextClauseMatcher( qp().indexKey() ) );
|
|
|
|
|
} else {
|
2010-06-03 12:24:13 -07:00
|
|
|
_matcher.reset( new CoveredIndexMatcher( qp().originalQuery(), qp().indexKey(), alwaysUseRecord() ) );
|
2010-06-02 17:10:34 -07:00
|
|
|
}
|
|
|
|
|
_init();
|
|
|
|
|
}
|
2009-02-24 11:49:55 -05:00
|
|
|
virtual void next() = 0;
|
2010-06-02 17:10:34 -07:00
|
|
|
|
2009-02-24 18:23:11 -05:00
|
|
|
virtual bool mayRecordPlan() const = 0;
|
2010-02-16 11:13:31 -05:00
|
|
|
|
2010-07-07 19:35:26 -07:00
|
|
|
virtual void prepareToYield() { massert( 13335, "yield not supported", false ); }
|
|
|
|
|
virtual void recoverFromYield() { massert( 13336, "yield not supported", false ); }
|
|
|
|
|
|
2010-02-16 11:13:31 -05:00
|
|
|
/** @return a copy of the inheriting class, which will be run with its own
|
2010-05-12 10:38:58 -07:00
|
|
|
query plan. If multiple plan sets are required for an $or query,
|
|
|
|
|
the QueryOp of the winning plan from a given set will be cloned
|
2010-06-02 21:24:04 -07:00
|
|
|
to generate QueryOps for the subsequent plan set. This function
|
|
|
|
|
should only be called after the query op has completed executing.
|
2010-02-16 11:13:31 -05:00
|
|
|
*/
|
2010-06-02 21:24:04 -07:00
|
|
|
QueryOp *createChild() {
|
2010-07-16 03:47:26 -07:00
|
|
|
if( _orConstraint.get() ) {
|
2010-07-20 14:49:36 -07:00
|
|
|
_matcher->advanceOrClause( _orConstraint );
|
2010-07-16 03:47:26 -07:00
|
|
|
_orConstraint.reset();
|
2010-06-02 21:24:04 -07:00
|
|
|
}
|
2010-06-02 17:10:34 -07:00
|
|
|
QueryOp *ret = _createChild();
|
|
|
|
|
ret->_oldMatcher = _matcher;
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
2010-05-12 11:03:51 -07:00
|
|
|
bool complete() const { return _complete; }
|
|
|
|
|
bool error() const { return _error; }
|
2010-05-12 11:52:43 -07:00
|
|
|
bool stopRequested() const { return _stopRequested; }
|
2010-06-21 13:41:34 -04:00
|
|
|
ExceptionInfo exception() const { return _exception; }
|
2010-05-12 11:03:51 -07:00
|
|
|
const QueryPlan &qp() const { return *_qp; }
|
2009-02-24 11:49:55 -05:00
|
|
|
// To be called by QueryPlanSet::Runner only.
|
2010-05-12 11:03:51 -07:00
|
|
|
void setQueryPlan( const QueryPlan *qp ) { _qp = qp; }
|
2010-06-21 13:41:34 -04:00
|
|
|
void setException( const DBException &e ) {
|
2010-05-12 11:03:51 -07:00
|
|
|
_error = true;
|
2010-06-21 13:41:34 -04:00
|
|
|
_exception = e.getInfo();
|
2009-02-24 15:45:18 -05:00
|
|
|
}
|
2010-06-02 17:10:34 -07:00
|
|
|
shared_ptr< CoveredIndexMatcher > matcher() const { return _matcher; }
|
2009-02-24 11:49:55 -05:00
|
|
|
protected:
|
2010-06-02 21:24:04 -07:00
|
|
|
void setComplete() {
|
2010-07-16 03:47:26 -07:00
|
|
|
_orConstraint = qp().frv();
|
2010-06-02 21:24:04 -07:00
|
|
|
_complete = true;
|
|
|
|
|
}
|
2010-05-12 11:03:51 -07:00
|
|
|
void setStop() { setComplete(); _stopRequested = true; }
|
2010-06-02 17:10:34 -07:00
|
|
|
|
|
|
|
|
virtual void _init() = 0;
|
|
|
|
|
|
|
|
|
|
virtual QueryOp *_createChild() const = 0;
|
2010-06-03 12:24:13 -07:00
|
|
|
|
|
|
|
|
virtual bool alwaysUseRecord() const { return false; }
|
2010-06-02 17:10:34 -07:00
|
|
|
|
2009-02-20 11:08:22 -05:00
|
|
|
private:
|
2010-05-12 11:03:51 -07:00
|
|
|
bool _complete;
|
|
|
|
|
bool _stopRequested;
|
2010-06-21 13:41:34 -04:00
|
|
|
ExceptionInfo _exception;
|
2010-05-12 11:03:51 -07:00
|
|
|
const QueryPlan *_qp;
|
|
|
|
|
bool _error;
|
2010-06-02 17:10:34 -07:00
|
|
|
shared_ptr< CoveredIndexMatcher > _matcher;
|
|
|
|
|
shared_ptr< CoveredIndexMatcher > _oldMatcher;
|
2010-07-16 03:47:26 -07:00
|
|
|
shared_ptr< FieldRangeVector > _orConstraint;
|
2009-02-20 11:08:22 -05:00
|
|
|
};
|
|
|
|
|
|
2009-05-28 15:22:24 -04:00
|
|
|
// Set of candidate query plans for a particular query. Used for running
|
|
|
|
|
// a QueryOp on these plans.
|
2009-02-19 10:01:20 -05:00
|
|
|
class QueryPlanSet {
|
|
|
|
|
public:
|
2010-03-29 12:55:44 -04:00
|
|
|
|
|
|
|
|
typedef boost::shared_ptr< QueryPlan > PlanPtr;
|
|
|
|
|
typedef vector< PlanPtr > PlanSet;
|
|
|
|
|
|
2009-04-30 16:25:36 -04:00
|
|
|
QueryPlanSet( const char *ns,
|
2010-06-01 16:21:21 -07:00
|
|
|
auto_ptr< FieldRangeSet > frs,
|
2010-06-02 12:48:41 -07:00
|
|
|
const BSONObj &originalQuery,
|
2009-04-30 16:25:36 -04:00
|
|
|
const BSONObj &order,
|
|
|
|
|
const BSONElement *hint = 0,
|
|
|
|
|
bool honorRecordedPlan = true,
|
|
|
|
|
const BSONObj &min = BSONObj(),
|
2010-06-28 12:44:23 -07:00
|
|
|
const BSONObj &max = BSONObj(),
|
2010-07-07 19:35:26 -07:00
|
|
|
bool bestGuessOnly = false,
|
|
|
|
|
bool mayYield = false);
|
2009-02-19 10:01:20 -05:00
|
|
|
int nPlans() const { return plans_.size(); }
|
2009-02-20 15:37:24 -05:00
|
|
|
shared_ptr< QueryOp > runOp( QueryOp &op );
|
2009-02-23 09:50:31 -05:00
|
|
|
template< class T >
|
|
|
|
|
shared_ptr< T > runOp( T &op ) {
|
|
|
|
|
return dynamic_pointer_cast< T >( runOp( static_cast< QueryOp& >( op ) ) );
|
|
|
|
|
}
|
2009-02-26 14:55:53 -05:00
|
|
|
BSONObj explain() const;
|
2009-02-26 15:53:33 -05:00
|
|
|
bool usingPrerecordedPlan() const { return usingPrerecordedPlan_; }
|
2010-03-29 12:55:44 -04:00
|
|
|
PlanPtr getBestGuess() const;
|
2010-05-12 10:58:16 -07:00
|
|
|
//for testing
|
2010-06-01 16:21:21 -07:00
|
|
|
const FieldRangeSet &fbs() const { return *fbs_; }
|
2009-02-19 10:01:20 -05:00
|
|
|
private:
|
2009-02-25 19:22:54 -05:00
|
|
|
void addOtherPlans( bool checkFirst );
|
|
|
|
|
void addPlan( PlanPtr plan, bool checkFirst ) {
|
|
|
|
|
if ( checkFirst && plan->indexKey().woCompare( plans_[ 0 ]->indexKey() ) == 0 )
|
|
|
|
|
return;
|
|
|
|
|
plans_.push_back( plan );
|
|
|
|
|
}
|
2009-02-25 10:48:41 -05:00
|
|
|
void init();
|
2009-07-07 13:17:53 -04:00
|
|
|
void addHint( IndexDetails &id );
|
2009-02-24 11:49:55 -05:00
|
|
|
struct Runner {
|
|
|
|
|
Runner( QueryPlanSet &plans, QueryOp &op );
|
2009-02-20 15:37:24 -05:00
|
|
|
shared_ptr< QueryOp > run();
|
2010-07-07 19:35:26 -07:00
|
|
|
void mayYield( const vector< shared_ptr< QueryOp > > &ops );
|
2009-02-20 11:08:22 -05:00
|
|
|
QueryOp &op_;
|
|
|
|
|
QueryPlanSet &plans_;
|
2009-02-26 10:28:27 -05:00
|
|
|
static void initOp( QueryOp &op );
|
|
|
|
|
static void nextOp( QueryOp &op );
|
2010-07-07 19:35:26 -07:00
|
|
|
static void prepareToYield( QueryOp &op );
|
|
|
|
|
static void recoverFromYield( QueryOp &op );
|
2009-02-20 11:08:22 -05:00
|
|
|
};
|
2009-07-07 13:17:53 -04:00
|
|
|
const char *ns;
|
2010-06-02 12:48:41 -07:00
|
|
|
BSONObj _originalQuery;
|
2010-06-01 16:21:21 -07:00
|
|
|
auto_ptr< FieldRangeSet > fbs_;
|
2009-02-20 11:08:22 -05:00
|
|
|
PlanSet plans_;
|
2009-02-25 10:48:41 -05:00
|
|
|
bool mayRecordPlan_;
|
|
|
|
|
bool usingPrerecordedPlan_;
|
|
|
|
|
BSONObj hint_;
|
|
|
|
|
BSONObj order_;
|
2009-03-24 12:12:04 -04:00
|
|
|
long long oldNScanned_;
|
2009-02-26 15:53:33 -05:00
|
|
|
bool honorRecordedPlan_;
|
2009-04-30 16:25:36 -04:00
|
|
|
BSONObj min_;
|
|
|
|
|
BSONObj max_;
|
2010-03-18 15:00:29 -04:00
|
|
|
string _special;
|
2010-05-24 23:51:24 -07:00
|
|
|
bool _bestGuessOnly;
|
2010-07-07 19:35:26 -07:00
|
|
|
bool _mayYield;
|
|
|
|
|
ElapsedTracker _yieldSometimesTracker;
|
2009-02-19 10:01:20 -05:00
|
|
|
};
|
2009-01-14 17:09:51 -05:00
|
|
|
|
2010-05-12 10:38:58 -07:00
|
|
|
// Handles $or type queries by generating a QueryPlanSet for each $or clause
|
|
|
|
|
// 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
|
2010-07-07 19:35:26 -07:00
|
|
|
// QueryPattern tracking to record successful plans on $or clauses for use by
|
|
|
|
|
// subsequent $or clauses, even though there may be a significant aggregate
|
2010-05-12 10:38:58 -07:00
|
|
|
// $nor component that would not be represented in QueryPattern.
|
|
|
|
|
class MultiPlanScanner {
|
|
|
|
|
public:
|
|
|
|
|
MultiPlanScanner( const char *ns,
|
|
|
|
|
const BSONObj &query,
|
|
|
|
|
const BSONObj &order,
|
|
|
|
|
const BSONElement *hint = 0,
|
|
|
|
|
bool honorRecordedPlan = true,
|
|
|
|
|
const BSONObj &min = BSONObj(),
|
2010-06-28 12:44:23 -07:00
|
|
|
const BSONObj &max = BSONObj(),
|
2010-07-07 19:35:26 -07:00
|
|
|
bool bestGuessOnly = false,
|
|
|
|
|
bool mayYield = false);
|
2010-05-12 10:38:58 -07:00
|
|
|
shared_ptr< QueryOp > runOp( QueryOp &op );
|
|
|
|
|
template< class T >
|
|
|
|
|
shared_ptr< T > runOp( T &op ) {
|
|
|
|
|
return dynamic_pointer_cast< T >( runOp( static_cast< QueryOp& >( op ) ) );
|
|
|
|
|
}
|
|
|
|
|
shared_ptr< QueryOp > runOpOnce( QueryOp &op );
|
|
|
|
|
template< class T >
|
|
|
|
|
shared_ptr< T > runOpOnce( T &op ) {
|
|
|
|
|
return dynamic_pointer_cast< T >( runOpOnce( static_cast< QueryOp& >( op ) ) );
|
|
|
|
|
}
|
2010-07-20 13:58:58 -07:00
|
|
|
bool mayRunMore() const { return _or ? ( !_tableScanned && !_fros.orFinished() ) : _i == 0; }
|
2010-05-26 01:30:50 -07:00
|
|
|
BSONObj oldExplain() const { assertNotOr(); return _currentQps->explain(); }
|
|
|
|
|
// just report this when only one query op
|
|
|
|
|
bool usingPrerecordedPlan() const {
|
|
|
|
|
return !_or && _currentQps->usingPrerecordedPlan();
|
|
|
|
|
}
|
2010-05-24 23:51:24 -07:00
|
|
|
void setBestGuessOnly() { _bestGuessOnly = true; }
|
2010-07-07 19:35:26 -07:00
|
|
|
void mayYield( bool val ) { _mayYield = val; }
|
2010-05-12 10:38:58 -07:00
|
|
|
private:
|
|
|
|
|
void assertNotOr() const {
|
|
|
|
|
massert( 13266, "not implemented for $or query", !_or );
|
|
|
|
|
}
|
2010-06-09 11:18:56 -07:00
|
|
|
bool uselessOr( const BSONElement &hint ) const;
|
2010-05-12 10:38:58 -07:00
|
|
|
const char * _ns;
|
|
|
|
|
bool _or;
|
|
|
|
|
BSONObj _query;
|
2010-06-02 12:04:46 -07:00
|
|
|
FieldRangeOrSet _fros;
|
2010-05-12 10:38:58 -07:00
|
|
|
auto_ptr< QueryPlanSet > _currentQps;
|
|
|
|
|
int _i;
|
2010-05-12 11:52:43 -07:00
|
|
|
bool _honorRecordedPlan;
|
2010-05-24 23:51:24 -07:00
|
|
|
bool _bestGuessOnly;
|
2010-06-09 11:18:56 -07:00
|
|
|
BSONObj _hint;
|
2010-07-07 19:35:26 -07:00
|
|
|
bool _mayYield;
|
2010-07-20 13:58:58 -07:00
|
|
|
bool _tableScanned;
|
2010-05-12 10:38:58 -07:00
|
|
|
};
|
|
|
|
|
|
2010-05-24 22:00:25 -07:00
|
|
|
class MultiCursor : public Cursor {
|
|
|
|
|
public:
|
|
|
|
|
class CursorOp : public QueryOp {
|
|
|
|
|
public:
|
2010-06-02 21:24:04 -07:00
|
|
|
CursorOp() {}
|
|
|
|
|
CursorOp( const QueryOp &other ) : QueryOp( other ) {}
|
2010-05-24 22:00:25 -07:00
|
|
|
virtual shared_ptr< Cursor > newCursor() const = 0;
|
|
|
|
|
};
|
|
|
|
|
// takes ownership of 'op'
|
2010-07-07 19:35:26 -07:00
|
|
|
MultiCursor( const char *ns, const BSONObj &pattern, const BSONObj &order, shared_ptr< CursorOp > op = shared_ptr< CursorOp >(), bool mayYield = false )
|
|
|
|
|
: _mps( new MultiPlanScanner( ns, pattern, order, 0, true, BSONObj(), BSONObj(), !op.get(), mayYield ) ) {
|
2010-05-25 13:03:47 -07:00
|
|
|
if ( op.get() ) {
|
|
|
|
|
_op = op;
|
|
|
|
|
} else {
|
|
|
|
|
_op.reset( new NoOp() );
|
|
|
|
|
}
|
2010-05-24 22:00:25 -07:00
|
|
|
if ( _mps->mayRunMore() ) {
|
|
|
|
|
nextClause();
|
|
|
|
|
if ( !ok() ) {
|
|
|
|
|
advance();
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
_c.reset( new BasicCursor( DiskLoc() ) );
|
|
|
|
|
}
|
|
|
|
|
}
|
2010-05-24 23:51:24 -07:00
|
|
|
// used to handoff a query to a getMore()
|
2010-06-02 21:24:04 -07:00
|
|
|
MultiCursor( auto_ptr< MultiPlanScanner > mps, const shared_ptr< Cursor > &c, const shared_ptr< CoveredIndexMatcher > &matcher, const QueryOp &op )
|
|
|
|
|
: _op( new NoOp( op ) ), _c( c ), _mps( mps ), _matcher( matcher ) {
|
2010-05-24 23:51:24 -07:00
|
|
|
_mps->setBestGuessOnly();
|
2010-07-07 19:35:26 -07:00
|
|
|
_mps->mayYield( false ); // with a NoOp, there's no need to yield in QueryPlanSet
|
2010-06-07 18:23:59 -07:00
|
|
|
if ( !ok() ) {
|
|
|
|
|
// would have been advanced by UserQueryOp if possible
|
|
|
|
|
advance();
|
|
|
|
|
}
|
2010-05-24 23:51:24 -07:00
|
|
|
}
|
2010-05-24 22:00:25 -07:00
|
|
|
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() {
|
|
|
|
|
_c->advance();
|
|
|
|
|
while( !ok() && _mps->mayRunMore() ) {
|
|
|
|
|
nextClause();
|
|
|
|
|
}
|
|
|
|
|
return ok();
|
|
|
|
|
}
|
|
|
|
|
virtual BSONObj currKey() const { return _c->currKey(); }
|
|
|
|
|
virtual DiskLoc refLoc() { return _c->refLoc(); }
|
2010-06-07 14:13:57 -07:00
|
|
|
virtual void noteLocation() {
|
|
|
|
|
_c->noteLocation();
|
|
|
|
|
}
|
2010-05-24 22:00:25 -07:00
|
|
|
virtual void checkLocation() {
|
|
|
|
|
_c->checkLocation();
|
|
|
|
|
}
|
2010-05-25 16:01:06 -07:00
|
|
|
virtual bool supportGetMore() { return true; }
|
2010-07-24 21:58:39 -04:00
|
|
|
virtual bool supportYields() { return _c->supportYields(); }
|
2010-05-24 22:00:25 -07:00
|
|
|
// with update we could potentially get the same document on multiple
|
|
|
|
|
// indexes, but update appears to already handle this with seenObjects
|
|
|
|
|
// so we don't have to do anything special here.
|
|
|
|
|
virtual bool getsetdup(DiskLoc loc) {
|
|
|
|
|
return _c->getsetdup( loc );
|
|
|
|
|
}
|
2010-05-25 13:03:47 -07:00
|
|
|
virtual CoveredIndexMatcher *matcher() const { return _matcher.get(); }
|
2010-06-28 12:44:23 -07:00
|
|
|
// just for testing
|
|
|
|
|
shared_ptr< Cursor > sub_c() const { return _c; }
|
2010-05-24 22:00:25 -07:00
|
|
|
private:
|
2010-05-24 23:51:24 -07:00
|
|
|
class NoOp : public CursorOp {
|
2010-06-02 17:10:34 -07:00
|
|
|
public:
|
2010-06-02 21:24:04 -07:00
|
|
|
NoOp() {}
|
|
|
|
|
NoOp( const QueryOp &other ) : CursorOp( other ) {}
|
2010-06-02 17:10:34 -07:00
|
|
|
virtual void _init() { setComplete(); }
|
2010-05-24 23:51:24 -07:00
|
|
|
virtual void next() {}
|
|
|
|
|
virtual bool mayRecordPlan() const { return false; }
|
2010-06-02 17:10:34 -07:00
|
|
|
virtual QueryOp *_createChild() const { return new NoOp(); }
|
2010-05-24 23:51:24 -07:00
|
|
|
virtual shared_ptr< Cursor > newCursor() const { return qp().newCursor(); }
|
|
|
|
|
};
|
2010-05-24 22:00:25 -07:00
|
|
|
void nextClause() {
|
|
|
|
|
shared_ptr< CursorOp > best = _mps->runOpOnce( *_op );
|
2010-06-21 13:41:34 -04:00
|
|
|
if ( ! best->complete() )
|
|
|
|
|
throw MsgAssertionException( best->exception() );
|
2010-05-24 22:00:25 -07:00
|
|
|
_c = best->newCursor();
|
2010-06-02 17:10:34 -07:00
|
|
|
_matcher = best->matcher();
|
2010-06-02 21:24:04 -07:00
|
|
|
_op = best;
|
2010-05-24 22:00:25 -07:00
|
|
|
}
|
2010-06-02 21:24:04 -07:00
|
|
|
shared_ptr< CursorOp > _op;
|
2010-05-24 22:00:25 -07:00
|
|
|
shared_ptr< Cursor > _c;
|
|
|
|
|
auto_ptr< MultiPlanScanner > _mps;
|
2010-06-02 17:10:34 -07:00
|
|
|
shared_ptr< CoveredIndexMatcher > _matcher;
|
2010-05-24 22:00:25 -07:00
|
|
|
};
|
|
|
|
|
|
2009-04-30 16:25:36 -04:00
|
|
|
// NOTE min, max, and keyPattern will be updated to be consistent with the selected index.
|
2009-07-07 13:17:53 -04:00
|
|
|
IndexDetails *indexDetailsForRange( const char *ns, string &errmsg, BSONObj &min, BSONObj &max, BSONObj &keyPattern );
|
2010-02-05 12:02:01 -05:00
|
|
|
|
|
|
|
|
inline bool isSimpleIdQuery( const BSONObj& query ){
|
2010-07-02 10:33:56 -04:00
|
|
|
BSONObjIterator i(query);
|
|
|
|
|
if( !i.more() ) return false;
|
|
|
|
|
BSONElement e = i.next();
|
|
|
|
|
if( i.more() ) return false;
|
|
|
|
|
if( strcmp("_id", e.fieldName()) != 0 ) return false;
|
|
|
|
|
return e.isSimpleType(); // e.g. not something like { _id : { $gt : ...
|
2010-02-05 12:02:01 -05:00
|
|
|
}
|
2010-05-25 13:03:47 -07:00
|
|
|
|
|
|
|
|
// matcher() will always work on the returned cursor
|
|
|
|
|
inline shared_ptr< Cursor > bestGuessCursor( const char *ns, const BSONObj &query, const BSONObj &sort ) {
|
|
|
|
|
if( !query.getField( "$or" ).eoo() ) {
|
|
|
|
|
return shared_ptr< Cursor >( new MultiCursor( ns, query, sort ) );
|
|
|
|
|
} else {
|
2010-06-01 16:21:21 -07:00
|
|
|
auto_ptr< FieldRangeSet > frs( new FieldRangeSet( ns, query ) );
|
|
|
|
|
shared_ptr< Cursor > ret = QueryPlanSet( ns, frs, query, sort ).getBestGuess()->newCursor();
|
2010-05-25 13:03:47 -07:00
|
|
|
if ( !query.isEmpty() ) {
|
2010-06-02 17:10:34 -07:00
|
|
|
shared_ptr< CoveredIndexMatcher > matcher( new CoveredIndexMatcher( query, ret->indexKeyPattern() ) );
|
2010-05-25 13:03:47 -07:00
|
|
|
ret->setMatcher( matcher );
|
|
|
|
|
}
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
}
|
2009-04-30 16:25:36 -04:00
|
|
|
|
2009-01-14 17:09:51 -05:00
|
|
|
} // namespace mongo
|