summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Storch <david.storch@10gen.com>2017-12-06 12:28:16 -0500
committerDavid Storch <david.storch@10gen.com>2017-12-13 16:01:07 -0500
commit1e0d2b2e113df9e56af430f5ff09bac224cf05f0 (patch)
tree8d96bbb45a8e8d21408e911038a78728816eb30c
parent06c1da7fa0d99fe643674fa7d5b0533b7d364791 (diff)
downloadmongo-1e0d2b2e113df9e56af430f5ff09bac224cf05f0.tar.gz
SERVER-32189 Delete dead SubplanStage handling for contained $or queries.
-rw-r--r--src/mongo/db/exec/subplan.cpp67
-rw-r--r--src/mongo/db/exec/subplan.h13
-rw-r--r--src/mongo/dbtests/query_stage_subplan.cpp118
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.