// @file explain.cpp - Helper classes for generating query explain output. /* Copyright 2012 10gen Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "explain.h" #include "cmdline.h" #include "../util/net/sock.h" #include "../util/mongoutils/str.h" namespace mongo { // !!! TODO get rid of const casts ExplainPlanInfo::ExplainPlanInfo() : _isMultiKey(), _n(), _nscannedObjects(), _nscanned(), _scanAndOrder(), _indexOnly(), _picked(), _done() { } void ExplainPlanInfo::notePlan( const Cursor &cursor, bool scanAndOrder, bool indexOnly ) { _cursorName = const_cast(cursor).toString(); _indexBounds = cursor.prettyIndexBounds().getOwned(); _scanAndOrder = scanAndOrder; _indexOnly = indexOnly; noteCursorUpdate( cursor ); } void ExplainPlanInfo::noteIterate( bool match, bool loadedRecord, const Cursor &cursor ) { if ( match ) { ++_n; } if ( loadedRecord ) { ++_nscannedObjects; } noteCursorUpdate( cursor ); } void ExplainPlanInfo::noteDone( const Cursor &cursor ) { _done = true; noteCursorUpdate( cursor ); BSONObjBuilder bob; const_cast(cursor).explainDetails( bob ); _details = bob.obj(); } void ExplainPlanInfo::notePicked() { _picked = true; } BSONObj ExplainPlanInfo::bson() const { BSONObjBuilder bob; bob.append( "cursor", _cursorName ); bob.appendNumber( "n", _n ); bob.appendNumber( "nscannedObjects", _nscannedObjects ); bob.appendNumber( "nscanned", _nscanned ); bob.append( "indexBounds", _indexBounds ); return bob.obj(); } BSONObj ExplainPlanInfo::pickedPlanBson( const ExplainClauseInfo &clauseInfo ) const { BSONObjBuilder bob; bob.append( "cursor", _cursorName ); bob.append( "isMultiKey", _isMultiKey ); bob.appendNumber( "n", clauseInfo.n() ); bob.appendNumber( "nscannedObjects", clauseInfo.nscannedObjects() ); bob.appendNumber( "nscanned", clauseInfo.nscanned() ); bob.appendNumber( "nscannedObjectsAllPlans", clauseInfo.nscannedObjectsAllPlans() ); bob.appendNumber( "nscannedAllPlans", clauseInfo.nscannedAllPlans() ); bob.append( "scanAndOrder", _scanAndOrder ); bob.append( "indexOnly", _indexOnly ); bob.appendNumber( "nYields", clauseInfo.nYields() ); bob.appendNumber( "nChunkSkips", clauseInfo.nChunkSkips() ); bob.appendNumber( "millis", clauseInfo.millis() ); bob.append( "indexBounds", _indexBounds ); bob.appendElements( _details ); return bob.obj(); } void ExplainPlanInfo::noteCursorUpdate( const Cursor &cursor ) { _isMultiKey = cursor.isMultiKey(); _nscanned = const_cast(cursor).nscanned(); } ExplainClauseInfo::ExplainClauseInfo() : _n(), _nscannedObjects(), _nChunkSkips(), _nYields() { } BSONObj ExplainClauseInfo::bson() const { BSONObjBuilder bb; bb.appendElements( virtualPickedPlan().pickedPlanBson( *this ) ); BSONArrayBuilder allPlans( bb.subarrayStart( "allPlans" ) ); for( list >::const_iterator i = _plans.begin(); i != _plans.end(); ++i ) { allPlans << (*i)->bson(); } allPlans.done(); return bb.obj(); } void ExplainClauseInfo::addPlanInfo( const shared_ptr &info ) { _plans.push_back( info ); } void ExplainClauseInfo::noteYield() { ++_nYields; } void ExplainClauseInfo::noteIterate( bool match, bool loadedRecord, bool chunkSkip ) { if ( match ) { ++_n; } if ( loadedRecord ) { ++_nscannedObjects; } if ( chunkSkip ) { ++_nChunkSkips; } } void ExplainClauseInfo::reviseN( long long n ) { _n = n; } void ExplainClauseInfo::stopTimer() { _timer.stop(); } long long ExplainClauseInfo::nscannedObjects() const { if ( _plans.empty() ) { return 0; } return virtualPickedPlan().nscannedObjects(); } long long ExplainClauseInfo::nscanned() const { if ( _plans.empty() ) { return 0; } return virtualPickedPlan().nscanned(); } long long ExplainClauseInfo::nscannedAllPlans() const { long long ret = 0; for( list >::const_iterator i = _plans.begin(); i != _plans.end(); ++i ) { ret += (*i)->nscanned(); } return ret; } const ExplainPlanInfo &ExplainClauseInfo::virtualPickedPlan() const { // Return a picked plan if possible. for( list >::const_iterator i = _plans.begin(); i != _plans.end(); ++i ) { if ( (*i)->picked() ) { return **i; } } // Return a done plan if possible. for( list >::const_iterator i = _plans.begin(); i != _plans.end(); ++i ) { if ( (*i)->done() ) { return **i; } } // Return a plan with the highest match count. long long maxN = -1; shared_ptr ret; for( list >::const_iterator i = _plans.begin(); i != _plans.end(); ++i ) { long long n = ( *i )->n(); if ( n > maxN ) { maxN = n; ret = *i; } } verify( ret ); return *ret; } void ExplainQueryInfo::noteIterate( bool match, bool loadedRecord, bool chunkSkip ) { verify( !_clauses.empty() ); _clauses.back()->noteIterate( match, loadedRecord, chunkSkip ); } void ExplainQueryInfo::noteYield() { verify( !_clauses.empty() ); _clauses.back()->noteYield(); } void ExplainQueryInfo::reviseN( long long n ) { verify( !_clauses.empty() ); _clauses.back()->reviseN( n ); } void ExplainQueryInfo::setAncillaryInfo( const AncillaryInfo &ancillaryInfo ) { _ancillaryInfo = ancillaryInfo; } BSONObj ExplainQueryInfo::bson() const { BSONObjBuilder bob; if ( _clauses.size() == 1 ) { bob.appendElements( _clauses.front()->bson() ); } else { long long n = 0; long long nscannedObjects = 0; long long nscanned = 0; long long nscannedObjectsAllPlans = 0; long long nscannedAllPlans = 0; BSONArrayBuilder clauseArray( bob.subarrayStart( "clauses" ) ); for( list >::const_iterator i = _clauses.begin(); i != _clauses.end(); ++i ) { clauseArray << (*i)->bson(); n += (*i)->n(); nscannedObjects += (*i)->nscannedObjects(); nscanned += (*i)->nscanned(); nscannedObjectsAllPlans += (*i)->nscannedObjectsAllPlans(); nscannedAllPlans += (*i)->nscannedAllPlans(); } clauseArray.done(); bob.appendNumber( "n", n ); bob.appendNumber( "nscannedObjects", nscannedObjects ); bob.appendNumber( "nscanned", nscanned ); bob.appendNumber( "nscannedObjectsAllPlans", nscannedObjectsAllPlans ); bob.appendNumber( "nscannedAllPlans", nscannedAllPlans ); bob.appendNumber( "millis", _timer.duration() ); } if ( !_ancillaryInfo._oldPlan.isEmpty() ) { bob.append( "oldPlan", _ancillaryInfo._oldPlan ); } bob.append( "server", server() ); return bob.obj(); } void ExplainQueryInfo::addClauseInfo( const shared_ptr &info ) { if ( !_clauses.empty() ) { _clauses.back()->stopTimer(); } _clauses.push_back( info ); } string ExplainQueryInfo::server() { return mongoutils::str::stream() << getHostNameCached() << ":" << cmdLine.port; } ExplainSinglePlanQueryInfo::ExplainSinglePlanQueryInfo() : _planInfo( new ExplainPlanInfo() ), _queryInfo( new ExplainQueryInfo() ) { shared_ptr clauseInfo( new ExplainClauseInfo() ); clauseInfo->addPlanInfo( _planInfo ); _queryInfo->addClauseInfo( clauseInfo ); } } // namespace mongo