summaryrefslogtreecommitdiff
path: root/src/mongo/db
diff options
context:
space:
mode:
authorMatt Broadstone <mbroadst@mongodb.com>2022-03-16 22:35:38 +0000
committerEvergreen Agent <no-reply@evergreen.mongodb.com>2022-03-17 18:02:03 +0000
commitcc774a291b08216b01eb14ad57fd40b8899b6532 (patch)
treeb9eb68ac3b25eea23a16a45a139c82e5240cb361 /src/mongo/db
parent1fe77b5bd9fb13f9eb74275359dcc4ba69f2d5e9 (diff)
downloadmongo-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/SConscript9
-rw-r--r--src/mongo/db/read_write_concern_provenance_base.idl11
-rw-r--r--src/mongo/db/write_concern_options.cpp66
-rw-r--r--src/mongo/db/write_concern_options.h9
-rw-r--r--src/mongo/db/write_concern_options.idl62
-rw-r--r--src/mongo/db/write_concern_options_test.cpp12
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