From ee08d86e202e58f488d1303341839a831e9bfc68 Mon Sep 17 00:00:00 2001 From: Benety Goh Date: Fri, 20 Nov 2020 16:39:11 -0500 Subject: SERVER-52525 extract index_key_validate::validateIndexSpecTTL() from createIndexes command --- .../noPassthroughWithMongod/time_series_create.js | 2 + src/mongo/db/catalog/index_key_validate.cpp | 51 ++++++++++++++++++++++ src/mongo/db/catalog/index_key_validate.h | 6 +++ src/mongo/db/commands/create_indexes.cpp | 47 ++------------------ 4 files changed, 62 insertions(+), 44 deletions(-) diff --git a/jstests/noPassthroughWithMongod/time_series_create.js b/jstests/noPassthroughWithMongod/time_series_create.js index 197bc20c4a6..20a68811472 100644 --- a/jstests/noPassthroughWithMongod/time_series_create.js +++ b/jstests/noPassthroughWithMongod/time_series_create.js @@ -94,6 +94,8 @@ testInvalidTimeseriesOptions("", ErrorCodes.TypeMismatch); testInvalidTimeseriesOptions({timeField: 100}, ErrorCodes.TypeMismatch); testInvalidTimeseriesOptions({timeField: "time", metaField: 100}, ErrorCodes.TypeMismatch); testInvalidTimeseriesOptions({timeField: "time", expireAfterSeconds: ""}, ErrorCodes.TypeMismatch); +testInvalidTimeseriesOptions({timeField: "time", expireAfterSeconds: NumberLong(-10)}, + ErrorCodes.TypeMismatch); testInvalidTimeseriesOptions({timeField: "time", invalidOption: {}}, 40415); testCompatibleCreateOptions({storageEngine: {}}); diff --git a/src/mongo/db/catalog/index_key_validate.cpp b/src/mongo/db/catalog/index_key_validate.cpp index 829a2b7392c..14e0ca05bdd 100644 --- a/src/mongo/db/catalog/index_key_validate.cpp +++ b/src/mongo/db/catalog/index_key_validate.cpp @@ -610,6 +610,57 @@ StatusWith validateIndexSpecCollation(OperationContext* opCtx, return indexSpec; } +Status validateIndexSpecTTL(const BSONObj& indexSpec) { + if (!indexSpec.hasField(IndexDescriptor::kExpireAfterSecondsFieldName)) { + return Status::OK(); + } + + const BSONElement expireAfterSecondsElt = + indexSpec[IndexDescriptor::kExpireAfterSecondsFieldName]; + if (!expireAfterSecondsElt.isNumber()) { + return {ErrorCodes::CannotCreateIndex, + str::stream() << "TTL index '" << IndexDescriptor::kExpireAfterSecondsFieldName + << "' option must be numeric, but received a type of '" + << typeName(expireAfterSecondsElt.type()) + << "'. Index spec: " << indexSpec}; + } + + if (expireAfterSecondsElt.safeNumberLong() < 0) { + return {ErrorCodes::CannotCreateIndex, + str::stream() << "TTL index '" << IndexDescriptor::kExpireAfterSecondsFieldName + << "' option cannot be less than 0. Index spec: " << indexSpec}; + } + + const std::string tooLargeErr = str::stream() + << "TTL index '" << IndexDescriptor::kExpireAfterSecondsFieldName + << "' option must be within an acceptable range, try a lower number. Index spec: " + << indexSpec; + + // 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. + try { + auto expireAfterMillis = + duration_cast(Seconds(expireAfterSecondsElt.safeNumberLong())); + if (expireAfterMillis > Date_t::now().toDurationSinceEpoch()) { + return {ErrorCodes::CannotCreateIndex, tooLargeErr}; + } + } catch (const AssertionException&) { + return {ErrorCodes::CannotCreateIndex, tooLargeErr}; + } + + const BSONObj key = indexSpec["key"].Obj(); + if (key.nFields() != 1) { + return {ErrorCodes::CannotCreateIndex, + str::stream() << "TTL indexes are single-field indexes, compound indexes do " + "not support TTL. Index spec: " + << indexSpec}; + } + + return Status::OK(); +} + GlobalInitializerRegisterer filterAllowedIndexFieldNamesInitializer( "FilterAllowedIndexFieldNames", [](InitializerContext* service) { if (filterAllowedIndexFieldNames) diff --git a/src/mongo/db/catalog/index_key_validate.h b/src/mongo/db/catalog/index_key_validate.h index 7c177c50355..e3e29f08bef 100644 --- a/src/mongo/db/catalog/index_key_validate.h +++ b/src/mongo/db/catalog/index_key_validate.h @@ -86,6 +86,12 @@ StatusWith validateIndexSpecCollation(OperationContext* opCtx, const BSONObj& indexSpec, const CollatorInterface* defaultCollator); +/** + * Validates the key pattern and the 'expireAfterSeconds' duration in the index specification + * 'indexSpec' for a TTL index. Returns success if 'indexSpec' does not refer to a TTL index. + */ +Status validateIndexSpecTTL(const BSONObj& indexSpec); + /** * Optional filtering function to adjust allowed index field names at startup. * Set it in a MONGO_INITIALIZER with 'FilterAllowedIndexFieldNames' as a dependant. diff --git a/src/mongo/db/commands/create_indexes.cpp b/src/mongo/db/commands/create_indexes.cpp index 3ecaafbc401..43f5a813313 100644 --- a/src/mongo/db/commands/create_indexes.cpp +++ b/src/mongo/db/commands/create_indexes.cpp @@ -227,50 +227,9 @@ Status validateTTLOptions(OperationContext* opCtx, const BSONObj& cmdObj) { const BSONElement& indexes = cmdObj[kIndexesFieldName]; for (const auto& index : indexes.Array()) { BSONObj indexObj = index.Obj(); - if (!indexObj.hasField(kExpireAfterSeconds)) { - continue; - } - - const BSONElement expireAfterSecondsElt = indexObj[kExpireAfterSeconds]; - if (!expireAfterSecondsElt.isNumber()) { - return {ErrorCodes::CannotCreateIndex, - str::stream() << "TTL index '" << kExpireAfterSeconds - << "' option must be numeric, but received a type of '" - << typeName(expireAfterSecondsElt.type()) - << "'. Index spec: " << indexObj}; - } - - if (expireAfterSecondsElt.safeNumberLong() < 0) { - return {ErrorCodes::CannotCreateIndex, - str::stream() << "TTL index '" << kExpireAfterSeconds - << "' option cannot be less than 0. Index spec: " << indexObj}; - } - - const std::string tooLargeErr = str::stream() - << "TTL index '" << kExpireAfterSeconds - << "' option must be within an acceptable range, try a lower number. Index spec: " - << indexObj; - - // 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. - try { - auto expireAfterMillis = - duration_cast(Seconds(expireAfterSecondsElt.safeNumberLong())); - if (expireAfterMillis > Date_t::now().toDurationSinceEpoch()) { - return {ErrorCodes::CannotCreateIndex, tooLargeErr}; - } - } catch (const AssertionException&) { - return {ErrorCodes::CannotCreateIndex, tooLargeErr}; - } - - const BSONObj key = indexObj["key"].Obj(); - if (key.nFields() != 1) { - return {ErrorCodes::CannotCreateIndex, - str::stream() << "TTL indexes are single-field indexes, compound indexes do " - "not support TTL. Index spec: " - << indexObj}; + auto status = index_key_validate::validateIndexSpecTTL(indexObj); + if (!status.isOK()) { + return status; } } -- cgit v1.2.1