summaryrefslogtreecommitdiff
path: root/src/mongo/dbtests/queryoptimizertests.cpp
diff options
context:
space:
mode:
authorEric Milkie <milkie@10gen.com>2012-10-18 19:15:12 -0400
committerEric Milkie <milkie@10gen.com>2012-10-18 19:15:48 -0400
commitb2d72c6390804e049d4094a34f09081f49cb95de (patch)
treebf7f8d136431342005fa7880c2d314913fbdcbc0 /src/mongo/dbtests/queryoptimizertests.cpp
parent4469161b8833728d62ad9ee00a9ee31799e1cc9a (diff)
downloadmongo-b2d72c6390804e049d4094a34f09081f49cb95de.tar.gz
split queryoptimizertests into two translation units
This file was taking too long to compile and causing build breakages.
Diffstat (limited to 'src/mongo/dbtests/queryoptimizertests.cpp')
-rw-r--r--src/mongo/dbtests/queryoptimizertests.cpp798
1 files changed, 9 insertions, 789 deletions
diff --git a/src/mongo/dbtests/queryoptimizertests.cpp b/src/mongo/dbtests/queryoptimizertests.cpp
index 9be3867b39d..15861f929cc 100644
--- a/src/mongo/dbtests/queryoptimizertests.cpp
+++ b/src/mongo/dbtests/queryoptimizertests.cpp
@@ -1,6 +1,3 @@
-// queryoptimizertests.cpp : query optimizer unit tests
-//
-
/**
* Copyright (C) 2009 10gen Inc.
*
@@ -17,14 +14,15 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#include "pch.h"
-#include "../db/queryoptimizer.h"
-#include "../db/instance.h"
-#include "../db/ops/count.h"
-#include "../db/ops/query.h"
-#include "../db/ops/delete.h"
+#include "mongo/pch.h"
+
+#include "mongo/db/instance.h"
#include "mongo/db/json.h"
-#include "dbtests.h"
+#include "mongo/db/ops/count.h"
+#include "mongo/db/ops/delete.h"
+#include "mongo/db/ops/query.h"
+#include "mongo/db/queryoptimizer.h"
+#include "mongo/dbtests/dbtests.h"
namespace mongo {
@@ -41,7 +39,7 @@ namespace mongo {
void __forceLinkGeoPlugin();
} // namespace mongo
-namespace QueryOptimizerTests {
+namespace {
using boost::shared_ptr;
@@ -835,755 +833,6 @@ namespace QueryOptimizerTests {
};
} // namespace QueryPlanTests
-
- namespace QueryPlanSetTests {
-
- class Base {
- public:
- Base() : _context( ns() ) {
- string err;
- userCreateNS( ns(), BSONObj(), err, false );
- }
- virtual ~Base() {
- if ( !nsd() )
- return;
- NamespaceDetailsTransient::get_inlock( ns() ).clearQueryCache();
- dropCollection( ns() );
- }
- protected:
- static void assembleRequest( const string &ns, BSONObj query, int nToReturn, int nToSkip, BSONObj *fieldsToReturn, int queryOptions, Message &toSend ) {
- // see query.h for the protocol we are using here.
- BufBuilder b;
- int opts = queryOptions;
- b.appendNum(opts);
- b.appendStr(ns);
- b.appendNum(nToSkip);
- b.appendNum(nToReturn);
- query.appendSelfToBufBuilder(b);
- if ( fieldsToReturn )
- fieldsToReturn->appendSelfToBufBuilder(b);
- toSend.setData(dbQuery, b.buf(), b.len());
- }
- QueryPattern makePattern( const BSONObj &query, const BSONObj &order ) {
- FieldRangeSet frs( ns(), query, true, true );
- return QueryPattern( frs, order );
- }
- shared_ptr<QueryPlanSet> makeQps( const BSONObj& query = BSONObj(),
- const BSONObj& order = BSONObj(),
- const BSONObj& hint = BSONObj(),
- bool allowSpecial = true ) {
- auto_ptr<FieldRangeSetPair> frsp( new FieldRangeSetPair( ns(), query ) );
- auto_ptr<FieldRangeSetPair> frspOrig( new FieldRangeSetPair( *frsp ) );
- return shared_ptr<QueryPlanSet>
- ( QueryPlanSet::make( ns(), frsp, frspOrig, query, order,
- shared_ptr<const ParsedQuery>(), hint,
- QueryPlanGenerator::Use, BSONObj(), BSONObj(),
- allowSpecial ) );
- }
- static const char *ns() { return "unittests.QueryPlanSetTests"; }
- static NamespaceDetails *nsd() { return nsdetails( ns() ); }
- DBDirectClient &client() { return _client; }
- private:
- Lock::GlobalWrite lk_;
- Client::Context _context;
- DBDirectClient _client;
- };
-
- class ToString : public Base {
- public:
- void run() {
- // Just test that we don't crash.
- makeQps( BSON( "a" << 1 ) )->toString();
- }
- };
-
- class NoIndexes : public Base {
- public:
- void run() {
- ASSERT_EQUALS( 1, makeQps( BSON( "a" << 4 ), BSON( "b" << 1 ) )->nPlans() );
- }
- };
-
- class Optimal : public Base {
- public:
- void run() {
- Helpers::ensureIndex( ns(), BSON( "a" << 1 ), false, "a_1" );
- Helpers::ensureIndex( ns(), BSON( "a" << 1 ), false, "b_2" );
- BSONObj query = BSON( "a" << 4 );
-
- // Only one optimal plan is added to the plan set.
- ASSERT_EQUALS( 1, makeQps( query )->nPlans() );
-
- // The optimal plan is recorded in the plan cache.
- FieldRangeSet frs( ns(), query, true, true );
- CachedQueryPlan cachedPlan =
- NamespaceDetailsTransient::get( ns() ).cachedQueryPlanForPattern
- ( QueryPattern( frs, BSONObj() ) );
- ASSERT_EQUALS( BSON( "a" << 1 ), cachedPlan.indexKey() );
- CandidatePlanCharacter planCharacter = cachedPlan.planCharacter();
- ASSERT( planCharacter.mayRunInOrderPlan() );
- ASSERT( !planCharacter.mayRunOutOfOrderPlan() );
- }
- };
-
- class NoOptimal : public Base {
- public:
- void run() {
- Helpers::ensureIndex( ns(), BSON( "a" << 1 ), false, "a_1" );
- Helpers::ensureIndex( ns(), BSON( "b" << 1 ), false, "b_1" );
- ASSERT_EQUALS( 3, makeQps( BSON( "a" << 4 ), BSON( "b" << 1 ) )->nPlans() );
- }
- };
-
- class NoSpec : public Base {
- public:
- void run() {
- Helpers::ensureIndex( ns(), BSON( "a" << 1 ), false, "a_1" );
- Helpers::ensureIndex( ns(), BSON( "b" << 1 ), false, "b_1" );
- ASSERT_EQUALS( 1, makeQps()->nPlans() );
- }
- };
-
- class HintSpec : public Base {
- public:
- void run() {
- Helpers::ensureIndex( ns(), BSON( "a" << 1 ), false, "a_1" );
- Helpers::ensureIndex( ns(), BSON( "b" << 1 ), false, "b_1" );
- ASSERT_EQUALS( 1, makeQps( BSON( "a" << 1 ), BSON( "b" << 1 ),
- BSON( "hint" << BSON( "a" << 1 ) ) )->nPlans() );
- }
- };
-
- class HintName : public Base {
- public:
- void run() {
- Helpers::ensureIndex( ns(), BSON( "a" << 1 ), false, "a_1" );
- Helpers::ensureIndex( ns(), BSON( "b" << 1 ), false, "b_1" );
- ASSERT_EQUALS( 1, makeQps( BSON( "a" << 1 ), BSON( "b" << 1 ),
- BSON( "hint" << "a_1" ) )->nPlans() );
- }
- };
-
- class NaturalHint : public Base {
- public:
- void run() {
- Helpers::ensureIndex( ns(), BSON( "a" << 1 ), false, "a_1" );
- Helpers::ensureIndex( ns(), BSON( "b" << 1 ), false, "b_1" );
- ASSERT_EQUALS( 1, makeQps( BSON( "a" << 1 ), BSON( "b" << 1 ),
- BSON( "hint" << BSON( "$natural" << 1 ) ) )->nPlans() );
- }
- };
-
- class NaturalSort : public Base {
- public:
- void run() {
- Helpers::ensureIndex( ns(), BSON( "a" << 1 ), false, "a_1" );
- Helpers::ensureIndex( ns(), BSON( "a" << 1 ), false, "b_2" );
- ASSERT_EQUALS( 1, makeQps( BSON( "a" << 1 ), BSON( "$natural" << 1 ) )->nPlans() );
- }
- };
-
- class BadHint : public Base {
- public:
- void run() {
- ASSERT_THROWS( makeQps( BSON( "a" << 1 ), BSON( "b" << 1 ),
- BSON( "hint" << "a_1" ) ),
- AssertionException );
- }
- };
-
- class Count : public Base {
- public:
- void run() {
- Helpers::ensureIndex( ns(), BSON( "a" << 1 ), false, "a_1" );
- Helpers::ensureIndex( ns(), BSON( "b" << 1 ), false, "b_1" );
- string err;
- int errCode;
- ASSERT_EQUALS( 0, runCount( ns(), BSON( "query" << BSON( "a" << 4 ) ), err, errCode ) );
- BSONObj one = BSON( "a" << 1 );
- BSONObj fourA = BSON( "a" << 4 );
- BSONObj fourB = BSON( "a" << 4 );
- theDataFileMgr.insertWithObjMod( ns(), one );
- ASSERT_EQUALS( 0, runCount( ns(), BSON( "query" << BSON( "a" << 4 ) ), err, errCode ) );
- theDataFileMgr.insertWithObjMod( ns(), fourA );
- ASSERT_EQUALS( 1, runCount( ns(), BSON( "query" << BSON( "a" << 4 ) ), err, errCode ) );
- theDataFileMgr.insertWithObjMod( ns(), fourB );
- ASSERT_EQUALS( 2, runCount( ns(), BSON( "query" << BSON( "a" << 4 ) ), err, errCode ) );
- ASSERT_EQUALS( 3, runCount( ns(), BSON( "query" << BSONObj() ), err, errCode ) );
- ASSERT_EQUALS( 3, runCount( ns(), BSON( "query" << BSON( "a" << GT << 0 ) ), err, errCode ) );
- // missing ns
- ASSERT_EQUALS( -1, runCount( "unittests.missingNS", BSONObj(), err, errCode ) );
- // impossible match
- ASSERT_EQUALS( 0, runCount( ns(), BSON( "query" << BSON( "a" << GT << 0 << LT << -1 ) ), err, errCode ) );
- }
- };
-
- class QueryMissingNs : public Base {
- public:
- QueryMissingNs() { log() << "querymissingns starts" << endl; }
- ~QueryMissingNs() {
- log() << "end QueryMissingNs" << endl;
- }
- void run() {
- Message m;
- assembleRequest( "unittests.missingNS", BSONObj(), 0, 0, 0, 0, m );
- DbMessage d(m);
- QueryMessage q(d);
- Message ret;
- runQuery( m, q, ret );
- ASSERT_EQUALS( 0, ((QueryResult*)ret.header())->nReturned );
- }
-
- };
-
- class UnhelpfulIndex : public Base {
- public:
- void run() {
- Helpers::ensureIndex( ns(), BSON( "a" << 1 ), false, "a_1" );
- Helpers::ensureIndex( ns(), BSON( "b" << 1 ), false, "b_1" );
- ASSERT_EQUALS( 2, makeQps( BSON( "a" << 1 << "c" << 2 ) )->nPlans() );
- }
- };
-
- class FindOne : public Base {
- public:
- void run() {
- BSONObj one = BSON( "a" << 1 );
- theDataFileMgr.insertWithObjMod( ns(), one );
- BSONObj result;
- ASSERT( Helpers::findOne( ns(), BSON( "a" << 1 ), result ) );
- ASSERT_THROWS( Helpers::findOne( ns(), BSON( "a" << 1 ), result, true ), AssertionException );
- Helpers::ensureIndex( ns(), BSON( "a" << 1 ), false, "a_1" );
- ASSERT( Helpers::findOne( ns(), BSON( "a" << 1 ), result, true ) );
- }
- };
-
- class Delete : public Base {
- public:
- void run() {
- Helpers::ensureIndex( ns(), BSON( "a" << 1 ), false, "a_1" );
- for( int i = 0; i < 200; ++i ) {
- BSONObj two = BSON( "a" << 2 );
- theDataFileMgr.insertWithObjMod( ns(), two );
- }
- BSONObj one = BSON( "a" << 1 );
- theDataFileMgr.insertWithObjMod( ns(), one );
- BSONObj delSpec = BSON( "a" << 1 << "_id" << NE << 0 );
- deleteObjects( ns(), delSpec, false );
-
- NamespaceDetailsTransient &nsdt = NamespaceDetailsTransient::get( ns() );
- QueryPattern queryPattern = FieldRangeSet( ns(), delSpec, true, true ).pattern();
- CachedQueryPlan cachedQueryPlan = nsdt.cachedQueryPlanForPattern( queryPattern );
- ASSERT_EQUALS( BSON( "a" << 1 ), cachedQueryPlan.indexKey() );
- ASSERT_EQUALS( 1, cachedQueryPlan.nScanned() );
- }
- };
-
- class DeleteOneScan : public Base {
- public:
- void run() {
- Helpers::ensureIndex( ns(), BSON( "_id" << 1 ), false, "_id_1" );
- BSONObj one = BSON( "_id" << 3 << "a" << 1 );
- BSONObj two = BSON( "_id" << 2 << "a" << 1 );
- BSONObj three = BSON( "_id" << 1 << "a" << -1 );
- theDataFileMgr.insertWithObjMod( ns(), one );
- theDataFileMgr.insertWithObjMod( ns(), two );
- theDataFileMgr.insertWithObjMod( ns(), three );
- deleteObjects( ns(), BSON( "_id" << GT << 0 << "a" << GT << 0 ), true );
- for( boost::shared_ptr<Cursor> c = theDataFileMgr.findAll( ns() ); c->ok(); c->advance() )
- ASSERT( 3 != c->current().getIntField( "_id" ) );
- }
- };
-
- class DeleteOneIndex : public Base {
- public:
- void run() {
- Helpers::ensureIndex( ns(), BSON( "a" << 1 ), false, "a" );
- BSONObj one = BSON( "a" << 2 << "_id" << 0 );
- BSONObj two = BSON( "a" << 1 << "_id" << 1 );
- BSONObj three = BSON( "a" << 0 << "_id" << 2 );
- theDataFileMgr.insertWithObjMod( ns(), one );
- theDataFileMgr.insertWithObjMod( ns(), two );
- theDataFileMgr.insertWithObjMod( ns(), three );
- deleteObjects( ns(), BSON( "a" << GTE << 0 ), true );
- for( boost::shared_ptr<Cursor> c = theDataFileMgr.findAll( ns() ); c->ok(); c->advance() )
- ASSERT( 2 != c->current().getIntField( "_id" ) );
- }
- };
-
- class InQueryIntervals : public Base {
- public:
- void run() {
- Helpers::ensureIndex( ns(), BSON( "a" << 1 ), false, "a_1" );
- for( int i = 0; i < 10; ++i ) {
- BSONObj temp = BSON( "a" << i );
- theDataFileMgr.insertWithObjMod( ns(), temp );
- }
- BSONObj query = fromjson( "{a:{$in:[2,3,6,9,11]}}" );
- BSONObj order;
- BSONObj hint = fromjson( "{$hint:{a:1}}" );
- auto_ptr< FieldRangeSetPair > frsp( new FieldRangeSetPair( ns(), query ) );
- shared_ptr<QueryPlanSet> s = makeQps( query, order, hint );
- scoped_ptr<QueryPlan> qp( QueryPlan::make( nsd(), 1, s->frsp(), frsp.get(),
- query, order ) );
- boost::shared_ptr<Cursor> c = qp->newCursor();
- double expected[] = { 2, 3, 6, 9 };
- for( int i = 0; i < 4; ++i, c->advance() ) {
- ASSERT_EQUALS( expected[ i ], c->current().getField( "a" ).number() );
- }
- ASSERT( !c->ok() );
-
- // now check reverse
- {
- order = BSON( "a" << -1 );
- auto_ptr< FieldRangeSetPair > frsp( new FieldRangeSetPair( ns(), query ) );
- shared_ptr<QueryPlanSet> s = makeQps( query, order, hint );
- scoped_ptr<QueryPlan> qp( QueryPlan::make( nsd(), 1, s->frsp(), frsp.get(),
- query, order ) );
- boost::shared_ptr<Cursor> c = qp->newCursor();
- double expected[] = { 9, 6, 3, 2 };
- for( int i = 0; i < 4; ++i, c->advance() ) {
- ASSERT_EQUALS( expected[ i ], c->current().getField( "a" ).number() );
- }
- ASSERT( !c->ok() );
- }
- }
- };
-
- class EqualityThenIn : public Base {
- public:
- void run() {
- Helpers::ensureIndex( ns(), BSON( "a" << 1 << "b" << 1 ), false, "a_1_b_1" );
- for( int i = 0; i < 10; ++i ) {
- BSONObj temp = BSON( "a" << 5 << "b" << i );
- theDataFileMgr.insertWithObjMod( ns(), temp );
- }
- auto_ptr< FieldRangeSetPair > frsp( new FieldRangeSetPair( ns(), fromjson( "{a:5,b:{$in:[2,3,6,9,11]}}" ) ) );
- scoped_ptr<QueryPlan> qp( QueryPlan::make( nsd(), 1, *frsp, frsp.get(),
- fromjson( "{a:5,b:{$in:[2,3,6,9,11]}}" ),
- BSONObj() ) );
- boost::shared_ptr<Cursor> c = qp->newCursor();
- double expected[] = { 2, 3, 6, 9 };
- ASSERT( c->ok() );
- for( int i = 0; i < 4; ++i, c->advance() ) {
- ASSERT( c->ok() );
- ASSERT_EQUALS( expected[ i ], c->current().getField( "b" ).number() );
- }
- ASSERT( !c->ok() );
- }
- };
-
- class NotEqualityThenIn : public Base {
- public:
- void run() {
- Helpers::ensureIndex( ns(), BSON( "a" << 1 << "b" << 1 ), false, "a_1_b_1" );
- for( int i = 0; i < 10; ++i ) {
- BSONObj temp = BSON( "a" << 5 << "b" << i );
- theDataFileMgr.insertWithObjMod( ns(), temp );
- }
- auto_ptr< FieldRangeSetPair > frsp( new FieldRangeSetPair( ns(), fromjson( "{a:{$gte:5},b:{$in:[2,3,6,9,11]}}" ) ) );
- scoped_ptr<QueryPlan> qp
- ( QueryPlan::make( nsd(), 1, *frsp, frsp.get(),
- fromjson( "{a:{$gte:5},b:{$in:[2,3,6,9,11]}}" ),
- BSONObj() ) );
- boost::shared_ptr<Cursor> c = qp->newCursor();
- int matches[] = { 2, 3, 6, 9 };
- for( int i = 0; i < 4; ++i, c->advance() ) {
- ASSERT_EQUALS( matches[ i ], c->current().getField( "b" ).number() );
- }
- ASSERT( !c->ok() );
- }
- };
-
- /** Exclude special plan candidate if there are btree plan candidates. SERVER-4531 */
- class ExcludeSpecialPlanWhenBtreePlan : public Base {
- public:
- void run() {
- Helpers::ensureIndex( ns(), BSON( "a" << "2d" ), false, "a_2d" );
- Helpers::ensureIndex( ns(), BSON( "a" << 1 ), false, "a_1" );
- shared_ptr<QueryPlanSet> s =
- makeQps( BSON( "a" << BSON_ARRAY( 0 << 0 ) << "b" << 1 ) );
- // Two query plans, btree and collection scan.
- ASSERT_EQUALS( 2, s->nPlans() );
- // Not the geo plan.
- ASSERT( s->firstPlan()->special().empty() );
- }
- };
-
- /** Exclude unindexed plan candidate if there is a special plan candidate. SERVER-4531 */
- class ExcludeUnindexedPlanWhenSpecialPlan : public Base {
- public:
- void run() {
- Helpers::ensureIndex( ns(), BSON( "a" << "2d" ), false, "a_2d" );
- shared_ptr<QueryPlanSet> s =
- makeQps( BSON( "a" << BSON_ARRAY( 0 << 0 ) << "b" << 1 ) );
- // Single query plan.
- ASSERT_EQUALS( 1, s->nPlans() );
- // It's the geo plan.
- ASSERT( !s->firstPlan()->special().empty() );
- }
- };
-
- class PossiblePlans : public Base {
- public:
- void run() {
- client().ensureIndex( ns(), BSON( "a" << 1 ) );
- client().ensureIndex( ns(), BSON( "b" << 1 ) );
-
- {
- shared_ptr<QueryPlanSet> qps = makeQps( BSON( "a" << 1 ), BSONObj() );
- ASSERT_EQUALS( 1, qps->nPlans() );
- ASSERT( qps->possibleInOrderPlan() );
- ASSERT( qps->haveInOrderPlan() );
- ASSERT( !qps->possibleOutOfOrderPlan() );
- ASSERT( !qps->hasPossiblyExcludedPlans() );
- ASSERT( !qps->usingCachedPlan() );
- }
-
- {
- shared_ptr<QueryPlanSet> qps = makeQps( BSON( "a" << 1 ), BSON( "b" << 1 ) );
- ASSERT_EQUALS( 3, qps->nPlans() );
- ASSERT( qps->possibleInOrderPlan() );
- ASSERT( qps->haveInOrderPlan() );
- ASSERT( qps->possibleOutOfOrderPlan() );
- ASSERT( !qps->hasPossiblyExcludedPlans() );
- ASSERT( !qps->usingCachedPlan() );
- }
-
- NamespaceDetailsTransient &nsdt = NamespaceDetailsTransient::get( ns() );
-
- nsdt.registerCachedQueryPlanForPattern( makePattern( BSON( "a" << 1 ), BSONObj() ),
- CachedQueryPlan( BSON( "a" << 1 ), 1,
- CandidatePlanCharacter( true, false ) ) );
- {
- shared_ptr<QueryPlanSet> qps = makeQps( BSON( "a" << 1 ), BSONObj() );
- ASSERT_EQUALS( 1, qps->nPlans() );
- ASSERT( qps->possibleInOrderPlan() );
- ASSERT( qps->haveInOrderPlan() );
- ASSERT( !qps->possibleOutOfOrderPlan() );
- ASSERT( !qps->hasPossiblyExcludedPlans() );
- ASSERT( qps->usingCachedPlan() );
- }
-
- nsdt.registerCachedQueryPlanForPattern
- ( makePattern( BSON( "a" << 1 ), BSON( "b" << 1 ) ),
- CachedQueryPlan( BSON( "a" << 1 ), 1,
- CandidatePlanCharacter( true, true ) ) );
-
- {
- shared_ptr<QueryPlanSet> qps = makeQps( BSON( "a" << 1 ), BSON( "b" << 1 ) );
- ASSERT_EQUALS( 1, qps->nPlans() );
- ASSERT( qps->possibleInOrderPlan() );
- ASSERT( !qps->haveInOrderPlan() );
- ASSERT( qps->possibleOutOfOrderPlan() );
- ASSERT( qps->hasPossiblyExcludedPlans() );
- ASSERT( qps->usingCachedPlan() );
- }
-
- nsdt.registerCachedQueryPlanForPattern
- ( makePattern( BSON( "a" << 1 ), BSON( "b" << 1 ) ),
- CachedQueryPlan( BSON( "b" << 1 ), 1,
- CandidatePlanCharacter( true, true ) ) );
-
- {
- shared_ptr<QueryPlanSet> qps = makeQps( BSON( "a" << 1 ), BSON( "b" << 1 ) );
- ASSERT_EQUALS( 1, qps->nPlans() );
- ASSERT( qps->possibleInOrderPlan() );
- ASSERT( qps->haveInOrderPlan() );
- ASSERT( qps->possibleOutOfOrderPlan() );
- ASSERT( qps->hasPossiblyExcludedPlans() );
- ASSERT( qps->usingCachedPlan() );
- }
-
- {
- shared_ptr<QueryPlanSet> qps = makeQps( BSON( "a" << 1 ), BSON( "c" << 1 ) );
- ASSERT_EQUALS( 2, qps->nPlans() );
- ASSERT( !qps->possibleInOrderPlan() );
- ASSERT( !qps->haveInOrderPlan() );
- ASSERT( qps->possibleOutOfOrderPlan() );
- ASSERT( !qps->hasPossiblyExcludedPlans() );
- ASSERT( !qps->usingCachedPlan() );
- }
- }
- };
-
- /** An unhelpful query plan will not be used if recorded in the query plan cache. */
- class AvoidUnhelpfulRecordedPlan : public Base {
- public:
- void run() {
- client().ensureIndex( ns(), BSON( "a" << 1 ) );
-
- // Record the {a:1} index for a {b:1} query.
- NamespaceDetailsTransient &nsdt = NamespaceDetailsTransient::get( ns() );
- nsdt.registerCachedQueryPlanForPattern
- ( makePattern( BSON( "b" << 1 ), BSONObj() ),
- CachedQueryPlan( BSON( "a" << 1 ), 1,
- CandidatePlanCharacter( true, false ) ) );
-
- // The {a:1} index is not used for a {b:1} query because it generates an unhelpful
- // plan.
- shared_ptr<QueryPlanSet> qps = makeQps( BSON( "b" << 1 ), BSONObj() );
- ASSERT_EQUALS( 1, qps->nPlans() );
- ASSERT_EQUALS( BSON( "$natural" << 1 ), qps->firstPlan()->indexKey() );
- }
- };
-
- /** An unhelpful query plan will not be used if recorded in the query plan cache. */
- class AvoidDisallowedRecordedPlan : public Base {
- public:
- void run() {
- client().insert( "unittests.system.indexes",
- BSON( "ns" << ns() <<
- "key" << BSON( "a" << 1 ) <<
- "name" << client().genIndexName( BSON( "a" << 1 ) ) <<
- "sparse" << true ) );
-
- // Record the {a:1} index for a {a:null} query.
- NamespaceDetailsTransient &nsdt = NamespaceDetailsTransient::get( ns() );
- nsdt.registerCachedQueryPlanForPattern
- ( makePattern( BSON( "a" << BSONNULL ), BSONObj() ),
- CachedQueryPlan( BSON( "a" << 1 ), 1,
- CandidatePlanCharacter( true, false ) ) );
-
- // The {a:1} index is not used for an {a:{$exists:false}} query because it generates
- // a disallowed plan.
- shared_ptr<QueryPlanSet> qps = makeQps( BSON( "a" << BSON( "$exists" << false ) ),
- BSONObj() );
- ASSERT_EQUALS( 1, qps->nPlans() );
- ASSERT_EQUALS( BSON( "$natural" << 1 ), qps->firstPlan()->indexKey() );
- }
- };
-
- /** Special plans are only selected when allowed. */
- class AllowSpecial : public Base {
- public:
- void run() {
- BSONObj naturalIndex = BSON( "$natural" << 1 );
- BSONObj specialIndex = BSON( "a" << "2d" );
- BSONObj query = BSON( "a" << BSON_ARRAY( 0 << 0 ) );
- client().ensureIndex( ns(), specialIndex );
-
- // The special plan is chosen if allowed.
- assertSingleIndex( specialIndex, makeQps( query ) );
-
- // The special plan is not chosen if not allowed
- assertSingleIndex( naturalIndex, makeQps( query, BSONObj(), BSONObj(), false ) );
-
- // Attempting to hint a special plan when not allowed triggers an assertion.
- ASSERT_THROWS( makeQps( query, BSONObj(), BSON( "$hint" << specialIndex ), false ),
- UserException );
-
- // Attempting to use a geo operator when special plans are not allowed triggers an
- // assertion.
- ASSERT_THROWS( makeQps( BSON( "a" << BSON( "$near" << BSON_ARRAY( 0 << 0 ) ) ),
- BSONObj(), BSONObj(), false ),
- UserException );
-
- // The special plan is not chosen if not allowed, even if cached.
- NamespaceDetailsTransient &nsdt = NamespaceDetailsTransient::get( ns() );
- nsdt.registerCachedQueryPlanForPattern
- ( makePattern( query, BSONObj() ),
- CachedQueryPlan( specialIndex, 1,
- CandidatePlanCharacter( true, false ) ) );
- assertSingleIndex( naturalIndex, makeQps( query, BSONObj(), BSONObj(), false ) );
- }
- private:
- void assertSingleIndex( const BSONObj& index, const shared_ptr<QueryPlanSet>& set ) {
- ASSERT_EQUALS( 1, set->nPlans() );
- ASSERT_EQUALS( index, set->firstPlan()->indexKey() );
- }
- };
-
- } // namespace QueryPlanSetTests
-
- class Base {
- public:
- Base() : _ctx( ns() ) {
- string err;
- userCreateNS( ns(), BSONObj(), err, false );
- }
- ~Base() {
- if ( !nsd() )
- return;
- string s( ns() );
- dropCollection( ns() );
- }
- protected:
- static const char *ns() { return "unittests.QueryOptimizerTests"; }
- static NamespaceDetails *nsd() { return nsdetails( ns() ); }
- QueryPattern makePattern( const BSONObj &query, const BSONObj &order ) {
- FieldRangeSet frs( ns(), query, true, true );
- return QueryPattern( frs, order );
- }
- shared_ptr<MultiPlanScanner> makeMps( const BSONObj &query, const BSONObj &order ) {
- shared_ptr<MultiPlanScanner> ret( MultiPlanScanner::make( ns(), query, order ) );
- return ret;
- }
- DBDirectClient &client() { return _client; }
- private:
- Lock::GlobalWrite lk_;
- Client::Context _ctx;
- DBDirectClient _client;
- };
-
- namespace MultiPlanScannerTests {
- class ToString : public Base {
- public:
- void run() {
- scoped_ptr<MultiPlanScanner> multiPlanScanner
- ( MultiPlanScanner::make( ns(), BSON( "a" << 1 ), BSONObj() ) );
- multiPlanScanner->toString(); // Just test that we don't crash.
- }
- };
-
- class PossiblePlans : public Base {
- public:
- void run() {
- client().ensureIndex( ns(), BSON( "a" << 1 ) );
- client().ensureIndex( ns(), BSON( "b" << 1 ) );
-
- {
- shared_ptr<MultiPlanScanner> mps = makeMps( BSON( "a" << 1 ), BSONObj() );
- ASSERT_EQUALS( 1, mps->currentNPlans() );
- ASSERT( mps->possibleInOrderPlan() );
- ASSERT( mps->haveInOrderPlan() );
- ASSERT( !mps->possibleOutOfOrderPlan() );
- ASSERT( !mps->hasPossiblyExcludedPlans() );
- }
-
- {
- shared_ptr<MultiPlanScanner> mps =
- makeMps( BSON( "a" << 1 ), BSON( "b" << 1 ) );
- ASSERT_EQUALS( 3, mps->currentNPlans() );
- ASSERT( mps->possibleInOrderPlan() );
- ASSERT( mps->haveInOrderPlan() );
- ASSERT( mps->possibleOutOfOrderPlan() );
- ASSERT( !mps->hasPossiblyExcludedPlans() );
- }
-
- NamespaceDetailsTransient &nsdt = NamespaceDetailsTransient::get( ns() );
-
- nsdt.registerCachedQueryPlanForPattern( makePattern( BSON( "a" << 1 ), BSONObj() ),
- CachedQueryPlan( BSON( "a" << 1 ), 1,
- CandidatePlanCharacter( true, false ) ) );
- {
- shared_ptr<MultiPlanScanner> mps = makeMps( BSON( "a" << 1 ), BSONObj() );
- ASSERT_EQUALS( 1, mps->currentNPlans() );
- ASSERT( mps->possibleInOrderPlan() );
- ASSERT( mps->haveInOrderPlan() );
- ASSERT( !mps->possibleOutOfOrderPlan() );
- ASSERT( !mps->hasPossiblyExcludedPlans() );
- }
-
- nsdt.registerCachedQueryPlanForPattern
- ( makePattern( BSON( "a" << 1 ), BSON( "b" << 1 ) ),
- CachedQueryPlan( BSON( "a" << 1 ), 1,
- CandidatePlanCharacter( true, true ) ) );
-
- {
- shared_ptr<MultiPlanScanner> mps =
- makeMps( BSON( "a" << 1 ), BSON( "b" << 1 ) );
- ASSERT_EQUALS( 1, mps->currentNPlans() );
- ASSERT( mps->possibleInOrderPlan() );
- ASSERT( !mps->haveInOrderPlan() );
- ASSERT( mps->possibleOutOfOrderPlan() );
- ASSERT( mps->hasPossiblyExcludedPlans() );
- }
-
- nsdt.registerCachedQueryPlanForPattern
- ( makePattern( BSON( "a" << 1 ), BSON( "b" << 1 ) ),
- CachedQueryPlan( BSON( "b" << 1 ), 1,
- CandidatePlanCharacter( true, true ) ) );
-
- {
- shared_ptr<MultiPlanScanner> mps =
- makeMps( BSON( "a" << 1 ), BSON( "b" << 1 ) );
- ASSERT_EQUALS( 1, mps->currentNPlans() );
- ASSERT( mps->possibleInOrderPlan() );
- ASSERT( mps->haveInOrderPlan() );
- ASSERT( mps->possibleOutOfOrderPlan() );
- ASSERT( mps->hasPossiblyExcludedPlans() );
- }
-
- {
- shared_ptr<MultiPlanScanner> mps =
- makeMps( BSON( "a" << 1 ), BSON( "c" << 1 ) );
- ASSERT_EQUALS( 2, mps->currentNPlans() );
- ASSERT( !mps->possibleInOrderPlan() );
- ASSERT( !mps->haveInOrderPlan() );
- ASSERT( mps->possibleOutOfOrderPlan() );
- ASSERT( !mps->hasPossiblyExcludedPlans() );
- }
-
- {
- shared_ptr<MultiPlanScanner> mps =
- makeMps( fromjson( "{$or:[{a:1},{a:2}]}" ), BSON( "c" << 1 ) );
- ASSERT_EQUALS( 1, mps->currentNPlans() );
- ASSERT( !mps->possibleInOrderPlan() );
- ASSERT( !mps->haveInOrderPlan() );
- ASSERT( mps->possibleOutOfOrderPlan() );
- ASSERT( !mps->hasPossiblyExcludedPlans() );
- }
-
- {
- shared_ptr<MultiPlanScanner> mps =
- makeMps( fromjson( "{$or:[{a:1,b:1},{a:2,b:2}]}" ), BSONObj() );
- ASSERT_EQUALS( 3, mps->currentNPlans() );
- ASSERT( mps->possibleInOrderPlan() );
- ASSERT( mps->haveInOrderPlan() );
- ASSERT( !mps->possibleOutOfOrderPlan() );
- ASSERT( !mps->hasPossiblyExcludedPlans() );
- }
- }
- };
-
- } // namespace MultiPlanScannerTests
-
- class BestGuess : public Base {
- public:
- void run() {
- Helpers::ensureIndex( ns(), BSON( "a" << 1 ), false, "a_1" );
- Helpers::ensureIndex( ns(), BSON( "b" << 1 ), false, "b_1" );
- BSONObj temp = BSON( "a" << 1 );
- theDataFileMgr.insertWithObjMod( ns(), temp );
- temp = BSON( "b" << 1 );
- theDataFileMgr.insertWithObjMod( ns(), temp );
-
- boost::shared_ptr< Cursor > c =
- NamespaceDetailsTransient::bestGuessCursor( ns(), BSON( "b" << 1 ), BSON( "a" << 1 ) );
- ASSERT_EQUALS( string( "a" ), c->indexKeyPattern().firstElement().fieldName() );
-
- c = NamespaceDetailsTransient::bestGuessCursor( ns(), BSON( "a" << 1 ),
- BSON( "b" << 1 ) );
- ASSERT_EQUALS( string( "b" ), c->indexKeyPattern().firstElementFieldName() );
- ASSERT( c->matcher() );
- ASSERT( c->currentMatches() ); // { b:1 } document
- c->advance();
- ASSERT( !c->currentMatches() ); // { a:1 } document
-
- c = NamespaceDetailsTransient::bestGuessCursor( ns(), fromjson( "{b:1,$or:[{z:1}]}" ),
- BSON( "a" << 1 ) );
- ASSERT_EQUALS( string( "a" ), c->indexKeyPattern().firstElement().fieldName() );
-
- c = NamespaceDetailsTransient::bestGuessCursor( ns(), fromjson( "{a:1,$or:[{y:1}]}" ),
- BSON( "b" << 1 ) );
- ASSERT_EQUALS( string( "b" ), c->indexKeyPattern().firstElementFieldName() );
-
- FieldRangeSet frs( "ns", BSON( "a" << 1 ), true, true );
- {
- SimpleMutex::scoped_lock lk(NamespaceDetailsTransient::_qcMutex);
- NamespaceDetailsTransient::get_inlock( ns() ).
- registerCachedQueryPlanForPattern( frs.pattern( BSON( "b" << 1 ) ),
- CachedQueryPlan( BSON( "a" << 1 ), 0,
- CandidatePlanCharacter( true, true ) ) );
- }
-
- c = NamespaceDetailsTransient::bestGuessCursor( ns(), fromjson( "{a:1,$or:[{y:1}]}" ),
- BSON( "b" << 1 ) );
- ASSERT_EQUALS( string( "b" ),
- c->indexKeyPattern().firstElement().fieldName() );
- }
- };
class All : public Suite {
public:
@@ -1633,35 +882,6 @@ namespace QueryOptimizerTests {
add<QueryPlanTests::QueryBoundsExactOrderSuffix::Unsatisfiable>();
add<QueryPlanTests::QueryBoundsExactOrderSuffix::EqualityUnsatisfiable>();
add<QueryPlanTests::Special>();
- add<QueryPlanSetTests::ToString>();
- add<QueryPlanSetTests::NoIndexes>();
- add<QueryPlanSetTests::Optimal>();
- add<QueryPlanSetTests::NoOptimal>();
- add<QueryPlanSetTests::NoSpec>();
- add<QueryPlanSetTests::HintSpec>();
- add<QueryPlanSetTests::HintName>();
- add<QueryPlanSetTests::NaturalHint>();
- add<QueryPlanSetTests::NaturalSort>();
- add<QueryPlanSetTests::BadHint>();
- add<QueryPlanSetTests::Count>();
- add<QueryPlanSetTests::QueryMissingNs>();
- add<QueryPlanSetTests::UnhelpfulIndex>();
- add<QueryPlanSetTests::FindOne>();
- add<QueryPlanSetTests::Delete>();
- add<QueryPlanSetTests::DeleteOneScan>();
- add<QueryPlanSetTests::DeleteOneIndex>();
- add<QueryPlanSetTests::InQueryIntervals>();
- add<QueryPlanSetTests::EqualityThenIn>();
- add<QueryPlanSetTests::NotEqualityThenIn>();
- add<QueryPlanSetTests::ExcludeSpecialPlanWhenBtreePlan>();
- add<QueryPlanSetTests::ExcludeUnindexedPlanWhenSpecialPlan>();
- add<QueryPlanSetTests::PossiblePlans>();
- add<QueryPlanSetTests::AvoidUnhelpfulRecordedPlan>();
- add<QueryPlanSetTests::AvoidDisallowedRecordedPlan>();
- add<QueryPlanSetTests::AllowSpecial>();
- add<MultiPlanScannerTests::ToString>();
- add<MultiPlanScannerTests::PossiblePlans>();
- add<BestGuess>();
}
} myall;