diff options
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)); |