diff options
author | Gregory Noma <gregory.noma@gmail.com> | 2021-05-24 18:24:17 -0400 |
---|---|---|
committer | Evergreen Agent <no-reply@evergreen.mongodb.com> | 2021-05-24 22:58:40 +0000 |
commit | 8d5547cdd6c45dd68c95dcf353e459e62da5222b (patch) | |
tree | 3f0ea8be0d8797da189d367f25520e1ebb28869d /src/mongo/db | |
parent | a5caa6782ad3f2cbe78e66bdeb258931a22ac348 (diff) | |
download | mongo-8d5547cdd6c45dd68c95dcf353e459e62da5222b.tar.gz |
SERVER-56933 Return options to create an identical time-series collection from listCollections
Diffstat (limited to 'src/mongo/db')
-rw-r--r-- | src/mongo/db/catalog/collection_options.cpp | 83 | ||||
-rw-r--r-- | src/mongo/db/catalog/collection_options.h | 9 | ||||
-rw-r--r-- | src/mongo/db/commands/create_command.cpp | 63 | ||||
-rw-r--r-- | src/mongo/db/commands/list_collections.cpp | 106 | ||||
-rw-r--r-- | src/mongo/db/exec/bucket_unpacker.cpp | 2 | ||||
-rw-r--r-- | src/mongo/db/exec/sample_from_timeseries_bucket.cpp | 2 | ||||
-rw-r--r-- | src/mongo/db/pipeline/document_source_internal_convert_bucket_index_stats.cpp | 2 | ||||
-rw-r--r-- | src/mongo/db/pipeline/document_source_internal_unpack_bucket.cpp | 2 | ||||
-rw-r--r-- | src/mongo/db/pipeline/document_source_internal_unpack_bucket_test/unpack_bucket_exec_test.cpp | 2 | ||||
-rw-r--r-- | src/mongo/db/timeseries/timeseries_constants.h (renamed from src/mongo/db/timeseries/timeseries_field_names.h) | 9 | ||||
-rw-r--r-- | src/mongo/db/timeseries/timeseries_index_schema_conversion_functions.cpp | 2 | ||||
-rw-r--r-- | src/mongo/db/timeseries/timeseries_index_schema_conversion_functions_test.cpp | 2 | ||||
-rw-r--r-- | src/mongo/db/views/resolved_view.cpp | 2 |
13 files changed, 193 insertions, 93 deletions
diff --git a/src/mongo/db/catalog/collection_options.cpp b/src/mongo/db/catalog/collection_options.cpp index b5f1d32afed..2eadc1b11f8 100644 --- a/src/mongo/db/catalog/collection_options.cpp +++ b/src/mongo/db/catalog/collection_options.cpp @@ -316,81 +316,90 @@ CollectionOptions CollectionOptions::fromCreateCommand(const CreateCommand& cmd) return options; } -BSONObj CollectionOptions::toBSON(bool includeUUID) const { +BSONObj CollectionOptions::toBSON(bool includeUUID, const StringDataSet& includeFields) const { BSONObjBuilder b; - appendBSON(&b, includeUUID); + appendBSON(&b, includeUUID, includeFields); return b.obj(); } -void CollectionOptions::appendBSON(BSONObjBuilder* builder, bool includeUUID) const { +void CollectionOptions::appendBSON(BSONObjBuilder* builder, + bool includeUUID, + const StringDataSet& includeFields) const { if (uuid && includeUUID) { builder->appendElements(uuid->toBSON()); } - if (capped) { - builder->appendBool("capped", true); - builder->appendNumber("size", cappedSize); + auto shouldAppend = [&](StringData option) { + return includeFields.empty() || includeFields.contains(option); + }; + + if (capped && shouldAppend(CreateCommand::kCappedFieldName)) { + builder->appendBool(CreateCommand::kCappedFieldName, true); + builder->appendNumber(CreateCommand::kSizeFieldName, cappedSize); if (cappedMaxDocs) - builder->appendNumber("max", cappedMaxDocs); + builder->appendNumber(CreateCommand::kMaxFieldName, cappedMaxDocs); } - if (autoIndexId != DEFAULT) - builder->appendBool("autoIndexId", autoIndexId == YES); + if (autoIndexId != DEFAULT && shouldAppend(CreateCommand::kAutoIndexIdFieldName)) + builder->appendBool(CreateCommand::kAutoIndexIdFieldName, autoIndexId == YES); - if (temp) - builder->appendBool("temp", true); + if (temp && shouldAppend(CreateCommand::kTempFieldName)) + builder->appendBool(CreateCommand::kTempFieldName, true); - if (recordPreImages) { - builder->appendBool("recordPreImages", true); + if (recordPreImages && shouldAppend(CreateCommand::kRecordPreImagesFieldName)) { + builder->appendBool(CreateCommand::kRecordPreImagesFieldName, true); } - if (!storageEngine.isEmpty()) { - builder->append("storageEngine", storageEngine); + if (!storageEngine.isEmpty() && shouldAppend(CreateCommand::kStorageEngineFieldName)) { + builder->append(CreateCommand::kStorageEngineFieldName, storageEngine); } - if (indexOptionDefaults.getStorageEngine()) { - builder->append("indexOptionDefaults", indexOptionDefaults.toBSON()); + if (indexOptionDefaults.getStorageEngine() && + shouldAppend(CreateCommand::kIndexOptionDefaultsFieldName)) { + builder->append(CreateCommand::kIndexOptionDefaultsFieldName, indexOptionDefaults.toBSON()); } - if (!validator.isEmpty()) { - builder->append("validator", validator); + if (!validator.isEmpty() && shouldAppend(CreateCommand::kValidatorFieldName)) { + builder->append(CreateCommand::kValidatorFieldName, validator); } - if (validationLevel) { - builder->append("validationLevel", ValidationLevel_serializer(*validationLevel)); + if (validationLevel && shouldAppend(CreateCommand::kValidationLevelFieldName)) { + builder->append(CreateCommand::kValidationLevelFieldName, + ValidationLevel_serializer(*validationLevel)); } - if (validationAction) { - builder->append("validationAction", ValidationAction_serializer(*validationAction)); + if (validationAction && shouldAppend(CreateCommand::kValidationActionFieldName)) { + builder->append(CreateCommand::kValidationActionFieldName, + ValidationAction_serializer(*validationAction)); } - if (!collation.isEmpty()) { - builder->append("collation", collation); + if (!collation.isEmpty() && shouldAppend(CreateCommand::kCollationFieldName)) { + builder->append(CreateCommand::kCollationFieldName, collation); } - if (clusteredIndex) { - builder->append("clusteredIndex", true); + if (clusteredIndex && shouldAppend(CreateCommand::kClusteredIndexFieldName)) { + builder->append(CreateCommand::kClusteredIndexFieldName, true); } - if (expireAfterSeconds) { - builder->append("expireAfterSeconds", *expireAfterSeconds); + if (expireAfterSeconds && shouldAppend(CreateCommand::kExpireAfterSecondsFieldName)) { + builder->append(CreateCommand::kExpireAfterSecondsFieldName, *expireAfterSeconds); } - if (!viewOn.empty()) { - builder->append("viewOn", viewOn); + if (!viewOn.empty() && shouldAppend(CreateCommand::kViewOnFieldName)) { + builder->append(CreateCommand::kViewOnFieldName, viewOn); } - if (!pipeline.isEmpty()) { - builder->appendArray("pipeline", pipeline); + if (!pipeline.isEmpty() && shouldAppend(CreateCommand::kPipelineFieldName)) { + builder->appendArray(CreateCommand::kPipelineFieldName, pipeline); } - if (!idIndex.isEmpty()) { - builder->append("idIndex", idIndex); + if (!idIndex.isEmpty() && shouldAppend(CreateCommand::kIdIndexFieldName)) { + builder->append(CreateCommand::kIdIndexFieldName, idIndex); } - if (timeseries) { - builder->append("timeseries", timeseries->toBSON()); + if (timeseries && shouldAppend(CreateCommand::kTimeseriesFieldName)) { + builder->append(CreateCommand::kTimeseriesFieldName, timeseries->toBSON()); } } diff --git a/src/mongo/db/catalog/collection_options.h b/src/mongo/db/catalog/collection_options.h index 16be9667804..7a901f79c28 100644 --- a/src/mongo/db/catalog/collection_options.h +++ b/src/mongo/db/catalog/collection_options.h @@ -92,10 +92,13 @@ struct CollectionOptions { /** * Serialize to BSON. The 'includeUUID' parameter is used for the listCollections command to do - * special formatting for the uuid. + * special formatting for the uuid. Aside from the UUID, if 'includeFields' is non-empty, only + * the specified fields will be included. */ - void appendBSON(BSONObjBuilder* builder, bool includeUUID) const; - BSONObj toBSON(bool includeUUID = true) const; + void appendBSON(BSONObjBuilder* builder, + bool includeUUID, + const StringDataSet& includeFields) const; + BSONObj toBSON(bool includeUUID = true, const StringDataSet& includeFields = {}) const; /** * Returns true if given options matches to this. diff --git a/src/mongo/db/commands/create_command.cpp b/src/mongo/db/commands/create_command.cpp index 40442e764dc..d94c77f5268 100644 --- a/src/mongo/db/commands/create_command.cpp +++ b/src/mongo/db/commands/create_command.cpp @@ -39,6 +39,7 @@ #include "mongo/db/query/collation/collator_factory_interface.h" #include "mongo/db/s/operation_sharding_state.h" #include "mongo/db/storage/storage_parameters_gen.h" +#include "mongo/db/timeseries/timeseries_constants.h" #include "mongo/logv2/log.h" namespace mongo { @@ -148,47 +149,33 @@ public: feature_flags::gTimeseriesCollection.isEnabled( serverGlobalParams.featureCompatibility)); - const auto timeseriesNotAllowedWith = [&cmd](StringData option) -> std::string { - return str::stream() << cmd.getNamespace() - << ": 'timeseries' is not allowed with '" << option << "'"; - }; + for (auto&& option : cmd.toBSON({})) { + auto fieldName = option.fieldNameStringData(); + + if (fieldName == CreateCommand::kCommandName) { + continue; + } + + // The 'capped' option defaults to false. We accept it unless it is set to true. + if (fieldName == CreateCommand::kCappedFieldName && !option.Bool()) { + continue; + } + + // The 'timeseries' option may be passed with a 'validator' or 'clusteredIndex' + // if a buckets collection is being restored. We assume the caller knows what + // they are doing. + if ((fieldName == CreateCommand::kValidatorFieldName || + fieldName == CreateCommand::kClusteredIndexFieldName) && + cmd.getNamespace().isTimeseriesBucketsCollection()) { + continue; + } - uassert(ErrorCodes::InvalidOptions, - timeseriesNotAllowedWith("capped"), - !cmd.getCapped()); - uassert(ErrorCodes::InvalidOptions, - timeseriesNotAllowedWith("autoIndexId"), - !cmd.getAutoIndexId()); - uassert(ErrorCodes::InvalidOptions, - timeseriesNotAllowedWith("idIndex"), - !cmd.getIdIndex()); - uassert( - ErrorCodes::InvalidOptions, timeseriesNotAllowedWith("size"), !cmd.getSize()); - uassert(ErrorCodes::InvalidOptions, timeseriesNotAllowedWith("max"), !cmd.getMax()); - - // The 'timeseries' option may be passed with a 'validator' or 'clusteredIndex' if a - // buckets collection is being restored. We assume the caller knows what they are - // doing. - if (!cmd.getNamespace().isTimeseriesBucketsCollection()) { - uassert(ErrorCodes::InvalidOptions, - timeseriesNotAllowedWith("validator"), - !cmd.getValidator()); uassert(ErrorCodes::InvalidOptions, - timeseriesNotAllowedWith("clusteredIndex"), - !cmd.getClusteredIndex()); + str::stream() + << cmd.getNamespace() << ": 'timeseries' is not allowed with '" + << fieldName << "'", + timeseries::kAllowedCollectionCreationOptions.contains(fieldName)); } - uassert(ErrorCodes::InvalidOptions, - timeseriesNotAllowedWith("validationLevel"), - !cmd.getValidationLevel()); - uassert(ErrorCodes::InvalidOptions, - timeseriesNotAllowedWith("validationAction"), - !cmd.getValidationAction()); - uassert(ErrorCodes::InvalidOptions, - timeseriesNotAllowedWith("viewOn"), - !cmd.getViewOn()); - uassert(ErrorCodes::InvalidOptions, - timeseriesNotAllowedWith("pipeline"), - !cmd.getPipeline()); auto hasDot = [](StringData field) -> bool { return field.find('.') != std::string::npos; diff --git a/src/mongo/db/commands/list_collections.cpp b/src/mongo/db/commands/list_collections.cpp index 03c012f06fa..dd3c3192776 100644 --- a/src/mongo/db/commands/list_collections.cpp +++ b/src/mongo/db/commands/list_collections.cpp @@ -63,6 +63,7 @@ #include "mongo/db/service_context.h" #include "mongo/db/storage/storage_engine.h" #include "mongo/db/storage/storage_options.h" +#include "mongo/db/timeseries/timeseries_constants.h" #include "mongo/db/views/view_catalog.h" #include "mongo/logv2/log.h" @@ -142,9 +143,11 @@ void _addWorkingSetMember(OperationContext* opCtx, } BSONObj buildViewBson(const ViewDefinition& view, bool nameOnly) { + invariant(!view.timeseries()); + BSONObjBuilder b; b.append("name", view.name().coll()); - b.append("type", view.timeseries() ? "timeseries" : "view"); + b.append("type", "view"); if (nameOnly) { return b.obj(); @@ -158,11 +161,47 @@ BSONObj buildViewBson(const ViewDefinition& view, bool nameOnly) { } optionsBuilder.doneFast(); - BSONObj info = BSON("readOnly" << !view.timeseries()); + BSONObj info = BSON("readOnly" << true); b.append("info", info); return b.obj(); } +BSONObj buildTimeseriesBson(OperationContext* opCtx, + const CollectionPtr& collection, + bool nameOnly) { + invariant(collection); + + BSONObjBuilder builder; + builder.append("name", collection->ns().getTimeseriesViewNamespace().coll()); + builder.append("type", "timeseries"); + + if (nameOnly) { + return builder.obj(); + } + + builder.append("options", + collection->getCollectionOptions().toBSON( + false /* includeUUID */, timeseries::kAllowedCollectionCreationOptions)); + builder.append("info", BSON("readOnly" << false)); + + return builder.obj(); +} + +BSONObj buildTimeseriesBson(StringData collName, bool nameOnly) { + BSONObjBuilder builder; + builder.append("name", collName); + builder.append("type", "timeseries"); + + if (nameOnly) { + return builder.obj(); + } + + builder.append("options", BSONObj{}); + builder.append("info", BSON("readOnly" << false)); + + return builder.obj(); +} + /** * Return an object describing the collection. Takes a collection lock if nameOnly is false. */ @@ -323,11 +362,32 @@ public: clk.emplace(opCtx, nss, MODE_IS); } - CollectionPtr collection = - CollectionCatalog::get(opCtx)->lookupCollectionByNamespace(opCtx, - nss); - BSONObj collBson = buildCollectionBson( - opCtx, collection, includePendingDrops, nameOnly); + auto collBson = [&] { + if (auto collection = + CollectionCatalog::get(opCtx)->lookupCollectionByNamespace( + opCtx, nss)) { + return buildCollectionBson( + opCtx, collection, includePendingDrops, nameOnly); + } + + auto view = viewCatalog->lookupWithoutValidatingDurableViews( + opCtx, nss.ns()); + if (view && view->timeseries()) { + if (auto bucketsCollection = CollectionCatalog::get(opCtx) + ->lookupCollectionByNamespace( + opCtx, view->viewOn())) { + return buildTimeseriesBson( + opCtx, bucketsCollection, nameOnly); + } else { + // The buckets collection does not exist, so the time-series + // view will be appended when we iterate through the view + // catalog below. + } + } + + return BSONObj{}; + }(); + if (!collBson.isEmpty()) { _addWorkingSetMember( opCtx, collBson, matcher.get(), ws.get(), root.get()); @@ -340,12 +400,27 @@ public: ResourcePattern::forExactNamespace(collection->ns())))) { return true; } + BSONObj collBson = buildCollectionBson( opCtx, collection, includePendingDrops, nameOnly); if (!collBson.isEmpty()) { _addWorkingSetMember( opCtx, collBson, matcher.get(), ws.get(), root.get()); } + + if (collection && collection->getTimeseriesOptions() && + viewCatalog->lookupWithoutValidatingDurableViews( + opCtx, collection->ns().getTimeseriesViewNamespace().ns())) { + // The time-series view for this buckets namespace exists, so add it + // here while we have the collection options. + _addWorkingSetMember( + opCtx, + buildTimeseriesBson(opCtx, collection, nameOnly), + matcher.get(), + ws.get(), + root.get()); + } + return true; }; @@ -379,6 +454,23 @@ public: return true; } + if (view.timeseries()) { + if (!CollectionCatalog::get(opCtx) + ->lookupCollectionByNamespaceForRead(opCtx, + view.viewOn())) { + // There is no buckets collection backing this time-series view, + // which means that it was not already added along with the + // buckets collection above. + _addWorkingSetMember( + opCtx, + buildTimeseriesBson(view.name().coll(), nameOnly), + matcher.get(), + ws.get(), + root.get()); + } + return true; + } + BSONObj viewBson = buildViewBson(view, nameOnly); if (!viewBson.isEmpty()) { _addWorkingSetMember( diff --git a/src/mongo/db/exec/bucket_unpacker.cpp b/src/mongo/db/exec/bucket_unpacker.cpp index 1e884dc1714..829e490213b 100644 --- a/src/mongo/db/exec/bucket_unpacker.cpp +++ b/src/mongo/db/exec/bucket_unpacker.cpp @@ -30,7 +30,7 @@ #include "mongo/platform/basic.h" #include "mongo/db/exec/bucket_unpacker.h" -#include "mongo/db/timeseries/timeseries_field_names.h" +#include "mongo/db/timeseries/timeseries_constants.h" namespace mongo { diff --git a/src/mongo/db/exec/sample_from_timeseries_bucket.cpp b/src/mongo/db/exec/sample_from_timeseries_bucket.cpp index 2d0fe7e72aa..06bdba2e77a 100644 --- a/src/mongo/db/exec/sample_from_timeseries_bucket.cpp +++ b/src/mongo/db/exec/sample_from_timeseries_bucket.cpp @@ -28,7 +28,7 @@ */ #include "mongo/db/exec/sample_from_timeseries_bucket.h" -#include "mongo/db/timeseries/timeseries_field_names.h" +#include "mongo/db/timeseries/timeseries_constants.h" namespace mongo { const char* SampleFromTimeseriesBucket::kStageType = "SAMPLE_FROM_TIMESERIES_BUCKET"; diff --git a/src/mongo/db/pipeline/document_source_internal_convert_bucket_index_stats.cpp b/src/mongo/db/pipeline/document_source_internal_convert_bucket_index_stats.cpp index 216472accc4..3e523696eb8 100644 --- a/src/mongo/db/pipeline/document_source_internal_convert_bucket_index_stats.cpp +++ b/src/mongo/db/pipeline/document_source_internal_convert_bucket_index_stats.cpp @@ -36,7 +36,7 @@ #include "mongo/db/list_indexes_gen.h" #include "mongo/db/pipeline/expression_context.h" #include "mongo/db/pipeline/lite_parsed_document_source.h" -#include "mongo/db/timeseries/timeseries_field_names.h" +#include "mongo/db/timeseries/timeseries_constants.h" #include "mongo/db/timeseries/timeseries_gen.h" #include "mongo/db/timeseries/timeseries_index_schema_conversion_functions.h" diff --git a/src/mongo/db/pipeline/document_source_internal_unpack_bucket.cpp b/src/mongo/db/pipeline/document_source_internal_unpack_bucket.cpp index 0b63d0aadb5..2fa8291fee2 100644 --- a/src/mongo/db/pipeline/document_source_internal_unpack_bucket.cpp +++ b/src/mongo/db/pipeline/document_source_internal_unpack_bucket.cpp @@ -52,7 +52,7 @@ #include "mongo/db/pipeline/expression_context.h" #include "mongo/db/pipeline/lite_parsed_document_source.h" #include "mongo/db/query/util/make_data_structure.h" -#include "mongo/db/timeseries/timeseries_field_names.h" +#include "mongo/db/timeseries/timeseries_constants.h" #include "mongo/logv2/log.h" #include "mongo/util/duration.h" #include "mongo/util/time_support.h" diff --git a/src/mongo/db/pipeline/document_source_internal_unpack_bucket_test/unpack_bucket_exec_test.cpp b/src/mongo/db/pipeline/document_source_internal_unpack_bucket_test/unpack_bucket_exec_test.cpp index 83f2fa6e894..35667959188 100644 --- a/src/mongo/db/pipeline/document_source_internal_unpack_bucket_test/unpack_bucket_exec_test.cpp +++ b/src/mongo/db/pipeline/document_source_internal_unpack_bucket_test/unpack_bucket_exec_test.cpp @@ -31,7 +31,7 @@ #include "mongo/db/pipeline/aggregation_context_fixture.h" #include "mongo/db/pipeline/document_source_internal_unpack_bucket.h" #include "mongo/db/pipeline/document_source_mock.h" -#include "mongo/db/timeseries/timeseries_field_names.h" +#include "mongo/db/timeseries/timeseries_constants.h" namespace mongo { namespace { diff --git a/src/mongo/db/timeseries/timeseries_field_names.h b/src/mongo/db/timeseries/timeseries_constants.h index b6d24a78bd8..e9a6ebf80ba 100644 --- a/src/mongo/db/timeseries/timeseries_field_names.h +++ b/src/mongo/db/timeseries/timeseries_constants.h @@ -30,6 +30,8 @@ #pragma once #include "mongo/base/string_data.h" +#include "mongo/db/commands/create_gen.h" +#include "mongo/util/string_map.h" namespace mongo { namespace timeseries { @@ -49,5 +51,12 @@ static constexpr StringData kMetaFieldName = "metaField"_sd; // These are hard-coded field names in index specs. static constexpr StringData kKeyFieldName = "key"_sd; +static const StringDataSet kAllowedCollectionCreationOptions{ + CreateCommand::kStorageEngineFieldName, + CreateCommand::kIndexOptionDefaultsFieldName, + CreateCommand::kCollationFieldName, + CreateCommand::kTimeseriesFieldName, + CreateCommand::kExpireAfterSecondsFieldName}; + } // namespace timeseries } // namespace mongo diff --git a/src/mongo/db/timeseries/timeseries_index_schema_conversion_functions.cpp b/src/mongo/db/timeseries/timeseries_index_schema_conversion_functions.cpp index 1aa910b73e9..5909670ff08 100644 --- a/src/mongo/db/timeseries/timeseries_index_schema_conversion_functions.cpp +++ b/src/mongo/db/timeseries/timeseries_index_schema_conversion_functions.cpp @@ -33,7 +33,7 @@ #include "mongo/db/timeseries/timeseries_index_schema_conversion_functions.h" -#include "mongo/db/timeseries/timeseries_field_names.h" +#include "mongo/db/timeseries/timeseries_constants.h" #include "mongo/db/timeseries/timeseries_gen.h" #include "mongo/logv2/log.h" #include "mongo/logv2/redaction.h" diff --git a/src/mongo/db/timeseries/timeseries_index_schema_conversion_functions_test.cpp b/src/mongo/db/timeseries/timeseries_index_schema_conversion_functions_test.cpp index 9eac6a67236..2763b5a3385 100644 --- a/src/mongo/db/timeseries/timeseries_index_schema_conversion_functions_test.cpp +++ b/src/mongo/db/timeseries/timeseries_index_schema_conversion_functions_test.cpp @@ -32,7 +32,7 @@ #include "mongo/db/timeseries/timeseries_index_schema_conversion_functions.h" #include "mongo/bson/bsonobj.h" -#include "mongo/db/timeseries/timeseries_field_names.h" +#include "mongo/db/timeseries/timeseries_constants.h" #include "mongo/db/timeseries/timeseries_gen.h" #include "mongo/unittest/unittest.h" diff --git a/src/mongo/db/views/resolved_view.cpp b/src/mongo/db/views/resolved_view.cpp index 2c1e9900fa6..9c754c559e7 100644 --- a/src/mongo/db/views/resolved_view.cpp +++ b/src/mongo/db/views/resolved_view.cpp @@ -36,7 +36,7 @@ #include "mongo/db/pipeline/document_source_index_stats.h" #include "mongo/db/pipeline/document_source_internal_convert_bucket_index_stats.h" #include "mongo/db/pipeline/document_source_internal_unpack_bucket.h" -#include "mongo/db/timeseries/timeseries_field_names.h" +#include "mongo/db/timeseries/timeseries_constants.h" #include "mongo/rpc/get_status_from_command_result.h" namespace mongo { |