diff options
| author | Lucas de Castro Borges <lucas@gnuabordo.com.br> | 2025-02-14 14:26:38 -0300 |
|---|---|---|
| committer | Lucas de Castro Borges <lucas@gnuabordo.com.br> | 2025-02-14 14:26:38 -0300 |
| commit | 294bc6ecabf14c09c9bc8644704921dcf97cb44e (patch) | |
| tree | 279b1e0bab53901a1647ac63c1c724f0f789a663 /src/mongo/db/catalog/index_key_validate.cpp | |
| parent | 70be7c27a251621187a1de533462ae2bb1e3bd39 (diff) | |
| parent | 1e917fd798aa25b7066d4b414b51184f13d5a092 (diff) | |
Update upstream source from tag 'upstream/6.0.10'debian/6.0.10-1
Update to upstream version '6.0.10'
with Debian dir 2d176fa254eee97b139f712fec5709641335a8c3
Diffstat (limited to 'src/mongo/db/catalog/index_key_validate.cpp')
| -rw-r--r-- | src/mongo/db/catalog/index_key_validate.cpp | 90 |
1 files changed, 71 insertions, 19 deletions
diff --git a/src/mongo/db/catalog/index_key_validate.cpp b/src/mongo/db/catalog/index_key_validate.cpp index d6f70219594..45f85b244f7 100644 --- a/src/mongo/db/catalog/index_key_validate.cpp +++ b/src/mongo/db/catalog/index_key_validate.cpp @@ -58,7 +58,7 @@ namespace mongo { namespace index_key_validate { -std::function<void(std::set<StringData>&)> filterAllowedIndexFieldNames; +std::function<void(std::map<StringData, std::set<IndexType>>&)> filterAllowedIndexFieldNames; using IndexVersion = IndexDescriptor::IndexVersion; @@ -68,6 +68,10 @@ namespace { // specification. MONGO_FAIL_POINT_DEFINE(skipIndexCreateFieldNameValidation); +// When the skipTTLIndexNaNExpireAfterSecondsValidation failpoint is enabled, validation for +// TTL index 'expireAfterSeconds' will be disabled. +MONGO_FAIL_POINT_DEFINE(skipTTLIndexNaNExpireAfterSecondsValidation); + static const std::set<StringData> allowedIdIndexFieldNames = { IndexDescriptor::kCollationFieldName, IndexDescriptor::kIndexNameFieldName, @@ -104,12 +108,16 @@ Status isIndexVersionAllowedForCreation(IndexVersion indexVersion, const BSONObj BSONObj buildRepairedIndexSpec( const NamespaceString& ns, const BSONObj& indexSpec, - const std::set<StringData>& allowedFieldNames, + const std::map<StringData, std::set<IndexType>>& allowedFieldNames, std::function<void(const BSONElement&, BSONObjBuilder*)> indexSpecHandleFn) { + const auto key = indexSpec.getObjectField(IndexDescriptor::kKeyPatternFieldName); + const auto indexName = IndexNames::nameToType(IndexNames::findPluginName(key)); BSONObjBuilder builder; for (const auto& indexSpecElem : indexSpec) { StringData fieldName = indexSpecElem.fieldNameStringData(); - if (allowedFieldNames.count(fieldName)) { + auto it = allowedFieldNames.find(fieldName); + if (it != allowedFieldNames.end() && + (it->second.empty() || it->second.count(indexName) != 0)) { indexSpecHandleFn(indexSpecElem, &builder); } else { LOGV2_WARNING(23878, @@ -263,9 +271,9 @@ BSONObj removeUnknownFields(const NamespaceString& ns, const BSONObj& indexSpec) BSONObj repairIndexSpec(const NamespaceString& ns, const BSONObj& indexSpec, - const std::set<StringData>& allowedFieldNames) { - auto fixBoolIndexSpecFn = [&indexSpec, &ns](const BSONElement& indexSpecElem, - BSONObjBuilder* builder) { + const std::map<StringData, std::set<IndexType>>& allowedFieldNames) { + auto fixIndexSpecFn = [&indexSpec, &ns](const BSONElement& indexSpecElem, + BSONObjBuilder* builder) { StringData fieldName = indexSpecElem.fieldNameStringData(); if ((IndexDescriptor::kBackgroundFieldName == fieldName || IndexDescriptor::kUniqueFieldName == fieldName || @@ -279,17 +287,28 @@ BSONObj repairIndexSpec(const NamespaceString& ns, "fieldName"_attr = redact(fieldName), "indexSpec"_attr = redact(indexSpec)); builder->appendBool(fieldName, true); + } else if (IndexDescriptor::kExpireAfterSecondsFieldName == fieldName && + !(indexSpecElem.isNumber() && !indexSpecElem.isNaN())) { + LOGV2_WARNING(6835900, + "Fixing expire field from TTL index spec", + "namespace"_attr = redact(ns.toString()), + "fieldName"_attr = redact(fieldName), + "indexSpec"_attr = redact(indexSpec)); + builder->appendNumber(fieldName, + durationCount<Seconds>(kExpireAfterSecondsForInactiveTTLIndex)); } else { builder->append(indexSpecElem); } }; - return buildRepairedIndexSpec(ns, indexSpec, allowedFieldNames, fixBoolIndexSpecFn); + + return buildRepairedIndexSpec(ns, indexSpec, allowedFieldNames, fixIndexSpecFn); } StatusWith<BSONObj> validateIndexSpec(OperationContext* opCtx, const BSONObj& indexSpec) { bool hasKeyPatternField = false; bool hasIndexNameField = false; bool hasNamespaceField = false; + bool isTTLIndexWithNaNExpireAfterSeconds = false; bool hasVersionField = false; bool hasCollationField = false; bool hasWeightsField = false; @@ -537,6 +556,8 @@ StatusWith<BSONObj> validateIndexSpec(OperationContext* opCtx, const BSONObj& in str::stream() << "The field '" << indexSpecElemFieldName << "' must be a number, but got " << typeName(indexSpecElem.type())}; + } else if (IndexDescriptor::kExpireAfterSecondsFieldName == indexSpecElemFieldName) { + isTTLIndexWithNaNExpireAfterSeconds = indexSpecElem.isNaN(); } else { // We can assume field name is valid at this point. Validation of fieldname is handled // prior to this in validateIndexSpecFieldNames(). @@ -608,6 +629,19 @@ StatusWith<BSONObj> validateIndexSpec(OperationContext* opCtx, const BSONObj& in modifiedSpec = modifiedSpec.removeField(IndexDescriptor::kNamespaceFieldName); } + if (isTTLIndexWithNaNExpireAfterSeconds && + !skipTTLIndexNaNExpireAfterSecondsValidation.shouldFail()) { + // We create a new index specification with the 'expireAfterSeconds' field set as + // kExpireAfterSecondsForInactiveTTLIndex if the current value is NaN. A similar + // treatment is done in repairIndexSpec(). This rewrites the 'expireAfterSeconds' + // value to be compliant with the 'safeInt' IDL type for the listIndexes response. + BSONObjBuilder builder; + builder.appendNumber(IndexDescriptor::kExpireAfterSecondsFieldName, + durationCount<Seconds>(kExpireAfterSecondsForInactiveTTLIndex)); + auto obj = builder.obj(); + modifiedSpec = modifiedSpec.addField(obj.firstElement()); + } + if (!hasVersionField) { // We create a new index specification with the 'v' field set as 'defaultIndexVersion' if // the field was omitted. @@ -768,7 +802,8 @@ StatusWith<BSONObj> validateIndexSpecCollation(OperationContext* opCtx, return indexSpec; } -Status validateExpireAfterSeconds(std::int64_t expireAfterSeconds) { +Status validateExpireAfterSeconds(std::int64_t expireAfterSeconds, + ValidateExpireAfterSecondsMode mode) { if (expireAfterSeconds < 0) { return {ErrorCodes::InvalidOptions, str::stream() << "TTL index '" << IndexDescriptor::kExpireAfterSecondsFieldName @@ -779,16 +814,31 @@ Status validateExpireAfterSeconds(std::int64_t expireAfterSeconds) { << "TTL index '" << IndexDescriptor::kExpireAfterSecondsFieldName << "' option must be within an acceptable range, try a lower number"; - // There are two cases where we can encounter an issue here. - // The first case is when we try to cast to millseconds from seconds, which could cause an - // overflow. The second case is where 'expireAfterSeconds' is larger than the current epoch - // time. - if (expireAfterSeconds > std::numeric_limits<std::int64_t>::max() / 1000) { - return {ErrorCodes::InvalidOptions, tooLargeErr}; - } - auto expireAfterMillis = duration_cast<Milliseconds>(Seconds(expireAfterSeconds)); - if (expireAfterMillis > Date_t::now().toDurationSinceEpoch()) { - return {ErrorCodes::InvalidOptions, tooLargeErr}; + if (mode == ValidateExpireAfterSecondsMode::kSecondaryTTLIndex) { + // Relax epoch restriction on TTL indexes. This allows us to export and import existing + // TTL indexes with large values or NaN for the 'expireAfterSeconds' field. + // Additionally, the 'expireAfterSeconds' for TTL indexes is defined as safeInt (int32_t) + // in the IDL for listIndexes and collMod. See list_indexes.idl and coll_mod.idl. + if (expireAfterSeconds > std::numeric_limits<std::int32_t>::max()) { + return {ErrorCodes::InvalidOptions, tooLargeErr}; + } + } else { + // Clustered collections with TTL. + // Note that 'expireAfterSeconds' is defined as safeInt64 in the IDL for the create and + // collMod commands. See create.idl and coll_mod.idl. + // There are two cases where we can encounter an issue here. + // The first case is when we try to cast to millseconds from seconds, which could cause an + // overflow. The second case is where 'expireAfterSeconds' is larger than the current epoch + // time. This isn't necessarily problematic for the general case, but for the specific case + // of time series collections, we cluster the collection by an OID value, where the + // timestamp portion is only a 32-bit unsigned integer offset of seconds since the epoch. + if (expireAfterSeconds > std::numeric_limits<std::int64_t>::max() / 1000) { + return {ErrorCodes::InvalidOptions, tooLargeErr}; + } + auto expireAfterMillis = duration_cast<Milliseconds>(Seconds(expireAfterSeconds)); + if (expireAfterMillis > Date_t::now().toDurationSinceEpoch()) { + return {ErrorCodes::InvalidOptions, tooLargeErr}; + } } return Status::OK(); } @@ -812,7 +862,9 @@ Status validateIndexSpecTTL(const BSONObj& indexSpec) { << "'. Index spec: " << indexSpec}; } - if (auto status = validateExpireAfterSeconds(expireAfterSecondsElt.safeNumberLong()); + if (auto status = + validateExpireAfterSeconds(expireAfterSecondsElt.safeNumberLong(), + ValidateExpireAfterSecondsMode::kSecondaryTTLIndex); !status.isOK()) { return {ErrorCodes::CannotCreateIndex, str::stream() << status.reason() << ". Index spec: " << indexSpec}; |
