diff options
author | Jack Mulrow <jack.mulrow@mongodb.com> | 2020-08-19 17:51:45 +0000 |
---|---|---|
committer | Evergreen Agent <no-reply@evergreen.mongodb.com> | 2020-08-26 21:09:39 +0000 |
commit | 81169c43da6fe06789bcec195909872943b85f53 (patch) | |
tree | 461fe851a0f0d1ef03702f3e82e99b2902481dac /src | |
parent | b24d4b2a96ef13bc233fd9cb9ecbefb905ee0ca8 (diff) | |
download | mongo-81169c43da6fe06789bcec195909872943b85f53.tar.gz |
SERVER-49289 Add collectionUUID option to aggregate
Diffstat (limited to 'src')
-rw-r--r-- | src/mongo/db/commands/run_aggregate.cpp | 22 | ||||
-rw-r--r-- | src/mongo/db/pipeline/aggregation_request.cpp | 9 | ||||
-rw-r--r-- | src/mongo/db/pipeline/aggregation_request.h | 12 | ||||
-rw-r--r-- | src/mongo/db/pipeline/aggregation_request_test.cpp | 21 | ||||
-rw-r--r-- | src/mongo/s/query/cluster_aggregate.cpp | 4 |
5 files changed, 66 insertions, 2 deletions
diff --git a/src/mongo/db/commands/run_aggregate.cpp b/src/mongo/db/commands/run_aggregate.cpp index 74c118f5b8f..13121ed3b15 100644 --- a/src/mongo/db/commands/run_aggregate.cpp +++ b/src/mongo/db/commands/run_aggregate.cpp @@ -514,6 +514,11 @@ Status runAggregate(OperationContext* opCtx, // If this is a change stream, perform special checks and change the execution namespace. if (liteParsedPipeline.hasChangeStream()) { + uassert(4928900, + str::stream() << AggregationRequest::kCollectionUUIDName + << " is not supported for a change stream", + !request.getCollectionUUID()); + // Replace the execution namespace with that of the oplog. nss = NamespaceString::kRsOplogNamespace; @@ -535,6 +540,11 @@ Status runAggregate(OperationContext* opCtx, // Obtain collection locks on the execution namespace; that is, the oplog. ctx.emplace(opCtx, nss, AutoGetCollectionViewMode::kViewsForbidden); } else if (nss.isCollectionlessAggregateNS() && pipelineInvolvedNamespaces.empty()) { + uassert(4928901, + str::stream() << AggregationRequest::kCollectionUUIDName + << " is not supported for a collectionless aggregation", + !request.getCollectionUUID()); + // If this is a collectionless agg with no foreign namespaces, don't acquire any locks. statsTracker.emplace(opCtx, nss, @@ -563,6 +573,10 @@ Status runAggregate(OperationContext* opCtx, if (ctx && ctx->getView() && !liteParsedPipeline.startsWithCollStats()) { invariant(nss != NamespaceString::kRsOplogNamespace); invariant(!nss.isCollectionlessAggregateNS()); + uassert(ErrorCodes::OptionNotSupportedOnView, + str::stream() << AggregationRequest::kCollectionUUIDName + << " is not supported against a view", + !request.getCollectionUUID()); // Check that the default collation of 'view' is compatible with the operation's // collation. The check is skipped if the request did not specify a collation. @@ -600,6 +614,14 @@ Status runAggregate(OperationContext* opCtx, return status; } + if (request.getCollectionUUID()) { + // If the namespace is not a view and collectionUUID was provided, verify the collection + // exists and has the expected UUID. + uassert(ErrorCodes::NamespaceNotFound, + "No collection found with the given namespace and UUID", + uuid && uuid == *request.getCollectionUUID()); + } + invariant(collatorToUse); expCtx = makeExpressionContext(opCtx, request, std::move(*collatorToUse), uuid); diff --git a/src/mongo/db/pipeline/aggregation_request.cpp b/src/mongo/db/pipeline/aggregation_request.cpp index 60fe025772a..a948696b526 100644 --- a/src/mongo/db/pipeline/aggregation_request.cpp +++ b/src/mongo/db/pipeline/aggregation_request.cpp @@ -205,6 +205,13 @@ StatusWith<AggregationRequest> AggregationRequest::parseFromBSON( auto bob = BSONObjBuilder{request.getLetParameters()}; bob.appendElementsUnique(elem.embeddedObject()); request._letParameters = bob.obj(); + } else if (kCollectionUUIDName == fieldName) { + auto collectionUUIDSW = UUID::parse(elem); + if (!collectionUUIDSW.isOK()) { + return collectionUUIDSW.getStatus(); + } + + request.setCollectionUUID(collectionUUIDSW.getValue()); } else if (fieldName == kUse44SortKeysName) { if (elem.type() != BSONType::Bool) { return {ErrorCodes::TypeMismatch, @@ -331,6 +338,8 @@ Document AggregationRequest::serializeToCommandObj() const { {kRuntimeConstantsName, _runtimeConstants ? Value(_runtimeConstants->toBSON()) : Value()}, {kIsMapReduceCommandName, _isMapReduceCommand ? Value(true) : Value()}, {kLetName, !_letParameters.isEmpty() ? Value(_letParameters) : Value()}, + // Only serialize collection UUID if one was specified. + {kCollectionUUIDName, _collectionUUID ? Value(*_collectionUUID) : Value()}, }; } } // namespace mongo diff --git a/src/mongo/db/pipeline/aggregation_request.h b/src/mongo/db/pipeline/aggregation_request.h index 454c246feb2..66e4d95df57 100644 --- a/src/mongo/db/pipeline/aggregation_request.h +++ b/src/mongo/db/pipeline/aggregation_request.h @@ -66,6 +66,7 @@ public: static constexpr StringData kUse44SortKeysName = "use44SortKeys"_sd; static constexpr StringData kIsMapReduceCommandName = "isMapReduceCommand"_sd; static constexpr StringData kLetName = "let"_sd; + static constexpr StringData kCollectionUUIDName = "collectionUUID"_sd; static constexpr long long kDefaultBatchSize = 101; @@ -227,6 +228,10 @@ public: return _isMapReduceCommand; } + const auto& getCollectionUUID() const { + return _collectionUUID; + } + // // Setters for optional fields. // @@ -299,6 +304,10 @@ public: _isMapReduceCommand = isMapReduce; } + void setCollectionUUID(UUID collectionUUID) { + _collectionUUID = std::move(collectionUUID); + } + private: // Required fields. const NamespaceString _nss; @@ -349,6 +358,9 @@ private: // $$NOW). boost::optional<RuntimeConstants> _runtimeConstants; + // The expected UUID of the namespace the aggregation executes on. + boost::optional<UUID> _collectionUUID; + // A document containing user-specified let parameter constants; i.e. values that do not change // once computed. BSONObj _letParameters; diff --git a/src/mongo/db/pipeline/aggregation_request_test.cpp b/src/mongo/db/pipeline/aggregation_request_test.cpp index f16b7a5891b..04a93a21ea1 100644 --- a/src/mongo/db/pipeline/aggregation_request_test.cpp +++ b/src/mongo/db/pipeline/aggregation_request_test.cpp @@ -56,12 +56,17 @@ const Document kDefaultCursorOptionDocument{ TEST(AggregationRequestTest, ShouldParseAllKnownOptions) { NamespaceString nss("a.collection"); - const BSONObj inputBson = fromjson( + BSONObj inputBson = fromjson( "{pipeline: [{$match: {a: 'abc'}}], explain: false, allowDiskUse: true, fromMongos: true, " "needsMerge: true, bypassDocumentValidation: true, collation: {locale: 'en_US'}, cursor: " "{batchSize: 10}, hint: {a: 1}, maxTimeMS: 100, readConcern: {level: 'linearizable'}, " "$queryOptions: {$readPreference: 'nearest'}, exchange: {policy: " "'roundrobin', consumers:NumberInt(2)}, isMapReduceCommand: true}"); + auto uuid = UUID::gen(); + BSONObjBuilder uuidBob; + uuid.appendToBuilder(&uuidBob, AggregationRequest::kCollectionUUIDName); + inputBson = inputBson.addField(uuidBob.obj().firstElement()); + auto request = unittest::assertGet(AggregationRequest::parseFromBSON(nss, inputBson)); ASSERT_FALSE(request.getExplain()); ASSERT_TRUE(request.shouldAllowDiskUse()); @@ -82,6 +87,7 @@ TEST(AggregationRequestTest, ShouldParseAllKnownOptions) { << "nearest")); ASSERT_TRUE(request.getExchangeSpec().is_initialized()); ASSERT_TRUE(request.getIsMapReduceCommand()); + ASSERT_EQ(*request.getCollectionUUID(), uuid); } TEST(AggregationRequestTest, ShouldParseExplicitExplainTrue) { @@ -193,6 +199,8 @@ TEST(AggregationRequestTest, ShouldSerializeOptionalValuesIfSet) { const auto letParamsObj = BSON("foo" << "bar"); request.setLetParameters(letParamsObj); + auto uuid = UUID::gen(); + request.setCollectionUUID(uuid); auto expectedSerialization = Document{{AggregationRequest::kCommandName, nss.coll()}, @@ -209,7 +217,8 @@ TEST(AggregationRequestTest, ShouldSerializeOptionalValuesIfSet) { {QueryRequest::kUnwrappedReadPrefField, readPrefObj}, {QueryRequest::cmdOptionMaxTimeMS, 10}, {AggregationRequest::kIsMapReduceCommandName, true}, - {AggregationRequest::kLetName, letParamsObj}}; + {AggregationRequest::kLetName, letParamsObj}, + {AggregationRequest::kCollectionUUIDName, uuid}}; ASSERT_DOCUMENT_EQ(request.serializeToCommandObj(), expectedSerialization); } @@ -503,6 +512,14 @@ TEST(AggregationRequestTest, ShouldRejectInvalidWriteConcern) { fromjson("{pipeline: [{$match: {a: 'abc'}}], cursor: {}, writeConcern: 'invalid'}"); ASSERT_NOT_OK(AggregationRequest::parseFromBSON(nss, inputBson).getStatus()); } + +TEST(AggregationRequestTest, ShouldRejectInvalidCollectionUUID) { + NamespaceString nss("a.collection"); + const BSONObj inputBSON = fromjson("{pipeline: [{$match: {}}], collectionUUID: 2}"); + ASSERT_EQUALS(AggregationRequest::parseFromBSON(nss, inputBSON).getStatus().code(), + ErrorCodes::InvalidUUID); +} + // // Ignore fields parsed elsewhere. // diff --git a/src/mongo/s/query/cluster_aggregate.cpp b/src/mongo/s/query/cluster_aggregate.cpp index ec2419f1aa3..56103395066 100644 --- a/src/mongo/s/query/cluster_aggregate.cpp +++ b/src/mongo/s/query/cluster_aggregate.cpp @@ -213,6 +213,10 @@ Status ClusterAggregate::runAggregate(OperationContext* opCtx, << ", " << AggregationRequest::kFromMongosName << "] cannot be set to 'true' when sent to mongos", !request.needsMerge() && !request.isFromMongos()); + uassert(4928902, + str::stream() << AggregationRequest::kCollectionUUIDName + << " is not supported on a mongos", + !request.getCollectionUUID()); const auto isSharded = [](OperationContext* opCtx, const NamespaceString& nss) { const auto resolvedNsRoutingInfo = |