summaryrefslogtreecommitdiff
path: root/src/mongo/db
diff options
context:
space:
mode:
authorYuhong Zhang <yuhong.zhang@mongodb.com>2022-03-04 00:06:12 +0000
committerEvergreen Agent <no-reply@evergreen.mongodb.com>2022-03-04 00:51:37 +0000
commite2675683edd92cd4105df52eff3f0eb64055181c (patch)
treeb513551b43e5fc0aa62e0b69d85f2f8eef27f743 /src/mongo/db
parenteb5eaa886390cde3857c445360c84c9ce0f52d28 (diff)
downloadmongo-e2675683edd92cd4105df52eff3f0eb64055181c.tar.gz
SERVER-63578 Convert a unique index to a non-unique index via the collMod command
Diffstat (limited to 'src/mongo/db')
-rw-r--r--src/mongo/db/catalog/coll_mod.cpp27
-rw-r--r--src/mongo/db/catalog/coll_mod_index.cpp36
-rw-r--r--src/mongo/db/catalog/coll_mod_index.h1
-rw-r--r--src/mongo/db/catalog/collection.h5
-rw-r--r--src/mongo/db/catalog/collection_impl.cpp4
-rw-r--r--src/mongo/db/catalog/collection_impl.h2
-rw-r--r--src/mongo/db/catalog/collection_mock.h2
-rw-r--r--src/mongo/db/coll_mod.idl8
-rw-r--r--src/mongo/db/op_observer.h1
-rw-r--r--src/mongo/db/op_observer_util.cpp3
-rw-r--r--src/mongo/db/storage/bson_collection_catalog_entry.cpp16
-rw-r--r--src/mongo/db/storage/bson_collection_catalog_entry.h2
12 files changed, 89 insertions, 18 deletions
diff --git a/src/mongo/db/catalog/coll_mod.cpp b/src/mongo/db/catalog/coll_mod.cpp
index 0ffc15e0e60..747de3e044a 100644
--- a/src/mongo/db/catalog/coll_mod.cpp
+++ b/src/mongo/db/catalog/coll_mod.cpp
@@ -185,7 +185,8 @@ StatusWith<ParsedCollModRequest> parseCollModRequest(OperationContext* opCtx,
}
if (!cmdIndex.getExpireAfterSeconds() && !cmdIndex.getHidden() &&
- !cmdIndex.getUnique() && !cmdIndex.getPrepareUnique()) {
+ !cmdIndex.getUnique() && !cmdIndex.getPrepareUnique() &&
+ !cmdIndex.getForceNonUnique()) {
return Status(ErrorCodes::InvalidOptions,
"no expireAfterSeconds, hidden, unique, or prepareUnique field");
}
@@ -201,6 +202,13 @@ StatusWith<ParsedCollModRequest> parseCollModRequest(OperationContext* opCtx,
serverGlobalParams.featureCompatibility));
}
+ if (cmdIndex.getUnique() && cmdIndex.getForceNonUnique()) {
+ return Status(
+ ErrorCodes::InvalidOptions,
+ "collMod does not support 'unique' and 'forceNonUnique' options at the "
+ "same time");
+ }
+
if (cmdIndex.getExpireAfterSeconds()) {
if (isTimeseries) {
return Status(ErrorCodes::InvalidOptions,
@@ -305,7 +313,7 @@ StatusWith<ParsedCollModRequest> parseCollModRequest(OperationContext* opCtx,
cmr.numModifications++;
if (bool unique = *cmdIndex.getUnique(); !unique) {
- return Status(ErrorCodes::BadValue, "Cannot make index non-unique");
+ return Status(ErrorCodes::BadValue, "'Unique: false' option is not supported");
}
// Attempting to converting a unique index should be treated as a no-op.
@@ -350,6 +358,21 @@ StatusWith<ParsedCollModRequest> parseCollModRequest(OperationContext* opCtx,
}
}
+ if (cmdIndex.getForceNonUnique()) {
+ cmr.numModifications++;
+ if (bool unique = *cmdIndex.getForceNonUnique(); !unique) {
+ return Status(ErrorCodes::BadValue, "'forceNonUnique: false' is not supported");
+ }
+
+ // Attempting to convert a non-unique index should be treated as a no-op.
+ if (!cmrIndex->idx->unique()) {
+ indexObjForOplog =
+ indexObjForOplog.removeField(CollModIndex::kForceNonUniqueFieldName);
+ } else {
+ cmrIndex->indexForceNonUnique = true;
+ }
+ }
+
// The index options doc must contain either the name or key pattern, but not both.
// If we have just one field, the index modifications requested matches the current
// state in catalog and there is nothing further to do.
diff --git a/src/mongo/db/catalog/coll_mod_index.cpp b/src/mongo/db/catalog/coll_mod_index.cpp
index 7be401d110b..4a6a26237ea 100644
--- a/src/mongo/db/catalog/coll_mod_index.cpp
+++ b/src/mongo/db/catalog/coll_mod_index.cpp
@@ -128,8 +128,7 @@ void getKeysForIndex(OperationContext* opCtx,
}
/**
- * Adjusts unique setting on an index.
- * An index can be converted to unique but removing the uniqueness property is not allowed.
+ * Adjusts unique setting on an index to true.
*/
void _processCollModIndexRequestUnique(OperationContext* opCtx,
AutoGetCollection* autoColl,
@@ -150,7 +149,7 @@ void _processCollModIndexRequestUnique(OperationContext* opCtx,
}
*newUnique = true;
- autoColl->getWritableCollection(opCtx)->updateUniqueSetting(opCtx, idx->indexName());
+ autoColl->getWritableCollection(opCtx)->updateUniqueSetting(opCtx, idx->indexName(), true);
// Resets 'prepareUnique' to false after converting to unique index;
autoColl->getWritableCollection(opCtx)->updatePrepareUniqueSetting(
opCtx, idx->indexName(), false);
@@ -173,6 +172,19 @@ void _processCollModIndexRequestPrepareUnique(OperationContext* opCtx,
}
}
+/**
+ * Adjusts unique setting on an index to false.
+ */
+void _processCollModIndexRequestForceNonUnique(OperationContext* opCtx,
+ AutoGetCollection* autoColl,
+ const IndexDescriptor* idx,
+ boost::optional<bool>* newForceNonUnique) {
+ invariant(idx->unique(), str::stream() << "Index is already non-unique: " << idx->infoObj());
+
+ *newForceNonUnique = true;
+ autoColl->getWritableCollection(opCtx)->updateUniqueSetting(opCtx, idx->indexName(), false);
+}
+
} // namespace
void processCollModIndexRequest(OperationContext* opCtx,
@@ -186,9 +198,11 @@ void processCollModIndexRequest(OperationContext* opCtx,
auto indexHidden = collModIndexRequest.indexHidden;
auto indexUnique = collModIndexRequest.indexUnique;
auto indexPrepareUnique = collModIndexRequest.indexPrepareUnique;
+ auto indexForceNonUnique = collModIndexRequest.indexForceNonUnique;
// Return early if there are no index modifications requested.
- if (!indexExpireAfterSeconds && !indexHidden && !indexUnique && !indexPrepareUnique) {
+ if (!indexExpireAfterSeconds && !indexHidden && !indexUnique && !indexPrepareUnique &&
+ !indexForceNonUnique) {
return;
}
@@ -199,6 +213,7 @@ void processCollModIndexRequest(OperationContext* opCtx,
boost::optional<bool> newUnique;
boost::optional<bool> newPrepareUnique;
boost::optional<bool> oldPrepareUnique;
+ boost::optional<bool> newForceNonUnique;
// TTL Index
if (indexExpireAfterSeconds) {
@@ -224,6 +239,11 @@ void processCollModIndexRequest(OperationContext* opCtx,
opCtx, autoColl, idx, *indexPrepareUnique, &newPrepareUnique, &oldPrepareUnique);
}
+ // User wants to convert an index back to be non-unique.
+ if (indexForceNonUnique) {
+ _processCollModIndexRequestForceNonUnique(opCtx, autoColl, idx, &newForceNonUnique);
+ }
+
*indexCollModInfo =
IndexCollModInfo{!newExpireSecs ? boost::optional<Seconds>() : Seconds(*newExpireSecs),
!oldExpireSecs ? boost::optional<Seconds>() : Seconds(*oldExpireSecs),
@@ -232,13 +252,14 @@ void processCollModIndexRequest(OperationContext* opCtx,
newUnique,
newPrepareUnique,
oldPrepareUnique,
+ newForceNonUnique,
idx->indexName()};
// This matches the default for IndexCatalog::refreshEntry().
auto flags = CreateIndexEntryFlags::kIsReady;
// Update data format version in storage engine metadata for index.
- if (indexUnique) {
+ if (indexUnique || indexForceNonUnique) {
flags = CreateIndexEntryFlags::kIsReady | CreateIndexEntryFlags::kUpdateMetadata;
}
@@ -254,6 +275,7 @@ void processCollModIndexRequest(OperationContext* opCtx,
newUnique,
oldPrepareUnique,
newPrepareUnique,
+ newForceNonUnique,
result](boost::optional<Timestamp>) {
// add the fields to BSONObjBuilder result
if (oldExpireSecs) {
@@ -276,6 +298,10 @@ void processCollModIndexRequest(OperationContext* opCtx,
result->append("prepareUnique_old", *oldPrepareUnique);
result->append("prepareUnique_new", *newPrepareUnique);
}
+ if (newForceNonUnique) {
+ invariant(*newForceNonUnique);
+ result->appendBool("forceNonUnique_new", true);
+ }
});
if (MONGO_unlikely(assertAfterIndexUpdate.shouldFail())) {
diff --git a/src/mongo/db/catalog/coll_mod_index.h b/src/mongo/db/catalog/coll_mod_index.h
index acd7849d70e..ed6f24f4dd5 100644
--- a/src/mongo/db/catalog/coll_mod_index.h
+++ b/src/mongo/db/catalog/coll_mod_index.h
@@ -49,6 +49,7 @@ struct ParsedCollModIndexRequest {
boost::optional<bool> indexHidden;
boost::optional<bool> indexUnique;
boost::optional<bool> indexPrepareUnique;
+ boost::optional<bool> indexForceNonUnique;
};
/**
diff --git a/src/mongo/db/catalog/collection.h b/src/mongo/db/catalog/collection.h
index aa5bc579c4e..d2e76c12993 100644
--- a/src/mongo/db/catalog/collection.h
+++ b/src/mongo/db/catalog/collection.h
@@ -604,10 +604,9 @@ public:
virtual void updateHiddenSetting(OperationContext* opCtx, StringData idxName, bool hidden) = 0;
/*
- * Converts the the given index to be unique.
- * This is a one-way transformation - the uniqueness constraint cannot be removed.
+ * Converts the the given index to be unique or non-unique.
*/
- virtual void updateUniqueSetting(OperationContext* opCtx, StringData idxName) = 0;
+ virtual void updateUniqueSetting(OperationContext* opCtx, StringData idxName, bool unique) = 0;
/*
* Disallows or allows new duplicates in the given index.
diff --git a/src/mongo/db/catalog/collection_impl.cpp b/src/mongo/db/catalog/collection_impl.cpp
index fa9a7d0dff4..92aec2083c5 100644
--- a/src/mongo/db/catalog/collection_impl.cpp
+++ b/src/mongo/db/catalog/collection_impl.cpp
@@ -1979,12 +1979,12 @@ void CollectionImpl::updateHiddenSetting(OperationContext* opCtx, StringData idx
});
}
-void CollectionImpl::updateUniqueSetting(OperationContext* opCtx, StringData idxName) {
+void CollectionImpl::updateUniqueSetting(OperationContext* opCtx, StringData idxName, bool unique) {
int offset = _metadata->findIndexOffset(idxName);
invariant(offset >= 0);
_writeMetadata(opCtx, [&](BSONCollectionCatalogEntry::MetaData& md) {
- md.indexes[offset].updateUniqueSetting();
+ md.indexes[offset].updateUniqueSetting(unique);
});
}
diff --git a/src/mongo/db/catalog/collection_impl.h b/src/mongo/db/catalog/collection_impl.h
index 5c21e188dc1..f89cdf96ede 100644
--- a/src/mongo/db/catalog/collection_impl.h
+++ b/src/mongo/db/catalog/collection_impl.h
@@ -443,7 +443,7 @@ public:
void updateHiddenSetting(OperationContext* opCtx, StringData idxName, bool hidden) final;
- void updateUniqueSetting(OperationContext* opCtx, StringData idxName) final;
+ void updateUniqueSetting(OperationContext* opCtx, StringData idxName, bool unique) final;
void updatePrepareUniqueSetting(OperationContext* opCtx,
StringData idxName,
diff --git a/src/mongo/db/catalog/collection_mock.h b/src/mongo/db/catalog/collection_mock.h
index 65529dfd820..6f9f4736af4 100644
--- a/src/mongo/db/catalog/collection_mock.h
+++ b/src/mongo/db/catalog/collection_mock.h
@@ -411,7 +411,7 @@ public:
std::abort();
}
- void updateUniqueSetting(OperationContext* opCtx, StringData idxName) {
+ void updateUniqueSetting(OperationContext* opCtx, StringData idxName, bool unique) {
std::abort();
}
diff --git a/src/mongo/db/coll_mod.idl b/src/mongo/db/coll_mod.idl
index a79677d9589..1b72151b12f 100644
--- a/src/mongo/db/coll_mod.idl
+++ b/src/mongo/db/coll_mod.idl
@@ -67,6 +67,10 @@ structs:
optional: true
type: safeBool
unstable: true
+ forceNonUnique:
+ optional: true
+ type: safeBool
+ unstable: true
CollModReply:
description: "The collMod command's reply."
@@ -100,6 +104,10 @@ structs:
optional: true
type: safeBool
unstable: true
+ forceNonUnique_new:
+ optional: true
+ type: safeBool
+ unstable: true
CollModRequest:
description: "The collMod command's request."
diff --git a/src/mongo/db/op_observer.h b/src/mongo/db/op_observer.h
index 030521b9f4d..f1e8031c440 100644
--- a/src/mongo/db/op_observer.h
+++ b/src/mongo/db/op_observer.h
@@ -101,6 +101,7 @@ struct IndexCollModInfo {
boost::optional<bool> unique;
boost::optional<bool> prepareUnique;
boost::optional<bool> oldPrepareUnique;
+ boost::optional<bool> forceNonUnique;
std::string indexName;
};
diff --git a/src/mongo/db/op_observer_util.cpp b/src/mongo/db/op_observer_util.cpp
index cd79324a2aa..60a5902cd82 100644
--- a/src/mongo/db/op_observer_util.cpp
+++ b/src/mongo/db/op_observer_util.cpp
@@ -67,6 +67,9 @@ BSONObj makeCollModCmdObj(const BSONObj& collModCmd,
if (indexInfo->prepareUnique)
indexObjBuilder.append("prepareUnique", indexInfo->prepareUnique.get());
+ if (indexInfo->forceNonUnique)
+ indexObjBuilder.append("forceNonUnique", indexInfo->forceNonUnique.get());
+
cmdObjBuilder.append(indexFieldName, indexObjBuilder.obj());
} else {
cmdObjBuilder.append(elem);
diff --git a/src/mongo/db/storage/bson_collection_catalog_entry.cpp b/src/mongo/db/storage/bson_collection_catalog_entry.cpp
index 58cdc01fe39..2e45db1a989 100644
--- a/src/mongo/db/storage/bson_collection_catalog_entry.cpp
+++ b/src/mongo/db/storage/bson_collection_catalog_entry.cpp
@@ -140,9 +140,19 @@ void BSONCollectionCatalogEntry::IndexMetaData::updateHiddenSetting(bool hidden)
}
-void BSONCollectionCatalogEntry::IndexMetaData::updateUniqueSetting() {
- BSONObjBuilder b(spec);
- b.appendBool("unique", true);
+void BSONCollectionCatalogEntry::IndexMetaData::updateUniqueSetting(bool unique) {
+ // If unique == false, we remove this field from catalog rather than add a field with false.
+ BSONObjBuilder b;
+ for (BSONObjIterator bi(spec); bi.more();) {
+ BSONElement e = bi.next();
+ if (e.fieldNameStringData() != "unique") {
+ b.append(e);
+ }
+ }
+
+ if (unique) {
+ b.append("unique", unique);
+ }
spec = b.obj();
}
diff --git a/src/mongo/db/storage/bson_collection_catalog_entry.h b/src/mongo/db/storage/bson_collection_catalog_entry.h
index 18bca894a71..b2afd733205 100644
--- a/src/mongo/db/storage/bson_collection_catalog_entry.h
+++ b/src/mongo/db/storage/bson_collection_catalog_entry.h
@@ -98,7 +98,7 @@ public:
void updateHiddenSetting(bool hidden);
- void updateUniqueSetting();
+ void updateUniqueSetting(bool unique);
void updatePrepareUniqueSetting(bool prepareUnique);