text: /**
   *    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.
   */
  
  #define MONGO_LOG_DEFAULT_COMPONENT ::mongo::logger::LogComponent::kCommand
  
  #include "mongo/platform/basic.h"
  
  
  #include "mongo/db/commands.h"
  #include "mongo/db/curop.h"
  #include "mongo/db/db_raii.h"
  #include "mongo/db/exec/count.h"
  #include "mongo/db/query/explain.h"
  #include "mongo/db/query/get_executor.h"
  #include "mongo/db/range_preserver.h"
  #include "mongo/db/repl/replication_coordinator_global.h"
  #include "mongo/util/log.h"
  
  namespace mongo {
  namespace {
  
  using std::unique_ptr;
  using std::string;
  using std::stringstream;
  
  /**
   * Implements the MongoD side of the count command.
   */
  class CmdCount : public Command {
  public:
      virtual bool isWriteCommandForConfigServer() const {
          return false;
      }
      CmdCount() : Command("count") {}
      virtual bool slaveOk() const {
          // ok on --slave setups
          return repl::getGlobalReplicationCoordinator()->getSettings().slave == repl::SimpleSlave;
      }
      virtual bool slaveOverrideOk() const {
          return true;
      }
      virtual bool maintenanceOk() const {
          return false;
      }
      virtual bool adminOnly() const {
          return false;
      }
      virtual void help(stringstream& help) const {
          help << "count objects in collection";
      }
      virtual void addRequiredPrivileges(const std::string& dbname,
                                         const BSONObj& cmdObj,
                                         std::vector<Privilege>* out) {
          ActionSet actions;
          actions.addAction(ActionType::find);
          out->push_back(Privilege(parseResourcePattern(dbname, cmdObj), actions));
      }
  
      virtual Status explain(OperationContext* txn,
                             const std::string& dbname,
                             const BSONObj& cmdObj,
                             ExplainCommon::Verbosity verbosity,
                             BSONObjBuilder* out) const {
          auto request = CountRequest::parseFromBSON(dbname, cmdObj);
          if (!request.isOK()) {
              return request.getStatus();
          }
  
          // Acquire the db read lock.
          AutoGetCollectionForRead ctx(txn, request.getValue().getNs());
          Collection* collection = ctx.getCollection();
  
          // Prevent chunks from being cleaned up during yields - this allows us to only check the
          // version on initial entry into count.
          RangePreserver preserver(collection);
  
          auto statusWithPlanExecutor = getExecutorCount(txn,
                                                         collection,
                                                         request.getValue(),
                                                         true,  // explain
                                                         PlanExecutor::YIELD_AUTO);
          if (!statusWithPlanExecutor.isOK()) {
              return statusWithPlanExecutor.getStatus();
          }
  
          unique_ptr<PlanExecutor> exec = std::move(statusWithPlanExecutor.getValue());
  
          Explain::explainStages(exec.get(), verbosity, out);
          return Status::OK();
      }
  
      virtual bool run(OperationContext* txn,
                       const string& dbname,
                       BSONObj& cmdObj,
                       int,
                       string& errmsg,
                       BSONObjBuilder& result) {
          auto request = CountRequest::parseFromBSON(dbname, cmdObj);
          if (!request.isOK()) {
              return appendCommandStatus(result, request.getStatus());
          }
  
          AutoGetCollectionForRead ctx(txn, request.getValue().getNs());
          Collection* collection = ctx.getCollection();
  
          // Prevent chunks from being cleaned up during yields - this allows us to only check the
          // version on initial entry into count.
          RangePreserver preserver(collection);
  
          auto statusWithPlanExecutor = getExecutorCount(txn,
                                                         collection,
                                                         request.getValue(),
                                                         false,  // !explain
                                                         PlanExecutor::YIELD_AUTO);
          if (!statusWithPlanExecutor.isOK()) {
              return appendCommandStatus(result, statusWithPlanExecutor.getStatus());
          }
  
          unique_ptr<PlanExecutor> exec = std::move(statusWithPlanExecutor.getValue());
  
          // Store the plan summary string in CurOp.
          if (NULL != CurOp::get(txn)) {
              CurOp::get(txn)->debug().planSummary = Explain::getPlanSummary(exec.get());
          }
  
          Status execPlanStatus = exec->executePlan();
          if (!execPlanStatus.isOK()) {
              return appendCommandStatus(result, execPlanStatus);
          }
  
          // Plan is done executing. We just need to pull the count out of the root stage.
          invariant(STAGE_COUNT == exec->getRootStage()->stageType());
          CountStage* countStage = static_cast<CountStage*>(exec->getRootStage());
          const CountStats* countStats =
              static_cast<const CountStats*>(countStage->getSpecificStats());
  
          result.appendNumber("n", countStats->nCounted);
          return true;
      }
  
  } cmdCount;
  
  }  // namespace
  }  // namespace mongo
  