summaryrefslogtreecommitdiff
path: root/src/mongo/db/catalog/index_key_validate.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/mongo/db/catalog/index_key_validate.cpp')
-rw-r--r--src/mongo/db/catalog/index_key_validate.cpp90
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};