summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBilly Donahue <billy.donahue@mongodb.com>2018-03-05 15:48:48 -0500
committerBilly Donahue <billy.donahue@mongodb.com>2018-03-06 10:04:53 -0500
commite3a3f54a6c0ec4f471e0aff7a48b8a30f8c0e9cb (patch)
tree9c2f4ac5a4d8751336397c8af59a08bf78f1c201
parent5dfa372b25b1a26bb12e09fdb029ff219b0f5343 (diff)
downloadmongo-e3a3f54a6c0ec4f471e0aff7a48b8a30f8c0e9cb.tar.gz
SERVER-33065 CommandReplyBuilder and CommandInvocation
Revert "Revert "SERVER-33065 CommandReplyBuilder and CommandInvocation"" This reverts commit 74177edb35b6ce7594e4751051010ceade592484. Leave out the unintentional repl/ change this time.
-rw-r--r--src/mongo/bson/bsonobjbuilder.h8
-rw-r--r--src/mongo/client/embedded/service_entry_point_embedded.cpp9
-rw-r--r--src/mongo/db/commands.cpp169
-rw-r--r--src/mongo/db/commands.h243
-rw-r--r--src/mongo/db/commands/count_cmd.cpp5
-rw-r--r--src/mongo/db/commands/distinct.cpp5
-rw-r--r--src/mongo/db/commands/explain_cmd.cpp10
-rw-r--r--src/mongo/db/commands/find_and_modify.cpp5
-rw-r--r--src/mongo/db/commands/find_cmd.cpp5
-rw-r--r--src/mongo/db/commands/group_cmd.cpp11
-rw-r--r--src/mongo/db/commands/mr_test.cpp10
-rw-r--r--src/mongo/db/commands/pipeline_command.cpp5
-rw-r--r--src/mongo/db/commands/write_commands/write_commands.cpp8
-rw-r--r--src/mongo/db/service_entry_point_common.cpp47
-rw-r--r--src/mongo/db/service_entry_point_common.h7
-rw-r--r--src/mongo/db/service_entry_point_mongod.cpp25
-rw-r--r--src/mongo/s/commands/cluster_count_cmd.cpp5
-rw-r--r--src/mongo/s/commands/cluster_distinct_cmd.cpp5
-rw-r--r--src/mongo/s/commands/cluster_explain_cmd.cpp10
-rw-r--r--src/mongo/s/commands/cluster_find_and_modify_cmd.cpp5
-rw-r--r--src/mongo/s/commands/cluster_find_cmd.cpp5
-rw-r--r--src/mongo/s/commands/cluster_pipeline_cmd.cpp5
-rw-r--r--src/mongo/s/commands/cluster_write_cmd.cpp13
-rw-r--r--src/mongo/s/commands/commands_public.cpp5
-rw-r--r--src/mongo/s/commands/strategy.cpp62
25 files changed, 453 insertions, 234 deletions
diff --git a/src/mongo/bson/bsonobjbuilder.h b/src/mongo/bson/bsonobjbuilder.h
index 38dae8234fa..c8aed97e15b 100644
--- a/src/mongo/bson/bsonobjbuilder.h
+++ b/src/mongo/bson/bsonobjbuilder.h
@@ -172,6 +172,14 @@ public:
}
}
+ /**
+ * The start offset of the object being built by this builder within its buffer.
+ * Needed for the object-resuming constructor.
+ */
+ std::size_t offset() const {
+ return _offset;
+ }
+
/** add all the fields from the object specified to this object */
BSONObjBuilder& appendElements(const BSONObj& x);
diff --git a/src/mongo/client/embedded/service_entry_point_embedded.cpp b/src/mongo/client/embedded/service_entry_point_embedded.cpp
index 850ce966b1f..2a0e2ecb3b3 100644
--- a/src/mongo/client/embedded/service_entry_point_embedded.cpp
+++ b/src/mongo/client/embedded/service_entry_point_embedded.cpp
@@ -91,19 +91,20 @@ public:
return false;
}
- void waitForReadConcern(OperationContext*, const Command*, const OpMsgRequest&) const override {
- }
+ void waitForReadConcern(OperationContext*,
+ const CommandInvocation*,
+ const OpMsgRequest&) const override {}
void waitForWriteConcern(OperationContext* opCtx,
const std::string& commandName,
const repl::OpTime& lastOpBeforeRun,
- BSONObjBuilder* commandResponseBuilder) const override {}
+ BSONObjBuilder commandResponseBuilder) const override {}
void waitForLinearizableReadConcern(OperationContext*) const override {}
void uassertCommandDoesNotSpecifyWriteConcern(const BSONObj&) const override {}
- void attachCurOpErrInfo(OperationContext*, BSONObjBuilder&) const override {}
+ void attachCurOpErrInfo(OperationContext*, const BSONObj&) const override {}
};
DbResponse ServiceEntryPointEmbedded::handleRequest(OperationContext* opCtx, const Message& m) {
diff --git a/src/mongo/db/commands.cpp b/src/mongo/db/commands.cpp
index fe1b6c0fc2d..17034ec2bf2 100644
--- a/src/mongo/db/commands.cpp
+++ b/src/mongo/db/commands.cpp
@@ -71,6 +71,23 @@ const WriteConcernOptions kMajorityWriteConcern(
WriteConcernOptions::SyncMode::UNSET,
Seconds(60));
+// A facade presenting CommandDefinition as an audit::CommandInterface.
+class CommandAuditHook : public audit::CommandInterface {
+public:
+ explicit CommandAuditHook(Command* command) : _command(command) {}
+
+ void redactForLogging(mutablebson::Document* cmdObj) const final {
+ _command->redactForLogging(cmdObj);
+ }
+
+ std::string parseNs(const std::string& dbname, const BSONObj& cmdObj) const final {
+ return _command->parseNs(dbname, cmdObj);
+ }
+
+private:
+ Command* _command;
+};
+
} // namespace
@@ -80,18 +97,22 @@ const WriteConcernOptions kMajorityWriteConcern(
BSONObj CommandHelpers::runCommandDirectly(OperationContext* opCtx, const OpMsgRequest& request) {
auto command = globalCommandRegistry()->findCommand(request.getCommandName());
invariant(command);
- BSONObjBuilder out;
+ BufBuilder bb;
+ CommandReplyBuilder crb(BSONObjBuilder{bb});
try {
- bool ok = command->publicRun(opCtx, request, out);
- appendCommandStatus(out, ok);
+ auto invocation = command->parse(opCtx, request);
+ invocation->run(opCtx, &crb);
+ auto body = crb.getBodyBuilder();
+ CommandHelpers::extractOrAppendOk(body);
} catch (const StaleConfigException&) {
// These exceptions are intended to be handled at a higher level.
throw;
} catch (const DBException& ex) {
- out.resetToEmpty();
- appendCommandStatus(out, ex.toStatus());
+ auto body = crb.getBodyBuilder();
+ body.resetToEmpty();
+ appendCommandStatus(body, ex.toStatus());
}
- return out.obj();
+ return BSONObj(bb.release());
}
std::string CommandHelpers::parseNsFullyQualified(StringData dbname, const BSONObj& cmdObj) {
@@ -167,6 +188,16 @@ void CommandHelpers::appendCommandStatus(BSONObjBuilder& result,
}
}
+bool CommandHelpers::extractOrAppendOk(BSONObjBuilder& reply) {
+ if (auto okField = reply.asTempObj()["ok"]) {
+ // If ok is present, use its truthiness.
+ return okField.trueValue();
+ }
+ // Missing "ok" field is an implied success.
+ CommandHelpers::appendCommandStatus(reply, true);
+ return true;
+}
+
void CommandHelpers::appendCommandWCStatus(BSONObjBuilder& result,
const Status& awaitReplicationStatus,
const WriteConcernResult& wcResult) {
@@ -306,10 +337,99 @@ bool CommandHelpers::isHelpRequest(const BSONElement& helpElem) {
constexpr StringData CommandHelpers::kHelpFieldName;
//////////////////////////////////////////////////////////////
+// CommandReplyBuilder
+
+CommandReplyBuilder::CommandReplyBuilder(BSONObjBuilder bodyObj)
+ : _bodyBuf(&bodyObj.bb()), _bodyOffset(bodyObj.offset()) {
+ // CommandReplyBuilder requires that bodyObj build into an externally-owned buffer.
+ invariant(!bodyObj.owned());
+ bodyObj.doneFast();
+}
+
+BSONObjBuilder CommandReplyBuilder::getBodyBuilder() const {
+ return BSONObjBuilder(BSONObjBuilder::ResumeBuildingTag{}, *_bodyBuf, _bodyOffset);
+}
+
+void CommandReplyBuilder::reset() {
+ getBodyBuilder().resetToEmpty();
+}
+
+//////////////////////////////////////////////////////////////
+// CommandInvocation
+
+CommandInvocation::~CommandInvocation() = default;
+
+//////////////////////////////////////////////////////////////
// Command
+class Command::InvocationShim final : public CommandInvocation {
+public:
+ InvocationShim(OperationContext*, const OpMsgRequest& request, Command* command)
+ : CommandInvocation(command),
+ _command(command),
+ _request(&request),
+ _dbName(_request->getDatabase().toString()) {}
+
+private:
+ void run(OperationContext* opCtx, CommandReplyBuilder* result) override {
+ try {
+ BSONObjBuilder bob = result->getBodyBuilder();
+ bool ok = _command->enhancedRun(opCtx, *_request, bob);
+ CommandHelpers::appendCommandStatus(bob, ok);
+ } catch (const ExceptionFor<ErrorCodes::Unauthorized>&) {
+ CommandAuditHook hook(_command);
+ audit::logCommandAuthzCheck(
+ opCtx->getClient(), *_request, &hook, ErrorCodes::Unauthorized);
+ throw;
+ }
+ }
+
+ void explain(OperationContext* opCtx,
+ ExplainOptions::Verbosity verbosity,
+ BSONObjBuilder* result) override {
+ uassertStatusOK(_command->explain(opCtx, *_request, verbosity, result));
+ }
+
+ NamespaceString ns() const override {
+ return NamespaceString(_command->parseNs(_dbName, cmdObj()));
+ }
+
+ bool supportsWriteConcern() const override {
+ return _command->supportsWriteConcern(cmdObj());
+ }
+
+ Command::AllowedOnSecondary secondaryAllowed(ServiceContext* context) const override {
+ return _command->secondaryAllowed(context);
+ }
+
+ bool supportsReadConcern(repl::ReadConcernLevel level) const override {
+ return _command->supportsReadConcern(_dbName, cmdObj(), level);
+ }
+
+ bool allowsAfterClusterTime() const override {
+ return _command->allowsAfterClusterTime(cmdObj());
+ }
+
+ void doCheckAuthorization(OperationContext* opCtx) const override {
+ uassertStatusOK(_command->checkAuthForRequest(opCtx, *_request));
+ }
+
+ const BSONObj& cmdObj() const {
+ return _request->body;
+ }
+
+ Command* const _command;
+ const OpMsgRequest* const _request;
+ const std::string _dbName;
+};
+
Command::~Command() = default;
+std::unique_ptr<CommandInvocation> Command::parse(OperationContext* opCtx,
+ const OpMsgRequest& request) {
+ return stdx::make_unique<InvocationShim>(opCtx, request, this);
+}
+
std::string Command::parseNs(const std::string& dbname, const BSONObj& cmdObj) const {
BSONElement first = cmdObj.firstElement();
if (first.type() != mongo::String)
@@ -335,8 +455,7 @@ Command::Command(StringData name, StringData oldName)
}
Status Command::explain(OperationContext* opCtx,
- const std::string& dbname,
- const BSONObj& cmdObj,
+ const OpMsgRequest& request,
ExplainOptions::Verbosity verbosity,
BSONObjBuilder* out) const {
return {ErrorCodes::IllegalOperation, str::stream() << "Cannot explain cmd: " << getName()};
@@ -396,25 +515,6 @@ static Status _checkAuthorizationImpl(Command* c,
return Status::OK();
}
-namespace {
-// A facade presenting CommandDefinition as an audit::CommandInterface.
-class CommandAuditHook : public audit::CommandInterface {
-public:
- explicit CommandAuditHook(Command* command) : _command(command) {}
-
- void redactForLogging(mutablebson::Document* cmdObj) const final {
- _command->redactForLogging(cmdObj);
- }
-
- std::string parseNs(const std::string& dbname, const BSONObj& cmdObj) const final {
- return _command->parseNs(dbname, cmdObj);
- }
-
-private:
- Command* _command;
-};
-} // namespace
-
Status Command::checkAuthorization(Command* c,
OperationContext* opCtx,
const OpMsgRequest& request) {
@@ -427,21 +527,6 @@ Status Command::checkAuthorization(Command* c,
return status;
}
-bool Command::publicRun(OperationContext* opCtx,
- const OpMsgRequest& request,
- BSONObjBuilder& result) {
- try {
- return enhancedRun(opCtx, request, result);
- } catch (const DBException& e) {
- if (e.code() == ErrorCodes::Unauthorized) {
- CommandAuditHook hook(this);
- audit::logCommandAuthzCheck(
- opCtx->getClient(), request, &hook, ErrorCodes::Unauthorized);
- }
- throw;
- }
-}
-
void Command::generateHelpResponse(OperationContext* opCtx,
rpc::ReplyBuilderInterface* replyBuilder,
const Command& command) {
diff --git a/src/mongo/db/commands.h b/src/mongo/db/commands.h
index 5779d89e7e9..2a9611fb70f 100644
--- a/src/mongo/db/commands.h
+++ b/src/mongo/db/commands.h
@@ -82,6 +82,13 @@ struct CommandHelpers {
static bool appendCommandStatus(BSONObjBuilder& result, const Status& status);
/**
+ * If "ok" field is present in `reply`, uses its truthiness.
+ * Otherwise, the absence of failure is considered success, `reply` is patched to indicate it.
+ * Returns true if reply indicates a success.
+ */
+ static bool extractOrAppendOk(BSONObjBuilder& reply);
+
+ /**
* Helper for setting a writeConcernError field in the command result object if
* a writeConcern error occurs.
*
@@ -187,6 +194,8 @@ struct CommandHelpers {
static constexpr StringData kHelpFieldName = "help"_sd;
};
+class CommandInvocation;
+
/**
* Serves as a base for server commands. See the constructor for more details.
*/
@@ -207,6 +216,9 @@ public:
// See https://gcc.gnu.org/wiki/VerboseDiagnostics#missing_vtable
virtual ~Command();
+ virtual std::unique_ptr<CommandInvocation> parse(OperationContext* opCtx,
+ const OpMsgRequest& request);
+
/**
* Returns the command's name. This value never changes for the lifetime of the command.
*/
@@ -240,16 +252,6 @@ public:
}
/**
- * supportsWriteConcern returns true if this command should be parsed for a writeConcern
- * field and wait for that write concern to be satisfied after the command runs.
- *
- * @param cmd is a BSONObj representation of the command that is used to determine if the
- * the command supports a write concern. Ex. aggregate only supports write concern
- * when $out is provided.
- */
- virtual bool supportsWriteConcern(const BSONObj& cmd) const = 0;
-
- /**
* Return true if only the admin ns has privileges to run this command.
*/
virtual bool adminOnly() const {
@@ -267,7 +269,7 @@ public:
return false;
}
- virtual AllowedOnSecondary secondaryAllowed(ServiceContext*) const = 0;
+ virtual AllowedOnSecondary secondaryAllowed(ServiceContext* context) const = 0;
/**
* Override and return fales if the command opcounters should not be incremented on
@@ -292,23 +294,6 @@ public:
}
/**
- * Commands which can be explained override this method. Any operation which has a query
- * part and executes as a tree of execution stages can be explained. A command should
- * implement explain by:
- *
- * 1) Calling its custom parse function in order to parse the command. The output of
- * this function should be a CanonicalQuery (representing the query part of the
- * operation), and a PlanExecutor which wraps the tree of execution stages.
- *
- * 2) Calling Explain::explainStages(...) on the PlanExecutor. This is the function
- * which knows how to convert an execution stage tree into explain output.
- */
- virtual Status explain(OperationContext* opCtx,
- const std::string& dbname,
- const BSONObj& cmdObj,
- ExplainOptions::Verbosity verbosity,
- BSONObjBuilder* out) const;
- /**
* Checks if the client associated with the given OperationContext is authorized to run this
* command.
*/
@@ -343,33 +328,6 @@ public:
}
/**
- * Returns true if this Command supports the given readConcern level. Takes the command object
- * and the name of the database on which it was invoked as arguments, so that readConcern can be
- * conditionally rejected based on the command's parameters and/or namespace.
- *
- * If a readConcern level argument is sent to a command that returns false the command processor
- * will reject the command, returning an appropriate error message.
- *
- * Note that this is never called on mongos. Sharded commands are responsible for forwarding
- * the option to the shards as needed. We rely on the shards to fail the commands in the
- * cases where it isn't supported.
- */
- virtual bool supportsReadConcern(const std::string& dbName,
- const BSONObj& cmdObj,
- repl::ReadConcernLevel level) const {
- return level == repl::ReadConcernLevel::kLocalReadConcern;
- }
-
- /**
- * Returns true if command allows afterClusterTime in its readConcern. The command may not allow
- * it if it is specifically intended not to take any LockManager locks. Waiting for
- * afterClusterTime takes the MODE_IS lock.
- */
- virtual bool allowsAfterClusterTime(const BSONObj& cmdObj) const {
- return true;
- }
-
- /**
* Returns LogicalOp for this command.
*/
virtual LogicalOp getLogicalOp() const {
@@ -402,13 +360,6 @@ public:
}
/**
- * Runs the command.
- *
- * Forwards to enhancedRun, but additionally runs audit checks if run throws unauthorized.
- */
- bool publicRun(OperationContext* opCtx, const OpMsgRequest& request, BSONObjBuilder& result);
-
- /**
* Generates a reply from the 'help' information associated with a command. The state of
* the passed ReplyBuilder will be in kOutputDocs after calling this method.
*/
@@ -456,6 +407,62 @@ public:
static bool testCommandsEnabled;
private:
+ class InvocationShim;
+
+ /**
+ * Commands which can be explained override this method. Any operation which has a query
+ * part and executes as a tree of execution stages can be explained. A command should
+ * implement explain by:
+ *
+ * 1) Calling its custom parse function in order to parse the command. The output of
+ * this function should be a CanonicalQuery (representing the query part of the
+ * operation), and a PlanExecutor which wraps the tree of execution stages.
+ *
+ * 2) Calling Explain::explainStages(...) on the PlanExecutor. This is the function
+ * which knows how to convert an execution stage tree into explain output.
+ */
+ virtual Status explain(OperationContext* opCtx,
+ const OpMsgRequest& request,
+ ExplainOptions::Verbosity verbosity,
+ BSONObjBuilder* out) const;
+
+ /**
+ * supportsWriteConcern returns true if this command should be parsed for a writeConcern
+ * field and wait for that write concern to be satisfied after the command runs.
+ *
+ * @param cmd is a BSONObj representation of the command that is used to determine if the
+ * the command supports a write concern. Ex. aggregate only supports write concern
+ * when $out is provided.
+ */
+ virtual bool supportsWriteConcern(const BSONObj& cmd) const = 0;
+
+ /**
+ * Returns true if this Command supports the given readConcern level. Takes the command object
+ * and the name of the database on which it was invoked as arguments, so that readConcern can be
+ * conditionally rejected based on the command's parameters and/or namespace.
+ *
+ * If a readConcern level argument is sent to a command that returns false the command processor
+ * will reject the command, returning an appropriate error message.
+ *
+ * Note that this is never called on mongos. Sharded commands are responsible for forwarding
+ * the option to the shards as needed. We rely on the shards to fail the commands in the
+ * cases where it isn't supported.
+ */
+ virtual bool supportsReadConcern(const std::string& dbName,
+ const BSONObj& cmdObj,
+ repl::ReadConcernLevel level) const {
+ return level == repl::ReadConcernLevel::kLocalReadConcern;
+ }
+
+ /**
+ * Returns true if command allows afterClusterTime in its readConcern. The command may not allow
+ * it if it is specifically intended not to take any LockManager locks. Waiting for
+ * afterClusterTime takes the MODE_IS lock.
+ */
+ virtual bool allowsAfterClusterTime(const BSONObj& cmdObj) const {
+ return true;
+ }
+
/**
* Runs the command.
*
@@ -480,6 +487,118 @@ private:
ServerStatusMetricField<Counter64> _commandsFailedMetric;
};
+class CommandReplyBuilder {
+public:
+ explicit CommandReplyBuilder(BSONObjBuilder bodyObj);
+
+ CommandReplyBuilder(const CommandReplyBuilder&) = delete;
+ CommandReplyBuilder& operator=(const CommandReplyBuilder&) = delete;
+
+ /**
+ * Returns a BSONObjBuilder that can be used to build the reply in-place. The returned
+ * builder (or an object into which it has been moved) must be completed before calling
+ * any more methods on this object. A builder is completed by a call to `done()` or by
+ * its destructor. Can be called repeatedly to append multiple things to the reply, as
+ * long as each returned builder must be completed between calls.
+ */
+ BSONObjBuilder getBodyBuilder() const;
+
+ void reset();
+
+private:
+ BufBuilder* const _bodyBuf;
+ const std::size_t _bodyOffset;
+};
+
+/**
+ * Represents a single invocation of a given command.
+ */
+class CommandInvocation {
+public:
+ CommandInvocation(const Command* definition) : _definition(definition) {}
+ virtual ~CommandInvocation();
+
+ /**
+ * Runs the command, filling in result. Any exception thrown from here will cause result
+ * to be reset and filled in with the error. Non-const to permit modifying the request
+ * type to perform normalization. Calls that return normally without setting an "ok"
+ * field into result are assumed to have completed successfully. Failure should be
+ * indicated either by throwing (preferred), or by calling
+ * `CommandHelpers::extractOrAppendOk`.
+ */
+ virtual void run(OperationContext* opCtx, CommandReplyBuilder* result) = 0;
+
+ virtual void explain(OperationContext* opCtx,
+ ExplainOptions::Verbosity verbosity,
+ BSONObjBuilder* result) = 0;
+
+ /**
+ * The primary namespace on which this command operates. May just be the db.
+ */
+ virtual NamespaceString ns() const = 0;
+
+ /**
+ * Returns true if this command should be parsed for a writeConcern field and wait
+ * for that write concern to be satisfied after the command runs.
+ */
+ virtual bool supportsWriteConcern() const = 0;
+
+ virtual Command::AllowedOnSecondary secondaryAllowed(ServiceContext* context) const = 0;
+
+ /**
+ * Returns true if this Command supports the given readConcern level. Takes the command object
+ * and the name of the database on which it was invoked as arguments, so that readConcern can be
+ * conditionally rejected based on the command's parameters and/or namespace.
+ *
+ * If a readConcern level argument is sent to a command that returns false the command processor
+ * will reject the command, returning an appropriate error message.
+ *
+ * Note that this is never called on mongos. Sharded commands are responsible for forwarding
+ * the option to the shards as needed. We rely on the shards to fail the commands in the
+ * cases where it isn't supported.
+ */
+ virtual bool supportsReadConcern(repl::ReadConcernLevel level) const {
+ return level == repl::ReadConcernLevel::kLocalReadConcern;
+ }
+
+ /**
+ * Returns true if command allows afterClusterTime in its readConcern. The command may not allow
+ * it if it is specifically intended not to take any LockManager locks. Waiting for
+ * afterClusterTime takes the MODE_IS lock.
+ */
+ virtual bool allowsAfterClusterTime() const {
+ return true;
+ }
+
+ /**
+ * The command definition that this invocation runs.
+ * Note: nonvirtual.
+ */
+ const Command* definition() const {
+ return _definition;
+ }
+
+ /**
+ * Throws DBException, most likely `ErrorCodes::Unauthorized`, unless
+ * the client executing "opCtx" is authorized to run the given command
+ * with the given parameters on the given named database.
+ * Note: nonvirtual.
+ */
+ void checkAuthorization(OperationContext* opCtx) const;
+
+protected:
+ ResourcePattern resourcePattern() const;
+
+private:
+ /**
+ * Polymorphic extension point for `checkAuthorization`.
+ * Throws unless `opCtx`'s client is authorized to `run()` this.
+ */
+ virtual void doCheckAuthorization(OperationContext* opCtx) const = 0;
+
+ const Command* const _definition;
+};
+
/**
* A subclass of Command that only cares about the BSONObj body and doesn't need access to document
* sequences.
diff --git a/src/mongo/db/commands/count_cmd.cpp b/src/mongo/db/commands/count_cmd.cpp
index d2e35f86fd6..a91561f6da6 100644
--- a/src/mongo/db/commands/count_cmd.cpp
+++ b/src/mongo/db/commands/count_cmd.cpp
@@ -110,10 +110,11 @@ public:
}
Status explain(OperationContext* opCtx,
- const std::string& dbname,
- const BSONObj& cmdObj,
+ const OpMsgRequest& opMsgRequest,
ExplainOptions::Verbosity verbosity,
BSONObjBuilder* out) const override {
+ std::string dbname = opMsgRequest.getDatabase().toString();
+ const BSONObj& cmdObj = opMsgRequest.body;
// Acquire locks and resolve possible UUID. The RAII object is optional, because in the case
// of a view, the locks need to be released.
boost::optional<AutoGetCollectionForReadCommand> ctx;
diff --git a/src/mongo/db/commands/distinct.cpp b/src/mongo/db/commands/distinct.cpp
index eba4fdd51bc..ae99b5a2332 100644
--- a/src/mongo/db/commands/distinct.cpp
+++ b/src/mongo/db/commands/distinct.cpp
@@ -110,10 +110,11 @@ public:
}
Status explain(OperationContext* opCtx,
- const std::string& dbname,
- const BSONObj& cmdObj,
+ const OpMsgRequest& request,
ExplainOptions::Verbosity verbosity,
BSONObjBuilder* out) const override {
+ std::string dbname = request.getDatabase().toString();
+ const BSONObj& cmdObj = request.body;
// Acquire locks and resolve possible UUID. The RAII object is optional, because in the case
// of a view, the locks need to be released.
boost::optional<AutoGetCollectionForReadCommand> ctx;
diff --git a/src/mongo/db/commands/explain_cmd.cpp b/src/mongo/db/commands/explain_cmd.cpp
index 8f0ce39a66a..7cdd1441d3f 100644
--- a/src/mongo/db/commands/explain_cmd.cpp
+++ b/src/mongo/db/commands/explain_cmd.cpp
@@ -154,13 +154,9 @@ public:
}
// Actually call the nested command's explain(...) method.
- Status explainStatus =
- commToExplain->explain(opCtx, dbname, explainObj, verbosity.getValue(), &result);
- if (!explainStatus.isOK()) {
- return CommandHelpers::appendCommandStatus(result, explainStatus);
- }
-
- return true;
+ commToExplain->parse(opCtx, OpMsgRequest::fromDBAndBody(dbname, explainObj))
+ ->explain(opCtx, verbosity.getValue(), &result);
+ return CommandHelpers::extractOrAppendOk(result);
}
} cmdExplain;
diff --git a/src/mongo/db/commands/find_and_modify.cpp b/src/mongo/db/commands/find_and_modify.cpp
index e836e7aa737..9757e8e450d 100644
--- a/src/mongo/db/commands/find_and_modify.cpp
+++ b/src/mongo/db/commands/find_and_modify.cpp
@@ -246,10 +246,11 @@ public:
}
Status explain(OperationContext* opCtx,
- const std::string& dbName,
- const BSONObj& cmdObj,
+ const OpMsgRequest& request,
ExplainOptions::Verbosity verbosity,
BSONObjBuilder* out) const override {
+ std::string dbName = request.getDatabase().toString();
+ const BSONObj& cmdObj = request.body;
const auto args(uassertStatusOK(FindAndModifyRequest::parseFromBSON(
CommandHelpers::parseNsCollectionRequired(dbName, cmdObj), cmdObj)));
const NamespaceString& nsString = args.getNamespaceString();
diff --git a/src/mongo/db/commands/find_cmd.cpp b/src/mongo/db/commands/find_cmd.cpp
index 451171fc348..7cd41a2a6a8 100644
--- a/src/mongo/db/commands/find_cmd.cpp
+++ b/src/mongo/db/commands/find_cmd.cpp
@@ -126,10 +126,11 @@ public:
}
Status explain(OperationContext* opCtx,
- const std::string& dbname,
- const BSONObj& cmdObj,
+ const OpMsgRequest& request,
ExplainOptions::Verbosity verbosity,
BSONObjBuilder* out) const override {
+ std::string dbname = request.getDatabase().toString();
+ const BSONObj& cmdObj = request.body;
// Acquire locks and resolve possible UUID. The RAII object is optional, because in the case
// of a view, the locks need to be released.
boost::optional<AutoGetCollectionForReadCommand> ctx;
diff --git a/src/mongo/db/commands/group_cmd.cpp b/src/mongo/db/commands/group_cmd.cpp
index 547dee36087..c4c67983ed8 100644
--- a/src/mongo/db/commands/group_cmd.cpp
+++ b/src/mongo/db/commands/group_cmd.cpp
@@ -117,11 +117,12 @@ private:
return nss.ns();
}
- virtual Status explain(OperationContext* opCtx,
- const std::string& dbname,
- const BSONObj& cmdObj,
- ExplainOptions::Verbosity verbosity,
- BSONObjBuilder* out) const {
+ Status explain(OperationContext* opCtx,
+ const OpMsgRequest& request,
+ ExplainOptions::Verbosity verbosity,
+ BSONObjBuilder* out) const override {
+ std::string dbname = request.getDatabase().toString();
+ const BSONObj& cmdObj = request.body;
GroupRequest groupRequest;
Status parseRequestStatus = _parseRequest(dbname, cmdObj, &groupRequest);
if (!parseRequestStatus.isOK()) {
diff --git a/src/mongo/db/commands/mr_test.cpp b/src/mongo/db/commands/mr_test.cpp
index 068699d88b3..8541e5f3f74 100644
--- a/src/mongo/db/commands/mr_test.cpp
+++ b/src/mongo/db/commands/mr_test.cpp
@@ -423,11 +423,11 @@ Status MapReduceCommandTest::_runCommand(StringData mapCode, StringData reduceCo
ASSERT(command) << "Unable to look up mapReduce command";
auto request = OpMsgRequest::fromDBAndBody(inputNss.db(), _makeCmdObj(mapCode, reduceCode));
- BSONObjBuilder result;
- auto success = command->publicRun(_opCtx.get(), request, result);
- if (!success) {
- auto status = getStatusFromCommandResult(result.obj());
- ASSERT_NOT_OK(status);
+ BufBuilder bb;
+ CommandReplyBuilder crb(BSONObjBuilder{bb});
+ command->parse(_opCtx.get(), request)->run(_opCtx.get(), &crb);
+ auto status = getStatusFromCommandResult(crb.getBodyBuilder().asTempObj());
+ if (!status.isOK()) {
return status.withContext(str::stream() << "mapReduce command failed: " << request.body);
}
diff --git a/src/mongo/db/commands/pipeline_command.cpp b/src/mongo/db/commands/pipeline_command.cpp
index 32ede313ee5..c37d19bd12c 100644
--- a/src/mongo/db/commands/pipeline_command.cpp
+++ b/src/mongo/db/commands/pipeline_command.cpp
@@ -96,10 +96,11 @@ public:
}
Status explain(OperationContext* opCtx,
- const std::string& dbname,
- const BSONObj& cmdObj,
+ const OpMsgRequest& request,
ExplainOptions::Verbosity verbosity,
BSONObjBuilder* out) const override {
+ std::string dbname = request.getDatabase().toString();
+ const BSONObj& cmdObj = request.body;
const auto aggregationRequest =
uassertStatusOK(AggregationRequest::parseFromBSON(dbname, cmdObj, verbosity));
diff --git a/src/mongo/db/commands/write_commands/write_commands.cpp b/src/mongo/db/commands/write_commands/write_commands.cpp
index 54410f26c0e..e1730107747 100644
--- a/src/mongo/db/commands/write_commands/write_commands.cpp
+++ b/src/mongo/db/commands/write_commands/write_commands.cpp
@@ -303,11 +303,9 @@ public:
}
Status explain(OperationContext* opCtx,
- const std::string& dbname,
- const BSONObj& cmdObj,
+ const OpMsgRequest& opMsgRequest,
ExplainOptions::Verbosity verbosity,
BSONObjBuilder* out) const final {
- const auto opMsgRequest(OpMsgRequest::fromDBAndBody(dbname, cmdObj));
const auto batch = UpdateOp::parse(opMsgRequest);
uassert(ErrorCodes::InvalidLength,
"explained write batches must be of size 1",
@@ -370,11 +368,9 @@ public:
}
Status explain(OperationContext* opCtx,
- const std::string& dbname,
- const BSONObj& cmdObj,
+ const OpMsgRequest& opMsgRequest,
ExplainOptions::Verbosity verbosity,
BSONObjBuilder* out) const final {
- const auto opMsgRequest(OpMsgRequest::fromDBAndBody(dbname, cmdObj));
const auto batch = DeleteOp::parse(opMsgRequest);
uassert(ErrorCodes::InvalidLength,
"explained write batches must be of size 1",
diff --git a/src/mongo/db/service_entry_point_common.cpp b/src/mongo/db/service_entry_point_common.cpp
index a2389e03e60..fe7595c0565 100644
--- a/src/mongo/db/service_entry_point_common.cpp
+++ b/src/mongo/db/service_entry_point_common.cpp
@@ -320,8 +320,7 @@ void appendReplyMetadata(OperationContext* opCtx,
* Given the specified command, returns an effective read concern which should be used or an error
* if the read concern is not valid for the command.
*/
-StatusWith<repl::ReadConcernArgs> _extractReadConcern(const Command* command,
- const std::string& dbName,
+StatusWith<repl::ReadConcernArgs> _extractReadConcern(const CommandInvocation* invocation,
const BSONObj& cmdObj) {
repl::ReadConcernArgs readConcernArgs;
@@ -330,7 +329,7 @@ StatusWith<repl::ReadConcernArgs> _extractReadConcern(const Command* command,
return readConcernParseStatus;
}
- if (!command->supportsReadConcern(dbName, cmdObj, readConcernArgs.getLevel())) {
+ if (!invocation->supportsReadConcern(readConcernArgs.getLevel())) {
return {ErrorCodes::InvalidOptions,
str::stream() << "Command does not support read concern "
<< readConcernArgs.toString()};
@@ -389,11 +388,12 @@ LogicalTime computeOperationTime(OperationContext* opCtx,
}
bool runCommandImpl(OperationContext* opCtx,
- Command* command,
+ CommandInvocation* invocation,
const OpMsgRequest& request,
rpc::ReplyBuilderInterface* replyBuilder,
LogicalTime startOperationTime,
const ServiceEntryPointCommon::Hooks& behaviors) {
+ const Command* command = invocation->definition();
auto bytesToReserve = command->reserveBytesForReply();
// SERVER-22100: In Windows DEBUG builds, the CRT heap debugging overhead, in conjunction with the
@@ -404,12 +404,11 @@ bool runCommandImpl(OperationContext* opCtx,
bytesToReserve = 0;
#endif
- BSONObjBuilder inPlaceReplyBob = replyBuilder->getInPlaceReplyBuilder(bytesToReserve);
+ CommandReplyBuilder crb(replyBuilder->getInPlaceReplyBuilder(bytesToReserve));
- bool result;
- if (!command->supportsWriteConcern(request.body)) {
+ if (!invocation->supportsWriteConcern()) {
behaviors.uassertCommandDoesNotSpecifyWriteConcern(request.body);
- result = command->publicRun(opCtx, request, inPlaceReplyBob);
+ invocation->run(opCtx, &crb);
} else {
auto wcResult = uassertStatusOK(extractWriteConcern(opCtx, request.body));
@@ -421,10 +420,9 @@ bool runCommandImpl(OperationContext* opCtx,
opCtx->setWriteConcern(wcResult);
ON_BLOCK_EXIT([&] {
behaviors.waitForWriteConcern(
- opCtx, command->getName(), lastOpBeforeRun, &inPlaceReplyBob);
+ opCtx, invocation->definition()->getName(), lastOpBeforeRun, crb.getBodyBuilder());
});
-
- result = command->publicRun(opCtx, request, inPlaceReplyBob);
+ invocation->run(opCtx, &crb);
// Nothing in run() should change the writeConcern.
dassert(SimpleBSONObjComparator::kInstance.evaluate(opCtx->getWriteConcern().toBSON() ==
@@ -433,9 +431,11 @@ bool runCommandImpl(OperationContext* opCtx,
behaviors.waitForLinearizableReadConcern(opCtx);
- CommandHelpers::appendCommandStatus(inPlaceReplyBob, result);
-
- behaviors.attachCurOpErrInfo(opCtx, inPlaceReplyBob);
+ const bool ok = [&] {
+ auto body = crb.getBodyBuilder();
+ return CommandHelpers::extractOrAppendOk(body);
+ }();
+ behaviors.attachCurOpErrInfo(opCtx, crb.getBodyBuilder().asTempObj());
auto operationTime = computeOperationTime(
opCtx, startOperationTime, repl::ReadConcernArgs::get(opCtx).getLevel());
@@ -443,16 +443,15 @@ bool runCommandImpl(OperationContext* opCtx,
// An uninitialized operation time means the cluster time is not propagated, so the operation
// time should not be attached to the response.
if (operationTime != LogicalTime::kUninitialized) {
- operationTime.appendAsOperationTime(&inPlaceReplyBob);
+ auto body = crb.getBodyBuilder();
+ operationTime.appendAsOperationTime(&body);
}
- inPlaceReplyBob.doneFast();
-
BSONObjBuilder metadataBob;
appendReplyMetadata(opCtx, request, &metadataBob);
replyBuilder->setMetadata(metadataBob.done());
- return result;
+ return ok;
}
/**
@@ -467,8 +466,8 @@ void execCommandDatabase(OperationContext* opCtx,
const OpMsgRequest& request,
rpc::ReplyBuilderInterface* replyBuilder,
const ServiceEntryPointCommon::Hooks& behaviors) {
-
auto startOperationTime = getClientOperationTime(opCtx);
+ auto invocation = command->parse(opCtx, request);
try {
{
stdx::lock_guard<Client> lk(*opCtx->getClient());
@@ -621,7 +620,7 @@ void execCommandDatabase(OperationContext* opCtx,
}
auto& readConcernArgs = repl::ReadConcernArgs::get(opCtx);
- readConcernArgs = uassertStatusOK(_extractReadConcern(command, dbname, request.body));
+ readConcernArgs = uassertStatusOK(_extractReadConcern(invocation.get(), request.body));
// TODO SERVER-33354: Remove whitelist.
if (readConcernArgs.getLevel() == repl::ReadConcernLevel::kSnapshotReadConcern) {
@@ -681,11 +680,11 @@ void execCommandDatabase(OperationContext* opCtx,
rpc::TrackingMetadata::get(opCtx).setIsLogged(true);
}
- behaviors.waitForReadConcern(opCtx, command, request);
+ behaviors.waitForReadConcern(opCtx, invocation.get(), request);
sessionTxnState.unstashTransactionResources();
- retval =
- runCommandImpl(opCtx, command, request, replyBuilder, startOperationTime, behaviors);
+ retval = runCommandImpl(
+ opCtx, invocation.get(), request, replyBuilder, startOperationTime, behaviors);
if (retval) {
if (opCtx->getWriteUnitOfWork()) {
@@ -725,7 +724,7 @@ void execCommandDatabase(OperationContext* opCtx,
// Note: the read concern may not have been successfully or yet placed on the opCtx, so
// parsing it separately here.
const std::string db = request.getDatabase().toString();
- auto readConcernArgsStatus = _extractReadConcern(command, db, request.body);
+ auto readConcernArgsStatus = _extractReadConcern(invocation.get(), request.body);
auto operationTime = readConcernArgsStatus.isOK()
? computeOperationTime(
opCtx, startOperationTime, readConcernArgsStatus.getValue().getLevel())
diff --git a/src/mongo/db/service_entry_point_common.h b/src/mongo/db/service_entry_point_common.h
index 73788d9ba66..f90fa0660af 100644
--- a/src/mongo/db/service_entry_point_common.h
+++ b/src/mongo/db/service_entry_point_common.h
@@ -61,18 +61,17 @@ struct ServiceEntryPointCommon {
virtual ~Hooks();
virtual bool lockedForWriting() const = 0;
virtual void waitForReadConcern(OperationContext* opCtx,
- const Command* command,
+ const CommandInvocation* invocation,
const OpMsgRequest& request) const = 0;
virtual void waitForWriteConcern(OperationContext* opCtx,
const std::string& commandName,
const repl::OpTime& lastOpBeforeRun,
- BSONObjBuilder* commandResponseBuilder) const = 0;
+ BSONObjBuilder commandResponseBuilder) const = 0;
virtual void waitForLinearizableReadConcern(OperationContext* opCtx) const = 0;
virtual void uassertCommandDoesNotSpecifyWriteConcern(const BSONObj& cmdObj) const = 0;
- virtual void attachCurOpErrInfo(OperationContext* opCtx,
- BSONObjBuilder& replyObj) const = 0;
+ virtual void attachCurOpErrInfo(OperationContext* opCtx, const BSONObj& replyObj) const = 0;
};
static DbResponse handleRequest(OperationContext* opCtx, const Message& m, const Hooks& hooks);
diff --git a/src/mongo/db/service_entry_point_mongod.cpp b/src/mongo/db/service_entry_point_mongod.cpp
index 84c8534e6fb..eb281981054 100644
--- a/src/mongo/db/service_entry_point_mongod.cpp
+++ b/src/mongo/db/service_entry_point_mongod.cpp
@@ -51,11 +51,10 @@ public:
}
void waitForReadConcern(OperationContext* opCtx,
- const Command* command,
+ const CommandInvocation* invocation,
const OpMsgRequest& request) const override {
- Status rcStatus = mongo::waitForReadConcern(opCtx,
- repl::ReadConcernArgs::get(opCtx),
- command->allowsAfterClusterTime(request.body));
+ Status rcStatus = mongo::waitForReadConcern(
+ opCtx, repl::ReadConcernArgs::get(opCtx), invocation->allowsAfterClusterTime());
if (!rcStatus.isOK()) {
if (rcStatus == ErrorCodes::ExceededTimeLimit) {
@@ -64,7 +63,7 @@ public:
LOG(debugLevel) << "Command on database " << request.getDatabase()
<< " timed out waiting for read concern to be satisfied. Command: "
<< redact(ServiceEntryPointCommon::getRedactedCopyForLogging(
- command, request.body));
+ invocation->definition(), request.body));
}
uassertStatusOK(rcStatus);
@@ -74,7 +73,7 @@ public:
void waitForWriteConcern(OperationContext* opCtx,
const std::string& commandName,
const repl::OpTime& lastOpBeforeRun,
- BSONObjBuilder* commandResponseBuilder) const override {
+ BSONObjBuilder commandResponseBuilder) const override {
auto lastOpAfterRun = repl::ReplClientInfo::forClient(opCtx->getClient()).getLastOp();
// Ensures that if we tried to do a write, we wait for write concern, even if that write was
// a noop.
@@ -88,15 +87,15 @@ public:
auto waitForWCStatus =
mongo::waitForWriteConcern(opCtx, lastOpAfterRun, opCtx->getWriteConcern(), &res);
- CommandHelpers::appendCommandWCStatus(*commandResponseBuilder, waitForWCStatus, res);
+ CommandHelpers::appendCommandWCStatus(commandResponseBuilder, waitForWCStatus, res);
// SERVER-22421: This code is to ensure error response backwards compatibility with the
// user management commands. This can be removed in 3.6.
if (!waitForWCStatus.isOK() && CommandHelpers::isUserManagementCommand(commandName)) {
- BSONObj temp = commandResponseBuilder->asTempObj().copy();
- commandResponseBuilder->resetToEmpty();
- CommandHelpers::appendCommandStatus(*commandResponseBuilder, waitForWCStatus);
- commandResponseBuilder->appendElementsUnique(temp);
+ BSONObj temp = commandResponseBuilder.asTempObj().copy();
+ commandResponseBuilder.resetToEmpty();
+ CommandHelpers::appendCommandStatus(commandResponseBuilder, waitForWCStatus);
+ commandResponseBuilder.appendElementsUnique(temp);
}
}
@@ -115,8 +114,8 @@ public:
}
}
- void attachCurOpErrInfo(OperationContext* opCtx, BSONObjBuilder& replyObj) const override {
- CurOp::get(opCtx)->debug().errInfo = getStatusFromCommandResult(replyObj.asTempObj());
+ void attachCurOpErrInfo(OperationContext* opCtx, const BSONObj& replyObj) const override {
+ CurOp::get(opCtx)->debug().errInfo = getStatusFromCommandResult(replyObj);
}
};
diff --git a/src/mongo/s/commands/cluster_count_cmd.cpp b/src/mongo/s/commands/cluster_count_cmd.cpp
index e5ddda0a004..568e0b40d02 100644
--- a/src/mongo/s/commands/cluster_count_cmd.cpp
+++ b/src/mongo/s/commands/cluster_count_cmd.cpp
@@ -220,10 +220,11 @@ public:
}
Status explain(OperationContext* opCtx,
- const std::string& dbname,
- const BSONObj& cmdObj,
+ const OpMsgRequest& request,
ExplainOptions::Verbosity verbosity,
BSONObjBuilder* out) const override {
+ std::string dbname = request.getDatabase().toString();
+ const BSONObj& cmdObj = request.body;
const NamespaceString nss(parseNs(dbname, cmdObj));
uassert(ErrorCodes::InvalidNamespace,
str::stream() << "Invalid namespace specified '" << nss.ns() << "'",
diff --git a/src/mongo/s/commands/cluster_distinct_cmd.cpp b/src/mongo/s/commands/cluster_distinct_cmd.cpp
index fe77b39aeaa..3d6c302092a 100644
--- a/src/mongo/s/commands/cluster_distinct_cmd.cpp
+++ b/src/mongo/s/commands/cluster_distinct_cmd.cpp
@@ -78,10 +78,11 @@ public:
}
Status explain(OperationContext* opCtx,
- const std::string& dbname,
- const BSONObj& cmdObj,
+ const OpMsgRequest& opMsgRequest,
ExplainOptions::Verbosity verbosity,
BSONObjBuilder* out) const override {
+ std::string dbname = opMsgRequest.getDatabase().toString();
+ const BSONObj& cmdObj = opMsgRequest.body;
const NamespaceString nss(parseNs(dbname, cmdObj));
auto targetingQuery = extractQuery(cmdObj);
diff --git a/src/mongo/s/commands/cluster_explain_cmd.cpp b/src/mongo/s/commands/cluster_explain_cmd.cpp
index 058beee615c..d6b249e7e36 100644
--- a/src/mongo/s/commands/cluster_explain_cmd.cpp
+++ b/src/mongo/s/commands/cluster_explain_cmd.cpp
@@ -147,13 +147,9 @@ public:
}
// Actually call the nested command's explain(...) method.
- Status explainStatus =
- commToExplain->explain(opCtx, dbName, explainObj, verbosity.getValue(), &result);
- if (!explainStatus.isOK()) {
- return CommandHelpers::appendCommandStatus(result, explainStatus);
- }
-
- return true;
+ commToExplain->parse(opCtx, OpMsgRequest{OpMsg{explainObj}})
+ ->explain(opCtx, verbosity.getValue(), &result);
+ return CommandHelpers::extractOrAppendOk(result);
}
} cmdExplainCluster;
diff --git a/src/mongo/s/commands/cluster_find_and_modify_cmd.cpp b/src/mongo/s/commands/cluster_find_and_modify_cmd.cpp
index 8a6799d8264..744b2e0feb1 100644
--- a/src/mongo/s/commands/cluster_find_and_modify_cmd.cpp
+++ b/src/mongo/s/commands/cluster_find_and_modify_cmd.cpp
@@ -97,10 +97,11 @@ public:
}
Status explain(OperationContext* opCtx,
- const std::string& dbName,
- const BSONObj& cmdObj,
+ const OpMsgRequest& request,
ExplainOptions::Verbosity verbosity,
BSONObjBuilder* out) const override {
+ std::string dbName = request.getDatabase().toString();
+ const BSONObj& cmdObj = request.body;
const NamespaceString nss(CommandHelpers::parseNsCollectionRequired(dbName, cmdObj));
auto routingInfo =
diff --git a/src/mongo/s/commands/cluster_find_cmd.cpp b/src/mongo/s/commands/cluster_find_cmd.cpp
index 65e26656b33..fca5dedd52c 100644
--- a/src/mongo/s/commands/cluster_find_cmd.cpp
+++ b/src/mongo/s/commands/cluster_find_cmd.cpp
@@ -98,10 +98,11 @@ public:
}
Status explain(OperationContext* opCtx,
- const std::string& dbname,
- const BSONObj& cmdObj,
+ const OpMsgRequest& request,
ExplainOptions::Verbosity verbosity,
BSONObjBuilder* out) const final {
+ std::string dbname = request.getDatabase().toString();
+ const BSONObj& cmdObj = request.body;
const NamespaceString nss(CommandHelpers::parseNsCollectionRequired(dbname, cmdObj));
// Parse the command BSON to a QueryRequest.
bool isExplain = true;
diff --git a/src/mongo/s/commands/cluster_pipeline_cmd.cpp b/src/mongo/s/commands/cluster_pipeline_cmd.cpp
index 600a0e558c3..816c94ec087 100644
--- a/src/mongo/s/commands/cluster_pipeline_cmd.cpp
+++ b/src/mongo/s/commands/cluster_pipeline_cmd.cpp
@@ -75,10 +75,11 @@ public:
}
Status explain(OperationContext* opCtx,
- const std::string& dbname,
- const BSONObj& cmdObj,
+ const OpMsgRequest& request,
ExplainOptions::Verbosity verbosity,
BSONObjBuilder* out) const override {
+ std::string dbname = request.getDatabase().toString();
+ const BSONObj& cmdObj = request.body;
return _runAggCommand(opCtx, dbname, cmdObj, verbosity, out);
}
diff --git a/src/mongo/s/commands/cluster_write_cmd.cpp b/src/mongo/s/commands/cluster_write_cmd.cpp
index 516cc2d7b82..11130f2d4ec 100644
--- a/src/mongo/s/commands/cluster_write_cmd.cpp
+++ b/src/mongo/s/commands/cluster_write_cmd.cpp
@@ -160,14 +160,9 @@ public:
}
Status explain(OperationContext* opCtx,
- const std::string& dbname,
- const BSONObj& cmdObj,
+ const OpMsgRequest& request,
ExplainOptions::Verbosity verbosity,
BSONObjBuilder* out) const final {
- OpMsgRequest request;
- request.body = cmdObj;
- invariant(request.getDatabase() == dbname); // Ensured by explain command's run() method.
-
const auto batchedRequest(parseRequest(_writeType, request));
// We can only explain write batches of size 1.
@@ -175,7 +170,7 @@ public:
return Status(ErrorCodes::InvalidLength, "explained write batches must be of size 1");
}
- const auto explainCmd = ClusterExplain::wrapAsExplain(cmdObj, verbosity);
+ const auto explainCmd = ClusterExplain::wrapAsExplain(request.body, verbosity);
// We will time how long it takes to run the commands on the shards.
Timer timer;
@@ -183,8 +178,8 @@ public:
// Target the command to the shards based on the singleton batch item.
BatchItemRef targetingBatchItem(&batchedRequest, 0);
std::vector<Strategy::CommandResult> shardResults;
- Status status =
- _commandOpWrite(opCtx, dbname, explainCmd, targetingBatchItem, &shardResults);
+ Status status = _commandOpWrite(
+ opCtx, request.getDatabase().toString(), explainCmd, targetingBatchItem, &shardResults);
if (!status.isOK()) {
return status;
}
diff --git a/src/mongo/s/commands/commands_public.cpp b/src/mongo/s/commands/commands_public.cpp
index c979710fbc9..7069833071f 100644
--- a/src/mongo/s/commands/commands_public.cpp
+++ b/src/mongo/s/commands/commands_public.cpp
@@ -375,10 +375,11 @@ public:
}
Status explain(OperationContext* opCtx,
- const std::string& dbname,
- const BSONObj& cmdObj,
+ const OpMsgRequest& request,
ExplainOptions::Verbosity verbosity,
BSONObjBuilder* out) const override {
+ std::string dbname = request.getDatabase().toString();
+ const BSONObj& cmdObj = request.body;
// We will time how long it takes to run the commands on the shards.
Timer timer;
BSONObj command = ClusterExplain::wrapAsExplain(cmdObj, verbosity);
diff --git a/src/mongo/s/commands/strategy.cpp b/src/mongo/s/commands/strategy.cpp
index 20e01646cf6..65c9c863094 100644
--- a/src/mongo/s/commands/strategy.cpp
+++ b/src/mongo/s/commands/strategy.cpp
@@ -145,8 +145,11 @@ void appendRequiredFieldsToResponse(OperationContext* opCtx, BSONObjBuilder* res
void execCommandClient(OperationContext* opCtx,
Command* c,
const OpMsgRequest& request,
- BSONObjBuilder& result) {
- ON_BLOCK_EXIT([opCtx, &result] { appendRequiredFieldsToResponse(opCtx, &result); });
+ CommandReplyBuilder* result) {
+ ON_BLOCK_EXIT([opCtx, &result] {
+ auto body = result->getBodyBuilder();
+ appendRequiredFieldsToResponse(opCtx, &body);
+ });
const auto dbname = request.getDatabase();
uassert(ErrorCodes::IllegalOperation,
@@ -161,10 +164,10 @@ void execCommandClient(OperationContext* opCtx,
StringData fieldName = element.fieldNameStringData();
if (fieldName == "help" && element.type() == Bool && element.Bool()) {
std::stringstream help;
- help << "help for: " << c->getName() << " ";
- help << c->help();
- result.append("help", help.str());
- CommandHelpers::appendCommandStatus(result, true, "");
+ help << "help for: " << c->getName() << " " << c->help();
+ auto body = result->getBodyBuilder();
+ body.append("help", help.str());
+ CommandHelpers::appendCommandStatus(body, true, "");
return;
}
@@ -176,7 +179,8 @@ void execCommandClient(OperationContext* opCtx,
Status status = Command::checkAuthorization(c, opCtx, request);
if (!status.isOK()) {
- CommandHelpers::appendCommandStatus(result, status);
+ auto body = result->getBodyBuilder();
+ CommandHelpers::appendCommandStatus(body, status);
return;
}
@@ -189,29 +193,35 @@ void execCommandClient(OperationContext* opCtx,
StatusWith<WriteConcernOptions> wcResult =
WriteConcernOptions::extractWCFromCommand(request.body);
if (!wcResult.isOK()) {
- CommandHelpers::appendCommandStatus(result, wcResult.getStatus());
+ auto body = result->getBodyBuilder();
+ CommandHelpers::appendCommandStatus(body, wcResult.getStatus());
return;
}
- bool supportsWriteConcern = c->supportsWriteConcern(request.body);
+ auto invocation = c->parse(opCtx, request);
+
+ bool supportsWriteConcern = invocation->supportsWriteConcern();
if (!supportsWriteConcern && !wcResult.getValue().usedDefault) {
// This command doesn't do writes so it should not be passed a writeConcern.
// If we did not use the default writeConcern, one was provided when it shouldn't have
// been by the user.
+ auto body = result->getBodyBuilder();
CommandHelpers::appendCommandStatus(
- result, Status(ErrorCodes::InvalidOptions, "Command does not support writeConcern"));
+ body, Status(ErrorCodes::InvalidOptions, "Command does not support writeConcern"));
return;
}
repl::ReadConcernArgs readConcernArgs;
auto readConcernParseStatus = readConcernArgs.initialize(request.body);
if (!readConcernParseStatus.isOK()) {
- CommandHelpers::appendCommandStatus(result, readConcernParseStatus);
+ auto body = result->getBodyBuilder();
+ CommandHelpers::appendCommandStatus(body, readConcernParseStatus);
return;
}
if (readConcernArgs.getLevel() == repl::ReadConcernLevel::kSnapshotReadConcern) {
+ auto body = result->getBodyBuilder();
CommandHelpers::appendCommandStatus(
- result,
+ body,
Status(ErrorCodes::InvalidOptions, "read concern snapshot is not supported on mongos"));
return;
}
@@ -223,25 +233,26 @@ void execCommandClient(OperationContext* opCtx,
auto metadataStatus = processCommandMetadata(opCtx, request.body);
if (!metadataStatus.isOK()) {
- CommandHelpers::appendCommandStatus(result, metadataStatus);
+ auto body = result->getBodyBuilder();
+ CommandHelpers::appendCommandStatus(body, metadataStatus);
return;
}
- bool ok = false;
if (!supportsWriteConcern) {
- ok = c->publicRun(opCtx, request, result);
+ invocation->run(opCtx, result);
} else {
// Change the write concern while running the command.
const auto oldWC = opCtx->getWriteConcern();
ON_BLOCK_EXIT([&] { opCtx->setWriteConcern(oldWC); });
opCtx->setWriteConcern(wcResult.getValue());
- ok = c->publicRun(opCtx, request, result);
+ invocation->run(opCtx, result);
}
+ auto body = result->getBodyBuilder();
+ bool ok = CommandHelpers::extractOrAppendOk(body);
if (!ok) {
c->incrementCommandsFailed();
}
- CommandHelpers::appendCommandStatus(result, ok);
}
void runCommand(OperationContext* opCtx, const OpMsgRequest& request, BSONObjBuilder&& builder) {
@@ -270,12 +281,13 @@ void runCommand(OperationContext* opCtx, const OpMsgRequest& request, BSONObjBui
initializeOperationSessionInfo(opCtx, request.body, command->requiresAuth(), true, true);
- int loops = 5;
+ CommandReplyBuilder crb(std::move(builder));
+ int loops = 5;
while (true) {
- builder.resetToEmpty();
+ crb.reset();
try {
- execCommandClient(opCtx, command, request, builder);
+ execCommandClient(opCtx, command, request, &crb);
return;
} catch (const StaleConfigException& e) {
if (e->getns().empty()) {
@@ -301,10 +313,11 @@ void runCommand(OperationContext* opCtx, const OpMsgRequest& request, BSONObjBui
continue;
} catch (const DBException& e) {
- ON_BLOCK_EXIT([opCtx, &builder] { appendRequiredFieldsToResponse(opCtx, &builder); });
- builder.resetToEmpty();
+ crb.reset();
+ BSONObjBuilder bob = crb.getBodyBuilder();
+ ON_BLOCK_EXIT([&] { appendRequiredFieldsToResponse(opCtx, &bob); });
command->incrementCommandsFailed();
- CommandHelpers::appendCommandStatus(builder, e.toStatus());
+ CommandHelpers::appendCommandStatus(bob, e.toStatus());
LastError::get(opCtx->getClient()).setLastError(e.code(), e.reason());
return;
}
@@ -577,6 +590,7 @@ void Strategy::killCursors(OperationContext* opCtx, DbMessage* dbm) {
}
void Strategy::writeOp(OperationContext* opCtx, DbMessage* dbm) {
+ BufBuilder bb;
runCommand(opCtx,
[&]() {
const auto& msg = dbm->msg();
@@ -595,7 +609,7 @@ void Strategy::writeOp(OperationContext* opCtx, DbMessage* dbm) {
MONGO_UNREACHABLE;
}
}(),
- BSONObjBuilder());
+ BSONObjBuilder{bb}); // built object is ignored
}
void Strategy::explainFind(OperationContext* opCtx,