diff options
-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; } } |