Skip to content

Commit 83a9a19

Browse files
committed
Merge remote branch 'main_readonly/master'
2 parents 8c62f17 + b8d0917 commit 83a9a19

File tree

8 files changed

+134
-71
lines changed

8 files changed

+134
-71
lines changed

jstests/explain4.js

Lines changed: 20 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,43 +1,43 @@
1-
// Basic validation of explain output fields
1+
// Basic validation of explain output fields.
22

33
t = db.jstests_explain4;
44
t.drop();
55

6-
function checkField( name, value ) {
6+
function checkField( explain, name, value ) {
77
assert( explain.hasOwnProperty( name ) );
88
if ( value != null ) {
99
assert.eq( value, explain[ name ], name );
1010
}
1111
}
1212

1313
function checkPlanFields( explain, matches, n ) {
14-
checkField( "cursor", "BasicCursor" );
15-
checkField( "n", n );
16-
checkField( "nscannedObjects", matches );
17-
checkField( "nscanned", matches );
18-
checkField( "indexBounds", {} );
14+
checkField( explain, "cursor", "BasicCursor" );
15+
checkField( explain, "n", n );
16+
checkField( explain, "nscannedObjects", matches );
17+
checkField( explain, "nscanned", matches );
18+
checkField( explain, "indexBounds", {} );
1919
}
2020

2121
function checkFields( matches, sort, limit ) {
22-
it = t.find();
22+
cursor = t.find();
2323
if ( sort ) {
24-
it.sort({a:1});
24+
cursor.sort({a:1});
2525
}
2626
if ( limit ) {
27-
it.limit( limit );
27+
cursor.limit( limit );
2828
}
29-
explain = it.explain( true );
29+
explain = cursor.explain( true );
3030
// printjson( explain );
3131
checkPlanFields( explain, matches, matches > 0 ? 1 : 0 );
32-
checkField( "scanAndOrder", sort );
33-
checkField( "millis" );
34-
checkField( "nYields" );
35-
checkField( "nChunkSkips", 0 );
36-
checkField( "isMultiKey", false );
37-
checkField( "indexOnly", false );
38-
checkField( "server" );
39-
checkField( "allPlans" );
40-
explain.allPlans.forEach( function( x ) { checkPlanFields( x, matches ); } );
32+
checkField( explain, "scanAndOrder", sort );
33+
checkField( explain, "millis" );
34+
checkField( explain, "nYields" );
35+
checkField( explain, "nChunkSkips", 0 );
36+
checkField( explain, "isMultiKey", false );
37+
checkField( explain, "indexOnly", false );
38+
checkField( explain, "server" );
39+
checkField( explain, "allPlans" );
40+
explain.allPlans.forEach( function( x ) { checkPlanFields( x, matches, matches ); } );
4141
}
4242

4343
checkFields( 0, false );

jstests/explain5.js

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,20 @@ for( i = 0; i < 10; ++i ) {
1111
t.save( {} );
1212
}
1313

14+
// Check with a single in order plan.
15+
16+
explain = t.find( {a:{$gt:0}} ).explain( true );
17+
assert.eq( 1, explain.n );
18+
assert.eq( 1, explain.allPlans[ 0 ].n );
19+
20+
// Check with a single out of order plan.
21+
22+
explain = t.find( {a:{$gt:0}} ).sort( {z:1} ).hint( {a:1} ).explain( true );
23+
assert.eq( 1, explain.n );
24+
assert.eq( 1, explain.allPlans[ 0 ].n );
25+
26+
// Check with multiple plans.
27+
1428
explain = t.find( {a:{$gt:0},b:{$gt:0}} ).explain( true );
1529
assert.eq( 1, explain.n );
1630
assert.eq( 1, explain.allPlans[ 0 ].n );

jstests/explain6.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,4 +20,4 @@ t.dropIndexes();
2020
explain = t.find().skip( 1 ).sort({a:1}).explain( true );
2121
// Skip is applied for an in memory sort.
2222
assert.eq( 0, explain.n );
23-
assert.eq( 0, explain.allPlans[ 0 ].n );
23+
assert.eq( 1, explain.allPlans[ 0 ].n );

jstests/explain9.js

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// Test that limit is applied by explain when there are both in order and out of order candidate
2+
// plans. SERVER-4150
3+
4+
t = db.jstests_explain9;
5+
t.drop();
6+
7+
t.ensureIndex( { a:1 } );
8+
9+
for( i = 0; i < 10; ++i ) {
10+
t.save( { a:i, b:0 } );
11+
}
12+
13+
explain = t.find( { a:{ $gte:0 }, b:0 } ).sort( { a:1 } ).limit( 5 ).explain( true );
14+
// Five results are expected, matching the limit spec.
15+
assert.eq( 5, explain.n );
16+
explain.allPlans.forEach( function( x ) {
17+
// Five results are expected for the in order plan.
18+
if ( x.cursor == "BtreeCursor a_1" ) {
19+
assert.eq( 5, x.n );
20+
}
21+
else {
22+
assert.gte( 5, x.n );
23+
}
24+
} );

jstests/updatek.js

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,12 @@ t = db.jstests_updatek;
55
t.drop();
66
t.save( { _id:0, '1':{}, '01':{} } );
77
t.update( {}, { $set:{ '1.b':1, '1.c':2 } } );
8-
assert.eq( { '01':{}, '1':{ b:1, c:2 }, _id:0 }, t.findOne() );
8+
// make sure correct fields are set, with no duplicates
9+
// must use string comparison because V8 reorders fields that are numerical in objects
10+
assert.eq( "{ \"01\" : { }, \"1\" : { \"b\" : 1, \"c\" : 2 }, \"_id\" : 0 }", tojson(t.findOne()) );
911

1012
t.drop();
1113
t.save( { _id:0, '1':{}, '01':{} } );
1214
t.update( {}, { $set:{ '1.b':1, '01.c':2 } } );
13-
assert.eq( { '01':{ c:2 }, '1':{ b:1 }, _id:0 }, t.findOne() );
15+
assert.eq( "{ \"01\" : { \"c\" : 2 }, \"1\" : { \"b\" : 1 }, \"_id\" : 0 }", tojson(t.findOne()) );
16+

src/mongo/db/ops/query.cpp

Lines changed: 38 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -234,13 +234,14 @@ namespace mongo {
234234
MatchCountingExplainStrategy::MatchCountingExplainStrategy
235235
( const ExplainQueryInfo::AncillaryInfo &ancillaryInfo ) :
236236
ExplainRecordingStrategy( ancillaryInfo ),
237-
_matches() {
237+
_orderedMatches() {
238238
}
239239

240-
void MatchCountingExplainStrategy::noteIterate( bool match, bool loadedObject, bool chunkSkip ) {
241-
_noteIterate( match, loadedObject, chunkSkip );
242-
if ( match ) {
243-
++_matches;
240+
void MatchCountingExplainStrategy::noteIterate( bool match, bool orderedMatch,
241+
bool loadedObject, bool chunkSkip ) {
242+
_noteIterate( match, orderedMatch, loadedObject, chunkSkip );
243+
if ( orderedMatch ) {
244+
++_orderedMatches;
244245
}
245246
}
246247

@@ -256,7 +257,8 @@ namespace mongo {
256257
_explainInfo->notePlan( *_cursor, scanAndOrder, indexOnly );
257258
}
258259

259-
void SimpleCursorExplainStrategy::_noteIterate( bool match, bool loadedObject, bool chunkSkip ) {
260+
void SimpleCursorExplainStrategy::_noteIterate( bool match, bool orderedMatch,
261+
bool loadedObject, bool chunkSkip ) {
260262
_explainInfo->noteIterate( match, loadedObject, chunkSkip, *_cursor );
261263
}
262264

@@ -276,9 +278,11 @@ namespace mongo {
276278
_cursor( cursor ) {
277279
}
278280

279-
void QueryOptimizerCursorExplainStrategy::_noteIterate( bool match, bool loadedObject,
280-
bool chunkSkip ) {
281-
_cursor->noteIterate( match, loadedObject, chunkSkip );
281+
void QueryOptimizerCursorExplainStrategy::_noteIterate( bool match, bool orderedMatch,
282+
bool loadedObject, bool chunkSkip ) {
283+
// Note ordered matches only; if an unordered plan is selected, the explain result will
284+
// be updated with reviseN().
285+
_cursor->noteIterate( orderedMatch, loadedObject, chunkSkip );
282286
}
283287

284288
shared_ptr<ExplainQueryInfo> QueryOptimizerCursorExplainStrategy::_doneQueryInfo() {
@@ -336,22 +340,22 @@ namespace mongo {
336340
_bufferedMatches() {
337341
}
338342

339-
bool OrderedBuildStrategy::handleMatch() {
343+
bool OrderedBuildStrategy::handleMatch( bool &orderedMatch ) {
340344
DiskLoc loc = _cursor->currLoc();
341345
if ( _cursor->getsetdup( loc ) ) {
342-
return false;
346+
return orderedMatch = false;
343347
}
344348
if ( _skip > 0 ) {
345349
--_skip;
346-
return false;
350+
return orderedMatch = false;
347351
}
348352
// Explain does not obey soft limits, so matches should not be buffered.
349353
if ( !_parsedQuery.isExplain() ) {
350354
fillQueryResultFromObj( _buf, _parsedQuery.getFields(), current( true ),
351355
( _parsedQuery.showDiskLoc() ? &loc : 0 ) );
352356
++_bufferedMatches;
353357
}
354-
return true;
358+
return orderedMatch = true;
355359
}
356360

357361
ReorderBuildStrategy::ReorderBuildStrategy( const ParsedQuery &parsedQuery,
@@ -363,17 +367,18 @@ namespace mongo {
363367
_bufferedMatches() {
364368
}
365369

366-
bool ReorderBuildStrategy::handleMatch() {
370+
bool ReorderBuildStrategy::handleMatch( bool &orderedMatch ) {
371+
orderedMatch = false;
367372
if ( _cursor->getsetdup( _cursor->currLoc() ) ) {
368373
return false;
369374
}
370-
return _handleMatchNoDedup();
375+
_handleMatchNoDedup();
376+
return true;
371377
}
372378

373-
bool ReorderBuildStrategy::_handleMatchNoDedup() {
379+
void ReorderBuildStrategy::_handleMatchNoDedup() {
374380
DiskLoc loc = _cursor->currLoc();
375381
_scanAndOrder->add( current( false ), _parsedQuery.showDiskLoc() ? &loc : 0 );
376-
return false;
377382
}
378383

379384
int ReorderBuildStrategy::rewriteMatches() {
@@ -412,18 +417,18 @@ namespace mongo {
412417
_reorderedMatches() {
413418
}
414419

415-
bool HybridBuildStrategy::handleMatch() {
420+
bool HybridBuildStrategy::handleMatch( bool &orderedMatch ) {
416421
if ( !_queryOptimizerCursor->currentPlanScanAndOrderRequired() ) {
417-
return _orderedBuild.handleMatch();
422+
return _orderedBuild.handleMatch( orderedMatch );
418423
}
419-
handleReorderMatch();
420-
return false;
424+
orderedMatch = false;
425+
return handleReorderMatch();
421426
}
422427

423-
void HybridBuildStrategy::handleReorderMatch() {
428+
bool HybridBuildStrategy::handleReorderMatch() {
424429
DiskLoc loc = _cursor->currLoc();
425430
if ( _scanAndOrderDups.getsetdup( loc ) ) {
426-
return;
431+
return false;
427432
}
428433
try {
429434
_reorderBuild._handleMatchNoDedup();
@@ -435,11 +440,12 @@ namespace mongo {
435440
}
436441
else if ( _queryOptimizerCursor->runningInitialInOrderPlan() ) {
437442
_queryOptimizerCursor->abortOutOfOrderPlans();
438-
return;
443+
return true;
439444
}
440445
}
441446
throw;
442-
}
447+
}
448+
return true;
443449
}
444450

445451
int HybridBuildStrategy::rewriteMatches() {
@@ -482,9 +488,10 @@ namespace mongo {
482488
if ( !chunkMatches() ) {
483489
return false;
484490
}
485-
bool orderedMatch = _builder->handleMatch();
486-
_explain->noteIterate( orderedMatch, true, false );
487-
return true;
491+
bool orderedMatch = false;
492+
bool match = _builder->handleMatch( orderedMatch );
493+
_explain->noteIterate( match, orderedMatch, true, false );
494+
return match;
488495
}
489496

490497
void QueryResponseBuilder::noteYield() {
@@ -497,7 +504,7 @@ namespace mongo {
497504

498505
bool QueryResponseBuilder::enoughTotalResults() const {
499506
if ( _parsedQuery.isExplain() ) {
500-
return _parsedQuery.enoughForExplain( _explain->matches() );
507+
return _parsedQuery.enoughForExplain( _explain->orderedMatches() );
501508
}
502509
return ( _parsedQuery.enough( _builder->bufferedMatches() ) ||
503510
_buf.len() >= MaxBytesToReturnToClientAtOnce );
@@ -584,7 +591,7 @@ namespace mongo {
584591
if ( _cursor->currentMatches( &details ) ) {
585592
return true;
586593
}
587-
_explain->noteIterate( false, details._loadedObject, false );
594+
_explain->noteIterate( false, false, details._loadedObject, false );
588595
return false;
589596
}
590597

@@ -596,7 +603,7 @@ namespace mongo {
596603
if ( _chunkManager->belongsToMe( _cursor->current() ) ) {
597604
return true;
598605
}
599-
_explain->noteIterate( false, true, true );
606+
_explain->noteIterate( false, false, true, true );
600607
return false;
601608
}
602609

0 commit comments

Comments
 (0)