diff options
author | Anton Korshunov <anton.korshunov@mongodb.com> | 2019-11-08 12:33:44 +0000 |
---|---|---|
committer | evergreen <evergreen@mongodb.com> | 2019-11-08 12:33:44 +0000 |
commit | fd49b082b075d6fbda620fcbafe2a822f667cffa (patch) | |
tree | 176e0a8f35a5e37d0b6dede8fb210ca47eb29ae2 | |
parent | 253e674104d298e2f6882b5c8207a2058e648b42 (diff) | |
download | mongo-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.js | 48 | ||||
-rw-r--r-- | src/mongo/db/pipeline/expression.cpp | 6 |
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; } } |