summaryrefslogtreecommitdiff
path: root/src/mongo/bson/mutable
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 /src/mongo/bson/mutable
parent9a81b7f1a6a8e0773703f2007c5a7268eb182b91 (diff)
downloadmongo-8fc4cbc675b90f96759b844ddfc4c52868a144f2.tar.gz
SERVER-19628 Add Decimal128 type support to update/delete code paths
Diffstat (limited to 'src/mongo/bson/mutable')
-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
8 files changed, 209 insertions, 3 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) {