diff options
author | Mathias Stearn <mathias@10gen.com> | 2012-10-26 19:35:31 -0400 |
---|---|---|
committer | Mathias Stearn <mathias@10gen.com> | 2012-11-16 17:32:59 -0500 |
commit | ffd194f68edf4a61bff35fac591452b715cff849 (patch) | |
tree | 67ed1e6300f04d1333fee769e9c1ba2f8b4d41d6 | |
parent | 7eb3fc28fbd9cb0464cbf6dba5ffb497ba2088e9 (diff) | |
download | mongo-ffd194f68edf4a61bff35fac591452b715cff849.tar.gz |
Rewrite Document and Value classes
43 files changed, 2636 insertions, 2267 deletions
diff --git a/src/mongo/base/string_data.h b/src/mongo/base/string_data.h index fbde3e611f5..2bef8d03258 100644 --- a/src/mongo/base/string_data.h +++ b/src/mongo/base/string_data.h @@ -99,6 +99,7 @@ namespace mongo { bool empty() const { return size() == 0; } string toString() const { return string(_data, _size); } + string toStdString() const { return string(data(), size()); } private: const char* const _data; // is always null terminated, but see "notes" above mutable size_t _size; // 'size' does not include the null terminator diff --git a/src/mongo/bson/bsonobjbuilder.h b/src/mongo/bson/bsonobjbuilder.h index 3d24d82645b..60801f0d60e 100644 --- a/src/mongo/bson/bsonobjbuilder.h +++ b/src/mongo/bson/bsonobjbuilder.h @@ -362,6 +362,10 @@ namespace mongo { BSONObjBuilder& append(const StringData& fieldName, const std::string& str) { return append(fieldName, str.c_str(), (int) str.size()+1); } + /** Append a string element */ + BSONObjBuilder& append(const StringData& fieldName, const StringData& str) { + return append(fieldName, str.data(), (int) str.size()+1); + } BSONObjBuilder& appendSymbol(const StringData& fieldName, const StringData& symbol) { _b.appendNum((char) Symbol); @@ -774,6 +778,11 @@ namespace mongo { return *this; } + BSONArrayBuilder& append(const StringData& s) { + _b.append(num(), s); + return *this; + } + bool isArray() const { return true; } diff --git a/src/mongo/db/pipeline/accumulator.cpp b/src/mongo/db/pipeline/accumulator.cpp index 993ec126504..4b610bec63b 100755 --- a/src/mongo/db/pipeline/accumulator.cpp +++ b/src/mongo/db/pipeline/accumulator.cpp @@ -36,10 +36,8 @@ namespace mongo { ExpressionNary() { } - void Accumulator::opToBson(BSONObjBuilder *pBuilder, - const std::string& opName, - const std::string& fieldName, - bool requireExpression) const { + void Accumulator::opToBson(BSONObjBuilder *pBuilder, StringData opName, + StringData fieldName, bool requireExpression) const { verify(vpOperand.size() == 1); BSONObjBuilder builder; vpOperand[0]->addToBsonObj(&builder, opName, requireExpression); @@ -47,7 +45,7 @@ namespace mongo { } void Accumulator::addToBsonObj(BSONObjBuilder *pBuilder, - const std::string& fieldName, + StringData fieldName, bool requireExpression) const { opToBson(pBuilder, getOpName(), fieldName, requireExpression); } diff --git a/src/mongo/db/pipeline/accumulator.h b/src/mongo/db/pipeline/accumulator.h index 1c947c9ca84..16606a0dbbd 100755 --- a/src/mongo/db/pipeline/accumulator.h +++ b/src/mongo/db/pipeline/accumulator.h @@ -31,9 +31,9 @@ namespace mongo { public: // virtuals from ExpressionNary virtual void addOperand(const intrusive_ptr<Expression> &pExpression); - virtual void addToBsonObj( - BSONObjBuilder *pBuilder, const std::string& fieldName, - bool requireExpression) const; + virtual void addToBsonObj(BSONObjBuilder *pBuilder, + StringData fieldName, + bool requireExpression) const; virtual void addToBsonArray(BSONArrayBuilder *pBuilder) const; /* @@ -41,7 +41,7 @@ namespace mongo { @returns the accumulated value */ - virtual intrusive_ptr<const Value> getValue() const = 0; + virtual Value getValue() const = 0; protected: Accumulator(); @@ -55,9 +55,8 @@ namespace mongo { @param fieldName the projected name @param opName the operator name */ - void opToBson( - BSONObjBuilder *pBuilder, const std::string& fieldName, const std::string& opName, - bool requireExpression) const; + void opToBson(BSONObjBuilder *pBuilder, StringData opName, + StringData fieldName, bool requireExpression) const; }; @@ -65,9 +64,8 @@ namespace mongo { public Accumulator { public: // virtuals from Expression - virtual intrusive_ptr<const Value> evaluate( - const intrusive_ptr<Document> &pDocument) const; - virtual intrusive_ptr<const Value> getValue() const; + virtual Value evaluate(const Document& pDocument) const; + virtual Value getValue() const; virtual const char *getOpName() const; /* @@ -81,7 +79,7 @@ namespace mongo { private: AccumulatorAddToSet(const intrusive_ptr<ExpressionContext> &pTheCtx); - typedef boost::unordered_set<intrusive_ptr<const Value>, Value::Hash > SetType; + typedef boost::unordered_set<Value, Value::Hash > SetType; mutable SetType set; mutable SetType::iterator itr; intrusive_ptr<ExpressionContext> pCtx; @@ -98,12 +96,12 @@ namespace mongo { public Accumulator { public: // virtuals from Expression - virtual intrusive_ptr<const Value> getValue() const; + virtual Value getValue() const; protected: AccumulatorSingleValue(); - mutable intrusive_ptr<const Value> pValue; /* current min/max */ + mutable Value pValue; /* current min/max */ }; @@ -111,8 +109,7 @@ namespace mongo { public AccumulatorSingleValue { public: // virtuals from Expression - virtual intrusive_ptr<const Value> evaluate( - const intrusive_ptr<Document> &pDocument) const; + virtual Value evaluate(const Document& pDocument) const; virtual const char *getOpName() const; /* @@ -132,8 +129,7 @@ namespace mongo { public AccumulatorSingleValue { public: // virtuals from Expression - virtual intrusive_ptr<const Value> evaluate( - const intrusive_ptr<Document> &pDocument) const; + virtual Value evaluate(const Document& pDocument) const; virtual const char *getOpName() const; /* @@ -153,9 +149,8 @@ namespace mongo { public Accumulator { public: // virtuals from Accumulator - virtual intrusive_ptr<const Value> evaluate( - const intrusive_ptr<Document> &pDocument) const; - virtual intrusive_ptr<const Value> getValue() const; + virtual Value evaluate(const Document& pDocument) const; + virtual Value getValue() const; virtual const char *getOpName() const; /* @@ -182,8 +177,7 @@ namespace mongo { public AccumulatorSingleValue { public: // virtuals from Expression - virtual intrusive_ptr<const Value> evaluate( - const intrusive_ptr<Document> &pDocument) const; + virtual Value evaluate(const Document& pDocument) const; virtual const char *getOpName() const; /* @@ -207,9 +201,8 @@ namespace mongo { public Accumulator { public: // virtuals from Expression - virtual intrusive_ptr<const Value> evaluate( - const intrusive_ptr<Document> &pDocument) const; - virtual intrusive_ptr<const Value> getValue() const; + virtual Value evaluate(const Document& pDocument) const; + virtual Value getValue() const; virtual const char *getOpName() const; /* @@ -224,7 +217,7 @@ namespace mongo { private: AccumulatorPush(const intrusive_ptr<ExpressionContext> &pTheCtx); - mutable vector<intrusive_ptr<const Value> > vpValue; + mutable vector<Value> vpValue; intrusive_ptr<ExpressionContext> pCtx; }; @@ -234,9 +227,8 @@ namespace mongo { typedef AccumulatorSum Super; public: // virtuals from Accumulator - virtual intrusive_ptr<const Value> evaluate( - const intrusive_ptr<Document> &pDocument) const; - virtual intrusive_ptr<const Value> getValue() const; + virtual Value evaluate(const Document& pDocument) const; + virtual Value getValue() const; virtual const char *getOpName() const; /* diff --git a/src/mongo/db/pipeline/accumulator_add_to_set.cpp b/src/mongo/db/pipeline/accumulator_add_to_set.cpp index 86d4366ff0c..81c96ff9091 100755 --- a/src/mongo/db/pipeline/accumulator_add_to_set.cpp +++ b/src/mongo/db/pipeline/accumulator_add_to_set.cpp @@ -21,12 +21,11 @@ #include "db/pipeline/value.h" namespace mongo { - intrusive_ptr<const Value> AccumulatorAddToSet::evaluate( - const intrusive_ptr<Document> &pDocument) const { + Value AccumulatorAddToSet::evaluate(const Document& pDocument) const { verify(vpOperand.size() == 1); - intrusive_ptr<const Value> prhs(vpOperand[0]->evaluate(pDocument)); + Value prhs(vpOperand[0]->evaluate(pDocument)); - if (prhs->getType() == Undefined) + if (prhs.getType() == Undefined) ; /* nothing to add to the array */ else if (!pCtx->getDoingMerge()) set.insert(prhs); @@ -37,20 +36,17 @@ namespace mongo { If we didn't, then we'd get an array of arrays, with one array from each shard that responds. */ - verify(prhs->getType() == Array); + verify(prhs.getType() == Array); - intrusive_ptr<ValueIterator> pvi(prhs->getArray()); - while(pvi->more()) { - intrusive_ptr<const Value> pElement(pvi->next()); - set.insert(pElement); - } + const vector<Value>& array = prhs.getArray(); + set.insert(array.begin(), array.end()); } - return Value::getNull(); + return Value(); } - intrusive_ptr<const Value> AccumulatorAddToSet::getValue() const { - vector<intrusive_ptr<const Value> > valVec; + Value AccumulatorAddToSet::getValue() const { + vector<Value> valVec; for (itr = set.begin(); itr != set.end(); ++itr) { valVec.push_back(*itr); diff --git a/src/mongo/db/pipeline/accumulator_avg.cpp b/src/mongo/db/pipeline/accumulator_avg.cpp index 3e69b204fbb..07787325992 100755 --- a/src/mongo/db/pipeline/accumulator_avg.cpp +++ b/src/mongo/db/pipeline/accumulator_avg.cpp @@ -26,8 +26,7 @@ namespace mongo { const char AccumulatorAvg::subTotalName[] = "subTotal"; const char AccumulatorAvg::countName[] = "count"; - intrusive_ptr<const Value> AccumulatorAvg::evaluate( - const intrusive_ptr<Document> &pDocument) const { + Value AccumulatorAvg::evaluate(const Document& pDocument) const { if (!pCtx->getDoingMerge()) { Super::evaluate(pDocument); } @@ -37,20 +36,19 @@ namespace mongo { both a subtotal and a count. This is what getValue() produced below. */ - intrusive_ptr<const Value> prhs(vpOperand[0]->evaluate(pDocument)); - verify(prhs->getType() == Object); - intrusive_ptr<Document> pShardDoc(prhs->getDocument()); + Value shardOut = vpOperand[0]->evaluate(pDocument); + verify(shardOut.getType() == Object); - intrusive_ptr<const Value> pSubTotal(pShardDoc->getValue(subTotalName)); - verify(pSubTotal.get()); - doubleTotal += pSubTotal->getDouble(); + Value subTotal = shardOut[subTotalName]; + verify(!subTotal.missing()); + doubleTotal += subTotal.getDouble(); - intrusive_ptr<const Value> pCount(pShardDoc->getValue(countName)); - verify(pCount.get()); - count += pCount->getLong(); + Value subCount = shardOut[countName]; + verify(!subCount.missing()); + count += subCount.getLong(); } - return Value::getZero(); + return Value(); } intrusive_ptr<Accumulator> AccumulatorAvg::create( @@ -59,7 +57,7 @@ namespace mongo { return pA; } - intrusive_ptr<const Value> AccumulatorAvg::getValue() const { + Value AccumulatorAvg::getValue() const { if (!pCtx->getInShard()) { double avg = 0; if (count) @@ -68,11 +66,11 @@ namespace mongo { return Value::createDouble(avg); } - intrusive_ptr<Document> pDocument(Document::create()); - pDocument->addField(subTotalName, Value::createDouble(doubleTotal)); - pDocument->addField(countName, Value::createLong(count)); + MutableDocument out; + out.addField(subTotalName, Value::createDouble(doubleTotal)); + out.addField(countName, Value::createLong(count)); - return Value::createDocument(pDocument); + return Value::createDocument(out.freeze()); } AccumulatorAvg::AccumulatorAvg( diff --git a/src/mongo/db/pipeline/accumulator_first.cpp b/src/mongo/db/pipeline/accumulator_first.cpp index 53d8f9595e9..b226fb552d6 100755 --- a/src/mongo/db/pipeline/accumulator_first.cpp +++ b/src/mongo/db/pipeline/accumulator_first.cpp @@ -21,12 +21,11 @@ namespace mongo { - intrusive_ptr<const Value> AccumulatorFirst::evaluate( - const intrusive_ptr<Document> &pDocument) const { + Value AccumulatorFirst::evaluate(const Document& pDocument) const { verify(vpOperand.size() == 1); /* only remember the first value seen */ - if (!pValue.get()) + if (pValue.missing()) pValue = vpOperand[0]->evaluate(pDocument); return pValue; diff --git a/src/mongo/db/pipeline/accumulator_last.cpp b/src/mongo/db/pipeline/accumulator_last.cpp index d934e64111b..ea65094d04b 100755 --- a/src/mongo/db/pipeline/accumulator_last.cpp +++ b/src/mongo/db/pipeline/accumulator_last.cpp @@ -21,8 +21,7 @@ namespace mongo { - intrusive_ptr<const Value> AccumulatorLast::evaluate( - const intrusive_ptr<Document> &pDocument) const { + Value AccumulatorLast::evaluate(const Document& pDocument) const { verify(vpOperand.size() == 1); /* always remember the last value seen */ diff --git a/src/mongo/db/pipeline/accumulator_min_max.cpp b/src/mongo/db/pipeline/accumulator_min_max.cpp index aec461bab02..bc48b6fb480 100755 --- a/src/mongo/db/pipeline/accumulator_min_max.cpp +++ b/src/mongo/db/pipeline/accumulator_min_max.cpp @@ -21,13 +21,12 @@ namespace mongo { - intrusive_ptr<const Value> AccumulatorMinMax::evaluate( - const intrusive_ptr<Document> &pDocument) const { + Value AccumulatorMinMax::evaluate(const Document& pDocument) const { verify(vpOperand.size() == 1); - intrusive_ptr<const Value> prhs(vpOperand[0]->evaluate(pDocument)); + Value prhs(vpOperand[0]->evaluate(pDocument)); /* if this is the first value, just use it */ - if (!pValue.get()) + if (pValue.missing()) pValue = prhs; else { /* compare with the current value; swap if appropriate */ diff --git a/src/mongo/db/pipeline/accumulator_push.cpp b/src/mongo/db/pipeline/accumulator_push.cpp index b7a6370d77f..4530b798465 100755 --- a/src/mongo/db/pipeline/accumulator_push.cpp +++ b/src/mongo/db/pipeline/accumulator_push.cpp @@ -21,15 +21,16 @@ #include "db/pipeline/value.h" namespace mongo { - intrusive_ptr<const Value> AccumulatorPush::evaluate( - const intrusive_ptr<Document> &pDocument) const { + Value AccumulatorPush::evaluate(const Document& pDocument) const { verify(vpOperand.size() == 1); - intrusive_ptr<const Value> prhs(vpOperand[0]->evaluate(pDocument)); + Value prhs(vpOperand[0]->evaluate(pDocument)); - if (prhs->getType() == Undefined) - ; /* nothing to add to the array */ - else if (!pCtx->getDoingMerge()) + if (prhs.getType() == Undefined) { + /* nothing to add to the array */ + } + else if (!pCtx->getDoingMerge()) { vpValue.push_back(prhs); + } else { /* If we're in the router, we need to take apart the arrays we @@ -37,19 +38,16 @@ namespace mongo { If we didn't, then we'd get an array of arrays, with one array from each shard that responds. */ - verify(prhs->getType() == Array); + verify(prhs.getType() == Array); - intrusive_ptr<ValueIterator> pvi(prhs->getArray()); - while(pvi->more()) { - intrusive_ptr<const Value> pElement(pvi->next()); - vpValue.push_back(pElement); - } + const vector<Value>& vec = prhs.getArray(); + vpValue.insert(vpValue.end(), vec.begin(), vec.end()); } - return Value::getNull(); + return Value(); } - intrusive_ptr<const Value> AccumulatorPush::getValue() const { + Value AccumulatorPush::getValue() const { return Value::createArray(vpValue); } diff --git a/src/mongo/db/pipeline/accumulator_single_value.cpp b/src/mongo/db/pipeline/accumulator_single_value.cpp index ea12ee333a2..76187d52222 100755 --- a/src/mongo/db/pipeline/accumulator_single_value.cpp +++ b/src/mongo/db/pipeline/accumulator_single_value.cpp @@ -21,12 +21,12 @@ namespace mongo { - intrusive_ptr<const Value> AccumulatorSingleValue::getValue() const { + Value AccumulatorSingleValue::getValue() const { return pValue; } AccumulatorSingleValue::AccumulatorSingleValue(): - pValue(intrusive_ptr<const Value>()) { + pValue(Value()) { } } diff --git a/src/mongo/db/pipeline/accumulator_sum.cpp b/src/mongo/db/pipeline/accumulator_sum.cpp index 775158c72cf..6fe091f3cf4 100755 --- a/src/mongo/db/pipeline/accumulator_sum.cpp +++ b/src/mongo/db/pipeline/accumulator_sum.cpp @@ -21,27 +21,26 @@ namespace mongo { - intrusive_ptr<const Value> AccumulatorSum::evaluate( - const intrusive_ptr<Document> &pDocument) const { + Value AccumulatorSum::evaluate(const Document& pDocument) const { verify(vpOperand.size() == 1); - intrusive_ptr<const Value> prhs(vpOperand[0]->evaluate(pDocument)); + Value prhs(vpOperand[0]->evaluate(pDocument)); - BSONType rhsType = prhs->getType(); + BSONType rhsType = prhs.getType(); // do nothing with non numeric types if (!(rhsType == NumberInt || rhsType == NumberLong || rhsType == NumberDouble)) - return Value::getZero(); + return Value(0); // upgrade to the widest type required to hold the result totalType = Value::getWidestNumeric(totalType, rhsType); if (totalType == NumberInt || totalType == NumberLong) { - long long v = prhs->coerceToLong(); + long long v = prhs.coerceToLong(); longTotal += v; doubleTotal += v; } else if (totalType == NumberDouble) { - double v = prhs->coerceToDouble(); + double v = prhs.coerceToDouble(); doubleTotal += v; } else { @@ -51,7 +50,7 @@ namespace mongo { count++; - return Value::getZero(); + return Value(); } intrusive_ptr<Accumulator> AccumulatorSum::create( @@ -60,7 +59,7 @@ namespace mongo { return pSummer; } - intrusive_ptr<const Value> AccumulatorSum::getValue() const { + Value AccumulatorSum::getValue() const { if (totalType == NumberLong) { return Value::createLong(longTotal); } diff --git a/src/mongo/db/pipeline/builder.cpp b/src/mongo/db/pipeline/builder.cpp index a85d75ce02f..69eddcf9747 100755 --- a/src/mongo/db/pipeline/builder.cpp +++ b/src/mongo/db/pipeline/builder.cpp @@ -46,7 +46,7 @@ namespace mongo { pBuilder->append(fieldName, d); } - void BuilderObj::append(const std::string& s) { + void BuilderObj::append(StringData s) { pBuilder->append(fieldName, s); } @@ -70,8 +70,7 @@ namespace mongo { pBuilder->appendTimestamp(fieldName, ot.getSecs(), ot.getInc()); } - BuilderObj::BuilderObj( - BSONObjBuilder *pObjBuilder, const std::string& theFieldName): + BuilderObj::BuilderObj(BSONObjBuilder *pObjBuilder, StringData theFieldName): pBuilder(pObjBuilder), fieldName(theFieldName) { } @@ -101,7 +100,7 @@ namespace mongo { pBuilder->append(d); } - void BuilderArray::append(const std::string& s) { + void BuilderArray::append(StringData s) { pBuilder->append(s); } diff --git a/src/mongo/db/pipeline/builder.h b/src/mongo/db/pipeline/builder.h index e6a68a72a32..5e4f5e48e38 100755 --- a/src/mongo/db/pipeline/builder.h +++ b/src/mongo/db/pipeline/builder.h @@ -44,7 +44,7 @@ namespace mongo { virtual void append(int i) = 0; virtual void append(long long ll) = 0; virtual void append(double d) = 0; - virtual void append(const std::string& s) = 0; + virtual void append(StringData s) = 0; virtual void append(const OID &o) = 0; virtual void append(const Date_t &d) = 0; virtual void append(const OpTime& ot) = 0; @@ -62,18 +62,18 @@ namespace mongo { virtual void append(int i); virtual void append(long long ll); virtual void append(double d); - virtual void append(const std::string& s); + virtual void append(StringData s); virtual void append(const OID &o); virtual void append(const Date_t &d); virtual void append(const OpTime& ot); virtual void append(BSONObjBuilder *pDone); virtual void append(BSONArrayBuilder *pDone); - BuilderObj(BSONObjBuilder *pBuilder, const std::string& fieldName); + BuilderObj(BSONObjBuilder *pBuilder, StringData fieldName); private: BSONObjBuilder *pBuilder; - string fieldName; + StringData fieldName; }; class BuilderArray : @@ -86,7 +86,7 @@ namespace mongo { virtual void append(int i); virtual void append(long long ll); virtual void append(double d); - virtual void append(const std::string& s); + virtual void append(StringData s); virtual void append(const OID &o); virtual void append(const Date_t &d); virtual void append(const OpTime& ot); diff --git a/src/mongo/db/pipeline/document.cpp b/src/mongo/db/pipeline/document.cpp index e34af40b8dd..65b0ec6e2de 100755..100644 --- a/src/mongo/db/pipeline/document.cpp +++ b/src/mongo/db/pipeline/document.cpp @@ -15,159 +15,292 @@ */ #include "pch.h" + +#include "mongo/db/pipeline/document.h" + #include <boost/functional/hash.hpp> -#include "db/jsobj.h" -#include "db/pipeline/document.h" -#include "db/pipeline/value.h" -#include "util/mongoutils/str.h" + +#include "mongo/db/jsobj.h" +#include "mongo/db/pipeline/field_path.h" +#include "mongo/util/mongoutils/str.h" namespace mongo { using namespace mongoutils; - string Document::idName("_id"); + Position DocumentStorage::findField(StringData requested) const { + if (_numFields >= HASH_TAB_MIN) { // hash lookup + const unsigned bucket = bucketForKey(requested); - intrusive_ptr<Document> Document::createFromBsonObj(BSONObj* pBsonObj) { - return new Document(pBsonObj); - } + Position pos = _hashTab[bucket]; + while (pos.found()) { + const ValueElement& elem = getField(pos); + if (str::equals(requested.data(), elem.name)) + return pos; - Document::Document(BSONObj* pBsonObj) { - const int fields = pBsonObj->nFields(); - vFieldName.reserve(fields); - vpValue.reserve(fields); - BSONObjIterator bsonIterator(pBsonObj->begin()); - while(bsonIterator.more()) { - BSONElement bsonElement(bsonIterator.next()); - string fieldName(bsonElement.fieldName()); + // possible collision + pos = elem.nextCollision; + } + } + else if (_numFields) { // linear scan + for (DocumentStorageIterator it = iteratorAll(); !it.atEnd(); it.advance()) { + if (size_t(it->nameLen) == requested.size() + && str::equals(it->name, requested.data())) { + return it.position(); + } + } + } - // LATER grovel through structures??? - intrusive_ptr<const Value> pValue( - Value::createFromBsonElement(&bsonElement)); + // if we got here, there's no such field + return Position(); + } - vFieldName.push_back(fieldName); - vpValue.push_back(pValue); + Value& DocumentStorage::appendField(StringData name) { + Position pos = getNextPosition(); + const int nameSize = name.size(); + + // these are the same for everyone + const Position nextCollision; + const Value value; + + // Make room for new field (and padding at end for alignment) + const unsigned newUsed = ValueElement::align(_usedBytes + sizeof(ValueElement) + nameSize); + if (_buffer + newUsed > _bufferEnd) + alloc(newUsed); + _usedBytes = newUsed; + + // Append structure of a ValueElement + char* dest = _buffer + pos.index; // must be after alloc since it changes _buffer +#define append(x) memcpy(dest, &(x), sizeof(x)); dest += sizeof(x) + append(value); + append(nextCollision); + append(nameSize); + memcpy(dest, name.data(), nameSize + 1/*NUL*/); + // Padding for alignment handled above +#undef append + + // Make sure next field starts where we expect it + fassert(16486, getField(pos).next()->ptr() == _buffer + _usedBytes); + + _numFields++; + + if (_numFields > HASH_TAB_MIN) { + addFieldToHashTable(pos); + } + else if (_numFields == HASH_TAB_MIN) { + // adds all fields to hash table (including the one we just added) + rehash(); } - } - void Document::toBson(BSONObjBuilder* pBuilder) const { - const size_t n = vFieldName.size(); - for(size_t i = 0; i < n; ++i) - vpValue[i]->addToBsonObj(pBuilder, vFieldName[i]); + return getField(pos); } - intrusive_ptr<Document> Document::create(size_t sizeHint) { - intrusive_ptr<Document> pDocument(new Document(sizeHint)); - return pDocument; + // Call after adding field to _fields and increasing _numFields + void DocumentStorage::addFieldToHashTable(Position pos) { + ValueElement& elem = getField(pos); + elem.nextCollision = Position(); + + const unsigned bucket = bucketForKey(elem.nameSD()); + + Position* posPtr = &_hashTab[bucket]; + while (posPtr->found()) { + // collision: walk links and add new to end + posPtr = &getField(*posPtr).nextCollision; + } + *posPtr = Position(pos.index); } - Document::Document(size_t sizeHint): - vFieldName(), - vpValue() { - if (sizeHint) { - vFieldName.reserve(sizeHint); - vpValue.reserve(sizeHint); + void DocumentStorage::alloc(unsigned newSize) { + const bool firstAlloc = !_buffer; + const bool doingRehash = needRehash(); + const size_t oldCapacity = _bufferEnd - _buffer; + + // make new bucket count big enough + while (needRehash() || hashTabBuckets() < HASH_TAB_INIT_SIZE) + _hashTabMask = hashTabBuckets()*2 - 1; + + // only allocate power-of-two sized space > 128 bytes + size_t capacity = 128; + while (capacity < newSize + hashTabBytes()) + capacity *= 2; + + uassert(16490, "Tried to make oversized document", + capacity <= size_t(BufferMaxSize)); + + boost::scoped_array<char> oldBuf(_buffer); + _buffer = new char[capacity]; + _bufferEnd = _buffer + capacity - hashTabBytes(); + + if (!firstAlloc) { + // This just copies the elements + memcpy(_buffer, oldBuf.get(), _usedBytes); + + if (_numFields >= HASH_TAB_MIN) { + // if we were hashing, deal with the hash table + if (doingRehash) { + rehash(); + } + else { + // no rehash needed so just slide table down to new position + memcpy(_hashTab, oldBuf.get() + oldCapacity, hashTabBytes()); + } + } } } - intrusive_ptr<Document> Document::clone() { - const size_t n = vFieldName.size(); - intrusive_ptr<Document> pNew(Document::create(n)); - for(size_t i = 0; i < n; ++i) - pNew->addField(vFieldName[i], vpValue[i]); + void DocumentStorage::reserveFields(size_t expectedFields) { + fassert(16487, !_buffer); + + unsigned buckets = HASH_TAB_INIT_SIZE; + while (buckets < expectedFields) + buckets *= 2; + _hashTabMask = buckets - 1; + + // Using expectedFields+1 to allow space for long field names + const size_t newSize = (expectedFields+1) * ValueElement::align(sizeof(ValueElement)); - return pNew; + uassert(16491, "Tried to make oversized document", + newSize <= size_t(BufferMaxSize)); + + _buffer = new char[newSize + hashTabBytes()]; + _bufferEnd = _buffer + newSize; } - Document::~Document() { + intrusive_ptr<DocumentStorage> DocumentStorage::clone() const { + intrusive_ptr<DocumentStorage> out (new DocumentStorage()); + + // Make a copy of the buffer. + // It is very important that the positions of each field are the same after cloning. + const size_t bufferBytes = (_bufferEnd + hashTabBytes()) - _buffer; + out->_buffer = new char[bufferBytes]; + out->_bufferEnd = out->_buffer + (_bufferEnd - _buffer); + memcpy(out->_buffer, _buffer, bufferBytes); + + // Copy remaining fields + out->_usedBytes = _usedBytes; + out->_numFields = _numFields; + out->_hashTabMask = _hashTabMask; + + // Tell values that they have been memcpyed (updates ref counts) + for (DocumentStorageIterator it = out->iteratorAll(); !it.atEnd(); it.advance()) { + it->val.memcpyed(); + } + + return out; } - FieldIterator *Document::createFieldIterator() { - return new FieldIterator(intrusive_ptr<Document>(this)); + DocumentStorage::~DocumentStorage() { + boost::scoped_array<char> deleteBufferAtScopeEnd (_buffer); + + for (DocumentStorageIterator it = iteratorAll(); !it.atEnd(); it.advance()) { + it->val.~Value(); // explicit destructor call + } } - intrusive_ptr<const Value> Document::getValue(const string &fieldName) { - /* - For now, assume the number of fields is small enough that iteration - is ok. Later, if this gets large, we can create a map into the - vector for these lookups. + Document::Document(const BSONObj& bson) { + MutableDocument md(bson.nFields()); - Note that because of the schema-less nature of this data, we always - have to look, and can't assume that the requested field is always - in a particular place as we would with a statically compilable - reference. - */ - const size_t n = vFieldName.size(); - for(size_t i = 0; i < n; ++i) { - if (fieldName.compare(vFieldName[i]) == 0) - return vpValue[i]; + BSONObjIterator it(bson); + while(it.more()) { + BSONElement bsonElement(it.next()); + md.addField(bsonElement.fieldName(), Value(bsonElement)); } - return(intrusive_ptr<const Value>()); + *this = md.freeze(); } - void Document::addField(const string &fieldName, - const intrusive_ptr<const Value> &pValue) { - vFieldName.push_back(fieldName); - vpValue.push_back(pValue); + void Document::toBson(BSONObjBuilder* pBuilder) const { + for (DocumentStorageIterator it = storage().iterator(); !it.atEnd(); it.advance()) { + it->val.addToBsonObj(pBuilder, it->nameSD()); + } } - void Document::setField(size_t index, - const string &fieldName, - const intrusive_ptr<const Value> &pValue) { - /* special case: should this field be removed? */ - if (!pValue.get()) { - vFieldName.erase(vFieldName.begin() + index); - vpValue.erase(vpValue.begin() + index); - return; + MutableDocument::MutableDocument(size_t expectedFields) + : _storageHolder(NULL) + , _storage(_storageHolder) + { + if (expectedFields) { + storage().reserveFields(expectedFields); } - - /* set the indicated field */ - vFieldName[index] = fieldName; - vpValue[index] = pValue; } - intrusive_ptr<const Value> Document::getField(const string &fieldName) const { - const size_t n = vFieldName.size(); - for(size_t i = 0; i < n; ++i) { - if (fieldName.compare(vFieldName[i]) == 0) - return vpValue[i]; + MutableValue MutableDocument::getNestedFieldHelper(MutableDocument& md, + const vector<Position>& positions, + size_t level) { + fassert(16488, !positions.empty()); + + if (level == positions.size()-1) { + return md[positions[level]]; } + else { + MutableDocument nested (md[positions[level]]); + return getNestedFieldHelper(nested, positions, level+1); + } + } - /* if we got here, there's no such field */ - return intrusive_ptr<const Value>(); + MutableValue MutableDocument::getNestedField(const vector<Position>& positions) { + return getNestedFieldHelper(*this, positions, 0); } - size_t Document::getApproximateSize() const { - size_t size = sizeof(Document); - const size_t n = vpValue.size(); - for(size_t i = 0; i < n; ++i) - size += vpValue[i]->getApproximateSize(); + static Value getNestedFieldHelper(const Document& doc, + const FieldPath& fieldNames, + vector<Position>* positions, + size_t level) { - return size; + fassert(16489, fieldNames.getPathLength()); + + const string& fieldName = fieldNames.getFieldName(level); + const Position pos = doc.positionOf(fieldName); + + if (!pos.found()) + return Value(); + + if (positions) + positions->push_back(pos); + + if (level == fieldNames.getPathLength()-1) + return doc.getField(pos); + + Value val = doc.getField(pos); + if (val.getType() != Object) + return Value(); + + return getNestedFieldHelper(val.getDocument(), fieldNames, positions, level+1); + } + + const Value Document::getNestedField(const FieldPath& fieldNames, + vector<Position>* positions) const { + return getNestedFieldHelper(*this, fieldNames, positions, 0); } - size_t Document::getFieldIndex(const string &fieldName) const { - const size_t n = vFieldName.size(); - size_t i = 0; - for(; i < n; ++i) { - if (fieldName.compare(vFieldName[i]) == 0) - break; + size_t Document::getApproximateSize() const { + if (!_storage) + return 0; // we've allocated no memory + + size_t size = sizeof(DocumentStorage); + size += storage().allocatedBytes(); + + for (DocumentStorageIterator it = storage().iterator(); !it.atEnd(); it.advance()) { + size += it->val.getApproximateSize(); + size -= sizeof(Value); // already accounted for above } - return i; + return size; } void Document::hash_combine(size_t &seed) const { - const size_t n = vFieldName.size(); - for(size_t i = 0; i < n; ++i) { - boost::hash_combine(seed, vFieldName[i]); - vpValue[i]->hash_combine(seed); + for (DocumentStorageIterator it = storage().iterator(); !it.atEnd(); it.advance()) { + StringData name = it->nameSD(); + boost::hash_range(seed, name.data(), name.data()+name.size()); + it->val.hash_combine(seed); } } - int Document::compare(const intrusive_ptr<Document> &rL, - const intrusive_ptr<Document> &rR) { - const size_t lSize = rL->vFieldName.size(); - const size_t rSize = rR->vFieldName.size(); + int Document::compare(const Document& rL, const Document& rR) { + const size_t lSize = rL->storage().size(); + const size_t rSize = rR->storage().size(); + + DocumentStorageIterator lIt = rL.storage().iterator(); + DocumentStorageIterator rIt = rR.storage().iterator(); for(size_t i = 0; true; ++i) { if (i >= lSize) { @@ -180,43 +313,35 @@ namespace mongo { if (i >= rSize) return 1; // right document is shorter - const int nameCmp = rL->vFieldName[i].compare(rR->vFieldName[i]); + const ValueElement& rField = rIt.get(); + const ValueElement& lField = lIt.get(); + + rIt.advance(); + lIt.advance(); + + const int nameCmp = strcmp(lField.name, rField.name); if (nameCmp) return nameCmp; // field names are unequal - const int valueCmp = Value::compare(rL->vpValue[i], rR->vpValue[i]); + const int valueCmp = Value::compare(lField.val, rField.val); if (valueCmp) return valueCmp; // fields are unequal } - - /* NOTREACHED */ - verify(false); - return 0; } string Document::toString() const { - // this is a temporary hack and it should only be used for debugging - BSONObjBuilder bb; - toBson(&bb); - return bb.done().toString(); - } - - /* ----------------------- FieldIterator ------------------------------- */ + if (empty()) + return "{}"; - FieldIterator::FieldIterator(const intrusive_ptr<Document> &pTheDocument): - pDocument(pTheDocument), - index(0) { - } + StringBuilder out; + const char* prefix = "{"; - bool FieldIterator::more() const { - return (index < pDocument->vFieldName.size()); - } + for (DocumentStorageIterator it = storage().iterator(); !it.atEnd(); it.advance()) { + out << prefix << it->name << ": " << it->val.toString(); + prefix = ", "; + } + out << '}'; - pair<string, intrusive_ptr<const Value> > FieldIterator::next() { - verify(more()); - pair<string, intrusive_ptr<const Value> > result( - pDocument->vFieldName[index], pDocument->vpValue[index]); - ++index; - return result; + return out.str(); } } diff --git a/src/mongo/db/pipeline/document.h b/src/mongo/db/pipeline/document.h index 3db7e9dfebf..09b1a356011 100755..100644 --- a/src/mongo/db/pipeline/document.h +++ b/src/mongo/db/pipeline/document.h @@ -16,234 +16,389 @@ #pragma once -#include "mongo/pch.h" +#include "mongo/db/pipeline/document_internal.h" -#include "util/intrusive_counter.h" +#include <boost/functional/hash.hpp> +#include "mongo/bson/util/builder.h" namespace mongo { class BSONObj; class FieldIterator; + class FieldPath; class Value; - - class Document : - public IntrusiveCounterUnsigned { + class MutableDocument; + + /** An internal class that represents the position of a field in a document. + * + * This is a low-level class that you usually don't need to worry about. + * + * The main use of this class for clients is to allow refetching or + * setting a field without looking it up again. It has a default + * constructor that represents a field not being in a document. It also + * has a method 'bool found()' that tells you if a field was found. + * + * For more details see document_internal.h + */ + class Position; + + /** A Document is similar to a BSONObj but with a different in-memory representation. + * + * A Document can be treated as a const map<string, const Value> that is + * very cheap to copy and is Assignable. Therefore, it is acceptable to + * pass and return by Value. Note that the data in a Document is + * immutable, but you can replace a Document instance with assignment. + * + * See Also: Value class in Value.h + */ + class Document { public: - ~Document(); - /* - Create a new Document from the given BSONObj. + /// Empty Document (does no allocation) + Document() {} - Document field values may be pointed to in the BSONObj, so it - must live at least as long as the resulting Document. + /// Create a new Document deep-converted from the given BSONObj. + explicit Document(const BSONObj& bson); - @returns shared pointer to the newly created Document - */ - static intrusive_ptr<Document> createFromBsonObj(BSONObj* pBsonObj); + void swap(Document& rhs) { _storage.swap(rhs._storage); } - /* - Create a new empty Document. + /// Look up a field by key name. Returns Value() if no such field. O(1) + const Value operator[] (StringData key) const { return getField(key); } + const Value getField(StringData key) const { return storage().getField(key); } - @param sizeHint a hint at what the number of fields will be; if - known, this can be used to increase memory allocation efficiency - @returns shared pointer to the newly created Document - */ - static intrusive_ptr<Document> create(size_t sizeHint = 0); + /// Look up a field by Position. See positionOf and getNestedField. + const Value operator[] (Position pos) const { return getField(pos); } + const Value getField(Position pos) const { return storage().getField(pos); } - /* - Clone a document. + /** Similar to BSONObj::getFieldDotted, but using FieldPath rather than a dotted string. + * If you pass a non-NULL positions vector, you get back a path suitable + * to pass to MutableDocument::setNestedField. + * + * TODO a version that doesn't use FieldPath + */ + const Value getNestedField(const FieldPath& fieldNames, + vector<Position>* positions=NULL) const; - The new document shares all the fields' values with the original. + /// Number of fields in this document. O(n) + size_t size() const { return storage().size(); } - This is not a deep copy. Only the fields on the top-level document - are cloned. + /// True if this document has no fields. + bool empty() const { return !_storage || storage().iterator().atEnd(); } - @returns the shallow clone of the document - */ - intrusive_ptr<Document> clone(); + /// Create a new FieldIterator that can be used to examine the Document's fields in order. + FieldIterator fieldIterator() const; - /* - Add this document to the BSONObj under construction with the - given BSONObjBuilder. - */ - void toBson(BSONObjBuilder *pBsonObjBuilder) const; + /// Convenience type for dealing with fields. Used by FieldIterator. + typedef pair<StringData, Value> FieldPair; - /* - Create a new FieldIterator that can be used to examine the - Document's fields. - */ - FieldIterator *createFieldIterator(); - - /* - Get the value of the specified field. - - @param fieldName the name of the field - @return point to the requested field - */ - intrusive_ptr<const Value> getValue(const string &fieldName); - - /* - Add the given field to the Document. - - BSON documents' fields are ordered; the new Field will be - appended to the current list of fields. - - It is an error to add a field that has the same name as another - field. - */ - void addField(const string &fieldName, - const intrusive_ptr<const Value> &pValue); - - /* - Set the given field to be at the specified position in the - Document. This will replace any field that is currently in that - position. The index must be within the current range of field - indices, otherwise behavior is undefined. - - pValue.get() may be NULL, in which case the field will be - removed. fieldName is ignored in this case. - - @param index the field index in the list of fields - @param fieldName the new field name - @param pValue the new Value - */ - void setField(size_t index, - const string &fieldName, - const intrusive_ptr<const Value> &pValue); - - /* - Convenience type for dealing with fields. + /** Get the approximate storage size of the document and sub-values in bytes. + * Note: Some memory may be shared with other Documents or between fields within + * a single Document so this can overestimate usage. */ - typedef pair<string, intrusive_ptr<const Value> > FieldPair; - - /* - Get the indicated field. + size_t getApproximateSize() const; - @param index the field index in the list of fields - @returns the field name and value of the field + /** Compare two documents. + * + * BSON document field order is significant, so this just goes through + * the fields in order. The comparison is done in roughly the same way + * as strings are compared, but comparing one field at a time instead + * of one character at a time. + * + * @returns an integer less than zero, zero, or an integer greater than + * zero, depending on whether lhs < rhs, lhs == rhs, or lhs > rhs + * Warning: may return values other than -1, 0, or 1 */ - FieldPair getField(size_t index) const; + static int compare(const Document& lhs, const Document& rhs); - /* - Get the number of fields in the Document. + string toString() const; // TODO support streams - @returns the number of fields in the Document + /** Calculate a hash value. + * + * Meant to be used to create composite hashes suitable for + * hashed container classes such as unordered_map. */ - size_t getFieldCount() const; + void hash_combine(size_t &seed) const; - /* - Get the index of the given field. + /// Add this document to the BSONObj under construction with the given BSONObjBuilder. + void toBson(BSONObjBuilder *pBsonObjBuilder) const; - @param fieldName the name of the field - @returns the index of the field, or if it does not exist, the number - of fields (getFieldCount()) - */ - size_t getFieldIndex(const string &fieldName) const; + /** Return the abstract Position of a field, suitable to pass to operator[] or getField(). + * This can potentially save time if you need to refer to a field multiple times. + */ + Position positionOf(StringData fieldName) const { return storage().findField(fieldName); } + + /** Clone a document. + * + * This should only be called by MutableDocument and tests + * + * The new document shares all the fields' values with the original. + * This is not a deep copy. Only the fields on the top-level document + * are cloned. + */ + Document clone() const { return Document(storage().clone().get()); } - /* - Get a field by name. + // TEMP for compatibility with legacy intrusive_ptr<Document> + Document& operator*() { return *this; } + const Document& operator*() const { return *this; } + Document* operator->() { return this; } + const Document* operator->() const { return this; } + const void* getPtr() const { return _storage.get(); } + void reset() { return _storage.reset(); } + static Document createFromBsonObj(BSONObj* pBsonObj) { return Document(*pBsonObj); } + size_t getFieldCount() const { return size(); } + Value getValue(StringData fieldName) const { return getField(fieldName); } - @param fieldName the name of the field - @returns the value of the field - */ - intrusive_ptr<const Value> getField(const string &fieldName) const; + // TODO: replace with logical equality once all current usages are fixed + bool operator== (const Document& lhs) const { return _storage == lhs._storage; } - /* - Get the approximate storage size of the document, in bytes. + explicit Document(const DocumentStorage* ptr) : _storage(ptr) {}; - Under the assumption that field name strings are shared, they are - not included in the total. + private: + friend class FieldIterator; + friend class ValueStorage; + friend class MutableDocument; - @returns the approximate storage - */ - size_t getApproximateSize() const; + const DocumentStorage& storage() const { + return (_storage ? *_storage : DocumentStorage::emptyDoc()); + } + intrusive_ptr<const DocumentStorage> _storage; + }; - /* - Compare two documents. + /** This class is returned by MutableDocument to allow you to modify its values. + * You are not allowed to hold variables of this type (enforced by the type system). + */ + class MutableValue { + public: + void operator= (const Value& v) { _val = v; } - BSON document field order is significant, so this just goes through - the fields in order. The comparison is done in roughly the same way - as strings are compared, but comparing one field at a time instead - of one character at a time. - */ - static int compare(const intrusive_ptr<Document> &rL, - const intrusive_ptr<Document> &rR); + /** These are designed to allow things like mutDoc["a"]["b"]["c"] = Value(10); + * It is safe to use even on nonexistent fields. + */ + MutableValue operator[] (StringData key) { return getField(key); } + MutableValue operator[] (Position pos) { return getField(pos); } - static string idName; // shared "_id" + MutableValue getField(StringData key); + MutableValue getField(Position pos); - /* - Calculate a hash value. + private: + friend class MutableDocument; - Meant to be used to create composite hashes suitable for - boost classes such as unordered_map<>. + /// can only be constructed or copied by self and friends + MutableValue(const MutableValue& other): _val(other._val) {} + explicit MutableValue(Value& val): _val(val) {} - @param seed value to augment with this' hash - */ - void hash_combine(size_t &seed) const; + /// Used by MutableDocument(MutableValue) + const RefCountable*& getDocPtr() { + if (_val.getType() != Object) + *this = Value(Document()); - // For debugging purposes only! - string toString() const; + return _val._storage.genericRCPtr; + } - private: - friend class FieldIterator; - - Document(size_t sizeHint); - Document(BSONObj* pBsonObj); + MutableValue& operator= (const MutableValue&); // not assignable with another MutableValue - /* these two vectors parallel each other */ - vector<string> vFieldName; - vector<intrusive_ptr<const Value> > vpValue; + Value& _val; }; - - class FieldIterator : - boost::noncopyable { + /** MutableDocument is a Document builder that supports both adding and updating fields. + * + * This class fills a similar role to BSONObjBuilder, but allows you to + * change existing fields and more easily write to sub-Documents. + * + * To preserve the immutability of Documents, MutableDocument will + * shallow-clone its storage on write (COW) if it is shared with any other + * Documents. + */ + class MutableDocument : boost::noncopyable { public: - /* - Ask if there are more fields to return. - @return true if there are more fields, false otherwise - */ - bool more() const; + /** Create a new empty Document. + * + * @param expectedFields a hint at what the number of fields will be, if known. + * this can be used to increase memory allocation efficiency. There is + * no impact on correctness if this field over or under estimates. + * + * TODO: find some way to convey field-name sizes to make even more efficient + */ + MutableDocument() :_storageHolder(NULL), _storage(_storageHolder) {} + explicit MutableDocument(size_t expectedFields); + + /// No copy yet. Copy-on-write. See storage() + explicit MutableDocument(const Document& d) : _storageHolder(NULL) + , _storage(_storageHolder) { + reset(d); + } + + ~MutableDocument() { + if (_storageHolder) + intrusive_ptr_release(_storageHolder); + } + + /** Replace the current base Document with the argument + * + * All Positions from the passed in Document are valid and refer to the + * same field in this MutableDocument. + */ + void reset(const Document& d=Document()) { reset(d._storage.get()); } + + /** Add the given field to the Document. + * + * BSON documents' fields are ordered; the new Field will be + * appended to the current list of fields. + * + * Unlike getField/setField, addField does not look for a field with the + * same name and therefore cannot be used to update fields. + * + * It is an error to add a field that has the same name as another field. + * + * TODO: This is currently allowed but getField only gets first field. + * Decide what level of support is needed for duplicate fields. + * If duplicates are not allowed, consider removing this method. + */ + void addField(StringData fieldName, const Value& val) { + storage().appendField(fieldName) = val; + } - /* - Move the iterator to point to the next field and return it. + /** Update field by key. If there is no field with that key, add one. + * + * If the new value is missing(), the field is logically removed. + */ + MutableValue operator[] (StringData key) { return getField(key); } + void setField(StringData key, const Value& val) { getField(key) = val; } + MutableValue getField(StringData key) { + return MutableValue(storage().getField(key)); + } + + /// Update field by Position. Must already be a valid Position. + MutableValue operator[] (Position pos) { return getField(pos); } + void setField(Position pos, const Value& val) { getField(pos) = val; } + MutableValue getField(Position pos) { + return MutableValue(storage().getField(pos).val); + } + + /// Logically remove a field. Note that memory usage does not decrease. + void remove(StringData key) { getField(key) = Value(); } + + /// Takes positions vector from Document::getNestedField. + MutableValue getNestedField(const vector<Position>& positions); + void setNestedField(const vector<Position>& positions, const Value& val) { + getNestedField(positions) = val; + } + + /** Convert to a read-only document and release reference. + * + * Call this to indicate that you are done with this Document and will + * not be making further changes from this MutableDocument. + * + * TODO: there are some optimizations that may make sense at freeze time. + */ + Document freeze() { + Document ret(storagePtr()); + reset(NULL); + return ret; + } + + /** Borrow a readable reference to this Document. + * + * Note that unlike freeze(), this indicates intention to continue + * modifying this document. The returned Document will not observe + * future changes to this MutableDocument. + */ + Document peek() { + return Document(storagePtr()); + } - @return the next field's <name, Value> - */ - Document::FieldPair next(); + private: + friend class MutableValue; // for access to next constructor + explicit MutableDocument(MutableValue mv) + : _storageHolder(NULL) + , _storage(mv.getDocPtr()) + {} + + void reset(const DocumentStorage* ds) { + if (_storage) intrusive_ptr_release(_storage); + _storage = ds; + if (_storage) intrusive_ptr_add_ref(_storage); + } + + // This is split into 3 functions to speed up the fast-path + DocumentStorage& storage() { + if (MONGO_unlikely( !_storage )) + return newStorage(); + + if (MONGO_unlikely( _storage->isShared() )) + return clonedStorage(); + + // This function exists to ensure this is safe + return const_cast<DocumentStorage&>(*storagePtr()); + } + DocumentStorage& newStorage() { + reset(new DocumentStorage); + return const_cast<DocumentStorage&>(*storagePtr()); + } + DocumentStorage& clonedStorage() { + reset(storagePtr()->clone().get()); + return const_cast<DocumentStorage&>(*storagePtr()); + } + + MutableValue getNestedFieldHelper(MutableDocument& md, + const vector<Position>& positions, + size_t level); + + // this should only be called by storage methods and peek/freeze + const DocumentStorage* storagePtr() const { + dassert(!_storage || typeid(*_storage) == typeid(const DocumentStorage)); + return static_cast<const DocumentStorage*>(_storage); + } + + // These are both const to prevent modifications bypassing storage() method. + // They always point to NULL or an object with dynamic type DocumentStorage. + const RefCountable* _storageHolder; // Only used in constructors and destructor + const RefCountable*& _storage; // references either above member or genericRCPtr in a Value + }; + + /// This is the public iterator over a document + class FieldIterator { + public: + explicit FieldIterator(const Document& doc) + : _doc(doc) + , _it(_doc.storage().iterator()) + {} - /* - Constructor. + /// Ask if there are more fields to return. + bool more() const { return !_it.atEnd(); } - @param pDocument points to the document whose fields are being - iterated - */ - FieldIterator(const intrusive_ptr<Document> &pDocument); + /// Get next item and advance iterator + Document::FieldPair next() { + verify(more()); + + Document::FieldPair fp (_it->nameSD(), _it->val); + _it.advance(); + return fp; + } private: - friend class Document; - - /* - We'll hang on to the original document to ensure we keep the - fieldPtr vector alive. - */ - intrusive_ptr<Document> pDocument; - size_t index; // current field in iteration + // We'll hang on to the original document to ensure we keep its storage alive + Document _doc; + DocumentStorageIterator _it; }; } +namespace std { + template<> + inline void swap(mongo::Document& lhs, mongo::Document& rhs) { lhs.swap(rhs); } +} /* ======================= INLINED IMPLEMENTATIONS ========================== */ namespace mongo { + inline FieldIterator Document::fieldIterator() const { + return FieldIterator(*this); + } - inline size_t Document::getFieldCount() const { - return vFieldName.size(); + inline MutableValue MutableValue::getField(Position pos) { + return MutableDocument(*this).getField(pos); } - - inline Document::FieldPair Document::getField(size_t index) const { - verify( index < vFieldName.size() ); - return FieldPair(vFieldName[index], vpValue[index]); + inline MutableValue MutableValue::getField(StringData key) { + return MutableDocument(*this).getField(key); } - } diff --git a/src/mongo/db/pipeline/document_internal.h b/src/mongo/db/pipeline/document_internal.h new file mode 100644 index 00000000000..39677bb5698 --- /dev/null +++ b/src/mongo/db/pipeline/document_internal.h @@ -0,0 +1,302 @@ +/** + * Copyright 2012 (c) 10gen Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#pragma once + +#include <third_party/murmurhash3/MurmurHash3.h> + +#include "mongo/util/intrusive_counter.h" +#include "mongo/db/pipeline/value.h" + +namespace mongo { + /** Helper class to make the position in a document abstract + * Warning: This is NOT guaranteed to be the ordered position. + * eg. the first field may not be at Position(0) + */ + class Position { + public: + // This represents "not found" similar to string::npos + Position() :index(static_cast<unsigned>(-1)) {} + bool found() const { return index != Position().index; } + + bool operator == (Position rhs) const { return this->index == rhs.index; } + bool operator != (Position rhs) const { return !(*this == rhs); } + + // For debugging and ASSERT_EQUALS in tests. + template <typename OStream> + friend OStream& operator<<(OStream& stream, Position p) { return stream << p.index; } + + private: + explicit Position(size_t i) :index(i) {} + unsigned index; + friend class DocumentStorage; + friend class DocumentStorageIterator; + }; + +#pragma pack(1) + /** This is how values are stored in the DocumentStorage buffer + * Internal class. Consumers shouldn't care about this. + */ + class ValueElement : boost::noncopyable { + public: + Value val; + Position nextCollision; // Position of next field with same hashBucket + const int nameLen; // doesn't include '\0' + const char name[1]; // pointer to start of name + + // implicit conversions to Value + operator Value&() { return val; } + operator const Value&() const { return val; } + + ValueElement* next() { + return align(plusBytes(sizeof(ValueElement) + nameLen)); + } + + const ValueElement* next() const { + return align(plusBytes(sizeof(ValueElement) + nameLen)); + } + + StringData nameSD() const { return StringData(name, nameLen); } + + + // helpers for doing pointer arithmetic with this class + // Note: These don't dereference 'this' so they are safe to use with NULL + char* ptr() { return reinterpret_cast<char*>(this); } + const char* ptr() const { return reinterpret_cast<const char*>(this); } + const ValueElement* plusBytes(size_t bytes) const { + return reinterpret_cast<const ValueElement*>(ptr() + bytes); + } + ValueElement* plusBytes(size_t bytes) { + return reinterpret_cast<ValueElement*>(ptr() + bytes); + } + + // Round number or pointer up to N-byte boundary. No change if already aligned. + template <typename T> + static T align(T size) { + const intmax_t ALIGNMENT = 8; // must be power of 2 and <= 16 (malloc alignment) + // Can't use c++ cast because of conversion between intmax_t and both ints and pointers + return (T)(((intmax_t)(size) + (ALIGNMENT-1)) & ~(ALIGNMENT-1)); + } + + private: + ValueElement(); // this class should never be constructed + ~ValueElement(); // or destructed + }; + // Real size is sizeof(ValueElement) + nameLen +#pragma pack() + BOOST_STATIC_ASSERT(sizeof(ValueElement) == (sizeof(Value) + + sizeof(Position) + + sizeof(int) + + 1)); + + // This is an internal class for Document. See FieldIterator for the public version. + class DocumentStorageIterator { + public: + // DocumentStorage::iterator() and iteratorAll() are easier to use + DocumentStorageIterator(const ValueElement* first, + const ValueElement* end, + bool includeMissing) + : _first(first) + , _it(first) + , _end(end) + , _includeMissing(includeMissing) { + skipMissing(); + } + + bool atEnd() const { return _it == _end; } + + const ValueElement& get() const { return *_it; } + + Position position() const { return Position(_it->ptr() - _first->ptr()); } + + void advance() { + advanceOne(); + skipMissing(); + } + + const ValueElement* operator-> () { return _it; } + const ValueElement& operator* () { return *_it; } + + private: + void advanceOne() { + _it = _it->next(); + } + + void skipMissing() { + if (!_includeMissing) { + while (!atEnd() && _it->val.missing()) { + advanceOne(); + } + } + } + + const ValueElement* _first; + const ValueElement* _it; + const ValueElement* _end; + bool _includeMissing; + }; + + /// Storage class used by both Document and MutableDocument + class DocumentStorage : public RefCountable { + public: + // Note: default constructor should zero-init to support emptyDoc() + DocumentStorage() : _buffer(NULL) + , _bufferEnd(NULL) + , _usedBytes(0) + , _numFields(0) + , _hashTabMask(0) + {} + ~DocumentStorage(); + + static const DocumentStorage& emptyDoc() { + static const char emptyBytes[sizeof(DocumentStorage)] = {0}; + return *reinterpret_cast<const DocumentStorage*>(emptyBytes); + } + + size_t size() const { + // can't use _numFields because it includes removed Fields + size_t count = 0; + for (DocumentStorageIterator it = iterator(); !it.atEnd(); it.advance()) + count++; + return count; + } + + /// Returns the position of the next field to be inserted + Position getNextPosition() const { return Position(_usedBytes); } + + /// Returns the position of the named field (may be missing) or Position() + Position findField(StringData name) const; + + // Document uses these + const ValueElement& getField(Position pos) const { + verify(pos.found()); + return *(_firstElement->plusBytes(pos.index)); + } + Value getField(StringData name) const { + Position pos = findField(name); + if (!pos.found()) + return Value(); + return getField(pos); + } + + // MutableDocument uses these + ValueElement& getField(Position pos) { + verify(pos.found()); + return *(_firstElement->plusBytes(pos.index)); + } + Value& getField(StringData name) { + Position pos = findField(name); + if (!pos.found()) + return appendField(name); // TODO: find a way to avoid hashing name twice + return getField(pos); + } + + /// Adds a new field with missing Value at the end of the document + Value& appendField(StringData name); + + /** Preallocates space for fields. Use this to attempt to prevent buffer growth. + * This is only valid to call before anything is added to the document. + */ + void reserveFields(size_t expectedFields); + + /// This skips missing values + DocumentStorageIterator iterator() const { + return DocumentStorageIterator(_firstElement, end(), false); + } + + /// This includes missing values + DocumentStorageIterator iteratorAll() const { + return DocumentStorageIterator(_firstElement, end(), true); + } + + /// Shallow copy of this. Caller owns memory. + intrusive_ptr<DocumentStorage> clone() const; + + size_t allocatedBytes() const { + return !_buffer ? 0 : (_bufferEnd - _buffer + hashTabBytes()); + } + + private: + + /// Same as lastElement->next() or firstElement() if empty. + const ValueElement* end() const { return _firstElement->plusBytes(_usedBytes); } + + /// Allocates space in _buffer. Copies existing data if there is any. + void alloc(unsigned newSize); + + /// Call after adding field to _buffer and increasing _numFields + void addFieldToHashTable(Position pos); + + // assumes _hashTabMask is (power of two) - 1 + unsigned hashTabBuckets() const { return _hashTabMask + 1; } + unsigned hashTabBytes() const { return hashTabBuckets() * sizeof(Position); } + + /// rehash on buffer growth if load-factor > .5 (attempt to keep lf < 1 when full) + bool needRehash() const { return _numFields*2 > hashTabBuckets(); } + + /// Initialize empty hash table + void hashTabInit() { memset(_hashTab, -1, hashTabBytes()); } + + static unsigned hashKey(StringData name) { + // TODO consider FNV-1a once we have a better benchmark corpus + unsigned out; + MurmurHash3_x86_32(name.data(), name.size(), 0, &out); + return out; + } + + unsigned bucketForKey(StringData name) const { + return hashKey(name) & _hashTabMask; + } + + /// Adds all fields to the hash table + void rehash() { + hashTabInit(); + for (DocumentStorageIterator it = iteratorAll(); !it.atEnd(); it.advance()) + addFieldToHashTable(it.position()); + } + + enum { + HASH_TAB_INIT_SIZE = 8, // must be power of 2 + HASH_TAB_MIN = 4, // don't hash fields for docs smaller than this + // set to 1 to always hash + }; + + // _buffer layout: + // ------------------------------------------------------------------------------- + // | ValueElement1 Name1 | ValueElement2 Name2 | ... FREE SPACE ... | Hash Table | + // ------------------------------------------------------------------------------- + // ^ _buffer and _firstElement point here ^ + // _bufferEnd and _hashTab point here ^ + // + // + // When the buffer grows, the hash table moves to the new end. + union { + char* _buffer; + ValueElement* _firstElement; + }; + + union { + // pointer to "end" of _buffer element space and start of hash table (same position) + char* _bufferEnd; + Position* _hashTab; // table lazily initialized once _numFields == HASH_TAB_MIN + }; + + unsigned _usedBytes; // position where next field would start + unsigned _numFields; // this includes removed fields + unsigned _hashTabMask; // equal to hashTabBuckets()-1 but used more often + // When adding a field, make sure to update clone() method + }; +} diff --git a/src/mongo/db/pipeline/document_source.h b/src/mongo/db/pipeline/document_source.h index 7fcce36e91f..02595148076 100755 --- a/src/mongo/db/pipeline/document_source.h +++ b/src/mongo/db/pipeline/document_source.h @@ -88,9 +88,14 @@ namespace mongo { /** @returns the current Document without advancing. * - * some implementations do the equivalent of verify(!eof()) so check eof() first + * It is illegal to call this without first checking eof() == false or advance() == true. + * + * While it is legal to call getCurrent() multiple times between calls to advance, and + * you will get the same Document returned, some DocumentSources do expensive work in + * getCurrent(). You are advised to cache the result if you plan to access it more than + * once. */ - virtual intrusive_ptr<Document> getCurrent() = 0; + virtual Document getCurrent() = 0; /** * Inform the source that it is no longer needed and may release its resources. After @@ -258,7 +263,7 @@ namespace mongo { virtual ~DocumentSourceBsonArray(); virtual bool eof(); virtual bool advance(); - virtual intrusive_ptr<Document> getCurrent(); + virtual Document getCurrent(); virtual void setSource(DocumentSource *pSource); /** @@ -301,7 +306,7 @@ namespace mongo { virtual ~DocumentSourceCommandShards(); virtual bool eof(); virtual bool advance(); - virtual intrusive_ptr<Document> getCurrent(); + virtual Document getCurrent(); virtual void setSource(DocumentSource *pSource); /* convenient shorthand for a commonly used type */ @@ -335,9 +340,11 @@ namespace mongo { */ void getNextDocument(); + bool unstarted; + bool hasCurrent; bool newSource; // set to true for the first item of a new source intrusive_ptr<DocumentSourceBsonArray> pBsonSource; - intrusive_ptr<Document> pCurrent; + Document pCurrent; ShardOutput::const_iterator iterator; ShardOutput::const_iterator listEnd; }; @@ -369,7 +376,7 @@ namespace mongo { virtual ~DocumentSourceCursor(); virtual bool eof(); virtual bool advance(); - virtual intrusive_ptr<Document> getCurrent(); + virtual Document getCurrent(); virtual void setSource(DocumentSource *pSource); /** @@ -437,7 +444,9 @@ namespace mongo { void findNext(); - intrusive_ptr<Document> pCurrent; + bool unstarted; + bool hasCurrent; + Document pCurrent; string ns; // namespace @@ -482,7 +491,7 @@ namespace mongo { virtual ~DocumentSourceFilterBase(); virtual bool eof(); virtual bool advance(); - virtual intrusive_ptr<Document> getCurrent(); + virtual Document getCurrent(); /** Create a BSONObj suitable for Matcher construction. @@ -508,15 +517,15 @@ namespace mongo { @param pDocument the document to test @returns true if the document matches the filter, false otherwise */ - virtual bool accept(const intrusive_ptr<Document> &pDocument) const = 0; + virtual bool accept(const Document& pDocument) const = 0; private: void findNext(); bool unstarted; - bool hasNext; - intrusive_ptr<Document> pCurrent; + bool hasCurrent; + Document pCurrent; }; @@ -571,7 +580,7 @@ namespace mongo { virtual void sourceToBson(BSONObjBuilder *pBuilder, bool explain) const; // virtuals from DocumentSourceFilterBase - virtual bool accept(const intrusive_ptr<Document> &pDocument) const; + virtual bool accept(const Document& pDocument) const; private: DocumentSourceFilter(const intrusive_ptr<Expression> &pFilter, @@ -589,7 +598,7 @@ namespace mongo { virtual bool eof(); virtual bool advance(); virtual const char *getSourceName() const; - virtual intrusive_ptr<Document> getCurrent(); + virtual Document getCurrent(); virtual GetDepsReturn getDependencies(set<string>& deps) const; /** @@ -668,7 +677,7 @@ namespace mongo { intrusive_ptr<Expression> pIdExpression; - typedef boost::unordered_map<intrusive_ptr<const Value>, + typedef boost::unordered_map<Value, vector<intrusive_ptr<Accumulator> >, Value::Hash> GroupsType; GroupsType groups; @@ -690,11 +699,10 @@ namespace mongo { vector<intrusive_ptr<Expression> > vpExpression; - intrusive_ptr<Document> makeDocument( - const GroupsType::iterator &rIter); + Document makeDocument(const GroupsType::iterator &rIter); GroupsType::iterator groupsIterator; - intrusive_ptr<Document> pCurrent; + Document pCurrent; }; @@ -735,7 +743,7 @@ namespace mongo { virtual void sourceToBson(BSONObjBuilder *pBuilder, bool explain) const; // virtuals from DocumentSourceFilterBase - virtual bool accept(const intrusive_ptr<Document> &pDocument) const; + virtual bool accept(const Document& pDocument) const; private: DocumentSourceMatch(const BSONObj &query, @@ -753,7 +761,7 @@ namespace mongo { virtual bool eof(); virtual bool advance(); virtual const char *getSourceName() const; - virtual intrusive_ptr<Document> getCurrent(); + virtual Document getCurrent(); /** Create a document source for output and pass-through. @@ -789,7 +797,7 @@ namespace mongo { virtual bool eof(); virtual bool advance(); virtual const char *getSourceName() const; - virtual intrusive_ptr<Document> getCurrent(); + virtual Document getCurrent(); virtual void optimize(); virtual GetDepsReturn getDependencies(set<string>& deps) const; @@ -839,7 +847,7 @@ namespace mongo { virtual bool eof(); virtual bool advance(); virtual const char *getSourceName() const; - virtual intrusive_ptr<Document> getCurrent(); + virtual Document getCurrent(); virtual GetDepsReturn getDependencies(set<string>& deps) const; @@ -932,8 +940,7 @@ namespace mongo { @returns a number less than, equal to, or greater than zero, indicating pL < pR, pL == pR, or pL > pR, respectively */ - int compare(const intrusive_ptr<Document> &pL, - const intrusive_ptr<Document> &pR); + int compare(const Document& pL, const Document& pR); /* This is a utility class just for the STL sort that is done @@ -941,9 +948,7 @@ namespace mongo { */ class Comparator { public: - bool operator()( - const intrusive_ptr<Document> &pL, - const intrusive_ptr<Document> &pR) { + bool operator()(const Document& pL, const Document& pR) { return (pSort->compare(pL, pR) < 0); } @@ -955,11 +960,11 @@ namespace mongo { DocumentSourceSort *pSort; }; - typedef vector<intrusive_ptr<Document> > VectorType; + typedef vector<Document> VectorType; VectorType documents; VectorType::iterator docIterator; - intrusive_ptr<Document> pCurrent; + Document pCurrent; }; @@ -970,7 +975,7 @@ namespace mongo { virtual ~DocumentSourceLimit(); virtual bool eof(); virtual bool advance(); - virtual intrusive_ptr<Document> getCurrent(); + virtual Document getCurrent(); virtual const char *getSourceName() const; virtual bool coalesce(const intrusive_ptr<DocumentSource> &pNextSource); @@ -1022,7 +1027,6 @@ namespace mongo { long long limit; long long count; - intrusive_ptr<Document> pCurrent; }; class DocumentSourceSkip : @@ -1032,7 +1036,7 @@ namespace mongo { virtual ~DocumentSourceSkip(); virtual bool eof(); virtual bool advance(); - virtual intrusive_ptr<Document> getCurrent(); + virtual Document getCurrent(); virtual const char *getSourceName() const; virtual bool coalesce(const intrusive_ptr<DocumentSource> &pNextSource); @@ -1088,7 +1092,7 @@ namespace mongo { long long skip; long long count; - intrusive_ptr<Document> pCurrent; + Document pCurrent; }; @@ -1100,7 +1104,7 @@ namespace mongo { virtual bool eof(); virtual bool advance(); virtual const char *getSourceName() const; - virtual intrusive_ptr<Document> getCurrent(); + virtual Document getCurrent(); virtual GetDepsReturn getDependencies(set<string>& deps) const; diff --git a/src/mongo/db/pipeline/document_source_bson_array.cpp b/src/mongo/db/pipeline/document_source_bson_array.cpp index 79bce29220a..64217cc9716 100755 --- a/src/mongo/db/pipeline/document_source_bson_array.cpp +++ b/src/mongo/db/pipeline/document_source_bson_array.cpp @@ -44,10 +44,10 @@ namespace mongo { return true; } - intrusive_ptr<Document> DocumentSourceBsonArray::getCurrent() { + Document DocumentSourceBsonArray::getCurrent() { verify(haveCurrent); BSONObj documentObj(currentElement.Obj()); - intrusive_ptr<Document> pDocument( + Document pDocument( Document::createFromBsonObj(&documentObj)); return pDocument; } diff --git a/src/mongo/db/pipeline/document_source_command_shards.cpp b/src/mongo/db/pipeline/document_source_command_shards.cpp index a0d5423b449..e1f04ae1a50 100644 --- a/src/mongo/db/pipeline/document_source_command_shards.cpp +++ b/src/mongo/db/pipeline/document_source_command_shards.cpp @@ -26,25 +26,25 @@ namespace mongo { bool DocumentSourceCommandShards::eof() { /* if we haven't even started yet, do so */ - if (!pCurrent.get()) + if (unstarted) getNextDocument(); - return (pCurrent.get() == NULL); + return !hasCurrent; } bool DocumentSourceCommandShards::advance() { DocumentSource::advance(); // check for interrupts - if (eof()) - return false; + if (unstarted) + getNextDocument(); // skip first /* advance */ getNextDocument(); - return (pCurrent.get() != NULL); + return hasCurrent; } - intrusive_ptr<Document> DocumentSourceCommandShards::getCurrent() { + Document DocumentSourceCommandShards::getCurrent() { verify(!eof()); return pCurrent; } @@ -64,6 +64,8 @@ namespace mongo { const ShardOutput& shardOutput, const intrusive_ptr<ExpressionContext> &pExpCtx): DocumentSource(pExpCtx), + unstarted(true), + hasCurrent(false), newSource(false), pBsonSource(), pCurrent(), @@ -81,11 +83,17 @@ namespace mongo { } void DocumentSourceCommandShards::getNextDocument() { + if (unstarted) { + unstarted = false; + hasCurrent = true; + } + while(true) { if (!pBsonSource.get()) { /* if there aren't any more futures, we're done */ if (iterator == listEnd) { - pCurrent.reset(); + pCurrent = Document(); + hasCurrent = false; return; } diff --git a/src/mongo/db/pipeline/document_source_cursor.cpp b/src/mongo/db/pipeline/document_source_cursor.cpp index c99504ad446..4e932069dbf 100755 --- a/src/mongo/db/pipeline/document_source_cursor.cpp +++ b/src/mongo/db/pipeline/document_source_cursor.cpp @@ -37,28 +37,25 @@ namespace mongo { bool DocumentSourceCursor::eof() { /* if we haven't gotten the first one yet, do so now */ - if (!pCurrent.get()) + if (unstarted) findNext(); - return (pCurrent.get() == NULL); + return !hasCurrent; } bool DocumentSourceCursor::advance() { DocumentSource::advance(); // check for interrupts /* if we haven't gotten the first one yet, do so now */ - if (!pCurrent.get()) + if (unstarted) findNext(); findNext(); - return (pCurrent.get() != NULL); + return hasCurrent; } - intrusive_ptr<Document> DocumentSourceCursor::getCurrent() { - /* if we haven't gotten the first one yet, do so now */ - if (!pCurrent.get()) - findNext(); - + Document DocumentSourceCursor::getCurrent() { + verify(hasCurrent); return pCurrent; } @@ -98,9 +95,11 @@ namespace mongo { } void DocumentSourceCursor::findNext() { + unstarted = false; if ( !_cursorWithContext ) { - pCurrent.reset(); + pCurrent = Document(); + hasCurrent = false; return; } @@ -135,6 +134,7 @@ namespace mongo { } pCurrent = Document::createFromBsonObj(&documentObj); + hasCurrent = true; cursor()->advance(); return; @@ -143,7 +143,8 @@ namespace mongo { // If we got here, there aren't any more documents. // The CursorWithContext (and its read lock) must be released, see SERVER-6123. dispose(); - pCurrent.reset(); + pCurrent = Document(); + hasCurrent = false; } void DocumentSourceCursor::setSource(DocumentSource *pSource) { @@ -193,7 +194,8 @@ namespace mongo { const shared_ptr<CursorWithContext>& cursorWithContext, const intrusive_ptr<ExpressionContext> &pCtx): DocumentSource(pCtx), - pCurrent(), + unstarted(true), + hasCurrent(false), _cursorWithContext( cursorWithContext ) {} diff --git a/src/mongo/db/pipeline/document_source_filter.cpp b/src/mongo/db/pipeline/document_source_filter.cpp index b9cb0a369d1..de0736beee5 100755 --- a/src/mongo/db/pipeline/document_source_filter.cpp +++ b/src/mongo/db/pipeline/document_source_filter.cpp @@ -63,10 +63,9 @@ namespace mongo { pFilter->addToBsonObj(pBuilder, filterName, false); } - bool DocumentSourceFilter::accept( - const intrusive_ptr<Document> &pDocument) const { - intrusive_ptr<const Value> pValue(pFilter->evaluate(pDocument)); - return pValue->coerceToBool(); + bool DocumentSourceFilter::accept(const Document& pDocument) const { + Value pValue(pFilter->evaluate(pDocument)); + return pValue.coerceToBool(); } intrusive_ptr<DocumentSource> DocumentSourceFilter::createFromBson( diff --git a/src/mongo/db/pipeline/document_source_filter_base.cpp b/src/mongo/db/pipeline/document_source_filter_base.cpp index 3354b3c6bc2..fd42993b23e 100755 --- a/src/mongo/db/pipeline/document_source_filter_base.cpp +++ b/src/mongo/db/pipeline/document_source_filter_base.cpp @@ -28,30 +28,27 @@ namespace mongo { } void DocumentSourceFilterBase::findNext() { - /* only do this the first time */ - if (unstarted) { - hasNext = !pSource->eof(); - unstarted = false; - } - - while(hasNext) { - boost::intrusive_ptr<Document> pDocument(pSource->getCurrent()); - hasNext = pSource->advance(); + unstarted = false; - if (accept(pDocument)) { - pCurrent = pDocument; + for(bool hasDoc = !pSource->eof(); hasDoc; hasDoc = pSource->advance()) { + pCurrent = pSource->getCurrent(); + if (accept(pCurrent)) { + pSource->advance(); // Start next call at correct position + hasCurrent = true; return; } } - pCurrent.reset(); + // Nothing matched + pCurrent = Document(); + hasCurrent = false; } bool DocumentSourceFilterBase::eof() { if (unstarted) findNext(); - return (pCurrent.get() == NULL); + return !hasCurrent; } bool DocumentSourceFilterBase::advance() { @@ -68,14 +65,11 @@ namespace mongo { */ findNext(); - return (pCurrent.get() != NULL); + return hasCurrent; } - boost::intrusive_ptr<Document> DocumentSourceFilterBase::getCurrent() { - if (unstarted) - findNext(); - - verify(pCurrent.get() != NULL); + Document DocumentSourceFilterBase::getCurrent() { + verify(hasCurrent); return pCurrent; } @@ -83,7 +77,7 @@ namespace mongo { const intrusive_ptr<ExpressionContext> &pExpCtx): DocumentSource(pExpCtx), unstarted(true), - hasNext(false), + hasCurrent(false), pCurrent() { } } diff --git a/src/mongo/db/pipeline/document_source_group.cpp b/src/mongo/db/pipeline/document_source_group.cpp index c84d73d16b6..1fca0cd9467 100755 --- a/src/mongo/db/pipeline/document_source_group.cpp +++ b/src/mongo/db/pipeline/document_source_group.cpp @@ -60,7 +60,7 @@ namespace mongo { return true; } - intrusive_ptr<Document> DocumentSourceGroup::getCurrent() { + Document DocumentSourceGroup::getCurrent() { if (!populated) populate(); @@ -72,7 +72,7 @@ namespace mongo { BSONObjBuilder insides; /* add the _id */ - pIdExpression->addToBsonObj(&insides, Document::idName.c_str(), true); + pIdExpression->addToBsonObj(&insides, "_id", true); /* add the remaining fields */ const size_t n = vFieldName.size(); @@ -173,7 +173,7 @@ namespace mongo { BSONElement groupField(groupIterator.next()); const char *pFieldName = groupField.fieldName(); - if (strcmp(pFieldName, Document::idName.c_str()) == 0) { + if (str::equals(pFieldName, "_id")) { uassert(15948, "a group's _id may only be specified once", !idSet); @@ -221,7 +221,7 @@ namespace mongo { case jstNULL: StringConstantId: // from string case above { - intrusive_ptr<const Value> pValue( + Value pValue( Value::createFromBsonElement(&groupField)); intrusive_ptr<ExpressionConstant> pConstant( ExpressionConstant::create(pValue)); @@ -313,14 +313,14 @@ namespace mongo { void DocumentSourceGroup::populate() { for(bool hasNext = !pSource->eof(); hasNext; hasNext = pSource->advance()) { - intrusive_ptr<Document> pDocument(pSource->getCurrent()); + Document pDocument(pSource->getCurrent()); /* get the _id value */ - intrusive_ptr<const Value> pId(pIdExpression->evaluate(pDocument)); + Value pId(pIdExpression->evaluate(pDocument)); /* treat Undefined the same as NULL SERVER-4674 */ - if (pId->getType() == Undefined) - pId = Value::getNull(); + if (pId.getType() == Undefined) + pId = Value(jstNULL); /* Look for the _id value in the map; if it's not there, add a @@ -334,10 +334,7 @@ namespace mongo { } else { /* insert a new group into the map */ - groups.insert(it, - pair<intrusive_ptr<const Value>, - vector<intrusive_ptr<Accumulator> > >( - pId, vector<intrusive_ptr<Accumulator> >())); + groups[pId] = vector<intrusive_ptr<Accumulator> >(); /* find the accumulator vector (the map value) */ it = groups.find(pId); @@ -370,23 +367,23 @@ namespace mongo { populated = true; } - intrusive_ptr<Document> DocumentSourceGroup::makeDocument( + Document DocumentSourceGroup::makeDocument( const GroupsType::iterator &rIter) { vector<intrusive_ptr<Accumulator> > *pGroup = &rIter->second; const size_t n = vFieldName.size(); - intrusive_ptr<Document> pResult(Document::create(1 + n)); + MutableDocument out (1 + n); /* add the _id field */ - pResult->addField(Document::idName, rIter->first); + out.addField("_id", rIter->first); /* add the rest of the fields */ for(size_t i = 0; i < n; ++i) { - intrusive_ptr<const Value> pValue((*pGroup)[i]->getValue()); - if (pValue->getType() != Undefined) - pResult->addField(vFieldName[i], pValue); + Value pValue((*pGroup)[i]->getValue()); + if (pValue.getType() != Undefined) + out.addField(vFieldName[i], pValue); } - return pResult; + return out.freeze(); } intrusive_ptr<DocumentSource> DocumentSourceGroup::getShardSource() { @@ -399,8 +396,7 @@ namespace mongo { intrusive_ptr<DocumentSourceGroup> pMerger(DocumentSourceGroup::create(pMergerExpCtx)); /* the merger will use the same grouping key */ - pMerger->setIdExpression(ExpressionFieldPath::create( - Document::idName.c_str())); + pMerger->setIdExpression(ExpressionFieldPath::create("_id")); const size_t n = vFieldName.size(); for(size_t i = 0; i < n; ++i) { diff --git a/src/mongo/db/pipeline/document_source_limit.cpp b/src/mongo/db/pipeline/document_source_limit.cpp index 999d80f0368..86dd4f22a5c 100644 --- a/src/mongo/db/pipeline/document_source_limit.cpp +++ b/src/mongo/db/pipeline/document_source_limit.cpp @@ -64,20 +64,16 @@ namespace mongo { ++count; if (count >= limit) { - - pCurrent.reset(); - // This is required for the DocumentSourceCursor to release its read lock, see // SERVER-6123. pSource->dispose(); return false; } - pCurrent = pSource->getCurrent(); return pSource->advance(); } - intrusive_ptr<Document> DocumentSourceLimit::getCurrent() { + Document DocumentSourceLimit::getCurrent() { return pSource->getCurrent(); } diff --git a/src/mongo/db/pipeline/document_source_match.cpp b/src/mongo/db/pipeline/document_source_match.cpp index 03006c1e043..b8900bf19c8 100644 --- a/src/mongo/db/pipeline/document_source_match.cpp +++ b/src/mongo/db/pipeline/document_source_match.cpp @@ -40,8 +40,7 @@ namespace mongo { pBuilder->append(matchName, *pQuery); } - bool DocumentSourceMatch::accept( - const intrusive_ptr<Document> &pDocument) const { + bool DocumentSourceMatch::accept(const Document& pDocument) const { /* The matcher only takes BSON documents, so we have to make one. diff --git a/src/mongo/db/pipeline/document_source_out.cpp b/src/mongo/db/pipeline/document_source_out.cpp index c19e066efe6..bbdaacfa053 100755 --- a/src/mongo/db/pipeline/document_source_out.cpp +++ b/src/mongo/db/pipeline/document_source_out.cpp @@ -40,7 +40,7 @@ namespace mongo { return pSource->advance(); } - boost::intrusive_ptr<Document> DocumentSourceOut::getCurrent() { + Document DocumentSourceOut::getCurrent() { return pSource->getCurrent(); } diff --git a/src/mongo/db/pipeline/document_source_project.cpp b/src/mongo/db/pipeline/document_source_project.cpp index bbd969fa997..17fcb98c42d 100644 --- a/src/mongo/db/pipeline/document_source_project.cpp +++ b/src/mongo/db/pipeline/document_source_project.cpp @@ -48,13 +48,12 @@ namespace mongo { return pSource->advance(); } - intrusive_ptr<Document> DocumentSourceProject::getCurrent() { - intrusive_ptr<Document> pInDocument(pSource->getCurrent()); - verify(pInDocument); + Document DocumentSourceProject::getCurrent() { + Document pInDocument(pSource->getCurrent()); /* create the result document */ const size_t sizeHint = pEO->getSizeHint(); - intrusive_ptr<Document> pResultDocument(Document::create(sizeHint)); + MutableDocument out (sizeHint); /* Use the ExpressionObject to create the base result. @@ -62,7 +61,7 @@ namespace mongo { If we're excluding fields at the top level, leave out the _id if it is found, because we took care of it above. */ - pEO->addToDocument(pResultDocument, pInDocument, /*root=*/pInDocument); + pEO->addToDocument(out, pInDocument, /*root=*/pInDocument); #if defined(_DEBUG) if (!_simpleProjection.getSpec().isEmpty()) { @@ -73,7 +72,7 @@ namespace mongo { BSONObj input = inputBuilder.done(); BSONObjBuilder outputBuilder; - pResultDocument->toBson(&outputBuilder); + out.peek().toBson(&outputBuilder); BSONObj output = outputBuilder.done(); BSONObj projected = _simpleProjection.transform(input); @@ -88,7 +87,7 @@ namespace mongo { } #endif - return pResultDocument; + return out.freeze(); } void DocumentSourceProject::optimize() { diff --git a/src/mongo/db/pipeline/document_source_skip.cpp b/src/mongo/db/pipeline/document_source_skip.cpp index 5024b817a90..833de29061a 100644 --- a/src/mongo/db/pipeline/document_source_skip.cpp +++ b/src/mongo/db/pipeline/document_source_skip.cpp @@ -87,7 +87,7 @@ namespace mongo { return pSource->advance(); } - intrusive_ptr<Document> DocumentSourceSkip::getCurrent() { + Document DocumentSourceSkip::getCurrent() { skipper(); return pCurrent; } diff --git a/src/mongo/db/pipeline/document_source_sort.cpp b/src/mongo/db/pipeline/document_source_sort.cpp index 4b911894cdb..8739874ccde 100755 --- a/src/mongo/db/pipeline/document_source_sort.cpp +++ b/src/mongo/db/pipeline/document_source_sort.cpp @@ -61,7 +61,7 @@ namespace mongo { return true; } - intrusive_ptr<Document> DocumentSourceSort::getCurrent() { + Document DocumentSourceSort::getCurrent() { if (!populated) populate(); @@ -164,7 +164,7 @@ namespace mongo { /* pull everything from the underlying source */ for(bool hasNext = !pSource->eof(); hasNext; hasNext = pSource->advance()) { - intrusive_ptr<Document> pDocument(pSource->getCurrent()); + Document pDocument(pSource->getCurrent()); documents.push_back(pDocument); dmm.addToTotal(pDocument->getApproximateSize()); @@ -182,8 +182,7 @@ namespace mongo { populated = true; } - int DocumentSourceSort::compare( - const intrusive_ptr<Document> &pL, const intrusive_ptr<Document> &pR) { + int DocumentSourceSort::compare(const Document& pL, const Document& pR) { /* populate() already checked that there is a non-empty sort key, @@ -196,8 +195,8 @@ namespace mongo { for(size_t i = 0; i < n; ++i) { /* evaluate the sort keys */ ExpressionFieldPath *pE = vSortKey[i].get(); - intrusive_ptr<const Value> pLeft(pE->evaluate(pL)); - intrusive_ptr<const Value> pRight(pE->evaluate(pR)); + Value pLeft(pE->evaluate(pL)); + Value pRight(pE->evaluate(pR)); /* Compare the two values; if they differ, return. If they are diff --git a/src/mongo/db/pipeline/document_source_unwind.cpp b/src/mongo/db/pipeline/document_source_unwind.cpp index 848251b350a..4086372133c 100755 --- a/src/mongo/db/pipeline/document_source_unwind.cpp +++ b/src/mongo/db/pipeline/document_source_unwind.cpp @@ -30,63 +30,53 @@ namespace mongo { /** @param unwindPath is the field path to the array to unwind. */ Unwinder(const FieldPath& unwindPath); /** Reset the unwinder to unwind a new document. */ - void resetDocument(const intrusive_ptr<Document>& document); + void resetDocument(const Document& document); /** @return true if done unwinding the last document passed to resetDocument(). */ bool eof() const; - /** - * Try to advance to the next document unwound from the document passed to resetDocument(). - * @return true if advanced to a new unwound document, but false if done advancing. - */ + /** Try to advance to the next document unwound from the document passed to resetDocument(). */ void advance(); /** * @return the current document unwound from the document provided to resetDocument(), using * the current value in the array located at the provided unwindPath. But @return - * intrusive_ptr<Document>() if resetDocument() has not been called or the results to unwind + * Document() if resetDocument() has not been called or the results to unwind * have been exhausted. */ - intrusive_ptr<Document> getCurrent() const; + Document getCurrent(); private: - /** - * @return the value at the unwind path, otherwise an empty pointer if no such value - * exists. The _unwindPathFieldIndexes attribute will be set as the field path is traversed - * to find the value to unwind. - */ - intrusive_ptr<const Value> extractUnwindValue(); // Path to the array to unwind. - FieldPath _unwindPath; - // The souce document to unwind. - intrusive_ptr<Document> _document; + const FieldPath _unwindPath; + + Value _inputArray; + MutableDocument _output; + // Document indexes of the field path components. - vector<int> _unwindPathFieldIndexes; - // Iterator over the array within _document to unwind. - intrusive_ptr<ValueIterator> _unwindArrayIterator; - // The last value returned from _unwindArrayIterator. - intrusive_ptr<const Value> _unwindArrayIteratorCurrent; + vector<Position> _unwindPathFieldIndexes; + // Index into the _inputArray to return next. + size_t _index; }; DocumentSourceUnwind::Unwinder::Unwinder(const FieldPath& unwindPath): _unwindPath(unwindPath) { } - void DocumentSourceUnwind::Unwinder::resetDocument(const intrusive_ptr<Document>& document) { - verify( document ); + void DocumentSourceUnwind::Unwinder::resetDocument(const Document& document) { // Reset document specific attributes. - _document = document; + _inputArray = Value(); + _output.reset(document); _unwindPathFieldIndexes.clear(); - _unwindArrayIterator.reset(); - _unwindArrayIteratorCurrent.reset(); + _index = 0; - intrusive_ptr<const Value> pathValue = extractUnwindValue(); // sets _unwindPathFieldIndexes - if (!pathValue) { + Value pathValue = document.getNestedField(_unwindPath, &_unwindPathFieldIndexes); + if (pathValue.missing()) { // The path does not exist. return; } bool nothingToEmit = - (pathValue->getType() == jstNULL) || - (pathValue->getType() == Undefined) || - ((pathValue->getType() == Array) && (pathValue->getArrayLength() == 0)); + (pathValue.getType() == jstNULL) || + (pathValue.getType() == Undefined) || + ((pathValue.getType() == Array) && (pathValue.getArrayLength() == 0)); if (nothingToEmit) { // The target field exists, but there are no values to unwind. @@ -96,100 +86,39 @@ namespace mongo { // The target field must be an array to unwind. uassert(15978, str::stream() << (string)DocumentSourceUnwind::unwindName << ": value at end of field path must be an array", - pathValue->getType() == Array); + pathValue.getType() == Array); - // Start the iterator used to unwind the array. - _unwindArrayIterator = pathValue->getArray(); - verify(_unwindArrayIterator->more()); // Checked above that the array is nonempty. - // Pull the first value out of the iterator. - _unwindArrayIteratorCurrent = _unwindArrayIterator->next(); + _inputArray = pathValue; + verify(!eof()); // Checked above that the array is nonempty. } bool DocumentSourceUnwind::Unwinder::eof() const { - return !_unwindArrayIteratorCurrent; + return (_inputArray.missing()) // getType asserts if missing + || (_inputArray.getType() != Array) + || (_index == _inputArray.getArrayLength()); } void DocumentSourceUnwind::Unwinder::advance() { - if (!_unwindArrayIterator) { - // resetDocument() has not been called or the supplied document had no results to - // unwind. - _unwindArrayIteratorCurrent = NULL; - } - else if (!_unwindArrayIterator->more()) { - // There are no more results to unwind. - _unwindArrayIteratorCurrent = NULL; - } - else { - _unwindArrayIteratorCurrent = _unwindArrayIterator->next(); + if (!eof()) { // don't advance past end() + _index++; } } - intrusive_ptr<Document> DocumentSourceUnwind::Unwinder::getCurrent() const { - if (!_unwindArrayIteratorCurrent) { - return NULL; - } - - // Clone all the documents along the field path so that the end values are not shared across - // documents that have come out of this pipeline operator. This is a partial deep clone. - // Because the value at the end will be replaced, everything along the path leading to that - // will be replaced in order not to share that change with any other clones (or the - // original). - - intrusive_ptr<Document> clone(_document->clone()); - intrusive_ptr<Document> current(clone); - const size_t n = _unwindPathFieldIndexes.size(); - verify(n); - for(size_t i = 0; i < n; ++i) { - const size_t fi = _unwindPathFieldIndexes[i]; - Document::FieldPair fp(current->getField(fi)); - if (i + 1 < n) { - // For every object in the path but the last, clone it and continue on down. - intrusive_ptr<Document> next = fp.second->getDocument()->clone(); - current->setField(fi, fp.first, Value::createDocument(next)); - current = next; - } - else { - // In the last nested document, subsitute the current unwound value. - current->setField(fi, fp.first, _unwindArrayIteratorCurrent); - } + Document DocumentSourceUnwind::Unwinder::getCurrent() { + if (eof()) { + return Document(); } - return clone; - } + // If needed, this will automatically clone all the documents along the + // field path so that the end values are not shared across documents + // that have come out of this pipeline operator. This is a partial deep + // clone. Because the value at the end will be replaced, everything + // along the path leading to that will be replaced in order not to share + // that change with any other clones (or the original). - intrusive_ptr<const Value> DocumentSourceUnwind::Unwinder::extractUnwindValue() { + _output.setNestedField(_unwindPathFieldIndexes, _inputArray[_index]); - intrusive_ptr<Document> current = _document; - intrusive_ptr<const Value> pathValue; - const size_t pathLength = _unwindPath.getPathLength(); - for(size_t i = 0; i < pathLength; ++i) { - - size_t idx = current->getFieldIndex(_unwindPath.getFieldName(i)); - - if (idx == current->getFieldCount()) { - // The target field is missing. - return NULL; - } - - // Record the indexes of the fields down the field path in order to quickly replace them - // as the documents along the field path are cloned. - _unwindPathFieldIndexes.push_back(idx); - - pathValue = current->getField(idx).second; - - if (i < pathLength - 1) { - - if (pathValue->getType() != Object) { - // The next field in the path cannot exist (inside a non object). - return NULL; - } - - // Move down the object tree. - current = pathValue->getDocument(); - } - } - - return pathValue; + return _output.peek(); } const char DocumentSourceUnwind::unwindName[] = "$unwind"; @@ -248,8 +177,8 @@ namespace mongo { return !_unwinder->eof(); } - intrusive_ptr<Document> DocumentSourceUnwind::getCurrent() { - lazyInit(); + Document DocumentSourceUnwind::getCurrent() { + verify(!eof()); return _unwinder->getCurrent(); } diff --git a/src/mongo/db/pipeline/expression.cpp b/src/mongo/db/pipeline/expression.cpp index 912c251bc76..bf9586ad551 100644 --- a/src/mongo/db/pipeline/expression.cpp +++ b/src/mongo/db/pipeline/expression.cpp @@ -326,8 +326,7 @@ namespace mongo { return pExpression; } - intrusive_ptr<const Value> ExpressionAdd::evaluate( - const intrusive_ptr<Document> &pDocument) const { + Value ExpressionAdd::evaluate(const Document& pDocument) const { /* We'll try to return the narrowest possible result value. To do that @@ -341,17 +340,17 @@ namespace mongo { const size_t n = vpOperand.size(); for (size_t i = 0; i < n; ++i) { - intrusive_ptr<const Value> pValue(vpOperand[i]->evaluate(pDocument)); + Value pValue(vpOperand[i]->evaluate(pDocument)); - BSONType valueType = pValue->getType(); + BSONType valueType = pValue.getType(); uassert(16415, "$add does not support dates", valueType != Date); uassert(16416, "$add does not support strings", valueType != String); - totalType = Value::getWidestNumeric(totalType, pValue->getType()); - doubleTotal += pValue->coerceToDouble(); - longTotal += pValue->coerceToLong(); + totalType = Value::getWidestNumeric(totalType, pValue.getType()); + doubleTotal += pValue.coerceToDouble(); + longTotal += pValue.coerceToLong(); } if (totalType == NumberLong) { @@ -417,10 +416,10 @@ namespace mongo { Evaluate and coerce the last argument to a boolean. If it's false, then we can replace this entire expression. */ - bool last = pLast->evaluate(intrusive_ptr<Document>())->coerceToBool(); + bool last = pLast->evaluate(Document()).coerceToBool(); if (!last) { intrusive_ptr<ExpressionConstant> pFinal( - ExpressionConstant::create(Value::getFalse())); + ExpressionConstant::create(Value(false))); return pFinal; } @@ -447,16 +446,15 @@ namespace mongo { return pE; } - intrusive_ptr<const Value> ExpressionAnd::evaluate( - const intrusive_ptr<Document> &pDocument) const { + Value ExpressionAnd::evaluate(const Document& pDocument) const { const size_t n = vpOperand.size(); for(size_t i = 0; i < n; ++i) { - intrusive_ptr<const Value> pValue(vpOperand[i]->evaluate(pDocument)); - if (!pValue->coerceToBool()) - return Value::getFalse(); + Value pValue(vpOperand[i]->evaluate(pDocument)); + if (!pValue.coerceToBool()) + return Value(false); } - return Value::getTrue(); + return Value(true); } const char *ExpressionAnd::getOpName() const { @@ -517,19 +515,17 @@ namespace mongo { pExpression->addDependencies(deps); } - intrusive_ptr<const Value> ExpressionCoerceToBool::evaluate( - const intrusive_ptr<Document> &pDocument) const { - - intrusive_ptr<const Value> pResult(pExpression->evaluate(pDocument)); - bool b = pResult->coerceToBool(); + Value ExpressionCoerceToBool::evaluate(const Document& pDocument) const { + Value pResult(pExpression->evaluate(pDocument)); + bool b = pResult.coerceToBool(); if (b) - return Value::getTrue(); - return Value::getFalse(); + return Value(true); + return Value(false); } - void ExpressionCoerceToBool::addToBsonObj( - BSONObjBuilder *pBuilder, const std::string& fieldName, - bool requireExpression) const { + void ExpressionCoerceToBool::addToBsonObj(BSONObjBuilder *pBuilder, + StringData fieldName, + bool requireExpression) const { // Serializing as an $and expression which will become a CoerceToBool BSONObjBuilder sub (pBuilder->subobjStart(fieldName)); BSONArrayBuilder arr (sub.subarrayStart("$and")); @@ -681,33 +677,27 @@ namespace mongo { pFieldPath, newOp, pConstant->getValue()); } - intrusive_ptr<const Value> ExpressionCompare::evaluate( - const intrusive_ptr<Document> &pDocument) const { + Value ExpressionCompare::evaluate(const Document& pDocument) const { checkArgCount(2); - intrusive_ptr<const Value> pLeft(vpOperand[0]->evaluate(pDocument)); - intrusive_ptr<const Value> pRight(vpOperand[1]->evaluate(pDocument)); + Value pLeft(vpOperand[0]->evaluate(pDocument)); + Value pRight(vpOperand[1]->evaluate(pDocument)); int cmp = signum(Value::compare(pLeft, pRight)); if (cmpOp == CMP) { switch(cmp) { case -1: - return Value::getMinusOne(); case 0: - return Value::getZero(); case 1: - return Value::getOne(); + return Value(cmp); default: verify(false); // CW TODO internal error - return Value::getNull(); } } bool returnValue = cmpLookup[cmpOp].truthValue[cmp + 1]; - if (returnValue) - return Value::getTrue(); - return Value::getFalse(); + return Value(returnValue); } const char *ExpressionCompare::getOpName() const { @@ -734,11 +724,10 @@ namespace mongo { ExpressionNary::addOperand(pExpression); } - intrusive_ptr<const Value> ExpressionCond::evaluate( - const intrusive_ptr<Document> &pDocument) const { + Value ExpressionCond::evaluate(const Document& pDocument) const { checkArgCount(3); - intrusive_ptr<const Value> pCond(vpOperand[0]->evaluate(pDocument)); - int idx = pCond->coerceToBool() ? 1 : 2; + Value pCond(vpOperand[0]->evaluate(pDocument)); + int idx = pCond.coerceToBool() ? 1 : 2; return vpOperand[idx]->evaluate(pDocument); } @@ -762,16 +751,12 @@ namespace mongo { pValue(Value::createFromBsonElement(pBsonElement)) { } - intrusive_ptr<ExpressionConstant> ExpressionConstant::create( - const intrusive_ptr<const Value> &pValue) { + intrusive_ptr<ExpressionConstant> ExpressionConstant::create(const Value& pValue) { intrusive_ptr<ExpressionConstant> pEC(new ExpressionConstant(pValue)); return pEC; } - ExpressionConstant::ExpressionConstant( - const intrusive_ptr<const Value> &pTheValue): - pValue(pTheValue) { - } + ExpressionConstant::ExpressionConstant(const Value& pTheValue): pValue(pTheValue) {} intrusive_ptr<Expression> ExpressionConstant::optimize() { @@ -783,14 +768,13 @@ namespace mongo { /* nothing to do */ } - intrusive_ptr<const Value> ExpressionConstant::evaluate( - const intrusive_ptr<Document> &pDocument) const { + Value ExpressionConstant::evaluate(const Document& pDocument) const { return pValue; } - void ExpressionConstant::addToBsonObj( - BSONObjBuilder *pBuilder, const std::string& fieldName, - bool requireExpression) const { + void ExpressionConstant::addToBsonObj(BSONObjBuilder *pBuilder, + StringData fieldName, + bool requireExpression) const { /* If we don't need an expression, but can use a naked scalar, do the regular thing. @@ -816,19 +800,19 @@ namespace mongo { which will look like an inclusion rather than a computed field. */ if (!requireExpression) { - pValue->addToBsonObj(pBuilder, fieldName); + pValue.addToBsonObj(pBuilder, fieldName); return; } // We require an expression, so build one here, and use that. BSONObjBuilder constBuilder (pBuilder->subobjStart(fieldName)); - pValue->addToBsonObj(&constBuilder, getOpName()); + pValue.addToBsonObj(&constBuilder, getOpName()); constBuilder.done(); } void ExpressionConstant::addToBsonArray( BSONArrayBuilder *pBuilder) const { - pValue->addToBsonArray(pBuilder); + pValue.addToBsonArray(pBuilder); } const char *ExpressionConstant::getOpName() const { @@ -855,11 +839,10 @@ namespace mongo { ExpressionNary::addOperand(pExpression); } - intrusive_ptr<const Value> ExpressionDayOfMonth::evaluate( - const intrusive_ptr<Document> &pDocument) const { + Value ExpressionDayOfMonth::evaluate(const Document& pDocument) const { checkArgCount(1); - intrusive_ptr<const Value> pDate(vpOperand[0]->evaluate(pDocument)); - tm date = pDate->coerceToTm(); + Value pDate(vpOperand[0]->evaluate(pDocument)); + tm date = pDate.coerceToTm(); return Value::createInt(date.tm_mday); } @@ -886,11 +869,10 @@ namespace mongo { ExpressionNary::addOperand(pExpression); } - intrusive_ptr<const Value> ExpressionDayOfWeek::evaluate( - const intrusive_ptr<Document> &pDocument) const { + Value ExpressionDayOfWeek::evaluate(const Document& pDocument) const { checkArgCount(1); - intrusive_ptr<const Value> pDate(vpOperand[0]->evaluate(pDocument)); - tm date = pDate->coerceToTm(); + Value pDate(vpOperand[0]->evaluate(pDocument)); + tm date = pDate.coerceToTm(); return Value::createInt(date.tm_wday+1); // MySQL uses 1-7 tm uses 0-6 } @@ -917,11 +899,10 @@ namespace mongo { ExpressionNary::addOperand(pExpression); } - intrusive_ptr<const Value> ExpressionDayOfYear::evaluate( - const intrusive_ptr<Document> &pDocument) const { + Value ExpressionDayOfYear::evaluate(const Document& pDocument) const { checkArgCount(1); - intrusive_ptr<const Value> pDate(vpOperand[0]->evaluate(pDocument)); - tm date = pDate->coerceToTm(); + Value pDate(vpOperand[0]->evaluate(pDocument)); + tm date = pDate.coerceToTm(); return Value::createInt(date.tm_yday+1); // MySQL uses 1-366 tm uses 0-365 } @@ -949,21 +930,20 @@ namespace mongo { ExpressionNary::addOperand(pExpression); } - intrusive_ptr<const Value> ExpressionDivide::evaluate( - const intrusive_ptr<Document> &pDocument) const { + Value ExpressionDivide::evaluate(const Document& pDocument) const { checkArgCount(2); - intrusive_ptr<const Value> pLeft(vpOperand[0]->evaluate(pDocument)); - intrusive_ptr<const Value> pRight(vpOperand[1]->evaluate(pDocument)); + Value pLeft(vpOperand[0]->evaluate(pDocument)); + Value pRight(vpOperand[1]->evaluate(pDocument)); uassert(16373, "$divide does not support dates", - pLeft->getType() != Date && pRight->getType() != Date); + pLeft.getType() != Date && pRight.getType() != Date); - double right = pRight->coerceToDouble(); + double right = pRight.coerceToDouble(); if (right == 0) - return Value::getUndefined(); + return Value(Undefined); - double left = pLeft->coerceToDouble(); + double left = pLeft.coerceToDouble(); return Value::createDouble(left / right); } @@ -1037,9 +1017,9 @@ namespace mongo { } void ExpressionObject::addToDocument( - const intrusive_ptr<Document> &pResult, - const intrusive_ptr<Document> &pDocument, - const intrusive_ptr<Document> &rootDoc + MutableDocument& out, + const Document& pDocument, + const Document& rootDoc ) const { const bool atRoot = (pDocument == rootDoc); @@ -1053,14 +1033,16 @@ namespace mongo { while(fields.more()) { Document::FieldPair field (fields.next()); - ExpressionMap::const_iterator exprIter = _expressions.find(field.first); + // TODO don't make a new string here + const string fieldName (field.first.data(), field.first.size()); + ExpressionMap::const_iterator exprIter = _expressions.find(fieldName); // This field is not supposed to be in the output (unless it is _id) if (exprIter == end) { - if (!_excludeId && atRoot && field.first == Document::idName) { + if (!_excludeId && atRoot && str::equals(field.first.data(), "_id")) { // _id from the root doc is always included (until exclusion is supported) // not updating doneFields since "_id" isn't in _expressions - pResult->addField(field.first, field.second); + out.addField(field.first, field.second); } continue; } @@ -1072,19 +1054,19 @@ namespace mongo { if (!expr) { // This means pull the matching field from the input document - pResult->addField(field.first, field.second); + out.addField(field.first, field.second); continue; } ExpressionObject* exprObj = dynamic_cast<ExpressionObject*>(expr); - BSONType valueType = field.second->getType(); + BSONType valueType = field.second.getType(); if ((valueType != Object && valueType != Array) || !exprObj ) { // This expression replace the whole field - intrusive_ptr<const Value> pValue(expr->evaluate(rootDoc)); + Value pValue(expr->evaluate(rootDoc)); // don't add field if nothing was found in the subobject - if (exprObj && pValue->getDocument()->getFieldCount() == 0) + if (exprObj && pValue.getDocument()->getFieldCount() == 0) continue; /* @@ -1093,8 +1075,8 @@ namespace mongo { force the appearnance of non-existent fields. */ // TODO make missing distinct from Undefined - if (pValue->getType() != Undefined) - pResult->addField(field.first, pValue); + if (pValue.getType() != Undefined) + out.addField(field.first, pValue); continue; @@ -1106,11 +1088,9 @@ namespace mongo { add it to the result. */ if (valueType == Object) { - intrusive_ptr<Document> doc = Document::create(exprObj->getSizeHint()); - exprObj->addToDocument(doc, - field.second->getDocument(), - rootDoc); - pResult->addField(field.first, Value::createDocument(doc)); + MutableDocument sub (exprObj->getSizeHint()); + exprObj->addToDocument(sub, field.second.getDocument(), rootDoc); + out.addField(field.first, Value(sub.freeze())); } else if (valueType == Array) { /* @@ -1118,24 +1098,19 @@ namespace mongo { but to each array element. Then, add the array of results to the current document. */ - vector<intrusive_ptr<const Value> > result; - intrusive_ptr<ValueIterator> pVI(field.second->getArray()); - while(pVI->more()) { - intrusive_ptr<const Value> next = pVI->next(); - + vector<Value> result; + const vector<Value>& input = field.second.getArray(); + for (size_t i=0; i < input.size(); i++) { // can't look for a subfield in a non-object value. - if (next->getType() != Object) + if (input[i].getType() != Object) continue; - intrusive_ptr<Document> doc = Document::create(exprObj->getSizeHint()); - exprObj->addToDocument(doc, - next->getDocument(), - rootDoc); - result.push_back(Value::createDocument(doc)); + MutableDocument doc (exprObj->getSizeHint()); + exprObj->addToDocument(doc, input[i].getDocument(), rootDoc); + result.push_back(Value(doc.freeze())); } - pResult->addField(field.first, - Value::createArray(result)); + out.addField(field.first, Value(result)); } else { verify( false ); @@ -1158,23 +1133,23 @@ namespace mongo { if (!it->second) continue; - intrusive_ptr<const Value> pValue(it->second->evaluate(rootDoc)); + Value pValue(it->second->evaluate(rootDoc)); /* Don't add non-existent values (note: different from NULL); this is consistent with existing selection syntax which doesn't force the appearnance of non-existent fields. */ - if (pValue->getType() == Undefined) + if (pValue.getType() == Undefined) continue; // don't add field if nothing was found in the subobject if (dynamic_cast<ExpressionObject*>(it->second.get()) - && pValue->getDocument()->getFieldCount() == 0) + && pValue.getDocument()->getFieldCount() == 0) continue; - pResult->addField(fieldName, pValue); + out.addField(fieldName, pValue); } } @@ -1183,20 +1158,17 @@ namespace mongo { return _expressions.size() + (_excludeId ? 0 : 1); } - intrusive_ptr<Document> ExpressionObject::evaluateDocument( - const intrusive_ptr<Document> &pDocument) const { + Document ExpressionObject::evaluateDocument(const Document& pDocument) const { /* create and populate the result */ - intrusive_ptr<Document> pResult( - Document::create(getSizeHint())); + MutableDocument out (getSizeHint()); - addToDocument(pResult, - Document::create(), // No inclusion field matching. + addToDocument(out, + Document(), // No inclusion field matching. pDocument); - return pResult; + return out.freeze(); } - intrusive_ptr<const Value> ExpressionObject::evaluate( - const intrusive_ptr<Document> &pDocument) const { + Value ExpressionObject::evaluate(const Document& pDocument) const { return Value::createDocument(evaluateDocument(pDocument)); } @@ -1277,9 +1249,9 @@ namespace mongo { } } - void ExpressionObject::addToBsonObj( - BSONObjBuilder *pBuilder, const std::string& fieldName, - bool requireExpression) const { + void ExpressionObject::addToBsonObj(BSONObjBuilder *pBuilder, + StringData fieldName, + bool requireExpression) const { BSONObjBuilder objBuilder (pBuilder->subobjStart(fieldName)); documentToBson(&objBuilder, requireExpression); @@ -1325,16 +1297,16 @@ namespace mongo { deps.insert(fieldPath.getPath(false)); } - intrusive_ptr<const Value> ExpressionFieldPath::evaluatePath( - size_t index, const size_t pathLength, - intrusive_ptr<Document> pDocument) const { - intrusive_ptr<const Value> pValue; /* the return value */ + Value ExpressionFieldPath::evaluatePath(size_t index, + size_t pathLength, + Document pDocument) const { + Value pValue; /* the return value */ pValue = pDocument->getValue(fieldPath.getFieldName(index)); /* if the field doesn't exist, quit with an undefined value */ - if (!pValue.get()) - return Value::getUndefined(); + if (pValue.missing()) + return Value(Undefined); /* if we've hit the end of the path, stop */ ++index; @@ -1344,13 +1316,13 @@ namespace mongo { /* We're diving deeper. If the value was null, return null. */ - BSONType type = pValue->getType(); + BSONType type = pValue.getType(); if ((type == Undefined) || (type == jstNULL)) - return Value::getUndefined(); + return Value(Undefined); if (type == Object) { /* extract from the next level down */ - return evaluatePath(index, pathLength, pValue->getDocument()); + return evaluatePath(index, pathLength, pValue.getDocument()); } if (type == Array) { @@ -1358,13 +1330,13 @@ namespace mongo { We're going to repeat this for each member of the array, building up a new array as we go. */ - vector<intrusive_ptr<const Value> > result; - intrusive_ptr<ValueIterator> pIter(pValue->getArray()); - while(pIter->more()) { - intrusive_ptr<const Value> pItem(pIter->next()); - BSONType iType = pItem->getType(); + vector<Value> result; + const vector<Value>& input = pValue.getArray(); + for (size_t i=0; i < input.size(); i++) { + const Value& item = input[i]; + BSONType iType = item.getType(); if ((iType == Undefined) || (iType == jstNULL)) { - result.push_back(pItem); + result.push_back(item); continue; } @@ -1374,25 +1346,23 @@ namespace mongo { fieldPath.getPath(false) << "' is not an object, and cannot be navigated", iType == Object); - intrusive_ptr<const Value> itemResult( - evaluatePath(index, pathLength, pItem->getDocument())); - result.push_back(itemResult); + + result.push_back(evaluatePath(index, pathLength, item.getDocument())); } return Value::createArray(result); } // subdocument field does not exist, return undefined - return Value::getUndefined(); + return Value(Undefined); } - intrusive_ptr<const Value> ExpressionFieldPath::evaluate( - const intrusive_ptr<Document> &pDocument) const { + Value ExpressionFieldPath::evaluate(const Document& pDocument) const { return evaluatePath(0, fieldPath.getPathLength(), pDocument); } - void ExpressionFieldPath::addToBsonObj( - BSONObjBuilder *pBuilder, const std::string& fieldName, - bool requireExpression) const { + void ExpressionFieldPath::addToBsonObj(BSONObjBuilder *pBuilder, + StringData fieldName, + bool requireExpression) const { pBuilder->append(fieldName, fieldPath.getPath(true)); } @@ -1409,15 +1379,15 @@ namespace mongo { intrusive_ptr<Expression> ExpressionFieldRange::optimize() { /* if there is no range to match, this will never evaluate true */ if (!pRange.get()) - return ExpressionConstant::create(Value::getFalse()); + return ExpressionConstant::create(Value(false)); /* If we ended up with a double un-ended range, anything matches. I don't know how that can happen, given intersect()'s interface, but here it is, just in case. */ - if (!pRange->pBottom.get() && !pRange->pTop.get()) - return ExpressionConstant::create(Value::getTrue()); + if (pRange->pBottom.missing() && pRange->pTop.missing()) + return ExpressionConstant::create(Value(true)); /* In all other cases, we have to test candidate values. The @@ -1431,20 +1401,19 @@ namespace mongo { pFieldPath->addDependencies(deps); } - intrusive_ptr<const Value> ExpressionFieldRange::evaluate( - const intrusive_ptr<Document> &pDocument) const { + Value ExpressionFieldRange::evaluate(const Document& pDocument) const { /* if there's no range, there can't be a match */ if (!pRange.get()) - return Value::getFalse(); + return Value(false); /* get the value of the specified field */ - intrusive_ptr<const Value> pValue(pFieldPath->evaluate(pDocument)); + Value pValue(pFieldPath->evaluate(pDocument)); /* see if it fits within any of the ranges */ if (pRange->contains(pValue)) - return Value::getTrue(); + return Value(true); - return Value::getFalse(); + return Value(false); } void ExpressionFieldRange::addToBson(Builder *pBuilder) const { @@ -1454,7 +1423,7 @@ namespace mongo { return; } - if (!pRange->pTop.get() && !pRange->pBottom.get()) { + if (pRange->pTop.missing() && pRange->pBottom.missing()) { /* any value will satisfy this predicate */ pBuilder->append(true); return; @@ -1463,10 +1432,10 @@ namespace mongo { // FIXME Append constant values using the $const operator. SERVER-6769 // FIXME This checks pointer equality not value equality. - if (pRange->pTop.get() == pRange->pBottom.get()) { + if (pRange->pTop == pRange->pBottom) { BSONArrayBuilder operands; pFieldPath->addToBsonArray(&operands); - pRange->pTop->addToBsonArray(&operands); + pRange->pTop.addToBsonArray(&operands); BSONObjBuilder equals; equals.append("$eq", operands.arr()); @@ -1475,30 +1444,30 @@ namespace mongo { } BSONObjBuilder leftOperator; - if (pRange->pBottom.get()) { + if (!pRange->pBottom.missing()) { BSONArrayBuilder leftOperands; pFieldPath->addToBsonArray(&leftOperands); - pRange->pBottom->addToBsonArray(&leftOperands); + pRange->pBottom.addToBsonArray(&leftOperands); leftOperator.append( (pRange->bottomOpen ? "$gt" : "$gte"), leftOperands.arr()); - if (!pRange->pTop.get()) { + if (pRange->pTop.missing()) { pBuilder->append(&leftOperator); return; } } BSONObjBuilder rightOperator; - if (pRange->pTop.get()) { + if (!pRange->pTop.missing()) { BSONArrayBuilder rightOperands; pFieldPath->addToBsonArray(&rightOperands); - pRange->pTop->addToBsonArray(&rightOperands); + pRange->pTop.addToBsonArray(&rightOperands); rightOperator.append( (pRange->topOpen ? "$lt" : "$lte"), rightOperands.arr()); - if (!pRange->pBottom.get()) { + if (pRange->pBottom.missing()) { pBuilder->append(&rightOperator); return; } @@ -1512,9 +1481,9 @@ namespace mongo { pBuilder->append(&andOperator); } - void ExpressionFieldRange::addToBsonObj( - BSONObjBuilder *pBuilder, const std::string& fieldName, - bool requireExpression) const { + void ExpressionFieldRange::addToBsonObj(BSONObjBuilder *pBuilder, + StringData fieldName, + bool requireExpression) const { BuilderObj builder(pBuilder, fieldName); addToBson(&builder); } @@ -1530,26 +1499,26 @@ namespace mongo { verify(pRange.get()); // otherwise, we can't do anything /* if there are no endpoints, then every value is accepted */ - if (!pRange->pBottom.get() && !pRange->pTop.get()) + if (pRange->pBottom.missing() && pRange->pTop.missing()) return; // nothing to add to the predicate /* we're going to need the field path */ string fieldPath(pFieldPath->getFieldPath(false)); BSONObjBuilder range; - if (pRange->pBottom.get()) { + if (!pRange->pBottom.missing()) { /* the test for equality doesn't generate a subobject */ - if (pRange->pBottom.get() == pRange->pTop.get()) { - pRange->pBottom->addToBsonObj(pBuilder, fieldPath); + if (pRange->pBottom == pRange->pTop) { + pRange->pBottom.addToBsonObj(pBuilder, fieldPath); return; } - pRange->pBottom->addToBsonObj( + pRange->pBottom.addToBsonObj( pBuilder, (pRange->bottomOpen ? "$gt" : "$gte")); } - if (pRange->pTop.get()) { - pRange->pTop->addToBsonObj( + if (!pRange->pTop.missing()) { + pRange->pTop.addToBsonObj( pBuilder, (pRange->topOpen ? "$lt" : "$lte")); } @@ -1558,7 +1527,7 @@ namespace mongo { intrusive_ptr<ExpressionFieldRange> ExpressionFieldRange::create( const intrusive_ptr<ExpressionFieldPath> &pFieldPath, CmpOp cmpOp, - const intrusive_ptr<const Value> &pValue) { + const Value& pValue) { intrusive_ptr<ExpressionFieldRange> pE( new ExpressionFieldRange(pFieldPath, cmpOp, pValue)); return pE; @@ -1566,13 +1535,12 @@ namespace mongo { ExpressionFieldRange::ExpressionFieldRange( const intrusive_ptr<ExpressionFieldPath> &pTheFieldPath, CmpOp cmpOp, - const intrusive_ptr<const Value> &pValue): + const Value& pValue): pFieldPath(pTheFieldPath), pRange(new Range(cmpOp, pValue)) { } - void ExpressionFieldRange::intersect( - CmpOp cmpOp, const intrusive_ptr<const Value> &pValue) { + void ExpressionFieldRange::intersect(CmpOp cmpOp, const Value& pValue) { /* create the new range */ scoped_ptr<Range> pNew(new Range(cmpOp, pValue)); @@ -1586,8 +1554,7 @@ namespace mongo { pRange.reset(pRange->intersect(pNew.get())); } - ExpressionFieldRange::Range::Range( - CmpOp cmpOp, const intrusive_ptr<const Value> &pValue): + ExpressionFieldRange::Range::Range(CmpOp cmpOp, const Value& pValue): bottomOpen(false), topOpen(false), pBottom(), @@ -1629,8 +1596,8 @@ namespace mongo { } ExpressionFieldRange::Range::Range( - const intrusive_ptr<const Value> &pTheBottom, bool theBottomOpen, - const intrusive_ptr<const Value> &pTheTop, bool theTopOpen): + const Value& pTheBottom, bool theBottomOpen, + const Value& pTheTop, bool theTopOpen): bottomOpen(theBottomOpen), topOpen(theTopOpen), pBottom(pTheBottom), @@ -1645,10 +1612,10 @@ namespace mongo { Start by assuming the maximum is from pRange. Then, if we have values of our own, see if they're greater. */ - intrusive_ptr<const Value> pMaxBottom(pRange->pBottom); + Value pMaxBottom(pRange->pBottom); bool maxBottomOpen = pRange->bottomOpen; - if (pBottom.get()) { - if (!pRange->pBottom.get()) { + if (!pBottom.missing()) { + if (pRange->pBottom.missing()) { pMaxBottom = pBottom; maxBottomOpen = bottomOpen; } @@ -1669,10 +1636,10 @@ namespace mongo { Start by assuming the minimum is from pRange. Then, if we have values of our own, see if they are less. */ - intrusive_ptr<const Value> pMinTop(pRange->pTop); + Value pMinTop(pRange->pTop); bool minTopOpen = pRange->topOpen; - if (pTop.get()) { - if (!pRange->pTop.get()) { + if (!pTop.missing()) { + if (pRange->pTop.missing()) { pMinTop = pTop; minTopOpen = topOpen; } @@ -1698,9 +1665,8 @@ namespace mongo { return NULL; } - bool ExpressionFieldRange::Range::contains( - const intrusive_ptr<const Value> &pValue) const { - if (pBottom.get()) { + bool ExpressionFieldRange::Range::contains(const Value& pValue) const { + if (!pBottom.missing()) { const int cmp = Value::compare(pValue, pBottom); if (cmp < 0) return false; @@ -1708,7 +1674,7 @@ namespace mongo { return false; } - if (pTop.get()) { + if (!pTop.missing()) { const int cmp = Value::compare(pValue, pTop); if (cmp > 0) return false; @@ -1739,11 +1705,10 @@ namespace mongo { ExpressionNary::addOperand(pExpression); } - intrusive_ptr<const Value> ExpressionMinute::evaluate( - const intrusive_ptr<Document> &pDocument) const { + Value ExpressionMinute::evaluate(const Document& pDocument) const { checkArgCount(1); - intrusive_ptr<const Value> pDate(vpOperand[0]->evaluate(pDocument)); - tm date = pDate->coerceToTm(); + Value pDate(vpOperand[0]->evaluate(pDocument)); + tm date = pDate.coerceToTm(); return Value::createInt(date.tm_min); } @@ -1771,14 +1736,13 @@ namespace mongo { ExpressionNary::addOperand(pExpression); } - intrusive_ptr<const Value> ExpressionMod::evaluate( - const intrusive_ptr<Document> &pDocument) const { + Value ExpressionMod::evaluate(const Document& pDocument) const { checkArgCount(2); - intrusive_ptr<const Value> pLeft(vpOperand[0]->evaluate(pDocument)); - intrusive_ptr<const Value> pRight(vpOperand[1]->evaluate(pDocument)); + Value pLeft(vpOperand[0]->evaluate(pDocument)); + Value pRight(vpOperand[1]->evaluate(pDocument)); - BSONType leftType = pLeft->getType(); - BSONType rightType = pRight->getType(); + BSONType leftType = pLeft.getType(); + BSONType rightType = pRight.getType(); uassert(16374, "$mod does not support dates", leftType != Date && rightType != Date); @@ -1788,31 +1752,31 @@ namespace mongo { if (rightType == jstNULL || rightType == Undefined) return pRight; // ensure we aren't modding by 0 - double right = pRight->coerceToDouble(); + double right = pRight.coerceToDouble(); if (right == 0) - return Value::getUndefined(); + return Value(Undefined); if (leftType == NumberDouble) { // left is a double, return a double - double left = pLeft->coerceToDouble(); + double left = pLeft.coerceToDouble(); return Value::createDouble(fmod(left, right)); } - else if (rightType == NumberDouble && pRight->coerceToInt() != right) { + else if (rightType == NumberDouble && pRight.coerceToInt() != right) { // the shell converts ints to doubles so if right is larger than int max or // if right truncates to something other than itself, it is a real double. // Integer-valued double case is handled below - double left = pLeft->coerceToDouble(); + double left = pLeft.coerceToDouble(); return Value::createDouble(fmod(left, right)); } if (leftType == NumberLong || rightType == NumberLong) { // if either is long, return long - long long left = pLeft->coerceToLong(); - long long rightLong = pRight->coerceToLong(); + long long left = pLeft.coerceToLong(); + long long rightLong = pRight.coerceToLong(); return Value::createLong(left % rightLong); } // lastly they must both be ints, return int - int left = pLeft->coerceToInt(); - int rightInt = pRight->coerceToInt(); + int left = pLeft.coerceToInt(); + int rightInt = pRight.coerceToInt(); return Value::createInt(left % rightInt); } @@ -1839,11 +1803,10 @@ namespace mongo { ExpressionNary::addOperand(pExpression); } - intrusive_ptr<const Value> ExpressionMonth::evaluate( - const intrusive_ptr<Document> &pDocument) const { + Value ExpressionMonth::evaluate(const Document& pDocument) const { checkArgCount(1); - intrusive_ptr<const Value> pDate(vpOperand[0]->evaluate(pDocument)); - tm date = pDate->coerceToTm(); + Value pDate(vpOperand[0]->evaluate(pDocument)); + tm date = pDate.coerceToTm(); return Value::createInt(date.tm_mon + 1); // MySQL uses 1-12 tm uses 0-11 } @@ -1865,8 +1828,7 @@ namespace mongo { ExpressionNary() { } - intrusive_ptr<const Value> ExpressionMultiply::evaluate( - const intrusive_ptr<Document> &pDocument) const { + Value ExpressionMultiply::evaluate(const Document& pDocument) const { /* We'll try to return the narrowest possible result value. To do that without creating intermediate Values, do the arithmetic for double @@ -1879,13 +1841,13 @@ namespace mongo { const size_t n = vpOperand.size(); for(size_t i = 0; i < n; ++i) { - intrusive_ptr<const Value> pValue(vpOperand[i]->evaluate(pDocument)); + Value pValue(vpOperand[i]->evaluate(pDocument)); - uassert(16375, "$multiply does not support dates", pValue->getType() != Date); + uassert(16375, "$multiply does not support dates", pValue.getType() != Date); - productType = Value::getWidestNumeric(productType, pValue->getType()); - doubleProduct *= pValue->coerceToDouble(); - longProduct *= pValue->coerceToLong(); + productType = Value::getWidestNumeric(productType, pValue.getType()); + doubleProduct *= pValue.coerceToDouble(); + longProduct *= pValue.coerceToLong(); } if (productType == NumberDouble) @@ -1925,11 +1887,10 @@ namespace mongo { ExpressionNary::addOperand(pExpression); } - intrusive_ptr<const Value> ExpressionHour::evaluate( - const intrusive_ptr<Document> &pDocument) const { + Value ExpressionHour::evaluate(const Document& pDocument) const { checkArgCount(1); - intrusive_ptr<const Value> pDate(vpOperand[0]->evaluate(pDocument)); - tm date = pDate->coerceToTm(); + Value pDate(vpOperand[0]->evaluate(pDocument)); + tm date = pDate.coerceToTm(); return Value::createInt(date.tm_hour); } @@ -1957,16 +1918,15 @@ namespace mongo { ExpressionNary::addOperand(pExpression); } - intrusive_ptr<const Value> ExpressionIfNull::evaluate( - const intrusive_ptr<Document> &pDocument) const { + Value ExpressionIfNull::evaluate(const Document& pDocument) const { checkArgCount(2); - intrusive_ptr<const Value> pLeft(vpOperand[0]->evaluate(pDocument)); - BSONType leftType = pLeft->getType(); + Value pLeft(vpOperand[0]->evaluate(pDocument)); + BSONType leftType = pLeft.getType(); if ((leftType != Undefined) && (leftType != jstNULL)) return pLeft; - intrusive_ptr<const Value> pRight(vpOperand[1]->evaluate(pDocument)); + Value pRight(vpOperand[1]->evaluate(pDocument)); return pRight; } @@ -1995,7 +1955,7 @@ namespace mongo { dynamic_cast<ExpressionConstant *>(pNew.get()); if (pConst) { ++constCount; - if (pConst->getValue()->getType() == String) + if (pConst->getValue().getType() == String) ++stringCount; } } @@ -2007,8 +1967,7 @@ namespace mongo { ExpressionConstant never refers to the argument Document. */ if (constCount == n) { - intrusive_ptr<const Value> pResult( - evaluate(intrusive_ptr<Document>())); + Value pResult(evaluate(Document())); intrusive_ptr<Expression> pReplacement( ExpressionConstant::create(pResult)); return pReplacement; @@ -2104,8 +2063,7 @@ namespace mongo { together before adding the result to the end of the expression operand vector. */ - intrusive_ptr<const Value> pResult( - pConst->evaluate(intrusive_ptr<Document>())); + Value pResult(pConst->evaluate(Document())); pNew->addOperand(ExpressionConstant::create(pResult)); } @@ -2138,9 +2096,9 @@ namespace mongo { arrBuilder.doneFast(); } - void ExpressionNary::addToBsonObj( - BSONObjBuilder *pBuilder, const std::string& fieldName, - bool requireExpression) const { + void ExpressionNary::addToBsonObj(BSONObjBuilder *pBuilder, + StringData fieldName, + bool requireExpression) const { BSONObjBuilder exprBuilder; toBson(&exprBuilder, getOpName()); pBuilder->append(fieldName, exprBuilder.done()); @@ -2186,15 +2144,12 @@ namespace mongo { ExpressionNary::addOperand(pExpression); } - intrusive_ptr<const Value> ExpressionNot::evaluate( - const intrusive_ptr<Document> &pDocument) const { + Value ExpressionNot::evaluate(const Document& pDocument) const { checkArgCount(1); - intrusive_ptr<const Value> pOp(vpOperand[0]->evaluate(pDocument)); + Value pOp(vpOperand[0]->evaluate(pDocument)); - bool b = pOp->coerceToBool(); - if (b) - return Value::getFalse(); - return Value::getTrue(); + bool b = pOp.coerceToBool(); + return Value(!b); } const char *ExpressionNot::getOpName() const { @@ -2215,16 +2170,15 @@ namespace mongo { ExpressionNary() { } - intrusive_ptr<const Value> ExpressionOr::evaluate( - const intrusive_ptr<Document> &pDocument) const { + Value ExpressionOr::evaluate(const Document& pDocument) const { const size_t n = vpOperand.size(); for(size_t i = 0; i < n; ++i) { - intrusive_ptr<const Value> pValue(vpOperand[i]->evaluate(pDocument)); - if (pValue->coerceToBool()) - return Value::getTrue(); + Value pValue(vpOperand[i]->evaluate(pDocument)); + if (pValue.coerceToBool()) + return Value(true); } - return Value::getFalse(); + return Value(false); } void ExpressionOr::toMatcherBson( @@ -2268,10 +2222,10 @@ namespace mongo { Evaluate and coerce the last argument to a boolean. If it's true, then we can replace this entire expression. */ - bool last = pLast->evaluate(intrusive_ptr<Document>())->coerceToBool(); + bool last = pLast->evaluate(Document()).coerceToBool(); if (last) { intrusive_ptr<ExpressionConstant> pFinal( - ExpressionConstant::create(Value::getTrue())); + ExpressionConstant::create(Value(true))); return pFinal; } @@ -2317,11 +2271,10 @@ namespace mongo { ExpressionNary::addOperand(pExpression); } - intrusive_ptr<const Value> ExpressionSecond::evaluate( - const intrusive_ptr<Document> &pDocument) const { + Value ExpressionSecond::evaluate(const Document& pDocument) const { checkArgCount(1); - intrusive_ptr<const Value> pDate(vpOperand[0]->evaluate(pDocument)); - tm date = pDate->coerceToTm(); + Value pDate(vpOperand[0]->evaluate(pDocument)); + tm date = pDate.coerceToTm(); return Value::createInt(date.tm_sec); } @@ -2349,22 +2302,22 @@ namespace mongo { ExpressionNary::addOperand(pExpression); } - intrusive_ptr<const Value> ExpressionStrcasecmp::evaluate( - const intrusive_ptr<Document> &pDocument) const { + Value ExpressionStrcasecmp::evaluate(const Document& pDocument) const { checkArgCount(2); - intrusive_ptr<const Value> pString1(vpOperand[0]->evaluate(pDocument)); - intrusive_ptr<const Value> pString2(vpOperand[1]->evaluate(pDocument)); + Value pString1(vpOperand[0]->evaluate(pDocument)); + Value pString2(vpOperand[1]->evaluate(pDocument)); /* boost::iequals returns a bool not an int so strings must actually be allocated */ - string str1 = boost::to_upper_copy( pString1->coerceToString() ); - string str2 = boost::to_upper_copy( pString2->coerceToString() ); + string str1 = boost::to_upper_copy( pString1.coerceToString() ); + string str2 = boost::to_upper_copy( pString2.coerceToString() ); int result = str1.compare(str2); if (result == 0) - return Value::getZero(); - if (result > 0) - return Value::getOne(); - return Value::getMinusOne(); + return Value(0); + else if (result > 0) + return Value(1); + else + return Value(-1); } const char *ExpressionStrcasecmp::getOpName() const { @@ -2391,28 +2344,27 @@ namespace mongo { ExpressionNary::addOperand(pExpression); } - intrusive_ptr<const Value> ExpressionSubstr::evaluate( - const intrusive_ptr<Document> &pDocument) const { + Value ExpressionSubstr::evaluate(const Document& pDocument) const { checkArgCount(3); - intrusive_ptr<const Value> pString(vpOperand[0]->evaluate(pDocument)); - intrusive_ptr<const Value> pLower(vpOperand[1]->evaluate(pDocument)); - intrusive_ptr<const Value> pLength(vpOperand[2]->evaluate(pDocument)); + Value pString(vpOperand[0]->evaluate(pDocument)); + Value pLower(vpOperand[1]->evaluate(pDocument)); + Value pLength(vpOperand[2]->evaluate(pDocument)); - string str = pString->coerceToString(); + string str = pString.coerceToString(); uassert(16034, str::stream() << getOpName() << ": starting index must be a numeric type (is BSON type " << - typeName(pLower->getType()) << ")", - (pLower->getType() == NumberInt - || pLower->getType() == NumberLong - || pLower->getType() == NumberDouble)); + typeName(pLower.getType()) << ")", + (pLower.getType() == NumberInt + || pLower.getType() == NumberLong + || pLower.getType() == NumberDouble)); uassert(16035, str::stream() << getOpName() << ": length must be a numeric type (is BSON type " << - typeName(pLength->getType() )<< ")", - (pLength->getType() == NumberInt - || pLength->getType() == NumberLong - || pLength->getType() == NumberDouble)); - string::size_type lower = static_cast< string::size_type >( pLower->coerceToLong() ); - string::size_type length = static_cast< string::size_type >( pLength->coerceToLong() ); + typeName(pLength.getType() )<< ")", + (pLength.getType() == NumberInt + || pLength.getType() == NumberLong + || pLength.getType() == NumberDouble)); + string::size_type lower = static_cast< string::size_type >( pLower.coerceToLong() ); + string::size_type length = static_cast< string::size_type >( pLength.coerceToLong() ); if ( lower >= str.length() ) { // If lower > str.length() then string::substr() will throw out_of_range, so return an // empty string if lower is not a valid string index. @@ -2445,27 +2397,26 @@ namespace mongo { ExpressionNary::addOperand(pExpression); } - intrusive_ptr<const Value> ExpressionSubtract::evaluate( - const intrusive_ptr<Document> &pDocument) const { + Value ExpressionSubtract::evaluate(const Document& pDocument) const { BSONType productType; checkArgCount(2); - intrusive_ptr<const Value> pLeft(vpOperand[0]->evaluate(pDocument)); - intrusive_ptr<const Value> pRight(vpOperand[1]->evaluate(pDocument)); + Value pLeft(vpOperand[0]->evaluate(pDocument)); + Value pRight(vpOperand[1]->evaluate(pDocument)); - productType = Value::getWidestNumeric(pRight->getType(), pLeft->getType()); + productType = Value::getWidestNumeric(pRight.getType(), pLeft.getType()); uassert(16376, "$subtract does not support dates", - pLeft->getType() != Date && pRight->getType() != Date); + pLeft.getType() != Date && pRight.getType() != Date); if (productType == NumberDouble) { - double right = pRight->coerceToDouble(); - double left = pLeft->coerceToDouble(); + double right = pRight.coerceToDouble(); + double left = pLeft.coerceToDouble(); return Value::createDouble(left - right); } - long long right = pRight->coerceToLong(); - long long left = pLeft->coerceToLong(); + long long right = pRight.coerceToLong(); + long long left = pLeft.coerceToLong(); if (productType == NumberLong) return Value::createLong(left - right); else if (productType == NumberInt) @@ -2498,11 +2449,10 @@ namespace mongo { ExpressionNary::addOperand(pExpression); } - intrusive_ptr<const Value> ExpressionToLower::evaluate( - const intrusive_ptr<Document> &pDocument) const { + Value ExpressionToLower::evaluate(const Document& pDocument) const { checkArgCount(1); - intrusive_ptr<const Value> pString(vpOperand[0]->evaluate(pDocument)); - string str = pString->coerceToString(); + Value pString(vpOperand[0]->evaluate(pDocument)); + string str = pString.coerceToString(); boost::to_lower(str); return Value::createString(str); } @@ -2531,11 +2481,10 @@ namespace mongo { ExpressionNary::addOperand(pExpression); } - intrusive_ptr<const Value> ExpressionToUpper::evaluate( - const intrusive_ptr<Document> &pDocument) const { + Value ExpressionToUpper::evaluate(const Document& pDocument) const { checkArgCount(1); - intrusive_ptr<const Value> pString(vpOperand[0]->evaluate(pDocument)); - string str(pString->coerceToString()); + Value pString(vpOperand[0]->evaluate(pDocument)); + string str(pString.coerceToString()); boost::to_upper(str); return Value::createString(str); } @@ -2563,11 +2512,10 @@ namespace mongo { ExpressionNary::addOperand(pExpression); } - intrusive_ptr<const Value> ExpressionWeek::evaluate( - const intrusive_ptr<Document> &pDocument) const { + Value ExpressionWeek::evaluate(const Document& pDocument) const { checkArgCount(1); - intrusive_ptr<const Value> pDate(vpOperand[0]->evaluate(pDocument)); - tm date = pDate->coerceToTm(); + Value pDate(vpOperand[0]->evaluate(pDocument)); + tm date = pDate.coerceToTm(); int dayOfWeek = date.tm_wday; int dayOfYear = date.tm_yday; int prevSundayDayOfYear = dayOfYear - dayOfWeek; // may be negative @@ -2611,11 +2559,10 @@ namespace mongo { ExpressionNary::addOperand(pExpression); } - intrusive_ptr<const Value> ExpressionYear::evaluate( - const intrusive_ptr<Document> &pDocument) const { + Value ExpressionYear::evaluate(const Document& pDocument) const { checkArgCount(1); - intrusive_ptr<const Value> pDate(vpOperand[0]->evaluate(pDocument)); - tm date = pDate->coerceToTm(); + Value pDate(vpOperand[0]->evaluate(pDocument)); + tm date = pDate.coerceToTm(); return Value::createInt(date.tm_year + 1900); // tm_year is years since 1900 } diff --git a/src/mongo/db/pipeline/expression.h b/src/mongo/db/pipeline/expression.h index c9f13990854..e3bbf57a237 100755 --- a/src/mongo/db/pipeline/expression.h +++ b/src/mongo/db/pipeline/expression.h @@ -29,6 +29,7 @@ namespace mongo { class BSONObjBuilder; class Builder; class Document; + class MutableDocument; class DocumentSource; class ExpressionContext; class Value; @@ -76,8 +77,7 @@ namespace mongo { @returns the computed value */ - virtual intrusive_ptr<const Value> evaluate( - const intrusive_ptr<Document> &pDocument) const = 0; + virtual Value evaluate(const Document& pDocument) const = 0; /* Add the Expression (and any descendant Expressions) into a BSON @@ -94,9 +94,9 @@ namespace mongo { $project which distinguish between field inclusion and virtual field specification; See ExpressionConstant. */ - virtual void addToBsonObj( - BSONObjBuilder *pBuilder, const std::string& fieldName, - bool requireExpression) const = 0; + virtual void addToBsonObj(BSONObjBuilder *pBuilder, + StringData fieldName, + bool requireExpression) const = 0; /* Add the Expression (and any descendant Expressions) into a BSON @@ -223,9 +223,9 @@ namespace mongo { public: // virtuals from Expression virtual intrusive_ptr<Expression> optimize(); - virtual void addToBsonObj( - BSONObjBuilder *pBuilder, const std::string& fieldName, - bool requireExpression) const; + virtual void addToBsonObj(BSONObjBuilder *pBuilder, + StringData fieldName, + bool requireExpression) const; virtual void addToBsonArray(BSONArrayBuilder *pBuilder) const; virtual void addDependencies(set<string>& deps, vector<string>* path=NULL) const; @@ -313,7 +313,7 @@ namespace mongo { public: // virtuals from Expression virtual ~ExpressionAdd(); - virtual intrusive_ptr<const Value> evaluate(const intrusive_ptr<Document> &pDocument) const; + virtual Value evaluate(const Document& pDocument) const; virtual const char *getOpName() const; // virtuals from ExpressionNary @@ -334,8 +334,7 @@ namespace mongo { // virtuals from Expression virtual ~ExpressionAnd(); virtual intrusive_ptr<Expression> optimize(); - virtual intrusive_ptr<const Value> evaluate( - const intrusive_ptr<Document> &pDocument) const; + virtual Value evaluate(const Document& pDocument) const; virtual const char *getOpName() const; virtual void toMatcherBson(BSONObjBuilder *pBuilder) const; @@ -365,11 +364,10 @@ namespace mongo { virtual ~ExpressionCoerceToBool(); virtual intrusive_ptr<Expression> optimize(); virtual void addDependencies(set<string>& deps, vector<string>* path=NULL) const; - virtual intrusive_ptr<const Value> evaluate( - const intrusive_ptr<Document> &pDocument) const; - virtual void addToBsonObj( - BSONObjBuilder *pBuilder, const std::string& fieldName, - bool requireExpression) const; + virtual Value evaluate(const Document& pDocument) const; + virtual void addToBsonObj(BSONObjBuilder *pBuilder, + StringData fieldName, + bool requireExpression) const; virtual void addToBsonArray(BSONArrayBuilder *pBuilder) const; static intrusive_ptr<ExpressionCoerceToBool> create( @@ -388,8 +386,7 @@ namespace mongo { // virtuals from ExpressionNary virtual ~ExpressionCompare(); virtual intrusive_ptr<Expression> optimize(); - virtual intrusive_ptr<const Value> evaluate( - const intrusive_ptr<Document> &pDocument) const; + virtual Value evaluate(const Document& pDocument) const; virtual const char *getOpName() const; virtual void addOperand(const intrusive_ptr<Expression> &pExpression); @@ -422,8 +419,7 @@ namespace mongo { public: // virtuals from ExpressionNary virtual ~ExpressionCond(); - virtual intrusive_ptr<const Value> evaluate( - const intrusive_ptr<Document> &pDocument) const; + virtual Value evaluate(const Document& pDocument) const; virtual const char *getOpName() const; virtual void addOperand(const intrusive_ptr<Expression> &pExpression); @@ -441,31 +437,29 @@ namespace mongo { virtual ~ExpressionConstant(); virtual intrusive_ptr<Expression> optimize(); virtual void addDependencies(set<string>& deps, vector<string>* path=NULL) const; - virtual intrusive_ptr<const Value> evaluate( - const intrusive_ptr<Document> &pDocument) const; + virtual Value evaluate(const Document& pDocument) const; virtual const char *getOpName() const; - virtual void addToBsonObj( - BSONObjBuilder *pBuilder, const std::string& fieldName, - bool requireExpression) const; + virtual void addToBsonObj(BSONObjBuilder *pBuilder, + StringData fieldName, + bool requireExpression) const; virtual void addToBsonArray(BSONArrayBuilder *pBuilder) const; static intrusive_ptr<ExpressionConstant> createFromBsonElement( BSONElement *pBsonElement); - static intrusive_ptr<ExpressionConstant> create( - const intrusive_ptr<const Value> &pValue); + static intrusive_ptr<ExpressionConstant> create(const Value& pValue); /* Get the constant value represented by this Expression. @returns the value */ - intrusive_ptr<const Value> getValue() const; + Value getValue() const; private: ExpressionConstant(BSONElement *pBsonElement); - ExpressionConstant(const intrusive_ptr<const Value> &pValue); + ExpressionConstant(const Value& pValue); - intrusive_ptr<const Value> pValue; + Value pValue; }; @@ -474,8 +468,7 @@ namespace mongo { public: // virtuals from ExpressionNary virtual ~ExpressionDayOfMonth(); - virtual intrusive_ptr<const Value> evaluate( - const intrusive_ptr<Document> &pDocument) const; + virtual Value evaluate(const Document& pDocument) const; virtual const char *getOpName() const; virtual void addOperand(const intrusive_ptr<Expression> &pExpression); @@ -491,8 +484,7 @@ namespace mongo { public: // virtuals from ExpressionNary virtual ~ExpressionDayOfWeek(); - virtual intrusive_ptr<const Value> evaluate( - const intrusive_ptr<Document> &pDocument) const; + virtual Value evaluate(const Document& pDocument) const; virtual const char *getOpName() const; virtual void addOperand(const intrusive_ptr<Expression> &pExpression); @@ -508,8 +500,7 @@ namespace mongo { public: // virtuals from ExpressionNary virtual ~ExpressionDayOfYear(); - virtual intrusive_ptr<const Value> evaluate( - const intrusive_ptr<Document> &pDocument) const; + virtual Value evaluate(const Document& pDocument) const; virtual const char *getOpName() const; virtual void addOperand(const intrusive_ptr<Expression> &pExpression); @@ -525,8 +516,7 @@ namespace mongo { public: // virtuals from ExpressionNary virtual ~ExpressionDivide(); - virtual intrusive_ptr<const Value> evaluate( - const intrusive_ptr<Document> &pDocument) const; + virtual Value evaluate(const Document& pDocument) const; virtual const char *getOpName() const; virtual void addOperand(const intrusive_ptr<Expression> &pExpression); @@ -544,11 +534,10 @@ namespace mongo { virtual ~ExpressionFieldPath(); virtual intrusive_ptr<Expression> optimize(); virtual void addDependencies(set<string>& deps, vector<string>* path=NULL) const; - virtual intrusive_ptr<const Value> evaluate( - const intrusive_ptr<Document> &pDocument) const; - virtual void addToBsonObj( - BSONObjBuilder *pBuilder, const std::string& fieldName, - bool requireExpression) const; + virtual Value evaluate(const Document& pDocument) const; + virtual void addToBsonObj(BSONObjBuilder *pBuilder, + StringData fieldName, + bool requireExpression) const; virtual void addToBsonArray(BSONArrayBuilder *pBuilder) const; /* @@ -599,9 +588,9 @@ namespace mongo { @param pDocument current document traversed to (not the top-level one) @returns the field found; could be an array */ - intrusive_ptr<const Value> evaluatePath( + Value evaluatePath( size_t index, const size_t pathLength, - intrusive_ptr<Document> pDocument) const; + Document pDocument) const; FieldPath fieldPath; }; @@ -614,11 +603,10 @@ namespace mongo { virtual ~ExpressionFieldRange(); virtual intrusive_ptr<Expression> optimize(); virtual void addDependencies(set<string>& deps, vector<string>* path=NULL) const; - virtual intrusive_ptr<const Value> evaluate( - const intrusive_ptr<Document> &pDocument) const; - virtual void addToBsonObj( - BSONObjBuilder *pBuilder, const std::string& fieldName, - bool requireExpression) const; + virtual Value evaluate(const Document& pDocument) const; + virtual void addToBsonObj(BSONObjBuilder *pBuilder, + StringData fieldName, + bool requireExpression) const; virtual void addToBsonArray(BSONArrayBuilder *pBuilder) const; virtual void toMatcherBson(BSONObjBuilder *pBuilder) const; @@ -644,7 +632,7 @@ namespace mongo { */ static intrusive_ptr<ExpressionFieldRange> create( const intrusive_ptr<ExpressionFieldPath> &pFieldPath, - CmpOp cmpOp, const intrusive_ptr<const Value> &pValue); + CmpOp cmpOp, const Value& pValue); /* Add an intersecting range. @@ -659,30 +647,30 @@ namespace mongo { @param cmpOp the comparison operator @param pValue the value to compare against */ - void intersect(CmpOp cmpOp, const intrusive_ptr<const Value> &pValue); + void intersect(CmpOp cmpOp, const Value& pValue); private: ExpressionFieldRange(const intrusive_ptr<ExpressionFieldPath> &pFieldPath, CmpOp cmpOp, - const intrusive_ptr<const Value> &pValue); + const Value& pValue); intrusive_ptr<ExpressionFieldPath> pFieldPath; class Range { public: - Range(CmpOp cmpOp, const intrusive_ptr<const Value> &pValue); + Range(CmpOp cmpOp, const Value& pValue); Range(const Range &rRange); Range *intersect(const Range *pRange) const; - bool contains(const intrusive_ptr<const Value> &pValue) const; + bool contains(const Value& pValue) const; - Range(const intrusive_ptr<const Value> &pBottom, bool bottomOpen, - const intrusive_ptr<const Value> &pTop, bool topOpen); + Range(const Value& pBottom, bool bottomOpen, + const Value& pTop, bool topOpen); bool bottomOpen; bool topOpen; - intrusive_ptr<const Value> pBottom; - intrusive_ptr<const Value> pTop; + Value pBottom; + Value pTop; }; scoped_ptr<Range> pRange; @@ -705,8 +693,7 @@ namespace mongo { public: // virtuals from ExpressionNary virtual ~ExpressionHour(); - virtual intrusive_ptr<const Value> evaluate( - const intrusive_ptr<Document> &pDocument) const; + virtual Value evaluate(const Document& pDocument) const; virtual const char *getOpName() const; virtual void addOperand(const intrusive_ptr<Expression> &pExpression); @@ -722,8 +709,7 @@ namespace mongo { public: // virtuals from ExpressionNary virtual ~ExpressionIfNull(); - virtual intrusive_ptr<const Value> evaluate( - const intrusive_ptr<Document> &pDocument) const; + virtual Value evaluate(const Document& pDocument) const; virtual const char *getOpName() const; virtual void addOperand(const intrusive_ptr<Expression> &pExpression); @@ -739,8 +725,7 @@ namespace mongo { public: // virtuals from ExpressionNary virtual ~ExpressionMinute(); - virtual intrusive_ptr<const Value> evaluate( - const intrusive_ptr<Document> &pDocument) const; + virtual Value evaluate(const Document& pDocument) const; virtual const char *getOpName() const; virtual void addOperand(const intrusive_ptr<Expression> &pExpression); @@ -756,8 +741,7 @@ namespace mongo { public: // virtuals from ExpressionNary virtual ~ExpressionMod(); - virtual intrusive_ptr<const Value> evaluate( - const intrusive_ptr<Document> &pDocument) const; + virtual Value evaluate(const Document& pDocument) const; virtual const char *getOpName() const; virtual void addOperand(const intrusive_ptr<Expression> &pExpression); @@ -773,8 +757,7 @@ namespace mongo { public: // virtuals from Expression virtual ~ExpressionMultiply(); - virtual intrusive_ptr<const Value> evaluate( - const intrusive_ptr<Document> &pDocument) const; + virtual Value evaluate(const Document& pDocument) const; virtual const char *getOpName() const; // virtuals from ExpressionNary @@ -797,8 +780,7 @@ namespace mongo { public: // virtuals from ExpressionNary virtual ~ExpressionMonth(); - virtual intrusive_ptr<const Value> evaluate( - const intrusive_ptr<Document> &pDocument) const; + virtual Value evaluate(const Document& pDocument) const; virtual const char *getOpName() const; virtual void addOperand(const intrusive_ptr<Expression> &pExpression); @@ -814,8 +796,7 @@ namespace mongo { public: // virtuals from ExpressionNary virtual ~ExpressionNot(); - virtual intrusive_ptr<const Value> evaluate( - const intrusive_ptr<Document> &pDocument) const; + virtual Value evaluate(const Document& pDocument) const; virtual const char *getOpName() const; virtual void addOperand(const intrusive_ptr<Expression> &pExpression); @@ -835,11 +816,10 @@ namespace mongo { virtual bool isSimple(); virtual void addDependencies(set<string>& deps, vector<string>* path=NULL) const; /** Only evaluates non inclusion expressions. For inclusions, use addToDocument(). */ - virtual intrusive_ptr<const Value> evaluate( - const intrusive_ptr<Document> &pDocument) const; - virtual void addToBsonObj( - BSONObjBuilder *pBuilder, const std::string& fieldName, - bool requireExpression) const; + virtual Value evaluate(const Document& pDocument) const; + virtual void addToBsonObj(BSONObjBuilder *pBuilder, + StringData fieldName, + bool requireExpression) const; virtual void addToBsonArray(BSONArrayBuilder *pBuilder) const; /* @@ -849,8 +829,7 @@ namespace mongo { @param pDocument the input Document @returns the result document */ - intrusive_ptr<Document> evaluateDocument( - const intrusive_ptr<Document> &pDocument) const; + Document evaluateDocument(const Document& pDocument) const; /* evaluate(), but add the evaluated fields to a given document @@ -860,9 +839,9 @@ namespace mongo { @param pDocument the input Document for this level @param rootDoc the root of the whole input document */ - void addToDocument(const intrusive_ptr<Document>& pResult, - const intrusive_ptr<Document>& pDocument, - const intrusive_ptr<Document>& rootDoc + void addToDocument(MutableDocument& pResult, + const Document& pDocument, + const Document& rootDoc ) const; // estimated number of fields that will be output @@ -991,8 +970,7 @@ namespace mongo { // virtuals from Expression virtual ~ExpressionOr(); virtual intrusive_ptr<Expression> optimize(); - virtual intrusive_ptr<const Value> evaluate( - const intrusive_ptr<Document> &pDocument) const; + virtual Value evaluate(const Document& pDocument) const; virtual const char *getOpName() const; virtual void toMatcherBson(BSONObjBuilder *pBuilder) const; @@ -1020,8 +998,7 @@ namespace mongo { public: // virtuals from ExpressionNary virtual ~ExpressionSecond(); - virtual intrusive_ptr<const Value> evaluate( - const intrusive_ptr<Document> &pDocument) const; + virtual Value evaluate(const Document& pDocument) const; virtual const char *getOpName() const; virtual void addOperand(const intrusive_ptr<Expression> &pExpression); @@ -1037,8 +1014,7 @@ namespace mongo { public: // virtuals from ExpressionNary virtual ~ExpressionStrcasecmp(); - virtual intrusive_ptr<const Value> evaluate( - const intrusive_ptr<Document> &pDocument) const; + virtual Value evaluate(const Document& pDocument) const; virtual const char *getOpName() const; virtual void addOperand(const intrusive_ptr<Expression> &pExpression); @@ -1054,8 +1030,7 @@ namespace mongo { public: // virtuals from ExpressionNary virtual ~ExpressionSubstr(); - virtual intrusive_ptr<const Value> evaluate( - const intrusive_ptr<Document> &pDocument) const; + virtual Value evaluate(const Document& pDocument) const; virtual const char *getOpName() const; virtual void addOperand(const intrusive_ptr<Expression> &pExpression); @@ -1071,8 +1046,7 @@ namespace mongo { public: // virtuals from ExpressionNary virtual ~ExpressionSubtract(); - virtual intrusive_ptr<const Value> evaluate( - const intrusive_ptr<Document> &pDocument) const; + virtual Value evaluate(const Document& pDocument) const; virtual const char *getOpName() const; virtual void addOperand(const intrusive_ptr<Expression> &pExpression); @@ -1088,8 +1062,7 @@ namespace mongo { public: // virtuals from ExpressionNary virtual ~ExpressionToLower(); - virtual intrusive_ptr<const Value> evaluate( - const intrusive_ptr<Document> &pDocument) const; + virtual Value evaluate(const Document& pDocument) const; virtual const char *getOpName() const; virtual void addOperand(const intrusive_ptr<Expression> &pExpression); @@ -1105,8 +1078,7 @@ namespace mongo { public: // virtuals from ExpressionNary virtual ~ExpressionToUpper(); - virtual intrusive_ptr<const Value> evaluate( - const intrusive_ptr<Document> &pDocument) const; + virtual Value evaluate(const Document& pDocument) const; virtual const char *getOpName() const; virtual void addOperand(const intrusive_ptr<Expression> &pExpression); @@ -1122,8 +1094,7 @@ namespace mongo { public: // virtuals from ExpressionNary virtual ~ExpressionWeek(); - virtual intrusive_ptr<const Value> evaluate( - const intrusive_ptr<Document> &pDocument) const; + virtual Value evaluate(const Document& pDocument) const; virtual const char *getOpName() const; virtual void addOperand(const intrusive_ptr<Expression> &pExpression); @@ -1139,8 +1110,7 @@ namespace mongo { public: // virtuals from ExpressionNary virtual ~ExpressionYear(); - virtual intrusive_ptr<const Value> evaluate( - const intrusive_ptr<Document> &pDocument) const; + virtual Value evaluate(const Document& pDocument) const; virtual const char *getOpName() const; virtual void addOperand(const intrusive_ptr<Expression> &pExpression); @@ -1164,7 +1134,7 @@ namespace mongo { return 0; } - inline intrusive_ptr<const Value> ExpressionConstant::getValue() const { + inline Value ExpressionConstant::getValue() const { return pValue; } diff --git a/src/mongo/db/pipeline/pipeline.cpp b/src/mongo/db/pipeline/pipeline.cpp index b7ebf86272c..8616e688aac 100644 --- a/src/mongo/db/pipeline/pipeline.cpp +++ b/src/mongo/db/pipeline/pipeline.cpp @@ -416,7 +416,7 @@ namespace mongo { // cant use subArrayStart() due to error handling BSONArrayBuilder resultArray; for(bool hasDoc = !pSource->eof(); hasDoc; hasDoc = pSource->advance()) { - intrusive_ptr<Document> pDocument(pSource->getCurrent()); + Document pDocument(pSource->getCurrent()); /* add the document to the result set */ BSONObjBuilder documentBuilder (resultArray.subobjStart()); @@ -476,8 +476,7 @@ namespace mongo { BSONArrayBuilder shardOpArray; // where we'll put the pipeline ops for(bool hasDocument = !pSourceBsonArray->eof(); hasDocument; hasDocument = pSourceBsonArray->advance()) { - intrusive_ptr<Document> pDocument( - pSourceBsonArray->getCurrent()); + Document pDocument = pSourceBsonArray->getCurrent(); BSONObjBuilder opBuilder; pDocument->toBson(&opBuilder); shardOpArray.append(opBuilder.obj()); diff --git a/src/mongo/db/pipeline/value.cpp b/src/mongo/db/pipeline/value.cpp index 5ab0bd65c61..a6d6e85f878 100644 --- a/src/mongo/db/pipeline/value.cpp +++ b/src/mongo/db/pipeline/value.cpp @@ -14,157 +14,168 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#include "pch.h" -#include "db/pipeline/value.h" +#include "mongo/pch.h" + +#include "mongo/db/pipeline/value.h" #include <boost/functional/hash.hpp> -#include "db/jsobj.h" -#include "db/pipeline/builder.h" -#include "db/pipeline/document.h" -#include "util/mongoutils/str.h" + +#include "mongo/db/jsobj.h" +#include "mongo/db/pipeline/builder.h" +#include "mongo/db/pipeline/document.h" +#include "mongo/util/mongoutils/str.h" namespace mongo { using namespace mongoutils; - const intrusive_ptr<const Value> Value::pFieldUndefined( - new ValueStatic(Undefined)); - const intrusive_ptr<const Value> Value::pFieldNull(new ValueStatic()); - const intrusive_ptr<const Value> Value::pFieldTrue(new ValueStatic(true)); - const intrusive_ptr<const Value> Value::pFieldFalse(new ValueStatic(false)); - const intrusive_ptr<const Value> Value::pFieldMinusOne(new ValueStatic(-1)); - const intrusive_ptr<const Value> Value::pFieldZero(new ValueStatic(0)); - const intrusive_ptr<const Value> Value::pFieldOne(new ValueStatic(1)); + void ValueStorage::putString(StringData s) { + const size_t sizeWithNUL = s.size() + 1; + if (sizeWithNUL <= sizeof(shortStrStorage)) { + shortStr = true; + shortStrSize = s.size(); + memcpy(shortStrStorage, s.data(), sizeWithNUL); + } + else { + intrusive_ptr<const RCString> rcs = RCString::create(s); + fassert(16492, rcs); + genericRCPtr = rcs.get(); + intrusive_ptr_add_ref(genericRCPtr); + refCounter = true; + } + } + + void ValueStorage::putDocument(const Document& d) { + genericRCPtr = d._storage.get(); + + if (genericRCPtr) { // NULL here means empty document + intrusive_ptr_add_ref(genericRCPtr); + refCounter = true; + } + } + + void ValueStorage::putVector(const RCVector* vec) { + fassert(16485, vec); - Value::~Value() { + genericRCPtr = vec; + intrusive_ptr_add_ref(genericRCPtr); + refCounter = true; } - Value::Value(): type(jstNULL) {} + Document ValueStorage::getDocument() const { + if (!genericRCPtr) + return Document(); - Value::Value(BSONType theType): type(theType) { - switch(type) { + dassert(typeid(*genericRCPtr) == typeid(const DocumentStorage)); + const DocumentStorage* documentPtr = static_cast<const DocumentStorage*>(genericRCPtr); + return Document(documentPtr); + } + + Value::Value(BSONType theType): _storage(theType) { + switch(getType()) { case Undefined: case jstNULL: case Object: // empty - case Array: // empty break; + case Array: // empty + _storage.putVector(new RCVector()); + case Bool: - boolValue = false; + _storage.boolValue = false; break; case NumberDouble: - doubleValue = 0; + _storage.doubleValue = 0; break; case NumberInt: - intValue = 0; + _storage.intValue = 0; break; case NumberLong: - longValue = 0; + _storage.longValue = 0; break; case Date: - dateValue = 0; + _storage.dateValue = 0; break; case Timestamp: - timestampValue = 0; + _storage.timestampValue = 0; break; default: // nothing else is allowed uassert(16001, str::stream() << - "can't create empty Value of type " << typeName(type), false); + "can't create empty Value of type " << typeName(getType()), false); break; } } - Value::Value(bool value) - : type(Bool) - , boolValue(value) - {} - intrusive_ptr<const Value> Value::createFromBsonElement( - BSONElement *pBsonElement) { - switch (pBsonElement->type()) { - case Undefined: - return getUndefined(); - case jstNULL: - return getNull(); - case Bool: - if (pBsonElement->boolean()) - return getTrue(); - else - return getFalse(); - default: - intrusive_ptr<const Value> pValue(new Value(pBsonElement)); - return pValue; - } + Value Value::createFromBsonElement(const BSONElement* pBsonElement) { + return Value(*pBsonElement); } - Value::Value(BSONElement *pBsonElement): - type(pBsonElement->type()), - pDocumentValue(), - vpValue() { - switch(type) { + Value::Value(const BSONElement& elem) : _storage(elem.type()) { + switch(getType()) { case NumberDouble: - doubleValue = pBsonElement->Double(); + _storage.doubleValue = elem.Double(); break; case String: - stringValue = pBsonElement->str(); + _storage.putString(StringData(elem.valuestr(), elem.valuestrsize()-1)); break; case Object: { - BSONObj document(pBsonElement->embeddedObject()); - pDocumentValue = Document::createFromBsonObj(&document); + _storage.putDocument(Document(elem.embeddedObject())); break; } case Array: { - vector<BSONElement> vElement(pBsonElement->Array()); + vector<BSONElement> vElement(elem.Array()); const size_t n = vElement.size(); - vpValue.reserve(n); // save on realloc()ing + intrusive_ptr<RCVector> vec (new RCVector); + vec->vec.reserve(n); // save on realloc()ing for(size_t i = 0; i < n; ++i) { - vpValue.push_back( - Value::createFromBsonElement(&vElement[i])); + vec->vec.push_back(Value::createFromBsonElement(&vElement[i])); } + _storage.putVector(vec.get()); break; } case jstOID: - BOOST_STATIC_ASSERT(sizeof(oidValue) == sizeof(OID)); - memcpy(oidValue, pBsonElement->OID().getData(), sizeof(oidValue)); + BOOST_STATIC_ASSERT(sizeof(_storage.oid) == sizeof(OID)); + memcpy(_storage.oid, elem.OID().getData(), sizeof(OID)); break; case Bool: - boolValue = pBsonElement->Bool(); + _storage.boolValue = elem.boolean(); break; case Date: // this is really signed but typed as unsigned for historical reasons - dateValue = static_cast<long long>(pBsonElement->Date().millis); + _storage.dateValue = static_cast<long long>(elem.date().millis); break; case RegEx: - stringValue = pBsonElement->regex(); - // TODO pBsonElement->regexFlags(); + _storage.putString(elem.regex()); + // TODO elem.regexFlags(); break; case NumberInt: - intValue = pBsonElement->numberInt(); + _storage.intValue = elem.numberInt(); break; case Timestamp: // asDate is a poorly named function that returns a ReplTime - timestampValue = pBsonElement->_opTime().asDate(); + _storage.timestampValue = elem._opTime().asDate(); break; case NumberLong: - longValue = pBsonElement->numberLong(); + _storage.longValue = elem.numberLong(); break; case Undefined: @@ -182,210 +193,70 @@ namespace mongo { case Code: case MaxKey: uassert(16002, str::stream() << - "can't create Value of BSON type " << typeName(type), false); + "can't create Value of BSON type " << typeName(getType()), false); break; } } - Value::Value(int value) - : type(NumberInt) - , intValue(value) - {} - - intrusive_ptr<const Value> Value::createInt(int value) { - intrusive_ptr<const Value> pValue(new Value(value)); - return pValue; - } - - intrusive_ptr<const Value> Value::createIntOrLong(long long value) { + Value Value::createIntOrLong(long long value) { if (value > numeric_limits<int>::max() || value < numeric_limits<int>::min()) { // it is too large to be an int and should remain a long - return new Value(value); + return Value(value); } // should be an int since all arguments were int and it fits return createInt(value); } - Value::Value(long long value) - : type(NumberLong) - , longValue(value) - {} - - intrusive_ptr<const Value> Value::createLong(long long value) { - intrusive_ptr<const Value> pValue(new Value(value)); - return pValue; - } - - Value::Value(double value) - : type(NumberDouble) - , doubleValue(value) - {} - - intrusive_ptr<const Value> Value::createDouble(double value) { - intrusive_ptr<const Value> pValue(new Value(value)); - return pValue; - } - - intrusive_ptr<const Value> Value::createDate(const long long &value) { + Value Value::createDate(const long long value) { // Can't directly construct because constructor would clash with createLong - intrusive_ptr<Value> pValue(new Value(Date)); - pValue->dateValue = value; - return pValue; - } - - Value::Value(const OpTime& value) - : type(Timestamp) - , timestampValue(value.asDate()) - {} - - intrusive_ptr<const Value> Value::createTimestamp(const OpTime& value) { - intrusive_ptr<const Value> pValue(new Value(value)); - return pValue; - } - - Value::Value(const string &value): - type(String), - pDocumentValue(), - vpValue() { - stringValue = value; - } - - intrusive_ptr<const Value> Value::createString(const string &value) { - intrusive_ptr<const Value> pValue(new Value(value)); - return pValue; - } - - Value::Value(const intrusive_ptr<Document> &pDocument): - type(Object), - pDocumentValue(pDocument), - vpValue() { - } - - intrusive_ptr<const Value> Value::createDocument( - const intrusive_ptr<Document> &pDocument) { - intrusive_ptr<const Value> pValue(new Value(pDocument)); - return pValue; - } - - Value::Value(const vector<intrusive_ptr<const Value> > &thevpValue): - type(Array), - pDocumentValue(), - vpValue(thevpValue) { - } - - intrusive_ptr<const Value> Value::createArray( - const vector<intrusive_ptr<const Value> > &vpValue) { - intrusive_ptr<const Value> pValue(new Value(vpValue)); - return pValue; + Value val (Date); + val._storage.dateValue = value; + return val; } double Value::getDouble() const { BSONType type = getType(); if (type == NumberInt) - return intValue; + return _storage.intValue; if (type == NumberLong) - return static_cast< double >( longValue ); + return static_cast< double >( _storage.longValue ); verify(type == NumberDouble); - return doubleValue; - } - - string Value::getString() const { - verify(getType() == String); - return stringValue; + return _storage.doubleValue; } - intrusive_ptr<Document> Value::getDocument() const { + Document Value::getDocument() const { verify(getType() == Object); - return pDocumentValue; - } - - ValueIterator::~ValueIterator() { - } - - Value::vi::~vi() { - } - - bool Value::vi::more() const { - return (nextIndex < size); - } - - intrusive_ptr<const Value> Value::vi::next() { - verify(more()); - return (*pvpValue)[nextIndex++]; - } - - Value::vi::vi(const intrusive_ptr<const Value> &pValue, - const vector<intrusive_ptr<const Value> > *thepvpValue): - size(thepvpValue->size()), - nextIndex(0), - pvpValue(thepvpValue) { - } - - intrusive_ptr<ValueIterator> Value::getArray() const { - verify(getType() == Array); - intrusive_ptr<ValueIterator> pVI( - new vi(intrusive_ptr<const Value>(this), &vpValue)); - return pVI; - } - - OID Value::getOid() const { - verify(getType() == jstOID); - return OID(oidValue); - } - - bool Value::getBool() const { - verify(getType() == Bool); - return boolValue; + return _storage.getDocument(); } - long long Value::getDate() const { - verify(getType() == Date); - return dateValue; - } + Value Value::operator[] (size_t index) const { + if (missing() || getType() != Array || index >= getArrayLength()) + return Value(); - OpTime Value::getTimestamp() const { - verify(getType() == Timestamp); - return timestampValue; + return getArray()[index]; } - string Value::getRegex() const { - verify(getType() == RegEx); - return stringValue; - } + Value Value::operator[] (StringData name) const { + if (missing() || getType() != Object) + return Value(); - string Value::getSymbol() const { - verify(getType() == Symbol); - return stringValue; + return getDocument()[name]; } - int Value::getInt() const { - verify(getType() == NumberInt); - return intValue; - } - - long long Value::getLong() const { - BSONType type = getType(); - if (type == NumberInt) - return intValue; - - verify(type == NumberLong); - return longValue; - } - - void Value::addToBson(Builder *pBuilder) const { + void Value::addToBson(Builder* pBuilder) const { switch(getType()) { case NumberDouble: pBuilder->append(getDouble()); break; case String: - pBuilder->append(getString()); + pBuilder->append(getStringData()); break; case Object: { - intrusive_ptr<Document> pDocument(getDocument()); + Document pDocument(getDocument()); BSONObjBuilder subBuilder; pDocument->toBson(&subBuilder); subBuilder.done(); @@ -394,10 +265,10 @@ namespace mongo { } case Array: { - const size_t n = vpValue.size(); + const size_t n = getArray().size(); BSONArrayBuilder arrayBuilder(n); for(size_t i = 0; i < n; ++i) { - vpValue[i]->addToBsonArray(&arrayBuilder); + getArray()[i].addToBsonArray(&arrayBuilder); } pBuilder->append(&arrayBuilder); @@ -464,25 +335,23 @@ namespace mongo { } } - void Value::addToBsonObj(BSONObjBuilder *pBuilder, const std::string& fieldName) const { + void Value::addToBsonObj(BSONObjBuilder* pBuilder, StringData fieldName) const { + if (missing()) return; + BuilderObj objBuilder(pBuilder, fieldName); addToBson(&objBuilder); } - void Value::addToBsonArray(BSONArrayBuilder *pBuilder) const { + void Value::addToBsonArray(BSONArrayBuilder* pBuilder) const { + if (missing()) return; + BuilderArray arrBuilder(pBuilder); addToBson(&arrBuilder); } bool Value::coerceToBool() const { // TODO Unify the implementation with BSONElement::trueValue(). - BSONType type = getType(); - switch(type) { - case NumberDouble: - if (doubleValue != 0) - return true; - break; - + switch(getType()) { case String: case Object: case Array: @@ -494,135 +363,112 @@ namespace mongo { case Timestamp: return true; - case Bool: - if (boolValue) - return true; - break; - - case CodeWScope: - verify(false); // CW TODO unimplemented - break; - - case NumberInt: - if (intValue != 0) - return true; - break; - - case NumberLong: - if (longValue != 0) - return true; - break; - case jstNULL: case Undefined: - /* nothing to do */ - break; + return false; + + case Bool: return _storage.boolValue; + case NumberInt: return _storage.intValue; + case NumberLong: return _storage.longValue; + case NumberDouble: return _storage.doubleValue; /* these shouldn't happen in this context */ + case CodeWScope: case MinKey: case EOO: case DBRef: case Code: case MaxKey: + default: verify(false); // CW TODO better message - break; } - - return false; } int Value::coerceToInt() const { - switch(type) { + switch(getType()) { case NumberDouble: - return (int)doubleValue; + return _storage.doubleValue; case NumberInt: - return intValue; + return _storage.intValue; case NumberLong: - return (int)longValue; + return _storage.longValue; case jstNULL: case Undefined: - break; + return 0; case String: default: uassert(16003, str::stream() << - "can't convert from BSON type " << typeName(type) << + "can't convert from BSON type " << typeName(getType()) << " to int", false); - } // switch(type) - - return (int)0; + } // switch(getType()) } long long Value::coerceToLong() const { - switch(type) { + switch(getType()) { case NumberDouble: - return (long long)doubleValue; + return _storage.doubleValue; case NumberInt: - return intValue; + return _storage.intValue; case NumberLong: - return longValue; + return _storage.longValue; case jstNULL: case Undefined: - break; + return 0; case String: default: uassert(16004, str::stream() << - "can't convert from BSON type " << typeName(type) << + "can't convert from BSON type " << typeName(getType()) << " to long", false); - } // switch(type) - - return (long long)0; + } // switch(getType()) } double Value::coerceToDouble() const { - switch(type) { + switch(getType()) { case NumberDouble: - return doubleValue; + return _storage.doubleValue; case NumberInt: - return (double)intValue; + return _storage.intValue; case NumberLong: - return (double)longValue; + return _storage.longValue; case jstNULL: case Undefined: - break; + return 0; case String: default: uassert(16005, str::stream() << - "can't convert from BSON type " << typeName(type) << + "can't convert from BSON type " << typeName(getType()) << " to double", false); - } // switch(type) - - return (double)0; + } // switch(getType()) } long long Value::coerceToDate() const { - switch(type) { - + switch(getType()) { case Date: - return dateValue; + return getDate(); case Timestamp: return getTimestamp().getSecs() * 1000LL; default: uassert(16006, str::stream() << - "can't convert from BSON type " << typeName(type) << " to Date", + "can't convert from BSON type " << typeName(getType()) << " to Date", false); - } // switch(type) + } // switch(getType()) } time_t Value::coerceToTimeT() const { @@ -678,21 +524,21 @@ namespace mongo { string Value::coerceToString() const { stringstream ss; - switch(type) { + switch(getType()) { case NumberDouble: - ss << doubleValue; + ss << _storage.doubleValue; return ss.str(); case NumberInt: - ss << intValue; + ss << _storage.intValue; return ss.str(); case NumberLong: - ss << longValue; + ss << _storage.longValue; return ss.str(); case String: - return stringValue; + return getString(); case Timestamp: ss << getTimestamp().toStringPretty(); @@ -703,36 +549,33 @@ namespace mongo { case jstNULL: case Undefined: - break; + return ""; default: uassert(16007, str::stream() << - "can't convert from BSON type " << typeName(type) << + "can't convert from BSON type " << typeName(getType()) << " to String", false); - } // switch(type) - - return ""; + } // switch(getType()) } OpTime Value::coerceToTimestamp() const { - switch(type) { - + switch(getType()) { case Timestamp: - return timestampValue; + return getTimestamp(); default: uassert(16378, str::stream() << - "can't convert from BSON type " << typeName(type) << + "can't convert from BSON type " << typeName(getType()) << " to timestamp", false); - } // switch(type) + } // switch(getType()) } - int Value::compare(const intrusive_ptr<const Value> &rL, - const intrusive_ptr<const Value> &rR) { - BSONType lType = rL->getType(); - BSONType rType = rR->getType(); + int Value::compare(const Value& rL, const Value& rR) { + // missing is treated as undefined for compatibility with BSONObj::woCompare + BSONType lType = rL.missing() ? Undefined : rL.getType(); + BSONType rType = rR.missing() ? Undefined : rR.getType(); /* Special handling for Undefined and NULL values; these are types, @@ -775,8 +618,8 @@ namespace mongo { /* if the biggest type of either is a double, compare as doubles */ if ((lType == NumberDouble) || (rType == NumberDouble)) { - const double left = rL->getDouble(); - const double right = rR->getDouble(); + const double left = rL.getDouble(); + const double right = rR.getDouble(); if (left < right) return -1; if (left > right) @@ -786,8 +629,8 @@ namespace mongo { /* if the biggest type of either is a long, compare as longs */ if ((lType == NumberLong) || (rType == NumberLong)) { - const long long left = rL->getLong(); - const long long right = rR->getLong(); + const long long left = rL.getLong(); + const long long right = rR.getLong(); if (left < right) return -1; if (left > right) @@ -797,8 +640,8 @@ namespace mongo { /* if we got here, they must both be ints; compare as ints */ { - const int left = rL->getInt(); - const int right = rR->getInt(); + const int left = rL.getInt(); + const int right = rR.getInt(); if (left < right) return -1; if (left > right) @@ -820,40 +663,47 @@ namespace mongo { /* these types were handled above */ verify(false); - case String: - return rL->stringValue.compare(rR->stringValue); + case String: { + StringData r = rR.getStringData(); + StringData l = rL.getStringData(); + size_t bytes = min(r.size(), l.size()); + int ret = memcmp(l.data(), r.data(), bytes); + if (ret) + return ret; + return l.size() - r.size(); + } case Object: - return Document::compare(rL->getDocument(), rR->getDocument()); + return Document::compare(rL.getDocument(), rR.getDocument()); case Array: { - intrusive_ptr<ValueIterator> pli(rL->getArray()); - intrusive_ptr<ValueIterator> pri(rR->getArray()); - - while(true) { - /* have we run out of left array? */ - if (!pli->more()) { - if (!pri->more()) - return 0; // the arrays are the same length - - return -1; // the left array is shorter - } - - /* have we run out of right array? */ - if (!pri->more()) - return 1; // the right array is shorter - - /* compare the two corresponding elements */ - intrusive_ptr<const Value> plv(pli->next()); - intrusive_ptr<const Value> prv(pri->next()); - const int cmp = Value::compare(plv, prv); + const vector<Value>& lArr = rL.getArray(); + const vector<Value>& rArr = rR.getArray(); + + vector<Value>::const_iterator lIt = lArr.begin(); + vector<Value>::const_iterator lEnd = lArr.end(); + vector<Value>::const_iterator rIt = rArr.begin(); + vector<Value>::const_iterator rEnd = rArr.end(); + + for ( ; lIt != lEnd && rIt != rEnd; ++lIt, ++rIt ) { + // compare the two corresponding elements + const int cmp = Value::compare(*lIt, *rIt); if (cmp) return cmp; // values are unequal } - /* NOTREACHED */ - verify(false); - break; + if (lIt == lEnd && rIt == rEnd) { + return 0; // the arrays are the same length + } + else if (lIt == lEnd && rIt != rEnd) { + return -1; // the left array is shorter + } + else if (lIt != lEnd && rIt == rEnd) { + return 1; // the right array is shorter + } + else { + verify(false); // we shouldn't have exited the loop in this case + } } case BinData: @@ -862,26 +712,25 @@ namespace mongo { uassert(16017, str::stream() << "comparisons of values of BSON type " << typeName(lType) << " are not supported", false); - // pBuilder->appendBinData(fieldName, ...); break; case jstOID: - if (rL->getOid() < rR->getOid()) + if (rL.getOid() < rR.getOid()) return -1; - if (rL->getOid() == rR->getOid()) + if (rL.getOid() == rR.getOid()) return 0; return 1; case Bool: - if (rL->boolValue == rR->boolValue) + if (rL.getBool() == rR.getBool()) return 0; - if (rL->boolValue) + if (rL.getBool()) return 1; return -1; case Date: { - long long l = rL->dateValue; - long long r = rR->dateValue; + long long l = rL.getDate(); + long long r = rR.getDate(); if (l < r) return -1; if (l > r) @@ -890,12 +739,12 @@ namespace mongo { } case RegEx: - return rL->stringValue.compare(rR->stringValue); + return rL.getRegex().compare(rR.getRegex()); case Timestamp: - if (rL->timestampValue < rR->timestampValue) + if (rL.getTimestamp() < rR.getTimestamp()) return -1; - if (rL->timestampValue > rR->timestampValue) + if (rL.getTimestamp() > rR.getTimestamp()) return 1; return 0; @@ -910,17 +759,13 @@ namespace mongo { case Code: case MaxKey: verify(false); - break; } // switch(lType) - /* NOTREACHED */ - return 0; + verify(false); } void Value::hash_combine(size_t &seed) const { - BSONType type = getType(); - - switch(type) { + switch(getType()) { /* Numbers whose values are equal need to hash to the same thing as well. Note that Value::compare() promotes numeric values to @@ -933,27 +778,25 @@ namespace mongo { */ case NumberDouble: case NumberLong: - case NumberInt: - { - const double d = getDouble(); - boost::hash_combine(seed, d); + case NumberInt: { + boost::hash_combine(seed, getDouble()); break; } - case String: - boost::hash_combine(seed, stringValue); + case String: { + StringData sd = getStringData(); + boost::hash_range(seed, sd.data(), (sd.data() + sd.size())); break; + } case Object: getDocument()->hash_combine(seed); break; case Array: { - intrusive_ptr<ValueIterator> pIter(getArray()); - while(pIter->more()) { - intrusive_ptr<const Value> pValue(pIter->next()); - pValue->hash_combine(seed); - }; + const vector<Value>& vec = getArray(); + for (size_t i=0; i < vec.size(); i++) + vec[i].hash_combine(seed); break; } @@ -961,7 +804,7 @@ namespace mongo { case Symbol: case CodeWScope: uassert(16018, str::stream() << - "hashes of values of BSON type " << typeName(type) << + "hashes of values of BSON type " << typeName(getType()) << " are not supported", false); break; @@ -970,19 +813,19 @@ namespace mongo { break; case Bool: - boost::hash_combine(seed, boolValue); + boost::hash_combine(seed, getBool()); break; case Date: - boost::hash_combine(seed, dateValue); + boost::hash_combine(seed, getDate()); break; case RegEx: - boost::hash_combine(seed, stringValue); + boost::hash_combine(seed, getRegex()); break; case Timestamp: - boost::hash_combine(seed, timestampValue); + boost::hash_combine(seed, _storage.timestampValue); break; case Undefined: @@ -997,7 +840,7 @@ namespace mongo { case MaxKey: verify(false); // CW TODO better message break; - } // switch(type) + } // switch(getType()) } BSONType Value::getWidestNumeric(BSONType lType, BSONType rType) { @@ -1067,18 +910,19 @@ namespace mongo { } size_t Value::getApproximateSize() const { - switch(type) { + switch(getType()) { case String: - return sizeof(Value) + stringValue.length(); + return sizeof(Value) + sizeof(RCString) + getStringData().size(); case Object: - return sizeof(Value) + pDocumentValue->getApproximateSize(); + return sizeof(Value) + getDocument()->getApproximateSize(); case Array: { size_t size = sizeof(Value); - const size_t n = vpValue.size(); + size += sizeof(RCVector); + const size_t n = getArray().size(); for(size_t i = 0; i < n; ++i) { - size += vpValue[i]->getApproximateSize(); + size += getArray()[i].getApproximateSize(); } return size; } @@ -1105,7 +949,6 @@ namespace mongo { case Code: case MaxKey: verify(false); // CW TODO better message - return sizeof(Value); } /* @@ -1116,14 +959,58 @@ namespace mongo { this final catch-all is here. */ verify(false); - return sizeof(Value); } + string Value::toString() const { + // TODO use StringBuilder when operator << is ready + stringstream out; + out << *this; + return out.str(); + } + + ostream& operator << (ostream& out, const Value& val) { + if (val.missing()) return out << "MISSING"; + + switch(val.getType()) { + case jstOID: return out << val.getOid(); + case String: return out << '"' << val.getString() << '"'; + case RegEx: return out << '/' << val.getRegex() << '/'; + case Symbol: return out << val.getSymbol(); + case Bool: return out << (val.getBool() ? "true" : "false"); + case NumberDouble: return out << val.getDouble(); + case NumberLong: return out << val.getLong(); + case NumberInt: return out << val.getInt(); + case jstNULL: return out << "null"; + case Undefined: return out << "undefined"; + case Date: return out << Date_t(val.getDate()).toString(); + case Timestamp: return out << val.getTimestamp().toString(); + case Object: return out << val.getDocument()->toString(); + case Array: { + out << "["; + const size_t n = val.getArray().size(); + for(size_t i = 0; i < n; i++) { + if (i) + out << ", "; + out << val.getArray()[i]; + } + out << "]"; + return out; + } - void ValueStatic::addRef() const { - } + /* these shouldn't happen in this context */ + case CodeWScope: + case BinData: + case MinKey: + case EOO: + case DBRef: + case Code: + case MaxKey: + verify(false); // CW TODO better message + } - void ValueStatic::release() const { + + // Not in default case to trigger better warning if a case is missing + verify(false); } } diff --git a/src/mongo/db/pipeline/value.h b/src/mongo/db/pipeline/value.h index 6a99b673a70..fe16446e968 100755..100644 --- a/src/mongo/db/pipeline/value.h +++ b/src/mongo/db/pipeline/value.h @@ -16,465 +16,285 @@ #pragma once -#include "mongo/pch.h" -#include "bson/bsontypes.h" -#include "bson/oid.h" -#include "util/intrusive_counter.h" -#include "util/optime.h" +#include "mongo/db/pipeline/value_internal.h" namespace mongo { class BSONElement; class Builder; - class Document; - class Value; - class ValueIterator : - public IntrusiveCounterUnsigned { - public: - virtual ~ValueIterator(); - - /* - Ask if there are more fields to return. - - @returns true if there are more fields, false otherwise - */ - virtual bool more() const = 0; - - /* - Move the iterator to point to the next field and return it. - - @returns the next field's <name, Value> - */ - virtual intrusive_ptr<const Value> next() = 0; - }; - - - /* - Values are immutable, so these are passed around as - intrusive_ptr<const Value>. + /** A variant type that can hold any type of data representable in BSON + * + * Small values are stored inline, but some values, such as large strings, + * are heap allocated. It has smart pointer capabilities built-in so it is + * safe and recommended to pass these around and return them by value. + * + * Values are immutable, but can be assigned. This means that once you have + * a Value, you can be assured that none of the data in that Value will + * change. However if you have a non-const Value you replace it with + * operator=. These rules are the same as BSONObj, and similar to + * shared_ptr<const Object> with stronger guarantees of constness. This is + * also the same as Java's String type. + * + * Thread-safety: A single Value instance can be safely shared between + * threads as long as there are no writers while other threads are + * accessing the object. Any number of threads can read from a Value + * concurrently. There are no restrictions on how threads access Value + * instances exclusively owned by them, even if they reference the same + * storage as Value in other threads. */ - class Value : - public IntrusiveCounterUnsigned { + class Value { public: - ~Value(); - - /* - Construct a Value from a BSONElement. - - This ignores the name of the element, and only uses the value, - whatever type it is. - - @returns a new Value initialized from the bsonElement - */ - static intrusive_ptr<const Value> createFromBsonElement( - BSONElement *pBsonElement); - - /* - Construct an integer-valued Value. - - For commonly used values, consider using one of the singleton - instances defined below. - - @param value the value - @returns a Value with the given value - */ - static intrusive_ptr<const Value> createInt(int value); - - /* - Construct a long or interger-valued Value. - Used when preforming arithmetic operations with int where the result may be too large - and need to be stored as long. The Value will be an int if value fits, otherwise it - will be a long. - - @param value the value - @returns a Value with the given value - */ - static intrusive_ptr<const Value> createIntOrLong(long long value); - - /* - Construct an long(long)-valued Value. - - For commonly used values, consider using one of the singleton - instances defined below. - - @param value the value - @returns a Value with the given value - */ - static intrusive_ptr<const Value> createLong(long long value); - - /* - Construct a double-valued Value. - - @param value the value - @returns a Value with the given value - */ - static intrusive_ptr<const Value> createDouble(double value); - - /* - Construct a string-valued Value. - - @param value the value - @returns a Value with the given value - */ - static intrusive_ptr<const Value> createString(const string &value); - - /* - Construct a date-valued Value. - - @param value the value - @returns a Value with the given value - */ - static intrusive_ptr<const Value> createDate(const long long &value); - - static intrusive_ptr<const Value> createTimestamp(const OpTime& value); - - /* - Construct a document-valued Value. + /** Construct a Value + * + * All types not listed will be rejected rather than converted (see private for why) + * + * Note: Currently these are all explicit conversions. + * I'm not sure if we want implicit or not. + * //TODO decide + */ - @param value the value - @returns a Value with the given value - */ - static intrusive_ptr<const Value> createDocument( - const intrusive_ptr<Document> &pDocument); + Value(): _storage() {} // "Missing" value + explicit Value(bool value) : _storage(Bool, value) {} + explicit Value(int value) : _storage(NumberInt, value) {} + explicit Value(long long value) : _storage(NumberLong, value) {} + explicit Value(double value) : _storage(NumberDouble, value) {} + explicit Value(const OpTime& value) : _storage(Timestamp, value.asDate()) {} + explicit Value(StringData value) : _storage(String, value) {} + explicit Value(const string& value) : _storage(String, StringData(value)) {} + explicit Value(const char* value) : _storage(String, StringData(value)) {} + explicit Value(const Document& doc) : _storage(Object, doc) {} + explicit Value(const vector<Value>& vec) : _storage(Array, new RCVector(vec)) {} + + /** Creates an empty or zero value of specified type. + * This is currently the only way to create Undefined or Null Values. + */ + explicit Value(BSONType type); - /* - Construct an array-valued Value. + // TODO: add an unsafe version that can share storage with the BSONElement + /// Deep-convert from BSONElement to Value + explicit Value(const BSONElement& elem); - @param value the value - @returns a Value with the given value + /** Construct a long or integer-valued Value. + * + * Used when preforming arithmetic operations with int where the + * result may be too large and need to be stored as long. The Value + * will be an int if value fits, otherwise it will be a long. */ - static intrusive_ptr<const Value> createArray( - const vector<intrusive_ptr<const Value> > &vpValue); + static Value createIntOrLong(long long value); - /* - Get the BSON type of the field. - - If the type is jstNULL, no value getter will work. - - @return the BSON type of the field. - */ - BSONType getType() const; + /** A "missing" value indicates the lack of a Value. + * This is similar to undefined/null but should not appear in output to BSON. + * Missing Values are returned by Document when accessing non-existent fields. + */ + bool missing() const { return _storage.type == EOO; } - /* - Getters. + /** Get the BSON type of the field. + * Warning: currently asserts if missing. This will probably change in the future. + */ + BSONType getType() const { return _storage.bsonType(); } - @returns the Value's value; asserts if the requested value type is - incorrect. - */ + /** Exact type getters. + * Asserts if the requested value type is not exactly correct. + * See coerceTo methods below for a more type-flexible alternative. + */ double getDouble() const; string getString() const; - intrusive_ptr<Document> getDocument() const; - intrusive_ptr<ValueIterator> getArray() const; + StringData getStringData() const; // May contain embedded NUL bytes + Document getDocument() const; OID getOid() const; bool getBool() const; - long long getDate() const; + long long getDate() const; // in milliseconds OpTime getTimestamp() const; string getRegex() const; string getSymbol() const; int getInt() const; long long getLong() const; - - /* - Get the length of an array value. - - @returns the length of the array, if this is array-valued; otherwise - throws an error - */ + const vector<Value>& getArray() const { return _storage.getArray(); } size_t getArrayLength() const; - /* - Add this value to the BSON object under construction. - */ - void addToBsonObj(BSONObjBuilder *pBuilder, const std::string& fieldName) const; + /// Access an element of a subarray. Returns Value() if missing or getType() != Array + Value operator[] (size_t index) const; - /* - Add this field to the BSON array under construction. + /// Access a field of a subdocument. Returns Value() if missing or getType() != Object + Value operator[] (StringData name) const; - As part of an array, the Value's name will be ignored. - */ - void addToBsonArray(BSONArrayBuilder *pBuilder) const; + /// Add this value to the BSON object under construction. + void addToBsonObj(BSONObjBuilder* pBuilder, StringData fieldName) const; - /* - Get references to singleton instances of commonly used field values. - */ - static intrusive_ptr<const Value> getUndefined(); - static intrusive_ptr<const Value> getNull(); - static intrusive_ptr<const Value> getTrue(); - static intrusive_ptr<const Value> getFalse(); - static intrusive_ptr<const Value> getMinusOne(); - static intrusive_ptr<const Value> getZero(); - static intrusive_ptr<const Value> getOne(); - - /** - * Coerce (cast) a value to a native bool using BSONElement::trueValue() rules, but with - * some types unsupported. SERVER-6120 - * @return the bool value + /// Add this field to the BSON array under construction. + void addToBsonArray(BSONArrayBuilder* pBuilder) const; + + /** Coerce a value to a bool using BSONElement::trueValue() rules. + * Some types unsupported. SERVER-6120 */ bool coerceToBool() const; - /* - Coerce (cast) a value to an int, using JSON rules. - - @returns the int value - */ + /** Coercion operators to extract values with fuzzy type logic. + * + * These currently assert if called on an unconvertible type. + * TODO: decided how to handle unsupported types. + */ + string coerceToString() const; int coerceToInt() const; - - /* - Coerce (cast) a value to a long long, using JSON rules. - - @returns the long value - */ long long coerceToLong() const; - - /* - Coerce (cast) a value to a double, using JSON rules. - - @returns the double value - */ double coerceToDouble() const; - - /* - Coerce (cast) a value to a date, using JSON rules. - - @returns the date value - */ + OpTime coerceToTimestamp() const; long long coerceToDate() const; time_t coerceToTimeT() const; tm coerceToTm() const; // broken-out time struct (see man gmtime) - OpTime coerceToTimestamp() const; - - /* - Coerce (cast) a value to a string, using JSON rules. - @returns the date value - */ - string coerceToString() const; - - /* - Compare two Values. - - @param rL left value - @param rR right value - @returns an integer less than zero, zero, or an integer greater than - zero, depending on whether rL < rR, rL == rR, or rL > rR + /** Compare two Values. + * @returns an integer less than zero, zero, or an integer greater than + * zero, depending on whether lhs < rhs, lhs == rhs, or lhs > rhs + * Warning: may return values other than -1, 0, or 1 + */ + static int compare(const Value& lhs, const Value& rhs); + + friend + bool operator==(const Value& v1, const Value& v2) { + if (v1._storage.identical(v2._storage)) { + // Simple case + return true; + } + return (Value::compare(v1, v2) == 0); + } + + /// This is for debugging, logging, etc. See getString() for how to extract a string. + string toString() const; + friend ostream& operator << (ostream& out, const Value& v); + + void swap(Value& rhs) { + _storage.swap(rhs._storage); + } + + /** Figure out what the widest of two numeric types is. + * + * Widest can be thought of as "most capable," or "able to hold the + * largest or most precise value." The progression is Int, Long, Double. */ - static int compare(const intrusive_ptr<const Value> &rL, - const intrusive_ptr<const Value> &rR); - - - /* - Figure out what the widest of two numeric types is. - - Widest can be thought of as "most capable," or "able to hold the - largest or most precise value." The progression is Int, Long, Double. - - @param rL left value - @param rR right value - @returns a BSONType of NumberInt, NumberLong, or NumberDouble - */ static BSONType getWidestNumeric(BSONType lType, BSONType rType); - /* - Get the approximate storage size of the value, in bytes. - - @returns approximate storage size of the value. - */ + /// Get the approximate memory size of the value, in bytes. Includes sizeof(Value) size_t getApproximateSize() const; - /* - Calculate a hash value. - - Meant to be used to create composite hashes suitable for - boost classes such as unordered_map<>. - - @param seed value to augment with this' hash - */ - void hash_combine(size_t &seed) const; - - /* - struct Hash is defined to enable the use of Values as - keys in boost::unordered_map<>. + /** Calculate a hash value. + * + * Meant to be used to create composite hashes suitable for + * hashed container classes such as unordered_map<>. + */ + void hash_combine(size_t& seed) const; - Values are always referenced as immutables in the form - intrusive_ptr<const Value>, so these operate on that construction. - */ - struct Hash : - unary_function<intrusive_ptr<const Value>, size_t> { - size_t operator()(const intrusive_ptr<const Value> &rV) const; + /// struct Hash is defined to enable the use of Values as keys in unordered_map. + struct Hash : unary_function<const Value&, size_t> { + size_t operator()(const Value& rV) const; }; - protected: - Value(); // creates null value - Value(BSONType type); // creates an empty (unitialized value) of type - // mostly useful for Undefined - Value(bool boolValue); - Value(int intValue); - - private: - Value(BSONElement *pBsonElement); - - Value(long long longValue); - Value(double doubleValue); - Value(const OpTime& timestampValue); - Value(const string &stringValue); - Value(const intrusive_ptr<Document> &pDocument); - Value(const vector<intrusive_ptr<const Value> > &vpValue); - - void addToBson(Builder *pBuilder) const; - - BSONType type; - - // store values that don't need a ctor/dtor in one of these - union { - double doubleValue; - bool boolValue; - int intValue; - long long longValue; - ReplTime timestampValue; - unsigned char oidValue[12]; - // The member below is redundant, but useful for clarity and searchability. - long long dateValue; - }; + /// Call this after memcpying to update ref counts if needed + void memcpyed() const { _storage.memcpyed(); } - string stringValue; // String, Regex, Symbol - intrusive_ptr<Document> pDocumentValue; - vector<intrusive_ptr<const Value> > vpValue; // for arrays + // LEGACY creation functions + static Value createFromBsonElement(const BSONElement* pBsonElement); + static Value createInt(int value) { return Value(value); } + static Value createLong(long long value) { return Value(value); } + static Value createDouble(double value) { return Value(value); } + static Value createTimestamp(const OpTime& value) { return Value(value); } + static Value createString(const string& value) { return Value(value); } + static Value createDocument(const Document& doc) { return Value(doc); } + static Value createArray(const vector<Value>& vec) { return Value(vec); } + static Value createDate(const long long value); - /* - These are often used as the result of boolean or comparison - expressions. + private: + /** This is a "honeypot" to prevent unexpected implicit conversions to the accepted argument + * types. bool is especially bad since without this it will accept any pointer. + * + * Template argument name was chosen to make produced error easier to read. + */ + template <typename InvalidArgumentType> Value(const InvalidArgumentType& invalidArgument); - These are obtained via public static getters defined above. - */ - static const intrusive_ptr<const Value> pFieldUndefined; - static const intrusive_ptr<const Value> pFieldNull; - static const intrusive_ptr<const Value> pFieldTrue; - static const intrusive_ptr<const Value> pFieldFalse; - static const intrusive_ptr<const Value> pFieldMinusOne; - static const intrusive_ptr<const Value> pFieldZero; - static const intrusive_ptr<const Value> pFieldOne; - - /* this implementation is used for getArray() */ - class vi : - public ValueIterator { - public: - // virtuals from ValueIterator - virtual ~vi(); - virtual bool more() const; - virtual intrusive_ptr<const Value> next(); - - private: - friend class Value; - vi(const intrusive_ptr<const Value> &pSource, - const vector<intrusive_ptr<const Value> > *pvpValue); - - size_t size; - size_t nextIndex; - const vector<intrusive_ptr<const Value> > *pvpValue; - }; /* class vi */ + void addToBson(Builder* pBuilder) const; + ValueStorage _storage; + friend class MutableValue; // gets and sets _storage.genericRCPtr }; + BOOST_STATIC_ASSERT(sizeof(Value) == 16); +} - /* - Equality operator for values. - - Useful for unordered_map<>, etc. - */ - inline bool operator==(const intrusive_ptr<const Value> &v1, - const intrusive_ptr<const Value> &v2) { - return (Value::compare(v1, v2) == 0); - } - - /* - For performance reasons, there are various sharable static values - defined in class Value, obtainable by methods such as getUndefined(), - getTrue(), getOne(), etc. We don't want these to go away as they are - used by a multitude of threads evaluating pipelines. In order to avoid - having to use atomic integers in the intrusive reference counter, this - class overrides the reference counting methods to do nothing, making it - safe to use for static Values. - - At this point, only the constructors necessary for the static Values in - common use have been defined. The remainder can be defined if necessary. - */ - class ValueStatic : - public Value { - public: - // virtuals from IntrusiveCounterUnsigned - virtual void addRef() const; - virtual void release() const; - - // constructors - ValueStatic(); - ValueStatic(BSONType type); - ValueStatic(bool boolValue); - ValueStatic(int intValue); - }; +namespace std { + // This is used by std::sort and others + template <> + inline void swap(mongo::Value& lhs, mongo::Value& rhs) { lhs.swap(rhs); } } /* ======================= INLINED IMPLEMENTATIONS ========================== */ namespace mongo { - inline BSONType Value::getType() const { - return type; - } - inline size_t Value::getArrayLength() const { verify(getType() == Array); - return vpValue.size(); + return getArray().size(); } - inline intrusive_ptr<const Value> Value::getUndefined() { - return pFieldUndefined; + inline size_t Value::Hash::operator()(const Value& v) const { + size_t seed = 0xf0afbeef; + v.hash_combine(seed); + return seed; } - inline intrusive_ptr<const Value> Value::getNull() { - return pFieldNull; + inline StringData Value::getStringData() const { + verify(getType() == String); + return _storage.getString(); } - inline intrusive_ptr<const Value> Value::getTrue() { - return pFieldTrue; + inline string Value::getString() const { + verify(getType() == String); + StringData sd = _storage.getString(); + return string(sd.data(), sd.size()); } - inline intrusive_ptr<const Value> Value::getFalse() { - return pFieldFalse; + inline OID Value::getOid() const { + verify(getType() == jstOID); + return OID(_storage.oid); } - inline intrusive_ptr<const Value> Value::getMinusOne() { - return pFieldMinusOne; + inline bool Value::getBool() const { + verify(getType() == Bool); + return _storage.boolValue; } - inline intrusive_ptr<const Value> Value::getZero() { - return pFieldZero; + inline long long Value::getDate() const { + verify(getType() == Date); + return _storage.dateValue; } - inline intrusive_ptr<const Value> Value::getOne() { - return pFieldOne; + inline OpTime Value::getTimestamp() const { + verify(getType() == Timestamp); + return _storage.timestampValue; } - inline size_t Value::Hash::operator()( - const intrusive_ptr<const Value> &rV) const { - size_t seed = 0xf0afbeef; - rV->hash_combine(seed); - return seed; + inline string Value::getRegex() const { + verify(getType() == RegEx); + StringData sd = _storage.getString(); + return string(sd.data(), sd.size()); } - inline ValueStatic::ValueStatic(): - Value() { + inline string Value::getSymbol() const { + verify(getType() == Symbol); + StringData sd = _storage.getString(); + return string(sd.data(), sd.size()); } - inline ValueStatic::ValueStatic(BSONType type): - Value(type) { + inline int Value::getInt() const { + verify(getType() == NumberInt); + return _storage.intValue; } - inline ValueStatic::ValueStatic(bool boolValue): - Value(boolValue) { - } + inline long long Value::getLong() const { + BSONType type = getType(); + if (type == NumberInt) + return _storage.intValue; - inline ValueStatic::ValueStatic(int intValue): - Value(intValue) { + verify(type == NumberLong); + return _storage.longValue; } - }; diff --git a/src/mongo/db/pipeline/value_internal.h b/src/mongo/db/pipeline/value_internal.h new file mode 100644 index 00000000000..578529158a9 --- /dev/null +++ b/src/mongo/db/pipeline/value_internal.h @@ -0,0 +1,182 @@ +/** + * Copyright (c) 2012 10gen Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#pragma once + +#include <algorithm> +#include "bson/bsontypes.h" +#include "bson/oid.h" +#include "util/intrusive_counter.h" +#include "util/optime.h" + + +namespace mongo { + class Document; + class DocumentStorage; + class Value; + + //TODO: a MutableVector, similar to MutableDocument + /// A heap-allocated reference-counted std::vector + class RCVector : public RefCountable { + public: + RCVector() {} + RCVector(const vector<Value>& v) :vec(v) {} + vector<Value> vec; + }; + +#pragma pack(1) + class ValueStorage { + public: + // This is a "missing" Value + ValueStorage() { zero(); type = EOO; } + + ValueStorage(BSONType t) { zero(); type = t;} + ValueStorage(BSONType t, int i) { zero(); type = t; intValue = i; } + ValueStorage(BSONType t, long long l) { zero(); type = t; longValue = l; } + ValueStorage(BSONType t, double d) { zero(); type = t; doubleValue = d; } + ValueStorage(BSONType t, ReplTime r) { zero(); type = t; timestampValue = r; } + ValueStorage(BSONType t, bool b) { zero(); type = t; boolValue = b; } + ValueStorage(BSONType t, const Document& d) { zero(); type = t; putDocument(d); } + ValueStorage(BSONType t, const RCVector* a) { zero(); type = t; putVector(a); } + ValueStorage(BSONType t, StringData s) { zero(); type = t; putString(s); } + + ValueStorage(BSONType t, OID& o) { + zero(); + type = t; + memcpy(&oid, &o, sizeof(OID)); + BOOST_STATIC_ASSERT(sizeof(OID) == sizeof(oid)); + } + + ValueStorage(const ValueStorage& rhs) { + memcpy(this, &rhs, sizeof(*this)); + memcpyed(); + } + + ~ValueStorage() { + if (refCounter) + intrusive_ptr_release(genericRCPtr); + DEV memset(this, 0xee, sizeof(*this)); + } + + ValueStorage& operator= (ValueStorage rhsCopy) { + this->swap(rhsCopy); + return *this; + } + + void swap(ValueStorage& rhs) { + // Don't need to update ref-counts because they will be the same in the end + char temp[sizeof(ValueStorage)]; + memcpy(temp, this, sizeof(*this)); + memcpy(this, &rhs, sizeof(*this)); + memcpy(&rhs, temp, sizeof(*this)); + } + + /// Call this after memcpying to update ref counts if needed + void memcpyed() const { + if (refCounter) + intrusive_ptr_add_ref(genericRCPtr); + } + + /// These are only to be called during Value construction on an empty Value + void putString(StringData s); + void putVector(const RCVector* v); + void putDocument(const Document& d); + + StringData getString() const { + if (shortStr) { + return StringData(shortStrStorage, shortStrSize); + } + else { + dassert(typeid(*genericRCPtr) == typeid(const RCString)); + const RCString* stringPtr = static_cast<const RCString*>(genericRCPtr); + return StringData(stringPtr->c_str(), stringPtr->size()); + } + } + + const vector<Value>& getArray() const { + dassert(typeid(*genericRCPtr) == typeid(const RCVector)); + const RCVector* arrayPtr = static_cast<const RCVector*>(genericRCPtr); + return arrayPtr->vec; + } + + // Document is incomplete here so this can't be inline + Document getDocument() const; + + BSONType bsonType() const { + verify(type != EOO); + return type; + } + + void zero() { + // This is important for identical() + memset(this, 0, sizeof(*this)); + } + + // Byte-for-byte identical + bool identical(const ValueStorage& other) const { + return (i64[0] == other.i64[0] + && i64[1] == other.i64[1]); + } + + // This data is public because this should only be used by Value which would be a friend + union { + struct { + BSONType type : 8; // one byte (offset 0) + union { // one byte (offset 1) + unsigned int flags : 8; + struct { + bool refCounter : 1; // true if we need to refCount + bool shortStr : 1; // true if we are using short strings + }; + }; + union { // 14 bytes (offset 2) + unsigned char oid[12]; + struct { + int shortStrSize : 8; // TODO Consider moving into flags union (4 bits) + char shortStrStorage[16 - 3]; // ValueStorage is 16 bytes, 3 byte offset + }; + struct { + union { // 6 bytes (offset 2) + char pad[6]; + char stringCache[6]; // TODO copy first few bytes of strings in here + }; + union { // 8 bytes (offset 8 and aligned) + // There should be no pointers to non-const data + const RefCountable* genericRCPtr; + + double doubleValue; + bool boolValue; + int intValue; + long long longValue; + ReplTime timestampValue; + long long dateValue; + }; + }; + }; + }; + long long i64[2]; + + // Note the following are currently unused, but may be useful later + int i32[4]; + unsigned long long u64[2]; + unsigned int u32[4]; + char raw[16]; + }; + }; + BOOST_STATIC_ASSERT(sizeof(ValueStorage) == 16); +#pragma pack() + +} diff --git a/src/mongo/dbtests/accumulatortests.cpp b/src/mongo/dbtests/accumulatortests.cpp index 6c329962740..efec273c197 100644 --- a/src/mongo/dbtests/accumulatortests.cpp +++ b/src/mongo/dbtests/accumulatortests.cpp @@ -42,21 +42,21 @@ namespace AccumulatorTests { _router->setDoingMerge( true ); } protected: - intrusive_ptr<Document> fromjson( const string& json ) { + Document fromjson( const string& json ) { return frombson( mongo::fromjson( json ) ); } - intrusive_ptr<Document> frombson( const BSONObj& bson ) { + Document frombson( const BSONObj& bson ) { BSONObj myBson = bson; return Document::createFromBsonObj( &myBson ); } - BSONObj fromDocument( const intrusive_ptr<Document>& document ) { + BSONObj fromDocument( const Document& document ) { BSONObjBuilder bob; document->toBson( &bob ); return bob.obj(); } - BSONObj fromValue( const intrusive_ptr<const Value>& value ) { + BSONObj fromValue( const Value& value ) { BSONObjBuilder bob; - value->addToBsonObj( &bob, "" ); + value.addToBsonObj( &bob, "" ); return bob.obj(); } /** Check binary equality, ensuring use of the same numeric types. */ @@ -102,7 +102,7 @@ namespace AccumulatorTests { public: void run() { createAccumulator(); - ASSERT_EQUALS( 0, accumulator()->getValue()->getDouble() ); + ASSERT_EQUALS( 0, accumulator()->getValue().getDouble() ); } }; @@ -112,7 +112,7 @@ namespace AccumulatorTests { void run() { createAccumulator(); accumulator()->evaluate( frombson( BSON( "d" << 3 ) ) ); - ASSERT_EQUALS( 3, accumulator()->getValue()->getDouble() ); + ASSERT_EQUALS( 3, accumulator()->getValue().getDouble() ); } }; @@ -122,7 +122,7 @@ namespace AccumulatorTests { void run() { createAccumulator(); accumulator()->evaluate( frombson( BSON( "d" << -4LL ) ) ); - ASSERT_EQUALS( -4, accumulator()->getValue()->getDouble() ); + ASSERT_EQUALS( -4, accumulator()->getValue().getDouble() ); } }; @@ -132,7 +132,7 @@ namespace AccumulatorTests { void run() { createAccumulator(); accumulator()->evaluate( frombson( BSON( "d" << 22.6 ) ) ); - ASSERT_EQUALS( 22.6, accumulator()->getValue()->getDouble() ); + ASSERT_EQUALS( 22.6, accumulator()->getValue().getDouble() ); } }; @@ -143,7 +143,7 @@ namespace AccumulatorTests { createAccumulator(); accumulator()->evaluate( frombson( BSON( "d" << 10 ) ) ); accumulator()->evaluate( frombson( BSON( "d" << 11 ) ) ); - ASSERT_EQUALS( 10.5, accumulator()->getValue()->getDouble() ); + ASSERT_EQUALS( 10.5, accumulator()->getValue().getDouble() ); } }; @@ -154,7 +154,7 @@ namespace AccumulatorTests { createAccumulator(); accumulator()->evaluate( frombson( BSON( "d" << 10 ) ) ); accumulator()->evaluate( frombson( BSON( "d" << 11.0 ) ) ); - ASSERT_EQUALS( 10.5, accumulator()->getValue()->getDouble() ); + ASSERT_EQUALS( 10.5, accumulator()->getValue().getDouble() ); } }; @@ -165,7 +165,7 @@ namespace AccumulatorTests { createAccumulator(); accumulator()->evaluate( frombson( BSON( "d" << numeric_limits<int>::max() ) ) ); accumulator()->evaluate( frombson( BSON( "d" << numeric_limits<int>::max() ) ) ); - ASSERT_EQUALS( numeric_limits<int>::max(), accumulator()->getValue()->getDouble() ); + ASSERT_EQUALS( numeric_limits<int>::max(), accumulator()->getValue().getDouble() ); } }; @@ -180,7 +180,7 @@ namespace AccumulatorTests { ( frombson( BSON( "d" << numeric_limits<long long>::max() ) ) ); ASSERT_EQUALS( ( (double)numeric_limits<long long>::max() + numeric_limits<long long>::max() ) / 2.0, - accumulator()->getValue()->getDouble() ); + accumulator()->getValue().getDouble() ); } }; @@ -196,7 +196,7 @@ namespace AccumulatorTests { createAccumulator(); accumulator()->evaluate( frombson( operand() ) ); assertBinaryEqual( expectedResult(), - fromDocument( accumulator()->getValue()->getDocument() ) ); + fromDocument( accumulator()->getValue().getDocument() ) ); } protected: virtual BSONObj operand() = 0; @@ -237,7 +237,7 @@ namespace AccumulatorTests { accumulator()->evaluate( frombson( a ) ); accumulator()->evaluate( frombson( b ) ); assertBinaryEqual( expectedResult(), - fromDocument( accumulator()->getValue()->getDocument() ) ); + fromDocument( accumulator()->getValue().getDocument() ) ); } }; @@ -280,7 +280,7 @@ namespace AccumulatorTests { accumulator()->evaluate( frombson( BSON( "d" << 2LL ) ) ); accumulator()->evaluate( frombson( BSON( "d" << 4.0 ) ) ); assertBinaryEqual( BSON( "subTotal" << 7.0 << "count" << 3LL ), - fromDocument( accumulator()->getValue()->getDocument() ) ); + fromDocument( accumulator()->getValue().getDocument() ) ); } }; @@ -345,7 +345,7 @@ namespace AccumulatorTests { void run() { createAccumulator(); // The accumulator returns no value in this case. - ASSERT( !accumulator()->getValue() ); + ASSERT( accumulator()->getValue().missing() ); } }; @@ -355,7 +355,7 @@ namespace AccumulatorTests { void run() { createAccumulator(); accumulator()->evaluate( fromjson( "{a:5}" ) ); - ASSERT_EQUALS( 5, accumulator()->getValue()->getInt() ); + ASSERT_EQUALS( 5, accumulator()->getValue().getInt() ); } }; @@ -365,7 +365,7 @@ namespace AccumulatorTests { void run() { createAccumulator(); accumulator()->evaluate( fromjson( "{}" ) ); - ASSERT_EQUALS( Undefined, accumulator()->getValue()->getType() ); + ASSERT_EQUALS( Undefined, accumulator()->getValue().getType() ); } }; @@ -376,7 +376,7 @@ namespace AccumulatorTests { createAccumulator(); accumulator()->evaluate( fromjson( "{a:5}" ) ); accumulator()->evaluate( fromjson( "{a:7}" ) ); - ASSERT_EQUALS( 5, accumulator()->getValue()->getInt() ); + ASSERT_EQUALS( 5, accumulator()->getValue().getInt() ); } }; @@ -387,7 +387,7 @@ namespace AccumulatorTests { createAccumulator(); accumulator()->evaluate( fromjson( "{}" ) ); accumulator()->evaluate( fromjson( "{a:7}" ) ); - ASSERT_EQUALS( Undefined, accumulator()->getValue()->getType() ); + ASSERT_EQUALS( Undefined, accumulator()->getValue().getType() ); } }; @@ -413,7 +413,7 @@ namespace AccumulatorTests { void run() { createAccumulator(); // The accumulator returns no value in this case. - ASSERT( !accumulator()->getValue() ); + ASSERT( accumulator()->getValue().missing() ); } }; @@ -423,7 +423,7 @@ namespace AccumulatorTests { void run() { createAccumulator(); accumulator()->evaluate( fromjson( "{b:5}" ) ); - ASSERT_EQUALS( 5, accumulator()->getValue()->getInt() ); + ASSERT_EQUALS( 5, accumulator()->getValue().getInt() ); } }; @@ -433,7 +433,7 @@ namespace AccumulatorTests { void run() { createAccumulator(); accumulator()->evaluate( fromjson( "{}" ) ); - ASSERT_EQUALS( Undefined, accumulator()->getValue()->getType() ); + ASSERT_EQUALS( Undefined, accumulator()->getValue().getType() ); } }; @@ -444,7 +444,7 @@ namespace AccumulatorTests { createAccumulator(); accumulator()->evaluate( fromjson( "{b:5}" ) ); accumulator()->evaluate( fromjson( "{b:7}" ) ); - ASSERT_EQUALS( 7, accumulator()->getValue()->getInt() ); + ASSERT_EQUALS( 7, accumulator()->getValue().getInt() ); } }; @@ -455,7 +455,7 @@ namespace AccumulatorTests { createAccumulator(); accumulator()->evaluate( fromjson( "{b:7}" ) ); accumulator()->evaluate( fromjson( "{}" ) ); - ASSERT_EQUALS( Undefined, accumulator()->getValue()->getType() ); + ASSERT_EQUALS( Undefined, accumulator()->getValue().getType() ); } }; @@ -481,7 +481,7 @@ namespace AccumulatorTests { void run() { createAccumulator(); // The accumulator returns no value in this case. - ASSERT( !accumulator()->getValue() ); + ASSERT( accumulator()->getValue().missing() ); } }; @@ -491,7 +491,7 @@ namespace AccumulatorTests { void run() { createAccumulator(); accumulator()->evaluate( fromjson( "{c:5}" ) ); - ASSERT_EQUALS( 5, accumulator()->getValue()->getInt() ); + ASSERT_EQUALS( 5, accumulator()->getValue().getInt() ); } }; @@ -501,7 +501,7 @@ namespace AccumulatorTests { void run() { createAccumulator(); accumulator()->evaluate( fromjson( "{}" ) ); - ASSERT_EQUALS( Undefined, accumulator()->getValue()->getType() ); + ASSERT_EQUALS( Undefined, accumulator()->getValue().getType() ); } }; @@ -512,7 +512,7 @@ namespace AccumulatorTests { createAccumulator(); accumulator()->evaluate( fromjson( "{c:5}" ) ); accumulator()->evaluate( fromjson( "{c:7}" ) ); - ASSERT_EQUALS( 5, accumulator()->getValue()->getInt() ); + ASSERT_EQUALS( 5, accumulator()->getValue().getInt() ); } }; @@ -523,7 +523,7 @@ namespace AccumulatorTests { createAccumulator(); accumulator()->evaluate( fromjson( "{c:7}" ) ); accumulator()->evaluate( fromjson( "{}" ) ); - ASSERT_EQUALS( Undefined, accumulator()->getValue()->getType() ); + ASSERT_EQUALS( Undefined, accumulator()->getValue().getType() ); } }; @@ -549,7 +549,7 @@ namespace AccumulatorTests { void run() { createAccumulator(); // The accumulator returns no value in this case. - ASSERT( !accumulator()->getValue() ); + ASSERT( accumulator()->getValue().missing() ); } }; @@ -559,7 +559,7 @@ namespace AccumulatorTests { void run() { createAccumulator(); accumulator()->evaluate( fromjson( "{d:5}" ) ); - ASSERT_EQUALS( 5, accumulator()->getValue()->getInt() ); + ASSERT_EQUALS( 5, accumulator()->getValue().getInt() ); } }; @@ -569,7 +569,7 @@ namespace AccumulatorTests { void run() { createAccumulator(); accumulator()->evaluate( fromjson( "{}" ) ); - ASSERT_EQUALS( Undefined, accumulator()->getValue()->getType() ); + ASSERT_EQUALS( Undefined, accumulator()->getValue().getType() ); } }; @@ -580,7 +580,7 @@ namespace AccumulatorTests { createAccumulator(); accumulator()->evaluate( fromjson( "{d:5}" ) ); accumulator()->evaluate( fromjson( "{d:7}" ) ); - ASSERT_EQUALS( 7, accumulator()->getValue()->getInt() ); + ASSERT_EQUALS( 7, accumulator()->getValue().getInt() ); } }; @@ -591,7 +591,7 @@ namespace AccumulatorTests { createAccumulator(); accumulator()->evaluate( fromjson( "{d:7}" ) ); accumulator()->evaluate( fromjson( "{}" ) ); - ASSERT_EQUALS( 7, accumulator()->getValue()->getInt() ); + ASSERT_EQUALS( 7, accumulator()->getValue().getInt() ); } }; @@ -616,7 +616,7 @@ namespace AccumulatorTests { public: void run() { createAccumulator(); - ASSERT_EQUALS( 0, accumulator()->getValue()->getInt() ); + ASSERT_EQUALS( 0, accumulator()->getValue().getInt() ); } }; @@ -626,7 +626,7 @@ namespace AccumulatorTests { void run() { createAccumulator(); accumulator()->evaluate( frombson( BSON( "d" << 5 ) ) ); - ASSERT_EQUALS( 5, accumulator()->getValue()->getInt() ); + ASSERT_EQUALS( 5, accumulator()->getValue().getInt() ); } }; @@ -636,7 +636,7 @@ namespace AccumulatorTests { void run() { createAccumulator(); accumulator()->evaluate( frombson( BSON( "d" << 6LL ) ) ); - ASSERT_EQUALS( 6, accumulator()->getValue()->getLong() ); + ASSERT_EQUALS( 6, accumulator()->getValue().getLong() ); } }; @@ -646,7 +646,7 @@ namespace AccumulatorTests { void run() { createAccumulator(); accumulator()->evaluate( frombson( BSON( "d" << 60000000000LL ) ) ); - ASSERT_EQUALS( 60000000000LL, accumulator()->getValue()->getLong() ); + ASSERT_EQUALS( 60000000000LL, accumulator()->getValue().getLong() ); } }; @@ -656,7 +656,7 @@ namespace AccumulatorTests { void run() { createAccumulator(); accumulator()->evaluate( frombson( BSON( "d" << 7.0 ) ) ); - ASSERT_EQUALS( 7.0, accumulator()->getValue()->getDouble() ); + ASSERT_EQUALS( 7.0, accumulator()->getValue().getDouble() ); } }; @@ -666,7 +666,7 @@ namespace AccumulatorTests { void run() { createAccumulator(); accumulator()->evaluate( frombson( BSON( "d" << 7.5 ) ) ); - ASSERT_EQUALS( 7.5, accumulator()->getValue()->getDouble() ); + ASSERT_EQUALS( 7.5, accumulator()->getValue().getDouble() ); } }; @@ -678,8 +678,8 @@ namespace AccumulatorTests { accumulator()->evaluate ( frombson( BSON( "d" << numeric_limits<double>::quiet_NaN() ) ) ); // NaN is unequal to itself. - ASSERT_NOT_EQUALS( accumulator()->getValue()->getDouble(), - accumulator()->getValue()->getDouble() ); + ASSERT_NOT_EQUALS( accumulator()->getValue().getDouble(), + accumulator()->getValue().getDouble() ); } }; @@ -696,8 +696,8 @@ namespace AccumulatorTests { virtual BSONObj summand2() { verify( false ); } virtual BSONObj expectedSum() = 0; void checkPairSum( BSONObj first, BSONObj second ) { - intrusive_ptr<Document> firstDocument = Document::createFromBsonObj( &first ); - intrusive_ptr<Document> secondDocument = Document::createFromBsonObj( &second ); + Document firstDocument = Document::createFromBsonObj( &first ); + Document secondDocument = Document::createFromBsonObj( &second ); createAccumulator(); accumulator()->evaluate( firstDocument ); accumulator()->evaluate( secondDocument ); @@ -705,7 +705,7 @@ namespace AccumulatorTests { } void checkSum() { BSONObjBuilder resultBuilder; - accumulator()->getValue()->addToBsonObj( &resultBuilder, "" ); + accumulator()->getValue().addToBsonObj( &resultBuilder, "" ); BSONObj result = resultBuilder.obj(); ASSERT_EQUALS( expectedSum().firstElement(), result.firstElement() ); ASSERT_EQUALS( expectedSum().firstElement().type(), result.firstElement().type() ); diff --git a/src/mongo/dbtests/documentsourcetests.cpp b/src/mongo/dbtests/documentsourcetests.cpp index b0872d655a5..17688f944ca 100644 --- a/src/mongo/dbtests/documentsourcetests.cpp +++ b/src/mongo/dbtests/documentsourcetests.cpp @@ -160,7 +160,7 @@ namespace DocumentSourceTests { // The read lock is still held. ASSERT( Lock::isReadLocked() ); // The result is as expected. - ASSERT_EQUALS( 1, source()->getCurrent()->getValue( "a" )->coerceToInt() ); + ASSERT_EQUALS( 1, source()->getCurrent()->getValue( "a" ).coerceToInt() ); // There are no more results. ASSERT( !source()->advance() ); // Exhausting the source releases the read lock. @@ -193,11 +193,11 @@ namespace DocumentSourceTests { createSource(); ASSERT( !source()->eof() ); // The result is as expected. - ASSERT_EQUALS( 1, source()->getCurrent()->getValue( "a" )->coerceToInt() ); + ASSERT_EQUALS( 1, source()->getCurrent()->getValue( "a" ).coerceToInt() ); // Get the next result. ASSERT( source()->advance() ); // The result is as expected. - ASSERT_EQUALS( 2, source()->getCurrent()->getValue( "a" )->coerceToInt() ); + ASSERT_EQUALS( 2, source()->getCurrent()->getValue( "a" ).coerceToInt() ); // The source still holds the lock. ASSERT( Lock::isReadLocked() ); source()->dispose(); @@ -206,7 +206,6 @@ namespace DocumentSourceTests { // The source cannot be advanced further. ASSERT( !source()->advance() ); ASSERT( source()->eof() ); - ASSERT( !source()->getCurrent() ); } }; @@ -318,7 +317,7 @@ namespace DocumentSourceTests { // The limit is not exhauted. ASSERT( !limit()->eof() ); // The limit's result is as expected. - ASSERT_EQUALS( 1, limit()->getCurrent()->getValue( "a" )->coerceToInt() ); + ASSERT_EQUALS( 1, limit()->getCurrent()->getValue( "a" ).coerceToInt() ); // The limit is exhausted. ASSERT( !limit()->advance() ); // The limit disposes the source, releasing the read lock. @@ -346,7 +345,7 @@ namespace DocumentSourceTests { // The limit is not exhauted. ASSERT( !limit()->eof() ); // The limit's result is as expected. - ASSERT_EQUALS( 1, limit()->getCurrent()->getValue( "a" )->coerceToInt() ); + ASSERT_EQUALS( 1, limit()->getCurrent()->getValue( "a" ).coerceToInt() ); // The limit is exhausted. ASSERT( !limit()->advance() ); // The limit disposes the match, which disposes the source and releases the read @@ -391,10 +390,7 @@ namespace DocumentSourceTests { void assertExhausted( const intrusive_ptr<DocumentSource> &source ) const { // eof() is true. ASSERT( source->eof() ); - // advance() can't be called (a verify assertion will be triggered) in this context. - // ASSERT( !_group->advance() ); - // getCurrent() does not assert, and returns an empty pointer. - ASSERT( !source->getCurrent() ); + // advance() and getCurrent() are illegal to call if eof() is true } private: /** Check that the group's spec round trips. */ @@ -587,12 +583,11 @@ namespace DocumentSourceTests { }; struct ValueCmp { - bool operator()( const intrusive_ptr<const Value>& a, - const intrusive_ptr<const Value>& b ) const { + bool operator()(const Value& a, const Value& b) { return Value::compare( a, b ) < 0; } }; - typedef map<intrusive_ptr<const Value>,intrusive_ptr<Document>,ValueCmp> IdMap; + typedef map<Value,Document,ValueCmp> IdMap; class CheckResultsBase : public Base { public: @@ -644,14 +639,10 @@ namespace DocumentSourceTests { // Load the results from the DocumentSourceGroup and sort them by _id. IdMap resultSet; while( !sink->eof() ) { - - intrusive_ptr<Document> current = sink->getCurrent(); - - // If not eof, current is non null. - ASSERT( current ); + Document current = sink->getCurrent(); // Save the current result. - intrusive_ptr<const Value> id = current->getValue( "_id" ); + Value id = current->getValue( "_id" ); resultSet[ id ] = current; // Advance. @@ -712,7 +703,7 @@ namespace DocumentSourceTests { client.insert( ns, BSONObj() ); createSource(); createGroup( BSON( "_id" << 1 ) ); - ASSERT_EQUALS( 1, group()->getCurrent()->getValue( "_id" )->getInt() ); + ASSERT_EQUALS( 1, group()->getCurrent()->getValue( "_id" ).getInt() ); } }; @@ -956,20 +947,7 @@ namespace DocumentSourceTests { createProject(); // Another result is available, so advance() succeeds. ASSERT( project()->advance() ); - ASSERT_EQUALS( 2, project()->getCurrent()->getField( "a" )->getInt() ); - } - }; - - /** getCurrent() is the first member function called. */ - class GetCurrentInit : public Base { - public: - void run() { - client.insert( ns, BSON( "_id" << 0 << "a" << 1 ) ); - createSource(); - createProject(); - // The first result exists and is as expected. - ASSERT( project()->getCurrent() ); - ASSERT_EQUALS( 1, project()->getCurrent()->getField( "a" )->getInt() ); + ASSERT_EQUALS( 2, project()->getCurrent()->getField( "a" ).getInt() ); } }; @@ -981,15 +959,15 @@ namespace DocumentSourceTests { createSource(); createProject( BSON( "a" << true << "c" << BSON( "d" << true ) ) ); // The first result exists and is as expected. - ASSERT( project()->getCurrent() ); - ASSERT_EQUALS( 1, project()->getCurrent()->getField( "a" )->getInt() ); - ASSERT( !project()->getCurrent()->getField( "b" ) ); + ASSERT(!project()->eof()); + ASSERT_EQUALS( 1, project()->getCurrent()->getField( "a" ).getInt() ); + ASSERT( project()->getCurrent()->getField( "b" ).missing() ); // The _id field is included by default in the root document. - ASSERT_EQUALS( 0, project()->getCurrent()->getField( "_id" )->getInt() ); + ASSERT_EQUALS( 0, project()->getCurrent()->getField( "_id" ).getInt() ); // The nested c.d inclusion. ASSERT_EQUALS( 1, - project()->getCurrent()->getField( "c" )->getDocument()-> - getField( "d" )->getInt() ); + project()->getCurrent()->getField( "c" ).getDocument()-> + getField( "d" ).getInt() ); } }; @@ -1050,12 +1028,12 @@ namespace DocumentSourceTests { createSource(); createProject(); ASSERT( !project()->eof() ); - ASSERT_EQUALS( 1, project()->getCurrent()->getField( "a" )->getInt() ); - ASSERT( !project()->getCurrent()->getField( "b" ) ); + ASSERT_EQUALS( 1, project()->getCurrent()->getField( "a" ).getInt() ); + ASSERT( project()->getCurrent()->getField( "b" ).missing() ); ASSERT( project()->advance() ); ASSERT( !project()->eof() ); - ASSERT_EQUALS( 3, project()->getCurrent()->getField( "a" )->getInt() ); - ASSERT( !project()->getCurrent()->getField( "b" ) ); + ASSERT_EQUALS( 3, project()->getCurrent()->getField( "a" ).getInt() ); + ASSERT( project()->getCurrent()->getField( "b" ).missing() ); ASSERT( !project()->advance() ); assertExhausted(); } @@ -1102,10 +1080,7 @@ namespace DocumentSourceTests { void assertExhausted() const { // eof() is true. ASSERT( _sort->eof() ); - // advance() triggers a verify assertion. - //ASSERT( !_sort->advance() ); - // getCurrent() does not assert, and returns an empty pointer. - ASSERT( !_sort->getCurrent() ); + // advance() and getCurrent() are illegal to call if eof() is true } private: /** @@ -1143,20 +1118,7 @@ namespace DocumentSourceTests { createSort(); // Another result is available, so advance() succeeds. ASSERT( sort()->advance() ); - ASSERT_EQUALS( 2, sort()->getCurrent()->getField( "a" )->getInt() ); - } - }; - - /** getCurrent() is the first member function called. */ - class GetCurrentInit : public Base { - public: - void run() { - client.insert( ns, BSON( "_id" << 0 << "a" << 1 ) ); - createSource(); - createSort(); - // The first result exists and is as expected. - ASSERT( sort()->getCurrent() ); - ASSERT_EQUALS( 1, sort()->getCurrent()->getField( "a" )->getInt() ); + ASSERT_EQUALS( 2, sort()->getCurrent()->getField( "a" ).getInt() ); } }; @@ -1169,12 +1131,8 @@ namespace DocumentSourceTests { createSort( sortSpec() ); // Load the results from the DocumentSourceUnwind. - vector<intrusive_ptr<Document> > resultSet; + vector<Document> resultSet; while( !sort()->eof() ) { - - // If not eof, current is non null. - ASSERT( sort()->getCurrent() ); - // Get the current result. resultSet.push_back( sort()->getCurrent() ); @@ -1190,7 +1148,7 @@ namespace DocumentSourceTests { // Convert results to BSON once they all have been retrieved (to detect any errors // resulting from incorrectly shared sub objects). BSONArrayBuilder bsonResultSet; - for( vector<intrusive_ptr<Document> >::const_iterator i = resultSet.begin(); + for( vector<Document>::const_iterator i = resultSet.begin(); i != resultSet.end(); ++i ) { BSONObjBuilder bob; (*i)->toBson( &bob ); @@ -1444,10 +1402,7 @@ namespace DocumentSourceTests { void assertExhausted() const { // eof() is true. ASSERT( _unwind->eof() ); - // advance() does not assert, and returns false. - ASSERT( !_unwind->advance() ); - // getCurrent() does not assert, and returns an empty pointer. - ASSERT( !_unwind->getCurrent() ); + // advance() and getCurrent() are illegal to call if eof() is true } private: /** @@ -1484,20 +1439,7 @@ namespace DocumentSourceTests { createUnwind(); // Another result is available, so advance() succeeds. ASSERT( unwind()->advance() ); - ASSERT_EQUALS( 2, unwind()->getCurrent()->getField( "a" )->coerceToInt() ); - } - }; - - /** getCurrent() is the first member function called. */ - class GetCurrentInit : public Base { - public: - void run() { - client.insert( ns, BSON( "_id" << 0 << "a" << BSON_ARRAY( 1 ) ) ); - createSource(); - createUnwind(); - // The first result exists and is as expected. - ASSERT( unwind()->getCurrent() ); - ASSERT_EQUALS( 1, unwind()->getCurrent()->getField( "a" )->coerceToInt() ); + ASSERT_EQUALS( 2, unwind()->getCurrent()->getField( "a" ).coerceToInt() ); } }; @@ -1510,12 +1452,8 @@ namespace DocumentSourceTests { createUnwind( unwindFieldPath() ); // Load the results from the DocumentSourceUnwind. - vector<intrusive_ptr<Document> > resultSet; + vector<Document> resultSet; while( !unwind()->eof() ) { - - // If not eof, current is non null. - ASSERT( unwind()->getCurrent() ); - // Get the current result. resultSet.push_back( unwind()->getCurrent() ); @@ -1531,7 +1469,7 @@ namespace DocumentSourceTests { // Convert results to BSON once they all have been retrieved (to detect any errors // resulting from incorrectly shared sub objects). BSONArrayBuilder bsonResultSet; - for( vector<intrusive_ptr<Document> >::const_iterator i = resultSet.begin(); + for( vector<Document>::const_iterator i = resultSet.begin(); i != resultSet.end(); ++i ) { BSONObjBuilder bob; (*i)->toBson( &bob ); @@ -1796,7 +1734,6 @@ namespace DocumentSourceTests { add<DocumentSourceProject::EofInit>(); add<DocumentSourceProject::AdvanceInit>(); - add<DocumentSourceProject::GetCurrentInit>(); add<DocumentSourceProject::Inclusion>(); add<DocumentSourceProject::Optimize>(); add<DocumentSourceProject::NonObjectSpec>(); @@ -1808,7 +1745,6 @@ namespace DocumentSourceTests { add<DocumentSourceSort::EofInit>(); add<DocumentSourceSort::AdvanceInit>(); - add<DocumentSourceSort::GetCurrentInit>(); add<DocumentSourceSort::Empty>(); add<DocumentSourceSort::SingleValue>(); add<DocumentSourceSort::TwoValues>(); @@ -1831,7 +1767,6 @@ namespace DocumentSourceTests { add<DocumentSourceUnwind::EofInit>(); add<DocumentSourceUnwind::AdvanceInit>(); - add<DocumentSourceUnwind::GetCurrentInit>(); add<DocumentSourceUnwind::Empty>(); add<DocumentSourceUnwind::MissingField>(); add<DocumentSourceUnwind::NullField>(); diff --git a/src/mongo/dbtests/documenttests.cpp b/src/mongo/dbtests/documenttests.cpp index 444b45ebe7f..9096c9807c4 100644 --- a/src/mongo/dbtests/documenttests.cpp +++ b/src/mongo/dbtests/documenttests.cpp @@ -19,29 +19,37 @@ #include "pch.h" #include "mongo/db/pipeline/document.h" +#include "mongo/db/pipeline/field_path.h" #include "mongo/db/pipeline/value.h" #include "dbtests.h" namespace DocumentTests { + mongo::Document::FieldPair getNthField(mongo::Document doc, size_t index) { + mongo::FieldIterator it (doc); + while (index--) // advance index times + it.next(); + return it.next(); + } + namespace Document { using mongo::Document; - BSONObj toBson( const intrusive_ptr<Document>& document ) { + BSONObj toBson( const Document& document ) { BSONObjBuilder bob; document->toBson( &bob ); return bob.obj(); } - intrusive_ptr<Document> fromBson( BSONObj obj ) { + Document fromBson( BSONObj obj ) { return Document::createFromBsonObj( &obj ); } - void assertRoundTrips( const intrusive_ptr<Document>& document1 ) { + void assertRoundTrips( const Document& document1 ) { BSONObj obj1 = toBson( document1 ); - intrusive_ptr<Document> document2 = fromBson( obj1 ); + Document document2 = fromBson( obj1 ); BSONObj obj2 = toBson( document2 ); ASSERT_EQUALS( obj1, obj2 ); ASSERT_EQUALS( 0, Document::compare( document1, document2 ) ); @@ -51,7 +59,7 @@ namespace DocumentTests { class Create { public: void run() { - intrusive_ptr<Document> document = Document::create(); + Document document; ASSERT_EQUALS( 0U, document->getFieldCount() ); assertRoundTrips( document ); } @@ -61,14 +69,14 @@ namespace DocumentTests { class CreateFromBsonObj { public: void run() { - intrusive_ptr<Document> document = fromBson( BSONObj() ); + Document document = fromBson( BSONObj() ); ASSERT_EQUALS( 0U, document->getFieldCount() ); document = fromBson( BSON( "a" << 1 << "b" << "q" ) ); ASSERT_EQUALS( 2U, document->getFieldCount() ); - ASSERT_EQUALS( "a", document->getField( 0 ).first ); - ASSERT_EQUALS( 1, document->getField( 0 ).second->getInt() ); - ASSERT_EQUALS( "b", document->getField( 1 ).first ); - ASSERT_EQUALS( "q", document->getField( 1 ).second->getString() ); + ASSERT_EQUALS( "a", getNthField(document, 0).first.toStdString() ); + ASSERT_EQUALS( 1, getNthField(document, 0).second.getInt() ); + ASSERT_EQUALS( "b", getNthField(document, 1).first.toStdString() ); + ASSERT_EQUALS( "q", getNthField(document, 1).second.getString() ); assertRoundTrips( document ); } }; @@ -77,17 +85,19 @@ namespace DocumentTests { class AddField { public: void run() { - intrusive_ptr<Document> document = Document::create(); - document->addField( "foo", Value::createInt( 1 ) ); - ASSERT_EQUALS( 1U, document->getFieldCount() ); - ASSERT_EQUALS( 1, document->getValue( "foo" )->getInt() ); - document->addField( "bar", Value::createInt( 99 ) ); - ASSERT_EQUALS( 2U, document->getFieldCount() ); - ASSERT_EQUALS( 99, document->getValue( "bar" )->getInt() ); + MutableDocument md; + md.addField( "foo", Value::createInt( 1 ) ); + ASSERT_EQUALS( 1U, md.peek().getFieldCount() ); + ASSERT_EQUALS( 1, md.peek().getValue( "foo" ).getInt() ); + md.addField( "bar", Value::createInt( 99 ) ); + ASSERT_EQUALS( 2U, md.peek().getFieldCount() ); + ASSERT_EQUALS( 99, md.peek().getValue( "bar" ).getInt() ); // No assertion is triggered by a duplicate field name. - document->addField( "a", Value::createInt( 5 ) ); - ASSERT_EQUALS( 3U, document->getFieldCount() ); - assertRoundTrips( document ); + md.addField( "a", Value::createInt( 5 ) ); + + Document final = md.freeze(); + ASSERT_EQUALS( 3U, final.getFieldCount() ); + assertRoundTrips( final ); } }; @@ -95,14 +105,14 @@ namespace DocumentTests { class GetValue { public: void run() { - intrusive_ptr<Document> document = fromBson( BSON( "a" << 1 << "b" << 2.2 ) ); - ASSERT_EQUALS( 1, document->getValue( "a" )->getInt() ); - ASSERT_EQUALS( 1, document->getField( "a" )->getInt() ); - ASSERT_EQUALS( 2.2, document->getValue( "b" )->getDouble() ); - ASSERT_EQUALS( 2.2, document->getField( "b" )->getDouble() ); + Document document = fromBson( BSON( "a" << 1 << "b" << 2.2 ) ); + ASSERT_EQUALS( 1, document->getValue( "a" ).getInt() ); + ASSERT_EQUALS( 1, document->getField( "a" ).getInt() ); + ASSERT_EQUALS( 2.2, document->getValue( "b" ).getDouble() ); + ASSERT_EQUALS( 2.2, document->getField( "b" ).getDouble() ); // Missing field. - ASSERT( !document->getValue( "c" ) ); - ASSERT( !document->getField( "c" ) ); + ASSERT( document->getValue( "c" ).missing() ); + ASSERT( document->getField( "c" ).missing() ); assertRoundTrips( document ); } }; @@ -111,51 +121,60 @@ namespace DocumentTests { class SetField { public: void run() { - intrusive_ptr<Document> document = - fromBson( BSON( "a" << 1 << "b" << 2.2 << "c" << 99 ) ); + Document original = fromBson(BSON("a" << 1 << "b" << 2.2 << "c" << 99)); + + // Initial positions. Used at end of function to make sure nothing moved + const Position apos = original.positionOf("a"); + const Position bpos = original.positionOf("c"); + const Position cpos = original.positionOf("c"); + + MutableDocument md (original); + // Set the first field. - document->setField( 0, "new", Value::createString( "foo" ) ); - ASSERT_EQUALS( 3U, document->getFieldCount() ); - ASSERT( !document->getValue( "a" ) ); - ASSERT_EQUALS( "foo", document->getValue( "new" )->getString() ); - ASSERT_EQUALS( "new", document->getField( 0 ).first ); - ASSERT_EQUALS( "foo", document->getField( 0 ).second->getString() ); - assertRoundTrips( document ); + md.setField( "a" , Value( "foo" ) ); + ASSERT_EQUALS( 3U, md.peek().getFieldCount() ); + ASSERT_EQUALS( "foo", md.peek().getValue( "a" ).getString() ); + ASSERT_EQUALS( "foo", getNthField(md.peek(), 0).second.getString() ); + assertRoundTrips( md.peek() ); // Set the second field. - document->setField( 1, "newer", Value::createString( "bar" ) ); - ASSERT_EQUALS( 3U, document->getFieldCount() ); - ASSERT( !document->getValue( "b" ) ); - ASSERT_EQUALS( "bar", document->getValue( "newer" )->getString() ); - ASSERT_EQUALS( "newer", document->getField( 1 ).first ); - ASSERT_EQUALS( "bar", document->getField( 1 ).second->getString() ); - assertRoundTrips( document ); + md["b"] = Value("bar"); + ASSERT_EQUALS( 3U, md.peek().getFieldCount() ); + ASSERT_EQUALS( "bar", md.peek().getValue( "b" ).getString() ); + ASSERT_EQUALS( "bar", getNthField(md.peek(), 1).second.getString() ); + assertRoundTrips( md.peek() ); + // Remove the second field. - document->setField( 1, "n/a", NULL ); - ASSERT_EQUALS( 2U, document->getFieldCount() ); - ASSERT( !document->getValue( "newer" ) ); - ASSERT_EQUALS( "c", document->getField( 1 ).first ); - ASSERT_EQUALS( 99, document->getField( 1 ).second->getInt() ); - assertRoundTrips( document ); - // Remove the first field. - document->setField( 0, "n/a", NULL ); - ASSERT_EQUALS( 1U, document->getFieldCount() ); - ASSERT( !document->getValue( "new" ) ); - ASSERT_EQUALS( "c", document->getField( 0 ).first ); - ASSERT_EQUALS( 99, document->getField( 0 ).second->getInt() ); - assertRoundTrips( document ); - } - }; + md.setField("b", Value()); + PRINT(md.peek().toString()); + ASSERT_EQUALS( 2U, md.peek().getFieldCount() ); + ASSERT( md.peek().getValue( "b" ).missing() ); + ASSERT_EQUALS( "a", getNthField(md.peek(), 0 ).first.toStdString() ); + ASSERT_EQUALS( "c", getNthField(md.peek(), 1 ).first.toStdString() ); + ASSERT_EQUALS( 99, md.peek().getValue("c").getInt() ); + assertRoundTrips( md.peek() ); - /** Get Document field indexes. */ - class GetFieldIndex { - public: - void run() { - intrusive_ptr<Document> document = - fromBson( BSON( "a" << 1 << "b" << 2.2 << "c" << 99 ) ); - ASSERT_EQUALS( 0U, document->getFieldIndex( "a" ) ); - ASSERT_EQUALS( 2U, document->getFieldIndex( "c" ) ); - ASSERT_EQUALS( 3U, document->getFieldIndex( "missing" ) ); - assertRoundTrips( document ); + // Remove the first field. + md["a"] = Value(); + ASSERT_EQUALS( 1U, md.peek().getFieldCount() ); + ASSERT( md.peek().getValue( "a" ).missing() ); + ASSERT_EQUALS( "c", getNthField(md.peek(), 0 ).first.toStdString() ); + ASSERT_EQUALS( 99, md.peek().getValue("c").getInt() ); + assertRoundTrips( md.peek() ); + + // Remove the final field. Verify document is empty. + md.remove("c"); + ASSERT( md.peek().empty() ); + ASSERT_EQUALS( 0U, md.peek().getFieldCount() ); + ASSERT_EQUALS( 0, Document::compare(md.peek(), Document()) ); + ASSERT( !FieldIterator(md.peek()).more() ); + ASSERT( md.peek().getValue( "c" ).missing() ); + assertRoundTrips( md.peek() ); + + // Make sure nothing moved + ASSERT_EQUALS(apos, md.peek().positionOf("a")); + ASSERT_EQUALS(bpos, md.peek().positionOf("c")); + ASSERT_EQUALS(cpos, md.peek().positionOf("c")); + ASSERT_EQUALS(Position(), md.peek().positionOf("d")); } }; @@ -214,20 +233,33 @@ namespace DocumentTests { class Clone { public: void run() { - intrusive_ptr<Document> document = fromBson( BSON( "a" << BSON( "b" << 1 ) ) ); - intrusive_ptr<Document> clonedDocument = document->clone(); + const Document document = fromBson( BSON( "a" << BSON( "b" << 1 ) ) ); + MutableDocument cloneOnDemand (document); + // Check equality. - ASSERT_EQUALS( 0, Document::compare( document, clonedDocument ) ); + ASSERT_EQUALS( 0, Document::compare( document, cloneOnDemand.peek() ) ); // Check pointer equality of sub document. - ASSERT_EQUALS( document->getValue( "a" )->getDocument(), - clonedDocument->getValue( "a" )->getDocument() ); - // Rename field in clone and ensure the original document's field is unchanged. - clonedDocument->setField( 0, "renamed", clonedDocument->getValue( "a" ) ); - ASSERT_EQUALS( "a", document->getField( 0 ).first ); - // Drop the field in the clone and ensure the original document is unchanged. - clonedDocument->setField( 0, "renamed", NULL ); - ASSERT_EQUALS( 0U, clonedDocument->getFieldCount() ); + ASSERT_EQUALS( document->getValue( "a" ).getDocument().getPtr(), + cloneOnDemand.peek().getValue( "a" ).getDocument().getPtr() ); + + + // Change field in clone and ensure the original document's field is unchanged. + cloneOnDemand.setField( StringData("a"), Value(2) ); + ASSERT_EQUALS( Value(1), document->getNestedField(FieldPath("a.b")) ); + + + // setNestedField and ensure the original document is unchanged. + + cloneOnDemand.reset(document); + vector<Position> path; + ASSERT_EQUALS( Value(1), document->getNestedField(FieldPath("a.b"), &path) ); + + cloneOnDemand.setNestedField(path, Value(2)); + + ASSERT_EQUALS( Value(1), document.getNestedField(FieldPath("a.b")) ); + ASSERT_EQUALS( Value(2), cloneOnDemand.peek().getNestedField(FieldPath("a.b")) ); ASSERT_EQUALS( BSON( "a" << BSON( "b" << 1 ) ), toBson( document ) ); + ASSERT_EQUALS( BSON( "a" << BSON( "b" << 2 ) ), toBson( cloneOnDemand.freeze() ) ); } }; @@ -235,9 +267,9 @@ namespace DocumentTests { class CloneMultipleFields { public: void run() { - intrusive_ptr<Document> document = + Document document = fromBson( fromjson( "{a:1,b:['ra',4],c:{z:1},d:'lal'}" ) ); - intrusive_ptr<Document> clonedDocument = document->clone(); + Document clonedDocument = document->clone(); ASSERT_EQUALS( 0, Document::compare( document, clonedDocument ) ); } }; @@ -246,8 +278,8 @@ namespace DocumentTests { class FieldIteratorEmpty { public: void run() { - scoped_ptr<FieldIterator> iterator( Document::create()->createFieldIterator() ); - ASSERT( !iterator->more() ); + FieldIterator iterator ( (Document()) ); + ASSERT( !iterator.more() ); } }; @@ -255,13 +287,12 @@ namespace DocumentTests { class FieldIteratorSingle { public: void run() { - scoped_ptr<FieldIterator> iterator - ( fromBson( BSON( "a" << 1 ) )->createFieldIterator() ); - ASSERT( iterator->more() ); - Document::FieldPair field = iterator->next(); - ASSERT_EQUALS( "a", field.first ); - ASSERT_EQUALS( 1, field.second->getInt() ); - ASSERT( !iterator->more() ); + FieldIterator iterator (fromBson( BSON( "a" << 1 ) )); + ASSERT( iterator.more() ); + Document::FieldPair field = iterator.next(); + ASSERT_EQUALS( "a", field.first.toStdString() ); + ASSERT_EQUALS( 1, field.second.getInt() ); + ASSERT( !iterator.more() ); } }; @@ -269,22 +300,22 @@ namespace DocumentTests { class FieldIteratorMultiple { public: void run() { - scoped_ptr<FieldIterator> iterator - ( fromBson( BSON( "a" << 1 << "b" << 5.6 << "c" << "z" ) )-> - createFieldIterator() ); - ASSERT( iterator->more() ); - Document::FieldPair field = iterator->next(); - ASSERT_EQUALS( "a", field.first ); - ASSERT_EQUALS( 1, field.second->getInt() ); - ASSERT( iterator->more() ); - field = iterator->next(); - ASSERT_EQUALS( "b", field.first ); - ASSERT_EQUALS( 5.6, field.second->getDouble() ); - ASSERT( iterator->more() ); - field = iterator->next(); - ASSERT_EQUALS( "c", field.first ); - ASSERT_EQUALS( "z", field.second->getString() ); - ASSERT( !iterator->more() ); + FieldIterator iterator (fromBson( BSON( "a" << 1 << "b" << 5.6 << "c" << "z" ))); + ASSERT( iterator.more() ); + Document::FieldPair field = iterator.next(); + ASSERT_EQUALS( "a", field.first.toStdString() ); + ASSERT_EQUALS( 1, field.second.getInt() ); + ASSERT( iterator.more() ); + + Document::FieldPair field2 = iterator.next(); + ASSERT_EQUALS( "b", field2.first.toStdString() ); + ASSERT_EQUALS( 5.6, field2.second.getDouble() ); + ASSERT( iterator.more() ); + + Document::FieldPair field3 = iterator.next(); + ASSERT_EQUALS( "c", field3.first.toStdString() ); + ASSERT_EQUALS( "z", field3.second.getString() ); + ASSERT( !iterator.more() ); } }; @@ -294,20 +325,20 @@ namespace DocumentTests { using mongo::Value; - BSONObj toBson( const intrusive_ptr<const Value>& value ) { + BSONObj toBson( const Value& value ) { BSONObjBuilder bob; - value->addToBsonObj( &bob, "" ); + value.addToBsonObj( &bob, "" ); return bob.obj(); } - intrusive_ptr<const Value> fromBson( const BSONObj& obj ) { + Value fromBson( const BSONObj& obj ) { BSONElement element = obj.firstElement(); return Value::createFromBsonElement( &element ); } - void assertRoundTrips( const intrusive_ptr<const Value>& value1 ) { + void assertRoundTrips( const Value& value1 ) { BSONObj obj1 = toBson( value1 ); - intrusive_ptr<const Value> value2 = fromBson( obj1 ); + Value value2 = fromBson( obj1 ); BSONObj obj2 = toBson( value2 ); ASSERT_EQUALS( obj1, obj2 ); ASSERT( value1 == value2 ); @@ -317,11 +348,11 @@ namespace DocumentTests { class Int { public: void run() { - intrusive_ptr<const Value> value = Value::createInt( 5 ); - ASSERT_EQUALS( 5, value->getInt() ); - ASSERT_EQUALS( 5, value->getLong() ); - ASSERT_EQUALS( 5, value->getDouble() ); - ASSERT_EQUALS( NumberInt, value->getType() ); + Value value = Value::createInt( 5 ); + ASSERT_EQUALS( 5, value.getInt() ); + ASSERT_EQUALS( 5, value.getLong() ); + ASSERT_EQUALS( 5, value.getDouble() ); + ASSERT_EQUALS( NumberInt, value.getType() ); assertRoundTrips( value ); } }; @@ -330,10 +361,10 @@ namespace DocumentTests { class Long { public: void run() { - intrusive_ptr<const Value> value = Value::createLong( 99 ); - ASSERT_EQUALS( 99, value->getLong() ); - ASSERT_EQUALS( 99, value->getDouble() ); - ASSERT_EQUALS( NumberLong, value->getType() ); + Value value = Value::createLong( 99 ); + ASSERT_EQUALS( 99, value.getLong() ); + ASSERT_EQUALS( 99, value.getDouble() ); + ASSERT_EQUALS( NumberLong, value.getType() ); assertRoundTrips( value ); } }; @@ -342,9 +373,9 @@ namespace DocumentTests { class Double { public: void run() { - intrusive_ptr<const Value> value = Value::createDouble( 5.5 ); - ASSERT_EQUALS( 5.5, value->getDouble() ); - ASSERT_EQUALS( NumberDouble, value->getType() ); + Value value = Value::createDouble( 5.5 ); + ASSERT_EQUALS( 5.5, value.getDouble() ); + ASSERT_EQUALS( NumberDouble, value.getType() ); assertRoundTrips( value ); } }; @@ -353,9 +384,9 @@ namespace DocumentTests { class String { public: void run() { - intrusive_ptr<const Value> value = Value::createString( "foo" ); - ASSERT_EQUALS( "foo", value->getString() ); - ASSERT_EQUALS( mongo::String, value->getType() ); + Value value = Value::createString( "foo" ); + ASSERT_EQUALS( "foo", value.getString() ); + ASSERT_EQUALS( mongo::String, value.getType() ); assertRoundTrips( value ); } }; @@ -367,8 +398,8 @@ namespace DocumentTests { string withNull( "a\0b", 3 ); BSONObj objWithNull = BSON( "" << withNull ); ASSERT_EQUALS( withNull, objWithNull[ "" ].str() ); - intrusive_ptr<const Value> value = fromBson( objWithNull ); - ASSERT_EQUALS( withNull, value->getString() ); + Value value = fromBson( objWithNull ); + ASSERT_EQUALS( withNull, value.getString() ); assertRoundTrips( value ); } }; @@ -377,9 +408,9 @@ namespace DocumentTests { class Date { public: void run() { - intrusive_ptr<const Value> value = Value::createDate(999); - ASSERT_EQUALS( 999, value->getDate() ); - ASSERT_EQUALS( mongo::Date, value->getType() ); + Value value = Value::createDate(999); + ASSERT_EQUALS( 999, value.getDate() ); + ASSERT_EQUALS( mongo::Date, value.getType() ); assertRoundTrips( value ); } }; @@ -388,9 +419,9 @@ namespace DocumentTests { class Timestamp { public: void run() { - intrusive_ptr<const Value> value = Value::createTimestamp( OpTime( 777 ) ); - ASSERT( OpTime( 777 ) == value->getTimestamp() ); - ASSERT_EQUALS( mongo::Timestamp, value->getType() ); + Value value = Value::createTimestamp( OpTime( 777 ) ); + ASSERT( OpTime( 777 ) == value.getTimestamp() ); + ASSERT_EQUALS( mongo::Timestamp, value.getType() ); assertRoundTrips( value ); } }; @@ -399,10 +430,10 @@ namespace DocumentTests { class EmptyDocument { public: void run() { - intrusive_ptr<mongo::Document> document = mongo::Document::create(); - intrusive_ptr<const Value> value = Value::createDocument( document ); - ASSERT_EQUALS( document, value->getDocument() ); - ASSERT_EQUALS( Object, value->getType() ); + mongo::Document document = mongo::Document(); + Value value = Value::createDocument( document ); + ASSERT_EQUALS( document.getPtr(), value.getDocument().getPtr() ); + ASSERT_EQUALS( Object, value.getType() ); assertRoundTrips( value ); } }; @@ -411,18 +442,20 @@ namespace DocumentTests { class Document { public: void run() { - intrusive_ptr<mongo::Document> document = mongo::Document::create(); - document->addField( "a", Value::createInt( 5 ) ); - document->addField( "apple", Value::createString( "rrr" ) ); - document->addField( "banana", Value::createDouble( -.3 ) ); - intrusive_ptr<const Value> value = Value::createDocument( document ); + mongo::MutableDocument md; + md.addField( "a", Value::createInt( 5 ) ); + md.addField( "apple", Value::createString( "rrr" ) ); + md.addField( "banana", Value::createDouble( -.3 ) ); + mongo::Document document = md.freeze(); + + Value value = Value::createDocument( document ); // Check document pointers are equal. - ASSERT_EQUALS( document, value->getDocument() ); + ASSERT_EQUALS( document.getPtr(), value.getDocument().getPtr() ); // Check document contents. - ASSERT_EQUALS( 5, document->getValue( "a" )->getInt() ); - ASSERT_EQUALS( "rrr", document->getValue( "apple" )->getString() ); - ASSERT_EQUALS( -.3, document->getValue( "banana" )->getDouble() ); - ASSERT_EQUALS( Object, value->getType() ); + ASSERT_EQUALS( 5, document->getValue( "a" ).getInt() ); + ASSERT_EQUALS( "rrr", document->getValue( "apple" ).getString() ); + ASSERT_EQUALS( -.3, document->getValue( "banana" ).getDouble() ); + ASSERT_EQUALS( Object, value.getType() ); assertRoundTrips( value ); } }; @@ -431,12 +464,13 @@ namespace DocumentTests { class EmptyArray { public: void run() { - vector<intrusive_ptr<const Value> > array; - intrusive_ptr<const Value> value = Value::createArray( array ); - intrusive_ptr<ValueIterator> arrayIterator = value->getArray(); - ASSERT( !arrayIterator->more() ); - ASSERT_EQUALS( Array, value->getType() ); - ASSERT_EQUALS( 0U, value->getArrayLength() ); + vector<Value> array; + Value value (array); + const vector<Value>& array2 = value.getArray(); + + ASSERT( array2.empty() ); + ASSERT_EQUALS( Array, value.getType() ); + ASSERT_EQUALS( 0U, value.getArrayLength() ); assertRoundTrips( value ); } }; @@ -445,19 +479,20 @@ namespace DocumentTests { class Array { public: void run() { - vector<intrusive_ptr<const Value> > array; + vector<Value> array; array.push_back( Value::createInt( 5 ) ); array.push_back( Value::createString( "lala" ) ); array.push_back( Value::createDouble( 3.14 ) ); - intrusive_ptr<const Value> value = Value::createArray( array ); - intrusive_ptr<ValueIterator> arrayIterator = value->getArray(); - ASSERT( arrayIterator->more() ); - ASSERT_EQUALS( 5, arrayIterator->next()->getInt() ); - ASSERT_EQUALS( "lala", arrayIterator->next()->getString() ); - ASSERT_EQUALS( 3.14, arrayIterator->next()->getDouble() ); - ASSERT( !arrayIterator->more() ); - ASSERT_EQUALS( mongo::Array, value->getType() ); - ASSERT_EQUALS( 3U, value->getArrayLength() ); + Value value = Value::createArray( array ); + const vector<Value>& array2 = value.getArray(); + + ASSERT( !array2.empty() ); + ASSERT_EQUALS( array2.size(), 3U); + ASSERT_EQUALS( 5, array2[0].getInt() ); + ASSERT_EQUALS( "lala", array2[1].getString() ); + ASSERT_EQUALS( 3.14, array2[2].getDouble() ); + ASSERT_EQUALS( mongo::Array, value.getType() ); + ASSERT_EQUALS( 3U, value.getArrayLength() ); assertRoundTrips( value ); } }; @@ -466,10 +501,10 @@ namespace DocumentTests { class Oid { public: void run() { - intrusive_ptr<const Value> value = + Value value = fromBson( BSON( "" << OID( "abcdefabcdefabcdefabcdef" ) ) ); - ASSERT_EQUALS( OID( "abcdefabcdefabcdefabcdef" ), value->getOid() ); - ASSERT_EQUALS( jstOID, value->getType() ); + ASSERT_EQUALS( OID( "abcdefabcdefabcdefabcdef" ), value.getOid() ); + ASSERT_EQUALS( jstOID, value.getType() ); assertRoundTrips( value ); } }; @@ -478,9 +513,9 @@ namespace DocumentTests { class Bool { public: void run() { - intrusive_ptr<const Value> value = fromBson( BSON( "" << true ) ); - ASSERT_EQUALS( true, value->getBool() ); - ASSERT_EQUALS( mongo::Bool, value->getType() ); + Value value = fromBson( BSON( "" << true ) ); + ASSERT_EQUALS( true, value.getBool() ); + ASSERT_EQUALS( mongo::Bool, value.getType() ); assertRoundTrips( value ); } }; @@ -489,9 +524,9 @@ namespace DocumentTests { class Regex { public: void run() { - intrusive_ptr<const Value> value = fromBson( fromjson( "{'':/abc/}" ) ); - ASSERT_EQUALS( "abc", value->getRegex() ); - ASSERT_EQUALS( RegEx, value->getType() ); + Value value = fromBson( fromjson( "{'':/abc/}" ) ); + ASSERT_EQUALS( "abc", value.getRegex() ); + ASSERT_EQUALS( RegEx, value.getType() ); if ( 0 ) { // SERVER-6470 assertRoundTrips( value ); } @@ -504,9 +539,9 @@ namespace DocumentTests { void run() { BSONObjBuilder bob; bob.appendSymbol( "", "FOOBAR" ); - intrusive_ptr<const Value> value = fromBson( bob.obj() ); - ASSERT_EQUALS( "FOOBAR", value->getSymbol() ); - ASSERT_EQUALS( mongo::Symbol, value->getType() ); + Value value = fromBson( bob.obj() ); + ASSERT_EQUALS( "FOOBAR", value.getSymbol() ); + ASSERT_EQUALS( mongo::Symbol, value.getType() ); assertRoundTrips( value ); } }; @@ -515,8 +550,8 @@ namespace DocumentTests { class Undefined { public: void run() { - intrusive_ptr<const Value> value = Value::getUndefined(); - ASSERT_EQUALS( mongo::Undefined, value->getType() ); + Value value = Value(mongo::Undefined); + ASSERT_EQUALS( mongo::Undefined, value.getType() ); assertRoundTrips( value ); } }; @@ -525,8 +560,8 @@ namespace DocumentTests { class Null { public: void run() { - intrusive_ptr<const Value> value = Value::getNull(); - ASSERT_EQUALS( jstNULL, value->getType() ); + Value value = Value(mongo::jstNULL); + ASSERT_EQUALS( jstNULL, value.getType() ); assertRoundTrips( value ); } }; @@ -535,9 +570,9 @@ namespace DocumentTests { class True { public: void run() { - intrusive_ptr<const Value> value = Value::getTrue(); - ASSERT_EQUALS( true, value->getBool() ); - ASSERT_EQUALS( mongo::Bool, value->getType() ); + Value value = Value(true); + ASSERT_EQUALS( true, value.getBool() ); + ASSERT_EQUALS( mongo::Bool, value.getType() ); assertRoundTrips( value ); } }; @@ -546,9 +581,9 @@ namespace DocumentTests { class False { public: void run() { - intrusive_ptr<const Value> value = Value::getFalse(); - ASSERT_EQUALS( false, value->getBool() ); - ASSERT_EQUALS( mongo::Bool, value->getType() ); + Value value = Value(false); + ASSERT_EQUALS( false, value.getBool() ); + ASSERT_EQUALS( mongo::Bool, value.getType() ); assertRoundTrips( value ); } }; @@ -557,9 +592,9 @@ namespace DocumentTests { class MinusOne { public: void run() { - intrusive_ptr<const Value> value = Value::getMinusOne(); - ASSERT_EQUALS( -1, value->getInt() ); - ASSERT_EQUALS( NumberInt, value->getType() ); + Value value = Value(-1); + ASSERT_EQUALS( -1, value.getInt() ); + ASSERT_EQUALS( NumberInt, value.getType() ); assertRoundTrips( value ); } }; @@ -568,9 +603,9 @@ namespace DocumentTests { class Zero { public: void run() { - intrusive_ptr<const Value> value = Value::getZero(); - ASSERT_EQUALS( 0, value->getInt() ); - ASSERT_EQUALS( NumberInt, value->getType() ); + Value value = Value(0); + ASSERT_EQUALS( 0, value.getInt() ); + ASSERT_EQUALS( NumberInt, value.getType() ); assertRoundTrips( value ); } }; @@ -579,9 +614,9 @@ namespace DocumentTests { class One { public: void run() { - intrusive_ptr<const Value> value = Value::getOne(); - ASSERT_EQUALS( 1, value->getInt() ); - ASSERT_EQUALS( NumberInt, value->getType() ); + Value value = Value(1); + ASSERT_EQUALS( 1, value.getInt() ); + ASSERT_EQUALS( NumberInt, value.getType() ); assertRoundTrips( value ); } }; @@ -593,10 +628,10 @@ namespace DocumentTests { virtual ~ToBoolBase() { } void run() { - ASSERT_EQUALS( expected(), value()->coerceToBool() ); + ASSERT_EQUALS( expected(), value().coerceToBool() ); } protected: - virtual intrusive_ptr<const Value> value() = 0; + virtual Value value() = 0; virtual bool expected() = 0; }; @@ -610,81 +645,81 @@ namespace DocumentTests { /** Coerce 0 to bool. */ class ZeroIntToBool : public ToBoolFalse { - intrusive_ptr<const Value> value() { return Value::createInt( 0 ); } + Value value() { return Value::createInt( 0 ); } }; /** Coerce -1 to bool. */ class NonZeroIntToBool : public ToBoolTrue { - intrusive_ptr<const Value> value() { return Value::createInt( -1 ); } + Value value() { return Value::createInt( -1 ); } }; /** Coerce 0LL to bool. */ class ZeroLongToBool : public ToBoolFalse { - intrusive_ptr<const Value> value() { return Value::createLong( 0 ); } + Value value() { return Value::createLong( 0 ); } }; /** Coerce 5LL to bool. */ class NonZeroLongToBool : public ToBoolTrue { - intrusive_ptr<const Value> value() { return Value::createLong( 5 ); } + Value value() { return Value::createLong( 5 ); } }; /** Coerce 0.0 to bool. */ class ZeroDoubleToBool : public ToBoolFalse { - intrusive_ptr<const Value> value() { return Value::createDouble( 0 ); } + Value value() { return Value::createDouble( 0 ); } }; /** Coerce -1.3 to bool. */ class NonZeroDoubleToBool : public ToBoolTrue { - intrusive_ptr<const Value> value() { return Value::createDouble( -1.3 ); } + Value value() { return Value::createDouble( -1.3 ); } }; /** Coerce "" to bool. */ class StringToBool : public ToBoolTrue { - intrusive_ptr<const Value> value() { return Value::createString( "" ); } + Value value() { return Value::createString( "" ); } }; /** Coerce {} to bool. */ class ObjectToBool : public ToBoolTrue { - intrusive_ptr<const Value> value() { - return Value::createDocument( mongo::Document::create() ); + Value value() { + return Value::createDocument( mongo::Document() ); } }; /** Coerce [] to bool. */ class ArrayToBool : public ToBoolTrue { - intrusive_ptr<const Value> value() { - return Value::createArray( vector<intrusive_ptr<const Value> >() ); + Value value() { + return Value::createArray( vector<Value>() ); } }; /** Coerce Date(0) to bool. */ class DateToBool : public ToBoolTrue { - intrusive_ptr<const Value> value() { return Value::createDate(0); } + Value value() { return Value::createDate(0); } }; /** Coerce js literal regex to bool. */ class RegexToBool : public ToBoolTrue { - intrusive_ptr<const Value> value() { return fromBson( fromjson( "{''://}" ) ); } + Value value() { return fromBson( fromjson( "{''://}" ) ); } }; /** Coerce true to bool. */ class TrueToBool : public ToBoolTrue { - intrusive_ptr<const Value> value() { return fromBson( BSON( "" << true ) ); } + Value value() { return fromBson( BSON( "" << true ) ); } }; /** Coerce false to bool. */ class FalseToBool : public ToBoolFalse { - intrusive_ptr<const Value> value() { return fromBson( BSON( "" << false ) ); } + Value value() { return fromBson( BSON( "" << false ) ); } }; /** Coerce null to bool. */ class NullToBool : public ToBoolFalse { - intrusive_ptr<const Value> value() { return Value::getNull(); } + Value value() { return Value(mongo::jstNULL); } }; /** Coerce undefined to bool. */ class UndefinedToBool : public ToBoolFalse { - intrusive_ptr<const Value> value() { return Value::getUndefined(); } + Value value() { return Value(mongo::Undefined); } }; class ToIntBase { @@ -692,46 +727,46 @@ namespace DocumentTests { virtual ~ToIntBase() { } void run() { - ASSERT_EQUALS( expected(), value()->coerceToInt() ); + ASSERT_EQUALS( expected(), value().coerceToInt() ); } protected: - virtual intrusive_ptr<const Value> value() = 0; + virtual Value value() = 0; virtual int expected() { return 0; } }; /** Coerce -5 to int. */ class IntToInt : public ToIntBase { - intrusive_ptr<const Value> value() { return Value::createInt( -5 ); } + Value value() { return Value::createInt( -5 ); } int expected() { return -5; } }; /** Coerce long to int. */ class LongToInt : public ToIntBase { - intrusive_ptr<const Value> value() { return Value::createLong( 0xff00000007LL ); } + Value value() { return Value::createLong( 0xff00000007LL ); } int expected() { return 7; } }; /** Coerce 9.8 to int. */ class DoubleToInt : public ToIntBase { - intrusive_ptr<const Value> value() { return Value::createDouble( 9.8 ); } + Value value() { return Value::createDouble( 9.8 ); } int expected() { return 9; } }; /** Coerce null to int. */ class NullToInt : public ToIntBase { - intrusive_ptr<const Value> value() { return Value::getNull(); } + Value value() { return Value(mongo::jstNULL); } }; /** Coerce undefined to int. */ class UndefinedToInt : public ToIntBase { - intrusive_ptr<const Value> value() { return Value::getUndefined(); } + Value value() { return Value(mongo::Undefined); } }; /** Coerce "" to int unsupported. */ class StringToInt { public: void run() { - ASSERT_THROWS( Value::createString( "" )->coerceToInt(), UserException ); + ASSERT_THROWS( Value::createString( "" ).coerceToInt(), UserException ); } }; @@ -740,46 +775,46 @@ namespace DocumentTests { virtual ~ToLongBase() { } void run() { - ASSERT_EQUALS( expected(), value()->coerceToLong() ); + ASSERT_EQUALS( expected(), value().coerceToLong() ); } protected: - virtual intrusive_ptr<const Value> value() = 0; + virtual Value value() = 0; virtual long long expected() { return 0; } }; /** Coerce -5 to long. */ class IntToLong : public ToLongBase { - intrusive_ptr<const Value> value() { return Value::createInt( -5 ); } + Value value() { return Value::createInt( -5 ); } long long expected() { return -5; } }; /** Coerce long to long. */ class LongToLong : public ToLongBase { - intrusive_ptr<const Value> value() { return Value::createLong( 0xff00000007LL ); } + Value value() { return Value::createLong( 0xff00000007LL ); } long long expected() { return 0xff00000007LL; } }; /** Coerce 9.8 to long. */ class DoubleToLong : public ToLongBase { - intrusive_ptr<const Value> value() { return Value::createDouble( 9.8 ); } + Value value() { return Value::createDouble( 9.8 ); } long long expected() { return 9; } }; /** Coerce null to long. */ class NullToLong : public ToLongBase { - intrusive_ptr<const Value> value() { return Value::getNull(); } + Value value() { return Value(mongo::jstNULL); } }; /** Coerce undefined to long. */ class UndefinedToLong : public ToLongBase { - intrusive_ptr<const Value> value() { return Value::getUndefined(); } + Value value() { return Value(mongo::Undefined); } }; /** Coerce string to long unsupported. */ class StringToLong { public: void run() { - ASSERT_THROWS( Value::createString( "" )->coerceToLong(), UserException ); + ASSERT_THROWS( Value::createString( "" ).coerceToLong(), UserException ); } }; @@ -788,22 +823,22 @@ namespace DocumentTests { virtual ~ToDoubleBase() { } void run() { - ASSERT_EQUALS( expected(), value()->coerceToDouble() ); + ASSERT_EQUALS( expected(), value().coerceToDouble() ); } protected: - virtual intrusive_ptr<const Value> value() = 0; + virtual Value value() = 0; virtual double expected() { return 0; } }; /** Coerce -5 to double. */ class IntToDouble : public ToDoubleBase { - intrusive_ptr<const Value> value() { return Value::createInt( -5 ); } + Value value() { return Value::createInt( -5 ); } double expected() { return -5; } }; /** Coerce long to double. */ class LongToDouble : public ToDoubleBase { - intrusive_ptr<const Value> value() { + Value value() { // A long that cannot be exactly represented as a double. return Value::createDouble( static_cast<double>( 0x8fffffffffffffffLL ) ); } @@ -812,25 +847,25 @@ namespace DocumentTests { /** Coerce double to double. */ class DoubleToDouble : public ToDoubleBase { - intrusive_ptr<const Value> value() { return Value::createDouble( 9.8 ); } + Value value() { return Value::createDouble( 9.8 ); } double expected() { return 9.8; } }; /** Coerce null to double. */ class NullToDouble : public ToDoubleBase { - intrusive_ptr<const Value> value() { return Value::getNull(); } + Value value() { return Value(mongo::jstNULL); } }; /** Coerce undefined to double. */ class UndefinedToDouble : public ToDoubleBase { - intrusive_ptr<const Value> value() { return Value::getUndefined(); } + Value value() { return Value(mongo::Undefined); } }; /** Coerce string to double unsupported. */ class StringToDouble { public: void run() { - ASSERT_THROWS( Value::createString( "" )->coerceToDouble(), UserException ); + ASSERT_THROWS( Value::createString( "" ).coerceToDouble(), UserException ); } }; @@ -839,16 +874,16 @@ namespace DocumentTests { virtual ~ToDateBase() { } void run() { - ASSERT_EQUALS( expected(), value()->coerceToDate() ); + ASSERT_EQUALS( expected(), value().coerceToDate() ); } protected: - virtual intrusive_ptr<const Value> value() = 0; + virtual Value value() = 0; virtual long long expected() = 0; }; /** Coerce date to date. */ class DateToDate : public ToDateBase { - intrusive_ptr<const Value> value() { return Value::createDate(888); } + Value value() { return Value::createDate(888); } long long expected() { return 888; } }; @@ -857,7 +892,7 @@ namespace DocumentTests { * is different from BSON behavior of interpreting all bytes as a date. */ class TimestampToDate : public ToDateBase { - intrusive_ptr<const Value> value() { + Value value() { return Value::createTimestamp( OpTime( 777, 666 ) ); } long long expected() { return 777 * 1000; } @@ -867,7 +902,7 @@ namespace DocumentTests { class StringToDate { public: void run() { - ASSERT_THROWS( Value::createString( "" )->coerceToDate(), UserException ); + ASSERT_THROWS( Value::createString( "" ).coerceToDate(), UserException ); } }; @@ -876,40 +911,40 @@ namespace DocumentTests { virtual ~ToStringBase() { } void run() { - ASSERT_EQUALS( expected(), value()->coerceToString() ); + ASSERT_EQUALS( expected(), value().coerceToString() ); } protected: - virtual intrusive_ptr<const Value> value() = 0; + virtual Value value() = 0; virtual string expected() { return ""; } }; /** Coerce -0.2 to string. */ class DoubleToString : public ToStringBase { - intrusive_ptr<const Value> value() { return Value::createDouble( -0.2 ); } + Value value() { return Value::createDouble( -0.2 ); } string expected() { return "-0.2"; } }; /** Coerce -4 to string. */ class IntToString : public ToStringBase { - intrusive_ptr<const Value> value() { return Value::createInt( -4 ); } + Value value() { return Value::createInt( -4 ); } string expected() { return "-4"; } }; /** Coerce 10000LL to string. */ class LongToString : public ToStringBase { - intrusive_ptr<const Value> value() { return Value::createLong( 10000 ); } + Value value() { return Value::createLong( 10000 ); } string expected() { return "10000"; } }; /** Coerce string to string. */ class StringToString : public ToStringBase { - intrusive_ptr<const Value> value() { return Value::createString( "fO_o" ); } + Value value() { return Value::createString( "fO_o" ); } string expected() { return "fO_o"; } }; /** Coerce timestamp to string. */ class TimestampToString : public ToStringBase { - intrusive_ptr<const Value> value() { + Value value() { return Value::createTimestamp( OpTime( 1, 2 ) ); } string expected() { return OpTime( 1, 2 ).toStringPretty(); } @@ -917,18 +952,18 @@ namespace DocumentTests { /** Coerce date to string. */ class DateToString : public ToStringBase { - intrusive_ptr<const Value> value() { return Value::createDate(1234567890LL*1000); } + Value value() { return Value::createDate(1234567890LL*1000); } string expected() { return "2009-02-13T23:31:30"; } // from js }; /** Coerce null to string. */ class NullToString : public ToStringBase { - intrusive_ptr<const Value> value() { return Value::getNull(); } + Value value() { return Value(mongo::jstNULL); } }; /** Coerce undefined to string. */ class UndefinedToString : public ToStringBase { - intrusive_ptr<const Value> value() { return Value::getUndefined(); } + Value value() { return Value(mongo::Undefined); } }; /** Coerce document to string unsupported. */ @@ -936,7 +971,7 @@ namespace DocumentTests { public: void run() { ASSERT_THROWS( Value::createDocument - ( mongo::Document::create() )->coerceToString(), + ( mongo::Document() ).coerceToString(), UserException ); } }; @@ -945,8 +980,8 @@ namespace DocumentTests { class TimestampToTimestamp { public: void run() { - intrusive_ptr<const Value> value = Value::createTimestamp( OpTime( 1010 ) ); - ASSERT( OpTime( 1010 ) == value->coerceToTimestamp() ); + Value value = Value::createTimestamp( OpTime( 1010 ) ); + ASSERT( OpTime( 1010 ) == value.coerceToTimestamp() ); } }; @@ -954,7 +989,7 @@ namespace DocumentTests { class DateToTimestamp { public: void run() { - ASSERT_THROWS( Value::createDate(1010)->coerceToTimestamp(), + ASSERT_THROWS( Value::createDate(1010).coerceToTimestamp(), UserException ); } }; @@ -1004,9 +1039,9 @@ namespace DocumentTests { public: void run() { BSONObjBuilder bob; - Value::createDouble( 4.4 )->addToBsonObj( &bob, "a" ); - Value::createInt( 22 )->addToBsonObj( &bob, "b" ); - Value::createString( "astring" )->addToBsonObj( &bob, "c" ); + Value::createDouble( 4.4 ).addToBsonObj( &bob, "a" ); + Value::createInt( 22 ).addToBsonObj( &bob, "b" ); + Value::createString( "astring" ).addToBsonObj( &bob, "c" ); ASSERT_EQUALS( BSON( "a" << 4.4 << "b" << 22 << "c" << "astring" ), bob.obj() ); } }; @@ -1016,9 +1051,9 @@ namespace DocumentTests { public: void run() { BSONArrayBuilder bab; - Value::createDouble( 4.4 )->addToBsonArray( &bab ); - Value::createInt( 22 )->addToBsonArray( &bab ); - Value::createString( "astring" )->addToBsonArray( &bab ); + Value::createDouble( 4.4 ).addToBsonArray( &bab ); + Value::createInt( 22 ).addToBsonArray( &bab ); + Value::createString( "astring" ).addToBsonArray( &bab ); ASSERT_EQUALS( BSON_ARRAY( 4.4 << 22 << "astring" ), bab.arr() ); } }; @@ -1130,7 +1165,9 @@ namespace DocumentTests { assertComparison( expectedResult, first.obj(), second.obj() ); } int cmp( const BSONObj& a, const BSONObj& b ) { - int result = Value::compare( fromBson( a ), fromBson( b ) ); + Value va = fromBson(a); + Value vb = fromBson(b); + int result = Value::compare(va, vb); return // sign result > 0 ? 1 : result < 0 ? -1 : @@ -1146,10 +1183,42 @@ namespace DocumentTests { } size_t hash( const BSONObj& obj ) { size_t seed = 0xf00ba6; - fromBson( obj )->hash_combine( seed ); + fromBson( obj ).hash_combine( seed ); return seed; } }; + + class SubFields { + public: + void run() { + const Value val = fromBson(fromjson( + "{'': {a: [{x:1, b:[1, {y:1, c:1234, z:1}, 1]}]}}")); + // ^ this outer object is removed by fromBson + + ASSERT(val.getType() == mongo::Object); + + ASSERT(val[999].missing()); + ASSERT(val["missing"].missing()); + ASSERT(val["a"].getType() == mongo::Array); + + ASSERT(val["a"][999].missing()); + ASSERT(val["a"]["missing"].missing()); + ASSERT(val["a"][0].getType() == mongo::Object); + + ASSERT(val["a"][0][999].missing()); + ASSERT(val["a"][0]["missing"].missing()); + ASSERT(val["a"][0]["b"].getType() == mongo::Array); + + ASSERT(val["a"][0]["b"][999].missing()); + ASSERT(val["a"][0]["b"]["missing"].missing()); + ASSERT(val["a"][0]["b"][1].getType() == mongo::Object); + + ASSERT(val["a"][0]["b"][1][999].missing()); + ASSERT(val["a"][0]["b"][1]["missing"].missing()); + ASSERT(val["a"][0]["b"][1]["c"].getType() == mongo::NumberInt); + ASSERT_EQUALS(val["a"][0]["b"][1]["c"].getInt(), 1234); + } + }; } // namespace Value @@ -1163,7 +1232,6 @@ namespace DocumentTests { add<Document::AddField>(); add<Document::GetValue>(); add<Document::SetField>(); - add<Document::GetFieldIndex>(); add<Document::Compare>(); add<Document::CompareNamedNull>(); add<Document::Clone>(); @@ -1248,6 +1316,7 @@ namespace DocumentTests { add<Value::AddToBsonObj>(); add<Value::AddToBsonArray>(); add<Value::Compare>(); + add<Value::SubFields>(); } } myall; diff --git a/src/mongo/dbtests/expressiontests.cpp b/src/mongo/dbtests/expressiontests.cpp index 26f4c04f69a..10fad013f71 100644 --- a/src/mongo/dbtests/expressiontests.cpp +++ b/src/mongo/dbtests/expressiontests.cpp @@ -33,9 +33,9 @@ namespace ExpressionTests { } /** Convert Value to a wrapped BSONObj with an empty string field name. */ - static BSONObj toBson( const intrusive_ptr<const Value>& value ) { + static BSONObj toBson( const Value& value ) { BSONObjBuilder bob; - value->addToBsonObj( &bob, "" ); + value.addToBsonObj( &bob, "" ); return bob.obj(); } @@ -47,19 +47,19 @@ namespace ExpressionTests { } /** Convert Document to BSON. */ - static BSONObj toBson( const intrusive_ptr<const Document>& document ) { + static BSONObj toBson( const Document& document ) { BSONObjBuilder bob; document->toBson( &bob ); return bob.obj(); } /** Create a Document from a BSONObj. */ - intrusive_ptr<Document> fromBson( BSONObj obj ) { + Document fromBson( BSONObj obj ) { return Document::createFromBsonObj( &obj ); } /** Create a Value from a BSONObj. */ - intrusive_ptr<const Value> valueFromBson( BSONObj obj ) { + Value valueFromBson( BSONObj obj ) { BSONElement element = obj.firstElement(); return Value::createFromBsonElement( &element ); } @@ -73,7 +73,7 @@ namespace ExpressionTests { intrusive_ptr<ExpressionNary> expression = ExpressionAdd::create(); populateOperands( expression ); ASSERT_EQUALS( expectedResult(), - toBson( expression->evaluate( Document::create() ) ) ); + toBson( expression->evaluate( Document() ) ) ); } protected: virtual void populateOperands( intrusive_ptr<ExpressionNary>& expression ) = 0; @@ -86,7 +86,7 @@ namespace ExpressionTests { void run() { intrusive_ptr<ExpressionNary> expression = ExpressionAdd::create(); expression->addOperand( ExpressionConstant::create( Value::createInt( 2 ) ) ); - ASSERT_EQUALS( BSON( "" << 2 ), toBson( expression->evaluate( NULL ) ) ); + ASSERT_EQUALS( BSON( "" << 2 ), toBson( expression->evaluate( Document() ) ) ); } }; @@ -102,7 +102,7 @@ namespace ExpressionTests { void run() { intrusive_ptr<ExpressionNary> expression = ExpressionAdd::create(); expression->addOperand( ExpressionConstant::create( Value::createDate( 123456 ) ) ); - ASSERT_THROWS( expression->evaluate( Document::create() ), UserException ); + ASSERT_THROWS( expression->evaluate( Document() ), UserException ); } }; @@ -112,7 +112,7 @@ namespace ExpressionTests { void run() { intrusive_ptr<ExpressionNary> expression = ExpressionAdd::create(); expression->addOperand( ExpressionConstant::create( Value::createString( "a" ) ) ); - ASSERT_THROWS( expression->evaluate( Document::create() ), UserException ); + ASSERT_THROWS( expression->evaluate( Document() ), UserException ); } }; @@ -121,8 +121,8 @@ namespace ExpressionTests { public: void run() { intrusive_ptr<ExpressionNary> expression = ExpressionAdd::create(); - expression->addOperand( ExpressionConstant::create( Value::getTrue() ) ); - ASSERT_THROWS( expression->evaluate( Document::create() ), UserException ); + expression->addOperand( ExpressionConstant::create( Value(true) ) ); + ASSERT_THROWS( expression->evaluate( Document() ), UserException ); } }; @@ -458,7 +458,7 @@ namespace ExpressionTests { intrusive_ptr<Expression> nested = ExpressionConstant::create( Value::createInt( 5 ) ); intrusive_ptr<Expression> expression = ExpressionCoerceToBool::create( nested ); - ASSERT( expression->evaluate( Document::create() )->getBool() ); + ASSERT( expression->evaluate( Document() ).getBool() ); } }; @@ -469,7 +469,7 @@ namespace ExpressionTests { intrusive_ptr<Expression> nested = ExpressionConstant::create( Value::createInt( 0 ) ); intrusive_ptr<Expression> expression = ExpressionCoerceToBool::create( nested ); - ASSERT( !expression->evaluate( Document::create() )->getBool() ); + ASSERT( !expression->evaluate( Document() ).getBool() ); } }; @@ -571,11 +571,11 @@ namespace ExpressionTests { ASSERT_EQUALS( spec(), expressionToBson( expression ) ); // Check evaluation result. ASSERT_EQUALS( expectedResult(), - toBson( expression->evaluate( Document::create() ) ) ); + toBson( expression->evaluate( Document() ) ) ); // Check that the result is the same after optimizing. intrusive_ptr<Expression> optimized = expression->optimize(); ASSERT_EQUALS( expectedResult(), - toBson( optimized->evaluate( Document::create() ) ) ); + toBson( optimized->evaluate( Document() ) ) ); } protected: virtual BSONObj spec() = 0; @@ -744,7 +744,7 @@ namespace ExpressionTests { BSONObj specObject = BSON( "" << BSON( "$ne" << BSON_ARRAY( "a" << 1 ) ) ); BSONElement specElement = specObject.firstElement(); intrusive_ptr<Expression> expression = Expression::parseOperand( &specElement ); - ASSERT_THROWS( expression->evaluate( Document::create() ), UserException ); + ASSERT_THROWS( expression->evaluate( Document() ), UserException ); } }; @@ -853,7 +853,7 @@ namespace ExpressionTests { intrusive_ptr<Expression> expression = ExpressionConstant::create( Value::createInt( 5 ) ); assertBinaryEqual( BSON( "" << 5 ), - toBson( expression->evaluate( Document::create() ) ) ); + toBson( expression->evaluate( Document() ) ) ); } }; @@ -866,7 +866,7 @@ namespace ExpressionTests { intrusive_ptr<Expression> expression = ExpressionConstant::createFromBsonElement( &specElement ); assertBinaryEqual( BSON( "" << "foo" ), - toBson( expression->evaluate( Document::create() ) ) ); + toBson( expression->evaluate( Document() ) ) ); } }; @@ -972,7 +972,7 @@ namespace ExpressionTests { intrusive_ptr<Expression> expression = ExpressionFieldPath::create( "a" ); // Result is undefined. assertBinaryEqual( fromjson( "{'':undefined}" ), - toBson( expression->evaluate( Document::create() ) ) ); + toBson( expression->evaluate( Document() ) ) ); } }; @@ -1165,7 +1165,7 @@ namespace ExpressionTests { ExpressionFieldRange::create( mongo::ExpressionFieldPath::create( "a" ), compareOp(), valueFromBson( value() ) ); ASSERT_EQUALS( expectedSpec(), expressionToBson( expression ) ); - ASSERT_EQUALS( toBson( expectedResult() ? Value::getTrue() : Value::getFalse() ), + ASSERT_EQUALS( toBson( expectedResult() ? Value(true) : Value(false) ), toBson( expression->evaluate( fromBson( sourceDocument() ) ) ) ); } protected: @@ -1302,7 +1302,7 @@ namespace ExpressionTests { void run() { intrusive_ptr<ExpressionFieldRange> expression = ExpressionFieldRange::create( mongo::ExpressionFieldPath::create( "a.b.c" ), - Expression::EQ, Value::getZero() ); + Expression::EQ, Value(0) ); set<string> dependencies; expression->addDependencies( dependencies ); ASSERT_EQUALS( 1U, dependencies.size() ); @@ -1316,8 +1316,8 @@ namespace ExpressionTests { void run() { intrusive_ptr<ExpressionFieldRange> expression = ExpressionFieldRange::create( mongo::ExpressionFieldPath::create( "a" ), - Expression::EQ, Value::getZero() ); - intrusive_ptr<Document> document = + Expression::EQ, Value(0) ); + Document document = fromBson( BSON( "a" << BSON_ARRAY( 1 << 0 << 2 ) ) ); ASSERT_THROWS( expression->evaluate( document ), UserException ); } @@ -1330,11 +1330,10 @@ namespace ExpressionTests { /** A dummy child of ExpressionNary used for testing. */ class Testable : public ExpressionNary { public: - virtual intrusive_ptr<const Value> evaluate - (const intrusive_ptr<Document> &pDocument) const { + virtual Value evaluate(const Document& pDocument) const { // Just put all the values in a list. This is not associative/commutative so // the results will change if a factory is provided and operations are reordered. - vector<intrusive_ptr<const Value> > values; + vector<Value> values; for( ExpressionVector::const_iterator i = vpOperand.begin(); i != vpOperand.end(); ++i ) { values.push_back( (*i)->evaluate( pDocument ) ); @@ -1667,10 +1666,10 @@ namespace ExpressionTests { void run() { _expression = ExpressionObject::create(); prepareExpression(); - intrusive_ptr<Document> document = fromBson( source() ); - intrusive_ptr<Document> result = Document::create(); + Document document = fromBson( source() ); + MutableDocument result; expression()->addToDocument( result, document, document ); - assertBinaryEqual( expected(), toBson( result ) ); + assertBinaryEqual( expected(), toBson( result.freeze() ) ); assertDependencies( expectedDependencies(), _expression ); ASSERT_EQUALS( expectedBsonRepresentation(), expressionToBson( _expression ) ); ASSERT_EQUALS( expectedIsSimple(), _expression->isSimple() ); @@ -1898,7 +1897,7 @@ namespace ExpressionTests { } void prepareExpression() { expression()->addField( mongo::FieldPath( "a" ), - ExpressionConstant::create( Value::getUndefined() ) ); + ExpressionConstant::create( Value(mongo::Undefined) ) ); } BSONObj expected() { return BSON( "_id" << 0 ); } BSONArray expectedDependencies() { return BSON_ARRAY( "_id" ); } @@ -1923,7 +1922,7 @@ namespace ExpressionTests { } void prepareExpression() { expression()->addField( mongo::FieldPath( "a" ), - ExpressionConstant::create( Value::getNull() ) ); + ExpressionConstant::create( Value(mongo::jstNULL) ) ); } BSONObj expected() { return BSON( "_id" << 0 << "a" << BSONNULL ); } BSONArray expectedDependencies() { return BSON_ARRAY( "_id" ); } @@ -1987,7 +1986,7 @@ namespace ExpressionTests { // Create a sub expression returning an empty object. intrusive_ptr<ExpressionObject> subExpression = ExpressionObject::create(); subExpression->addField( mongo::FieldPath( "b" ), - ExpressionConstant::create( Value::getUndefined() ) ); + ExpressionConstant::create( Value(mongo::Undefined) ) ); expression()->addField( mongo::FieldPath( "a" ), subExpression ); } BSONObj expected() { return BSON( "_id" << 0 ); } @@ -2374,7 +2373,7 @@ namespace ExpressionTests { ASSERT_EQUALS( BSON( "b" << 5 << "c" << 1 ), toBson( expression->evaluate ( fromBson - ( BSON( "_id" << 0 << "a" << 1 ) ) )->getDocument() ) ); + ( BSON( "_id" << 0 << "a" << 1 ) ) ).getDocument() ) ); } }; @@ -2968,7 +2967,7 @@ namespace ExpressionTests { intrusive_ptr<Expression> expression = Expression::parseOperand( &specElement ); ASSERT_EQUALS( spec, expressionToBson( expression ) ); ASSERT_EQUALS( BSON( "" << expectedResult ), - toBson( expression->evaluate( Document::create() ) ) ); + toBson( expression->evaluate( Document() ) ) ); } }; @@ -3016,7 +3015,7 @@ namespace ExpressionTests { intrusive_ptr<Expression> expression = Expression::parseOperand( &specElement ); ASSERT_EQUALS( spec(), expressionToBson( expression ) ); ASSERT_EQUALS( BSON( "" << expectedResult() ), - toBson( expression->evaluate( Document::create() ) ) ); + toBson( expression->evaluate( Document() ) ) ); } protected: virtual string str() = 0; @@ -3083,7 +3082,7 @@ namespace ExpressionTests { intrusive_ptr<Expression> expression = Expression::parseOperand( &specElement ); ASSERT_EQUALS( spec(), expressionToBson( expression ) ); ASSERT_EQUALS( BSON( "" << expectedResult() ), - toBson( expression->evaluate( Document::create() ) ) ); + toBson( expression->evaluate( Document() ) ) ); } protected: virtual string str() = 0; @@ -3126,7 +3125,7 @@ namespace ExpressionTests { intrusive_ptr<Expression> expression = Expression::parseOperand( &specElement ); ASSERT_EQUALS( spec(), expressionToBson( expression ) ); ASSERT_EQUALS( BSON( "" << expectedResult() ), - toBson( expression->evaluate( Document::create() ) ) ); + toBson( expression->evaluate( Document() ) ) ); } protected: virtual string str() = 0; diff --git a/src/mongo/util/intrusive_counter.cpp b/src/mongo/util/intrusive_counter.cpp index edce3fb39a7..38712e44c6f 100755..100644 --- a/src/mongo/util/intrusive_counter.cpp +++ b/src/mongo/util/intrusive_counter.cpp @@ -14,9 +14,28 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#include "util/intrusive_counter.h" +#include "pch.h" +#include "mongo/util/intrusive_counter.h" +#include "mongo/util/mongoutils/str.h" namespace mongo { + using namespace mongoutils; + + intrusive_ptr<const RCString> RCString::create(StringData s) { + const size_t sizeWithNUL = s.size() + 1; + const size_t bytesNeeded = sizeof(RCString) + sizeWithNUL; + uassert(16493, str::stream() << "Tried to create string longer than " + << (BSONObjMaxUserSize/1024/1024) << "MB", + bytesNeeded < static_cast<size_t>(BSONObjMaxUserSize)); + + intrusive_ptr<RCString> ptr = new (bytesNeeded) RCString(); // uses custom operator new + + ptr->_size = s.size(); + char* stringStart = reinterpret_cast<char*>(ptr.get()) + sizeof(RCString); + memcpy(stringStart, s.data(), sizeWithNUL); + + return ptr; + } void IntrusiveCounterUnsigned::addRef() const { ++counter; diff --git a/src/mongo/util/intrusive_counter.h b/src/mongo/util/intrusive_counter.h index 547f5bfd969..83f1f75a670 100755..100644 --- a/src/mongo/util/intrusive_counter.h +++ b/src/mongo/util/intrusive_counter.h @@ -18,6 +18,8 @@ #include <boost/intrusive_ptr.hpp> #include <boost/noncopyable.hpp> +#include "mongo/platform/atomic_word.h" +#include "mongo/base/string_data.h" namespace mongo { @@ -66,6 +68,52 @@ namespace mongo { mutable unsigned counter; }; + /// This is an alternative base class to the above ones (will replace them eventually) + class RefCountable : boost::noncopyable { + public: + /// If false you have exclusive access to this object. This is useful for implementing COW. + bool isShared() const { + // TODO: switch to unfenced read method after SERVER-6973 + return reinterpret_cast<unsigned&>(_count) > 1; + } + + friend void intrusive_ptr_add_ref(const RefCountable* ptr) { + ptr->_count.addAndFetch(1); + }; + + friend void intrusive_ptr_release(const RefCountable* ptr) { + if (ptr->_count.subtractAndFetch(1) == 0) { + delete ptr; // uses subclass destructor and operator delete + } + }; + + protected: + RefCountable() {} + virtual ~RefCountable() {} + + private: + mutable AtomicUInt32 _count; // default initialized to 0 + }; + + /// This is an immutable reference-counted string + class RCString : public RefCountable { + public: + const char* c_str() const { return reinterpret_cast<const char*>(this) + sizeof(RCString); } + int size() const { return _size; } + StringData stringData() const { return StringData(c_str(), _size); } + + static intrusive_ptr<const RCString> create(StringData s); + void operator delete (void* ptr) { free(ptr); } + + private: + // these can only be created by calling create() + RCString() {}; + void* operator new (size_t objSize, size_t realSize) { return malloc(realSize); } + + int _size; // does NOT include trailing NUL byte. + // char[_size+1] array allocated past end of class + }; + }; /* ======================= INLINED IMPLEMENTATIONS ========================== */ |