diff options
author | Matt Kneiser <matt.kneiser@mongodb.com> | 2022-01-13 00:22:12 +0000 |
---|---|---|
committer | Evergreen Agent <no-reply@evergreen.mongodb.com> | 2022-01-13 06:29:13 +0000 |
commit | fd6b2a875f8942a4ba7fea916b4c52a913e14f49 (patch) | |
tree | 8e18dd580f8d03a42f27ed7af357cae38a2ff745 | |
parent | 47c77eedf3bb0b4305f2ca1d8e362b27d599008f (diff) | |
download | mongo-fd6b2a875f8942a4ba7fea916b4c52a913e14f49.tar.gz |
SERVER-27209 Eliminate dangerous BSONElement string extraction methods
- Fix: Change return type of BSONObj::getStringField to include size
(StringData vs. char*). A char* only contains the data with an ending NULL
termination. Whereas a StringData contains data + size so caller knows how
to interpret data if there are embedded NULLs.
- Cleanup: Remove old tag - CachedSizeTag - that disambiguated BSONElement ctors.
A dangling reference to 'maxLen' in a comment led me to this historical issue.
$ git log -S'maxLen' -- src/mongo/bson/bsonelement.h
commit 0d38ef5
Author: Mathias Stearn mathias@10gen.com
Date: Tue Dec 19 14:23:08 2017 -0500
SERVER-32302 Compute BSONElement sizes eagerly
- Test: Add tests for NULL bytes being returned by getStringField
- $ ninja -j400 +bson_obj_test
- Cleanup: Move BSONElement::valuestr() from public to private
- Cleanup: Remove BSONElement::valuestrsafe()
- Cleanup: Remove all external callers of valuestr/valuestrsafe and cleanup
their callsites with better alternatives.
- Cleanup: Make multi-line BSONElement & BSONObj public API comments
conform to style guidelines
- Nit: Fix spelling in a comment
68 files changed, 535 insertions, 352 deletions
diff --git a/src/mongo/bson/bson_obj_test.cpp b/src/mongo/bson/bson_obj_test.cpp index f9581d03c80..049fab19d52 100644 --- a/src/mongo/bson/bson_obj_test.cpp +++ b/src/mongo/bson/bson_obj_test.cpp @@ -764,4 +764,43 @@ TEST(BSONObj, sizeChecks) { ErrorCodes::BSONObjectTooLarge); } +TEST(BSONObj, nullByteInStringBasic) { + const size_t size = 3; + StringData str("b\0c", size); + + // { "a": "b\0c" } + BSONObjBuilder b; + b.append("a"_sd, str); + BSONObj obj{b.obj()}; + + ASSERT_EQ(str.size(), obj.getStringField("a").size()); + ASSERT_EQ(str, obj.getStringField("a")); +} + +TEST(BSONObj, nullByteInStringMulti) { + const size_t size = 5; + StringData str("b\0c\0d", size); + + // { "a": "b\0c\0d" } + BSONObjBuilder b; + b.append("a"_sd, str); + BSONObj obj{b.obj()}; + + ASSERT_EQ(str.size(), obj.getStringField("a").size()); + ASSERT_EQ(str, obj.getStringField("a")); +} + +TEST(BSONObj, nullByteInStringFull) { + const size_t size = 9; + StringData str("\0\0\0\0\0\0\0\0\0", size); + + // { "a": "\0\0\0\0\0\0\0\0\0" } + BSONObjBuilder b; + b.append("a"_sd, str); + BSONObj obj{b.obj()}; + + ASSERT_EQ(str.size(), obj.getStringField("a").size()); + ASSERT_EQ(str, obj.getStringField("a")); +} + } // unnamed namespace diff --git a/src/mongo/bson/bson_validate.cpp b/src/mongo/bson/bson_validate.cpp index e37d09cba20..08659efecd1 100644 --- a/src/mongo/bson/bson_validate.cpp +++ b/src/mongo/bson/bson_validate.cpp @@ -171,8 +171,7 @@ private: if constexpr (precise) { auto nameLen = obj - _currElem; - _currFrame->elem = - BSONElement(_currElem, nameLen, nameLen + len, BSONElement::CachedSizeTag()); + _currFrame->elem = BSONElement(_currElem, nameLen, nameLen + len); } return cursor.ptr; } diff --git a/src/mongo/bson/bsonelement.cpp b/src/mongo/bson/bsonelement.cpp index e02dfd07f33..c6cf2437be2 100644 --- a/src/mongo/bson/bsonelement.cpp +++ b/src/mongo/bson/bsonelement.cpp @@ -132,10 +132,10 @@ BSONObj BSONElement::_jsonStringGenerator(const Generator& g, switch (type()) { case mongo::String: - g.writeString(buffer, StringData(valuestr(), valuestrsize() - 1)); + g.writeString(buffer, valueStringData()); break; case Symbol: - g.writeSymbol(buffer, StringData(valuestr(), valuestrsize() - 1)); + g.writeSymbol(buffer, valueStringData()); break; case NumberLong: g.writeInt64(buffer, _numberLong()); @@ -182,9 +182,7 @@ BSONObj BSONElement::_jsonStringGenerator(const Generator& g, } case DBRef: // valuestrsize() returns the size including the null terminator - g.writeDBRef(buffer, - StringData(valuestr(), valuestrsize() - 1), - OID::from(valuestr() + valuestrsize())); + g.writeDBRef(buffer, valueStringData(), OID::from(valuestr() + valuestrsize())); break; case jstOID: g.writeOID(buffer, __oid()); @@ -273,15 +271,7 @@ namespace { // Compares two string elements using a simple binary compare. int compareElementStringValues(const BSONElement& leftStr, const BSONElement& rightStr) { - // we use memcmp as we allow zeros in UTF8 strings - int lsz = leftStr.valuestrsize(); - int rsz = rightStr.valuestrsize(); - int common = std::min(lsz, rsz); - int res = memcmp(leftStr.valuestr(), rightStr.valuestr(), common); - if (res) - return res; - // longer std::string is the greater one - return lsz - rsz; + return leftStr.valueStringData().compare(rightStr.valueStringData()); } } // namespace @@ -650,8 +640,8 @@ BSONElement BSONElement::operator[](StringData field) const { namespace { MONGO_COMPILER_NOINLINE void msgAssertedBadType [[noreturn]] (const char* data) { // We intentionally read memory that may be out of the allocated memory's boundary, so do not - // do this when the adress sanitizer is enabled. We do this in an attempt to log as much context - // about the failure, even if that risks undefined behavior or a segmentation fault. + // do this when the address sanitizer is enabled. We do this in an attempt to log as much + // context about the failure, even if that risks undefined behavior or a segmentation fault. #if !__has_feature(address_sanitizer) bool logMemory = true; #else @@ -908,7 +898,7 @@ std::string BSONElement::_asCode() const { switch (type()) { case mongo::String: case Code: - return std::string(valuestr(), valuestrsize() - 1); + return valueStringData().toString(); case CodeWScope: return std::string(codeWScopeCode(), ConstDataView(valuestr()).read<LittleEndian<int>>() - 1); diff --git a/src/mongo/bson/bsonelement.h b/src/mongo/bson/bsonelement.h index c99623c7e80..722ea0937b3 100644 --- a/src/mongo/bson/bsonelement.h +++ b/src/mongo/bson/bsonelement.h @@ -62,7 +62,8 @@ typedef BSONElement be; typedef BSONObj bo; typedef BSONObjBuilder bob; -/** BSONElement represents an "element" in a BSONObj. So for the object { a : 3, b : "abc" }, +/** + BSONElement represents an "element" in a BSONObj. So for the object { a : 3, b : "abc" }, 'a : 3' is the first element (key+value). The BSONElement object points into the BSONObj's data. Thus the BSONObj must stay in scope @@ -105,11 +106,12 @@ public: const StringData::ComparatorInterface* comparator); - /** These functions, which start with a capital letter, throw if the - element is not of the required type. Example: - - std::string foo = obj["foo"].String(); // std::exception if not a std::string type or DNE - */ + /** + * These functions, which start with a capital letter, throw if the + * element is not of the required type. Example: + * + * std::string foo = obj["foo"].String(); // std::exception if not a std::string type or DNE + */ std::string String() const { return chk(mongo::String).str(); } @@ -146,17 +148,19 @@ public: return chk(jstOID).__oid(); } - /** @return the embedded object associated with this field. - Note the returned object is a reference to within the parent bson object. If that - object is out of scope, this pointer will no longer be valid. Call getOwned() on the - returned BSONObj if you need your own copy. - throws AssertionException if the element is not of type object. - */ + /** + * @return the embedded object associated with this field. + * Note the returned object is a reference to within the parent bson object. If that + * object is out of scope, this pointer will no longer be valid. Call getOwned() on the + * returned BSONObj if you need your own copy. + * throws AssertionException if the element is not of type object. + */ BSONObj Obj() const; - /** populate v with the value of the element. If type does not match, throw exception. - useful in templates -- see also BSONObj::Vals(). - */ + /** + * populate v with the value of the element. If type does not match, throw exception. + * useful in templates -- see also BSONObj::Vals(). + */ void Val(Date_t& v) const { v = Date(); } @@ -183,9 +187,10 @@ public: v = String(); } - /** Use ok() to check if a value is assigned: - if( myObj["foo"].ok() ) ... - */ + /** + * Use ok() to check if a value is assigned: + * if( myObj["foo"].ok() ) ... + */ bool ok() const { return !eoo(); } @@ -249,25 +254,31 @@ public: return toString(); } - /** Returns the type of the element */ + /** + * Returns the type of the element + */ BSONType type() const { const signed char typeByte = ConstDataView(data).read<signed char>(); return static_cast<BSONType>(typeByte); } - /** retrieve a field within this element - throws exception if *this is not an embedded object - */ + /** + * retrieve a field within this element + * throws exception if *this is not an embedded object + */ BSONElement operator[](StringData field) const; - /** See canonicalizeBSONType in bsontypes.h */ + /** + * See canonicalizeBSONType in bsontypes.h + */ int canonicalType() const { return canonicalizeBSONType(type()); } - /** Indicates if it is the end-of-object element, which is present at the end of - every BSON object. - */ + /** + * Indicates if it is the end-of-object element, which is present at the end of + * every BSON object. + */ bool eoo() const { return type() == EOO; } @@ -279,16 +290,21 @@ public: return totalSize; } - /** Wrap this element up as a singleton object. */ + /** + * Wrap this element up as a singleton object. + */ BSONObj wrap() const; - /** Wrap this element up as a singleton object with a new name. */ + /** + * Wrap this element up as a singleton object with a new name. + */ BSONObj wrap(StringData newName) const; - /** field name of the element. e.g., for - name : "Joe" - "name" is the fieldname - */ + /** + * field name of the element. e.g., for + * name : "Joe" + * "name" is the fieldname + */ const char* fieldName() const { if (eoo()) return ""; // no fieldname for it. @@ -306,11 +322,15 @@ public: return StringData(fieldName(), eoo() ? 0 : fieldNameSize() - 1); } - /** raw data of the element's value (so be careful). */ + /** + * raw data of the element's value (so be careful). + */ const char* value() const { return (data + fieldNameSize() + 1); } - /** size in bytes of the element's value (when applicable). */ + /** + * size in bytes of the element's value (when applicable). + */ int valuesize() const { return size() - fieldNameSize() - 1; } @@ -319,9 +339,11 @@ public: return type() == mongo::Bool; } - /** @return value of a boolean element. - You must assure element is a boolean before - calling. */ + /** + * @return value of a boolean element. + * You must assure element is a boolean before + * calling. + */ bool boolean() const { return *value() ? true : false; } @@ -330,40 +352,52 @@ public: return isBoolean() && boolean(); } - /** Retrieve a java style date value from the element. - Ensure element is of type Date before calling. - @see Bool(), trueValue() - */ + /** + * Retrieve a java style date value from the element. + * Ensure element is of type Date before calling. + * @see Bool(), trueValue() + */ Date_t date() const { return Date_t::fromMillisSinceEpoch(ConstDataView(value()).read<LittleEndian<long long>>()); } - /** Convert the value to boolean, regardless of its type, in a javascript-like fashion - (i.e., treats zero and null and eoo as false). - */ + /** + * Convert the value to boolean, regardless of its type, in a javascript-like fashion + * (i.e., treats zero and null and eoo as false). + */ bool trueValue() const; - /** True if element is of a numeric type. */ + /** + * True if element is of a numeric type. + */ bool isNumber() const; - /** Return double value for this field. MUST be NumberDouble type. */ + /** + * Return double value for this field. MUST be NumberDouble type. + */ double _numberDouble() const { return ConstDataView(value()).read<LittleEndian<double>>(); } - /** Return int value for this field. MUST be NumberInt type. */ + /** + * Return int value for this field. MUST be NumberInt type. + */ int _numberInt() const { return ConstDataView(value()).read<LittleEndian<int>>(); } - /** Return decimal128 value for this field. MUST be NumberDecimal type. */ + /** + * Return decimal128 value for this field. MUST be NumberDecimal type. + */ Decimal128 _numberDecimal() const { uint64_t low = ConstDataView(value()).read<LittleEndian<long long>>(); uint64_t high = ConstDataView(value() + sizeof(long long)).read<LittleEndian<long long>>(); return Decimal128(Decimal128::Value({low, high})); } - /** Return long long value for this field. MUST be NumberLong type. */ + /** + * Return long long value for this field. MUST be NumberLong type. + */ long long _numberLong() const { return ConstDataView(value()).read<LittleEndian<long long>>(); } @@ -378,11 +412,13 @@ public: */ int numberInt() const; - /** Like numberInt() but with well-defined behavior for doubles that - * are NaNs, or too large/small to be represented as int. - * NaNs -> 0 - * very large doubles -> INT_MAX - * very small doubles -> INT_MIN */ + /** + * Like numberInt() but with well-defined behavior for doubles that + * are NaNs, or too large/small to be represented as int. + * NaNs -> 0 + * very large doubles -> INT_MAX + * very small doubles -> INT_MIN + */ int safeNumberInt() const; /** @@ -395,19 +431,23 @@ public: */ long long numberLong() const; - /** Like numberLong() but with well-defined behavior for doubles that - * are NaNs, or too large/small to be represented as long longs. - * NaNs -> 0 - * very large doubles -> LLONG_MAX - * very small doubles -> LLONG_MIN */ + /** + * Like numberLong() but with well-defined behavior for doubles that + * are NaNs, or too large/small to be represented as long longs. + * NaNs -> 0 + * very large doubles -> LLONG_MAX + * very small doubles -> LLONG_MIN + */ long long safeNumberLong() const; - /** This safeNumberLongForHash() function does the same thing as safeNumberLong, but it - * preserves edge-case behavior from older versions. + /** + * This safeNumberLongForHash() function does the same thing as safeNumberLong, but it + * preserves edge-case behavior from older versions. */ long long safeNumberLongForHash() const; - /** Convert a numeric field to long long, and uassert the conversion is exact. + /** + * Convert a numeric field to long long, and uassert the conversion is exact. */ long long exactNumberLong() const; @@ -452,67 +492,75 @@ public: */ StatusWith<int> parseIntegerElementToInt() const; - /** Retrieve decimal value for the element safely. */ + /** + * Retrieve decimal value for the element safely. + */ Decimal128 numberDecimal() const; - /** Retrieve the numeric value of the element. If not of a numeric type, returns 0. - Note: casts to double, data loss may occur with large (>52 bit) NumberLong values. - */ + /** + * Retrieve the numeric value of the element. If not of a numeric type, returns 0. + * Note: casts to double, data loss may occur with large (>52 bit) NumberLong values. + */ double numberDouble() const; - /** Retrieve the numeric value of the element. If not of a numeric type, returns 0. - Note: casts to double, data loss may occur with large (>52 bit) NumberLong values. - */ + /** + * Retrieve the numeric value of the element. If not of a numeric type, returns 0. + * Note: casts to double, data loss may occur with large (>52 bit) NumberLong values. + */ double number() const { return numberDouble(); } - /** Like numberDouble() but with well-defined behavior for doubles that - * are NaNs, or too large/small to be represented as doubles. - * NaNs -> 0 - * very large decimals -> DOUBLE_MAX - * very small decimals -> DOUBLE_MIN */ + /** + * Like numberDouble() but with well-defined behavior for doubles that + * are NaNs, or too large/small to be represented as doubles. + * NaNs -> 0 + * very large decimals -> DOUBLE_MAX + * very small decimals -> DOUBLE_MIN + */ double safeNumberDouble() const; - /** Retrieve the object ID stored in the object. - You must ensure the element is of type jstOID first. */ + /** + * Retrieve the object ID stored in the object. + * You must ensure the element is of type jstOID first. + */ mongo::OID __oid() const { return OID::from(value()); } - /** True if element is null. */ + /** + * True if element is null. + */ bool isNull() const { return type() == jstNULL; } - /** Size of a BSON String element. - Requires that type() == mongo::String. - @return String size including its null-termination. - */ + /** + * Size of a BSON String element. + * Requires that type() == mongo::String. + * @return String size including its null-termination. + */ int valuestrsize() const { return ConstDataView(value()).read<LittleEndian<int>>(); } - // for objects the size *includes* the size of the size field + /** + * for objects the size *includes* the size of the size field + */ size_t objsize() const { return ConstDataView(value()).read<LittleEndian<uint32_t>>(); } - /** Get a string's value. Also gives you start of the real data for an embedded object. - You must assure data is of an appropriate type first -- see also valuestrsafe(). - */ - const char* valuestr() const { - return value() + 4; - } - - /** Like valuestr, but returns a valid empty string if `type() != mongo::String`. */ - const char* valuestrsafe() const { - return type() == mongo::String ? valuestr() : ""; - } - /** Like valuestrsafe, but returns StringData. */ + /** + * Get a string's value. Returns a valid empty string if + * `type() != mongo::String`. + */ StringData valueStringDataSafe() const { return type() == mongo::String ? StringData(valuestr(), valuestrsize() - 1) : StringData(); } - /** Like valuestrsafe, but returns std::string. */ + + /** + * Like valueStringDataSafe, but returns std::string. + */ std::string str() const { return valueStringDataSafe().toString(); } @@ -525,41 +573,54 @@ public: return StringData(valuestr(), valuestrsize() - 1); } - /** Get javascript code of a CodeWScope data element. */ + /** + * Get javascript code of a CodeWScope data element. + */ const char* codeWScopeCode() const { massert(16177, "not codeWScope", type() == CodeWScope); return value() + 4 + 4; // two ints precede code (see BSON spec) } - /** Get length of the code part of the CodeWScope object - * This INCLUDES the null char at the end */ + /** + * Get length of the code part of the CodeWScope object + * This INCLUDES the null char at the end + */ int codeWScopeCodeLen() const { massert(16178, "not codeWScope", type() == CodeWScope); return ConstDataView(value() + 4).read<LittleEndian<int>>(); } - /* Get the scope SavedContext of a CodeWScope data element. + /** + * Get the scope SavedContext of a CodeWScope data element. */ const char* codeWScopeScopeData() const { return codeWScopeCode() + codeWScopeCodeLen(); } - /** Get the embedded object this element holds. */ + /** + * Get the embedded object this element holds. + */ BSONObj embeddedObject() const; - /* uasserts if not an object */ + /** + * uasserts if not an object + */ BSONObj embeddedObjectUserCheck() const; BSONObj codeWScopeObject() const; - /** Get raw binary data. Element must be of type BinData. Doesn't handle type 2 specially */ + /** + * Get raw binary data. Element must be of type BinData. Doesn't handle type 2 specially + */ const char* binData(int& len) const { // BinData: <int len> <byte subtype> <byte[len] data> verify(type() == BinData); len = valuestrsize(); return value() + 5; } - /** Get binary data. Element must be of type BinData. Handles type 2 */ + /** + * Get binary data. Element must be of type BinData. Handles type 2 + */ const char* binDataClean(int& len) const { // BinData: <int len> <byte subtype> <byte[len] data> if (binDataType() != ByteArrayDeprecated) { @@ -586,13 +647,17 @@ public: return {first, last}; } - /** Retrieve the regex std::string for a Regex element */ + /** + * Retrieve the regex std::string for a Regex element + */ const char* regex() const { verify(type() == RegEx); return value(); } - /** Retrieve the regex flags (options) for a Regex element */ + /** + * Retrieve the regex flags (options) for a Regex element + */ const char* regexFlags() const { const char* p = regex(); return p + strlen(p) + 1; @@ -663,10 +728,14 @@ public: return data; } - /** Constructs an empty element */ + /** + * Constructs an empty element + */ BSONElement(); - /** True if this element may contain subobjects. */ + /** + * True if this element may contain subobjects. + */ bool mayEncapsulate() const { switch (type()) { case Object: @@ -678,7 +747,9 @@ public: } } - /** True if this element can be a BSONObj */ + /** + * True if this element can be a BSONObj + */ bool isABSONObj() const { switch (type()) { case Object: @@ -751,7 +822,6 @@ public: return mongo::OID::from(start); } - // @param maxLen don't scan more than maxLen bytes explicit BSONElement(const char* d) : data(d) { // While we should skip the type, and add 1 for the terminating null byte, just include // the type byte in the strlen call: the extra byte cancels out. As an extra bonus, this @@ -761,14 +831,12 @@ public: totalSize = computeSize(type, d, fieldNameSize_); } - struct CachedSizeTag {}; // Opts in to next constructor. - /** * Construct a BSONElement where you already know the length of the name and/or the total size * of the element. fieldNameSize includes the null terminator. You may pass -1 for either or * both sizes to indicate that they are unknown and should be computed. */ - BSONElement(const char* d, int fieldNameSize, int totalSize, CachedSizeTag) : data(d) { + BSONElement(const char* d, int fieldNameSize, int totalSize) : data(d) { if (eoo()) { fieldNameSize_ = 0; this->totalSize = 1; @@ -832,6 +900,18 @@ public: static const long long kSmallestSafeLongLongAsDouble; private: + /** + * Get a string's value. Also gives you start of the real data for an embedded object. + * You must assure data is of an appropriate type first, like the type check in + * valueStringDataSafe(). You should use the string's size when performing any operations + * on the data to disambiguate between potential embedded null's and the terminating null. + * This function is only used in limited forms internally. Not to be exposed publicly. + * If a char* is desired use valueStringDataSafe().rawData(). + */ + const char* valuestr() const { + return value() + 4; + } + template <typename Generator> BSONObj _jsonStringGenerator(const Generator& g, bool includeSeparator, @@ -885,7 +965,9 @@ inline bool BSONElement::trueValue() const { } } -/** @return true if element is of a numeric type. */ +/** + * @return true if element is of a numeric type. + */ inline bool BSONElement::isNumber() const { switch (type()) { case NumberLong: @@ -1003,11 +1085,13 @@ inline long long BSONElement::numberLong() const { } } -/** Like numberLong() but with well-defined behavior for doubles and decimals that - * are NaNs, or too large/small to be represented as long longs. - * NaNs -> 0 - * very large values -> LLONG_MAX - * very small values -> LLONG_MIN */ +/** + * Like numberLong() but with well-defined behavior for doubles and decimals that + * are NaNs, or too large/small to be represented as long longs. + * NaNs -> 0 + * very large values -> LLONG_MAX + * very small values -> LLONG_MIN + */ inline long long BSONElement::safeNumberLong() const { switch (type()) { case NumberDouble: { diff --git a/src/mongo/bson/bsonobj.cpp b/src/mongo/bson/bsonobj.cpp index a2f6e120ea4..73f824735ed 100644 --- a/src/mongo/bson/bsonobj.cpp +++ b/src/mongo/bson/bsonobj.cpp @@ -601,9 +601,9 @@ bool BSONObj::getBoolField(StringData name) const { return e.type() == Bool ? e.boolean() : false; } -const char* BSONObj::getStringField(StringData name) const { +StringData BSONObj::getStringField(StringData name) const { BSONElement e = getField(name); - return e.type() == String ? e.valuestr() : ""; + return e.valueStringDataSafe(); } bool BSONObj::getObjectID(BSONElement& e) const { diff --git a/src/mongo/bson/bsonobj.h b/src/mongo/bson/bsonobj.h index c7c484ec9c5..10c6483e70d 100644 --- a/src/mongo/bson/bsonobj.h +++ b/src/mongo/bson/bsonobj.h @@ -124,7 +124,9 @@ public: static constexpr char kMinBSONLength = 5; - /** Construct an empty BSONObj -- that is, {}. */ + /** + * Construct an empty BSONObj -- that is, {}. + */ BSONObj() { // Little endian ordering here, but that is ok regardless as BSON is spec'd to be // little endian external to the system. (i.e. the rest of the implementation of @@ -132,7 +134,8 @@ public: _objdata = kEmptyObjectPrototype; } - /** Construct a BSONObj from data in the proper format. + /** + * Construct a BSONObj from data in the proper format. * Use this constructor when something else owns bsonData's buffer */ template <typename Traits = DefaultSizeTrait> @@ -144,7 +147,9 @@ public: : _objdata(ownedBuffer.get() ? ownedBuffer.get() : BSONObj().objdata()), _ownedBuffer(std::move(ownedBuffer)) {} - /** Move construct a BSONObj */ + /** + * Move construct a BSONObj + */ BSONObj(BSONObj&& other) noexcept : _objdata(std::move(other._objdata)), _ownedBuffer(std::move(other._ownedBuffer)) { other._objdata = BSONObj()._objdata; // To return to an empty state. @@ -154,18 +159,23 @@ public: // The explicit move constructor above will inhibit generation of the copy ctor, so // explicitly request the default implementation. - /** Copy construct a BSONObj. */ + /** + * Copy construct a BSONObj. + */ BSONObj(const BSONObj&) = default; - /** Provide assignment semantics. We use the value taking form so that we can use copy - * and swap, and consume both lvalue and rvalue references. + /** + * Provide assignment semantics. We use the value taking form so that we can use copy + * and swap, and consume both lvalue and rvalue references. */ BSONObj& operator=(BSONObj otherCopy) noexcept { this->swap(otherCopy); return *this; } - /** Swap this BSONObj with 'other' */ + /** + * Swap this BSONObj with 'other' + */ void swap(BSONObj& other) noexcept { using std::swap; swap(_objdata, other._objdata); @@ -238,23 +248,31 @@ public: return std::move(sink._ownedBuffer); } - /** If the data buffer is under the control of this BSONObj, return it. - Else return an owned copy. - */ + /** + * If the data buffer is under the control of this BSONObj, return it. + * Else return an owned copy. + */ BSONObj getOwned() const; - /** Returns an owned copy of the given BSON object. */ + /** + * Returns an owned copy of the given BSON object. + */ static BSONObj getOwned(const BSONObj& obj); - /** @return a new full (and owned) copy of the object. */ + /** + * @return a new full (and owned) copy of the object. + */ BSONObj copy() const; - /** @return a new full (and owned) redacted copy of the object. */ + /** + * @return a new full (and owned) redacted copy of the object. + */ BSONObj redact() const; - /** Readable representation of a BSON object in an extended JSON-style notation. - This is an abbreviated representation which might be used for logging. - */ + /** + * Readable representation of a BSON object in an extended JSON-style notation. + * This is an abbreviated representation which might be used for logging. + */ enum { maxToStringRecursionDepth = 100 }; std::string toString(bool redactValues = false) const; @@ -264,9 +282,10 @@ public: bool redactValues = false, int depth = 0) const; - /** Properly formatted JSON string. - @param pretty if true we try to add some lf's and indentation - */ + /** + * Properly formatted JSON string. + * @param pretty if true we try to add some lf's and indentation + */ std::string jsonString(JsonStringFormat format = ExtendedCanonicalV2_0_0, int pretty = 0, bool isArray = false, @@ -313,8 +332,9 @@ public: BSONObj addFields(const BSONObj& from, const boost::optional<StringDataSet>& fields = boost::none) const; - /** remove specified field and return a new object with the remaining fields. - slowish as builds a full new object + /** + * Remove specified field and return a new object with the remaining fields. + * slowish as builds a full new object */ BSONObj removeField(StringData name) const; @@ -324,9 +344,10 @@ public: BSONObj removeFields(const std::set<std::string>& fields) const; BSONObj removeFields(const StringDataSet& fields) const; - /** returns # of top level fields in the object - note: iterates to count the fields - */ + /** + * Returns # of top level fields in the object + * note: iterates to count the fields + */ int nFields() const; /** @@ -335,18 +356,19 @@ public: template <class Container> Container getFieldNames() const; - /** Get the field of the specified name. eoo() is true on the returned - element if not found. - */ + /** + * Get the field of the specified name. eoo() is true on the returned + * element if not found. + */ BSONElement getField(StringData name) const; - /** Get several fields at once. This is faster than separate getField() calls as the size of - elements iterated can then be calculated only once each. - @param n number of fieldNames, and number of elements in the fields array - @param fields if a field is found its element is stored in its corresponding position in - this array. if not found the array element is unchanged. + /** + * Get several fields at once. This is faster than separate getField() calls as the size of + * elements iterated can then be calculated only once each. + * @param n number of fieldNames, and number of elements in the fields array + * @param fields if a field is found its element is stored in its corresponding position in + * this array. if not found the array element is unchanged. */ - void getFields(unsigned n, const char** fieldNames, BSONElement* fields) const; /** @@ -358,9 +380,10 @@ public: std::array<BSONElement, N>* fields) const; - /** Get the field of the specified name. eoo() is true on the returned - element if not found. - */ + /** + * Get the field of the specified name. eoo() is true on the returned + * element if not found. + */ BSONElement operator[](StringData field) const { return getField(field); } @@ -372,40 +395,52 @@ public: return getField(s.c_str()); } - /** @return true if field exists */ + /** + * @return true if field exists + */ bool hasField(StringData name) const { return !getField(name).eoo(); } - /** @return true if field exists */ + /** + * @return true if field exists + */ bool hasElement(StringData name) const { return hasField(name); } - /** @return "" if DNE or wrong type */ - const char* getStringField(StringData name) const; + /** + * Looks up the element with the given 'name'. If the element is a string, + * returns it as a StringData. Otherwise returns an empty StringData. + */ + StringData getStringField(StringData name) const; - /** @return subobject of the given name */ + /** + * @return subobject of the given name + */ BSONObj getObjectField(StringData name) const; - /** @return INT_MIN if not present - does some type conversions */ + /** + * @return INT_MIN if not present - does some type conversions + */ int getIntField(StringData name) const; - /** @return false if not present - @see BSONElement::trueValue() + /** + * @return false if not present + * @see BSONElement::trueValue() */ bool getBoolField(StringData name) const; - /** @param pattern a BSON obj indicating a set of (un-dotted) field - * names. Element values are ignored. - * @return a BSON obj constructed by taking the elements of this obj - * that correspond to the fields in pattern. Field names of the - * returned object are replaced with the empty string. If field in - * pattern is missing, it is omitted from the returned object. - * - * Example: if this = {a : 4 , b : 5 , c : 6}) - * this.extractFieldsUnDotted({a : 1 , c : 1}) -> {"" : 4 , "" : 6 } - * this.extractFieldsUnDotted({b : "blah"}) -> {"" : 5} + /** + * @param pattern a BSON obj indicating a set of (un-dotted) field + * names. Element values are ignored. + * @return a BSON obj constructed by taking the elements of this obj + * that correspond to the fields in pattern. Field names of the + * returned object are replaced with the empty string. If field in + * pattern is missing, it is omitted from the returned object. * + * Example: if this = {a : 4 , b : 5 , c : 6}) + * this.extractFieldsUnDotted({a : 1 , c : 1}) -> {"" : 4 , "" : 6 } + * this.extractFieldsUnDotted({b : "blah"}) -> {"" : 5} */ BSONObj extractFieldsUndotted(const BSONObj& pattern) const; void extractFieldsUndotted(BSONObjBuilder* b, const BSONObj& pattern) const; @@ -415,22 +450,29 @@ public: BSONElement getFieldUsingIndexNames(StringData fieldName, const BSONObj& indexKey) const; - /** arrays are bson objects with numeric and increasing field names - @return true if field names are numeric and increasing + /** + * arrays are bson objects with numeric and increasing field names + * @return true if field names are numeric and increasing */ bool couldBeArray() const; - /** @return the raw data of the object */ + /** + * @return the raw data of the object + */ const char* objdata() const { return _objdata; } - /** @return total size of the BSON object in bytes */ + /** + * @return total size of the BSON object in bytes + */ int objsize() const { return ConstDataView(objdata()).read<LittleEndian<int>>(); } - /** performs a cursory check on the object's size only. */ + /** + * performs a cursory check on the object's size only. + */ template <typename Traits = DefaultSizeTrait> bool isValid() const { static_assert(Traits::MaxSize > 0 && Traits::MaxSize <= std::numeric_limits<int>::max(), @@ -445,19 +487,23 @@ public: */ Status storageValidEmbedded() const; - /** @return true if object is empty -- i.e., {} */ + /** + * @return true if object is empty -- i.e., {} + */ bool isEmpty() const { return objsize() <= kMinBSONLength; } - /* + /** * Whether this BSONObj is the "empty prototype" special case. */ bool isEmptyPrototype() const { return _objdata == kEmptyObjectPrototype; } - /** Alternative output format */ + /** + * Alternative output format + */ std::string hexDump() const; // @@ -528,9 +574,10 @@ public: */ bool isFieldNamePrefixOf(const BSONObj& otherObj) const; - /** This is "shallow equality" -- ints and doubles won't match. for a - deep equality test use woCompare (which is slower). - */ + /** + * This is "shallow equality" -- ints and doubles won't match. for a + * deep equality test use woCompare (which is slower). + */ bool binaryEqual(const BSONObj& r) const { int os = objsize(); if (os == r.objsize()) { @@ -539,12 +586,15 @@ public: return false; } - /** @return first field of the object */ + /** + * @return first field of the object + */ BSONElement firstElement() const { return BSONElement(objdata() + 4); } - /** faster than firstElement().fieldName() - for the first element we can easily find the + /** + * faster than firstElement().fieldName() - for the first element we can easily find the * fieldname without computing the element size. */ const char* firstElementFieldName() const { @@ -561,21 +611,26 @@ public: return (BSONType)*p; } - /** Get the _id field from the object. For good performance drivers should - assure that _id is the first element of the object; however, correct operation - is assured regardless. - @return true if found - */ + /** + * Get the _id field from the object. For good performance drivers should + * assure that _id is the first element of the object; however, correct operation + * is assured regardless. + * @return true if found + */ bool getObjectID(BSONElement& e) const; - // Return a version of this object where top level elements of types - // that are not part of the bson wire protocol are replaced with - // std::string identifier equivalents. - // TODO Support conversion of element types other than min and max. + /** + * Return a version of this object where top level elements of types + * that are not part of the bson wire protocol are replaced with + * std::string identifier equivalents. + * TODO Support conversion of element types other than min and max. + */ BSONObj clientReadable() const; - /** Return new object with the field names replaced by those in the - passed object. */ + /** + * Return new object with the field names replaced by those in the + * passed object. + */ BSONObj replaceFieldNames(const BSONObj& obj) const; static BSONObj stripFieldNames(const BSONObj& obj); @@ -587,9 +642,13 @@ public: */ bool valid() const; - /** add all elements of the object to the specified vector */ + /** + * add all elements of the object to the specified vector + */ void elems(std::vector<BSONElement>&) const; - /** add all elements of the object to the specified list */ + /** + * add all elements of the object to the specified list + */ void elems(std::list<BSONElement>&) const; friend class BSONObjIterator; @@ -709,14 +768,18 @@ public: return BSONObjStlIterator(eooElem); } - /** pre-increment */ + /** + * pre-increment + */ BSONObjStlIterator& operator++() { dassert(!_cur.eoo()); *this = BSONObjStlIterator(BSONElement(_cur.rawdata() + _cur.size())); return *this; } - /** post-increment */ + /** + * post-increment + */ BSONObjStlIterator operator++(int) { BSONObjStlIterator oldPos = *this; ++*this; @@ -755,7 +818,8 @@ private: */ class BSONObjIterator { public: - /** Create an iterator for a BSON object. + /** + * Create an iterator for a BSON object. */ explicit BSONObjIterator(const BSONObj& jso) { int sz = jso.objsize(); @@ -772,7 +836,7 @@ public: _theend = end - 1; } - /* + /** * Advance '_pos' by currentElement.size(). The element passed in must be equivalent to the * current element '_pos' is at. */ @@ -791,13 +855,17 @@ public: return sz <= (_theend - _pos) && memcmp(otherElement.rawdata(), _pos, sz) == 0; } - /** @return true if more elements exist to be enumerated. */ + /** + * @return true if more elements exist to be enumerated. + */ bool more() const { return _pos < _theend; } - /** @return true if more elements exist to be enumerated INCLUDING the EOO element which is - * always at the end. */ + /** + * @return true if more elements exist to be enumerated INCLUDING the EOO element which is + * always at the end. + */ bool moreWithEOO() const { return _pos <= _theend; } @@ -809,13 +877,17 @@ public: return e; } - /** pre-increment */ + /** + * pre-increment + */ BSONObjIterator& operator++() { next(); return *this; } - /** post-increment */ + /** + * post-increment + */ BSONObjIterator operator++(int) { BSONObjIterator oldPos = *this; next(); @@ -841,7 +913,9 @@ private: const char* _theend; }; -/** Base class implementing ordered iteration through BSONElements. */ +/** + * Base class implementing ordered iteration through BSONElements. + */ class BSONIteratorSorted { BSONIteratorSorted(const BSONIteratorSorted&) = delete; BSONIteratorSorted& operator=(const BSONIteratorSorted&) = delete; @@ -861,8 +935,7 @@ public: const auto& element = _fields[_cur++]; return BSONElement(element.fieldName.rawData() - 1, // Include type byte element.fieldName.size() + 1, // Add null terminator - element.totalSize, - BSONElement::CachedSizeTag{}); + element.totalSize); } return BSONElement(); @@ -882,7 +955,9 @@ private: int _cur; }; -/** Provides iteration of a BSONObj's BSONElements in lexical field order. */ +/** + * Provides iteration of a BSONObj's BSONElements in lexical field order. + */ class BSONObjIteratorSorted : public BSONIteratorSorted { public: BSONObjIteratorSorted(const BSONObj& object); diff --git a/src/mongo/bson/mutable/document.cpp b/src/mongo/bson/mutable/document.cpp index 56fa36d11f5..db1dabe62b4 100644 --- a/src/mongo/bson/mutable/document.cpp +++ b/src/mongo/bson/mutable/document.cpp @@ -481,8 +481,7 @@ struct ElementRep { BSONElement toSerializedElement(const BSONObj& holder) const { return BSONElement(holder.objdata() + offset, // _fieldNameSize, - _totalSize, - BSONElement::CachedSizeTag()); + _totalSize); } private: diff --git a/src/mongo/bson/mutable/element.h b/src/mongo/bson/mutable/element.h index c60bd4ea589..a0312c31b12 100644 --- a/src/mongo/bson/mutable/element.h +++ b/src/mongo/bson/mutable/element.h @@ -760,10 +760,7 @@ inline Element::Element(Document* doc, RepIdx repIdx) : _doc(doc), _repIdx(repId } inline StringData Element::getValueStringOrSymbol() const { - const BSONElement value = getValue(); - const char* str = value.valuestr(); - const size_t size = value.valuestrsize() - 1; - return StringData(str, size); + return getValue().valueStringData(); } inline bool operator==(const Element& l, const Element& r) { diff --git a/src/mongo/bson/util/bsoncolumn.cpp b/src/mongo/bson/util/bsoncolumn.cpp index ce288893f2d..c681ba3ddae 100644 --- a/src/mongo/bson/util/bsoncolumn.cpp +++ b/src/mongo/bson/util/bsoncolumn.cpp @@ -117,10 +117,7 @@ int BSONColumn::ElementStorage::Element::size() const { } BSONElement BSONColumn::ElementStorage::Element::element() const { - return {_buffer, - _nameSize + 1, - _valueSize + _nameSize + kElementValueOffset, - BSONElement::CachedSizeTag{}}; + return {_buffer, _nameSize + 1, _valueSize + _nameSize + kElementValueOffset}; } BSONColumn::ElementStorage::ContiguousBlock::ContiguousBlock(ElementStorage& storage) @@ -575,7 +572,7 @@ BSONColumn::Iterator::DecodingState::_loadControl(BSONColumn& column, if (_isLiteral(control)) { // Load BSONElement from the literal and set last encoded in case we need to calculate // deltas from this literal - BSONElement literalElem(buffer, 1, -1, BSONElement::CachedSizeTag{}); + BSONElement literalElem(buffer, 1, -1); _loadLiteral(literalElem); _decoder64 = boost::none; diff --git a/src/mongo/bson/util/bsoncolumnbuilder.cpp b/src/mongo/bson/util/bsoncolumnbuilder.cpp index 139a7adf747..7e9dd840676 100644 --- a/src/mongo/bson/util/bsoncolumnbuilder.cpp +++ b/src/mongo/bson/util/bsoncolumnbuilder.cpp @@ -766,7 +766,7 @@ bool BSONColumnBuilder::EncodingState::_appendDouble(double value, double previo } BSONElement BSONColumnBuilder::EncodingState::_previous() const { - return {_prev.get(), 1, _prevSize, BSONElement::CachedSizeTag{}}; + return {_prev.get(), 1, _prevSize}; } diff --git a/src/mongo/client/authenticate.cpp b/src/mongo/client/authenticate.cpp index 2c26e03953e..3c1b7ad6535 100644 --- a/src/mongo/client/authenticate.cpp +++ b/src/mongo/client/authenticate.cpp @@ -112,7 +112,7 @@ StatusWith<OpMsgRequest> createX509AuthCmd(const BSONObj& params, StringData cli if (username != clientName.toString()) { StringBuilder message; message << "Username \""; - message << params[saslCommandUserFieldName].valuestr(); + message << params[saslCommandUserFieldName].valueStringData(); message << "\" does not match the provided client certificate user \""; message << clientName.toString() << "\""; return {ErrorCodes::AuthenticationFailed, message.str()}; diff --git a/src/mongo/client/dbclient_base.cpp b/src/mongo/client/dbclient_base.cpp index c4b38ddb3e3..98c49806f40 100644 --- a/src/mongo/client/dbclient_base.cpp +++ b/src/mongo/client/dbclient_base.cpp @@ -97,7 +97,8 @@ bool DBClientBase::isOk(const BSONObj& o) { bool DBClientBase::isNotPrimaryErrorString(const BSONElement& e) { return e.type() == String && - (str::contains(e.valuestr(), "not primary") || str::contains(e.valuestr(), "not master")); + (str::contains(e.valueStringData(), "not primary") || + str::contains(e.valueStringData(), "not master")); } void DBClientBase::setRequestMetadataWriter(rpc::RequestMetadataWriter writer) { diff --git a/src/mongo/client/replica_set_monitor_integration_test.cpp b/src/mongo/client/replica_set_monitor_integration_test.cpp index 3f2af0e8ce4..383411d0d61 100644 --- a/src/mongo/client/replica_set_monitor_integration_test.cpp +++ b/src/mongo/client/replica_set_monitor_integration_test.cpp @@ -141,7 +141,7 @@ public: ASSERT_OK(cmdStatus); const auto shards = res.data["shards"].Array(); ASSERT_FALSE(shards.empty()); - return shards.front().embeddedObject().getStringField("host"); + return shards.front().embeddedObject().getStringField("host").toString(); } protected: diff --git a/src/mongo/client/sdam/sdam_json_test_runner.cpp b/src/mongo/client/sdam/sdam_json_test_runner.cpp index 749585cf01c..b3fdcba09f0 100644 --- a/src/mongo/client/sdam/sdam_json_test_runner.cpp +++ b/src/mongo/client/sdam/sdam_json_test_runner.cpp @@ -486,7 +486,7 @@ private: _jsonTest = fromjson(json.str()); } - _testName = _jsonTest.getStringField("description"); + _testName = _jsonTest.getStringField("description").toString(); _testUri = uassertStatusOK(mongo::MongoURI::parse(_jsonTest["uri"].String())); _replicaSetName = _testUri.getOption("replicaSet"); diff --git a/src/mongo/client/sdam/server_description.cpp b/src/mongo/client/sdam/server_description.cpp index 042ad352c28..babe5d1a777 100644 --- a/src/mongo/client/sdam/server_description.cpp +++ b/src/mongo/client/sdam/server_description.cpp @@ -129,7 +129,7 @@ void ServerDescription::saveHosts(const BSONObj response) { void ServerDescription::saveTags(BSONObj tagsObj) { const auto keys = tagsObj.getFieldNames<std::set<std::string>>(); for (const auto& key : keys) { - _tags[key] = tagsObj.getStringField(key); + _tags[key] = tagsObj.getStringField(key).toString(); } } diff --git a/src/mongo/client/sdam/server_selection_json_test_runner.cpp b/src/mongo/client/sdam/server_selection_json_test_runner.cpp index 72379e27165..a8ebedef4c7 100644 --- a/src/mongo/client/sdam/server_selection_json_test_runner.cpp +++ b/src/mongo/client/sdam/server_selection_json_test_runner.cpp @@ -162,7 +162,7 @@ private: // Only create the initial server description if the original avg rtt is not "NULL". If it // is, the test case is meant to mimic creating the first ServerDescription which we will do // above. - std::string origRttAsString = _jsonTest.getStringField("avg_rtt_ms"); + std::string origRttAsString = _jsonTest.getStringField("avg_rtt_ms").toString(); if (origRttAsString.compare("NULL") != 0) { auto serverDescription = ServerDescriptionBuilder() .withAddress(HostAndPort("dummy")) @@ -286,7 +286,7 @@ private: // lowercased keywords. Also, change the key "tags_set" to "tags". // This can throw for test cases that have invalid read preferences. auto readPrefObj = _jsonTest.getObjectField("read_preference"); - std::string mode = readPrefObj.getStringField("mode"); + std::string mode = readPrefObj.getStringField("mode").toString(); mode[0] = ctype::toLower(mode[0]); auto tagSetsObj = readPrefObj["tag_sets"]; auto tags = tagSetsObj ? BSONArray(readPrefObj["tag_sets"].Obj()) : BSONArray(); @@ -364,7 +364,7 @@ private: auto tagsObj = server.getObjectField("tags"); const auto keys = tagsObj.getFieldNames<std::set<std::string>>(); for (const auto& key : keys) { - serverDescription.withTag(key, tagsObj.getStringField(key)); + serverDescription.withTag(key, tagsObj.getStringField(key).toString()); } serverDescriptions.push_back(serverDescription.instance()); diff --git a/src/mongo/db/auth/authorization_manager_impl.cpp b/src/mongo/db/auth/authorization_manager_impl.cpp index a733e5bf058..0f9f7ff2e47 100644 --- a/src/mongo/db/auth/authorization_manager_impl.cpp +++ b/src/mongo/db/auth/authorization_manager_impl.cpp @@ -138,7 +138,7 @@ public: Status set(const BSONElement& newValueElement) { if (newValueElement.type() == String) { - return setFromString(newValueElement.valuestrsafe()); + return setFromString(newValueElement.str()); } else if (newValueElement.type() == Array) { auto array = static_cast<BSONArray>(newValueElement.embeddedObject()); std::vector<UserName> out; @@ -244,7 +244,7 @@ bool loggedCommandOperatesOnAuthzData(const NamespaceString& nss, const BSONObj& return true; } else if (cmdName == "renameCollection") { const NamespaceString fromNamespace(cmdObj.firstElement().valueStringDataSafe()); - const NamespaceString toNamespace(cmdObj["to"].valueStringDataSafe()); + const NamespaceString toNamespace(cmdObj.getStringField("to")); if (fromNamespace.isAdminDB() || toNamespace.isAdminDB()) { return isAuthzCollection(fromNamespace.coll()) || isAuthzCollection(toNamespace.coll()); diff --git a/src/mongo/db/catalog/commit_quorum_options.cpp b/src/mongo/db/catalog/commit_quorum_options.cpp index 0a85a812b56..dba101c94e8 100644 --- a/src/mongo/db/catalog/commit_quorum_options.cpp +++ b/src/mongo/db/catalog/commit_quorum_options.cpp @@ -77,7 +77,7 @@ Status CommitQuorumOptions::parse(const BSONElement& commitQuorumElement) { } numNodes = static_cast<decltype(numNodes)>(cNumNodes); } else if (commitQuorumElement.type() == String) { - mode = commitQuorumElement.valuestrsafe(); + mode = commitQuorumElement.str(); if (mode.empty()) { return Status(ErrorCodes::FailedToParse, str::stream() << "commitQuorum can't be an empty string"); diff --git a/src/mongo/db/catalog/index_build_block.cpp b/src/mongo/db/catalog/index_build_block.cpp index 9773f85af46..9afe74f4b77 100644 --- a/src/mongo/db/catalog/index_build_block.cpp +++ b/src/mongo/db/catalog/index_build_block.cpp @@ -89,7 +89,7 @@ Status IndexBuildBlock::initForResume(OperationContext* opCtx, const IndexStateInfo& stateInfo, IndexBuildPhaseEnum phase) { - _indexName = _spec.getStringField("name"); + _indexName = _spec.getStringField("name").toString(); auto descriptor = collection->getIndexCatalog()->findIndexByName( opCtx, _indexName, true /* includeUnfinishedIndexes */); diff --git a/src/mongo/db/catalog/index_catalog_impl.cpp b/src/mongo/db/catalog/index_catalog_impl.cpp index 976e79f350f..09ae09ed303 100644 --- a/src/mongo/db/catalog/index_catalog_impl.cpp +++ b/src/mongo/db/catalog/index_catalog_impl.cpp @@ -893,7 +893,7 @@ Status IndexCatalogImpl::_doesSpecConflictWithExisting(OperationContext* opCtx, const CollectionPtr& collection, const BSONObj& spec, const bool includeUnfinishedIndexes) const { - const char* name = spec.getStringField(IndexDescriptor::kIndexNameFieldName); + StringData name = spec.getStringField(IndexDescriptor::kIndexNameFieldName); invariant(name[0]); const BSONObj key = spec.getObjectField(IndexDescriptor::kKeyPatternFieldName); diff --git a/src/mongo/db/catalog/index_key_validate.cpp b/src/mongo/db/catalog/index_key_validate.cpp index cabb38f8b1b..caa209fd3c3 100644 --- a/src/mongo/db/catalog/index_key_validate.cpp +++ b/src/mongo/db/catalog/index_key_validate.cpp @@ -224,11 +224,10 @@ Status validateKeyPattern(const BSONObj& key, IndexDescriptor::IndexVersion inde // "$**" is acceptable for a text index or wildcard index. if ((keyElement.fieldNameStringData() == "$**") && - ((keyElement.isNumber()) || (keyElement.valuestrsafe() == IndexNames::TEXT))) + ((keyElement.isNumber()) || (keyElement.str() == IndexNames::TEXT))) continue; - if ((keyElement.fieldNameStringData() == "_fts") && - keyElement.valuestrsafe() != IndexNames::TEXT) { + if ((keyElement.fieldNameStringData() == "_fts") && keyElement.str() != IndexNames::TEXT) { return Status(code, "Index key contains an illegal field name: '_fts'"); } diff --git a/src/mongo/db/commands/distinct.cpp b/src/mongo/db/commands/distinct.cpp index 96054086f0f..931441118d3 100644 --- a/src/mongo/db/commands/distinct.cpp +++ b/src/mongo/db/commands/distinct.cpp @@ -246,7 +246,7 @@ public: executor.getValue()->getPlanExplainer().getPlanSummary()); } - const auto key = cmdObj[ParsedDistinct::kKeyField].valuestrsafe(); + const auto key = cmdObj.getStringField(ParsedDistinct::kKeyField); std::vector<BSONObj> distinctValueHolder; BSONElementSet values(executor.getValue()->getCanonicalQuery()->getCollator()); diff --git a/src/mongo/db/commands/oplog_application_checks.cpp b/src/mongo/db/commands/oplog_application_checks.cpp index bd6198ea3a5..80e0e8720e3 100644 --- a/src/mongo/db/commands/oplog_application_checks.cpp +++ b/src/mongo/db/commands/oplog_application_checks.cpp @@ -177,8 +177,8 @@ Status OplogApplicationChecks::checkOperation(const BSONElement& e) { str::stream() << "\"op\" field is not a string: " << e.fieldName()}; } // operation type -- see logOp() comments for types - const char* opType = opElement.valuestrsafe(); - if (*opType == '\0') { + StringData opType = opElement.valueStringDataSafe(); + if (opType.empty()) { return {ErrorCodes::IllegalOperation, str::stream() << "\"op\" field value cannot be empty: " << e.fieldName()}; } @@ -198,7 +198,7 @@ Status OplogApplicationChecks::checkOperation(const BSONElement& e) { return {ErrorCodes::IllegalOperation, str::stream() << "namespaces cannot have embedded null characters"}; } - if (*opType != 'n' && nsElement.String().empty()) { + if (opType != "n"_sd && nsElement.String().empty()) { return {ErrorCodes::IllegalOperation, str::stream() << "\"ns\" field value cannot be empty when op type is not 'n': " << e.fieldName()}; diff --git a/src/mongo/db/commands/parameters.cpp b/src/mongo/db/commands/parameters.cpp index 5a7840e655f..aaff8b03898 100644 --- a/src/mongo/db/commands/parameters.cpp +++ b/src/mongo/db/commands/parameters.cpp @@ -222,7 +222,7 @@ public: const BSONObj& cmdObj, string& errmsg, BSONObjBuilder& result) { - bool all = *cmdObj.firstElement().valuestrsafe() == '*'; + bool all = cmdObj.firstElement().str() == "*"; int before = result.len(); diff --git a/src/mongo/db/exec/sbe/stages/makeobj.cpp b/src/mongo/db/exec/sbe/stages/makeobj.cpp index 54e1ce05e89..e1c9cd6320e 100644 --- a/src/mongo/db/exec/sbe/stages/makeobj.cpp +++ b/src/mongo/db/exec/sbe/stages/makeobj.cpp @@ -258,8 +258,7 @@ void MakeObjStageBase<MakeObjOutputType::bsonObject>::produceObject() { auto nextBe = bson::advance(be, sv.size()); if (!isFieldProjectedOrRestricted(key)) { - bob.append(BSONElement( - be, sv.size() + 1, nextBe - be, BSONElement::CachedSizeTag{})); + bob.append(BSONElement(be, sv.size() + 1, nextBe - be)); --nFieldsNeededIfInclusion; } diff --git a/src/mongo/db/field_parser.cpp b/src/mongo/db/field_parser.cpp index 94207bf7d22..0afde43f427 100644 --- a/src/mongo/db/field_parser.cpp +++ b/src/mongo/db/field_parser.cpp @@ -206,7 +206,7 @@ FieldParser::FieldState FieldParser::extract(BSONElement elem, if (elem.type() == String) { // Extract everything, including embedded null characters. - *out = string(elem.valuestr(), elem.valuestrsize() - 1); + *out = elem.str(); return FIELD_SET; } diff --git a/src/mongo/db/fts/fts_element_iterator.cpp b/src/mongo/db/fts/fts_element_iterator.cpp index 00c0dd39134..689964f58d6 100644 --- a/src/mongo/db/fts/fts_element_iterator.cpp +++ b/src/mongo/db/fts/fts_element_iterator.cpp @@ -149,7 +149,8 @@ FTSIteratorValue FTSElementIterator::advance() { case String: // Only index strings on exact match or wildcard. if (exactMatch || _spec.wildcard()) { - return FTSIteratorValue(elem.valuestr(), _frame._language, weight); + return FTSIteratorValue( + elem.valueStringData().rawData(), _frame._language, weight); } break; diff --git a/src/mongo/db/fts/fts_index_format_test.cpp b/src/mongo/db/fts/fts_index_format_test.cpp index 7d5dd4a3eae..9c40895de01 100644 --- a/src/mongo/db/fts/fts_index_format_test.cpp +++ b/src/mongo/db/fts/fts_index_format_test.cpp @@ -87,7 +87,7 @@ TEST(FTSIndexFormat, ExtraBack1) { auto key = KeyString::toBson(*keys.begin(), Ordering::make(BSONObj())); ASSERT_EQUALS(3, key.nFields()); BSONObjIterator i(key); - ASSERT_EQUALS(StringData("cat"), i.next().valuestr()); + ASSERT_EQUALS(StringData("cat"), i.next().valueStringDataSafe()); ASSERT(i.next().numberDouble() > 0); ASSERT_EQUALS(5, i.next().numberInt()); } @@ -111,7 +111,7 @@ TEST(FTSIndexFormat, ExtraFront1) { ASSERT_EQUALS(3, key.nFields()); BSONObjIterator i(key); ASSERT_EQUALS(5, i.next().numberInt()); - ASSERT_EQUALS(StringData("cat"), i.next().valuestr()); + ASSERT_EQUALS(StringData("cat"), i.next().valueStringDataSafe()); ASSERT(i.next().numberDouble() > 0); } diff --git a/src/mongo/db/fts/fts_spec.cpp b/src/mongo/db/fts/fts_spec.cpp index 04d36348511..64b333d5c25 100644 --- a/src/mongo/db/fts/fts_spec.cpp +++ b/src/mongo/db/fts/fts_spec.cpp @@ -109,7 +109,7 @@ FTSSpec::FTSSpec(const BSONObj& indexInfo) { " correct options."); } - _languageOverrideField = indexInfo["language_override"].valuestrsafe(); + _languageOverrideField = indexInfo.getStringField("language_override").toString(); _wildcard = false; @@ -290,7 +290,7 @@ StatusWith<BSONObj> FTSSpec::fixSpec(const BSONObj& spec) { while (i.more()) { BSONElement e = i.next(); if (e.fieldNameStringData() == "_fts") { - if (INDEX_NAME != e.valuestrsafe()) { + if (INDEX_NAME != e.str()) { return {ErrorCodes::CannotCreateIndex, "expecting _fts:\"text\""}; } addedFtsStuff = true; @@ -300,7 +300,7 @@ StatusWith<BSONObj> FTSSpec::fixSpec(const BSONObj& spec) { return {ErrorCodes::CannotCreateIndex, "expecting _ftsx:1"}; } b.append(e); - } else if (e.type() == String && INDEX_NAME == e.valuestr()) { + } else if (e.type() == String && INDEX_NAME == e.str()) { if (!addedFtsStuff) { _addFTSStuff(&b); addedFtsStuff = true; diff --git a/src/mongo/db/fts/fts_spec_legacy.cpp b/src/mongo/db/fts/fts_spec_legacy.cpp index 06ed2e17088..8d88f9a772a 100644 --- a/src/mongo/db/fts/fts_spec_legacy.cpp +++ b/src/mongo/db/fts/fts_spec_legacy.cpp @@ -53,8 +53,8 @@ void _addFTSStuff(BSONObjBuilder* b) { const FTSLanguage& FTSSpec::_getLanguageToUseV1(const BSONObj& userDoc) const { BSONElement e = userDoc[_languageOverrideField]; if (e.type() == String) { - const char* x = e.valuestrsafe(); - if (strlen(x) > 0) { + StringData x = e.valueStringData(); + if (e.size() > 0) { // make() w/ TEXT_INDEX_VERSION_1 guaranteed to not fail. return FTSLanguage::make(x, TEXT_INDEX_VERSION_1); } @@ -144,7 +144,7 @@ void FTSSpec::_scoreRecurseV1(const Tools& tools, if (x.type() == String) { double w = 1; _weightV1(x.fieldName(), &w); - _scoreStringV1(tools, x.valuestr(), term_freqs, w); + _scoreStringV1(tools, x.valueStringData(), term_freqs, w); } else if (x.isABSONObj()) { _scoreRecurseV1(tools, x.Obj(), term_freqs); } @@ -181,10 +181,10 @@ void FTSSpec::_scoreDocumentV1(const BSONObj& obj, TermFrequencyMap* term_freqs) if (leftOverName[0] && x.isABSONObj()) x = dps::extractElementAtPath(x.Obj(), leftOverName); if (x.type() == String) - _scoreStringV1(tools, x.valuestr(), term_freqs, weight); + _scoreStringV1(tools, x.valueStringData(), term_freqs, weight); } } else if (e.type() == String) { - _scoreStringV1(tools, e.valuestr(), term_freqs, weight); + _scoreStringV1(tools, e.valueStringData(), term_freqs, weight); } } } diff --git a/src/mongo/db/global_settings.cpp b/src/mongo/db/global_settings.cpp index 57485100d81..02508cbb5de 100644 --- a/src/mongo/db/global_settings.cpp +++ b/src/mongo/db/global_settings.cpp @@ -86,7 +86,7 @@ Status AllowListedClusterNetworkSetting::set(const mongo::BSONElement& e) { if (sub.type() != mongo::String) { return {ErrorCodes::BadValue, "Expected array of strings"}; } - allowlistedClusterNetwork->push_back(sub.valuestr()); + allowlistedClusterNetwork->push_back(sub.str()); } } else { return {ErrorCodes::BadValue, "Expected array or null"}; diff --git a/src/mongo/db/index/expression_keys_private.cpp b/src/mongo/db/index/expression_keys_private.cpp index 336a154151b..f2e3648c971 100644 --- a/src/mongo/db/index/expression_keys_private.cpp +++ b/src/mongo/db/index/expression_keys_private.cpp @@ -697,7 +697,7 @@ void ExpressionKeysPrivate::getS2Keys(SharedBufferFragmentBuilder& pooledBufferB bool lastPathComponentCausesIndexToBeMultikey; std::vector<KeyString::HeapBuilder> updatedKeysToAdd; - if (IndexNames::GEO_2DSPHERE_BUCKET == keyElem.valuestr()) { + if (IndexNames::GEO_2DSPHERE_BUCKET == keyElem.str()) { timeseries::dotted_path_support::extractAllElementsAlongBucketPath( obj, keyElem.fieldName(), @@ -738,7 +738,7 @@ void ExpressionKeysPrivate::getS2Keys(SharedBufferFragmentBuilder& pooledBufferB expandArrayOnTrailingField, arrayComponents); - if (IndexNames::GEO_2DSPHERE == keyElem.valuestr()) { + if (IndexNames::GEO_2DSPHERE == keyElem.str()) { if (params.indexVersion >= S2_INDEX_VERSION_2) { // For >= V2, // geo: null, diff --git a/src/mongo/db/index/expression_params.cpp b/src/mongo/db/index/expression_params.cpp index 5b4223facd6..ddd7a500056 100644 --- a/src/mongo/db/index/expression_params.cpp +++ b/src/mongo/db/index/expression_params.cpp @@ -47,7 +47,7 @@ void ExpressionParams::parseTwoDParams(const BSONObj& infoObj, TwoDIndexingParam while (i.more()) { BSONElement e = i.next(); - if (e.type() == String && IndexNames::GEO_2D == e.valuestr()) { + if (e.type() == String && IndexNames::GEO_2D == e.str()) { uassert(16800, "can't have 2 geo fields", out->geo.empty()); uassert(16801, "2d has to be first in index", out->other.empty()); out->geo = e.fieldName(); diff --git a/src/mongo/db/index/index_build_interceptor.cpp b/src/mongo/db/index/index_build_interceptor.cpp index 051d2bdb7f7..a2734b76bad 100644 --- a/src/mongo/db/index/index_build_interceptor.cpp +++ b/src/mongo/db/index/index_build_interceptor.cpp @@ -287,8 +287,7 @@ Status IndexBuildInterceptor::_applyWrite(OperationContext* opCtx, reader, _indexCatalogEntry->accessMethod()->getSortedDataInterface()->getKeyStringVersion()); - const Op opType = - (strcmp(operation.getStringField("op"), "i") == 0) ? Op::kInsert : Op::kDelete; + const Op opType = operation.getStringField("op") == "i"_sd ? Op::kInsert : Op::kDelete; const KeyStringSet keySet{keyString}; const RecordId opRecordId = [&]() { @@ -328,7 +327,7 @@ Status IndexBuildInterceptor::_applyWrite(OperationContext* opCtx, } else { invariant(opType == Op::kDelete); if (kDebugBuild) - invariant(strcmp(operation.getStringField("op"), "d") == 0); + invariant(operation.getStringField("op") == "d"_sd); int64_t numDeleted; Status s = accessMethod->removeKeys( diff --git a/src/mongo/db/index_builds_coordinator.cpp b/src/mongo/db/index_builds_coordinator.cpp index a8b60a314d6..4ed1c74f021 100644 --- a/src/mongo/db/index_builds_coordinator.cpp +++ b/src/mongo/db/index_builds_coordinator.cpp @@ -496,7 +496,7 @@ std::vector<std::string> IndexBuildsCoordinator::extractIndexNames( const std::vector<BSONObj>& specs) { std::vector<std::string> indexNames; for (const auto& spec : specs) { - std::string name = spec.getStringField(IndexDescriptor::kIndexNameFieldName); + std::string name = spec.getStringField(IndexDescriptor::kIndexNameFieldName).toString(); invariant(!name.empty(), str::stream() << "Bad spec passed into ReplIndexBuildState constructor, missing '" << IndexDescriptor::kIndexNameFieldName << "' field: " << spec); @@ -533,7 +533,7 @@ Status IndexBuildsCoordinator::_startIndexBuildForRecovery(OperationContext* opC std::vector<std::string> indexNames; for (auto& spec : specs) { - std::string name = spec.getStringField(IndexDescriptor::kIndexNameFieldName); + std::string name = spec.getStringField(IndexDescriptor::kIndexNameFieldName).toString(); if (name.empty()) { return Status(ErrorCodes::CannotCreateIndex, str::stream() @@ -661,7 +661,8 @@ Status IndexBuildsCoordinator::_setUpResumeIndexBuild(OperationContext* opCtx, auto durableCatalog = DurableCatalog::get(opCtx); for (auto spec : specs) { - std::string indexName = spec.getStringField(IndexDescriptor::kIndexNameFieldName); + std::string indexName = + spec.getStringField(IndexDescriptor::kIndexNameFieldName).toString(); if (indexName.empty()) { return Status(ErrorCodes::CannotCreateIndex, str::stream() @@ -859,7 +860,8 @@ void IndexBuildsCoordinator::applyStartIndexBuild(OperationContext* opCtx, const bool includeUnfinished = false; for (const auto& spec : oplogEntry.indexSpecs) { - std::string name = spec.getStringField(IndexDescriptor::kIndexNameFieldName); + std::string name = + spec.getStringField(IndexDescriptor::kIndexNameFieldName).toString(); uassert(ErrorCodes::BadValue, str::stream() << "Index spec is missing the 'name' field " << spec, !name.empty()); diff --git a/src/mongo/db/matcher/expression_leaf.cpp b/src/mongo/db/matcher/expression_leaf.cpp index 9ecd363b421..9c97567aeca 100644 --- a/src/mongo/db/matcher/expression_leaf.cpp +++ b/src/mongo/db/matcher/expression_leaf.cpp @@ -267,7 +267,8 @@ bool RegexMatchExpression::matchesSingleElement(const BSONElement& e, MatchDetai // String values stored in documents can contain embedded NUL bytes. We construct a // pcrecpp::StringPiece instance using the full length of the string to avoid truncating // 'data' early. - pcrecpp::StringPiece data(e.valuestr(), e.valuestrsize() - 1); + auto stringData = e.valueStringData(); + pcrecpp::StringPiece data{stringData.rawData(), static_cast<int>(stringData.size())}; return _re->PartialMatch(data); } case RegEx: diff --git a/src/mongo/db/operation_context.cpp b/src/mongo/db/operation_context.cpp index 41881288b9a..d39cb440260 100644 --- a/src/mongo/db/operation_context.cpp +++ b/src/mongo/db/operation_context.cpp @@ -203,7 +203,7 @@ namespace { // specified in the fail point info. bool opShouldFail(Client* client, const BSONObj& failPointInfo) { // Only target the client with the specified connection number. - if (client->desc() != failPointInfo["threadName"].valuestrsafe()) { + if (client->desc() != failPointInfo.getStringField("threadName")) { return false; } diff --git a/src/mongo/db/pipeline/expression.cpp b/src/mongo/db/pipeline/expression.cpp index a41f610db04..5e24100be98 100644 --- a/src/mongo/db/pipeline/expression.cpp +++ b/src/mongo/db/pipeline/expression.cpp @@ -246,7 +246,7 @@ intrusive_ptr<Expression> Expression::parseOperand(ExpressionContext* const expC const VariablesParseState& vps) { BSONType type = exprElement.type(); - if (type == String && exprElement.valuestr()[0] == '$') { + if (type == String && exprElement.valueStringData()[0] == '$') { /* if we got here, this is a field path expression */ return ExpressionFieldPath::parse(expCtx, exprElement.str(), vps); } else if (type == Object) { diff --git a/src/mongo/db/query/query_request_helper.cpp b/src/mongo/db/query/query_request_helper.cpp index 27b0cf5c969..1a602bfb0cd 100644 --- a/src/mongo/db/query/query_request_helper.cpp +++ b/src/mongo/db/query/query_request_helper.cpp @@ -204,7 +204,7 @@ bool isTextScoreMeta(BSONElement elt) { if (mongo::String != metaElt.type()) { return false; } - if (StringData{metaElt.valuestr()} != metaTextScore) { + if (metaElt.valueStringData() != metaTextScore) { return false; } // must have exactly 1 element diff --git a/src/mongo/db/repl/apply_ops.cpp b/src/mongo/db/repl/apply_ops.cpp index 65b533ba218..8cd9fb586b3 100644 --- a/src/mongo/db/repl/apply_ops.cpp +++ b/src/mongo/db/repl/apply_ops.cpp @@ -87,7 +87,7 @@ Status _applyOps(OperationContext* opCtx, // Apply each op in the given 'applyOps' command object. for (const auto& opObj : ops) { // Ignore 'n' operations. - const char* opType = opObj["op"].valuestrsafe(); + const char* opType = opObj.getStringField("op").rawData(); if (*opType == 'n') continue; diff --git a/src/mongo/db/repl/apply_ops_command_info.cpp b/src/mongo/db/repl/apply_ops_command_info.cpp index 57144240cdb..97b9e90976e 100644 --- a/src/mongo/db/repl/apply_ops_command_info.cpp +++ b/src/mongo/db/repl/apply_ops_command_info.cpp @@ -47,7 +47,7 @@ namespace { */ bool _parseAreOpsCrudOnly(const BSONObj& applyOpCmd) { for (const auto& elem : applyOpCmd.firstElement().Obj()) { - const char* opType = elem.Obj().getField("op").valuestrsafe(); + const char* opType = elem.Obj().getStringField("op").rawData(); // All atomic ops have an opType of length 1. if (opType[0] == '\0' || opType[1] != '\0') diff --git a/src/mongo/db/repl/collection_cloner.cpp b/src/mongo/db/repl/collection_cloner.cpp index f38cb4e64c6..530b0773edb 100644 --- a/src/mongo/db/repl/collection_cloner.cpp +++ b/src/mongo/db/repl/collection_cloner.cpp @@ -276,7 +276,8 @@ BaseCloner::AfterStageBehavior CollectionCloner::setupIndexBuildersForUnfinished std::vector<std::string> indexNames; std::vector<BSONObj> indexSpecs; for (const auto& indexSpec : groupedIndexSpec.second) { - std::string indexName = indexSpec.getStringField(IndexDescriptor::kIndexNameFieldName); + std::string indexName = + indexSpec.getStringField(IndexDescriptor::kIndexNameFieldName).toString(); indexNames.push_back(indexName); indexSpecs.push_back(indexSpec.getOwned()); } diff --git a/src/mongo/db/repl/repl_set_commands.cpp b/src/mongo/db/repl/repl_set_commands.cpp index 7299875254e..fcaedea011c 100644 --- a/src/mongo/db/repl/repl_set_commands.cpp +++ b/src/mongo/db/repl/repl_set_commands.cpp @@ -643,7 +643,7 @@ public: uassertStatusOK(status); HostAndPort targetHostAndPort; - status = targetHostAndPort.initialize(cmdObj["replSetSyncFrom"].valuestrsafe()); + status = targetHostAndPort.initialize(cmdObj.getStringField("replSetSyncFrom")); uassertStatusOK(status); uassertStatusOK(ReplicationCoordinator::get(opCtx)->processReplSetSyncFrom( diff --git a/src/mongo/db/repl/rollback_impl.cpp b/src/mongo/db/repl/rollback_impl.cpp index 87ce469669e..b4dad5b54ba 100644 --- a/src/mongo/db/repl/rollback_impl.cpp +++ b/src/mongo/db/repl/rollback_impl.cpp @@ -412,7 +412,7 @@ StatusWith<std::set<NamespaceString>> RollbackImpl::_namespacesForOp(const Oplog switch (oplogEntry.getCommandType()) { case OplogEntry::CommandType::kRenameCollection: { // Add both the 'from' and 'to' namespaces. - namespaces.insert(NamespaceString(firstElem.valuestrsafe())); + namespaces.insert(NamespaceString(firstElem.valueStringDataSafe())); namespaces.insert(NamespaceString(obj.getStringField("to"))); break; } diff --git a/src/mongo/db/repl/rs_rollback.cpp b/src/mongo/db/repl/rs_rollback.cpp index 2bdf56d833f..f76bb325ee5 100644 --- a/src/mongo/db/repl/rs_rollback.cpp +++ b/src/mongo/db/repl/rs_rollback.cpp @@ -367,7 +367,7 @@ Status rollback_internal::updateFixUpInfoFromLocalOplogEntry(OperationContext* o // } // ... // } - NamespaceString collectionNamespace(nss.getSisterNS(first.valuestr())); + NamespaceString collectionNamespace(nss.getSisterNS(first.valueStringDataSafe())); // Registers the collection to be removed from the drop pending collection // reaper and to be renamed from its drop pending namespace to original namespace. @@ -388,7 +388,7 @@ Status rollback_internal::updateFixUpInfoFromLocalOplogEntry(OperationContext* o // ns: "foo.x" // } - string ns = nss.db().toString() + '.' + first.valuestr(); + string ns = nss.db().toString() + '.' + first.str(); string indexName; auto status = bsonExtractStringField(obj, "index", &indexName); @@ -607,7 +607,7 @@ Status rollback_internal::updateFixUpInfoFromLocalOplogEntry(OperationContext* o BSONObj cmd = obj; - std::string ns = first.valuestrsafe(); + std::string ns = first.str(); if (ns.empty()) { static constexpr char message[] = "Collection name missing from oplog entry"; LOGV2(21667, message, "oplogEntry"_attr = redact(obj)); diff --git a/src/mongo/db/repl_index_build_state.cpp b/src/mongo/db/repl_index_build_state.cpp index 9048112a6dd..d636bd93093 100644 --- a/src/mongo/db/repl_index_build_state.cpp +++ b/src/mongo/db/repl_index_build_state.cpp @@ -47,7 +47,7 @@ namespace { std::vector<std::string> extractIndexNames(const std::vector<BSONObj>& specs) { std::vector<std::string> indexNames; for (const auto& spec : specs) { - std::string name = spec.getStringField(IndexDescriptor::kIndexNameFieldName); + std::string name = spec.getStringField(IndexDescriptor::kIndexNameFieldName).toString(); invariant(!name.empty(), str::stream() << "Bad spec passed into ReplIndexBuildState constructor, missing '" << IndexDescriptor::kIndexNameFieldName << "' field: " << spec); diff --git a/src/mongo/db/s/config/sharding_catalog_manager_shard_operations.cpp b/src/mongo/db/s/config/sharding_catalog_manager_shard_operations.cpp index ed00416ef5d..9c8f449399c 100644 --- a/src/mongo/db/s/config/sharding_catalog_manager_shard_operations.cpp +++ b/src/mongo/db/s/config/sharding_catalog_manager_shard_operations.cpp @@ -327,7 +327,7 @@ StatusWith<ShardType> ShardingCatalogManager::_validateHostAsShard( auto resIsMaster = std::move(swCommandResponse.getValue().response); // Fail if the node being added is a mongos. - const std::string msg = resIsMaster.getStringField("msg"); + const std::string msg = resIsMaster.getStringField("msg").toString(); if (msg == "isdbgrid") { return {ErrorCodes::IllegalOperation, "cannot add a mongos as a shard"}; } diff --git a/src/mongo/db/storage/bson_collection_catalog_entry.cpp b/src/mongo/db/storage/bson_collection_catalog_entry.cpp index fca3b271332..b5527947067 100644 --- a/src/mongo/db/storage/bson_collection_catalog_entry.cpp +++ b/src/mongo/db/storage/bson_collection_catalog_entry.cpp @@ -238,7 +238,7 @@ BSONObj BSONCollectionCatalogEntry::MetaData::toBSON(bool hasExclusiveAccess) co } void BSONCollectionCatalogEntry::MetaData::parse(const BSONObj& obj) { - ns = obj["ns"].valuestrsafe(); + ns = obj.getStringField("ns").toString(); if (obj["options"].isABSONObj()) { options = uassertStatusOK( diff --git a/src/mongo/db/storage/ephemeral_for_test/ephemeral_for_test_sorted_impl.cpp b/src/mongo/db/storage/ephemeral_for_test/ephemeral_for_test_sorted_impl.cpp index 0c89471b5bd..76c659b4f7a 100644 --- a/src/mongo/db/storage/ephemeral_for_test/ephemeral_for_test_sorted_impl.cpp +++ b/src/mongo/db/storage/ephemeral_for_test/ephemeral_for_test_sorted_impl.cpp @@ -450,7 +450,7 @@ BSONObj createObjFromRadixKey(const std::string& radixKey, auto it = BSONObjIterator(bsonObj); ++it; // We want the second part KeyString::Builder ks(version); - ks.resetFromBuffer((*it).valuestr(), (*it).valuestrsize()); + ks.resetFromBuffer((*it).valueStringDataSafe().rawData(), (*it).valueStringDataSafe().size()); return KeyString::toBsonSafe(ks.getBuffer(), ks.getSize(), order, typeBits); } diff --git a/src/mongo/db/storage/wiredtiger/wiredtiger_kv_engine.cpp b/src/mongo/db/storage/wiredtiger/wiredtiger_kv_engine.cpp index a4046d08846..150d27ef2cf 100644 --- a/src/mongo/db/storage/wiredtiger/wiredtiger_kv_engine.cpp +++ b/src/mongo/db/storage/wiredtiger/wiredtiger_kv_engine.cpp @@ -1567,7 +1567,7 @@ Status WiredTigerKVEngine::createSortedDataInterface(OperationContext* opCtx, if (auto storageEngineOptions = collOptions.indexOptionDefaults.getStorageEngine()) { collIndexOptions = dps::extractElementAtPath(*storageEngineOptions, _canonicalName + ".configString") - .valuestrsafe(); + .str(); } // Some unittests use a OperationContextNoop that can't support such lookups. auto ns = collOptions.uuid diff --git a/src/mongo/db/timeseries/bucket_catalog.cpp b/src/mongo/db/timeseries/bucket_catalog.cpp index 3c0c3b37ec7..9b06cdbdc33 100644 --- a/src/mongo/db/timeseries/bucket_catalog.cpp +++ b/src/mongo/db/timeseries/bucket_catalog.cpp @@ -88,8 +88,7 @@ void normalizeObject(BSONObjBuilder* builder, const BSONObj& obj) { BSONElement element() const { return BSONElement(fieldName.rawData() - 1, // Include type byte before field name fieldName.size() + 1, // Include null terminator after field name - totalSize, - BSONElement::CachedSizeTag{}); + totalSize); } bool operator<(const Field& rhs) const { return fieldName < rhs.fieldName; diff --git a/src/mongo/db/timeseries/flat_bson.cpp b/src/mongo/db/timeseries/flat_bson.cpp index 595dde0942f..e5b15133b1e 100644 --- a/src/mongo/db/timeseries/flat_bson.cpp +++ b/src/mongo/db/timeseries/flat_bson.cpp @@ -673,7 +673,7 @@ void FlatBSON<Derived, Element, Value>::_setTypeArray( } BSONElement BSONElementValue::get() const { - return BSONElement(_buffer.get(), 1, _size, BSONElement::CachedSizeTag{}); + return BSONElement(_buffer.get(), 1, _size); } void BSONElementValue::set(const BSONElement& elem) { diff --git a/src/mongo/db/write_concern_options.cpp b/src/mongo/db/write_concern_options.cpp index baf56a1a669..53883d0cba0 100644 --- a/src/mongo/db/write_concern_options.cpp +++ b/src/mongo/db/write_concern_options.cpp @@ -179,7 +179,7 @@ StatusWith<WriteConcernOptions> WriteConcernOptions::parse(const BSONObj& obj) { writeConcern.notExplicitWValue = false; } else if (wEl.type() == String) { writeConcern.wNumNodes = 0; - writeConcern.wMode = wEl.valuestrsafe(); + writeConcern.wMode = wEl.str(); writeConcern.notExplicitWValue = false; } else if (wEl.eoo() || wEl.type() == jstNULL || wEl.type() == Undefined) { writeConcern.wNumNodes = 1; diff --git a/src/mongo/dbtests/commandtests.cpp b/src/mongo/dbtests/commandtests.cpp index 2e354795911..5f0518c2776 100644 --- a/src/mongo/dbtests/commandtests.cpp +++ b/src/mongo/dbtests/commandtests.cpp @@ -135,7 +135,7 @@ struct Type0 : Base { BSONObj result; ASSERT(db.runCommand("test", BSON("filemd5" << 0), result)); - ASSERT_EQUALS(string("5eb63bbbe01eeed093cb22bb8f5acdc3"), result["md5"].valuestr()); + ASSERT_EQUALS(string("5eb63bbbe01eeed093cb22bb8f5acdc3"), result.getStringField("md5")); } }; struct Type2 : Base { @@ -159,7 +159,7 @@ struct Type2 : Base { BSONObj result; ASSERT(db.runCommand("test", BSON("filemd5" << 0), result)); - ASSERT_EQUALS(string("5eb63bbbe01eeed093cb22bb8f5acdc3"), result["md5"].valuestr()); + ASSERT_EQUALS(string("5eb63bbbe01eeed093cb22bb8f5acdc3"), result.getStringField("md5")); } }; } // namespace FileMD5 diff --git a/src/mongo/dbtests/jsobjtests.cpp b/src/mongo/dbtests/jsobjtests.cpp index 99f984e0c87..0c20c6f0f9b 100644 --- a/src/mongo/dbtests/jsobjtests.cpp +++ b/src/mongo/dbtests/jsobjtests.cpp @@ -843,9 +843,10 @@ class WrongStringSize : public Base { } BSONObj invalid() const { BSONObj ret = valid(); - ASSERT_EQUALS(ret.firstElement().valuestr()[0], 'b'); - ASSERT_EQUALS(ret.firstElement().valuestr()[1], 0); - ((char*)ret.firstElement().valuestr())[1] = 1; + ASSERT_TRUE(ret.firstElement().valueStringData().size() >= 1); + ASSERT_EQUALS(ret.firstElement().valueStringData()[0], 'b'); + ASSERT_EQUALS(ret.firstElement().valueStringData()[1], 0); + ((char*)ret.firstElement().valueStringData().rawData())[1] = 1; return ret.copy(); } }; diff --git a/src/mongo/dbtests/jsontests.cpp b/src/mongo/dbtests/jsontests.cpp index 334bb3730d9..cb16219f51d 100644 --- a/src/mongo/dbtests/jsontests.cpp +++ b/src/mongo/dbtests/jsontests.cpp @@ -728,7 +728,7 @@ TEST(FromJsonTest, Utf8Test) { using namespace std::literals::string_literals; const std::string u = "\xea\x80\x80\xea\x80\x80"s; BSONObj built = B().append("a", u).obj(); - ASSERT_EQUALS(built.firstElement().valuestr(), u); + ASSERT_EQUALS(built.firstElement().str(), u); checkEquivalenceEach({ // EscapedUnicodeToUtf8 diff --git a/src/mongo/dbtests/jstests.cpp b/src/mongo/dbtests/jstests.cpp index 7a6522c5c81..38a33c35472 100644 --- a/src/mongo/dbtests/jstests.cpp +++ b/src/mongo/dbtests/jstests.cpp @@ -234,7 +234,7 @@ public: s->invoke("z = { x : 'eliot' };", nullptr, nullptr); out = s->getObject("z"); - ASSERT_EQUALS((string) "eliot", out["x"].valuestr()); + ASSERT_EQUALS("eliot", out["x"].str()); ASSERT_EQUALS(1, out.nFields()); BSONObj o = BSON("x" << 17); @@ -598,7 +598,7 @@ public: out = s->getObject("c"); stringstream ss; ss << "NumberLong(\"" << val << "\")"; - ASSERT_EQUALS(ss.str(), out.firstElement().valuestr()); + ASSERT_EQUALS(ss.str(), out.firstElement().str()); ASSERT(s->exec("d = {d:a.a.toNumber()}", "foo", false, true, false)); out = s->getObject("d"); @@ -688,7 +688,7 @@ public: out = s->getObject("c"); stringstream ss; ss << "NumberLong(\"" << val << "\")"; - ASSERT_EQUALS(ss.str(), out.firstElement().valuestr()); + ASSERT_EQUALS(ss.str(), out.firstElement().str()); ASSERT(s->exec("d = {d:a.a.toNumber()}", "foo", false, true, false)); out = s->getObject("d"); @@ -732,7 +732,7 @@ public: out = s->getObject("c"); stringstream ss; ss << "NumberDecimal(\"" << val.toString() << "\")"; - ASSERT_EQUALS(ss.str(), out.firstElement().valuestr()); + ASSERT_EQUALS(ss.str(), out.firstElement().str()); } }; diff --git a/src/mongo/embedded/mongo_embedded/mongo_embedded_test.cpp b/src/mongo/embedded/mongo_embedded/mongo_embedded_test.cpp index 3785a11754e..7d728bf451a 100644 --- a/src/mongo/embedded/mongo_embedded/mongo_embedded_test.cpp +++ b/src/mongo/embedded/mongo_embedded/mongo_embedded_test.cpp @@ -368,7 +368,7 @@ TEST_F(MongodbCAPITest, KillOp) { // See if we find the sleep command among the running commands for (const auto& elt : inprog) { auto inprogObj = inprog.getObjectField(elt.fieldNameStringData()); - std::string ns = inprogObj.getStringField("ns"); + std::string ns = inprogObj.getStringField("ns").toString(); if (ns == "admin.$cmd") { opid = inprogObj.getIntField("opid"); break; diff --git a/src/mongo/idl/basic_types.h b/src/mongo/idl/basic_types.h index e674b8ae1d4..d77ae561376 100644 --- a/src/mongo/idl/basic_types.h +++ b/src/mongo/idl/basic_types.h @@ -190,7 +190,7 @@ public: if (wEl.isNumber()) { return WriteConcernW{wEl.safeNumberLong()}; } else if (wEl.type() == BSONType::String) { - return WriteConcernW{wEl.valuestrsafe()}; + return WriteConcernW{wEl.str()}; } else if (wEl.eoo() || wEl.type() == BSONType::jstNULL || wEl.type() == BSONType::Undefined) { return WriteConcernW{}; diff --git a/src/mongo/rpc/op_msg_integration_test.cpp b/src/mongo/rpc/op_msg_integration_test.cpp index 3f235f58388..ee6cc857d6a 100644 --- a/src/mongo/rpc/op_msg_integration_test.cpp +++ b/src/mongo/rpc/op_msg_integration_test.cpp @@ -82,7 +82,7 @@ std::string getThreadNameByAppName(DBClientBase* conn, StringData appName) { const auto cursorResponse = CursorResponse::parseFromBSON(curOpReply->getCommandReply()); ASSERT_OK(cursorResponse.getStatus()); const auto batch = cursorResponse.getValue().getBatch(); - return batch.empty() ? "" : batch[0].getStringField("desc"); + return batch.empty() ? "" : batch[0].getStringField("desc").toString(); } TEST(OpMsg, UnknownRequiredFlagClosesConnection) { diff --git a/src/mongo/s/request_types/add_shard_request_type.cpp b/src/mongo/s/request_types/add_shard_request_type.cpp index 3860552a63a..fbb77b17f66 100644 --- a/src/mongo/s/request_types/add_shard_request_type.cpp +++ b/src/mongo/s/request_types/add_shard_request_type.cpp @@ -82,7 +82,7 @@ StatusWith<AddShardRequest> AddShardRequest::parseInternalFields(const BSONObj& << " must be a string"}; } - auto swConnString = ConnectionString::parse(firstElement.valuestrsafe()); + auto swConnString = ConnectionString::parse(firstElement.str()); if (!swConnString.isOK()) { return swConnString.getStatus(); } diff --git a/src/mongo/s/transaction_router_test.cpp b/src/mongo/s/transaction_router_test.cpp index e92b32860e1..7c4c0c20cda 100644 --- a/src/mongo/s/transaction_router_test.cpp +++ b/src/mongo/s/transaction_router_test.cpp @@ -1523,7 +1523,7 @@ TEST_F(TransactionRouterTestWithDefaultSession, ASSERT_EQ(expectedParticipants.size(), participantElements.size()); for (const auto& element : participantElements) { - auto shardId = element["shardId"].valuestr(); + auto shardId = element["shardId"].str(); ASSERT_EQ(1ull, expectedParticipants.count(shardId)); expectedParticipants.erase(shardId); } @@ -1575,7 +1575,7 @@ TEST_F(TransactionRouterTestWithDefaultSession, ASSERT_EQ(expectedParticipants.size(), participantElements.size()); for (const auto& element : participantElements) { - auto shardId = element["shardId"].valuestr(); + auto shardId = element["shardId"].str(); ASSERT_EQ(1ull, expectedParticipants.count(shardId)); expectedParticipants.erase(shardId); } @@ -1741,7 +1741,7 @@ TEST_F(TransactionRouterTestWithDefaultSession, ASSERT_EQ(expectedParticipants.size(), participantElements.size()); for (const auto& element : participantElements) { - auto shardId = element["shardId"].valuestr(); + auto shardId = element["shardId"].str(); ASSERT_EQ(1ull, expectedParticipants.count(shardId)); expectedParticipants.erase(shardId); } @@ -1803,7 +1803,7 @@ TEST_F(TransactionRouterTestWithDefaultSession, ASSERT_EQ(expectedParticipants.size(), participantElements.size()); for (const auto& element : participantElements) { - auto shardId = element["shardId"].valuestr(); + auto shardId = element["shardId"].str(); ASSERT_EQ(1ull, expectedParticipants.count(shardId)); expectedParticipants.erase(shardId); } diff --git a/src/mongo/scripting/engine.cpp b/src/mongo/scripting/engine.cpp index c7808f1a9f5..e78a71a48d4 100644 --- a/src/mongo/scripting/engine.cpp +++ b/src/mongo/scripting/engine.cpp @@ -256,9 +256,9 @@ void Scope::loadStored(OperationContext* opCtx, bool ignoreNotConnected) { } try { - setElement(n.valuestr(), v, o); - thisTime.insert(n.valuestr()); - _storedNames.insert(n.valuestr()); + setElement(n.valueStringDataSafe().rawData(), v, o); + thisTime.insert(n.str()); + _storedNames.insert(n.str()); } catch (const DBException& setElemEx) { if (setElemEx.code() == ErrorCodes::Interrupted) { throw; @@ -266,7 +266,7 @@ void Scope::loadStored(OperationContext* opCtx, bool ignoreNotConnected) { LOGV2_ERROR(22781, "unable to load stored JavaScript function {n_valuestr}(): {setElemEx}", - "n_valuestr"_attr = n.valuestr(), + "n_valuestr"_attr = n.valueStringDataSafe(), "setElemEx"_attr = redact(setElemEx)); } } diff --git a/src/mongo/scripting/utils.cpp b/src/mongo/scripting/utils.cpp index 51b084c44f0..08e08f3f6d7 100644 --- a/src/mongo/scripting/utils.cpp +++ b/src/mongo/scripting/utils.cpp @@ -40,12 +40,12 @@ static BSONObj native_hex_md5(const BSONObj& args, void* data) { uassert(10261, "hex_md5 takes a single string argument -- hex_md5(string)", args.nFields() == 1 && args.firstElement().type() == String); - const char* s = args.firstElement().valuestrsafe(); + StringData sd = args.firstElement().valueStringDataSafe(); md5digest d; md5_state_t st; md5_init(&st); - md5_append(&st, (const md5_byte_t*)s, strlen(s)); + md5_append(&st, reinterpret_cast<const md5_byte_t*>(sd.rawData()), sd.size()); md5_finish(&st, d); return BSON("" << digestToString(d)); diff --git a/src/mongo/shell/shell_utils.cpp b/src/mongo/shell/shell_utils.cpp index 79f89b28c57..8789168f3c8 100644 --- a/src/mongo/shell/shell_utils.cpp +++ b/src/mongo/shell/shell_utils.cpp @@ -446,8 +446,8 @@ BSONObj replMonitorStats(const BSONObj& a, void* data) { "replMonitorStats requires a single string argument (the ReplSet name)", a.nFields() == 1 && a.firstElement().type() == String); - auto name = a.firstElement().valuestrsafe(); - auto rsm = ReplicaSetMonitor::get(name); + auto name = a.firstElement().valueStringDataSafe(); + auto rsm = ReplicaSetMonitor::get(name.toString()); if (!rsm) { return BSON("" << "no ReplSetMonitor exists by that name"); @@ -482,7 +482,7 @@ BSONObj fileExistsJS(const BSONObj& a, void*) { uassert(40678, "fileExists expects one string argument", a.nFields() == 1 && a.firstElement().type() == String); - return BSON("" << fileExists(a.firstElement().valuestrsafe())); + return BSON("" << fileExists(a.firstElement().str())); } BSONObj isInteractive(const BSONObj& a, void*) { diff --git a/src/mongo/shell/shell_utils_extended.cpp b/src/mongo/shell/shell_utils_extended.cpp index fbdddc1318d..6cd6dc6ed0d 100644 --- a/src/mongo/shell/shell_utils_extended.cpp +++ b/src/mongo/shell/shell_utils_extended.cpp @@ -77,7 +77,7 @@ BSONObj listFiles(const BSONObj& _args, void* data) { BSONArrayBuilder lst; - string rootname = args.firstElement().valuestrsafe(); + string rootname = args.firstElement().str(); boost::filesystem::path root(rootname); stringstream ss; ss << "listFiles: no such directory: " << rootname; @@ -178,16 +178,16 @@ BSONObj cat(const BSONObj& args, void* data) { mode |= std::ios::binary; } - ifstream f(filePath.valuestrsafe(), mode); - uassert(CANT_OPEN_FILE, "couldn't open file {}"_format(filePath.valuestrsafe()), f.is_open()); + ifstream f(filePath.valueStringDataSafe().rawData(), mode); + uassert(CANT_OPEN_FILE, "couldn't open file {}"_format(filePath.str()), f.is_open()); std::streamsize fileSize = 0; // will throw on filesystem error - fileSize = boost::filesystem::file_size(filePath.valuestrsafe()); + fileSize = boost::filesystem::file_size(filePath.str()); static constexpr auto kFileSizeLimit = 1024 * 1024 * 16; uassert( 13301, "cat() : file {} too big to load as a variable (file is {} bytes, limit is {} bytes.)"_format( - filePath.valuestrsafe(), fileSize, kFileSizeLimit), + filePath.str(), fileSize, kFileSizeLimit), fileSize < kFileSizeLimit); std::ostringstream ss; @@ -252,8 +252,8 @@ BSONObj copyFileRange(const BSONObj& args, void* data) { BSONObj md5sumFile(const BSONObj& args, void* data) { BSONElement e = singleArg(args); stringstream ss; - FILE* f = fopen(e.valuestrsafe(), "rb"); - uassert(CANT_OPEN_FILE, str::stream() << "couldn't open file " << e.valuestrsafe(), f); + FILE* f = fopen(e.valueStringDataSafe().rawData(), "rb"); + uassert(CANT_OPEN_FILE, str::stream() << "couldn't open file " << e.str(), f); ON_BLOCK_EXIT([&] { fclose(f); }); md5digest d; @@ -315,7 +315,7 @@ BSONObj removeFile(const BSONObj& args, void* data) { BSONElement e = singleArg(args); bool found = false; - boost::filesystem::path root(e.valuestrsafe()); + boost::filesystem::path root(e.str()); if (boost::filesystem::exists(root)) { found = true; boost::filesystem::remove_all(root); diff --git a/src/mongo/shell/shell_utils_launcher.cpp b/src/mongo/shell/shell_utils_launcher.cpp index 65532f18632..d45b2eda43d 100644 --- a/src/mongo/shell/shell_utils_launcher.cpp +++ b/src/mongo/shell/shell_utils_launcher.cpp @@ -401,7 +401,7 @@ ProgramRunner::ProgramRunner(const BSONObj& args, const BSONObj& env, bool isMon "cannot pass an empty argument to ProgramRunner", !args.isEmpty()); - string program(args.firstElement().valuestrsafe()); + string program(args.firstElement().str()); uassert(ErrorCodes::FailedToParse, "invalid program name passed to ProgramRunner", !program.empty()); @@ -455,7 +455,7 @@ ProgramRunner::ProgramRunner(const BSONObj& args, const BSONObj& env, bool isMon uassert(ErrorCodes::FailedToParse, "Program arguments must be strings", e.type() == mongo::String); - str = e.valuestr(); + str = e.str(); } if (isMongo) { if (str == "--port") { @@ -476,7 +476,7 @@ ProgramRunner::ProgramRunner(const BSONObj& args, const BSONObj& env, bool isMon "Environment variable values must be strings", e.type() == mongo::String); - _envp.emplace(std::string(e.fieldName()), std::string(e.valuestr())); + _envp.emplace(std::string(e.fieldName()), e.str()); } // Import this process' environment into _envp, for all keys that have not already been set. @@ -935,7 +935,7 @@ BSONObj RunNonMongoProgram(const BSONObj& a, void* data) { BSONObj ResetDbpath(const BSONObj& a, void* data) { uassert(ErrorCodes::FailedToParse, "Expected 1 field", a.nFields() == 1); - string path = a.firstElement().valuestrsafe(); + string path = a.firstElement().str(); if (path.empty()) { LOGV2_WARNING(22824, "ResetDbpath(): nothing to do, path was empty"); return undefinedReturn; @@ -980,7 +980,7 @@ BSONObj ResetDbpath(const BSONObj& a, void* data) { BSONObj PathExists(const BSONObj& a, void* data) { uassert(ErrorCodes::FailedToParse, "Expected 1 field", a.nFields() == 1); - string path = a.firstElement().valuestrsafe(); + string path = a.firstElement().str(); if (path.empty()) { LOGV2_WARNING(22825, "PathExists(): path was empty"); return BSON(string("") << false); diff --git a/src/mongo/util/fail_point.cpp b/src/mongo/util/fail_point.cpp index cfc6f60d0d4..0d6875d4c9a 100644 --- a/src/mongo/util/fail_point.cpp +++ b/src/mongo/util/fail_point.cpp @@ -167,7 +167,7 @@ StatusWith<FailPoint::ModeOptions> FailPoint::parseBSON(const BSONObj& obj) { if (modeElem.eoo()) { return {ErrorCodes::IllegalOperation, "When setting a failpoint, you must supply a 'mode'"}; } else if (modeElem.type() == String) { - const std::string modeStr(modeElem.valuestr()); + const std::string modeStr(modeElem.str()); if (modeStr == "off") { mode = FailPoint::off; } else if (modeStr == "alwaysOn") { |