summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--jstests/core/apply_ops1.js45
-rw-r--r--jstests/core/batch_write_command_insert.js26
-rw-r--r--jstests/core/collation.js60
-rw-r--r--jstests/libs/get_index_helpers.js69
-rw-r--r--jstests/multiVersion/index_version_missing_from_oplog.js67
-rw-r--r--jstests/noPassthrough/collation_clone_collection.js27
-rw-r--r--jstests/noPassthrough/index_version_autoupgrade.js148
-rw-r--r--jstests/replsets/oplog_format_create_indexes.js61
-rw-r--r--src/mongo/db/catalog/collection_compact.cpp36
-rw-r--r--src/mongo/db/catalog/database.cpp14
-rw-r--r--src/mongo/db/catalog/index_catalog.cpp123
-rw-r--r--src/mongo/db/catalog/index_catalog.h4
-rw-r--r--src/mongo/db/catalog/index_create.cpp10
-rw-r--r--src/mongo/db/catalog/index_create.h7
-rw-r--r--src/mongo/db/catalog/index_key_validate.cpp40
-rw-r--r--src/mongo/db/cloner.cpp64
-rw-r--r--src/mongo/db/commands/create_indexes.cpp94
-rw-r--r--src/mongo/db/commands/drop_indexes.cpp42
-rw-r--r--src/mongo/db/commands/mr.cpp10
-rw-r--r--src/mongo/db/dbhelpers.cpp4
-rw-r--r--src/mongo/db/dbhelpers.h2
-rw-r--r--src/mongo/db/index/index_descriptor.cpp35
-rw-r--r--src/mongo/db/index/index_descriptor.h29
-rw-r--r--src/mongo/db/index_builder.cpp2
-rw-r--r--src/mongo/db/repair_database.cpp32
-rw-r--r--src/mongo/db/repl/collection_bulk_loader_impl.cpp4
-rw-r--r--src/mongo/db/repl/oplog.cpp31
-rw-r--r--src/mongo/db/repl/rs_rollback_test.cpp9
-rw-r--r--src/mongo/db/repl/storage_interface_impl_test.cpp7
-rw-r--r--src/mongo/db/s/migration_destination_manager.cpp36
-rw-r--r--src/mongo/db/storage/mmap_v1/repair_database.cpp2
-rw-r--r--src/mongo/dbtests/counttests.cpp6
-rw-r--r--src/mongo/dbtests/dbtests.cpp13
-rw-r--r--src/mongo/dbtests/indexcatalogtests.cpp16
-rw-r--r--src/mongo/dbtests/indexupdatetests.cpp74
-rw-r--r--src/mongo/dbtests/multikey_paths_test.cpp31
-rw-r--r--src/mongo/dbtests/query_stage_ixscan.cpp8
-rw-r--r--src/mongo/dbtests/querytests.cpp8
-rw-r--r--src/mongo/dbtests/rollbacktests.cpp24
-rw-r--r--src/mongo/dbtests/validate_tests.cpp28
40 files changed, 1049 insertions, 299 deletions
diff --git a/jstests/core/apply_ops1.js b/jstests/core/apply_ops1.js
index c8da48e3a84..1e6b545a210 100644
--- a/jstests/core/apply_ops1.js
+++ b/jstests/core/apply_ops1.js
@@ -1,5 +1,8 @@
(function() {
"use strict";
+
+ load("jstests/libs/get_index_helpers.js");
+
var t = db.apply_ops1;
t.drop();
@@ -244,13 +247,10 @@
}));
assert.eq(1, res.applied, "Incorrect number of operations applied");
assert.eq(true, res.results[0], "Foreground index creation failed");
- res = t.getIndexes();
- assert.eq(1,
- res.filter(function(element, index, array) {
- return element.name == 'a_1';
- })
- .length,
- 'Foreground index not found in listIndexes result: ' + tojson(res));
+ var allIndexes = t.getIndexes();
+ var spec = GetIndexHelpers.findByName(allIndexes, "a_1");
+ assert.neq(null, spec, "Foreground index 'a_1' not found: " + tojson(allIndexes));
+ assert.eq(1, spec.v, "Expected v=1 index to be built since 'v' field was omitted");
// Background indexes are created in the foreground when processed by applyOps.
res = assert.commandWorked(db.adminCommand({
@@ -267,11 +267,28 @@
}));
assert.eq(1, res.applied, "Incorrect number of operations applied");
assert.eq(true, res.results[0], "Background index creation failed");
- res = t.getIndexes();
- assert.eq(1,
- res.filter(function(element, index, array) {
- return element.name == 'b_1';
- })
- .length,
- 'Background index not found in listIndexes result: ' + tojson(res));
+ allIndexes = t.getIndexes();
+ spec = GetIndexHelpers.findByName(allIndexes, "b_1");
+ assert.neq(null, spec, "Background index 'b_1' not found: " + tojson(allIndexes));
+ assert.eq(1, spec.v, "Expected v=1 index to be built since 'v' field was omitted");
+
+ // Foreground v=2 index build.
+ res = assert.commandWorked(db.adminCommand({
+ applyOps: [{
+ "op": "i",
+ "ns": db.getName() + ".system.indexes",
+ "o": {
+ ns: t.getFullName(),
+ key: {c: 1},
+ name: "c_1",
+ v: 2,
+ }
+ }]
+ }));
+ assert.eq(1, res.applied, "Incorrect number of operations applied");
+ assert.eq(true, res.results[0], "Foreground v=2 index creation failed");
+ allIndexes = t.getIndexes();
+ spec = GetIndexHelpers.findByName(allIndexes, "c_1");
+ assert.neq(null, spec, "Foreground index 'c_1' not found: " + tojson(allIndexes));
+ assert.eq(2, spec.v, "Expected v=2 index to be built");
})();
diff --git a/jstests/core/batch_write_command_insert.js b/jstests/core/batch_write_command_insert.js
index c5897cb9bac..581639002d8 100644
--- a/jstests/core/batch_write_command_insert.js
+++ b/jstests/core/batch_write_command_insert.js
@@ -2,6 +2,8 @@
// Ensures that mongod respects the batch write protocol for inserts
//
+load("jstests/libs/get_index_helpers.js");
+
var coll = db.getCollection("batch_write_insert");
coll.drop();
@@ -261,7 +263,24 @@ request = {
result = coll.runCommand(request);
assert(result.ok, tojson(result));
assert.eq(1, result.n);
-assert.eq(coll.getIndexes().length, 2);
+var allIndexes = coll.getIndexes();
+var spec = GetIndexHelpers.findByName(allIndexes, "x_1");
+assert.neq(null, spec, "Index with name 'x_1' not found: " + tojson(allIndexes));
+assert.lte(2, spec.v, tojson(spec));
+
+// Test that a v=1 index can be created by inserting into the system.indexes collection.
+coll.drop();
+request = {
+ insert: "system.indexes",
+ documents: [{ns: coll.toString(), key: {x: 1}, name: "x_1", v: 1}]
+};
+result = coll.runCommand(request);
+assert(result.ok, tojson(result));
+assert.eq(1, result.n);
+allIndexes = coll.getIndexes();
+spec = GetIndexHelpers.findByName(allIndexes, "x_1");
+assert.neq(null, spec, "Index with name 'x_1' not found: " + tojson(allIndexes));
+assert.eq(1, spec.v, tojson(spec));
//
// Duplicate index insertion gives n = 0
@@ -382,4 +401,7 @@ request = {
result = coll.runCommand(request);
assert(result.ok, tojson(result));
assert.eq(1, result.n);
-assert.eq(coll.getIndexes().length, 2);
+allIndexes = coll.getIndexes();
+spec = GetIndexHelpers.findByName(allIndexes, "x_1");
+assert.neq(null, spec, "Index with name 'x_1' not found: " + tojson(allIndexes));
+assert.lte(2, spec.v, tojson(spec));
diff --git a/jstests/core/collation.js b/jstests/core/collation.js
index b97ca8cee68..7b8a939a042 100644
--- a/jstests/core/collation.js
+++ b/jstests/core/collation.js
@@ -3,6 +3,7 @@
'use strict';
load("jstests/libs/analyze_plan.js");
+ load("jstests/libs/get_index_helpers.js");
var coll = db.collation;
coll.drop();
@@ -16,27 +17,12 @@
var isMongos = (isMaster.msg === "isdbgrid");
var assertIndexHasCollation = function(keyPattern, collation) {
- var foundIndex = false;
var indexSpecs = coll.getIndexes();
- for (var i = 0; i < indexSpecs.length; ++i) {
- if (bsonWoCompare(indexSpecs[i].key, keyPattern) === 0) {
- foundIndex = true;
- // We assume that the key pattern is unique, even though indices with different
- // collations but the same key pattern are allowed.
- if (collation.locale === "simple") {
- // The simple collation is not explicitly stored in the catalog, so we expect
- // the "collation" field to be absent.
- assert(!indexSpecs[i].hasOwnProperty("collation"),
- "Expected the simple collation in: " + tojson(indexSpecs[i]));
- } else {
- assert.eq(indexSpecs[i].collation,
- collation,
- "Expected collation " + tojson(collation) + " in: " +
- tojson(indexSpecs[i]));
- }
- }
- }
- assert(foundIndex, "index with key pattern " + tojson(keyPattern) + " not found");
+ var found = GetIndexHelpers.findByKeyPattern(indexSpecs, keyPattern, collation);
+ assert.neq(null,
+ found,
+ "Index with key pattern " + tojson(keyPattern) + " and collation " +
+ tojson(collation) + " not found: " + tojson(indexSpecs));
};
var getQueryCollation = function(explainRes) {
@@ -128,10 +114,44 @@
version: "57.1",
});
+ // Ensure that an index which specifies the "simple" collation as an overriding collation still
+ // does not use the collection default.
+ assert.commandWorked(coll.ensureIndex({d: 1}, {collation: {locale: "simple"}}));
+ assertIndexHasCollation({d: 1}, {locale: "simple"});
+
// Ensure that a v=1 index doesn't inherit the collection-default collation.
assert.commandWorked(coll.ensureIndex({c: 1}, {v: 1}));
assertIndexHasCollation({c: 1}, {locale: "simple"});
+ // Test that all indexes retain their current collation when the collection is re-indexed.
+ assert.commandWorked(coll.reIndex());
+ assertIndexHasCollation({a: 1}, {
+ locale: "fr_CA",
+ caseLevel: false,
+ caseFirst: "off",
+ strength: 3,
+ numericOrdering: false,
+ alternate: "non-ignorable",
+ maxVariable: "punct",
+ normalization: false,
+ backwards: true,
+ version: "57.1",
+ });
+ assertIndexHasCollation({b: 1}, {
+ locale: "en_US",
+ caseLevel: false,
+ caseFirst: "off",
+ strength: 3,
+ numericOrdering: false,
+ alternate: "non-ignorable",
+ maxVariable: "punct",
+ normalization: false,
+ backwards: false,
+ version: "57.1",
+ });
+ assertIndexHasCollation({d: 1}, {locale: "simple"});
+ assertIndexHasCollation({c: 1}, {locale: "simple"});
+
coll.drop();
//
diff --git a/jstests/libs/get_index_helpers.js b/jstests/libs/get_index_helpers.js
new file mode 100644
index 00000000000..77468ab17cb
--- /dev/null
+++ b/jstests/libs/get_index_helpers.js
@@ -0,0 +1,69 @@
+"use strict";
+
+/**
+ * Helpers for filtering the index specifications returned by DBCollection.prototype.getIndexes().
+ */
+var GetIndexHelpers = (function() {
+
+ /**
+ * Returns the index specification with the name 'indexName' if it is present in the
+ * 'indexSpecs' array, and returns null otherwise.
+ */
+ function getIndexSpecByName(indexSpecs, indexName) {
+ if (typeof indexName !== "string") {
+ throw new Error("'indexName' parameter must be a string, but got " + tojson(indexName));
+ }
+
+ const found = indexSpecs.filter(spec => spec.name === indexName);
+
+ if (found.length > 1) {
+ throw new Error("Found multiple indexes with name '" + indexName + "': " +
+ tojson(indexSpecs));
+ }
+ return (found.length === 1) ? found[0] : null;
+ }
+
+ /**
+ * Returns the index specification with the key pattern 'keyPattern' and the collation
+ * 'collation' if it is present in the 'indexSpecs' array, and returns null otherwise.
+ *
+ * The 'collation' parameter is optional and is only required to be specified when multiple
+ * indexes with the same key pattern exist.
+ */
+ function getIndexSpecByKeyPattern(indexSpecs, keyPattern, collation) {
+ const collationWasSpecified = arguments.length >= 3;
+ const foundByKeyPattern = indexSpecs.filter(spec => {
+ return bsonWoCompare(spec.key, keyPattern) === 0;
+ });
+
+ if (!collationWasSpecified) {
+ if (foundByKeyPattern.length > 1) {
+ throw new Error("Found multiple indexes with key pattern " + tojson(keyPattern) +
+ " and 'collation' parameter was not specified: " +
+ tojson(indexSpecs));
+ }
+ return (foundByKeyPattern.length === 1) ? foundByKeyPattern[0] : null;
+ }
+
+ const foundByKeyPatternAndCollation = foundByKeyPattern.filter(spec => {
+ if (collation.locale === "simple") {
+ // The simple collation is not explicitly stored in the index catalog, so we expect
+ // the "collation" field to be absent.
+ return !spec.hasOwnProperty("collation");
+ }
+ return bsonWoCompare(spec.collation, collation) === 0;
+ });
+
+ if (foundByKeyPatternAndCollation.length > 1) {
+ throw new Error("Found multiple indexes with key pattern" + tojson(keyPattern) +
+ " and collation " + tojson(collation) + ": " + tojson(indexSpecs));
+ }
+ return (foundByKeyPatternAndCollation.length === 1) ? foundByKeyPatternAndCollation[0]
+ : null;
+ }
+
+ return {
+ findByName: getIndexSpecByName,
+ findByKeyPattern: getIndexSpecByKeyPattern,
+ };
+})();
diff --git a/jstests/multiVersion/index_version_missing_from_oplog.js b/jstests/multiVersion/index_version_missing_from_oplog.js
new file mode 100644
index 00000000000..4a3f820ae49
--- /dev/null
+++ b/jstests/multiVersion/index_version_missing_from_oplog.js
@@ -0,0 +1,67 @@
+/**
+ * Tests that newer versions of mongod infer the absence of the "v" field in the oplog entry for the
+ * index creation as a request to build a v=1 index.
+ */
+(function() {
+ "use strict";
+
+ load("jstests/libs/get_index_helpers.js");
+
+ var replSetName = "index_version_missing";
+ var nodes = [
+ // The 3.2.1 version of mongod includes the index specification the user requested in the
+ // corresponding oplog entry, and not the index specification the server actually built. We
+ // therefore use this version of mongod to test the behavior of newer versions when the "v"
+ // field isn't always present in the oplog entry for the index creation.
+ {binVersion: "3.2.1"},
+ {binVersion: "latest"},
+ ];
+
+ var rst = new ReplSetTest({name: replSetName, nodes: nodes});
+ rst.startSet();
+
+ // Rig the election so that the 3.2.1 node becomes the primary.
+ var replSetConfig = rst.getReplSetConfig();
+ replSetConfig.members[1].priority = 0;
+
+ rst.initiate(replSetConfig);
+
+ var primaryDB = rst.getPrimary().getDB("test");
+ var secondaryDB = rst.getSecondary().getDB("test");
+
+ assert.commandWorked(primaryDB.index_version_missing.createIndex({withoutAnyOptions: 1}));
+ var allIndexes = primaryDB.index_version_missing.getIndexes();
+ var spec = GetIndexHelpers.findByKeyPattern(allIndexes, {withoutAnyOptions: 1});
+ assert.neq(null,
+ spec,
+ "Index with key pattern {withoutAnyOptions: 1} not found: " + tojson(allIndexes));
+ assert.eq(1, spec.v, "Expected 3.2.1 primary to build a v=1 index: " + tojson(spec));
+
+ assert.commandWorked(primaryDB.index_version_missing.createIndex({withV1: 1}, {v: 1}));
+ allIndexes = primaryDB.index_version_missing.getIndexes();
+ spec = GetIndexHelpers.findByKeyPattern(allIndexes, {withV1: 1});
+ assert.neq(null, spec, "Index with key pattern {withV1: 1} not found: " + tojson(allIndexes));
+ assert.eq(1, spec.v, "Expected 3.2.1 primary to build a v=1 index: " + tojson(spec));
+
+ rst.awaitReplication();
+
+ // Verify that the 3.4 secondary builds a v=1 index when the index version is omitted from the
+ // corresponding oplog entry.
+ allIndexes = secondaryDB.index_version_missing.getIndexes();
+ spec = GetIndexHelpers.findByKeyPattern(allIndexes, {withoutAnyOptions: 1});
+ assert.neq(null,
+ spec,
+ "Index with key pattern {withoutAnyOptions: 1} not found: " + tojson(allIndexes));
+ assert.eq(1, spec.v, "Expected 3.4 secondary to implicitly build a v=1 index: " + tojson(spec));
+
+ // Verify that the 3.4 secondary builds a v=1 index when it is explicitly requested by the user.
+ allIndexes = secondaryDB.index_version_missing.getIndexes();
+ spec = GetIndexHelpers.findByKeyPattern(allIndexes, {withV1: 1});
+ assert.neq(null, spec, "Index with key pattern {withV1: 1} not found: " + tojson(allIndexes));
+ assert.eq(
+ 1,
+ spec.v,
+ "Expected 3.4 secondary to build a v=1 index when explicitly requested: " + tojson(spec));
+
+ rst.stopSet();
+})();
diff --git a/jstests/noPassthrough/collation_clone_collection.js b/jstests/noPassthrough/collation_clone_collection.js
index 18bdbbe2806..1db8fec3841 100644
--- a/jstests/noPassthrough/collation_clone_collection.js
+++ b/jstests/noPassthrough/collation_clone_collection.js
@@ -24,6 +24,18 @@
sourceColl.find({_id: "foo"}).toArray(),
"query should have performed a case-insensitive match");
+ assert.commandWorked(
+ sourceColl.createIndex({withSimpleCollation: 1}, {collation: {locale: "simple"}}));
+ assert.commandWorked(sourceColl.createIndex({withDefaultCollation: 1}));
+ assert.commandWorked(
+ sourceColl.createIndex({withNonDefaultCollation: 1}, {collation: {locale: "fr"}}));
+ var sourceIndexInfos = sourceColl.getIndexes().map(function(indexInfo) {
+ // We remove the "ns" field from the index specification when comparing whether the indexes
+ // that were cloned are equivalent because they were built on a different namespace.
+ delete indexInfo.ns;
+ return indexInfo;
+ });
+
// Test that the "cloneCollection" command respects the collection-default collation.
destColl.drop();
assert.commandWorked(destColl.getDB().runCommand({
@@ -35,4 +47,19 @@
var destCollectionInfos = destColl.getDB().getCollectionInfos({name: destColl.getName()});
assert.eq(sourceCollectionInfos, destCollectionInfos);
assert.eq([{_id: "FOO"}], destColl.find({}).toArray());
+
+ var destIndexInfos = destColl.getIndexes().map(function(indexInfo) {
+ // We remove the "ns" field from the index specification when comparing whether the indexes
+ // that were cloned are equivalent because they were built on a different namespace.
+ delete indexInfo.ns;
+ return indexInfo;
+ });
+
+ assert.eq(sourceIndexInfos.length,
+ destIndexInfos.length,
+ "Number of indexes don't match; source: " + tojson(sourceIndexInfos) + ", dest: " +
+ tojson(destIndexInfos));
+ for (var i = 0; i < sourceIndexInfos.length; ++i) {
+ assert.contains(sourceIndexInfos[i], destIndexInfos);
+ }
})();
diff --git a/jstests/noPassthrough/index_version_autoupgrade.js b/jstests/noPassthrough/index_version_autoupgrade.js
new file mode 100644
index 00000000000..c6f2b766c75
--- /dev/null
+++ b/jstests/noPassthrough/index_version_autoupgrade.js
@@ -0,0 +1,148 @@
+/**
+ * Tests whether various MongoDB commands automatically upgrade the index version of existing
+ * indexes when they are rebuilt on a collection.
+ */
+(function() {
+ "use strict";
+
+ load("jstests/libs/get_index_helpers.js");
+
+ function getFeatureCompatibilityVersion(conn) {
+ const res = assert.commandWorked(
+ conn.adminCommand({getParameter: 1, featureCompatibilityVersion: 1}));
+ return res.featureCompatibilityVersion;
+ }
+
+ var conn = MongoRunner.runMongod({});
+ assert.neq(null, conn, "mongod was unable to start up");
+ assert.eq("3.4", getFeatureCompatibilityVersion(conn));
+
+ var testDB = conn.getDB("test");
+ assert.commandWorked(testDB.runCommand({create: "index_version_autoupgrade"}));
+ var allIndexes = testDB.index_version_autoupgrade.getIndexes();
+ var spec = GetIndexHelpers.findByKeyPattern(allIndexes, {_id: 1});
+ assert.neq(null, spec, "Index with key pattern {_id: 1} not found: " + tojson(allIndexes));
+ var defaultIndexVersion = spec.v;
+ assert.lte(2, defaultIndexVersion, "Expected the defaultIndexVersion to be at least v=2");
+
+ /**
+ * Tests whether the execution of the 'commandFn' function automatically upgrades the index
+ * version of existing indexes.
+ *
+ * The 'commandFn' function takes a single argument of the collection to act on and returns a
+ * collection to validate the index versions of. Most often the 'commandFn' function returns
+ * its input collection, but is able to return a reference to a different collection to support
+ * testing the effects of cloning commands.
+ *
+ * If 'doesAutoUpgrade' is true, then this function verifies that the indexes on the returned
+ * collection have been upgraded to the 'defaultIndexVersion'. If 'doesAutoUpgrade' is false,
+ * then this function verifies that the indexes on the returned collection are unchanged.
+ */
+ function testIndexVersionAutoUpgrades(commandFn, doesAutoUpgrade) {
+ testDB.dropDatabase();
+ var coll = testDB.index_version_autoupgrade;
+
+ assert.commandWorked(coll.createIndex({withoutAnyOptions: 1}));
+ var allIndexes = coll.getIndexes();
+ var spec = GetIndexHelpers.findByKeyPattern(allIndexes, {withoutAnyOptions: 1});
+ assert.neq(
+ null,
+ spec,
+ "Index with key pattern {withoutAnyOptions: 1} not found: " + tojson(allIndexes));
+ assert.eq(defaultIndexVersion,
+ spec.v,
+ "Expected an index with the default version to be built: " + tojson(spec));
+
+ assert.commandWorked(coll.createIndex({withV1: 1}, {v: 1}));
+ allIndexes = coll.getIndexes();
+ spec = GetIndexHelpers.findByKeyPattern(allIndexes, {withV1: 1});
+ assert.neq(
+ null, spec, "Index with key pattern {withV1: 1} not found: " + tojson(allIndexes));
+ assert.eq(1, spec.v, "Expected a v=1 index to be built: " + tojson(spec));
+
+ assert.commandWorked(coll.createIndex({withV2: 1}, {v: 2}));
+ allIndexes = coll.getIndexes();
+ spec = GetIndexHelpers.findByKeyPattern(allIndexes, {withV2: 1});
+ assert.neq(
+ null, spec, "Index with key pattern {withV2: 1} not found: " + tojson(allIndexes));
+ assert.eq(2, spec.v, "Expected a v=2 index to be built: " + tojson(spec));
+
+ var collToVerify = commandFn(coll);
+ var expectedResults;
+
+ if (doesAutoUpgrade) {
+ expectedResults = [
+ {keyPattern: {withoutAnyOptions: 1}, version: defaultIndexVersion},
+ {keyPattern: {withV1: 1}, version: defaultIndexVersion},
+ {keyPattern: {withV2: 1}, version: defaultIndexVersion},
+ ];
+
+ } else {
+ expectedResults = [
+ {keyPattern: {withoutAnyOptions: 1}, version: defaultIndexVersion},
+ {keyPattern: {withV1: 1}, version: 1},
+ {keyPattern: {withV2: 1}, version: 2},
+ ];
+ }
+
+ expectedResults.forEach(function(expected) {
+ var allIndexes = coll.getIndexes();
+ var spec = GetIndexHelpers.findByKeyPattern(allIndexes, expected.keyPattern);
+ assert.neq(null,
+ spec,
+ "Index with key pattern " + tojson(expected.keyPattern) + " not found: " +
+ tojson(allIndexes));
+ assert.eq(expected.version,
+ spec.v,
+ "Expected index to be rebuilt with " +
+ (doesAutoUpgrade ? "the default" : "its original") + " version: " +
+ tojson(spec));
+ });
+ }
+
+ // Test that the "reIndex" command upgrades all existing indexes to the latest version.
+ testIndexVersionAutoUpgrades(function(coll) {
+ assert.commandWorked(coll.getDB().runCommand({reIndex: coll.getName()}));
+ return coll;
+ }, true);
+
+ // Test that the "repairDatabase" command doesn't upgrade existing indexes to the latest
+ // version.
+ testIndexVersionAutoUpgrades(function(coll) {
+ assert.commandWorked(coll.getDB().runCommand({repairDatabase: 1}));
+ return coll;
+ }, false);
+
+ // Test that the "compact" command doesn't upgrade existing indexes to the latest version.
+ testIndexVersionAutoUpgrades(function(coll) {
+ assert.commandWorked(coll.getDB().runCommand({compact: coll.getName()}));
+ return coll;
+ }, false);
+
+ // Test that the "copydb" command doesn't upgrade existing indexes to the latest version.
+ testIndexVersionAutoUpgrades(function(coll) {
+ assert.commandWorked(coll.getDB().adminCommand({
+ copydb: 1,
+ fromdb: coll.getDB().getName(),
+ todb: "copied",
+ }));
+ return coll.getDB().getSiblingDB("copied")[coll.getName()];
+ }, false);
+
+ // Test that the "cloneCollection" command doesn't upgrade existing indexes to the latest
+ // version.
+ var cloneConn = MongoRunner.runMongod({});
+ assert.neq(null, cloneConn, "mongod was unable to start up");
+ assert.eq("3.4", getFeatureCompatibilityVersion(cloneConn));
+ testIndexVersionAutoUpgrades(function(coll) {
+ var cloneDB = cloneConn.getDB(coll.getDB().getName());
+ assert.commandWorked(cloneDB.runCommand({
+ cloneCollection: coll.getFullName(),
+ from: conn.host,
+ }));
+ return cloneDB[coll.getName()];
+ }, false);
+ MongoRunner.stopMongod(cloneConn);
+
+ MongoRunner.stopMongod(conn);
+})();
diff --git a/jstests/replsets/oplog_format_create_indexes.js b/jstests/replsets/oplog_format_create_indexes.js
new file mode 100644
index 00000000000..fb26bd3f2d6
--- /dev/null
+++ b/jstests/replsets/oplog_format_create_indexes.js
@@ -0,0 +1,61 @@
+/**
+ * Tests that the index's full specification is included in the oplog entry corresponding to its
+ * creation.
+ */
+(function() {
+ "use strict";
+
+ load("jstests/libs/get_index_helpers.js");
+
+ var rst = new ReplSetTest({nodes: 1});
+ rst.startSet();
+ rst.initiate();
+
+ var primary = rst.getPrimary();
+
+ var testDB = primary.getDB("test");
+ var oplogColl = primary.getDB("local").oplog.rs;
+
+ function testOplogEntryContainsIndexInfoObj(coll, keyPattern, indexOptions) {
+ assert.commandWorked(coll.createIndex(keyPattern, indexOptions));
+ var allIndexes = coll.getIndexes();
+ var indexSpec = GetIndexHelpers.findByKeyPattern(allIndexes, keyPattern);
+ assert.neq(
+ null,
+ indexSpec,
+ "Index with key pattern " + tojson(keyPattern) + " not found: " + tojson(allIndexes));
+
+ var indexCreationOplogQuery = {op: "i", ns: testDB.system.indexes.getFullName()};
+ var allOplogEntries = oplogColl.find(indexCreationOplogQuery).toArray();
+ var found = allOplogEntries.filter(function(entry) {
+ return bsonWoCompare(entry.o, indexSpec) === 0;
+ });
+ assert.eq(1,
+ found.length,
+ "Failed to find full index specification " + tojson(indexSpec) +
+ " in any oplog entry from index creation: " + tojson(allOplogEntries));
+
+ assert.commandWorked(coll.dropIndex(keyPattern));
+ }
+
+ // Test that options both explicitly included in the command and implicitly filled in with
+ // defaults by the server are serialized into the corresponding oplog entry.
+ testOplogEntryContainsIndexInfoObj(testDB.oplog_format, {withoutAnyOptions: 1});
+ testOplogEntryContainsIndexInfoObj(testDB.oplog_format, {withV1: 1}, {v: 1});
+ testOplogEntryContainsIndexInfoObj(testDB.oplog_format,
+ {partialIndex: 1},
+ {partialFilterExpression: {field: {$exists: true}}});
+
+ // Test that the representation of an index's collation in the oplog on a collection with a
+ // non-simple default collation exactly matches that of the index's full specification.
+ assert.commandWorked(
+ testDB.runCommand({create: "oplog_format_collation", collation: {locale: "fr"}}));
+ testOplogEntryContainsIndexInfoObj(testDB.oplog_format_collation, {withDefaultCollation: 1});
+ testOplogEntryContainsIndexInfoObj(
+ testDB.oplog_format_collation, {withNonDefaultCollation: 1}, {collation: {locale: "en"}});
+ testOplogEntryContainsIndexInfoObj(testDB.oplog_format_collation, {withV1: 1}, {v: 1});
+ testOplogEntryContainsIndexInfoObj(
+ testDB.oplog_format_collation, {withSimpleCollation: 1}, {collation: {locale: "simple"}});
+
+ rst.stopSet();
+})();
diff --git a/src/mongo/db/catalog/collection_compact.cpp b/src/mongo/db/catalog/collection_compact.cpp
index afba5e94987..a89ce69fbd7 100644
--- a/src/mongo/db/catalog/collection_compact.cpp
+++ b/src/mongo/db/catalog/collection_compact.cpp
@@ -42,6 +42,7 @@
#include "mongo/db/commands/server_status.h"
#include "mongo/db/curop.h"
#include "mongo/db/index/index_access_method.h"
+#include "mongo/db/index/index_descriptor.h"
#include "mongo/db/operation_context.h"
#include "mongo/util/log.h"
#include "mongo/util/touch_pages.h"
@@ -51,25 +52,32 @@ namespace mongo {
using std::endl;
using std::vector;
+using IndexVersion = IndexDescriptor::IndexVersion;
+
namespace {
BSONObj _compactAdjustIndexSpec(const BSONObj& oldSpec) {
- BSONObjBuilder b;
- BSONObj::iterator i(oldSpec);
- while (i.more()) {
- BSONElement e = i.next();
- if (str::equals(e.fieldName(), "v")) {
- // Drop any preexisting index version spec. The default index version will
- // be used instead for the new index.
- continue;
- }
- if (str::equals(e.fieldName(), "background")) {
+ BSONObjBuilder bob;
+
+ for (auto&& indexSpecElem : oldSpec) {
+ auto indexSpecElemFieldName = indexSpecElem.fieldNameStringData();
+ if (IndexDescriptor::kIndexVersionFieldName == indexSpecElemFieldName) {
+ IndexVersion indexVersion = static_cast<IndexVersion>(indexSpecElem.numberInt());
+ if (IndexVersion::kV0 == indexVersion) {
+ // We automatically upgrade v=0 indexes to v=1 indexes.
+ bob.append(IndexDescriptor::kIndexVersionFieldName,
+ static_cast<int>(IndexVersion::kV1));
+ } else {
+ bob.append(IndexDescriptor::kIndexVersionFieldName, static_cast<int>(indexVersion));
+ }
+ } else if (IndexDescriptor::kBackgroundFieldName == indexSpecElemFieldName) {
// Create the new index in the foreground.
continue;
+ } else {
+ bob.append(indexSpecElem);
}
- // Pass the element through to the new index spec.
- b.append(e);
}
- return b.obj();
+
+ return bob.obj();
}
class MyCompactAdaptor : public RecordStoreCompactAdaptor {
@@ -182,7 +190,7 @@ StatusWith<CompactStats> Collection::compact(OperationContext* txn,
indexer.allowInterruption();
indexer.ignoreUniqueConstraint(); // in compact we should be doing no checking
- Status status = indexer.init(indexSpecs);
+ Status status = indexer.init(indexSpecs).getStatus();
if (!status.isOK())
return StatusWith<CompactStats>(status);
diff --git a/src/mongo/db/catalog/database.cpp b/src/mongo/db/catalog/database.cpp
index feb4111edac..005a4f23abf 100644
--- a/src/mongo/db/catalog/database.cpp
+++ b/src/mongo/db/catalog/database.cpp
@@ -56,6 +56,7 @@
#include "mongo/db/query/collation/collator_factory_interface.h"
#include "mongo/db/repl/oplog.h"
#include "mongo/db/repl/replication_coordinator_global.h"
+#include "mongo/db/server_options.h"
#include "mongo/db/server_parameters.h"
#include "mongo/db/service_context.h"
#include "mongo/db/service_context_d.h"
@@ -576,8 +577,19 @@ Collection* Database::createCollection(OperationContext* txn,
if (collection->requiresIdIndex()) {
if (options.autoIndexId == CollectionOptions::YES ||
options.autoIndexId == CollectionOptions::DEFAULT) {
+ // The creation of the _id index isn't replicated and is instead implicit in the
+ // creation of the collection. This means that the version of the _id index to build
+ // is technically unspecified. However, we're able to use the
+ // featureCompatibilityVersion of this server to determine the default index version
+ // to use because we apply commands (opType == 'c') in their own batch. This
+ // guarantees the write to the admin.system.version collection from the
+ // "setFeatureCompatibilityVersion" command either happens entirely before the
+ // collection creation or it happens entirely after.
+ const auto featureCompatibilityVersion =
+ serverGlobalParams.featureCompatibilityVersion.load();
IndexCatalog* ic = collection->getIndexCatalog();
- uassertStatusOK(ic->createIndexOnEmptyCollection(txn, ic->getDefaultIdIndexSpec()));
+ uassertStatusOK(ic->createIndexOnEmptyCollection(
+ txn, ic->getDefaultIdIndexSpec(featureCompatibilityVersion)));
}
}
diff --git a/src/mongo/db/catalog/index_catalog.cpp b/src/mongo/db/catalog/index_catalog.cpp
index 02158d2d6db..123f7722024 100644
--- a/src/mongo/db/catalog/index_catalog.cpp
+++ b/src/mongo/db/catalog/index_catalog.cpp
@@ -59,6 +59,7 @@
#include "mongo/db/matcher/extensions_callback_disallow_extensions.h"
#include "mongo/db/operation_context.h"
#include "mongo/db/ops/delete.h"
+#include "mongo/db/query/collation/collation_spec.h"
#include "mongo/db/query/collation/collator_factory_interface.h"
#include "mongo/db/query/internal_plans.h"
#include "mongo/db/repl/replication_coordinator_global.h"
@@ -468,36 +469,41 @@ Status IndexCatalog::_isSpecOk(OperationContext* txn, const BSONObj& spec) const
const NamespaceString& nss = _collection->ns();
BSONElement vElt = spec["v"];
- if (!vElt.eoo()) {
- if (!vElt.isNumber()) {
- return Status(ErrorCodes::CannotCreateIndex,
- str::stream() << "non-numeric value for \"v\" field: " << vElt);
- }
+ if (!vElt) {
+ return {ErrorCodes::InternalError,
+ str::stream()
+ << "An internal operation failed to specify the 'v' field, which is a required "
+ "property of an index specification: "
+ << spec};
+ }
- auto vEltAsInt = representAs<int>(vElt.number());
- if (!vEltAsInt) {
- return {
- ErrorCodes::CannotCreateIndex,
+ if (!vElt.isNumber()) {
+ return Status(ErrorCodes::CannotCreateIndex,
+ str::stream() << "non-numeric value for \"v\" field: " << vElt);
+ }
+
+ auto vEltAsInt = representAs<int>(vElt.number());
+ if (!vEltAsInt) {
+ return {ErrorCodes::CannotCreateIndex,
str::stream() << "Index version must be representable as a 32-bit integer, but got "
<< vElt.toString(false, false)};
- }
+ }
- auto indexVersion = static_cast<IndexVersion>(*vEltAsInt);
+ auto indexVersion = static_cast<IndexVersion>(*vEltAsInt);
- // SERVER-16893 Forbid use of v0 indexes with non-mmapv1 engines
- if (indexVersion == IndexVersion::kV0 &&
- !txn->getServiceContext()->getGlobalStorageEngine()->isMmapV1()) {
- return Status(ErrorCodes::CannotCreateIndex,
- str::stream() << "use of v0 indexes is only allowed with the "
- << "mmapv1 storage engine");
- }
+ // SERVER-16893 Forbid use of v0 indexes with non-mmapv1 engines
+ if (indexVersion == IndexVersion::kV0 &&
+ !txn->getServiceContext()->getGlobalStorageEngine()->isMmapV1()) {
+ return Status(ErrorCodes::CannotCreateIndex,
+ str::stream() << "use of v0 indexes is only allowed with the "
+ << "mmapv1 storage engine");
+ }
- if (!IndexDescriptor::isIndexVersionSupported(indexVersion)) {
- return Status(ErrorCodes::CannotCreateIndex,
- str::stream() << "this version of mongod cannot build new indexes "
- << "of version number "
- << static_cast<int>(indexVersion));
- }
+ if (!IndexDescriptor::isIndexVersionSupported(indexVersion)) {
+ return Status(ErrorCodes::CannotCreateIndex,
+ str::stream() << "this version of mongod cannot build new indexes "
+ << "of version number "
+ << static_cast<int>(indexVersion));
}
if (nss.isSystemDotIndexes())
@@ -566,7 +572,15 @@ Status IndexCatalog::_isSpecOk(OperationContext* txn, const BSONObj& spec) const
}
collator = std::move(statusWithCollator.getValue());
- if (vElt && static_cast<IndexVersion>(vElt.numberInt()) < IndexVersion::kV2) {
+ if (!collator) {
+ return {ErrorCodes::InternalError,
+ str::stream() << "An internal operation specified the collation "
+ << CollationSpec::kSimpleSpec
+ << " explicitly, which should instead be implied by omitting the "
+ "'collation' field from the index specification"};
+ }
+
+ if (static_cast<IndexVersion>(vElt.numberInt()) < IndexVersion::kV2) {
return {ErrorCodes::CannotCreateIndex,
str::stream() << "Index version " << vElt.fieldNameStringData() << "="
<< vElt.numberInt()
@@ -576,8 +590,8 @@ Status IndexCatalog::_isSpecOk(OperationContext* txn, const BSONObj& spec) const
}
string pluginName = IndexNames::findPluginName(key);
- if (collator && (pluginName != IndexNames::BTREE) &&
- (pluginName != IndexNames::GEO_2DSPHERE) && (pluginName != IndexNames::HASHED)) {
+ if ((pluginName != IndexNames::BTREE) && (pluginName != IndexNames::GEO_2DSPHERE) &&
+ (pluginName != IndexNames::HASHED)) {
return Status(ErrorCodes::CannotCreateIndex,
str::stream() << "Index type '" << pluginName
<< "' does not support collation: "
@@ -769,13 +783,21 @@ Status IndexCatalog::_doesSpecConflictWithExisting(OperationContext* txn,
return Status::OK();
}
-BSONObj IndexCatalog::getDefaultIdIndexSpec() const {
+BSONObj IndexCatalog::getDefaultIdIndexSpec(
+ ServerGlobalParams::FeatureCompatibilityVersions featureCompatibilityVersion) const {
dassert(_idObj["_id"].type() == NumberInt);
+ const auto indexVersion = IndexDescriptor::getDefaultIndexVersion(featureCompatibilityVersion);
+
BSONObjBuilder b;
+ b.append("v", static_cast<int>(indexVersion));
b.append("name", "_id_");
b.append("ns", _collection->ns().ns());
b.append("key", _idObj);
+ if (_collection->getDefaultCollator() && indexVersion >= IndexVersion::kV2) {
+ // Creating an index with the "collation" option requires a v=2 index.
+ b.append("collation", _collection->getDefaultCollator()->getSpec().toBSON());
+ }
return b.obj();
}
@@ -1353,16 +1375,12 @@ StatusWith<BSONObj> IndexCatalog::_fixIndexSpec(OperationContext* txn,
BSONObjBuilder b;
- auto indexVersion = IndexDescriptor::getDefaultIndexVersion(
- serverGlobalParams.featureCompatibilityVersion.load());
- if (!o["v"].eoo()) {
- // We've already verified in IndexCatalog::_isSpecOk() that the index version is
- // representable as a 32-bit integer.
- indexVersion = static_cast<IndexVersion>(o["v"].numberInt());
- }
+ // We've already verified in IndexCatalog::_isSpecOk() that the index version is present and
+ // that it is representable as a 32-bit integer.
+ auto vElt = o["v"];
+ invariant(vElt);
- // idea is to put things we use a lot earlier
- b.append("v", static_cast<int>(indexVersion));
+ b.append("v", vElt.numberInt());
if (o["unique"].trueValue())
b.appendBool("unique", true); // normalize to bool true in case was int 1 or something...
@@ -1376,35 +1394,6 @@ StatusWith<BSONObj> IndexCatalog::_fixIndexSpec(OperationContext* txn,
}
b.append("name", name);
- if (auto collationElt = spec["collation"]) {
- // This should already have been verified by _isSpecOk().
- invariant(collationElt.type() == BSONType::Object);
-
- auto collator = CollatorFactoryInterface::get(txn->getServiceContext())
- ->makeFromBSON(collationElt.Obj());
- if (!collator.isOK()) {
- return collator.getStatus();
- }
-
- // If the collator factory returned a non-null collator, set the collation option to the
- // result of serializing the collator's spec back into BSON. We do this in order to fill in
- // all options that the user omitted.
- //
- // If the collator factory returned a null collator (representing the "simple" collation),
- // we simply omit the "collation" from the index spec. This ensures that indices with the
- // simple collation built on versions which do not support the collation feature have the
- // same format for representing the simple collation as indices built on this version.
- if (collator.getValue()) {
- b.append("collation", collator.getValue()->getSpec().toBSON());
- }
- } else if (collection->getDefaultCollator() && indexVersion >= IndexVersion::kV2) {
- // The user did not specify an explicit collation for this index and the collection has a
- // default collator. If we're building a v=2 index, then we should inherit the collection
- // default. However, if we're building a v=1 index, then we're implicitly building an index
- // that's using the "simple" collation.
- b.append("collation", collection->getDefaultCollator()->getSpec().toBSON());
- }
-
{
BSONObjIterator i(o);
while (i.more()) {
@@ -1415,7 +1404,7 @@ StatusWith<BSONObj> IndexCatalog::_fixIndexSpec(OperationContext* txn,
// skip
} else if (s == "dropDups") {
// dropDups is silently ignored and removed from the spec as of SERVER-14710.
- } else if (s == "v" || s == "unique" || s == "key" || s == "name" || s == "collation") {
+ } else if (s == "v" || s == "unique" || s == "key" || s == "name") {
// covered above
} else {
b.append(e);
diff --git a/src/mongo/db/catalog/index_catalog.h b/src/mongo/db/catalog/index_catalog.h
index 37ec3305615..4ff19ddd60f 100644
--- a/src/mongo/db/catalog/index_catalog.h
+++ b/src/mongo/db/catalog/index_catalog.h
@@ -37,6 +37,7 @@
#include "mongo/db/jsobj.h"
#include "mongo/db/operation_context.h"
#include "mongo/db/record_id.h"
+#include "mongo/db/server_options.h"
#include "mongo/db/storage/record_store.h"
#include "mongo/platform/unordered_map.h"
@@ -81,7 +82,8 @@ public:
/**
* Returns the spec for the id index to create by default for this collection.
*/
- BSONObj getDefaultIdIndexSpec() const;
+ BSONObj getDefaultIdIndexSpec(
+ ServerGlobalParams::FeatureCompatibilityVersions featureCompatibilityVersion) const;
IndexDescriptor* findIdIndex(OperationContext* txn) const;
diff --git a/src/mongo/db/catalog/index_create.cpp b/src/mongo/db/catalog/index_create.cpp
index 7f27589085d..8549b4f8b0c 100644
--- a/src/mongo/db/catalog/index_create.cpp
+++ b/src/mongo/db/catalog/index_create.cpp
@@ -147,12 +147,12 @@ void MultiIndexBlock::removeExistingIndexes(std::vector<BSONObj>* specs) const {
}
}
-Status MultiIndexBlock::init(const BSONObj& spec) {
+StatusWith<std::vector<BSONObj>> MultiIndexBlock::init(const BSONObj& spec) {
const auto indexes = std::vector<BSONObj>(1, spec);
return init(indexes);
}
-Status MultiIndexBlock::init(const std::vector<BSONObj>& indexSpecs) {
+StatusWith<std::vector<BSONObj>> MultiIndexBlock::init(const std::vector<BSONObj>& indexSpecs) {
WriteUnitOfWork wunit(_txn);
invariant(_indexes.empty());
@@ -182,6 +182,9 @@ Status MultiIndexBlock::init(const std::vector<BSONObj>& indexSpecs) {
_buildInBackground = (_buildInBackground && info["background"].trueValue());
}
+ std::vector<BSONObj> indexInfoObjs;
+ indexInfoObjs.reserve(indexSpecs.size());
+
for (size_t i = 0; i < indexSpecs.size(); i++) {
BSONObj info = indexSpecs[i];
StatusWith<BSONObj> statusWithInfo =
@@ -190,6 +193,7 @@ Status MultiIndexBlock::init(const std::vector<BSONObj>& indexSpecs) {
if (!status.isOK())
return status;
info = statusWithInfo.getValue();
+ indexInfoObjs.push_back(info);
IndexToBuild index;
index.block.reset(new IndexCatalog::IndexBuildBlock(_txn, _collection, info));
@@ -241,7 +245,7 @@ Status MultiIndexBlock::init(const std::vector<BSONObj>& indexSpecs) {
}
}
- return Status::OK();
+ return indexInfoObjs;
}
Status MultiIndexBlock::insertAllDocumentsInCollection(std::set<RecordId>* dupsOut) {
diff --git a/src/mongo/db/catalog/index_create.h b/src/mongo/db/catalog/index_create.h
index e5f18a42ed3..88dd5db8393 100644
--- a/src/mongo/db/catalog/index_create.h
+++ b/src/mongo/db/catalog/index_create.h
@@ -106,14 +106,15 @@ public:
void removeExistingIndexes(std::vector<BSONObj>* specs) const;
/**
- * Prepares the index(es) for building.
+ * Prepares the index(es) for building and returns the canonicalized form of the requested index
+ * specifications.
*
* Does not need to be called inside of a WriteUnitOfWork (but can be due to nesting).
*
* Requires holding an exclusive database lock.
*/
- Status init(const std::vector<BSONObj>& specs);
- Status init(const BSONObj& spec);
+ StatusWith<std::vector<BSONObj>> init(const std::vector<BSONObj>& specs);
+ StatusWith<std::vector<BSONObj>> init(const BSONObj& spec);
/**
* Inserts all documents in the Collection into the indexes and logs with timing info.
diff --git a/src/mongo/db/catalog/index_key_validate.cpp b/src/mongo/db/catalog/index_key_validate.cpp
index 44c2db2d5e7..c211d22b1d4 100644
--- a/src/mongo/db/catalog/index_key_validate.cpp
+++ b/src/mongo/db/catalog/index_key_validate.cpp
@@ -50,13 +50,6 @@ using std::string;
using IndexVersion = IndexDescriptor::IndexVersion;
-namespace {
-const StringData kKeyPatternFieldName = "key"_sd;
-const StringData kNamespaceFieldName = "ns"_sd;
-const StringData kVersionFieldName = "v"_sd;
-const StringData kCollationFieldName = "collation"_sd;
-} // namespace
-
Status validateKeyPattern(const BSONObj& key) {
const ErrorCodes::Error code = ErrorCodes::CannotCreateIndex;
@@ -158,10 +151,10 @@ StatusWith<BSONObj> validateIndexSpec(
for (auto&& indexSpecElem : indexSpec) {
auto indexSpecElemFieldName = indexSpecElem.fieldNameStringData();
- if (kKeyPatternFieldName == indexSpecElemFieldName) {
+ if (IndexDescriptor::kKeyPatternFieldName == indexSpecElemFieldName) {
if (indexSpecElem.type() != BSONType::Object) {
return {ErrorCodes::TypeMismatch,
- str::stream() << "The field '" << kKeyPatternFieldName
+ str::stream() << "The field '" << IndexDescriptor::kKeyPatternFieldName
<< "' must be an object, but got "
<< typeName(indexSpecElem.type())};
}
@@ -179,10 +172,10 @@ StatusWith<BSONObj> validateIndexSpec(
}
hasKeyPatternField = true;
- } else if (kNamespaceFieldName == indexSpecElemFieldName) {
+ } else if (IndexDescriptor::kNamespaceFieldName == indexSpecElemFieldName) {
if (indexSpecElem.type() != BSONType::String) {
return {ErrorCodes::TypeMismatch,
- str::stream() << "The field '" << kNamespaceFieldName
+ str::stream() << "The field '" << IndexDescriptor::kNamespaceFieldName
<< "' must be a string, but got "
<< typeName(indexSpecElem.type())};
}
@@ -190,13 +183,15 @@ StatusWith<BSONObj> validateIndexSpec(
StringData ns = indexSpecElem.valueStringData();
if (ns.empty()) {
return {ErrorCodes::BadValue,
- str::stream() << "The field '" << kNamespaceFieldName
+ str::stream() << "The field '" << IndexDescriptor::kNamespaceFieldName
<< "' cannot be an empty string"};
}
if (ns != expectedNamespace.ns()) {
return {ErrorCodes::BadValue,
- str::stream() << "The value of the field '" << kNamespaceFieldName << "' ("
+ str::stream() << "The value of the field '"
+ << IndexDescriptor::kNamespaceFieldName
+ << "' ("
<< ns
<< ") doesn't match the namespace '"
<< expectedNamespace.ns()
@@ -204,10 +199,10 @@ StatusWith<BSONObj> validateIndexSpec(
}
hasNamespaceField = true;
- } else if (kVersionFieldName == indexSpecElemFieldName) {
+ } else if (IndexDescriptor::kIndexVersionFieldName == indexSpecElemFieldName) {
if (!indexSpecElem.isNumber()) {
return {ErrorCodes::TypeMismatch,
- str::stream() << "The field '" << kVersionFieldName
+ str::stream() << "The field '" << IndexDescriptor::kIndexVersionFieldName
<< "' must be a number, but got "
<< typeName(indexSpecElem.type())};
}
@@ -230,10 +225,10 @@ StatusWith<BSONObj> validateIndexSpec(
hasVersionField = true;
resolvedIndexVersion = requestedIndexVersion;
- } else if (kCollationFieldName == indexSpecElemFieldName) {
+ } else if (IndexDescriptor::kCollationFieldName == indexSpecElemFieldName) {
if (indexSpecElem.type() != BSONType::Object) {
return {ErrorCodes::TypeMismatch,
- str::stream() << "The field '" << kNamespaceFieldName
+ str::stream() << "The field '" << IndexDescriptor::kNamespaceFieldName
<< "' must be an object, but got "
<< typeName(indexSpecElem.type())};
}
@@ -251,7 +246,7 @@ StatusWith<BSONObj> validateIndexSpec(
if (!hasKeyPatternField) {
return {ErrorCodes::FailedToParse,
- str::stream() << "The '" << kKeyPatternFieldName
+ str::stream() << "The '" << IndexDescriptor::kKeyPatternFieldName
<< "' field is a required property of an index specification"};
}
@@ -259,9 +254,9 @@ StatusWith<BSONObj> validateIndexSpec(
return {ErrorCodes::CannotCreateIndex,
str::stream() << "Invalid index specification " << indexSpec
<< "; cannot create an index with the '"
- << kCollationFieldName
+ << IndexDescriptor::kCollationFieldName
<< "' option and "
- << kVersionFieldName
+ << IndexDescriptor::kIndexVersionFieldName
<< "="
<< static_cast<int>(*resolvedIndexVersion)};
}
@@ -272,13 +267,14 @@ StatusWith<BSONObj> validateIndexSpec(
if (!hasNamespaceField) {
// We create a new index specification with the 'ns' field set as 'expectedNamespace' if
// the field was omitted.
- bob.append(kNamespaceFieldName, expectedNamespace.ns());
+ bob.append(IndexDescriptor::kNamespaceFieldName, expectedNamespace.ns());
}
if (!hasVersionField) {
// We create a new index specification with the 'v' field set as 'defaultIndexVersion'
// if the field was omitted.
- bob.append(kVersionFieldName, static_cast<int>(*resolvedIndexVersion));
+ bob.append(IndexDescriptor::kIndexVersionFieldName,
+ static_cast<int>(*resolvedIndexVersion));
}
bob.appendElements(indexSpec);
diff --git a/src/mongo/db/cloner.cpp b/src/mongo/db/cloner.cpp
index 249b807ab53..e935e38b9f7 100644
--- a/src/mongo/db/cloner.cpp
+++ b/src/mongo/db/cloner.cpp
@@ -52,6 +52,7 @@
#include "mongo/db/concurrency/write_conflict_exception.h"
#include "mongo/db/dbdirectclient.h"
#include "mongo/db/dbhelpers.h"
+#include "mongo/db/index/index_descriptor.h"
#include "mongo/db/index_builder.h"
#include "mongo/db/jsobj.h"
#include "mongo/db/namespace_string.h"
@@ -75,6 +76,8 @@ using std::string;
using std::unique_ptr;
using std::vector;
+using IndexVersion = IndexDescriptor::IndexVersion;
+
MONGO_EXPORT_SERVER_PARAMETER(skipCorruptDocumentsWhenCloning, bool, false);
BSONElement getErrField(const BSONObj& o);
@@ -84,33 +87,32 @@ BSONElement getErrField(const BSONObj& o);
we need to fix up the value in the "ns" parameter so that the name prefix is correct on a
copy to a new name.
*/
-BSONObj fixindex(const string& newDbName, BSONObj o) {
- BSONObjBuilder b;
- BSONObjIterator i(o);
- while (i.moreWithEOO()) {
- BSONElement e = i.next();
- if (e.eoo())
- break;
-
- // for now, skip the "v" field so that v:0 indexes will be upgraded to v:1
- if (string("v") == e.fieldName()) {
- continue;
- }
-
- if (string("ns") == e.fieldName()) {
- uassert(10024, "bad ns field for index during dbcopy", e.type() == String);
- const char* p = strchr(e.valuestr(), '.');
+BSONObj fixIndexSpec(const string& newDbName, BSONObj indexSpec) {
+ BSONObjBuilder bob;
+
+ for (auto&& indexSpecElem : indexSpec) {
+ auto indexSpecElemFieldName = indexSpecElem.fieldNameStringData();
+ if (IndexDescriptor::kIndexVersionFieldName == indexSpecElemFieldName) {
+ IndexVersion indexVersion = static_cast<IndexVersion>(indexSpecElem.numberInt());
+ if (IndexVersion::kV0 == indexVersion) {
+ // We automatically upgrade v=0 indexes to v=1 indexes.
+ bob.append(IndexDescriptor::kIndexVersionFieldName,
+ static_cast<int>(IndexVersion::kV1));
+ } else {
+ bob.append(IndexDescriptor::kIndexVersionFieldName, static_cast<int>(indexVersion));
+ }
+ } else if (IndexDescriptor::kNamespaceFieldName == indexSpecElemFieldName) {
+ uassert(10024, "bad ns field for index during dbcopy", indexSpecElem.type() == String);
+ const char* p = strchr(indexSpecElem.valuestr(), '.');
uassert(10025, "bad ns field for index during dbcopy [2]", p);
string newname = newDbName + p;
- b.append("ns", newname);
+ bob.append(IndexDescriptor::kNamespaceFieldName, newname);
} else {
- b.append(e);
+ bob.append(indexSpecElem);
}
}
- BSONObj res = b.obj();
-
- return res;
+ return bob.obj();
}
Cloner::Cloner() {}
@@ -328,7 +330,7 @@ void Cloner::copyIndexes(OperationContext* txn,
_conn->getIndexSpecs(from_collection.ns(), slaveOk ? QueryOption_SlaveOk : 0);
for (list<BSONObj>::const_iterator it = sourceIndexes.begin(); it != sourceIndexes.end();
++it) {
- indexesToBuild.push_back(fixindex(to_collection.db().toString(), *it));
+ indexesToBuild.push_back(fixIndexSpec(to_collection.db().toString(), *it));
}
}
@@ -375,7 +377,7 @@ void Cloner::copyIndexes(OperationContext* txn,
if (indexesToBuild.empty())
return;
- uassertStatusOK(indexer.init(indexesToBuild));
+ auto indexInfoObjs = uassertStatusOK(indexer.init(indexesToBuild));
uassertStatusOK(indexer.insertAllDocumentsInCollection());
WriteUnitOfWork wunit(txn);
@@ -383,10 +385,8 @@ void Cloner::copyIndexes(OperationContext* txn,
if (txn->writesAreReplicated()) {
const string targetSystemIndexesCollectionName = to_collection.getSystemIndexesCollection();
const char* createIndexNs = targetSystemIndexesCollectionName.c_str();
- for (vector<BSONObj>::const_iterator it = indexesToBuild.begin();
- it != indexesToBuild.end();
- ++it) {
- getGlobalServiceContext()->getOpObserver()->onCreateIndex(txn, createIndexNs, *it);
+ for (auto&& infoObj : indexInfoObjs) {
+ getGlobalServiceContext()->getOpObserver()->onCreateIndex(txn, createIndexNs, infoObj);
}
}
wunit.commit();
@@ -674,7 +674,11 @@ Status Cloner::copyDb(OperationContext* txn,
MultiIndexBlock indexer(txn, c);
indexer.allowInterruption();
- uassertStatusOK(indexer.init(c->getIndexCatalog()->getDefaultIdIndexSpec()));
+ const auto featureCompatibilityVersion =
+ serverGlobalParams.featureCompatibilityVersion.load();
+ auto indexInfoObjs = uassertStatusOK(indexer.init(
+ c->getIndexCatalog()->getDefaultIdIndexSpec(featureCompatibilityVersion)));
+ invariant(indexInfoObjs.size() == 1);
uassertStatusOK(indexer.insertAllDocumentsInCollection(&dups));
// This must be done before we commit the indexer. See the comment about
@@ -694,9 +698,7 @@ Status Cloner::copyDb(OperationContext* txn,
indexer.commit();
if (txn->writesAreReplicated()) {
getGlobalServiceContext()->getOpObserver()->onCreateIndex(
- txn,
- c->ns().getSystemIndexesCollection().c_str(),
- c->getIndexCatalog()->getDefaultIdIndexSpec());
+ txn, c->ns().getSystemIndexesCollection().c_str(), indexInfoObjs[0]);
}
wunit.commit();
}
diff --git a/src/mongo/db/commands/create_indexes.cpp b/src/mongo/db/commands/create_indexes.cpp
index fc0c78b8e3a..176b6a84b6d 100644
--- a/src/mongo/db/commands/create_indexes.cpp
+++ b/src/mongo/db/commands/create_indexes.cpp
@@ -42,8 +42,10 @@
#include "mongo/db/commands.h"
#include "mongo/db/concurrency/write_conflict_exception.h"
#include "mongo/db/curop.h"
+#include "mongo/db/index/index_descriptor.h"
#include "mongo/db/op_observer.h"
#include "mongo/db/ops/insert.h"
+#include "mongo/db/query/collation/collator_factory_interface.h"
#include "mongo/db/repl/repl_client_info.h"
#include "mongo/db/repl/replication_coordinator_global.h"
#include "mongo/db/s/collection_metadata.h"
@@ -58,6 +60,8 @@ namespace mongo {
using std::string;
+using IndexVersion = IndexDescriptor::IndexVersion;
+
namespace {
const StringData kIndexesFieldName = "indexes"_sd;
@@ -121,6 +125,79 @@ StatusWith<std::vector<BSONObj>> parseAndValidateIndexSpecs(
return indexSpecs;
}
+/**
+ * Returns index specifications with attributes (such as "collation") that are inherited from the
+ * collection filled in.
+ *
+ * The returned index specifications will not be equivalent to the ones specified as 'indexSpecs' if
+ * any missing attributes were filled in; however, the returned index specifications will match the
+ * form stored in the IndexCatalog should any of these indexes already exist.
+ */
+StatusWith<std::vector<BSONObj>> resolveCollectionDefaultProperties(
+ OperationContext* txn, const Collection* collection, std::vector<BSONObj> indexSpecs) {
+ std::vector<BSONObj> indexSpecsWithDefaults = std::move(indexSpecs);
+
+ for (size_t i = 0, numIndexSpecs = indexSpecsWithDefaults.size(); i < numIndexSpecs; ++i) {
+ const BSONObj& indexSpec = indexSpecsWithDefaults[i];
+ if (auto collationElem = indexSpec[IndexDescriptor::kCollationFieldName]) {
+ // validateIndexSpec() should have already verified that 'collationElem' is an object.
+ invariant(collationElem.type() == BSONType::Object);
+
+ auto collator = CollatorFactoryInterface::get(txn->getServiceContext())
+ ->makeFromBSON(collationElem.Obj());
+ if (!collator.isOK()) {
+ return collator.getStatus();
+ }
+
+ if (collator.getValue()) {
+ // If the collator factory returned a non-null collator, then inject the entire
+ // collation specification into the index specification. This is necessary to fill
+ // in any options that the user omitted.
+ BSONObjBuilder bob;
+
+ for (auto&& indexSpecElem : indexSpec) {
+ if (IndexDescriptor::kCollationFieldName !=
+ indexSpecElem.fieldNameStringData()) {
+ bob.append(indexSpecElem);
+ }
+ }
+ bob.append(IndexDescriptor::kCollationFieldName,
+ collator.getValue()->getSpec().toBSON());
+
+ indexSpecsWithDefaults[i] = bob.obj();
+ } else {
+ // If the collator factory returned a null collator (representing the "simple"
+ // collation), then we simply omit the "collation" from the index specification.
+ // This is desirable to make the representation for the "simple" collation
+ // consistent between v=1 and v=2 indexes.
+ indexSpecsWithDefaults[i] =
+ indexSpec.removeField(IndexDescriptor::kCollationFieldName);
+ }
+ } else if (collection->getDefaultCollator()) {
+ // validateIndexSpec() should have added the "v" field if it was not present and
+ // verified that 'versionElem' is a number.
+ auto versionElem = indexSpec[IndexDescriptor::kIndexVersionFieldName];
+ invariant(versionElem.isNumber());
+
+ if (IndexVersion::kV2 <= static_cast<IndexVersion>(versionElem.numberInt())) {
+ // The user did not specify an explicit collation for this index and the collection
+ // has a default collator. If we're building a v=2 index, then we should inherit the
+ // collection default. However, if we're building a v=1 index, then we're implicitly
+ // building an index that's using the "simple" collation.
+ BSONObjBuilder bob;
+
+ bob.appendElements(indexSpec);
+ bob.append(IndexDescriptor::kCollationFieldName,
+ collection->getDefaultCollator()->getSpec().toBSON());
+
+ indexSpecsWithDefaults[i] = bob.obj();
+ }
+ }
+ }
+
+ return indexSpecsWithDefaults;
+}
+
} // namespace
/**
@@ -203,6 +280,13 @@ public:
result.appendBool("createdCollectionAutomatically", true);
}
+ auto indexSpecsWithDefaults =
+ resolveCollectionDefaultProperties(txn, collection, std::move(specs));
+ if (!indexSpecsWithDefaults.isOK()) {
+ return appendCommandStatus(result, indexSpecsWithDefaults.getStatus());
+ }
+ specs = std::move(indexSpecsWithDefaults.getValue());
+
const int numIndexesBefore = collection->getIndexCatalog()->numIndexesTotal(txn);
result.append("numIndexesBefore", numIndexesBefore);
@@ -240,8 +324,9 @@ public:
}
}
+ std::vector<BSONObj> indexInfoObjs;
MONGO_WRITE_CONFLICT_RETRY_LOOP_BEGIN {
- uassertStatusOK(indexer.init(specs));
+ indexInfoObjs = uassertStatusOK(indexer.init(specs));
}
MONGO_WRITE_CONFLICT_RETRY_LOOP_END(txn, "createIndexes", ns.ns());
@@ -306,11 +391,12 @@ public:
indexer.commit();
- for (size_t i = 0; i < specs.size(); i++) {
+ for (auto&& infoObj : indexInfoObjs) {
std::string systemIndexes = ns.getSystemIndexesCollection();
auto opObserver = getGlobalServiceContext()->getOpObserver();
- if (opObserver)
- opObserver->onCreateIndex(txn, systemIndexes, specs[i]);
+ if (opObserver) {
+ opObserver->onCreateIndex(txn, systemIndexes, infoObj);
+ }
}
wunit.commit();
diff --git a/src/mongo/db/commands/drop_indexes.cpp b/src/mongo/db/commands/drop_indexes.cpp
index 347e3762d9e..cdca8e10bcb 100644
--- a/src/mongo/db/commands/drop_indexes.cpp
+++ b/src/mongo/db/commands/drop_indexes.cpp
@@ -144,14 +144,38 @@ public:
BackgroundOperation::assertNoBgOpInProgForNs(toReIndexNs.ns());
+ const auto featureCompatibilityVersion =
+ serverGlobalParams.featureCompatibilityVersion.load();
+ const auto defaultIndexVersion =
+ IndexDescriptor::getDefaultIndexVersion(featureCompatibilityVersion);
+
vector<BSONObj> all;
{
vector<string> indexNames;
collection->getCatalogEntry()->getAllIndexes(txn, &indexNames);
+ all.reserve(indexNames.size());
+
for (size_t i = 0; i < indexNames.size(); i++) {
const string& name = indexNames[i];
BSONObj spec = collection->getCatalogEntry()->getIndexSpec(txn, name);
- all.push_back(spec.removeField("v").getOwned());
+
+ {
+ BSONObjBuilder bob;
+
+ for (auto&& indexSpecElem : spec) {
+ auto indexSpecElemFieldName = indexSpecElem.fieldNameStringData();
+ if (IndexDescriptor::kIndexVersionFieldName == indexSpecElemFieldName) {
+ // We create a new index specification with the 'v' field set as
+ // 'defaultIndexVersion'.
+ bob.append(IndexDescriptor::kIndexVersionFieldName,
+ static_cast<int>(defaultIndexVersion));
+ } else {
+ bob.append(indexSpecElem);
+ }
+ }
+
+ all.push_back(bob.obj());
+ }
const BSONObj key = spec.getObjectField("key");
const Status keyStatus = validateKeyPattern(key);
@@ -179,13 +203,15 @@ public:
MultiIndexBlock indexer(txn, collection);
// do not want interruption as that will leave us without indexes.
- Status status = indexer.init(all);
- if (!status.isOK())
- return appendCommandStatus(result, status);
+ auto indexInfoObjs = indexer.init(all);
+ if (!indexInfoObjs.isOK()) {
+ return appendCommandStatus(result, indexInfoObjs.getStatus());
+ }
- status = indexer.insertAllDocumentsInCollection();
- if (!status.isOK())
+ auto status = indexer.insertAllDocumentsInCollection();
+ if (!status.isOK()) {
return appendCommandStatus(result, status);
+ }
{
WriteUnitOfWork wunit(txn);
@@ -202,8 +228,8 @@ public:
replCoord->forceSnapshotCreation(); // Ensures a newer snapshot gets created even if idle.
collection->setMinimumVisibleSnapshot(snapshotName);
- result.append("nIndexes", (int)all.size());
- result.append("indexes", all);
+ result.append("nIndexes", static_cast<int>(indexInfoObjs.getValue().size()));
+ result.append("indexes", indexInfoObjs.getValue());
return true;
}
diff --git a/src/mongo/db/commands/mr.cpp b/src/mongo/db/commands/mr.cpp
index e4efbba4d2f..eb304a56f49 100644
--- a/src/mongo/db/commands/mr.cpp
+++ b/src/mongo/db/commands/mr.cpp
@@ -94,6 +94,8 @@ using std::stringstream;
using std::unique_ptr;
using std::vector;
+using IndexVersion = IndexDescriptor::IndexVersion;
+
namespace dps = ::mongo::dotted_path_support;
namespace mr {
@@ -414,8 +416,14 @@ void State::prepTempCollection() {
incColl = incCtx.db()->createCollection(_txn, _config.incLong, options);
invariant(incColl);
+ // We explicitly create a v=2 index on the "0" field so that it is always possible for a
+ // user to emit() decimal keys. Since the incremental collection is not replicated to
+ // any secondaries, there is no risk of inadvertently crashing an older version of
+ // MongoDB when the featureCompatibilityVersion of this server is 3.2.
BSONObj indexSpec = BSON("key" << BSON("0" << 1) << "ns" << _config.incLong << "name"
- << "_temp_0");
+ << "_temp_0"
+ << "v"
+ << static_cast<int>(IndexVersion::kV2));
Status status =
incColl->getIndexCatalog()->createIndexOnEmptyCollection(_txn, indexSpec);
if (!status.isOK()) {
diff --git a/src/mongo/db/dbhelpers.cpp b/src/mongo/db/dbhelpers.cpp
index fa728c3bdca..53e73c11119 100644
--- a/src/mongo/db/dbhelpers.cpp
+++ b/src/mongo/db/dbhelpers.cpp
@@ -85,18 +85,20 @@ using logger::LogComponent;
void Helpers::ensureIndex(OperationContext* txn,
Collection* collection,
BSONObj keyPattern,
+ IndexDescriptor::IndexVersion indexVersion,
bool unique,
const char* name) {
BSONObjBuilder b;
b.append("name", name);
b.append("ns", collection->ns().ns());
b.append("key", keyPattern);
+ b.append("v", static_cast<int>(indexVersion));
b.appendBool("unique", unique);
BSONObj o = b.done();
MultiIndexBlock indexer(txn, collection);
- Status status = indexer.init(o);
+ Status status = indexer.init(o).getStatus();
if (status.code() == ErrorCodes::IndexAlreadyExists)
return;
uassertStatusOK(status);
diff --git a/src/mongo/db/dbhelpers.h b/src/mongo/db/dbhelpers.h
index 6386a943552..f3acb9f190c 100644
--- a/src/mongo/db/dbhelpers.h
+++ b/src/mongo/db/dbhelpers.h
@@ -32,6 +32,7 @@
#include <memory>
#include "mongo/db/db.h"
+#include "mongo/db/index/index_descriptor.h"
#include "mongo/db/record_id.h"
#include "mongo/db/storage/data_protector.h"
@@ -66,6 +67,7 @@ struct Helpers {
static void ensureIndex(OperationContext* txn,
Collection* collection,
BSONObj keyPattern,
+ IndexDescriptor::IndexVersion indexVersion,
bool unique,
const char* name);
diff --git a/src/mongo/db/index/index_descriptor.cpp b/src/mongo/db/index/index_descriptor.cpp
index def508b07af..d86ee74498b 100644
--- a/src/mongo/db/index/index_descriptor.cpp
+++ b/src/mongo/db/index/index_descriptor.cpp
@@ -50,21 +50,38 @@ void populateOptionsMap(std::map<StringData, BSONElement>& theMap, const BSONObj
const BSONElement e = it.next();
StringData fieldName = e.fieldNameStringData();
- if (fieldName == "key" || fieldName == "ns" || fieldName == "name" ||
- fieldName == "v" || // not considered for equivalence
- fieldName == "textIndexVersion" || // same as "v"
- fieldName == "2dsphereIndexVersion" || // same as "v"
- fieldName == "background" || // this is a creation time option only
- fieldName == "dropDups" || // this is now ignored
- fieldName == "sparse" || // checked specially
- fieldName == "unique" // check specially
+ if (fieldName == IndexDescriptor::kKeyPatternFieldName ||
+ fieldName == IndexDescriptor::kNamespaceFieldName ||
+ fieldName == IndexDescriptor::kIndexNameFieldName ||
+ fieldName ==
+ IndexDescriptor::kIndexVersionFieldName || // not considered for equivalence
+ fieldName == IndexDescriptor::kTextVersionFieldName || // same as index version
+ fieldName == IndexDescriptor::k2dsphereVersionFieldName || // same as index version
+ fieldName ==
+ IndexDescriptor::kBackgroundFieldName || // this is a creation time option only
+ fieldName == IndexDescriptor::kDropDuplicatesFieldName || // this is now ignored
+ fieldName == IndexDescriptor::kSparseFieldName || // checked specially
+ fieldName == IndexDescriptor::kUniqueFieldName // check specially
) {
continue;
}
theMap[fieldName] = e;
}
}
-}
+} // namespace
+
+const StringData IndexDescriptor::k2dsphereVersionFieldName = "2dsphereIndexVersion"_sd;
+const StringData IndexDescriptor::kBackgroundFieldName = "background"_sd;
+const StringData IndexDescriptor::kCollationFieldName = "collation"_sd;
+const StringData IndexDescriptor::kDropDuplicatesFieldName = "dropDups"_sd;
+const StringData IndexDescriptor::kIndexNameFieldName = "name"_sd;
+const StringData IndexDescriptor::kIndexVersionFieldName = "v"_sd;
+const StringData IndexDescriptor::kKeyPatternFieldName = "key"_sd;
+const StringData IndexDescriptor::kNamespaceFieldName = "ns"_sd;
+const StringData IndexDescriptor::kPartialFilterExprFieldName = "partialFilterExpression"_sd;
+const StringData IndexDescriptor::kSparseFieldName = "sparse"_sd;
+const StringData IndexDescriptor::kTextVersionFieldName = "textIndexVersion"_sd;
+const StringData IndexDescriptor::kUniqueFieldName = "unique"_sd;
bool IndexDescriptor::isIndexVersionSupported(IndexVersion indexVersion) {
switch (indexVersion) {
diff --git a/src/mongo/db/index/index_descriptor.h b/src/mongo/db/index/index_descriptor.h
index 429f24fcf1e..581548b8b34 100644
--- a/src/mongo/db/index/index_descriptor.h
+++ b/src/mongo/db/index/index_descriptor.h
@@ -56,6 +56,19 @@ class IndexDescriptor {
public:
enum class IndexVersion { kV0 = 0, kV1 = 1, kV2 = 2 };
+ static const StringData k2dsphereVersionFieldName;
+ static const StringData kBackgroundFieldName;
+ static const StringData kCollationFieldName;
+ static const StringData kDropDuplicatesFieldName;
+ static const StringData kIndexNameFieldName;
+ static const StringData kIndexVersionFieldName;
+ static const StringData kKeyPatternFieldName;
+ static const StringData kNamespaceFieldName;
+ static const StringData kPartialFilterExprFieldName;
+ static const StringData kSparseFieldName;
+ static const StringData kTextVersionFieldName;
+ static const StringData kUniqueFieldName;
+
/**
* OnDiskIndexData is a pointer to the memory mapped per-index data.
* infoObj is a copy of the index-describing BSONObj contained in the OnDiskIndexData.
@@ -64,19 +77,19 @@ public:
: _collection(collection),
_accessMethodName(accessMethodName),
_infoObj(infoObj.getOwned()),
- _numFields(infoObj.getObjectField("key").nFields()),
- _keyPattern(infoObj.getObjectField("key").getOwned()),
- _indexName(infoObj.getStringField("name")),
- _parentNS(infoObj.getStringField("ns")),
+ _numFields(infoObj.getObjectField(IndexDescriptor::kKeyPatternFieldName).nFields()),
+ _keyPattern(infoObj.getObjectField(IndexDescriptor::kKeyPatternFieldName).getOwned()),
+ _indexName(infoObj.getStringField(IndexDescriptor::kIndexNameFieldName)),
+ _parentNS(infoObj.getStringField(IndexDescriptor::kNamespaceFieldName)),
_isIdIndex(isIdIndexPattern(_keyPattern)),
- _sparse(infoObj["sparse"].trueValue()),
- _unique(_isIdIndex || infoObj["unique"].trueValue()),
- _partial(!infoObj["partialFilterExpression"].eoo()),
+ _sparse(infoObj[IndexDescriptor::kSparseFieldName].trueValue()),
+ _unique(_isIdIndex || infoObj[kUniqueFieldName].trueValue()),
+ _partial(!infoObj[kPartialFilterExprFieldName].eoo()),
_cachedEntry(NULL) {
_indexNamespace = makeIndexNamespace(_parentNS, _indexName);
_version = IndexVersion::kV0;
- BSONElement e = _infoObj["v"];
+ BSONElement e = _infoObj[IndexDescriptor::kIndexVersionFieldName];
if (e.isNumber()) {
_version = static_cast<IndexVersion>(e.numberInt());
}
diff --git a/src/mongo/db/index_builder.cpp b/src/mongo/db/index_builder.cpp
index fbefb512ff2..352b5f2d5cc 100644
--- a/src/mongo/db/index_builder.cpp
+++ b/src/mongo/db/index_builder.cpp
@@ -161,7 +161,7 @@ Status IndexBuilder::_build(OperationContext* txn,
try {
- status = indexer.init(_index);
+ status = indexer.init(_index).getStatus();
if (status.code() == ErrorCodes::IndexAlreadyExists) {
if (allowBackgroundBuilding) {
// Must set this in case anyone is waiting for this build.
diff --git a/src/mongo/db/repair_database.cpp b/src/mongo/db/repair_database.cpp
index 4f5665e55da..9172b2f3a1d 100644
--- a/src/mongo/db/repair_database.cpp
+++ b/src/mongo/db/repair_database.cpp
@@ -44,6 +44,7 @@
#include "mongo/db/catalog/document_validation.h"
#include "mongo/db/catalog/index_create.h"
#include "mongo/db/catalog/index_key_validate.h"
+#include "mongo/db/index/index_descriptor.h"
#include "mongo/db/repl/replication_coordinator.h"
#include "mongo/db/storage/mmap_v1/mmap_v1_engine.h"
#include "mongo/db/storage/storage_engine.h"
@@ -55,6 +56,8 @@ namespace mongo {
using std::endl;
using std::string;
+using IndexVersion = IndexDescriptor::IndexVersion;
+
namespace {
Status rebuildIndexesOnCollection(OperationContext* txn,
DatabaseCatalogEntry* dbce,
@@ -66,10 +69,35 @@ Status rebuildIndexesOnCollection(OperationContext* txn,
{
// Fetch all indexes
cce->getAllIndexes(txn, &indexNames);
+ indexSpecs.reserve(indexNames.size());
+
for (size_t i = 0; i < indexNames.size(); i++) {
const string& name = indexNames[i];
BSONObj spec = cce->getIndexSpec(txn, name);
- indexSpecs.push_back(spec.removeField("v").getOwned());
+
+ {
+ BSONObjBuilder bob;
+
+ for (auto&& indexSpecElem : spec) {
+ auto indexSpecElemFieldName = indexSpecElem.fieldNameStringData();
+ if (IndexDescriptor::kIndexVersionFieldName == indexSpecElemFieldName) {
+ IndexVersion indexVersion =
+ static_cast<IndexVersion>(indexSpecElem.numberInt());
+ if (IndexVersion::kV0 == indexVersion) {
+ // We automatically upgrade v=0 indexes to v=1 indexes.
+ bob.append(IndexDescriptor::kIndexVersionFieldName,
+ static_cast<int>(IndexVersion::kV1));
+ } else {
+ bob.append(IndexDescriptor::kIndexVersionFieldName,
+ static_cast<int>(indexVersion));
+ }
+ } else {
+ bob.append(indexSpecElem);
+ }
+ }
+
+ indexSpecs.push_back(bob.obj());
+ }
const BSONObj key = spec.getObjectField("key");
const Status keyStatus = validateKeyPattern(key);
@@ -116,7 +144,7 @@ Status rebuildIndexesOnCollection(OperationContext* txn,
collection.reset(new Collection(txn, ns, cce, dbce->getRecordStore(ns), dbce));
indexer.reset(new MultiIndexBlock(txn, collection.get()));
- Status status = indexer->init(indexSpecs);
+ Status status = indexer->init(indexSpecs).getStatus();
if (!status.isOK()) {
// The WUOW will handle cleanup, so the indexer shouldn't do its own.
indexer->abortWithoutCleanup();
diff --git a/src/mongo/db/repl/collection_bulk_loader_impl.cpp b/src/mongo/db/repl/collection_bulk_loader_impl.cpp
index 3f8e78d46ca..c3354e3f225 100644
--- a/src/mongo/db/repl/collection_bulk_loader_impl.cpp
+++ b/src/mongo/db/repl/collection_bulk_loader_impl.cpp
@@ -93,7 +93,7 @@ Status CollectionBulkLoaderImpl::init(OperationContext* txn,
invariant(txn->getClient() == &cc());
if (secondaryIndexSpecs.size()) {
_secondaryIndexesBlock->ignoreUniqueConstraint();
- auto status = _secondaryIndexesBlock->init(secondaryIndexSpecs);
+ auto status = _secondaryIndexesBlock->init(secondaryIndexSpecs).getStatus();
if (!status.isOK()) {
return status;
}
@@ -101,7 +101,7 @@ Status CollectionBulkLoaderImpl::init(OperationContext* txn,
_secondaryIndexesBlock.reset();
}
if (!_idIndexSpec.isEmpty()) {
- auto status = _idIndexBlock->init(_idIndexSpec);
+ auto status = _idIndexBlock->init(_idIndexSpec).getStatus();
if (!status.isOK()) {
return status;
}
diff --git a/src/mongo/db/repl/oplog.cpp b/src/mongo/db/repl/oplog.cpp
index ac2d26bc1b8..16f655443f0 100644
--- a/src/mongo/db/repl/oplog.cpp
+++ b/src/mongo/db/repl/oplog.cpp
@@ -64,6 +64,7 @@
#include "mongo/db/dbhelpers.h"
#include "mongo/db/global_timestamp.h"
#include "mongo/db/index/index_access_method.h"
+#include "mongo/db/index/index_descriptor.h"
#include "mongo/db/index_builder.h"
#include "mongo/db/keypattern.h"
#include "mongo/db/namespace_string.h"
@@ -102,6 +103,8 @@ using std::stringstream;
using std::unique_ptr;
using std::vector;
+using IndexVersion = IndexDescriptor::IndexVersion;
+
namespace repl {
std::string rsOplogName = "local.oplog.rs";
std::string masterSlaveOplogName = "local.oplog.$main";
@@ -708,9 +711,10 @@ Status applyOperation_inlock(OperationContext* txn,
uassert(ErrorCodes::TypeMismatch,
str::stream() << "Expected object for index spec in field 'o': " << op,
fieldO.isABSONObj());
+ BSONObj indexSpec = fieldO.embeddedObject();
std::string indexNs;
- uassertStatusOK(bsonExtractStringField(o, "ns", &indexNs));
+ uassertStatusOK(bsonExtractStringField(indexSpec, "ns", &indexNs));
const NamespaceString indexNss(indexNs);
uassert(ErrorCodes::InvalidNamespace,
str::stream() << "Invalid namespace in index spec: " << op,
@@ -723,24 +727,39 @@ Status applyOperation_inlock(OperationContext* txn,
nsToDatabaseSubstring(ns) == indexNss.db());
opCounters->gotInsert();
- if (o["background"].trueValue()) {
+
+ if (!indexSpec["v"]) {
+ // If the "v" field isn't present in the index specification, then we assume it is a
+ // v=1 index from an older version of MongoDB. This is because
+ // (1) we haven't built v=0 indexes as the default for a long time, and
+ // (2) the index version has been included in the corresponding oplog entry since
+ // v=2 indexes were introduced.
+ BSONObjBuilder bob;
+
+ bob.append("v", static_cast<int>(IndexVersion::kV1));
+ bob.appendElements(indexSpec);
+
+ indexSpec = bob.obj();
+ }
+
+ if (indexSpec["background"].trueValue()) {
Lock::TempRelease release(txn->lockState());
if (txn->lockState()->isLocked()) {
// If TempRelease fails, background index build will deadlock.
- LOG(3) << "apply op: building background index " << o
+ LOG(3) << "apply op: building background index " << indexSpec
<< " in the foreground because temp release failed";
- IndexBuilder builder(o);
+ IndexBuilder builder(indexSpec);
Status status = builder.buildInForeground(txn, db);
uassertStatusOK(status);
} else {
- IndexBuilder* builder = new IndexBuilder(o);
+ IndexBuilder* builder = new IndexBuilder(indexSpec);
// This spawns a new thread and returns immediately.
builder->go();
// Wait for thread to start and register itself
IndexBuilder::waitForBgIndexStarting();
}
} else {
- IndexBuilder builder(o);
+ IndexBuilder builder(indexSpec);
Status status = builder.buildInForeground(txn, db);
uassertStatusOK(status);
}
diff --git a/src/mongo/db/repl/rs_rollback_test.cpp b/src/mongo/db/repl/rs_rollback_test.cpp
index 7bbe51cde11..14f42cb7158 100644
--- a/src/mongo/db/repl/rs_rollback_test.cpp
+++ b/src/mongo/db/repl/rs_rollback_test.cpp
@@ -42,6 +42,7 @@
#include "mongo/db/concurrency/d_concurrency.h"
#include "mongo/db/db_raii.h"
#include "mongo/db/dbhelpers.h"
+#include "mongo/db/index/index_descriptor.h"
#include "mongo/db/jsobj.h"
#include "mongo/db/repl/oplog.h"
#include "mongo/db/repl/oplog_interface.h"
@@ -61,6 +62,8 @@ namespace {
using namespace mongo;
using namespace mongo::repl;
+const auto kIndexVersion = IndexDescriptor::IndexVersion::kV2;
+
const OplogInterfaceMock::Operations kEmptyMockOperations;
ReplSettings createReplSettings() {
@@ -423,11 +426,13 @@ TEST_F(RSRollbackTest, RollbackCreateIndexCommand) {
<< "key"
<< BSON("a" << 1)
<< "name"
- << "a_1");
+ << "a_1"
+ << "v"
+ << static_cast<int>(kIndexVersion));
{
Lock::DBLock dbLock(_txn->lockState(), "test", MODE_X);
MultiIndexBlock indexer(_txn.get(), collection);
- ASSERT_OK(indexer.init(indexSpec));
+ ASSERT_OK(indexer.init(indexSpec).getStatus());
WriteUnitOfWork wunit(_txn.get());
indexer.commit();
wunit.commit();
diff --git a/src/mongo/db/repl/storage_interface_impl_test.cpp b/src/mongo/db/repl/storage_interface_impl_test.cpp
index 3fd75e28429..218f925b97d 100644
--- a/src/mongo/db/repl/storage_interface_impl_test.cpp
+++ b/src/mongo/db/repl/storage_interface_impl_test.cpp
@@ -41,6 +41,7 @@
#include "mongo/db/curop.h"
#include "mongo/db/db_raii.h"
#include "mongo/db/dbhelpers.h"
+#include "mongo/db/index/index_descriptor.h"
#include "mongo/db/namespace_string.h"
#include "mongo/db/operation_context.h"
#include "mongo/db/repl/oplog.h"
@@ -60,13 +61,17 @@ namespace {
using namespace mongo;
using namespace mongo::repl;
+const auto kIndexVersion = IndexDescriptor::IndexVersion::kV2;
+
BSONObj makeIdIndexSpec(const NamespaceString& nss) {
return BSON("ns" << nss.toString() << "name"
<< "_id_"
<< "key"
<< BSON("_id" << 1)
<< "unique"
- << true);
+ << true
+ << "v"
+ << static_cast<int>(kIndexVersion));
}
/**
diff --git a/src/mongo/db/s/migration_destination_manager.cpp b/src/mongo/db/s/migration_destination_manager.cpp
index dac91d5ba69..2368ade2b07 100644
--- a/src/mongo/db/s/migration_destination_manager.cpp
+++ b/src/mongo/db/s/migration_destination_manager.cpp
@@ -44,11 +44,9 @@
#include "mongo/db/catalog/index_create.h"
#include "mongo/db/db_raii.h"
#include "mongo/db/dbhelpers.h"
-#include "mongo/db/index/index_descriptor.h"
#include "mongo/db/namespace_string.h"
#include "mongo/db/operation_context.h"
#include "mongo/db/ops/delete.h"
-#include "mongo/db/query/collation/collation_spec.h"
#include "mongo/db/range_deleter_service.h"
#include "mongo/db/repl/repl_client_info.h"
#include "mongo/db/repl/replication_coordinator_global.h"
@@ -73,8 +71,6 @@ namespace mongo {
using std::string;
using str::stream;
-using IndexVersion = IndexDescriptor::IndexVersion;
-
namespace {
Tee* migrateLog = RamLog::get("migrate");
@@ -563,33 +559,16 @@ void MigrationDestinationManager::_migrateDriver(OperationContext* txn,
return;
}
- // Attach simple collation if the index does not specify a collation. Otherwise, this
- // will be back-filled with the collection default collation.
- std::vector<BSONObj> indexSpecsWithCollation;
- for (const BSONObj& spec : indexSpecs) {
- if (spec["collation"]) {
- indexSpecsWithCollation.push_back(spec.getOwned());
- } else if (IndexVersion::kV2 <= static_cast<IndexVersion>(spec["v"].numberInt())) {
- indexSpecsWithCollation.emplace_back(
- BSONObjBuilder()
- .appendElements(spec)
- .append("collation", CollationSpec::kSimpleSpec)
- .obj());
- } else {
- indexSpecsWithCollation.push_back(spec.getOwned());
- }
- }
-
- Status status = indexer.init(indexSpecsWithCollation);
- if (!status.isOK()) {
+ auto indexInfoObjs = indexer.init(indexSpecs);
+ if (!indexInfoObjs.isOK()) {
errmsg = str::stream() << "failed to create index before migrating data. "
- << " error: " << redact(status);
+ << " error: " << redact(indexInfoObjs.getStatus());
warning() << errmsg;
setState(FAIL);
return;
}
- status = indexer.insertAllDocumentsInCollection();
+ auto status = indexer.insertAllDocumentsInCollection();
if (!status.isOK()) {
errmsg = str::stream() << "failed to create index before migrating data. "
<< " error: " << redact(status);
@@ -601,13 +580,10 @@ void MigrationDestinationManager::_migrateDriver(OperationContext* txn,
WriteUnitOfWork wunit(txn);
indexer.commit();
- for (size_t i = 0; i < indexSpecsWithCollation.size(); i++) {
+ for (auto&& infoObj : indexInfoObjs.getValue()) {
// make sure to create index on secondaries as well
getGlobalServiceContext()->getOpObserver()->onCreateIndex(
- txn,
- db->getSystemIndexesName(),
- indexSpecsWithCollation[i],
- true /* fromMigrate */);
+ txn, db->getSystemIndexesName(), infoObj, true /* fromMigrate */);
}
wunit.commit();
diff --git a/src/mongo/db/storage/mmap_v1/repair_database.cpp b/src/mongo/db/storage/mmap_v1/repair_database.cpp
index 6b4284ee57a..d82a89031d6 100644
--- a/src/mongo/db/storage/mmap_v1/repair_database.cpp
+++ b/src/mongo/db/storage/mmap_v1/repair_database.cpp
@@ -397,7 +397,7 @@ Status MMAPV1Engine::repairDatabase(OperationContext* txn,
indexes.push_back(desc->infoObj());
}
- Status status = indexer.init(indexes);
+ Status status = indexer.init(indexes).getStatus();
if (!status.isOK()) {
return status;
}
diff --git a/src/mongo/dbtests/counttests.cpp b/src/mongo/dbtests/counttests.cpp
index 2e28621b833..689b87fe599 100644
--- a/src/mongo/dbtests/counttests.cpp
+++ b/src/mongo/dbtests/counttests.cpp
@@ -36,6 +36,7 @@
#include "mongo/db/db_raii.h"
#include "mongo/db/dbdirectclient.h"
#include "mongo/db/dbhelpers.h"
+#include "mongo/db/index/index_descriptor.h"
#include "mongo/db/json.h"
#include "mongo/stdx/thread.h"
@@ -43,6 +44,10 @@
namespace CountTests {
+namespace {
+const auto kIndexVersion = IndexDescriptor::IndexVersion::kV2;
+} // namespace
+
class Base {
public:
Base()
@@ -83,6 +88,7 @@ protected:
Helpers::ensureIndex(&_txn,
_collection,
key,
+ kIndexVersion,
/*unique=*/false,
/*name=*/key.firstElementFieldName());
}
diff --git a/src/mongo/dbtests/dbtests.cpp b/src/mongo/dbtests/dbtests.cpp
index 96ad9253f08..8ddd7f4aa4c 100644
--- a/src/mongo/dbtests/dbtests.cpp
+++ b/src/mongo/dbtests/dbtests.cpp
@@ -39,6 +39,7 @@
#include "mongo/db/catalog/index_create.h"
#include "mongo/db/commands.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/repl/replication_coordinator_mock.h"
#include "mongo/db/service_context.h"
@@ -54,6 +55,9 @@
namespace mongo {
namespace dbtests {
+namespace {
+const auto kIndexVersion = IndexDescriptor::IndexVersion::kV2;
+} // namespace
void initWireSpec() {
WireSpec& spec = WireSpec::instance();
@@ -67,9 +71,12 @@ void initWireSpec() {
Status createIndex(OperationContext* txn, StringData ns, const BSONObj& keys, bool unique) {
BSONObjBuilder specBuilder;
- specBuilder << "name" << DBClientBase::genIndexName(keys) << "ns" << ns << "key" << keys;
+ specBuilder.append("name", DBClientBase::genIndexName(keys));
+ specBuilder.append("ns", ns);
+ specBuilder.append("key", keys);
+ specBuilder.append("v", static_cast<int>(kIndexVersion));
if (unique) {
- specBuilder << "unique" << true;
+ specBuilder.appendBool("unique", true);
}
return createIndexFromSpec(txn, ns, specBuilder.done());
}
@@ -84,7 +91,7 @@ Status createIndexFromSpec(OperationContext* txn, StringData ns, const BSONObj&
wunit.commit();
}
MultiIndexBlock indexer(txn, coll);
- Status status = indexer.init(spec);
+ Status status = indexer.init(spec).getStatus();
if (status == ErrorCodes::IndexAlreadyExists) {
return Status::OK();
}
diff --git a/src/mongo/dbtests/indexcatalogtests.cpp b/src/mongo/dbtests/indexcatalogtests.cpp
index ef2f1effb81..068ede905a1 100644
--- a/src/mongo/dbtests/indexcatalogtests.cpp
+++ b/src/mongo/dbtests/indexcatalogtests.cpp
@@ -29,6 +29,9 @@
#include "mongo/dbtests/dbtests.h"
namespace IndexCatalogTests {
+namespace {
+const auto kIndexVersion = IndexDescriptor::IndexVersion::kV2;
+} // namespace
static const char* const _ns = "unittests.indexcatalog";
@@ -135,12 +138,13 @@ public:
OldClientWriteContext ctx(&txn, _ns);
const std::string indexName = "x_1";
- ASSERT_OK(dbtests::createIndexFromSpec(&txn,
- _ns,
- BSON("name" << indexName << "ns" << _ns << "key"
- << BSON("x" << 1)
- << "expireAfterSeconds"
- << 5)));
+ ASSERT_OK(dbtests::createIndexFromSpec(
+ &txn,
+ _ns,
+ BSON("name" << indexName << "ns" << _ns << "key" << BSON("x" << 1) << "v"
+ << static_cast<int>(kIndexVersion)
+ << "expireAfterSeconds"
+ << 5)));
const IndexDescriptor* desc = _catalog->findIndexByName(&txn, indexName);
ASSERT(desc);
diff --git a/src/mongo/dbtests/indexupdatetests.cpp b/src/mongo/dbtests/indexupdatetests.cpp
index 6b9ee3e23ae..0a379851b09 100644
--- a/src/mongo/dbtests/indexupdatetests.cpp
+++ b/src/mongo/dbtests/indexupdatetests.cpp
@@ -48,6 +48,10 @@ namespace IndexUpdateTests {
using std::unique_ptr;
+namespace {
+const auto kIndexVersion = IndexDescriptor::IndexVersion::kV2;
+} // namespace
+
static const char* const _ns = "unittests.indexupdate";
/**
@@ -376,12 +380,14 @@ public:
<< coll->ns().ns()
<< "key"
<< BSON("a" << 1)
+ << "v"
+ << static_cast<int>(kIndexVersion)
<< "unique"
<< true
<< "background"
<< background);
- ASSERT_OK(indexer.init(spec));
+ ASSERT_OK(indexer.init(spec).getStatus());
ASSERT_OK(indexer.insertAllDocumentsInCollection());
WriteUnitOfWork wunit(&_txn);
@@ -428,12 +434,14 @@ public:
<< coll->ns().ns()
<< "key"
<< BSON("a" << 1)
+ << "v"
+ << static_cast<int>(kIndexVersion)
<< "unique"
<< true
<< "background"
<< background);
- ASSERT_OK(indexer.init(spec));
+ ASSERT_OK(indexer.init(spec).getStatus());
const Status status = indexer.insertAllDocumentsInCollection();
ASSERT_EQUALS(status.code(), ErrorCodes::DuplicateKey);
}
@@ -479,12 +487,14 @@ public:
<< coll->ns().ns()
<< "key"
<< BSON("a" << 1)
+ << "v"
+ << static_cast<int>(kIndexVersion)
<< "unique"
<< true
<< "background"
<< background);
- ASSERT_OK(indexer.init(spec));
+ ASSERT_OK(indexer.init(spec).getStatus());
std::set<RecordId> dups;
ASSERT_OK(indexer.insertAllDocumentsInCollection(&dups));
@@ -524,7 +534,9 @@ public:
// Request an interrupt.
getGlobalServiceContext()->setKillAllOperations();
BSONObj indexInfo = BSON("key" << BSON("a" << 1) << "ns" << _ns << "name"
- << "a_1");
+ << "a_1"
+ << "v"
+ << static_cast<int>(kIndexVersion));
// The call is interrupted because mayInterrupt == true.
ASSERT_TRUE(buildIndexInterrupted(indexInfo, true));
// only want to interrupt the index build
@@ -557,7 +569,9 @@ public:
// Request an interrupt.
getGlobalServiceContext()->setKillAllOperations();
BSONObj indexInfo = BSON("key" << BSON("a" << 1) << "ns" << _ns << "name"
- << "a_1");
+ << "a_1"
+ << "v"
+ << static_cast<int>(kIndexVersion));
// The call is not interrupted because mayInterrupt == false.
ASSERT_FALSE(buildIndexInterrupted(indexInfo, false));
// only want to interrupt the index build
@@ -593,7 +607,9 @@ public:
// Request an interrupt.
getGlobalServiceContext()->setKillAllOperations();
BSONObj indexInfo = BSON("key" << BSON("_id" << 1) << "ns" << _ns << "name"
- << "_id_");
+ << "_id_"
+ << "v"
+ << static_cast<int>(kIndexVersion));
// The call is interrupted because mayInterrupt == true.
ASSERT_TRUE(buildIndexInterrupted(indexInfo, true));
// only want to interrupt the index build
@@ -629,7 +645,9 @@ public:
// Request an interrupt.
getGlobalServiceContext()->setKillAllOperations();
BSONObj indexInfo = BSON("key" << BSON("_id" << 1) << "ns" << _ns << "name"
- << "_id_");
+ << "_id_"
+ << "v"
+ << static_cast<int>(kIndexVersion));
// The call is not interrupted because mayInterrupt == false.
ASSERT_FALSE(buildIndexInterrupted(indexInfo, false));
// only want to interrupt the index build
@@ -653,7 +671,7 @@ public:
// Request an interrupt.
getGlobalServiceContext()->setKillAllOperations();
// The call is not interrupted.
- Helpers::ensureIndex(&_txn, collection(), BSON("a" << 1), false, "a_1");
+ Helpers::ensureIndex(&_txn, collection(), BSON("a" << 1), kIndexVersion, false, "a_1");
// only want to interrupt the index build
getGlobalServiceContext()->unsetKillAllOperations();
// The new index is listed in getIndexSpecs because the index build completed.
@@ -721,7 +739,7 @@ public:
Status IndexBuildBase::createIndex(const std::string& dbname, const BSONObj& indexSpec) {
MultiIndexBlock indexer(&_txn, collection());
- Status status = indexer.init(indexSpec);
+ Status status = indexer.init(indexSpec).getStatus();
if (status == ErrorCodes::IndexAlreadyExists) {
return Status::OK();
}
@@ -750,7 +768,9 @@ public:
<< "ns"
<< _ns
<< "key"
- << BSON("x" << 1 << "y" << 1))));
+ << BSON("x" << 1 << "y" << 1)
+ << "v"
+ << static_cast<int>(kIndexVersion))));
}
};
@@ -767,7 +787,9 @@ public:
<< "unique"
<< true
<< "key"
- << BSON("x" << 1 << "y" << 1))));
+ << BSON("x" << 1 << "y" << 1)
+ << "v"
+ << static_cast<int>(kIndexVersion))));
}
};
@@ -780,7 +802,9 @@ public:
<< "ns"
<< _ns
<< "key"
- << BSON("x" << 1 << "y" << 1))));
+ << BSON("x" << 1 << "y" << 1)
+ << "v"
+ << static_cast<int>(kIndexVersion))));
}
};
@@ -795,7 +819,9 @@ public:
<< "ns"
<< _ns
<< "key"
- << BSON("y" << 1 << "x" << 1))));
+ << BSON("y" << 1 << "x" << 1)
+ << "v"
+ << static_cast<int>(kIndexVersion))));
}
};
@@ -818,7 +844,9 @@ public:
<< 3600
<< "key"
<< BSON("superIdx"
- << "2d"))));
+ << "2d")
+ << "v"
+ << static_cast<int>(kIndexVersion))));
}
};
@@ -840,7 +868,9 @@ public:
<< 1
<< "key"
<< BSON("superIdx"
- << "2d"))));
+ << "2d")
+ << "v"
+ << static_cast<int>(kIndexVersion))));
}
};
@@ -864,7 +894,9 @@ public:
<< 3600
<< "key"
<< BSON("superIdx"
- << "2d"))));
+ << "2d")
+ << "v"
+ << static_cast<int>(kIndexVersion))));
}
};
@@ -887,7 +919,9 @@ public:
<< 3600
<< "key"
<< BSON("superIdx"
- << "2d"))));
+ << "2d")
+ << "v"
+ << static_cast<int>(kIndexVersion))));
}
};
@@ -908,7 +942,9 @@ public:
<< 2400
<< "key"
<< BSON("superIdx"
- << "2d"))));
+ << "2d")
+ << "v"
+ << static_cast<int>(kIndexVersion))));
}
};
@@ -959,6 +995,8 @@ protected:
<< _ns
<< "key"
<< BSON("a" << 1)
+ << "v"
+ << static_cast<int>(kIndexVersion)
<< "storageEngine"
<< storageEngineValue);
}
diff --git a/src/mongo/dbtests/multikey_paths_test.cpp b/src/mongo/dbtests/multikey_paths_test.cpp
index ccbaa741983..b9ad16ce17b 100644
--- a/src/mongo/dbtests/multikey_paths_test.cpp
+++ b/src/mongo/dbtests/multikey_paths_test.cpp
@@ -34,6 +34,7 @@
#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/multikey_paths.h"
#include "mongo/db/namespace_string.h"
#include "mongo/db/service_context.h"
@@ -44,6 +45,8 @@
namespace mongo {
namespace {
+const auto kIndexVersion = IndexDescriptor::IndexVersion::kV2;
+
/**
* Fixture for testing correctness of multikey paths.
*
@@ -157,7 +160,9 @@ TEST_F(MultikeyPathsTest, PathsUpdatedOnIndexCreation) {
<< "ns"
<< _nss.ns()
<< "key"
- << keyPattern));
+ << keyPattern
+ << "v"
+ << static_cast<int>(kIndexVersion)));
assertMultikeyPaths(collection, keyPattern, {std::set<size_t>{}, {0U}});
}
@@ -191,7 +196,9 @@ TEST_F(MultikeyPathsTest, PathsUpdatedOnIndexCreationWithMultipleDocuments) {
<< "ns"
<< _nss.ns()
<< "key"
- << keyPattern));
+ << keyPattern
+ << "v"
+ << static_cast<int>(kIndexVersion)));
assertMultikeyPaths(collection, keyPattern, {{0U}, {0U}});
}
@@ -208,7 +215,9 @@ TEST_F(MultikeyPathsTest, PathsUpdatedOnDocumentInsert) {
<< "ns"
<< _nss.ns()
<< "key"
- << keyPattern));
+ << keyPattern
+ << "v"
+ << static_cast<int>(kIndexVersion)));
{
WriteUnitOfWork wuow(_opCtx.get());
@@ -251,7 +260,9 @@ TEST_F(MultikeyPathsTest, PathsUpdatedOnDocumentUpdate) {
<< "ns"
<< _nss.ns()
<< "key"
- << keyPattern));
+ << keyPattern
+ << "v"
+ << static_cast<int>(kIndexVersion)));
{
WriteUnitOfWork wuow(_opCtx.get());
@@ -304,7 +315,9 @@ TEST_F(MultikeyPathsTest, PathsNotUpdatedOnDocumentDelete) {
<< "ns"
<< _nss.ns()
<< "key"
- << keyPattern));
+ << keyPattern
+ << "v"
+ << static_cast<int>(kIndexVersion)));
{
WriteUnitOfWork wuow(_opCtx.get());
@@ -348,7 +361,9 @@ TEST_F(MultikeyPathsTest, PathsUpdatedForMultipleIndexesOnDocumentInsert) {
<< "ns"
<< _nss.ns()
<< "key"
- << keyPatternAB));
+ << keyPatternAB
+ << "v"
+ << static_cast<int>(kIndexVersion)));
BSONObj keyPatternAC = BSON("a" << 1 << "c" << 1);
createIndex(collection,
@@ -357,7 +372,9 @@ TEST_F(MultikeyPathsTest, PathsUpdatedForMultipleIndexesOnDocumentInsert) {
<< "ns"
<< _nss.ns()
<< "key"
- << keyPatternAC));
+ << keyPatternAC
+ << "v"
+ << static_cast<int>(kIndexVersion)));
{
WriteUnitOfWork wuow(_opCtx.get());
OpDebug* const nullOpDebug = nullptr;
diff --git a/src/mongo/dbtests/query_stage_ixscan.cpp b/src/mongo/dbtests/query_stage_ixscan.cpp
index 3d22cf19fd9..7ad4e1a0b84 100644
--- a/src/mongo/dbtests/query_stage_ixscan.cpp
+++ b/src/mongo/dbtests/query_stage_ixscan.cpp
@@ -32,11 +32,15 @@
#include "mongo/db/db_raii.h"
#include "mongo/db/exec/index_scan.h"
#include "mongo/db/exec/working_set.h"
+#include "mongo/db/index/index_descriptor.h"
#include "mongo/db/jsobj.h"
#include "mongo/db/json.h"
#include "mongo/dbtests/dbtests.h"
namespace QueryStageIxscan {
+namespace {
+const auto kIndexVersion = IndexDescriptor::IndexVersion::kV2;
+} // namespace
class IndexScanTest {
public:
@@ -57,7 +61,9 @@ public:
ASSERT_OK(_coll->getIndexCatalog()->createIndexOnEmptyCollection(
&_txn,
BSON("ns" << ns() << "key" << BSON("x" << 1) << "name"
- << DBClientBase::genIndexName(BSON("x" << 1)))));
+ << DBClientBase::genIndexName(BSON("x" << 1))
+ << "v"
+ << static_cast<int>(kIndexVersion))));
wunit.commit();
}
diff --git a/src/mongo/dbtests/querytests.cpp b/src/mongo/dbtests/querytests.cpp
index 888d9bd4454..de55f54f45d 100644
--- a/src/mongo/dbtests/querytests.cpp
+++ b/src/mongo/dbtests/querytests.cpp
@@ -40,6 +40,7 @@
#include "mongo/db/dbdirectclient.h"
#include "mongo/db/dbhelpers.h"
#include "mongo/db/global_timestamp.h"
+#include "mongo/db/index/index_descriptor.h"
#include "mongo/db/json.h"
#include "mongo/db/lasterror.h"
#include "mongo/db/query/find.h"
@@ -56,6 +57,10 @@ using std::endl;
using std::string;
using std::vector;
+namespace {
+const auto kIndexVersion = IndexDescriptor::IndexVersion::kV2;
+} // namespace
+
class Base {
public:
Base() : _scopedXact(&_txn, MODE_X), _lk(_txn.lockState()), _context(&_txn, ns()) {
@@ -89,7 +94,8 @@ protected:
}
void addIndex(const BSONObj& key) {
- Helpers::ensureIndex(&_txn, _collection, key, false, key.firstElementFieldName());
+ Helpers::ensureIndex(
+ &_txn, _collection, key, kIndexVersion, false, key.firstElementFieldName());
}
void insert(const char* s) {
diff --git a/src/mongo/dbtests/rollbacktests.cpp b/src/mongo/dbtests/rollbacktests.cpp
index 99d90fec617..0986db98aa6 100644
--- a/src/mongo/dbtests/rollbacktests.cpp
+++ b/src/mongo/dbtests/rollbacktests.cpp
@@ -36,6 +36,7 @@
#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/record_id.h"
#include "mongo/dbtests/dbtests.h"
#include "mongo/unittest/unittest.h"
@@ -47,6 +48,8 @@ using std::string;
namespace RollbackTests {
namespace {
+const auto kIndexVersion = IndexDescriptor::IndexVersion::kV2;
+
void dropDatabase(OperationContext* txn, const NamespaceString& nss) {
ScopedTransaction transaction(txn, MODE_X);
Lock::GlobalWrite globalWriteLock(txn->lockState());
@@ -477,7 +480,8 @@ public:
IndexCatalog* catalog = coll->getIndexCatalog();
string idxName = "a";
- BSONObj spec = BSON("ns" << ns << "key" << BSON("a" << 1) << "name" << idxName);
+ BSONObj spec = BSON("ns" << ns << "key" << BSON("a" << 1) << "name" << idxName << "v"
+ << static_cast<int>(kIndexVersion));
// END SETUP / START TEST
@@ -518,7 +522,8 @@ public:
IndexCatalog* catalog = coll->getIndexCatalog();
string idxName = "a";
- BSONObj spec = BSON("ns" << ns << "key" << BSON("a" << 1) << "name" << idxName);
+ BSONObj spec = BSON("ns" << ns << "key" << BSON("a" << 1) << "name" << idxName << "v"
+ << static_cast<int>(kIndexVersion));
{
WriteUnitOfWork uow(&txn);
@@ -571,7 +576,8 @@ public:
IndexCatalog* catalog = coll->getIndexCatalog();
string idxName = "a";
- BSONObj spec = BSON("ns" << ns << "key" << BSON("a" << 1) << "name" << idxName);
+ BSONObj spec = BSON("ns" << ns << "key" << BSON("a" << 1) << "name" << idxName << "v"
+ << static_cast<int>(kIndexVersion));
// END SETUP / START TEST
@@ -615,7 +621,8 @@ public:
IndexCatalog* catalog = coll->getIndexCatalog();
string idxName = "a";
- BSONObj spec = BSON("ns" << ns << "key" << BSON("a" << 1) << "name" << idxName);
+ BSONObj spec = BSON("ns" << ns << "key" << BSON("a" << 1) << "name" << idxName << "v"
+ << static_cast<int>(kIndexVersion));
{
WriteUnitOfWork uow(&txn);
@@ -677,9 +684,12 @@ public:
string idxNameA = "indexA";
string idxNameB = "indexB";
string idxNameC = "indexC";
- BSONObj specA = BSON("ns" << ns << "key" << BSON("a" << 1) << "name" << idxNameA);
- BSONObj specB = BSON("ns" << ns << "key" << BSON("b" << 1) << "name" << idxNameB);
- BSONObj specC = BSON("ns" << ns << "key" << BSON("c" << 1) << "name" << idxNameC);
+ BSONObj specA = BSON("ns" << ns << "key" << BSON("a" << 1) << "name" << idxNameA << "v"
+ << static_cast<int>(kIndexVersion));
+ BSONObj specB = BSON("ns" << ns << "key" << BSON("b" << 1) << "name" << idxNameB << "v"
+ << static_cast<int>(kIndexVersion));
+ BSONObj specC = BSON("ns" << ns << "key" << BSON("c" << 1) << "name" << idxNameC << "v"
+ << static_cast<int>(kIndexVersion));
// END SETUP / START TEST
diff --git a/src/mongo/dbtests/validate_tests.cpp b/src/mongo/dbtests/validate_tests.cpp
index 14da85c8844..3eb1706fbce 100644
--- a/src/mongo/dbtests/validate_tests.cpp
+++ b/src/mongo/dbtests/validate_tests.cpp
@@ -46,6 +46,10 @@ namespace ValidateTests {
using std::unique_ptr;
+namespace {
+const auto kIndexVersion = IndexDescriptor::IndexVersion::kV2;
+} // namespace
+
static const char* const _ns = "unittests.validate_tests";
/**
@@ -175,6 +179,8 @@ public:
<< coll->ns().ns()
<< "key"
<< BSON("a" << 1)
+ << "v"
+ << static_cast<int>(kIndexVersion)
<< "background"
<< false));
@@ -236,6 +242,8 @@ public:
<< coll->ns().ns()
<< "key"
<< BSON("a" << 1)
+ << "v"
+ << static_cast<int>(kIndexVersion)
<< "background"
<< false));
@@ -369,6 +377,8 @@ public:
<< coll->ns().ns()
<< "key"
<< BSON("a.b" << 1)
+ << "v"
+ << static_cast<int>(kIndexVersion)
<< "background"
<< false));
@@ -433,6 +443,8 @@ public:
<< coll->ns().ns()
<< "key"
<< BSON("a" << 1)
+ << "v"
+ << static_cast<int>(kIndexVersion)
<< "background"
<< false
<< "sparse"
@@ -491,6 +503,8 @@ public:
<< coll->ns().ns()
<< "key"
<< BSON("a" << 1)
+ << "v"
+ << static_cast<int>(kIndexVersion)
<< "background"
<< false
<< "partialFilterExpression"
@@ -545,6 +559,8 @@ public:
<< "key"
<< BSON("x"
<< "2dsphere")
+ << "v"
+ << static_cast<int>(kIndexVersion)
<< "background"
<< false
<< "partialFilterExpression"
@@ -561,6 +577,8 @@ public:
<< "key"
<< BSON("x"
<< "2dsphere")
+ << "v"
+ << static_cast<int>(kIndexVersion)
<< "background"
<< false
<< "partialFilterExpression"
@@ -606,6 +624,8 @@ public:
<< coll->ns().ns()
<< "key"
<< BSON("a" << 1 << "b" << -1)
+ << "v"
+ << static_cast<int>(kIndexVersion)
<< "background"
<< false));
ASSERT_OK(status);
@@ -618,6 +638,8 @@ public:
<< coll->ns().ns()
<< "key"
<< BSON("a" << -1 << "b" << 1)
+ << "v"
+ << static_cast<int>(kIndexVersion)
<< "background"
<< false));
@@ -666,7 +688,8 @@ public:
auto status = dbtests::createIndexFromSpec(
&_txn,
coll->ns().ns(),
- BSON("name" << indexName << "ns" << coll->ns().ns() << "key" << BSON("a" << 1)
+ BSON("name" << indexName << "ns" << coll->ns().ns() << "key" << BSON("a" << 1) << "v"
+ << static_cast<int>(kIndexVersion)
<< "background"
<< false));
@@ -727,7 +750,8 @@ public:
auto status = dbtests::createIndexFromSpec(
&_txn,
coll->ns().ns(),
- BSON("name" << indexName << "ns" << coll->ns().ns() << "key" << BSON("a" << 1)
+ BSON("name" << indexName << "ns" << coll->ns().ns() << "key" << BSON("a" << 1) << "v"
+ << static_cast<int>(kIndexVersion)
<< "background"
<< false));