summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Percy <david.percy@mongodb.com>2020-01-22 18:12:37 +0000
committerEvergreen Agent <no-reply@evergreen.mongodb.com>2020-03-31 23:04:51 +0000
commitce1ca544dcc479b2638d83f3115994bf3da9de88 (patch)
tree7f26602c2ba40b91b2fcac961f916f3711cb9c15
parent084ae4ef6a3d050ee8599d04d9bac3ed5b7369d9 (diff)
downloadmongo-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.js24
-rw-r--r--src/mongo/db/query/index_bounds.cpp21
-rw-r--r--src/mongo/db/query/index_bounds_test.cpp8
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);
}
//