diff options
Diffstat (limited to 'src/mongo/db')
23 files changed, 419 insertions, 218 deletions
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; } |