summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/mongo/db/commands/find_and_modify.cpp4
-rw-r--r--src/mongo/db/exec/and_hash.cpp2
-rw-r--r--src/mongo/db/exec/and_sorted.cpp2
-rw-r--r--src/mongo/db/exec/cached_plan.cpp2
-rw-r--r--src/mongo/db/exec/count.cpp2
-rw-r--r--src/mongo/db/exec/delete.cpp2
-rw-r--r--src/mongo/db/exec/fetch.cpp2
-rw-r--r--src/mongo/db/exec/group.cpp2
-rw-r--r--src/mongo/db/exec/keep_mutations.cpp2
-rw-r--r--src/mongo/db/exec/limit.cpp2
-rw-r--r--src/mongo/db/exec/merge_sort.cpp2
-rw-r--r--src/mongo/db/exec/multi_plan.cpp32
-rw-r--r--src/mongo/db/exec/multi_plan.h6
-rw-r--r--src/mongo/db/exec/near.cpp2
-rw-r--r--src/mongo/db/exec/or.cpp2
-rw-r--r--src/mongo/db/exec/plan_stats.h10
-rw-r--r--src/mongo/db/exec/projection.cpp2
-rw-r--r--src/mongo/db/exec/shard_filter.cpp2
-rw-r--r--src/mongo/db/exec/skip.cpp2
-rw-r--r--src/mongo/db/exec/sort.cpp2
-rw-r--r--src/mongo/db/exec/sort_key_generator.cpp2
-rw-r--r--src/mongo/db/exec/subplan.cpp2
-rw-r--r--src/mongo/db/exec/text.cpp2
-rw-r--r--src/mongo/db/exec/text_match.cpp2
-rw-r--r--src/mongo/db/exec/text_or.cpp4
-rw-r--r--src/mongo/db/exec/update.cpp2
-rw-r--r--src/mongo/db/query/explain.cpp47
-rw-r--r--src/mongo/db/query/explain.h9
-rw-r--r--src/mongo/db/query/plan_ranker.cpp4
-rw-r--r--src/mongo/dbtests/query_stage_multiplan.cpp118
30 files changed, 190 insertions, 86 deletions
diff --git a/src/mongo/db/commands/find_and_modify.cpp b/src/mongo/db/commands/find_and_modify.cpp
index 78b367a39ea..79d2f6405b2 100644
--- a/src/mongo/db/commands/find_and_modify.cpp
+++ b/src/mongo/db/commands/find_and_modify.cpp
@@ -73,7 +73,7 @@ const UpdateStats* getUpdateStats(const PlanStageStats* stats) {
// The stats may refer to an update stage, or a projection stage wrapping an update stage.
if (StageType::STAGE_PROJECTION == stats->stageType) {
invariant(stats->children.size() == 1);
- stats = stats->children[0];
+ stats = stats->children[0].get();
}
invariant(StageType::STAGE_UPDATE == stats->stageType);
@@ -84,7 +84,7 @@ const DeleteStats* getDeleteStats(const PlanStageStats* stats) {
// The stats may refer to a delete stage, or a projection stage wrapping a delete stage.
if (StageType::STAGE_PROJECTION == stats->stageType) {
invariant(stats->children.size() == 1);
- stats = stats->children[0];
+ stats = stats->children[0].get();
}
invariant(StageType::STAGE_DELETE == stats->stageType);
diff --git a/src/mongo/db/exec/and_hash.cpp b/src/mongo/db/exec/and_hash.cpp
index 4518802afba..911ec6482cb 100644
--- a/src/mongo/db/exec/and_hash.cpp
+++ b/src/mongo/db/exec/and_hash.cpp
@@ -490,7 +490,7 @@ unique_ptr<PlanStageStats> AndHashStage::getStats() {
unique_ptr<PlanStageStats> ret = make_unique<PlanStageStats>(_commonStats, STAGE_AND_HASH);
ret->specific = make_unique<AndHashStats>(_specificStats);
for (size_t i = 0; i < _children.size(); ++i) {
- ret->children.push_back(_children[i]->getStats().release());
+ ret->children.emplace_back(_children[i]->getStats());
}
return ret;
diff --git a/src/mongo/db/exec/and_sorted.cpp b/src/mongo/db/exec/and_sorted.cpp
index 277d3424cf3..edc0450b214 100644
--- a/src/mongo/db/exec/and_sorted.cpp
+++ b/src/mongo/db/exec/and_sorted.cpp
@@ -290,7 +290,7 @@ unique_ptr<PlanStageStats> AndSortedStage::getStats() {
unique_ptr<PlanStageStats> ret = make_unique<PlanStageStats>(_commonStats, STAGE_AND_SORTED);
ret->specific = make_unique<AndSortedStats>(_specificStats);
for (size_t i = 0; i < _children.size(); ++i) {
- ret->children.push_back(_children[i]->getStats().release());
+ ret->children.emplace_back(_children[i]->getStats());
}
return ret;
diff --git a/src/mongo/db/exec/cached_plan.cpp b/src/mongo/db/exec/cached_plan.cpp
index a823a687792..dd2a89330fd 100644
--- a/src/mongo/db/exec/cached_plan.cpp
+++ b/src/mongo/db/exec/cached_plan.cpp
@@ -319,7 +319,7 @@ std::unique_ptr<PlanStageStats> CachedPlanStage::getStats() {
std::unique_ptr<PlanStageStats> ret =
stdx::make_unique<PlanStageStats>(_commonStats, STAGE_CACHED_PLAN);
ret->specific = stdx::make_unique<CachedPlanStats>(_specificStats);
- ret->children.push_back(child()->getStats().release());
+ ret->children.emplace_back(child()->getStats());
return ret;
}
diff --git a/src/mongo/db/exec/count.cpp b/src/mongo/db/exec/count.cpp
index 83f7f952fae..269449d4a1b 100644
--- a/src/mongo/db/exec/count.cpp
+++ b/src/mongo/db/exec/count.cpp
@@ -167,7 +167,7 @@ unique_ptr<PlanStageStats> CountStage::getStats() {
unique_ptr<PlanStageStats> ret = make_unique<PlanStageStats>(_commonStats, STAGE_COUNT);
ret->specific = make_unique<CountStats>(_specificStats);
if (!_children.empty()) {
- ret->children.push_back(child()->getStats().release());
+ ret->children.emplace_back(child()->getStats());
}
return ret;
}
diff --git a/src/mongo/db/exec/delete.cpp b/src/mongo/db/exec/delete.cpp
index cd179d1a7d7..aacd2e65d62 100644
--- a/src/mongo/db/exec/delete.cpp
+++ b/src/mongo/db/exec/delete.cpp
@@ -266,7 +266,7 @@ unique_ptr<PlanStageStats> DeleteStage::getStats() {
_commonStats.isEOF = isEOF();
unique_ptr<PlanStageStats> ret = make_unique<PlanStageStats>(_commonStats, STAGE_DELETE);
ret->specific = make_unique<DeleteStats>(_specificStats);
- ret->children.push_back(child()->getStats().release());
+ ret->children.emplace_back(child()->getStats());
return ret;
}
diff --git a/src/mongo/db/exec/fetch.cpp b/src/mongo/db/exec/fetch.cpp
index 6e972a9346e..4088d1d10b3 100644
--- a/src/mongo/db/exec/fetch.cpp
+++ b/src/mongo/db/exec/fetch.cpp
@@ -237,7 +237,7 @@ unique_ptr<PlanStageStats> FetchStage::getStats() {
unique_ptr<PlanStageStats> ret = make_unique<PlanStageStats>(_commonStats, STAGE_FETCH);
ret->specific = make_unique<FetchStats>(_specificStats);
- ret->children.push_back(child()->getStats().release());
+ ret->children.emplace_back(child()->getStats());
return ret;
}
diff --git a/src/mongo/db/exec/group.cpp b/src/mongo/db/exec/group.cpp
index 43b8bf7ec6c..4ce1d4031db 100644
--- a/src/mongo/db/exec/group.cpp
+++ b/src/mongo/db/exec/group.cpp
@@ -290,7 +290,7 @@ unique_ptr<PlanStageStats> GroupStage::getStats() {
_commonStats.isEOF = isEOF();
unique_ptr<PlanStageStats> ret = make_unique<PlanStageStats>(_commonStats, STAGE_GROUP);
ret->specific = make_unique<GroupStats>(_specificStats);
- ret->children.push_back(child()->getStats().release());
+ ret->children.emplace_back(child()->getStats());
return ret;
}
diff --git a/src/mongo/db/exec/keep_mutations.cpp b/src/mongo/db/exec/keep_mutations.cpp
index d43cbb560f8..f1d38d033c6 100644
--- a/src/mongo/db/exec/keep_mutations.cpp
+++ b/src/mongo/db/exec/keep_mutations.cpp
@@ -125,7 +125,7 @@ unique_ptr<PlanStageStats> KeepMutationsStage::getStats() {
_commonStats.isEOF = isEOF();
unique_ptr<PlanStageStats> ret =
make_unique<PlanStageStats>(_commonStats, STAGE_KEEP_MUTATIONS);
- ret->children.push_back(child()->getStats().release());
+ ret->children.emplace_back(child()->getStats());
return ret;
}
diff --git a/src/mongo/db/exec/limit.cpp b/src/mongo/db/exec/limit.cpp
index c4ea7485c3a..c595a455f25 100644
--- a/src/mongo/db/exec/limit.cpp
+++ b/src/mongo/db/exec/limit.cpp
@@ -99,7 +99,7 @@ unique_ptr<PlanStageStats> LimitStage::getStats() {
_commonStats.isEOF = isEOF();
unique_ptr<PlanStageStats> ret = make_unique<PlanStageStats>(_commonStats, STAGE_LIMIT);
ret->specific = make_unique<LimitStats>(_specificStats);
- ret->children.push_back(child()->getStats().release());
+ ret->children.emplace_back(child()->getStats());
return ret;
}
diff --git a/src/mongo/db/exec/merge_sort.cpp b/src/mongo/db/exec/merge_sort.cpp
index 98b6037d990..5c1bbb64e3a 100644
--- a/src/mongo/db/exec/merge_sort.cpp
+++ b/src/mongo/db/exec/merge_sort.cpp
@@ -254,7 +254,7 @@ unique_ptr<PlanStageStats> MergeSortStage::getStats() {
unique_ptr<PlanStageStats> ret = make_unique<PlanStageStats>(_commonStats, STAGE_SORT_MERGE);
ret->specific = make_unique<MergeSortStats>(_specificStats);
for (size_t i = 0; i < _children.size(); ++i) {
- ret->children.push_back(_children[i]->getStats().release());
+ ret->children.emplace_back(_children[i]->getStats());
}
return ret;
}
diff --git a/src/mongo/db/exec/multi_plan.cpp b/src/mongo/db/exec/multi_plan.cpp
index a00ea97769c..3075acd3aed 100644
--- a/src/mongo/db/exec/multi_plan.cpp
+++ b/src/mongo/db/exec/multi_plan.cpp
@@ -342,24 +342,6 @@ Status MultiPlanStage::pickBestPlan(PlanYieldPolicy* yieldPolicy) {
return Status::OK();
}
-vector<PlanStageStats*> MultiPlanStage::generateCandidateStats() {
- OwnedPointerVector<PlanStageStats> candidateStats;
-
- for (size_t ix = 0; ix < _candidates.size(); ix++) {
- if (ix == (size_t)_bestPlanIdx) {
- continue;
- }
- if (ix == (size_t)_backupPlanIdx) {
- continue;
- }
-
- unique_ptr<PlanStageStats> stats = _candidates[ix].root->getStats();
- candidateStats.push_back(stats.release());
- }
-
- return candidateStats.release();
-}
-
bool MultiPlanStage::workAllPlans(size_t numResults, PlanYieldPolicy* yieldPolicy) {
bool doneWorking = false;
@@ -498,15 +480,13 @@ QuerySolution* MultiPlanStage::bestSolution() {
}
unique_ptr<PlanStageStats> MultiPlanStage::getStats() {
- if (bestPlanChosen()) {
- return _candidates[_bestPlanIdx].root->getStats();
- }
- if (hasBackupPlan()) {
- return _candidates[_backupPlanIdx].root->getStats();
- }
_commonStats.isEOF = isEOF();
-
- return make_unique<PlanStageStats>(_commonStats, STAGE_MULTI_PLAN);
+ unique_ptr<PlanStageStats> ret = make_unique<PlanStageStats>(_commonStats, STAGE_MULTI_PLAN);
+ ret->specific = make_unique<MultiPlanStats>(_specificStats);
+ for (auto&& child : _children) {
+ ret->children.emplace_back(child->getStats());
+ }
+ return ret;
}
const SpecificStats* MultiPlanStage::getSpecificStats() const {
diff --git a/src/mongo/db/exec/multi_plan.h b/src/mongo/db/exec/multi_plan.h
index 229aca6aa4c..0a9818b85cd 100644
--- a/src/mongo/db/exec/multi_plan.h
+++ b/src/mongo/db/exec/multi_plan.h
@@ -150,12 +150,6 @@ public:
// Used by explain.
//
- /**
- * Gathers execution stats for all losing plans. Caller takes ownership of
- * all pointers in the returned vector.
- */
- std::vector<PlanStageStats*> generateCandidateStats();
-
static const char* kStageType;
private:
diff --git a/src/mongo/db/exec/near.cpp b/src/mongo/db/exec/near.cpp
index 2095c22ae45..3a25432c2b9 100644
--- a/src/mongo/db/exec/near.cpp
+++ b/src/mongo/db/exec/near.cpp
@@ -313,7 +313,7 @@ unique_ptr<PlanStageStats> NearStage::getStats() {
unique_ptr<PlanStageStats> ret = make_unique<PlanStageStats>(_commonStats, _stageType);
ret->specific.reset(_specificStats.clone());
for (size_t i = 0; i < _childrenIntervals.size(); ++i) {
- ret->children.push_back(_childrenIntervals[i]->covering->getStats().release());
+ ret->children.emplace_back(_childrenIntervals[i]->covering->getStats());
}
return ret;
}
diff --git a/src/mongo/db/exec/or.cpp b/src/mongo/db/exec/or.cpp
index 0bc195ca236..dae040ff9b5 100644
--- a/src/mongo/db/exec/or.cpp
+++ b/src/mongo/db/exec/or.cpp
@@ -162,7 +162,7 @@ unique_ptr<PlanStageStats> OrStage::getStats() {
unique_ptr<PlanStageStats> ret = make_unique<PlanStageStats>(_commonStats, STAGE_OR);
ret->specific = make_unique<OrStats>(_specificStats);
for (size_t i = 0; i < _children.size(); ++i) {
- ret->children.push_back(_children[i]->getStats().release());
+ ret->children.emplace_back(_children[i]->getStats());
}
return ret;
diff --git a/src/mongo/db/exec/plan_stats.h b/src/mongo/db/exec/plan_stats.h
index 8a7648bd4d7..14e07391920 100644
--- a/src/mongo/db/exec/plan_stats.h
+++ b/src/mongo/db/exec/plan_stats.h
@@ -106,12 +106,6 @@ private:
struct PlanStageStats {
PlanStageStats(const CommonStats& c, StageType t) : stageType(t), common(c) {}
- ~PlanStageStats() {
- for (size_t i = 0; i < children.size(); ++i) {
- delete children[i];
- }
- }
-
/**
* Make a deep copy.
*/
@@ -122,7 +116,7 @@ struct PlanStageStats {
}
for (size_t i = 0; i < children.size(); ++i) {
invariant(children[i]);
- stats->children.push_back(children[i]->clone());
+ stats->children.emplace_back(children[i]->clone());
}
return stats;
}
@@ -137,7 +131,7 @@ struct PlanStageStats {
std::unique_ptr<SpecificStats> specific;
// The stats of the node's children.
- std::vector<PlanStageStats*> children;
+ std::vector<std::unique_ptr<PlanStageStats>> children;
private:
MONGO_DISALLOW_COPYING(PlanStageStats);
diff --git a/src/mongo/db/exec/projection.cpp b/src/mongo/db/exec/projection.cpp
index 8f12efd53f6..a9e60bdace9 100644
--- a/src/mongo/db/exec/projection.cpp
+++ b/src/mongo/db/exec/projection.cpp
@@ -244,7 +244,7 @@ unique_ptr<PlanStageStats> ProjectionStage::getStats() {
projStats->projObj = _projObj;
ret->specific = std::move(projStats);
- ret->children.push_back(child()->getStats().release());
+ ret->children.emplace_back(child()->getStats());
return ret;
}
diff --git a/src/mongo/db/exec/shard_filter.cpp b/src/mongo/db/exec/shard_filter.cpp
index e62b1bc8549..021e5c09c8b 100644
--- a/src/mongo/db/exec/shard_filter.cpp
+++ b/src/mongo/db/exec/shard_filter.cpp
@@ -136,7 +136,7 @@ unique_ptr<PlanStageStats> ShardFilterStage::getStats() {
_commonStats.isEOF = isEOF();
unique_ptr<PlanStageStats> ret =
make_unique<PlanStageStats>(_commonStats, STAGE_SHARDING_FILTER);
- ret->children.push_back(child()->getStats().release());
+ ret->children.emplace_back(child()->getStats());
ret->specific = make_unique<ShardingFilterStats>(_specificStats);
return ret;
}
diff --git a/src/mongo/db/exec/skip.cpp b/src/mongo/db/exec/skip.cpp
index 15f829ed8bd..5c5a27a685f 100644
--- a/src/mongo/db/exec/skip.cpp
+++ b/src/mongo/db/exec/skip.cpp
@@ -102,7 +102,7 @@ unique_ptr<PlanStageStats> SkipStage::getStats() {
_specificStats.skip = _toSkip;
unique_ptr<PlanStageStats> ret = make_unique<PlanStageStats>(_commonStats, STAGE_SKIP);
ret->specific = make_unique<SkipStats>(_specificStats);
- ret->children.push_back(child()->getStats().release());
+ ret->children.emplace_back(child()->getStats());
return ret;
}
diff --git a/src/mongo/db/exec/sort.cpp b/src/mongo/db/exec/sort.cpp
index 6780e1bee82..b7b6fc0fc86 100644
--- a/src/mongo/db/exec/sort.cpp
+++ b/src/mongo/db/exec/sort.cpp
@@ -239,7 +239,7 @@ unique_ptr<PlanStageStats> SortStage::getStats() {
unique_ptr<PlanStageStats> ret = make_unique<PlanStageStats>(_commonStats, STAGE_SORT);
ret->specific = make_unique<SortStats>(_specificStats);
- ret->children.push_back(child()->getStats().release());
+ ret->children.emplace_back(child()->getStats());
return ret;
}
diff --git a/src/mongo/db/exec/sort_key_generator.cpp b/src/mongo/db/exec/sort_key_generator.cpp
index 8c4d57482c0..b9a02b17ed2 100644
--- a/src/mongo/db/exec/sort_key_generator.cpp
+++ b/src/mongo/db/exec/sort_key_generator.cpp
@@ -315,7 +315,7 @@ PlanStage::StageState SortKeyGeneratorStage::work(WorkingSetID* out) {
std::unique_ptr<PlanStageStats> SortKeyGeneratorStage::getStats() {
auto ret = stdx::make_unique<PlanStageStats>(_commonStats, STAGE_SORT_KEY_GENERATOR);
- ret->children.push_back(child()->getStats().release());
+ ret->children.emplace_back(child()->getStats());
return ret;
}
diff --git a/src/mongo/db/exec/subplan.cpp b/src/mongo/db/exec/subplan.cpp
index 4e77c642ed4..ad84fb6869d 100644
--- a/src/mongo/db/exec/subplan.cpp
+++ b/src/mongo/db/exec/subplan.cpp
@@ -532,7 +532,7 @@ PlanStage::StageState SubplanStage::work(WorkingSetID* out) {
unique_ptr<PlanStageStats> SubplanStage::getStats() {
_commonStats.isEOF = isEOF();
unique_ptr<PlanStageStats> ret = make_unique<PlanStageStats>(_commonStats, STAGE_SUBPLAN);
- ret->children.push_back(child()->getStats().release());
+ ret->children.emplace_back(child()->getStats());
return ret;
}
diff --git a/src/mongo/db/exec/text.cpp b/src/mongo/db/exec/text.cpp
index 2988fa1a305..5c2f1fa1e67 100644
--- a/src/mongo/db/exec/text.cpp
+++ b/src/mongo/db/exec/text.cpp
@@ -106,7 +106,7 @@ unique_ptr<PlanStageStats> TextStage::getStats() {
unique_ptr<PlanStageStats> ret = make_unique<PlanStageStats>(_commonStats, STAGE_TEXT);
ret->specific = make_unique<TextStats>(_specificStats);
- ret->children.push_back(child()->getStats().release());
+ ret->children.emplace_back(child()->getStats());
return ret;
}
diff --git a/src/mongo/db/exec/text_match.cpp b/src/mongo/db/exec/text_match.cpp
index 6e5ffe951e6..0dc2bf8b5a6 100644
--- a/src/mongo/db/exec/text_match.cpp
+++ b/src/mongo/db/exec/text_match.cpp
@@ -65,7 +65,7 @@ std::unique_ptr<PlanStageStats> TextMatchStage::getStats() {
unique_ptr<PlanStageStats> ret = make_unique<PlanStageStats>(_commonStats, STAGE_TEXT_MATCH);
ret->specific = make_unique<TextMatchStats>(_specificStats);
- ret->children.push_back(child()->getStats().release());
+ ret->children.emplace_back(child()->getStats());
return ret;
}
diff --git a/src/mongo/db/exec/text_or.cpp b/src/mongo/db/exec/text_or.cpp
index dec75810a15..a022a7cce0d 100644
--- a/src/mongo/db/exec/text_or.cpp
+++ b/src/mongo/db/exec/text_or.cpp
@@ -122,8 +122,8 @@ std::unique_ptr<PlanStageStats> TextOrStage::getStats() {
unique_ptr<PlanStageStats> ret = make_unique<PlanStageStats>(_commonStats, STAGE_TEXT_OR);
ret->specific = make_unique<TextOrStats>(_specificStats);
- for (auto& child : _children) {
- ret->children.push_back(child->getStats().release());
+ for (auto&& child : _children) {
+ ret->children.emplace_back(child->getStats());
}
return ret;
diff --git a/src/mongo/db/exec/update.cpp b/src/mongo/db/exec/update.cpp
index 59c25bdbc36..768aeebebfb 100644
--- a/src/mongo/db/exec/update.cpp
+++ b/src/mongo/db/exec/update.cpp
@@ -1000,7 +1000,7 @@ unique_ptr<PlanStageStats> UpdateStage::getStats() {
_commonStats.isEOF = isEOF();
unique_ptr<PlanStageStats> ret = make_unique<PlanStageStats>(_commonStats, STAGE_UPDATE);
ret->specific = make_unique<UpdateStats>(_specificStats);
- ret->children.push_back(child()->getStats().release());
+ ret->children.emplace_back(child()->getStats());
return ret;
}
diff --git a/src/mongo/db/query/explain.cpp b/src/mongo/db/query/explain.cpp
index 61cb9dfca0e..88984d6e2c1 100644
--- a/src/mongo/db/query/explain.cpp
+++ b/src/mongo/db/query/explain.cpp
@@ -60,16 +60,25 @@ using std::vector;
* Traverse the tree rooted at 'root', and add all tree nodes into the list 'flattened'.
*/
void flattenStatsTree(const PlanStageStats* root, vector<const PlanStageStats*>* flattened) {
+ invariant(root->stageType != STAGE_MULTI_PLAN);
flattened->push_back(root);
- for (size_t i = 0; i < root->children.size(); ++i) {
- flattenStatsTree(root->children[i], flattened);
+ for (auto&& child : root->children) {
+ flattenStatsTree(child.get(), flattened);
}
}
/**
- * Traverse the tree rooted at 'root', and add all nodes into the list 'flattened'.
+ * Traverse the tree rooted at 'root', and add all nodes into the list 'flattened'. If a
+ * MultiPlanStage is encountered, only add the best plan and its children to 'flattened'.
*/
void flattenExecTree(const PlanStage* root, vector<const PlanStage*>* flattened) {
+ if (root->stageType() == STAGE_MULTI_PLAN) {
+ // Only add the winning stage from a MultiPlanStage.
+ auto mps = static_cast<const MultiPlanStage*>(root);
+ const PlanStage* winningStage = mps->getChildren()[mps->bestPlanIdx()].get();
+ return flattenExecTree(winningStage, flattened);
+ }
+
flattened->push_back(root);
const auto& children = root->getChildren();
for (size_t i = 0; i < children.size(); ++i) {
@@ -483,7 +492,7 @@ void Explain::statsToBSON(const PlanStageStats& stats,
// static
void Explain::generatePlannerInfo(PlanExecutor* exec,
PlanStageStats* winnerStats,
- const vector<PlanStageStats*>& rejectedStats,
+ const vector<unique_ptr<PlanStageStats>>& rejectedStats,
BSONObjBuilder* out) {
CanonicalQuery* query = exec->getCanonicalQuery();
@@ -596,8 +605,8 @@ void Explain::explainStages(PlanExecutor* exec,
// Get stats of the winning plan from the trial period, if the verbosity level
// is high enough and there was a runoff between multiple plans.
unique_ptr<PlanStageStats> winningStatsTrial;
- if (verbosity >= ExplainCommon::EXEC_ALL_PLANS && NULL != mps) {
- winningStatsTrial = exec->getStats();
+ if (verbosity >= ExplainCommon::EXEC_ALL_PLANS && mps) {
+ winningStatsTrial = std::move(exec->getStats()->children[mps->bestPlanIdx()]);
invariant(winningStatsTrial.get());
}
@@ -611,13 +620,21 @@ void Explain::explainStages(PlanExecutor* exec,
// Step 2: collect plan stats (which also give the structure of the plan tree).
//
- // Get stats for the winning plan.
- unique_ptr<PlanStageStats> winningStats(exec->getStats());
+ // Get stats for the winning plan. If there is only a single candidate plan, it is considered
+ // the winner.
+ unique_ptr<PlanStageStats> winningStats(
+ mps ? std::move(mps->getStats()->children[mps->bestPlanIdx()])
+ : std::move(exec->getStats()));
// Get stats for the rejected plans, if more than one plan was considered.
- OwnedPointerVector<PlanStageStats> allPlansStats;
- if (NULL != mps) {
- allPlansStats = mps->generateCandidateStats();
+ vector<unique_ptr<PlanStageStats>> allPlansStats;
+ if (mps && verbosity >= ExplainCommon::EXEC_ALL_PLANS) {
+ auto mpsStats = mps->getStats();
+ for (size_t i = 0; i < mpsStats->children.size(); ++i) {
+ if (i != static_cast<size_t>(mps->bestPlanIdx())) {
+ allPlansStats.emplace_back(std::move(mpsStats->children[i]));
+ }
+ }
}
//
@@ -625,7 +642,7 @@ void Explain::explainStages(PlanExecutor* exec,
//
if (verbosity >= ExplainCommon::QUERY_PLANNER) {
- generatePlannerInfo(exec, winningStats.get(), allPlansStats.vector(), out);
+ generatePlannerInfo(exec, winningStats.get(), allPlansStats, out);
}
if (verbosity >= ExplainCommon::EXEC_STATS) {
@@ -651,15 +668,15 @@ void Explain::explainStages(PlanExecutor* exec,
// from the trial period of the winning plan. The "allPlansExecution" section
// will contain an apples-to-apples comparison of the winning plan's stats against
// all rejected plans' stats collected during the trial period.
- if (NULL != mps) {
+ if (mps) {
invariant(winningStatsTrial.get());
- allPlansStats.push_back(winningStatsTrial.release());
+ allPlansStats.emplace_back(std::move(winningStatsTrial));
}
BSONArrayBuilder allPlansBob(execBob.subarrayStart("allPlansExecution"));
for (size_t i = 0; i < allPlansStats.size(); ++i) {
BSONObjBuilder planBob(allPlansBob.subobjStart());
- generateExecStats(allPlansStats[i], verbosity, &planBob, boost::none);
+ generateExecStats(allPlansStats[i].get(), verbosity, &planBob, boost::none);
planBob.doneFast();
}
allPlansBob.doneFast();
diff --git a/src/mongo/db/query/explain.h b/src/mongo/db/query/explain.h
index 1177dc1601c..069d89d7cb4 100644
--- a/src/mongo/db/query/explain.h
+++ b/src/mongo/db/query/explain.h
@@ -164,10 +164,11 @@ private:
* @param winnerStats -- the stats tree for the winning plan.
* @param rejectedStats -- an array of stats trees, one per rejected plan
*/
- static void generatePlannerInfo(PlanExecutor* exec,
- PlanStageStats* winnerStats,
- const std::vector<PlanStageStats*>& rejectedStats,
- BSONObjBuilder* out);
+ static void generatePlannerInfo(
+ PlanExecutor* exec,
+ PlanStageStats* winnerStats,
+ const std::vector<std::unique_ptr<PlanStageStats>>& rejectedStats,
+ BSONObjBuilder* out);
/**
* Generates the execution stats section for the stats tree 'stats',
diff --git a/src/mongo/db/query/plan_ranker.cpp b/src/mongo/db/query/plan_ranker.cpp
index b89b9fe3f70..ff8344da6a0 100644
--- a/src/mongo/db/query/plan_ranker.cpp
+++ b/src/mongo/db/query/plan_ranker.cpp
@@ -169,7 +169,7 @@ double computeSelectivity(const PlanStageStats* stats) {
} else {
double sum = 0;
for (size_t i = 0; i < stats->children.size(); ++i) {
- sum += computeSelectivity(stats->children[i]);
+ sum += computeSelectivity(stats->children[i].get());
}
return sum;
}
@@ -180,7 +180,7 @@ bool hasStage(const StageType type, const PlanStageStats* stats) {
return true;
}
for (size_t i = 0; i < stats->children.size(); ++i) {
- if (hasStage(type, stats->children[i])) {
+ if (hasStage(type, stats->children[i].get())) {
return true;
}
}
diff --git a/src/mongo/dbtests/query_stage_multiplan.cpp b/src/mongo/dbtests/query_stage_multiplan.cpp
index 70da6dbec58..d772c80ce5f 100644
--- a/src/mongo/dbtests/query_stage_multiplan.cpp
+++ b/src/mongo/dbtests/query_stage_multiplan.cpp
@@ -35,6 +35,7 @@
#include "mongo/db/exec/index_scan.h"
#include "mongo/db/exec/multi_plan.h"
#include "mongo/db/exec/plan_stage.h"
+#include "mongo/db/exec/queued_data_stage.h"
#include "mongo/db/json.h"
#include "mongo/db/matcher/expression_parser.h"
#include "mongo/db/operation_context_impl.h"
@@ -287,6 +288,121 @@ public:
}
};
+// Test the structure and values of the explain output.
+class MPSExplainAllPlans : public QueryStageMultiPlanBase {
+public:
+ void run() {
+ // Insert a document to create the collection.
+ insert(BSON("x" << 1));
+
+ const int nDocs = 500;
+
+ auto ws = stdx::make_unique<WorkingSet>();
+ auto firstPlan = stdx::make_unique<QueuedDataStage>(&_txn, ws.get());
+ auto secondPlan = stdx::make_unique<QueuedDataStage>(&_txn, ws.get());
+
+ for (int i = 0; i < nDocs; ++i) {
+ addMember(firstPlan.get(), ws.get(), BSON("x" << 1));
+
+ // Make the second plan slower by inserting a NEED_TIME between every result.
+ addMember(secondPlan.get(), ws.get(), BSON("x" << 1));
+ secondPlan->pushBack(PlanStage::NEED_TIME);
+ }
+
+ AutoGetCollectionForRead ctx(&_txn, nss.ns());
+
+ auto cq = uassertStatusOK(CanonicalQuery::canonicalize(nss, BSON("x" << 1)));
+ unique_ptr<MultiPlanStage> mps =
+ make_unique<MultiPlanStage>(&_txn, ctx.getCollection(), cq.get());
+
+ // Put each plan into the MultiPlanStage. Takes ownership of 'firstPlan' and 'secondPlan'.
+ auto firstSoln = stdx::make_unique<QuerySolution>();
+ auto secondSoln = stdx::make_unique<QuerySolution>();
+ mps->addPlan(firstSoln.release(), firstPlan.release(), ws.get());
+ mps->addPlan(secondSoln.release(), secondPlan.release(), ws.get());
+
+ // Making a PlanExecutor chooses the best plan.
+ auto exec = uassertStatusOK(PlanExecutor::make(
+ &_txn, std::move(ws), std::move(mps), ctx.getCollection(), PlanExecutor::YIELD_MANUAL));
+
+ auto root = static_cast<MultiPlanStage*>(exec->getRootStage());
+ ASSERT_TRUE(root->bestPlanChosen());
+ // The first QueuedDataStage should have won.
+ ASSERT_EQ(root->bestPlanIdx(), 0);
+
+ BSONObjBuilder bob;
+ Explain::explainStages(exec.get(), ExplainCommon::EXEC_ALL_PLANS, &bob);
+ BSONObj explained = bob.done();
+
+ ASSERT_EQ(explained["executionStats"]["nReturned"].Int(), nDocs);
+ ASSERT_EQ(explained["executionStats"]["executionStages"]["needTime"].Int(), 0);
+ auto allPlansStats = explained["executionStats"]["allPlansExecution"].Array();
+ ASSERT_EQ(allPlansStats.size(), 2UL);
+ for (auto&& planStats : allPlansStats) {
+ int maxEvaluationResults = internalQueryPlanEvaluationMaxResults;
+ ASSERT_EQ(planStats["executionStages"]["stage"].String(), "QUEUED_DATA");
+ if (planStats["executionStages"]["needTime"].Int() > 0) {
+ // This is the losing plan. Should only have advanced about half the time.
+ ASSERT_LT(planStats["nReturned"].Int(), maxEvaluationResults);
+ } else {
+ // This is the winning plan. Stats here should be from the trial period.
+ ASSERT_EQ(planStats["nReturned"].Int(), maxEvaluationResults);
+ }
+ }
+ }
+
+private:
+ /**
+ * Allocates a new WorkingSetMember with data 'dataObj' in 'ws', and adds the WorkingSetMember
+ * to 'qds'.
+ */
+ void addMember(QueuedDataStage* qds, WorkingSet* ws, BSONObj dataObj) {
+ WorkingSetID id = ws->allocate();
+ WorkingSetMember* wsm = ws->get(id);
+ wsm->obj = Snapshotted<BSONObj>(SnapshotId(), BSON("x" << 1));
+ wsm->transitionToOwnedObj();
+ qds->pushBack(id);
+ }
+};
+
+// Test that the plan summary only includes stats from the winning plan.
+//
+// This is a regression test for SERVER-20111.
+class MPSSummaryStats : public QueryStageMultiPlanBase {
+public:
+ void run() {
+ const int N = 5000;
+ for (int i = 0; i < N; ++i) {
+ insert(BSON("foo" << (i % 10)));
+ }
+
+ // Add two indices to give more plans.
+ addIndex(BSON("foo" << 1));
+ addIndex(BSON("foo" << -1 << "bar" << 1));
+
+ AutoGetCollectionForRead ctx(&_txn, nss.ns());
+ Collection* coll = ctx.getCollection();
+
+ // Create the executor (Matching all documents).
+ auto queryObj = BSON("foo" << BSON("$gte" << 0));
+ auto cq = uassertStatusOK(CanonicalQuery::canonicalize(nss, queryObj));
+ auto exec =
+ uassertStatusOK(getExecutor(&_txn, coll, std::move(cq), PlanExecutor::YIELD_MANUAL));
+
+ ASSERT_EQ(exec->getRootStage()->stageType(), STAGE_MULTI_PLAN);
+
+ exec->executePlan();
+
+ PlanSummaryStats stats;
+ Explain::getSummaryStats(*exec, &stats);
+
+ // If only the winning plan's stats are recorded, we should not have examined more than the
+ // total number of documents/index keys.
+ ASSERT_LTE(stats.totalDocsExamined, static_cast<size_t>(N));
+ ASSERT_LTE(stats.totalKeysExamined, static_cast<size_t>(N));
+ }
+};
+
class All : public Suite {
public:
All() : Suite("query_stage_multiplan") {}
@@ -294,6 +410,8 @@ public:
void setupTests() {
add<MPSCollectionScanVsHighlySelectiveIXScan>();
add<MPSBackupPlan>();
+ add<MPSExplainAllPlans>();
+ add<MPSSummaryStats>();
}
};