summaryrefslogtreecommitdiff
path: root/src/mongo/db/matcher
diff options
context:
space:
mode:
authorNikita Lapkov <nikita.lapkov@mongodb.com>2020-08-26 16:08:52 +0000
committerEvergreen Agent <no-reply@evergreen.mongodb.com>2020-08-27 19:54:59 +0000
commitcb131974041efdcd0b4a99fe3bd316cbb9b4db41 (patch)
treefdbfd41bdf5ee6ef2401495c8871df8dfaca47ac /src/mongo/db/matcher
parentc932e02133220269f37122499f7379f1758b7ad4 (diff)
downloadmongo-cb131974041efdcd0b4a99fe3bd316cbb9b4db41.tar.gz
SERVER-32960 Make $mod always truncate divisor and remainder toward zero
Diffstat (limited to 'src/mongo/db/matcher')
-rw-r--r--src/mongo/db/matcher/expression_parser.cpp11
-rw-r--r--src/mongo/db/matcher/expression_parser_leaf_test.cpp40
2 files changed, 49 insertions, 2 deletions
diff --git a/src/mongo/db/matcher/expression_parser.cpp b/src/mongo/db/matcher/expression_parser.cpp
index 1077c1ef8ec..cdef3bfd379 100644
--- a/src/mongo/db/matcher/expression_parser.cpp
+++ b/src/mongo/db/matcher/expression_parser.cpp
@@ -512,10 +512,17 @@ StatusWithMatchExpression parseMOD(StringData name,
if (iter.more())
return {Status(ErrorCodes::BadValue, "malformed mod, too many elements")};
+ auto truncateToInt = [](const BSONElement& element) -> int {
+ if (element.type() == BSONType::NumberDecimal) {
+ return element.numberDecimal().toInt(Decimal128::kRoundTowardZero);
+ }
+ return element.numberInt();
+ };
+
return {std::make_unique<ModMatchExpression>(
name,
- divisor.numberInt(),
- remainder.numberInt(),
+ truncateToInt(divisor),
+ truncateToInt(remainder),
doc_validation_error::createAnnotation(
expCtx, elem.fieldNameStringData().toString(), BSON(name << elem.wrap())))};
}
diff --git a/src/mongo/db/matcher/expression_parser_leaf_test.cpp b/src/mongo/db/matcher/expression_parser_leaf_test.cpp
index 3c1ac33dc3e..ac71de80045 100644
--- a/src/mongo/db/matcher/expression_parser_leaf_test.cpp
+++ b/src/mongo/db/matcher/expression_parser_leaf_test.cpp
@@ -350,6 +350,46 @@ TEST(MatchExpressionParserLeafTest, SimpleMod1) {
ASSERT(result.getValue()->matchesBSON(BSON("x" << 8)));
}
+TEST(MatchExpressionParserLeafTest, ModFloatTruncate) {
+ auto assertDivisorAndRemainder =
+ [](const BSONObj& query, int expectedDivisor, int expectedRemainder) {
+ boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
+ StatusWithMatchExpression result = MatchExpressionParser::parse(query, expCtx);
+ ASSERT_OK(result.getStatus());
+ auto modExpr = checked_cast<ModMatchExpression*>(result.getValue().get());
+ ASSERT_EQ(modExpr->getDivisor(), expectedDivisor);
+ ASSERT_EQ(modExpr->getRemainder(), expectedRemainder);
+ };
+
+ std::vector<BSONObj> positiveCases = {
+ BSON("x" << BSON("$mod" << BSON_ARRAY(3 << 2))),
+ BSON("x" << BSON("$mod" << BSON_ARRAY(3LL << 2LL))),
+ BSON("x" << BSON("$mod" << BSON_ARRAY(3.2 << 2.2))),
+ BSON("x" << BSON("$mod" << BSON_ARRAY(3.7 << 2.7))),
+ BSON("x" << BSON("$mod" << BSON_ARRAY(Decimal128("3") << Decimal128("2")))),
+ BSON("x" << BSON("$mod" << BSON_ARRAY(Decimal128("3.2") << Decimal128("2.2")))),
+ BSON("x" << BSON("$mod" << BSON_ARRAY(Decimal128("3.7") << Decimal128("2.7")))),
+ };
+
+ for (const auto& testCase : positiveCases) {
+ assertDivisorAndRemainder(testCase, 3, 2);
+ }
+
+ std::vector<BSONObj> negativeCases = {
+ BSON("x" << BSON("$mod" << BSON_ARRAY(-3 << -2))),
+ BSON("x" << BSON("$mod" << BSON_ARRAY(-3LL << -2LL))),
+ BSON("x" << BSON("$mod" << BSON_ARRAY(-3.2 << -2.2))),
+ BSON("x" << BSON("$mod" << BSON_ARRAY(-3.7 << -2.7))),
+ BSON("x" << BSON("$mod" << BSON_ARRAY(Decimal128("-3") << Decimal128("-2")))),
+ BSON("x" << BSON("$mod" << BSON_ARRAY(Decimal128("-3.2") << Decimal128("-2.2")))),
+ BSON("x" << BSON("$mod" << BSON_ARRAY(Decimal128("-3.7") << Decimal128("-2.7")))),
+ };
+
+ for (const auto& testCase : negativeCases) {
+ assertDivisorAndRemainder(testCase, -3, -2);
+ }
+}
+
TEST(MatchExpressionParserLeafTest, IdCollation) {
BSONObj query = BSON("$id"
<< "string");