summaryrefslogtreecommitdiff
path: root/src/mongo/db/query/query_planner.h
diff options
context:
space:
mode:
authorRuoxin Xu <ruoxin.xu@mongodb.com>2021-09-24 13:43:15 +0000
committerEvergreen Agent <no-reply@evergreen.mongodb.com>2021-09-24 14:23:54 +0000
commit634867703b7eb40f073198d27633a7c5506e4604 (patch)
tree70a161bc3cb70f3f3fca99dc64f4e179320df8e2 /src/mongo/db/query/query_planner.h
parentc607f9b63f4764335749ae40a5762d6a305558c1 (diff)
downloadmongo-634867703b7eb40f073198d27633a7c5506e4604.tar.gz
SERVER-59854 Remove PlanCacheIndexabilityState from the PlanCache
This patch includes a number of changes to facilitate the use of PlanCacheIndexabilityState with SBE and classic plan cache keys. * Introduced a plan cache key factory. * Moved PlanCacheIndexabilityState from the PlanCache into CollectionQueryInfo. * Templetized QueryPlanner::planSubqueries() so it can be used with classic and SBE plan caches. * Removed dependency on the CanonicalQuery from the PlanCache class. Co-authored-by: Anton Korshunov <anton.korshunov@mongodb.com>
Diffstat (limited to 'src/mongo/db/query/query_planner.h')
-rw-r--r--src/mongo/db/query/query_planner.h105
1 files changed, 99 insertions, 6 deletions
diff --git a/src/mongo/db/query/query_planner.h b/src/mongo/db/query/query_planner.h
index 6d4fc4d1500..817afff0dea 100644
--- a/src/mongo/db/query/query_planner.h
+++ b/src/mongo/db/query/query_planner.h
@@ -36,6 +36,16 @@
#include "mongo/db/query/query_solution.h"
namespace mongo {
+// The logging facility enforces the rule that logging should not be done in a header file. Since
+// template classes and functions below must be defined in the header file and since they use the
+// logging facility, we have to define the helper functions below to perform the actual logging
+// operation from template code.
+namespace log_detail {
+void logSubplannerIndexEntry(const IndexEntry& entry, size_t childIndex);
+void logCachedPlanFound(size_t numChildren, size_t childIndex);
+void logCachedPlanNotFound(size_t numChildren, size_t childIndex);
+void logNumberOfSolutions(size_t numSolutions);
+} // namespace log_detail
class Collection;
class CollectionPtr;
@@ -119,14 +129,21 @@ public:
const CachedSolution& cachedSoln);
/**
- * Plan each branch of the rooted $or query independently, and store the resulting
+ * Plan each branch of the rooted $or query independently, and return the resulting
* lists of query solutions in 'SubqueriesPlanningResult'.
+ *
+ * The 'createPlanCacheKey' callback is used to create a plan cache key of the specified
+ * 'KeyType' for each of the branches to look up the plan in the 'planCache'.
*/
- static StatusWith<SubqueriesPlanningResult> planSubqueries(OperationContext* opCtx,
- const CollectionPtr& collection,
- const PlanCache* planCache,
- const CanonicalQuery& query,
- const QueryPlannerParams& params);
+ template <typename KeyType, typename... Args>
+ static StatusWith<SubqueriesPlanningResult> planSubqueries(
+ OperationContext* opCtx,
+ const PlanCacheBase<KeyType, Args...>* planCache,
+ std::function<KeyType(const CanonicalQuery& cq, const CollectionPtr& coll)>
+ createPlanCacheKey,
+ const CollectionPtr& collection,
+ const CanonicalQuery& query,
+ const QueryPlannerParams& params);
/**
* Generates and returns the index tag tree that will be inserted into the plan cache. This data
@@ -174,4 +191,80 @@ public:
std::function<StatusWith<std::unique_ptr<QuerySolution>>(
CanonicalQuery* cq, std::vector<std::unique_ptr<QuerySolution>>)> multiplanCallback);
};
+
+template <typename KeyType, typename... Args>
+StatusWith<QueryPlanner::SubqueriesPlanningResult> QueryPlanner::planSubqueries(
+ OperationContext* opCtx,
+ const PlanCacheBase<KeyType, Args...>* planCache,
+ std::function<KeyType(const CanonicalQuery& cq, const CollectionPtr& coll)> createPlanCacheKey,
+ const CollectionPtr& collection,
+ const CanonicalQuery& query,
+ const QueryPlannerParams& params) {
+ invariant(query.root()->matchType() == MatchExpression::OR);
+ invariant(query.root()->numChildren(), "Cannot plan subqueries for an $or with no children");
+
+ SubqueriesPlanningResult planningResult{query.root()->shallowClone()};
+ for (size_t i = 0; i < params.indices.size(); ++i) {
+ const IndexEntry& ie = params.indices[i];
+ const auto insertionRes = planningResult.indexMap.insert(std::make_pair(ie.identifier, i));
+ // Be sure the key was not already in the map.
+ invariant(insertionRes.second);
+ log_detail::logSubplannerIndexEntry(ie, i);
+ }
+
+ for (size_t i = 0; i < planningResult.orExpression->numChildren(); ++i) {
+ // We need a place to shove the results from planning this branch.
+ planningResult.branches.push_back(
+ std::make_unique<SubqueriesPlanningResult::BranchPlanningResult>());
+ auto branchResult = planningResult.branches.back().get();
+ auto orChild = planningResult.orExpression->getChild(i);
+
+ // Turn the i-th child into its own query.
+ auto statusWithCQ = CanonicalQuery::canonicalize(opCtx, query, orChild);
+ if (!statusWithCQ.isOK()) {
+ str::stream ss;
+ ss << "Can't canonicalize subchild " << orChild->debugString() << " "
+ << statusWithCQ.getStatus().reason();
+ return Status(ErrorCodes::BadValue, ss);
+ }
+
+ branchResult->canonicalQuery = std::move(statusWithCQ.getValue());
+
+ // Plan the i-th child. We might be able to find a plan for the i-th child in the plan
+ // cache. If there's no cached plan, then we generate and rank plans using the MPS.
+
+ // Populate branchResult->cachedSolution if an active cachedSolution entry exists.
+ if (planCache && shouldCacheQuery(*branchResult->canonicalQuery)) {
+ if (auto cachedSol = planCache->getCacheEntryIfActive(
+ createPlanCacheKey(*branchResult->canonicalQuery, collection))) {
+ // We have a CachedSolution. Store it for later.
+ log_detail::logCachedPlanFound(planningResult.orExpression->numChildren(), i);
+
+ branchResult->cachedSolution = std::move(cachedSol);
+ }
+ }
+
+ if (!branchResult->cachedSolution) {
+ // No CachedSolution found. We'll have to plan from scratch.
+ log_detail::logCachedPlanNotFound(planningResult.orExpression->numChildren(), i);
+
+ // We don't set NO_TABLE_SCAN because peeking at the cache data will keep us from
+ // considering any plan that's a collscan.
+ invariant(branchResult->solutions.empty());
+ auto statusWithMultiPlanSolns =
+ QueryPlanner::planForMultiPlanner(*branchResult->canonicalQuery, params);
+ if (!statusWithMultiPlanSolns.isOK()) {
+ str::stream ss;
+ ss << "Can't plan for subchild " << branchResult->canonicalQuery->toString() << " "
+ << statusWithMultiPlanSolns.getStatus().reason();
+ return Status(ErrorCodes::BadValue, ss);
+ }
+ branchResult->solutions = std::move(statusWithMultiPlanSolns.getValue());
+
+ log_detail::logNumberOfSolutions(branchResult->solutions.size());
+ }
+ }
+
+ return std::move(planningResult);
+}
} // namespace mongo