summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--jstests/replsets/apply_ops_idempotency.js19
-rw-r--r--src/mongo/db/catalog/rename_collection.cpp115
-rw-r--r--src/mongo/db/catalog/rename_collection.h8
-rw-r--r--src/mongo/db/catalog/rename_collection_test.cpp228
4 files changed, 339 insertions, 31 deletions
diff --git a/jstests/replsets/apply_ops_idempotency.js b/jstests/replsets/apply_ops_idempotency.js
index c15e0b1c63a..7173bbd0301 100644
--- a/jstests/replsets/apply_ops_idempotency.js
+++ b/jstests/replsets/apply_ops_idempotency.js
@@ -106,6 +106,25 @@
assert.commandWorked(y.renameCollection(x.getName(), true));
assert.commandWorked(z.renameCollection(y.getName()));
},
+ renameCollectionWithinDatabaseDroppingTargetByUUID: (mydb) => {
+ assert.commandWorked(mydb.createCollection("x"));
+ assert.commandWorked(mydb.createCollection("y"));
+ assert.commandWorked(mydb.createCollection("z"));
+
+ assert.commandWorked(mydb.x.renameCollection('xx'));
+ // When replayed on a up-to-date db, this oplog entry may drop
+ // collection z rather than collection x if the dropTarget is not
+ // specified by UUID. (See SERVER-33087)
+ assert.commandWorked(mydb.y.renameCollection('xx', true));
+ assert.commandWorked(mydb.xx.renameCollection('yy'));
+ assert.commandWorked(mydb.z.renameCollection('xx'));
+ },
+ renameCollectionWithinDatabaseDropTargetEvenWhenSourceIsEmpty: (mydb) => {
+ assert.commandWorked(mydb.createCollection("x"));
+ assert.commandWorked(mydb.createCollection("y"));
+ assert.commandWorked(mydb.x.renameCollection('y', true));
+ assert(mydb.y.drop());
+ },
renameCollectionAcrossDatabases: (mydb) => {
let otherdb = mydb.getSiblingDB(mydb + '_');
let [x, y] = getCollections(mydb, ['x', 'y']);
diff --git a/src/mongo/db/catalog/rename_collection.cpp b/src/mongo/db/catalog/rename_collection.cpp
index 5ea50be213c..1d93179c373 100644
--- a/src/mongo/db/catalog/rename_collection.cpp
+++ b/src/mongo/db/catalog/rename_collection.cpp
@@ -68,6 +68,46 @@ NamespaceString getNamespaceFromUUIDElement(OperationContext* opCtx, const BSONE
return getNamespaceFromUUID(opCtx, uuid);
}
+Status renameTargetCollectionToTmp(OperationContext* opCtx,
+ const NamespaceString& sourceNs,
+ const UUID& sourceUUID,
+ Database* const targetDB,
+ const NamespaceString& targetNs,
+ const UUID& targetUUID) {
+ repl::UnreplicatedWritesBlock uwb(opCtx);
+
+ auto tmpNameResult = targetDB->makeUniqueCollectionNamespace(opCtx, "tmp%%%%%.rename");
+ if (!tmpNameResult.isOK()) {
+ return tmpNameResult.getStatus().withContext(
+ str::stream() << "Cannot generate a temporary collection name for the target "
+ << targetNs.ns()
+ << " ("
+ << targetUUID
+ << ") so that the source"
+ << sourceNs.ns()
+ << " ("
+ << sourceUUID
+ << ") could be renamed to "
+ << targetNs.ns());
+ }
+ const auto& tmpName = tmpNameResult.getValue();
+ const bool stayTemp = true;
+ return writeConflictRetry(opCtx, "renameCollection", targetNs.ns(), [&] {
+ WriteUnitOfWork wunit(opCtx);
+ auto status = targetDB->renameCollection(opCtx, targetNs.ns(), tmpName.ns(), stayTemp);
+ if (!status.isOK())
+ return status;
+
+ wunit.commit();
+
+ log() << "Successfully renamed the target " << targetNs.ns() << " (" << targetUUID
+ << ") to " << tmpName << " so that the source " << sourceNs.ns() << " (" << sourceUUID
+ << ") could be renamed to " << targetNs.ns();
+
+ return Status::OK();
+ });
+}
+
Status renameCollectionCommon(OperationContext* opCtx,
const NamespaceString& source,
const NamespaceString& target,
@@ -75,8 +115,8 @@ Status renameCollectionCommon(OperationContext* opCtx,
repl::OpTime renameOpTimeFromApplyOps,
const RenameCollectionOptions& options) {
auto uuidString = targetUUID ? targetUUID->toString() : "no UUID";
- log() << "renameCollection: renaming collection " << source << " to " << target << " ("
- << uuidString << ")";
+ log() << "renameCollection: renaming collection " << uuidString << " from " << source << " to "
+ << target;
// A valid 'renameOpTimeFromApplyOps' is not allowed when writes are replicated.
if (!renameOpTimeFromApplyOps.isNull() && opCtx->writesAreReplicated()) {
@@ -157,11 +197,34 @@ Status renameCollectionCommon(OperationContext* opCtx,
return Status(ErrorCodes::NamespaceExists, "target namespace exists");
}
+ // If UUID doesn't point to the existing target, we should rename the target rather than
+ // drop it.
+ if (options.dropTargetUUID && options.dropTargetUUID != targetColl->uuid()) {
+ auto dropTargetNssFromUUID = getNamespaceFromUUID(opCtx, options.dropTargetUUID.get());
+ // We need to rename the targetColl to a temporary name.
+ auto status = renameTargetCollectionToTmp(
+ opCtx, source, targetUUID.get(), targetDB, target, targetColl->uuid().get());
+ if (!status.isOK())
+ return status;
+ targetColl = nullptr;
+ }
} else if (targetDB->getViewCatalog()->lookup(opCtx, target.ns())) {
return Status(ErrorCodes::NamespaceExists,
str::stream() << "a view already exists with that name: " << target.ns());
}
+ // When reapplying oplog entries (such as in the case of initial sync) we need
+ // to identify the collection to drop by UUID, as otherwise we might end up
+ // dropping the wrong collection.
+ if (!targetColl && options.dropTargetUUID) {
+ invariant(options.dropTarget);
+ auto dropTargetNssFromUUID = getNamespaceFromUUID(opCtx, options.dropTargetUUID.get());
+ if (!dropTargetNssFromUUID.isEmpty() && !dropTargetNssFromUUID.isDropPendingNamespace()) {
+ invariant(dropTargetNssFromUUID.db() == target.db());
+ targetColl = targetDB->getCollection(opCtx, dropTargetNssFromUUID);
+ }
+ }
+
auto sourceUUID = sourceColl->uuid();
// If we are renaming in the same database, just rename the namespace and we're done.
if (sourceDB == targetDB) {
@@ -206,7 +269,7 @@ Status renameCollectionCommon(OperationContext* opCtx,
// No logOp necessary because the entire renameCollection command is one logOp.
repl::UnreplicatedWritesBlock uwb(opCtx);
- status = targetDB->dropCollection(opCtx, target.ns(), renameOpTime);
+ status = targetDB->dropCollection(opCtx, targetColl->ns().ns(), renameOpTime);
if (!status.isOK()) {
return status;
}
@@ -435,22 +498,48 @@ Status renameCollectionForApplyOps(OperationContext* opCtx,
sourceNss = uiNss;
}
- if (sourceNss.isDropPendingNamespace()) {
- return Status(ErrorCodes::NamespaceNotFound,
- str::stream() << "renameCollectionForApplyOps() cannot accept a source "
- "collection that is in a drop-pending state: "
- << sourceNss.toString()
- << " (UUID: "
- << ui.toString(false)
- << ")");
- }
-
OptionalCollectionUUID targetUUID;
if (!ui.eoo())
targetUUID = uassertStatusOK(UUID::parse(ui));
RenameCollectionOptions options;
options.dropTarget = cmd["dropTarget"].trueValue();
+ if (cmd["dropTarget"].type() == BinData) {
+ auto uuid = uassertStatusOK(UUID::parse(cmd["dropTarget"]));
+ options.dropTargetUUID = uuid;
+ }
+
+ const Collection* const sourceColl =
+ AutoGetCollectionForRead(opCtx, sourceNss, AutoGetCollection::ViewMode::kViewsPermitted)
+ .getCollection();
+
+ if (sourceNss.isDropPendingNamespace() || sourceColl == nullptr) {
+ NamespaceString dropTargetNss;
+
+ if (options.dropTarget)
+ dropTargetNss = targetNss;
+
+ if (options.dropTargetUUID) {
+ dropTargetNss = getNamespaceFromUUID(opCtx, options.dropTargetUUID.get());
+ }
+
+ // Downgrade renameCollection to dropCollection.
+ if (!dropTargetNss.isEmpty()) {
+ BSONObjBuilder unusedResult;
+ return dropCollection(opCtx,
+ dropTargetNss,
+ unusedResult,
+ renameOpTime,
+ DropCollectionSystemCollectionMode::kAllowSystemCollectionDrops);
+ }
+
+ return Status(ErrorCodes::NamespaceNotFound,
+ str::stream()
+ << "renameCollection() cannot accept a source "
+ "collection that does not exist or is in a drop-pending state: "
+ << sourceNss.toString());
+ }
+
options.stayTemp = cmd["stayTemp"].trueValue();
return renameCollectionCommon(opCtx, sourceNss, targetNss, targetUUID, renameOpTime, options);
}
diff --git a/src/mongo/db/catalog/rename_collection.h b/src/mongo/db/catalog/rename_collection.h
index a744a339fec..83ccfacbc8c 100644
--- a/src/mongo/db/catalog/rename_collection.h
+++ b/src/mongo/db/catalog/rename_collection.h
@@ -29,6 +29,7 @@
#include "mongo/base/status.h"
#include "mongo/bson/bsonelement.h"
#include "mongo/bson/bsonobj.h"
+#include "mongo/db/catalog/collection_options.h"
#include "mongo/util/uuid.h"
namespace mongo {
@@ -40,12 +41,13 @@ class OpTime;
} // namespace repl
/**
- * 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.
+ * Renames the collection from "source" to "target" and drops the existing collection with UUID
+ * dropTargetUUID iff "dropTarget" is true. "stayTemp" indicates whether a collection should
+ * maintain its temporariness.
*/
struct RenameCollectionOptions {
bool dropTarget = false;
+ OptionalCollectionUUID dropTargetUUID;
bool stayTemp = false;
};
Status renameCollection(OperationContext* opCtx,
diff --git a/src/mongo/db/catalog/rename_collection_test.cpp b/src/mongo/db/catalog/rename_collection_test.cpp
index 60386382669..b3a919fc8a2 100644
--- a/src/mongo/db/catalog/rename_collection_test.cpp
+++ b/src/mongo/db/catalog/rename_collection_test.cpp
@@ -290,6 +290,15 @@ CollectionOptions _makeCollectionOptionsWithUuid() {
}
/**
+ * Creates a collection with UUID and returns the UUID.
+ */
+CollectionUUID _createCollectionWithUUID(OperationContext* opCtx, const NamespaceString& nss) {
+ const auto options = _makeCollectionOptionsWithUuid();
+ _createCollection(opCtx, nss, options);
+ return options.uuid.get();
+}
+
+/**
* Returns true if collection exists.
*/
bool _collectionExists(OperationContext* opCtx, const NamespaceString& nss) {
@@ -318,6 +327,14 @@ CollectionUUID _getCollectionUuid(OperationContext* opCtx, const NamespaceString
}
/**
+ * Get collection namespace by UUID.
+ */
+NamespaceString _getCollectionNssFromUUID(OperationContext* opCtx, const UUID& uuid) {
+ Collection* source = UUIDCatalog::get(opCtx).lookupCollectionByUUID(uuid);
+ return source ? source->ns() : NamespaceString();
+}
+
+/**
* Returns true if namespace refers to a temporary collection.
*/
bool _isTempCollection(OperationContext* opCtx, const NamespaceString& nss) {
@@ -499,12 +516,9 @@ TEST_F(RenameCollectionTest, RenameCollectionForApplyOpsAcrossDatabaseWithTarget
}
TEST_F(RenameCollectionTest, RenameCollectionToItselfByNsForApplyOps) {
- _createCollection(_opCtx.get(), _sourceNss);
auto dbName = _sourceNss.db().toString();
- AutoGetDb autoDb(_opCtx.get(), dbName, MODE_X);
- auto db = autoDb.getDb();
- auto uuid = db->getCollection(_opCtx.get(), _sourceNss)->uuid();
- auto uuidDoc = BSON("ui" << uuid.get());
+ auto uuid = _createCollectionWithUUID(_opCtx.get(), _sourceNss);
+ auto uuidDoc = BSON("ui" << uuid);
auto cmd = BSON("renameCollection" << _sourceNss.ns() << "to" << _sourceNss.ns() << "dropTarget"
<< true);
ASSERT_OK(renameCollectionForApplyOps(_opCtx.get(), dbName, uuidDoc["ui"], cmd, {}));
@@ -512,12 +526,9 @@ TEST_F(RenameCollectionTest, RenameCollectionToItselfByNsForApplyOps) {
}
TEST_F(RenameCollectionTest, RenameCollectionToItselfByUUIDForApplyOps) {
- _createCollection(_opCtx.get(), _targetNss);
auto dbName = _targetNss.db().toString();
- AutoGetDb autoDb(_opCtx.get(), dbName, MODE_X);
- auto db = autoDb.getDb();
- auto uuid = db->getCollection(_opCtx.get(), _targetNss)->uuid();
- auto uuidDoc = BSON("ui" << uuid.get());
+ auto uuid = _createCollectionWithUUID(_opCtx.get(), _targetNss);
+ auto uuidDoc = BSON("ui" << uuid);
auto cmd = BSON("renameCollection" << _sourceNss.ns() << "to" << _targetNss.ns() << "dropTarget"
<< true);
ASSERT_OK(renameCollectionForApplyOps(_opCtx.get(), dbName, uuidDoc["ui"], cmd, {}));
@@ -526,18 +537,119 @@ TEST_F(RenameCollectionTest, RenameCollectionToItselfByUUIDForApplyOps) {
TEST_F(RenameCollectionTest, RenameCollectionByUUIDRatherThanNsForApplyOps) {
auto realRenameFromNss = NamespaceString("test.bar2");
- _createCollection(_opCtx.get(), realRenameFromNss);
auto dbName = realRenameFromNss.db().toString();
- AutoGetDb autoDb(_opCtx.get(), dbName, MODE_X);
- auto db = autoDb.getDb();
- auto uuid = db->getCollection(_opCtx.get(), realRenameFromNss)->uuid();
- auto uuidDoc = BSON("ui" << uuid.get());
+ auto uuid = _createCollectionWithUUID(_opCtx.get(), realRenameFromNss);
+ auto uuidDoc = BSON("ui" << uuid);
auto cmd = BSON("renameCollection" << _sourceNss.ns() << "to" << _targetNss.ns() << "dropTarget"
<< true);
ASSERT_OK(renameCollectionForApplyOps(_opCtx.get(), dbName, uuidDoc["ui"], cmd, {}));
ASSERT_TRUE(_collectionExists(_opCtx.get(), _targetNss));
}
+TEST_F(RenameCollectionTest, RenameCollectionForApplyOpsDropTargetByUUIDTargetDoesNotExist) {
+ const auto& collA = NamespaceString("test.A");
+ const auto& collB = NamespaceString("test.B");
+ const auto& collC = NamespaceString("test.C");
+ auto dbName = collA.db().toString();
+ auto collAUUID = _createCollectionWithUUID(_opCtx.get(), collA);
+ auto collCUUID = _createCollectionWithUUID(_opCtx.get(), collC);
+ auto uuidDoc = BSON("ui" << collAUUID);
+ // Rename A to B, drop C, where B is not an existing collection
+ auto cmd =
+ BSON("renameCollection" << collA.ns() << "to" << collB.ns() << "dropTarget" << collCUUID);
+ ASSERT_OK(renameCollectionForApplyOps(_opCtx.get(), dbName, uuidDoc["ui"], cmd, {}));
+ // A and C should be dropped
+ ASSERT_FALSE(_collectionExists(_opCtx.get(), collA));
+ ASSERT_FALSE(_collectionExists(_opCtx.get(), collC));
+ // B (originally A) should exist
+ ASSERT_TRUE(_collectionExists(_opCtx.get(), collB));
+}
+
+TEST_F(RenameCollectionTest, RenameCollectionForApplyOpsDropTargetByUUIDTargetExists) {
+ const auto& collA = NamespaceString("test.A");
+ const auto& collB = NamespaceString("test.B");
+ const auto& collC = NamespaceString("test.C");
+ auto dbName = collA.db().toString();
+ auto collAUUID = _createCollectionWithUUID(_opCtx.get(), collA);
+ auto collBUUID = _createCollectionWithUUID(_opCtx.get(), collB);
+ auto collCUUID = _createCollectionWithUUID(_opCtx.get(), collC);
+ auto uuidDoc = BSON("ui" << collAUUID);
+ // Rename A to B, drop C, where B is an existing collection
+ // B should be kept but with a temporary name
+ auto cmd =
+ BSON("renameCollection" << collA.ns() << "to" << collB.ns() << "dropTarget" << collCUUID);
+ ASSERT_OK(renameCollectionForApplyOps(_opCtx.get(), dbName, uuidDoc["ui"], cmd, {}));
+ // A and C should be dropped
+ ASSERT_FALSE(_collectionExists(_opCtx.get(), collA));
+ ASSERT_FALSE(_collectionExists(_opCtx.get(), collC));
+ // B (originally A) should exist
+ ASSERT_TRUE(_collectionExists(_opCtx.get(), collB));
+ // The original B should exist too, but with a temporary name
+ const auto& tmpB = UUIDCatalog::get(_opCtx.get()).lookupNSSByUUID(collBUUID);
+ ASSERT_FALSE(tmpB.isEmpty());
+ ASSERT_TRUE(tmpB.coll().startsWith("tmp"));
+ ASSERT_TRUE(tmpB != collB);
+}
+
+TEST_F(RenameCollectionTest,
+ RenameCollectionForApplyOpsDropTargetByUUIDTargetExistsButTemporarily) {
+
+ const auto& collA = NamespaceString("test.A");
+ const auto& collB = NamespaceString("test.B");
+ const auto& collC = NamespaceString("test.C");
+
+ CollectionOptions collectionOptions = _makeCollectionOptionsWithUuid();
+ collectionOptions.temp = true;
+ _createCollection(_opCtx.get(), collB, collectionOptions);
+ auto collBUUID = _getCollectionUuid(_opCtx.get(), collB);
+
+ auto dbName = collA.db().toString();
+ auto collAUUID = _createCollectionWithUUID(_opCtx.get(), collA);
+ auto collCUUID = _createCollectionWithUUID(_opCtx.get(), collC);
+ auto uuidDoc = BSON("ui" << collAUUID);
+ // Rename A to B, drop C, where B is an existing collection
+ // B should be kept but with a temporary name
+ auto cmd =
+ BSON("renameCollection" << collA.ns() << "to" << collB.ns() << "dropTarget" << collCUUID);
+ ASSERT_OK(renameCollectionForApplyOps(_opCtx.get(), dbName, uuidDoc["ui"], cmd, {}));
+ // A and C should be dropped
+ ASSERT_FALSE(_collectionExists(_opCtx.get(), collA));
+ ASSERT_FALSE(_collectionExists(_opCtx.get(), collC));
+ // B (originally A) should exist
+ ASSERT_TRUE(_collectionExists(_opCtx.get(), collB));
+ // The original B should exist too, but with a temporary name
+ const auto& tmpB = UUIDCatalog::get(_opCtx.get()).lookupNSSByUUID(collBUUID);
+ ASSERT_FALSE(tmpB.isEmpty());
+ ASSERT_TRUE(tmpB != collB);
+ ASSERT_TRUE(tmpB.coll().startsWith("tmp"));
+ ASSERT_TRUE(_isTempCollection(_opCtx.get(), tmpB));
+}
+
+TEST_F(RenameCollectionTest,
+ RenameCollectionForApplyOpsDropTargetByUUIDTargetExistsButRealDropTargetDoesNotExist) {
+ const auto& collA = NamespaceString("test.A");
+ const auto& collB = NamespaceString("test.B");
+ auto dbName = collA.db().toString();
+ auto collAUUID = _createCollectionWithUUID(_opCtx.get(), collA);
+ auto collBUUID = _createCollectionWithUUID(_opCtx.get(), collB);
+ auto collCUUID = UUID::gen();
+ auto uuidDoc = BSON("ui" << collAUUID);
+ // Rename A to B, drop C, where B is an existing collection
+ // B should be kept but with a temporary name
+ auto cmd =
+ BSON("renameCollection" << collA.ns() << "to" << collB.ns() << "dropTarget" << collCUUID);
+ ASSERT_OK(renameCollectionForApplyOps(_opCtx.get(), dbName, uuidDoc["ui"], cmd, {}));
+ // A and C should be dropped
+ ASSERT_FALSE(_collectionExists(_opCtx.get(), collA));
+ // B (originally A) should exist
+ ASSERT_TRUE(_collectionExists(_opCtx.get(), collB));
+ // The original B should exist too, but with a temporary name
+ const auto& tmpB = UUIDCatalog::get(_opCtx.get()).lookupNSSByUUID(collBUUID);
+ ASSERT_FALSE(tmpB.isEmpty());
+ ASSERT_TRUE(tmpB != collB);
+ ASSERT_TRUE(tmpB.coll().startsWith("tmp"));
+}
+
TEST_F(RenameCollectionTest,
RenameCollectionReturnsNamespaceExitsIfTargetExistsAndDropTargetIsFalse) {
_createCollection(_opCtx.get(), _sourceNss);
@@ -622,6 +734,92 @@ DEATH_TEST_F(RenameCollectionTest,
ASSERT_OK(renameCollectionForApplyOps(_opCtx.get(), dbName, {}, cmd, renameOpTime));
}
+TEST_F(RenameCollectionTest, RenameCollectionForApplyOpsSourceAndTargetDoNotExist) {
+ auto uuidDoc = BSON("ui" << UUID::gen());
+ auto cmd = BSON("renameCollection" << _sourceNss.ns() << "to" << _targetNss.ns() << "dropTarget"
+ << "true");
+ ASSERT_EQUALS(ErrorCodes::NamespaceNotFound,
+ renameCollectionForApplyOps(
+ _opCtx.get(), _sourceNss.db().toString(), uuidDoc["ui"], cmd, {}));
+ ASSERT_FALSE(_collectionExists(_opCtx.get(), _sourceNss));
+ ASSERT_FALSE(_collectionExists(_opCtx.get(), _targetNss));
+}
+
+TEST_F(RenameCollectionTest, RenameCollectionForApplyOpsDropTargetEvenIfSourceDoesNotExist) {
+ _createCollectionWithUUID(_opCtx.get(), _targetNss);
+ auto missingSourceNss = NamespaceString("test.bar2");
+ auto uuidDoc = BSON("ui" << UUID::gen());
+ auto cmd =
+ BSON("renameCollection" << missingSourceNss.ns() << "to" << _targetNss.ns() << "dropTarget"
+ << "true");
+ ASSERT_OK(renameCollectionForApplyOps(
+ _opCtx.get(), missingSourceNss.db().toString(), uuidDoc["ui"], cmd, {}));
+ ASSERT_FALSE(_collectionExists(_opCtx.get(), _targetNss));
+}
+
+TEST_F(RenameCollectionTest, RenameCollectionForApplyOpsDropTargetByUUIDEvenIfSourceDoesNotExist) {
+ auto missingSourceNss = NamespaceString("test.bar2");
+ auto dropTargetNss = NamespaceString("test.bar3");
+ _createCollectionWithUUID(_opCtx.get(), _targetNss);
+ auto dropTargetUUID = _createCollectionWithUUID(_opCtx.get(), dropTargetNss);
+ auto uuidDoc = BSON("ui" << UUID::gen());
+ auto cmd =
+ BSON("renameCollection" << missingSourceNss.ns() << "to" << _targetNss.ns() << "dropTarget"
+ << dropTargetUUID);
+ ASSERT_OK(renameCollectionForApplyOps(
+ _opCtx.get(), missingSourceNss.db().toString(), uuidDoc["ui"], cmd, {}));
+ ASSERT_TRUE(_collectionExists(_opCtx.get(), _targetNss));
+ ASSERT_FALSE(_collectionExists(_opCtx.get(), dropTargetNss));
+}
+
+TEST_F(RenameCollectionTest, RenameCollectionForApplyOpsDropTargetEvenIfSourceIsDropPending) {
+ repl::OpTime dropOpTime(Timestamp(Seconds(100), 0), 1LL);
+ auto dropPendingNss = _sourceNss.makeDropPendingNamespace(dropOpTime);
+
+ auto dropTargetUUID = _createCollectionWithUUID(_opCtx.get(), _targetNss);
+ auto uuidDoc = BSON("ui" << _createCollectionWithUUID(_opCtx.get(), dropPendingNss));
+ auto cmd =
+ BSON("renameCollection" << dropPendingNss.ns() << "to" << _targetNss.ns() << "dropTarget"
+ << "true");
+
+ repl::UnreplicatedWritesBlock uwb(_opCtx.get());
+ repl::OpTime renameOpTime = {Timestamp(Seconds(200), 1U), 1LL};
+ ASSERT_OK(renameCollectionForApplyOps(
+ _opCtx.get(), dropPendingNss.db().toString(), uuidDoc["ui"], cmd, renameOpTime));
+
+ // Source collections stays in drop-pending state.
+ ASSERT_TRUE(_collectionExists(_opCtx.get(), dropPendingNss));
+ ASSERT_FALSE(_collectionExists(_opCtx.get(), _targetNss));
+ ASSERT_EQUALS(_targetNss.makeDropPendingNamespace(renameOpTime),
+ _getCollectionNssFromUUID(_opCtx.get(), dropTargetUUID));
+}
+
+TEST_F(RenameCollectionTest, RenameCollectionForApplyOpsDropTargetByUUIDEvenIfSourceIsDropPending) {
+ repl::OpTime dropOpTime(Timestamp(Seconds(100), 0), 1LL);
+ auto dropPendingNss = _sourceNss.makeDropPendingNamespace(dropOpTime);
+ auto dropTargetNss = NamespaceString("test.bar2");
+
+ _createCollectionWithUUID(_opCtx.get(), _targetNss);
+
+ auto dropTargetUUID = _createCollectionWithUUID(_opCtx.get(), dropTargetNss);
+ auto uuidDoc = BSON("ui" << _createCollectionWithUUID(_opCtx.get(), dropPendingNss));
+ auto cmd =
+ BSON("renameCollection" << dropPendingNss.ns() << "to" << _targetNss.ns() << "dropTarget"
+ << dropTargetUUID);
+
+ repl::UnreplicatedWritesBlock uwb(_opCtx.get());
+ repl::OpTime renameOpTime = {Timestamp(Seconds(200), 1U), 1LL};
+ ASSERT_OK(renameCollectionForApplyOps(
+ _opCtx.get(), dropPendingNss.db().toString(), uuidDoc["ui"], cmd, renameOpTime));
+
+ // Source collections stays in drop-pending state.
+ ASSERT_TRUE(_collectionExists(_opCtx.get(), dropPendingNss));
+ ASSERT_FALSE(_collectionExists(_opCtx.get(), dropTargetNss));
+ ASSERT_EQUALS(dropTargetNss.makeDropPendingNamespace(renameOpTime),
+ _getCollectionNssFromUUID(_opCtx.get(), dropTargetUUID));
+ ASSERT_TRUE(_collectionExists(_opCtx.get(), _targetNss));
+}
+
void _testRenameCollectionStayTemp(OperationContext* opCtx,
const NamespaceString& sourceNss,
const NamespaceString& targetNss,