summaryrefslogtreecommitdiff
path: root/src/mongo/db/query/query_planner.cpp
diff options
context:
space:
mode:
authorLucas de Castro Borges <lucas@gnuabordo.com.br>2025-02-14 14:26:38 -0300
committerLucas de Castro Borges <lucas@gnuabordo.com.br>2025-02-14 14:26:38 -0300
commit294bc6ecabf14c09c9bc8644704921dcf97cb44e (patch)
tree279b1e0bab53901a1647ac63c1c724f0f789a663 /src/mongo/db/query/query_planner.cpp
parent70be7c27a251621187a1de533462ae2bb1e3bd39 (diff)
parent1e917fd798aa25b7066d4b414b51184f13d5a092 (diff)
Update upstream source from tag 'upstream/6.0.10'debian/6.0.10-1
Update to upstream version '6.0.10' with Debian dir 2d176fa254eee97b139f712fec5709641335a8c3
Diffstat (limited to 'src/mongo/db/query/query_planner.cpp')
-rw-r--r--src/mongo/db/query/query_planner.cpp136
1 files changed, 83 insertions, 53 deletions
diff --git a/src/mongo/db/query/query_planner.cpp b/src/mongo/db/query/query_planner.cpp
index c258c9e6867..e04516a6330 100644
--- a/src/mongo/db/query/query_planner.cpp
+++ b/src/mongo/db/query/query_planner.cpp
@@ -252,6 +252,11 @@ 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;
@@ -469,13 +474,57 @@ 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,
- int direction = 1) {
- std::unique_ptr<QuerySolutionNode> solnRoot(
- QueryPlannerAccess::makeCollectionScan(query, tailable, params, direction));
- return QueryPlannerAnalysis::analyzeDataAccess(query, params, std::move(solnRoot));
+ boost::optional<int> direction = boost::none) {
+ return buildCollscanSolnWithNode(query, tailable, params, direction).first;
}
std::unique_ptr<QuerySolution> buildWholeIXSoln(
@@ -491,11 +540,6 @@ 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) {
@@ -661,7 +705,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);
+ auto soln = buildCollscanSoln(query, false, params, winnerCacheData.wholeIXSolnDir);
if (!soln) {
return Status(ErrorCodes::NoQueryExecutionPlans,
"plan cache error: collection scan soln");
@@ -683,10 +727,11 @@ StatusWith<std::unique_ptr<QuerySolution>> QueryPlanner::planFromCache(
"filter"_attr = redact(clone->debugString()),
"cacheData"_attr = redact(winnerCacheData.toString()));
- stdx::unordered_set<string> fields;
+ RelevantFieldIndexMap fields;
QueryPlannerIXSelect::getFields(query.root(), &fields);
+ // We will not cache queries with 'hint'.
std::vector<IndexEntry> expandedIndexes =
- QueryPlannerIXSelect::expandIndexes(fields, params.indices);
+ QueryPlannerIXSelect::expandIndexes(fields, params.indices, false /* indexHinted */);
// Map from index name to index number.
map<IndexEntry::Identifier, size_t> indexMap;
@@ -888,13 +933,14 @@ StatusWith<std::vector<std::unique_ptr<QuerySolution>>> QueryPlanner::plan(
}
// Figure out what fields we care about.
- stdx::unordered_set<string> fields;
+ RelevantFieldIndexMap fields;
QueryPlannerIXSelect::getFields(query.root(), &fields);
for (auto&& field : fields) {
- LOGV2_DEBUG(20970, 5, "Predicate over field", "field"_attr = field);
+ LOGV2_DEBUG(20970, 5, "Predicate over field", "field"_attr = field.first);
}
- fullIndexList = QueryPlannerIXSelect::expandIndexes(fields, std::move(fullIndexList));
+ fullIndexList = QueryPlannerIXSelect::expandIndexes(
+ fields, std::move(fullIndexList), !hintedIndex.isEmpty());
std::vector<IndexEntry> relevantIndices;
if (!hintedIndexEntry) {
@@ -1257,37 +1303,6 @@ 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
@@ -1339,6 +1354,8 @@ StatusWith<std::vector<std::unique_ptr<QuerySolution>>> QueryPlanner::plan(
"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 =
@@ -1348,21 +1365,34 @@ StatusWith<std::vector<std::unique_ptr<QuerySolution>>> QueryPlanner::plan(
return Status(ErrorCodes::NoQueryExecutionPlans, "No query solutions");
}
- if (possibleToCollscan && (collscanRequested || collScanRequired)) {
- auto collscan = buildCollscanSoln(query, isTailable, params);
- if (!collscan && collScanRequired) {
+ 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) {
return Status(ErrorCodes::NoQueryExecutionPlans,
"Failed to build collection scan soln");
}
- if (collscan) {
+
+ // 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)) {
LOGV2_DEBUG(20984,
5,
"Planner: outputting a collection scan",
- "collectionScan"_attr = redact(collscan->toString()));
+ "collectionScan"_attr = redact(collscanSoln->toString()));
SolutionCacheData* scd = new SolutionCacheData();
scd->solnType = SolutionCacheData::COLLSCAN_SOLN;
- collscan->cacheData.reset(scd);
- out.push_back(std::move(collscan));
+ scd->wholeIXSolnDir = direction;
+ collscanSoln->cacheData.reset(scd);
+ out.push_back(std::move(collscanSoln));
}
}