diff options
author | Geert Bosch <geert@mongodb.com> | 2017-10-23 11:54:05 -0400 |
---|---|---|
committer | Benety Goh <benety@mongodb.com> | 2017-10-23 14:38:28 -0400 |
commit | 9af189775db7b17b265d18a04ca0dc314792a4c5 (patch) | |
tree | e4093f56c08cf030f65a279514f9def861b3b205 | |
parent | 04a7b956a32f5b1d6d3b169bb4e8ad829ac6e733 (diff) | |
download | mongo-9af189775db7b17b265d18a04ca0dc314792a4c5.tar.gz |
SERVER-31539 applyOps create fails if UUID already exists in different DB
-rw-r--r-- | jstests/replsets/apply_ops_create_with_uuid.js | 50 | ||||
-rw-r--r-- | src/mongo/db/catalog/create_collection.cpp | 6 |
2 files changed, 56 insertions, 0 deletions
diff --git a/jstests/replsets/apply_ops_create_with_uuid.js b/jstests/replsets/apply_ops_create_with_uuid.js new file mode 100644 index 00000000000..3628d6700f2 --- /dev/null +++ b/jstests/replsets/apply_ops_create_with_uuid.js @@ -0,0 +1,50 @@ +(function() { + // Test applyOps behavior for collection creation with explicit UUIDs. + "use strict"; + + const replTest = new ReplSetTest({nodes: 1}); + replTest.startSet(); + replTest.initiate(); + + const db = replTest.getPrimary().getDB('test'); + + const uuid = UUID(); + // Two applyOps to create a foo collection with given uuid, one each for 'test' and 'test2' dbs. + var ops = (uuid => ["test", "test2"].map(db => { + return {op: "c", ns: db + ".$cmd", ui: uuid, o: {create: "foo"}}; + }))(uuid); + + function checkUUID(coll, uuid) { + const cmd = {listCollections: 1, filter: {name: coll}}; + const res = assert.commandWorked(db.runCommand(cmd), tojson(cmd)); + assert.eq(res.cursor.firstBatch[0].info.uuid, + uuid, + tojson(cmd) + " did not return expected uuid: " + tojson(res)); + } + + jsTestLog("Create a test.foo collection with uuid " + uuid + " through applyOps."); + let cmd = {applyOps: [ops[0]]}; + let res = assert.commandWorked(db.runCommand(cmd), tojson(cmd)); + + // Check that test.foo has the expected UUID. + jsTestLog("Check that test.foo has UUID " + uuid); + checkUUID("foo", uuid); + + // Change the ops to refer to bar, instead of foo. Command should still work, renaming the + // collection. Second command should fail as it tries to associate the "test2.foo" name with + // an existing collection in the "test" database. This must fail. + jsTestLog("Create test.bar and try to create test2.foo collections with the same UUID."); + ops[0].o.create = "bar"; + res = assert.commandFailed(db.runCommand({applyOps: ops})); + assert.eq(res.results, + [true, false], + "expected first operation " + tojson(ops[0]) + " to succeed, and second operation " + + tojson(ops[1]) + " to fail, got " + tojson(res)); + + jsTestLog("Check that test.bar has UUID " + uuid); + checkUUID("bar", uuid); + jsTestLog("Check that test.foo no longer exists"); + assert.eq(db.getCollectionInfos({name: "foo"}).length, + 0, + "expected foo collection to no longer exist"); +}()); diff --git a/src/mongo/db/catalog/create_collection.cpp b/src/mongo/db/catalog/create_collection.cpp index 4164ffd510a..b4f1565f01c 100644 --- a/src/mongo/db/catalog/create_collection.cpp +++ b/src/mongo/db/catalog/create_collection.cpp @@ -41,6 +41,7 @@ #include "mongo/db/operation_context.h" #include "mongo/db/ops/insert.h" #include "mongo/db/repl/replication_coordinator_global.h" +#include "mongo/logger/redaction.h" namespace mongo { namespace { @@ -188,6 +189,11 @@ Status createCollectionForApplyOps(OperationContext* opCtx, // If the collection with the requested UUID already exists, but with a different // name, just rename it to 'newCollName'. if (catalog.lookupCollectionByUUID(uuid)) { + uassert(40655, + str::stream() << "Invalid name " << redact(newCollName.ns()) + << " for UUID " + << uuid.toString(), + currentName.db() == newCollName.db()); Status status = db->renameCollection(opCtx, currentName.ns(), newCollName.ns(), stayTemp); if (!status.isOK()) |