diff options
Diffstat (limited to 'src/mongo')
-rw-r--r-- | src/mongo/client/parallel.cpp | 59 | ||||
-rw-r--r-- | src/mongo/db/commands/count.cpp | 1 | ||||
-rw-r--r-- | src/mongo/db/exec/index_scan.cpp | 10 | ||||
-rw-r--r-- | src/mongo/db/exec/multi_plan.cpp | 14 | ||||
-rw-r--r-- | src/mongo/db/exec/subplan.cpp | 8 | ||||
-rw-r--r-- | src/mongo/db/pipeline/document_source_cursor.cpp | 1 | ||||
-rw-r--r-- | src/mongo/db/query/SConscript | 2 | ||||
-rw-r--r-- | src/mongo/db/query/explain.cpp | 90 | ||||
-rw-r--r-- | src/mongo/db/query/explain.h | 15 | ||||
-rw-r--r-- | src/mongo/db/query/explain_plan.cpp | 322 | ||||
-rw-r--r-- | src/mongo/db/query/explain_plan.h | 60 | ||||
-rw-r--r-- | src/mongo/db/query/index_bounds_builder_test.cpp | 50 | ||||
-rw-r--r-- | src/mongo/db/query/new_find.cpp | 221 | ||||
-rw-r--r-- | src/mongo/db/query/planner_analysis.cpp | 8 | ||||
-rw-r--r-- | src/mongo/db/query/query_planner_test.cpp | 90 | ||||
-rw-r--r-- | src/mongo/db/query/type_explain.cpp | 807 | ||||
-rw-r--r-- | src/mongo/db/query/type_explain.h | 292 | ||||
-rw-r--r-- | src/mongo/dbtests/querytests.cpp | 25 | ||||
-rw-r--r-- | src/mongo/s/strategy.cpp | 2 | ||||
-rw-r--r-- | src/mongo/shell/shardingtest.js | 16 |
20 files changed, 279 insertions, 1814 deletions
diff --git a/src/mongo/client/parallel.cpp b/src/mongo/client/parallel.cpp index 3b9acd836e1..9d385823081 100644 --- a/src/mongo/client/parallel.cpp +++ b/src/mongo/client/parallel.cpp @@ -121,11 +121,13 @@ namespace mongo { string cursorType; BSONObj indexBounds; BSONObj oldPlan; - + long long millis = 0; double numExplains = 0; - map<string,long long> counters; + long long nReturned = 0; + long long keysExamined = 0; + long long docsExamined = 0; map<string,list<BSONObj> > out; { @@ -151,31 +153,34 @@ namespace mongo { y.append( temp ); - BSONObjIterator k( temp ); - while ( k.more() ) { - BSONElement z = k.next(); - if ( z.fieldName()[0] != 'n' ) - continue; - long long& c = counters[z.fieldName()]; - c += z.numberLong(); + if (temp.hasField("executionStats")) { + // Here we assume that the shard gave us back explain 2.0 style output. + BSONObj execStats = temp["executionStats"].Obj(); + if (execStats.hasField("nReturned")) { + nReturned += execStats["nReturned"].numberLong(); + } + if (execStats.hasField("totalKeysExamined")) { + keysExamined += execStats["totalKeysExamined"].numberLong(); + } + if (execStats.hasField("totalDocsExamined")) { + docsExamined += execStats["totalDocsExamined"].numberLong(); + } } - - millis += temp["millis"].numberLong(); - numExplains++; - - if ( temp["cursor"].type() == String ) { - if ( cursorType.size() == 0 ) - cursorType = temp["cursor"].String(); - else if ( cursorType != temp["cursor"].String() ) - cursorType = "multiple"; + else { + // Here we assume that the shard gave us back explain 1.0 style output. + if (temp.hasField("n")) { + nReturned += temp["n"].numberLong(); + } + if (temp.hasField("nscanned")) { + keysExamined += temp["nscanned"].numberLong(); + } + if (temp.hasField("nscannedObjects")) { + docsExamined += temp["nscannedObjects"].numberLong(); + } } - if ( temp["indexBounds"].type() == Object ) - indexBounds = temp["indexBounds"].Obj(); - - if ( temp["oldPlan"].type() == Object ) - oldPlan = temp["oldPlan"].Obj(); - + millis += temp["executionStats"]["executionTimeMillis"].numberLong(); + numExplains++; } y.done(); } @@ -183,8 +188,10 @@ namespace mongo { } b.append( "cursor" , cursorType ); - for ( map<string,long long>::iterator i=counters.begin(); i!=counters.end(); ++i ) - b.appendNumber( i->first , i->second ); + + b.appendNumber( "nReturned" , nReturned ); + b.appendNumber( "totalKeysExamined" , keysExamined ); + b.appendNumber( "totalDocsExamined" , docsExamined ); b.appendNumber( "millisShardTotal" , millis ); b.append( "millisShardAvg" , diff --git a/src/mongo/db/commands/count.cpp b/src/mongo/db/commands/count.cpp index 89c0de4940b..df0ec5fe62c 100644 --- a/src/mongo/db/commands/count.cpp +++ b/src/mongo/db/commands/count.cpp @@ -35,7 +35,6 @@ #include "mongo/db/curop.h" #include "mongo/db/query/get_executor.h" #include "mongo/db/query/explain.h" -#include "mongo/db/query/type_explain.h" #include "mongo/util/log.h" namespace mongo { diff --git a/src/mongo/db/exec/index_scan.cpp b/src/mongo/db/exec/index_scan.cpp index e5a8cfe2019..9bedd8db6c7 100644 --- a/src/mongo/db/exec/index_scan.cpp +++ b/src/mongo/db/exec/index_scan.cpp @@ -33,7 +33,6 @@ #include "mongo/db/index/index_access_method.h" #include "mongo/db/index/index_cursor.h" #include "mongo/db/index/index_descriptor.h" -#include "mongo/db/query/explain.h" #include "mongo/util/log.h" namespace { @@ -381,14 +380,7 @@ namespace mongo { if (_specificStats.indexType.empty()) { _specificStats.indexType = "BtreeCursor"; // TODO amName; - // TODO this can be simplified once the new explain format is - // the default. Probably won't need to include explain.h here either. - if (enableNewExplain) { - _specificStats.indexBounds = _params.bounds.toBSON(); - } - else { - _specificStats.indexBounds = _params.bounds.toLegacyBSON(); - } + _specificStats.indexBounds = _params.bounds.toBSON(); _specificStats.indexBoundsVerbose = _params.bounds.toString(); _specificStats.direction = _params.direction; diff --git a/src/mongo/db/exec/multi_plan.cpp b/src/mongo/db/exec/multi_plan.cpp index 2dd765d9553..19c16bbc7d3 100644 --- a/src/mongo/db/exec/multi_plan.cpp +++ b/src/mongo/db/exec/multi_plan.cpp @@ -110,6 +110,7 @@ namespace mongo { if (!bestPlan.results.empty()) { *out = bestPlan.results.front(); bestPlan.results.pop_front(); + _commonStats.advanced++; return PlanStage::ADVANCED; } @@ -140,10 +141,23 @@ namespace mongo { _backupPlanIdx = kNoSuchPlan; } + // Increment stats. + if (PlanStage::ADVANCED == state) { + _commonStats.advanced++; + } + else if (PlanStage::NEED_TIME == state) { + _commonStats.needTime++; + } + return state; } void MultiPlanStage::pickBestPlan() { + // Adds the amount of time taken by pickBestPlan() to executionTimeMillis. There's lots of + // execution work that happens here, so this is needed for the time accounting to + // make sense. + ScopedTimer timer(&_commonStats.executionTimeMillis); + // Run each plan some number of times. This number is at least as great as // 'internalQueryPlanEvaluationWorks', but may be larger for big collections. size_t numWorks = internalQueryPlanEvaluationWorks; diff --git a/src/mongo/db/exec/subplan.cpp b/src/mongo/db/exec/subplan.cpp index 5d4bcbd2dc0..281c0baafae 100644 --- a/src/mongo/db/exec/subplan.cpp +++ b/src/mongo/db/exec/subplan.cpp @@ -148,6 +148,10 @@ namespace mongo { } Status SubplanStage::planSubqueries() { + // Adds the amount of time taken by planSubqueries() to executionTimeMillis. There's lots of + // work that happens here, so this is needed for the time accounting to make sense. + ScopedTimer timer(&_commonStats.executionTimeMillis); + MatchExpression* theOr = _query->root(); for (size_t i = 0; i < _plannerParams.indices.size(); ++i) { @@ -209,6 +213,10 @@ namespace mongo { } Status SubplanStage::pickBestPlan() { + // Adds the amount of time taken by pickBestPlan() to executionTimeMillis. There's lots of + // work that happens here, so this is needed for the time accounting to make sense. + ScopedTimer timer(&_commonStats.executionTimeMillis); + // This is what we annotate with the index selections and then turn into a solution. auto_ptr<OrMatchExpression> theOr( static_cast<OrMatchExpression*>(_query->root()->shallowClone())); diff --git a/src/mongo/db/pipeline/document_source_cursor.cpp b/src/mongo/db/pipeline/document_source_cursor.cpp index bae0e53a680..5e96f5e7cf2 100644 --- a/src/mongo/db/pipeline/document_source_cursor.cpp +++ b/src/mongo/db/pipeline/document_source_cursor.cpp @@ -35,7 +35,6 @@ #include "mongo/db/pipeline/document.h" #include "mongo/db/query/explain.h" #include "mongo/db/query/find_constants.h" -#include "mongo/db/query/type_explain.h" #include "mongo/db/storage_options.h" #include "mongo/s/d_state.h" #include "mongo/s/stale_exception.h" // for SendStaleConfigException diff --git a/src/mongo/db/query/SConscript b/src/mongo/db/query/SConscript index 96339abc388..bf0a38ee291 100644 --- a/src/mongo/db/query/SConscript +++ b/src/mongo/db/query/SConscript @@ -34,13 +34,11 @@ env.Library( target='query', source=[ "explain.cpp", - "explain_plan.cpp", "get_executor.cpp", "new_find.cpp", "plan_executor.cpp", "plan_ranker.cpp", "stage_builder.cpp", - "type_explain.cpp", ], LIBDEPS=[ "query_planner", diff --git a/src/mongo/db/query/explain.cpp b/src/mongo/db/query/explain.cpp index 12ae7b3884d..4a14debc1f6 100644 --- a/src/mongo/db/query/explain.cpp +++ b/src/mongo/db/query/explain.cpp @@ -31,7 +31,6 @@ #include "mongo/db/query/explain.h" #include "mongo/db/exec/multi_plan.h" -#include "mongo/db/query/explain_plan.h" #include "mongo/db/query/get_executor.h" #include "mongo/db/query/plan_executor.h" #include "mongo/db/query/query_planner.h" @@ -195,8 +194,6 @@ namespace mongo { using mongoutils::str::stream; - MONGO_EXPORT_SERVER_PARAMETER(enableNewExplain, bool, false); - // static void Explain::statsToBSON(const PlanStageStats& stats, Explain::Verbosity verbosity, @@ -545,7 +542,7 @@ namespace mongo { MultiPlanStage* mps = getMultiPlanStage(exec->getRootStage()); // The executionStats verbosity level requires that we run the winning plan - // until if finishes. + // until it finishes. if (verbosity >= Explain::EXEC_STATS) { Status s = exec->executePlan(); if (!s.isOK()) { @@ -672,89 +669,4 @@ namespace mongo { } } - // TODO: This is temporary and should get deleted. There are a few small ways in which - // this differs from 2.6 explain, but I'm not too worried because this entire format is - // going away soon: - // 1) 'indexBounds' field excluded from idhack explain. - // 2) 'filterSet' field (for index filters) excluded. - Status Explain::legacyExplain(PlanExecutor* exec, TypeExplain** explain) { - invariant(exec); - invariant(explain); - - scoped_ptr<PlanStageStats> stats(exec->getStats()); - if (NULL == stats.get()) { - return Status(ErrorCodes::InternalError, "no stats available to explain plan"); - } - - // Special explain format for EOF. - if (STAGE_EOF == stats->stageType) { - *explain = new TypeExplain(); - - // Fill in mandatory fields. - (*explain)->setN(0); - (*explain)->setNScannedObjects(0); - (*explain)->setNScanned(0); - - // Fill in all the main fields that don't have a default in the explain data structure. - (*explain)->setCursor("BasicCursor"); - (*explain)->setScanAndOrder(false); - (*explain)->setIsMultiKey(false); - (*explain)->setIndexOnly(false); - (*explain)->setNYields(0); - (*explain)->setNChunkSkips(0); - - TypeExplain* allPlans = new TypeExplain; - allPlans->setCursor("BasicCursor"); - (*explain)->addToAllPlans(allPlans); // ownership xfer - - (*explain)->setNScannedObjectsAllPlans(0); - (*explain)->setNScannedAllPlans(0); - - return Status::OK(); - } - - // Special explain format for idhack. - vector<PlanStageStats*> statNodes; - flattenStatsTree(stats.get(), &statNodes); - PlanStageStats* idhack = NULL; - for (size_t i = 0; i < statNodes.size(); i++) { - if (STAGE_IDHACK == statNodes[i]->stageType) { - idhack = statNodes[i]; - break; - } - } - - if (NULL != idhack) { - // Explain format does not match 2.4 and is intended - // to indicate clearly that the ID hack has been applied. - *explain = new TypeExplain(); - - IDHackStats* idhackStats = static_cast<IDHackStats*>(idhack->specific.get()); - - (*explain)->setCursor("IDCursor"); - (*explain)->setIDHack(true); - (*explain)->setN(stats->common.advanced); - (*explain)->setNScanned(idhackStats->keysExamined); - (*explain)->setNScannedObjects(idhackStats->docsExamined); - - return Status::OK(); - } - - Status status = explainPlan(*stats, explain, true /* full details */); - if (!status.isOK()) { - return status; - } - - // Fill in explain fields that are accounted by on the runner level. - TypeExplain* chosenPlan = NULL; - explainPlan(*stats, &chosenPlan, false /* no full details */); - if (chosenPlan) { - (*explain)->addToAllPlans(chosenPlan); - } - (*explain)->setNScannedObjectsAllPlans((*explain)->getNScannedObjects()); - (*explain)->setNScannedAllPlans((*explain)->getNScanned()); - - return Status::OK(); - } - } // namespace mongo diff --git a/src/mongo/db/query/explain.h b/src/mongo/db/query/explain.h index ec018f6af7a..f617809a34c 100644 --- a/src/mongo/db/query/explain.h +++ b/src/mongo/db/query/explain.h @@ -34,17 +34,12 @@ #include "mongo/db/query/plan_executor.h" #include "mongo/db/query/query_planner_params.h" #include "mongo/db/query/query_solution.h" -#include "mongo/db/query/type_explain.h" namespace mongo { class Collection; class OperationContext; - // Temporarily hide the new explain implementation behind a setParameter. - // TODO: take this out, and make the new implementation the default. - extern bool enableNewExplain; - /** * A container for the summary statistics that the profiler, slow query log, and * other non-explain debug mechanisms may want to collect. @@ -181,16 +176,6 @@ namespace mongo { */ static void explainCountEmptyQuery(BSONObjBuilder* out); - /** - * Generate the legacy explain format from a PlanExecutor. - * - * On success, the caller owns 'explain'. - * - * TODO: THIS IS TEMPORARY. Once the legacy explain code is deleted, we won't - * need this anymore. - */ - static Status legacyExplain(PlanExecutor* exec, TypeExplain** explain); - private: /** * Private helper that does the heavy-lifting for the public statsToBSON(...) functions diff --git a/src/mongo/db/query/explain_plan.cpp b/src/mongo/db/query/explain_plan.cpp deleted file mode 100644 index 4c3a9fb06f8..00000000000 --- a/src/mongo/db/query/explain_plan.cpp +++ /dev/null @@ -1,322 +0,0 @@ -/** - * Copyright (C) 2013-2014 MongoDB 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/>. - * - * As a special exception, the copyright holders give permission to link the - * code of portions of this program with the OpenSSL library under certain - * conditions as described in each individual source file and distribute - * linked combinations including the program with the OpenSSL library. You - * must comply with the GNU Affero General Public License in all respects for - * all of the code used other than as permitted herein. If you modify file(s) - * with this exception, you may extend this exception to your version of the - * file(s), but you are not obligated to do so. If you do not wish to do so, - * delete this exception statement from your version. If you delete this - * exception statement from all source files in the program, then also delete - * it in the license file. - */ - -// THIS FILE IS DEPRECATED -- the old explain implementation is being replaced - -#include "mongo/db/query/explain_plan.h" - -#include "mongo/db/query/explain.h" -#include "mongo/db/query/stage_types.h" -#include "mongo/db/query/type_explain.h" -#include "mongo/util/mongoutils/str.h" - -namespace mongo { - - using mongoutils::str::stream; - - namespace { - - bool isOrStage(StageType stageType) { - return stageType == STAGE_OR || stageType == STAGE_SORT_MERGE; - } - - bool isNearStage(StageType stageType) { - return stageType == STAGE_GEO_NEAR_2D || stageType == STAGE_GEO_NEAR_2DSPHERE; - } - - bool isIntersectPlan(const PlanStageStats& stats) { - if (stats.stageType == STAGE_AND_HASH || stats.stageType == STAGE_AND_SORTED) { - return true; - } - for (size_t i = 0; i < stats.children.size(); ++i) { - if (isIntersectPlan(*stats.children[i])) { - return true; - } - } - return false; - } - - void getLeafNodes(const PlanStageStats& stats, vector<const PlanStageStats*>* leafNodesOut) { - if (0 == stats.children.size()) { - leafNodesOut->push_back(&stats); - } - for (size_t i = 0; i < stats.children.size(); ++i) { - getLeafNodes(*stats.children[i], leafNodesOut); - } - } - - const PlanStageStats* findNode(const PlanStageStats* root, StageType type) { - if (root->stageType == type) { - return root; - } - for (size_t i = 0; i < root->children.size(); ++i) { - const PlanStageStats* ret = findNode(root->children[i], type); - if (NULL != ret) { - return ret; - } - } - return NULL; - } - - } // namespace - - Status explainIntersectPlan(const PlanStageStats& stats, TypeExplain** explainOut, bool fullDetails) { - auto_ptr<TypeExplain> res(new TypeExplain); - res->setCursor("Complex Plan"); - res->setN(stats.common.advanced); - - // Sum the various counters at the leaves. - vector<const PlanStageStats*> leaves; - getLeafNodes(stats, &leaves); - - long long nScanned = 0; - long long nScannedObjects = 0; - for (size_t i = 0; i < leaves.size(); ++i) { - TypeExplain* leafExplain; - explainPlan(*leaves[i], &leafExplain, false); - nScanned += leafExplain->getNScanned(); - nScannedObjects += leafExplain->getNScannedObjects(); - delete leafExplain; - } - - res->setNScanned(nScanned); - // XXX: this isn't exactly "correct" -- for ixscans we have to find out if it's part of a - // subtree rooted at a fetch, etc. etc. do we want to just add the # of advances of a - // fetch node minus the number of alreadyHasObj for those nodes? - res->setNScannedObjects(nScannedObjects); - - uint64_t chunkSkips = 0; - const PlanStageStats* shardFilter = findNode(&stats, STAGE_SHARDING_FILTER); - if (NULL != shardFilter) { - const ShardingFilterStats* sfs - = static_cast<const ShardingFilterStats*>(shardFilter->specific.get()); - chunkSkips = sfs->chunkSkips; - } - - res->setNChunkSkips(chunkSkips); - - if (fullDetails) { - res->setNYields(stats.common.yields); - BSONObjBuilder bob; - Explain::statsToBSON(stats, &bob); - res->stats = bob.obj(); - } - - *explainOut = res.release(); - return Status::OK(); - } - - namespace { - - Status explainPlan(const PlanStageStats& stats, TypeExplain** explainOut, - bool fullDetails, bool covered) { - // - // Temporary explain for index intersection - // - - if (isIntersectPlan(stats)) { - return explainIntersectPlan(stats, explainOut, fullDetails); - } - - // - // Legacy explain implementation - // - - // Descend the plan looking for structural properties: - // + Are there any OR clauses? If so, explain each branch. - // + What type(s) are the leaf nodes and what are their properties? - // + Did we need a sort? - - bool sortPresent = false; - size_t chunkSkips = 0; - - - // XXX: TEMPORARY HACK - GEONEAR explains like OR queries (both have children) until the - // new explain framework makes this file go away. - const PlanStageStats* orStage = NULL; - const PlanStageStats* root = &stats; - const PlanStageStats* leaf = root; - - while (leaf->children.size() > 0) { - // We shouldn't be here if there are any ANDs - if (leaf->children.size() > 1) { - verify(isOrStage(leaf->stageType) || isNearStage(leaf->stageType)); - } - - if (isOrStage(leaf->stageType) || isNearStage(leaf->stageType)) { - orStage = leaf; - break; - } - - if (leaf->stageType == STAGE_FETCH) { - covered = false; - } - - if (leaf->stageType == STAGE_SORT) { - sortPresent = true; - } - - if (STAGE_SHARDING_FILTER == leaf->stageType) { - const ShardingFilterStats* sfs - = static_cast<const ShardingFilterStats*>(leaf->specific.get()); - chunkSkips = sfs->chunkSkips; - } - - leaf = leaf->children[0]; - } - - auto_ptr<TypeExplain> res(new TypeExplain); - - // Accounting for 'nscanned' and 'nscannedObjects' is specific to the kind of leaf: - // - // + on collection scan, both are the same; all the documents retrieved were - // fetched in practice. To get how many documents were retrieved, one simply - // looks at the number of 'advanced' in the stats. - // - // + on an index scan, we'd neeed to look into the index scan cursor to extract the - // number of keys that cursor retrieved, and into the stage's stats 'advanced' for - // nscannedObjects', which would be the number of keys that survived the IXSCAN - // filter. Those keys would have been FETCH-ed, if a fetch is present. - - if (orStage != NULL) { - size_t nScanned = 0; - size_t nScannedObjects = 0; - const std::vector<PlanStageStats*>& children = orStage->children; - for (std::vector<PlanStageStats*>::const_iterator it = children.begin(); - it != children.end(); - ++it) { - TypeExplain* childExplain = NULL; - explainPlan(**it, &childExplain, false /* no full details */, covered); - if (childExplain) { - // Override child's indexOnly value if we have a non-covered - // query (implied by a FETCH stage). - // - // As we run explain on each child, explainPlan() sets indexOnly - // based only on the information in each child. This does not - // consider the possibility of a FETCH stage above the OR/MERGE_SORT - // stage, in which case the child's indexOnly may be erroneously set - // to true. - if (!covered && childExplain->isIndexOnlySet()) { - childExplain->setIndexOnly(false); - } - - // 'res' takes ownership of 'childExplain'. - res->addToClauses(childExplain); - nScanned += childExplain->getNScanned(); - nScannedObjects += childExplain->getNScannedObjects(); - } - } - // We set the cursor name for backwards compatibility with 2.4. - if (isOrStage(leaf->stageType)) { - res->setCursor("QueryOptimizerCursor"); - } - else { - if (leaf->stageType == STAGE_GEO_NEAR_2D) - res->setCursor("GeoSearchCursor"); - else - res->setCursor("S2NearCursor"); - - res->setIndexOnly(false); - res->setIsMultiKey(false); - } - res->setNScanned(nScanned); - res->setNScannedObjects(nScannedObjects); - } - else if (leaf->stageType == STAGE_COLLSCAN) { - CollectionScanStats* csStats = static_cast<CollectionScanStats*>(leaf->specific.get()); - res->setCursor("BasicCursor"); - res->setNScanned(csStats->docsTested); - res->setNScannedObjects(csStats->docsTested); - res->setIndexOnly(false); - res->setIsMultiKey(false); - } - else if (leaf->stageType == STAGE_TEXT) { - TextStats* tStats = static_cast<TextStats*>(leaf->specific.get()); - res->setCursor("TextCursor"); - res->setNScanned(tStats->keysExamined); - res->setNScannedObjects(tStats->fetches); - } - else if (leaf->stageType == STAGE_IXSCAN) { - IndexScanStats* indexStats = static_cast<IndexScanStats*>(leaf->specific.get()); - verify(indexStats); - string direction = indexStats->direction > 0 ? "" : " reverse"; - res->setCursor(indexStats->indexType + " " + indexStats->indexName + direction); - res->setNScanned(indexStats->keysExamined); - - // If we're covered, that is, no FETCH is present, then, by definition, - // nScannedObject would be zero because no full document would have been fetched - // from disk. - res->setNScannedObjects(covered ? 0 : leaf->common.advanced); - - res->setIndexBounds(indexStats->indexBounds); - res->setIsMultiKey(indexStats->isMultiKey); - res->setIndexOnly(covered); - } - else if (leaf->stageType == STAGE_DISTINCT) { - DistinctScanStats* dss = static_cast<DistinctScanStats*>(leaf->specific.get()); - verify(dss); - res->setCursor("DistinctCursor"); - res->setN(dss->keysExamined); - res->setNScanned(dss->keysExamined); - // Distinct hack stage is fully covered. - res->setNScannedObjects(0); - } - else { - return Status(ErrorCodes::InternalError, "cannot interpret execution plan"); - } - - // How many documents did the query return? - res->setN(root->common.advanced); - res->setScanAndOrder(sortPresent); - res->setNChunkSkips(chunkSkips); - - // Statistics for the plan (appear only in a detailed mode) - // TODO: if we can get this from the runner, we can kill "detailed mode" - if (fullDetails) { - res->setNYields(root->common.yields); - BSONObjBuilder bob; - Explain::statsToBSON(*root, &bob); - res->stats = bob.obj(); - } - - *explainOut = res.release(); - return Status::OK(); - } - - } // namespace - - Status explainPlan(const PlanStageStats& stats, TypeExplain** explainOut, bool fullDetails) { - // This function merely calls a recursive helper of the same name. The boolean "covered" is - // used to determine the value of nscannedObjects for subtrees along the way. Recursive - // calls will pass false for "covered" if a fetch stage has been seen at that point in the - // traversal. - const bool covered = true; - return explainPlan(stats, explainOut, fullDetails, covered); - } - -} // namespace mongo diff --git a/src/mongo/db/query/explain_plan.h b/src/mongo/db/query/explain_plan.h deleted file mode 100644 index 4313c2c4a51..00000000000 --- a/src/mongo/db/query/explain_plan.h +++ /dev/null @@ -1,60 +0,0 @@ -/** - * Copyright (C) 2013 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/>. - * - * As a special exception, the copyright holders give permission to link the - * code of portions of this program with the OpenSSL library under certain - * conditions as described in each individual source file and distribute - * linked combinations including the program with the OpenSSL library. You - * must comply with the GNU Affero General Public License in all respects for - * all of the code used other than as permitted herein. If you modify file(s) - * with this exception, you may extend this exception to your version of the - * file(s), but you are not obligated to do so. If you do not wish to do so, - * delete this exception statement from your version. If you delete this - * exception statement from all source files in the program, then also delete - * it in the license file. - */ - -// THIS FILE IS DEPRECATED -- the old explain implementation is being replaced - -#pragma once - -#include "mongo/base/status.h" -#include "mongo/db/exec/plan_stats.h" -#include "mongo/db/query/query_solution.h" - -namespace mongo { - - class TypeExplain; - struct PlanInfo; - - /** - * Returns OK, allocating and filling in '*explainOut' describing the access paths used in - * the 'stats' tree of a given query solution. The caller has the ownership of - * '*explainOut', on success. Otherwise return an error status describing the problem. - * - * If 'fullDetails' was requested, the explain will return all available information about - * the plan, otherwise, just a summary. The fields in the summary are: 'cursor', 'n', - * 'nscannedObjects', 'nscanned', and 'indexBounds'. The remaining fields are: 'isMultKey', - * 'nscannedObjectsAllPlans', 'nscannedAllPlans', 'scanAndOrder', 'indexOnly', 'nYields', - * 'nChunkSkips', 'millis', 'allPlans', and 'oldPlan'. - * - * All these fields are documented in type_explain.h - * - * TODO: This is temporarily in place to support the legacy explain format. Once legacy - * explain is removed, this function should be deleted. - */ - Status explainPlan(const PlanStageStats& stats, TypeExplain** explainOut, bool fullDetails); - -} // namespace mongo diff --git a/src/mongo/db/query/index_bounds_builder_test.cpp b/src/mongo/db/query/index_bounds_builder_test.cpp index 0a89cef23fb..84b3b96ea51 100644 --- a/src/mongo/db/query/index_bounds_builder_test.cpp +++ b/src/mongo/db/query/index_bounds_builder_test.cpp @@ -1119,4 +1119,54 @@ namespace { ASSERT_EQUALS(tightness, IndexBoundsBuilder::EXACT); } + // Test $type bounds for Code BSON type. + TEST(IndexBoundsBuilderTest, CodeTypeBounds) { + IndexEntry testIndex = IndexEntry(BSONObj()); + BSONObj obj = fromjson("{a: {$type: 13}}"); + auto_ptr<MatchExpression> expr(parseMatchExpression(obj)); + BSONElement elt = obj.firstElement(); + + OrderedIntervalList oil; + IndexBoundsBuilder::BoundsTightness tightness; + IndexBoundsBuilder::translate(expr.get(), elt, testIndex, &oil, &tightness); + + // Build the expected interval. + BSONObjBuilder bob; + bob.appendCode("", ""); + bob.appendCodeWScope("", "", BSONObj()); + BSONObj expectedInterval = bob.obj(); + + // Check the output of translate(). + ASSERT_EQUALS(oil.name, "a"); + ASSERT_EQUALS(oil.intervals.size(), 1U); + ASSERT_EQUALS(Interval::INTERVAL_EQUALS, oil.intervals[0].compare( + Interval(expectedInterval, true, true))); + ASSERT(tightness == IndexBoundsBuilder::INEXACT_FETCH); + } + + // Test $type bounds for Code With Scoped BSON type. + TEST(IndexBoundsBuilderTest, CodeWithScopeTypeBounds) { + IndexEntry testIndex = IndexEntry(BSONObj()); + BSONObj obj = fromjson("{a: {$type: 15}}"); + auto_ptr<MatchExpression> expr(parseMatchExpression(obj)); + BSONElement elt = obj.firstElement(); + + OrderedIntervalList oil; + IndexBoundsBuilder::BoundsTightness tightness; + IndexBoundsBuilder::translate(expr.get(), elt, testIndex, &oil, &tightness); + + // Build the expected interval. + BSONObjBuilder bob; + bob.appendCodeWScope("", "", BSONObj()); + bob.appendMaxKey(""); + BSONObj expectedInterval = bob.obj(); + + // Check the output of translate(). + ASSERT_EQUALS(oil.name, "a"); + ASSERT_EQUALS(oil.intervals.size(), 1U); + ASSERT_EQUALS(Interval::INTERVAL_EQUALS, oil.intervals[0].compare( + Interval(expectedInterval, true, true))); + ASSERT(tightness == IndexBoundsBuilder::INEXACT_FETCH); + } + } // namespace diff --git a/src/mongo/db/query/new_find.cpp b/src/mongo/db/query/new_find.cpp index 6b7b8af08e1..b29aca68c25 100644 --- a/src/mongo/db/query/new_find.cpp +++ b/src/mongo/db/query/new_find.cpp @@ -90,11 +90,6 @@ namespace { return n >= pq.getNumToReturn(); } - bool enoughForExplain(const mongo::LiteParsedQuery& pq, long long n) { - if (pq.wantMore() || 0 == pq.getNumToReturn()) { return false; } - return n >= pq.getNumToReturn(); - } - /** * Returns true if 'me' is a GTE or GE predicate over the "ts" field. * Such predicates can be used for the oplog start hack. @@ -511,60 +506,6 @@ namespace mongo { // We use this a lot below. const LiteParsedQuery& pq = cq->getParsed(); - // set this outside loop. we will need to use this both within loop and when deciding - // to fill in explain information - const bool isExplain = pq.isExplain(); - - // New-style explains get diverted through a separate path which calls back into the - // query planner and query execution mechanisms. - // - // TODO temporary until find() becomes a real command. - if (isExplain && enableNewExplain) { - size_t options = QueryPlannerParams::DEFAULT; - if (shardingState.needCollectionMetadata(pq.ns())) { - options |= QueryPlannerParams::INCLUDE_SHARD_FILTER; - } - - BufBuilder bb; - bb.skip(sizeof(QueryResult::Value)); - - PlanExecutor* rawExec; - // Takes ownership of 'cq'. - Status execStatus = getExecutor(txn, collection, cq, &rawExec, options); - if (!execStatus.isOK()) { - uasserted(17510, "Explain error: " + execStatus.reason()); - } - - scoped_ptr<PlanExecutor> exec(rawExec); - BSONObjBuilder explainBob; - Status explainStatus = Explain::explainStages(exec.get(), Explain::EXEC_ALL_PLANS, - &explainBob); - if (!explainStatus.isOK()) { - uasserted(18521, "Explain error: " + explainStatus.reason()); - } - - // Add the resulting object to the return buffer. - BSONObj explainObj = explainBob.obj(); - bb.appendBuf((void*)explainObj.objdata(), explainObj.objsize()); - - curop.debug().iscommand = true; - // TODO: Does this get overwritten/do we really need to set this twice? - curop.debug().query = q.query; - - // Set query result fields. - QueryResult::View qr = bb.buf(); - bb.decouple(); - qr.setResultFlagsToOk(); - qr.msgdata().setLen(bb.len()); - curop.debug().responseLength = bb.len(); - qr.msgdata().setOperation(opReply); - qr.setCursorId(0); - qr.setStartingFrom(0); - qr.setNReturned(1); - result.setData(qr.view2ptr(), true); - return ""; - } - // We'll now try to get the query executor that will execute this query for us. There // are a few cases in which we know upfront which executor we should get and, therefore, // we shortcut the selection process here. @@ -606,6 +547,41 @@ namespace mongo { verify(NULL != rawExec); auto_ptr<PlanExecutor> exec(rawExec); + // If it's actually an explain, do the explain and return rather than falling through + // to the normal query execution loop. + if (pq.isExplain()) { + BufBuilder bb; + bb.skip(sizeof(QueryResult::Value)); + + BSONObjBuilder explainBob; + Status explainStatus = Explain::explainStages(exec.get(), Explain::EXEC_ALL_PLANS, + &explainBob); + if (!explainStatus.isOK()) { + uasserted(18521, "Explain error: " + explainStatus.reason()); + } + + // Add the resulting object to the return buffer. + BSONObj explainObj = explainBob.obj(); + bb.appendBuf((void*)explainObj.objdata(), explainObj.objsize()); + + curop.debug().iscommand = true; + // TODO: Does this get overwritten/do we really need to set this twice? + curop.debug().query = q.query; + + // Set query result fields. + QueryResult::View qr = bb.buf(); + bb.decouple(); + qr.setResultFlagsToOk(); + qr.msgdata().setLen(bb.len()); + curop.debug().responseLength = bb.len(); + qr.msgdata().setOperation(opReply); + qr.setCursorId(0); + qr.setStartingFrom(0); + qr.setNReturned(1); + result.setData(qr.view2ptr(), true); + return ""; + } + // We freak out later if this changes before we're done with the query. const ChunkVersion shardingVersionAtStart = shardingState.getVersion(cq->ns()); @@ -662,10 +638,8 @@ namespace mongo { curop.debug().planSummary = stats.summaryStr.c_str(); while (PlanExecutor::ADVANCED == (state = exec->getNext(&obj, NULL))) { - // Add result to output buffer. This is unnecessary if explain info is requested - if (!isExplain) { - bb.appendBuf((void*)obj.objdata(), obj.objsize()); - } + // Add result to output buffer. + bb.appendBuf((void*)obj.objdata(), obj.objsize()); // Count the result. ++numResults; @@ -681,13 +655,8 @@ namespace mongo { // TODO: only one type of 2d search doesn't support this. We need a way to pull it out // of CanonicalQuery. :( const bool supportsGetMore = true; - if (isExplain) { - if (enoughForExplain(pq, numResults)) { - break; - } - } - else if (!supportsGetMore && (enough(pq, numResults) - || bb.len() >= MaxBytesToReturnToClientAtOnce)) { + if (!supportsGetMore && (enough(pq, numResults) + || bb.len() >= MaxBytesToReturnToClientAtOnce)) { break; } else if (enoughForFirstBatch(pq, numResults, bb.len())) { @@ -743,59 +712,33 @@ namespace mongo { shardingState.getVersion(pq.ns())); } - // Used to fill in explain and to determine if the query is slow enough to be logged. - int elapsedMillis = curop.elapsedMillis(); - - // Get explain information if: - // 1) it is needed by an explain query; - // 2) profiling is enabled; or - // 3) profiling is disabled but we still need explain details to log a "slow" query. - // Producing explain information is expensive and should be done only if we are certain - // the information will be used. - boost::scoped_ptr<TypeExplain> explain(NULL); - if (isExplain || - ctx.ctx().db()->getProfilingLevel() > 0 || - elapsedMillis > serverGlobalParams.slowMS) { - // Ask the executor to produce explain information. - TypeExplain* bareExplain; - Status res = Explain::legacyExplain(exec.get(), &bareExplain); - if (res.isOK()) { - explain.reset(bareExplain); - } - else if (isExplain) { - error() << "could not produce explain of query '" << pq.getFilter() - << "', error: " << res.reason(); - // If numResults and the data in bb don't correspond, we'll crash later when rooting - // through the reply msg. - BSONObj emptyObj; - bb.appendBuf((void*)emptyObj.objdata(), emptyObj.objsize()); - // The explain output is actually a result. - numResults = 1; - // TODO: we can fill out millis etc. here just fine even if the plan screwed up. + // Set debug information for consumption by the profiler. + if (ctx.ctx().db()->getProfilingLevel() > 0 || + curop.elapsedMillis() > serverGlobalParams.slowMS) { + PlanSummaryStats newStats; + Explain::getSummaryStats(exec.get(), &newStats); + + curop.debug().ntoskip = pq.getSkip(); + curop.debug().nreturned = numResults; + curop.debug().scanAndOrder = newStats.hasSortStage; + curop.debug().nscanned = newStats.totalKeysExamined; + curop.debug().nscannedObjects = newStats.totalDocsExamined; + curop.debug().idhack = newStats.isIdhack; + + // Get BSON stats. + scoped_ptr<PlanStageStats> execStats(exec->getStats()); + BSONObjBuilder statsBob; + Explain::statsToBSON(*execStats, &statsBob); + curop.debug().execStats.set(statsBob.obj()); + + // Replace exec stats with plan summary if stats cannot fit into CachedBSONObj. + if (curop.debug().execStats.tooBig() && !curop.debug().planSummary.empty()) { + BSONObjBuilder bob; + bob.append("summary", curop.debug().planSummary.toString()); + curop.debug().execStats.set(bob.done()); } } - // Fill in the missing run-time fields in explain, starting with propeties of - // the process running the query. - if (isExplain && NULL != explain.get()) { - std::string server = mongoutils::str::stream() - << getHostNameCached() << ":" << serverGlobalParams.port; - explain->setServer(server); - - // We might have skipped some results due to chunk migration etc. so our count is - // correct. - explain->setN(numResults); - - // Clock the whole operation. - explain->setMillis(elapsedMillis); - - BSONObj explainObj = explain->toBSON(); - bb.appendBuf((void*)explainObj.objdata(), explainObj.objsize()); - - // The explain output is actually a result. - numResults = 1; - } - long long ccId = 0; if (saveClientCursor) { // We won't use the executor until it's getMore'd. @@ -849,44 +792,6 @@ namespace mongo { qr.setStartingFrom(0); qr.setNReturned(numResults); - // Set debug information for consumption by the profiler. - curop.debug().ntoskip = pq.getSkip(); - curop.debug().nreturned = numResults; - if (NULL != explain.get()) { - if (explain->isScanAndOrderSet()) { - curop.debug().scanAndOrder = explain->getScanAndOrder(); - } - else { - curop.debug().scanAndOrder = false; - } - - if (explain->isNScannedSet()) { - curop.debug().nscanned = explain->getNScanned(); - } - - if (explain->isNScannedObjectsSet()) { - curop.debug().nscannedObjects = explain->getNScannedObjects(); - } - - if (explain->isIDHackSet()) { - curop.debug().idhack = explain->getIDHack(); - } - - if (!explain->stats.isEmpty()) { - // execStats is a CachedBSONObj because it lives in the race-prone - // curop. - curop.debug().execStats.set(explain->stats); - - // Replace exec stats with plan summary if stats cannot fit into CachedBSONObj. - if (curop.debug().execStats.tooBig() && !curop.debug().planSummary.empty()) { - BSONObjBuilder bob; - bob.append("summary", curop.debug().planSummary.toString()); - curop.debug().execStats.set(bob.done()); - } - - } - } - // curop.debug().exhaust is set above. return curop.debug().exhaust ? pq.ns() : ""; } diff --git a/src/mongo/db/query/planner_analysis.cpp b/src/mongo/db/query/planner_analysis.cpp index 233e3b3e4b8..9c723ab46c2 100644 --- a/src/mongo/db/query/planner_analysis.cpp +++ b/src/mongo/db/query/planner_analysis.cpp @@ -448,6 +448,11 @@ namespace mongo { // We have no idea what the client intended. One way to handle the ambiguity // of a limited OR stage is to use the SPLIT_LIMITED_SORT hack. // + // If wantMore is false (meaning that 'ntoreturn' was initially passed to + // the server as a negative value), then we treat numToReturn as a limit. + // Since there is no limit-batchSize ambiguity in this case, we do not use the + // SPLIT_LIMITED_SORT hack. + // // If numToReturn is really a limit, then we want to add a limit to this // SORT stage, and hence perform a topK. // @@ -458,7 +463,8 @@ namespace mongo { // with the topK first. If the client wants a limit, they'll get the efficiency // of topK. If they want a batchSize, the other OR branch will deliver the missing // results. The OR stage handles deduping. - if (params.options & QueryPlannerParams::SPLIT_LIMITED_SORT + if (query.getParsed().wantMore() + && params.options & QueryPlannerParams::SPLIT_LIMITED_SORT && !QueryPlannerCommon::hasNode(query.root(), MatchExpression::TEXT) && !QueryPlannerCommon::hasNode(query.root(), MatchExpression::GEO) && !QueryPlannerCommon::hasNode(query.root(), MatchExpression::GEO_NEAR)) { diff --git a/src/mongo/db/query/query_planner_test.cpp b/src/mongo/db/query/query_planner_test.cpp index 4e4c92f930a..19faeb6d1c1 100644 --- a/src/mongo/db/query/query_planner_test.cpp +++ b/src/mongo/db/query/query_planner_test.cpp @@ -535,6 +535,68 @@ namespace { assertSolutionExists("{fetch: {node: {ixscan: {pattern: {x: 1}}}}}"); } + TEST_F(QueryPlannerTest, ExistsBounds) { + addIndex(BSON("b" << 1)); + + runQuery(fromjson("{b: {$exists: true}}")); + assertNumSolutions(2U); + assertSolutionExists("{cscan: {dir: 1}}"); + assertSolutionExists("{fetch: {filter: {b: {$exists: true}}, node: " + "{ixscan: {pattern: {b: 1}, bounds: " + "{b: [['MinKey', 'MaxKey', true, true]]}}}}}"); + + // This ends up being a double negation, which we currently don't index. + runQuery(fromjson("{b: {$not: {$exists: false}}}")); + assertNumSolutions(1U); + assertSolutionExists("{cscan: {dir: 1}}"); + + runQuery(fromjson("{b: {$exists: false}}")); + assertNumSolutions(2U); + assertSolutionExists("{cscan: {dir: 1}}"); + assertSolutionExists("{fetch: {filter: {b: {$exists: false}}, node: " + "{ixscan: {pattern: {b: 1}, bounds: " + "{b: [[null, null, true, true]]}}}}}"); + + runQuery(fromjson("{b: {$not: {$exists: true}}}")); + assertNumSolutions(2U); + assertSolutionExists("{cscan: {dir: 1}}"); + assertSolutionExists("{fetch: {filter: {b: {$exists: false}}, node: " + "{ixscan: {pattern: {b: 1}, bounds: " + "{b: [[null, null, true, true]]}}}}}"); + } + + TEST_F(QueryPlannerTest, ExistsBoundsCompound) { + addIndex(BSON("a" << 1 << "b" << 1)); + + runQuery(fromjson("{a: 1, b: {$exists: true}}")); + assertNumSolutions(2U); + assertSolutionExists("{cscan: {dir: 1}}"); + assertSolutionExists("{fetch: {filter: {b: {$exists: true}}, node: " + "{ixscan: {pattern: {a: 1, b: 1}, bounds: " + "{a: [[1,1,true,true]], b: [['MinKey','MaxKey',true,true]]}}}}}"); + + // This ends up being a double negation, which we currently don't index. + runQuery(fromjson("{a: 1, b: {$not: {$exists: false}}}")); + assertNumSolutions(2U); + assertSolutionExists("{cscan: {dir: 1}}"); + assertSolutionExists("{fetch: {node: {ixscan: {pattern: {a: 1, b: 1}, bounds: " + "{a: [[1,1,true,true]], b: [['MinKey','MaxKey',true,true]]}}}}}"); + + runQuery(fromjson("{a: 1, b: {$exists: false}}")); + assertNumSolutions(2U); + assertSolutionExists("{cscan: {dir: 1}}"); + assertSolutionExists("{fetch: {filter: {b: {$exists: false}}, node: " + "{ixscan: {pattern: {a: 1, b: 1}, bounds: " + "{a: [[1,1,true,true]], b: [[null,null,true,true]]}}}}}"); + + runQuery(fromjson("{a: 1, b: {$not: {$exists: true}}}")); + assertNumSolutions(2U); + assertSolutionExists("{cscan: {dir: 1}}"); + assertSolutionExists("{fetch: {filter: {b: {$exists: false}}, node: " + "{ixscan: {pattern: {a: 1, b: 1}, bounds: " + "{a: [[1,1,true,true]], b: [[null,null,true,true]]}}}}}"); + } + // // skip and limit // @@ -1525,6 +1587,19 @@ namespace { "node: {ixscan: {filter: null, pattern: {'foo.b': 1}}}}}");*/ } + TEST_F(QueryPlannerTest, BasicAllElemMatch2) { + // true means multikey + addIndex(BSON("a.x" << 1), true); + + runQuery(fromjson("{a: {$all: [{$elemMatch: {x: 3}}, {$elemMatch: {y: 5}}]}}")); + + assertNumSolutions(2U); + assertSolutionExists("{cscan: {dir: 1}}"); + assertSolutionExists("{fetch: {filter: {a:{$all:[{$elemMatch:{x:3}},{$elemMatch:{y:5}}]}}," + "node: {ixscan: {pattern: {'a.x': 1}," + "bounds: {'a.x': [[3,3,true,true]]}}}}}"); + } + // SERVER-13677 TEST_F(QueryPlannerTest, ElemMatchWithAllElemMatchChild) { addIndex(BSON("a.b.c.d" << 1)); @@ -3535,6 +3610,21 @@ namespace { "bounds: {a: [['MinKey','MaxKey',true,true]]}}}}}"); } + TEST_F(QueryPlannerTest, BoundsTypeMinKeyMaxKey) { + params.options = QueryPlannerParams::NO_TABLE_SCAN; + addIndex(BSON("a" << 1)); + + runQuery(fromjson("{a: {$type: -1}}")); + assertNumSolutions(1U); + assertSolutionExists("{fetch: {node: {ixscan: {pattern: {a: 1}, bounds:" + "{a: [['MinKey','MinKey',true,true]]}}}}}"); + + runQuery(fromjson("{a: {$type: 127}}")); + assertNumSolutions(1U); + assertSolutionExists("{fetch: {node: {ixscan: {pattern: {a: 1}, bounds:" + "{a: [['MaxKey','MaxKey',true,true]]}}}}}"); + } + // // Tests related to building index bounds for multikey // indices, combined with compound and $elemMatch diff --git a/src/mongo/db/query/type_explain.cpp b/src/mongo/db/query/type_explain.cpp deleted file mode 100644 index 329eb1dcb28..00000000000 --- a/src/mongo/db/query/type_explain.cpp +++ /dev/null @@ -1,807 +0,0 @@ -/** - * Copyright (C) 2013 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/>. - * - * As a special exception, the copyright holders give permission to link the - * code of portions of this program with the OpenSSL library under certain - * conditions as described in each individual source file and distribute - * linked combinations including the program with the OpenSSL library. You - * must comply with the GNU Affero General Public License in all respects - * for all of the code used other than as permitted herein. If you modify - * file(s) with this exception, you may extend this exception to your - * version of the file(s), but you are not obligated to do so. If you do not - * wish to do so, delete this exception statement from your version. If you - * delete this exception statement from all source files in the program, - * then also delete it in the license file. - */ - -// THIS FILE IS DEPRECATED -- the old explain implementation is being replaced - -#include "mongo/db/query/type_explain.h" - -#include "mongo/db/field_parser.h" -#include "mongo/util/mongoutils/str.h" - -namespace mongo { - - // TODO: This doesn't need to be so complicated or serializable. Let's throw this out when we - // move to explain V2 - - using mongoutils::str::stream; - - const BSONField<std::vector<TypeExplain*> > TypeExplain::clauses("clauses"); - const BSONField<std::string> TypeExplain::cursor("cursor"); - const BSONField<bool> TypeExplain::isMultiKey("isMultiKey"); - const BSONField<long long> TypeExplain::n("n", 0); - const BSONField<long long> TypeExplain::nScannedObjects("nscannedObjects", 0); - const BSONField<long long> TypeExplain::nScanned("nscanned", 0); - const BSONField<long long> TypeExplain::nScannedObjectsAllPlans("nscannedObjectsAllPlans"); - const BSONField<long long> TypeExplain::nScannedAllPlans("nscannedAllPlans"); - const BSONField<bool> TypeExplain::scanAndOrder("scanAndOrder"); - const BSONField<bool> TypeExplain::indexOnly("indexOnly"); - const BSONField<long long> TypeExplain::nYields("nYields"); - const BSONField<long long> TypeExplain::nChunkSkips("nChunkSkips"); - const BSONField<long long> TypeExplain::millis("millis"); - const BSONField<BSONObj> TypeExplain::indexBounds("indexBounds"); - const BSONField<std::vector<TypeExplain*> > TypeExplain::allPlans("allPlans"); - const BSONField<TypeExplain*> TypeExplain::oldPlan("oldPlan"); - const BSONField<bool> TypeExplain::indexFilterApplied("filterSet"); - const BSONField<std::string> TypeExplain::server("server"); - - TypeExplain::TypeExplain() { - clear(); - } - - TypeExplain::~TypeExplain() { - unsetClauses(); - unsetAllPlans(); - } - - bool TypeExplain::isValid(std::string* errMsg) const { - std::string dummy; - if (errMsg == NULL) { - errMsg = &dummy; - } - - // All the mandatory fields must be present. - if (!_isNSet) { - *errMsg = stream() << "missing " << n.name() << " field"; - return false; - } - - if (!_isNScannedObjectsSet) { - *errMsg = stream() << "missing " << nScannedObjects.name() << " field"; - return false; - } - - if (!_isNScannedSet) { - *errMsg = stream() << "missing " << nScanned.name() << " field"; - return false; - } - - return true; - } - - BSONObj TypeExplain::toBSON() const { - BSONObjBuilder builder; - - if (_clauses.get()) { - BSONArrayBuilder clausesBuilder(builder.subarrayStart(clauses())); - for (std::vector<TypeExplain*>::const_iterator it = _clauses->begin(); - it != _clauses->end(); - ++it) { - BSONObj clausesDocument = (*it)->toBSON(); - clausesBuilder.append(clausesDocument); - } - clausesBuilder.done(); - } - - if (_isCursorSet) builder.append(cursor(), _cursor); - - if (_isIsMultiKeySet) builder.append(isMultiKey(), _isMultiKey); - - if (_isNSet) { - builder.appendNumber(n(), _n); - } - else { - builder.appendNumber(n(), n.getDefault()); - } - - if (_isNScannedObjectsSet) { - builder.appendNumber(nScannedObjects(), _nScannedObjects); - } - else { - builder.appendNumber(nScannedObjects(), nScannedObjects.getDefault()); - } - - if (_isNScannedSet) { - builder.appendNumber(nScanned(), _nScanned); - } - else { - builder.appendNumber(nScanned(), nScanned.getDefault()); - } - - if (_isNScannedObjectsAllPlansSet) - builder.appendNumber(nScannedObjectsAllPlans(), _nScannedObjectsAllPlans); - - if (_isNScannedAllPlansSet) builder.appendNumber(nScannedAllPlans(), _nScannedAllPlans); - - if (_isScanAndOrderSet) builder.append(scanAndOrder(), _scanAndOrder); - - if (_isIndexOnlySet) builder.append(indexOnly(), _indexOnly); - - if (_isNYieldsSet) builder.appendNumber(nYields(), _nYields); - - if (_isNChunkSkipsSet) builder.appendNumber(nChunkSkips(), _nChunkSkips); - - if (_isMillisSet) builder.appendNumber(millis(), _millis); - - if (_isIndexBoundsSet) builder.append(indexBounds(), _indexBounds); - - if (_allPlans.get()) { - BSONArrayBuilder allPlansBuilder(builder.subarrayStart(allPlans())); - for (std::vector<TypeExplain*>::const_iterator it = _allPlans->begin(); - it != _allPlans->end(); - ++it) { - BSONObj allPlansObject = (*it)->toBSON(); - allPlansBuilder.append(allPlansObject); - } - allPlansBuilder.done(); - } - - if (_oldPlan.get()) builder.append(oldPlan(), _oldPlan->toBSON()); - - if (_isServerSet) builder.append(server(), _server); - - if (_isIndexFilterAppliedSet) builder.append(indexFilterApplied(), _indexFilterApplied); - - // Add this at the end as it can be huge - if (!stats.isEmpty()) { - builder.append("stats", stats); - } - - return builder.obj(); - } - - bool TypeExplain::parseBSON(const BSONObj& source, string* errMsg) { - clear(); - - std::string dummy; - if (!errMsg) errMsg = &dummy; - - FieldParser::FieldState fieldState; - - std::vector<TypeExplain*>* bareClauses = NULL; - fieldState = FieldParser::extract(source, clauses, &bareClauses, errMsg); - if (fieldState == FieldParser::FIELD_INVALID) return false; - if (fieldState == FieldParser::FIELD_SET) _clauses.reset(bareClauses); - - fieldState = FieldParser::extract(source, cursor, &_cursor, errMsg); - if (fieldState == FieldParser::FIELD_INVALID) return false; - _isCursorSet = fieldState == FieldParser::FIELD_SET; - - fieldState = FieldParser::extract(source, isMultiKey, &_isMultiKey, errMsg); - if (fieldState == FieldParser::FIELD_INVALID) return false; - _isIsMultiKeySet = fieldState == FieldParser::FIELD_SET; - - fieldState = FieldParser::extract(source, n, &_n, errMsg); - if (fieldState == FieldParser::FIELD_INVALID) return false; - _isNSet = fieldState == FieldParser::FIELD_SET; - - fieldState = FieldParser::extract(source, nScannedObjects, &_nScannedObjects, errMsg); - if (fieldState == FieldParser::FIELD_INVALID) return false; - _isNScannedObjectsSet = fieldState == FieldParser::FIELD_SET; - - fieldState = FieldParser::extract(source, nScanned, &_nScanned, errMsg); - if (fieldState == FieldParser::FIELD_INVALID) return false; - _isNScannedSet = fieldState == FieldParser::FIELD_SET; - - fieldState = FieldParser::extract(source, - nScannedObjectsAllPlans, - &_nScannedObjectsAllPlans, - errMsg); - if (fieldState == FieldParser::FIELD_INVALID) return false; - _isNScannedObjectsAllPlansSet = fieldState == FieldParser::FIELD_SET; - - fieldState = FieldParser::extract(source, nScannedAllPlans, &_nScannedAllPlans, errMsg); - if (fieldState == FieldParser::FIELD_INVALID) return false; - _isNScannedAllPlansSet = fieldState == FieldParser::FIELD_SET; - - fieldState = FieldParser::extract(source, scanAndOrder, &_scanAndOrder, errMsg); - if (fieldState == FieldParser::FIELD_INVALID) return false; - _isScanAndOrderSet = fieldState == FieldParser::FIELD_SET; - - fieldState = FieldParser::extract(source, indexOnly, &_indexOnly, errMsg); - if (fieldState == FieldParser::FIELD_INVALID) return false; - _isIndexOnlySet = fieldState == FieldParser::FIELD_SET; - - fieldState = FieldParser::extract(source, nYields, &_nYields, errMsg); - if (fieldState == FieldParser::FIELD_INVALID) return false; - _isNYieldsSet = fieldState == FieldParser::FIELD_SET; - - fieldState = FieldParser::extract(source, nChunkSkips, &_nChunkSkips, errMsg); - if (fieldState == FieldParser::FIELD_INVALID) return false; - _isNChunkSkipsSet = fieldState == FieldParser::FIELD_SET; - - fieldState = FieldParser::extract(source, millis, &_millis, errMsg); - if (fieldState == FieldParser::FIELD_INVALID) return false; - _isMillisSet = fieldState == FieldParser::FIELD_SET; - - fieldState = FieldParser::extract(source, indexBounds, &_indexBounds, errMsg); - if (fieldState == FieldParser::FIELD_INVALID) return false; - _isIndexBoundsSet = fieldState == FieldParser::FIELD_SET; - - std::vector<TypeExplain*>* bareAllPlans = NULL; - fieldState = FieldParser::extract(source, allPlans, &bareAllPlans, errMsg); - if (fieldState == FieldParser::FIELD_INVALID) return false; - if (fieldState == FieldParser::FIELD_SET) _allPlans.reset(bareAllPlans); - - TypeExplain* bareOldPlan = NULL; - fieldState = FieldParser::extract(source, oldPlan, &bareOldPlan, errMsg); - if (fieldState == FieldParser::FIELD_INVALID) return false; - if (fieldState == FieldParser::FIELD_SET) _oldPlan.reset(bareOldPlan); - - fieldState = FieldParser::extract(source, server, &_server, errMsg); - if (fieldState == FieldParser::FIELD_INVALID) return false; - _isServerSet = fieldState == FieldParser::FIELD_SET; - - return true; - } - - void TypeExplain::clear() { - unsetClauses(); - - _cursor.clear(); - _isCursorSet = false; - - _isMultiKey = false; - _isIsMultiKeySet = false; - - _n = 0; - _isNSet = false; - - _nScannedObjects = 0; - _isNScannedObjectsSet = false; - - _nScanned = 0; - _isNScannedSet = false; - - _nScannedObjectsAllPlans = 0; - _isNScannedObjectsAllPlansSet = false; - - _nScannedAllPlans = 0; - _isNScannedAllPlansSet = false; - - _scanAndOrder = false; - _isScanAndOrderSet = false; - - _indexOnly = false; - _isIndexOnlySet = false; - - _idHack = false; - _isIDHackSet = false; - - _indexFilterApplied = false; - _isIndexFilterAppliedSet = false; - - _nYields = 0; - _isNYieldsSet = false; - - _nChunkSkips = 0; - _isNChunkSkipsSet = false; - - _millis = 0; - _isMillisSet = false; - - _indexBounds = BSONObj(); - _isIndexBoundsSet = false; - - unsetAllPlans(); - - unsetOldPlan(); - - _server.clear(); - _isServerSet = false; - - } - - void TypeExplain::cloneTo(TypeExplain* other) const { - other->clear(); - - other->unsetClauses(); - if (_clauses.get()) { - for(std::vector<TypeExplain*>::const_iterator it = _clauses->begin(); - it != _clauses->end(); - ++it) { - TypeExplain* clausesItem = new TypeExplain; - (*it)->cloneTo(clausesItem); - other->addToClauses(clausesItem); - } - } - - other->_cursor = _cursor; - other->_isCursorSet = _isCursorSet; - - other->_isMultiKey = _isMultiKey; - other->_isIsMultiKeySet = _isIsMultiKeySet; - - other->_n = _n; - other->_isNSet = _isNSet; - - other->_nScannedObjects = _nScannedObjects; - other->_isNScannedObjectsSet = _isNScannedObjectsSet; - - other->_nScanned = _nScanned; - other->_isNScannedSet = _isNScannedSet; - - other->_nScannedObjectsAllPlans = _nScannedObjectsAllPlans; - other->_isNScannedObjectsAllPlansSet = _isNScannedObjectsAllPlansSet; - - other->_nScannedAllPlans = _nScannedAllPlans; - other->_isNScannedAllPlansSet = _isNScannedAllPlansSet; - - other->_scanAndOrder = _scanAndOrder; - other->_isScanAndOrderSet = _isScanAndOrderSet; - - other->_indexOnly = _indexOnly; - other->_isIndexOnlySet = _isIndexOnlySet; - - other->_idHack = _idHack; - other->_isIDHackSet = _isIDHackSet; - - other->_indexFilterApplied = _indexFilterApplied; - other->_isIndexFilterAppliedSet = _isIndexFilterAppliedSet; - - other->_nYields = _nYields; - other->_isNYieldsSet = _isNYieldsSet; - - other->_nChunkSkips = _nChunkSkips; - other->_isNChunkSkipsSet = _isNChunkSkipsSet; - - other->_millis = _millis; - other->_isMillisSet = _isMillisSet; - - other->_indexBounds = _indexBounds; - other->_isIndexBoundsSet = _isIndexBoundsSet; - - other->unsetAllPlans(); - if (_allPlans.get()) { - for(std::vector<TypeExplain*>::const_iterator it = _allPlans->begin(); - it != _allPlans->end(); - ++it) { - TypeExplain* allPlansItem = new TypeExplain; - (*it)->cloneTo(allPlansItem); - other->addToAllPlans(allPlansItem); - } - } - - other->unsetOldPlan(); - if (_oldPlan.get()) { - TypeExplain* oldPlanCopy = new TypeExplain; - _oldPlan->cloneTo(oldPlanCopy); - other->setOldPlan(oldPlanCopy); - } - - other->_server = _server; - other->_isServerSet = _isServerSet; - } - - std::string TypeExplain::toString() const { - return toBSON().toString(); - } - - void TypeExplain::setClauses(const std::vector<TypeExplain*>& clauses) { - unsetClauses(); - for(std::vector<TypeExplain*>::const_iterator it = clauses.begin(); - it != clauses.end(); - ++it) { - TypeExplain* clausesItem = new TypeExplain; - (*it)->cloneTo(clausesItem); - addToClauses(clausesItem); - } - } - - void TypeExplain::addToClauses(TypeExplain* clauses) { - if (_clauses.get() == NULL) { - _clauses.reset(new std::vector<TypeExplain*>); - } - _clauses->push_back(clauses); - } - - void TypeExplain::unsetClauses() { - if (_clauses.get()) { - for(std::vector<TypeExplain*>::const_iterator it = _clauses->begin(); - it != _clauses->end(); - ++it) { - delete *it; - } - } - _clauses.reset(); - } - - bool TypeExplain::isClausesSet() const { - return _clauses.get() != NULL; - } - - size_t TypeExplain::sizeClauses() const { - verify(_clauses.get()); - return _clauses->size(); - } - - const std::vector<TypeExplain*>& TypeExplain::getClauses() const { - verify(_clauses.get()); - return *_clauses; - } - - const TypeExplain* TypeExplain::getClausesAt(size_t pos) const { - verify(_clauses.get()); - verify(_clauses->size() > pos); - return _clauses->at(pos); - } - - void TypeExplain::setCursor(const StringData& cursor) { - _cursor = cursor.toString(); - _isCursorSet = true; - } - - void TypeExplain::unsetCursor() { - _isCursorSet = false; - } - - bool TypeExplain::isCursorSet() const { - return _isCursorSet; - } - - const std::string& TypeExplain::getCursor() const { - verify(_isCursorSet); - return _cursor; - } - - void TypeExplain::setIsMultiKey(bool isMultiKey) { - _isMultiKey = isMultiKey; - _isIsMultiKeySet = true; - } - - void TypeExplain::unsetIsMultiKey() { - _isIsMultiKeySet = false; - } - - bool TypeExplain::isIsMultiKeySet() const { - return _isIsMultiKeySet; - } - - bool TypeExplain::getIsMultiKey() const { - verify(_isIsMultiKeySet); - return _isMultiKey; - } - - void TypeExplain::setN(long long n) { - _n = n; - _isNSet = true; - } - - void TypeExplain::unsetN() { - _isNSet = false; - } - - bool TypeExplain::isNSet() const { - return _isNSet; - } - - long long TypeExplain::getN() const { - verify(_isNSet); - return _n; - } - - void TypeExplain::setNScannedObjects(long long nScannedObjects) { - _nScannedObjects = nScannedObjects; - _isNScannedObjectsSet = true; - } - - void TypeExplain::unsetNScannedObjects() { - _isNScannedObjectsSet = false; - } - - bool TypeExplain::isNScannedObjectsSet() const { - return _isNScannedObjectsSet; - } - - long long TypeExplain::getNScannedObjects() const { - verify(_isNScannedObjectsSet); - return _nScannedObjects; - } - - void TypeExplain::setNScanned(long long nScanned) { - _nScanned = nScanned; - _isNScannedSet = true; - } - - void TypeExplain::unsetNScanned() { - _isNScannedSet = false; - } - - bool TypeExplain::isNScannedSet() const { - return _isNScannedSet; - } - - long long TypeExplain::getNScanned() const { - verify(_isNScannedSet); - return _nScanned; - } - - void TypeExplain::setNScannedObjectsAllPlans(long long nScannedObjectsAllPlans) { - _nScannedObjectsAllPlans = nScannedObjectsAllPlans; - _isNScannedObjectsAllPlansSet = true; - } - - void TypeExplain::unsetNScannedObjectsAllPlans() { - _isNScannedObjectsAllPlansSet = false; - } - - bool TypeExplain::isNScannedObjectsAllPlansSet() const { - return _isNScannedObjectsAllPlansSet; - } - - long long TypeExplain::getNScannedObjectsAllPlans() const { - verify(_isNScannedObjectsAllPlansSet); - return _nScannedObjectsAllPlans; - } - - void TypeExplain::setNScannedAllPlans(long long nScannedAllPlans) { - _nScannedAllPlans = nScannedAllPlans; - _isNScannedAllPlansSet = true; - } - - void TypeExplain::unsetNScannedAllPlans() { - _isNScannedAllPlansSet = false; - } - - bool TypeExplain::isNScannedAllPlansSet() const { - return _isNScannedAllPlansSet; - } - - long long TypeExplain::getNScannedAllPlans() const { - verify(_isNScannedAllPlansSet); - return _nScannedAllPlans; - } - - void TypeExplain::setScanAndOrder(bool scanAndOrder) { - _scanAndOrder = scanAndOrder; - _isScanAndOrderSet = true; - } - - void TypeExplain::unsetScanAndOrder() { - _isScanAndOrderSet = false; - } - - bool TypeExplain::isScanAndOrderSet() const { - return _isScanAndOrderSet; - } - - bool TypeExplain::getScanAndOrder() const { - verify(_isScanAndOrderSet); - return _scanAndOrder; - } - - void TypeExplain::setIndexOnly(bool indexOnly) { - _indexOnly = indexOnly; - _isIndexOnlySet = true; - } - - void TypeExplain::unsetIndexOnly() { - _isIndexOnlySet = false; - } - - bool TypeExplain::isIndexOnlySet() const { - return _isIndexOnlySet; - } - - bool TypeExplain::getIndexOnly() const { - verify(_isIndexOnlySet); - return _indexOnly; - } - - void TypeExplain::setIDHack(bool idhack) { - _idHack = idhack; - _isIDHackSet = true; - } - - void TypeExplain::unsetIDHack() { - _isIDHackSet = false; - } - - bool TypeExplain::isIDHackSet() const { - return _isIDHackSet; - } - - bool TypeExplain::getIDHack() const { - verify(_isIDHackSet); - return _idHack; - } - - void TypeExplain::setIndexFilterApplied(bool indexFilterApplied) { - _indexFilterApplied = indexFilterApplied; - _isIndexFilterAppliedSet = true; - } - - void TypeExplain::unsetIndexFilterApplied() { - _isIndexFilterAppliedSet = false; - } - - bool TypeExplain::isIndexFilterAppliedSet() const { - return _isIndexFilterAppliedSet; - } - - bool TypeExplain::getIndexFilterApplied() const { - verify(_isIndexFilterAppliedSet); - return _indexFilterApplied; - } - - void TypeExplain::setNYields(long long nYields) { - _nYields = nYields; - _isNYieldsSet = true; - } - - void TypeExplain::unsetNYields() { - _isNYieldsSet = false; - } - - bool TypeExplain::isNYieldsSet() const { - return _isNYieldsSet; - } - - long long TypeExplain::getNYields() const { - verify(_isNYieldsSet); - return _nYields; - } - - void TypeExplain::setNChunkSkips(long long nChunkSkips) { - _nChunkSkips = nChunkSkips; - _isNChunkSkipsSet = true; - } - - void TypeExplain::unsetNChunkSkips() { - _isNChunkSkipsSet = false; - } - - bool TypeExplain::isNChunkSkipsSet() const { - return _isNChunkSkipsSet; - } - - long long TypeExplain::getNChunkSkips() const { - verify(_isNChunkSkipsSet); - return _nChunkSkips; - } - - void TypeExplain::setMillis(long long millis) { - _millis = millis; - _isMillisSet = true; - } - - void TypeExplain::unsetMillis() { - _isMillisSet = false; - } - - bool TypeExplain::isMillisSet() const { - return _isMillisSet; - } - - long long TypeExplain::getMillis() const { - verify(_isMillisSet); - return _millis; - } - - void TypeExplain::setIndexBounds(const BSONObj& indexBounds) { - _indexBounds = indexBounds.getOwned(); - _isIndexBoundsSet = true; - } - - void TypeExplain::unsetIndexBounds() { - _isIndexBoundsSet = false; - } - - bool TypeExplain::isIndexBoundsSet() const { - return _isIndexBoundsSet; - } - - const BSONObj& TypeExplain::getIndexBounds() const { - verify(_isIndexBoundsSet); - return _indexBounds; - } - - void TypeExplain::setAllPlans(const std::vector<TypeExplain*>& allPlans) { - unsetAllPlans(); - for (std::vector<TypeExplain*>::const_iterator it = allPlans.begin(); - it != allPlans.end(); - ++it) { - TypeExplain* allPlansItem = new TypeExplain; - (*it)->cloneTo(allPlansItem); - addToClauses(allPlansItem); - } - } - - void TypeExplain::addToAllPlans(TypeExplain* allPlans) { - if (_allPlans.get() == NULL) { - _allPlans.reset(new std::vector<TypeExplain*>); - } - _allPlans->push_back(allPlans); - } - - void TypeExplain::unsetAllPlans() { - if (_allPlans.get()) { - for (std::vector<TypeExplain*>::const_iterator it = _allPlans->begin(); - it != _allPlans->end(); - ++it) { - delete *it; - } - _allPlans.reset(); - } - } - - bool TypeExplain::isAllPlansSet() const { - return _allPlans.get() != NULL; - } - - size_t TypeExplain::sizeAllPlans() const { - verify(_allPlans.get()); - return _allPlans->size(); - } - - const std::vector<TypeExplain*>& TypeExplain::getAllPlans() const { - verify(_allPlans.get()); - return *_allPlans; - } - - const TypeExplain* TypeExplain::getAllPlansAt(size_t pos) const { - verify(_allPlans.get()); - verify(_allPlans->size() > pos); - return _allPlans->at(pos); - } - - void TypeExplain::setOldPlan(TypeExplain* oldPlan) { - _oldPlan.reset(oldPlan); - } - - void TypeExplain::unsetOldPlan() { - _oldPlan.reset(); - } - - bool TypeExplain::isOldPlanSet() const { - return _oldPlan.get() != NULL; - } - - const TypeExplain* TypeExplain::getOldPlan() const { - verify(_oldPlan.get()); - return _oldPlan.get(); - } - - void TypeExplain::setServer(const StringData& server) { - _server = server.toString(); - _isServerSet = true; - } - - void TypeExplain::unsetServer() { - _isServerSet = false; - } - - bool TypeExplain::isServerSet() const { - return _isServerSet; - } - - const std::string& TypeExplain::getServer() const { - verify(_isServerSet); - return _server; - } - -} // namespace mongo diff --git a/src/mongo/db/query/type_explain.h b/src/mongo/db/query/type_explain.h deleted file mode 100644 index 6cca71f2bf0..00000000000 --- a/src/mongo/db/query/type_explain.h +++ /dev/null @@ -1,292 +0,0 @@ -/** - * Copyright (C) 2013 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/>. - * - * As a special exception, the copyright holders give permission to link the - * code of portions of this program with the OpenSSL library under certain - * conditions as described in each individual source file and distribute - * linked combinations including the program with the OpenSSL library. You - * must comply with the GNU Affero General Public License in all respects - * for all of the code used other than as permitted herein. If you modify - * file(s) with this exception, you may extend this exception to your - * version of the file(s), but you are not obligated to do so. If you do not - * wish to do so, delete this exception statement from your version. If you - * delete this exception statement from all source files in the program, - * then also delete it in the license file. - */ - -// THIS FILE IS DEPRECATED -- the old explain implementation is being replaced - -#pragma once - -#include <string> - -#include "mongo/base/string_data.h" -#include "mongo/bson/bson_field.h" -#include "mongo/s/bson_serializable.h" - -namespace mongo { - - /** - * Contains query debug information that describes the - * query plan. Generally this information depends only on - * the planning process that happens without running the - * query. The exception is the multi plan runner, in which - * case plan selection depends on actually running the query. - * - * Currently, just a summary std::string describing the plan - * used to run the query. - */ - struct PlanInfo { - PlanInfo() : planSummary("") { } - std::string planSummary; - }; - - /** - * This class represents the layout and content of a TypeExplain runCommand, - * the response side. - */ - class TypeExplain : public BSONSerializable { - MONGO_DISALLOW_COPYING(TypeExplain); - public: - - // - // schema declarations - // - - static const BSONField<std::vector<TypeExplain*> > clauses; - static const BSONField<std::string> cursor; - static const BSONField<bool> isMultiKey; - static const BSONField<long long> n; - static const BSONField<long long> nScannedObjects; - static const BSONField<long long> nScanned; - static const BSONField<long long> nScannedObjectsAllPlans; - static const BSONField<long long> nScannedAllPlans; - static const BSONField<bool> scanAndOrder; - static const BSONField<bool> indexOnly; - static const BSONField<long long> nYields; - static const BSONField<long long> nChunkSkips; - static const BSONField<long long> millis; - static const BSONField<BSONObj> indexBounds; - static const BSONField<std::vector<TypeExplain*> > allPlans; - static const BSONField<TypeExplain*> oldPlan; - static const BSONField<bool> indexFilterApplied; - static const BSONField<std::string> server; - - // - // construction / destruction - // - - TypeExplain(); - virtual ~TypeExplain(); - - /** Copies all the fields present in 'this' to 'other'. */ - void cloneTo(TypeExplain* other) const; - - // - // bson serializable interface implementation - // - - virtual bool isValid(std::string* errMsg) const; - virtual BSONObj toBSON() const; - virtual bool parseBSON(const BSONObj& source, std::string* errMsg); - virtual void clear(); - virtual std::string toString() const; - - // - // individual field accessors - // - - void setClauses(const std::vector<TypeExplain*>& clauses); - void addToClauses(TypeExplain* clauses); - void unsetClauses(); - bool isClausesSet() const; - size_t sizeClauses() const; - const std::vector<TypeExplain*>& getClauses() const; - const TypeExplain* getClausesAt(size_t pos) const; - - void setCursor(const StringData& cursor); - void unsetCursor(); - bool isCursorSet() const; - const std::string& getCursor() const; - - void setIsMultiKey(bool isMultiKey); - void unsetIsMultiKey(); - bool isIsMultiKeySet() const; - bool getIsMultiKey() const; - - void setN(long long n); - void unsetN(); - bool isNSet() const; - long long getN() const; - - void setNScannedObjects(long long nScannedObjects); - void unsetNScannedObjects(); - bool isNScannedObjectsSet() const; - long long getNScannedObjects() const; - - void setNScanned(long long nScanned); - void unsetNScanned(); - bool isNScannedSet() const; - long long getNScanned() const; - - void setNScannedObjectsAllPlans(long long nScannedObjectsAllPlans); - void unsetNScannedObjectsAllPlans(); - bool isNScannedObjectsAllPlansSet() const; - long long getNScannedObjectsAllPlans() const; - - void setNScannedAllPlans(long long nScannedAllPlans); - void unsetNScannedAllPlans(); - bool isNScannedAllPlansSet() const; - long long getNScannedAllPlans() const; - - void setScanAndOrder(bool scanAndOrder); - void unsetScanAndOrder(); - bool isScanAndOrderSet() const; - bool getScanAndOrder() const; - - void setIndexOnly(bool indexOnly); - void unsetIndexOnly(); - bool isIndexOnlySet() const; - bool getIndexOnly() const; - - void setIDHack(bool idhack); - void unsetIDHack(); - bool isIDHackSet() const; - bool getIDHack() const; - - void setIndexFilterApplied(bool indexFilterApplied); - void unsetIndexFilterApplied(); - bool isIndexFilterAppliedSet() const; - bool getIndexFilterApplied() const; - - void setNYields(long long nYields); - void unsetNYields(); - bool isNYieldsSet() const; - long long getNYields() const; - - void setNChunkSkips(long long nChunkSkips); - void unsetNChunkSkips(); - bool isNChunkSkipsSet() const; - long long getNChunkSkips() const; - - void setMillis(long long millis); - void unsetMillis(); - bool isMillisSet() const; - long long getMillis() const; - - void setIndexBounds(const BSONObj& indexBounds); - void unsetIndexBounds(); - bool isIndexBoundsSet() const; - const BSONObj& getIndexBounds() const; - - void setAllPlans(const std::vector<TypeExplain*>& allPlans); - void addToAllPlans(TypeExplain* allPlans); - void unsetAllPlans(); - bool isAllPlansSet() const; - size_t sizeAllPlans() const; - const std::vector<TypeExplain*>& getAllPlans() const; - const TypeExplain* getAllPlansAt(size_t pos) const; - - void setOldPlan(TypeExplain* oldPlan); - void unsetOldPlan(); - bool isOldPlanSet() const; - const TypeExplain* getOldPlan() const; - - void setServer(const StringData& server); - void unsetServer(); - bool isServerSet() const; - const std::string& getServer() const; - - // Opaque stats object - BSONObj stats; - - private: - // Convention: (M)andatory, (O)ptional - - // (O) explain for branches on a $or query - boost::scoped_ptr<std::vector<TypeExplain*> >_clauses; - - // (O) type and name of the cursor used on the leaf stage - std::string _cursor; - bool _isCursorSet; - - // (O) type and name of the cursor used on the leaf stage - bool _isMultiKey; - bool _isIsMultiKeySet; - - // (M) number of documents returned by the query - long long _n; - bool _isNSet; - - // (M) number of documents fetched entirely from the disk - long long _nScannedObjects; - bool _isNScannedObjectsSet; - - // (M) number of entries retrieved either from an index or collection - long long _nScanned; - bool _isNScannedSet; - - // (O) number of documents fetched entirely from the disk across all plans - long long _nScannedObjectsAllPlans; - bool _isNScannedObjectsAllPlansSet; - - // (O) number of entries retrieved either from an index or collection across all plans - long long _nScannedAllPlans; - bool _isNScannedAllPlansSet; - - // (O) whether this plan involved sorting - bool _scanAndOrder; - bool _isScanAndOrderSet; - - // (O) number of entries retrieved either from an index or collection across all plans - bool _indexOnly; - bool _isIndexOnlySet; - - // (O) whether the idhack was used to answer this query - bool _idHack; - bool _isIDHackSet; - - // (O) whether index filters were used in planning this query - bool _indexFilterApplied; - bool _isIndexFilterAppliedSet; - - // (O) number times this plan released and reacquired its lock - long long _nYields; - bool _isNYieldsSet; - - // (O) number times this plan skipped over migrated data - long long _nChunkSkips; - bool _isNChunkSkipsSet; - - // (O) elapsed time this plan took running, in milliseconds - long long _millis; - bool _isMillisSet; - - // (O) keys used to seek in and out of an index - BSONObj _indexBounds; - bool _isIndexBoundsSet; - - // (O) alternative plans considered - boost::scoped_ptr<std::vector<TypeExplain*> > _allPlans; - - // (O) cached plan for this query - boost::scoped_ptr<TypeExplain> _oldPlan; - - // (O) server's host:port against which the query ran - std::string _server; - bool _isServerSet; - }; - -} // namespace mongo diff --git a/src/mongo/dbtests/querytests.cpp b/src/mongo/dbtests/querytests.cpp index 48686d3c621..a14d3e620b3 100644 --- a/src/mongo/dbtests/querytests.cpp +++ b/src/mongo/dbtests/querytests.cpp @@ -632,8 +632,9 @@ namespace QueryTests { // Check number of results and filterSet flag in explain. // filterSet is not available in oplog replay mode. BSONObj explainObj = c->next(); - ASSERT_EQUALS( 1, explainObj.getIntField( "n" ) ); - ASSERT_FALSE( explainObj.hasField( "filterSet" ) ); + ASSERT( explainObj.hasField("executionStats") ); + BSONObj execStats = explainObj["executionStats"].Obj(); + ASSERT_EQUALS( 1, execStats.getIntField( "nReturned" ) ); ASSERT( !c->more() ); } @@ -973,26 +974,6 @@ namespace QueryTests { checkMatch(); _client.ensureIndex( _ns, BSON( "a" << 1 ) ); checkMatch(); - // Use explain queries to check index bounds. - { - BSONObj explain = _client.findOne( _ns, QUERY( "a" << BSON( "$type" << (int)Code ) ).explain() ); - BSONObjBuilder lower; - lower.appendCode( "", "" ); - BSONObjBuilder upper; - upper.appendCodeWScope( "", "", BSONObj() ); - ASSERT( lower.done().firstElement().valuesEqual( explain[ "indexBounds" ].Obj()[ "a" ].Array()[ 0 ].Array()[ 0 ] ) ); - ASSERT( upper.done().firstElement().valuesEqual( explain[ "indexBounds" ].Obj()[ "a" ].Array()[ 0 ].Array()[ 1 ] ) ); - } - { - BSONObj explain = _client.findOne( _ns, QUERY( "a" << BSON( "$type" << (int)CodeWScope ) ).explain() ); - BSONObjBuilder lower; - lower.appendCodeWScope( "", "", BSONObj() ); - // This upper bound may change if a new bson type is added. - BSONObjBuilder upper; - upper << "" << BSON( "$maxElement" << 1 ); - ASSERT( lower.done().firstElement().valuesEqual( explain[ "indexBounds" ].Obj()[ "a" ].Array()[ 0 ].Array()[ 0 ] ) ); - ASSERT( upper.done().firstElement().valuesEqual( explain[ "indexBounds" ].Obj()[ "a" ].Array()[ 0 ].Array()[ 1 ] ) ); - } } private: void checkMatch() { diff --git a/src/mongo/s/strategy.cpp b/src/mongo/s/strategy.cpp index 7ef2cf0c40b..34d05825598 100644 --- a/src/mongo/s/strategy.cpp +++ b/src/mongo/s/strategy.cpp @@ -171,7 +171,7 @@ namespace mongo { if ( qSpec.isExplain() ) { BSONObjBuilder explain_builder; cursor->explain( explain_builder ); - explain_builder.appendNumber( "millis", + explain_builder.appendNumber( "executionTimeMillis", static_cast<long long>(queryTimer.millis()) ); BSONObj b = explain_builder.obj(); diff --git a/src/mongo/shell/shardingtest.js b/src/mongo/shell/shardingtest.js index b3c0a1171ca..1c8241ec392 100644 --- a/src/mongo/shell/shardingtest.js +++ b/src/mongo/shell/shardingtest.js @@ -874,21 +874,21 @@ ShardingTest.prototype.getShard = function( coll, query, includeEmpty ){ ShardingTest.prototype.getShards = function( coll, query, includeEmpty ){ if( ! coll.getDB ) coll = this.s.getCollection( coll ) - + var explain = coll.find( query ).explain() var shards = [] - + if( explain.shards ){ - - for( var shardName in explain.shards ){ + for( var shardName in explain.shards ){ for( var i = 0; i < explain.shards[shardName].length; i++ ){ - if( includeEmpty || ( explain.shards[shardName][i].n && explain.shards[shardName][i].n > 0 ) ) + var hasResults = explain.shards[shardName][i].executionStats.nReturned && + explain.shards[shardName][i].executionStats.nReturned > 0; + if( includeEmpty || hasResults ) shards.push( shardName ) } } - } - + for( var i = 0; i < shards.length; i++ ){ for( var j = 0; j < this._connections.length; j++ ){ if ( connectionURLTheSame( this._connections[j] , shards[i] ) ){ @@ -897,7 +897,7 @@ ShardingTest.prototype.getShards = function( coll, query, includeEmpty ){ } } } - + return shards } |