diff options
author | Nikita Lapkov <nikita.lapkov@mongodb.com> | 2020-08-26 16:08:52 +0000 |
---|---|---|
committer | Evergreen Agent <no-reply@evergreen.mongodb.com> | 2020-08-27 19:54:59 +0000 |
commit | cb131974041efdcd0b4a99fe3bd316cbb9b4db41 (patch) | |
tree | fdbfd41bdf5ee6ef2401495c8871df8dfaca47ac /src/mongo/db | |
parent | c932e02133220269f37122499f7379f1758b7ad4 (diff) | |
download | mongo-cb131974041efdcd0b4a99fe3bd316cbb9b4db41.tar.gz |
SERVER-32960 Make $mod always truncate divisor and remainder toward zero
Diffstat (limited to 'src/mongo/db')
-rw-r--r-- | src/mongo/db/matcher/expression_parser.cpp | 11 | ||||
-rw-r--r-- | src/mongo/db/matcher/expression_parser_leaf_test.cpp | 40 |
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"); |