summaryrefslogtreecommitdiff
path: root/dbtests/queryoptimizertests.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'dbtests/queryoptimizertests.cpp')
-rw-r--r--dbtests/queryoptimizertests.cpp2903
1 files changed, 0 insertions, 2903 deletions
diff --git a/dbtests/queryoptimizertests.cpp b/dbtests/queryoptimizertests.cpp
deleted file mode 100644
index 3c4f61fcec9..00000000000
--- a/dbtests/queryoptimizertests.cpp
+++ /dev/null
@@ -1,2903 +0,0 @@
-// queryoptimizertests.cpp : query optimizer unit tests
-//
-
-/**
- * Copyright (C) 2009 10gen Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License, version 3,
- * as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#include "pch.h"
-#include "../db/queryoptimizer.h"
-#include "../db/querypattern.h"
-#include "../db/instance.h"
-#include "../db/ops/query.h"
-#include "../db/ops/delete.h"
-#include "dbtests.h"
-
-
-namespace mongo {
- extern BSONObj id_obj;
- void runQuery(Message& m, QueryMessage& q, Message &response ) {
- CurOp op( &(cc()) );
- op.ensureStarted();
- runQuery( m , q , op, response );
- }
- void runQuery(Message& m, QueryMessage& q ) {
- Message response;
- runQuery( m, q, response );
- }
- void __forceLinkGeoPlugin();
- boost::shared_ptr<Cursor> newQueryOptimizerCursor( const char *ns, const BSONObj &query, const BSONObj &order = BSONObj() );
-} // namespace mongo
-
-namespace QueryOptimizerTests {
-
- void dropCollection( const char *ns ) {
- string errmsg;
- BSONObjBuilder result;
- dropCollection( ns, errmsg, result );
- }
-
- namespace QueryPlanTests {
-
- class Base {
- public:
- Base() : _ctx( ns() ) , indexNum_( 0 ) {
- string err;
- userCreateNS( ns(), BSONObj(), err, false );
- }
- ~Base() {
- if ( !nsd() )
- return;
- dropCollection( ns() );
- }
- protected:
- static const char *ns() { return "unittests.QueryPlanTests"; }
- static NamespaceDetails *nsd() { return nsdetails( ns() ); }
- IndexDetails *index( const BSONObj &key ) {
- stringstream ss;
- ss << indexNum_++;
- string name = ss.str();
- client_.resetIndexCache();
- client_.ensureIndex( ns(), key, false, name.c_str() );
- NamespaceDetails *d = nsd();
- for( int i = 0; i < d->nIndexes; ++i ) {
- if ( d->idx(i).keyPattern() == key /*indexName() == name*/ || ( d->idx(i).isIdIndex() && IndexDetails::isIdIndexPattern( key ) ) )
- return &d->idx(i);
- }
- assert( false );
- return 0;
- }
- int indexno( const BSONObj &key ) {
- return nsd()->idxNo( *index(key) );
- }
- BSONObj startKey( const QueryPlan &p ) const {
- return p.frv()->startKey();
- }
- BSONObj endKey( const QueryPlan &p ) const {
- return p.frv()->endKey();
- }
- private:
- dblock lk_;
- Client::Context _ctx;
- 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 INDEXNO(x) nsd()->idxNo( *this->index( BSON(x) ) )
-#define INDEX(x) this->index( BSON(x) )
- auto_ptr< FieldRangeSetPair > FieldRangeSetPair_GLOBAL;
-#define FRSP(x) ( FieldRangeSetPair_GLOBAL.reset( new FieldRangeSetPair( ns(), x ) ), *FieldRangeSetPair_GLOBAL )
- auto_ptr< FieldRangeSetPair > FieldRangeSetPair_GLOBAL2;
-#define FRSP2(x) ( FieldRangeSetPair_GLOBAL2.reset( new FieldRangeSetPair( ns(), x ) ), FieldRangeSetPair_GLOBAL2.get() )
-
- class NoIndex : public Base {
- public:
- void run() {
- QueryPlan p( nsd(), -1, FRSP( BSONObj() ), FRSP2( BSONObj() ), BSONObj(), BSONObj() );
- ASSERT( !p.optimal() );
- ASSERT( !p.scanAndOrderRequired() );
- ASSERT( !p.exactKeyMatch() );
- }
- };
-
- class SimpleOrder : public Base {
- public:
- void run() {
- BSONObjBuilder b;
- b.appendMinKey( "" );
- BSONObj start = b.obj();
- BSONObjBuilder b2;
- b2.appendMaxKey( "" );
- BSONObj end = b2.obj();
-
- QueryPlan p( nsd(), INDEXNO( "a" << 1 ), FRSP( BSONObj() ), FRSP2( BSONObj() ), BSONObj(), BSON( "a" << 1 ) );
- ASSERT( !p.scanAndOrderRequired() );
- ASSERT( !startKey( p ).woCompare( start ) );
- ASSERT( !endKey( p ).woCompare( end ) );
- QueryPlan p2( nsd(), INDEXNO( "a" << 1 << "b" << 1 ), FRSP( BSONObj() ), FRSP2( BSONObj() ), BSONObj(), BSON( "a" << 1 << "b" << 1 ) );
- ASSERT( !p2.scanAndOrderRequired() );
- QueryPlan p3( nsd(), INDEXNO( "a" << 1 ), FRSP( BSONObj() ), FRSP2( BSONObj() ), BSONObj(), BSON( "b" << 1 ) );
- ASSERT( p3.scanAndOrderRequired() );
- ASSERT( !startKey( p3 ).woCompare( start ) );
- ASSERT( !endKey( p3 ).woCompare( end ) );
- }
- };
-
- class MoreIndexThanNeeded : public Base {
- public:
- void run() {
- QueryPlan p( nsd(), INDEXNO( "a" << 1 << "b" << 1 ), FRSP( BSONObj() ), FRSP2( BSONObj() ), BSONObj(), BSON( "a" << 1 ) );
- ASSERT( !p.scanAndOrderRequired() );
- }
- };
-
- class IndexSigns : public Base {
- public:
- void run() {
- QueryPlan p( nsd(), INDEXNO( "a" << 1 << "b" << -1 ) , FRSP( BSONObj() ), FRSP2( BSONObj() ), BSONObj(), BSON( "a" << 1 << "b" << -1 ) );
- ASSERT( !p.scanAndOrderRequired() );
- ASSERT_EQUALS( 1, p.direction() );
- QueryPlan p2( nsd(), INDEXNO( "a" << 1 << "b" << 1 ), FRSP( BSONObj() ), FRSP2( BSONObj() ), BSONObj(), BSON( "a" << 1 << "b" << -1 ) );
- ASSERT( p2.scanAndOrderRequired() );
- ASSERT_EQUALS( 0, p2.direction() );
- QueryPlan p3( nsd(), indexno( id_obj ), FRSP( BSONObj() ), FRSP2( BSONObj() ), BSONObj(), BSON( "_id" << 1 ) );
- ASSERT( !p3.scanAndOrderRequired() );
- ASSERT_EQUALS( 1, p3.direction() );
- }
- };
-
- class IndexReverse : public Base {
- public:
- void run() {
- BSONObjBuilder b;
- b.appendMinKey( "" );
- b.appendMaxKey( "" );
- BSONObj start = b.obj();
- BSONObjBuilder b2;
- b2.appendMaxKey( "" );
- b2.appendMinKey( "" );
- BSONObj end = b2.obj();
- QueryPlan p( nsd(), INDEXNO( "a" << -1 << "b" << 1 ),FRSP( BSONObj() ), FRSP2( BSONObj() ), BSONObj(), BSON( "a" << 1 << "b" << -1 ) );
- ASSERT( !p.scanAndOrderRequired() );
- ASSERT_EQUALS( -1, p.direction() );
- ASSERT( !startKey( p ).woCompare( start ) );
- ASSERT( !endKey( p ).woCompare( end ) );
- QueryPlan p2( nsd(), INDEXNO( "a" << 1 << "b" << 1 ), FRSP( BSONObj() ), FRSP2( BSONObj() ), BSONObj(), BSON( "a" << -1 << "b" << -1 ) );
- ASSERT( !p2.scanAndOrderRequired() );
- ASSERT_EQUALS( -1, p2.direction() );
- QueryPlan p3( nsd(), INDEXNO( "a" << 1 << "b" << -1 ), FRSP( BSONObj() ), FRSP2( BSONObj() ), BSONObj(), BSON( "a" << -1 << "b" << -1 ) );
- ASSERT( p3.scanAndOrderRequired() );
- ASSERT_EQUALS( 0, p3.direction() );
- }
- };
-
- class NoOrder : public Base {
- public:
- void run() {
- BSONObjBuilder b;
- b.append( "", 3 );
- b.appendMinKey( "" );
- BSONObj start = b.obj();
- BSONObjBuilder b2;
- b2.append( "", 3 );
- b2.appendMaxKey( "" );
- BSONObj end = b2.obj();
- QueryPlan p( nsd(), INDEXNO( "a" << -1 << "b" << 1 ), FRSP( BSON( "a" << 3 ) ), FRSP2( BSON( "a" << 3 ) ), BSON( "a" << 3 ), BSONObj() );
- ASSERT( !p.scanAndOrderRequired() );
- ASSERT( !startKey( p ).woCompare( start ) );
- ASSERT( !endKey( p ).woCompare( end ) );
- QueryPlan p2( nsd(), INDEXNO( "a" << -1 << "b" << 1 ), FRSP( BSON( "a" << 3 ) ), FRSP2( BSON( "a" << 3 ) ), BSON( "a" << 3 ), BSONObj() );
- ASSERT( !p2.scanAndOrderRequired() );
- ASSERT( !startKey( p ).woCompare( start ) );
- ASSERT( !endKey( p ).woCompare( end ) );
- }
- };
-
- class EqualWithOrder : public Base {
- public:
- void run() {
- QueryPlan p( nsd(), INDEXNO( "a" << 1 << "b" << 1 ), FRSP( BSON( "a" << 4 ) ), FRSP2( BSON( "a" << 4 ) ), BSON( "a" << 4 ), BSON( "b" << 1 ) );
- ASSERT( !p.scanAndOrderRequired() );
- QueryPlan p2( nsd(), INDEXNO( "a" << 1 << "b" << 1 << "c" << 1 ), FRSP( BSON( "b" << 4 ) ), FRSP2( BSON( "b" << 4 ) ), BSON( "b" << 4 ), BSON( "a" << 1 << "c" << 1 ) );
- ASSERT( !p2.scanAndOrderRequired() );
- QueryPlan p3( nsd(), INDEXNO( "a" << 1 << "b" << 1 ), FRSP( BSON( "b" << 4 ) ), FRSP2( BSON( "b" << 4 ) ), BSON( "b" << 4 ), BSON( "a" << 1 << "c" << 1 ) );
- ASSERT( p3.scanAndOrderRequired() );
- }
- };
-
- class Optimal : public Base {
- public:
- void run() {
- QueryPlan p( nsd(), INDEXNO( "a" << 1 ), FRSP( BSONObj() ), FRSP2( BSONObj() ), BSONObj(), BSON( "a" << 1 ) );
- ASSERT( p.optimal() );
- QueryPlan p2( nsd(), INDEXNO( "a" << 1 << "b" << 1 ), FRSP( BSONObj() ), FRSP2( BSONObj() ), BSONObj(), BSON( "a" << 1 ) );
- ASSERT( p2.optimal() );
- QueryPlan p3( nsd(), INDEXNO( "a" << 1 << "b" << 1 ), FRSP( BSON( "a" << 1 ) ), FRSP2( BSON( "a" << 1 ) ), BSON( "a" << 1 ), BSON( "a" << 1 ) );
- ASSERT( p3.optimal() );
- QueryPlan p4( nsd(), INDEXNO( "a" << 1 << "b" << 1 ), FRSP( BSON( "b" << 1 ) ), FRSP2( BSON( "b" << 1 ) ), BSON( "b" << 1 ), BSON( "a" << 1 ) );
- ASSERT( !p4.optimal() );
- QueryPlan p5( nsd(), INDEXNO( "a" << 1 << "b" << 1 ), FRSP( BSON( "a" << 1 ) ), FRSP2( BSON( "a" << 1 ) ), BSON( "a" << 1 ), BSON( "b" << 1 ) );
- ASSERT( p5.optimal() );
- QueryPlan p6( nsd(), INDEXNO( "a" << 1 << "b" << 1 ), FRSP( BSON( "b" << 1 ) ), FRSP2( BSON( "b" << 1 ) ), BSON( "b" << 1 ), BSON( "b" << 1 ) );
- ASSERT( !p6.optimal() );
- QueryPlan p7( nsd(), INDEXNO( "a" << 1 << "b" << 1 ), FRSP( BSON( "a" << 1 << "b" << 1 ) ), FRSP2( BSON( "a" << 1 << "b" << 1 ) ), BSON( "a" << 1 << "b" << 1 ), BSON( "a" << 1 ) );
- ASSERT( p7.optimal() );
- QueryPlan p8( nsd(), INDEXNO( "a" << 1 << "b" << 1 ), FRSP( BSON( "a" << 1 << "b" << LT << 1 ) ), FRSP2( BSON( "a" << 1 << "b" << LT << 1 ) ), BSON( "a" << 1 << "b" << LT << 1 ), BSON( "a" << 1 ) );
- ASSERT( p8.optimal() );
- QueryPlan p9( nsd(), INDEXNO( "a" << 1 << "b" << 1 << "c" << 1 ), FRSP( BSON( "a" << 1 << "b" << LT << 1 ) ), FRSP2( BSON( "a" << 1 << "b" << LT << 1 ) ), BSON( "a" << 1 << "b" << LT << 1 ), BSON( "a" << 1 ) );
- ASSERT( p9.optimal() );
- }
- };
-
- class MoreOptimal : public Base {
- public:
- void run() {
- QueryPlan p10( nsd(), INDEXNO( "a" << 1 << "b" << 1 << "c" << 1 ), FRSP( BSON( "a" << 1 ) ), FRSP2( BSON( "a" << 1 ) ), BSON( "a" << 1 ), BSONObj() );
- ASSERT( p10.optimal() );
- QueryPlan p11( nsd(), INDEXNO( "a" << 1 << "b" << 1 << "c" << 1 ), FRSP( BSON( "a" << 1 << "b" << LT << 1 ) ), FRSP2( BSON( "a" << 1 << "b" << LT << 1 ) ), BSON( "a" << 1 << "b" << LT << 1 ), BSONObj() );
- ASSERT( p11.optimal() );
- QueryPlan p12( nsd(), INDEXNO( "a" << 1 << "b" << 1 << "c" << 1 ), FRSP( BSON( "a" << LT << 1 ) ), FRSP2( BSON( "a" << LT << 1 ) ), BSON( "a" << LT << 1 ), BSONObj() );
- ASSERT( p12.optimal() );
- QueryPlan p13( nsd(), INDEXNO( "a" << 1 << "b" << 1 << "c" << 1 ), FRSP( BSON( "a" << LT << 1 ) ), FRSP2( BSON( "a" << LT << 1 ) ), BSON( "a" << LT << 1 ), BSON( "a" << 1 ) );
- ASSERT( p13.optimal() );
- }
- };
-
- class KeyMatch : public Base {
- public:
- void run() {
- QueryPlan p( nsd(), INDEXNO( "a" << 1 ), FRSP( BSONObj() ), FRSP2( BSONObj() ), BSONObj(), BSON( "a" << 1 ) );
- ASSERT( !p.exactKeyMatch() );
- QueryPlan p2( nsd(), INDEXNO( "b" << 1 << "a" << 1 ), FRSP( BSONObj() ), FRSP2( BSONObj() ), BSONObj(), BSON( "a" << 1 ) );
- ASSERT( !p2.exactKeyMatch() );
- QueryPlan p3( nsd(), INDEXNO( "b" << 1 << "a" << 1 ), FRSP( BSON( "b" << "z" ) ), FRSP2( BSON( "b" << "z" ) ), BSON( "b" << "z" ), BSON( "a" << 1 ) );
- ASSERT( !p3.exactKeyMatch() );
- QueryPlan p4( nsd(), INDEXNO( "b" << 1 << "a" << 1 << "c" << 1 ), FRSP( BSON( "c" << "y" << "b" << "z" ) ), FRSP2( BSON( "c" << "y" << "b" << "z" ) ), BSON( "c" << "y" << "b" << "z" ), BSON( "a" << 1 ) );
- ASSERT( !p4.exactKeyMatch() );
- QueryPlan p5( nsd(), INDEXNO( "b" << 1 << "a" << 1 << "c" << 1 ), FRSP( BSON( "c" << "y" << "b" << "z" ) ), FRSP2( BSON( "c" << "y" << "b" << "z" ) ), BSON( "c" << "y" << "b" << "z" ), BSONObj() );
- ASSERT( !p5.exactKeyMatch() );
- QueryPlan p6( nsd(), INDEXNO( "b" << 1 << "a" << 1 << "c" << 1 ), FRSP( BSON( "c" << LT << "y" << "b" << GT << "z" ) ), FRSP2( BSON( "c" << LT << "y" << "b" << GT << "z" ) ), BSON( "c" << LT << "y" << "b" << GT << "z" ), BSONObj() );
- ASSERT( !p6.exactKeyMatch() );
- QueryPlan p7( nsd(), INDEXNO( "b" << 1 ), FRSP( BSONObj() ), FRSP2( BSONObj() ), BSONObj(), BSON( "a" << 1 ) );
- ASSERT( !p7.exactKeyMatch() );
- QueryPlan p8( nsd(), INDEXNO( "a" << 1 << "b" << 1 ), FRSP( BSON( "b" << "y" << "a" << "z" ) ), FRSP2( BSON( "b" << "y" << "a" << "z" ) ), BSON( "b" << "y" << "a" << "z" ), BSONObj() );
- ASSERT( p8.exactKeyMatch() );
- QueryPlan p9( nsd(), INDEXNO( "a" << 1 ), FRSP( BSON( "a" << "z" ) ), FRSP2( BSON( "a" << "z" ) ), BSON( "a" << "z" ), BSON( "a" << 1 ) );
- ASSERT( p9.exactKeyMatch() );
- }
- };
-
- class MoreKeyMatch : public Base {
- public:
- void run() {
- QueryPlan p( nsd(), INDEXNO( "a" << 1 ), FRSP( BSON( "a" << "r" << "b" << NE << "q" ) ), FRSP2( BSON( "a" << "r" << "b" << NE << "q" ) ), BSON( "a" << "r" << "b" << NE << "q" ), BSON( "a" << 1 ) );
- ASSERT( !p.exactKeyMatch() );
- }
- };
-
- class ExactKeyQueryTypes : public Base {
- public:
- void run() {
- QueryPlan p( nsd(), INDEXNO( "a" << 1 ), FRSP( BSON( "a" << "b" ) ), FRSP2( BSON( "a" << "b" ) ), BSON( "a" << "b" ), BSONObj() );
- ASSERT( p.exactKeyMatch() );
- QueryPlan p2( nsd(), INDEXNO( "a" << 1 ), FRSP( BSON( "a" << 4 ) ), FRSP2( BSON( "a" << 4 ) ), BSON( "a" << 4 ), BSONObj() );
- ASSERT( !p2.exactKeyMatch() );
- QueryPlan p3( nsd(), INDEXNO( "a" << 1 ), FRSP( BSON( "a" << BSON( "c" << "d" ) ) ), FRSP2( BSON( "a" << BSON( "c" << "d" ) ) ), BSON( "a" << BSON( "c" << "d" ) ), BSONObj() );
- ASSERT( !p3.exactKeyMatch() );
- BSONObjBuilder b;
- b.appendRegex( "a", "^ddd" );
- BSONObj q = b.obj();
- QueryPlan p4( nsd(), INDEXNO( "a" << 1 ), FRSP( q ), FRSP2( q ), q, BSONObj() );
- ASSERT( !p4.exactKeyMatch() );
- QueryPlan p5( nsd(), INDEXNO( "a" << 1 << "b" << 1 ), FRSP( BSON( "a" << "z" << "b" << 4 ) ), FRSP2( BSON( "a" << "z" << "b" << 4 ) ), BSON( "a" << "z" << "b" << 4 ), BSONObj() );
- ASSERT( !p5.exactKeyMatch() );
- }
- };
-
- class Unhelpful : public Base {
- public:
- void run() {
- QueryPlan p( nsd(), INDEXNO( "a" << 1 << "b" << 1 ), FRSP( BSON( "b" << 1 ) ), FRSP2( BSON( "b" << 1 ) ), BSON( "b" << 1 ), BSONObj() );
- ASSERT( !p.range( "a" ).nontrivial() );
- ASSERT( p.unhelpful() );
- QueryPlan p2( nsd(), INDEXNO( "a" << 1 << "b" << 1 ), FRSP( BSON( "b" << 1 << "c" << 1 ) ), FRSP2( BSON( "b" << 1 << "c" << 1 ) ), BSON( "b" << 1 << "c" << 1 ), BSON( "a" << 1 ) );
- ASSERT( !p2.scanAndOrderRequired() );
- ASSERT( !p2.range( "a" ).nontrivial() );
- ASSERT( !p2.unhelpful() );
- QueryPlan p3( nsd(), INDEXNO( "b" << 1 ), FRSP( BSON( "b" << 1 << "c" << 1 ) ), FRSP2( BSON( "b" << 1 << "c" << 1 ) ), BSON( "b" << 1 << "c" << 1 ), BSONObj() );
- ASSERT( p3.range( "b" ).nontrivial() );
- ASSERT( !p3.unhelpful() );
- QueryPlan p4( nsd(), INDEXNO( "b" << 1 << "c" << 1 ), FRSP( BSON( "c" << 1 << "d" << 1 ) ), FRSP2( BSON( "c" << 1 << "d" << 1 ) ), BSON( "c" << 1 << "d" << 1 ), BSONObj() );
- ASSERT( !p4.range( "b" ).nontrivial() );
- ASSERT( p4.unhelpful() );
- }
- };
-
- } // 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() );
- }
- 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());
- }
- protected:
- static const char *ns() { return "unittests.QueryPlanSetTests"; }
- static NamespaceDetails *nsd() { return nsdetails( ns() ); }
- private:
- dblock lk_;
- Client::Context _context;
- };
-
- class NoIndexes : public Base {
- public:
- void run() {
- auto_ptr< FieldRangeSetPair > frsp( new FieldRangeSetPair( ns(), BSON( "a" << 4 ) ) );
- auto_ptr< FieldRangeSetPair > frspOrig( new FieldRangeSetPair( *frsp ) );
- QueryPlanSet s( ns(), frsp, frspOrig, BSON( "a" << 4 ), BSON( "b" << 1 ) );
- ASSERT_EQUALS( 1, s.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" );
- auto_ptr< FieldRangeSetPair > frsp( new FieldRangeSetPair( ns(), BSON( "a" << 4 ) ) );
- auto_ptr< FieldRangeSetPair > frspOrig( new FieldRangeSetPair( *frsp ) );
- QueryPlanSet s( ns(), frsp, frspOrig, BSON( "a" << 4 ), BSONObj() );
- ASSERT_EQUALS( 1, s.nPlans() );
- }
- };
-
- 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" );
- auto_ptr< FieldRangeSetPair > frsp( new FieldRangeSetPair( ns(), BSON( "a" << 4 ) ) );
- auto_ptr< FieldRangeSetPair > frspOrig( new FieldRangeSetPair( *frsp ) );
- QueryPlanSet s( ns(), frsp, frspOrig, BSON( "a" << 4 ), BSON( "b" << 1 ) );
- ASSERT_EQUALS( 3, s.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" );
- auto_ptr< FieldRangeSetPair > frsp( new FieldRangeSetPair( ns(), BSONObj() ) );
- auto_ptr< FieldRangeSetPair > frspOrig( new FieldRangeSetPair( *frsp ) );
- QueryPlanSet s( ns(), frsp, frspOrig, BSONObj(), BSONObj() );
- ASSERT_EQUALS( 1, s.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" );
- BSONObj b = BSON( "hint" << BSON( "a" << 1 ) );
- BSONElement e = b.firstElement();
- auto_ptr< FieldRangeSetPair > frsp( new FieldRangeSetPair( ns(), BSON( "a" << 1 ) ) );
- auto_ptr< FieldRangeSetPair > frspOrig( new FieldRangeSetPair( *frsp ) );
- QueryPlanSet s( ns(), frsp, frspOrig, BSON( "a" << 1 ), BSON( "b" << 1 ), true, &e );
- ASSERT_EQUALS( 1, s.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" );
- BSONObj b = BSON( "hint" << "a_1" );
- BSONElement e = b.firstElement();
- auto_ptr< FieldRangeSetPair > frsp( new FieldRangeSetPair( ns(), BSON( "a" << 1 ) ) );
- auto_ptr< FieldRangeSetPair > frspOrig( new FieldRangeSetPair( *frsp ) );
- QueryPlanSet s( ns(), frsp, frspOrig, BSON( "a" << 1 ), BSON( "b" << 1 ), true, &e );
- ASSERT_EQUALS( 1, s.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" );
- BSONObj b = BSON( "hint" << BSON( "$natural" << 1 ) );
- BSONElement e = b.firstElement();
- auto_ptr< FieldRangeSetPair > frsp( new FieldRangeSetPair( ns(), BSON( "a" << 1 ) ) );
- auto_ptr< FieldRangeSetPair > frspOrig( new FieldRangeSetPair( *frsp ) );
- QueryPlanSet s( ns(), frsp, frspOrig, BSON( "a" << 1 ), BSON( "b" << 1 ), true, &e );
- ASSERT_EQUALS( 1, s.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" );
- auto_ptr< FieldRangeSetPair > frsp( new FieldRangeSetPair( ns(), BSON( "a" << 1 ) ) );
- auto_ptr< FieldRangeSetPair > frspOrig( new FieldRangeSetPair( *frsp ) );
- QueryPlanSet s( ns(), frsp, frspOrig, BSON( "a" << 1 ), BSON( "$natural" << 1 ) );
- ASSERT_EQUALS( 1, s.nPlans() );
- }
- };
-
- class BadHint : public Base {
- public:
- void run() {
- BSONObj b = BSON( "hint" << "a_1" );
- BSONElement e = b.firstElement();
- auto_ptr< FieldRangeSetPair > frsp( new FieldRangeSetPair( ns(), BSON( "a" << 1 ) ) );
- auto_ptr< FieldRangeSetPair > frspOrig( new FieldRangeSetPair( *frsp ) );
- ASSERT_EXCEPTION( QueryPlanSet s( ns(), frsp, frspOrig, BSON( "a" << 1 ), BSON( "b" << 1 ), true, &e ),
- 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;
- ASSERT_EQUALS( 0, runCount( ns(), BSON( "query" << BSON( "a" << 4 ) ), err ) );
- 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 ) );
- theDataFileMgr.insertWithObjMod( ns(), fourA );
- ASSERT_EQUALS( 1, runCount( ns(), BSON( "query" << BSON( "a" << 4 ) ), err ) );
- theDataFileMgr.insertWithObjMod( ns(), fourB );
- ASSERT_EQUALS( 2, runCount( ns(), BSON( "query" << BSON( "a" << 4 ) ), err ) );
- ASSERT_EQUALS( 3, runCount( ns(), BSON( "query" << BSONObj() ), err ) );
- ASSERT_EQUALS( 3, runCount( ns(), BSON( "query" << BSON( "a" << GT << 0 ) ), err ) );
- // missing ns
- ASSERT_EQUALS( -1, runCount( "unittests.missingNS", BSONObj(), err ) );
- // impossible match
- ASSERT_EQUALS( 0, runCount( ns(), BSON( "query" << BSON( "a" << GT << 0 << LT << -1 ) ), err ) );
- }
- };
-
- 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" );
- auto_ptr< FieldRangeSetPair > frsp( new FieldRangeSetPair( ns(), BSON( "a" << 1 << "c" << 2 ) ) );
- auto_ptr< FieldRangeSetPair > frspOrig( new FieldRangeSetPair( *frsp ) );
- QueryPlanSet s( ns(), frsp, frspOrig, BSON( "a" << 1 << "c" << 2 ), BSONObj() );
- ASSERT_EQUALS( 2, s.nPlans() );
- }
- };
-
- class SingleException : public Base {
- public:
- void run() {
- Helpers::ensureIndex( ns(), BSON( "a" << 1 ), false, "a_1" );
- Helpers::ensureIndex( ns(), BSON( "b" << 1 ), false, "b_1" );
- auto_ptr< FieldRangeSetPair > frsp( new FieldRangeSetPair( ns(), BSON( "a" << 4 ) ) );
- auto_ptr< FieldRangeSetPair > frspOrig( new FieldRangeSetPair( *frsp ) );
- QueryPlanSet s( ns(), frsp, frspOrig, BSON( "a" << 4 ), BSON( "b" << 1 ) );
- ASSERT_EQUALS( 3, s.nPlans() );
- bool threw = false;
- auto_ptr< TestOp > t( new TestOp( true, threw ) );
- boost::shared_ptr< TestOp > done = s.runOp( *t );
- ASSERT( threw );
- ASSERT( done->complete() );
- ASSERT( done->exception().empty() );
- ASSERT( !done->error() );
- }
- private:
- class TestOp : public QueryOp {
- public:
- TestOp( bool iThrow, bool &threw ) : iThrow_( iThrow ), threw_( threw ), i_(), youThrow_( false ) {}
- virtual void _init() {}
- virtual void next() {
- if ( iThrow_ )
- threw_ = true;
- massert( 10408 , "throw", !iThrow_ );
- if ( ++i_ > 10 )
- setComplete();
- }
- virtual QueryOp *_createChild() const {
- QueryOp *op = new TestOp( youThrow_, threw_ );
- youThrow_ = !youThrow_;
- return op;
- }
- virtual bool mayRecordPlan() const { return true; }
- virtual long long nscanned() { return 0; }
- private:
- bool iThrow_;
- bool &threw_;
- int i_;
- mutable bool youThrow_;
- };
- };
-
- class AllException : public Base {
- public:
- void run() {
- Helpers::ensureIndex( ns(), BSON( "a" << 1 ), false, "a_1" );
- Helpers::ensureIndex( ns(), BSON( "b" << 1 ), false, "b_1" );
- auto_ptr< FieldRangeSetPair > frsp( new FieldRangeSetPair( ns(), BSON( "a" << 4 ) ) );
- auto_ptr< FieldRangeSetPair > frspOrig( new FieldRangeSetPair( *frsp ) );
- QueryPlanSet s( ns(), frsp, frspOrig, BSON( "a" << 4 ), BSON( "b" << 1 ) );
- ASSERT_EQUALS( 3, s.nPlans() );
- auto_ptr< TestOp > t( new TestOp() );
- boost::shared_ptr< TestOp > done = s.runOp( *t );
- ASSERT( !done->complete() );
- ASSERT_EQUALS( "throw", done->exception().msg );
- ASSERT( done->error() );
- }
- private:
- class TestOp : public QueryOp {
- public:
- virtual void _init() {}
- virtual void next() {
- massert( 10409 , "throw", false );
- }
- virtual QueryOp *_createChild() const {
- return new TestOp();
- }
- virtual bool mayRecordPlan() const { return true; }
- virtual long long nscanned() { return 0; }
- };
- };
-
- class SaveGoodIndex : public Base {
- public:
- void run() {
- Helpers::ensureIndex( ns(), BSON( "a" << 1 ), false, "a_1" );
- Helpers::ensureIndex( ns(), BSON( "b" << 1 ), false, "b_1" );
- // No best plan - all must be tried.
- nPlans( 3 );
- runQuery();
- // Best plan selected by query.
- nPlans( 1 );
- nPlans( 1 );
- Helpers::ensureIndex( ns(), BSON( "c" << 1 ), false, "c_1" );
- // Best plan cleared when new index added.
- nPlans( 3 );
- runQuery();
- // Best plan selected by query.
- nPlans( 1 );
-
- {
- DBDirectClient client;
- for( int i = 0; i < 334; ++i ) {
- client.insert( ns(), BSON( "i" << i ) );
- client.update( ns(), QUERY( "i" << i ), BSON( "i" << i + 1 ) );
- client.remove( ns(), BSON( "i" << i + 1 ) );
- }
- }
- // Best plan cleared by ~1000 writes.
- nPlans( 3 );
-
- auto_ptr< FieldRangeSetPair > frsp( new FieldRangeSetPair( ns(), BSON( "a" << 4 ) ) );
- auto_ptr< FieldRangeSetPair > frspOrig( new FieldRangeSetPair( *frsp ) );
- QueryPlanSet s( ns(), frsp, frspOrig, BSON( "a" << 4 ), BSON( "b" << 1 ) );
- NoRecordTestOp original;
- s.runOp( original );
- // NoRecordTestOp doesn't record a best plan (test cases where mayRecordPlan() is false).
- nPlans( 3 );
-
- BSONObj hint = fromjson( "{hint:{$natural:1}}" );
- BSONElement hintElt = hint.firstElement();
- auto_ptr< FieldRangeSetPair > frsp2( new FieldRangeSetPair( ns(), BSON( "a" << 4 ) ) );
- auto_ptr< FieldRangeSetPair > frspOrig2( new FieldRangeSetPair( *frsp2 ) );
- QueryPlanSet s2( ns(), frsp2, frspOrig2, BSON( "a" << 4 ), BSON( "b" << 1 ), true, &hintElt );
- TestOp newOriginal;
- s2.runOp( newOriginal );
- // No plan recorded when a hint is used.
- nPlans( 3 );
-
- auto_ptr< FieldRangeSetPair > frsp3( new FieldRangeSetPair( ns(), BSON( "a" << 4 ), true ) );
- auto_ptr< FieldRangeSetPair > frspOrig3( new FieldRangeSetPair( *frsp3 ) );
- QueryPlanSet s3( ns(), frsp3, frspOrig3, BSON( "a" << 4 ), BSON( "b" << 1 << "c" << 1 ) );
- TestOp newerOriginal;
- s3.runOp( newerOriginal );
- // Plan recorded was for a different query pattern (different sort spec).
- nPlans( 3 );
-
- // Best plan still selected by query after all these other tests.
- runQuery();
- nPlans( 1 );
- }
- private:
- void nPlans( int n ) {
- auto_ptr< FieldRangeSetPair > frsp( new FieldRangeSetPair( ns(), BSON( "a" << 4 ) ) );
- auto_ptr< FieldRangeSetPair > frspOrig( new FieldRangeSetPair( *frsp ) );
- QueryPlanSet s( ns(), frsp, frspOrig, BSON( "a" << 4 ), BSON( "b" << 1 ) );
- ASSERT_EQUALS( n, s.nPlans() );
- }
- void runQuery() {
- auto_ptr< FieldRangeSetPair > frsp( new FieldRangeSetPair( ns(), BSON( "a" << 4 ) ) );
- auto_ptr< FieldRangeSetPair > frspOrig( new FieldRangeSetPair( *frsp ) );
- QueryPlanSet s( ns(), frsp, frspOrig, BSON( "a" << 4 ), BSON( "b" << 1 ) );
- TestOp original;
- s.runOp( original );
- }
- class TestOp : public QueryOp {
- public:
- virtual void _init() {}
- virtual void next() {
- setComplete();
- }
- virtual QueryOp *_createChild() const {
- return new TestOp();
- }
- virtual bool mayRecordPlan() const { return true; }
- virtual long long nscanned() { return 0; }
- };
- class NoRecordTestOp : public TestOp {
- virtual bool mayRecordPlan() const { return false; }
- virtual QueryOp *_createChild() const { return new NoRecordTestOp(); }
- };
- };
-
- class TryAllPlansOnErr : public Base {
- public:
- void run() {
- Helpers::ensureIndex( ns(), BSON( "a" << 1 ), false, "a_1" );
-
- auto_ptr< FieldRangeSetPair > frsp( new FieldRangeSetPair( ns(), BSON( "a" << 4 ) ) );
- auto_ptr< FieldRangeSetPair > frspOrig( new FieldRangeSetPair( *frsp ) );
- QueryPlanSet s( ns(), frsp, frspOrig, BSON( "a" << 4 ), BSON( "b" << 1 ) );
- ScanOnlyTestOp op;
- s.runOp( op );
- pair< BSONObj, long long > best = QueryUtilIndexed::bestIndexForPatterns( s.frsp(), BSON( "b" << 1 ) );
- ASSERT( fromjson( "{$natural:1}" ).woCompare( best.first ) == 0 );
- ASSERT_EQUALS( 1, best.second );
-
- auto_ptr< FieldRangeSetPair > frsp2( new FieldRangeSetPair( ns(), BSON( "a" << 4 ) ) );
- auto_ptr< FieldRangeSetPair > frspOrig2( new FieldRangeSetPair( *frsp2 ) );
- QueryPlanSet s2( ns(), frsp2, frspOrig2, BSON( "a" << 4 ), BSON( "b" << 1 ) );
- TestOp op2;
- ASSERT( s2.runOp( op2 )->complete() );
- }
- private:
- class TestOp : public QueryOp {
- public:
- TestOp() {}
- virtual void _init() {}
- virtual void next() {
- if ( qp().indexKey().firstElementFieldName() == string( "$natural" ) )
- massert( 10410 , "throw", false );
- setComplete();
- }
- virtual QueryOp *_createChild() const {
- return new TestOp();
- }
- virtual bool mayRecordPlan() const { return true; }
- virtual long long nscanned() { return 1; }
- };
- class ScanOnlyTestOp : public TestOp {
- virtual void next() {
- if ( qp().indexKey().firstElement().fieldName() == string( "$natural" ) )
- setComplete();
- massert( 10411 , "throw", false );
- }
- virtual QueryOp *_createChild() const {
- return new ScanOnlyTestOp();
- }
- };
- };
-
- 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_EXCEPTION( 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 );
- deleteObjects( ns(), BSON( "a" << 1 ), false );
- ASSERT( BSON( "a" << 1 ).woCompare( NamespaceDetailsTransient::get_inlock( ns() ).indexForPattern( FieldRangeSet( ns(), BSON( "a" << 1 ), true ).pattern() ) ) == 0 );
- ASSERT_EQUALS( 1, NamespaceDetailsTransient::get_inlock( ns() ).nScannedForPattern( FieldRangeSet( ns(), BSON( "a" << 1 ), true ).pattern() ) );
- }
- };
-
- 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 TryOtherPlansBeforeFinish : public Base {
- public:
- void run() {
- Helpers::ensureIndex( ns(), BSON( "a" << 1 ), false, "a_1" );
- for( int i = 0; i < 100; ++i ) {
- for( int j = 0; j < 2; ++j ) {
- BSONObj temp = BSON( "a" << 100 - i - 1 << "b" << i );
- theDataFileMgr.insertWithObjMod( ns(), temp );
- }
- }
- Message m;
- // Need to return at least 2 records to cause plan to be recorded.
- assembleRequest( ns(), QUERY( "b" << 0 << "a" << GTE << 0 ).obj, 2, 0, 0, 0, m );
- stringstream ss;
- {
- DbMessage d(m);
- QueryMessage q(d);
- runQuery( m, q);
- }
- ASSERT( BSON( "$natural" << 1 ).woCompare( NamespaceDetailsTransient::get_inlock( ns() ).indexForPattern( FieldRangeSet( ns(), BSON( "b" << 0 << "a" << GTE << 0 ), true ).pattern() ) ) == 0 );
-
- Message m2;
- assembleRequest( ns(), QUERY( "b" << 99 << "a" << GTE << 0 ).obj, 2, 0, 0, 0, m2 );
- {
- DbMessage d(m2);
- QueryMessage q(d);
- runQuery( m2, q);
- }
- ASSERT( BSON( "a" << 1 ).woCompare( NamespaceDetailsTransient::get_inlock( ns() ).indexForPattern( FieldRangeSet( ns(), BSON( "b" << 0 << "a" << GTE << 0 ), true ).pattern() ) ) == 0 );
- ASSERT_EQUALS( 3, NamespaceDetailsTransient::get_inlock( ns() ).nScannedForPattern( FieldRangeSet( ns(), BSON( "b" << 0 << "a" << GTE << 0 ), true ).pattern() ) );
- }
- };
-
- 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 hint = fromjson( "{$hint:{a:1}}" );
- BSONElement hintElt = hint.firstElement();
- auto_ptr< FieldRangeSetPair > frsp( new FieldRangeSetPair( ns(), fromjson( "{a:{$in:[2,3,6,9,11]}}" ) ) );
- auto_ptr< FieldRangeSetPair > frspOrig( new FieldRangeSetPair( *frsp ) );
- QueryPlanSet s( ns(), frsp, frspOrig, fromjson( "{a:{$in:[2,3,6,9,11]}}" ), BSONObj(), true, &hintElt );
- QueryPlan qp( nsd(), 1, s.frsp(), s.originalFrsp(), fromjson( "{a:{$in:[2,3,6,9,11]}}" ), BSONObj() );
- 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
- {
- auto_ptr< FieldRangeSetPair > frsp( new FieldRangeSetPair( ns(), fromjson( "{a:{$in:[2,3,6,9,11]}}" ) ) );
- auto_ptr< FieldRangeSetPair > frspOrig( new FieldRangeSetPair( *frsp ) );
- QueryPlanSet s( ns(), frsp, frspOrig, fromjson( "{a:{$in:[2,3,6,9,11]}}" ), BSON( "a" << -1 ), true, &hintElt );
- QueryPlan qp( nsd(), 1, s.frsp(), s.originalFrsp(), fromjson( "{a:{$in:[2,3,6,9,11]}}" ), BSON( "a" << -1 ) );
- 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 );
- }
- BSONObj hint = fromjson( "{$hint:{a:1,b:1}}" );
- auto_ptr< FieldRangeSetPair > frsp( new FieldRangeSetPair( ns(), fromjson( "{a:5,b:{$in:[2,3,6,9,11]}}" ) ) );
- QueryPlan qp( 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 );
- }
- BSONObj hint = fromjson( "{$hint:{a:1,b:1}}" );
- auto_ptr< FieldRangeSetPair > frsp( new FieldRangeSetPair( ns(), fromjson( "{a:{$gte:5},b:{$in:[2,3,6,9,11]}}" ) ) );
- QueryPlan qp( 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() );
- }
- };
-
- } // 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() ); }
- DBDirectClient &client() { return _client; }
- private:
- dblock lk_;
- Client::Context _ctx;
- DBDirectClient _client;
- };
-
- 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 = bestGuessCursor( ns(), BSON( "b" << 1 ), BSON( "a" << 1 ) );
- ASSERT_EQUALS( string( "a" ), c->indexKeyPattern().firstElement().fieldName() );
- c = bestGuessCursor( ns(), BSON( "a" << 1 ), BSON( "b" << 1 ) );
- ASSERT_EQUALS( string( "b" ), c->indexKeyPattern().firstElementFieldName() );
- boost::shared_ptr< MultiCursor > m = dynamic_pointer_cast< MultiCursor >( bestGuessCursor( ns(), fromjson( "{b:1,$or:[{z:1}]}" ), BSON( "a" << 1 ) ) );
- ASSERT_EQUALS( string( "a" ), m->sub_c()->indexKeyPattern().firstElement().fieldName() );
- m = dynamic_pointer_cast< MultiCursor >( bestGuessCursor( ns(), fromjson( "{a:1,$or:[{y:1}]}" ), BSON( "b" << 1 ) ) );
- ASSERT_EQUALS( string( "b" ), m->sub_c()->indexKeyPattern().firstElementFieldName() );
-
- FieldRangeSet frs( "ns", BSON( "a" << 1 ), true );
- {
- SimpleMutex::scoped_lock lk(NamespaceDetailsTransient::_qcMutex);
- NamespaceDetailsTransient::get_inlock( ns() ).registerIndexForPattern( frs.pattern( BSON( "b" << 1 ) ), BSON( "a" << 1 ), 0 );
- }
- m = dynamic_pointer_cast< MultiCursor >( bestGuessCursor( ns(), fromjson( "{a:1,$or:[{y:1}]}" ), BSON( "b" << 1 ) ) );
- ASSERT_EQUALS( string( "b" ), m->sub_c()->indexKeyPattern().firstElement().fieldName() );
- }
- };
-
- namespace MultiCursorTests {
-
- /**
- * Helper class for validating a set of attempted candidate plans The boiler plate
- * implementation is similar to MultiCursor::NoOp.
- */
- class PlanValidatingCursorOp : public MultiCursor::CursorOp {
- public:
- PlanValidatingCursorOp( const BSONObj &expectedIndexKey ) :
- _expectedIndexKey( expectedIndexKey ) {
- }
- virtual void _init() {
- ASSERT_EQUALS( _expectedIndexKey, qp().indexKey() );
- }
- virtual void next() { setComplete(); }
- virtual bool mayRecordPlan() const { return false; }
- virtual QueryOp *_createChild() const {
- return new PlanValidatingCursorOp( _expectedIndexKey );
- }
- virtual boost::shared_ptr<Cursor> newCursor() const { return qp().newCursor(); }
- virtual long long nscanned() { return 0; }
- private:
- BSONObj _expectedIndexKey;
- };
-
- /** Test MultiCursor with hintIdElseNatural set to true. */
- class HintIdElseNaturalBase : public Base {
- public:
- virtual ~HintIdElseNaturalBase() {}
- void run() {
- client().insert( ns(), BSON( "a" << 1 << "b" << 1 ) );
- client().insert( ns(), BSON( "a" << 1 << "b" << 1 ) );
- client().ensureIndex( ns(), BSON( "a" << 1 ) );
-
- // Create a MultiCursor and validate its plans in the planValidator.
- boost::shared_ptr<MultiCursor::CursorOp> planValidator
- ( new PlanValidatingCursorOp( expectedPlanIndexKey() ) );
- MultiCursor cursor( ns(), BSON( "a" << 1 << "b" << 1 ), BSONObj(), planValidator,
- false, /* hintIdElseNatural */ true );
-
- // Validate the index key returned by the cursor iterates.
- do {
- ASSERT_EQUALS( expectedCursorIndexKey(), cursor.indexKeyPattern() );
- } while( cursor.advance() );
- }
- protected:
- virtual BSONObj expectedPlanIndexKey() const = 0;
- virtual BSONObj expectedCursorIndexKey() const { return expectedPlanIndexKey(); }
- };
-
- /** An _id hint is used when possible if hintIdElseNatural is specified. */
- class IdHint : public HintIdElseNaturalBase {
- virtual BSONObj expectedPlanIndexKey() const { return BSON( "_id" << 1 ); }
- };
-
- /**
- * An $natural hint is used when hintIdElseNatural is specified and no _id index is
- * available.
- */
- class NaturalHintFallback : public HintIdElseNaturalBase {
- public:
- NaturalHintFallback() {
- client().dropCollection( ns() );
- // Recreate the collection as capped, without an _id index.
- client().createCollection( ns(), 5000, true );
- }
- private:
- virtual BSONObj expectedPlanIndexKey() const { return BSON( "$natural" << 1 ); }
- virtual BSONObj expectedCursorIndexKey() const { return BSONObj(); }
- };
-
- } // namespace MultiCursorTests
-
- namespace QueryOptimizerCursorTests {
-
- class Base {
- public:
- Base() {
- dblock lk;
- Client::Context ctx( ns() );
- string err;
- userCreateNS( ns(), BSONObj(), err, false );
- dropCollection( ns() );
- }
- ~Base() {
- cc().curop()->reset();
- }
- protected:
- DBDirectClient _cli;
- static const char *ns() { return "unittests.QueryOptimizerTests"; }
- void setQueryOptimizerCursor( const BSONObj &query, const BSONObj &order = BSONObj() ) {
- _c = newQueryOptimizerCursor( ns(), query, order );
- if ( ok() && !mayReturnCurrent() ) {
- advance();
- }
- }
- bool ok() const { return _c->ok(); }
- /** Handles matching and deduping. */
- bool advance() {
- while( _c->advance() && !mayReturnCurrent() );
- return ok();
- }
- int itcount() {
- int ret = 0;
- while( ok() ) {
- ++ret;
- advance();
- }
- return ret;
- }
- BSONObj current() const { return _c->current(); }
- bool mayReturnCurrent() {
- return _c->matcher()->matchesCurrent( _c.get() ) && !_c->getsetdup( _c->currLoc() );
- }
- bool prepareToYield() const { return _c->prepareToYield(); }
- void recoverFromYield() {
- _c->recoverFromYield();
- if ( ok() && !mayReturnCurrent() ) {
- advance();
- }
- }
- boost::shared_ptr<Cursor> c() { return _c; }
- long long nscanned() const { return _c->nscanned(); }
- private:
- boost::shared_ptr<Cursor> _c;
- };
-
- /** No results for empty collection. */
- class Empty : public Base {
- public:
- void run() {
- dblock lk;
- Client::Context ctx( ns() );
- boost::shared_ptr<Cursor> c = newQueryOptimizerCursor( ns(), BSONObj() );
- ASSERT( !c->ok() );
- ASSERT_EXCEPTION( c->_current(), AssertionException );
- ASSERT_EXCEPTION( c->current(), AssertionException );
- ASSERT( c->currLoc().isNull() );
- ASSERT( !c->advance() );
- ASSERT_EXCEPTION( c->currKey(), AssertionException );
- ASSERT_EXCEPTION( c->getsetdup( DiskLoc() ), AssertionException );
- ASSERT_EXCEPTION( c->isMultiKey(), AssertionException );
- ASSERT_EXCEPTION( c->matcher(), AssertionException );
- }
- };
-
- /** Simple table scan. */
- class Unindexed : public Base {
- public:
- void run() {
- _cli.insert( ns(), BSON( "_id" << 1 ) );
- _cli.insert( ns(), BSON( "_id" << 2 ) );
-
- dblock lk;
- Client::Context ctx( ns() );
- setQueryOptimizerCursor( BSONObj() );
- ASSERT_EQUALS( 2, itcount() );
- }
- };
-
- /** Basic test with two indexes and deduping requirement. */
- class Basic : public Base {
- public:
- void run() {
- _cli.insert( ns(), BSON( "_id" << 1 << "a" << 2 ) );
- _cli.insert( ns(), BSON( "_id" << 2 << "a" << 1 ) );
- _cli.ensureIndex( ns(), BSON( "a" << 1 ) );
-
- dblock lk;
- Client::Context ctx( ns() );
- setQueryOptimizerCursor( BSON( "_id" << GT << 0 << "a" << GT << 0 ) );
- ASSERT( ok() );
- ASSERT_EQUALS( BSON( "_id" << 1 << "a" << 2 ), current() );
- ASSERT( advance() );
- ASSERT_EQUALS( BSON( "_id" << 2 << "a" << 1 ), current() );
- ASSERT( !advance() );
- ASSERT( !ok() );
- }
- };
-
- class NoMatch : public Base {
- public:
- void run() {
- _cli.ensureIndex( ns(), BSON( "a" << 1 ) );
-
- dblock lk;
- Client::Context ctx( ns() );
- setQueryOptimizerCursor( BSON( "_id" << GT << 5 << LT << 4 << "a" << GT << 0 ) );
- ASSERT( !ok() );
- }
- };
-
- /** Order of results indicates that interleaving is occurring. */
- class Interleaved : public Base {
- public:
- void run() {
- _cli.insert( ns(), BSON( "_id" << 1 << "a" << 2 ) );
- _cli.insert( ns(), BSON( "_id" << 3 << "a" << 1 ) );
- _cli.insert( ns(), BSON( "_id" << 2 << "a" << 2 ) );
- _cli.ensureIndex( ns(), BSON( "a" << 1 ) );
-
- dblock lk;
- Client::Context ctx( ns() );
- setQueryOptimizerCursor( BSON( "_id" << GT << 0 << "a" << GT << 0 ) );
- ASSERT( ok() );
- ASSERT_EQUALS( BSON( "_id" << 1 << "a" << 2 ), current() );
- ASSERT( advance() );
- ASSERT_EQUALS( BSON( "_id" << 3 << "a" << 1 ), current() );
- ASSERT( advance() );
- ASSERT_EQUALS( BSON( "_id" << 2 << "a" << 2 ), current() );
- ASSERT( !advance() );
- ASSERT( !ok() );
- }
- };
-
- /** Some values on each index do not match. */
- class NotMatch : public Base {
- public:
- void run() {
- _cli.insert( ns(), BSON( "_id" << 0 << "a" << 10 ) );
- _cli.insert( ns(), BSON( "_id" << 10 << "a" << 0 ) );
- _cli.insert( ns(), BSON( "_id" << 11 << "a" << 12 ) );
- _cli.insert( ns(), BSON( "_id" << 12 << "a" << 11 ) );
- _cli.ensureIndex( ns(), BSON( "a" << 1 ) );
-
- dblock lk;
- Client::Context ctx( ns() );
- setQueryOptimizerCursor( BSON( "_id" << GT << 5 << "a" << GT << 5 ) );
- ASSERT( ok() );
- ASSERT_EQUALS( BSON( "_id" << 11 << "a" << 12 ), current() );
- ASSERT( advance() );
- ASSERT_EQUALS( BSON( "_id" << 12 << "a" << 11 ), current() );
- ASSERT( !advance() );
- ASSERT( !ok() );
- }
- };
-
- /** After the first 101 matches for a plan, we stop interleaving the plans. */
- class StopInterleaving : public Base {
- public:
- void run() {
- for( int i = 0; i < 101; ++i ) {
- _cli.insert( ns(), BSON( "_id" << i << "a" << i ) );
- }
- for( int i = 101; i < 200; ++i ) {
- _cli.insert( ns(), BSON( "_id" << i << "a" << (301-i) ) );
- }
- _cli.ensureIndex( ns(), BSON( "a" << 1 ) );
-
- dblock lk;
- Client::Context ctx( ns() );
- setQueryOptimizerCursor( BSON( "_id" << GT << -1 << "a" << GT << -1 ) );
- for( int i = 0; i < 200; ++i ) {
- ASSERT( ok() );
- ASSERT_EQUALS( i, current().getIntField( "_id" ) );
- advance();
- }
- ASSERT( !advance() );
- ASSERT( !ok() );
- }
- };
-
- /** Test correct deduping with the takeover cursor. */
- class TakeoverWithDup : public Base {
- public:
- void run() {
- for( int i = 0; i < 101; ++i ) {
- _cli.insert( ns(), BSON( "_id" << i << "a" << i ) );
- }
- _cli.insert( ns(), BSON( "_id" << 500 << "a" << BSON_ARRAY( 0 << 300 ) ) );
- _cli.ensureIndex( ns(), BSON( "a" << 1 ) );
- dblock lk;
- Client::Context ctx( ns() );
- setQueryOptimizerCursor( BSON( "_id" << GT << -1 << "a" << GT << -1 ) );
- ASSERT_EQUALS( 102, itcount() );
- }
- };
-
- /** Test usage of matcher with takeover cursor. */
- class TakeoverWithNonMatches : public Base {
- public:
- void run() {
- for( int i = 0; i < 101; ++i ) {
- _cli.insert( ns(), BSON( "_id" << i << "a" << i ) );
- }
- _cli.insert( ns(), BSON( "_id" << 101 << "a" << 600 ) );
- _cli.ensureIndex( ns(), BSON( "a" << 1 ) );
- dblock lk;
- Client::Context ctx( ns() );
- setQueryOptimizerCursor( BSON( "_id" << GT << -1 << "a" << LT << 500 ) );
- ASSERT_EQUALS( 101, itcount() );
- }
- };
-
- /** Check deduping of dups within just the takeover cursor. */
- class TakeoverWithTakeoverDup : public Base {
- public:
- void run() {
- for( int i = 0; i < 101; ++i ) {
- _cli.insert( ns(), BSON( "_id" << i*2 << "a" << 0 ) );
- _cli.insert( ns(), BSON( "_id" << i*2+1 << "a" << 1 ) );
- }
- _cli.insert( ns(), BSON( "_id" << 202 << "a" << BSON_ARRAY( 2 << 3 ) ) );
- _cli.ensureIndex( ns(), BSON( "a" << 1 ) );
- dblock lk;
- Client::Context ctx( ns() );
- setQueryOptimizerCursor( BSON( "_id" << GT << -1 << "a" << GT << 0) );
- ASSERT_EQUALS( 102, itcount() );
- }
- };
-
- /** Basic test with $or query. */
- class BasicOr : public Base {
- public:
- void run() {
- _cli.insert( ns(), BSON( "_id" << 0 << "a" << 0 ) );
- _cli.insert( ns(), BSON( "_id" << 1 << "a" << 1 ) );
- _cli.ensureIndex( ns(), BSON( "a" << 1 ) );
- dblock lk;
- Client::Context ctx( ns() );
- setQueryOptimizerCursor( BSON( "$or" << BSON_ARRAY( BSON( "_id" << 0 ) << BSON( "a" << 1 ) ) ) );
- ASSERT_EQUALS( BSON( "_id" << 0 << "a" << 0 ), current() );
- ASSERT( advance() );
- ASSERT_EQUALS( BSON( "_id" << 1 << "a" << 1 ), current() );
- ASSERT( !advance() );
- }
- };
-
- /** $or first clause empty. */
- class OrFirstClauseEmpty : public Base {
- public:
- void run() {
- _cli.insert( ns(), BSON( "_id" << 0 << "a" << 1 ) );
- _cli.insert( ns(), BSON( "_id" << 1 << "a" << 1 ) );
- _cli.ensureIndex( ns(), BSON( "a" << 1 ) );
- dblock lk;
- Client::Context ctx( ns() );
- setQueryOptimizerCursor( BSON( "$or" << BSON_ARRAY( BSON( "_id" << -1 ) << BSON( "a" << 1 ) ) ) );
- ASSERT_EQUALS( BSON( "_id" << 0 << "a" << 1 ), current() );
- ASSERT( advance() );
- ASSERT_EQUALS( BSON( "_id" << 1 << "a" << 1 ), current() );
- ASSERT( !advance() );
- }
- };
-
- /** $or second clause empty. */
- class OrSecondClauseEmpty : public Base {
- public:
- void run() {
- _cli.insert( ns(), BSON( "_id" << 0 << "a" << 1 ) );
- _cli.insert( ns(), BSON( "_id" << 1 << "a" << 1 ) );
- _cli.ensureIndex( ns(), BSON( "a" << 1 ) );
- dblock lk;
- Client::Context ctx( ns() );
- setQueryOptimizerCursor( BSON( "$or" << BSON_ARRAY( BSON( "_id" << 0 ) << BSON( "_id" << -1 ) << BSON( "a" << 1 ) ) ) );
- ASSERT_EQUALS( BSON( "_id" << 0 << "a" << 1 ), current() );
- ASSERT( advance() );
- ASSERT_EQUALS( BSON( "_id" << 1 << "a" << 1 ), current() );
- ASSERT( !advance() );
- }
- };
-
- /** $or multiple clauses empty empty. */
- class OrMultipleClausesEmpty : public Base {
- public:
- void run() {
- _cli.insert( ns(), BSON( "_id" << 0 << "a" << 1 ) );
- _cli.insert( ns(), BSON( "_id" << 1 << "a" << 1 ) );
- _cli.ensureIndex( ns(), BSON( "a" << 1 ) );
- dblock lk;
- Client::Context ctx( ns() );
- setQueryOptimizerCursor( BSON( "$or" << BSON_ARRAY( BSON( "_id" << 2 ) << BSON( "_id" << 4 ) << BSON( "_id" << 0 ) << BSON( "_id" << -1 ) << BSON( "_id" << 6 ) << BSON( "a" << 1 ) << BSON( "_id" << 9 ) ) ) );
- ASSERT_EQUALS( BSON( "_id" << 0 << "a" << 1 ), current() );
- ASSERT( advance() );
- ASSERT_EQUALS( BSON( "_id" << 1 << "a" << 1 ), current() );
- ASSERT( !advance() );
- }
- };
-
- /** Check that takeover occurs at proper match count with $or clauses */
- class TakeoverCountOr : public Base {
- public:
- void run() {
- for( int i = 0; i < 60; ++i ) {
- _cli.insert( ns(), BSON( "_id" << i << "a" << 0 ) );
- }
- for( int i = 60; i < 120; ++i ) {
- _cli.insert( ns(), BSON( "_id" << i << "a" << 1 ) );
- }
- for( int i = 120; i < 150; ++i ) {
- _cli.insert( ns(), BSON( "_id" << i << "a" << (200-i) ) );
- }
- _cli.ensureIndex( ns(), BSON( "a" << 1 ) );
- dblock lk;
- Client::Context ctx( ns() );
- setQueryOptimizerCursor( BSON( "$or" << BSON_ARRAY( BSON( "a" << 0 ) << BSON( "a" << 1 ) << BSON( "_id" << GTE << 120 << "a" << GT << 1 ) ) ) );
- for( int i = 0; i < 120; ++i ) {
- ASSERT( ok() );
- advance();
- }
- // Expect to be scanning on _id index only.
- for( int i = 120; i < 150; ++i ) {
- ASSERT_EQUALS( i, current().getIntField( "_id" ) );
- advance();
- }
- ASSERT( !ok() );
- }
- };
-
- /** Takeover just at end of clause. */
- class TakeoverEndOfOrClause : public Base {
- public:
- void run() {
- for( int i = 0; i < 102; ++i ) {
- _cli.insert( ns(), BSON( "_id" << i ) );
- }
- dblock lk;
- Client::Context ctx( ns() );
- setQueryOptimizerCursor( BSON( "$or" << BSON_ARRAY( BSON( "_id" << LT << 101 ) << BSON( "_id" << 101 ) ) ) );
- for( int i = 0; i < 102; ++i ) {
- ASSERT_EQUALS( i, current().getIntField( "_id" ) );
- advance();
- }
- ASSERT( !ok() );
- }
- };
-
- class TakeoverBeforeEndOfOrClause : public Base {
- public:
- void run() {
- for( int i = 0; i < 101; ++i ) {
- _cli.insert( ns(), BSON( "_id" << i ) );
- }
- dblock lk;
- Client::Context ctx( ns() );
- setQueryOptimizerCursor( BSON( "$or" << BSON_ARRAY( BSON( "_id" << LT << 100 ) << BSON( "_id" << 100 ) ) ) );
- for( int i = 0; i < 101; ++i ) {
- ASSERT_EQUALS( i, current().getIntField( "_id" ) );
- advance();
- }
- ASSERT( !ok() );
- }
- };
-
- class TakeoverAfterEndOfOrClause : public Base {
- public:
- void run() {
- for( int i = 0; i < 103; ++i ) {
- _cli.insert( ns(), BSON( "_id" << i ) );
- }
- dblock lk;
- Client::Context ctx( ns() );
- setQueryOptimizerCursor( BSON( "$or" << BSON_ARRAY( BSON( "_id" << LT << 102 ) << BSON( "_id" << 102 ) ) ) );
- for( int i = 0; i < 103; ++i ) {
- ASSERT_EQUALS( i, current().getIntField( "_id" ) );
- advance();
- }
- ASSERT( !ok() );
- }
- };
-
- /** Test matching and deduping done manually by cursor client. */
- class ManualMatchingDeduping : public Base {
- public:
- void run() {
- _cli.insert( ns(), BSON( "_id" << 0 << "a" << 10 ) );
- _cli.insert( ns(), BSON( "_id" << 10 << "a" << 0 ) );
- _cli.insert( ns(), BSON( "_id" << 11 << "a" << 12 ) );
- _cli.insert( ns(), BSON( "_id" << 12 << "a" << 11 ) );
- _cli.ensureIndex( ns(), BSON( "a" << 1 ) );
-
- dblock lk;
- Client::Context ctx( ns() );
- boost::shared_ptr< Cursor > c = newQueryOptimizerCursor( ns(), BSON( "_id" << GT << 5 << "a" << GT << 5 ) );
- ASSERT( c->ok() );
-
- // _id 10 {_id:1}
- ASSERT_EQUALS( 10, c->current().getIntField( "_id" ) );
- ASSERT( !c->matcher()->matchesCurrent( c.get() ) );
- ASSERT( c->advance() );
-
- // _id 0 {a:1}
- ASSERT_EQUALS( 0, c->current().getIntField( "_id" ) );
- ASSERT( !c->matcher()->matchesCurrent( c.get() ) );
- ASSERT( c->advance() );
-
- // _id 0 {$natural:1}
- ASSERT_EQUALS( 0, c->current().getIntField( "_id" ) );
- ASSERT( !c->matcher()->matchesCurrent( c.get() ) );
- ASSERT( c->advance() );
-
- // _id 11 {_id:1}
- ASSERT_EQUALS( BSON( "_id" << 11 << "a" << 12 ), c->current() );
- ASSERT( c->matcher()->matchesCurrent( c.get() ) );
- ASSERT( !c->getsetdup( c->currLoc() ) );
- ASSERT( c->advance() );
-
- // _id 12 {a:1}
- ASSERT_EQUALS( BSON( "_id" << 12 << "a" << 11 ), c->current() );
- ASSERT( c->matcher()->matchesCurrent( c.get() ) );
- ASSERT( !c->getsetdup( c->currLoc() ) );
- ASSERT( c->advance() );
-
- // _id 10 {$natural:1}
- ASSERT_EQUALS( 10, c->current().getIntField( "_id" ) );
- ASSERT( !c->matcher()->matchesCurrent( c.get() ) );
- ASSERT( c->advance() );
-
- // _id 12 {_id:1}
- ASSERT_EQUALS( BSON( "_id" << 12 << "a" << 11 ), c->current() );
- ASSERT( c->matcher()->matchesCurrent( c.get() ) );
- ASSERT( c->getsetdup( c->currLoc() ) );
- ASSERT( c->advance() );
-
- // _id 11 {a:1}
- ASSERT_EQUALS( BSON( "_id" << 11 << "a" << 12 ), c->current() );
- ASSERT( c->matcher()->matchesCurrent( c.get() ) );
- ASSERT( c->getsetdup( c->currLoc() ) );
- ASSERT( c->advance() );
-
- // _id 11 {$natural:1}
- ASSERT_EQUALS( 11, c->current().getIntField( "_id" ) );
- ASSERT( c->matcher()->matchesCurrent( c.get() ) );
- ASSERT( c->getsetdup( c->currLoc() ) );
-
- // {_id:1} scan is complete.
- ASSERT( !c->advance() );
- ASSERT( !c->ok() );
-
- // Scan the results again - this time the winning plan has been
- // recorded.
- c = newQueryOptimizerCursor( ns(), BSON( "_id" << GT << 5 << "a" << GT << 5 ) );
- ASSERT( c->ok() );
-
- // _id 10 {_id:1}
- ASSERT_EQUALS( 10, c->current().getIntField( "_id" ) );
- ASSERT( !c->matcher()->matchesCurrent( c.get() ) );
- ASSERT( c->advance() );
-
- // _id 11 {_id:1}
- ASSERT_EQUALS( BSON( "_id" << 11 << "a" << 12 ), c->current() );
- ASSERT( c->matcher()->matchesCurrent( c.get() ) );
- ASSERT( !c->getsetdup( c->currLoc() ) );
- ASSERT( c->advance() );
-
- // _id 12 {_id:1}
- ASSERT_EQUALS( BSON( "_id" << 12 << "a" << 11 ), c->current() );
- ASSERT( c->matcher()->matchesCurrent( c.get() ) );
- ASSERT( !c->getsetdup( c->currLoc() ) );
-
- // {_id:1} scan complete
- ASSERT( !c->advance() );
- ASSERT( !c->ok() );
- }
- };
-
- /** Curr key must be correct for currLoc for correct matching. */
- class ManualMatchingUsingCurrKey : public Base {
- public:
- void run() {
- _cli.insert( ns(), BSON( "_id" << "a" ) );
- _cli.insert( ns(), BSON( "_id" << "b" ) );
- _cli.insert( ns(), BSON( "_id" << "ba" ) );
-
- dblock lk;
- Client::Context ctx( ns() );
- boost::shared_ptr< Cursor > c = newQueryOptimizerCursor( ns(), fromjson( "{_id:/a/}" ) );
- ASSERT( c->ok() );
- // "a"
- ASSERT( c->matcher()->matchesCurrent( c.get() ) );
- ASSERT( !c->getsetdup( c->currLoc() ) );
- ASSERT( c->advance() );
- ASSERT( c->ok() );
-
- // "b"
- ASSERT( !c->matcher()->matchesCurrent( c.get() ) );
- ASSERT( c->advance() );
- ASSERT( c->ok() );
-
- // "ba"
- ASSERT( c->matcher()->matchesCurrent( c.get() ) );
- ASSERT( !c->getsetdup( c->currLoc() ) );
- ASSERT( !c->advance() );
- }
- };
-
- /** Test matching and deduping done manually by cursor client. */
- class ManualMatchingDedupingTakeover : public Base {
- public:
- void run() {
- for( int i = 0; i < 150; ++i ) {
- _cli.insert( ns(), BSON( "_id" << i << "a" << 0 ) );
- }
- _cli.insert( ns(), BSON( "_id" << 300 << "a" << 1 ) );
- _cli.ensureIndex( ns(), BSON( "a" << 1 ) );
-
- dblock lk;
- Client::Context ctx( ns() );
- boost::shared_ptr< Cursor > c = newQueryOptimizerCursor( ns(), BSON( "$or" << BSON_ARRAY( BSON( "_id" << LT << 300 ) << BSON( "a" << 1 ) ) ) );
- for( int i = 0; i < 151; ++i ) {
- ASSERT( c->ok() );
- ASSERT( c->matcher()->matchesCurrent( c.get() ) );
- ASSERT( !c->getsetdup( c->currLoc() ) );
- c->advance();
- }
- ASSERT( !c->ok() );
- }
- };
-
- /** Test single key matching bounds. */
- class Singlekey : public Base {
- public:
- void run() {
- _cli.insert( ns(), BSON( "a" << "10" ) );
- _cli.ensureIndex( ns(), BSON( "a" << 1 ) );
-
- dblock lk;
- Client::Context ctx( ns() );
- boost::shared_ptr< Cursor > c = newQueryOptimizerCursor( ns(), BSON( "a" << GT << 1 << LT << 5 ) );
- // Two sided bounds work.
- ASSERT( !c->ok() );
- }
- };
-
- /** Test multi key matching bounds. */
- class Multikey : public Base {
- public:
- void run() {
- _cli.insert( ns(), BSON( "a" << BSON_ARRAY( 1 << 10 ) ) );
- _cli.ensureIndex( ns(), BSON( "a" << 1 ) );
-
- dblock lk;
- Client::Context ctx( ns() );
- setQueryOptimizerCursor( BSON( "a" << GT << 5 << LT << 3 ) );
- // Multi key bounds work.
- ASSERT( ok() );
- }
- };
-
- /** Add other plans when the recorded one is doing more poorly than expected. */
- class AddOtherPlans : public Base {
- public:
- void run() {
- _cli.insert( ns(), BSON( "_id" << 0 << "a" << 0 << "b" << 0 ) );
- _cli.insert( ns(), BSON( "_id" << 1 << "a" << 1 << "b" << 0 ) );
- for( int i = 100; i < 150; ++i ) {
- _cli.insert( ns(), BSON( "_id" << i << "a" << 100 << "b" << i ) );
- }
- _cli.ensureIndex( ns(), BSON( "a" << 1 ) );
- _cli.ensureIndex( ns(), BSON( "b" << 1 ) );
-
- dblock lk;
- Client::Context ctx( ns() );
- boost::shared_ptr<Cursor> c = newQueryOptimizerCursor( ns(), BSON( "a" << 0 << "b" << 0 ) );
-
- ASSERT_EQUALS( BSON( "_id" << 0 << "a" << 0 << "b" << 0 ), c->current() );
- ASSERT( c->advance() );
- ASSERT_EQUALS( BSON( "_id" << 0 << "a" << 0 << "b" << 0 ), c->current() );
- ASSERT( c->advance() );
- // $natrual plan
- ASSERT_EQUALS( BSON( "_id" << 0 << "a" << 0 << "b" << 0 ), c->current() );
- ASSERT( !c->advance() );
-
- c = newQueryOptimizerCursor( ns(), BSON( "a" << 100 << "b" << 149 ) );
- // Try {a:1}, which was successful previously.
- for( int i = 0; i < 11; ++i ) {
- ASSERT( 149 != c->current().getIntField( "b" ) );
- ASSERT( c->advance() );
- }
- // Now try {b:1} plan.
- ASSERT_EQUALS( 149, c->current().getIntField( "b" ) );
- ASSERT( c->advance() );
- // {b:1} plan finished.
- ASSERT( !c->advance() );
- }
- };
-
- /** Check $or clause range elimination. */
- class OrRangeElimination : public Base {
- public:
- void run() {
- _cli.insert( ns(), BSON( "_id" << 1 ) );
-
- dblock lk;
- Client::Context ctx( ns() );
- boost::shared_ptr<Cursor> c = newQueryOptimizerCursor( ns(), BSON( "$or" << BSON_ARRAY( BSON( "_id" << GT << 0 ) << BSON( "_id" << 1 ) ) ) );
- ASSERT( c->ok() );
- ASSERT( !c->advance() );
- }
- };
-
- /** Check $or match deduping - in takeover cursor. */
- class OrDedup : public Base {
- public:
- void run() {
- for( int i = 0; i < 150; ++i ) {
- _cli.insert( ns(), BSON( "_id" << i << "a" << i ) );
- }
- _cli.ensureIndex( ns(), BSON( "a" << 1 ) );
-
- dblock lk;
- Client::Context ctx( ns() );
- boost::shared_ptr<Cursor> c = newQueryOptimizerCursor( ns(), BSON( "$or" << BSON_ARRAY( BSON( "_id" << LT << 140 ) << BSON( "_id" << 145 ) << BSON( "a" << 145 ) ) ) );
-
- while( c->current().getIntField( "_id" ) < 140 ) {
- ASSERT( c->advance() );
- }
- // Match from second $or clause.
- ASSERT_EQUALS( 145, c->current().getIntField( "_id" ) );
- ASSERT( c->matcher()->matchesCurrent( c.get() ) );
- ASSERT( c->advance() );
- // Match from third $or clause.
- ASSERT_EQUALS( 145, c->current().getIntField( "_id" ) );
- // $or deduping is handled by the matcher.
- ASSERT( !c->matcher()->matchesCurrent( c.get() ) );
- ASSERT( !c->advance() );
- }
- };
-
- /** Standard dups with a multikey cursor. */
- class EarlyDups : public Base {
- public:
- void run() {
- _cli.insert( ns(), BSON( "a" << BSON_ARRAY( 0 << 1 << 200 ) ) );
- for( int i = 2; i < 150; ++i ) {
- _cli.insert( ns(), BSON( "a" << i ) );
- }
- _cli.ensureIndex( ns(), BSON( "a" << 1 ) );
-
- dblock lk;
- Client::Context ctx( ns() );
- setQueryOptimizerCursor( BSON( "a" << GT << -1 ) );
- ASSERT_EQUALS( 149, itcount() );
- }
- };
-
- /** Pop or clause in takeover cursor. */
- class OrPopInTakeover : public Base {
- public:
- void run() {
- for( int i = 0; i < 150; ++i ) {
- _cli.insert( ns(), BSON( "_id" << i ) );
- }
-
- dblock lk;
- Client::Context ctx( ns() );
- boost::shared_ptr<Cursor> c = newQueryOptimizerCursor( ns(), BSON( "$or" << BSON_ARRAY( BSON( "_id" << LTE << 147 ) << BSON( "_id" << 148 ) << BSON( "_id" << 149 ) ) ) );
- for( int i = 0; i < 150; ++i ) {
- ASSERT( c->ok() );
- ASSERT_EQUALS( i, c->current().getIntField( "_id" ) );
- c->advance();
- }
- ASSERT( !c->ok() );
- }
- };
-
- /** Or clause iteration abandoned once full collection scan is performed. */
- class OrCollectionScanAbort : public Base {
- public:
- void run() {
- _cli.insert( ns(), BSON( "_id" << 0 << "a" << BSON_ARRAY( 1 << 2 << 3 << 4 << 5 ) << "b" << 4 ) );
- _cli.insert( ns(), BSON( "_id" << 1 << "a" << BSON_ARRAY( 6 << 7 << 8 << 9 << 10 ) << "b" << 4 ) );
- _cli.ensureIndex( ns(), BSON( "a" << 1 ) );
-
- dblock lk;
- Client::Context ctx( ns() );
- boost::shared_ptr<Cursor> c = newQueryOptimizerCursor( ns(), BSON( "$or" << BSON_ARRAY( BSON( "a" << LT << 6 << "b" << 4 ) << BSON( "a" << GTE << 6 << "b" << 4 ) ) ) );
-
- ASSERT( c->ok() );
-
- // _id 0 on {a:1}
- ASSERT_EQUALS( 0, c->current().getIntField( "_id" ) );
- ASSERT( c->matcher()->matchesCurrent( c.get() ) );
- ASSERT( !c->getsetdup( c->currLoc() ) );
- c->advance();
-
- // _id 0 on {$natural:1}
- ASSERT_EQUALS( 0, c->current().getIntField( "_id" ) );
- ASSERT( c->matcher()->matchesCurrent( c.get() ) );
- ASSERT( c->getsetdup( c->currLoc() ) );
- c->advance();
-
- // _id 0 on {a:1}
- ASSERT_EQUALS( 0, c->current().getIntField( "_id" ) );
- ASSERT( c->matcher()->matchesCurrent( c.get() ) );
- ASSERT( c->getsetdup( c->currLoc() ) );
- c->advance();
-
- // _id 1 on {$natural:1}
- ASSERT_EQUALS( 1, c->current().getIntField( "_id" ) );
- ASSERT( c->matcher()->matchesCurrent( c.get() ) );
- ASSERT( !c->getsetdup( c->currLoc() ) );
- c->advance();
-
- // _id 0 on {a:1}
- ASSERT_EQUALS( 0, c->current().getIntField( "_id" ) );
- ASSERT( c->matcher()->matchesCurrent( c.get() ) );
- ASSERT( c->getsetdup( c->currLoc() ) );
- c->advance();
-
- // {$natural:1} finished
- ASSERT( !c->ok() );
- }
- };
-
- /** Simple geo query. */
- class Geo : public Base {
- public:
- void run() {
- _cli.insert( ns(), BSON( "_id" << 0 << "loc" << BSON( "lon" << 30 << "lat" << 30 ) ) );
- _cli.insert( ns(), BSON( "_id" << 1 << "loc" << BSON( "lon" << 31 << "lat" << 31 ) ) );
- _cli.ensureIndex( ns(), BSON( "loc" << "2d" ) );
-
- dblock lk;
- Client::Context ctx( ns() );
- setQueryOptimizerCursor( BSON( "loc" << BSON( "$near" << BSON_ARRAY( 30 << 30 ) ) ) );
- ASSERT( ok() );
- ASSERT_EQUALS( 0, current().getIntField( "_id" ) );
- ASSERT( advance() );
- ASSERT_EQUALS( 1, current().getIntField( "_id" ) );
- ASSERT( !advance() );
- ASSERT( !ok() );
- }
- };
-
- /** Yield cursor and delete current entry, then continue iteration. */
- class YieldNoOp : public Base {
- public:
- void run() {
- _cli.insert( ns(), BSON( "_id" << 1 ) );
- _cli.insert( ns(), BSON( "_id" << 2 ) );
-
- {
- dblock lk;
- Client::Context ctx( ns() );
- setQueryOptimizerCursor( BSON( "_id" << GT << 0 ) );
- ASSERT_EQUALS( 1, current().getIntField( "_id" ) );
- ASSERT( prepareToYield() );
- }
-
- {
- dblock lk;
- Client::Context ctx( ns() );
- recoverFromYield();
- ASSERT( ok() );
- ASSERT_EQUALS( 2, current().getIntField( "_id" ) );
- ASSERT( !advance() );
- ASSERT( !ok() );
- ASSERT( prepareToYield() );
- recoverFromYield();
- }
- }
- };
-
- /** Yield cursor and delete current entry. */
- class YieldDelete : public Base {
- public:
- void run() {
- _cli.insert( ns(), BSON( "_id" << 1 ) );
-
- {
- dblock lk;
- Client::Context ctx( ns() );
- setQueryOptimizerCursor( BSON( "_id" << 1 ) );
- ASSERT_EQUALS( 1, current().getIntField( "_id" ) );
- ASSERT( prepareToYield() );
- }
-
- _cli.remove( ns(), BSON( "_id" << 1 ) );
-
- {
- dblock lk;
- Client::Context ctx( ns() );
- recoverFromYield();
- ASSERT( !ok() );
- ASSERT( !advance() );
- }
- }
- };
-
- /** Yield cursor and delete current entry, then continue iteration. */
- class YieldDeleteContinue : public Base {
- public:
- void run() {
- _cli.insert( ns(), BSON( "_id" << 1 ) );
- _cli.insert( ns(), BSON( "_id" << 2 ) );
-
- {
- dblock lk;
- Client::Context ctx( ns() );
- setQueryOptimizerCursor( BSON( "_id" << GT << 0 ) );
- ASSERT_EQUALS( 1, current().getIntField( "_id" ) );
- ASSERT( prepareToYield() );
- }
-
- _cli.remove( ns(), BSON( "_id" << 1 ) );
-
- {
- dblock lk;
- Client::Context ctx( ns() );
- recoverFromYield();
- ASSERT( ok() );
- ASSERT_EQUALS( 2, current().getIntField( "_id" ) );
- ASSERT( !advance() );
- ASSERT( !ok() );
- }
- }
- };
-
- /** Yield cursor and delete current entry, then continue iteration. */
- class YieldDeleteContinueFurther : public Base {
- public:
- void run() {
- _cli.insert( ns(), BSON( "_id" << 1 ) );
- _cli.insert( ns(), BSON( "_id" << 2 ) );
- _cli.insert( ns(), BSON( "_id" << 3 ) );
-
- {
- dblock lk;
- Client::Context ctx( ns() );
- setQueryOptimizerCursor( BSON( "_id" << GT << 0 ) );
- ASSERT_EQUALS( 1, current().getIntField( "_id" ) );
- ASSERT( prepareToYield() );
- }
-
- _cli.remove( ns(), BSON( "_id" << 1 ) );
-
- {
- dblock lk;
- Client::Context ctx( ns() );
- recoverFromYield();
- ASSERT( ok() );
- ASSERT_EQUALS( 2, current().getIntField( "_id" ) );
- ASSERT( advance() );
- ASSERT_EQUALS( 3, current().getIntField( "_id" ) );
- ASSERT( !advance() );
- ASSERT( !ok() );
- }
- }
- };
-
- /** Yield and update current. */
- class YieldUpdate : public Base {
- public:
- void run() {
- _cli.insert( ns(), BSON( "a" << 1 ) );
- _cli.insert( ns(), BSON( "a" << 2 ) );
- _cli.ensureIndex( ns(), BSON( "a" << 1 ) );
-
- {
- dblock lk;
- Client::Context ctx( ns() );
- setQueryOptimizerCursor( BSON( "a" << GT << 0 ) );
- ASSERT_EQUALS( 1, current().getIntField( "a" ) );
- ASSERT( prepareToYield() );
- }
-
- _cli.update( ns(), BSON( "a" << 1 ), BSON( "$set" << BSON( "a" << 3 ) ) );
-
- {
- dblock lk;
- Client::Context ctx( ns() );
- recoverFromYield();
- ASSERT( ok() );
- ASSERT_EQUALS( 2, current().getIntField( "a" ) );
- ASSERT( !advance() );
- ASSERT( !ok() );
- }
- }
- };
-
- /** Yield and drop collection. */
- class YieldDrop : public Base {
- public:
- void run() {
- _cli.insert( ns(), BSON( "_id" << 1 ) );
- _cli.insert( ns(), BSON( "_id" << 2 ) );
-
- {
- dblock lk;
- Client::Context ctx( ns() );
- setQueryOptimizerCursor( BSON( "_id" << GT << 0 ) );
- ASSERT_EQUALS( 1, current().getIntField( "_id" ) );
- ASSERT( prepareToYield() );
- }
-
- _cli.dropCollection( ns() );
-
- {
- dblock lk;
- Client::Context ctx( ns() );
- recoverFromYield();
- ASSERT( !ok() );
- }
- }
- };
-
- /** Yield and drop collection with $or query. */
- class YieldDropOr : public Base {
- public:
- void run() {
- _cli.insert( ns(), BSON( "_id" << 1 ) );
- _cli.insert( ns(), BSON( "_id" << 2 ) );
-
- {
- dblock lk;
- Client::Context ctx( ns() );
- setQueryOptimizerCursor( BSON( "$or" << BSON_ARRAY( BSON( "_id" << 1 ) << BSON( "_id" << 2 ) ) ) );
- ASSERT_EQUALS( 1, current().getIntField( "_id" ) );
- ASSERT( prepareToYield() );
- }
-
- _cli.dropCollection( ns() );
-
- {
- dblock lk;
- Client::Context ctx( ns() );
- ASSERT_EXCEPTION( recoverFromYield(), MsgAssertionException );
- ASSERT( !ok() );
- }
- }
- };
-
- /** Yield and remove document with $or query. */
- class YieldRemoveOr : public Base {
- public:
- void run() {
- _cli.insert( ns(), BSON( "_id" << 1 ) );
- _cli.insert( ns(), BSON( "_id" << 2 ) );
-
- {
- dblock lk;
- Client::Context ctx( ns() );
- setQueryOptimizerCursor( BSON( "$or" << BSON_ARRAY( BSON( "_id" << 1 ) << BSON( "_id" << 2 ) ) ) );
- ASSERT_EQUALS( 1, current().getIntField( "_id" ) );
- ASSERT( prepareToYield() );
- }
-
- _cli.remove( ns(), BSON( "_id" << 1 ) );
-
- {
- dblock lk;
- Client::Context ctx( ns() );
- recoverFromYield();
- ASSERT( ok() );
- ASSERT_EQUALS( 2, current().getIntField( "_id" ) );
- }
- }
- };
-
- /** Yield and overwrite current in capped collection. */
- class YieldCappedOverwrite : public Base {
- public:
- void run() {
- _cli.createCollection( ns(), 1000, true );
- _cli.insert( ns(), BSON( "x" << 1 ) );
-
- {
- dblock lk;
- Client::Context ctx( ns() );
- setQueryOptimizerCursor( BSON( "x" << GT << 0 ) );
- ASSERT_EQUALS( 1, current().getIntField( "x" ) );
- ASSERT( prepareToYield() );
- }
-
- int x = 2;
- while( _cli.count( ns(), BSON( "x" << 1 ) ) > 0 ) {
- _cli.insert( ns(), BSON( "x" << x++ ) );
- }
-
- {
- dblock lk;
- Client::Context ctx( ns() );
- ASSERT_EXCEPTION( recoverFromYield(), MsgAssertionException );
- ASSERT( !ok() );
- }
- }
- };
-
- /** Yield and drop unrelated index - see SERVER-2454. */
- class YieldDropIndex : public Base {
- public:
- void run() {
- _cli.insert( ns(), BSON( "_id" << 1 ) );
- _cli.ensureIndex( ns(), BSON( "a" << 1 ) );
-
- {
- dblock lk;
- Client::Context ctx( ns() );
- setQueryOptimizerCursor( BSON( "_id" << 1 ) );
- ASSERT_EQUALS( 1, current().getIntField( "_id" ) );
- ASSERT( prepareToYield() );
- }
-
- _cli.dropIndex( ns(), BSON( "a" << 1 ) );
-
- {
- dblock lk;
- Client::Context ctx( ns() );
- recoverFromYield();
- ASSERT( !ok() );
- }
- }
- };
-
- /** Yielding with multiple plans active. */
- class YieldMultiplePlansNoOp : public Base {
- public:
- void run() {
- _cli.insert( ns(), BSON( "_id" << 1 << "a" << 2 ) );
- _cli.insert( ns(), BSON( "_id" << 2 << "a" << 1 ) );
- _cli.ensureIndex( ns(), BSON( "a" << 1 ) );
-
- {
- dblock lk;
- Client::Context ctx( ns() );
- setQueryOptimizerCursor( BSON( "_id" << GT << 0 << "a" << GT << 0 ) );
- ASSERT_EQUALS( 1, current().getIntField( "_id" ) );
- ASSERT( prepareToYield() );
- }
-
- {
- dblock lk;
- Client::Context ctx( ns() );
- recoverFromYield();
- ASSERT( ok() );
- ASSERT_EQUALS( 2, current().getIntField( "_id" ) );
- ASSERT( !advance() );
- ASSERT( !ok() );
- }
- }
- };
-
- /** Yielding with advance and multiple plans active. */
- class YieldMultiplePlansAdvanceNoOp : public Base {
- public:
- void run() {
- _cli.insert( ns(), BSON( "_id" << 1 << "a" << 2 ) );
- _cli.insert( ns(), BSON( "_id" << 2 << "a" << 1 ) );
- _cli.insert( ns(), BSON( "_id" << 3 << "a" << 3 ) );
- _cli.ensureIndex( ns(), BSON( "a" << 1 ) );
-
- {
- dblock lk;
- Client::Context ctx( ns() );
- setQueryOptimizerCursor( BSON( "_id" << GT << 0 << "a" << GT << 0 ) );
- ASSERT_EQUALS( 1, current().getIntField( "_id" ) );
- advance();
- ASSERT_EQUALS( 2, current().getIntField( "_id" ) );
- ASSERT( prepareToYield() );
- }
-
- {
- dblock lk;
- Client::Context ctx( ns() );
- recoverFromYield();
- ASSERT( ok() );
- ASSERT_EQUALS( 3, current().getIntField( "_id" ) );
- ASSERT( !advance() );
- ASSERT( !ok() );
- }
- }
- };
-
- /** Yielding with delete and multiple plans active. */
- class YieldMultiplePlansDelete : public Base {
- public:
- void run() {
- _cli.insert( ns(), BSON( "_id" << 1 << "a" << 2 ) );
- _cli.insert( ns(), BSON( "_id" << 2 << "a" << 1 ) );
- _cli.insert( ns(), BSON( "_id" << 3 << "a" << 4 ) );
- _cli.insert( ns(), BSON( "_id" << 4 << "a" << 3 ) );
- _cli.ensureIndex( ns(), BSON( "a" << 1 ) );
-
- {
- dblock lk;
- Client::Context ctx( ns() );
- setQueryOptimizerCursor( BSON( "_id" << GT << 0 << "a" << GT << 0 ) );
- ASSERT_EQUALS( 1, current().getIntField( "_id" ) );
- advance();
- ASSERT_EQUALS( 2, current().getIntField( "_id" ) );
- ASSERT( prepareToYield() );
- }
-
- _cli.remove( ns(), BSON( "_id" << 2 ) );
-
- {
- dblock lk;
- Client::Context ctx( ns() );
- c()->recoverFromYield();
- ASSERT( ok() );
- // index {a:1} active during yield
- ASSERT_EQUALS( 1, current().getIntField( "_id" ) );
- ASSERT( advance() );
- ASSERT_EQUALS( 3, current().getIntField( "_id" ) );
- ASSERT( advance() );
- ASSERT_EQUALS( 4, current().getIntField( "_id" ) );
- ASSERT( !advance() );
- ASSERT( !ok() );
- }
- }
- };
-
- /** Yielding with delete, multiple plans active, and $or clause. */
- class YieldMultiplePlansDeleteOr : public Base {
- public:
- void run() {
- _cli.insert( ns(), BSON( "_id" << 1 << "a" << 2 ) );
- _cli.insert( ns(), BSON( "_id" << 2 << "a" << 1 ) );
- _cli.ensureIndex( ns(), BSON( "a" << 1 ) );
-
- {
- dblock lk;
- Client::Context ctx( ns() );
- setQueryOptimizerCursor( BSON( "$or" << BSON_ARRAY( BSON( "_id" << 1 << "a" << 2 ) << BSON( "_id" << 2 << "a" << 1 ) ) ) );
- ASSERT_EQUALS( 1, current().getIntField( "_id" ) );
- ASSERT( prepareToYield() );
- }
-
- _cli.remove( ns(), BSON( "_id" << 1 ) );
-
- {
- dblock lk;
- Client::Context ctx( ns() );
- c()->recoverFromYield();
- ASSERT( ok() );
- ASSERT_EQUALS( 2, current().getIntField( "_id" ) );
- ASSERT( !advance() );
- ASSERT( !ok() );
- }
- }
- };
-
- /** Yielding with delete, multiple plans active with advancement to the second, and $or clause. */
- class YieldMultiplePlansDeleteOrAdvance : public Base {
- public:
- void run() {
- _cli.insert( ns(), BSON( "_id" << 1 << "a" << 2 ) );
- _cli.insert( ns(), BSON( "_id" << 2 << "a" << 1 ) );
- _cli.ensureIndex( ns(), BSON( "a" << 1 ) );
-
- {
- dblock lk;
- Client::Context ctx( ns() );
- setQueryOptimizerCursor( BSON( "$or" << BSON_ARRAY( BSON( "_id" << 1 << "a" << 2 ) << BSON( "_id" << 2 << "a" << 1 ) ) ) );
- ASSERT_EQUALS( 1, current().getIntField( "_id" ) );
- ASSERT( prepareToYield() );
- c()->advance();
- ASSERT_EQUALS( 1, current().getIntField( "_id" ) );
- }
-
- _cli.remove( ns(), BSON( "_id" << 1 ) );
-
- {
- dblock lk;
- Client::Context ctx( ns() );
- c()->recoverFromYield();
- ASSERT( ok() );
- ASSERT_EQUALS( 2, current().getIntField( "_id" ) );
- ASSERT( !advance() );
- ASSERT( !ok() );
- }
- }
- };
-
- /** Yielding with multiple plans and capped overwrite. */
- class YieldMultiplePlansCappedOverwrite : public Base {
- public:
- void run() {
- _cli.createCollection( ns(), 1000, true );
- _cli.insert( ns(), BSON( "_id" << 1 << "a" << 1 ) );
- _cli.ensureIndex( ns(), BSON( "_id" << 1 ) );
-
- {
- dblock lk;
- Client::Context ctx( ns() );
- setQueryOptimizerCursor( BSON( "_id" << GT << 0 << "a" << GT << 0 ) );
- ASSERT_EQUALS( 1, current().getIntField( "_id" ) );
- ASSERT( prepareToYield() );
- }
-
- int i = 1;
- while( _cli.count( ns(), BSON( "_id" << 1 ) ) > 0 ) {
- ++i;
- _cli.insert( ns(), BSON( "_id" << i << "a" << i ) );
- }
-
- {
- dblock lk;
- Client::Context ctx( ns() );
- recoverFromYield();
- ASSERT( ok() );
- // {$natural:1} plan does not recover, {_id:1} plan does.
- ASSERT( 1 < current().getIntField( "_id" ) );
- }
- }
- };
-
- /**
- * Yielding with multiple plans and capped overwrite with unrecoverable cursor
- * active at time of yield.
- */
- class YieldMultiplePlansCappedOverwriteManual : public Base {
- public:
- void run() {
- _cli.createCollection( ns(), 1000, true );
- _cli.insert( ns(), BSON( "a" << 1 << "b" << 1 ) );
- _cli.ensureIndex( ns(), BSON( "a" << 1 ) );
-
- boost::shared_ptr<Cursor> c;
- {
- dblock lk;
- Client::Context ctx( ns() );
- c = newQueryOptimizerCursor( ns(), BSON( "a" << GT << 0 << "b" << GT << 0 ) );
- ASSERT_EQUALS( 1, c->current().getIntField( "a" ) );
- ASSERT( !c->getsetdup( c->currLoc() ) );
- c->advance();
- ASSERT_EQUALS( 1, c->current().getIntField( "a" ) );
- ASSERT( c->getsetdup( c->currLoc() ) );
- ASSERT( c->prepareToYield() );
- }
-
- int i = 1;
- while( _cli.count( ns(), BSON( "a" << 1 ) ) > 0 ) {
- ++i;
- _cli.insert( ns(), BSON( "a" << i << "b" << i ) );
- }
-
- {
- dblock lk;
- Client::Context ctx( ns() );
- c->recoverFromYield();
- ASSERT( c->ok() );
- // {$natural:1} plan does not recover, {_id:1} plan does.
- ASSERT( 1 < c->current().getIntField( "a" ) );
- }
- }
- };
-
- /**
- * Yielding with multiple plans and capped overwrite with unrecoverable cursor
- * inctive at time of yield.
- */
- class YieldMultiplePlansCappedOverwriteManual2 : public Base {
- public:
- void run() {
- _cli.createCollection( ns(), 1000, true );
- _cli.insert( ns(), BSON( "_id" << 1 << "a" << 1 ) );
- _cli.ensureIndex( ns(), BSON( "_id" << 1 ) );
-
- boost::shared_ptr<Cursor> c;
- {
- dblock lk;
- Client::Context ctx( ns() );
- c = newQueryOptimizerCursor( ns(), BSON( "_id" << GT << 0 << "a" << GT << 0 ) );
- ASSERT_EQUALS( 1, c->current().getIntField( "_id" ) );
- ASSERT( !c->getsetdup( c->currLoc() ) );
- ASSERT( c->prepareToYield() );
- }
-
- int n = 1;
- while( _cli.count( ns(), BSON( "_id" << 1 ) ) > 0 ) {
- ++n;
- _cli.insert( ns(), BSON( "_id" << n << "a" << n ) );
- }
-
- {
- dblock lk;
- Client::Context ctx( ns() );
- c->recoverFromYield();
- ASSERT( c->ok() );
- // {$natural:1} plan does not recover, {_id:1} plan does.
- ASSERT( 1 < c->current().getIntField( "_id" ) );
- ASSERT( !c->getsetdup( c->currLoc() ) );
- int i = c->current().getIntField( "_id" );
- ASSERT( c->advance() );
- ASSERT( c->getsetdup( c->currLoc() ) );
- while( i < n ) {
- ASSERT( c->advance() );
- ++i;
- ASSERT_EQUALS( i, c->current().getIntField( "_id" ) );
- }
- }
- }
- };
-
- /** Try and fail to yield a geo query. */
- class TryYieldGeo : public Base {
- public:
- void run() {
- _cli.insert( ns(), BSON( "_id" << 0 << "loc" << BSON( "lon" << 30 << "lat" << 30 ) ) );
- _cli.ensureIndex( ns(), BSON( "loc" << "2d" ) );
-
- dblock lk;
- Client::Context ctx( ns() );
- setQueryOptimizerCursor( BSON( "loc" << BSON( "$near" << BSON_ARRAY( 50 << 50 ) ) ) );
- ASSERT( ok() );
- ASSERT_EQUALS( 0, current().getIntField( "_id" ) );
- ASSERT( !prepareToYield() );
- ASSERT( ok() );
- ASSERT_EQUALS( 0, current().getIntField( "_id" ) );
- ASSERT( !advance() );
- ASSERT( !ok() );
- }
- };
-
- /** Yield with takeover cursor. */
- class YieldTakeover : public Base {
- public:
- void run() {
- for( int i = 0; i < 150; ++i ) {
- _cli.insert( ns(), BSON( "_id" << i << "a" << i ) );
- }
- _cli.ensureIndex( ns(), BSON( "a" << 1 ) );
-
- {
- dblock lk;
- Client::Context ctx( ns() );
- setQueryOptimizerCursor( BSON( "_id" << GTE << 0 << "a" << GTE << 0 ) );
- for( int i = 0; i < 120; ++i ) {
- ASSERT( advance() );
- }
- ASSERT( ok() );
- ASSERT_EQUALS( 120, current().getIntField( "_id" ) );
- ASSERT( prepareToYield() );
- }
-
- _cli.remove( ns(), BSON( "_id" << 120 ) );
-
- {
- dblock lk;
- Client::Context ctx( ns() );
- recoverFromYield();
- ASSERT( ok() );
- ASSERT_EQUALS( 121, current().getIntField( "_id" ) );
- ASSERT( advance() );
- ASSERT_EQUALS( 122, current().getIntField( "_id" ) );
- }
- }
- };
-
- /** Yield with BacicCursor takeover cursor. */
- class YieldTakeoverBasic : public Base {
- public:
- void run() {
- for( int i = 0; i < 150; ++i ) {
- _cli.insert( ns(), BSON( "_id" << i << "a" << BSON_ARRAY( i << i+1 ) ) );
- }
- _cli.ensureIndex( ns(), BSON( "a" << 1 ) );
-
- auto_ptr<ClientCursor> cc;
- auto_ptr<ClientCursor::YieldData> data( new ClientCursor::YieldData() );
- {
- dblock lk;
- Client::Context ctx( ns() );
- setQueryOptimizerCursor( BSON( "b" << NE << 0 << "a" << GTE << 0 ) );
- cc.reset( new ClientCursor( QueryOption_NoCursorTimeout, c(), ns() ) );
- for( int i = 0; i < 120; ++i ) {
- ASSERT( advance() );
- }
- ASSERT( ok() );
- ASSERT_EQUALS( 120, current().getIntField( "_id" ) );
- cc->prepareToYield( *data );
- }
- _cli.remove( ns(), BSON( "_id" << 120 ) );
-
- {
- dblock lk;
- Client::Context ctx( ns() );
- ASSERT( ClientCursor::recoverFromYield( *data ) );
- ASSERT( ok() );
- ASSERT_EQUALS( 121, current().getIntField( "_id" ) );
- ASSERT( advance() );
- ASSERT_EQUALS( 122, current().getIntField( "_id" ) );
- }
- }
- };
-
- /** Yield with advance of inactive cursor. */
- class YieldInactiveCursorAdvance : public Base {
- public:
- void run() {
- for( int i = 0; i < 10; ++i ) {
- _cli.insert( ns(), BSON( "_id" << i << "a" << 10 - i ) );
- }
- _cli.ensureIndex( ns(), BSON( "a" << 1 ) );
-
- {
- dblock lk;
- Client::Context ctx( ns() );
- setQueryOptimizerCursor( BSON( "_id" << GT << 0 << "a" << GT << 0 ) );
- ASSERT( ok() );
- ASSERT_EQUALS( 1, current().getIntField( "_id" ) );
- ASSERT( advance() );
- ASSERT_EQUALS( 9, current().getIntField( "_id" ) );
- ASSERT( advance() );
- ASSERT_EQUALS( 2, current().getIntField( "_id" ) );
- ASSERT( prepareToYield() );
- }
-
- _cli.remove( ns(), BSON( "_id" << 9 ) );
-
- {
- dblock lk;
- Client::Context ctx( ns() );
- recoverFromYield();
- ASSERT( ok() );
- ASSERT_EQUALS( 8, current().getIntField( "_id" ) );
- ASSERT( advance() );
- ASSERT_EQUALS( 3, current().getIntField( "_id" ) );
- ASSERT( advance() );
- ASSERT_EQUALS( 7, current().getIntField( "_id" ) );
- }
- }
- };
-
- class OrderId : public Base {
- public:
- void run() {
- for( int i = 0; i < 10; ++i ) {
- _cli.insert( ns(), BSON( "_id" << i ) );
- }
-
- dblock lk;
- Client::Context ctx( ns() );
- setQueryOptimizerCursor( BSONObj(), BSON( "_id" << 1 ) );
-
- for( int i = 0; i < 10; ++i, advance() ) {
- ASSERT( ok() );
- ASSERT_EQUALS( i, current().getIntField( "_id" ) );
- }
- }
- };
-
- class OrderMultiIndex : public Base {
- public:
- void run() {
- for( int i = 0; i < 10; ++i ) {
- _cli.insert( ns(), BSON( "_id" << i << "a" << 1 ) );
- }
- _cli.ensureIndex( ns(), BSON( "_id" << 1 << "a" << 1 ) );
-
- dblock lk;
- Client::Context ctx( ns() );
- setQueryOptimizerCursor( BSON( "_id" << GTE << 0 << "a" << GTE << 0 ), BSON( "_id" << 1 ) );
-
- for( int i = 0; i < 10; ++i, advance() ) {
- ASSERT( ok() );
- ASSERT_EQUALS( i, current().getIntField( "_id" ) );
- }
- }
- };
-
- class OrderReject : public Base {
- public:
- void run() {
- for( int i = 0; i < 10; ++i ) {
- _cli.insert( ns(), BSON( "_id" << i << "a" << i % 5 ) );
- }
- _cli.ensureIndex( ns(), BSON( "a" << 1 ) );
-
- dblock lk;
- Client::Context ctx( ns() );
- setQueryOptimizerCursor( BSON( "a" << GTE << 3 ), BSON( "_id" << 1 ) );
-
- ASSERT( ok() );
- ASSERT_EQUALS( 3, current().getIntField( "_id" ) );
- ASSERT( advance() );
- ASSERT_EQUALS( 4, current().getIntField( "_id" ) );
- ASSERT( advance() );
- ASSERT_EQUALS( 8, current().getIntField( "_id" ) );
- ASSERT( advance() );
- ASSERT_EQUALS( 9, current().getIntField( "_id" ) );
- ASSERT( !advance() );
- }
- };
-
- class OrderNatural : public Base {
- public:
- void run() {
- _cli.insert( ns(), BSON( "_id" << 5 ) );
- _cli.insert( ns(), BSON( "_id" << 4 ) );
- _cli.insert( ns(), BSON( "_id" << 6 ) );
- _cli.ensureIndex( ns(), BSON( "a" << 1 ) );
-
- dblock lk;
- Client::Context ctx( ns() );
- setQueryOptimizerCursor( BSON( "_id" << GT << 0 ), BSON( "$natural" << 1 ) );
-
- ASSERT( ok() );
- ASSERT_EQUALS( 5, current().getIntField( "_id" ) );
- ASSERT( advance() );
- ASSERT_EQUALS( 4, current().getIntField( "_id" ) );
- ASSERT( advance() );
- ASSERT_EQUALS( 6, current().getIntField( "_id" ) );
- ASSERT( !advance() );
- }
- };
-
- class OrderUnindexed : public Base {
- public:
- void run() {
- dblock lk;
- Client::Context ctx( ns() );
- ASSERT( !newQueryOptimizerCursor( ns(), BSONObj(), BSON( "a" << 1 ) ).get() );
- }
- };
-
- class RecordedOrderInvalid : public Base {
- public:
- void run() {
- _cli.insert( ns(), BSON( "a" << 1 << "b" << 1 ) );
- _cli.insert( ns(), BSON( "a" << 2 << "b" << 2 ) );
- _cli.insert( ns(), BSON( "a" << 3 << "b" << 3 ) );
- _cli.ensureIndex( ns(), BSON( "a" << 1 ) );
- _cli.ensureIndex( ns(), BSON( "b" << 1 ) );
- ASSERT( _cli.query( ns(), QUERY( "a" << 2 ).sort( "b" ) )->more() );
-
- dblock lk;
- Client::Context ctx( ns() );
- boost::shared_ptr<Cursor> c = newQueryOptimizerCursor( ns(), BSON( "a" << 2 ), BSON( "b" << 1 ) );
- // Check that we are scanning {b:1} not {a:1}.
- for( int i = 0; i < 3; ++i ) {
- ASSERT( c->ok() );
- c->advance();
- }
- ASSERT( !c->ok() );
- }
- };
-
- class KillOp : public Base {
- public:
- void run() {
- _cli.insert( ns(), BSON( "_id" << 1 << "b" << 1 ) );
- _cli.insert( ns(), BSON( "_id" << 2 << "b" << 2 ) );
- _cli.ensureIndex( ns(), BSON( "b" << 1 ) );
-
- mongolock lk( false );
- Client::Context ctx( ns() );
- setQueryOptimizerCursor( BSON( "_id" << GT << 0 << "b" << GT << 0 ) );
- ASSERT( ok() );
- cc().curop()->kill();
- // First advance() call throws, subsequent calls just fail.
- ASSERT_EXCEPTION( advance(), MsgAssertionException );
- ASSERT( !advance() );
- }
- };
-
- class KillOpFirstClause : public Base {
- public:
- void run() {
- _cli.insert( ns(), BSON( "_id" << 1 << "b" << 1 ) );
- _cli.insert( ns(), BSON( "_id" << 2 << "b" << 2 ) );
- _cli.ensureIndex( ns(), BSON( "b" << 1 ) );
-
- mongolock lk( false );
- Client::Context ctx( ns() );
- boost::shared_ptr<Cursor> c = newQueryOptimizerCursor( ns(), BSON( "$or" << BSON_ARRAY( BSON( "_id" << GT << 0 ) << BSON( "b" << GT << 0 ) ) ) );
- ASSERT( c->ok() );
- cc().curop()->kill();
- // First advance() call throws, subsequent calls just fail.
- ASSERT_EXCEPTION( c->advance(), MsgAssertionException );
- ASSERT( !c->advance() );
- }
- };
-
- class Nscanned : public Base {
- public:
- void run() {
- for( int i = 0; i < 120; ++i ) {
- _cli.insert( ns(), BSON( "_id" << i << "a" << i ) );
- }
-
- dblock lk;
- Client::Context ctx( ns() );
- boost::shared_ptr<Cursor> c = newQueryOptimizerCursor( ns(), BSON( "_id" << GTE << 0 << "a" << GTE << 0 ) );
- ASSERT( c->ok() );
- ASSERT_EQUALS( 2, c->nscanned() );
- c->advance();
- ASSERT( c->ok() );
- ASSERT_EQUALS( 2, c->nscanned() );
- c->advance();
- for( int i = 3; i < 222; ++i ) {
- ASSERT( c->ok() );
- c->advance();
- }
- ASSERT( !c->ok() );
- }
- };
-
- namespace GetCursor {
-
- class Base : public QueryOptimizerCursorTests::Base {
- public:
- Base() {
- // create collection
- _cli.insert( ns(), BSON( "_id" << 5 ) );
- }
- virtual ~Base() {}
- void run() {
- dblock lk;
- Client::Context ctx( ns() );
- boost::shared_ptr<Cursor> c = NamespaceDetailsTransient::getCursor( ns(), query(), order() );
- string type = c->toString().substr( 0, expectedType().length() );
- ASSERT_EQUALS( expectedType(), type );
- check( c );
- }
- protected:
- virtual string expectedType() const = 0;
- virtual BSONObj query() const { return BSONObj(); }
- virtual BSONObj order() const { return BSONObj(); }
- virtual void check( const boost::shared_ptr<Cursor> &c ) {
- ASSERT( c->ok() );
- ASSERT( !c->matcher() );
- ASSERT_EQUALS( 5, c->current().getIntField( "_id" ) );
- ASSERT( !c->advance() );
- }
- };
-
- class NoConstraints : public Base {
- string expectedType() const { return "BasicCursor"; }
- };
-
- class SimpleId : public Base {
- public:
- SimpleId() {
- _cli.insert( ns(), BSON( "_id" << 0 ) );
- _cli.insert( ns(), BSON( "_id" << 10 ) );
- }
- string expectedType() const { return "BtreeCursor _id_"; }
- BSONObj query() const { return BSON( "_id" << 5 ); }
- };
-
- class OptimalIndex : public Base {
- public:
- OptimalIndex() {
- _cli.ensureIndex( ns(), BSON( "a" << 1 ) );
- _cli.insert( ns(), BSON( "a" << 5 ) );
- _cli.insert( ns(), BSON( "a" << 6 ) );
- }
- string expectedType() const { return "BtreeCursor a_1"; }
- BSONObj query() const { return BSON( "a" << GTE << 5 ); }
- void check( const boost::shared_ptr<Cursor> &c ) {
- ASSERT( c->ok() );
- ASSERT( c->matcher() );
- ASSERT_EQUALS( 5, c->current().getIntField( "a" ) );
- ASSERT( c->matcher()->matchesCurrent( c.get() ) );
- ASSERT( c->advance() );
- ASSERT_EQUALS( 6, c->current().getIntField( "a" ) );
- ASSERT( c->matcher()->matchesCurrent( c.get() ) );
- ASSERT( !c->advance() );
- }
- };
-
- class Geo : public Base {
- public:
- Geo() {
- _cli.insert( ns(), BSON( "_id" << 44 << "loc" << BSON_ARRAY( 44 << 45 ) ) );
- _cli.ensureIndex( ns(), BSON( "loc" << "2d" ) );
- }
- string expectedType() const { return "GeoSearchCursor"; }
- BSONObj query() const { return fromjson( "{ loc : { $near : [50,50] } }" ); }
- void check( const boost::shared_ptr<Cursor> &c ) {
- ASSERT( c->ok() );
- ASSERT( c->matcher() );
- ASSERT( c->matcher()->matchesCurrent( c.get() ) );
- ASSERT_EQUALS( 44, c->current().getIntField( "_id" ) );
- ASSERT( !c->advance() );
- }
- };
-
- class OutOfOrder : public QueryOptimizerCursorTests::Base {
- public:
- void run() {
- _cli.insert( ns(), BSON( "_id" << 5 ) );
- dblock lk;
- Client::Context ctx( ns() );
- boost::shared_ptr<Cursor> c = NamespaceDetailsTransient::getCursor( ns(), BSONObj(), BSON( "b" << 1 ) );
- ASSERT( !c );
- }
- };
-
- class BestSavedOutOfOrder : public QueryOptimizerCursorTests::Base {
- public:
- void run() {
- _cli.insert( ns(), BSON( "_id" << 5 << "b" << BSON_ARRAY( 1 << 2 << 3 << 4 << 5 ) ) );
- _cli.insert( ns(), BSON( "_id" << 1 << "b" << 6 ) );
- _cli.ensureIndex( ns(), BSON( "b" << 1 ) );
- // record {_id:1} index for this query
- ASSERT( _cli.query( ns(), QUERY( "_id" << GT << 0 << "b" << GT << 0 ).sort( "b" ) )->more() );
- dblock lk;
- Client::Context ctx( ns() );
- boost::shared_ptr<Cursor> c = NamespaceDetailsTransient::getCursor( ns(), BSON( "_id" << GT << 0 << "b" << GT << 0 ), BSON( "b" << 1 ) );
- // {_id:1} requires scan and order, so {b:1} must be chosen.
- ASSERT( c );
- ASSERT_EQUALS( 5, c->current().getIntField( "_id" ) );
- }
- };
-
- class MultiIndex : public Base {
- public:
- MultiIndex() {
- _cli.ensureIndex( ns(), BSON( "a" << 1 ) );
- }
- string expectedType() const { return "QueryOptimizerCursor"; }
- BSONObj query() const { return BSON( "_id" << GT << 0 << "a" << GT << 0 ); }
- void check( const boost::shared_ptr<Cursor> &c ) {}
- };
-
- } // namespace GetCursor
-
- } // namespace QueryOptimizerCursorTests
-
- class All : public Suite {
- public:
- All() : Suite( "queryoptimizer" ) {}
-
- void setupTests() {
- __forceLinkGeoPlugin();
- add<QueryPlanTests::NoIndex>();
- add<QueryPlanTests::SimpleOrder>();
- add<QueryPlanTests::MoreIndexThanNeeded>();
- add<QueryPlanTests::IndexSigns>();
- add<QueryPlanTests::IndexReverse>();
- add<QueryPlanTests::NoOrder>();
- add<QueryPlanTests::EqualWithOrder>();
- add<QueryPlanTests::Optimal>();
- add<QueryPlanTests::MoreOptimal>();
- add<QueryPlanTests::KeyMatch>();
- add<QueryPlanTests::MoreKeyMatch>();
- add<QueryPlanTests::ExactKeyQueryTypes>();
- add<QueryPlanTests::Unhelpful>();
- 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::SingleException>();
- add<QueryPlanSetTests::AllException>();
- add<QueryPlanSetTests::SaveGoodIndex>();
- add<QueryPlanSetTests::TryAllPlansOnErr>();
- add<QueryPlanSetTests::FindOne>();
- add<QueryPlanSetTests::Delete>();
- add<QueryPlanSetTests::DeleteOneScan>();
- add<QueryPlanSetTests::DeleteOneIndex>();
- add<QueryPlanSetTests::TryOtherPlansBeforeFinish>();
- add<QueryPlanSetTests::InQueryIntervals>();
- add<QueryPlanSetTests::EqualityThenIn>();
- add<QueryPlanSetTests::NotEqualityThenIn>();
- add<BestGuess>();
- add<MultiCursorTests::IdHint>();
- add<MultiCursorTests::NaturalHintFallback>();
- add<QueryOptimizerCursorTests::Empty>();
- add<QueryOptimizerCursorTests::Unindexed>();
- add<QueryOptimizerCursorTests::Basic>();
- add<QueryOptimizerCursorTests::NoMatch>();
- add<QueryOptimizerCursorTests::Interleaved>();
- add<QueryOptimizerCursorTests::NotMatch>();
- add<QueryOptimizerCursorTests::StopInterleaving>();
- add<QueryOptimizerCursorTests::TakeoverWithDup>();
- add<QueryOptimizerCursorTests::TakeoverWithNonMatches>();
- add<QueryOptimizerCursorTests::TakeoverWithTakeoverDup>();
- add<QueryOptimizerCursorTests::BasicOr>();
- add<QueryOptimizerCursorTests::OrFirstClauseEmpty>();
- add<QueryOptimizerCursorTests::OrSecondClauseEmpty>();
- add<QueryOptimizerCursorTests::OrMultipleClausesEmpty>();
- add<QueryOptimizerCursorTests::TakeoverCountOr>();
- add<QueryOptimizerCursorTests::TakeoverEndOfOrClause>();
- add<QueryOptimizerCursorTests::TakeoverBeforeEndOfOrClause>();
- add<QueryOptimizerCursorTests::TakeoverAfterEndOfOrClause>();
- add<QueryOptimizerCursorTests::ManualMatchingDeduping>();
- add<QueryOptimizerCursorTests::ManualMatchingUsingCurrKey>();
- add<QueryOptimizerCursorTests::ManualMatchingDedupingTakeover>();
- add<QueryOptimizerCursorTests::Singlekey>();
- add<QueryOptimizerCursorTests::Multikey>();
- add<QueryOptimizerCursorTests::AddOtherPlans>();
- add<QueryOptimizerCursorTests::OrRangeElimination>();
- add<QueryOptimizerCursorTests::OrDedup>();
- add<QueryOptimizerCursorTests::EarlyDups>();
- add<QueryOptimizerCursorTests::OrPopInTakeover>();
- add<QueryOptimizerCursorTests::OrCollectionScanAbort>();
- add<QueryOptimizerCursorTests::Geo>();
- add<QueryOptimizerCursorTests::YieldNoOp>();
- add<QueryOptimizerCursorTests::YieldDelete>();
- add<QueryOptimizerCursorTests::YieldDeleteContinue>();
- add<QueryOptimizerCursorTests::YieldDeleteContinueFurther>();
- add<QueryOptimizerCursorTests::YieldUpdate>();
- add<QueryOptimizerCursorTests::YieldDrop>();
- add<QueryOptimizerCursorTests::YieldDropOr>();
- add<QueryOptimizerCursorTests::YieldRemoveOr>();
- add<QueryOptimizerCursorTests::YieldCappedOverwrite>();
- add<QueryOptimizerCursorTests::YieldDropIndex>();
- add<QueryOptimizerCursorTests::YieldMultiplePlansNoOp>();
- add<QueryOptimizerCursorTests::YieldMultiplePlansAdvanceNoOp>();
- add<QueryOptimizerCursorTests::YieldMultiplePlansDelete>();
- add<QueryOptimizerCursorTests::YieldMultiplePlansDeleteOr>();
- add<QueryOptimizerCursorTests::YieldMultiplePlansDeleteOrAdvance>();
- add<QueryOptimizerCursorTests::YieldMultiplePlansCappedOverwrite>();
- add<QueryOptimizerCursorTests::YieldMultiplePlansCappedOverwriteManual>();
- add<QueryOptimizerCursorTests::YieldMultiplePlansCappedOverwriteManual2>();
- add<QueryOptimizerCursorTests::TryYieldGeo>();
- add<QueryOptimizerCursorTests::YieldTakeover>();
- add<QueryOptimizerCursorTests::YieldTakeoverBasic>();
- add<QueryOptimizerCursorTests::YieldInactiveCursorAdvance>();
- add<QueryOptimizerCursorTests::OrderId>();
- add<QueryOptimizerCursorTests::OrderMultiIndex>();
- add<QueryOptimizerCursorTests::OrderReject>();
- add<QueryOptimizerCursorTests::OrderNatural>();
- add<QueryOptimizerCursorTests::OrderUnindexed>();
- add<QueryOptimizerCursorTests::RecordedOrderInvalid>();
- add<QueryOptimizerCursorTests::KillOp>();
- add<QueryOptimizerCursorTests::KillOpFirstClause>();
- add<QueryOptimizerCursorTests::Nscanned>();
- add<QueryOptimizerCursorTests::GetCursor::NoConstraints>();
- add<QueryOptimizerCursorTests::GetCursor::SimpleId>();
- add<QueryOptimizerCursorTests::GetCursor::OptimalIndex>();
- add<QueryOptimizerCursorTests::GetCursor::Geo>();
- add<QueryOptimizerCursorTests::GetCursor::OutOfOrder>();
- add<QueryOptimizerCursorTests::GetCursor::BestSavedOutOfOrder>();
- add<QueryOptimizerCursorTests::GetCursor::MultiIndex>();
- }
- } myall;
-
-} // namespace QueryOptimizerTests
-