diff options
| author | Lucas de Castro Borges <lucas@gnuabordo.com.br> | 2025-02-11 15:07:35 -0300 |
|---|---|---|
| committer | Lucas de Castro Borges <lucas@gnuabordo.com.br> | 2025-02-11 15:07:35 -0300 |
| commit | 4cb8841196d0625dfa3825aa326f071cd27c7b8b (patch) | |
| tree | 1682a647d4463397c119183369ae6f750d5fdcff /src/mongo/db/query/query_planner.cpp | |
| parent | aa03c6362cbaa767638e6eed9b031d86dd2643d1 (diff) | |
| parent | 8f0827553e09872941945a093b647a4211a9db7f (diff) | |
Update upstream source from tag 'upstream/6.0.0'master
Update to upstream version '6.0.0'
with Debian dir 5604a80ec1c96ca76f25f40d78e6ef855abec322
Diffstat (limited to 'src/mongo/db/query/query_planner.cpp')
| -rw-r--r-- | src/mongo/db/query/query_planner.cpp | 195 |
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); } |
