summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlya Berciu <alyacarina@gmail.com>2021-05-21 15:24:08 +0100
committerEvergreen Agent <no-reply@evergreen.mongodb.com>2021-05-25 13:58:09 +0000
commit8a16d9e587db35a2a9e21a0ec21ef07c47c7e2e5 (patch)
tree3132da2a2791cae9ca3de25b7729395643c8a259
parent8b9eaa903128dea6c70093bddd8d2a241473ac24 (diff)
downloadmongo-8a16d9e587db35a2a9e21a0ec21ef07c47c7e2e5.tar.gz
SERVER-57091 Fix infinite loop in GranularityRounderPreferredNumbers::roundDown
(cherry picked from commit 441bef2c968ab24fe52d910c26dc3ecac366d0eb)
-rw-r--r--jstests/aggregation/sources/bucketauto/granularity_near_zero.js22
-rw-r--r--src/mongo/db/pipeline/granularity_rounder_preferred_numbers.cpp7
2 files changed, 29 insertions, 0 deletions
diff --git a/jstests/aggregation/sources/bucketauto/granularity_near_zero.js b/jstests/aggregation/sources/bucketauto/granularity_near_zero.js
new file mode 100644
index 00000000000..7f456f9428d
--- /dev/null
+++ b/jstests/aggregation/sources/bucketauto/granularity_near_zero.js
@@ -0,0 +1,22 @@
+// Tests when the granularity rounder needs to approach zero to round correctly. This test was
+// designed to reproduce SERVER-57091.
+(function() {
+"use strict";
+const coll = db.server57091;
+coll.drop();
+coll.insertOne({});
+
+const res =
+ coll.aggregate([{
+ $bucketAuto: {
+ groupBy: {
+ $reduce:
+ {input: [], initialValue: 4.940656484124654e-324, in : {$size: ["$$value"]}}
+ },
+ buckets: NumberLong("8"),
+ granularity: "R80"
+ }
+ }])
+ .toArray();
+assert.eq(res, [{_id: {min: 0, max: 1.02e-321}, count: 1}]);
+})();
diff --git a/src/mongo/db/pipeline/granularity_rounder_preferred_numbers.cpp b/src/mongo/db/pipeline/granularity_rounder_preferred_numbers.cpp
index d99f640521c..efdcf46e432 100644
--- a/src/mongo/db/pipeline/granularity_rounder_preferred_numbers.cpp
+++ b/src/mongo/db/pipeline/granularity_rounder_preferred_numbers.cpp
@@ -306,6 +306,13 @@ Value GranularityRounderPreferredNumbers::roundDown(Value value) {
multiplier /= 10.0;
}
+ // It is possible to get a number so small that the resulting multiplier would have to be
+ // smaller than the precision of a double, in which case the multiplier would equal 0. In
+ // this case, we can round down to 0.0.
+ if (multiplier == 0) {
+ return Value(0.0);
+ }
+
double previousMax;
while (number > (_baseSeries.back() * multiplier)) {
previousMax = _baseSeries.back() * multiplier;