summaryrefslogtreecommitdiff
path: root/src/mongo/db/catalog
diff options
context:
space:
mode:
authormatt dannenberg <matt.dannenberg@10gen.com>2015-03-13 06:35:51 -0400
committermatt dannenberg <matt.dannenberg@10gen.com>2015-04-09 10:57:16 -0400
commit1725d76f448323a2bbaa11ffd37fd7b10cd6a64b (patch)
treebe35489fd99d5b0040f27d86b731cfcae1e4479a /src/mongo/db/catalog
parentacc7a72194990f35ff706bdcab7ec443c39fb0d5 (diff)
downloadmongo-1725d76f448323a2bbaa11ffd37fd7b10cd6a64b.tar.gz
SERVER-17573 move OpObserver calls into the datalayer as much as possible and eliminate repl bools
Diffstat (limited to 'src/mongo/db/catalog')
-rw-r--r--src/mongo/db/catalog/apply_ops.cpp215
-rw-r--r--src/mongo/db/catalog/apply_ops.h45
-rw-r--r--src/mongo/db/catalog/capped_utils.cpp261
-rw-r--r--src/mongo/db/catalog/capped_utils.h58
-rw-r--r--src/mongo/db/catalog/coll_mod.cpp182
-rw-r--r--src/mongo/db/catalog/coll_mod.h45
-rw-r--r--src/mongo/db/catalog/collection.cpp101
-rw-r--r--src/mongo/db/catalog/collection.h34
-rw-r--r--src/mongo/db/catalog/database.cpp28
-rw-r--r--src/mongo/db/catalog/database.h4
-rw-r--r--src/mongo/db/catalog/drop_collection.cpp109
-rw-r--r--src/mongo/db/catalog/drop_collection.h43
-rw-r--r--src/mongo/db/catalog/drop_database.cpp111
-rw-r--r--src/mongo/db/catalog/drop_database.h38
-rw-r--r--src/mongo/db/catalog/drop_indexes.cpp194
-rw-r--r--src/mongo/db/catalog/drop_indexes.h46
-rw-r--r--src/mongo/db/catalog/rename_collection.cpp292
-rw-r--r--src/mongo/db/catalog/rename_collection.h46
18 files changed, 1786 insertions, 66 deletions
diff --git a/src/mongo/db/catalog/apply_ops.cpp b/src/mongo/db/catalog/apply_ops.cpp
new file mode 100644
index 00000000000..2ef6d46c9d0
--- /dev/null
+++ b/src/mongo/db/catalog/apply_ops.cpp
@@ -0,0 +1,215 @@
+/**
+ * Copyright (C) 2015 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/catalog/apply_ops.h"
+
+#include "mongo/db/client.h"
+#include "mongo/db/commands/dbhash.h"
+#include "mongo/db/concurrency/write_conflict_exception.h"
+#include "mongo/db/db_raii.h"
+#include "mongo/db/dbdirectclient.h"
+#include "mongo/db/matcher/matcher.h"
+#include "mongo/db/op_observer.h"
+#include "mongo/db/operation_context_impl.h"
+#include "mongo/db/repl/oplog.h"
+#include "mongo/db/repl/replication_coordinator_global.h"
+#include "mongo/db/service_context.h"
+#include "mongo/util/log.h"
+
+namespace mongo {
+ Status applyOps(OperationContext* txn,
+ const std::string& dbName,
+ const BSONObj& applyOpCmd,
+ BSONObjBuilder* result) {
+ // SERVER-4328 todo : is global ok or does this take a long time? i believe multiple
+ // ns used so locking individually requires more analysis
+ ScopedTransaction scopedXact(txn, MODE_X);
+ Lock::GlobalWrite globalWriteLock(txn->lockState());
+
+ bool userInitiatedWritesAndNotPrimary = txn->writesAreReplicated() &&
+ !repl::getGlobalReplicationCoordinator()->canAcceptWritesForDatabase(dbName);
+
+ if (userInitiatedWritesAndNotPrimary) {
+ return Status(ErrorCodes::NotMaster,
+ str::stream() << "Not primary while applying ops to database " << dbName);
+ }
+
+ bool shouldReplicateWrites = txn->writesAreReplicated();
+ txn->setReplicatedWrites(false);
+ BSONObj ops = applyOpCmd.firstElement().Obj();
+ // Preconditions check reads the database state, so needs to be done locked
+ if (applyOpCmd["preCondition"].type() == Array) {
+ BSONObjIterator i(applyOpCmd["preCondition"].Obj());
+ while (i.more()) {
+ BSONObj f = i.next().Obj();
+
+ DBDirectClient db(txn);
+ BSONObj realres = db.findOne(f["ns"].String() , f["q"].Obj());
+
+ // Apply-ops would never have a $where matcher, so use the default callback,
+ // which will throw an error if $where is found.
+ Matcher m(f["res"].Obj());
+ if (! m.matches(realres)) {
+ result->append("got" , realres);
+ result->append("whatFailed" , f);
+ txn->setReplicatedWrites(shouldReplicateWrites);
+ return Status(ErrorCodes::BadValue, "pre-condition failed");
+ }
+ }
+ }
+
+ // apply
+ int num = 0;
+ int errors = 0;
+
+ BSONObjIterator i(ops);
+ BSONArrayBuilder ab;
+ const bool alwaysUpsert = applyOpCmd.hasField("alwaysUpsert") ?
+ applyOpCmd["alwaysUpsert"].trueValue() : true;
+
+ while (i.more()) {
+ BSONElement e = i.next();
+ const BSONObj& temp = e.Obj();
+
+ // Ignore 'n' operations.
+ const char *opType = temp["op"].valuestrsafe();
+ if (*opType == 'n') continue;
+
+ const std::string ns = temp["ns"].String();
+
+ // Run operations under a nested lock as a hack to prevent yielding.
+ //
+ // The list of operations is supposed to be applied atomically; yielding
+ // would break atomicity by allowing an interruption or a shutdown to occur
+ // after only some operations are applied. We are already locked globally
+ // at this point, so taking a DBLock on the namespace creates a nested lock,
+ // and yields are disallowed for operations that hold a nested lock.
+ //
+ // We do not have a wrapping WriteUnitOfWork so it is possible for a journal
+ // commit to happen with a subset of ops applied.
+ // TODO figure out what to do about this.
+ Lock::GlobalWrite globalWriteLockDisallowTempRelease(txn->lockState());
+
+ // Ensures that yielding will not happen (see the comment above).
+ DEV {
+ Locker::LockSnapshot lockSnapshot;
+ invariant(!txn->lockState()->saveLockStateAndUnlock(&lockSnapshot));
+ };
+
+ OldClientContext ctx(txn, ns);
+
+ Status status(ErrorCodes::InternalError, "");
+ while (true) {
+ try {
+ // We assume that in the WriteConflict retry case, either the op rolls back
+ // any changes it makes or is otherwise safe to rerun.
+ status =
+ repl::applyOperation_inlock(txn,
+ ctx.db(),
+ temp,
+ !txn->writesAreReplicated(),
+ alwaysUpsert);
+ break;
+ }
+ catch (const WriteConflictException& wce) {
+ LOG(2) << "WriteConflictException in applyOps command, retrying.";
+ txn->recoveryUnit()->commitAndRestart();
+ continue;
+ }
+ }
+
+ ab.append(status.isOK());
+ if (!status.isOK()) {
+ errors++;
+ }
+
+ num++;
+
+ WriteUnitOfWork wuow(txn);
+ logOpForDbHash(txn, ns.c_str());
+ wuow.commit();
+ }
+
+ result->append("applied" , num);
+ result->append("results" , ab.arr());
+ txn->setReplicatedWrites(shouldReplicateWrites);
+
+ if (txn->writesAreReplicated()) {
+ // We want this applied atomically on slaves
+ // so we re-wrap without the pre-condition for speed
+
+ std::string tempNS = str::stream() << dbName << ".$cmd";
+
+ // TODO: possibly use mutable BSON to remove preCondition field
+ // once it is available
+ BSONObjIterator iter(applyOpCmd);
+ BSONObjBuilder cmdBuilder;
+
+ while (iter.more()) {
+ BSONElement elem(iter.next());
+ if (strcmp(elem.fieldName(), "preCondition") != 0) {
+ cmdBuilder.append(elem);
+ }
+ }
+
+ const BSONObj cmdRewritten = cmdBuilder.done();
+
+ // We currently always logOp the command regardless of whether the individial ops
+ // succeeded and rely on any failures to also happen on secondaries. This isn't
+ // perfect, but it's what the command has always done and is part of its "correct"
+ // behavior.
+ while (true) {
+ try {
+ WriteUnitOfWork wunit(txn);
+ getGlobalServiceContext()->getOpObserver()->onApplyOps(txn,
+ tempNS,
+ cmdRewritten);
+ wunit.commit();
+ break;
+ }
+ catch (const WriteConflictException& wce) {
+ LOG(2) <<
+ "WriteConflictException while logging applyOps command, retrying.";
+ txn->recoveryUnit()->commitAndRestart();
+ continue;
+ }
+ }
+ }
+
+ if (errors != 0) {
+ return Status(ErrorCodes::UnknownError, "");
+ }
+
+ return Status::OK();
+ }
+
+} // namespace mongo
diff --git a/src/mongo/db/catalog/apply_ops.h b/src/mongo/db/catalog/apply_ops.h
new file mode 100644
index 00000000000..13639deb586
--- /dev/null
+++ b/src/mongo/db/catalog/apply_ops.h
@@ -0,0 +1,45 @@
+/**
+ * Copyright (C) 2015 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/base/status.h"
+
+namespace mongo {
+ class BSONObj;
+ class BSONObjBuilder;
+ class OperationContext;
+
+ /**
+ * Applies ops contained in "applyOpCmd" and populates fields in "result" to be returned to the
+ * user.
+ */
+ Status applyOps(OperationContext* txn,
+ const std::string& dbName,
+ const BSONObj& applyOpCmd,
+ BSONObjBuilder* result);
+
+} // namespace mongo
diff --git a/src/mongo/db/catalog/capped_utils.cpp b/src/mongo/db/catalog/capped_utils.cpp
new file mode 100644
index 00000000000..76be6e8d040
--- /dev/null
+++ b/src/mongo/db/catalog/capped_utils.cpp
@@ -0,0 +1,261 @@
+/**
+ * Copyright (C) 2015 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/catalog/capped_utils.h"
+
+#include "mongo/db/background.h"
+#include "mongo/db/catalog/collection.h"
+#include "mongo/db/catalog/database.h"
+#include "mongo/db/catalog/index_catalog.h"
+#include "mongo/db/client.h"
+#include "mongo/db/db_raii.h"
+#include "mongo/db/index_builder.h"
+#include "mongo/db/operation_context_impl.h"
+#include "mongo/db/query/internal_plans.h"
+#include "mongo/db/repl/replication_coordinator_global.h"
+#include "mongo/db/service_context.h"
+
+namespace mongo {
+namespace {
+ std::vector<BSONObj> stopIndexBuildsEmptyCapped(OperationContext* opCtx,
+ Database* db,
+ const NamespaceString& ns) {
+ IndexCatalog::IndexKillCriteria criteria;
+ criteria.ns = ns;
+ return IndexBuilder::killMatchingIndexBuilds(db->getCollection(ns), criteria);
+ }
+
+ std::vector<BSONObj> stopIndexBuildsConvertToCapped(OperationContext* opCtx,
+ Database* db,
+ const NamespaceString& ns) {
+ IndexCatalog::IndexKillCriteria criteria;
+ criteria.ns = ns;
+ Collection* coll = db->getCollection(ns);
+ if (coll) {
+ return IndexBuilder::killMatchingIndexBuilds(coll, criteria);
+ }
+ return std::vector<BSONObj>();
+ }
+
+} // namespace
+
+ Status emptyCapped(OperationContext* txn,
+ const NamespaceString& collectionName) {
+ ScopedTransaction scopedXact(txn, MODE_IX);
+ AutoGetDb autoDb(txn, collectionName.db(), MODE_X);
+
+ bool userInitiatedWritesAndNotPrimary = txn->writesAreReplicated() &&
+ !repl::getGlobalReplicationCoordinator()->canAcceptWritesForDatabase(
+ collectionName.db());
+
+ if (userInitiatedWritesAndNotPrimary) {
+ return Status(ErrorCodes::NotMaster,
+ str::stream() << "Not primary while truncating collection "
+ << collectionName.ns());
+ }
+
+ Database* db = autoDb.getDb();
+ massert(13429, "no such database", db);
+
+ Collection* collection = db->getCollection(collectionName);
+ massert(28584, "no such collection", collection);
+
+ std::vector<BSONObj> indexes = stopIndexBuildsEmptyCapped(txn, db, collectionName);
+
+ WriteUnitOfWork wuow(txn);
+
+ Status status = collection->truncate(txn);
+ if (!status.isOK()) {
+ return status;
+ }
+
+ IndexBuilder::restoreIndexes(txn, indexes);
+
+ getGlobalServiceContext()->getOpObserver()->onEmptyCapped(txn, collection->ns());
+
+ wuow.commit();
+
+ return Status::OK();
+ }
+
+ Status cloneCollectionAsCapped(OperationContext* txn,
+ Database* db,
+ const std::string& shortFrom,
+ const std::string& shortTo,
+ double size,
+ bool temp) {
+
+ std::string fromNs = db->name() + "." + shortFrom;
+ std::string toNs = db->name() + "." + shortTo;
+
+ Collection* fromCollection = db->getCollection(fromNs);
+ if (!fromCollection)
+ return Status(ErrorCodes::NamespaceNotFound,
+ str::stream() << "source collection " << fromNs << " does not exist");
+
+ if (db->getCollection(toNs))
+ return Status(ErrorCodes::NamespaceExists, "to collection already exists");
+
+ // create new collection
+ {
+ OldClientContext ctx(txn, toNs);
+ BSONObjBuilder spec;
+ spec.appendBool("capped", true);
+ spec.append("size", size);
+ if (temp)
+ spec.appendBool("temp", true);
+
+ WriteUnitOfWork wunit(txn);
+ Status status = userCreateNS(txn, ctx.db(), toNs, spec.done());
+ if (!status.isOK())
+ return status;
+ wunit.commit();
+ }
+
+ Collection* toCollection = db->getCollection(toNs);
+ invariant(toCollection); // we created above
+
+ // how much data to ignore because it won't fit anyway
+ // datasize and extentSize can't be compared exactly, so add some padding to 'size'
+
+ long long allocatedSpaceGuess =
+ std::max(static_cast<long long>(size * 2),
+ static_cast<long long>(toCollection->getRecordStore()->storageSize(txn) * 2));
+
+ long long excessSize = fromCollection->dataSize(txn) - allocatedSpaceGuess;
+
+ boost::scoped_ptr<PlanExecutor> exec(InternalPlanner::collectionScan(
+ txn,
+ fromNs,
+ fromCollection,
+ InternalPlanner::FORWARD));
+
+
+ while (true) {
+ BSONObj obj;
+ PlanExecutor::ExecState state = exec->getNext(&obj, NULL);
+
+ switch(state) {
+ case PlanExecutor::IS_EOF:
+ return Status::OK();
+ case PlanExecutor::DEAD:
+ db->dropCollection(txn, toNs);
+ return Status(ErrorCodes::InternalError, "executor turned dead while iterating");
+ case PlanExecutor::FAILURE:
+ return Status(ErrorCodes::InternalError, "executor error while iterating");
+ case PlanExecutor::ADVANCED:
+ if (excessSize > 0) {
+ excessSize -= (4 * obj.objsize()); // 4x is for padding, power of 2, etc...
+ continue;
+ }
+
+ WriteUnitOfWork wunit(txn);
+ toCollection->insertDocument(txn, obj, true, txn->writesAreReplicated());
+ wunit.commit();
+ }
+ }
+
+ invariant(false); // unreachable
+ }
+
+ Status convertToCapped(OperationContext* txn,
+ const NamespaceString& collectionName,
+ double size) {
+
+ StringData dbname = collectionName.db();
+ StringData shortSource = collectionName.coll();
+
+ ScopedTransaction transaction(txn, MODE_IX);
+ AutoGetDb autoDb(txn, collectionName.db(), MODE_X);
+
+ bool userInitiatedWritesAndNotPrimary = txn->writesAreReplicated() &&
+ !repl::getGlobalReplicationCoordinator()->canAcceptWritesForDatabase(dbname);
+
+ if (userInitiatedWritesAndNotPrimary) {
+ return Status(ErrorCodes::NotMaster,
+ str::stream() << "Not primary while converting "
+ << collectionName.ns() << " to a capped collection");
+ }
+
+ Database* const db = autoDb.getDb();
+ if (!db) {
+ return Status(ErrorCodes::DatabaseNotFound,
+ str::stream() << "database " << dbname << " not found");
+ }
+
+ stopIndexBuildsConvertToCapped(txn, db, collectionName);
+ BackgroundOperation::assertNoBgOpInProgForDb(dbname);
+
+ std::string shortTmpName = str::stream() << "tmp.convertToCapped." << shortSource;
+ std::string longTmpName = str::stream() << dbname << "." << shortTmpName;
+
+ WriteUnitOfWork wunit(txn);
+ if (db->getCollection(longTmpName)) {
+ Status status = db->dropCollection(txn, longTmpName);
+ if (!status.isOK())
+ return status;
+ }
+
+
+ bool shouldReplicateWrites = txn->writesAreReplicated();
+ txn->setReplicatedWrites(false);
+ Status status = cloneCollectionAsCapped(txn,
+ db,
+ shortSource.toString(),
+ shortTmpName,
+ size,
+ true);
+
+ if (!status.isOK()) {
+ txn->setReplicatedWrites(shouldReplicateWrites);
+ return status;
+ }
+
+ verify(db->getCollection(longTmpName));
+
+ status = db->dropCollection(txn, collectionName.ns());
+ txn->setReplicatedWrites(shouldReplicateWrites);
+ if (!status.isOK())
+ return status;
+
+ status = db->renameCollection(txn, longTmpName, collectionName.ns(), false);
+ if (!status.isOK())
+ return status;
+
+ getGlobalServiceContext()->getOpObserver()->onConvertToCapped(
+ txn,
+ NamespaceString(collectionName),
+ size);
+
+ wunit.commit();
+ return Status::OK();
+ }
+
+} // namespace mongo
diff --git a/src/mongo/db/catalog/capped_utils.h b/src/mongo/db/catalog/capped_utils.h
new file mode 100644
index 00000000000..05104230fcf
--- /dev/null
+++ b/src/mongo/db/catalog/capped_utils.h
@@ -0,0 +1,58 @@
+/**
+ * Copyright (C) 2015 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/base/status.h"
+
+namespace mongo {
+ class Database;
+ class NamespaceString;
+ class OperationContext;
+
+ /**
+ * Drops all documents contained in the capped collection, "collectionName".
+ */
+ Status emptyCapped(OperationContext* txn,
+ const NamespaceString& collectionName);
+
+ /**
+ * Clones the collection "shortFrom" to the capped collection "shortTo" with a size of "size".
+ */
+ Status cloneCollectionAsCapped(OperationContext* txn,
+ Database* db,
+ const std::string& shortFrom,
+ const std::string& shortTo,
+ double size,
+ bool temp);
+
+ /**
+ * Converts the collection "collectionName" to a capped collection with a size of "size".
+ */
+ Status convertToCapped(OperationContext* txn,
+ const NamespaceString& collectionName,
+ double size);
+} // namespace mongo
diff --git a/src/mongo/db/catalog/coll_mod.cpp b/src/mongo/db/catalog/coll_mod.cpp
new file mode 100644
index 00000000000..765f2285792
--- /dev/null
+++ b/src/mongo/db/catalog/coll_mod.cpp
@@ -0,0 +1,182 @@
+/**
+ * Copyright (C) 2015 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/catalog/coll_mod.h"
+
+#include "mongo/db/catalog/collection.h"
+#include "mongo/db/catalog/collection_catalog_entry.h"
+#include "mongo/db/catalog/database.h"
+#include "mongo/db/client.h"
+#include "mongo/db/db_raii.h"
+#include "mongo/db/index/index_descriptor.h"
+#include "mongo/db/repl/replication_coordinator_global.h"
+#include "mongo/db/service_context.h"
+
+namespace mongo {
+ Status collMod(OperationContext* txn,
+ const NamespaceString& ns,
+ const BSONObj& cmdObj,
+ BSONObjBuilder* result) {
+ StringData dbName = ns.db();
+ ScopedTransaction transaction(txn, MODE_IX);
+ AutoGetDb autoDb(txn, dbName, MODE_X);
+ Database* const db = autoDb.getDb();
+ Collection* coll = db ? db->getCollection(ns) : NULL;
+
+ // If db/collection does not exist, short circuit and return.
+ if (!db || !coll) {
+ return Status(ErrorCodes::NamespaceNotFound, "ns does not exist");
+ }
+
+ OldClientContext ctx(txn, ns);
+
+ bool userInitiatedWritesAndNotPrimary = txn->writesAreReplicated() &&
+ !repl::getGlobalReplicationCoordinator()->canAcceptWritesForDatabase(dbName);
+
+ if (userInitiatedWritesAndNotPrimary) {
+ return Status(ErrorCodes::NotMaster,
+ str::stream() << "Not primary while setting collection options on "
+ << ns.toString());
+ }
+
+ WriteUnitOfWork wunit(txn);
+
+ Status errorStatus = Status::OK();
+
+ BSONForEach(e, cmdObj) {
+ if (str::equals("collMod", e.fieldName())) {
+ // no-op
+ }
+ else if (str::startsWith(e.fieldName(), "$")) {
+ // no-op ignore top-level fields prefixed with $. They are for the command processor
+ }
+ else if (LiteParsedQuery::cmdOptionMaxTimeMS == e.fieldNameStringData()) {
+ // no-op
+ }
+ else if (str::equals("index", e.fieldName())) {
+ BSONObj indexObj = e.Obj();
+ BSONObj keyPattern = indexObj.getObjectField("keyPattern");
+
+ if (keyPattern.isEmpty()){
+ errorStatus = Status(ErrorCodes::InvalidOptions, "no keyPattern specified");
+ continue;
+ }
+
+ BSONElement newExpireSecs = indexObj["expireAfterSeconds"];
+ if (newExpireSecs.eoo()) {
+ errorStatus = Status(ErrorCodes::InvalidOptions, "no expireAfterSeconds field");
+ continue;
+ }
+ if (! newExpireSecs.isNumber()) {
+ errorStatus = Status(ErrorCodes::InvalidOptions,
+ "expireAfterSeconds field must be a number");
+ continue;
+ }
+
+ const IndexDescriptor* idx = coll->getIndexCatalog()
+ ->findIndexByKeyPattern(txn, keyPattern);
+ if (idx == NULL) {
+ errorStatus = Status(ErrorCodes::InvalidOptions,
+ str::stream() << "cannot find index " << keyPattern
+ << " for ns " << ns.toString());
+ continue;
+ }
+ BSONElement oldExpireSecs = idx->infoObj().getField("expireAfterSeconds");
+ if (oldExpireSecs.eoo()){
+ errorStatus = Status(ErrorCodes::InvalidOptions,
+ "no expireAfterSeconds field to update");
+ continue;
+ }
+ if (! oldExpireSecs.isNumber()) {
+ errorStatus = Status(ErrorCodes::InvalidOptions,
+ "existing expireAfterSeconds field is not a number");
+ continue;
+ }
+
+ if (oldExpireSecs != newExpireSecs) {
+ result->appendAs(oldExpireSecs, "expireAfterSeconds_old");
+ // Change the value of "expireAfterSeconds" on disk.
+ coll->getCatalogEntry()->updateTTLSetting(txn,
+ idx->indexName(),
+ newExpireSecs.numberLong());
+ // Notify the index catalog that the definition of this index changed.
+ idx = coll->getIndexCatalog()->refreshEntry(txn, idx);
+ result->appendAs(newExpireSecs , "expireAfterSeconds_new");
+ }
+ }
+ else {
+ // As of SERVER-17312 we only support these two options. When SERVER-17320 is
+ // resolved this will need to be enhanced to handle other options.
+ typedef CollectionOptions CO;
+ const StringData name = e.fieldNameStringData();
+ const int flag = (name == "usePowerOf2Sizes") ? CO::Flag_UsePowerOf2Sizes :
+ (name == "noPadding") ? CO::Flag_NoPadding :
+ 0;
+ if (!flag) {
+ errorStatus = Status(ErrorCodes::InvalidOptions,
+ str::stream() << "unknown option to collMod: " << name);
+ continue;
+ }
+
+ CollectionCatalogEntry* cce = coll->getCatalogEntry();
+
+ const int oldFlags = cce->getCollectionOptions(txn).flags;
+ const bool oldSetting = oldFlags & flag;
+ const bool newSetting = e.trueValue();
+
+ result->appendBool(name.toString() + "_old", oldSetting);
+ result->appendBool(name.toString() + "_new", newSetting);
+
+ const int newFlags = newSetting
+ ? (oldFlags | flag) // set flag
+ : (oldFlags & ~flag); // clear flag
+
+ // NOTE we do this unconditionally to ensure that we note that the user has
+ // explicitly set flags, even if they are just setting the default.
+ cce->updateFlags(txn, newFlags);
+
+ const CollectionOptions newOptions = cce->getCollectionOptions(txn);
+ invariant(newOptions.flags == newFlags);
+ invariant(newOptions.flagsSet);
+ }
+ }
+
+ if (!errorStatus.isOK()) {
+ return errorStatus;
+ }
+
+ getGlobalServiceContext()->getOpObserver()->onCollMod(txn,
+ (dbName.toString() + ".$cmd").c_str(),
+ cmdObj);
+
+ wunit.commit();
+ return Status::OK();
+ }
+} // namespace mongo
diff --git a/src/mongo/db/catalog/coll_mod.h b/src/mongo/db/catalog/coll_mod.h
new file mode 100644
index 00000000000..1f511f45145
--- /dev/null
+++ b/src/mongo/db/catalog/coll_mod.h
@@ -0,0 +1,45 @@
+/**
+ * Copyright (C) 2015 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/base/status.h"
+
+namespace mongo {
+ class BSONObj;
+ class BSONObjBuilder;
+ class NamespaceString;
+ class OperationContext;
+
+ /**
+ * Performs the collection modification described in "cmdObj" on the collection "ns".
+ */
+ Status collMod(OperationContext* txn,
+ const NamespaceString& ns,
+ const BSONObj& cmdObj,
+ BSONObjBuilder* result);
+} // namespace mongo
+
diff --git a/src/mongo/db/catalog/collection.cpp b/src/mongo/db/catalog/collection.cpp
index 2940e9c7c9b..88f8cde49b3 100644
--- a/src/mongo/db/catalog/collection.cpp
+++ b/src/mongo/db/catalog/collection.cpp
@@ -38,15 +38,19 @@
#include "mongo/base/counter.h"
#include "mongo/base/owned_pointer_map.h"
-#include "mongo/db/clientcursor.h"
-#include "mongo/db/commands/server_status_metric.h"
-#include "mongo/db/curop.h"
#include "mongo/db/catalog/collection_catalog_entry.h"
#include "mongo/db/catalog/database_catalog_entry.h"
#include "mongo/db/catalog/index_create.h"
+#include "mongo/db/clientcursor.h"
+#include "mongo/db/commands/server_status_metric.h"
+#include "mongo/db/curop.h"
#include "mongo/db/index/index_access_method.h"
+#include "mongo/db/op_observer.h"
#include "mongo/db/operation_context.h"
+#include "mongo/db/ops/update_driver.h"
+#include "mongo/db/ops/update_request.h"
#include "mongo/db/repl/replication_coordinator_global.h"
+#include "mongo/db/service_context.h"
#include "mongo/db/storage/mmap_v1/mmap_v1_options.h"
#include "mongo/db/storage/record_fetcher.h"
@@ -169,9 +173,9 @@ namespace mongo {
return true;
}
- StatusWith<RecordId> Collection::insertDocument( OperationContext* txn,
+ StatusWith<RecordId> Collection::insertDocument(OperationContext* txn,
const DocWriter* doc,
- bool enforceQuota ) {
+ bool enforceQuota) {
dassert(txn->lockState()->isCollectionLockedForMode(ns().toString(), MODE_IX));
invariant( !_indexCatalog.haveAnyIndexes() ); // eventually can implement, just not done
@@ -181,12 +185,16 @@ namespace mongo {
if ( !loc.isOK() )
return loc;
+ // we cannot call into the OpObserver here because the document being written is not present
+ // fortunately, this is currently only used for adding entries to the oplog.
+
return StatusWith<RecordId>( loc );
}
- StatusWith<RecordId> Collection::insertDocument( OperationContext* txn,
+ StatusWith<RecordId> Collection::insertDocument(OperationContext* txn,
const BSONObj& docToInsert,
- bool enforceQuota ) {
+ bool enforceQuota,
+ bool fromMigrate) {
const SnapshotId sid = txn->recoveryUnit()->getSnapshotId();
@@ -200,13 +208,19 @@ namespace mongo {
StatusWith<RecordId> res = _insertDocument( txn, docToInsert, enforceQuota );
invariant( sid == txn->recoveryUnit()->getSnapshotId() );
+ if (res.isOK()) {
+ getGlobalServiceContext()->getOpObserver()->onInsert(txn,
+ ns(),
+ docToInsert,
+ fromMigrate);
+ }
return res;
}
- StatusWith<RecordId> Collection::insertDocument( OperationContext* txn,
+ StatusWith<RecordId> Collection::insertDocument(OperationContext* txn,
const BSONObj& doc,
MultiIndexBlock* indexBlock,
- bool enforceQuota ) {
+ bool enforceQuota) {
dassert(txn->lockState()->isCollectionLockedForMode(ns().toString(), MODE_IX));
StatusWith<RecordId> loc = _recordStore->insertRecord( txn,
@@ -221,6 +235,8 @@ namespace mongo {
if ( !status.isOK() )
return StatusWith<RecordId>( status );
+ getGlobalServiceContext()->getOpObserver()->onInsert(txn, ns(), doc);
+
return loc;
}
@@ -271,11 +287,11 @@ namespace mongo {
return Status::OK();
}
- void Collection::deleteDocument( OperationContext* txn,
- const RecordId& loc,
- bool cappedOK,
- bool noWarn,
- BSONObj* deletedId ) {
+ void Collection::deleteDocument(OperationContext* txn,
+ const RecordId& loc,
+ bool cappedOK,
+ bool noWarn,
+ BSONObj* deletedId) {
if ( isCapped() && !cappedOK ) {
log() << "failing remove on a capped ns " << _ns << endl;
uasserted( 10089, "cannot remove from a capped collection" );
@@ -284,9 +300,11 @@ namespace mongo {
Snapshotted<BSONObj> doc = docFor(txn, loc);
- if (deletedId) {
- BSONElement e = doc.value()["_id"];
- if (e.type()) {
+ BSONElement e = doc.value()["_id"];
+ BSONObj id;
+ if (e.type()) {
+ id = e.wrap();
+ if (deletedId) {
*deletedId = e.wrap();
}
}
@@ -299,6 +317,10 @@ namespace mongo {
_recordStore->deleteRecord(txn, loc);
_infoCache.notifyOfWriteOp();
+
+ if (!id.isEmpty()) {
+ getGlobalServiceContext()->getOpObserver()->onDelete(txn, ns().ns(), id);
+ }
}
Counter64 moveCounter;
@@ -306,25 +328,26 @@ namespace mongo {
StatusWith<RecordId> Collection::updateDocument( OperationContext* txn,
const RecordId& oldLocation,
- const Snapshotted<BSONObj>& objOld,
- const BSONObj& objNew,
+ const Snapshotted<BSONObj>& oldDoc,
+ const BSONObj& newDoc,
bool enforceQuota,
bool indexesAffected,
- OpDebug* debug ) {
+ OpDebug* debug,
+ oplogUpdateEntryArgs& args) {
dassert(txn->lockState()->isCollectionLockedForMode(ns().toString(), MODE_IX));
- invariant(objOld.snapshotId() == txn->recoveryUnit()->getSnapshotId());
+ invariant(oldDoc.snapshotId() == txn->recoveryUnit()->getSnapshotId());
SnapshotId sid = txn->recoveryUnit()->getSnapshotId();
- BSONElement oldId = objOld.value()["_id"];
- if ( !oldId.eoo() && ( oldId != objNew["_id"] ) )
+ BSONElement oldId = oldDoc.value()["_id"];
+ if ( !oldId.eoo() && ( oldId != newDoc["_id"] ) )
return StatusWith<RecordId>( ErrorCodes::InternalError,
"in Collection::updateDocument _id mismatch",
13596 );
// At the end of this step, we will have a map of UpdateTickets, one per index, which
- // represent the index updates needed to be done, based on the changes between objOld and
- // objNew.
+ // represent the index updates needed to be done, based on the changes between oldDoc and
+ // newDoc.
OwnedPointerMap<IndexDescriptor*,UpdateTicket> updateTickets;
if ( indexesAffected ) {
IndexCatalog::IndexIterator ii = _indexCatalog.getIndexIterator( txn, true );
@@ -341,8 +364,8 @@ namespace mongo {
UpdateTicket* updateTicket = new UpdateTicket();
updateTickets.mutableMap()[descriptor] = updateTicket;
Status ret = iam->validateUpdate(txn,
- objOld.value(),
- objNew,
+ oldDoc.value(),
+ newDoc,
oldLocation,
options,
updateTicket,
@@ -357,8 +380,8 @@ namespace mongo {
// object is removed from all indexes.
StatusWith<RecordId> newLocation = _recordStore->updateRecord( txn,
oldLocation,
- objNew.objdata(),
- objNew.objsize(),
+ newDoc.objdata(),
+ newDoc.objsize(),
_enforceQuota( enforceQuota ),
this );
@@ -381,10 +404,13 @@ namespace mongo {
debug->nmoved += 1;
}
- Status s = _indexCatalog.indexRecord(txn, objNew, newLocation.getValue());
+ Status s = _indexCatalog.indexRecord(txn, newDoc, newLocation.getValue());
if (!s.isOK())
return StatusWith<RecordId>(s);
invariant( sid == txn->recoveryUnit()->getSnapshotId() );
+ args.ns = ns().ns();
+ getGlobalServiceContext()->getOpObserver()->onUpdate(txn, args);
+
return newLocation;
}
@@ -410,6 +436,9 @@ namespace mongo {
}
invariant( sid == txn->recoveryUnit()->getSnapshotId() );
+ args.ns = ns().ns();
+ getGlobalServiceContext()->getOpObserver()->onUpdate(txn, args);
+
return newLocation;
}
@@ -435,7 +464,8 @@ namespace mongo {
const RecordId& loc,
const Snapshotted<RecordData>& oldRec,
const char* damageSource,
- const mutablebson::DamageVector& damages ) {
+ const mutablebson::DamageVector& damages,
+ oplogUpdateEntryArgs& args) {
dassert(txn->lockState()->isCollectionLockedForMode(ns().toString(), MODE_IX));
invariant(oldRec.snapshotId() == txn->recoveryUnit()->getSnapshotId());
@@ -443,7 +473,14 @@ namespace mongo {
// Broadcast the mutation so that query results stay correct.
_cursorManager.invalidateDocument(txn, loc, INVALIDATION_MUTATION);
- return _recordStore->updateWithDamages(txn, loc, oldRec.value(), damageSource, damages);
+ Status status =
+ _recordStore->updateWithDamages(txn, loc, oldRec.value(), damageSource, damages);
+
+ if (status.isOK()) {
+ args.ns = ns().ns();
+ getGlobalServiceContext()->getOpObserver()->onUpdate(txn, args);
+ }
+ return status;
}
bool Collection::_enforceQuota( bool userEnforeQuota ) const {
diff --git a/src/mongo/db/catalog/collection.h b/src/mongo/db/catalog/collection.h
index f1518065aaf..9f537cb5fdb 100644
--- a/src/mongo/db/catalog/collection.h
+++ b/src/mongo/db/catalog/collection.h
@@ -39,6 +39,7 @@
#include "mongo/db/catalog/index_catalog.h"
#include "mongo/db/exec/collection_scan_common.h"
#include "mongo/db/namespace_string.h"
+#include "mongo/db/op_observer.h"
#include "mongo/db/record_id.h"
#include "mongo/db/storage/capped_callback.h"
#include "mongo/db/storage/record_store.h"
@@ -54,6 +55,9 @@ namespace mongo {
class MultiIndexBlock;
class OperationContext;
+ class UpdateDriver;
+ class UpdateRequest;
+
class RecordIterator;
class RecordFetcher;
@@ -167,7 +171,8 @@ namespace mongo {
*/
StatusWith<RecordId> insertDocument( OperationContext* txn,
const BSONObj& doc,
- bool enforceQuota );
+ bool enforceQuota,
+ bool fromMigrate = false);
StatusWith<RecordId> insertDocument( OperationContext* txn,
const DocWriter* doc,
@@ -196,22 +201,23 @@ namespace mongo {
* if not, it is moved
* @return the post update location of the doc (may or may not be the same as oldLocation)
*/
- StatusWith<RecordId> updateDocument( OperationContext* txn,
- const RecordId& oldLocation,
- const Snapshotted<BSONObj>& oldDoc,
- const BSONObj& newDoc,
- bool enforceQuota,
- bool indexesAffected,
- OpDebug* debug );
-
+ StatusWith<RecordId> updateDocument(OperationContext* txn,
+ const RecordId& oldLocation,
+ const Snapshotted<BSONObj>& oldDoc,
+ const BSONObj& newDoc,
+ bool enforceQuota,
+ bool indexesAffected,
+ OpDebug* debug,
+ oplogUpdateEntryArgs& args);
/**
* right now not allowed to modify indexes
*/
- Status updateDocumentWithDamages( OperationContext* txn,
- const RecordId& loc,
- const Snapshotted<RecordData>& oldRec,
- const char* damageSource,
- const mutablebson::DamageVector& damages );
+ Status updateDocumentWithDamages(OperationContext* txn,
+ const RecordId& loc,
+ const Snapshotted<RecordData>& oldRec,
+ const char* damageSource,
+ const mutablebson::DamageVector& damages,
+ oplogUpdateEntryArgs& args);
// -----------
diff --git a/src/mongo/db/catalog/database.cpp b/src/mongo/db/catalog/database.cpp
index 814184ef694..46b58a16618 100644
--- a/src/mongo/db/catalog/database.cpp
+++ b/src/mongo/db/catalog/database.cpp
@@ -55,6 +55,7 @@
#include "mongo/db/introspect.h"
#include "mongo/db/op_observer.h"
#include "mongo/db/repl/oplog.h"
+#include "mongo/db/repl/replication_coordinator_global.h"
#include "mongo/db/server_parameters.h"
#include "mongo/db/stats/top.h"
#include "mongo/db/storage_options.h"
@@ -272,8 +273,6 @@ namespace mongo {
continue;
}
- getGlobalServiceContext()->getOpObserver()->onDropCollection(
- txn, NamespaceString(ns));
wunit.commit();
}
catch (const WriteConflictException& exp) {
@@ -351,7 +350,7 @@ namespace mongo {
_dbEntry->appendExtraStats( opCtx, output, scale );
}
- Status Database::dropCollection( OperationContext* txn, StringData fullns ) {
+ Status Database::dropCollection(OperationContext* txn, StringData fullns) {
invariant(txn->lockState()->isDbLockedForMode(name(), MODE_X));
LOG(1) << "dropCollection: " << fullns << endl;
@@ -363,12 +362,12 @@ namespace mongo {
return Status::OK();
}
+ NamespaceString nss(fullns);
{
- NamespaceString s( fullns );
- verify( s.db() == _name );
+ verify(nss.db() == _name);
- if( s.isSystem() ) {
- if( s.coll() == "system.profile" ) {
+ if (nss.isSystem()) {
+ if (nss.isSystemDotProfile()) {
if ( _profile != 0 )
return Status( ErrorCodes::IllegalOperation,
"turn off profiling before dropping system.profile collection" );
@@ -417,6 +416,7 @@ namespace mongo {
}
}
+ getGlobalServiceContext()->getOpObserver()->onDropCollection(txn, nss);
return Status::OK();
}
@@ -486,7 +486,6 @@ namespace mongo {
Collection* Database::createCollection( OperationContext* txn,
StringData ns,
const CollectionOptions& options,
- bool allocateDefaultSpace,
bool createIdIndex ) {
massert( 17399, "collection already exists", getCollection( ns ) == NULL );
massertNamespaceNotIndex( ns, "createCollection" );
@@ -513,7 +512,7 @@ namespace mongo {
txn->recoveryUnit()->registerChange( new AddCollectionChange(this, ns) );
- Status status = _dbEntry->createCollection(txn, ns, options, allocateDefaultSpace);
+ Status status = _dbEntry->createCollection(txn, ns, options, true /*allocateDefaultSpace*/);
massertNoTraceStatusOK(status);
@@ -537,6 +536,8 @@ namespace mongo {
}
+ getGlobalServiceContext()->getOpObserver()->onCreateCollection(txn, nss, options);
+
return collection;
}
@@ -592,7 +593,6 @@ namespace mongo {
Database* db,
StringData ns,
BSONObj options,
- bool logForReplication,
bool createDefaultIndexes ) {
invariant( db );
@@ -619,13 +619,7 @@ namespace mongo {
if ( !status.isOK() )
return status;
- invariant( db->createCollection( txn, ns, collectionOptions, true, createDefaultIndexes ) );
-
- if ( logForReplication ) {
- getGlobalServiceContext()->getOpObserver()->onCreateCollection(txn,
- NamespaceString(ns),
- collectionOptions);
- }
+ invariant(db->createCollection(txn, ns, collectionOptions, createDefaultIndexes));
return Status::OK();
}
diff --git a/src/mongo/db/catalog/database.h b/src/mongo/db/catalog/database.h
index 57fbece74d8..251946310fb 100644
--- a/src/mongo/db/catalog/database.h
+++ b/src/mongo/db/catalog/database.h
@@ -82,12 +82,11 @@ namespace mongo {
const DatabaseCatalogEntry* getDatabaseCatalogEntry() const;
- Status dropCollection( OperationContext* txn, StringData fullns );
+ Status dropCollection(OperationContext* txn, StringData fullns);
Collection* createCollection( OperationContext* txn,
StringData ns,
const CollectionOptions& options = CollectionOptions(),
- bool allocateSpace = true,
bool createDefaultIndexes = true );
/**
@@ -162,7 +161,6 @@ namespace mongo {
Database* db,
StringData ns,
BSONObj options,
- bool logForReplication,
bool createDefaultIndexes = true );
} // namespace mongo
diff --git a/src/mongo/db/catalog/drop_collection.cpp b/src/mongo/db/catalog/drop_collection.cpp
new file mode 100644
index 00000000000..bf58462449f
--- /dev/null
+++ b/src/mongo/db/catalog/drop_collection.cpp
@@ -0,0 +1,109 @@
+/**
+ * Copyright (C) 2015 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/catalog/drop_collection.h"
+
+#include "mongo/db/catalog/collection.h"
+#include "mongo/db/catalog/database.h"
+#include "mongo/db/catalog/index_catalog.h"
+#include "mongo/db/client.h"
+#include "mongo/db/concurrency/write_conflict_exception.h"
+#include "mongo/db/curop.h"
+#include "mongo/db/db_raii.h"
+#include "mongo/db/index_builder.h"
+#include "mongo/db/operation_context_impl.h"
+#include "mongo/db/repl/replication_coordinator_global.h"
+#include "mongo/db/server_options.h"
+#include "mongo/db/service_context.h"
+#include "mongo/util/log.h"
+
+namespace mongo {
+namespace {
+ std::vector<BSONObj> stopIndexBuilds(OperationContext* opCtx,
+ Database* db,
+ const NamespaceString& collectionName) {
+ IndexCatalog::IndexKillCriteria criteria;
+ criteria.ns = collectionName;
+ return IndexBuilder::killMatchingIndexBuilds(db->getCollection(collectionName),
+ criteria);
+ }
+
+} // namespace
+ Status dropCollection(OperationContext* txn,
+ const NamespaceString& collectionName,
+ BSONObjBuilder& result) {
+ if (!serverGlobalParams.quiet) {
+ log() << "CMD: drop " << collectionName;
+ }
+
+ std::string dbname = collectionName.db().toString();
+
+ MONGO_WRITE_CONFLICT_RETRY_LOOP_BEGIN {
+ ScopedTransaction transaction(txn, MODE_IX);
+
+ AutoGetDb autoDb(txn, dbname, MODE_X);
+ Database* const db = autoDb.getDb();
+ Collection* coll = db ? db->getCollection(collectionName) : nullptr;
+
+ // If db/collection does not exist, short circuit and return.
+ if ( !db || !coll ) {
+ return Status(ErrorCodes::NamespaceNotFound, "ns not found");
+ }
+ OldClientContext context(txn, collectionName);
+
+ bool userInitiatedWritesAndNotPrimary = txn->writesAreReplicated() &&
+ !repl::getGlobalReplicationCoordinator()->canAcceptWritesForDatabase(dbname);
+
+ if (userInitiatedWritesAndNotPrimary) {
+ return Status(ErrorCodes::NotMaster,
+ str::stream() << "Not primary while dropping collection "
+ << collectionName.ns());
+ }
+
+ int numIndexes = coll->getIndexCatalog()->numIndexesTotal(txn);
+
+ stopIndexBuilds(txn, db, collectionName);
+
+ result.append("ns", collectionName);
+ result.append("nIndexesWas", numIndexes);
+ WriteUnitOfWork wunit(txn);
+ Status s = db->dropCollection(txn, collectionName.ns());
+
+ if ( !s.isOK() ) {
+ return s;
+ }
+
+ wunit.commit();
+ } MONGO_WRITE_CONFLICT_RETRY_LOOP_END(txn, "drop", collectionName.ns());
+ return Status::OK();
+ }
+} // namespace mongo
diff --git a/src/mongo/db/catalog/drop_collection.h b/src/mongo/db/catalog/drop_collection.h
new file mode 100644
index 00000000000..a12f5e8419c
--- /dev/null
+++ b/src/mongo/db/catalog/drop_collection.h
@@ -0,0 +1,43 @@
+/**
+ * Copyright (C) 2015 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/base/status.h"
+
+namespace mongo {
+ class BSONObjBuilder;
+ class NamespaceString;
+ class OperationContext;
+
+ /**
+ * Drops the collection "collectionName" and populates "result" with statistics about what
+ * was removed.
+ */
+ Status dropCollection(OperationContext* txn,
+ const NamespaceString& collectionName,
+ BSONObjBuilder& result);
+} // namespace mongo
diff --git a/src/mongo/db/catalog/drop_database.cpp b/src/mongo/db/catalog/drop_database.cpp
new file mode 100644
index 00000000000..655305d216e
--- /dev/null
+++ b/src/mongo/db/catalog/drop_database.cpp
@@ -0,0 +1,111 @@
+/**
+ * Copyright (C) 2015 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/catalog/drop_database.h"
+
+#include "mongo/db/catalog/database.h"
+#include "mongo/db/catalog/database_catalog_entry.h"
+#include "mongo/db/catalog/index_catalog.h"
+#include "mongo/db/client.h"
+#include "mongo/db/concurrency/write_conflict_exception.h"
+#include "mongo/db/curop.h"
+#include "mongo/db/db_raii.h"
+#include "mongo/db/index_builder.h"
+#include "mongo/db/op_observer.h"
+#include "mongo/db/operation_context_impl.h"
+#include "mongo/db/repl/replication_coordinator_global.h"
+#include "mongo/db/service_context.h"
+#include "mongo/util/log.h"
+
+namespace mongo {
+namespace {
+ std::vector<BSONObj> stopIndexBuilds(OperationContext* opCtx, Database* db) {
+ invariant(db);
+ std::list<std::string> collections;
+ db->getDatabaseCatalogEntry()->getCollectionNamespaces(&collections);
+
+ std::vector<BSONObj> allKilledIndexes;
+ for (std::list<std::string>::iterator it = collections.begin();
+ it != collections.end();
+ ++it) {
+ std::string ns = *it;
+
+ IndexCatalog::IndexKillCriteria criteria;
+ criteria.ns = ns;
+ std::vector<BSONObj> killedIndexes =
+ IndexBuilder::killMatchingIndexBuilds(db->getCollection(ns), criteria);
+ allKilledIndexes.insert(allKilledIndexes.end(),
+ killedIndexes.begin(),
+ killedIndexes.end());
+ }
+ return allKilledIndexes;
+ }
+} // namespace
+
+ Status dropDatabase(OperationContext* txn, const std::string& dbName) {
+ MONGO_WRITE_CONFLICT_RETRY_LOOP_BEGIN {
+ ScopedTransaction transaction(txn, MODE_X);
+ Lock::GlobalWrite lk(txn->lockState());
+ AutoGetDb autoDB(txn, dbName, MODE_X);
+ Database* const db = autoDB.getDb();
+ if (!db) {
+ // DB doesn't exist, so deem it a success.
+ return Status::OK();
+ }
+ OldClientContext context(txn, dbName);
+
+ bool userInitiatedWritesAndNotPrimary = txn->writesAreReplicated() &&
+ !repl::getGlobalReplicationCoordinator()->canAcceptWritesForDatabase(dbName);
+
+ if (userInitiatedWritesAndNotPrimary) {
+ return Status(ErrorCodes::NotMaster,
+ str::stream() << "Not primary while dropping database " << dbName);
+ }
+
+ log() << "dropDatabase " << dbName << " starting";
+
+ stopIndexBuilds(txn, db);
+ mongo::dropDatabase(txn, db);
+
+ log() << "dropDatabase " << dbName << " finished";
+
+ WriteUnitOfWork wunit(txn);
+
+ getGlobalServiceContext()->getOpObserver()->onDropDatabase(txn, dbName + ".$cmd");
+
+ wunit.commit();
+ } MONGO_WRITE_CONFLICT_RETRY_LOOP_END(txn, "dropDatabase", dbName);
+
+ return Status::OK();
+ }
+
+} // namespace mongo
diff --git a/src/mongo/db/catalog/drop_database.h b/src/mongo/db/catalog/drop_database.h
new file mode 100644
index 00000000000..184d66d5bf2
--- /dev/null
+++ b/src/mongo/db/catalog/drop_database.h
@@ -0,0 +1,38 @@
+/**
+ * Copyright (C) 2015 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/base/status.h"
+
+namespace mongo {
+ class OperationContext;
+
+ /**
+ * Drops the database "dbName".
+ */
+ Status dropDatabase(OperationContext* txn, const std::string& dbName);
+} // namespace mongo
diff --git a/src/mongo/db/catalog/drop_indexes.cpp b/src/mongo/db/catalog/drop_indexes.cpp
new file mode 100644
index 00000000000..0ac1c2dae13
--- /dev/null
+++ b/src/mongo/db/catalog/drop_indexes.cpp
@@ -0,0 +1,194 @@
+/**
+ * Copyright (C) 2015 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/catalog/drop_indexes.h"
+
+#include "mongo/db/catalog/collection.h"
+#include "mongo/db/catalog/database.h"
+#include "mongo/db/catalog/index_catalog.h"
+#include "mongo/db/client.h"
+#include "mongo/db/concurrency/write_conflict_exception.h"
+#include "mongo/db/curop.h"
+#include "mongo/db/db_raii.h"
+#include "mongo/db/index/index_descriptor.h"
+#include "mongo/db/index_builder.h"
+#include "mongo/db/repl/replication_coordinator_global.h"
+#include "mongo/db/service_context.h"
+#include "mongo/util/log.h"
+
+namespace mongo {
+namespace {
+ std::vector<BSONObj> stopIndexBuilds(OperationContext* opCtx,
+ Database* db,
+ const std::string& toDeleteNs,
+ const BSONObj& cmdObj) {
+ Collection* collection = db->getCollection(toDeleteNs);
+ IndexCatalog::IndexKillCriteria criteria;
+
+ // Get index name to drop
+ BSONElement toDrop = cmdObj.getField("index");
+
+ if (toDrop.type() == String) {
+ // Kill all in-progress indexes
+ if (strcmp("*", toDrop.valuestr()) == 0) {
+ criteria.ns = toDeleteNs;
+ return IndexBuilder::killMatchingIndexBuilds(collection, criteria);
+ }
+ // Kill an in-progress index by name
+ else {
+ criteria.name = toDrop.valuestr();
+ return IndexBuilder::killMatchingIndexBuilds(collection, criteria);
+ }
+ }
+ // Kill an in-progress index build by index key
+ else if (toDrop.type() == Object) {
+ criteria.key = toDrop.Obj();
+ return IndexBuilder::killMatchingIndexBuilds(collection, criteria);
+ }
+
+ return std::vector<BSONObj>();
+ }
+
+ Status wrappedRun(OperationContext* txn,
+ const StringData& dbname,
+ const std::string& toDeleteNs,
+ Database* const db,
+ const BSONObj& jsobj,
+ BSONObjBuilder* anObjBuilder) {
+ if (!serverGlobalParams.quiet) {
+ LOG(0) << "CMD: dropIndexes " << toDeleteNs;
+ }
+ Collection* collection = db ? db->getCollection(toDeleteNs) : nullptr;
+
+ // If db/collection does not exist, short circuit and return.
+ if (!db || !collection) {
+ return Status(ErrorCodes::NamespaceNotFound, "ns not found");
+ }
+
+ OldClientContext ctx(txn, toDeleteNs);
+ stopIndexBuilds(txn, db, toDeleteNs, jsobj);
+
+ IndexCatalog* indexCatalog = collection->getIndexCatalog();
+ anObjBuilder->appendNumber("nIndexesWas", indexCatalog->numIndexesTotal(txn));
+
+
+ BSONElement f = jsobj.getField("index");
+ if (f.type() == String) {
+
+ std::string indexToDelete = f.valuestr();
+
+ if (indexToDelete == "*") {
+ Status s = indexCatalog->dropAllIndexes(txn, false);
+ if (!s.isOK()) {
+ return s;
+ }
+ anObjBuilder->append("msg", "non-_id indexes dropped for collection");
+ return Status::OK();
+ }
+
+ IndexDescriptor* desc = collection->getIndexCatalog()->findIndexByName(txn,
+ indexToDelete);
+ if (desc == NULL) {
+ return Status(ErrorCodes::IndexNotFound,
+ str::stream() << "index not found with name ["
+ << indexToDelete << "]");
+ }
+
+ if (desc->isIdIndex()) {
+ return Status(ErrorCodes::InvalidOptions, "cannot drop _id index");
+ }
+
+ Status s = indexCatalog->dropIndex(txn, desc);
+ if (!s.isOK()) {
+ return s;
+ }
+
+ return Status::OK();
+ }
+
+ if (f.type() == Object) {
+ IndexDescriptor* desc =
+ collection->getIndexCatalog()->findIndexByKeyPattern(txn, f.embeddedObject());
+ if (desc == NULL) {
+ return Status(ErrorCodes::InvalidOptions,
+ str::stream() << "can't find index with key: "
+ << f.embeddedObject().toString());
+ }
+
+ if (desc->isIdIndex()) {
+ return Status(ErrorCodes::InvalidOptions, "cannot drop _id index");
+ }
+
+ Status s = indexCatalog->dropIndex(txn, desc);
+ if (!s.isOK()) {
+ return s;
+ }
+
+ return Status::OK();
+ }
+
+ return Status(ErrorCodes::IndexNotFound, "invalid index name spec");
+ }
+} // namespace
+
+ Status dropIndexes(OperationContext* txn,
+ const NamespaceString& ns,
+ const BSONObj& idxDescriptor,
+ BSONObjBuilder* result) {
+ StringData dbName = ns.db();
+ MONGO_WRITE_CONFLICT_RETRY_LOOP_BEGIN {
+ ScopedTransaction transaction(txn, MODE_IX);
+ AutoGetDb autoDb(txn, dbName, MODE_X);
+
+ bool userInitiatedWritesAndNotPrimary = txn->writesAreReplicated() &&
+ !repl::getGlobalReplicationCoordinator()->canAcceptWritesForDatabase(dbName);
+
+ if (userInitiatedWritesAndNotPrimary) {
+ return Status(ErrorCodes::NotMaster,
+ str::stream() << "Not primary while dropping indexes in "
+ << ns.toString());
+ }
+
+ WriteUnitOfWork wunit(txn);
+ Status status = wrappedRun(txn, dbName, ns, autoDb.getDb(), idxDescriptor, result);
+ if (!status.isOK()) {
+ return status;
+ }
+ getGlobalServiceContext()->getOpObserver()->onDropIndex(txn,
+ dbName.toString() + ".$cmd",
+ idxDescriptor);
+ wunit.commit();
+ } MONGO_WRITE_CONFLICT_RETRY_LOOP_END(txn, "dropIndexes", dbName);
+ return Status::OK();
+ }
+
+} // namespace mongo
diff --git a/src/mongo/db/catalog/drop_indexes.h b/src/mongo/db/catalog/drop_indexes.h
new file mode 100644
index 00000000000..ba07687098e
--- /dev/null
+++ b/src/mongo/db/catalog/drop_indexes.h
@@ -0,0 +1,46 @@
+/**
+ * Copyright (C) 2015 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/base/status.h"
+
+namespace mongo {
+ class BSONObj;
+ class BSONObjBuilder;
+ class NamespaceString;
+ class OperationContext;
+
+ /**
+ * Drops the index from collection "ns" that matches the "idxDescriptor" and populates
+ * "result" with some statistics about the dropped index.
+ */
+ Status dropIndexes(OperationContext* txn,
+ const NamespaceString& ns,
+ const BSONObj& idxDescriptor,
+ BSONObjBuilder* result);
+
+} // namespace mongo
diff --git a/src/mongo/db/catalog/rename_collection.cpp b/src/mongo/db/catalog/rename_collection.cpp
new file mode 100644
index 00000000000..8d21fea5d8c
--- /dev/null
+++ b/src/mongo/db/catalog/rename_collection.cpp
@@ -0,0 +1,292 @@
+/**
+ * Copyright (C) 2015 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/catalog/rename_collection.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/database_holder.h"
+#include "mongo/db/catalog/index_catalog.h"
+#include "mongo/db/catalog/index_create.h"
+#include "mongo/db/client.h"
+#include "mongo/db/db_raii.h"
+#include "mongo/db/index/index_descriptor.h"
+#include "mongo/db/index_builder.h"
+#include "mongo/db/jsobj.h"
+#include "mongo/db/repl/replication_coordinator_global.h"
+#include "mongo/db/service_context.h"
+#include "mongo/util/scopeguard.h"
+
+namespace mongo {
+namespace {
+ static void dropCollection(OperationContext* txn, Database* db, StringData collName) {
+ WriteUnitOfWork wunit(txn);
+ if (db->dropCollection(txn, collName).isOK()) {
+ // ignoring failure case
+ wunit.commit();
+ }
+ }
+
+ // renameCollection's
+ std::vector<BSONObj> stopIndexBuilds(OperationContext* opCtx,
+ Database* db,
+ const NamespaceString& source,
+ const NamespaceString& target) {
+
+ IndexCatalog::IndexKillCriteria criteria;
+ criteria.ns = source;
+ std::vector<BSONObj> prelim =
+ IndexBuilder::killMatchingIndexBuilds(db->getCollection(source), criteria);
+
+ std::vector<BSONObj> indexes;
+
+ for (int i = 0; i < static_cast<int>(prelim.size()); i++) {
+ // Change the ns
+ BSONObj stripped = prelim[i].removeField("ns");
+ BSONObjBuilder builder;
+ builder.appendElements(stripped);
+ builder.append("ns", target);
+ indexes.push_back(builder.obj());
+ }
+
+ return indexes;
+ }
+} // namespace
+
+ Status renameCollection(OperationContext* txn,
+ const NamespaceString& source,
+ const NamespaceString& target,
+ bool dropTarget,
+ bool stayTemp) {
+ ScopedTransaction transaction(txn, MODE_X);
+ Lock::GlobalWrite globalWriteLock(txn->lockState());
+ // We stay in source context the whole time. This is mostly to set the CurOp namespace.
+ OldClientContext ctx(txn, source);
+
+ bool userInitiatedWritesAndNotPrimary = txn->writesAreReplicated() &&
+ !repl::getGlobalReplicationCoordinator()->canAcceptWritesForDatabase(source.db());
+
+ if (userInitiatedWritesAndNotPrimary) {
+ return Status(ErrorCodes::NotMaster, str::stream()
+ << "Not primary while renaming collection " << source.ns()
+ << " to " << target.ns());
+ }
+
+ Database* const sourceDB = dbHolder().get(txn, source.db());
+ Collection* const sourceColl = sourceDB ? sourceDB->getCollection(source.ns()) : nullptr;
+ if (!sourceColl) {
+ return Status(ErrorCodes::NamespaceNotFound, "source namespace does not exist");
+ }
+
+ {
+ // Ensure that collection name does not exceed maximum length.
+ // Ensure that index names do not push the length over the max.
+ // Iterator includes unfinished indexes.
+ IndexCatalog::IndexIterator sourceIndIt =
+ sourceColl->getIndexCatalog()->getIndexIterator(txn, true);
+ int longestIndexNameLength = 0;
+ while (sourceIndIt.more()) {
+ int thisLength = sourceIndIt.next()->indexName().length();
+ if (thisLength > longestIndexNameLength)
+ longestIndexNameLength = thisLength;
+ }
+
+ unsigned int longestAllowed =
+ std::min(int(NamespaceString::MaxNsCollectionLen),
+ int(NamespaceString::MaxNsLen) - 2/*strlen(".$")*/ - longestIndexNameLength);
+ if (target.size() > longestAllowed) {
+ StringBuilder sb;
+ sb << "collection name length of " << target.size()
+ << " exceeds maximum length of " << longestAllowed
+ << ", allowing for index names";
+ return Status(ErrorCodes::InvalidLength, sb.str());
+ }
+ }
+
+ const std::vector<BSONObj> indexesInProg = stopIndexBuilds(txn, sourceDB, source, target);
+ // Dismissed on success
+ ScopeGuard indexBuildRestorer = MakeGuard(IndexBuilder::restoreIndexes, txn, indexesInProg);
+
+ Database* const targetDB = dbHolder().openDb(txn, target.db());
+
+ {
+ WriteUnitOfWork wunit(txn);
+
+ // Check if the target namespace exists and if dropTarget is true.
+ // If target exists and dropTarget is not true, return false.
+ if (targetDB->getCollection(target)) {
+ if (!dropTarget) {
+ printStackTrace();
+ return Status(ErrorCodes::NamespaceExists, "target namespace exists");
+ }
+
+ Status s = targetDB->dropCollection(txn, target.ns());
+ if (!s.isOK()) {
+ return s;
+ }
+ }
+
+ // If we are renaming in the same database, just
+ // rename the namespace and we're done.
+ if (sourceDB == targetDB) {
+ Status s = targetDB->renameCollection(txn, source.ns(), target.ns(), stayTemp);
+ if (!s.isOK()) {
+ return s;
+ }
+
+ getGlobalServiceContext()->getOpObserver()->onRenameCollection(
+ txn,
+ NamespaceString(source),
+ NamespaceString(target),
+ dropTarget,
+ stayTemp);
+
+ wunit.commit();
+ indexBuildRestorer.Dismiss();
+ return Status::OK();
+ }
+
+ wunit.commit();
+ }
+
+ // If we get here, we are renaming across databases, so we must copy all the data and
+ // indexes, then remove the source collection.
+
+ // Create the target collection. It will be removed if we fail to copy the collection.
+ // TODO use a temp collection and unset the temp flag on success.
+ Collection* targetColl = nullptr;
+ {
+ CollectionOptions options;
+ options.setNoIdIndex();
+
+ if (sourceColl->isCapped()) {
+ const CollectionOptions sourceOpts =
+ sourceColl->getCatalogEntry()->getCollectionOptions(txn);
+
+ options.capped = true;
+ options.cappedSize = sourceOpts.cappedSize;
+ options.cappedMaxDocs = sourceOpts.cappedMaxDocs;
+ }
+
+ WriteUnitOfWork wunit(txn);
+
+ // No logOp necessary because the entire renameCollection command is one logOp.
+ bool shouldReplicateWrites = txn->writesAreReplicated();
+ txn->setReplicatedWrites(false);
+ targetColl = targetDB->createCollection(txn, target.ns(), options);
+ txn->setReplicatedWrites(shouldReplicateWrites);
+ if (!targetColl) {
+ return Status(ErrorCodes::OutOfDiskSpace, "Failed to create target collection.");
+ }
+
+ wunit.commit();
+ }
+
+ // Dismissed on success
+ ScopeGuard targetCollectionDropper = MakeGuard(dropCollection, txn, targetDB, target.ns());
+
+ MultiIndexBlock indexer(txn, targetColl);
+ indexer.allowInterruption();
+
+ // Copy the index descriptions from the source collection, adjusting the ns field.
+ {
+ std::vector<BSONObj> indexesToCopy;
+ IndexCatalog::IndexIterator sourceIndIt =
+ sourceColl->getIndexCatalog()->getIndexIterator(txn, true);
+ while (sourceIndIt.more()) {
+ const BSONObj currIndex = sourceIndIt.next()->infoObj();
+
+ // Process the source index.
+ BSONObjBuilder newIndex;
+ newIndex.append("ns", target);
+ newIndex.appendElementsUnique(currIndex);
+ indexesToCopy.push_back(newIndex.obj());
+ }
+ indexer.init(indexesToCopy);
+ }
+
+ {
+ // Copy over all the data from source collection to target collection.
+ boost::scoped_ptr<RecordIterator> sourceIt(sourceColl->getIterator(txn));
+ while (!sourceIt->isEOF()) {
+ txn->checkForInterrupt();
+
+ const Snapshotted<BSONObj> obj = sourceColl->docFor(txn, sourceIt->getNext());
+
+ WriteUnitOfWork wunit(txn);
+ // No logOp necessary because the entire renameCollection command is one logOp.
+ bool shouldReplicateWrites = txn->writesAreReplicated();
+ txn->setReplicatedWrites(false);
+ Status status =
+ targetColl->insertDocument(txn, obj.value(), &indexer, true).getStatus();
+ txn->setReplicatedWrites(shouldReplicateWrites);
+ if (!status.isOK())
+ return status;
+ wunit.commit();
+ }
+ }
+
+ Status status = indexer.doneInserting();
+ if (!status.isOK())
+ return status;
+
+ {
+ // Getting here means we successfully built the target copy. We now remove the
+ // source collection and finalize the rename.
+ WriteUnitOfWork wunit(txn);
+
+ bool shouldReplicateWrites = txn->writesAreReplicated();
+ txn->setReplicatedWrites(false);
+ Status status = sourceDB->dropCollection(txn, source.ns());
+ txn->setReplicatedWrites(shouldReplicateWrites);
+ if (!status.isOK())
+ return status;
+
+ indexer.commit();
+
+ getGlobalServiceContext()->getOpObserver()->onRenameCollection(
+ txn,
+ NamespaceString(source),
+ NamespaceString(target),
+ dropTarget,
+ stayTemp);
+
+ wunit.commit();
+ }
+
+ indexBuildRestorer.Dismiss();
+ targetCollectionDropper.Dismiss();
+ return Status::OK();
+ }
+
+} // namespace mongo
diff --git a/src/mongo/db/catalog/rename_collection.h b/src/mongo/db/catalog/rename_collection.h
new file mode 100644
index 00000000000..1ec0b754779
--- /dev/null
+++ b/src/mongo/db/catalog/rename_collection.h
@@ -0,0 +1,46 @@
+/**
+ * Copyright (C) 2015 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/base/status.h"
+
+namespace mongo {
+ class NamespaceString;
+ class OperationContext;
+
+ /**
+ * Renames the collection "source" to "target" and drops the existing collection named "target"
+ * iff "dropTarget" is true. "stayTemp" indicates whether a collection should maintain its
+ * temporariness.
+ */
+ Status renameCollection(OperationContext* txn,
+ const NamespaceString& source,
+ const NamespaceString& target,
+ bool dropTarget,
+ bool stayTemp);
+
+} // namespace mongo