diff options
author | David Percy <david.percy@mongodb.com> | 2020-01-22 18:12:37 +0000 |
---|---|---|
committer | Evergreen Agent <no-reply@evergreen.mongodb.com> | 2020-03-31 23:04:51 +0000 |
commit | ce1ca544dcc479b2638d83f3115994bf3da9de88 (patch) | |
tree | 7f26602c2ba40b91b2fcac961f916f3711cb9c15 | |
parent | 084ae4ef6a3d050ee8599d04d9bac3ed5b7369d9 (diff) | |
download | mongo-ce1ca544dcc479b2638d83f3115994bf3da9de88.tar.gz |
SERVER-45508 Fix crash when planning collated sort within multi point interval
-rw-r--r-- | jstests/core/index_sort_within_multiple_point_ranges.js | 24 | ||||
-rw-r--r-- | src/mongo/db/query/index_bounds.cpp | 21 | ||||
-rw-r--r-- | src/mongo/db/query/index_bounds_test.cpp | 8 |
3 files changed, 45 insertions, 8 deletions
diff --git a/jstests/core/index_sort_within_multiple_point_ranges.js b/jstests/core/index_sort_within_multiple_point_ranges.js new file mode 100644 index 00000000000..bbf175e9d9e --- /dev/null +++ b/jstests/core/index_sort_within_multiple_point_ranges.js @@ -0,0 +1,24 @@ +/* + * This is a regression test for SERVER-45508, which was an invariant failure in the query planner. + * + * Previously the invariant would be triggered only when all of these happen together: + * - an index with a collation exists + * - the query planner chooses index bounds with more than one point range + * - the point ranges are indexed in descending order + * - more than one index can satisfy the query + * - the query asks for a sort within each point range + * @tags: [assumes_no_implicit_collection_creation_after_drop] + */ +(function() { +'use strict'; + +const coll = db.collation_multi_point_range; +coll.drop(); +assert.commandWorked(db.createCollection(coll.getName(), {collation: {locale: 'fr'}})); +assert.commandWorked(coll.createIndex({x: -1})); +assert.commandWorked(coll.createIndex({x: -1, y: 1})); +coll.insert({x: 2, y: 99}); + +assert.commandWorked(coll.find({x: {$in: [2, 5]}}).sort({y: 1}).explain()); +assert.eq(1, coll.find({x: {$in: [2, 5]}}).sort({y: 1}).itcount()); +})(); diff --git a/src/mongo/db/query/index_bounds.cpp b/src/mongo/db/query/index_bounds.cpp index 929f73368f3..bea6a10f75a 100644 --- a/src/mongo/db/query/index_bounds.cpp +++ b/src/mongo/db/query/index_bounds.cpp @@ -225,14 +225,19 @@ OrderedIntervalList OrderedIntervalList::reverseClone() const { } Interval::Direction OrderedIntervalList::computeDirection() const { - for (auto&& iv : intervals) { - const auto dir = iv.getDirection(); - if (dir != Interval::Direction::kDirectionNone) { - return dir; - } - } - - return Interval::Direction::kDirectionNone; + if (intervals.empty()) + return Interval::Direction::kDirectionNone; + + // Because the interval list is ordered, we only need to compare the two endpoints of the + // overall list. If the endpoints are ascending or descending, then each interval already + // respects that order. And if the endpoints are equal, then all the intervals must be squeezed + // into a single point. + bool compareFieldNames = false; + int res = intervals.front().start.woCompare(intervals.back().end, compareFieldNames); + if (res == 0) + return Interval::Direction::kDirectionNone; + return res < 0 ? Interval::Direction::kDirectionAscending + : Interval::Direction::kDirectionDescending; } // static diff --git a/src/mongo/db/query/index_bounds_test.cpp b/src/mongo/db/query/index_bounds_test.cpp index beded95e98a..a6328bbe08a 100644 --- a/src/mongo/db/query/index_bounds_test.cpp +++ b/src/mongo/db/query/index_bounds_test.cpp @@ -173,6 +173,14 @@ TEST(IndexBoundsTest, OILReverseClone) { Interval(BSON("" << 20 << "" << 7), false, true)}; ASSERT(reverseList == listClone); ASSERT(listClone.computeDirection() == Interval::Direction::kDirectionDescending); + + OrderedIntervalList descendingPoints("foo"); + descendingPoints.intervals.push_back(Interval(BSON("" << 7 << "" << 7), true, true)); + descendingPoints.intervals.push_back(Interval(BSON("" << 5 << "" << 5), true, true)); + ASSERT(descendingPoints.computeDirection() == Interval::Direction::kDirectionDescending); + + OrderedIntervalList ascendingPoints = descendingPoints.reverseClone(); + ASSERT(ascendingPoints.computeDirection() == Interval::Direction::kDirectionAscending); } // |