diff options
author | Matt Broadstone <mbroadst@mongodb.com> | 2022-03-16 22:35:38 +0000 |
---|---|---|
committer | Evergreen Agent <no-reply@evergreen.mongodb.com> | 2022-03-17 18:02:03 +0000 |
commit | cc774a291b08216b01eb14ad57fd40b8899b6532 (patch) | |
tree | b9eb68ac3b25eea23a16a45a139c82e5240cb361 /src/mongo/db | |
parent | 1fe77b5bd9fb13f9eb74275359dcc4ba69f2d5e9 (diff) | |
download | mongo-cc774a291b08216b01eb14ad57fd40b8899b6532.tar.gz |
SERVER-64577: Disallow wTags write concern in FCV less than 5.3
also fixes SERVER-63100
Diffstat (limited to 'src/mongo/db')
-rw-r--r-- | src/mongo/db/SConscript | 9 | ||||
-rw-r--r-- | src/mongo/db/read_write_concern_provenance_base.idl | 11 | ||||
-rw-r--r-- | src/mongo/db/write_concern_options.cpp | 66 | ||||
-rw-r--r-- | src/mongo/db/write_concern_options.h | 9 | ||||
-rw-r--r-- | src/mongo/db/write_concern_options.idl | 62 | ||||
-rw-r--r-- | src/mongo/db/write_concern_options_test.cpp | 12 |
6 files changed, 163 insertions, 6 deletions
diff --git a/src/mongo/db/SConscript b/src/mongo/db/SConscript index c3152e8c8bf..19b521180e7 100644 --- a/src/mongo/db/SConscript +++ b/src/mongo/db/SConscript @@ -452,15 +452,18 @@ env.Library( ) env.Library( - target="write_concern_options", + target='write_concern_options', source=[ - "write_concern_options.cpp", + 'write_concern_options.cpp', + 'write_concern_options.idl', ], LIBDEPS=[ '$BUILD_DIR/mongo/bson/util/bson_extract', - '$BUILD_DIR/mongo/idl/basic_types', 'read_write_concern_provenance', ], + LIBDEPS_PRIVATE=[ + '$BUILD_DIR/mongo/db/server_options_core', # For serverGlobalParams + ] ) env.Library( diff --git a/src/mongo/db/read_write_concern_provenance_base.idl b/src/mongo/db/read_write_concern_provenance_base.idl index 4695d5f0a34..017b89a6984 100644 --- a/src/mongo/db/read_write_concern_provenance_base.idl +++ b/src/mongo/db/read_write_concern_provenance_base.idl @@ -32,6 +32,17 @@ global: imports: - "mongo/idl/basic_types.idl" +enums: + ReadWriteConcernProvenanceSource: + description: "Provenance sources" + type: string + values: + clientSupplied: "clientSupplied" + implicitDefault: "implicitDefault" + customDefault: "customDefault" + getLastErrorDefaults: "getLastErrorDefaults" + internalWriteDefault: "internalWriteDefault" + structs: ReadWriteConcernProvenanceBase: description: "Represents the 'provenance' (ie. original source) of a read or write concern." diff --git a/src/mongo/db/write_concern_options.cpp b/src/mongo/db/write_concern_options.cpp index 4f1822884fa..c87e21691fc 100644 --- a/src/mongo/db/write_concern_options.cpp +++ b/src/mongo/db/write_concern_options.cpp @@ -32,13 +32,13 @@ #include <type_traits> #include "mongo/db/write_concern_options.h" +#include "mongo/db/write_concern_options_gen.h" #include "mongo/base/status.h" #include "mongo/base/string_data.h" #include "mongo/bson/util/bson_extract.h" #include "mongo/db/field_parser.h" #include "mongo/db/repl/repl_set_config.h" -#include "mongo/idl/basic_types_gen.h" #include "mongo/util/str.h" namespace mongo { @@ -176,6 +176,70 @@ StatusWith<WriteConcernOptions> WriteConcernOptions::extractWCFromCommand(const return parse(writeConcernObj); } +WriteConcernW deserializeWriteConcernW(BSONElement wEl) { + // Preserve pre-5.3 behavior to avoid issues with mixed-version clusters. This will eventually + // be removed when we release 7.0. + if (serverGlobalParams.featureCompatibility.isVersionInitialized() && + serverGlobalParams.featureCompatibility.isLessThan( + multiversion::FeatureCompatibilityVersion::kVersion_5_3)) { + uassert(ErrorCodes::FailedToParse, + "w has to be a number or string; found: {}"_format(typeName(wEl.type())), + wEl.type() != BSONType::Object); + } + + if (wEl.isNumber()) { + auto wNum = wEl.safeNumberLong(); + if (wNum < 0 || wNum > static_cast<long long>(repl::ReplSetConfig::kMaxMembers)) { + uasserted(ErrorCodes::FailedToParse, + "w has to be a non-negative number and not greater than {}; found: {}"_format( + repl::ReplSetConfig::kMaxMembers, wNum)); + } + + return WriteConcernW{wNum}; + } else if (wEl.type() == BSONType::String) { + return WriteConcernW{wEl.str()}; + } else if (wEl.type() == BSONType::Object) { + auto wTags = wEl.Obj(); + uassert(ErrorCodes::FailedToParse, "tagged write concern requires tags", !wTags.isEmpty()); + + WTags tags; + for (auto e : wTags) { + uassert( + ErrorCodes::FailedToParse, + "tags must be a single level document with only number values; found: {}"_format( + e.toString()), + e.isNumber()); + + tags.try_emplace(e.fieldName(), e.safeNumberInt()); + } + + return WriteConcernW{std::move(tags)}; + } else if (wEl.eoo() || wEl.type() == BSONType::jstNULL || wEl.type() == BSONType::Undefined) { + return WriteConcernW{}; + } + uasserted(ErrorCodes::FailedToParse, + "w has to be a number, string, or object; found: {}"_format(typeName(wEl.type()))); +} + +void serializeWriteConcernW(const WriteConcernW& w, StringData fieldName, BSONObjBuilder* builder) { + stdx::visit( + visit_helper::Overloaded{[&](int64_t wNumNodes) { + builder->appendNumber(fieldName, + static_cast<long long>(wNumNodes)); + }, + [&](std::string wMode) { builder->append(fieldName, wMode); }, + [&](WTags wTags) { builder->append(fieldName, wTags); }}, + w); +} + +std::int64_t parseWTimeoutFromBSON(BSONElement element) { + constexpr std::array<mongo::BSONType, 4> validTypes{ + NumberLong, NumberInt, NumberDecimal, NumberDouble}; + bool isValidType = std::any_of( + validTypes.begin(), validTypes.end(), [&](auto type) { return element.type() == type; }); + return isValidType ? element.safeNumberLong() : 0; +} + BSONObj WriteConcernOptions::toBSON() const { BSONObjBuilder builder; serializeWriteConcernW(w, "w", &builder); diff --git a/src/mongo/db/write_concern_options.h b/src/mongo/db/write_concern_options.h index 4478aa173fe..e1525ec29f3 100644 --- a/src/mongo/db/write_concern_options.h +++ b/src/mongo/db/write_concern_options.h @@ -33,12 +33,14 @@ #include "mongo/db/jsobj.h" #include "mongo/db/read_write_concern_provenance.h" -#include "mongo/idl/basic_types_gen.h" namespace mongo { class Status; +using WTags = StringMap<int64_t>; +using WriteConcernW = stdx::variant<std::string, std::int64_t, WTags>; + struct WriteConcernOptions { public: enum class SyncMode { UNSET, NONE, FSYNC, JOURNAL }; @@ -175,4 +177,9 @@ private: ReadWriteConcernProvenance _provenance; }; +// Helpers for IDL parsing +WriteConcernW deserializeWriteConcernW(BSONElement wEl); +void serializeWriteConcernW(const WriteConcernW& w, StringData fieldName, BSONObjBuilder* builder); +std::int64_t parseWTimeoutFromBSON(BSONElement element); + } // namespace mongo diff --git a/src/mongo/db/write_concern_options.idl b/src/mongo/db/write_concern_options.idl index 07c3cdbffc4..8f9c595f24e 100644 --- a/src/mongo/db/write_concern_options.idl +++ b/src/mongo/db/write_concern_options.idl @@ -33,7 +33,7 @@ global: imports: - "mongo/idl/basic_types.idl" - + - "mongo/db/read_write_concern_provenance_base.idl" types: WriteConcern: @@ -42,3 +42,63 @@ types: cpp_type: "mongo::WriteConcernOptions" serializer: "mongo::WriteConcernOptions::toBSON" deserializer: "mongo::WriteConcernOptions::deserializerForIDL" + + writeConcernW: + bson_serialization_type: any + description: >- + A string or integer representing the 'w' option in a document specifying write concern. + See https://docs.mongodb.com/manual/reference/write-concern/" + cpp_type: "mongo::WriteConcernW" + serializer: "::mongo::serializeWriteConcernW" + deserializer: "::mongo::deserializeWriteConcernW" + + writeConcernWTimeout: + bson_serialization_type: any + description: >- + An integer representing the 'wtimeout' option in a document specifying write concern. + See https://docs.mongodb.com/manual/reference/write-concern/" + cpp_type: std::int64_t + deserializer: "::mongo::parseWTimeoutFromBSON" + +structs: + WriteConcernIdl: + description: "WriteConcern object parser" + strict: true + fields: + w: + type: writeConcernW + cpp_name: writeConcernW + optional: true + unstable: false + j: + type: safeBool + optional: true + unstable: false + wtimeout: + type: writeConcernWTimeout + default: 0 + unstable: false + fsync: + type: safeBool + optional: true + unstable: false + # Fields with names wElectionId, wOpTime, and getLastError are accepted in the WriteConcern document for + # backwards-compatibility reasons, but their values are entirely ignored. + wElectionId: + type: any + ignore: true + unstable: false + wOpTime: + type: any + ignore: true + unstable: false + getLastError: + type: any + ignore: true + unstable: false + provenance: + description: "The source for this provenance" + cpp_name: source + type: ReadWriteConcernProvenanceSource + optional: true + unstable: false diff --git a/src/mongo/db/write_concern_options_test.cpp b/src/mongo/db/write_concern_options_test.cpp index e9a3d111a08..abce6fa331e 100644 --- a/src/mongo/db/write_concern_options_test.cpp +++ b/src/mongo/db/write_concern_options_test.cpp @@ -187,6 +187,10 @@ TEST(WriteConcernOptionsTest, ParseWithTags) { ASSERT_STRING_CONTAINS(status.reason(), "tags must be a single level document with only number values"); + status = WriteConcernOptions::parse(BSON("w" << BSONObj())).getStatus(); + ASSERT_EQUALS(ErrorCodes::FailedToParse, status); + ASSERT_STRING_CONTAINS(status.reason(), "tagged write concern requires tags"); + auto sw = WriteConcernOptions::parse(BSON("w" << BSON("abc" << 1))); ASSERT_OK(sw.getStatus()); @@ -209,5 +213,13 @@ TEST(WriteConcernOptionsTest, ParseWithTags) { ASSERT(wc != wc5); } +TEST(WriteConcernOptionsTest, WTagsNotPermittedFCVLessThan53) { + serverGlobalParams.mutableFeatureCompatibility.setVersion( + multiversion::FeatureCompatibilityVersion::kVersion_5_2); + auto status = WriteConcernOptions::parse(BSON("w" << BSON("abc" << 1))).getStatus(); + ASSERT_EQUALS(ErrorCodes::FailedToParse, status); + ASSERT_STRING_CONTAINS(status.reason(), "w has to be a number or string"); +} + } // namespace } // namespace mongo |