summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorYuhong Zhang <yuhong.zhang@mongodb.com>2022-02-15 15:38:29 +0000
committerEvergreen Agent <no-reply@evergreen.mongodb.com>2022-02-15 17:49:46 +0000
commit0d49a8ea04960ffc6817b439bcec2c6ec31a620d (patch)
tree5fe686b940a237915a7d667716628bd300ffa465
parent4931ea397a8119b28309c4fbe3604079e840717b (diff)
downloadmongo-0d49a8ea04960ffc6817b439bcec2c6ec31a620d.tar.gz
SERVER-63481 Add `disallowNewDuplicateKeys` to `createIndex`
-rw-r--r--jstests/core/index_disallowNewDuplicateKeys.js53
-rw-r--r--jstests/noPassthrough/collmod_index_noop.js35
-rw-r--r--src/mongo/db/catalog/index_catalog_impl.cpp7
-rw-r--r--src/mongo/db/create_indexes.idl4
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"