summaryrefslogtreecommitdiff
path: root/src/mongo/db/query/query_planner.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/mongo/db/query/query_planner.cpp')
-rw-r--r--src/mongo/db/query/query_planner.cpp195
1 files changed, 61 insertions, 134 deletions
diff --git a/src/mongo/db/query/query_planner.cpp b/src/mongo/db/query/query_planner.cpp
index 32af6762a17..c258c9e6867 100644
--- a/src/mongo/db/query/query_planner.cpp
+++ b/src/mongo/db/query/query_planner.cpp
@@ -252,11 +252,6 @@ void tryToAddColumnScan(const QueryPlannerParams& params,
// collection scan. Add that solution.
out.push_back(QueryPlannerAnalysis::analyzeDataAccess(query, params, std::move(columnScan)));
}
-
-bool collscanIsBounded(const CollectionScanNode* collscan) {
- return collscan->minRecord || collscan->maxRecord;
-}
-
} // namespace
using std::numeric_limits;
@@ -339,9 +334,6 @@ string optionString(size_t options) {
case QueryPlannerParams::RETURN_OWNED_DATA:
ss << "RETURN_OWNED_DATA ";
break;
- case QueryPlannerParams::STRICT_NO_TABLE_SCAN:
- ss << "STRICT_NO_TABLE_SCAN ";
- break;
case QueryPlannerParams::DEFAULT:
MONGO_UNREACHABLE;
break;
@@ -477,57 +469,13 @@ static BSONObj finishMaxObj(const IndexEntry& indexEntry,
}
}
-bool providesSort(const CanonicalQuery& query, const BSONObj& kp) {
- return query.getFindCommandRequest().getSort().isPrefixOf(
- kp, SimpleBSONElementComparator::kInstance);
-}
-
-/**
- * Determine whether this query has a sort that can be provided by the clustered index, if so, which
- * direction the scan should be. If the collection is not clustered, or the sort cannot be provided,
- * returns 'boost::none'.
- */
-boost::optional<int> determineClusteredScanDirection(const CanonicalQuery& query,
- const QueryPlannerParams& params) {
- if (params.clusteredInfo && query.getSortPattern() &&
- CollatorInterface::collatorsMatch(params.clusteredCollectionCollator,
- query.getCollator())) {
- auto kp = clustered_util::getSortPattern(params.clusteredInfo->getIndexSpec());
- if (providesSort(query, kp)) {
- return 1;
- } else if (providesSort(query, QueryPlannerCommon::reverseSortObj(kp))) {
- return -1;
- }
- }
-
- return boost::none;
-}
-
-/**
- * Determine the direction of the scan needed for the query. Defaults to 1 unless this is a
- * clustered collection and we have a sort that can be provided by the clustered index.
- */
-int determineCollscanDirection(const CanonicalQuery& query, const QueryPlannerParams& params) {
- return determineClusteredScanDirection(query, params).value_or(1);
-}
-
-std::pair<std::unique_ptr<QuerySolution>, const CollectionScanNode*> buildCollscanSolnWithNode(
- const CanonicalQuery& query,
- bool tailable,
- const QueryPlannerParams& params,
- boost::optional<int> direction = boost::none) {
- std::unique_ptr<QuerySolutionNode> solnRoot(QueryPlannerAccess::makeCollectionScan(
- query, tailable, params, direction.value_or(determineCollscanDirection(query, params))));
- const auto* collscanNode = checked_cast<const CollectionScanNode*>(solnRoot.get());
- return std::make_pair(
- QueryPlannerAnalysis::analyzeDataAccess(query, params, std::move(solnRoot)), collscanNode);
-}
-
std::unique_ptr<QuerySolution> buildCollscanSoln(const CanonicalQuery& query,
bool tailable,
const QueryPlannerParams& params,
- boost::optional<int> direction = boost::none) {
- return buildCollscanSolnWithNode(query, tailable, params, direction).first;
+ int direction = 1) {
+ std::unique_ptr<QuerySolutionNode> solnRoot(
+ QueryPlannerAccess::makeCollectionScan(query, tailable, params, direction));
+ return QueryPlannerAnalysis::analyzeDataAccess(query, params, std::move(solnRoot));
}
std::unique_ptr<QuerySolution> buildWholeIXSoln(
@@ -543,6 +491,11 @@ std::unique_ptr<QuerySolution> buildWholeIXSoln(
return QueryPlannerAnalysis::analyzeDataAccess(query, params, std::move(solnRoot));
}
+bool providesSort(const CanonicalQuery& query, const BSONObj& kp) {
+ return query.getFindCommandRequest().getSort().isPrefixOf(
+ kp, SimpleBSONElementComparator::kInstance);
+}
+
StatusWith<std::unique_ptr<PlanCacheIndexTree>> QueryPlanner::cacheDataFromTaggedTree(
const MatchExpression* const taggedTree, const vector<IndexEntry>& relevantIndices) {
if (!taggedTree) {
@@ -708,7 +661,7 @@ StatusWith<std::unique_ptr<QuerySolution>> QueryPlanner::planFromCache(
} else if (SolutionCacheData::COLLSCAN_SOLN == winnerCacheData.solnType) {
// The cached solution is a collection scan. We don't cache collscans
// with tailable==true, hence the false below.
- auto soln = buildCollscanSoln(query, false, params, winnerCacheData.wholeIXSolnDir);
+ auto soln = buildCollscanSoln(query, false, params);
if (!soln) {
return Status(ErrorCodes::NoQueryExecutionPlans,
"plan cache error: collection scan soln");
@@ -730,11 +683,10 @@ StatusWith<std::unique_ptr<QuerySolution>> QueryPlanner::planFromCache(
"filter"_attr = redact(clone->debugString()),
"cacheData"_attr = redact(winnerCacheData.toString()));
- RelevantFieldIndexMap fields;
+ stdx::unordered_set<string> fields;
QueryPlannerIXSelect::getFields(query.root(), &fields);
- // We will not cache queries with 'hint'.
std::vector<IndexEntry> expandedIndexes =
- QueryPlannerIXSelect::expandIndexes(fields, params.indices, false /* indexHinted */);
+ QueryPlannerIXSelect::expandIndexes(fields, params.indices);
// Map from index name to index number.
map<IndexEntry::Identifier, size_t> indexMap;
@@ -785,38 +737,6 @@ StatusWith<std::unique_ptr<QuerySolution>> QueryPlanner::planFromCache(
return {std::move(soln)};
}
-// If no table scan option is set the planner may not return any plan containing a collection scan.
-// Yet clusteredIdxScans are still allowed as they are not a full collection scan but a bounded
-// collection scan.
-bool noTableScan(const QueryPlannerParams& params) {
- return (params.options & QueryPlannerParams::NO_TABLE_SCAN);
-}
-
-// Used internally if the planner should also avoid retruning a plan containing a clusteredIDX scan.
-bool noTableAndClusteredIDXScan(const QueryPlannerParams& params) {
- return (params.options & QueryPlannerParams::STRICT_NO_TABLE_SCAN);
-}
-
-bool isClusteredScan(QuerySolutionNode* node) {
- if (node->getType() == STAGE_COLLSCAN) {
- auto collectionScanSolnNode = dynamic_cast<CollectionScanNode*>(node);
- return (collectionScanSolnNode->doClusteredCollectionScan());
- }
- return false;
-}
-
-// Check if this is a real coll scan or a hidden ClusteredIDX scan.
-bool isColusteredIDXScanSoln(QuerySolution* collscanSoln) {
- if (collscanSoln->root()->getType() == STAGE_SHARDING_FILTER) {
- auto child = collscanSoln->root()->children.begin();
- return isClusteredScan(*child);
- }
- if (collscanSoln->root()->getType() == STAGE_COLLSCAN) {
- return isClusteredScan(collscanSoln->root());
- }
- return false;
-}
-
StatusWith<std::vector<std::unique_ptr<QuerySolution>>> QueryPlanner::plan(
const CanonicalQuery& query, const QueryPlannerParams& params) {
// It's a little silly to ask for a count and for owned data. This could indicate a bug
@@ -870,6 +790,7 @@ StatusWith<std::vector<std::unique_ptr<QuerySolution>>> QueryPlanner::plan(
// any $natural sort to have been normalized to a $natural hint upstream.
// Additionally, if the hint matches the collection's cluster key, we also output a
// collscan utilizing the cluster key.
+
if (naturalHint) {
// Perform validation specific to $natural.
LOGV2_DEBUG(20969, 5, "Forcing a table scan due to hinted $natural");
@@ -929,7 +850,7 @@ StatusWith<std::vector<std::unique_ptr<QuerySolution>>> QueryPlanner::plan(
out.push_back(std::move(soln));
return {std::move(out)};
}
- } // namespace mongo
+ }
// Hints require us to only consider the hinted index. If index filters in the query
// settings were used to override the allowed indices for planning, we should not use the
@@ -967,14 +888,13 @@ StatusWith<std::vector<std::unique_ptr<QuerySolution>>> QueryPlanner::plan(
}
// Figure out what fields we care about.
- RelevantFieldIndexMap fields;
+ stdx::unordered_set<string> fields;
QueryPlannerIXSelect::getFields(query.root(), &fields);
for (auto&& field : fields) {
- LOGV2_DEBUG(20970, 5, "Predicate over field", "field"_attr = field.first);
+ LOGV2_DEBUG(20970, 5, "Predicate over field", "field"_attr = field);
}
- fullIndexList = QueryPlannerIXSelect::expandIndexes(
- fields, std::move(fullIndexList), !hintedIndex.isEmpty());
+ fullIndexList = QueryPlannerIXSelect::expandIndexes(fields, std::move(fullIndexList));
std::vector<IndexEntry> relevantIndices;
if (!hintedIndexEntry) {
@@ -1337,6 +1257,37 @@ StatusWith<std::vector<std::unique_ptr<QuerySolution>>> QueryPlanner::plan(
}
}
}
+
+ // The base index is sorted on some key, so it's possible we might want to use
+ // a collection scan to provide the sort requested
+ if (params.clusteredInfo) {
+ if (CollatorInterface::collatorsMatch(params.clusteredCollectionCollator,
+ query.getCollator())) {
+ auto kp = clustered_util::getSortPattern(params.clusteredInfo->getIndexSpec());
+ int direction = 0;
+ if (providesSort(query, kp)) {
+ direction = 1;
+ } else if (providesSort(query, QueryPlannerCommon::reverseSortObj(kp))) {
+ direction = -1;
+ }
+
+ if (direction != 0) {
+ auto soln = buildCollscanSoln(query, isTailable, params, direction);
+ if (soln) {
+ LOGV2_DEBUG(6082401,
+ 5,
+ "Planner: outputting soln that uses clustered index to "
+ "provide sort");
+ SolutionCacheData* scd = new SolutionCacheData();
+ scd->solnType = SolutionCacheData::COLLSCAN_SOLN;
+ scd->wholeIXSolnDir = direction;
+
+ soln->cacheData.reset(scd);
+ out.push_back(std::move(soln));
+ }
+ }
+ }
+ }
}
// If a projection exists, there may be an index that allows for a covered plan, even if
@@ -1383,13 +1334,11 @@ StatusWith<std::vector<std::unique_ptr<QuerySolution>>> QueryPlanner::plan(
// No indexed plans? We must provide a collscan if possible or else we can't run the query.
bool collScanRequired = 0 == out.size();
- if (collScanRequired && noTableAndClusteredIDXScan(params)) {
+ if (collScanRequired && !canTableScan) {
return Status(ErrorCodes::NoQueryExecutionPlans,
"No indexed plans available, and running with 'notablescan'");
}
- bool clusteredCollection = params.clusteredInfo.has_value();
-
// geoNear and text queries *require* an index.
// Also, if a hint is specified it indicates that we MUST use it.
bool possibleToCollscan =
@@ -1399,53 +1348,31 @@ StatusWith<std::vector<std::unique_ptr<QuerySolution>>> QueryPlanner::plan(
return Status(ErrorCodes::NoQueryExecutionPlans, "No query solutions");
}
- bool isClusteredIDXScan = false;
- if (possibleToCollscan && (collscanRequested || collScanRequired || clusteredCollection)) {
- auto clusteredScanDirection = determineClusteredScanDirection(query, params);
- auto direction = clusteredScanDirection.value_or(1);
- auto [collscanSoln, collscanNode] =
- buildCollscanSolnWithNode(query, isTailable, params, direction);
- if (!collscanSoln && collScanRequired) {
+ if (possibleToCollscan && (collscanRequested || collScanRequired)) {
+ auto collscan = buildCollscanSoln(query, isTailable, params);
+ if (!collscan && collScanRequired) {
return Status(ErrorCodes::NoQueryExecutionPlans,
"Failed to build collection scan soln");
}
- isClusteredIDXScan = isColusteredIDXScanSoln(collscanSoln.get());
- // We consider collection scan in the following cases:
- // 1. collScanRequested - specifically requested by caller.
- // 2. collScanRequired - there are no other possible plans, so we fallback to full scan.
- // 3. collscanIsBounded - collection is clustered and clustered index is used.
- // 4. clusteredScanDirection - collection is clustered and sort, provided by clustered
- // index, is used
- if (collscanSoln &&
- (collscanRequested || collScanRequired || collscanIsBounded(collscanNode) ||
- clusteredScanDirection)) {
+ if (collscan) {
LOGV2_DEBUG(20984,
5,
"Planner: outputting a collection scan",
- "collectionScan"_attr = redact(collscanSoln->toString()));
+ "collectionScan"_attr = redact(collscan->toString()));
SolutionCacheData* scd = new SolutionCacheData();
scd->solnType = SolutionCacheData::COLLSCAN_SOLN;
- scd->wholeIXSolnDir = direction;
- collscanSoln->cacheData.reset(scd);
- out.push_back(std::move(collscanSoln));
+ collscan->cacheData.reset(scd);
+ out.push_back(std::move(collscan));
}
}
- // Make sure to respect the notablescan option. A clustered IDX scan is allowed even under a
- // NOTABLE option. Only in the case of a strict NOTABLE scan option a clustered IDX scan is not
- // allowed. This option is used in mongoS for shardPruning.
invariant(out.size() > 0);
- if (collScanRequired && noTableScan(params) && !isClusteredIDXScan) {
- return Status(ErrorCodes::NoQueryExecutionPlans,
- "No indexed plans available, and running with 'notablescan'");
- }
return {std::move(out)};
}
/**
- * The 'query' might contain parts of aggregation pipeline. For now, we plan those separately
- * and later attach the agg portion of the plan to the solution(s) for the "find" part of the
- * query.
+ * The 'query' might contain parts of aggregation pipeline. For now, we plan those separately and
+ * later attach the agg portion of the plan to the solution(s) for the "find" part of the query.
*/
std::unique_ptr<QuerySolution> QueryPlanner::extendWithAggPipeline(
const CanonicalQuery& query,
@@ -1625,13 +1552,13 @@ StatusWith<QueryPlanner::SubqueriesPlanningResult> QueryPlanner::planSubqueries(
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::makeForSubplanner(opCtx, query, i);
+ auto statusWithCQ = CanonicalQuery::canonicalize(opCtx, query, orChild);
if (!statusWithCQ.isOK()) {
str::stream ss;
- ss << "Can't canonicalize subchild "
- << planningResult.orExpression->getChild(i)->debugString() << " "
+ ss << "Can't canonicalize subchild " << orChild->debugString() << " "
<< statusWithCQ.getStatus().reason();
return Status(ErrorCodes::BadValue, ss);
}