From f076d57d427e7fde945303aaec32ba594de5872c Mon Sep 17 00:00:00 2001 From: David Storch Date: Fri, 9 May 2014 13:55:49 -0400 Subject: SERVER-13890 index bounds builder should not pass already-constructed intervals to translate() (cherry picked from commit d19801d6209d6635e4fb9c42245723f1f1869bf0) --- src/mongo/db/query/index_bounds_builder.cpp | 12 +++++++++++- src/mongo/db/query/index_bounds_builder_test.cpp | 15 +++++++++++++++ src/mongo/db/query/query_planner_test.cpp | 12 ++++++++++++ 3 files changed, 38 insertions(+), 1 deletion(-) diff --git a/src/mongo/db/query/index_bounds_builder.cpp b/src/mongo/db/query/index_bounds_builder.cpp index 71367e21488..0aff65e317b 100644 --- a/src/mongo/db/query/index_bounds_builder.cpp +++ b/src/mongo/db/query/index_bounds_builder.cpp @@ -197,7 +197,14 @@ namespace mongo { const IndexEntry& index, OrderedIntervalList* oilOut, BoundsTightness* tightnessOut) { - translate(expr, elt, index, oilOut, tightnessOut); + OrderedIntervalList arg; + translate(expr, elt, index, &arg, tightnessOut); + + // Append the new intervals to oilOut. + oilOut->intervals.insert(oilOut->intervals.end(), arg.intervals.begin(), + arg.intervals.end()); + + // Union the appended intervals with the existing ones. unionize(oilOut); } @@ -216,6 +223,9 @@ namespace mongo { const IndexEntry& index, OrderedIntervalList* oilOut, BoundsTightness* tightnessOut) { + // We expect that the OIL we are constructing starts out empty. + invariant(oilOut->intervals.empty()); + oilOut->name = elt.fieldName(); bool isHashed = false; diff --git a/src/mongo/db/query/index_bounds_builder_test.cpp b/src/mongo/db/query/index_bounds_builder_test.cpp index 1420e48b69c..0a89cef23fb 100644 --- a/src/mongo/db/query/index_bounds_builder_test.cpp +++ b/src/mongo/db/query/index_bounds_builder_test.cpp @@ -1104,4 +1104,19 @@ namespace { ASSERT_EQUALS(tightness, IndexBoundsBuilder::EXACT); } + TEST(IndexBoundsBuilderTest, UnionizeWithNE) { + IndexEntry testIndex = IndexEntry(BSONObj()); + vector toUnionize; + toUnionize.push_back(fromjson("{a: {$ne: 3}}")); + toUnionize.push_back(fromjson("{a: {$ne: 4}}}")); + OrderedIntervalList oil; + IndexBoundsBuilder::BoundsTightness tightness; + testTranslateAndUnion(toUnionize, &oil, &tightness); + ASSERT_EQUALS(oil.name, "a"); + ASSERT_EQUALS(oil.intervals.size(), 1U); + ASSERT_EQUALS(Interval::INTERVAL_EQUALS, oil.intervals[0].compare( + IndexBoundsBuilder::allValues())); + ASSERT_EQUALS(tightness, IndexBoundsBuilder::EXACT); + } + } // namespace diff --git a/src/mongo/db/query/query_planner_test.cpp b/src/mongo/db/query/query_planner_test.cpp index acb8fb09b98..8e465a40287 100644 --- a/src/mongo/db/query/query_planner_test.cpp +++ b/src/mongo/db/query/query_planner_test.cpp @@ -3156,6 +3156,18 @@ namespace { "c: [[1,10,false,false]]}}}}}"); } + // Test that planner properly unionizes the index bounds for two negation + // predicates (SERVER-13890). + TEST_F(QueryPlannerTest, IndexBoundsOrOfNegations) { + addIndex(BSON("a" << 1)); + runQuery(fromjson("{$or: [{a: {$ne: 3}}, {a: {$ne: 4}}]}")); + + assertNumSolutions(2U); + assertSolutionExists("{cscan: {dir: 1}}"); + assertSolutionExists("{fetch: {filter: null, node: {ixscan: {pattern: {a:1}, " + "bounds: {a: [['MinKey','MaxKey',true,true]]}}}}}"); + } + // // Tests related to building index bounds for multikey // indices, combined with compound and $elemMatch -- cgit v1.2.1