summaryrefslogtreecommitdiff
path: root/src/mongo/db/commands.cpp
diff options
context:
space:
mode:
authorBilly Donahue <billy.donahue@mongodb.com>2018-02-27 15:14:14 -0500
committerBilly Donahue <billy.donahue@mongodb.com>2018-03-02 16:31:01 -0500
commitad94e51e0dd40b0d0c38215a36caf75a4be48415 (patch)
tree8f621add05d506f88c23e64260780919e1f2d60e /src/mongo/db/commands.cpp
parentae20f392b61ddc90d2191856e76940ca3c7a3ed2 (diff)
downloadmongo-ad94e51e0dd40b0d0c38215a36caf75a4be48415.tar.gz
SERVER-33065 CommandReplyBuilder and CommandInvocation
remove publicRun from mr_test.cpp change explain to take OpMsgRequest private explain private allowsAfterClusterTime private supportsWriteConcern supportsReadConcern remove publicRun cluster_explain_cmd.cpp: do not inject "$db" field. let explain() exceptions escape update cluster distinct explain
Diffstat (limited to 'src/mongo/db/commands.cpp')
-rw-r--r--src/mongo/db/commands.cpp169
1 files changed, 127 insertions, 42 deletions
diff --git a/src/mongo/db/commands.cpp b/src/mongo/db/commands.cpp
index 2aaeda9b5ed..0d8170618d4 100644
--- a/src/mongo/db/commands.cpp
+++ b/src/mongo/db/commands.cpp
@@ -71,6 +71,23 @@ const WriteConcernOptions kMajorityWriteConcern(
WriteConcernOptions::SyncMode::UNSET,
Seconds(60));
+// A facade presenting CommandDefinition as an audit::CommandInterface.
+class CommandAuditHook : public audit::CommandInterface {
+public:
+ explicit CommandAuditHook(Command* command) : _command(command) {}
+
+ void redactForLogging(mutablebson::Document* cmdObj) const final {
+ _command->redactForLogging(cmdObj);
+ }
+
+ std::string parseNs(const std::string& dbname, const BSONObj& cmdObj) const final {
+ return _command->parseNs(dbname, cmdObj);
+ }
+
+private:
+ Command* _command;
+};
+
} // namespace
@@ -80,18 +97,22 @@ const WriteConcernOptions kMajorityWriteConcern(
BSONObj CommandHelpers::runCommandDirectly(OperationContext* opCtx, const OpMsgRequest& request) {
auto command = globalCommandRegistry()->findCommand(request.getCommandName());
invariant(command);
- BSONObjBuilder out;
+ BufBuilder bb;
+ CommandReplyBuilder crb(BSONObjBuilder{bb});
try {
- bool ok = command->publicRun(opCtx, request, out);
- appendCommandStatus(out, ok);
+ auto invocation = command->parse(opCtx, request);
+ invocation->run(opCtx, &crb);
+ auto body = crb.getBodyBuilder();
+ CommandHelpers::extractOrAppendOk(body);
} catch (const StaleConfigException&) {
// These exceptions are intended to be handled at a higher level.
throw;
} catch (const DBException& ex) {
- out.resetToEmpty();
- appendCommandStatus(out, ex.toStatus());
+ auto body = crb.getBodyBuilder();
+ body.resetToEmpty();
+ appendCommandStatus(body, ex.toStatus());
}
- return out.obj();
+ return BSONObj(bb.release());
}
std::string CommandHelpers::parseNsFullyQualified(StringData dbname, const BSONObj& cmdObj) {
@@ -167,6 +188,16 @@ void CommandHelpers::appendCommandStatus(BSONObjBuilder& result,
}
}
+bool CommandHelpers::extractOrAppendOk(BSONObjBuilder& reply) {
+ if (auto okField = reply.asTempObj()["ok"]) {
+ // If ok is present, use its truthiness.
+ return okField.trueValue();
+ }
+ // Missing "ok" field is an implied success.
+ CommandHelpers::appendCommandStatus(reply, true);
+ return true;
+}
+
void CommandHelpers::appendCommandWCStatus(BSONObjBuilder& result,
const Status& awaitReplicationStatus,
const WriteConcernResult& wcResult) {
@@ -306,10 +337,99 @@ bool CommandHelpers::isHelpRequest(const BSONElement& helpElem) {
constexpr StringData CommandHelpers::kHelpFieldName;
//////////////////////////////////////////////////////////////
+// CommandReplyBuilder
+
+CommandReplyBuilder::CommandReplyBuilder(BSONObjBuilder bodyObj)
+ : _bodyBuf(&bodyObj.bb()), _bodyOffset(bodyObj.offset()) {
+ // CommandReplyBuilder requires that bodyObj build into an externally-owned buffer.
+ invariant(!bodyObj.owned());
+ bodyObj.doneFast();
+}
+
+BSONObjBuilder CommandReplyBuilder::getBodyBuilder() const {
+ return BSONObjBuilder(BSONObjBuilder::ResumeBuildingTag{}, *_bodyBuf, _bodyOffset);
+}
+
+void CommandReplyBuilder::reset() {
+ getBodyBuilder().resetToEmpty();
+}
+
+//////////////////////////////////////////////////////////////
+// CommandInvocation
+
+CommandInvocation::~CommandInvocation() = default;
+
+//////////////////////////////////////////////////////////////
// Command
+class Command::InvocationShim final : public CommandInvocation {
+public:
+ InvocationShim(OperationContext*, const OpMsgRequest& request, Command* command)
+ : CommandInvocation(command),
+ _command(command),
+ _request(&request),
+ _dbName(_request->getDatabase().toString()) {}
+
+private:
+ void run(OperationContext* opCtx, CommandReplyBuilder* result) override {
+ try {
+ BSONObjBuilder bob = result->getBodyBuilder();
+ bool ok = _command->enhancedRun(opCtx, *_request, bob);
+ CommandHelpers::appendCommandStatus(bob, ok);
+ } catch (const ExceptionFor<ErrorCodes::Unauthorized>&) {
+ CommandAuditHook hook(_command);
+ audit::logCommandAuthzCheck(
+ opCtx->getClient(), *_request, &hook, ErrorCodes::Unauthorized);
+ throw;
+ }
+ }
+
+ void explain(OperationContext* opCtx,
+ ExplainOptions::Verbosity verbosity,
+ BSONObjBuilder* result) override {
+ uassertStatusOK(_command->explain(opCtx, *_request, verbosity, result));
+ }
+
+ NamespaceString ns() const override {
+ return NamespaceString(_command->parseNs(_dbName, cmdObj()));
+ }
+
+ bool supportsWriteConcern() const override {
+ return _command->supportsWriteConcern(cmdObj());
+ }
+
+ Command::AllowedOnSecondary secondaryAllowed(ServiceContext* context) const override {
+ return _command->secondaryAllowed(context);
+ }
+
+ bool supportsReadConcern(repl::ReadConcernLevel level) const override {
+ return _command->supportsReadConcern(_dbName, cmdObj(), level);
+ }
+
+ bool allowsAfterClusterTime() const override {
+ return _command->allowsAfterClusterTime(cmdObj());
+ }
+
+ void doCheckAuthorization(OperationContext* opCtx) const override {
+ uassertStatusOK(_command->checkAuthForRequest(opCtx, *_request));
+ }
+
+ const BSONObj& cmdObj() const {
+ return _request->body;
+ }
+
+ Command* const _command;
+ const OpMsgRequest* const _request;
+ const std::string _dbName;
+};
+
Command::~Command() = default;
+std::unique_ptr<CommandInvocation> Command::parse(OperationContext* opCtx,
+ const OpMsgRequest& request) {
+ return stdx::make_unique<InvocationShim>(opCtx, request, this);
+}
+
std::string Command::parseNs(const std::string& dbname, const BSONObj& cmdObj) const {
BSONElement first = cmdObj.firstElement();
if (first.type() != mongo::String)
@@ -335,8 +455,7 @@ Command::Command(StringData name, StringData oldName)
}
Status Command::explain(OperationContext* opCtx,
- const std::string& dbname,
- const BSONObj& cmdObj,
+ const OpMsgRequest& request,
ExplainOptions::Verbosity verbosity,
BSONObjBuilder* out) const {
return {ErrorCodes::IllegalOperation, str::stream() << "Cannot explain cmd: " << getName()};
@@ -396,25 +515,6 @@ static Status _checkAuthorizationImpl(Command* c,
return Status::OK();
}
-namespace {
-// A facade presenting CommandDefinition as an audit::CommandInterface.
-class CommandAuditHook : public audit::CommandInterface {
-public:
- explicit CommandAuditHook(Command* command) : _command(command) {}
-
- void redactForLogging(mutablebson::Document* cmdObj) const final {
- _command->redactForLogging(cmdObj);
- }
-
- std::string parseNs(const std::string& dbname, const BSONObj& cmdObj) const final {
- return _command->parseNs(dbname, cmdObj);
- }
-
-private:
- Command* _command;
-};
-} // namespace
-
Status Command::checkAuthorization(Command* c,
OperationContext* opCtx,
const OpMsgRequest& request) {
@@ -427,21 +527,6 @@ Status Command::checkAuthorization(Command* c,
return status;
}
-bool Command::publicRun(OperationContext* opCtx,
- const OpMsgRequest& request,
- BSONObjBuilder& result) {
- try {
- return enhancedRun(opCtx, request, result);
- } catch (const DBException& e) {
- if (e.code() == ErrorCodes::Unauthorized) {
- CommandAuditHook hook(this);
- audit::logCommandAuthzCheck(
- opCtx->getClient(), request, &hook, ErrorCodes::Unauthorized);
- }
- throw;
- }
-}
-
void Command::generateHelpResponse(OperationContext* opCtx,
rpc::ReplyBuilderInterface* replyBuilder,
const Command& command) {