diff options
author | David Hatch <david.hatch@mongodb.com> | 2015-07-31 16:19:20 -0400 |
---|---|---|
committer | Raymond Jacobson <raymond.jacobson@10gen.com> | 2015-08-13 11:34:59 -0400 |
commit | 8fc4cbc675b90f96759b844ddfc4c52868a144f2 (patch) | |
tree | 1e5a687b2f9ce2e027d9fb0d20d7f58fd4016240 /src/mongo/bson | |
parent | 9a81b7f1a6a8e0773703f2007c5a7268eb182b91 (diff) | |
download | mongo-8fc4cbc675b90f96759b844ddfc4c52868a144f2.tar.gz |
SERVER-19628 Add Decimal128 type support to update/delete code paths
Diffstat (limited to 'src/mongo/bson')
-rw-r--r-- | src/mongo/bson/mutable/const_element-inl.h | 4 | ||||
-rw-r--r-- | src/mongo/bson/mutable/const_element.h | 1 | ||||
-rw-r--r-- | src/mongo/bson/mutable/document.cpp | 27 | ||||
-rw-r--r-- | src/mongo/bson/mutable/document.h | 3 | ||||
-rw-r--r-- | src/mongo/bson/mutable/element-inl.h | 5 | ||||
-rw-r--r-- | src/mongo/bson/mutable/element.cpp | 4 | ||||
-rw-r--r-- | src/mongo/bson/mutable/element.h | 11 | ||||
-rw-r--r-- | src/mongo/bson/mutable/mutable_bson_test.cpp | 157 |
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) { |