summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Hatch <david.hatch@mongodb.com>2015-07-31 16:19:20 -0400
committerRaymond Jacobson <raymond.jacobson@10gen.com>2015-08-13 11:34:59 -0400
commit8fc4cbc675b90f96759b844ddfc4c52868a144f2 (patch)
tree1e5a687b2f9ce2e027d9fb0d20d7f58fd4016240
parent9a81b7f1a6a8e0773703f2007c5a7268eb182b91 (diff)
downloadmongo-8fc4cbc675b90f96759b844ddfc4c52868a144f2.tar.gz
SERVER-19628 Add Decimal128 type support to update/delete code paths
-rw-r--r--src/mongo/bson/mutable/const_element-inl.h4
-rw-r--r--src/mongo/bson/mutable/const_element.h1
-rw-r--r--src/mongo/bson/mutable/document.cpp27
-rw-r--r--src/mongo/bson/mutable/document.h3
-rw-r--r--src/mongo/bson/mutable/element-inl.h5
-rw-r--r--src/mongo/bson/mutable/element.cpp4
-rw-r--r--src/mongo/bson/mutable/element.h11
-rw-r--r--src/mongo/bson/mutable/mutable_bson_test.cpp157
-rw-r--r--src/mongo/db/matcher/expression_algo_test.cpp16
-rw-r--r--src/mongo/db/matcher/expression_leaf.cpp1
-rw-r--r--src/mongo/db/matcher/expression_parser_leaf_test.cpp25
-rw-r--r--src/mongo/db/ops/modifier_bit_test.cpp9
-rw-r--r--src/mongo/db/ops/modifier_inc_test.cpp123
-rw-r--r--src/mongo/platform/decimal128.h2
-rw-r--r--src/mongo/scripting/engine_v8.cpp2
-rw-r--r--src/mongo/scripting/mozjs/implscope.cpp2
-rw-r--r--src/mongo/util/safe_num-inl.h4
-rw-r--r--src/mongo/util/safe_num.cpp50
-rw-r--r--src/mongo/util/safe_num.h25
-rw-r--r--src/mongo/util/safe_num_test.cpp106
20 files changed, 559 insertions, 18 deletions
diff --git a/src/mongo/bson/mutable/const_element-inl.h b/src/mongo/bson/mutable/const_element-inl.h
index 562a47c16a0..325df232b09 100644
--- a/src/mongo/bson/mutable/const_element-inl.h
+++ b/src/mongo/bson/mutable/const_element-inl.h
@@ -148,6 +148,10 @@ inline int64_t ConstElement::getValueLong() const {
return _basis.getValueLong();
}
+inline Decimal128 ConstElement::getValueDecimal() const {
+ return _basis.getValueDecimal();
+}
+
inline bool ConstElement::isValueMinKey() const {
return _basis.isValueMinKey();
}
diff --git a/src/mongo/bson/mutable/const_element.h b/src/mongo/bson/mutable/const_element.h
index 3c6b71d593a..80ad1aac724 100644
--- a/src/mongo/bson/mutable/const_element.h
+++ b/src/mongo/bson/mutable/const_element.h
@@ -83,6 +83,7 @@ public:
inline int32_t getValueInt() const;
inline Timestamp getValueTimestamp() const;
inline int64_t getValueLong() const;
+ inline Decimal128 getValueDecimal() const;
inline bool isValueMinKey() const;
inline bool isValueMaxKey() const;
inline SafeNum getValueSafeNum() const;
diff --git a/src/mongo/bson/mutable/document.cpp b/src/mongo/bson/mutable/document.cpp
index f07073ad5e0..307177db043 100644
--- a/src/mongo/bson/mutable/document.cpp
+++ b/src/mongo/bson/mutable/document.cpp
@@ -1450,7 +1450,7 @@ bool Element::isNumeric() const {
const ElementRep& thisRep = impl.getElementRep(_repIdx);
const BSONType type = impl.getType(thisRep);
return ((type == mongo::NumberLong) || (type == mongo::NumberInt) ||
- (type == mongo::NumberDouble));
+ (type == mongo::NumberDouble) || (type == mongo::NumberDecimal));
}
bool Element::isIntegral() const {
@@ -1478,6 +1478,8 @@ SafeNum Element::getValueSafeNum() const {
return static_cast<long long int>(getValueLong());
case mongo::NumberDouble:
return getValueDouble();
+ case mongo::NumberDecimal:
+ return getValueDecimal();
default:
return SafeNum();
}
@@ -1845,6 +1847,15 @@ Status Element::setValueLong(const int64_t value) {
return setValue(newValue._repIdx);
}
+Status Element::setValueDecimal(const Decimal128 value) {
+ verify(ok());
+ Document::Impl& impl = getDocument().getImpl();
+ ElementRep thisRep = impl.getElementRep(_repIdx);
+ const StringData fieldName = impl.getFieldNameForNewElement(thisRep);
+ Element newValue = getDocument().makeElementDecimal(fieldName, value);
+ return setValue(newValue._repIdx);
+}
+
Status Element::setValueMinKey() {
verify(ok());
Document::Impl& impl = getDocument().getImpl();
@@ -1888,6 +1899,8 @@ Status Element::setValueSafeNum(const SafeNum value) {
return setValueLong(value._value.int64Val);
case mongo::NumberDouble:
return setValueDouble(value._value.doubleVal);
+ case mongo::NumberDecimal:
+ return setValueDecimal(value._value.decimalVal);
default:
return Status(ErrorCodes::UnsupportedFormat,
"Don't know how to handle unexpected SafeNum type");
@@ -2444,6 +2457,16 @@ Element Document::makeElementLong(StringData fieldName, const int64_t value) {
return Element(this, impl.insertLeafElement(leafRef, fieldName.size() + 1));
}
+Element Document::makeElementDecimal(StringData fieldName, const Decimal128 value) {
+ Impl& impl = getImpl();
+ dassert(impl.doesNotAlias(fieldName));
+
+ BSONObjBuilder& builder = impl.leafBuilder();
+ const int leafRef = builder.len();
+ builder.append(fieldName, value);
+ return Element(this, impl.insertLeafElement(leafRef, fieldName.size() + 1));
+}
+
Element Document::makeElementMinKey(StringData fieldName) {
Impl& impl = getImpl();
dassert(impl.doesNotAlias(fieldName));
@@ -2516,6 +2539,8 @@ Element Document::makeElementSafeNum(StringData fieldName, SafeNum value) {
return makeElementLong(fieldName, value._value.int64Val);
case mongo::NumberDouble:
return makeElementDouble(fieldName, value._value.doubleVal);
+ case mongo::NumberDecimal:
+ return makeElementDecimal(fieldName, value._value.decimalVal);
default:
// Return an invalid element to indicate that we failed.
return end();
diff --git a/src/mongo/bson/mutable/document.h b/src/mongo/bson/mutable/document.h
index 3a24eac74cc..1efd9e95729 100644
--- a/src/mongo/bson/mutable/document.h
+++ b/src/mongo/bson/mutable/document.h
@@ -377,6 +377,9 @@ public:
/** Create a new long integer Element with the given value and field name. */
Element makeElementLong(StringData fieldName, int64_t value);
+ /** Create a new dec128 Element with the given value and field name. */
+ Element makeElementDecimal(StringData fieldName, Decimal128 value);
+
/** Create a new min key Element with the given field name. */
Element makeElementMinKey(StringData fieldName);
diff --git a/src/mongo/bson/mutable/element-inl.h b/src/mongo/bson/mutable/element-inl.h
index 6dd2810e43c..c88ad2503c5 100644
--- a/src/mongo/bson/mutable/element-inl.h
+++ b/src/mongo/bson/mutable/element-inl.h
@@ -101,6 +101,11 @@ inline int64_t Element::getValueLong() const {
return getValue()._numberLong();
}
+inline Decimal128 Element::getValueDecimal() const {
+ dassert(hasValue() && isType(mongo::NumberDecimal));
+ return getValue()._numberDecimal();
+}
+
inline bool Element::isValueMinKey() const {
return isType(mongo::MinKey);
}
diff --git a/src/mongo/bson/mutable/element.cpp b/src/mongo/bson/mutable/element.cpp
index 4ff1959559f..05cac77de80 100644
--- a/src/mongo/bson/mutable/element.cpp
+++ b/src/mongo/bson/mutable/element.cpp
@@ -131,6 +131,10 @@ Status Element::appendTimestamp(StringData fieldName, Timestamp value) {
return pushBack(getDocument().makeElementTimestamp(fieldName, value));
}
+Status Element::appendDecimal(StringData fieldName, Decimal128 value) {
+ return pushBack(getDocument().makeElementDecimal(fieldName, value));
+}
+
Status Element::appendLong(StringData fieldName, int64_t value) {
return pushBack(getDocument().makeElementLong(fieldName, value));
}
diff --git a/src/mongo/bson/mutable/element.h b/src/mongo/bson/mutable/element.h
index 673180c0c64..68bf99dfc08 100644
--- a/src/mongo/bson/mutable/element.h
+++ b/src/mongo/bson/mutable/element.h
@@ -258,7 +258,7 @@ public:
bool hasValue() const;
/** Returns true if this element is a numeric type (e.g. NumberLong). Currently, the
- * only numeric BSON types are NumberLong, NumberInt, and NumberDouble.
+ * only numeric BSON types are NumberLong, NumberInt, NumberDouble, and NumberDecimal.
*/
bool isNumeric() const;
@@ -324,6 +324,9 @@ public:
/** Get the value from a long valued Element. */
inline int64_t getValueLong() const;
+ /** Get the value from a decimal valued Element. */
+ inline Decimal128 getValueDecimal() const;
+
/** Returns true if this Element is the min key type. */
inline bool isValueMinKey() const;
@@ -444,6 +447,9 @@ public:
/** Set the value of this Element to the given long integer */
Status setValueLong(int64_t value);
+ /** Set the value of this Element to the given decimal. */
+ Status setValueDecimal(Decimal128 value);
+
/** Set the value of this Element to MinKey. */
Status setValueMinKey();
@@ -567,6 +573,9 @@ public:
/** Append the provided long integer as a new field with the provided name. */
Status appendLong(StringData fieldName, int64_t value);
+ /** Append the provided decimal as a new field with the provided name. */
+ Status appendDecimal(StringData fieldName, Decimal128 value);
+
/** Append a max key as a new field with the provided name. */
Status appendMinKey(StringData fieldName);
diff --git a/src/mongo/bson/mutable/mutable_bson_test.cpp b/src/mongo/bson/mutable/mutable_bson_test.cpp
index f8086da510f..f44afe2d9df 100644
--- a/src/mongo/bson/mutable/mutable_bson_test.cpp
+++ b/src/mongo/bson/mutable/mutable_bson_test.cpp
@@ -35,6 +35,7 @@
#include "mongo/bson/mutable/mutable_bson_test_utils.h"
#include "mongo/bson/mutable/damage_vector.h"
#include "mongo/db/json.h"
+#include "mongo/platform/decimal128.h"
#include "mongo/unittest/unittest.h"
namespace {
@@ -562,6 +563,11 @@ TEST(Element, setters) {
t0.setValueDouble(123.45);
ASSERT_EQUALS(mongo::NumberDouble, t0.getType());
+ if (mongo::Decimal128::enabled) {
+ t0.setValueDecimal(mongo::Decimal128("123.45E1234"));
+ ASSERT_EQUALS(mongo::NumberDecimal, t0.getType());
+ }
+
t0.setValueOID(mongo::OID("47cc67093475061e3d95369d"));
ASSERT_EQUALS(mongo::jstOID, t0.getType());
@@ -606,6 +612,43 @@ TEST(Element, toString) {
ASSERT_FALSE(docChild.ok());
}
+TEST(DecimalType, createElement) {
+ if (mongo::Decimal128::enabled) {
+ mmb::Document doc;
+
+ mmb::Element d0 = doc.makeElementDecimal("d0", mongo::Decimal128("12345"));
+ ASSERT_TRUE(mongo::Decimal128("12345").isEqual(d0.getValueDecimal()));
+ }
+}
+
+TEST(DecimalType, setElement) {
+ if (mongo::Decimal128::enabled) {
+ mmb::Document doc;
+
+ mmb::Element d0 = doc.makeElementDecimal("d0", mongo::Decimal128("128"));
+ d0.setValueDecimal(mongo::Decimal128("123456"));
+ ASSERT_TRUE(mongo::Decimal128("123456").isEqual(d0.getValueDecimal()));
+
+ d0.setValueDouble(0.1);
+ ASSERT_EQUALS(0.1, d0.getValueDouble());
+ d0.setValueDecimal(mongo::Decimal128("23"));
+ ASSERT_TRUE(mongo::Decimal128("23").isEqual(d0.getValueDecimal()));
+ }
+}
+
+TEST(DecimalType, appendElement) {
+ if (mongo::Decimal128::enabled) {
+ mmb::Document doc;
+
+ mmb::Element d0 = doc.makeElementObject("e0");
+ d0.appendDecimal("precision", mongo::Decimal128(34));
+
+ mmb::Element it = mmb::findFirstChildNamed(d0, "precision");
+ ASSERT_TRUE(it.ok());
+ ASSERT_TRUE(mongo::Decimal128(34).isEqual(it.getValueDecimal()));
+ }
+}
+
TEST(TimestampType, createElement) {
mmb::Document doc;
@@ -671,6 +714,13 @@ TEST(SafeNumType, getSafeNum) {
ASSERT_EQUALS(123.456789, t0.getValueDouble());
num = t0.getValueSafeNum();
ASSERT_EQUALS(num, 123.456789);
+
+ if (mongo::Decimal128::enabled) {
+ t0.setValueDecimal(mongo::Decimal128("12345678.1234"));
+ ASSERT_TRUE(mongo::Decimal128("12345678.1234").isEqual(t0.getValueDecimal()));
+ num = t0.getValueSafeNum();
+ ASSERT_EQUALS(num, mongo::Decimal128("12345678.1234"));
+ }
}
TEST(SafeNumType, setSafeNum) {
@@ -744,8 +794,30 @@ static const char jsonSample[] =
"pattern:/match.*this/,"
"lastfield:\"last\"}";
+static const char jsonSampleWithDecimal[] =
+ "{_id:ObjectId(\"47cc67093475061e3d95369d\"),"
+ "query:\"kate hudson\","
+ "owner:1234567887654321,"
+ "date:\"2011-05-13T14:22:46.777Z\","
+ "score:123.456,"
+ "decimal:NumberDecimal(\"2\"),"
+ "field1:Infinity,"
+ "\"field2\":-Infinity,"
+ "\"field3\":NaN,"
+ "users:["
+ "{uname:\"@aaaa\",editid:\"123\",date:1303959350,yes_votes:0,no_votes:0},"
+ "{uname:\"@bbbb\",editid:\"456\",date:1303959350,yes_votes:0,no_votes:0},"
+ "{uname:\"@cccc\",editid:\"789\",date:1303959350,yes_votes:0,no_votes:0}],"
+ "pattern:/match.*this/,"
+ "lastfield:\"last\"}";
+
TEST(Serialization, RoundTrip) {
- mongo::BSONObj obj = mongo::fromjson(jsonSample);
+ mongo::BSONObj obj;
+ if (mongo::Decimal128::enabled) {
+ obj = mongo::fromjson(jsonSampleWithDecimal);
+ } else {
+ obj = mongo::fromjson(jsonSample);
+ }
mmb::Document doc(obj.copy());
mongo::BSONObj built = doc.getObject();
ASSERT_EQUALS(obj, built);
@@ -1313,6 +1385,11 @@ TEST(Element, IsNumeric) {
elt = doc.makeElementDouble("dummy", 42.0);
ASSERT_TRUE(elt.isNumeric());
+
+ if (mongo::Decimal128::enabled) {
+ elt = doc.makeElementDecimal("dummy", mongo::Decimal128(20));
+ ASSERT_TRUE(elt.isNumeric());
+ }
}
TEST(Element, IsIntegral) {
@@ -1332,6 +1409,11 @@ TEST(Element, IsIntegral) {
elt = doc.makeElementDouble("dummy", 42.0);
ASSERT_FALSE(elt.isIntegral());
+
+ if (mongo::Decimal128::enabled) {
+ elt = doc.makeElementDecimal("dummy", mongo::Decimal128(20));
+ ASSERT_FALSE(elt.isIntegral());
+ }
}
TEST(Document, ArraySerialization) {
@@ -2330,6 +2412,48 @@ TEST(TypeSupport, EncodingEquivalenceLong) {
ASSERT_TRUE(identical(b.getValue(), c.getValue()));
}
+TEST(TypeSupport, EncodingEquivalenceDecimal) {
+ if (mongo::Decimal128::enabled) {
+ mongo::BSONObjBuilder builder;
+ const char name[] = "thing";
+ const mongo::Decimal128 value1 = mongo::Decimal128(2);
+ builder.append(name, value1);
+ mongo::BSONObj source = builder.done();
+ const mongo::BSONElement thing = source.firstElement();
+ ASSERT_TRUE(thing.type() == mongo::NumberDecimal);
+
+ mmb::Document doc;
+
+ // Construct via direct call to append/make
+ ASSERT_OK(doc.root().appendDecimal(name, value1));
+ mmb::Element a = doc.root().rightChild();
+ ASSERT_TRUE(a.ok());
+ ASSERT_EQUALS(a.getType(), mongo::NumberDecimal);
+ ASSERT_TRUE(a.hasValue());
+ ASSERT_TRUE(value1.isEqual(mmb::ConstElement(a).getValueDecimal()));
+
+ // Construct via call passong BSON element
+ ASSERT_OK(doc.root().appendElement(thing));
+ mmb::Element b = doc.root().rightChild();
+ ASSERT_TRUE(b.ok());
+ ASSERT_EQUALS(b.getType(), mongo::NumberDecimal);
+ ASSERT_TRUE(b.hasValue());
+
+ // Construct via setValue call
+ ASSERT_OK(doc.root().appendNull(name));
+ mmb::Element c = doc.root().rightChild();
+ ASSERT_TRUE(c.ok());
+ c.setValueDecimal(value1);
+ ASSERT_EQUALS(c.getType(), mongo::NumberDecimal);
+ ASSERT_TRUE(c.hasValue());
+
+ // Ensure identity:
+ ASSERT_TRUE(identical(thing, mmb::ConstElement(a).getValue()));
+ ASSERT_TRUE(identical(a.getValue(), b.getValue()));
+ ASSERT_TRUE(identical(b.getValue(), c.getValue()));
+ }
+}
+
TEST(TypeSupport, EncodingEquivalenceMinKey) {
mongo::BSONObjBuilder builder;
const char name[] = "thing";
@@ -2853,6 +2977,37 @@ TEST(DocumentInPlace, NumberDoubleLifecycle) {
// ASSERT_EQUALS(value1, x.getValueDouble());
}
+TEST(DocumentInPlace, NumberDecimalLifecycle) {
+ if (mongo::Decimal128::enabled) {
+ const mongo::Decimal128 value1 = mongo::Decimal128(32);
+ const mongo::Decimal128 value2 = mongo::Decimal128(2);
+
+ mongo::BSONObj obj(BSON("x" << value1));
+ mmb::Document doc(obj, mmb::Document::kInPlaceEnabled);
+
+ mmb::Element x = doc.root().leftChild();
+
+ mmb::DamageVector damages;
+ const char* source = NULL;
+
+ x.setValueDecimal(value2);
+ ASSERT_TRUE(doc.getInPlaceUpdates(&damages, &source));
+ ASSERT_EQUALS(1U, damages.size());
+ apply(&obj, damages, source);
+ ASSERT_TRUE(x.hasValue());
+ ASSERT_TRUE(x.isType(mongo::NumberDecimal));
+ ASSERT_TRUE(value2.isEqual(x.getValueDecimal()));
+
+ // TODO: Re-enable when in-place updates to leaf elements is supported
+ // x.setValueDecimal(value1);
+ // ASSERT_TRUE(doc.getInPlaceUpdates(&damages, &source));
+ // apply(&obj, damages, source);
+ // ASSERT_TRUE(x.hasValue());
+ // ASSERT_TRUE(x.isType(mongo::NumberDecimal));
+ // ASSERT_TRUE(value1.isEqual(x.getValueDecimal()));
+ }
+}
+
// Doubles and longs are the same size, 8 bytes, so we should be able to do in-place
// updates between them.
TEST(DocumentInPlace, DoubleToLongAndBack) {
diff --git a/src/mongo/db/matcher/expression_algo_test.cpp b/src/mongo/db/matcher/expression_algo_test.cpp
index 9effbf6b0b9..e3fb3e504ea 100644
--- a/src/mongo/db/matcher/expression_algo_test.cpp
+++ b/src/mongo/db/matcher/expression_algo_test.cpp
@@ -37,6 +37,7 @@
#include "mongo/db/matcher/expression.h"
#include "mongo/db/matcher/expression_algo.h"
#include "mongo/db/matcher/expression_parser.h"
+#include "mongo/platform/decimal128.h"
namespace mongo {
@@ -121,6 +122,21 @@ TEST(ExpressionAlgoIsSubsetOf, Compare_NaN) {
ASSERT_FALSE(expression::isSubsetOf(gt.get(), nan.get()));
ASSERT_FALSE(expression::isSubsetOf(nan.get(), in.get()));
ASSERT_FALSE(expression::isSubsetOf(in.get(), nan.get()));
+
+ if (Decimal128::enabled) {
+ ParsedMatchExpression decNan("{x : NumberDecimal(\"NaN\") }");
+ ASSERT_TRUE(expression::isSubsetOf(decNan.get(), decNan.get()));
+ ASSERT_TRUE(expression::isSubsetOf(nan.get(), decNan.get()));
+ ASSERT_TRUE(expression::isSubsetOf(decNan.get(), nan.get()));
+ ASSERT_FALSE(expression::isSubsetOf(decNan.get(), lt.get()));
+ ASSERT_FALSE(expression::isSubsetOf(lt.get(), decNan.get()));
+ ASSERT_FALSE(expression::isSubsetOf(decNan.get(), lte.get()));
+ ASSERT_FALSE(expression::isSubsetOf(lte.get(), decNan.get()));
+ ASSERT_FALSE(expression::isSubsetOf(decNan.get(), gte.get()));
+ ASSERT_FALSE(expression::isSubsetOf(gte.get(), decNan.get()));
+ ASSERT_FALSE(expression::isSubsetOf(decNan.get(), gt.get()));
+ ASSERT_FALSE(expression::isSubsetOf(gt.get(), decNan.get()));
+ }
}
TEST(ExpressionAlgoIsSubsetOf, Compare_EQ) {
diff --git a/src/mongo/db/matcher/expression_leaf.cpp b/src/mongo/db/matcher/expression_leaf.cpp
index 79836a43f2c..903b8c434a1 100644
--- a/src/mongo/db/matcher/expression_leaf.cpp
+++ b/src/mongo/db/matcher/expression_leaf.cpp
@@ -416,6 +416,7 @@ const std::unordered_map<std::string, BSONType> TypeMatchExpression::typeAliasMa
{"int", NumberInt},
{"timestamp", bsonTimestamp},
{"long", NumberLong},
+ {"decimal", NumberDecimal},
{"maxKey", MaxKey},
{"minKey", MinKey}};
diff --git a/src/mongo/db/matcher/expression_parser_leaf_test.cpp b/src/mongo/db/matcher/expression_parser_leaf_test.cpp
index cb1cd8ff375..c5650c25d67 100644
--- a/src/mongo/db/matcher/expression_parser_leaf_test.cpp
+++ b/src/mongo/db/matcher/expression_parser_leaf_test.cpp
@@ -38,6 +38,7 @@
#include "mongo/db/json.h"
#include "mongo/db/matcher/expression.h"
#include "mongo/db/matcher/expression_leaf.h"
+#include "mongo/platform/decimal128.h"
#include "mongo/util/log.h"
namespace mongo {
@@ -608,6 +609,17 @@ TEST(MatchExpressionParserLeafTest, TypeDoubleOperator) {
ASSERT(!result.getValue()->matchesBSON(BSON("x" << 5)));
}
+TEST(MatchExpressionParserLeafTest, TypeDecimalOperator) {
+ if (Decimal128::enabled) {
+ BSONObj query = BSON("x" << BSON("$type" << mongo::NumberDecimal));
+ StatusWithMatchExpression result = MatchExpressionParser::parse(query);
+ ASSERT_TRUE(result.isOK());
+
+ ASSERT_FALSE(result.getValue()->matchesBSON(BSON("x" << 5.3)));
+ ASSERT_TRUE(result.getValue()->matchesBSON(BSON("x" << mongo::Decimal128("1"))));
+ }
+}
+
TEST(MatchExpressionParserLeafTest, TypeNull) {
BSONObj query = BSON("x" << BSON("$type" << jstNULL));
StatusWithMatchExpression result = MatchExpressionParser::parse(query);
@@ -657,6 +669,19 @@ TEST(MatchExpressionParserLeafTest, TypeStringnameDouble) {
ASSERT_FALSE(tmeNumberDouble->matchesBSON(fromjson("{a: NumberInt(5)}")));
}
+TEST(MatchExpressionParserLeafTest, TypeStringNameNumberDecimal) {
+ if (Decimal128::enabled) {
+ StatusWithMatchExpression typeNumberDecimal =
+ MatchExpressionParser::parse(fromjson("{a: {$type: 'decimal'}}"));
+ ASSERT_OK(typeNumberDecimal.getStatus());
+ TypeMatchExpression* tmeNumberDecimal =
+ static_cast<TypeMatchExpression*>(typeNumberDecimal.getValue().get());
+ ASSERT(tmeNumberDecimal->getType() == NumberDecimal);
+ ASSERT_TRUE(tmeNumberDecimal->matchesBSON(BSON("a" << mongo::Decimal128("1"))));
+ ASSERT_FALSE(tmeNumberDecimal->matchesBSON(fromjson("{a: true}")));
+ }
+}
+
TEST(MatchExpressionParserLeafTest, TypeStringnameNumberInt) {
StatusWithMatchExpression typeNumberInt =
MatchExpressionParser::parse(fromjson("{a: {$type: 'int'}}"));
diff --git a/src/mongo/db/ops/modifier_bit_test.cpp b/src/mongo/db/ops/modifier_bit_test.cpp
index b6a4fb38a7b..2e63dbe4953 100644
--- a/src/mongo/db/ops/modifier_bit_test.cpp
+++ b/src/mongo/db/ops/modifier_bit_test.cpp
@@ -37,11 +37,13 @@
#include "mongo/db/jsobj.h"
#include "mongo/db/json.h"
#include "mongo/db/ops/log_builder.h"
+#include "mongo/platform/decimal128.h"
#include "mongo/unittest/unittest.h"
namespace {
using mongo::BSONObj;
+using mongo::Decimal128;
using mongo::LogBuilder;
using mongo::ModifierBit;
using mongo::ModifierInterface;
@@ -116,6 +118,13 @@ TEST(Init, FailToInitWithInvalidValue) {
modObj = fromjson("{ $bit : { a : { or : 1.0 } } }");
ASSERT_NOT_OK(mod.init(modObj["$bit"].embeddedObject().firstElement(),
ModifierInterface::Options::normal()));
+
+ if (mongo::Decimal128::enabled) {
+ // The argument to the sub-operator must be integral
+ modObj = fromjson("{ $bit : { a : { or : NumberDecimal(\"1.0\") } } }");
+ ASSERT_NOT_OK(mod.init(modObj["$bit"].embeddedObject().firstElement(),
+ ModifierInterface::Options::normal()));
+ }
}
TEST(Init, ParsesAndInt) {
diff --git a/src/mongo/db/ops/modifier_inc_test.cpp b/src/mongo/db/ops/modifier_inc_test.cpp
index 6a0e3a332bd..52c829f7842 100644
--- a/src/mongo/db/ops/modifier_inc_test.cpp
+++ b/src/mongo/db/ops/modifier_inc_test.cpp
@@ -39,11 +39,13 @@
#include "mongo/db/jsobj.h"
#include "mongo/db/json.h"
#include "mongo/db/ops/log_builder.h"
+#include "mongo/platform/decimal128.h"
#include "mongo/unittest/unittest.h"
namespace {
using mongo::BSONObj;
+using mongo::Decimal128;
using mongo::LogBuilder;
using mongo::ModifierInc;
using mongo::ModifierInterface;
@@ -122,6 +124,12 @@ TEST(Init, InitParsesNumberDouble) {
Mod incMod(BSON("$inc" << BSON("a" << 1.0)));
}
+TEST(Init, InitParsesNumberDecimal) {
+ if (mongo::Decimal128::enabled) {
+ Mod incMod(BSON("$inc" << BSON("a" << Decimal128(1.0))));
+ }
+}
+
TEST(SimpleMod, PrepareSimpleOK) {
Document doc(fromjson("{ a : 1 }"));
Mod incMod(fromjson("{ $inc: { a : 1 }}"));
@@ -275,6 +283,16 @@ TEST(NoOp, Double) {
ASSERT_TRUE(execInfo.noOp);
}
+TEST(NoOp, Decimal) {
+ if (mongo::Decimal128::enabled) {
+ Document doc(BSON("a" << Decimal128("1.0")));
+ Mod incMod(BSON("$inc" << BSON("a" << Decimal128("0.0"))));
+ ModifierInterface::ExecInfo execInfo;
+ ASSERT_OK(incMod.prepare(doc.root(), "", &execInfo));
+ ASSERT_TRUE(execInfo.noOp);
+ }
+}
+
TEST(Upcasting, UpcastIntToLong) {
// Checks that $inc : NumberLong(0) turns a NumberInt into a NumberLong and logs it
// correctly.
@@ -370,9 +388,112 @@ TEST(Upcasting, DoublesStayDoubles) {
ASSERT_EQUALS(mongo::NumberDouble, logDoc.root()["$set"]["a"].getType());
}
+TEST(Upcasting, UpcastIntToDecimal) {
+ if (mongo::Decimal128::enabled) {
+ // Checks that $inc : NumberDecimal(0) turns a NumberInt into a NumberDecimal and logs it
+ // correctly.
+ Document doc(BSON("a" << static_cast<int>(1)));
+ ASSERT_EQUALS(mongo::NumberInt, doc.root()["a"].getType());
+
+ Mod incMod(fromjson("{ $inc : { a : NumberDecimal(\"0\") }}"));
+
+ ModifierInterface::ExecInfo execInfo;
+ ASSERT_OK(incMod.prepare(doc.root(), "", &execInfo));
+ ASSERT_FALSE(execInfo.noOp);
+
+ ASSERT_OK(incMod.apply());
+ ASSERT_FALSE(doc.isInPlaceModeEnabled());
+ ASSERT_EQUALS(fromjson("{ a : NumberDecimal(\"1.0\") }"), doc);
+ ASSERT_EQUALS(mongo::NumberDecimal, doc.root()["a"].getType());
+
+ Document logDoc;
+ LogBuilder logBuilder(logDoc.root());
+ ASSERT_OK(incMod.log(&logBuilder));
+ ASSERT_EQUALS(fromjson("{ $set : { a : NumberDecimal(\"1.0\") }}"), logDoc);
+ ASSERT_EQUALS(mongo::NumberDecimal, logDoc.root()["$set"]["a"].getType());
+ }
+}
+
+TEST(Upcasting, UpcastLongToDecimal) {
+ if (mongo::Decimal128::enabled) {
+ // Checks that $inc : NumberDecimal(0) turns a NumberLong into a NumberDecimal and logs it
+ // correctly.
+ Document doc(BSON("a" << static_cast<long long>(1)));
+ ASSERT_EQUALS(mongo::NumberLong, doc.root()["a"].getType());
+
+ Mod incMod(fromjson("{ $inc : { a : NumberDecimal(\"0\") }}"));
+
+ ModifierInterface::ExecInfo execInfo;
+ ASSERT_OK(incMod.prepare(doc.root(), "", &execInfo));
+ ASSERT_FALSE(execInfo.noOp);
+
+ ASSERT_OK(incMod.apply());
+ ASSERT_FALSE(doc.isInPlaceModeEnabled());
+ ASSERT_EQUALS(fromjson("{ a : NumberDecimal(\"1.0\") }"), doc);
+ ASSERT_EQUALS(mongo::NumberDecimal, doc.root()["a"].getType());
+
+ Document logDoc;
+ LogBuilder logBuilder(logDoc.root());
+ ASSERT_OK(incMod.log(&logBuilder));
+ ASSERT_EQUALS(fromjson("{ $set : { a : NumberDecimal(\"1.0\") }}"), logDoc);
+ ASSERT_EQUALS(mongo::NumberDecimal, logDoc.root()["$set"]["a"].getType());
+ }
+}
+
+TEST(Upcasting, UpcastDoubleToDecimal) {
+ if (mongo::Decimal128::enabled) {
+ // Checks that $inc : NumberDecimal(0) turns a double into a NumberDecimal and logs it
+ // correctly.
+ Document doc(BSON("a" << static_cast<double>(1.0)));
+ ASSERT_EQUALS(mongo::NumberDouble, doc.root()["a"].getType());
+
+ Mod incMod(fromjson("{ $inc : { a : NumberDecimal(\"0\") }}"));
+
+ ModifierInterface::ExecInfo execInfo;
+ ASSERT_OK(incMod.prepare(doc.root(), "", &execInfo));
+ ASSERT_FALSE(execInfo.noOp);
+
+ ASSERT_OK(incMod.apply());
+ ASSERT_FALSE(doc.isInPlaceModeEnabled());
+ ASSERT_EQUALS(fromjson("{ a : NumberDecimal(\"1.0\") }"), doc);
+ ASSERT_EQUALS(mongo::NumberDecimal, doc.root()["a"].getType());
+
+ Document logDoc;
+ LogBuilder logBuilder(logDoc.root());
+ ASSERT_OK(incMod.log(&logBuilder));
+ ASSERT_EQUALS(fromjson("{ $set : { a : NumberDecimal(\"1.0\") }}"), logDoc);
+ ASSERT_EQUALS(mongo::NumberDecimal, logDoc.root()["$set"]["a"].getType());
+ }
+}
+
+TEST(Upcasting, DecimalsStayDecimals) {
+ if (mongo::Decimal128::enabled) {
+ // Checks that $inc : NumberDecimal(1) keeps a NumberDecimal as a NumberDecimal and logs it
+ // correctly.
+ Document doc(BSON("a" << mongo::Decimal128("1.0")));
+ ASSERT_EQUALS(mongo::NumberDecimal, doc.root()["a"].getType());
+
+ Mod incMod(fromjson("{ $inc : { a : NumberDecimal(\"1\") }}"));
+
+ ModifierInterface::ExecInfo execInfo;
+ ASSERT_OK(incMod.prepare(doc.root(), "", &execInfo));
+ ASSERT_FALSE(execInfo.noOp);
+
+ ASSERT_OK(incMod.apply());
+ ASSERT_TRUE(doc.isInPlaceModeEnabled());
+ ASSERT_EQUALS(fromjson("{ a : NumberDecimal(\"2.0\") }"), doc);
+ ASSERT_EQUALS(mongo::NumberDecimal, doc.root()["a"].getType());
+
+ Document logDoc;
+ LogBuilder logBuilder(logDoc.root());
+ ASSERT_OK(incMod.log(&logBuilder));
+ ASSERT_EQUALS(fromjson("{ $set : { a : NumberDecimal(\"2.0\") }}"), logDoc);
+ ASSERT_EQUALS(mongo::NumberDecimal, logDoc.root()["$set"]["a"].getType());
+ }
+}
+
// The only interesting overflow cases are int->long via increment: we never overflow to
// double, and we never decrease precision on decrement.
-
TEST(Spilling, OverflowIntToLong) {
const int initial_value = std::numeric_limits<int32_t>::max();
diff --git a/src/mongo/platform/decimal128.h b/src/mongo/platform/decimal128.h
index 7a1188a8904..cc6245f93af 100644
--- a/src/mongo/platform/decimal128.h
+++ b/src/mongo/platform/decimal128.h
@@ -306,6 +306,4 @@ private:
Value _value;
};
-std::ostream& operator<<(std::ostream& stream, const Decimal128& value);
-
} // namespace mongo
diff --git a/src/mongo/scripting/engine_v8.cpp b/src/mongo/scripting/engine_v8.cpp
index b2ee26f9fcc..e4463106ece 100644
--- a/src/mongo/scripting/engine_v8.cpp
+++ b/src/mongo/scripting/engine_v8.cpp
@@ -1321,7 +1321,7 @@ void V8Scope::installBSONTypes() {
injectV8Function("BinData", BinDataFT(), _global);
injectV8Function("NumberLong", NumberLongFT(), _global);
injectV8Function("NumberInt", NumberIntFT(), _global);
- if (experimentalDecimalSupport) {
+ if (Decimal128::enabled) {
injectV8Function("NumberDecimal", NumberDecimalFT(), _global);
}
injectV8Function("Timestamp", TimestampFT(), _global);
diff --git a/src/mongo/scripting/mozjs/implscope.cpp b/src/mongo/scripting/mozjs/implscope.cpp
index e71137d005a..23b7d4c05fb 100644
--- a/src/mongo/scripting/mozjs/implscope.cpp
+++ b/src/mongo/scripting/mozjs/implscope.cpp
@@ -669,7 +669,7 @@ void MozJSImplScope::installBSONTypes() {
_nativeFunctionProto.install(_global);
_numberIntProto.install(_global);
_numberLongProto.install(_global);
- if (experimentalDecimalSupport) {
+ if (Decimal128::enabled) {
_numberDecimalProto.install(_global);
}
_objectProto.install(_global);
diff --git a/src/mongo/util/safe_num-inl.h b/src/mongo/util/safe_num-inl.h
index 0327fa54b00..8ac16756f17 100644
--- a/src/mongo/util/safe_num-inl.h
+++ b/src/mongo/util/safe_num-inl.h
@@ -53,6 +53,10 @@ inline SafeNum::SafeNum(double num) : _type(NumberDouble) {
_value.doubleVal = num;
}
+inline SafeNum::SafeNum(Decimal128 num) : _type(NumberDecimal) {
+ _value.decimalVal = num.getValue();
+}
+
inline bool SafeNum::operator==(const SafeNum& rhs) const {
return isEquivalent(rhs);
}
diff --git a/src/mongo/util/safe_num.cpp b/src/mongo/util/safe_num.cpp
index 8976f27a251..f49fcd7b53a 100644
--- a/src/mongo/util/safe_num.cpp
+++ b/src/mongo/util/safe_num.cpp
@@ -51,6 +51,10 @@ SafeNum::SafeNum(const BSONElement& element) {
_type = NumberDouble;
_value.doubleVal = element.Double();
break;
+ case NumberDecimal:
+ _type = NumberDecimal;
+ _value.decimalVal = element.Decimal().getValue();
+ break;
default:
_type = EOO;
}
@@ -68,6 +72,9 @@ std::string SafeNum::debugString() const {
case NumberDouble:
os << "(NumberDouble)" << _value.doubleVal;
break;
+ case NumberDecimal:
+ os << "(NumberDecimal)" << getDecimal(*this).toString();
+ break;
case EOO:
os << "(EOO)";
break;
@@ -99,6 +106,14 @@ bool SafeNum::isEquivalent(const SafeNum& rhs) const {
// If the types of either side are mixed, we'll try to find the shortest type we
// can upconvert to that would not sacrifice the accuracy in the process.
+ // If one side is a decimal, compare both sides as decimals.
+ if (_type == NumberDecimal || rhs._type == NumberDecimal) {
+ // Note: isEqual is faster than using compareDecimals, however it does not handle
+ // comparing NaN as equal (differing from BSONElement::woCompare). This case
+ // is not handled for double comparison above eihter.
+ return getDecimal(*this).isEqual(getDecimal(rhs));
+ }
+
// If none of the sides is a double, compare them as long's.
if (_type != NumberDouble && rhs._type != NumberDouble) {
return getLongLong(*this) == getLongLong(rhs);
@@ -134,6 +149,8 @@ bool SafeNum::isIdentical(const SafeNum& rhs) const {
return _value.int64Val == rhs._value.int64Val;
case NumberDouble:
return _value.doubleVal == rhs._value.doubleVal;
+ case NumberDecimal:
+ return Decimal128(_value.decimalVal).isEqual(rhs._value.decimalVal);
case EOO:
// EOO doesn't match anything, including itself.
default:
@@ -160,6 +177,23 @@ double SafeNum::getDouble(const SafeNum& snum) {
return snum._value.int64Val;
case NumberDouble:
return snum._value.doubleVal;
+ case NumberDecimal:
+ return Decimal128(snum._value.decimalVal).toDouble();
+ default:
+ return 0.0;
+ }
+}
+
+Decimal128 SafeNum::getDecimal(const SafeNum& snum) {
+ switch (snum._type) {
+ case NumberInt:
+ return snum._value.int32Val;
+ case NumberLong:
+ return snum._value.int64Val;
+ case NumberDouble:
+ return snum._value.doubleVal;
+ case NumberDecimal:
+ return snum._value.decimalVal;
default:
return 0.0;
}
@@ -211,6 +245,10 @@ SafeNum addFloats(double lDouble, double rDouble) {
return SafeNum(sum);
}
+SafeNum addDecimals(Decimal128 lDecimal, Decimal128 rDecimal) {
+ return SafeNum(lDecimal.add(rDecimal));
+}
+
SafeNum mulInt32Int32(int lInt32, int rInt32) {
// NOTE: Please see "Secure Coding in C and C++", Second Edition, page 264-265 for
// details on this algorithm (for an alternative resources, see
@@ -274,6 +312,10 @@ SafeNum mulFloats(double lDouble, double rDouble) {
return SafeNum(product);
}
+SafeNum mulDecimals(Decimal128 lDecimal, Decimal128 rDecimal) {
+ return SafeNum(lDecimal.multiply(rDecimal));
+}
+
} // namespace
SafeNum SafeNum::addInternal(const SafeNum& lhs, const SafeNum& rhs) {
@@ -296,6 +338,10 @@ SafeNum SafeNum::addInternal(const SafeNum& lhs, const SafeNum& rhs) {
return addInt64Int64(lhs._value.int64Val, rhs._value.int64Val);
}
+ if (lType == NumberDecimal || rType == NumberDecimal) {
+ return addDecimals(getDecimal(lhs), getDecimal(rhs));
+ }
+
if ((lType == NumberInt || lType == NumberLong || lType == NumberDouble) &&
(rType == NumberInt || rType == NumberLong || rType == NumberDouble)) {
return addFloats(getDouble(lhs), getDouble(rhs));
@@ -324,6 +370,10 @@ SafeNum SafeNum::mulInternal(const SafeNum& lhs, const SafeNum& rhs) {
return mulInt64Int64(lhs._value.int64Val, rhs._value.int64Val);
}
+ if (lType == NumberDecimal || rType == NumberDecimal) {
+ return mulDecimals(getDecimal(lhs), getDecimal(rhs));
+ }
+
if ((lType == NumberInt || lType == NumberLong || lType == NumberDouble) &&
(rType == NumberInt || rType == NumberLong || rType == NumberDouble)) {
return mulFloats(getDouble(lhs), getDouble(rhs));
diff --git a/src/mongo/util/safe_num.h b/src/mongo/util/safe_num.h
index 839996bd370..8151d5aa4d4 100644
--- a/src/mongo/util/safe_num.h
+++ b/src/mongo/util/safe_num.h
@@ -43,7 +43,8 @@ class Document;
* SafeNum holds and does arithmetic on a number in a safe way, handling overflow
* and casting for the user. 32-bit integers will overflow into 64-bit integers. But
* 64-bit integers will NOT overflow to doubles. Also, this class does NOT
- * downcast. This class should be as conservative as possible about upcasting, but
+ * downcast. Doubles will NOT overflow to decimal, but mixed type arithmetic with a decimal
+ * will. This class should be as conservative as possible about upcasting, but
* should never lose precision.
*
* This class does not throw any exceptions, so the user should call type() before
@@ -82,6 +83,7 @@ public:
SafeNum(int num);
SafeNum(long long int num);
SafeNum(double num);
+ SafeNum(Decimal128 num);
// TODO: add Paul's mutablebson::Element ctor
//
@@ -90,8 +92,9 @@ public:
/**
* Returns true if the numeric quantity of 'rhs' and 'this' are the same. That is,
- * an int32(10), an int64(10), and a double(10) are equivalent. An EOO-typed safe
- * num is equivalent only to another EOO-typed instance. Otherwise, returns false.
+ * an int32(10), an int64(10), a double(10), and a decimal(10) are equivalent. An
+ * EOO-typed safe num is equivalent only to another EOO-typed instance. Otherwise,
+ * returns false.
*/
bool isEquivalent(const SafeNum& rhs) const;
bool operator==(const SafeNum& rhs) const;
@@ -124,8 +127,8 @@ public:
//
// logical operation support. Note that these operations are only supported for
- // integral types. Attempts to apply with either side holding a double value
- // will result in an EOO typed safenum.
+ // integral types. Attempts to apply with either side holding a double or decimal
+ // value will result in an EOO typed safenum.
//
// Bitwise 'and' support
@@ -170,7 +173,7 @@ public:
static const long long maxIntInDouble = 9007199254740992LL; // 2^53
private:
- // One of the following: NumberInt, NumberLong, NumberDouble, or EOO.
+ // One of the following: NumberInt, NumberLong, NumberDouble, NumberDecimal, or EOO.
BSONType _type;
// Value of the safe num. Indeterminate if _type is EOO.
@@ -178,6 +181,7 @@ private:
int int32Val;
long long int int64Val;
double doubleVal;
+ Decimal128::Value decimalVal;
} _value;
/**
@@ -211,7 +215,7 @@ private:
/**
* Extracts the value of 'snum' in a long format. It assumes 'snum' is an NumberInt
- * or a NumberDouble.
+ * or a NumberLong.
*/
static long long getLongLong(const SafeNum& snum);
@@ -220,6 +224,13 @@ private:
* SafeNum, i.e., that _type is not EOO.
*/
static double getDouble(const SafeNum& snum);
+
+ /**
+ * Extracts the value of 'snum' in a Decimal128 format. It assumes 'snum' is an
+ * NumberInt, NumberDouble, or NumberLong. Integral values are converted exactly.
+ * NumberDouble is converted to 15 digits of precision, as defined in Decimal128.
+ */
+ static Decimal128 getDecimal(const SafeNum& snum);
};
// Convenience method for unittest code. Please use accessors otherwise.
diff --git a/src/mongo/util/safe_num_test.cpp b/src/mongo/util/safe_num_test.cpp
index ff910f7b3fc..7d679bdb3d1 100644
--- a/src/mongo/util/safe_num_test.cpp
+++ b/src/mongo/util/safe_num_test.cpp
@@ -31,12 +31,15 @@
#undef MONGO_PCH_WHITELISTED // for malloc/realloc pulled from bson
#include "mongo/bson/bsontypes.h"
+#include "mongo/bson/bsonobj.h"
+#include "mongo/platform/decimal128.h"
#include "mongo/util/safe_num.h"
#include "mongo/unittest/unittest.h"
namespace {
using mongo::SafeNum;
+using mongo::Decimal128;
TEST(Basics, Initialization) {
const SafeNum numInt(0);
@@ -47,6 +50,35 @@ TEST(Basics, Initialization) {
const SafeNum numDouble(0.0);
ASSERT_EQUALS(numDouble.type(), mongo::NumberDouble);
+
+ if (mongo::Decimal128::enabled) {
+ const SafeNum numDecimal(Decimal128("1.0"));
+ ASSERT_EQUALS(numDecimal.type(), mongo::NumberDecimal);
+ }
+}
+
+TEST(Basics, BSONElementInitialization) {
+ mongo::BSONObj o;
+ if (mongo::Decimal128::enabled) {
+ o = BSON("numberInt" << 1 << "numberLong" << 1LL << "numberDouble" << 0.1 << "NumberDecimal"
+ << Decimal128("1"));
+ } else {
+ o = BSON("numberInt" << 1 << "numberLong" << 1LL << "numberDouble" << 0.1);
+ }
+
+ const SafeNum numInt(o.getField("numberInt"));
+ ASSERT_EQUALS(numInt.type(), mongo::NumberInt);
+
+ const SafeNum numLong(o.getField("numberLong"));
+ ASSERT_EQUALS(numLong.type(), mongo::NumberLong);
+
+ const SafeNum numDouble(o.getField("numberDouble"));
+ ASSERT_EQUALS(numDouble.type(), mongo::NumberDouble);
+
+ if (mongo::Decimal128::enabled) {
+ const SafeNum numDecimal(o.getField("NumberDecimal"));
+ ASSERT_EQUALS(numDecimal.type(), mongo::NumberDecimal);
+ }
}
TEST(Comparison, EOO) {
@@ -70,6 +102,13 @@ TEST(Comparison, StrictTypeComparison) {
ASSERT_FALSE(one.isIdentical(oneLong));
ASSERT_FALSE(oneLong.isIdentical(oneDouble));
ASSERT_FALSE(oneDouble.isIdentical(one));
+ ASSERT_TRUE(oneDouble.isIdentical(oneDouble));
+
+ if (mongo::Decimal128::enabled) {
+ const SafeNum oneDecimal(Decimal128(1));
+ ASSERT_FALSE(oneDecimal.isIdentical(one));
+ ASSERT_TRUE(oneDecimal.isIdentical(oneDecimal));
+ }
}
TEST(Comparison, EquivalenceComparisonNormal) {
@@ -79,6 +118,11 @@ TEST(Comparison, EquivalenceComparisonNormal) {
ASSERT_EQUALS(one, oneLong);
ASSERT_EQUALS(oneLong, oneDouble);
ASSERT_EQUALS(oneDouble, one);
+
+ if (mongo::Decimal128::enabled) {
+ const SafeNum oneDecimal(Decimal128(1));
+ ASSERT_EQUALS(oneDecimal, one);
+ }
}
TEST(Comparison, MaxIntInDouble) {
@@ -111,12 +155,26 @@ TEST(Addition, UpConvertion) {
ASSERT_EQUALS((zeroInt32 + zeroDouble).type(), mongo::NumberDouble);
ASSERT_EQUALS((zeroInt64 + zeroDouble).type(), mongo::NumberDouble);
+
const SafeNum stillInt32(zeroInt32 + zeroInt32);
const SafeNum stillInt64(zeroInt64 + zeroInt64);
const SafeNum stillDouble(zeroDouble + zeroDouble);
ASSERT_EQUALS(stillInt32.type(), mongo::NumberInt);
ASSERT_EQUALS(stillInt64.type(), mongo::NumberLong);
ASSERT_EQUALS(stillDouble.type(), mongo::NumberDouble);
+
+ if (mongo::Decimal128::enabled) {
+ const SafeNum zeroDecimal(Decimal128(0));
+ ASSERT_EQUALS((zeroInt64 + zeroDecimal).type(), mongo::NumberDecimal);
+ ASSERT_EQUALS((zeroInt32 + zeroDecimal).type(), mongo::NumberDecimal);
+ ASSERT_EQUALS((zeroDouble + zeroDecimal).type(), mongo::NumberDecimal);
+ ASSERT_EQUALS((zeroDecimal + zeroInt32).type(), mongo::NumberDecimal);
+ ASSERT_EQUALS((zeroDecimal + zeroInt64).type(), mongo::NumberDecimal);
+ ASSERT_EQUALS((zeroDecimal + zeroDouble).type(), mongo::NumberDecimal);
+
+ const SafeNum stillDecimal(zeroDecimal + zeroDecimal);
+ ASSERT_EQUALS(stillDecimal.type(), mongo::NumberDecimal);
+ }
}
TEST(Addition, Overflow32to64) {
@@ -191,7 +249,7 @@ TEST(Addition, Negative64toDouble) {
ASSERT_NOT_EQUALS(int64MinusOne, doubleResult);
}
-TEST(BitAnd, DoubleIsIgnored) {
+TEST(BitAnd, FloatingPointIsIgnored) {
const SafeNum val_int(static_cast<int>(1));
const SafeNum val_ll(static_cast<long long>(1));
const SafeNum val_double(1.0);
@@ -200,6 +258,16 @@ TEST(BitAnd, DoubleIsIgnored) {
ASSERT_FALSE((val_ll & val_double).isValid());
ASSERT_FALSE((val_double & val_ll).isValid());
ASSERT_FALSE((val_double & val_double).isValid());
+
+ if (mongo::Decimal128::enabled) {
+ const SafeNum val_decimal(Decimal128(1));
+ ASSERT_FALSE((val_int & val_decimal).isValid());
+ ASSERT_FALSE((val_double & val_decimal).isValid());
+ ASSERT_FALSE((val_ll & val_decimal).isValid());
+ ASSERT_FALSE((val_decimal & val_int).isValid());
+ ASSERT_FALSE((val_decimal & val_ll).isValid());
+ ASSERT_FALSE((val_decimal & val_double).isValid());
+ }
}
TEST(BitAnd, 32and32) {
@@ -235,7 +303,7 @@ TEST(BitAnd, MixedSize) {
ASSERT_TRUE(expected.isIdentical(result_b_s));
}
-TEST(BitOr, DoubleIsIgnored) {
+TEST(BitOr, FloatingPointIsIgnored) {
const SafeNum val_int(static_cast<int>(1));
const SafeNum val_ll(static_cast<long long>(1));
const SafeNum val_double(1.0);
@@ -244,6 +312,16 @@ TEST(BitOr, DoubleIsIgnored) {
ASSERT_FALSE((val_ll | val_double).isValid());
ASSERT_FALSE((val_double | val_ll).isValid());
ASSERT_FALSE((val_double | val_double).isValid());
+
+ if (mongo::Decimal128::enabled) {
+ const SafeNum val_decimal(Decimal128(1));
+ ASSERT_FALSE((val_decimal | val_int).isValid());
+ ASSERT_FALSE((val_decimal | val_double).isValid());
+ ASSERT_FALSE((val_decimal | val_ll).isValid());
+ ASSERT_FALSE((val_int | val_decimal).isValid());
+ ASSERT_FALSE((val_ll | val_decimal).isValid());
+ ASSERT_FALSE((val_double | val_decimal).isValid());
+ }
}
TEST(BitOr, 32and32) {
@@ -278,7 +356,7 @@ TEST(BitOr, MixedSize) {
ASSERT_TRUE(expected.isIdentical(result_b_s));
}
-TEST(BitXor, DoubleIsIgnored) {
+TEST(BitXor, FloatingPointIsIgnored) {
const SafeNum val_int(static_cast<int>(1));
const SafeNum val_ll(static_cast<long long>(1));
const SafeNum val_double(1.0);
@@ -287,6 +365,16 @@ TEST(BitXor, DoubleIsIgnored) {
ASSERT_FALSE((val_ll ^ val_double).isValid());
ASSERT_FALSE((val_double ^ val_ll).isValid());
ASSERT_FALSE((val_double ^ val_double).isValid());
+
+ if (mongo::Decimal128::enabled) {
+ const SafeNum val_decimal(Decimal128(1));
+ ASSERT_FALSE((val_decimal ^ val_int).isValid());
+ ASSERT_FALSE((val_decimal ^ val_ll).isValid());
+ ASSERT_FALSE((val_decimal ^ val_double).isValid());
+ ASSERT_FALSE((val_int ^ val_decimal).isValid());
+ ASSERT_FALSE((val_ll ^ val_decimal).isValid());
+ ASSERT_FALSE((val_double ^ val_decimal).isValid());
+ }
}
TEST(BitXor, 32and32) {
@@ -374,6 +462,18 @@ TEST(Multiplication, UpConvertion) {
ASSERT_EQUALS(stillInt32.type(), mongo::NumberInt);
ASSERT_EQUALS(stillInt64.type(), mongo::NumberLong);
ASSERT_EQUALS(stillDouble.type(), mongo::NumberDouble);
+
+ if (mongo::Decimal128::enabled) {
+ const SafeNum zeroDecimal(Decimal128(0));
+ ASSERT_EQUALS((zeroDecimal * zeroInt32).type(), mongo::NumberDecimal);
+ ASSERT_EQUALS((zeroInt32 * zeroDecimal).type(), mongo::NumberDecimal);
+ ASSERT_EQUALS((zeroDecimal * zeroInt64).type(), mongo::NumberDecimal);
+ ASSERT_EQUALS((zeroInt64 * zeroDecimal).type(), mongo::NumberDecimal);
+ ASSERT_EQUALS((zeroDecimal * zeroDouble).type(), mongo::NumberDecimal);
+ ASSERT_EQUALS((zeroDouble * zeroDecimal).type(), mongo::NumberDecimal);
+ const SafeNum stillDecimal(zeroDecimal * zeroDecimal);
+ ASSERT_EQUALS(stillDecimal.type(), mongo::NumberDecimal);
+ }
}
TEST(Multiplication, Overflow32to64) {