diff options
author | Nick Zolnierz <nicholas.zolnierz@mongodb.com> | 2018-03-05 09:49:29 -0500 |
---|---|---|
committer | Nick Zolnierz <nicholas.zolnierz@mongodb.com> | 2018-03-05 12:23:39 -0500 |
commit | 74177edb35b6ce7594e4751051010ceade592484 (patch) | |
tree | 3dad385af5647a24291e8ab26ca3fdcf164e5dfd /src | |
parent | 9a364b4c60a3f249947468a918071e3bf586dabc (diff) | |
download | mongo-74177edb35b6ce7594e4751051010ceade592484.tar.gz |
Revert "SERVER-33065 CommandReplyBuilder and CommandInvocation"
This reverts commit ad94e51e0dd40b0d0c38215a36caf75a4be48415.
Diffstat (limited to 'src')
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, |