summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAnton Korshunov <anton.korshunov@mongodb.com>2019-11-08 12:33:44 +0000
committerevergreen <evergreen@mongodb.com>2019-11-08 12:33:44 +0000
commitfd49b082b075d6fbda620fcbafe2a822f667cffa (patch)
tree176e0a8f35a5e37d0b6dede8fb210ca47eb29ae2
parent253e674104d298e2f6882b5c8207a2058e648b42 (diff)
downloadmongo-fd49b082b075d6fbda620fcbafe2a822f667cffa.tar.gz
SERVER-42756 $multiply with NaN operand violates commutativity
(cherry picked from commit aaa9874e04dbc2a4a33aeb7bfad9bee60f7145e0) (cherry picked from commit 6729eaa16ca2794425fd90f034506e8d30a0cb5f)
-rw-r--r--jstests/aggregation/bugs/server42756.js48
-rw-r--r--src/mongo/db/pipeline/expression.cpp6
2 files changed, 52 insertions, 2 deletions
diff --git a/jstests/aggregation/bugs/server42756.js b/jstests/aggregation/bugs/server42756.js
new file mode 100644
index 00000000000..a270dfcda02
--- /dev/null
+++ b/jstests/aggregation/bugs/server42756.js
@@ -0,0 +1,48 @@
+// SERVER-42756 Test that commutative arithmetic operations with special arguments doesn't violate
+// commutativity.
+(function() {
+ "use strict";
+
+ const coll = db[jsTest.name()];
+ coll.drop();
+ const numbers = [1.0, NumberInt("1"), NumberLong("1"), NumberDecimal("1.0")];
+ const specials = [{val: NaN, path: "$nan"}, {val: Infinity, path: "$inf"}];
+
+ assert.commandWorked(coll.insert({inf: Infinity, nan: NaN}));
+
+ (function testCommutativityWithConstArguments() {
+ specials.forEach((special) => {
+ numbers.forEach((num) => {
+ const expected = [
+ {a: (num instanceof NumberDecimal ? NumberDecimal(special.val) : special.val)}
+ ];
+ assert.eq(
+ expected,
+ coll.aggregate([{$project: {a: {"$multiply": [special.val, num]}, _id: 0}}])
+ .toArray());
+ assert.eq(
+ expected,
+ coll.aggregate([{$project: {a: {"$multiply": [num, special.val]}, _id: 0}}])
+ .toArray());
+ });
+ });
+ })();
+
+ (function testCommutativityWithNonConstArgument() {
+ specials.forEach((special) => {
+ numbers.forEach((num) => {
+ const expected = [
+ {a: (num instanceof NumberDecimal ? NumberDecimal(special.val) : special.val)}
+ ];
+ assert.eq(
+ expected,
+ coll.aggregate([{$project: {a: {"$multiply": [special.path, num]}, _id: 0}}])
+ .toArray());
+ assert.eq(
+ expected,
+ coll.aggregate([{$project: {a: {"$multiply": [num, special.path]}, _id: 0}}])
+ .toArray());
+ });
+ });
+ })();
+})();
diff --git a/src/mongo/db/pipeline/expression.cpp b/src/mongo/db/pipeline/expression.cpp
index 8dac3067d0e..1d7a9e3c159 100644
--- a/src/mongo/db/pipeline/expression.cpp
+++ b/src/mongo/db/pipeline/expression.cpp
@@ -2686,8 +2686,10 @@ Value ExpressionMultiply::evaluate(const Document& root, Variables* variables) c
decimalProduct = decimalProduct.multiply(val.coerceToDecimal());
} else {
doubleProduct *= val.coerceToDouble();
- if (mongoSignedMultiplyOverflow64(longProduct, val.coerceToLong(), &longProduct)) {
- // The 'longProduct' would have overflowed, so we're abandoning it.
+ if (!std::isfinite(val.coerceToDouble()) ||
+ mongoSignedMultiplyOverflow64(longProduct, val.coerceToLong(), &longProduct)) {
+ // The number is either Infinity or NaN, or the 'longProduct' would have
+ // overflowed, so we're abandoning it.
productType = NumberDouble;
}
}