diff options
author | KevinCybura <KevinCybura@gmail.com> | 2018-02-06 15:14:26 -0500 |
---|---|---|
committer | Charlie Swanson <charlie.swanson@mongodb.com> | 2018-02-10 11:52:43 -0500 |
commit | e6e8dcf826c8e25b53c8368a0d538dfbdeaee589 (patch) | |
tree | b0461f127e4723b86ab7cd29cac637b9c1448914 /src | |
parent | c4c4bef71ecd64db91e1252200d82a2f5c265cc6 (diff) | |
download | mongo-e6e8dcf826c8e25b53c8368a0d538dfbdeaee589.tar.gz |
SERVER-32873 Fix incorrect handling of NumberLong in $pow function
Signed-off-by: Charlie Swanson <charlie.swanson@mongodb.com>
Closes #1211
Diffstat (limited to 'src')
-rw-r--r-- | src/mongo/db/pipeline/expression.cpp | 15 | ||||
-rw-r--r-- | src/mongo/db/pipeline/expression_test.cpp | 16 |
2 files changed, 28 insertions, 3 deletions
diff --git a/src/mongo/db/pipeline/expression.cpp b/src/mongo/db/pipeline/expression.cpp index 3c4b5ef3dae..3ab0c300cd3 100644 --- a/src/mongo/db/pipeline/expression.cpp +++ b/src/mongo/db/pipeline/expression.cpp @@ -3154,14 +3154,23 @@ Value ExpressionPow::evaluate(const Document& root) const { long long baseLong = baseVal.getLong(); long long expLong = expVal.getLong(); - // If the result cannot be represented as a long, return a double. Otherwise if either number - // is a long, return a long. If both numbers are ints, then return an int if the result fits or - // a long if it is too big. + // If the result cannot be represented as a long, return a double. Otherwise if either number is + // a long, return a long. If both numbers are ints, then return an int if the result fits or a + // long if it is too big. if (!representableAsLong(baseLong, expLong)) { return Value(std::pow(baseLong, expLong)); } long long result = 1; + + // When 'baseLong' == -1 and 'expLong' is < 0 the following for loop will never run because + // 'expLong' will always be less than 0 so result will always be 1. This is not always correct + // because the result can potentially be -1. ex: 'baselong' = -1 'expLong' = -5 then result + // should be -1. + if (baseLong == -1 && expLong < 0) { + expLong = expLong % 2 == 0 ? 2 : 1; + } + // Use repeated multiplication, since pow() casts args to doubles which could result in loss of // precision if arguments are very large. for (int i = 0; i < expLong; i++) { diff --git a/src/mongo/db/pipeline/expression_test.cpp b/src/mongo/db/pipeline/expression_test.cpp index b2610e97bdb..345c5042f09 100644 --- a/src/mongo/db/pipeline/expression_test.cpp +++ b/src/mongo/db/pipeline/expression_test.cpp @@ -2193,6 +2193,22 @@ TEST(ExpressionFromAccumulators, StdDevSamp) { {{}, Value(BSONNULL)}}); } +TEST(ExpressionPowTest, NegativeOneRaisedToNegativeOddExponentShouldOutPutNegativeOne) { + assertExpectedResults("$pow", + { + {{Value(-1), Value(-1)}, Value(-1)}, + {{Value(-1), Value(-2)}, Value(1)}, + {{Value(-1), Value(-3)}, Value(-1)}, + + {{Value(-1LL), Value(0LL)}, Value(1LL)}, + {{Value(-1LL), Value(-1LL)}, Value(-1LL)}, + {{Value(-1LL), Value(-2LL)}, Value(1LL)}, + {{Value(-1LL), Value(-3LL)}, Value(-1LL)}, + {{Value(-1LL), Value(-4LL)}, Value(1LL)}, + {{Value(-1LL), Value(-5LL)}, Value(-1LL)}, + }); +} + namespace FieldPath { /** The provided field path does not pass validation. */ |