text: // drop_indexes.cpp
  
  /**
  *    Copyright (C) 2013 10gen 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 <string>
  #include <vector>
  
  #include "mongo/db/background.h"
  #include "mongo/db/catalog/collection.h"
  #include "mongo/db/catalog/collection_catalog_entry.h"
  #include "mongo/db/catalog/database.h"
  #include "mongo/db/catalog/drop_indexes.h"
  #include "mongo/db/catalog/index_catalog.h"
  #include "mongo/db/catalog/index_create.h"
  #include "mongo/db/catalog/index_key_validate.h"
  #include "mongo/db/client.h"
  #include "mongo/db/commands.h"
  #include "mongo/db/concurrency/write_conflict_exception.h"
  #include "mongo/db/curop.h"
  #include "mongo/db/db_raii.h"
  #include "mongo/db/dbdirectclient.h"
  #include "mongo/db/index/index_descriptor.h"
  #include "mongo/db/index_builder.h"
  #include "mongo/db/op_observer.h"
  #include "mongo/db/repl/replication_coordinator_global.h"
  #include "mongo/db/service_context.h"
  #include "mongo/util/log.h"
  
  namespace mongo {
  
  using std::endl;
  using std::string;
  using std::stringstream;
  using std::vector;
  
  /* "dropIndexes" is now the preferred form - "deleteIndexes" deprecated */
  class CmdDropIndexes : public BasicCommand {
  public:
      virtual bool slaveOk() const {
          return false;
      }
      virtual bool supportsWriteConcern(const BSONObj& cmd) const override {
          return true;
      }
      virtual void help(stringstream& help) const {
          help << "drop indexes for a collection";
      }
      virtual void addRequiredPrivileges(const std::string& dbname,
                                         const BSONObj& cmdObj,
                                         std::vector<Privilege>* out) {
          ActionSet actions;
          actions.addAction(ActionType::dropIndex);
          out->push_back(Privilege(parseResourcePattern(dbname, cmdObj), actions));
      }
  
      CmdDropIndexes() : BasicCommand("dropIndexes", "deleteIndexes") {}
      bool run(OperationContext* opCtx,
               const string& dbname,
               const BSONObj& jsobj,
               BSONObjBuilder& result) {
          const NamespaceString nss = CommandHelpers::parseNsCollectionRequired(dbname, jsobj);
          return CommandHelpers::appendCommandStatus(result, dropIndexes(opCtx, nss, jsobj, &result));
      }
  
  } cmdDropIndexes;
  
  class CmdReIndex : public ErrmsgCommandDeprecated {
  public:
      virtual bool slaveOk() const {
          return true;
      }  // can reindex on a secondary
      virtual bool supportsWriteConcern(const BSONObj& cmd) const override {
          return false;
      }
      virtual void help(stringstream& help) const {
          help << "re-index a collection";
      }
      virtual void addRequiredPrivileges(const std::string& dbname,
                                         const BSONObj& cmdObj,
                                         std::vector<Privilege>* out) {
          ActionSet actions;
          actions.addAction(ActionType::reIndex);
          out->push_back(Privilege(parseResourcePattern(dbname, cmdObj), actions));
      }
      CmdReIndex() : ErrmsgCommandDeprecated("reIndex") {}
  
      bool errmsgRun(OperationContext* opCtx,
                     const string& dbname,
                     const BSONObj& jsobj,
                     string& errmsg,
                     BSONObjBuilder& result) {
          DBDirectClient db(opCtx);
  
          const NamespaceString toReIndexNs =
              CommandHelpers::parseNsCollectionRequired(dbname, jsobj);
  
          LOG(0) << "CMD: reIndex " << toReIndexNs;
  
          Lock::DBLock dbXLock(opCtx, dbname, MODE_X);
          OldClientContext ctx(opCtx, toReIndexNs.ns());
  
          Collection* collection = ctx.db()->getCollection(opCtx, toReIndexNs);
          if (!collection) {
              if (ctx.db()->getViewCatalog()->lookup(opCtx, toReIndexNs.ns()))
                  return CommandHelpers::appendCommandStatus(
                      result, {ErrorCodes::CommandNotSupportedOnView, "can't re-index a view"});
              else
                  return CommandHelpers::appendCommandStatus(
                      result, {ErrorCodes::NamespaceNotFound, "collection does not exist"});
          }
  
          BackgroundOperation::assertNoBgOpInProgForNs(toReIndexNs.ns());
  
          const auto featureCompatibilityVersion =
              serverGlobalParams.featureCompatibility.getVersion();
          const auto defaultIndexVersion =
              IndexDescriptor::getDefaultIndexVersion(featureCompatibilityVersion);
  
          vector<BSONObj> all;
          {
              vector<string> indexNames;
              collection->getCatalogEntry()->getAllIndexes(opCtx, &indexNames);
              all.reserve(indexNames.size());
  
              for (size_t i = 0; i < indexNames.size(); i++) {
                  const string& name = indexNames[i];
                  BSONObj spec = collection->getCatalogEntry()->getIndexSpec(opCtx, name);
  
                  {
                      BSONObjBuilder bob;
  
                      for (auto&& indexSpecElem : spec) {
                          auto indexSpecElemFieldName = indexSpecElem.fieldNameStringData();
                          if (IndexDescriptor::kIndexVersionFieldName == indexSpecElemFieldName) {
                              // We create a new index specification with the 'v' field set as
                              // 'defaultIndexVersion'.
                              bob.append(IndexDescriptor::kIndexVersionFieldName,
                                         static_cast<int>(defaultIndexVersion));
                          } else {
                              bob.append(indexSpecElem);
                          }
                      }
  
                      all.push_back(bob.obj());
                  }
  
                  const BSONObj key = spec.getObjectField("key");
                  const Status keyStatus =
                      index_key_validate::validateKeyPattern(key, defaultIndexVersion);
                  if (!keyStatus.isOK()) {
                      errmsg = str::stream()
                          << "Cannot rebuild index " << spec << ": " << keyStatus.reason()
                          << " For more info see http://dochub.mongodb.org/core/index-validation";
                      return false;
                  }
              }
          }
  
          result.appendNumber("nIndexesWas", all.size());
  
          {
              WriteUnitOfWork wunit(opCtx);
              collection->getIndexCatalog()->dropAllIndexes(opCtx, true);
              wunit.commit();
          }
  
          MultiIndexBlock indexer(opCtx, collection);
          // do not want interruption as that will leave us without indexes.
  
          auto indexInfoObjs = indexer.init(all);
          if (!indexInfoObjs.isOK()) {
              return CommandHelpers::appendCommandStatus(result, indexInfoObjs.getStatus());
          }
  
          auto status = indexer.insertAllDocumentsInCollection();
          if (!status.isOK()) {
              return CommandHelpers::appendCommandStatus(result, status);
          }
  
          {
              WriteUnitOfWork wunit(opCtx);
              indexer.commit();
              wunit.commit();
          }
  
          // Do not allow majority reads from this collection until all original indexes are visible.
          // This was also done when dropAllIndexes() committed, but we need to ensure that no one
          // tries to read in the intermediate state where all indexes are newer than the current
          // snapshot so are unable to be used.
          auto replCoord = repl::ReplicationCoordinator::get(opCtx);
          auto snapshotName = replCoord->getMinimumVisibleSnapshot(opCtx);
          collection->setMinimumVisibleSnapshot(snapshotName);
  
          result.append("nIndexes", static_cast<int>(indexInfoObjs.getValue().size()));
          result.append("indexes", indexInfoObjs.getValue());
  
          return true;
      }
  } cmdReIndex;
  }
  