diff options
author | David Storch <david.storch@10gen.com> | 2018-06-12 11:06:07 -0400 |
---|---|---|
committer | David Storch <david.storch@10gen.com> | 2018-06-12 11:06:07 -0400 |
commit | 3215980c2500010c47da2dad0410c291e7854fa8 (patch) | |
tree | 81200f15a76debfba0a9deb23017481e5c2d8a8c /src/mongo/db/query | |
parent | 5c02fd1ae02a5243338cc36bf44cf4c8574edc4e (diff) | |
download | mongo-3215980c2500010c47da2dad0410c291e7854fa8.tar.gz |
Revert "SERVER-35455 Eliminate owned raw pointers from QueryPlannerAccess."
This reverts commit dd1a525f1d1b9e3cf8f902d6c04a17607d565dea.
Diffstat (limited to 'src/mongo/db/query')
-rw-r--r-- | src/mongo/db/query/planner_access.cpp | 387 | ||||
-rw-r--r-- | src/mongo/db/query/planner_access.h | 190 | ||||
-rw-r--r-- | src/mongo/db/query/query_planner.cpp | 14 | ||||
-rw-r--r-- | src/mongo/db/query/query_solution.h | 16 |
4 files changed, 306 insertions, 301 deletions
diff --git a/src/mongo/db/query/planner_access.cpp b/src/mongo/db/query/planner_access.cpp index b8c3a1cefd0..cd64f75b409 100644 --- a/src/mongo/db/query/planner_access.cpp +++ b/src/mongo/db/query/planner_access.cpp @@ -115,8 +115,8 @@ bool scansAreEquivalent(const QuerySolutionNode* lhs, const QuerySolutionNode* r * their index scans reversed to provide the sort. Otherwise, returns an empty vector. * 'nodes' must not be empty. */ -std::vector<bool> canProvideSortWithMergeSort( - const std::vector<std::unique_ptr<QuerySolutionNode>>& nodes, const BSONObj& requestedSort) { +std::vector<bool> canProvideSortWithMergeSort(const std::vector<QuerySolutionNode*>& nodes, + const BSONObj& requestedSort) { invariant(!nodes.empty()); std::vector<bool> shouldReverseScan; const auto reverseSort = QueryPlannerCommon::reverseSortObj(requestedSort); @@ -142,6 +142,7 @@ using std::unique_ptr; using std::vector; using stdx::make_unique; +// static std::unique_ptr<QuerySolutionNode> QueryPlannerAccess::makeCollectionScan( const CanonicalQuery& query, bool tailable, const QueryPlannerParams& params) { // Make the (only) node, a collection scan. @@ -176,11 +177,12 @@ std::unique_ptr<QuerySolutionNode> QueryPlannerAccess::makeCollectionScan( return std::move(csn); } -std::unique_ptr<QuerySolutionNode> QueryPlannerAccess::makeLeafNode( +// static +QuerySolutionNode* QueryPlannerAccess::makeLeafNode( const CanonicalQuery& query, const IndexEntry& index, size_t pos, - const MatchExpression* expr, + MatchExpression* expr, IndexBoundsBuilder::BoundsTightness* tightnessOut) { // We're guaranteed that all GEO_NEARs are first. This slightly violates the "sort index // predicates by their position in the compound index" rule but GEO_NEAR isn't an ixscan. @@ -195,13 +197,13 @@ std::unique_ptr<QuerySolutionNode> QueryPlannerAccess::makeLeafNode( if (MatchExpression::GEO_NEAR == expr->matchType()) { // We must not keep the expression node around. *tightnessOut = IndexBoundsBuilder::EXACT; - auto nearExpr = static_cast<const GeoNearMatchExpression*>(expr); + GeoNearMatchExpression* nearExpr = static_cast<GeoNearMatchExpression*>(expr); BSONElement elt = index.keyPattern.firstElement(); bool indexIs2D = (String == elt.type() && "2d" == elt.String()); if (indexIs2D) { - auto ret = stdx::make_unique<GeoNear2DNode>(index); + GeoNear2DNode* ret = new GeoNear2DNode(index); ret->nq = &nearExpr->getData(); ret->baseBounds.fields.resize(index.keyPattern.nFields()); if (NULL != query.getProj()) { @@ -211,7 +213,7 @@ std::unique_ptr<QuerySolutionNode> QueryPlannerAccess::makeLeafNode( return ret; } else { - auto ret = stdx::make_unique<GeoNear2DSphereNode>(index); + GeoNear2DSphereNode* ret = new GeoNear2DSphereNode(index); ret->nq = &nearExpr->getData(); ret->baseBounds.fields.resize(index.keyPattern.nFields()); if (NULL != query.getProj()) { @@ -223,8 +225,8 @@ std::unique_ptr<QuerySolutionNode> QueryPlannerAccess::makeLeafNode( } else if (MatchExpression::TEXT == expr->matchType()) { // We must not keep the expression node around. *tightnessOut = IndexBoundsBuilder::EXACT; - auto textExpr = static_cast<const TextMatchExpressionBase*>(expr); - auto ret = stdx::make_unique<TextNode>(index); + TextMatchExpressionBase* textExpr = static_cast<TextMatchExpressionBase*>(expr); + TextNode* ret = new TextNode(index); ret->ftsQuery = textExpr->getFTSQuery().clone(); // Count the number of prefix fields before the "text" field. @@ -241,7 +243,7 @@ std::unique_ptr<QuerySolutionNode> QueryPlannerAccess::makeLeafNode( } else { // Note that indexKeyPattern.firstElement().fieldName() may not equal expr->path() // because expr might be inside an array operator that provides a path prefix. - auto isn = stdx::make_unique<IndexScanNode>(index); + IndexScanNode* isn = new IndexScanNode(index); isn->bounds.fields.resize(index.keyPattern.nFields()); isn->addKeyMetadata = query.getQueryRequest().returnKey(); isn->queryCollator = query.getCollator(); @@ -446,6 +448,7 @@ void QueryPlannerAccess::mergeWithLeafNode(MatchExpression* expr, ScanBuildingSt } } +// static void QueryPlannerAccess::finishTextNode(QuerySolutionNode* node, const IndexEntry& index) { TextNode* tn = static_cast<TextNode*>(node); @@ -528,6 +531,7 @@ void QueryPlannerAccess::finishTextNode(QuerySolutionNode* node, const IndexEntr tn->indexPrefix = prefixBob.obj(); } +// static bool QueryPlannerAccess::orNeedsFetch(const ScanBuildingState* scanState) { if (scanState->loosestBounds == IndexBoundsBuilder::EXACT) { return false; @@ -540,8 +544,9 @@ bool QueryPlannerAccess::orNeedsFetch(const ScanBuildingState* scanState) { } } +// static void QueryPlannerAccess::finishAndOutputLeaf(ScanBuildingState* scanState, - vector<std::unique_ptr<QuerySolutionNode>>* out) { + vector<QuerySolutionNode*>* out) { finishLeafNode(scanState->currentScan.get(), scanState->indices[scanState->currentIndexNumber]); if (MatchExpression::OR == scanState->root->matchType()) { @@ -549,13 +554,13 @@ void QueryPlannerAccess::finishAndOutputLeaf(ScanBuildingState* scanState, // In order to correctly evaluate the predicates for this index, we have to // fetch the full documents. Add a fetch node above the index scan whose filter // includes *all* of the predicates used to generate the ixscan. - auto fetch = stdx::make_unique<FetchNode>(); + FetchNode* fetch = new FetchNode(); // Takes ownership. - fetch->filter = std::move(scanState->curOr); + fetch->filter.reset(scanState->curOr.release()); // Takes ownership. fetch->children.push_back(scanState->currentScan.release()); - scanState->currentScan = std::move(fetch); + scanState->currentScan.reset(fetch); } else if (scanState->loosestBounds == IndexBoundsBuilder::INEXACT_COVERED) { // This an OR, at least one of the predicates used to generate 'currentScan' // is inexact covered, but none is inexact fetch. This means that we can put @@ -566,13 +571,14 @@ void QueryPlannerAccess::finishAndOutputLeaf(ScanBuildingState* scanState, // Say we have index {a: 1} and query {$or: [{a: /foo/}, {a: /bar/}]}. // The entire query, {$or: [{a: /foo/}, {a: /bar/}]}, should be a filter // in the index scan stage itself. - scanState->currentScan->filter = std::move(scanState->curOr); + scanState->currentScan->filter.reset(scanState->curOr.release()); } } - out->push_back(std::move(scanState->currentScan)); + out->push_back(scanState->currentScan.release()); } +// static void QueryPlannerAccess::finishLeafNode(QuerySolutionNode* node, const IndexEntry& index) { const StageType type = node->getType(); @@ -641,6 +647,7 @@ void QueryPlannerAccess::finishLeafNode(QuerySolutionNode* node, const IndexEntr IndexBoundsBuilder::alignBounds(bounds, index.keyPattern); } +// static void QueryPlannerAccess::findElemMatchChildren(const MatchExpression* node, vector<MatchExpression*>* out, vector<MatchExpression*>* subnodesOut) { @@ -657,9 +664,12 @@ void QueryPlannerAccess::findElemMatchChildren(const MatchExpression* node, } } -std::vector<std::unique_ptr<QuerySolutionNode>> QueryPlannerAccess::collapseEquivalentScans( - std::vector<std::unique_ptr<QuerySolutionNode>> scans) { - invariant(scans.size() > 0); +// static +std::vector<QuerySolutionNode*> QueryPlannerAccess::collapseEquivalentScans( + const std::vector<QuerySolutionNode*> scans) { + std::vector<std::unique_ptr<QuerySolutionNode>> ownedScans = + transitional_tools_do_not_use::spool_vector(scans); + invariant(ownedScans.size() > 0); // Scans that need to be collapsed will be adjacent to each other in the list due to how we // sort the query predicate. We step through the list, either merging the current scan into @@ -667,11 +677,11 @@ std::vector<std::unique_ptr<QuerySolutionNode>> QueryPlannerAccess::collapseEqui // be merged. std::vector<std::unique_ptr<QuerySolutionNode>> collapsedScans; - collapsedScans.push_back(std::move(scans[0])); - for (size_t i = 1; i < scans.size(); ++i) { - if (scansAreEquivalent(collapsedScans.back().get(), scans[i].get())) { + collapsedScans.push_back(std::move(ownedScans[0])); + for (size_t i = 1; i < ownedScans.size(); ++i) { + if (scansAreEquivalent(collapsedScans.back().get(), ownedScans[i].get())) { // We collapse the entry from 'ownedScans' into the back of 'collapsedScans'. - std::unique_ptr<QuerySolutionNode> collapseFrom = std::move(scans[i]); + std::unique_ptr<QuerySolutionNode> collapseFrom(std::move(ownedScans[i])); FetchNode* collapseFromFetch = getFetchNode(collapseFrom.get()); FetchNode* collapseIntoFetch = getFetchNode(collapsedScans.back().get()); @@ -701,20 +711,21 @@ std::vector<std::unique_ptr<QuerySolutionNode>> QueryPlannerAccess::collapseEqui collapseIntoFetch->filter = MatchExpression::optimize(std::move(collapsedFilter)); } else { // Scans are not equivalent and can't be collapsed. - collapsedScans.push_back(std::move(scans[i])); + collapsedScans.push_back(std::move(ownedScans[i])); } } invariant(collapsedScans.size() > 0); - return collapsedScans; + return transitional_tools_do_not_use::leak_vector(collapsedScans); } +// static bool QueryPlannerAccess::processIndexScans(const CanonicalQuery& query, MatchExpression* root, bool inArrayOperator, const std::vector<IndexEntry>& indices, const QueryPlannerParams& params, - std::vector<std::unique_ptr<QuerySolutionNode>>* out) { + std::vector<QuerySolutionNode*>* out) { // Initialize the ScanBuildingState. ScanBuildingState scanState(root, inArrayOperator, indices); @@ -794,11 +805,11 @@ bool QueryPlannerAccess::processIndexScans(const CanonicalQuery& query, // Reset state before producing a new leaf. scanState.resetForNextScan(scanState.ixtag); - scanState.currentScan = makeLeafNode(query, - indices[scanState.currentIndexNumber], - scanState.ixtag->pos, - child, - &scanState.tightness); + scanState.currentScan.reset(makeLeafNode(query, + indices[scanState.currentIndexNumber], + scanState.ixtag->pos, + child, + &scanState.tightness)); handleFilter(&scanState); } @@ -812,11 +823,11 @@ bool QueryPlannerAccess::processIndexScans(const CanonicalQuery& query, return true; } -bool QueryPlannerAccess::processIndexScansElemMatch( - const CanonicalQuery& query, - ScanBuildingState* scanState, - const QueryPlannerParams& params, - std::vector<std::unique_ptr<QuerySolutionNode>>* out) { +// static +bool QueryPlannerAccess::processIndexScansElemMatch(const CanonicalQuery& query, + ScanBuildingState* scanState, + const QueryPlannerParams& params, + std::vector<QuerySolutionNode*>* out) { MatchExpression* root = scanState->root; MatchExpression* child = root->getChild(scanState->curChild); const vector<IndexEntry>& indices = scanState->indices; @@ -845,22 +856,23 @@ bool QueryPlannerAccess::processIndexScansElemMatch( MatchExpression* subnode = emSubnodes[i]; if (!Indexability::isBoundsGenerating(subnode)) { - // 'subnode' is beneath an $elemMatch. When planning the children of array operators, we - // keep ownership of the match expression node. Therefore, we pass nullptr for the - // 'ownedRoot' argument. - auto childSolution = _buildIndexedDataAccess(query, subnode, nullptr, indices, params); - - // _buildIndexedDataAccess(...) returns NULL in error conditions, when it is unable to - // construct a query solution from a tagged match expression tree. If we are unable to - // construct a solution according to the instructions from the enumerator, then we bail - // out early (by returning false) rather than continuing on and potentially constructing - // an invalid solution tree. - if (!childSolution) { + // Must pass true for 'inArrayOperator' because the subnode is + // beneath an ELEM_MATCH_OBJECT. + QuerySolutionNode* childSolution = + buildIndexedDataAccess(query, subnode, true, indices, params); + + // buildIndexedDataAccess(...) returns NULL in error conditions, when + // it is unable to construct a query solution from a tagged match + // expression tree. If we are unable to construct a solution according + // to the instructions from the enumerator, then we bail out early + // (by returning false) rather than continuing on and potentially + // constructing an invalid solution tree. + if (NULL == childSolution) { return false; } // Output the resulting solution tree. - out->push_back(std::move(childSolution)); + out->push_back(childSolution); } } @@ -899,11 +911,11 @@ bool QueryPlannerAccess::processIndexScansElemMatch( scanState->currentIndexNumber = scanState->ixtag->index; scanState->tightness = IndexBoundsBuilder::INEXACT_FETCH; - scanState->currentScan = makeLeafNode(query, - indices[scanState->currentIndexNumber], - scanState->ixtag->pos, - emChild, - &scanState->tightness); + scanState->currentScan.reset(makeLeafNode(query, + indices[scanState->currentIndexNumber], + scanState->ixtag->pos, + emChild, + &scanState->tightness)); } } @@ -914,49 +926,52 @@ bool QueryPlannerAccess::processIndexScansElemMatch( return true; } -bool QueryPlannerAccess::processIndexScansSubnode( - const CanonicalQuery& query, - ScanBuildingState* scanState, - const QueryPlannerParams& params, - std::vector<std::unique_ptr<QuerySolutionNode>>* out) { +// static +bool QueryPlannerAccess::processIndexScansSubnode(const CanonicalQuery& query, + ScanBuildingState* scanState, + const QueryPlannerParams& params, + std::vector<QuerySolutionNode*>* out) { MatchExpression* root = scanState->root; MatchExpression* child = root->getChild(scanState->curChild); const vector<IndexEntry>& indices = scanState->indices; bool inArrayOperator = scanState->inArrayOperator; - // We may detach the current child from the tree and assume ownership. - std::unique_ptr<MatchExpression> ownedChild; - if (MatchExpression::AND == root->matchType() && MatchExpression::ELEM_MATCH_OBJECT == child->matchType()) { return processIndexScansElemMatch(query, scanState, params, out); } else if (!inArrayOperator) { - // The logical sub-tree is responsible for fully evaluating itself. Any required filters or - // fetches are already hung on it. As such, we remove the filter branch from our tree and - // assume ownership of it. + // The logical sub-tree is responsible for fully evaluating itself. Any + // required filters or fetches are already hung on it. As such, we remove the + // filter branch from our tree. buildIndexedDataAccess takes ownership of the + // child. root->getChildVector()->erase(root->getChildVector()->begin() + scanState->curChild); - ownedChild.reset(child); + // The curChild of today is the curChild+1 of yesterday. } else { ++scanState->curChild; } // If inArrayOperator: takes ownership of child, which is OK, since we detached // child from root. - auto childSolution = - _buildIndexedDataAccess(query, child, std::move(ownedChild), indices, params); - if (!childSolution) { + QuerySolutionNode* childSolution = + buildIndexedDataAccess(query, child, inArrayOperator, indices, params); + if (NULL == childSolution) { return false; } - out->push_back(std::move(childSolution)); + out->push_back(childSolution); return true; } -std::unique_ptr<QuerySolutionNode> QueryPlannerAccess::buildIndexedAnd( - const CanonicalQuery& query, - MatchExpression* root, - std::unique_ptr<MatchExpression> ownedRoot, - const vector<IndexEntry>& indices, - const QueryPlannerParams& params) { +// static +QuerySolutionNode* QueryPlannerAccess::buildIndexedAnd(const CanonicalQuery& query, + MatchExpression* root, + bool inArrayOperator, + const vector<IndexEntry>& indices, + const QueryPlannerParams& params) { + unique_ptr<MatchExpression> autoRoot; + if (!inArrayOperator) { + autoRoot.reset(root); + } + // If we are not allowed to trim for ixisect, then clone the match expression before // passing it to processIndexScans(), which may do the trimming. If we end up with // an index intersection solution, then we use our copy of the match expression to be @@ -968,8 +983,7 @@ std::unique_ptr<QuerySolutionNode> QueryPlannerAccess::buildIndexedAnd( clonedRoot = root->shallowClone(); } - std::vector<std::unique_ptr<QuerySolutionNode>> ixscanNodes; - const bool inArrayOperator = !ownedRoot; + vector<QuerySolutionNode*> ixscanNodes; if (!processIndexScans(query, root, inArrayOperator, indices, params, &ixscanNodes)) { return NULL; } @@ -980,7 +994,7 @@ std::unique_ptr<QuerySolutionNode> QueryPlannerAccess::buildIndexedAnd( // // This is the node we're about to return. - std::unique_ptr<QuerySolutionNode> andResult; + QuerySolutionNode* andResult; // We must use an index for at least one child of the AND. We shouldn't be here if this // isn't the case. @@ -988,7 +1002,7 @@ std::unique_ptr<QuerySolutionNode> QueryPlannerAccess::buildIndexedAnd( // Short-circuit: an AND of one child is just the child. if (ixscanNodes.size() == 1) { - andResult = std::move(ixscanNodes[0]); + andResult = ixscanNodes[0]; } else { // Figure out if we want AndHashNode or AndSortedNode. bool allSortedByDiskLoc = true; @@ -999,24 +1013,21 @@ std::unique_ptr<QuerySolutionNode> QueryPlannerAccess::buildIndexedAnd( } } if (allSortedByDiskLoc) { - auto asn = stdx::make_unique<AndSortedNode>(); - asn->addChildren(std::move(ixscanNodes)); - andResult = std::move(asn); + AndSortedNode* asn = new AndSortedNode(); + asn->children.swap(ixscanNodes); + andResult = asn; } else if (internalQueryPlannerEnableHashIntersection.load()) { - { - auto ahn = stdx::make_unique<AndHashNode>(); - ahn->addChildren(std::move(ixscanNodes)); - andResult = std::move(ahn); - } - + AndHashNode* ahn = new AndHashNode(); + ahn->children.swap(ixscanNodes); + andResult = ahn; // The AndHashNode provides the sort order of its last child. If any of the // possible subnodes of AndHashNode provides the sort order we care about, we put // that one last. - for (size_t i = 0; i < andResult->children.size(); ++i) { - andResult->children[i]->computeProperties(); - const BSONObjSet& sorts = andResult->children[i]->getSort(); + for (size_t i = 0; i < ahn->children.size(); ++i) { + ahn->children[i]->computeProperties(); + const BSONObjSet& sorts = ahn->children[i]->getSort(); if (sorts.end() != sorts.find(query.getQueryRequest().getSort())) { - std::swap(andResult->children[i], andResult->children.back()); + std::swap(ahn->children[i], ahn->children.back()); break; } } @@ -1025,7 +1036,11 @@ std::unique_ptr<QuerySolutionNode> QueryPlannerAccess::buildIndexedAnd( // Clean up the index scans and bail out by returning NULL. LOG(5) << "Can't build index intersection solution: " << "AND_SORTED is not possible and AND_HASH is disabled."; - return nullptr; + + for (size_t i = 0; i < ixscanNodes.size(); i++) { + delete ixscanNodes[i]; + } + return NULL; } } @@ -1040,32 +1055,32 @@ std::unique_ptr<QuerySolutionNode> QueryPlannerAccess::buildIndexedAnd( // We got an index intersection solution, and we aren't allowed to answer predicates // using the index. We add a fetch with the entire filter. invariant(clonedRoot.get()); - auto fetch = stdx::make_unique<FetchNode>(); - fetch->filter = std::move(clonedRoot); + FetchNode* fetch = new FetchNode(); + fetch->filter.reset(clonedRoot.release()); // Takes ownership of 'andResult'. - fetch->children.push_back(andResult.release()); + fetch->children.push_back(andResult); return fetch; } // If there are any nodes still attached to the AND, we can't answer them using the // index, so we put a fetch with filter. if (root->numChildren() > 0) { - auto fetch = stdx::make_unique<FetchNode>(); - verify(ownedRoot); - if (ownedRoot->numChildren() == 1) { + FetchNode* fetch = new FetchNode(); + verify(NULL != autoRoot.get()); + if (autoRoot->numChildren() == 1) { // An $and of one thing is that thing. - MatchExpression* child = ownedRoot->getChild(0); - ownedRoot->getChildVector()->clear(); + MatchExpression* child = autoRoot->getChild(0); + autoRoot->getChildVector()->clear(); // Takes ownership. fetch->filter.reset(child); // 'autoRoot' will delete the empty $and. } else { // root->numChildren() > 1 // Takes ownership. - fetch->filter = std::move(ownedRoot); + fetch->filter.reset(autoRoot.release()); } // takes ownership - fetch->children.push_back(andResult.release()); - andResult = std::move(fetch); + fetch->children.push_back(andResult); + andResult = fetch; } else { // root has no children, let autoRoot get rid of it when it goes out of scope. } @@ -1073,15 +1088,18 @@ std::unique_ptr<QuerySolutionNode> QueryPlannerAccess::buildIndexedAnd( return andResult; } -std::unique_ptr<QuerySolutionNode> QueryPlannerAccess::buildIndexedOr( - const CanonicalQuery& query, - MatchExpression* root, - std::unique_ptr<MatchExpression> ownedRoot, - const vector<IndexEntry>& indices, - const QueryPlannerParams& params) { +// static +QuerySolutionNode* QueryPlannerAccess::buildIndexedOr(const CanonicalQuery& query, + MatchExpression* root, + bool inArrayOperator, + const vector<IndexEntry>& indices, + const QueryPlannerParams& params) { + unique_ptr<MatchExpression> autoRoot; + if (!inArrayOperator) { + autoRoot.reset(root); + } - const bool inArrayOperator = !ownedRoot; - std::vector<std::unique_ptr<QuerySolutionNode>> ixscanNodes; + vector<QuerySolutionNode*> ixscanNodes; if (!processIndexScans(query, root, inArrayOperator, indices, params, &ixscanNodes)) { return NULL; } @@ -1099,13 +1117,13 @@ std::unique_ptr<QuerySolutionNode> QueryPlannerAccess::buildIndexedOr( // If all index scans are identical, then we collapse them into a single scan. This prevents // us from creating OR plans where the branches of the OR perform duplicate work. - ixscanNodes = collapseEquivalentScans(std::move(ixscanNodes)); + ixscanNodes = collapseEquivalentScans(ixscanNodes); - std::unique_ptr<QuerySolutionNode> orResult; + QuerySolutionNode* orResult = NULL; // An OR of one node is just that node. if (1 == ixscanNodes.size()) { - orResult = std::move(ixscanNodes[0]); + orResult = ixscanNodes[0]; } else { std::vector<bool> shouldReverseScan; @@ -1122,18 +1140,18 @@ std::unique_ptr<QuerySolutionNode> QueryPlannerAccess::buildIndexedOr( invariant(ixscanNodes.size() == shouldReverseScan.size()); for (size_t i = 0; i < ixscanNodes.size(); ++i) { if (shouldReverseScan[i]) { - QueryPlannerCommon::reverseScans(ixscanNodes[i].get()); + QueryPlannerCommon::reverseScans(ixscanNodes[i]); } } - auto msn = stdx::make_unique<MergeSortNode>(); + MergeSortNode* msn = new MergeSortNode(); msn->sort = query.getQueryRequest().getSort(); - msn->addChildren(std::move(ixscanNodes)); - orResult = std::move(msn); + msn->children.swap(ixscanNodes); + orResult = msn; } else { - auto orn = stdx::make_unique<OrNode>(); - orn->addChildren(std::move(ixscanNodes)); - orResult = std::move(orn); + OrNode* orn = new OrNode(); + orn->children.swap(ixscanNodes); + orResult = orn; } } @@ -1148,48 +1166,47 @@ std::unique_ptr<QuerySolutionNode> QueryPlannerAccess::buildIndexedOr( return orResult; } -std::unique_ptr<QuerySolutionNode> QueryPlannerAccess::buildIndexedDataAccess( - const CanonicalQuery& query, - std::unique_ptr<MatchExpression> root, - const vector<IndexEntry>& indices, - const QueryPlannerParams& params) { - MatchExpression* unownedRoot = root.get(); - return _buildIndexedDataAccess(query, unownedRoot, std::move(root), indices, params); -} - -std::unique_ptr<QuerySolutionNode> QueryPlannerAccess::_buildIndexedDataAccess( - const CanonicalQuery& query, - MatchExpression* root, - std::unique_ptr<MatchExpression> ownedRoot, - const vector<IndexEntry>& indices, - const QueryPlannerParams& params) { +// static +QuerySolutionNode* QueryPlannerAccess::buildIndexedDataAccess(const CanonicalQuery& query, + MatchExpression* root, + bool inArrayOperator, + const vector<IndexEntry>& indices, + const QueryPlannerParams& params) { if (root->getCategory() == MatchExpression::MatchCategory::kLogical && !Indexability::isBoundsGeneratingNot(root)) { if (MatchExpression::AND == root->matchType()) { // Takes ownership of root. - return buildIndexedAnd(query, root, std::move(ownedRoot), indices, params); + return buildIndexedAnd(query, root, inArrayOperator, indices, params); } else if (MatchExpression::OR == root->matchType()) { // Takes ownership of root. - return buildIndexedOr(query, root, std::move(ownedRoot), indices, params); + return buildIndexedOr(query, root, inArrayOperator, indices, params); } else { - return nullptr; + // Can't do anything with negated logical nodes index-wise. + if (!inArrayOperator) { + delete root; + } + return NULL; } } else { - if (!root->getTag()) { + unique_ptr<MatchExpression> autoRoot; + if (!inArrayOperator) { + autoRoot.reset(root); + } + + if (NULL == root->getTag()) { // No index to use here, not in the context of logical operator, so we're SOL. - return nullptr; + return NULL; } else if (Indexability::isBoundsGenerating(root)) { // Make an index scan over the tagged index #. IndexTag* tag = static_cast<IndexTag*>(root->getTag()); IndexBoundsBuilder::BoundsTightness tightness = IndexBoundsBuilder::EXACT; - auto soln = makeLeafNode(query, indices[tag->index], tag->pos, root, &tightness); + QuerySolutionNode* soln = + makeLeafNode(query, indices[tag->index], tag->pos, root, &tightness); verify(NULL != soln); - finishLeafNode(soln.get(), indices[tag->index]); + finishLeafNode(soln, indices[tag->index]); - if (!ownedRoot) { - // We're performing access planning for the child of an array operator such as - // $elemMatch value. + if (inArrayOperator) { return soln; } @@ -1205,49 +1222,52 @@ std::unique_ptr<QuerySolutionNode> QueryPlannerAccess::_buildIndexedDataAccess( } else if (tightness == IndexBoundsBuilder::INEXACT_COVERED && !indices[tag->index].multikey) { verify(NULL == soln->filter.get()); - soln->filter = std::move(ownedRoot); + soln->filter.reset(autoRoot.release()); return soln; } else { - auto fetch = stdx::make_unique<FetchNode>(); - fetch->filter = std::move(ownedRoot); - fetch->children.push_back(soln.release()); + FetchNode* fetch = new FetchNode(); + verify(NULL != autoRoot.get()); + fetch->filter.reset(autoRoot.release()); + fetch->children.push_back(soln); return fetch; } } else if (Indexability::arrayUsesIndexOnChildren(root)) { - std::unique_ptr<QuerySolutionNode> solution; + QuerySolutionNode* solution = NULL; invariant(root->matchType() == MatchExpression::ELEM_MATCH_OBJECT); // The child is an AND. invariant(1 == root->numChildren()); - - // Recursively build a data access plan for the child of the $elemMatch object. We - // maintain ownership of 'ownedRoot'. - solution = _buildIndexedDataAccess(query, root->getChild(0), nullptr, indices, params); - if (!solution) { - return nullptr; + solution = buildIndexedDataAccess(query, root->getChild(0), true, indices, params); + if (NULL == solution) { + return NULL; } // There may be an array operator above us. - if (!ownedRoot) { + if (inArrayOperator) { return solution; } - auto fetch = stdx::make_unique<FetchNode>(); - fetch->filter = std::move(ownedRoot); - fetch->children.push_back(solution.release()); + FetchNode* fetch = new FetchNode(); + // Takes ownership of 'root'. + verify(NULL != autoRoot.get()); + fetch->filter.reset(autoRoot.release()); + fetch->children.push_back(solution); return fetch; } } - return nullptr; + if (!inArrayOperator) { + delete root; + } + + return NULL; } -std::unique_ptr<QuerySolutionNode> QueryPlannerAccess::scanWholeIndex( - const IndexEntry& index, - const CanonicalQuery& query, - const QueryPlannerParams& params, - int direction) { - std::unique_ptr<QuerySolutionNode> solnRoot; +QuerySolutionNode* QueryPlannerAccess::scanWholeIndex(const IndexEntry& index, + const CanonicalQuery& query, + const QueryPlannerParams& params, + int direction) { + QuerySolutionNode* solnRoot = NULL; // Build an ixscan over the id index, use it, and return it. unique_ptr<IndexScanNode> isn = make_unique<IndexScanNode>(index); @@ -1265,19 +1285,20 @@ std::unique_ptr<QuerySolutionNode> QueryPlannerAccess::scanWholeIndex( // If it's find({}) remove the no-op root. if (MatchExpression::AND == filter->matchType() && (0 == filter->numChildren())) { - solnRoot = std::move(isn); + solnRoot = isn.release(); } else { // TODO: We may not need to do the fetch if the predicates in root are covered. But // for now it's safe (though *maybe* slower). unique_ptr<FetchNode> fetch = make_unique<FetchNode>(); fetch->filter = std::move(filter); fetch->children.push_back(isn.release()); - solnRoot = std::move(fetch); + solnRoot = fetch.release(); } return solnRoot; } +// static void QueryPlannerAccess::addFilterToSolutionNode(QuerySolutionNode* node, MatchExpression* match, MatchExpression::MatchType type) { @@ -1306,6 +1327,7 @@ void QueryPlannerAccess::addFilterToSolutionNode(QuerySolutionNode* node, } } +// static void QueryPlannerAccess::handleFilter(ScanBuildingState* scanState) { if (MatchExpression::OR == scanState->root->matchType()) { handleFilterOr(scanState); @@ -1317,6 +1339,7 @@ void QueryPlannerAccess::handleFilter(ScanBuildingState* scanState) { } } +// static void QueryPlannerAccess::handleFilterOr(ScanBuildingState* scanState) { MatchExpression* root = scanState->root; MatchExpression* child = root->getChild(scanState->curChild); @@ -1337,6 +1360,7 @@ void QueryPlannerAccess::handleFilterOr(ScanBuildingState* scanState) { } } +// static void QueryPlannerAccess::handleFilterAnd(ScanBuildingState* scanState) { MatchExpression* root = scanState->root; MatchExpression* child = root->getChild(scanState->curChild); @@ -1372,16 +1396,15 @@ void QueryPlannerAccess::handleFilterAnd(ScanBuildingState* scanState) { } } -std::unique_ptr<QuerySolutionNode> QueryPlannerAccess::makeIndexScan( - const IndexEntry& index, - const CanonicalQuery& query, - const QueryPlannerParams& params, - const BSONObj& startKey, - const BSONObj& endKey) { - std::unique_ptr<QuerySolutionNode> solnRoot; +QuerySolutionNode* QueryPlannerAccess::makeIndexScan(const IndexEntry& index, + const CanonicalQuery& query, + const QueryPlannerParams& params, + const BSONObj& startKey, + const BSONObj& endKey) { + QuerySolutionNode* solnRoot = NULL; // Build an ixscan over the id index, use it, and return it. - auto isn = stdx::make_unique<IndexScanNode>(index); + IndexScanNode* isn = new IndexScanNode(index); isn->direction = 1; isn->addKeyMetadata = query.getQueryRequest().returnKey(); isn->bounds.isSimpleRange = true; @@ -1394,14 +1417,14 @@ std::unique_ptr<QuerySolutionNode> QueryPlannerAccess::makeIndexScan( // If it's find({}) remove the no-op root. if (MatchExpression::AND == filter->matchType() && (0 == filter->numChildren())) { - solnRoot = std::move(isn); + solnRoot = isn; } else { // TODO: We may not need to do the fetch if the predicates in root are covered. But // for now it's safe (though *maybe* slower). unique_ptr<FetchNode> fetch = make_unique<FetchNode>(); fetch->filter = std::move(filter); - fetch->children.push_back(isn.release()); - solnRoot = std::move(fetch); + fetch->children.push_back(isn); + solnRoot = fetch.release(); } return solnRoot; diff --git a/src/mongo/db/query/planner_access.h b/src/mongo/db/query/planner_access.h index e2097f2ab62..79765471477 100644 --- a/src/mongo/db/query/planner_access.h +++ b/src/mongo/db/query/planner_access.h @@ -95,42 +95,6 @@ namespace mongo { class QueryPlannerAccess { public: /** - * Return a CollectionScanNode that scans as requested in 'query'. - */ - static std::unique_ptr<QuerySolutionNode> makeCollectionScan(const CanonicalQuery& query, - bool tailable, - const QueryPlannerParams& params); - - /** - * Return a plan that uses the provided index as a proxy for a collection scan. - */ - static std::unique_ptr<QuerySolutionNode> scanWholeIndex(const IndexEntry& index, - const CanonicalQuery& query, - const QueryPlannerParams& params, - int direction = 1); - - /** - * Return a plan that scans the provided index from [startKey to endKey). - */ - static std::unique_ptr<QuerySolutionNode> makeIndexScan(const IndexEntry& index, - const CanonicalQuery& query, - const QueryPlannerParams& params, - const BSONObj& startKey, - const BSONObj& endKey); - - /** - * Consructs a data access plan for 'query' which answers the predicate contained in 'root'. - * Assumes the presence of the passed in indices. Planning behavior is controlled by the - * settings in 'params'. - */ - static std::unique_ptr<QuerySolutionNode> buildIndexedDataAccess( - const CanonicalQuery& query, - std::unique_ptr<MatchExpression> root, - const std::vector<IndexEntry>& indices, - const QueryPlannerParams& params); - -private: - /** * Building the leaves (i.e. the index scans) is done by looping through * predicates one at a time. During the process, there is a fair amount of state * information to keep track of, which we consolidate into this data structure. @@ -162,7 +126,7 @@ private: loosestBounds = IndexBoundsBuilder::EXACT; if (MatchExpression::OR == root->matchType()) { - curOr = stdx::make_unique<OrMatchExpression>(); + curOr.reset(new OrMatchExpression()); } } @@ -218,42 +182,72 @@ private: ScanBuildingState(); }; - // When recursively building data access, the caller may either continue to hold ownership of - // 'root', or may transfer ownership. When the caller holds ownership, it passes an owned - // pointer in 'root' and nullptr in 'ownedRoot'. When the caller transfers ownership, it passes - // owned and unowned pointers to the same match expression node in 'root' and 'ownedRoot'. + /** + * Return a CollectionScanNode that scans as requested in 'query'. + */ + static std::unique_ptr<QuerySolutionNode> makeCollectionScan(const CanonicalQuery& query, + bool tailable, + const QueryPlannerParams& params); + + /** + * Return a plan that uses the provided index as a proxy for a collection scan. + */ + static QuerySolutionNode* scanWholeIndex(const IndexEntry& index, + const CanonicalQuery& query, + const QueryPlannerParams& params, + int direction = 1); + + /** + * Return a plan that scans the provided index from [startKey to endKey). + */ + static QuerySolutionNode* makeIndexScan(const IndexEntry& index, + const CanonicalQuery& query, + const QueryPlannerParams& params, + const BSONObj& startKey, + const BSONObj& endKey); + + // + // Indexed Data Access methods. // - // The caller only holds ownership when planning inside an "array operator", e.g. when - // recursively performing access planning for an $elemMatch object. Therefore, whether or not - // 'ownedRoot' is null is also used as a check for whether we need to apply special logic for - // the "in array operator" case. + // The inArrayOperator flag deserves some attention. It is set when we're processing a + // child of an MatchExpression::ELEM_MATCH_OBJECT. // - // Specifically, for $elemMatch nodes, the entire $elemMatch filter must be attached to a FETCH - // node. This is why the caller retains ownership of the $elemMatch filter. However, the - // recursive call for children of the $elemMatch will also refrain from adding any FETCH stages. - // There will be a final FETCH node added to which the entire $elemMatch filter will be affixed. - static std::unique_ptr<QuerySolutionNode> _buildIndexedDataAccess( - const CanonicalQuery& query, - MatchExpression* root, - std::unique_ptr<MatchExpression> ownedRoot, - const std::vector<IndexEntry>& indices, - const QueryPlannerParams& params); - - // See _buildIndexedDataAccess() for description of 'root' and 'ownedRoot'. - static std::unique_ptr<QuerySolutionNode> buildIndexedAnd( - const CanonicalQuery& query, - MatchExpression* root, - std::unique_ptr<MatchExpression> ownedRoot, - const std::vector<IndexEntry>& indices, - const QueryPlannerParams& params); - - // See _buildIndexedDataAccess() for description of 'root' and 'ownedRoot'. - static std::unique_ptr<QuerySolutionNode> buildIndexedOr( - const CanonicalQuery& query, - MatchExpression* root, - std::unique_ptr<MatchExpression> ownedRoot, - const std::vector<IndexEntry>& indices, - const QueryPlannerParams& params); + // When true, the following behavior changes for all methods below that take it as an argument: + // 0. No deletion of MatchExpression(s). In fact, + // 1. No mutation of the MatchExpression at all. We need the tree as-is in order to perform + // a filter on the entire tree. + // 2. No fetches performed. There will be a final fetch by the caller of buildIndexedDataAccess + // who set the value of inArrayOperator to true. + // 3. No compound indices are used and no bounds are combined. These are + // incorrect in the context of these operators. + // + + /** + * If 'inArrayOperator' is false, takes ownership of 'root'. + */ + static QuerySolutionNode* buildIndexedDataAccess(const CanonicalQuery& query, + MatchExpression* root, + bool inArrayOperator, + const std::vector<IndexEntry>& indices, + const QueryPlannerParams& params); + + /** + * Takes ownership of 'root'. + */ + static QuerySolutionNode* buildIndexedAnd(const CanonicalQuery& query, + MatchExpression* root, + bool inArrayOperator, + const std::vector<IndexEntry>& indices, + const QueryPlannerParams& params); + + /** + * Takes ownership of 'root'. + */ + static QuerySolutionNode* buildIndexedOr(const CanonicalQuery& query, + MatchExpression* root, + bool inArrayOperator, + const std::vector<IndexEntry>& indices, + const QueryPlannerParams& params); /** * Traverses the tree rooted at the $elemMatch expression 'node', @@ -272,8 +266,8 @@ private: /** * Given a list of OR-related subtrees returned by processIndexScans(), looks for logically - * equivalent IndexScanNodes and combines them. This is an optimization to avoid creating plans - * that repeat index access work. + * equivalent IndexScanNodes and combines them. This is an optimization to avoid creating + * plans that repeat index access work. * * Example: * Suppose processIndexScans() returns a list of the following three query solutions: @@ -285,16 +279,20 @@ private: * 2) FETCH (filter: {$or:[{d:1}, {e:1}]}) -> IXSCAN (bounds: {c: [[3,3]]}) * * Used as a helper for buildIndexedOr(). + * + * Takes ownership of 'scans'. The caller assumes ownership of the pointers in the returned + * list of QuerySolutionNode*. */ - static std::vector<std::unique_ptr<QuerySolutionNode>> collapseEquivalentScans( - std::vector<std::unique_ptr<QuerySolutionNode>> scans); + static std::vector<QuerySolutionNode*> collapseEquivalentScans( + const std::vector<QuerySolutionNode*> scans); /** * Helper used by buildIndexedAnd and buildIndexedOr. * - * The children of AND and OR nodes are sorted by the index that the subtree rooted at that node - * uses. Child nodes that use the same index are adjacent to one another to facilitate grouping - * of index scans. As such, the processing for AND and OR is almost identical. + * The children of AND and OR nodes are sorted by the index that the subtree rooted at + * that node uses. Child nodes that use the same index are adjacent to one another to + * facilitate grouping of index scans. As such, the processing for AND and OR is + * almost identical. * * Does not take ownership of 'root' but may remove children from it. */ @@ -303,18 +301,19 @@ private: bool inArrayOperator, const std::vector<IndexEntry>& indices, const QueryPlannerParams& params, - std::vector<std::unique_ptr<QuerySolutionNode>>* out); + std::vector<QuerySolutionNode*>* out); /** - * Used by processIndexScans(...) in order to recursively build a data access plan for a - * "subnode", a node in the MatchExpression tree which is indexed by virtue of its children. + * Used by processIndexScans(...) in order to recursively build a data access + * plan for a "subnode", a node in the MatchExpression tree which is indexed by + * virtue of its children. * * The resulting scans are outputted in the out-parameter 'out'. */ static bool processIndexScansSubnode(const CanonicalQuery& query, ScanBuildingState* scanState, const QueryPlannerParams& params, - std::vector<std::unique_ptr<QuerySolutionNode>>* out); + std::vector<QuerySolutionNode*>* out); /** * Used by processIndexScansSubnode(...) to build the leaves of the solution tree for an @@ -325,7 +324,7 @@ private: static bool processIndexScansElemMatch(const CanonicalQuery& query, ScanBuildingState* scanState, const QueryPlannerParams& params, - std::vector<std::unique_ptr<QuerySolutionNode>>* out); + std::vector<QuerySolutionNode*>* out); // // Helpers for creating an index scan. @@ -334,18 +333,17 @@ private: /** * Create a new data access node. * - * If the node is an index scan, the bounds for 'expr' are computed and placed into the first - * field's OIL position. The rest of the OILs are allocated but uninitialized. + * If the node is an index scan, the bounds for 'expr' are computed and placed into the + * first field's OIL position. The rest of the OILs are allocated but uninitialized. * - * If the node is a geo node, grab the geo data from 'expr' and stuff it into the geo solution - * node of the appropriate type. + * If the node is a geo node, grab the geo data from 'expr' and stuff it into the + * geo solution node of the appropriate type. */ - static std::unique_ptr<QuerySolutionNode> makeLeafNode( - const CanonicalQuery& query, - const IndexEntry& index, - size_t pos, - const MatchExpression* expr, - IndexBoundsBuilder::BoundsTightness* tightnessOut); + static QuerySolutionNode* makeLeafNode(const CanonicalQuery& query, + const IndexEntry& index, + size_t pos, + MatchExpression* expr, + IndexBoundsBuilder::BoundsTightness* tightnessOut); /** * Merge the predicate 'expr' with the leaf node 'node'. @@ -375,11 +373,11 @@ private: * 'scanState'. The resulting scan is outputted in the out-parameter 'out', transferring * ownership in the process. * - * If 'scanState' is building an index scan for OR-related predicates, filters may be affixed to - * the scan as necessary. + * If 'scanState' is building an index scan for OR-related predicates, filters + * may be affixed to the scan as necessary. */ static void finishAndOutputLeaf(ScanBuildingState* scanState, - std::vector<std::unique_ptr<QuerySolutionNode>>* out); + std::vector<QuerySolutionNode*>* out); /** * Returns true if the current scan in 'scanState' requires a FetchNode. diff --git a/src/mongo/db/query/query_planner.cpp b/src/mongo/db/query/query_planner.cpp index 5d65081b66e..7e77db9b567 100644 --- a/src/mongo/db/query/query_planner.cpp +++ b/src/mongo/db/query/query_planner.cpp @@ -502,7 +502,7 @@ StatusWith<std::unique_ptr<QuerySolution>> QueryPlanner::planFromCache( // Use the cached index assignments to build solnRoot. std::unique_ptr<QuerySolutionNode> solnRoot(QueryPlannerAccess::buildIndexedDataAccess( - query, std::move(clone), params.indices, params)); + query, clone.release(), false, params.indices, params)); if (!solnRoot) { return Status(ErrorCodes::BadValue, @@ -824,15 +824,15 @@ StatusWith<std::vector<std::unique_ptr<QuerySolution>>> QueryPlanner::plan( PlanEnumerator isp(enumParams); isp.init().transitional_ignore(); - unique_ptr<MatchExpression> nextTaggedTree; - while ((nextTaggedTree = isp.getNext()) && (out.size() < params.maxIndexedSolutions)) { + unique_ptr<MatchExpression> rawTree; + while ((rawTree = isp.getNext()) && (out.size() < params.maxIndexedSolutions)) { LOG(5) << "About to build solntree from tagged tree:" << endl - << redact(nextTaggedTree->toString()); + << redact(rawTree.get()->toString()); // Store the plan cache index tree before calling prepareForAccessingPlanning(), so that // the PlanCacheIndexTree has the same sort as the MatchExpression used to generate the // plan cache key. - std::unique_ptr<MatchExpression> clone(nextTaggedTree->shallowClone()); + std::unique_ptr<MatchExpression> clone(rawTree.get()->shallowClone()); std::unique_ptr<PlanCacheIndexTree> cacheData; auto statusWithCacheData = cacheDataFromTaggedTree(clone.get(), relevantIndices); if (!statusWithCacheData.isOK()) { @@ -844,11 +844,11 @@ StatusWith<std::vector<std::unique_ptr<QuerySolution>>> QueryPlanner::plan( // We have already cached the tree in canonical order, so now we can order the nodes for // access planning. - prepareForAccessPlanning(nextTaggedTree.get()); + prepareForAccessPlanning(rawTree.get()); // This can fail if enumeration makes a mistake. std::unique_ptr<QuerySolutionNode> solnRoot(QueryPlannerAccess::buildIndexedDataAccess( - query, std::move(nextTaggedTree), relevantIndices, params)); + query, rawTree.release(), false, relevantIndices, params)); if (!solnRoot) { continue; diff --git a/src/mongo/db/query/query_solution.h b/src/mongo/db/query/query_solution.h index 0877be76724..66fccded3ab 100644 --- a/src/mongo/db/query/query_solution.h +++ b/src/mongo/db/query/query_solution.h @@ -144,23 +144,7 @@ struct QuerySolutionNode { } } - /** - * Adds a vector of query solution nodes to the list of children of this node. - * - * TODO SERVER-35512: Once 'children' are held by unique_ptr, this method should no longer be - * necessary. - */ - void addChildren(std::vector<std::unique_ptr<QuerySolutionNode>> newChildren) { - children.reserve(children.size() + newChildren.size()); - std::transform(newChildren.begin(), - newChildren.end(), - std::back_inserter(children), - [](auto& child) { return child.release(); }); - } - // These are owned here. - // - // TODO SERVER-35512: Make this a vector of unique_ptr. std::vector<QuerySolutionNode*> children; // If a stage has a non-NULL filter all values outputted from that stage must pass that |