diff options
author | Arun Banala <arun.banala@mongodb.com> | 2020-10-21 17:39:22 +0100 |
---|---|---|
committer | Evergreen Agent <no-reply@evergreen.mongodb.com> | 2020-11-20 12:21:39 +0000 |
commit | 23054195b6bf8ae84d7c06610b062f22632402fd (patch) | |
tree | 7d152189e559ee82249bb8f3e29b5d1a6394ea86 | |
parent | db72156b34591a37f98f1eeae0e5d0c67ed555ff (diff) | |
download | mongo-23054195b6bf8ae84d7c06610b062f22632402fd.tar.gz |
SERVER-50445 Return the value in double when NumberLong subtraction overflows in ExpressionSubtract
(cherry picked from commit 3518bd82e49b6941ee7a2f3a868df40114b0d8fc)
(cherry picked from commit 309b631dd16e90e1f3fb8bf3567df1fedc92d715)
-rw-r--r-- | src/mongo/db/pipeline/expression.cpp | 10 | ||||
-rw-r--r-- | src/mongo/db/pipeline/expression_test.cpp | 34 |
2 files changed, 41 insertions, 3 deletions
diff --git a/src/mongo/db/pipeline/expression.cpp b/src/mongo/db/pipeline/expression.cpp index 78932b2c044..61e8dfcd0bc 100644 --- a/src/mongo/db/pipeline/expression.cpp +++ b/src/mongo/db/pipeline/expression.cpp @@ -4430,9 +4430,13 @@ Value ExpressionSubtract::evaluate(const Document& root, Variables* variables) c double left = lhs.coerceToDouble(); return Value(left - right); } else if (diffType == NumberLong) { - long long right = rhs.coerceToLong(); - long long left = lhs.coerceToLong(); - return Value(left - right); + long long result; + + // If there is an overflow, convert the values to doubles. + if (mongoSignedSubtractOverflow64(lhs.coerceToLong(), rhs.coerceToLong(), &result)) { + return Value(lhs.coerceToDouble() - rhs.coerceToDouble()); + } + return Value(result); } else if (diffType == NumberInt) { long long right = rhs.coerceToLong(); long long left = lhs.coerceToLong(); diff --git a/src/mongo/db/pipeline/expression_test.cpp b/src/mongo/db/pipeline/expression_test.cpp index a95cf3ff303..54a959e0d0f 100644 --- a/src/mongo/db/pipeline/expression_test.cpp +++ b/src/mongo/db/pipeline/expression_test.cpp @@ -6416,4 +6416,38 @@ TEST(NowAndClusterTime, BasicTest) { } } } // namespace NowAndClusterTime + +TEST(ExpressionSubtractTest, OverflowLong) { + const auto maxLong = std::numeric_limits<long long int>::max(); + const auto minLong = std::numeric_limits<long long int>::min(); + intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); + + // The following subtractions should not fit into a long long data type. + BSONObj obj = BSON("$subtract" << BSON_ARRAY(maxLong << minLong)); + auto expression = Expression::parseExpression(expCtx, obj, expCtx->variablesParseState); + Value result = expression->evaluate({}, &expCtx->variables); + ASSERT_EQ(result.getType(), BSONType::NumberDouble); + ASSERT_EQ(result.getDouble(), static_cast<double>(maxLong) - minLong); + + obj = BSON("$subtract" << BSON_ARRAY(minLong << maxLong)); + expression = Expression::parseExpression(expCtx, obj, expCtx->variablesParseState); + result = expression->evaluate({}, &expCtx->variables); + ASSERT_EQ(result.getType(), BSONType::NumberDouble); + ASSERT_EQ(result.getDouble(), static_cast<double>(minLong) - maxLong); + + // minLong = -1 - maxLong. The below subtraction should fit into long long data type. + obj = BSON("$subtract" << BSON_ARRAY(-1 << maxLong)); + expression = Expression::parseExpression(expCtx, obj, expCtx->variablesParseState); + result = expression->evaluate({}, &expCtx->variables); + ASSERT_EQ(result.getType(), BSONType::NumberLong); + ASSERT_EQ(result.getLong(), -1LL - maxLong); + + // The minLong's negation does not fit into long long, hence it should be converted to double + // data type. + obj = BSON("$subtract" << BSON_ARRAY(0 << minLong)); + expression = Expression::parseExpression(expCtx, obj, expCtx->variablesParseState); + result = expression->evaluate({}, &expCtx->variables); + ASSERT_EQ(result.getType(), BSONType::NumberDouble); + ASSERT_EQ(result.getDouble(), static_cast<double>(minLong) * -1); +} } // namespace ExpressionTests |