summaryrefslogtreecommitdiff
path: root/src/mongo/db/commands/group_cmd.cpp
diff options
context:
space:
mode:
authorKaloian Manassiev <kaloian.manassiev@mongodb.com>2015-07-20 11:20:18 -0400
committerKaloian Manassiev <kaloian.manassiev@mongodb.com>2015-07-21 13:09:31 -0400
commit8f618d4928c1d44abacc8a48d5f398afc6cbdf80 (patch)
tree80cc3bce2fba192b1546242d15afab3ed472c764 /src/mongo/db/commands/group_cmd.cpp
parentc0ff368c1969914e4fc615eceb694c90863c887a (diff)
downloadmongo-8f618d4928c1d44abacc8a48d5f398afc6cbdf80.tar.gz
SERVER-19500 Cleanup explain logic
No functional changes, just moving code around.
Diffstat (limited to 'src/mongo/db/commands/group_cmd.cpp')
-rw-r--r--src/mongo/db/commands/group_cmd.cpp240
1 files changed, 240 insertions, 0 deletions
diff --git a/src/mongo/db/commands/group_cmd.cpp b/src/mongo/db/commands/group_cmd.cpp
new file mode 100644
index 00000000000..1fd20f39a6f
--- /dev/null
+++ b/src/mongo/db/commands/group_cmd.cpp
@@ -0,0 +1,240 @@
+/**
+ * Copyright (C) 2014 MongoDB Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the GNU Affero General Public License in all respects for
+ * all of the code used other than as permitted herein. If you modify file(s)
+ * with this exception, you may extend this exception to your version of the
+ * file(s), but you are not obligated to do so. If you do not wish to do so,
+ * delete this exception statement from your version. If you delete this
+ * exception statement from all source files in the program, then also delete
+ * it in the license file.
+ */
+
+#include "mongo/platform/basic.h"
+
+#include "mongo/db/auth/action_set.h"
+#include "mongo/db/auth/action_type.h"
+#include "mongo/db/auth/authorization_session.h"
+#include "mongo/db/auth/privilege.h"
+#include "mongo/db/commands.h"
+#include "mongo/db/db_raii.h"
+#include "mongo/db/exec/group.h"
+#include "mongo/db/exec/working_set_common.h"
+#include "mongo/db/query/get_executor.h"
+
+namespace mongo {
+
+using std::unique_ptr;
+using std::string;
+
+namespace {
+
+class GroupCommand : public Command {
+public:
+ GroupCommand() : Command("group") {}
+
+private:
+ virtual bool isWriteCommandForConfigServer() const {
+ return false;
+ }
+
+ virtual bool maintenanceOk() const {
+ return false;
+ }
+
+ virtual bool slaveOk() const {
+ return false;
+ }
+
+ virtual bool slaveOverrideOk() const {
+ return true;
+ }
+
+ bool supportsReadConcern() const final {
+ return true;
+ }
+
+ virtual void help(std::stringstream& help) const {
+ help << "http://dochub.mongodb.org/core/aggregation";
+ }
+
+ virtual Status checkAuthForCommand(ClientBasic* client,
+ const std::string& dbname,
+ const BSONObj& cmdObj) {
+ std::string ns = parseNs(dbname, cmdObj);
+ if (!AuthorizationSession::get(client)
+ ->isAuthorizedForActionsOnNamespace(NamespaceString(ns), ActionType::find)) {
+ return Status(ErrorCodes::Unauthorized, "unauthorized");
+ }
+ return Status::OK();
+ }
+
+ virtual std::string parseNs(const std::string& dbname, const BSONObj& cmdObj) const {
+ const BSONObj& p = cmdObj.firstElement().embeddedObjectUserCheck();
+ uassert(17211, "ns has to be set", p["ns"].type() == String);
+ return dbname + "." + p["ns"].String();
+ }
+
+ virtual Status explain(OperationContext* txn,
+ const std::string& dbname,
+ const BSONObj& cmdObj,
+ ExplainCommon::Verbosity verbosity,
+ BSONObjBuilder* out) const {
+ GroupRequest groupRequest;
+ Status parseRequestStatus = _parseRequest(dbname, cmdObj, &groupRequest);
+ if (!parseRequestStatus.isOK()) {
+ return parseRequestStatus;
+ }
+
+ groupRequest.explain = true;
+
+ AutoGetCollectionForRead ctx(txn, groupRequest.ns);
+ Collection* coll = ctx.getCollection();
+
+ auto statusWithPlanExecutor =
+ getExecutorGroup(txn, coll, groupRequest, PlanExecutor::YIELD_AUTO);
+ if (!statusWithPlanExecutor.isOK()) {
+ return statusWithPlanExecutor.getStatus();
+ }
+
+ unique_ptr<PlanExecutor> planExecutor = std::move(statusWithPlanExecutor.getValue());
+
+ Explain::explainStages(planExecutor.get(), verbosity, out);
+ return Status::OK();
+ }
+
+ virtual bool run(OperationContext* txn,
+ const std::string& dbname,
+ BSONObj& cmdObj,
+ int options,
+ std::string& errmsg,
+ BSONObjBuilder& result) {
+ GroupRequest groupRequest;
+ Status parseRequestStatus = _parseRequest(dbname, cmdObj, &groupRequest);
+ if (!parseRequestStatus.isOK()) {
+ return appendCommandStatus(result, parseRequestStatus);
+ }
+
+ AutoGetCollectionForRead ctx(txn, groupRequest.ns);
+ Collection* coll = ctx.getCollection();
+
+ auto statusWithPlanExecutor =
+ getExecutorGroup(txn, coll, groupRequest, PlanExecutor::YIELD_AUTO);
+ if (!statusWithPlanExecutor.isOK()) {
+ return appendCommandStatus(result, statusWithPlanExecutor.getStatus());
+ }
+
+ unique_ptr<PlanExecutor> planExecutor = std::move(statusWithPlanExecutor.getValue());
+
+ // Group executors return ADVANCED exactly once, with the entire group result.
+ BSONObj retval;
+ PlanExecutor::ExecState state = planExecutor->getNext(&retval, NULL);
+ if (PlanExecutor::ADVANCED != state) {
+ invariant(PlanExecutor::FAILURE == state || PlanExecutor::DEAD == state);
+
+ if (WorkingSetCommon::isValidStatusMemberObject(retval)) {
+ return appendCommandStatus(result, WorkingSetCommon::getMemberObjectStatus(retval));
+ }
+ return appendCommandStatus(result,
+ Status(ErrorCodes::BadValue,
+ str::stream() << "error encountered during group "
+ << "operation, executor returned "
+ << PlanExecutor::statestr(state)));
+ }
+
+ invariant(planExecutor->isEOF());
+
+ invariant(STAGE_GROUP == planExecutor->getRootStage()->stageType());
+ GroupStage* groupStage = static_cast<GroupStage*>(planExecutor->getRootStage());
+ const GroupStats* groupStats =
+ static_cast<const GroupStats*>(groupStage->getSpecificStats());
+ const CommonStats* groupChildStats = groupStage->getChildren()[0]->getCommonStats();
+
+ result.appendArray("retval", retval);
+ result.append("count", static_cast<long long>(groupChildStats->advanced));
+ result.append("keys", static_cast<long long>(groupStats->nGroups));
+
+ return true;
+ }
+
+private:
+ /**
+ * Parse a group command object.
+ *
+ * If 'cmdObj' is well-formed, returns Status::OK() and fills in out-argument 'request'.
+ *
+ * If a parsing error is encountered, returns an error Status.
+ */
+ Status _parseRequest(const std::string& dbname,
+ const BSONObj& cmdObj,
+ GroupRequest* request) const {
+ request->ns = parseNs(dbname, cmdObj);
+
+ // By default, group requests are regular group not explain of group.
+ request->explain = false;
+
+ const BSONObj& p = cmdObj.firstElement().embeddedObjectUserCheck();
+
+ if (p["cond"].type() == Object) {
+ request->query = p["cond"].embeddedObject().getOwned();
+ } else if (p["condition"].type() == Object) {
+ request->query = p["condition"].embeddedObject().getOwned();
+ } else if (p["query"].type() == Object) {
+ request->query = p["query"].embeddedObject().getOwned();
+ } else if (p["q"].type() == Object) {
+ request->query = p["q"].embeddedObject().getOwned();
+ }
+
+ if (p["key"].type() == Object) {
+ request->keyPattern = p["key"].embeddedObjectUserCheck().getOwned();
+ if (!p["$keyf"].eoo()) {
+ return Status(ErrorCodes::BadValue, "can't have key and $keyf");
+ }
+ } else if (!p["$keyf"].eoo()) {
+ request->keyFunctionCode = p["$keyf"]._asCode();
+ } else {
+ // No key specified. Use the entire object as the key.
+ }
+
+ BSONElement reduce = p["$reduce"];
+ if (reduce.eoo()) {
+ return Status(ErrorCodes::BadValue, "$reduce has to be set");
+ }
+ request->reduceCode = reduce._asCode();
+
+ if (reduce.type() == CodeWScope) {
+ request->reduceScope = reduce.codeWScopeObject().getOwned();
+ }
+
+ if (p["initial"].type() != Object) {
+ return Status(ErrorCodes::BadValue, "initial has to be an object");
+ }
+ request->initial = p["initial"].embeddedObject().getOwned();
+
+ if (!p["finalize"].eoo()) {
+ request->finalize = p["finalize"]._asCode();
+ }
+
+ return Status::OK();
+ }
+
+} cmdGroup;
+
+} // namespace
+} // namespace mongo