Skip to content

Commit

Permalink
SERVER-19628 Add Decimal128 type support to update/delete code paths
Browse files Browse the repository at this point in the history
  • Loading branch information
David Hatch authored and Raymond Jacobson committed Aug 13, 2015
1 parent 9a81b7f commit 8fc4cbc
Show file tree
Hide file tree
Showing 20 changed files with 559 additions and 18 deletions.
4 changes: 4 additions & 0 deletions src/mongo/bson/mutable/const_element-inl.h
Original file line number Diff line number Diff line change
Expand Up @@ -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();
}
Expand Down
1 change: 1 addition & 0 deletions src/mongo/bson/mutable/const_element.h
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ class ConstElement {
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;
Expand Down
27 changes: 26 additions & 1 deletion src/mongo/bson/mutable/document.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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();
}
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -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");
Expand Down Expand Up @@ -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));
Expand Down Expand Up @@ -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();
Expand Down
3 changes: 3 additions & 0 deletions src/mongo/bson/mutable/document.h
Original file line number Diff line number Diff line change
Expand Up @@ -377,6 +377,9 @@ class Document {
/** 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);

Expand Down
5 changes: 5 additions & 0 deletions src/mongo/bson/mutable/element-inl.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand Down
4 changes: 4 additions & 0 deletions src/mongo/bson/mutable/element.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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));
}
Expand Down
11 changes: 10 additions & 1 deletion src/mongo/bson/mutable/element.h
Original file line number Diff line number Diff line change
Expand Up @@ -258,7 +258,7 @@ class Element {
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;

Expand Down Expand Up @@ -324,6 +324,9 @@ class Element {
/** 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;

Expand Down Expand Up @@ -444,6 +447,9 @@ class Element {
/** 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();

Expand Down Expand Up @@ -567,6 +573,9 @@ class Element {
/** 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);

Expand Down
157 changes: 156 additions & 1 deletion src/mongo/bson/mutable/mutable_bson_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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());

Expand Down Expand Up @@ -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;

Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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) {
Expand All @@ -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) {
Expand Down Expand Up @@ -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";
Expand Down Expand Up @@ -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) {
Expand Down
Loading

0 comments on commit 8fc4cbc

Please sign in to comment.