From db0f38e88bf5e3d7391c233a9b4dec418355faaa Mon Sep 17 00:00:00 2001 From: Aaron Date: Fri, 20 Feb 2009 11:08:22 -0500 Subject: [PATCH] Dummy count implementation --- db/btree.h | 1 + db/btreecursor.cpp | 26 +++++ db/instance.h | 1 + db/queryoptimizer.cpp | 91 ++++++++++++++--- db/queryoptimizer.h | 72 +++++++++++++- dbtests/queryoptimizertests.cpp | 169 ++++++++++++++++++++++---------- util/goodies.h | 1 + 7 files changed, 292 insertions(+), 69 deletions(-) diff --git a/db/btree.h b/db/btree.h index 76e9d770af5..f5767a37f66 100644 --- a/db/btree.h +++ b/db/btree.h @@ -208,6 +208,7 @@ namespace mongo { BSONObj endKey; public: BtreeCursor(IndexDetails&, const BSONObj& startKey, int direction, const BSONObj& query); + BtreeCursor( IndexDetails&, const BSONObj &startKey, const BSONObj &endKey, int direction ); virtual bool ok() { return !bucket.isNull(); } diff --git a/db/btreecursor.cpp b/db/btreecursor.cpp index 614b1313cbd..a6a0ad9a41a 100644 --- a/db/btreecursor.cpp +++ b/db/btreecursor.cpp @@ -61,6 +61,32 @@ namespace mongo { checkEnd(); } + BtreeCursor::BtreeCursor( IndexDetails &_id, const BSONObj &_startKey, const BSONObj &_endKey, int _direction ) : + startKey( _startKey ), + endKey( _endKey ), + indexDetails( _id ), + order( _id.keyPattern() ), + direction( _direction ) { + bool found; + if ( otherTraceLevel >= 12 ) { + if ( otherTraceLevel >= 200 ) { + out() << "::BtreeCursor() qtl>200. validating entire index." << endl; + indexDetails.head.btree()->fullValidate(indexDetails.head, order); + } + else { + out() << "BTreeCursor(). dumping head bucket" << endl; + indexDetails.head.btree()->dump(); + } + } + + bucket = indexDetails.head.btree()-> + locate(indexDetails, indexDetails.head, startKey, order, keyOfs, found, direction > 0 ? minDiskLoc : maxDiskLoc, direction); + + skipUnusedKeys(); + + checkEnd(); + } + string BtreeCursor::simpleRegexEnd( string regex ) { ++regex[ regex.length() - 1 ]; return regex; diff --git a/db/instance.h b/db/instance.h index 334090aa5b9..d8c441186ff 100644 --- a/db/instance.h +++ b/db/instance.h @@ -19,6 +19,7 @@ #pragma once +#include "../client/dbclient.h" #include "security.h" namespace mongo { diff --git a/db/queryoptimizer.cpp b/db/queryoptimizer.cpp index 343b75affd4..65296b54183 100644 --- a/db/queryoptimizer.cpp +++ b/db/queryoptimizer.cpp @@ -17,6 +17,8 @@ */ #include "stdafx.h" + +#include "btree.h" #include "pdfile.h" #include "queryoptimizer.h" @@ -91,7 +93,8 @@ namespace mongo { return o; } - FieldBoundSet::FieldBoundSet( const BSONObj &query ) : + FieldBoundSet::FieldBoundSet( const char *ns, const BSONObj &query ) : + ns_( ns ), query_( query.copy() ) { BSONObjIterator i( query_ ); while( i.more() ) { @@ -120,18 +123,23 @@ namespace mongo { return *trivialBound_; } - QueryPlan::QueryPlan( const FieldBoundSet &fbs, const BSONObj &order, const BSONObj &idxKey ) : + QueryPlan::QueryPlan( const FieldBoundSet &fbs, const BSONObj &order, const IndexDetails *index ) : + fbs_( fbs ), + order_( order ), + index_( index ), optimal_( false ), scanAndOrderRequired_( true ), keyMatch_( false ), exactKeyMatch_( false ), direction_( 0 ) { // full table scan case - if ( idxKey.isEmpty() ) { - if ( order.isEmpty() ) + if ( !index_ ) { + if ( order_.isEmpty() ) scanAndOrderRequired_ = false; return; } + + BSONObj idxKey = index->keyPattern(); BSONObjIterator o( order ); BSONObjIterator k( idxKey ); if ( !o.more() ) @@ -212,8 +220,16 @@ namespace mongo { endKey_ = ( direction_ >= 0 ) ? highKey : lowKey; } + auto_ptr< Cursor > QueryPlan::newCursor() const { + if ( !index_ ) + return theDataFileMgr.findAll( fbs_.ns() ); + else + return auto_ptr< Cursor >( new BtreeCursor( *const_cast< IndexDetails* >( index_ ), startKey_, endKey_, direction_ ) ); + //TODO This constructor should really take a const ref to the index details. + } + QueryPlanSet::QueryPlanSet( const char *ns, const BSONObj &query, const BSONObj &order, const BSONElement *hint ) : - fbs_( query ) { + fbs_( ns, query ) { NamespaceDetails *d = nsdetails( ns ); assert( d ); @@ -223,7 +239,7 @@ namespace mongo { for (int i = 0; i < d->nIndexes; i++ ) { IndexDetails& ii = d->indexes[i]; if ( ii.indexName() == hintstr ) { - plans_.push_back( QueryPlan( fbs_, order, ii.keyPattern() ) ); + plans_.push_back( PlanPtr( new QueryPlan( fbs_, order, &ii ) ) ); return; } } @@ -233,7 +249,7 @@ namespace mongo { for (int i = 0; i < d->nIndexes; i++ ) { IndexDetails& ii = d->indexes[i]; if( ii.keyPattern().woCompare(hintobj) == 0 ) { - plans_.push_back( QueryPlan( fbs_, order, ii.keyPattern() ) ); + plans_.push_back( PlanPtr( new QueryPlan( fbs_, order, &ii ) ) ); return; } } @@ -242,25 +258,76 @@ namespace mongo { } // Table scan plan - plans_.push_back( QueryPlan( fbs_, order, emptyObj ) ); + plans_.push_back( PlanPtr( new QueryPlan( fbs_, order ) ) ); // If table scan is optimal if ( fbs_.nNontrivialBounds() == 0 && order.isEmpty() ) return; - vector< QueryPlan > plans; + PlanSet plans; for( int i = 0; i < d->nIndexes; ++i ) { - QueryPlan p( fbs_, order, d->indexes[ i ].keyPattern() ); - if ( p.optimal() ) { + PlanPtr p( new QueryPlan( fbs_, order, &d->indexes[ i ] ) ); + if ( p->optimal() ) { plans_.push_back( p ); return; } plans.push_back( p ); } - for( vector< QueryPlan >::iterator i = plans.begin(); i != plans.end(); ++i ) + for( PlanSet::iterator i = plans.begin(); i != plans.end(); ++i ) plans_.push_back( *i ); } + auto_ptr< QueryOp > QueryPlanSet::runOp( QueryOp &op ) { + RunnerSet s( *this, op ); + return s.run(); + } + + QueryPlanSet::RunnerSet::RunnerSet( QueryPlanSet &plans, QueryOp &op ) : + op_( op ), + plans_( plans ), + startBarrier_( plans_.nPlans() ), + firstDone_( false ) { + } + + auto_ptr< QueryOp > QueryPlanSet::RunnerSet::run() { + boost::thread_group threads; + auto_ptr< QueryOp > ops[ plans_.nPlans() ]; + for( int i = 0; i < plans_.nPlans(); ++i ) { + ops[ i ] = auto_ptr< QueryOp >( op_.clone() ); + Runner r( *plans_.plans_[ i ], *this, *ops[ i ] ); + threads.create_thread( r ); + } + threads.join_all(); + cout << "really done" << endl; + for( int i = 0; i < plans_.nPlans(); ++i ) + if ( ops[ i ]->done() ) + return ops[ i ]; + assert( false ); + return auto_ptr< QueryOp >( 0 ); + } + + class CountOp : public QueryOp { + public: + virtual void run( const QueryPlan &qp, QueryAborter &qa ) { + for( int i = 0; i < 100000; ++i ) + qa.mayAbort(); + cout << "done" << endl; + } + virtual QueryOp *clone() const { + return new CountOp( *this ); + } + int count() const { return 1; } + }; + + int doCount( const char *ns, const BSONObj &cmd, string &err ) { + BSONObj query = cmd.getObjectField("query"); + QueryPlanSet qps( ns, query, emptyObj ); + auto_ptr< QueryOp > original( new CountOp () ); + auto_ptr< QueryOp > o = qps.runOp( *original ); + return dynamic_cast< CountOp* >( o.get() )->count(); + } + + // QueryPlan QueryOptimizer::getPlan( // const char *ns, // BSONObj* query, diff --git a/db/queryoptimizer.h b/db/queryoptimizer.h index cd8b228069b..cd69c5ab7fe 100644 --- a/db/queryoptimizer.h +++ b/db/queryoptimizer.h @@ -45,7 +45,7 @@ namespace mongo { class FieldBoundSet { public: - FieldBoundSet( const BSONObj &query ); + FieldBoundSet( const char *ns, const BSONObj &query ); const FieldBound &bound( const char *fieldName ) const { map< string, FieldBound >::const_iterator f = bounds_.find( fieldName ); if ( f == bounds_.end() ) @@ -65,16 +65,20 @@ namespace mongo { ++count; return count; } + const char *ns() const { return ns_; } private: static FieldBound *trivialBound_; static FieldBound &trivialBound(); map< string, FieldBound > bounds_; + const char *ns_; BSONObj query_; }; + class IndexDetails; class QueryPlan { public: - QueryPlan( const FieldBoundSet &fbs, const BSONObj &order, const BSONObj &idxKey ); + QueryPlan( const FieldBoundSet &fbs, const BSONObj &order, const IndexDetails *index = 0 ); + QueryPlan( const QueryPlan &other ); /* If true, no other index can do better. */ bool optimal() const { return optimal_; } /* ScanAndOrder processing will be required if true */ @@ -88,7 +92,11 @@ namespace mongo { int direction() const { return direction_; } BSONObj startKey() const { return startKey_; } BSONObj endKey() const { return endKey_; } + auto_ptr< Cursor > newCursor() const; private: + const FieldBoundSet &fbs_; + const BSONObj &order_; + const IndexDetails *index_; bool optimal_; bool scanAndOrderRequired_; bool keyMatch_; @@ -98,15 +106,73 @@ namespace mongo { BSONObj endKey_; }; + class QueryAborter { + public: + QueryAborter( bool &firstDone ) : + firstDone_( firstDone ){} + class AbortException : public std::exception { + }; + void mayAbort() { + if ( firstDone_ ) + throw AbortException(); + } + private: + bool &firstDone_; + }; + + class QueryOp { + public: + QueryOp() : done_( false ) {} + virtual ~QueryOp() {} + virtual void run( const QueryPlan &qp, QueryAborter &qa ) = 0; + virtual QueryOp *clone() const = 0; + bool done() const { return done_; } + void setDone() { done_ = true; } + private: + bool done_; + }; + class QueryPlanSet { public: QueryPlanSet( const char *ns, const BSONObj &query, const BSONObj &order, const BSONElement *hint = 0 ); int nPlans() const { return plans_.size(); } + auto_ptr< QueryOp > runOp( QueryOp &op ); private: + struct RunnerSet { + RunnerSet( QueryPlanSet &plans, QueryOp &op ); + auto_ptr< QueryOp > run(); + QueryOp &op_; + QueryPlanSet &plans_; + boost::barrier startBarrier_; + bool firstDone_; + }; + struct Runner { + Runner( QueryPlan &plan, RunnerSet &set, QueryOp &op ) : + plan_( plan ), + set_( set ), + op_( op ) {} + void operator()() { + try { + set_.startBarrier_.wait(); + QueryAborter aborter( set_.firstDone_ ); + op_.run( plan_, aborter ); + set_.firstDone_ = true; + op_.setDone(); + } catch ( const QueryAborter::AbortException & ) { + } + } + QueryPlan &plan_; + RunnerSet &set_; + QueryOp &op_; + }; FieldBoundSet fbs_; - vector< QueryPlan > plans_; + typedef boost::shared_ptr< QueryPlan > PlanPtr; + typedef vector< PlanPtr > PlanSet; + PlanSet plans_; }; + int doCount( const char *ns, const BSONObj &cmd, string &err ); + // class QueryOptimizer { // public: // static QueryPlan getPlan( diff --git a/dbtests/queryoptimizertests.cpp b/dbtests/queryoptimizertests.cpp index ad1fbeb3e17..f0f5622be9e 100644 --- a/dbtests/queryoptimizertests.cpp +++ b/dbtests/queryoptimizertests.cpp @@ -21,6 +21,7 @@ #include "../db/db.h" #include "../db/dbhelpers.h" +#include "../db/instance.h" #include "dbtests.h" @@ -31,7 +32,7 @@ namespace QueryOptimizerTests { public: virtual ~Base() {} void run() { - FieldBoundSet s( query() ); + FieldBoundSet s( "ns", query() ); checkElt( lower(), s.bound( "a" ).lower() ); checkElt( upper(), s.bound( "a" ).upper() ); } @@ -53,7 +54,7 @@ namespace QueryOptimizerTests { public: virtual ~Bad() {} void run() { - ASSERT_EXCEPTION( FieldBoundSet f( query() ), AssertionException ); + ASSERT_EXCEPTION( FieldBoundSet f( "ns", query() ), AssertionException ); } protected: virtual BSONObj query() = 0; @@ -163,10 +164,52 @@ namespace QueryOptimizerTests { } // namespace FieldBoundTests namespace QueryPlanTests { - class NoIndex { + class Base { + public: + Base() : indexNum_( 0 ) { + setClient( ns() ); + string err; + userCreateNS( ns(), emptyObj, err, false ); + } + ~Base() { + if ( !nsd() ) + return; + string s( ns() ); + dropNS( s ); + } + protected: + static const char *ns() { return "QueryPlanTests.coll"; } + static NamespaceDetails *nsd() { return nsdetails( ns() ); } + const IndexDetails *index( const BSONObj &key ) { + dbtemprelease r; + stringstream ss; + ss << indexNum_++; + string name = ss.str(); + client_.resetIndexCache(); + client_.ensureIndex( ns(), key, name.c_str() ); + NamespaceDetails *d = nsd(); + for( int i = 0; i < d->nIndexes; ++i ) { + if ( d->indexes[ i ].indexName() == name ) + return &d->indexes[ i ]; + } + assert( false ); + return 0; + } + private: + dblock lk_; + int indexNum_; + static DBDirectClient client_; + }; + DBDirectClient Base::client_; + + // There's a limit of 10 indexes total, make sure not to exceed this in a given test. +#define INDEX(x) this->index( BSON(x) ) +#define FBS(x) FieldBoundSet( ns(), x ) + + class NoIndex : public Base { public: void run() { - QueryPlan p( FieldBoundSet( emptyObj ), emptyObj, emptyObj ); + QueryPlan p( FBS( emptyObj ), emptyObj, 0 ); ASSERT( !p.optimal() ); ASSERT( !p.scanAndOrderRequired() ); ASSERT( !p.keyMatch() ); @@ -174,7 +217,7 @@ namespace QueryOptimizerTests { } }; - class SimpleOrder { + class SimpleOrder : public Base { public: void run() { BSONObjBuilder b; @@ -184,40 +227,40 @@ namespace QueryOptimizerTests { b2.appendMaxKey( "" ); BSONObj end = b2.obj(); - QueryPlan p( FieldBoundSet( emptyObj ), BSON( "a" << 1 ), BSON( "a" << 1 ) ); + QueryPlan p( FBS( emptyObj ), BSON( "a" << 1 ), INDEX( "a" << 1 ) ); ASSERT( !p.scanAndOrderRequired() ); ASSERT( !p.startKey().woCompare( start ) ); ASSERT( !p.endKey().woCompare( end ) ); - QueryPlan p2( FieldBoundSet( emptyObj ), BSON( "a" << 1 << "b" << 1 ), BSON( "a" << 1 << "b" << 1 ) ); + QueryPlan p2( FBS( emptyObj ), BSON( "a" << 1 << "b" << 1 ), INDEX( "a" << 1 << "b" << 1 ) ); ASSERT( !p2.scanAndOrderRequired() ); - QueryPlan p3( FieldBoundSet( emptyObj ), BSON( "b" << 1 ), BSON( "a" << 1 ) ); + QueryPlan p3( FBS( emptyObj ), BSON( "b" << 1 ), INDEX( "a" << 1 ) ); ASSERT( p3.scanAndOrderRequired() ); ASSERT( !p3.startKey().woCompare( start ) ); ASSERT( !p3.endKey().woCompare( end ) ); } }; - class MoreIndexThanNeeded { + class MoreIndexThanNeeded : public Base { public: void run() { - QueryPlan p( FieldBoundSet( emptyObj ), BSON( "a" << 1 ), BSON( "a" << 1 << "b" << 1 ) ); + QueryPlan p( FBS( emptyObj ), BSON( "a" << 1 ), INDEX( "a" << 1 << "b" << 1 ) ); ASSERT( !p.scanAndOrderRequired() ); } }; - class IndexSigns { + class IndexSigns : public Base { public: void run() { - QueryPlan p( FieldBoundSet( emptyObj ), BSON( "a" << 1 << "b" << -1 ), BSON( "a" << 1 << "b" << -1 ) ); + QueryPlan p( FBS( emptyObj ), BSON( "a" << 1 << "b" << -1 ), INDEX( "a" << 1 << "b" << -1 ) ); ASSERT( !p.scanAndOrderRequired() ); ASSERT_EQUALS( 1, p.direction() ); - QueryPlan p2( FieldBoundSet( emptyObj ), BSON( "a" << 1 << "b" << -1 ), BSON( "a" << 1 << "b" << 1 ) ); + QueryPlan p2( FBS( emptyObj ), BSON( "a" << 1 << "b" << -1 ), INDEX( "a" << 1 << "b" << 1 ) ); ASSERT( p2.scanAndOrderRequired() ); ASSERT_EQUALS( 0, p2.direction() ); } }; - class IndexReverse { + class IndexReverse : public Base { public: void run() { BSONObjBuilder b; @@ -228,21 +271,21 @@ namespace QueryOptimizerTests { b2.appendMaxKey( "" ); b2.appendMaxKey( "" ); BSONObj high = b2.obj(); - QueryPlan p( FieldBoundSet( emptyObj ), BSON( "a" << 1 << "b" << -1 ), BSON( "a" << -1 << "b" << 1 ) ); + QueryPlan p( FBS( emptyObj ), BSON( "a" << 1 << "b" << -1 ), INDEX( "a" << -1 << "b" << 1 ) ); ASSERT( !p.scanAndOrderRequired() ); ASSERT_EQUALS( -1, p.direction() ); ASSERT( !p.endKey().woCompare( low ) ); ASSERT( !p.startKey().woCompare( high ) ); - QueryPlan p2( FieldBoundSet( emptyObj ), BSON( "a" << -1 << "b" << -1 ), BSON( "a" << 1 << "b" << 1 ) ); + QueryPlan p2( FBS( emptyObj ), BSON( "a" << -1 << "b" << -1 ), INDEX( "a" << 1 << "b" << 1 ) ); ASSERT( !p2.scanAndOrderRequired() ); ASSERT_EQUALS( -1, p2.direction() ); - QueryPlan p3( FieldBoundSet( emptyObj ), BSON( "a" << -1 << "b" << -1 ), BSON( "a" << 1 << "b" << -1 ) ); + QueryPlan p3( FBS( emptyObj ), BSON( "a" << -1 << "b" << -1 ), INDEX( "a" << 1 << "b" << -1 ) ); ASSERT( p3.scanAndOrderRequired() ); ASSERT_EQUALS( 0, p3.direction() ); } }; - class NoOrder { + class NoOrder : public Base { public: void run() { BSONObjBuilder b; @@ -253,105 +296,111 @@ namespace QueryOptimizerTests { b2.append( "", 3 ); b2.appendMaxKey( "" ); BSONObj end = b2.obj(); - QueryPlan p( FieldBoundSet( BSON( "a" << 3 ) ), emptyObj, BSON( "a" << -1 << "b" << 1 ) ); + QueryPlan p( FBS( BSON( "a" << 3 ) ), emptyObj, INDEX( "a" << -1 << "b" << 1 ) ); ASSERT( !p.scanAndOrderRequired() ); ASSERT( !p.startKey().woCompare( start ) ); ASSERT( !p.endKey().woCompare( end ) ); - QueryPlan p2( FieldBoundSet( BSON( "a" << 3 ) ), BSONObj(), BSON( "a" << -1 << "b" << 1 ) ); + QueryPlan p2( FBS( BSON( "a" << 3 ) ), BSONObj(), INDEX( "a" << -1 << "b" << 1 ) ); ASSERT( !p2.scanAndOrderRequired() ); ASSERT( !p.startKey().woCompare( start ) ); ASSERT( !p.endKey().woCompare( end ) ); } }; - class EqualWithOrder { + class EqualWithOrder : public Base { public: void run() { - QueryPlan p( FieldBoundSet( BSON( "a" << 4 ) ), BSON( "b" << 1 ), BSON( "a" << 1 << "b" << 1 ) ); + QueryPlan p( FBS( BSON( "a" << 4 ) ), BSON( "b" << 1 ), INDEX( "a" << 1 << "b" << 1 ) ); ASSERT( !p.scanAndOrderRequired() ); - QueryPlan p2( FieldBoundSet( BSON( "b" << 4 ) ), BSON( "a" << 1 << "c" << 1 ), BSON( "a" << 1 << "b" << 1 << "c" << 1 ) ); + QueryPlan p2( FBS( BSON( "b" << 4 ) ), BSON( "a" << 1 << "c" << 1 ), INDEX( "a" << 1 << "b" << 1 << "c" << 1 ) ); ASSERT( !p2.scanAndOrderRequired() ); - QueryPlan p3( FieldBoundSet( BSON( "b" << 4 ) ), BSON( "a" << 1 << "c" << 1 ), BSON( "a" << 1 << "b" << 1 ) ); + QueryPlan p3( FBS( BSON( "b" << 4 ) ), BSON( "a" << 1 << "c" << 1 ), INDEX( "a" << 1 << "b" << 1 ) ); ASSERT( p3.scanAndOrderRequired() ); } }; - class Optimal { + class Optimal : public Base { public: void run() { - QueryPlan p( FieldBoundSet( emptyObj ), BSON( "a" << 1 ), BSON( "a" << 1 ) ); + QueryPlan p( FBS( emptyObj ), BSON( "a" << 1 ), INDEX( "a" << 1 ) ); ASSERT( p.optimal() ); - QueryPlan p2( FieldBoundSet( emptyObj ), BSON( "a" << 1 ), BSON( "a" << 1 << "b" << 1 ) ); + QueryPlan p2( FBS( emptyObj ), BSON( "a" << 1 ), INDEX( "a" << 1 << "b" << 1 ) ); ASSERT( p2.optimal() ); - QueryPlan p3( FieldBoundSet( BSON( "a" << 1 ) ), BSON( "a" << 1 ), BSON( "a" << 1 << "b" << 1 ) ); + QueryPlan p3( FBS( BSON( "a" << 1 ) ), BSON( "a" << 1 ), INDEX( "a" << 1 << "b" << 1 ) ); ASSERT( p3.optimal() ); - QueryPlan p4( FieldBoundSet( BSON( "b" << 1 ) ), BSON( "a" << 1 ), BSON( "a" << 1 << "b" << 1 ) ); + QueryPlan p4( FBS( BSON( "b" << 1 ) ), BSON( "a" << 1 ), INDEX( "a" << 1 << "b" << 1 ) ); ASSERT( !p4.optimal() ); - QueryPlan p5( FieldBoundSet( BSON( "a" << 1 ) ), BSON( "b" << 1 ), BSON( "a" << 1 << "b" << 1 ) ); + QueryPlan p5( FBS( BSON( "a" << 1 ) ), BSON( "b" << 1 ), INDEX( "a" << 1 << "b" << 1 ) ); ASSERT( p5.optimal() ); - QueryPlan p6( FieldBoundSet( BSON( "b" << 1 ) ), BSON( "b" << 1 ), BSON( "a" << 1 << "b" << 1 ) ); + QueryPlan p6( FBS( BSON( "b" << 1 ) ), BSON( "b" << 1 ), INDEX( "a" << 1 << "b" << 1 ) ); ASSERT( !p6.optimal() ); - QueryPlan p7( FieldBoundSet( BSON( "a" << 1 << "b" << 1 ) ), BSON( "a" << 1 ), BSON( "a" << 1 << "b" << 1 ) ); + QueryPlan p7( FBS( BSON( "a" << 1 << "b" << 1 ) ), BSON( "a" << 1 ), INDEX( "a" << 1 << "b" << 1 ) ); ASSERT( p7.optimal() ); - QueryPlan p8( FieldBoundSet( BSON( "a" << 1 << "b" << LT << 1 ) ), BSON( "a" << 1 ), BSON( "a" << 1 << "b" << 1 ) ); + QueryPlan p8( FBS( BSON( "a" << 1 << "b" << LT << 1 ) ), BSON( "a" << 1 ), INDEX( "a" << 1 << "b" << 1 ) ); ASSERT( p8.optimal() ); - QueryPlan p9( FieldBoundSet( BSON( "a" << 1 << "b" << LT << 1 ) ), BSON( "a" << 1 ), BSON( "a" << 1 << "b" << 1 << "c" << 1 ) ); + QueryPlan p9( FBS( BSON( "a" << 1 << "b" << LT << 1 ) ), BSON( "a" << 1 ), INDEX( "a" << 1 << "b" << 1 << "c" << 1 ) ); ASSERT( p9.optimal() ); - QueryPlan p10( FieldBoundSet( BSON( "a" << 1 ) ), emptyObj, BSON( "a" << 1 << "b" << 1 << "c" << 1 ) ); + QueryPlan p10( FBS( BSON( "a" << 1 ) ), emptyObj, INDEX( "a" << 1 << "b" << 1 << "c" << 1 ) ); ASSERT( p10.optimal() ); - QueryPlan p11( FieldBoundSet( BSON( "a" << 1 << "b" << LT << 1 ) ), emptyObj, BSON( "a" << 1 << "b" << 1 << "c" << 1 ) ); + } + }; + + class MoreOptimal : public Base { + public: + void run() { + QueryPlan p11( FBS( BSON( "a" << 1 << "b" << LT << 1 ) ), emptyObj, INDEX( "a" << 1 << "b" << 1 << "c" << 1 ) ); ASSERT( p11.optimal() ); - QueryPlan p12( FieldBoundSet( BSON( "a" << LT << 1 ) ), emptyObj, BSON( "a" << 1 << "b" << 1 << "c" << 1 ) ); + QueryPlan p12( FBS( BSON( "a" << LT << 1 ) ), emptyObj, INDEX( "a" << 1 << "b" << 1 << "c" << 1 ) ); ASSERT( p12.optimal() ); - QueryPlan p13( FieldBoundSet( BSON( "a" << LT << 1 ) ), BSON( "a" << 1 ), BSON( "a" << 1 << "b" << 1 << "c" << 1 ) ); + QueryPlan p13( FBS( BSON( "a" << LT << 1 ) ), BSON( "a" << 1 ), INDEX( "a" << 1 << "b" << 1 << "c" << 1 ) ); ASSERT( p13.optimal() ); } }; - class KeyMatch { + class KeyMatch : public Base { public: void run() { - QueryPlan p( FieldBoundSet( emptyObj ), BSON( "a" << 1 ), BSON( "a" << 1 ) ); + QueryPlan p( FBS( emptyObj ), BSON( "a" << 1 ), INDEX( "a" << 1 ) ); ASSERT( p.keyMatch() ); ASSERT( p.exactKeyMatch() ); - QueryPlan p2( FieldBoundSet( emptyObj ), BSON( "a" << 1 ), BSON( "b" << 1 << "a" << 1 ) ); + QueryPlan p2( FBS( emptyObj ), BSON( "a" << 1 ), INDEX( "b" << 1 << "a" << 1 ) ); ASSERT( p2.keyMatch() ); ASSERT( p2.exactKeyMatch() ); - QueryPlan p3( FieldBoundSet( BSON( "b" << "z" ) ), BSON( "a" << 1 ), BSON( "b" << 1 << "a" << 1 ) ); + QueryPlan p3( FBS( BSON( "b" << "z" ) ), BSON( "a" << 1 ), INDEX( "b" << 1 << "a" << 1 ) ); ASSERT( p3.keyMatch() ); ASSERT( p3.exactKeyMatch() ); - QueryPlan p4( FieldBoundSet( BSON( "c" << "y" << "b" << "z" ) ), BSON( "a" << 1 ), BSON( "b" << 1 << "a" << 1 << "c" << 1 ) ); + QueryPlan p4( FBS( BSON( "c" << "y" << "b" << "z" ) ), BSON( "a" << 1 ), INDEX( "b" << 1 << "a" << 1 << "c" << 1 ) ); ASSERT( p4.keyMatch() ); ASSERT( p4.exactKeyMatch() ); - QueryPlan p5( FieldBoundSet( BSON( "c" << "y" << "b" << "z" ) ), emptyObj, BSON( "b" << 1 << "a" << 1 << "c" << 1 ) ); + QueryPlan p5( FBS( BSON( "c" << "y" << "b" << "z" ) ), emptyObj, INDEX( "b" << 1 << "a" << 1 << "c" << 1 ) ); ASSERT( p5.keyMatch() ); ASSERT( p5.exactKeyMatch() ); - QueryPlan p6( FieldBoundSet( BSON( "c" << LT << "y" << "b" << GT << "z" ) ), emptyObj, BSON( "b" << 1 << "a" << 1 << "c" << 1 ) ); + QueryPlan p6( FBS( BSON( "c" << LT << "y" << "b" << GT << "z" ) ), emptyObj, INDEX( "b" << 1 << "a" << 1 << "c" << 1 ) ); ASSERT( p6.keyMatch() ); ASSERT( !p6.exactKeyMatch() ); - QueryPlan p7( FieldBoundSet( emptyObj ), BSON( "a" << 1 ), BSON( "b" << 1 ) ); + QueryPlan p7( FBS( emptyObj ), BSON( "a" << 1 ), INDEX( "b" << 1 ) ); ASSERT( !p7.keyMatch() ); ASSERT( !p7.exactKeyMatch() ); - QueryPlan p8( FieldBoundSet( BSON( "d" << "y" ) ), BSON( "a" << 1 ), BSON( "a" << 1 ) ); + QueryPlan p8( FBS( BSON( "d" << "y" ) ), BSON( "a" << 1 ), INDEX( "a" << 1 ) ); ASSERT( !p8.keyMatch() ); ASSERT( !p8.exactKeyMatch() ); } }; - class ExactKeyQueryTypes { + class ExactKeyQueryTypes : public Base { public: void run() { - QueryPlan p( FieldBoundSet( BSON( "a" << "b" ) ), emptyObj, BSON( "a" << 1 ) ); + QueryPlan p( FBS( BSON( "a" << "b" ) ), emptyObj, INDEX( "a" << 1 ) ); ASSERT( p.exactKeyMatch() ); - QueryPlan p2( FieldBoundSet( BSON( "a" << 4 ) ), emptyObj, BSON( "a" << 1 ) ); + QueryPlan p2( FBS( BSON( "a" << 4 ) ), emptyObj, INDEX( "a" << 1 ) ); ASSERT( !p2.exactKeyMatch() ); - QueryPlan p3( FieldBoundSet( BSON( "a" << BSON( "c" << "d" ) ) ), emptyObj, BSON( "a" << 1 ) ); + QueryPlan p3( FBS( BSON( "a" << BSON( "c" << "d" ) ) ), emptyObj, INDEX( "a" << 1 ) ); ASSERT( !p3.exactKeyMatch() ); BSONObjBuilder b; b.appendRegex( "a", "^ddd" ); - QueryPlan p4( FieldBoundSet( b.obj() ), emptyObj, BSON( "a" << 1 ) ); + QueryPlan p4( FBS( b.obj() ), emptyObj, INDEX( "a" << 1 ) ); ASSERT( !p4.exactKeyMatch() ); - QueryPlan p5( FieldBoundSet( BSON( "a" << "z" << "b" << 4 ) ), emptyObj, BSON( "a" << 1 << "b" << 1 ) ); + QueryPlan p5( FBS( BSON( "a" << "z" << "b" << 4 ) ), emptyObj, INDEX( "a" << 1 << "b" << 1 ) ); ASSERT( !p5.exactKeyMatch() ); } }; @@ -373,7 +422,7 @@ namespace QueryOptimizerTests { dropNS( s ); } protected: - static const char *ns() { return "QueryPlanTests.coll"; } + static const char *ns() { return "QueryPlanSetTests.coll"; } static NamespaceDetails *nsd() { return nsdetails( ns() ); } private: dblock lk_; @@ -451,6 +500,16 @@ namespace QueryOptimizerTests { } }; + class Count : public Base { + public: + void run() { + Helpers::ensureIndex( ns(), BSON( "a" << 1 ), "a_1" ); + Helpers::ensureIndex( ns(), BSON( "b" << 1 ), "b_1" ); + string err; + doCount( ns(), BSON( "query" << BSON( "a" << 4 ) ), err ); + } + }; + } // namespace QueryPlanSetTests class All : public UnitTest::Suite { @@ -478,6 +537,7 @@ namespace QueryOptimizerTests { add< QueryPlanTests::NoOrder >(); add< QueryPlanTests::EqualWithOrder >(); add< QueryPlanTests::Optimal >(); + add< QueryPlanTests::MoreOptimal >(); add< QueryPlanTests::KeyMatch >(); add< QueryPlanTests::ExactKeyQueryTypes >(); add< QueryPlanSetTests::NoIndexes >(); @@ -487,6 +547,7 @@ namespace QueryOptimizerTests { add< QueryPlanSetTests::HintSpec >(); add< QueryPlanSetTests::HintName >(); add< QueryPlanSetTests::BadHint >(); + add< QueryPlanSetTests::Count >(); } }; diff --git a/util/goodies.h b/util/goodies.h index 38e10b48e39..ed06adf2102 100644 --- a/util/goodies.h +++ b/util/goodies.h @@ -112,6 +112,7 @@ namespace mongo { } // namespace mongo +#include #include #include