diff options
Diffstat (limited to 'src/mongo/db/pipeline/value.h')
-rw-r--r-- | src/mongo/db/pipeline/value.h | 588 |
1 files changed, 299 insertions, 289 deletions
diff --git a/src/mongo/db/pipeline/value.h b/src/mongo/db/pipeline/value.h index 41201f64873..0852407a84a 100644 --- a/src/mongo/db/pipeline/value.h +++ b/src/mongo/db/pipeline/value.h @@ -33,328 +33,338 @@ #include "mongo/platform/unordered_set.h" namespace mongo { - class BSONElement; +class BSONElement; - /** A variant type that can hold any type of data representable in BSON +/** 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 std::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: + /** Construct a Value * - * 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. + * All types not listed will be rejected rather than converted (see private for why) * - * 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 std::string type. + * Note: Currently these are all explicit conversions. + * I'm not sure if we want implicit or not. + * //TODO decide + */ + + 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 Timestamp& value) : _storage(bsonTimestamp, value) {} + explicit Value(const OID& value) : _storage(jstOID, value) {} + explicit Value(StringData value) : _storage(String, value) {} + explicit Value(const std::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 BSONObj& obj); + explicit Value(const BSONArray& arr); + explicit Value(std::vector<Value> vec) : _storage(Array, new RCVector(std::move(vec))) {} + explicit Value(const BSONBinData& bd) : _storage(BinData, bd) {} + explicit Value(const BSONRegEx& re) : _storage(RegEx, re) {} + explicit Value(const BSONCodeWScope& cws) : _storage(CodeWScope, cws) {} + explicit Value(const BSONDBRef& dbref) : _storage(DBRef, dbref) {} + explicit Value(const BSONSymbol& sym) : _storage(Symbol, sym.symbol) {} + explicit Value(const BSONCode& code) : _storage(Code, code.code) {} + explicit Value(const NullLabeler&) : _storage(jstNULL) {} // BSONNull + explicit Value(const UndefinedLabeler&) : _storage(Undefined) {} // BSONUndefined + explicit Value(const MinKeyLabeler&) : _storage(MinKey) {} // MINKEY + explicit Value(const MaxKeyLabeler&) : _storage(MaxKey) {} // MAXKEY + explicit Value(const Date_t& date) : _storage(Date, date.toMillisSinceEpoch()) {} + + // TODO: add an unsafe version that can share storage with the BSONElement + /// Deep-convert from BSONElement to Value + explicit Value(const BSONElement& elem); + + /** Construct a long or integer-valued Value. * - * 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. + * 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 Value createIntOrLong(long long value); + + /** 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. */ - class Value { - public: - /** 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 - */ - - 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 Timestamp& value) : _storage(bsonTimestamp, value) {} - explicit Value(const OID& value) : _storage(jstOID, value) {} - explicit Value(StringData value) : _storage(String, value) {} - explicit Value(const std::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 BSONObj& obj); - explicit Value(const BSONArray& arr); - explicit Value(std::vector<Value> vec) : _storage(Array, new RCVector(std::move(vec))) {} - explicit Value(const BSONBinData& bd) : _storage(BinData, bd) {} - explicit Value(const BSONRegEx& re) : _storage(RegEx, re) {} - explicit Value(const BSONCodeWScope& cws) : _storage(CodeWScope, cws) {} - explicit Value(const BSONDBRef& dbref) : _storage(DBRef, dbref) {} - explicit Value(const BSONSymbol& sym) : _storage(Symbol, sym.symbol) {} - explicit Value(const BSONCode& code) : _storage(Code, code.code) {} - explicit Value(const NullLabeler&) : _storage(jstNULL) {} // BSONNull - explicit Value(const UndefinedLabeler&) : _storage(Undefined) {} // BSONUndefined - explicit Value(const MinKeyLabeler&) : _storage(MinKey) {} // MINKEY - explicit Value(const MaxKeyLabeler&) : _storage(MaxKey) {} // MAXKEY - explicit Value(const Date_t& date) : _storage(Date, date.toMillisSinceEpoch()) {} - - // TODO: add an unsafe version that can share storage with the BSONElement - /// Deep-convert from BSONElement to Value - explicit Value(const BSONElement& elem); - - /** 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 Value createIntOrLong(long long value); - - /** 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; } - - /// true if missing() or type is jstNULL or Undefined - bool nullish() const { - return missing() - || _storage.type == jstNULL - || _storage.type == Undefined; - } + bool missing() const { + return _storage.type == EOO; + } - /// true if type represents a number - bool numeric() const { - return _storage.type == NumberDouble - || _storage.type == NumberLong - || _storage.type == NumberInt; - } + /// true if missing() or type is jstNULL or Undefined + bool nullish() const { + return missing() || _storage.type == jstNULL || _storage.type == Undefined; + } - /** - * Returns true if this value is a numeric type that can be represented as a 32-bit integer, - * and false otherwise. - */ - bool integral() const; - - /// Get the BSON type of the field. - BSONType getType() const { return _storage.bsonType(); } - - /** 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; - std::string getString() const; - Document getDocument() const; - OID getOid() const; - bool getBool() const; - long long getDate() const; // in milliseconds - Timestamp getTimestamp() const; - const char* getRegex() const; - const char* getRegexFlags() const; - std::string getSymbol() const; - std::string getCode() const; - int getInt() const; - long long getLong() const; - const std::vector<Value>& getArray() const { return _storage.getArray(); } - size_t getArrayLength() const; - - /// Access an element of a subarray. Returns Value() if missing or getType() != Array - Value operator[] (size_t index) const; - - /// Access a field of a subdocument. Returns Value() if missing or getType() != Object - Value operator[] (StringData name) const; - - /// Add this value to the BSON object under construction. - void addToBsonObj(BSONObjBuilder* pBuilder, StringData fieldName) const; - - /// Add this field to the BSON array under construction. - void addToBsonArray(BSONArrayBuilder* pBuilder) const; - - // Support BSONObjBuilder and BSONArrayBuilder "stream" API - friend BSONObjBuilder& operator << (BSONObjBuilderValueStream& builder, const Value& val); - - /** Coerce a value to a bool using BSONElement::trueValue() rules. - */ - bool coerceToBool() const; - - /** 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. - */ - std::string coerceToString() const; - int coerceToInt() const; - long long coerceToLong() const; - double coerceToDouble() const; - Timestamp coerceToTimestamp() const; - long long coerceToDate() const; - time_t coerceToTimeT() const; - tm coerceToTm() const; // broken-out time struct (see man gmtime) - - - /** 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); - } - - friend bool operator!=(const Value& v1, const Value& v2) { - return !(v1 == v2); - } + /// true if type represents a number + bool numeric() const { + return _storage.type == NumberDouble || _storage.type == NumberLong || + _storage.type == NumberInt; + } - friend bool operator<(const Value& lhs, const Value& rhs) { - return (Value::compare(lhs, rhs) < 0); - } + /** + * Returns true if this value is a numeric type that can be represented as a 32-bit integer, + * and false otherwise. + */ + bool integral() const; - /// This is for debugging, logging, etc. See getString() for how to extract a string. - std::string toString() const; - friend std::ostream& operator << (std::ostream& out, const Value& v); + /// Get the BSON type of the field. + BSONType getType() const { + return _storage.bsonType(); + } - void swap(Value& rhs) { - _storage.swap(rhs._storage); - } + /** 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; + std::string getString() const; + Document getDocument() const; + OID getOid() const; + bool getBool() const; + long long getDate() const; // in milliseconds + Timestamp getTimestamp() const; + const char* getRegex() const; + const char* getRegexFlags() const; + std::string getSymbol() const; + std::string getCode() const; + int getInt() const; + long long getLong() const; + const std::vector<Value>& getArray() const { + return _storage.getArray(); + } + size_t getArrayLength() const; - /** 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 BSONType getWidestNumeric(BSONType lType, BSONType rType); - - /// 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 - * hashed container classes such as unordered_map<>. - */ - void hash_combine(size_t& seed) const; - - /// struct Hash is defined to enable the use of Values as keys in unordered_map. - struct Hash : std::unary_function<const Value&, size_t> { - size_t operator()(const Value& rV) const; - }; - - /// Call this after memcpying to update ref counts if needed - void memcpyed() const { _storage.memcpyed(); } - - /// members for Sorter - struct SorterDeserializeSettings {}; // unused - void serializeForSorter(BufBuilder& buf) const; - static Value deserializeForSorter(BufReader& buf, const SorterDeserializeSettings&); - int memUsageForSorter() const { return getApproximateSize(); } - Value getOwned() const { return *this; } - - 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> - explicit Value(const InvalidArgumentType& invalidArgument); - - explicit Value(const ValueStorage& storage) :_storage(storage) {} - - // does no type checking - StringData getStringData() const; // May contain embedded NUL bytes - - ValueStorage _storage; - friend class MutableValue; // gets and sets _storage.genericRCPtr - }; - BOOST_STATIC_ASSERT(sizeof(Value) == 16); + /// Access an element of a subarray. Returns Value() if missing or getType() != Array + Value operator[](size_t index) const; - typedef unordered_set<Value, Value::Hash> ValueSet; -} + /// Access a field of a subdocument. Returns Value() if missing or getType() != Object + Value operator[](StringData name) const; -namespace std { - // This is used by std::sort and others - template <> - inline void swap(mongo::Value& lhs, mongo::Value& rhs) { lhs.swap(rhs); } -} + /// Add this value to the BSON object under construction. + void addToBsonObj(BSONObjBuilder* pBuilder, StringData fieldName) const; -/* ======================= INLINED IMPLEMENTATIONS ========================== */ + /// Add this field to the BSON array under construction. + void addToBsonArray(BSONArrayBuilder* pBuilder) const; -namespace mongo { + // Support BSONObjBuilder and BSONArrayBuilder "stream" API + friend BSONObjBuilder& operator<<(BSONObjBuilderValueStream& builder, const Value& val); - inline size_t Value::getArrayLength() const { - verify(getType() == Array); - return getArray().size(); - } + /** Coerce a value to a bool using BSONElement::trueValue() rules. + */ + bool coerceToBool() const; - inline size_t Value::Hash::operator()(const Value& v) const { - size_t seed = 0xf0afbeef; - v.hash_combine(seed); - return seed; - } + /** 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. + */ + std::string coerceToString() const; + int coerceToInt() const; + long long coerceToLong() const; + double coerceToDouble() const; + Timestamp coerceToTimestamp() const; + long long coerceToDate() const; + time_t coerceToTimeT() const; + tm coerceToTm() const; // broken-out time struct (see man gmtime) + + + /** 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); - inline StringData Value::getStringData() const { - return _storage.getString(); + 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); } - inline std::string Value::getString() const { - verify(getType() == String); - return _storage.getString().toString(); + friend bool operator!=(const Value& v1, const Value& v2) { + return !(v1 == v2); } - inline OID Value::getOid() const { - verify(getType() == jstOID); - return OID(_storage.oid); + friend bool operator<(const Value& lhs, const Value& rhs) { + return (Value::compare(lhs, rhs) < 0); } - inline bool Value::getBool() const { - verify(getType() == Bool); - return _storage.boolValue; - } + /// This is for debugging, logging, etc. See getString() for how to extract a string. + std::string toString() const; + friend std::ostream& operator<<(std::ostream& out, const Value& v); - inline long long Value::getDate() const { - verify(getType() == Date); - return _storage.dateValue; + void swap(Value& rhs) { + _storage.swap(rhs._storage); } - inline Timestamp Value::getTimestamp() const { - verify(getType() == bsonTimestamp); - return Timestamp(_storage.timestampValue); - } + /** 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 BSONType getWidestNumeric(BSONType lType, BSONType rType); - inline const char* Value::getRegex() const { - verify(getType() == RegEx); - return _storage.getString().rawData(); // this is known to be NUL terminated - } - inline const char* Value::getRegexFlags() const { - verify(getType() == RegEx); - const char* pattern = _storage.getString().rawData(); // this is known to be NUL terminated - const char* flags = pattern + strlen(pattern) + 1; // first byte after pattern's NUL - dassert(flags + strlen(flags) == pattern + _storage.getString().size()); - return flags; + /// 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 + * hashed container classes such as unordered_map<>. + */ + void hash_combine(size_t& seed) const; + + /// struct Hash is defined to enable the use of Values as keys in unordered_map. + struct Hash : std::unary_function<const Value&, size_t> { + size_t operator()(const Value& rV) const; + }; + + /// Call this after memcpying to update ref counts if needed + void memcpyed() const { + _storage.memcpyed(); } - inline std::string Value::getSymbol() const { - verify(getType() == Symbol); - return _storage.getString().toString(); + /// members for Sorter + struct SorterDeserializeSettings {}; // unused + void serializeForSorter(BufBuilder& buf) const; + static Value deserializeForSorter(BufReader& buf, const SorterDeserializeSettings&); + int memUsageForSorter() const { + return getApproximateSize(); } - inline std::string Value::getCode() const { - verify(getType() == Code); - return _storage.getString().toString(); + Value getOwned() const { + return *this; } - inline int Value::getInt() const { - verify(getType() == NumberInt); - return _storage.intValue; - } +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> + explicit Value(const InvalidArgumentType& invalidArgument); - inline long long Value::getLong() const { - BSONType type = getType(); - if (type == NumberInt) - return _storage.intValue; + explicit Value(const ValueStorage& storage) : _storage(storage) {} - verify(type == NumberLong); - return _storage.longValue; - } + // does no type checking + StringData getStringData() const; // May contain embedded NUL bytes + + ValueStorage _storage; + friend class MutableValue; // gets and sets _storage.genericRCPtr +}; +BOOST_STATIC_ASSERT(sizeof(Value) == 16); + +typedef unordered_set<Value, Value::Hash> ValueSet; +} + +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 size_t Value::getArrayLength() const { + verify(getType() == Array); + return getArray().size(); +} + +inline size_t Value::Hash::operator()(const Value& v) const { + size_t seed = 0xf0afbeef; + v.hash_combine(seed); + return seed; +} + +inline StringData Value::getStringData() const { + return _storage.getString(); +} + +inline std::string Value::getString() const { + verify(getType() == String); + return _storage.getString().toString(); +} + +inline OID Value::getOid() const { + verify(getType() == jstOID); + return OID(_storage.oid); +} + +inline bool Value::getBool() const { + verify(getType() == Bool); + return _storage.boolValue; +} + +inline long long Value::getDate() const { + verify(getType() == Date); + return _storage.dateValue; +} + +inline Timestamp Value::getTimestamp() const { + verify(getType() == bsonTimestamp); + return Timestamp(_storage.timestampValue); +} + +inline const char* Value::getRegex() const { + verify(getType() == RegEx); + return _storage.getString().rawData(); // this is known to be NUL terminated +} +inline const char* Value::getRegexFlags() const { + verify(getType() == RegEx); + const char* pattern = _storage.getString().rawData(); // this is known to be NUL terminated + const char* flags = pattern + strlen(pattern) + 1; // first byte after pattern's NUL + dassert(flags + strlen(flags) == pattern + _storage.getString().size()); + return flags; +} + +inline std::string Value::getSymbol() const { + verify(getType() == Symbol); + return _storage.getString().toString(); +} +inline std::string Value::getCode() const { + verify(getType() == Code); + return _storage.getString().toString(); +} + +inline int Value::getInt() const { + verify(getType() == NumberInt); + return _storage.intValue; +} + +inline long long Value::getLong() const { + BSONType type = getType(); + if (type == NumberInt) + return _storage.intValue; + + verify(type == NumberLong); + return _storage.longValue; +} }; |