summaryrefslogtreecommitdiff
path: root/src/mongo/db
diff options
context:
space:
mode:
authorArun Banala <arun.banala@mongodb.com>2020-10-21 17:39:22 +0100
committerEvergreen Agent <no-reply@evergreen.mongodb.com>2020-11-20 12:25:42 +0000
commit948e065e29ea96d6d248c50f4cfa8fb71cbcec55 (patch)
treedab000290d9e1ba8b8ede12f8ba253ce1b7454a6 /src/mongo/db
parentaf44d6124a35aec82a22693cade942c4f9d2f53a (diff)
downloadmongo-948e065e29ea96d6d248c50f4cfa8fb71cbcec55.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)
Diffstat (limited to 'src/mongo/db')
-rw-r--r--src/mongo/db/pipeline/expression.cpp10
-rw-r--r--src/mongo/db/pipeline/expression_test.cpp33
2 files changed, 40 insertions, 3 deletions
diff --git a/src/mongo/db/pipeline/expression.cpp b/src/mongo/db/pipeline/expression.cpp
index 304c0d302fe..51a284e6a52 100644
--- a/src/mongo/db/pipeline/expression.cpp
+++ b/src/mongo/db/pipeline/expression.cpp
@@ -4447,9 +4447,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 56a87101f87..0e08a8661e9 100644
--- a/src/mongo/db/pipeline/expression_test.cpp
+++ b/src/mongo/db/pipeline/expression_test.cpp
@@ -5948,4 +5948,37 @@ public:
SuiteInstance<All> myall;
+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