summaryrefslogtreecommitdiff
path: root/src/mongo/db
diff options
context:
space:
mode:
Diffstat (limited to 'src/mongo/db')
-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
15 files changed, 180 insertions, 379 deletions
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());
}
};