/** * 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 . * * 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 #include "mongo/client/read_preference.h" #include "mongo/db/auth/authorization_session.h" #include "mongo/db/commands.h" #include "mongo/db/matcher/extensions_callback_noop.h" #include "mongo/db/query/cursor_response.h" #include "mongo/db/stats/counters.h" #include "mongo/db/views/resolved_view.h" #include "mongo/rpc/get_status_from_command_result.h" #include "mongo/s/commands/cluster_aggregate.h" #include "mongo/s/commands/strategy.h" #include "mongo/s/query/cluster_find.h" namespace mongo { namespace { using std::unique_ptr; using std::string; using std::vector; const char kTermField[] = "term"; /** * Implements the find command on mongos. */ class ClusterFindCmd : public Command { MONGO_DISALLOW_COPYING(ClusterFindCmd); public: ClusterFindCmd() : Command("find") {} virtual bool supportsWriteConcern(const BSONObj& cmd) const override { return false; } bool slaveOk() const final { return false; } bool slaveOverrideOk() const final { return true; } bool maintenanceOk() const final { return false; } bool adminOnly() const final { return false; } bool shouldAffectCommandCounter() const final { return false; } void help(std::stringstream& help) const final { help << "query for documents"; } /** * In order to run the find command, you must be authorized for the "find" action * type on the collection. */ Status checkAuthForCommand(Client* client, const std::string& dbname, const BSONObj& cmdObj) final { const NamespaceString nss(parseNs(dbname, cmdObj)); auto hasTerm = cmdObj.hasField(kTermField); return AuthorizationSession::get(client)->checkAuthForFind(nss, hasTerm); } Status explain(OperationContext* txn, const std::string& dbname, const BSONObj& cmdObj, ExplainCommon::Verbosity verbosity, const rpc::ServerSelectionMetadata& serverSelectionMetadata, BSONObjBuilder* out) const final { const NamespaceString nss(parseNsCollectionRequired(dbname, cmdObj)); // Parse the command BSON to a QueryRequest. bool isExplain = true; auto qr = QueryRequest::makeFromFindCommand(std::move(nss), cmdObj, isExplain); if (!qr.isOK()) { return qr.getStatus(); } auto result = Strategy::explainFind( txn, cmdObj, *qr.getValue(), verbosity, serverSelectionMetadata, out); if (result == ErrorCodes::CommandOnShardedViewNotSupportedOnMongod) { auto resolvedView = ResolvedView::fromBSON(out->asTempObj()); out->resetToEmpty(); auto aggCmdOnView = qr.getValue().get()->asAggregationCommand(); if (!aggCmdOnView.isOK()) { return aggCmdOnView.getStatus(); } auto aggCmd = resolvedView.asExpandedViewAggregation(aggCmdOnView.getValue()); if (!aggCmd.isOK()) { return aggCmd.getStatus(); } int queryOptions = 0; ClusterAggregate::Namespaces nsStruct; nsStruct.requestedNss = std::move(nss); nsStruct.executionNss = std::move(resolvedView.getNamespace()); auto status = ClusterAggregate::runAggregate(txn, nsStruct, aggCmd.getValue(), queryOptions, out); appendCommandStatus(*out, status); return status; } return result; } bool run(OperationContext* txn, const std::string& dbname, BSONObj& cmdObj, int options, std::string& errmsg, BSONObjBuilder& result) final { // We count find command as a query op. globalOpCounters.gotQuery(); const NamespaceString nss(parseNsCollectionRequired(dbname, cmdObj)); const bool isExplain = false; auto qr = QueryRequest::makeFromFindCommand(nss, cmdObj, isExplain); if (!qr.isOK()) { return appendCommandStatus(result, qr.getStatus()); } auto cq = CanonicalQuery::canonicalize(txn, std::move(qr.getValue()), ExtensionsCallbackNoop()); if (!cq.isOK()) { return appendCommandStatus(result, cq.getStatus()); } // Extract read preference. If no read preference is specified in the query, will we pass // down a "primaryOnly" or "secondary" read pref, depending on the slaveOk setting. auto readPref = ClusterFind::extractUnwrappedReadPref(cmdObj, options & QueryOption_SlaveOk); if (!readPref.isOK()) { return appendCommandStatus(result, readPref.getStatus()); } // Do the work to generate the first batch of results. This blocks waiting to get responses // from the shard(s). std::vector batch; BSONObj viewDefinition; auto cursorId = ClusterFind::runQuery( txn, *cq.getValue(), readPref.getValue(), &batch, &viewDefinition); if (!cursorId.isOK()) { if (cursorId.getStatus() == ErrorCodes::CommandOnShardedViewNotSupportedOnMongod) { auto aggCmdOnView = cq.getValue()->getQueryRequest().asAggregationCommand(); if (!aggCmdOnView.isOK()) { return appendCommandStatus(result, aggCmdOnView.getStatus()); } auto resolvedView = ResolvedView::fromBSON(viewDefinition); auto aggCmd = resolvedView.asExpandedViewAggregation(aggCmdOnView.getValue()); if (!aggCmd.isOK()) { return appendCommandStatus(result, aggCmd.getStatus()); } // We pass both the underlying collection namespace and the view namespace here. The // underlying collection namespace is used to execute the aggregation on mongoD. Any // cursor returned will be registered under the view namespace so that subsequent // getMore and killCursors calls against the view have access. ClusterAggregate::Namespaces nsStruct; nsStruct.requestedNss = std::move(nss); nsStruct.executionNss = std::move(resolvedView.getNamespace()); auto status = ClusterAggregate::runAggregate( txn, nsStruct, aggCmd.getValue(), options, &result); appendCommandStatus(result, status); return status.isOK(); } return appendCommandStatus(result, cursorId.getStatus()); } // Build the response document. CursorResponseBuilder firstBatch(/*firstBatch*/ true, &result); for (const auto& obj : batch) { firstBatch.append(obj); } firstBatch.done(cursorId.getValue(), nss.ns()); return true; } } cmdFindCluster; } // namespace } // namespace mongo