1
// queryoptimizertests.cpp : query optimizer unit tests
5
* Copyright (C) 2009 10gen Inc.
7
* This program is free software: you can redistribute it and/or modify
8
* it under the terms of the GNU Affero General Public License, version 3,
9
* as published by the Free Software Foundation.
11
* This program is distributed in the hope that it will be useful,
12
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
* GNU Affero General Public License for more details.
16
* You should have received a copy of the GNU Affero General Public License
17
* along with this program. If not, see <http://www.gnu.org/licenses/>.
21
#include "../db/queryoptimizer.h"
24
#include "../db/dbhelpers.h"
25
#include "../db/instance.h"
26
#include "../db/query.h"
31
extern BSONObj id_obj;
32
auto_ptr< QueryResult > runQuery(Message& m, QueryMessage& q ){
34
return runQuery( m , q , op );
38
namespace QueryOptimizerTests {
40
namespace FieldRangeTests {
45
FieldRangeSet s( "ns", query() );
46
checkElt( lower(), s.range( "a" ).min() );
47
checkElt( upper(), s.range( "a" ).max() );
48
ASSERT_EQUALS( lowerInclusive(), s.range( "a" ).minInclusive() );
49
ASSERT_EQUALS( upperInclusive(), s.range( "a" ).maxInclusive() );
52
virtual BSONObj query() = 0;
53
virtual BSONElement lower() { return minKey.firstElement(); }
54
virtual bool lowerInclusive() { return true; }
55
virtual BSONElement upper() { return maxKey.firstElement(); }
56
virtual bool upperInclusive() { return true; }
58
static void checkElt( BSONElement expected, BSONElement actual ) {
59
if ( expected.woCompare( actual, false ) ) {
61
ss << "expected: " << expected << ", got: " << actual;
68
class NumericBase : public Base {
71
o = BSON( "min" << -numeric_limits<double>::max() << "max" << numeric_limits<double>::max() );
74
virtual BSONElement lower() { return o["min"]; }
75
virtual BSONElement upper() { return o["max"]; }
80
class Empty : public Base {
81
virtual BSONObj query() { return BSONObj(); }
84
class Eq : public Base {
86
Eq() : o_( BSON( "a" << 1 ) ) {}
87
virtual BSONObj query() { return o_; }
88
virtual BSONElement lower() { return o_.firstElement(); }
89
virtual BSONElement upper() { return o_.firstElement(); }
93
class DupEq : public Eq {
95
virtual BSONObj query() { return BSON( "a" << 1 << "b" << 2 << "a" << 1 ); }
98
class Lt : public NumericBase {
100
Lt() : o_( BSON( "-" << 1 ) ) {}
101
virtual BSONObj query() { return BSON( "a" << LT << 1 ); }
102
virtual BSONElement upper() { return o_.firstElement(); }
103
virtual bool upperInclusive() { return false; }
107
class Lte : public Lt {
108
virtual BSONObj query() { return BSON( "a" << LTE << 1 ); }
109
virtual bool upperInclusive() { return true; }
112
class Gt : public NumericBase {
114
Gt() : o_( BSON( "-" << 1 ) ) {}
115
virtual BSONObj query() { return BSON( "a" << GT << 1 ); }
116
virtual BSONElement lower() { return o_.firstElement(); }
117
virtual bool lowerInclusive() { return false; }
121
class Gte : public Gt {
122
virtual BSONObj query() { return BSON( "a" << GTE << 1 ); }
123
virtual bool lowerInclusive() { return true; }
126
class TwoLt : public Lt {
127
virtual BSONObj query() { return BSON( "a" << LT << 1 << LT << 5 ); }
130
class TwoGt : public Gt {
131
virtual BSONObj query() { return BSON( "a" << GT << 0 << GT << 1 ); }
134
class EqGte : public Eq {
135
virtual BSONObj query() { return BSON( "a" << 1 << "a" << GTE << 1 ); }
141
FieldRangeSet fbs( "ns", BSON( "a" << 1 << "a" << GTE << 2 ) );
142
ASSERT( !fbs.matchPossible() );
146
class Regex : public Base {
148
Regex() : o1_( BSON( "" << "abc" ) ), o2_( BSON( "" << "abd" ) ) {}
149
virtual BSONObj query() {
151
b.appendRegex( "a", "^abc" );
154
virtual BSONElement lower() { return o1_.firstElement(); }
155
virtual BSONElement upper() { return o2_.firstElement(); }
156
virtual bool upperInclusive() { return false; }
160
class RegexObj : public Base {
162
RegexObj() : o1_( BSON( "" << "abc" ) ), o2_( BSON( "" << "abd" ) ) {}
163
virtual BSONObj query() { return BSON("a" << BSON("$regex" << "^abc")); }
164
virtual BSONElement lower() { return o1_.firstElement(); }
165
virtual BSONElement upper() { return o2_.firstElement(); }
166
virtual bool upperInclusive() { return false; }
170
class UnhelpfulRegex : public Base {
171
virtual BSONObj query() {
173
b.appendRegex( "a", "abc" );
178
class In : public Base {
180
In() : o1_( BSON( "-" << -3 ) ), o2_( BSON( "-" << 44 ) ) {}
181
virtual BSONObj query() {
185
vals.push_back( 44 );
186
vals.push_back( -1 );
187
vals.push_back( -3 );
190
bb.append( "$in", vals );
192
b.append( "a", bb.done() );
195
virtual BSONElement lower() { return o1_.firstElement(); }
196
virtual BSONElement upper() { return o2_.firstElement(); }
203
FieldRangeSet s( "ns", BSON( "a" << 1 ) );
204
ASSERT( s.range( "a" ).equality() );
205
FieldRangeSet s2( "ns", BSON( "a" << GTE << 1 << LTE << 1 ) );
206
ASSERT( s2.range( "a" ).equality() );
207
FieldRangeSet s3( "ns", BSON( "a" << GT << 1 << LTE << 1 ) );
208
ASSERT( !s3.range( "a" ).equality() );
209
FieldRangeSet s4( "ns", BSON( "a" << GTE << 1 << LT << 1 ) );
210
ASSERT( !s4.range( "a" ).equality() );
211
FieldRangeSet s5( "ns", BSON( "a" << GTE << 1 << LTE << 1 << GT << 1 ) );
212
ASSERT( !s5.range( "a" ).equality() );
213
FieldRangeSet s6( "ns", BSON( "a" << GTE << 1 << LTE << 1 << LT << 1 ) );
214
ASSERT( !s6.range( "a" ).equality() );
218
class SimplifiedQuery {
221
FieldRangeSet fbs( "ns", BSON( "a" << GT << 1 << GT << 5 << LT << 10 << "b" << 4 << "c" << LT << 4 << LT << 6 << "d" << GTE << 0 << GT << 0 << "e" << GTE << 0 << LTE << 10 ) );
222
BSONObj simple = fbs.simplifiedQuery();
223
cout << "simple: " << simple << endl;
224
ASSERT( !simple.getObjectField( "a" ).woCompare( fromjson( "{$gt:5,$lt:10}" ) ) );
225
ASSERT_EQUALS( 4, simple.getIntField( "b" ) );
226
ASSERT( !simple.getObjectField( "c" ).woCompare( BSON("$gte" << -numeric_limits<double>::max() << "$lt" << 4 ) ) );
227
ASSERT( !simple.getObjectField( "d" ).woCompare( BSON("$gt" << 0 << "$lte" << numeric_limits<double>::max() ) ) );
228
ASSERT( !simple.getObjectField( "e" ).woCompare( fromjson( "{$gte:0,$lte:10}" ) ) );
232
class QueryPatternTest {
235
ASSERT( p( BSON( "a" << 1 ) ) == p( BSON( "a" << 1 ) ) );
236
ASSERT( p( BSON( "a" << 1 ) ) == p( BSON( "a" << 5 ) ) );
237
ASSERT( p( BSON( "a" << 1 ) ) != p( BSON( "b" << 1 ) ) );
238
ASSERT( p( BSON( "a" << 1 ) ) != p( BSON( "a" << LTE << 1 ) ) );
239
ASSERT( p( BSON( "a" << 1 ) ) != p( BSON( "a" << 1 << "b" << 2 ) ) );
240
ASSERT( p( BSON( "a" << 1 << "b" << 3 ) ) != p( BSON( "a" << 1 ) ) );
241
ASSERT( p( BSON( "a" << LT << 1 ) ) == p( BSON( "a" << LTE << 5 ) ) );
242
ASSERT( p( BSON( "a" << LT << 1 << GTE << 0 ) ) == p( BSON( "a" << LTE << 5 << GTE << 0 ) ) );
243
ASSERT( p( BSON( "a" << 1 ) ) < p( BSON( "a" << 1 << "b" << 1 ) ) );
244
ASSERT( !( p( BSON( "a" << 1 << "b" << 1 ) ) < p( BSON( "a" << 1 ) ) ) );
245
ASSERT( p( BSON( "a" << 1 ), BSON( "b" << 1 ) ) == p( BSON( "a" << 4 ), BSON( "b" << "a" ) ) );
246
ASSERT( p( BSON( "a" << 1 ), BSON( "b" << 1 ) ) == p( BSON( "a" << 4 ), BSON( "b" << -1 ) ) );
247
ASSERT( p( BSON( "a" << 1 ), BSON( "b" << 1 ) ) != p( BSON( "a" << 4 ), BSON( "c" << 1 ) ) );
248
ASSERT( p( BSON( "a" << 1 ), BSON( "b" << 1 << "c" << -1 ) ) == p( BSON( "a" << 4 ), BSON( "b" << -1 << "c" << 1 ) ) );
249
ASSERT( p( BSON( "a" << 1 ), BSON( "b" << 1 << "c" << 1 ) ) != p( BSON( "a" << 4 ), BSON( "b" << 1 ) ) );
250
ASSERT( p( BSON( "a" << 1 ), BSON( "b" << 1 ) ) != p( BSON( "a" << 4 ), BSON( "b" << 1 << "c" << 1 ) ) );
253
static QueryPattern p( const BSONObj &query, const BSONObj &sort = BSONObj() ) {
254
return FieldRangeSet( "", query ).pattern( sort );
261
ASSERT_EQUALS( 0, FieldRangeSet( "ns", BSON( "$where" << 1 ) ).nNontrivialRanges() );
268
FieldRangeSet f( "", BSON( "a" << 1 ) );
269
ASSERT( f.range( "a" ).min().woCompare( BSON( "a" << 2.0 ).firstElement() ) < 0 );
270
ASSERT( f.range( "a" ).min().woCompare( BSON( "a" << 0.0 ).firstElement() ) > 0 );
277
FieldRangeSet f( "", fromjson( "{a:{$gt:4,$in:[1,2,3,4,5,6]}}" ) );
278
ASSERT( f.range( "a" ).min().woCompare( BSON( "a" << 5.0 ).firstElement(), false ) == 0 );
279
ASSERT( f.range( "a" ).max().woCompare( BSON( "a" << 6.0 ).firstElement(), false ) == 0 );
286
FieldRangeSet f( "", fromjson( "{a:{$lt:4,$in:[1,2,3,4,5,6]}}" ) );
287
ASSERT( f.range( "a" ).min().woCompare( BSON( "a" << 1.0 ).firstElement(), false ) == 0 );
288
ASSERT( f.range( "a" ).max().woCompare( BSON( "a" << 3.0 ).firstElement(), false ) == 0 );
295
FieldRangeSet frs1( "", fromjson( "{a:{$in:[1,3,5,7,9]}}" ) );
296
FieldRangeSet frs2( "", fromjson( "{a:{$in:[2,3,5,8,9]}}" ) );
297
FieldRange fr1 = frs1.range( "a" );
298
FieldRange fr2 = frs2.range( "a" );
300
ASSERT( fr1.min().woCompare( BSON( "a" << 3.0 ).firstElement(), false ) == 0 );
301
ASSERT( fr1.max().woCompare( BSON( "a" << 9.0 ).firstElement(), false ) == 0 );
302
vector< FieldInterval > intervals = fr1.intervals();
303
vector< FieldInterval >::const_iterator j = intervals.begin();
304
double expected[] = { 3, 5, 9 };
305
for( int i = 0; i < 3; ++i, ++j ) {
306
ASSERT_EQUALS( expected[ i ], j->lower_.bound_.number() );
307
ASSERT( j->lower_.inclusive_ );
308
ASSERT( j->lower_ == j->upper_ );
310
ASSERT( j == intervals.end() );
314
} // namespace FieldRangeTests
316
namespace QueryPlanTests {
319
Base() : indexNum_( 0 ) {
322
userCreateNS( ns(), BSONObj(), err, false );
331
static const char *ns() { return "unittests.QueryPlanTests"; }
332
static NamespaceDetails *nsd() { return nsdetails( ns() ); }
333
IndexDetails *index( const BSONObj &key ) {
336
string name = ss.str();
337
client_.resetIndexCache();
338
client_.ensureIndex( ns(), key, false, name.c_str() );
339
NamespaceDetails *d = nsd();
340
for( int i = 0; i < d->nIndexes; ++i ) {
341
if ( d->idx(i).keyPattern() == key /*indexName() == name*/ || ( d->idx(i).isIdIndex() && IndexDetails::isIdIndexPattern( key ) ) )
347
int indexno( const BSONObj &key ) {
348
return nsd()->idxNo( *index(key) );
350
BSONObj startKey( const QueryPlan &p ) const {
351
BoundList bl = p.indexBounds();
352
return bl[ 0 ].first.getOwned();
354
BSONObj endKey( const QueryPlan &p ) const {
355
BoundList bl = p.indexBounds();
356
return bl[ bl.size() - 1 ].second.getOwned();
361
static DBDirectClient client_;
363
DBDirectClient Base::client_;
365
// There's a limit of 10 indexes total, make sure not to exceed this in a given test.
366
#define INDEXNO(x) nsd()->idxNo( *this->index( BSON(x) ) )
367
#define INDEX(x) this->index( BSON(x) )
368
auto_ptr< FieldRangeSet > FieldRangeSet_GLOBAL;
369
#define FBS(x) ( FieldRangeSet_GLOBAL.reset( new FieldRangeSet( ns(), x ) ), *FieldRangeSet_GLOBAL )
371
class NoIndex : public Base {
374
QueryPlan p( nsd(), -1, FBS( BSONObj() ), BSONObj() );
375
ASSERT( !p.optimal() );
376
ASSERT( !p.scanAndOrderRequired() );
377
ASSERT( !p.exactKeyMatch() );
381
class SimpleOrder : public Base {
385
b.appendMinKey( "" );
386
BSONObj start = b.obj();
388
b2.appendMaxKey( "" );
389
BSONObj end = b2.obj();
391
QueryPlan p( nsd(), INDEXNO( "a" << 1 ), FBS( BSONObj() ), BSON( "a" << 1 ) );
392
ASSERT( !p.scanAndOrderRequired() );
393
ASSERT( !startKey( p ).woCompare( start ) );
394
ASSERT( !endKey( p ).woCompare( end ) );
395
QueryPlan p2( nsd(), INDEXNO( "a" << 1 << "b" << 1 ), FBS( BSONObj() ), BSON( "a" << 1 << "b" << 1 ) );
396
ASSERT( !p2.scanAndOrderRequired() );
397
QueryPlan p3( nsd(), INDEXNO( "a" << 1 ), FBS( BSONObj() ), BSON( "b" << 1 ) );
398
ASSERT( p3.scanAndOrderRequired() );
399
ASSERT( !startKey( p3 ).woCompare( start ) );
400
ASSERT( !endKey( p3 ).woCompare( end ) );
404
class MoreIndexThanNeeded : public Base {
407
QueryPlan p( nsd(), INDEXNO( "a" << 1 << "b" << 1 ), FBS( BSONObj() ), BSON( "a" << 1 ) );
408
ASSERT( !p.scanAndOrderRequired() );
412
class IndexSigns : public Base {
415
QueryPlan p( nsd(), INDEXNO( "a" << 1 << "b" << -1 ) , FBS( BSONObj() ), BSON( "a" << 1 << "b" << -1 ) );
416
ASSERT( !p.scanAndOrderRequired() );
417
ASSERT_EQUALS( 1, p.direction() );
418
QueryPlan p2( nsd(), INDEXNO( "a" << 1 << "b" << 1 ), FBS( BSONObj() ), BSON( "a" << 1 << "b" << -1 ) );
419
ASSERT( p2.scanAndOrderRequired() );
420
ASSERT_EQUALS( 0, p2.direction() );
421
QueryPlan p3( nsd(), indexno( id_obj ), FBS( BSONObj() ), BSON( "_id" << 1 ) );
422
ASSERT( !p3.scanAndOrderRequired() );
423
ASSERT_EQUALS( 1, p3.direction() );
427
class IndexReverse : public Base {
431
b.appendMinKey( "" );
432
b.appendMaxKey( "" );
433
BSONObj start = b.obj();
435
b2.appendMaxKey( "" );
436
b2.appendMinKey( "" );
437
BSONObj end = b2.obj();
438
QueryPlan p( nsd(), INDEXNO( "a" << -1 << "b" << 1 ),FBS( BSONObj() ), BSON( "a" << 1 << "b" << -1 ) );
439
ASSERT( !p.scanAndOrderRequired() );
440
ASSERT_EQUALS( -1, p.direction() );
441
ASSERT( !startKey( p ).woCompare( start ) );
442
ASSERT( !endKey( p ).woCompare( end ) );
443
QueryPlan p2( nsd(), INDEXNO( "a" << 1 << "b" << 1 ), FBS( BSONObj() ), BSON( "a" << -1 << "b" << -1 ) );
444
ASSERT( !p2.scanAndOrderRequired() );
445
ASSERT_EQUALS( -1, p2.direction() );
446
QueryPlan p3( nsd(), INDEXNO( "a" << 1 << "b" << -1 ), FBS( BSONObj() ), BSON( "a" << -1 << "b" << -1 ) );
447
ASSERT( p3.scanAndOrderRequired() );
448
ASSERT_EQUALS( 0, p3.direction() );
452
class NoOrder : public Base {
457
b.appendMinKey( "" );
458
BSONObj start = b.obj();
461
b2.appendMaxKey( "" );
462
BSONObj end = b2.obj();
463
QueryPlan p( nsd(), INDEXNO( "a" << -1 << "b" << 1 ), FBS( BSON( "a" << 3 ) ), BSONObj() );
464
ASSERT( !p.scanAndOrderRequired() );
465
ASSERT( !startKey( p ).woCompare( start ) );
466
ASSERT( !endKey( p ).woCompare( end ) );
467
QueryPlan p2( nsd(), INDEXNO( "a" << -1 << "b" << 1 ), FBS( BSON( "a" << 3 ) ), BSONObj() );
468
ASSERT( !p2.scanAndOrderRequired() );
469
ASSERT( !startKey( p ).woCompare( start ) );
470
ASSERT( !endKey( p ).woCompare( end ) );
474
class EqualWithOrder : public Base {
477
QueryPlan p( nsd(), INDEXNO( "a" << 1 << "b" << 1 ), FBS( BSON( "a" << 4 ) ), BSON( "b" << 1 ) );
478
ASSERT( !p.scanAndOrderRequired() );
479
QueryPlan p2( nsd(), INDEXNO( "a" << 1 << "b" << 1 << "c" << 1 ), FBS( BSON( "b" << 4 ) ), BSON( "a" << 1 << "c" << 1 ) );
480
ASSERT( !p2.scanAndOrderRequired() );
481
QueryPlan p3( nsd(), INDEXNO( "a" << 1 << "b" << 1 ), FBS( BSON( "b" << 4 ) ), BSON( "a" << 1 << "c" << 1 ) );
482
ASSERT( p3.scanAndOrderRequired() );
486
class Optimal : public Base {
489
QueryPlan p( nsd(), INDEXNO( "a" << 1 ), FBS( BSONObj() ), BSON( "a" << 1 ) );
490
ASSERT( p.optimal() );
491
QueryPlan p2( nsd(), INDEXNO( "a" << 1 << "b" << 1 ), FBS( BSONObj() ), BSON( "a" << 1 ) );
492
ASSERT( p2.optimal() );
493
QueryPlan p3( nsd(), INDEXNO( "a" << 1 << "b" << 1 ), FBS( BSON( "a" << 1 ) ), BSON( "a" << 1 ) );
494
ASSERT( p3.optimal() );
495
QueryPlan p4( nsd(), INDEXNO( "a" << 1 << "b" << 1 ), FBS( BSON( "b" << 1 ) ), BSON( "a" << 1 ) );
496
ASSERT( !p4.optimal() );
497
QueryPlan p5( nsd(), INDEXNO( "a" << 1 << "b" << 1 ), FBS( BSON( "a" << 1 ) ), BSON( "b" << 1 ) );
498
ASSERT( p5.optimal() );
499
QueryPlan p6( nsd(), INDEXNO( "a" << 1 << "b" << 1 ), FBS( BSON( "b" << 1 ) ), BSON( "b" << 1 ) );
500
ASSERT( !p6.optimal() );
501
QueryPlan p7( nsd(), INDEXNO( "a" << 1 << "b" << 1 ), FBS( BSON( "a" << 1 << "b" << 1 ) ), BSON( "a" << 1 ) );
502
ASSERT( p7.optimal() );
503
QueryPlan p8( nsd(), INDEXNO( "a" << 1 << "b" << 1 ), FBS( BSON( "a" << 1 << "b" << LT << 1 ) ), BSON( "a" << 1 ) );
504
ASSERT( p8.optimal() );
505
QueryPlan p9( nsd(), INDEXNO( "a" << 1 << "b" << 1 << "c" << 1 ), FBS( BSON( "a" << 1 << "b" << LT << 1 ) ), BSON( "a" << 1 ) );
506
ASSERT( p9.optimal() );
510
class MoreOptimal : public Base {
513
QueryPlan p10( nsd(), INDEXNO( "a" << 1 << "b" << 1 << "c" << 1 ), FBS( BSON( "a" << 1 ) ), BSONObj() );
514
ASSERT( p10.optimal() );
515
QueryPlan p11( nsd(), INDEXNO( "a" << 1 << "b" << 1 << "c" << 1 ), FBS( BSON( "a" << 1 << "b" << LT << 1 ) ), BSONObj() );
516
ASSERT( p11.optimal() );
517
QueryPlan p12( nsd(), INDEXNO( "a" << 1 << "b" << 1 << "c" << 1 ), FBS( BSON( "a" << LT << 1 ) ), BSONObj() );
518
ASSERT( p12.optimal() );
519
QueryPlan p13( nsd(), INDEXNO( "a" << 1 << "b" << 1 << "c" << 1 ), FBS( BSON( "a" << LT << 1 ) ), BSON( "a" << 1 ) );
520
ASSERT( p13.optimal() );
524
class KeyMatch : public Base {
527
QueryPlan p( nsd(), INDEXNO( "a" << 1 ), FBS( BSONObj() ), BSON( "a" << 1 ) );
528
ASSERT( !p.exactKeyMatch() );
529
QueryPlan p2( nsd(), INDEXNO( "b" << 1 << "a" << 1 ), FBS( BSONObj() ), BSON( "a" << 1 ) );
530
ASSERT( !p2.exactKeyMatch() );
531
QueryPlan p3( nsd(), INDEXNO( "b" << 1 << "a" << 1 ), FBS( BSON( "b" << "z" ) ), BSON( "a" << 1 ) );
532
ASSERT( !p3.exactKeyMatch() );
533
QueryPlan p4( nsd(), INDEXNO( "b" << 1 << "a" << 1 << "c" << 1 ), FBS( BSON( "c" << "y" << "b" << "z" ) ), BSON( "a" << 1 ) );
534
ASSERT( !p4.exactKeyMatch() );
535
QueryPlan p5( nsd(), INDEXNO( "b" << 1 << "a" << 1 << "c" << 1 ), FBS( BSON( "c" << "y" << "b" << "z" ) ), BSONObj() );
536
ASSERT( !p5.exactKeyMatch() );
537
QueryPlan p6( nsd(), INDEXNO( "b" << 1 << "a" << 1 << "c" << 1 ), FBS( BSON( "c" << LT << "y" << "b" << GT << "z" ) ), BSONObj() );
538
ASSERT( !p6.exactKeyMatch() );
539
QueryPlan p7( nsd(), INDEXNO( "b" << 1 ), FBS( BSONObj() ), BSON( "a" << 1 ) );
540
ASSERT( !p7.exactKeyMatch() );
541
QueryPlan p8( nsd(), INDEXNO( "a" << 1 << "b" << 1 ), FBS( BSON( "b" << "y" << "a" << "z" ) ), BSONObj() );
542
ASSERT( p8.exactKeyMatch() );
543
QueryPlan p9( nsd(), INDEXNO( "a" << 1 ), FBS( BSON( "a" << "z" ) ), BSON( "a" << 1 ) );
544
ASSERT( p9.exactKeyMatch() );
548
class MoreKeyMatch : public Base {
551
QueryPlan p( nsd(), INDEXNO( "a" << 1 ), FBS( BSON( "a" << "r" << "b" << NE << "q" ) ), BSON( "a" << 1 ) );
552
ASSERT( !p.exactKeyMatch() );
556
class ExactKeyQueryTypes : public Base {
559
QueryPlan p( nsd(), INDEXNO( "a" << 1 ), FBS( BSON( "a" << "b" ) ), BSONObj() );
560
ASSERT( p.exactKeyMatch() );
561
QueryPlan p2( nsd(), INDEXNO( "a" << 1 ), FBS( BSON( "a" << 4 ) ), BSONObj() );
562
ASSERT( !p2.exactKeyMatch() );
563
QueryPlan p3( nsd(), INDEXNO( "a" << 1 ), FBS( BSON( "a" << BSON( "c" << "d" ) ) ), BSONObj() );
564
ASSERT( !p3.exactKeyMatch() );
566
b.appendRegex( "a", "^ddd" );
567
QueryPlan p4( nsd(), INDEXNO( "a" << 1 ), FBS( b.obj() ), BSONObj() );
568
ASSERT( !p4.exactKeyMatch() );
569
QueryPlan p5( nsd(), INDEXNO( "a" << 1 << "b" << 1 ), FBS( BSON( "a" << "z" << "b" << 4 ) ), BSONObj() );
570
ASSERT( !p5.exactKeyMatch() );
574
class Unhelpful : public Base {
577
QueryPlan p( nsd(), INDEXNO( "a" << 1 << "b" << 1 ), FBS( BSON( "b" << 1 ) ), BSONObj() );
578
ASSERT( !p.range( "a" ).nontrivial() );
579
ASSERT( p.unhelpful() );
580
QueryPlan p2( nsd(), INDEXNO( "a" << 1 << "b" << 1 ), FBS( BSON( "b" << 1 << "c" << 1 ) ), BSON( "a" << 1 ) );
581
ASSERT( !p2.scanAndOrderRequired() );
582
ASSERT( !p2.range( "a" ).nontrivial() );
583
ASSERT( !p2.unhelpful() );
584
QueryPlan p3( nsd(), INDEXNO( "b" << 1 ), FBS( BSON( "b" << 1 << "c" << 1 ) ), BSONObj() );
585
ASSERT( p3.range( "b" ).nontrivial() );
586
ASSERT( !p3.unhelpful() );
587
QueryPlan p4( nsd(), INDEXNO( "b" << 1 << "c" << 1 ), FBS( BSON( "c" << 1 << "d" << 1 ) ), BSONObj() );
588
ASSERT( !p4.range( "b" ).nontrivial() );
589
ASSERT( p4.unhelpful() );
593
} // namespace QueryPlanTests
595
namespace QueryPlanSetTests {
601
userCreateNS( ns(), BSONObj(), err, false );
606
NamespaceDetailsTransient::_get( ns() ).clearQueryCache();
610
static void assembleRequest( const string &ns, BSONObj query, int nToReturn, int nToSkip, BSONObj *fieldsToReturn, int queryOptions, Message &toSend ) {
611
// see query.h for the protocol we are using here.
613
int opts = queryOptions;
615
b.append(ns.c_str());
618
query.appendSelfToBufBuilder(b);
619
if ( fieldsToReturn )
620
fieldsToReturn->appendSelfToBufBuilder(b);
621
toSend.setData(dbQuery, b.buf(), b.len());
624
static const char *ns() { return "unittests.QueryPlanSetTests"; }
625
static NamespaceDetails *nsd() { return nsdetails( ns() ); }
630
class NoIndexes : public Base {
633
QueryPlanSet s( ns(), BSON( "a" << 4 ), BSON( "b" << 1 ) );
634
ASSERT_EQUALS( 1, s.nPlans() );
638
class Optimal : public Base {
641
Helpers::ensureIndex( ns(), BSON( "a" << 1 ), false, "a_1" );
642
Helpers::ensureIndex( ns(), BSON( "a" << 1 ), false, "b_2" );
643
QueryPlanSet s( ns(), BSON( "a" << 4 ), BSONObj() );
644
ASSERT_EQUALS( 1, s.nPlans() );
648
class NoOptimal : public Base {
651
Helpers::ensureIndex( ns(), BSON( "a" << 1 ), false, "a_1" );
652
Helpers::ensureIndex( ns(), BSON( "b" << 1 ), false, "b_1" );
653
QueryPlanSet s( ns(), BSON( "a" << 4 ), BSON( "b" << 1 ) );
654
ASSERT_EQUALS( 3, s.nPlans() );
658
class NoSpec : public Base {
661
Helpers::ensureIndex( ns(), BSON( "a" << 1 ), false, "a_1" );
662
Helpers::ensureIndex( ns(), BSON( "b" << 1 ), false, "b_1" );
663
QueryPlanSet s( ns(), BSONObj(), BSONObj() );
664
ASSERT_EQUALS( 1, s.nPlans() );
668
class HintSpec : public Base {
671
Helpers::ensureIndex( ns(), BSON( "a" << 1 ), false, "a_1" );
672
Helpers::ensureIndex( ns(), BSON( "b" << 1 ), false, "b_1" );
673
BSONObj b = BSON( "hint" << BSON( "a" << 1 ) );
674
BSONElement e = b.firstElement();
675
QueryPlanSet s( ns(), BSON( "a" << 1 ), BSON( "b" << 1 ), &e );
676
ASSERT_EQUALS( 1, s.nPlans() );
680
class HintName : public Base {
683
Helpers::ensureIndex( ns(), BSON( "a" << 1 ), false, "a_1" );
684
Helpers::ensureIndex( ns(), BSON( "b" << 1 ), false, "b_1" );
685
BSONObj b = BSON( "hint" << "a_1" );
686
BSONElement e = b.firstElement();
687
QueryPlanSet s( ns(), BSON( "a" << 1 ), BSON( "b" << 1 ), &e );
688
ASSERT_EQUALS( 1, s.nPlans() );
692
class NaturalHint : public Base {
695
Helpers::ensureIndex( ns(), BSON( "a" << 1 ), false, "a_1" );
696
Helpers::ensureIndex( ns(), BSON( "b" << 1 ), false, "b_1" );
697
BSONObj b = BSON( "hint" << BSON( "$natural" << 1 ) );
698
BSONElement e = b.firstElement();
699
QueryPlanSet s( ns(), BSON( "a" << 1 ), BSON( "b" << 1 ), &e );
700
ASSERT_EQUALS( 1, s.nPlans() );
704
class NaturalSort : public Base {
707
Helpers::ensureIndex( ns(), BSON( "a" << 1 ), false, "a_1" );
708
Helpers::ensureIndex( ns(), BSON( "a" << 1 ), false, "b_2" );
709
QueryPlanSet s( ns(), BSON( "a" << 1 ), BSON( "$natural" << 1 ) );
710
ASSERT_EQUALS( 1, s.nPlans() );
714
class BadHint : public Base {
717
BSONObj b = BSON( "hint" << "a_1" );
718
BSONElement e = b.firstElement();
719
ASSERT_EXCEPTION( QueryPlanSet s( ns(), BSON( "a" << 1 ), BSON( "b" << 1 ), &e ),
720
AssertionException );
724
class Count : public Base {
727
Helpers::ensureIndex( ns(), BSON( "a" << 1 ), false, "a_1" );
728
Helpers::ensureIndex( ns(), BSON( "b" << 1 ), false, "b_1" );
730
ASSERT_EQUALS( 0, runCount( ns(), BSON( "query" << BSON( "a" << 4 ) ), err ) );
731
BSONObj one = BSON( "a" << 1 );
732
BSONObj fourA = BSON( "a" << 4 );
733
BSONObj fourB = BSON( "a" << 4 );
734
theDataFileMgr.insert( ns(), one );
735
ASSERT_EQUALS( 0, runCount( ns(), BSON( "query" << BSON( "a" << 4 ) ), err ) );
736
theDataFileMgr.insert( ns(), fourA );
737
ASSERT_EQUALS( 1, runCount( ns(), BSON( "query" << BSON( "a" << 4 ) ), err ) );
738
theDataFileMgr.insert( ns(), fourB );
739
ASSERT_EQUALS( 2, runCount( ns(), BSON( "query" << BSON( "a" << 4 ) ), err ) );
740
ASSERT_EQUALS( 3, runCount( ns(), BSON( "query" << BSONObj() ), err ) );
741
ASSERT_EQUALS( 3, runCount( ns(), BSON( "query" << BSON( "a" << GT << 0 ) ), err ) );
743
ASSERT_EQUALS( -1, runCount( "unittests.missingNS", BSONObj(), err ) );
745
ASSERT_EQUALS( 0, runCount( ns(), BSON( "query" << BSON( "a" << GT << 0 << LT << -1 ) ), err ) );
749
class QueryMissingNs : public Base {
753
assembleRequest( "unittests.missingNS", BSONObj(), 0, 0, 0, 0, m );
758
ASSERT_EQUALS( 0, runQuery( m, q)->nReturned );
762
class UnhelpfulIndex : public Base {
765
Helpers::ensureIndex( ns(), BSON( "a" << 1 ), false, "a_1" );
766
Helpers::ensureIndex( ns(), BSON( "b" << 1 ), false, "b_1" );
767
QueryPlanSet s( ns(), BSON( "a" << 1 << "c" << 2 ), BSONObj() );
768
ASSERT_EQUALS( 2, s.nPlans() );
772
class SingleException : public Base {
775
Helpers::ensureIndex( ns(), BSON( "a" << 1 ), false, "a_1" );
776
Helpers::ensureIndex( ns(), BSON( "b" << 1 ), false, "b_1" );
777
QueryPlanSet s( ns(), BSON( "a" << 4 ), BSON( "b" << 1 ) );
778
ASSERT_EQUALS( 3, s.nPlans() );
780
auto_ptr< TestOp > t( new TestOp( true, threw ) );
781
boost::shared_ptr< TestOp > done = s.runOp( *t );
783
ASSERT( done->complete() );
784
ASSERT( done->exceptionMessage().empty() );
785
ASSERT( !done->error() );
788
class TestOp : public QueryOp {
790
TestOp( bool iThrow, bool &threw ) : iThrow_( iThrow ), threw_( threw ), i_(), youThrow_( false ) {}
791
virtual void init() {}
792
virtual void next() {
795
massert( 10408 , "throw", !iThrow_ );
799
virtual QueryOp *clone() const {
800
QueryOp *op = new TestOp( youThrow_, threw_ );
801
youThrow_ = !youThrow_;
804
virtual bool mayRecordPlan() const { return true; }
809
mutable bool youThrow_;
813
class AllException : public Base {
816
Helpers::ensureIndex( ns(), BSON( "a" << 1 ), false, "a_1" );
817
Helpers::ensureIndex( ns(), BSON( "b" << 1 ), false, "b_1" );
818
QueryPlanSet s( ns(), BSON( "a" << 4 ), BSON( "b" << 1 ) );
819
ASSERT_EQUALS( 3, s.nPlans() );
820
auto_ptr< TestOp > t( new TestOp() );
821
boost::shared_ptr< TestOp > done = s.runOp( *t );
822
ASSERT( !done->complete() );
823
ASSERT_EQUALS( "throw", done->exceptionMessage() );
824
ASSERT( done->error() );
827
class TestOp : public QueryOp {
829
virtual void init() {}
830
virtual void next() {
831
massert( 10409 , "throw", false );
833
virtual QueryOp *clone() const {
836
virtual bool mayRecordPlan() const { return true; }
840
class SaveGoodIndex : public Base {
843
Helpers::ensureIndex( ns(), BSON( "a" << 1 ), false, "a_1" );
844
Helpers::ensureIndex( ns(), BSON( "b" << 1 ), false, "b_1" );
849
Helpers::ensureIndex( ns(), BSON( "c" << 1 ), false, "c_1" );
855
DBDirectClient client;
856
for( int i = 0; i < 34; ++i ) {
857
client.insert( ns(), BSON( "i" << i ) );
858
client.update( ns(), QUERY( "i" << i ), BSON( "i" << i + 1 ) );
859
client.remove( ns(), BSON( "i" << i + 1 ) );
864
QueryPlanSet s( ns(), BSON( "a" << 4 ), BSON( "b" << 1 ) );
865
NoRecordTestOp original;
869
BSONObj hint = fromjson( "{hint:{$natural:1}}" );
870
BSONElement hintElt = hint.firstElement();
871
QueryPlanSet s2( ns(), BSON( "a" << 4 ), BSON( "b" << 1 ), &hintElt );
873
s2.runOp( newOriginal );
876
QueryPlanSet s3( ns(), BSON( "a" << 4 ), BSON( "b" << 1 << "c" << 1 ) );
877
TestOp newerOriginal;
878
s3.runOp( newerOriginal );
885
void nPlans( int n ) {
886
QueryPlanSet s( ns(), BSON( "a" << 4 ), BSON( "b" << 1 ) );
887
ASSERT_EQUALS( n, s.nPlans() );
890
QueryPlanSet s( ns(), BSON( "a" << 4 ), BSON( "b" << 1 ) );
894
class TestOp : public QueryOp {
896
virtual void init() {}
897
virtual void next() {
900
virtual QueryOp *clone() const {
903
virtual bool mayRecordPlan() const { return true; }
905
class NoRecordTestOp : public TestOp {
906
virtual bool mayRecordPlan() const { return false; }
907
virtual QueryOp *clone() const { return new NoRecordTestOp(); }
911
class TryAllPlansOnErr : public Base {
914
Helpers::ensureIndex( ns(), BSON( "a" << 1 ), false, "a_1" );
916
QueryPlanSet s( ns(), BSON( "a" << 4 ), BSON( "b" << 1 ) );
919
ASSERT( fromjson( "{$natural:1}" ).woCompare( NamespaceDetailsTransient::_get( ns() ).indexForPattern( s.fbs().pattern( BSON( "b" << 1 ) ) ) ) == 0 );
920
ASSERT_EQUALS( 1, NamespaceDetailsTransient::_get( ns() ).nScannedForPattern( s.fbs().pattern( BSON( "b" << 1 ) ) ) );
922
QueryPlanSet s2( ns(), BSON( "a" << 4 ), BSON( "b" << 1 ) );
924
ASSERT( s2.runOp( op2 )->complete() );
927
class TestOp : public QueryOp {
929
virtual void init() {}
930
virtual void next() {
931
if ( qp().indexKey().firstElement().fieldName() == string( "$natural" ) )
932
massert( 10410 , "throw", false );
935
virtual QueryOp *clone() const {
938
virtual bool mayRecordPlan() const { return true; }
940
class ScanOnlyTestOp : public TestOp {
941
virtual void next() {
942
if ( qp().indexKey().firstElement().fieldName() == string( "$natural" ) )
944
massert( 10411 , "throw", false );
946
virtual QueryOp *clone() const {
947
return new ScanOnlyTestOp();
952
class FindOne : public Base {
955
BSONObj one = BSON( "a" << 1 );
956
theDataFileMgr.insert( ns(), one );
958
ASSERT( Helpers::findOne( ns(), BSON( "a" << 1 ), result ) );
959
ASSERT_EXCEPTION( Helpers::findOne( ns(), BSON( "a" << 1 ), result, true ), AssertionException );
960
Helpers::ensureIndex( ns(), BSON( "a" << 1 ), false, "a_1" );
961
ASSERT( Helpers::findOne( ns(), BSON( "a" << 1 ), result, true ) );
965
class Delete : public Base {
968
Helpers::ensureIndex( ns(), BSON( "a" << 1 ), false, "a_1" );
969
for( int i = 0; i < 200; ++i ) {
970
BSONObj two = BSON( "a" << 2 );
971
theDataFileMgr.insert( ns(), two );
973
BSONObj one = BSON( "a" << 1 );
974
theDataFileMgr.insert( ns(), one );
975
deleteObjects( ns(), BSON( "a" << 1 ), false );
976
ASSERT( BSON( "a" << 1 ).woCompare( NamespaceDetailsTransient::_get( ns() ).indexForPattern( FieldRangeSet( ns(), BSON( "a" << 1 ) ).pattern() ) ) == 0 );
977
ASSERT_EQUALS( 2, NamespaceDetailsTransient::_get( ns() ).nScannedForPattern( FieldRangeSet( ns(), BSON( "a" << 1 ) ).pattern() ) );
981
class DeleteOneScan : public Base {
984
Helpers::ensureIndex( ns(), BSON( "_id" << 1 ), false, "_id_1" );
985
BSONObj one = BSON( "_id" << 3 << "a" << 1 );
986
BSONObj two = BSON( "_id" << 2 << "a" << 1 );
987
BSONObj three = BSON( "_id" << 1 << "a" << -1 );
988
theDataFileMgr.insert( ns(), one );
989
theDataFileMgr.insert( ns(), two );
990
theDataFileMgr.insert( ns(), three );
991
deleteObjects( ns(), BSON( "_id" << GT << 0 << "a" << GT << 0 ), true );
992
for( auto_ptr< Cursor > c = theDataFileMgr.findAll( ns() ); c->ok(); c->advance() )
993
ASSERT( 3 != c->current().getIntField( "_id" ) );
997
class DeleteOneIndex : public Base {
1000
Helpers::ensureIndex( ns(), BSON( "a" << 1 ), false, "a" );
1001
BSONObj one = BSON( "a" << 2 << "_id" << 0 );
1002
BSONObj two = BSON( "a" << 1 << "_id" << 1 );
1003
BSONObj three = BSON( "a" << 0 << "_id" << 2 );
1004
theDataFileMgr.insert( ns(), one );
1005
theDataFileMgr.insert( ns(), two );
1006
theDataFileMgr.insert( ns(), three );
1007
deleteObjects( ns(), BSON( "a" << GTE << 0 << "_id" << GT << 0 ), true );
1008
for( auto_ptr< Cursor > c = theDataFileMgr.findAll( ns() ); c->ok(); c->advance() )
1009
ASSERT( 2 != c->current().getIntField( "_id" ) );
1013
class TryOtherPlansBeforeFinish : public Base {
1016
Helpers::ensureIndex( ns(), BSON( "a" << 1 ), false, "a_1" );
1017
for( int i = 0; i < 100; ++i ) {
1018
for( int j = 0; j < 2; ++j ) {
1019
BSONObj temp = BSON( "a" << 100 - i - 1 << "b" << i );
1020
theDataFileMgr.insert( ns(), temp );
1024
// Need to return at least 2 records to cause plan to be recorded.
1025
assembleRequest( ns(), QUERY( "b" << 0 << "a" << GTE << 0 ).obj, 2, 0, 0, 0, m );
1032
ASSERT( BSON( "$natural" << 1 ).woCompare( NamespaceDetailsTransient::_get( ns() ).indexForPattern( FieldRangeSet( ns(), BSON( "b" << 0 << "a" << GTE << 0 ) ).pattern() ) ) == 0 );
1035
assembleRequest( ns(), QUERY( "b" << 99 << "a" << GTE << 0 ).obj, 2, 0, 0, 0, m2 );
1041
ASSERT( BSON( "a" << 1 ).woCompare( NamespaceDetailsTransient::_get( ns() ).indexForPattern( FieldRangeSet( ns(), BSON( "b" << 0 << "a" << GTE << 0 ) ).pattern() ) ) == 0 );
1042
ASSERT_EQUALS( 2, NamespaceDetailsTransient::_get( ns() ).nScannedForPattern( FieldRangeSet( ns(), BSON( "b" << 0 << "a" << GTE << 0 ) ).pattern() ) );
1046
class InQueryIntervals : public Base {
1049
Helpers::ensureIndex( ns(), BSON( "a" << 1 ), false, "a_1" );
1050
for( int i = 0; i < 10; ++i ) {
1051
BSONObj temp = BSON( "a" << i );
1052
theDataFileMgr.insert( ns(), temp );
1054
BSONObj hint = fromjson( "{$hint:{a:1}}" );
1055
BSONElement hintElt = hint.firstElement();
1056
QueryPlanSet s( ns(), fromjson( "{a:{$in:[2,3,6,9,11]}}" ), BSONObj(), &hintElt );
1057
QueryPlan qp( nsd(), 1, s.fbs(), BSONObj() );
1058
auto_ptr< Cursor > c = qp.newCursor();
1059
double expected[] = { 2, 3, 6, 9 };
1060
for( int i = 0; i < 4; ++i, c->advance() ) {
1061
ASSERT_EQUALS( expected[ i ], c->current().getField( "a" ).number() );
1065
// now check reverse
1067
QueryPlanSet s( ns(), fromjson( "{a:{$in:[2,3,6,9,11]}}" ), BSON( "a" << -1 ), &hintElt );
1068
QueryPlan qp( nsd(), 1, s.fbs(), BSON( "a" << -1 ) );
1069
auto_ptr< Cursor > c = qp.newCursor();
1070
double expected[] = { 9, 6, 3, 2 };
1071
for( int i = 0; i < 4; ++i, c->advance() ) {
1072
ASSERT_EQUALS( expected[ i ], c->current().getField( "a" ).number() );
1079
class EqualityThenIn : public Base {
1082
Helpers::ensureIndex( ns(), BSON( "a" << 1 << "b" << 1 ), false, "a_1_b_1" );
1083
for( int i = 0; i < 10; ++i ) {
1084
BSONObj temp = BSON( "a" << 5 << "b" << i );
1085
theDataFileMgr.insert( ns(), temp );
1087
BSONObj hint = fromjson( "{$hint:{a:1,b:1}}" );
1088
BSONElement hintElt = hint.firstElement();
1089
QueryPlanSet s( ns(), fromjson( "{a:5,b:{$in:[2,3,6,9,11]}}" ), BSONObj(), &hintElt );
1090
QueryPlan qp( nsd(), 1, s.fbs(), BSONObj() );
1091
auto_ptr< Cursor > c = qp.newCursor();
1092
double expected[] = { 2, 3, 6, 9 };
1093
for( int i = 0; i < 4; ++i, c->advance() ) {
1094
ASSERT_EQUALS( expected[ i ], c->current().getField( "b" ).number() );
1100
class NotEqualityThenIn : public Base {
1103
Helpers::ensureIndex( ns(), BSON( "a" << 1 << "b" << 1 ), false, "a_1_b_1" );
1104
for( int i = 0; i < 10; ++i ) {
1105
BSONObj temp = BSON( "a" << 5 << "b" << i );
1106
theDataFileMgr.insert( ns(), temp );
1108
BSONObj hint = fromjson( "{$hint:{a:1,b:1}}" );
1109
BSONElement hintElt = hint.firstElement();
1110
QueryPlanSet s( ns(), fromjson( "{a:{$gte:5},b:{$in:[2,3,6,9,11]}}" ), BSONObj(), &hintElt );
1111
QueryPlan qp( nsd(), 1, s.fbs(), BSONObj() );
1112
auto_ptr< Cursor > c = qp.newCursor();
1113
for( int i = 2; i < 10; ++i, c->advance() ) {
1114
ASSERT_EQUALS( i, c->current().getField( "b" ).number() );
1120
} // namespace QueryPlanSetTests
1122
class All : public Suite {
1124
All() : Suite( "queryoptimizer" ){}
1127
add< FieldRangeTests::Empty >();
1128
add< FieldRangeTests::Eq >();
1129
add< FieldRangeTests::DupEq >();
1130
add< FieldRangeTests::Lt >();
1131
add< FieldRangeTests::Lte >();
1132
add< FieldRangeTests::Gt >();
1133
add< FieldRangeTests::Gte >();
1134
add< FieldRangeTests::TwoLt >();
1135
add< FieldRangeTests::TwoGt >();
1136
add< FieldRangeTests::EqGte >();
1137
add< FieldRangeTests::EqGteInvalid >();
1138
add< FieldRangeTests::Regex >();
1139
add< FieldRangeTests::RegexObj >();
1140
add< FieldRangeTests::UnhelpfulRegex >();
1141
add< FieldRangeTests::In >();
1142
add< FieldRangeTests::Equality >();
1143
add< FieldRangeTests::SimplifiedQuery >();
1144
add< FieldRangeTests::QueryPatternTest >();
1145
add< FieldRangeTests::NoWhere >();
1146
add< FieldRangeTests::Numeric >();
1147
add< FieldRangeTests::InLowerBound >();
1148
add< FieldRangeTests::InUpperBound >();
1149
add< FieldRangeTests::MultiBound >();
1150
add< QueryPlanTests::NoIndex >();
1151
add< QueryPlanTests::SimpleOrder >();
1152
add< QueryPlanTests::MoreIndexThanNeeded >();
1153
add< QueryPlanTests::IndexSigns >();
1154
add< QueryPlanTests::IndexReverse >();
1155
add< QueryPlanTests::NoOrder >();
1156
add< QueryPlanTests::EqualWithOrder >();
1157
add< QueryPlanTests::Optimal >();
1158
add< QueryPlanTests::MoreOptimal >();
1159
add< QueryPlanTests::KeyMatch >();
1160
add< QueryPlanTests::MoreKeyMatch >();
1161
add< QueryPlanTests::ExactKeyQueryTypes >();
1162
add< QueryPlanTests::Unhelpful >();
1163
add< QueryPlanSetTests::NoIndexes >();
1164
add< QueryPlanSetTests::Optimal >();
1165
add< QueryPlanSetTests::NoOptimal >();
1166
add< QueryPlanSetTests::NoSpec >();
1167
add< QueryPlanSetTests::HintSpec >();
1168
add< QueryPlanSetTests::HintName >();
1169
add< QueryPlanSetTests::NaturalHint >();
1170
add< QueryPlanSetTests::NaturalSort >();
1171
add< QueryPlanSetTests::BadHint >();
1172
add< QueryPlanSetTests::Count >();
1173
add< QueryPlanSetTests::QueryMissingNs >();
1174
add< QueryPlanSetTests::UnhelpfulIndex >();
1175
add< QueryPlanSetTests::SingleException >();
1176
add< QueryPlanSetTests::AllException >();
1177
add< QueryPlanSetTests::SaveGoodIndex >();
1178
add< QueryPlanSetTests::TryAllPlansOnErr >();
1179
add< QueryPlanSetTests::FindOne >();
1180
add< QueryPlanSetTests::Delete >();
1181
add< QueryPlanSetTests::DeleteOneScan >();
1182
add< QueryPlanSetTests::DeleteOneIndex >();
1183
add< QueryPlanSetTests::TryOtherPlansBeforeFinish >();
1184
add< QueryPlanSetTests::InQueryIntervals >();
1185
add< QueryPlanSetTests::EqualityThenIn >();
1186
add< QueryPlanSetTests::NotEqualityThenIn >();
1190
} // namespace QueryOptimizerTests