summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorNick Zolnierz <nicholas.zolnierz@mongodb.com>2018-03-05 09:49:29 -0500
committerNick Zolnierz <nicholas.zolnierz@mongodb.com>2018-03-05 12:23:39 -0500
commit74177edb35b6ce7594e4751051010ceade592484 (patch)
tree3dad385af5647a24291e8ab26ca3fdcf164e5dfd /src
parent9a364b4c60a3f249947468a918071e3bf586dabc (diff)
downloadmongo-74177edb35b6ce7594e4751051010ceade592484.tar.gz
Revert "SERVER-33065 CommandReplyBuilder and CommandInvocation"
This reverts commit ad94e51e0dd40b0d0c38215a36caf75a4be48415.
Diffstat (limited to 'src')
-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/repl/oplog.cpp4
-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
26 files changed, 236 insertions, 455 deletions
diff --git a/src/mongo/bson/bsonobjbuilder.h b/src/mongo/bson/bsonobjbuilder.h
index c8aed97e15b..38dae8234fa 100644
--- a/src/mongo/bson/bsonobjbuilder.h
+++ b/src/mongo/bson/bsonobjbuilder.h
@@ -172,14 +172,6 @@ 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 2a0e2ecb3b3..850ce966b1f 100644
--- a/src/mongo/client/embedded/service_entry_point_embedded.cpp
+++ b/src/mongo/client/embedded/service_entry_point_embedded.cpp
@@ -91,20 +91,19 @@ public:
return false;
}
- void waitForReadConcern(OperationContext*,
- const CommandInvocation*,
- const OpMsgRequest&) const override {}
+ void waitForReadConcern(OperationContext*, const Command*, 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*, const BSONObj&) const override {}
+ void attachCurOpErrInfo(OperationContext*, BSONObjBuilder&) 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 0d8170618d4..2aaeda9b5ed 100644
--- a/src/mongo/db/commands.cpp
+++ b/src/mongo/db/commands.cpp
@@ -71,23 +71,6 @@ 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
@@ -97,22 +80,18 @@ private:
BSONObj CommandHelpers::runCommandDirectly(OperationContext* opCtx, const OpMsgRequest& request) {
auto command = globalCommandRegistry()->findCommand(request.getCommandName());
invariant(command);
- BufBuilder bb;
- CommandReplyBuilder crb(BSONObjBuilder{bb});
+ BSONObjBuilder out;
try {
- auto invocation = command->parse(opCtx, request);
- invocation->run(opCtx, &crb);
- auto body = crb.getBodyBuilder();
- CommandHelpers::extractOrAppendOk(body);
+ bool ok = command->publicRun(opCtx, request, out);
+ appendCommandStatus(out, ok);
} catch (const StaleConfigException&) {
// These exceptions are intended to be handled at a higher level.
throw;
} catch (const DBException& ex) {
- auto body = crb.getBodyBuilder();
- body.resetToEmpty();
- appendCommandStatus(body, ex.toStatus());
+ out.resetToEmpty();
+ appendCommandStatus(out, ex.toStatus());
}
- return BSONObj(bb.release());
+ return out.obj();
}
std::string CommandHelpers::parseNsFullyQualified(StringData dbname, const BSONObj& cmdObj) {
@@ -188,16 +167,6 @@ 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) {
@@ -337,99 +306,10 @@ 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)
@@ -455,7 +335,8 @@ Command::Command(StringData name, StringData oldName)
}
Status Command::explain(OperationContext* opCtx,
- const OpMsgRequest& request,
+ const std::string& dbname,
+ const BSONObj& cmdObj,
ExplainOptions::Verbosity verbosity,
BSONObjBuilder* out) const {
return {ErrorCodes::IllegalOperation, str::stream() << "Cannot explain cmd: " << getName()};
@@ -515,6 +396,25 @@ 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) {
@@ -527,6 +427,21 @@ 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 218e2070204..e9516c75f29 100644
--- a/src/mongo/db/commands.h
+++ b/src/mongo/db/commands.h
@@ -82,13 +82,6 @@ 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.
*
@@ -193,8 +186,6 @@ struct CommandHelpers {
static constexpr StringData kHelpFieldName = "help"_sd;
};
-class CommandInvocation;
-
/**
* Serves as a base for server commands. See the constructor for more details.
*/
@@ -215,9 +206,6 @@ 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.
*/
@@ -251,6 +239,16 @@ 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 {
@@ -268,7 +266,7 @@ public:
return false;
}
- virtual AllowedOnSecondary secondaryAllowed(ServiceContext* context) const = 0;
+ virtual AllowedOnSecondary secondaryAllowed(ServiceContext*) const = 0;
/**
* Override and return fales if the command opcounters should not be incremented on
@@ -293,6 +291,23 @@ 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.
*/
@@ -327,6 +342,33 @@ 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 {
@@ -359,6 +401,13 @@ 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.
*/
@@ -406,62 +455,6 @@ 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.
*
@@ -486,118 +479,6 @@ 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 a91561f6da6..d2e35f86fd6 100644
--- a/src/mongo/db/commands/count_cmd.cpp
+++ b/src/mongo/db/commands/count_cmd.cpp
@@ -110,11 +110,10 @@ public:
}
Status explain(OperationContext* opCtx,
- const OpMsgRequest& opMsgRequest,
+ const std::string& dbname,
+ const BSONObj& cmdObj,
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 ae99b5a2332..eba4fdd51bc 100644
--- a/src/mongo/db/commands/distinct.cpp
+++ b/src/mongo/db/commands/distinct.cpp
@@ -110,11 +110,10 @@ public:
}
Status explain(OperationContext* opCtx,
- const OpMsgRequest& request,
+ const std::string& dbname,
+ const BSONObj& cmdObj,
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 7cdd1441d3f..8f0ce39a66a 100644
--- a/src/mongo/db/commands/explain_cmd.cpp
+++ b/src/mongo/db/commands/explain_cmd.cpp
@@ -154,9 +154,13 @@ public:
}
// Actually call the nested command's explain(...) method.
- commToExplain->parse(opCtx, OpMsgRequest::fromDBAndBody(dbname, explainObj))
- ->explain(opCtx, verbosity.getValue(), &result);
- return CommandHelpers::extractOrAppendOk(result);
+ Status explainStatus =
+ commToExplain->explain(opCtx, dbname, explainObj, verbosity.getValue(), &result);
+ if (!explainStatus.isOK()) {
+ return CommandHelpers::appendCommandStatus(result, explainStatus);
+ }
+
+ return true;
}
} cmdExplain;
diff --git a/src/mongo/db/commands/find_and_modify.cpp b/src/mongo/db/commands/find_and_modify.cpp
index 9757e8e450d..e836e7aa737 100644
--- a/src/mongo/db/commands/find_and_modify.cpp
+++ b/src/mongo/db/commands/find_and_modify.cpp
@@ -246,11 +246,10 @@ public:
}
Status explain(OperationContext* opCtx,
- const OpMsgRequest& request,
+ const std::string& dbName,
+ const BSONObj& cmdObj,
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 7cd41a2a6a8..451171fc348 100644
--- a/src/mongo/db/commands/find_cmd.cpp
+++ b/src/mongo/db/commands/find_cmd.cpp
@@ -126,11 +126,10 @@ public:
}
Status explain(OperationContext* opCtx,
- const OpMsgRequest& request,
+ const std::string& dbname,
+ const BSONObj& cmdObj,
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 c4c67983ed8..547dee36087 100644
--- a/src/mongo/db/commands/group_cmd.cpp
+++ b/src/mongo/db/commands/group_cmd.cpp
@@ -117,12 +117,11 @@ private:
return nss.ns();
}
- 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;
+ virtual Status explain(OperationContext* opCtx,
+ const std::string& dbname,
+ const BSONObj& cmdObj,
+ ExplainOptions::Verbosity verbosity,
+ BSONObjBuilder* out) const {
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 8541e5f3f74..068699d88b3 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));
- BufBuilder bb;
- CommandReplyBuilder crb(BSONObjBuilder{bb});
- command->parse(_opCtx.get(), request)->run(_opCtx.get(), &crb);
- auto status = getStatusFromCommandResult(crb.getBodyBuilder().asTempObj());
- if (!status.isOK()) {
+ BSONObjBuilder result;
+ auto success = command->publicRun(_opCtx.get(), request, result);
+ if (!success) {
+ auto status = getStatusFromCommandResult(result.obj());
+ ASSERT_NOT_OK(status);
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 c37d19bd12c..32ede313ee5 100644
--- a/src/mongo/db/commands/pipeline_command.cpp
+++ b/src/mongo/db/commands/pipeline_command.cpp
@@ -96,11 +96,10 @@ public:
}
Status explain(OperationContext* opCtx,
- const OpMsgRequest& request,
+ const std::string& dbname,
+ const BSONObj& cmdObj,
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 e1730107747..54410f26c0e 100644
--- a/src/mongo/db/commands/write_commands/write_commands.cpp
+++ b/src/mongo/db/commands/write_commands/write_commands.cpp
@@ -303,9 +303,11 @@ public:
}
Status explain(OperationContext* opCtx,
- const OpMsgRequest& opMsgRequest,
+ const std::string& dbname,
+ const BSONObj& cmdObj,
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",
@@ -368,9 +370,11 @@ public:
}
Status explain(OperationContext* opCtx,
- const OpMsgRequest& opMsgRequest,
+ const std::string& dbname,
+ const BSONObj& cmdObj,
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/repl/oplog.cpp b/src/mongo/db/repl/oplog.cpp
index 4beec73c4d0..84fdf55dbe4 100644
--- a/src/mongo/db/repl/oplog.cpp
+++ b/src/mongo/db/repl/oplog.cpp
@@ -392,11 +392,11 @@ void _logOpsInner(OperationContext* opCtx,
checkOplogInsert(oplogCollection->insertDocumentsForOplog(opCtx, writers, timestamps, nDocs));
// Set replCoord last optime only after we're sure the WUOW didn't abort and roll back.
- opCtx->recoveryUnit()->onCommit([replCoord, finalOpTime] {
+ opCtx->recoveryUnit()->onCommit([opCtx, replCoord, finalOpTime] {
// Optimes on the primary should always represent consistent database states.
replCoord->setMyLastAppliedOpTimeForward(
finalOpTime, ReplicationCoordinator::DataConsistency::Consistent);
- ReplClientInfo::forClient(Client::getCurrent()).setLastOp(finalOpTime);
+ ReplClientInfo::forClient(opCtx->getClient()).setLastOp(finalOpTime);
});
}
diff --git a/src/mongo/db/service_entry_point_common.cpp b/src/mongo/db/service_entry_point_common.cpp
index 52623b256a9..38cf2cffda1 100644
--- a/src/mongo/db/service_entry_point_common.cpp
+++ b/src/mongo/db/service_entry_point_common.cpp
@@ -319,7 +319,8 @@ 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 CommandInvocation* invocation,
+StatusWith<repl::ReadConcernArgs> _extractReadConcern(const Command* command,
+ const std::string& dbName,
const BSONObj& cmdObj) {
repl::ReadConcernArgs readConcernArgs;
@@ -328,7 +329,7 @@ StatusWith<repl::ReadConcernArgs> _extractReadConcern(const CommandInvocation* i
return readConcernParseStatus;
}
- if (!invocation->supportsReadConcern(readConcernArgs.getLevel())) {
+ if (!command->supportsReadConcern(dbName, cmdObj, readConcernArgs.getLevel())) {
return {ErrorCodes::InvalidOptions,
str::stream() << "Command does not support read concern "
<< readConcernArgs.toString()};
@@ -387,12 +388,11 @@ LogicalTime computeOperationTime(OperationContext* opCtx,
}
bool runCommandImpl(OperationContext* opCtx,
- CommandInvocation* invocation,
+ Command* command,
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
@@ -403,11 +403,12 @@ bool runCommandImpl(OperationContext* opCtx,
bytesToReserve = 0;
#endif
- CommandReplyBuilder crb(replyBuilder->getInPlaceReplyBuilder(bytesToReserve));
+ BSONObjBuilder inPlaceReplyBob = replyBuilder->getInPlaceReplyBuilder(bytesToReserve);
- if (!invocation->supportsWriteConcern()) {
+ bool result;
+ if (!command->supportsWriteConcern(request.body)) {
behaviors.uassertCommandDoesNotSpecifyWriteConcern(request.body);
- invocation->run(opCtx, &crb);
+ result = command->publicRun(opCtx, request, inPlaceReplyBob);
} else {
auto wcResult = uassertStatusOK(extractWriteConcern(opCtx, request.body));
@@ -419,9 +420,10 @@ bool runCommandImpl(OperationContext* opCtx,
opCtx->setWriteConcern(wcResult);
ON_BLOCK_EXIT([&] {
behaviors.waitForWriteConcern(
- opCtx, invocation->definition()->getName(), lastOpBeforeRun, crb.getBodyBuilder());
+ opCtx, command->getName(), lastOpBeforeRun, &inPlaceReplyBob);
});
- invocation->run(opCtx, &crb);
+
+ result = command->publicRun(opCtx, request, inPlaceReplyBob);
// Nothing in run() should change the writeConcern.
dassert(SimpleBSONObjComparator::kInstance.evaluate(opCtx->getWriteConcern().toBSON() ==
@@ -430,11 +432,9 @@ bool runCommandImpl(OperationContext* opCtx,
behaviors.waitForLinearizableReadConcern(opCtx);
- const bool ok = [&] {
- auto body = crb.getBodyBuilder();
- return CommandHelpers::extractOrAppendOk(body);
- }();
- behaviors.attachCurOpErrInfo(opCtx, crb.getBodyBuilder().asTempObj());
+ CommandHelpers::appendCommandStatus(inPlaceReplyBob, result);
+
+ behaviors.attachCurOpErrInfo(opCtx, inPlaceReplyBob);
auto operationTime = computeOperationTime(
opCtx, startOperationTime, repl::ReadConcernArgs::get(opCtx).getLevel());
@@ -442,15 +442,16 @@ 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) {
- auto body = crb.getBodyBuilder();
- operationTime.appendAsOperationTime(&body);
+ operationTime.appendAsOperationTime(&inPlaceReplyBob);
}
+ inPlaceReplyBob.doneFast();
+
BSONObjBuilder metadataBob;
appendReplyMetadata(opCtx, request, &metadataBob);
replyBuilder->setMetadata(metadataBob.done());
- return ok;
+ return result;
}
/**
@@ -465,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());
@@ -619,7 +620,7 @@ void execCommandDatabase(OperationContext* opCtx,
}
auto& readConcernArgs = repl::ReadConcernArgs::get(opCtx);
- readConcernArgs = uassertStatusOK(_extractReadConcern(invocation.get(), request.body));
+ readConcernArgs = uassertStatusOK(_extractReadConcern(command, dbname, request.body));
// TODO SERVER-33354: Remove whitelist.
if (readConcernArgs.getLevel() == repl::ReadConcernLevel::kSnapshotReadConcern) {
@@ -679,11 +680,11 @@ void execCommandDatabase(OperationContext* opCtx,
rpc::TrackingMetadata::get(opCtx).setIsLogged(true);
}
- behaviors.waitForReadConcern(opCtx, invocation.get(), request);
+ behaviors.waitForReadConcern(opCtx, command, request);
sessionTxnState.unstashTransactionResources();
- retval = runCommandImpl(
- opCtx, invocation.get(), request, replyBuilder, startOperationTime, behaviors);
+ retval =
+ runCommandImpl(opCtx, command, request, replyBuilder, startOperationTime, behaviors);
if (retval) {
if (opCtx->getWriteUnitOfWork()) {
@@ -716,7 +717,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(invocation.get(), request.body);
+ auto readConcernArgsStatus = _extractReadConcern(command, db, 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 f90fa0660af..73788d9ba66 100644
--- a/src/mongo/db/service_entry_point_common.h
+++ b/src/mongo/db/service_entry_point_common.h
@@ -61,17 +61,18 @@ struct ServiceEntryPointCommon {
virtual ~Hooks();
virtual bool lockedForWriting() const = 0;
virtual void waitForReadConcern(OperationContext* opCtx,
- const CommandInvocation* invocation,
+ const Command* command,
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, const BSONObj& replyObj) const = 0;
+ virtual void attachCurOpErrInfo(OperationContext* opCtx,
+ BSONObjBuilder& 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 eb281981054..84c8534e6fb 100644
--- a/src/mongo/db/service_entry_point_mongod.cpp
+++ b/src/mongo/db/service_entry_point_mongod.cpp
@@ -51,10 +51,11 @@ public:
}
void waitForReadConcern(OperationContext* opCtx,
- const CommandInvocation* invocation,
+ const Command* command,
const OpMsgRequest& request) const override {
- Status rcStatus = mongo::waitForReadConcern(
- opCtx, repl::ReadConcernArgs::get(opCtx), invocation->allowsAfterClusterTime());
+ Status rcStatus = mongo::waitForReadConcern(opCtx,
+ repl::ReadConcernArgs::get(opCtx),
+ command->allowsAfterClusterTime(request.body));
if (!rcStatus.isOK()) {
if (rcStatus == ErrorCodes::ExceededTimeLimit) {
@@ -63,7 +64,7 @@ public:
LOG(debugLevel) << "Command on database " << request.getDatabase()
<< " timed out waiting for read concern to be satisfied. Command: "
<< redact(ServiceEntryPointCommon::getRedactedCopyForLogging(
- invocation->definition(), request.body));
+ command, request.body));
}
uassertStatusOK(rcStatus);
@@ -73,7 +74,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.
@@ -87,15 +88,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);
}
}
@@ -114,8 +115,8 @@ public:
}
}
- void attachCurOpErrInfo(OperationContext* opCtx, const BSONObj& replyObj) const override {
- CurOp::get(opCtx)->debug().errInfo = getStatusFromCommandResult(replyObj);
+ void attachCurOpErrInfo(OperationContext* opCtx, BSONObjBuilder& replyObj) const override {
+ CurOp::get(opCtx)->debug().errInfo = getStatusFromCommandResult(replyObj.asTempObj());
}
};
diff --git a/src/mongo/s/commands/cluster_count_cmd.cpp b/src/mongo/s/commands/cluster_count_cmd.cpp
index bac5ee5d969..794cf1d9fb3 100644
--- a/src/mongo/s/commands/cluster_count_cmd.cpp
+++ b/src/mongo/s/commands/cluster_count_cmd.cpp
@@ -218,11 +218,10 @@ public:
}
Status explain(OperationContext* opCtx,
- const OpMsgRequest& request,
+ const std::string& dbname,
+ const BSONObj& cmdObj,
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 9787476f035..1fb071ea75c 100644
--- a/src/mongo/s/commands/cluster_distinct_cmd.cpp
+++ b/src/mongo/s/commands/cluster_distinct_cmd.cpp
@@ -78,11 +78,10 @@ public:
}
Status explain(OperationContext* opCtx,
- const OpMsgRequest& opMsgRequest,
+ const std::string& dbname,
+ const BSONObj& cmdObj,
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 d6b249e7e36..058beee615c 100644
--- a/src/mongo/s/commands/cluster_explain_cmd.cpp
+++ b/src/mongo/s/commands/cluster_explain_cmd.cpp
@@ -147,9 +147,13 @@ public:
}
// Actually call the nested command's explain(...) method.
- commToExplain->parse(opCtx, OpMsgRequest{OpMsg{explainObj}})
- ->explain(opCtx, verbosity.getValue(), &result);
- return CommandHelpers::extractOrAppendOk(result);
+ Status explainStatus =
+ commToExplain->explain(opCtx, dbName, explainObj, verbosity.getValue(), &result);
+ if (!explainStatus.isOK()) {
+ return CommandHelpers::appendCommandStatus(result, explainStatus);
+ }
+
+ return true;
}
} 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 744b2e0feb1..8a6799d8264 100644
--- a/src/mongo/s/commands/cluster_find_and_modify_cmd.cpp
+++ b/src/mongo/s/commands/cluster_find_and_modify_cmd.cpp
@@ -97,11 +97,10 @@ public:
}
Status explain(OperationContext* opCtx,
- const OpMsgRequest& request,
+ const std::string& dbName,
+ const BSONObj& cmdObj,
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 fca5dedd52c..65e26656b33 100644
--- a/src/mongo/s/commands/cluster_find_cmd.cpp
+++ b/src/mongo/s/commands/cluster_find_cmd.cpp
@@ -98,11 +98,10 @@ public:
}
Status explain(OperationContext* opCtx,
- const OpMsgRequest& request,
+ const std::string& dbname,
+ const BSONObj& cmdObj,
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 816c94ec087..600a0e558c3 100644
--- a/src/mongo/s/commands/cluster_pipeline_cmd.cpp
+++ b/src/mongo/s/commands/cluster_pipeline_cmd.cpp
@@ -75,11 +75,10 @@ public:
}
Status explain(OperationContext* opCtx,
- const OpMsgRequest& request,
+ const std::string& dbname,
+ const BSONObj& cmdObj,
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 11130f2d4ec..516cc2d7b82 100644
--- a/src/mongo/s/commands/cluster_write_cmd.cpp
+++ b/src/mongo/s/commands/cluster_write_cmd.cpp
@@ -160,9 +160,14 @@ public:
}
Status explain(OperationContext* opCtx,
- const OpMsgRequest& request,
+ const std::string& dbname,
+ const BSONObj& cmdObj,
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.
@@ -170,7 +175,7 @@ public:
return Status(ErrorCodes::InvalidLength, "explained write batches must be of size 1");
}
- const auto explainCmd = ClusterExplain::wrapAsExplain(request.body, verbosity);
+ const auto explainCmd = ClusterExplain::wrapAsExplain(cmdObj, verbosity);
// We will time how long it takes to run the commands on the shards.
Timer timer;
@@ -178,8 +183,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, request.getDatabase().toString(), explainCmd, targetingBatchItem, &shardResults);
+ Status status =
+ _commandOpWrite(opCtx, dbname, 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 0188f00f132..54499f66924 100644
--- a/src/mongo/s/commands/commands_public.cpp
+++ b/src/mongo/s/commands/commands_public.cpp
@@ -626,11 +626,10 @@ public:
}
Status explain(OperationContext* opCtx,
- const OpMsgRequest& request,
+ const std::string& dbname,
+ const BSONObj& cmdObj,
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 c9e9276764d..6a10b1b6856 100644
--- a/src/mongo/s/commands/strategy.cpp
+++ b/src/mongo/s/commands/strategy.cpp
@@ -145,11 +145,8 @@ void appendRequiredFieldsToResponse(OperationContext* opCtx, BSONObjBuilder* res
void execCommandClient(OperationContext* opCtx,
Command* c,
const OpMsgRequest& request,
- CommandReplyBuilder* result) {
- ON_BLOCK_EXIT([opCtx, &result] {
- auto body = result->getBodyBuilder();
- appendRequiredFieldsToResponse(opCtx, &body);
- });
+ BSONObjBuilder& result) {
+ ON_BLOCK_EXIT([opCtx, &result] { appendRequiredFieldsToResponse(opCtx, &result); });
const auto dbname = request.getDatabase();
uassert(ErrorCodes::IllegalOperation,
@@ -164,10 +161,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() << " " << c->help();
- auto body = result->getBodyBuilder();
- body.append("help", help.str());
- CommandHelpers::appendCommandStatus(body, true, "");
+ help << "help for: " << c->getName() << " ";
+ help << c->help();
+ result.append("help", help.str());
+ CommandHelpers::appendCommandStatus(result, true, "");
return;
}
@@ -179,8 +176,7 @@ void execCommandClient(OperationContext* opCtx,
Status status = Command::checkAuthorization(c, opCtx, request);
if (!status.isOK()) {
- auto body = result->getBodyBuilder();
- CommandHelpers::appendCommandStatus(body, status);
+ CommandHelpers::appendCommandStatus(result, status);
return;
}
@@ -193,35 +189,29 @@ void execCommandClient(OperationContext* opCtx,
StatusWith<WriteConcernOptions> wcResult =
WriteConcernOptions::extractWCFromCommand(request.body);
if (!wcResult.isOK()) {
- auto body = result->getBodyBuilder();
- CommandHelpers::appendCommandStatus(body, wcResult.getStatus());
+ CommandHelpers::appendCommandStatus(result, wcResult.getStatus());
return;
}
- auto invocation = c->parse(opCtx, request);
-
- bool supportsWriteConcern = invocation->supportsWriteConcern();
+ bool supportsWriteConcern = c->supportsWriteConcern(request.body);
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(
- body, Status(ErrorCodes::InvalidOptions, "Command does not support writeConcern"));
+ result, Status(ErrorCodes::InvalidOptions, "Command does not support writeConcern"));
return;
}
repl::ReadConcernArgs readConcernArgs;
auto readConcernParseStatus = readConcernArgs.initialize(request.body);
if (!readConcernParseStatus.isOK()) {
- auto body = result->getBodyBuilder();
- CommandHelpers::appendCommandStatus(body, readConcernParseStatus);
+ CommandHelpers::appendCommandStatus(result, readConcernParseStatus);
return;
}
if (readConcernArgs.getLevel() == repl::ReadConcernLevel::kSnapshotReadConcern) {
- auto body = result->getBodyBuilder();
CommandHelpers::appendCommandStatus(
- body,
+ result,
Status(ErrorCodes::InvalidOptions, "read concern snapshot is not supported on mongos"));
return;
}
@@ -233,26 +223,25 @@ void execCommandClient(OperationContext* opCtx,
auto metadataStatus = processCommandMetadata(opCtx, request.body);
if (!metadataStatus.isOK()) {
- auto body = result->getBodyBuilder();
- CommandHelpers::appendCommandStatus(body, metadataStatus);
+ CommandHelpers::appendCommandStatus(result, metadataStatus);
return;
}
+ bool ok = false;
if (!supportsWriteConcern) {
- invocation->run(opCtx, result);
+ ok = c->publicRun(opCtx, request, 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());
- invocation->run(opCtx, result);
+ ok = c->publicRun(opCtx, request, 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) {
@@ -281,13 +270,12 @@ void runCommand(OperationContext* opCtx, const OpMsgRequest& request, BSONObjBui
initializeOperationSessionInfo(opCtx, request.body, command->requiresAuth(), true, true);
- CommandReplyBuilder crb(std::move(builder));
-
int loops = 5;
+
while (true) {
- crb.reset();
+ builder.resetToEmpty();
try {
- execCommandClient(opCtx, command, request, &crb);
+ execCommandClient(opCtx, command, request, builder);
return;
} catch (const StaleConfigException& e) {
if (e->getns().empty()) {
@@ -313,11 +301,10 @@ void runCommand(OperationContext* opCtx, const OpMsgRequest& request, BSONObjBui
continue;
} catch (const DBException& e) {
- crb.reset();
- BSONObjBuilder bob = crb.getBodyBuilder();
- ON_BLOCK_EXIT([&] { appendRequiredFieldsToResponse(opCtx, &bob); });
+ ON_BLOCK_EXIT([opCtx, &builder] { appendRequiredFieldsToResponse(opCtx, &builder); });
+ builder.resetToEmpty();
command->incrementCommandsFailed();
- CommandHelpers::appendCommandStatus(bob, e.toStatus());
+ CommandHelpers::appendCommandStatus(builder, e.toStatus());
LastError::get(opCtx->getClient()).setLastError(e.code(), e.reason());
return;
}
@@ -590,7 +577,6 @@ void Strategy::killCursors(OperationContext* opCtx, DbMessage* dbm) {
}
void Strategy::writeOp(OperationContext* opCtx, DbMessage* dbm) {
- BufBuilder bb;
runCommand(opCtx,
[&]() {
const auto& msg = dbm->msg();
@@ -609,7 +595,7 @@ void Strategy::writeOp(OperationContext* opCtx, DbMessage* dbm) {
MONGO_UNREACHABLE;
}
}(),
- BSONObjBuilder{bb}); // built object is ignored
+ BSONObjBuilder());
}
void Strategy::explainFind(OperationContext* opCtx,