diff options
Diffstat (limited to 'src/mongo/db/catalog/coll_mod.cpp')
| -rw-r--r-- | src/mongo/db/catalog/coll_mod.cpp | 107 |
1 files changed, 97 insertions, 10 deletions
diff --git a/src/mongo/db/catalog/coll_mod.cpp b/src/mongo/db/catalog/coll_mod.cpp index 8af0ba8efc9..9e92ed1d260 100644 --- a/src/mongo/db/catalog/coll_mod.cpp +++ b/src/mongo/db/catalog/coll_mod.cpp @@ -33,6 +33,7 @@ #include "mongo/db/catalog/coll_mod.h" +#include "mongo/db/stats/counters.h" #include <boost/optional.hpp> #include <memory> @@ -44,7 +45,7 @@ #include "mongo/db/catalog/index_catalog.h" #include "mongo/db/catalog/index_key_validate.h" #include "mongo/db/coll_mod_gen.h" -#include "mongo/db/concurrency/write_conflict_exception.h" +#include "mongo/db/concurrency/exception_util.h" #include "mongo/db/curop_failpoint_helpers.h" #include "mongo/db/db_raii.h" #include "mongo/db/index/index_descriptor.h" @@ -53,6 +54,7 @@ #include "mongo/db/repl/replication_coordinator.h" #include "mongo/db/s/collection_sharding_state.h" #include "mongo/db/s/database_sharding_state.h" +#include "mongo/db/s/shard_key_index_util.h" #include "mongo/db/server_options.h" #include "mongo/db/service_context.h" #include "mongo/db/storage/recovery_unit.h" @@ -62,6 +64,7 @@ #include "mongo/db/views/view_catalog_helpers.h" #include "mongo/idl/command_generic_argument.h" #include "mongo/logv2/log.h" +#include "mongo/s/grid.h" #include "mongo/util/fail_point.h" #include "mongo/util/version/releases.h" #include "mongo/util/visit_helper.h" @@ -109,7 +112,7 @@ struct ParsedCollModRequest { boost::optional<Collection::Validator> collValidator; boost::optional<ValidationActionEnum> collValidationAction; boost::optional<ValidationLevelEnum> collValidationLevel; - bool recordPreImages = false; + boost::optional<bool> recordPreImages; boost::optional<ChangeStreamPreAndPostImagesOptions> changeStreamPreAndPostImagesOptions; int numModifications = 0; bool dryRun = false; @@ -250,7 +253,8 @@ StatusWith<std::pair<ParsedCollModRequest, BSONObj>> parseCollModRequest(Operati "TTL indexes are not supported for capped collections."}; } if (auto status = index_key_validate::validateExpireAfterSeconds( - *cmdIndex.getExpireAfterSeconds()); + *cmdIndex.getExpireAfterSeconds(), + index_key_validate::ValidateExpireAfterSecondsMode::kSecondaryTTLIndex); !status.isOK()) { return {ErrorCodes::InvalidOptions, status.reason()}; } @@ -284,7 +288,8 @@ StatusWith<std::pair<ParsedCollModRequest, BSONObj>> parseCollModRequest(Operati } } else { std::vector<const IndexDescriptor*> indexes; - coll->getIndexCatalog()->findIndexesByKeyPattern(opCtx, keyPattern, false, &indexes); + coll->getIndexCatalog()->findIndexesByKeyPattern( + opCtx, keyPattern, IndexCatalog::InclusionPolicy::kReady, &indexes); if (indexes.size() > 1) { return {ErrorCodes::AmbiguousIndexKeyPattern, @@ -337,7 +342,7 @@ StatusWith<std::pair<ParsedCollModRequest, BSONObj>> parseCollModRequest(Operati if (cmrIndex->idx->unique()) { indexForOplog->setUnique(boost::none); } else { - // Disallow one-step unique convertion. The user has to set + // Disallow one-step unique conversion. The user has to set // 'prepareUnique' to true first. if (!cmrIndex->idx->prepareUnique()) { return Status(ErrorCodes::InvalidOptions, @@ -363,6 +368,28 @@ StatusWith<std::pair<ParsedCollModRequest, BSONObj>> parseCollModRequest(Operati return {ErrorCodes::BadValue, "can't hide _id index"}; } + // If the index is not hidden and we are trying to hide it, check if it is possible + // to drop the shard key index, so it could be possible to hide it. + if (!cmrIndex->idx->hidden() && *cmdIndex.getHidden()) { + if (auto catalogClient = Grid::get(opCtx)->catalogClient()) { + try { + auto shardedColl = catalogClient->getCollection(opCtx, nss); + + if (isLastNonHiddenShardKeyIndex(opCtx, + coll, + coll->getIndexCatalog(), + cmrIndex->idx->indexName(), + shardedColl.getKeyPattern().toBSON())) { + return {ErrorCodes::InvalidOptions, + "Can't hide the only compatible index for this collection's " + "shard key"}; + } + } catch (ExceptionFor<ErrorCodes::NamespaceNotFound>&) { + // The collection is unsharded or doesn't exist. + } + } + } + // Hiding a hidden index or unhiding a visible index should be treated as a no-op. if (cmrIndex->idx->hidden() == *cmdIndex.getHidden()) { indexForOplog->setHidden(boost::none); @@ -378,6 +405,24 @@ StatusWith<std::pair<ParsedCollModRequest, BSONObj>> parseCollModRequest(Operati cmrIndex->idx->unique()) { indexForOplog->setPrepareUnique(boost::none); } else { + // Checks if the index key pattern conflicts with the shard key pattern. + if (auto catalogClient = Grid::get(opCtx)->catalogClient()) { + try { + auto shardedColl = catalogClient->getCollection(opCtx, nss); + const ShardKeyPattern shardKeyPattern(shardedColl.getKeyPattern()); + if (!shardKeyPattern.isIndexUniquenessCompatible( + cmrIndex->idx->keyPattern())) { + return {ErrorCodes::InvalidOptions, + fmt::format( + "cannot set 'prepareUnique' for index {} with shard key " + "pattern {}", + cmrIndex->idx->keyPattern().toString(), + shardKeyPattern.toBSON().toString())}; + } + } catch (ExceptionFor<ErrorCodes::NamespaceNotFound>&) { + // The collection is unsharded or doesn't exist. + } + } cmrIndex->indexPrepareUnique = cmdIndex.getPrepareUnique(); } } @@ -430,6 +475,11 @@ StatusWith<std::pair<ParsedCollModRequest, BSONObj>> parseCollModRequest(Operati validatorObj.getOwned(), MatchExpressionParser::kDefaultSpecialFeatures, maxFeatureCompatibilityVersion); + + // Increment counters to track the usage of schema validators. + validatorCounters.incrementCounters( + cmd.kCommandName, parsed.collValidator->validatorDoc, parsed.collValidator->isOK()); + if (!parsed.collValidator->isOK()) { return parsed.collValidator->getStatus(); } @@ -533,7 +583,9 @@ StatusWith<std::pair<ParsedCollModRequest, BSONObj>> parseCollModRequest(Operati }, [&oplogEntryBuilder](std::int64_t value) { oplogEntryBuilder.append(CollMod::kExpireAfterSecondsFieldName, value); - return index_key_validate::validateExpireAfterSeconds(value); + return index_key_validate::validateExpireAfterSeconds( + value, + index_key_validate::ValidateExpireAfterSecondsMode::kClusteredTTLIndex); }, }, *expireAfterSeconds); @@ -596,7 +648,8 @@ void _setClusteredExpireAfterSeconds( if (!oldExpireAfterSeconds) { auto ttlCache = &TTLCollectionCache::get(opCtx->getServiceContext()); opCtx->recoveryUnit()->onCommit([ttlCache, uuid = coll->uuid()](auto _) { - ttlCache->registerTTLInfo(uuid, TTLCollectionCache::ClusteredId()); + ttlCache->registerTTLInfo( + uuid, TTLCollectionCache::Info{TTLCollectionCache::ClusteredId{}}); }); } @@ -846,7 +899,7 @@ Status _collModInternal(OperationContext* opCtx, cmrNew.recordPreImages = false; } - if (cmrNew.recordPreImages) { + if (cmrNew.recordPreImages && *cmrNew.recordPreImages) { cmrNew.changeStreamPreAndPostImagesOptions = ChangeStreamPreAndPostImagesOptions(false); } @@ -893,8 +946,9 @@ Status _collModInternal(OperationContext* opCtx, "Failed to set validationLevel"); } - if (cmrNew.recordPreImages != oldCollOptions.recordPreImages) { - coll.getWritableCollection(opCtx)->setRecordPreImages(opCtx, cmrNew.recordPreImages); + if (cmrNew.recordPreImages.has_value() && + *cmrNew.recordPreImages != oldCollOptions.recordPreImages) { + coll.getWritableCollection(opCtx)->setRecordPreImages(opCtx, *cmrNew.recordPreImages); } if (cmrNew.changeStreamPreAndPostImagesOptions.has_value() && @@ -963,6 +1017,39 @@ Status _collModInternal(OperationContext* opCtx, } // namespace +bool isCollModIndexUniqueConversion(const CollModRequest& request) { + auto index = request.getIndex(); + if (!index) { + return false; + } + if (auto indexUnique = index->getUnique(); !indexUnique) { + return false; + } + // Checks if the request is an actual unique conversion instead of a dry run. + if (auto dryRun = request.getDryRun(); dryRun && *dryRun) { + return false; + } + return true; +} + +CollModRequest makeCollModDryRunRequest(const CollModRequest& request) { + CollModRequest dryRunRequest; + CollModIndex dryRunIndex; + const auto& requestIndex = request.getIndex(); + dryRunIndex.setUnique(true); + if (auto keyPattern = requestIndex->getKeyPattern()) { + dryRunIndex.setKeyPattern(keyPattern); + } else if (auto name = requestIndex->getName()) { + dryRunIndex.setName(name); + } + if (auto uuid = request.getCollectionUUID()) { + dryRunRequest.setCollectionUUID(uuid); + } + dryRunRequest.setIndex(dryRunIndex); + dryRunRequest.setDryRun(true); + return dryRunRequest; +} + Status processCollModCommand(OperationContext* opCtx, const NamespaceStringOrUUID& nsOrUUID, const CollMod& cmd, |
