summaryrefslogtreecommitdiff
path: root/db/queryoptimizer.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'db/queryoptimizer.cpp')
-rw-r--r--db/queryoptimizer.cpp431
1 files changed, 311 insertions, 120 deletions
diff --git a/db/queryoptimizer.cpp b/db/queryoptimizer.cpp
index fa08323..3d4cbd0 100644
--- a/db/queryoptimizer.cpp
+++ b/db/queryoptimizer.cpp
@@ -16,13 +16,14 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#include "stdafx.h"
+#include "pch.h"
#include "db.h"
#include "btree.h"
#include "pdfile.h"
#include "queryoptimizer.h"
#include "cmdline.h"
+#include "clientcursor.h"
//#define DEBUGQO(x) cout << x << endl;
#define DEBUGQO(x)
@@ -51,9 +52,10 @@ namespace mongo {
QueryPlan::QueryPlan(
NamespaceDetails *_d, int _idxNo,
- const FieldRangeSet &fbs, const BSONObj &order, const BSONObj &startKey, const BSONObj &endKey , string special ) :
+ const FieldRangeSet &fbs, const BSONObj &originalQuery, const BSONObj &order, const BSONObj &startKey, const BSONObj &endKey , string special ) :
d(_d), idxNo(_idxNo),
fbs_( fbs ),
+ _originalQuery( originalQuery ),
order_( order ),
index_( 0 ),
optimal_( false ),
@@ -63,7 +65,8 @@ namespace mongo {
endKeyInclusive_( endKey.isEmpty() ),
unhelpful_( false ),
_special( special ),
- _type(0){
+ _type(0),
+ _startOrEndSpec( !startKey.isEmpty() || !endKey.isEmpty() ){
if ( !fbs_.matchPossible() ) {
unhelpful_ = true;
@@ -84,7 +87,8 @@ namespace mongo {
optimal_ = true;
_type = index_->getSpec().getType();
massert( 13040 , (string)"no type for special: " + _special , _type );
- scanAndOrderRequired_ = _type->scanAndOrderRequired( fbs.query() , order );
+ // 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;
}
@@ -153,38 +157,39 @@ namespace mongo {
if ( exactIndexedQueryCount == fbs.nNontrivialRanges() &&
orderFieldsUnindexed.size() == 0 &&
exactIndexedQueryCount == index_->keyPattern().nFields() &&
- exactIndexedQueryCount == fbs.query().nFields() ) {
+ exactIndexedQueryCount == _originalQuery.nFields() ) {
exactKeyMatch_ = true;
}
- indexBounds_ = fbs.indexBounds( idxKey, direction_ );
- if ( !startKey.isEmpty() || !endKey.isEmpty() ) {
+ _frv.reset( new FieldRangeVector( fbs, idxKey, direction_ ) );
+ if ( _startOrEndSpec ) {
BSONObj newStart, newEnd;
if ( !startKey.isEmpty() )
- newStart = startKey;
+ _startKey = startKey;
else
- newStart = indexBounds_[ 0 ].first;
+ _startKey = _frv->startKey();
if ( !endKey.isEmpty() )
- newEnd = endKey;
+ _endKey = endKey;
else
- newEnd = indexBounds_[ indexBounds_.size() - 1 ].second;
- BoundList newBounds;
- newBounds.push_back( make_pair( newStart, newEnd ) );
- indexBounds_ = newBounds;
+ _endKey = _frv->endKey();
}
+
if ( ( scanAndOrderRequired_ || order_.isEmpty() ) &&
- !fbs.range( idxKey.firstElement().fieldName() ).nontrivial() )
+ !fbs.range( idxKey.firstElement().fieldName() ).nontrivial() ) {
unhelpful_ = true;
+ }
}
- auto_ptr< Cursor > QueryPlan::newCursor( const DiskLoc &startLoc , int numWanted ) const {
+ shared_ptr<Cursor> QueryPlan::newCursor( const DiskLoc &startLoc , int numWanted ) const {
- if ( _type )
- return _type->newCursor( fbs_.query() , order_ , numWanted );
+ if ( _type ) {
+ // hopefully safe to use original query in these contexts - don't think we can mix type with $or clause separation yet
+ return _type->newCursor( _originalQuery , order_ , numWanted );
+ }
if ( !fbs_.matchPossible() ){
if ( fbs_.nNontrivialRanges() )
checkTableScanAllowed( fbs_.ns() );
- return auto_ptr< Cursor >( new BasicCursor( DiskLoc() ) );
+ return shared_ptr<Cursor>( new BasicCursor( DiskLoc() ) );
}
if ( !index_ ){
if ( fbs_.nNontrivialRanges() )
@@ -194,17 +199,19 @@ namespace mongo {
massert( 10363 , "newCursor() with start location not implemented for indexed plans", startLoc.isNull() );
- if ( indexBounds_.size() < 2 ) {
+ if ( _startOrEndSpec ) {
// we are sure to spec endKeyInclusive_
- return auto_ptr< Cursor >( new BtreeCursor( d, idxNo, *index_, indexBounds_[ 0 ].first, indexBounds_[ 0 ].second, endKeyInclusive_, direction_ >= 0 ? 1 : -1 ) );
+ return shared_ptr<Cursor>( new BtreeCursor( d, idxNo, *index_, _startKey, _endKey, endKeyInclusive_, direction_ >= 0 ? 1 : -1 ) );
+ } else if ( index_->getSpec().getType() ) {
+ return shared_ptr<Cursor>( new BtreeCursor( d, idxNo, *index_, _frv->startKey(), _frv->endKey(), true, direction_ >= 0 ? 1 : -1 ) );
} else {
- return auto_ptr< Cursor >( new BtreeCursor( d, idxNo, *index_, indexBounds_, direction_ >= 0 ? 1 : -1 ) );
+ return shared_ptr<Cursor>( new BtreeCursor( d, idxNo, *index_, _frv, direction_ >= 0 ? 1 : -1 ) );
}
}
- auto_ptr< Cursor > QueryPlan::newReverseCursor() const {
+ shared_ptr<Cursor> QueryPlan::newReverseCursor() const {
if ( !fbs_.matchPossible() )
- return auto_ptr< Cursor >( new BasicCursor( DiskLoc() ) );
+ return shared_ptr<Cursor>( new BasicCursor( DiskLoc() ) );
if ( !index_ ) {
int orderSpec = order_.getIntField( "$natural" );
if ( orderSpec == INT_MIN )
@@ -212,7 +219,7 @@ namespace mongo {
return findTableScan( fbs_.ns(), BSON( "$natural" << -orderSpec ) );
}
massert( 10364 , "newReverseCursor() not implemented for indexed plans", false );
- return auto_ptr< Cursor >( 0 );
+ return shared_ptr<Cursor>();
}
BSONObj QueryPlan::indexKey() const {
@@ -228,10 +235,10 @@ namespace mongo {
}
}
- QueryPlanSet::QueryPlanSet( const char *_ns, const BSONObj &query, const BSONObj &order, const BSONElement *hint, bool honorRecordedPlan, const BSONObj &min, const BSONObj &max ) :
+ QueryPlanSet::QueryPlanSet( const char *_ns, auto_ptr< FieldRangeSet > frs, const BSONObj &originalQuery, const BSONObj &order, const BSONElement *hint, bool honorRecordedPlan, const BSONObj &min, const BSONObj &max, bool bestGuessOnly, bool mayYield ) :
ns(_ns),
- query_( query.getOwned() ),
- fbs_( _ns, query ),
+ _originalQuery( originalQuery ),
+ fbs_( frs ),
mayRecordPlan_( true ),
usingPrerecordedPlan_( false ),
hint_( BSONObj() ),
@@ -239,7 +246,10 @@ namespace mongo {
oldNScanned_( 0 ),
honorRecordedPlan_( honorRecordedPlan ),
min_( min.getOwned() ),
- max_( max.getOwned() ) {
+ max_( max.getOwned() ),
+ _bestGuessOnly( bestGuessOnly ),
+ _mayYield( mayYield ),
+ _yieldSometimesTracker( 256, 20 ){
if ( hint && !hint->eoo() ) {
hint_ = hint->wrap();
}
@@ -251,59 +261,70 @@ namespace mongo {
string errmsg;
BSONObj keyPattern = id.keyPattern();
// This reformats min_ and max_ to be used for index lookup.
- massert( 10365 , errmsg, indexDetailsForRange( fbs_.ns(), errmsg, min_, max_, keyPattern ) );
+ massert( 10365 , errmsg, indexDetailsForRange( fbs_->ns(), errmsg, min_, max_, keyPattern ) );
}
NamespaceDetails *d = nsdetails(ns);
- plans_.push_back( PlanPtr( new QueryPlan( d, d->idxNo(id), fbs_, order_, min_, max_ ) ) );
+ plans_.push_back( PlanPtr( new QueryPlan( d, d->idxNo(id), *fbs_, _originalQuery, order_, min_, max_ ) ) );
+ }
+
+ // returns an IndexDetails * for a hint, 0 if hint is $natural.
+ // hint must not be eoo()
+ IndexDetails *parseHint( const BSONElement &hint, NamespaceDetails *d ) {
+ massert( 13292, "hint eoo", !hint.eoo() );
+ if( hint.type() == String ) {
+ string hintstr = hint.valuestr();
+ NamespaceDetails::IndexIterator i = d->ii();
+ while( i.more() ) {
+ IndexDetails& ii = i.next();
+ if ( ii.indexName() == hintstr ) {
+ return &ii;
+ }
+ }
+ }
+ else if( hint.type() == Object ) {
+ BSONObj hintobj = hint.embeddedObject();
+ uassert( 10112 , "bad hint", !hintobj.isEmpty() );
+ if ( !strcmp( hintobj.firstElement().fieldName(), "$natural" ) ) {
+ return 0;
+ }
+ NamespaceDetails::IndexIterator i = d->ii();
+ while( i.more() ) {
+ IndexDetails& ii = i.next();
+ if( ii.keyPattern().woCompare(hintobj) == 0 ) {
+ return &ii;
+ }
+ }
+ }
+ uassert( 10113 , "bad hint", false );
+ return 0;
}
void QueryPlanSet::init() {
- DEBUGQO( "QueryPlanSet::init " << ns << "\t" << query_ );
+ DEBUGQO( "QueryPlanSet::init " << ns << "\t" << _originalQuery );
plans_.clear();
mayRecordPlan_ = true;
usingPrerecordedPlan_ = false;
- const char *ns = fbs_.ns();
+ const char *ns = fbs_->ns();
NamespaceDetails *d = nsdetails( ns );
- if ( !d || !fbs_.matchPossible() ) {
+ if ( !d || !fbs_->matchPossible() ) {
// Table scan plan, when no matches are possible
- plans_.push_back( PlanPtr( new QueryPlan( d, -1, fbs_, order_ ) ) );
+ plans_.push_back( PlanPtr( new QueryPlan( d, -1, *fbs_, _originalQuery, order_ ) ) );
return;
}
BSONElement hint = hint_.firstElement();
if ( !hint.eoo() ) {
mayRecordPlan_ = false;
- if( hint.type() == String ) {
- string hintstr = hint.valuestr();
- NamespaceDetails::IndexIterator i = d->ii();
- while( i.more() ) {
- IndexDetails& ii = i.next();
- if ( ii.indexName() == hintstr ) {
- addHint( ii );
- return;
- }
- }
- }
- else if( hint.type() == Object ) {
- BSONObj hintobj = hint.embeddedObject();
- uassert( 10112 , "bad hint", !hintobj.isEmpty() );
- if ( !strcmp( hintobj.firstElement().fieldName(), "$natural" ) ) {
- massert( 10366 , "natural order cannot be specified with $min/$max", min_.isEmpty() && max_.isEmpty() );
- // Table scan plan
- plans_.push_back( PlanPtr( new QueryPlan( d, -1, fbs_, order_ ) ) );
- return;
- }
- NamespaceDetails::IndexIterator i = d->ii();
- while( i.more() ) {
- IndexDetails& ii = i.next();
- if( ii.keyPattern().woCompare(hintobj) == 0 ) {
- addHint( ii );
- return;
- }
- }
+ IndexDetails *id = parseHint( hint, d );
+ if ( id ) {
+ addHint( *id );
+ } else {
+ massert( 10366 , "natural order cannot be specified with $min/$max", min_.isEmpty() && max_.isEmpty() );
+ // Table scan plan
+ plans_.push_back( PlanPtr( new QueryPlan( d, -1, *fbs_, _originalQuery, order_ ) ) );
}
- uassert( 10113 , "bad hint", false );
+ return;
}
if ( !min_.isEmpty() || !max_.isEmpty() ) {
@@ -311,56 +332,54 @@ namespace mongo {
BSONObj keyPattern;
IndexDetails *idx = indexDetailsForRange( ns, errmsg, min_, max_, keyPattern );
massert( 10367 , errmsg, idx );
- plans_.push_back( PlanPtr( new QueryPlan( d, d->idxNo(*idx), fbs_, order_, min_, max_ ) ) );
+ plans_.push_back( PlanPtr( new QueryPlan( d, d->idxNo(*idx), *fbs_, _originalQuery, order_, min_, max_ ) ) );
return;
}
- if ( isSimpleIdQuery( query_ ) ){
+ if ( isSimpleIdQuery( _originalQuery ) ){
int idx = d->findIdIndex();
if ( idx >= 0 ){
usingPrerecordedPlan_ = true;
mayRecordPlan_ = false;
- plans_.push_back( PlanPtr( new QueryPlan( d , idx , fbs_ , order_ ) ) );
+ plans_.push_back( PlanPtr( new QueryPlan( d , idx , *fbs_ , _originalQuery, order_ ) ) );
return;
}
}
- if ( query_.isEmpty() && order_.isEmpty() ){
- plans_.push_back( PlanPtr( new QueryPlan( d, -1, fbs_, order_ ) ) );
+ if ( _originalQuery.isEmpty() && order_.isEmpty() ){
+ plans_.push_back( PlanPtr( new QueryPlan( d, -1, *fbs_, _originalQuery, order_ ) ) );
return;
}
- DEBUGQO( "\t special : " << fbs_.getSpecial() );
- if ( fbs_.getSpecial().size() ){
- _special = fbs_.getSpecial();
+ DEBUGQO( "\t special : " << fbs_->getSpecial() );
+ if ( fbs_->getSpecial().size() ){
+ _special = fbs_->getSpecial();
NamespaceDetails::IndexIterator i = d->ii();
while( i.more() ) {
int j = i.pos();
IndexDetails& ii = i.next();
const IndexSpec& spec = ii.getSpec();
- if ( spec.getTypeName() == _special && spec.suitability( query_ , order_ ) ){
+ if ( spec.getTypeName() == _special && spec.suitability( _originalQuery , order_ ) ){
usingPrerecordedPlan_ = true;
mayRecordPlan_ = true;
- plans_.push_back( PlanPtr( new QueryPlan( d , j , fbs_ , order_ ,
+ plans_.push_back( PlanPtr( new QueryPlan( d , j , *fbs_ , _originalQuery, order_ ,
BSONObj() , BSONObj() , _special ) ) );
return;
}
}
- uassert( 13038 , (string)"can't find special index: " + _special + " for: " + query_.toString() , 0 );
+ uassert( 13038 , (string)"can't find special index: " + _special + " for: " + _originalQuery.toString() , 0 );
}
if ( honorRecordedPlan_ ) {
scoped_lock lk(NamespaceDetailsTransient::_qcMutex);
NamespaceDetailsTransient& nsd = NamespaceDetailsTransient::get_inlock( ns );
- BSONObj bestIndex = nsd.indexForPattern( fbs_.pattern( order_ ) );
+ BSONObj bestIndex = nsd.indexForPattern( fbs_->pattern( order_ ) );
if ( !bestIndex.isEmpty() ) {
- usingPrerecordedPlan_ = true;
- mayRecordPlan_ = false;
- oldNScanned_ = nsd.nScannedForPattern( fbs_.pattern( order_ ) );
+ PlanPtr p;
+ oldNScanned_ = nsd.nScannedForPattern( fbs_->pattern( order_ ) );
if ( !strcmp( bestIndex.firstElement().fieldName(), "$natural" ) ) {
// Table scan plan
- plans_.push_back( PlanPtr( new QueryPlan( d, -1, fbs_, order_ ) ) );
- return;
+ p.reset( new QueryPlan( d, -1, *fbs_, _originalQuery, order_ ) );
}
NamespaceDetails::IndexIterator i = d->ii();
@@ -368,11 +387,17 @@ namespace mongo {
int j = i.pos();
IndexDetails& ii = i.next();
if( ii.keyPattern().woCompare(bestIndex) == 0 ) {
- plans_.push_back( PlanPtr( new QueryPlan( d, j, fbs_, order_ ) ) );
- return;
+ p.reset( new QueryPlan( d, j, *fbs_, _originalQuery, order_ ) );
}
}
- massert( 10368 , "Unable to locate previously recorded index", false );
+
+ massert( 10368 , "Unable to locate previously recorded index", p.get() );
+ if ( !( _bestGuessOnly && p->scanAndOrderRequired() ) ) {
+ usingPrerecordedPlan_ = true;
+ mayRecordPlan_ = false;
+ plans_.push_back( p );
+ return;
+ }
}
}
@@ -380,16 +405,16 @@ namespace mongo {
}
void QueryPlanSet::addOtherPlans( bool checkFirst ) {
- const char *ns = fbs_.ns();
+ const char *ns = fbs_->ns();
NamespaceDetails *d = nsdetails( ns );
if ( !d )
return;
// If table scan is optimal or natural order requested or tailable cursor requested
- if ( !fbs_.matchPossible() || ( fbs_.nNontrivialRanges() == 0 && order_.isEmpty() ) ||
+ if ( !fbs_->matchPossible() || ( fbs_->nNontrivialRanges() == 0 && order_.isEmpty() ) ||
( !order_.isEmpty() && !strcmp( order_.firstElement().fieldName(), "$natural" ) ) ) {
// Table scan plan
- addPlan( PlanPtr( new QueryPlan( d, -1, fbs_, order_ ) ), checkFirst );
+ addPlan( PlanPtr( new QueryPlan( d, -1, *fbs_, _originalQuery, order_ ) ), checkFirst );
return;
}
@@ -401,12 +426,12 @@ namespace mongo {
const IndexSpec& spec = id.getSpec();
IndexSuitability suitability = HELPFUL;
if ( normalQuery ){
- suitability = spec.suitability( query_ , order_ );
+ suitability = spec.suitability( fbs_->simplifiedQuery() , order_ );
if ( suitability == USELESS )
continue;
}
- PlanPtr p( new QueryPlan( d, i, fbs_, order_ ) );
+ PlanPtr p( new QueryPlan( d, i, *fbs_, _originalQuery, order_ ) );
if ( p->optimal() ) {
addPlan( p, checkFirst );
return;
@@ -418,7 +443,7 @@ namespace mongo {
addPlan( *i, checkFirst );
// Table scan plan
- addPlan( PlanPtr( new QueryPlan( d, -1, fbs_, order_ ) ), checkFirst );
+ addPlan( PlanPtr( new QueryPlan( d, -1, *fbs_, _originalQuery, order_ ) ), checkFirst );
}
shared_ptr< QueryOp > QueryPlanSet::runOp( QueryOp &op ) {
@@ -426,11 +451,11 @@ namespace mongo {
Runner r( *this, op );
shared_ptr< QueryOp > res = r.run();
// plans_.size() > 1 if addOtherPlans was called in Runner::run().
- if ( res->complete() || plans_.size() > 1 )
+ if ( _bestGuessOnly || res->complete() || plans_.size() > 1 )
return res;
{
scoped_lock lk(NamespaceDetailsTransient::_qcMutex);
- NamespaceDetailsTransient::get_inlock( fbs_.ns() ).registerIndexForPattern( fbs_.pattern( order_ ), BSONObj(), 0 );
+ NamespaceDetailsTransient::get_inlock( fbs_->ns() ).registerIndexForPattern( fbs_->pattern( order_ ), BSONObj(), 0 );
}
init();
}
@@ -441,33 +466,80 @@ namespace mongo {
BSONObj QueryPlanSet::explain() const {
vector< BSONObj > arr;
for( PlanSet::const_iterator i = plans_.begin(); i != plans_.end(); ++i ) {
- auto_ptr< Cursor > c = (*i)->newCursor();
+ shared_ptr<Cursor> c = (*i)->newCursor();
BSONObjBuilder explain;
explain.append( "cursor", c->toString() );
- explain.appendArray( "indexBounds", c->prettyIndexBounds() );
+ explain.append( "indexBounds", c->prettyIndexBounds() );
arr.push_back( explain.obj() );
}
BSONObjBuilder b;
b.append( "allPlans", arr );
return b.obj();
}
+
+ QueryPlanSet::PlanPtr QueryPlanSet::getBestGuess() const {
+ assert( plans_.size() );
+ if ( plans_[ 0 ]->scanAndOrderRequired() ){
+ for ( unsigned i=1; i<plans_.size(); i++ ){
+ if ( ! plans_[i]->scanAndOrderRequired() )
+ return plans_[i];
+ }
+
+ stringstream ss;
+ ss << "best guess plan requested, but scan and order required:";
+ ss << " query: " << fbs_->simplifiedQuery();
+ ss << " order: " << order_;
+ ss << " choices: ";
+ for ( unsigned i=0; i<plans_.size(); i++ ){
+ ss << plans_[i]->indexKey() << " ";
+ }
+
+ string s = ss.str();
+ msgassertedNoTrace( 13284, s.c_str() );
+ }
+ return plans_[0];
+ }
QueryPlanSet::Runner::Runner( QueryPlanSet &plans, QueryOp &op ) :
op_( op ),
plans_( plans ) {
}
+ void QueryPlanSet::Runner::mayYield( const vector< shared_ptr< QueryOp > > &ops ) {
+ if ( plans_._mayYield ) {
+ if ( plans_._yieldSometimesTracker.ping() ) {
+ int micros = ClientCursor::yieldSuggest();
+ if ( micros > 0 ) {
+ for( vector< shared_ptr< QueryOp > >::const_iterator i = ops.begin(); i != ops.end(); ++i ) {
+ if ( !prepareToYield( **i ) ) {
+ return;
+ }
+ }
+ ClientCursor::staticYield( micros );
+ for( vector< shared_ptr< QueryOp > >::const_iterator i = ops.begin(); i != ops.end(); ++i ) {
+ recoverFromYield( **i );
+ }
+ }
+ }
+ }
+ }
+
shared_ptr< QueryOp > QueryPlanSet::Runner::run() {
massert( 10369 , "no plans", plans_.plans_.size() > 0 );
- if ( plans_.plans_.size() > 1 )
- log(1) << " running multiple plans" << endl;
-
vector< shared_ptr< QueryOp > > ops;
- for( PlanSet::iterator i = plans_.plans_.begin(); i != plans_.plans_.end(); ++i ) {
- shared_ptr< QueryOp > op( op_.clone() );
- op->setQueryPlan( i->get() );
- ops.push_back( op );
+ if ( plans_._bestGuessOnly ) {
+ shared_ptr< QueryOp > op( op_.createChild() );
+ op->setQueryPlan( plans_.getBestGuess().get() );
+ ops.push_back( op );
+ } else {
+ if ( plans_.plans_.size() > 1 )
+ log(1) << " running multiple plans" << endl;
+ for( PlanSet::iterator i = plans_.plans_.begin(); i != plans_.plans_.end(); ++i ) {
+ shared_ptr< QueryOp > op( op_.createChild() );
+ op->setQueryPlan( i->get() );
+ ops.push_back( op );
+ }
}
for( vector< shared_ptr< QueryOp > >::iterator i = ops.begin(); i != ops.end(); ++i ) {
@@ -483,13 +555,16 @@ namespace mongo {
unsigned errCount = 0;
bool first = true;
for( vector< shared_ptr< QueryOp > >::iterator i = ops.begin(); i != ops.end(); ++i ) {
+ mayYield( ops );
QueryOp &op = **i;
nextOp( op );
if ( op.complete() ) {
- if ( first )
+ if ( first ) {
nScanned += nScannedBackup;
- if ( plans_.mayRecordPlan_ && op.mayRecordPlan() )
+ }
+ if ( plans_.mayRecordPlan_ && op.mayRecordPlan() ) {
op.qp().registerSelf( nScanned );
+ }
return *i;
}
if ( op.error() )
@@ -498,12 +573,12 @@ namespace mongo {
}
if ( errCount == ops.size() )
break;
- if ( plans_.usingPrerecordedPlan_ && nScanned > plans_.oldNScanned_ * 10 && plans_._special.empty() ) {
+ if ( !plans_._bestGuessOnly && plans_.usingPrerecordedPlan_ && nScanned > plans_.oldNScanned_ * 10 && plans_._special.empty() ) {
plans_.addOtherPlans( true );
PlanSet::iterator i = plans_.plans_.begin();
++i;
for( ; i != plans_.plans_.end(); ++i ) {
- shared_ptr< QueryOp > op( op_.clone() );
+ shared_ptr< QueryOp > op( op_.createChild() );
op->setQueryPlan( i->get() );
ops.push_back( op );
initOp( *op );
@@ -519,27 +594,143 @@ namespace mongo {
return ops[ 0 ];
}
+#define GUARD_OP_EXCEPTION( op, expression ) \
+ try { \
+ expression; \
+ } \
+ catch ( DBException& e ) { \
+ op.setException( e.getInfo() ); \
+ } \
+ catch ( const std::exception &e ) { \
+ op.setException( ExceptionInfo( e.what() , 0 ) ); \
+ } \
+ catch ( ... ) { \
+ op.setException( ExceptionInfo( "Caught unknown exception" , 0 ) ); \
+ }
+
+
void QueryPlanSet::Runner::initOp( QueryOp &op ) {
- try {
- op.init();
- } catch ( const std::exception &e ) {
- op.setExceptionMessage( e.what() );
- } catch ( ... ) {
- op.setExceptionMessage( "Caught unknown exception" );
- }
+ GUARD_OP_EXCEPTION( op, op.init() );
}
void QueryPlanSet::Runner::nextOp( QueryOp &op ) {
- try {
- if ( !op.error() )
- op.next();
- } catch ( const std::exception &e ) {
- op.setExceptionMessage( e.what() );
- } catch ( ... ) {
- op.setExceptionMessage( "Caught unknown exception" );
- }
+ GUARD_OP_EXCEPTION( op, if ( !op.error() ) { op.next(); } );
+ }
+
+ bool QueryPlanSet::Runner::prepareToYield( QueryOp &op ) {
+ GUARD_OP_EXCEPTION( op,
+ if ( op.error() ) {
+ return true;
+ } else {
+ return op.prepareToYield();
+ } );
+ return true;
}
+ void QueryPlanSet::Runner::recoverFromYield( QueryOp &op ) {
+ GUARD_OP_EXCEPTION( op, if ( !op.error() ) { op.recoverFromYield(); } );
+ }
+
+
+ MultiPlanScanner::MultiPlanScanner( const char *ns,
+ const BSONObj &query,
+ const BSONObj &order,
+ const BSONElement *hint,
+ bool honorRecordedPlan,
+ const BSONObj &min,
+ const BSONObj &max,
+ bool bestGuessOnly,
+ bool mayYield ) :
+ _ns( ns ),
+ _or( !query.getField( "$or" ).eoo() ),
+ _query( query.getOwned() ),
+ _fros( ns, _query ),
+ _i(),
+ _honorRecordedPlan( honorRecordedPlan ),
+ _bestGuessOnly( bestGuessOnly ),
+ _hint( ( hint && !hint->eoo() ) ? hint->wrap() : BSONObj() ),
+ _mayYield( mayYield ),
+ _tableScanned()
+ {
+ if ( !order.isEmpty() || !min.isEmpty() || !max.isEmpty() || !_fros.getSpecial().empty() ) {
+ _or = false;
+ }
+ if ( _or && uselessOr( _hint.firstElement() ) ) {
+ _or = false;
+ }
+ // if _or == false, don't use or clauses for index selection
+ if ( !_or ) {
+ auto_ptr< FieldRangeSet > frs( new FieldRangeSet( ns, _query ) );
+ _currentQps.reset( new QueryPlanSet( ns, frs, _query, order, hint, honorRecordedPlan, min, max, _bestGuessOnly, _mayYield ) );
+ } else {
+ BSONElement e = _query.getField( "$or" );
+ massert( 13268, "invalid $or spec", e.type() == Array && e.embeddedObject().nFields() > 0 );
+ }
+ }
+
+ shared_ptr< QueryOp > MultiPlanScanner::runOpOnce( QueryOp &op ) {
+ massert( 13271, "can't run more ops", mayRunMore() );
+ if ( !_or ) {
+ ++_i;
+ return _currentQps->runOp( op );
+ }
+ ++_i;
+ auto_ptr< FieldRangeSet > frs( _fros.topFrs() );
+ BSONElement hintElt = _hint.firstElement();
+ _currentQps.reset( new QueryPlanSet( _ns, frs, _query, BSONObj(), &hintElt, _honorRecordedPlan, BSONObj(), BSONObj(), _bestGuessOnly, _mayYield ) );
+ shared_ptr< QueryOp > ret( _currentQps->runOp( op ) );
+ if ( ret->qp().willScanTable() ) {
+ _tableScanned = true;
+ }
+ _fros.popOrClause();
+ return ret;
+ }
+
+ shared_ptr< QueryOp > MultiPlanScanner::runOp( QueryOp &op ) {
+ shared_ptr< QueryOp > ret = runOpOnce( op );
+ while( !ret->stopRequested() && mayRunMore() ) {
+ ret = runOpOnce( *ret );
+ }
+ return ret;
+ }
+
+ bool MultiPlanScanner::uselessOr( const BSONElement &hint ) const {
+ NamespaceDetails *nsd = nsdetails( _ns );
+ if ( !nsd ) {
+ return true;
+ }
+ IndexDetails *id = 0;
+ if ( !hint.eoo() ) {
+ IndexDetails *id = parseHint( hint, nsd );
+ if ( !id ) {
+ return true;
+ }
+ }
+ vector< BSONObj > ret;
+ _fros.allClausesSimplified( ret );
+ for( vector< BSONObj >::const_iterator i = ret.begin(); i != ret.end(); ++i ) {
+ if ( id ) {
+ if ( id->getSpec().suitability( *i, BSONObj() ) == USELESS ) {
+ return true;
+ }
+ } else {
+ bool useful = false;
+ NamespaceDetails::IndexIterator j = nsd->ii();
+ while( j.more() ) {
+ IndexDetails &id = j.next();
+ if ( id.getSpec().suitability( *i, BSONObj() ) != USELESS ) {
+ useful = true;
+ break;
+ }
+ }
+ if ( !useful ) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
bool indexWorks( const BSONObj &idxPattern, const BSONObj &sampleKey, int direction, int firstSignificantField ) {
BSONObjIterator p( idxPattern );
BSONObjIterator k( sampleKey );