summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMax Hirschhorn <max.hirschhorn@mongodb.com>2016-09-14 20:49:17 -0400
committerMax Hirschhorn <max.hirschhorn@mongodb.com>2016-09-14 20:49:17 -0400
commit8302d0735b34a16cac000e5e345722487536e5bc (patch)
treec9ef60fe493eda60feacddfd67c0ec298939c4ef
parent46acb1b94944bc7aa68ff6a8b3cd2d340b272c6f (diff)
downloadmongo-8302d0735b34a16cac000e5e345722487536e5bc.tar.gz
SERVER-24033 Write full index spec in oplog entry for index creation.
This ensures that the index version (aka the "v" field) is always present in the oplog entry when creating indexes on a 3.4 primary. We can therefore assume that if the "v" field isn't present in the corresponding oplog entry, then a v=1 index should be built. Changes MultiBlockIndex::init() to return the index specifications that were actually created. The "repairDatabase", "compact", "copydb", and "cloneCollection" commands no longer automatically upgrade the index version to the current default version. Instead, the only command that does so is the "reIndex" command.
-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));