summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--buildscripts/idl/idl/binder.py4
-rw-r--r--jstests/noPassthrough/shell_cmd_assertions.js13
-rw-r--r--src/mongo/db/SConscript2
-rw-r--r--src/mongo/db/read_write_concern_provenance.h1
-rw-r--r--src/mongo/db/read_write_concern_provenance_base.idl11
-rw-r--r--src/mongo/db/write_concern_options.cpp74
-rw-r--r--src/mongo/db/write_concern_options.h2
-rw-r--r--src/mongo/idl/basic_types.h43
-rw-r--r--src/mongo/idl/basic_types.idl59
-rw-r--r--src/mongo/idl/idl_test.cpp41
10 files changed, 208 insertions, 42 deletions
diff --git a/buildscripts/idl/idl/binder.py b/buildscripts/idl/idl/binder.py
index 2b62f6ece67..20eebf0158c 100644
--- a/buildscripts/idl/idl/binder.py
+++ b/buildscripts/idl/idl/binder.py
@@ -143,6 +143,10 @@ def _validate_cpp_type(ctxt, idl_type, syntax_type):
if idl_type.cpp_type == "std::vector<std::uint8_t>":
return
+ # Support variant for writeConcernW.
+ if idl_type.cpp_type == "stdx::variant<std::string, std::int64_t>":
+ return
+
# Check for std fixed integer types which are not allowed. These are not allowed even if they
# have the "std::" prefix.
for std_numeric_type in [
diff --git a/jstests/noPassthrough/shell_cmd_assertions.js b/jstests/noPassthrough/shell_cmd_assertions.js
index 207ac40fa12..cfb5e4fceca 100644
--- a/jstests/noPassthrough/shell_cmd_assertions.js
+++ b/jstests/noPassthrough/shell_cmd_assertions.js
@@ -162,9 +162,6 @@ tests.push(function collInsertCmdErr() {
assert.throws(() => assert.commandWorked(res));
assert.throws(() => assert.commandWorkedIgnoringWriteErrors(res));
assert.doesNotThrow(() => assert.commandFailed(res));
- assert.doesNotThrow(() => assert.commandFailedWithCode(res, ErrorCodes.FailedToParse));
- assert.doesNotThrow(
- () => assert.commandFailedWithCode(res, [ErrorCodes.FailedToParse, kFakeErrCode]));
});
tests.push(function collMultiInsertCmdErr() {
@@ -173,16 +170,6 @@ tests.push(function collMultiInsertCmdErr() {
assert.throws(() => assert.commandWorked(res));
assert.throws(() => assert.commandWorkedIgnoringWriteErrors(res));
assert.doesNotThrow(() => assert.commandFailed(res));
- assert.doesNotThrow(() => assert.commandFailedWithCode(res, ErrorCodes.FailedToParse));
- assert.doesNotThrow(
- () => assert.commandFailedWithCode(res, [ErrorCodes.FailedToParse, kFakeErrCode]));
- assert.doesNotThrow(() => assert.commandWorkedOrFailedWithCode(
- res,
- [ErrorCodes.FailedToParse, kFakeErrCode],
- "threw even though failed with correct error codes"));
- assert.throws(
- () => assert.commandWorkedOrFailedWithCode(
- res, [kFakeErrCode], "didn't throw even though failed with incorrect error codes"));
});
tests.push(function mapReduceOk() {
diff --git a/src/mongo/db/SConscript b/src/mongo/db/SConscript
index 97bc4e7bf1f..488ccbe28a8 100644
--- a/src/mongo/db/SConscript
+++ b/src/mongo/db/SConscript
@@ -400,6 +400,7 @@ env.Library(
],
LIBDEPS=[
'$BUILD_DIR/mongo/base',
+ '$BUILD_DIR/mongo/idl/basic_types',
'$BUILD_DIR/mongo/idl/idl_parser',
],
LIBDEPS_PRIVATE=[
@@ -414,6 +415,7 @@ env.Library(
],
LIBDEPS=[
'$BUILD_DIR/mongo/bson/util/bson_extract',
+ '$BUILD_DIR/mongo/idl/basic_types',
'read_write_concern_provenance',
],
)
diff --git a/src/mongo/db/read_write_concern_provenance.h b/src/mongo/db/read_write_concern_provenance.h
index 54ecf7426ea..686d1843d6d 100644
--- a/src/mongo/db/read_write_concern_provenance.h
+++ b/src/mongo/db/read_write_concern_provenance.h
@@ -34,6 +34,7 @@
#include "mongo/bson/bsonelement.h"
#include "mongo/bson/bsonobjbuilder.h"
#include "mongo/db/read_write_concern_provenance_base_gen.h"
+#include "mongo/idl/basic_types_gen.h"
namespace mongo {
diff --git a/src/mongo/db/read_write_concern_provenance_base.idl b/src/mongo/db/read_write_concern_provenance_base.idl
index e77d8a4c6be..4695d5f0a34 100644
--- a/src/mongo/db/read_write_concern_provenance_base.idl
+++ b/src/mongo/db/read_write_concern_provenance_base.idl
@@ -32,17 +32,6 @@ global:
imports:
- "mongo/idl/basic_types.idl"
-
-enums:
- ReadWriteConcernProvenanceSource:
- description: "Provenance sources"
- type: string
- values:
- clientSupplied: "clientSupplied"
- implicitDefault: "implicitDefault"
- customDefault: "customDefault"
- getLastErrorDefaults: "getLastErrorDefaults"
-
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 b25140c65cb..17e30eddff3 100644
--- a/src/mongo/db/write_concern_options.cpp
+++ b/src/mongo/db/write_concern_options.cpp
@@ -29,6 +29,8 @@
#include "mongo/platform/basic.h"
+#include <type_traits>
+
#include "mongo/db/write_concern_options.h"
#include "mongo/base/status.h"
@@ -36,6 +38,7 @@
#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 {
@@ -197,13 +200,11 @@ WriteConcernOptions WriteConcernOptions::deserializerForIDL(const BSONObj& obj)
}
StatusWith<WriteConcernOptions> WriteConcernOptions::extractWCFromCommand(const BSONObj& cmdObj) {
- WriteConcernOptions writeConcern;
-
// Return the default write concern if no write concern is provided. We check for the existence
// of the write concern field up front in order to avoid the expense of constructing an error
// status in bsonExtractTypedField() below.
if (!cmdObj.hasField(kWriteConcernField)) {
- return writeConcern;
+ return WriteConcernOptions();
}
BSONElement writeConcernElement;
@@ -216,15 +217,70 @@ StatusWith<WriteConcernOptions> WriteConcernOptions::extractWCFromCommand(const
BSONObj writeConcernObj = writeConcernElement.Obj();
// Empty write concern is interpreted to default.
if (writeConcernObj.isEmpty()) {
- return writeConcern;
+ return WriteConcernOptions();
+ }
+
+ // Ensure that the write concern document complies with the strict IDL definition, found in
+ // idl/basic_types.idl.
+ try {
+ auto writeConcernIdl = WriteConcernIdl::parse(
+ IDLParserErrorContext("WriteConcernOptions::extractWCFromCommand"), writeConcernObj);
+ // Convert the IDL parsed WriteConcern into a WriteConcernOptions object.
+ auto sw = convertFromIdl(writeConcernIdl);
+ if (!sw.isOK()) {
+ return sw.getStatus();
+ }
+ auto writeConcernOptions = sw.getValue();
+ writeConcernOptions.usedDefault = false;
+ return writeConcernOptions;
+ } catch (const DBException& ex) {
+ return ex.toStatus();
+ }
+}
+
+StatusWith<WriteConcernOptions> WriteConcernOptions::convertFromIdl(
+ const WriteConcernIdl& writeConcernIdl) {
+ WriteConcernOptions writeConcern;
+ auto parsedW = writeConcernIdl.getWriteConcernW();
+ if (!parsedW.usedDefault()) {
+ writeConcern.usedDefaultW = false;
+ auto wVal = parsedW.getValue();
+ if (auto wNum = stdx::get_if<std::int64_t>(&wVal)) {
+ if (*wNum < 0 ||
+ *wNum >
+ static_cast<std::decay_t<decltype(*wNum)>>(repl::ReplSetConfig::kMaxMembers)) {
+ uasserted(ErrorCodes::FailedToParse,
+ str::stream() << "w has to be a non-negative number and not greater than "
+ << repl::ReplSetConfig::kMaxMembers);
+ }
+ writeConcern.wNumNodes = static_cast<decltype(writeConcern.wNumNodes)>(*wNum);
+ } else {
+ auto wMode = stdx::get_if<std::string>(&wVal);
+ invariant(wMode);
+ writeConcern.wNumNodes = 0; // Have to reset from default 1.
+ writeConcern.wMode = std::move(*wMode);
+ }
+ }
+
+ auto j = writeConcernIdl.getJ();
+ auto fsync = writeConcernIdl.getFsync();
+ if (j && fsync && *j && *fsync) {
+ // If j and fsync are both set to true
+ return Status{ErrorCodes::FailedToParse, "fsync and j options cannot be used together"};
+ }
+ if (j && *j) {
+ writeConcern.syncMode = SyncMode::JOURNAL;
+ } else if (fsync && *fsync) {
+ writeConcern.syncMode = SyncMode::FSYNC;
+ } else if (j) {
+ // j has been set to false
+ writeConcern.syncMode = SyncMode::NONE;
}
- auto sw = parse(writeConcernObj);
- if (!sw.isOK()) {
- return sw.getStatus();
+ writeConcern.wTimeout = writeConcernIdl.getWtimeout();
+ if (auto source = writeConcernIdl.getSource()) {
+ writeConcern._provenance = ReadWriteConcernProvenance(*source);
}
- writeConcern = sw.getValue();
- writeConcern.usedDefault = false;
return writeConcern;
}
diff --git a/src/mongo/db/write_concern_options.h b/src/mongo/db/write_concern_options.h
index 7bc696f728d..870646fb528 100644
--- a/src/mongo/db/write_concern_options.h
+++ b/src/mongo/db/write_concern_options.h
@@ -33,6 +33,7 @@
#include "mongo/db/jsobj.h"
#include "mongo/db/read_write_concern_provenance.h"
+#include "mongo/idl/basic_types_gen.h"
namespace mongo {
@@ -143,6 +144,7 @@ public:
private:
ReadWriteConcernProvenance _provenance;
+ static StatusWith<WriteConcernOptions> convertFromIdl(const WriteConcernIdl& writeConcernIdl);
};
} // namespace mongo
diff --git a/src/mongo/idl/basic_types.h b/src/mongo/idl/basic_types.h
index 01e38a5cb99..fb574d44a19 100644
--- a/src/mongo/idl/basic_types.h
+++ b/src/mongo/idl/basic_types.h
@@ -33,6 +33,7 @@
#include "mongo/base/string_data.h"
#include "mongo/bson/bsonobjbuilder.h"
+#include "mongo/stdx/variant.h"
namespace mongo {
@@ -132,4 +133,44 @@ public:
private:
BSONElement _element;
};
-} // namespace mongo \ No newline at end of file
+class WriteConcernW {
+public:
+ static WriteConcernW deserializeWriteConcernW(BSONElement wEl) {
+ if (wEl.isNumber()) {
+ return WriteConcernW{wEl.safeNumberLong()};
+ } else if (wEl.type() == BSONType::String) {
+ return WriteConcernW{wEl.valuestrsafe()};
+ } else if (wEl.eoo() || wEl.type() == BSONType::jstNULL ||
+ wEl.type() == BSONType::Undefined) {
+ return WriteConcernW{};
+ }
+ uasserted(ErrorCodes::FailedToParse, "w has to be a number or string");
+ }
+
+ void serializeWriteConcernW(BSONObjBuilder* builder) const {
+ if (auto stringVal = stdx::get_if<std::string>(&_w)) {
+ builder->append("w", *stringVal);
+ return;
+ }
+ auto intVal = stdx::get_if<std::int64_t>(&_w);
+ invariant(intVal);
+ builder->appendIntOrLL("w", *intVal);
+ }
+
+ WriteConcernW() : _w{1}, _usedDefault{true} {};
+
+ bool usedDefault() const {
+ return _usedDefault;
+ }
+
+ stdx::variant<std::string, std::int64_t> getValue() const {
+ return _w;
+ }
+
+private:
+ WriteConcernW(std::int64_t w) : _w{w}, _usedDefault{false} {};
+ WriteConcernW(std::string&& w) : _w(std::move(w)), _usedDefault{false} {};
+ stdx::variant<std::string, std::int64_t> _w;
+ bool _usedDefault;
+};
+} // namespace mongo
diff --git a/src/mongo/idl/basic_types.idl b/src/mongo/idl/basic_types.idl
index 2b5a4719fec..f2aea90db79 100644
--- a/src/mongo/idl/basic_types.idl
+++ b/src/mongo/idl/basic_types.idl
@@ -202,8 +202,33 @@ types:
serializer: mongo::IDLAnyType::serializeToBSON
deserializer: mongo::IDLAnyType::parseFromBSON
-structs:
+ writeConcernW:
+ bson_serialization_type:
+ - string
+ - int
+ - decimal
+ - double
+ - long
+ 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"
+ default: "mongo::WriteConcernW()"
+ serializer: "mongo::WriteConcernW::serializeWriteConcernW"
+ deserializer: "mongo::WriteConcernW::deserializeWriteConcernW"
+enums:
+ ReadWriteConcernProvenanceSource:
+ description: "Provenance sources"
+ type: string
+ values:
+ clientSupplied: "clientSupplied"
+ implicitDefault: "implicitDefault"
+ customDefault: "customDefault"
+ getLastErrorDefaults: "getLastErrorDefaults"
+
+
+structs:
OkReply:
description: "Shared by commands that reply with just {ok: 1} and no additional information"
strict: true
@@ -230,3 +255,35 @@ structs:
type: safeInt64
optional: true
validator: { gte: 0 }
+ WriteConcernIdl:
+ description: "WriteConcern object parser"
+ strict: true
+ fields:
+ w:
+ type: writeConcernW
+ cpp_name: writeConcernW
+ j:
+ type: safeBool
+ optional: true
+ wtimeout:
+ type: safeInt64
+ default: 0
+ fsync:
+ type: safeBool
+ optional: true
+ # 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
+ wOpTime:
+ type: any
+ ignore: true
+ getLastError:
+ type: any
+ ignore: true
+ provenance:
+ description: "The source for this provenance"
+ cpp_name: source
+ type: ReadWriteConcernProvenanceSource
+ optional: true
diff --git a/src/mongo/idl/idl_test.cpp b/src/mongo/idl/idl_test.cpp
index b411ee280ec..da7e8ee6f18 100644
--- a/src/mongo/idl/idl_test.cpp
+++ b/src/mongo/idl/idl_test.cpp
@@ -552,13 +552,40 @@ TEST(IDLStructTests, TestNonStrictStruct) {
}
}
-// Test some basic properties of the OkReply struct. TODO SERVER-51380 replace with a more
-// thorough/integration-oriented test once some basic OkReplying-commands like ping get implemented.
-TEST(IDLStructTests, TestOkReplyStruct) {
- OkReply okReply;
-
- // Ensure that the serialized OkReply struct is the empty BSON object.
- ASSERT_BSONOBJ_EQ(okReply.toBSON(), BSONObj());
+TEST(IDLStructTests, WriteConcernTest) {
+ IDLParserErrorContext ctxt("root");
+ // Numeric w value
+ {
+ auto writeConcernDoc = BSON("w" << 1 << "j" << true << "wtimeout" << 5000);
+ auto writeConcernStruct = WriteConcernIdl::parse(ctxt, writeConcernDoc);
+ BSONObjBuilder builder;
+ writeConcernStruct.serialize(&builder);
+ ASSERT_BSONOBJ_EQ(builder.obj(), writeConcernDoc);
+ }
+ // String w value
+ {
+ auto writeConcernDoc = BSON("w"
+ << "majority"
+ << "j" << true << "wtimeout" << 5000);
+ auto writeConcernStruct = WriteConcernIdl::parse(ctxt, writeConcernDoc);
+ BSONObjBuilder builder;
+ writeConcernStruct.serialize(&builder);
+ ASSERT_BSONOBJ_EQ(builder.obj(), writeConcernDoc);
+ }
+ // Ignore options wElectionId, wOpTime, getLastError
+ {
+ auto writeConcernDoc = BSON("w"
+ << "majority"
+ << "j" << true << "wtimeout" << 5000 << "wElectionId" << 12345
+ << "wOpTime" << 98765 << "getLastError" << true);
+ auto writeConcernDocWithoutIgnoredFields = BSON("w"
+ << "majority"
+ << "j" << true << "wtimeout" << 5000);
+ auto writeConcernStruct = WriteConcernIdl::parse(ctxt, writeConcernDoc);
+ BSONObjBuilder builder;
+ writeConcernStruct.serialize(&builder);
+ ASSERT_BSONOBJ_EQ(builder.obj(), writeConcernDocWithoutIgnoredFields);
+ }
}
/// Struct default comparison tests