list<intrusive_ptr<DocumentSource>> DocumentSourceChangeStream::createFromBson( BSONElement elem, const intrusive_ptr<ExpressionContext>& expCtx) { // TODO: Add sharding support here (SERVER-29141). uassert( 40470, "The $changeStream stage is not supported on sharded systems.", !expCtx->inRouter); uassert(40471, "Only default collation is allowed when using a $changeStream stage.", !expCtx->getCollator()); auto replCoord = repl::ReplicationCoordinator::get(expCtx->opCtx); uassert(40573, "The $changeStream stage is only supported on replica sets", replCoord); Timestamp startFrom = replCoord->getLastCommittedOpTime().getTimestamp(); intrusive_ptr<DocumentSourceCheckResumeToken> resumeStage = nullptr; auto spec = DocumentSourceChangeStreamSpec::parse(IDLParserErrorContext("$changeStream"), elem.embeddedObject()); if (auto resumeAfter = spec.getResumeAfter()) { ResumeToken token = resumeAfter.get(); startFrom = token.getTimestamp(); DocumentSourceCheckResumeTokenSpec spec; spec.setResumeToken(std::move(token)); resumeStage = DocumentSourceCheckResumeToken::create(expCtx, std::move(spec)); } const bool changeStreamIsResuming = resumeStage != nullptr; auto fullDocOption = spec.getFullDocument(); uassert(40575, str::stream() << "unrecognized value for the 'fullDocument' option to the " "$changeStream stage. Expected \"default\" or " "\"updateLookup\", got \"" << fullDocOption << "\"", fullDocOption == "updateLookup"_sd || fullDocOption == "default"_sd); const bool shouldLookupPostImage = (fullDocOption == "updateLookup"_sd); auto oplogMatch = DocumentSourceOplogMatch::create( buildMatchFilter(expCtx->ns, startFrom, changeStreamIsResuming), expCtx); auto transformation = createTransformationStage(elem.embeddedObject(), expCtx); list<intrusive_ptr<DocumentSource>> stages = {oplogMatch, transformation}; if (resumeStage) { stages.push_back(resumeStage); } auto closeCursorSource = DocumentSourceCloseCursor::create(expCtx); stages.push_back(closeCursorSource); if (shouldLookupPostImage) { stages.push_back(DocumentSourceLookupChangePostImage::create(expCtx)); } return stages; }
DocumentSourceChangeStreamTransform::DocumentSourceChangeStreamTransform( const boost::intrusive_ptr<ExpressionContext>& expCtx, const ServerGlobalParams::FeatureCompatibility::Version& fcv, BSONObj changeStreamSpec) : DocumentSource(expCtx), _changeStreamSpec(changeStreamSpec.getOwned()), _isIndependentOfAnyCollection(expCtx->ns.isCollectionlessAggregateNS()), _fcv(fcv) { _nsRegex.emplace(DocumentSourceChangeStream::getNsRegexForChangeStream(expCtx->ns)); auto spec = DocumentSourceChangeStreamSpec::parse(IDLParserErrorContext("$changeStream"), _changeStreamSpec); // If the change stream spec includes a resumeToken with a shard key, populate the document key // cache with the field paths. auto resumeAfter = spec.getResumeAfter(); auto startAfter = spec.getStartAfter(); if (resumeAfter || startAfter) { ResumeToken token = resumeAfter ? resumeAfter.get() : startAfter.get(); ResumeTokenData tokenData = token.getData(); if (!tokenData.documentKey.missing() && tokenData.uuid) { std::vector<FieldPath> docKeyFields; auto docKey = tokenData.documentKey.getDocument(); auto iter = docKey.fieldIterator(); while (iter.more()) { auto fieldPair = iter.next(); docKeyFields.push_back(fieldPair.first); } // If the document key from the resume token has more than one field, that means it // includes the shard key and thus should never change. auto isFinal = docKey.size() > 1; _documentKeyCache[tokenData.uuid.get()] = DocumentKeyCacheEntry({docKeyFields, isFinal}); } } }
list<intrusive_ptr<DocumentSource>> DocumentSourceChangeStream::createFromBson( BSONElement elem, const intrusive_ptr<ExpressionContext>& expCtx) { // TODO: Add sharding support here (SERVER-29141). uassert( 40470, "The $changeStream stage is not supported on sharded systems.", !expCtx->inMongos); uassert(40471, "Only default collation is allowed when using a $changeStream stage.", !expCtx->getCollator()); auto replCoord = repl::ReplicationCoordinator::get(expCtx->opCtx); uassert(40573, "The $changeStream stage is only supported on replica sets", replCoord && replCoord->getReplicationMode() == repl::ReplicationCoordinator::Mode::modeReplSet); Timestamp startFrom = replCoord->getMyLastAppliedOpTime().getTimestamp(); intrusive_ptr<DocumentSource> resumeStage = nullptr; auto spec = DocumentSourceChangeStreamSpec::parse(IDLParserErrorContext("$changeStream"), elem.embeddedObject()); if (auto resumeAfter = spec.getResumeAfter()) { ResumeToken token = resumeAfter.get(); auto resumeNamespace = UUIDCatalog::get(expCtx->opCtx).lookupNSSByUUID(token.getUuid()); uassert(40615, "The resume token UUID does not exist. Has the collection been dropped?", !resumeNamespace.isEmpty()); startFrom = token.getTimestamp(); if (expCtx->needsMerge) { DocumentSourceShardCheckResumabilitySpec spec; spec.setResumeToken(std::move(token)); resumeStage = DocumentSourceShardCheckResumability::create(expCtx, std::move(spec)); } else { DocumentSourceEnsureResumeTokenPresentSpec spec; spec.setResumeToken(std::move(token)); resumeStage = DocumentSourceEnsureResumeTokenPresent::create(expCtx, std::move(spec)); } } const bool changeStreamIsResuming = resumeStage != nullptr; auto fullDocOption = spec.getFullDocument(); uassert(40575, str::stream() << "unrecognized value for the 'fullDocument' option to the " "$changeStream stage. Expected \"default\" or " "\"updateLookup\", got \"" << fullDocOption << "\"", fullDocOption == "updateLookup"_sd || fullDocOption == "default"_sd); const bool shouldLookupPostImage = (fullDocOption == "updateLookup"_sd); auto oplogMatch = DocumentSourceOplogMatch::create( buildMatchFilter(expCtx->ns, startFrom, changeStreamIsResuming), expCtx); auto transformation = createTransformationStage(elem.embeddedObject(), expCtx); list<intrusive_ptr<DocumentSource>> stages = {oplogMatch, transformation}; if (resumeStage) { stages.push_back(resumeStage); } auto closeCursorSource = DocumentSourceCloseCursor::create(expCtx); stages.push_back(closeCursorSource); if (shouldLookupPostImage) { stages.push_back(DocumentSourceLookupChangePostImage::create(expCtx)); } return stages; }