diff options
author | David Storch <david.storch@10gen.com> | 2017-12-06 12:28:16 -0500 |
---|---|---|
committer | David Storch <david.storch@10gen.com> | 2017-12-13 16:01:07 -0500 |
commit | 1e0d2b2e113df9e56af430f5ff09bac224cf05f0 (patch) | |
tree | 8d96bbb45a8e8d21408e911038a78728816eb30c /src | |
parent | 06c1da7fa0d99fe643674fa7d5b0533b7d364791 (diff) | |
download | mongo-1e0d2b2e113df9e56af430f5ff09bac224cf05f0.tar.gz |
SERVER-32189 Delete dead SubplanStage handling for contained $or queries.
Diffstat (limited to 'src')
-rw-r--r-- | src/mongo/db/exec/subplan.cpp | 67 | ||||
-rw-r--r-- | src/mongo/db/exec/subplan.h | 13 | ||||
-rw-r--r-- | src/mongo/dbtests/query_stage_subplan.cpp | 118 |
3 files changed, 2 insertions, 196 deletions
diff --git a/src/mongo/db/exec/subplan.cpp b/src/mongo/db/exec/subplan.cpp index f8b30a6a7a3..0d3ee99ffc4 100644 --- a/src/mongo/db/exec/subplan.cpp +++ b/src/mongo/db/exec/subplan.cpp @@ -71,30 +71,9 @@ SubplanStage::SubplanStage(OperationContext* opCtx, _plannerParams(params), _query(cq) { invariant(_collection); + invariant(_query->root()->matchType() == MatchExpression::OR); } -namespace { - -/** - * Returns true if 'expr' is an AND that contains a single OR child. - */ -bool isContainedOr(const MatchExpression* expr) { - if (MatchExpression::AND != expr->matchType()) { - return false; - } - - size_t numOrs = 0; - for (size_t i = 0; i < expr->numChildren(); ++i) { - if (MatchExpression::OR == expr->getChild(i)->matchType()) { - ++numOrs; - } - } - - return (numOrs == 1U); -} - -} // namespace - bool SubplanStage::canUseSubplanning(const CanonicalQuery& query) { const QueryRequest& qr = query.getQueryRequest(); const MatchExpression* expr = query.root(); @@ -126,54 +105,12 @@ bool SubplanStage::canUseSubplanning(const CanonicalQuery& query) { return false; } - // TODO: For now we only allow rooted OR. We should consider also allowing contained OR that - // does not have a TEXT or GEO_NEAR node. + // We can only subplan rooted $or queries. return MatchExpression::OR == expr->matchType(); } -std::unique_ptr<MatchExpression> SubplanStage::rewriteToRootedOr( - std::unique_ptr<MatchExpression> root) { - dassert(isContainedOr(root.get())); - - // Detach the OR from the root. - std::vector<MatchExpression*>& rootChildren = *root->getChildVector(); - std::unique_ptr<MatchExpression> orChild; - for (size_t i = 0; i < rootChildren.size(); ++i) { - if (MatchExpression::OR == rootChildren[i]->matchType()) { - orChild.reset(rootChildren[i]); - rootChildren.erase(rootChildren.begin() + i); - break; - } - } - - // We should have found an OR, and the OR should have at least 2 children. - invariant(orChild); - invariant(orChild->getChildVector()); - invariant(orChild->getChildVector()->size() > 1U); - - // AND the existing root with each OR child. - std::vector<MatchExpression*>& orChildren = *orChild->getChildVector(); - for (size_t i = 0; i < orChildren.size(); ++i) { - std::unique_ptr<AndMatchExpression> ama = stdx::make_unique<AndMatchExpression>(); - ama->add(orChildren[i]); - ama->add(root->shallowClone().release()); - orChildren[i] = ama.release(); - } - - // Normalize and sort the resulting match expression. - orChild = MatchExpression::optimize(std::move(orChild)); - CanonicalQuery::sortTree(orChild.get()); - - return orChild; -} - Status SubplanStage::planSubqueries() { _orExpression = _query->root()->shallowClone(); - if (isContainedOr(_orExpression.get())) { - _orExpression = rewriteToRootedOr(std::move(_orExpression)); - invariant(CanonicalQuery::isValid(_orExpression.get(), _query->getQueryRequest()).isOK()); - } - for (size_t i = 0; i < _plannerParams.indices.size(); ++i) { const IndexEntry& ie = _plannerParams.indices[i]; _indexMap[ie.name] = i; diff --git a/src/mongo/db/exec/subplan.h b/src/mongo/db/exec/subplan.h index 315e79b93ca..c61fb3201d4 100644 --- a/src/mongo/db/exec/subplan.h +++ b/src/mongo/db/exec/subplan.h @@ -107,19 +107,6 @@ public: */ Status pickBestPlan(PlanYieldPolicy* yieldPolicy); - /** - * Takes a match expression, 'root', which has a single "contained OR". This means that - * 'root' is an AND with exactly one OR child. - * - * Returns a logically equivalent query after rewriting so that the contained OR is at the - * root of the expression tree. - * - * Used internally so that the subplanner can be used for contained OR type queries, but - * exposed for testing. - */ - static std::unique_ptr<MatchExpression> rewriteToRootedOr( - std::unique_ptr<MatchExpression> root); - // // For testing. // diff --git a/src/mongo/dbtests/query_stage_subplan.cpp b/src/mongo/dbtests/query_stage_subplan.cpp index 279eafac9c7..c15f0fc6e71 100644 --- a/src/mongo/dbtests/query_stage_subplan.cpp +++ b/src/mongo/dbtests/query_stage_subplan.cpp @@ -402,8 +402,6 @@ TEST_F(QueryStageSubplanTest, QueryStageSubplanCanUseSubplanning) { } // Can't use subplanning for a single contained $or. - // - // TODO: Consider allowing this to use subplanning (see SERVER-13732). { std::string findCmd = "{find: 'testns'," @@ -413,8 +411,6 @@ TEST_F(QueryStageSubplanTest, QueryStageSubplanCanUseSubplanning) { } // Can't use subplanning if the contained $or query has a geo predicate. - // - // TODO: Consider allowing this to use subplanning (see SERVER-13732). { std::string findCmd = "{find: 'testns'," @@ -446,120 +442,6 @@ TEST_F(QueryStageSubplanTest, QueryStageSubplanCanUseSubplanning) { } /** - * Unit test the subplan stage's rewriteToRootedOr() method. - */ -TEST_F(QueryStageSubplanTest, QueryStageSubplanRewriteToRootedOr) { - // Rewrite (AND (OR a b) e) => (OR (AND a e) (AND b e)) - { - BSONObj queryObj = fromjson("{$or:[{a:1}, {b:1}], e:1}"); - boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); - StatusWithMatchExpression expr = MatchExpressionParser::parse(queryObj, expCtx); - ASSERT_OK(expr.getStatus()); - std::unique_ptr<MatchExpression> rewrittenExpr = - SubplanStage::rewriteToRootedOr(std::move(expr.getValue())); - - std::string findCmdRewritten = - "{find: 'testns'," - "filter: {$or:[{a:1,e:1}, {b:1,e:1}]}}"; - std::unique_ptr<CanonicalQuery> cqRewritten = cqFromFindCommand(findCmdRewritten); - - ASSERT(rewrittenExpr->equivalent(cqRewritten->root())); - } - - // Rewrite (AND (OR a b) e f) => (OR (AND a e f) (AND b e f)) - { - BSONObj queryObj = fromjson("{$or:[{a:1}, {b:1}], e:1, f:1}"); - boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); - StatusWithMatchExpression expr = MatchExpressionParser::parse(queryObj, expCtx); - ASSERT_OK(expr.getStatus()); - std::unique_ptr<MatchExpression> rewrittenExpr = - SubplanStage::rewriteToRootedOr(std::move(expr.getValue())); - - std::string findCmdRewritten = - "{find: 'testns'," - "filter: {$or:[{a:1,e:1,f:1}, {b:1,e:1,f:1}]}}"; - std::unique_ptr<CanonicalQuery> cqRewritten = cqFromFindCommand(findCmdRewritten); - - ASSERT(rewrittenExpr->equivalent(cqRewritten->root())); - } - - // Rewrite (AND (OR (AND a b) (AND c d) e f) => (OR (AND a b e f) (AND c d e f)) - { - BSONObj queryObj = fromjson("{$or:[{a:1,b:1}, {c:1,d:1}], e:1,f:1}"); - boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); - StatusWithMatchExpression expr = MatchExpressionParser::parse(queryObj, expCtx); - ASSERT_OK(expr.getStatus()); - std::unique_ptr<MatchExpression> rewrittenExpr = - SubplanStage::rewriteToRootedOr(std::move(expr.getValue())); - - std::string findCmdRewritten = - "{find: 'testns'," - "filter: {$or:[{a:1,b:1,e:1,f:1}," - "{c:1,d:1,e:1,f:1}]}}"; - std::unique_ptr<CanonicalQuery> cqRewritten = cqFromFindCommand(findCmdRewritten); - - ASSERT(rewrittenExpr->equivalent(cqRewritten->root())); - } -} - -/** - * Test the subplan stage's ability to answer a contained $or query. - */ -TEST_F(QueryStageSubplanTest, QueryStageSubplanPlanContainedOr) { - OldClientWriteContext ctx(opCtx(), nss.ns()); - addIndex(BSON("b" << 1 << "a" << 1)); - addIndex(BSON("c" << 1 << "a" << 1)); - - BSONObj query = fromjson("{a: 1, $or: [{b: 2}, {c: 3}]}"); - - // Two of these documents match. - insert(BSON("_id" << 1 << "a" << 1 << "b" << 2)); - insert(BSON("_id" << 2 << "a" << 2 << "b" << 2)); - insert(BSON("_id" << 3 << "a" << 1 << "c" << 3)); - insert(BSON("_id" << 4 << "a" << 1 << "c" << 4)); - - auto qr = stdx::make_unique<QueryRequest>(nss); - qr->setFilter(query); - auto cq = unittest::assertGet(CanonicalQuery::canonicalize(opCtx(), std::move(qr))); - - Collection* collection = ctx.getCollection(); - - // Get planner params. - QueryPlannerParams plannerParams; - fillOutPlannerParams(opCtx(), collection, cq.get(), &plannerParams); - - WorkingSet ws; - std::unique_ptr<SubplanStage> subplan( - new SubplanStage(opCtx(), collection, &ws, plannerParams, cq.get())); - - // Plan selection should succeed due to falling back on regular planning. - PlanYieldPolicy yieldPolicy(PlanExecutor::NO_YIELD, _clock); - ASSERT_OK(subplan->pickBestPlan(&yieldPolicy)); - - // Work the stage until it produces all results. - size_t numResults = 0; - PlanStage::StageState stageState = PlanStage::NEED_TIME; - while (stageState != PlanStage::IS_EOF) { - WorkingSetID id = WorkingSet::INVALID_ID; - stageState = subplan->work(&id); - ASSERT_NE(stageState, PlanStage::DEAD); - ASSERT_NE(stageState, PlanStage::FAILURE); - - if (stageState == PlanStage::ADVANCED) { - ++numResults; - WorkingSetMember* member = ws.get(id); - ASSERT(member->hasObj()); - ASSERT(SimpleBSONObjComparator::kInstance.evaluate( - member->obj.value() == BSON("_id" << 1 << "a" << 1 << "b" << 2)) || - SimpleBSONObjComparator::kInstance.evaluate( - member->obj.value() == BSON("_id" << 3 << "a" << 1 << "c" << 3))); - } - } - - ASSERT_EQ(numResults, 2U); -} - -/** * Test the subplan stage's ability to answer a rooted $or query with a $ne and a sort. * * Regression test for SERVER-19388. |