diff options
author | Yuhong Zhang <yuhong.zhang@mongodb.com> | 2022-02-15 15:38:29 +0000 |
---|---|---|
committer | Evergreen Agent <no-reply@evergreen.mongodb.com> | 2022-02-15 17:49:46 +0000 |
commit | 0d49a8ea04960ffc6817b439bcec2c6ec31a620d (patch) | |
tree | 5fe686b940a237915a7d667716628bd300ffa465 | |
parent | 4931ea397a8119b28309c4fbe3604079e840717b (diff) | |
download | mongo-0d49a8ea04960ffc6817b439bcec2c6ec31a620d.tar.gz |
SERVER-63481 Add `disallowNewDuplicateKeys` to `createIndex`
-rw-r--r-- | jstests/core/index_disallowNewDuplicateKeys.js | 53 | ||||
-rw-r--r-- | jstests/noPassthrough/collmod_index_noop.js | 35 | ||||
-rw-r--r-- | src/mongo/db/catalog/index_catalog_impl.cpp | 7 | ||||
-rw-r--r-- | src/mongo/db/create_indexes.idl | 4 |
4 files changed, 93 insertions, 6 deletions
diff --git a/jstests/core/index_disallowNewDuplicateKeys.js b/jstests/core/index_disallowNewDuplicateKeys.js new file mode 100644 index 00000000000..cbcdee2ce9e --- /dev/null +++ b/jstests/core/index_disallowNewDuplicateKeys.js @@ -0,0 +1,53 @@ +/** + * Tests that the createIndex command accepts a disallowNewDuplicateKeys field and works accordingly + * then. + * + * @tags: [requires_fcv_53] + */ +(function() { +"use strict"; + +const coll = db.index_disallowNewDuplicateKeys; +coll.drop(); + +const collModIndexUniqueEnabled = assert + .commandWorked(db.getMongo().adminCommand( + {getParameter: 1, featureFlagCollModIndexUnique: 1})) + .featureFlagCollModIndexUnique.value; + +if (!collModIndexUniqueEnabled) { + jsTestLog('Skipping test because the collMod unique index feature flag is disabled.'); + return; +} + +assert.commandWorked(coll.insert({_id: 0, a: 1})); + +// Starts rejecting new duplicate keys. +assert.commandWorked(coll.createIndex({a: 1}, {disallowNewDuplicateKeys: true})); + +// Disallows creating another index on the same key with a different option. +assert.commandFailedWithCode(coll.createIndex({a: 1}, {disallowNewDuplicateKeys: false}), + ErrorCodes.IndexOptionsConflict); + +// Checks the index is rejecting duplicates but accepting other keys. +assert.commandFailedWithCode(coll.insert({_id: 1, a: 1}), ErrorCodes.DuplicateKey); +assert.commandWorked(coll.insert({_id: 2, a: 2})); + +// Checks that the disallowNewDuplicateKeys field exists when getIndexes is called. +let indexesWithComments = coll.getIndexes().filter(function(doc) { + return friendlyEqual(doc.disallowNewDuplicateKeys, true); +}); +assert.eq(1, indexesWithComments.length); + +// Removes the field and checks the index works as a regular index. +assert.commandWorked(coll.runCommand({ + collMod: "index_disallowNewDuplicateKeys", + index: {keyPattern: {a: 1}, disallowNewDuplicateKeys: false} +})); +assert.commandWorked(coll.insert({_id: 1, a: 1})); + +indexesWithComments = coll.getIndexes().filter(function(doc) { + return friendlyEqual(doc.disallowNewDuplicateKeys, true); +}); +assert.eq(0, indexesWithComments.length); +})();
\ No newline at end of file diff --git a/jstests/noPassthrough/collmod_index_noop.js b/jstests/noPassthrough/collmod_index_noop.js index 77b83aa5e49..510b0448185 100644 --- a/jstests/noPassthrough/collmod_index_noop.js +++ b/jstests/noPassthrough/collmod_index_noop.js @@ -1,8 +1,8 @@ /** - * Validate that the 'collMod' command with 'hidden' or 'unique' fields will return expected result - * document for the command and generate expected oplog entries in which the index modifications - * (hiding/unhiding/convert to unique) will be no-ops if no other index option (TTL, for example) - * is involved. + * Validate that the 'collMod' command with 'hidden,' 'unique,' or 'disallowNewDuplicateKeys' fields + * will return expected result document for the command and generate expected oplog entries in which + * the index modifications (hiding/unhiding/convert to unique/allowing duplicates/disallowing + * duplicates) will be no-ops if no other index option (TTL, for example) is involved. * * @tags: [ * # TODO(SERVER-61181): Fix validation errors under ephemeralForTest. @@ -24,7 +24,7 @@ rst.startSet(); rst.initiate(); const dbName = jsTestName(); -const collName = "hidden_index"; +const collName = "collmod_index_noop"; const primary = rst.getPrimary(); const primaryDB = primary.getDB(dbName); const primaryColl = primaryDB[collName]; @@ -53,6 +53,10 @@ function validateResultForCollMod(result, expectedResult) { assert.eq(result.expireAfterSeconds_old, expectedResult.expireAfterSeconds_old, result); assert.eq(result.expireAfterSeconds_new, expectedResult.expireAfterSeconds_new, result); assert.eq(result.unique_new, expectedResult.unique_new, result); + assert.eq( + result.disallowNewDuplicateKeys_old, expectedResult.disallowNewDuplicateKeys_old, result); + assert.eq( + result.disallowNewDuplicateKeys_new, expectedResult.disallowNewDuplicateKeys_new, result); } primaryColl.drop(); @@ -64,6 +68,9 @@ assert.commandWorked(primaryColl.createIndex({e: 1}, {hidden: true, unique: true assert.commandWorked(primaryColl.createIndex({f: 1}, {unique: true, expireAfterSeconds: 15})); assert.commandWorked( primaryColl.createIndex({g: 1}, {hidden: true, unique: true, expireAfterSeconds: 25})); +if (collModIndexUniqueEnabled) { + assert.commandWorked(primaryColl.createIndex({h: 1}, {disallowNewDuplicateKeys: true})); +} // Hiding a non-hidden index will generate the oplog entry with a 'hidden_old: false'. let result = assert.commandWorked(primaryColl.hideIndex('a_1')); @@ -213,6 +220,24 @@ if (collModIndexUniqueEnabled) { assert(idxSpec.hidden, tojson(idxSpec)); assert.eq(idxSpec.expireAfterSeconds, 30); assert(idxSpec.unique, tojson(idxSpec)); + + // Validate that if the 'disallowNewDuplicateKeys' option is specified but is a no-op, the + // operation as a whole will be a no-op. + result = assert.commandWorked(primaryDB.runCommand({ + "collMod": primaryColl.getName(), + "index": {"name": "h_1", "disallowNewDuplicateKeys": true}, + })); + validateResultForCollMod(result, {}); + validateCollModOplogEntryCount({ + "o.index.name": "h_1", + }, + 0); + + // Test that the index was unchanged. + idxSpec = GetIndexHelpers.findByName(primaryColl.getIndexes(), "h_1"); + assert.eq(idxSpec.hidden, undefined); + assert.eq(idxSpec.expireAfterSeconds, undefined); + assert(idxSpec.disallowNewDuplicateKeys, tojson(idxSpec)); } rst.stopSet(); diff --git a/src/mongo/db/catalog/index_catalog_impl.cpp b/src/mongo/db/catalog/index_catalog_impl.cpp index 163d1807b57..431e35dfbb3 100644 --- a/src/mongo/db/catalog/index_catalog_impl.cpp +++ b/src/mongo/db/catalog/index_catalog_impl.cpp @@ -1754,6 +1754,10 @@ StatusWith<BSONObj> IndexCatalogImpl::_fixIndexSpec(OperationContext* opCtx, if (o["hidden"].trueValue()) b.appendBool("hidden", true); // normalize to bool true in case was int 1 or something... + if (o["disallowNewDuplicateKeys"].trueValue()) + b.appendBool("disallowNewDuplicateKeys", + true); // normalize to bool true in case was int 1 or something... + BSONObj key = fixIndexKey(o["key"].Obj()); b.append("key", key); @@ -1782,7 +1786,8 @@ StatusWith<BSONObj> IndexCatalogImpl::_fixIndexSpec(OperationContext* opCtx, } else if (s == "dropDups" || s == "ns") { // dropDups is silently ignored and removed from the spec as of SERVER-14710. // ns is removed from the spec as of 4.4. - } else if (s == "v" || s == "unique" || s == "key" || s == "name" || s == "hidden") { + } else if (s == "v" || s == "unique" || s == "key" || s == "name" || s == "hidden" || + s == "disallowNewDuplicateKeys") { // covered above } else { b.append(e); diff --git a/src/mongo/db/create_indexes.idl b/src/mongo/db/create_indexes.idl index 812b0e3a40f..33815ce79b8 100644 --- a/src/mongo/db/create_indexes.idl +++ b/src/mongo/db/create_indexes.idl @@ -180,6 +180,10 @@ structs: type: safeBool optional: true unstable: false + disallowNewDuplicateKeys: + type: safeBool + optional: true + unstable: true commands: createIndexes: description: "Command for creating indexes on a collection" |