diff options
author | James Wahlin <james.wahlin@10gen.com> | 2017-04-05 09:59:54 -0400 |
---|---|---|
committer | James Wahlin <james.wahlin@10gen.com> | 2017-04-29 09:21:00 -0400 |
commit | 5273c2bad7df58c44afdd677609b8656f399a906 (patch) | |
tree | 0257684f5555293d72e07179c6b058081c247019 /src/mongo | |
parent | 2e8e60bfef83ac9c7bf494b0f80977686cb1b772 (diff) | |
download | mongo-5273c2bad7df58c44afdd677609b8656f399a906.tar.gz |
SERVER-28651 Move agg var ownership to ExpressionContext
Diffstat (limited to 'src/mongo')
32 files changed, 780 insertions, 906 deletions
diff --git a/src/mongo/db/pipeline/document_source_bucket.cpp b/src/mongo/db/pipeline/document_source_bucket.cpp index 7c671ea2998..853ef0aaa42 100644 --- a/src/mongo/db/pipeline/document_source_bucket.cpp +++ b/src/mongo/db/pipeline/document_source_bucket.cpp @@ -46,7 +46,7 @@ namespace { intrusive_ptr<ExpressionConstant> getExpressionConstant( const boost::intrusive_ptr<ExpressionContext>& expCtx, BSONElement expressionElem, - VariablesParseState vps) { + const VariablesParseState& vps) { auto expr = Expression::parseOperand(expCtx, expressionElem, vps)->optimize(); return dynamic_cast<ExpressionConstant*>(expr.get()); } @@ -64,8 +64,7 @@ vector<intrusive_ptr<DocumentSource>> DocumentSourceBucket::createFromBson( BSONObjBuilder groupObjBuilder; BSONObjBuilder switchObjBuilder; - VariablesIdGenerator idGenerator; - VariablesParseState vps(&idGenerator); + VariablesParseState vps = pExpCtx->variablesParseState; vector<Value> boundaryValues; BSONElement groupByField; diff --git a/src/mongo/db/pipeline/document_source_bucket_auto.cpp b/src/mongo/db/pipeline/document_source_bucket_auto.cpp index b00feee2b68..a31e91ff845 100644 --- a/src/mongo/db/pipeline/document_source_bucket_auto.cpp +++ b/src/mongo/db/pipeline/document_source_bucket_auto.cpp @@ -118,8 +118,8 @@ Value DocumentSourceBucketAuto::extractKey(const Document& doc) { return Value(BSONNULL); } - _variables->setRoot(doc); - Value key = _groupByExpression->evaluate(_variables.get()); + pExpCtx->variables.setRoot(doc); + Value key = _groupByExpression->evaluate(); if (_granularityRounder) { uassert(40258, @@ -151,9 +151,9 @@ void DocumentSourceBucketAuto::addDocumentToBucket(const pair<Value, Document>& bucket._max = entry.first; const size_t numAccumulators = _accumulatorFactories.size(); - _variables->setRoot(entry.second); + pExpCtx->variables.setRoot(entry.second); for (size_t k = 0; k < numAccumulators; k++) { - bucket._accums[k]->process(_expressions[k]->evaluate(_variables.get()), false); + bucket._accums[k]->process(_expressions[k]->evaluate(), false); } } @@ -358,7 +358,6 @@ Value DocumentSourceBucketAuto::serialize( intrusive_ptr<DocumentSourceBucketAuto> DocumentSourceBucketAuto::create( const intrusive_ptr<ExpressionContext>& pExpCtx, const boost::intrusive_ptr<Expression>& groupByExpression, - Variables::Id numVariables, int numBuckets, std::vector<AccumulationStatement> accumulationStatements, const boost::intrusive_ptr<GranularityRounder>& granularityRounder, @@ -375,7 +374,6 @@ intrusive_ptr<DocumentSourceBucketAuto> DocumentSourceBucketAuto::create( } return new DocumentSourceBucketAuto(pExpCtx, groupByExpression, - numVariables, numBuckets, accumulationStatements, granularityRounder, @@ -385,7 +383,6 @@ intrusive_ptr<DocumentSourceBucketAuto> DocumentSourceBucketAuto::create( DocumentSourceBucketAuto::DocumentSourceBucketAuto( const intrusive_ptr<ExpressionContext>& pExpCtx, const boost::intrusive_ptr<Expression>& groupByExpression, - Variables::Id numVariables, int numBuckets, std::vector<AccumulationStatement> accumulationStatements, const boost::intrusive_ptr<GranularityRounder>& granularityRounder, @@ -393,7 +390,6 @@ DocumentSourceBucketAuto::DocumentSourceBucketAuto( : DocumentSource(pExpCtx), _nBuckets(numBuckets), _maxMemoryUsageBytes(maxMemoryUsageBytes), - _variables(stdx::make_unique<Variables>(numVariables)), _groupByExpression(groupByExpression), _granularityRounder(granularityRounder) { @@ -434,8 +430,7 @@ intrusive_ptr<DocumentSource> DocumentSourceBucketAuto::createFromBson( << typeName(elem.type()), elem.type() == BSONType::Object); - VariablesIdGenerator idGenerator; - VariablesParseState vps(&idGenerator); + VariablesParseState vps = pExpCtx->variablesParseState; vector<AccumulationStatement> accumulationStatements; boost::intrusive_ptr<Expression> groupByExpression; boost::optional<int> numBuckets; @@ -489,12 +484,8 @@ intrusive_ptr<DocumentSource> DocumentSourceBucketAuto::createFromBson( "$bucketAuto requires 'groupBy' and 'buckets' to be specified", groupByExpression && numBuckets); - return DocumentSourceBucketAuto::create(pExpCtx, - groupByExpression, - idGenerator.getIdCount(), - numBuckets.get(), - accumulationStatements, - granularityRounder); + return DocumentSourceBucketAuto::create( + pExpCtx, groupByExpression, numBuckets.get(), accumulationStatements, granularityRounder); } } // namespace mongo diff --git a/src/mongo/db/pipeline/document_source_bucket_auto.h b/src/mongo/db/pipeline/document_source_bucket_auto.h index 59fec253134..a60bf5cb752 100644 --- a/src/mongo/db/pipeline/document_source_bucket_auto.h +++ b/src/mongo/db/pipeline/document_source_bucket_auto.h @@ -69,7 +69,6 @@ public: static boost::intrusive_ptr<DocumentSourceBucketAuto> create( const boost::intrusive_ptr<ExpressionContext>& expCtx, const boost::intrusive_ptr<Expression>& groupByExpression, - Variables::Id numVariables, int numBuckets, std::vector<AccumulationStatement> accumulationStatements = {}, const boost::intrusive_ptr<GranularityRounder>& granularityRounder = nullptr, @@ -87,7 +86,6 @@ protected: private: DocumentSourceBucketAuto(const boost::intrusive_ptr<ExpressionContext>& pExpCtx, const boost::intrusive_ptr<Expression>& groupByExpression, - Variables::Id numVariables, int numBuckets, std::vector<AccumulationStatement> accumulationStatements, const boost::intrusive_ptr<GranularityRounder>& granularityRounder, @@ -154,7 +152,6 @@ private: bool _populated = false; std::vector<Bucket> _buckets; std::vector<Bucket>::iterator _bucketsIterator; - std::unique_ptr<Variables> _variables; boost::intrusive_ptr<Expression> _groupByExpression; boost::intrusive_ptr<GranularityRounder> _granularityRounder; long long _nDocuments = 0; diff --git a/src/mongo/db/pipeline/document_source_bucket_auto_test.cpp b/src/mongo/db/pipeline/document_source_bucket_auto_test.cpp index debd2eede82..7afa30e1c8b 100644 --- a/src/mongo/db/pipeline/document_source_bucket_auto_test.cpp +++ b/src/mongo/db/pipeline/document_source_bucket_auto_test.cpp @@ -353,18 +353,12 @@ TEST_F(BucketAutoTests, ShouldBeAbleToCorrectlySpillToDisk) { expCtx->extSortAllowed = true; const size_t maxMemoryUsageBytes = 1000; - VariablesIdGenerator idGen; - VariablesParseState vps(&idGen); + VariablesParseState vps = expCtx->variablesParseState; auto groupByExpression = ExpressionFieldPath::parse(expCtx, "$a", vps); const int numBuckets = 2; - auto bucketAutoStage = DocumentSourceBucketAuto::create(expCtx, - groupByExpression, - idGen.getIdCount(), - numBuckets, - {}, - nullptr, - maxMemoryUsageBytes); + auto bucketAutoStage = DocumentSourceBucketAuto::create( + expCtx, groupByExpression, numBuckets, {}, nullptr, maxMemoryUsageBytes); string largeStr(maxMemoryUsageBytes, 'x'); auto mock = DocumentSourceMock::create({Document{{"a", 0}, {"largeStr", largeStr}}, @@ -395,18 +389,12 @@ TEST_F(BucketAutoTests, ShouldBeAbleToPauseLoadingWhileSpilled) { expCtx->extSortAllowed = true; const size_t maxMemoryUsageBytes = 1000; - VariablesIdGenerator idGen; - VariablesParseState vps(&idGen); + VariablesParseState vps = expCtx->variablesParseState; auto groupByExpression = ExpressionFieldPath::parse(expCtx, "$a", vps); const int numBuckets = 2; - auto bucketAutoStage = DocumentSourceBucketAuto::create(expCtx, - groupByExpression, - idGen.getIdCount(), - numBuckets, - {}, - nullptr, - maxMemoryUsageBytes); + auto bucketAutoStage = DocumentSourceBucketAuto::create( + expCtx, groupByExpression, numBuckets, {}, nullptr, maxMemoryUsageBytes); auto sort = DocumentSourceSort::create(expCtx, BSON("_id" << -1), -1, maxMemoryUsageBytes); string largeStr(maxMemoryUsageBytes, 'x'); @@ -557,7 +545,7 @@ TEST_F(BucketAutoTests, FailsWithInvalidNumberOfBuckets) { const int numBuckets = 0; ASSERT_THROWS_CODE( DocumentSourceBucketAuto::create( - getExpCtx(), ExpressionConstant::create(getExpCtx(), Value(0)), 0, numBuckets), + getExpCtx(), ExpressionConstant::create(getExpCtx(), Value(0)), numBuckets), UserException, 40243); } @@ -641,18 +629,12 @@ TEST_F(BucketAutoTests, FailsWithInvalidOutputFieldName) { void assertCannotSpillToDisk(const boost::intrusive_ptr<ExpressionContext>& expCtx) { const size_t maxMemoryUsageBytes = 1000; - VariablesIdGenerator idGen; - VariablesParseState vps(&idGen); + VariablesParseState vps = expCtx->variablesParseState; auto groupByExpression = ExpressionFieldPath::parse(expCtx, "$a", vps); const int numBuckets = 2; - auto bucketAutoStage = DocumentSourceBucketAuto::create(expCtx, - groupByExpression, - idGen.getIdCount(), - numBuckets, - {}, - nullptr, - maxMemoryUsageBytes); + auto bucketAutoStage = DocumentSourceBucketAuto::create( + expCtx, groupByExpression, numBuckets, {}, nullptr, maxMemoryUsageBytes); string largeStr(maxMemoryUsageBytes, 'x'); auto mock = DocumentSourceMock::create( @@ -683,18 +665,12 @@ TEST_F(BucketAutoTests, ShouldCorrectlyTrackMemoryUsageBetweenPauses) { expCtx->extSortAllowed = false; const size_t maxMemoryUsageBytes = 1000; - VariablesIdGenerator idGen; - VariablesParseState vps(&idGen); + VariablesParseState vps = expCtx->variablesParseState; auto groupByExpression = ExpressionFieldPath::parse(expCtx, "$a", vps); const int numBuckets = 2; - auto bucketAutoStage = DocumentSourceBucketAuto::create(expCtx, - groupByExpression, - idGen.getIdCount(), - numBuckets, - {}, - nullptr, - maxMemoryUsageBytes); + auto bucketAutoStage = DocumentSourceBucketAuto::create( + expCtx, groupByExpression, numBuckets, {}, nullptr, maxMemoryUsageBytes); string largeStr(maxMemoryUsageBytes / 2, 'x'); auto mock = DocumentSourceMock::create({Document{{"a", 0}, {"largeStr", largeStr}}, diff --git a/src/mongo/db/pipeline/document_source_graph_lookup.cpp b/src/mongo/db/pipeline/document_source_graph_lookup.cpp index 7cc78ba8e95..d0c338c0855 100644 --- a/src/mongo/db/pipeline/document_source_graph_lookup.cpp +++ b/src/mongo/db/pipeline/document_source_graph_lookup.cpp @@ -325,9 +325,10 @@ void DocumentSourceGraphLookUp::performSearch() { // Make sure _input is set before calling performSearch(). invariant(_input); - _variables->setRoot(*_input); - Value startingValue = _startWith->evaluateInternal(_variables.get()); - _variables->clearRoot(); + auto& variables = pExpCtx->variables; + variables.setRoot(*_input); + Value startingValue = _startWith->evaluateInternal(); + variables.clearRoot(); // If _startWith evaluates to an array, treat each value as a separate starting point. if (startingValue.isArray()) { @@ -494,7 +495,6 @@ intrusive_ptr<DocumentSourceGraphLookUp> DocumentSourceGraphLookUp::create( depthField, maxDepth, unwindSrc)); - source->_variables.reset(new Variables()); return source; } @@ -509,8 +509,7 @@ intrusive_ptr<DocumentSource> DocumentSourceGraphLookUp::createFromBson( boost::optional<long long> maxDepth; boost::optional<BSONObj> additionalFilter; - VariablesIdGenerator idGenerator; - VariablesParseState vps(&idGenerator); + VariablesParseState vps = expCtx->variablesParseState; for (auto&& argument : elem.Obj()) { const auto argName = argument.fieldNameStringData(); @@ -607,8 +606,6 @@ intrusive_ptr<DocumentSource> DocumentSourceGraphLookUp::createFromBson( maxDepth, boost::none)); - newSource->_variables.reset(new Variables(idGenerator.getIdCount())); - return std::move(newSource); } } // namespace mongo diff --git a/src/mongo/db/pipeline/document_source_graph_lookup.h b/src/mongo/db/pipeline/document_source_graph_lookup.h index e196b4578ca..86b27c191cc 100644 --- a/src/mongo/db/pipeline/document_source_graph_lookup.h +++ b/src/mongo/db/pipeline/document_source_graph_lookup.h @@ -204,9 +204,6 @@ private: // need it for multiple "getNext()" calls. boost::optional<Document> _input; - // The variables that are in scope to be used by the '_startWith' expression. - std::unique_ptr<Variables> _variables; - // Keep track of a $unwind that was absorbed into this stage. boost::optional<boost::intrusive_ptr<DocumentSourceUnwind>> _unwind; diff --git a/src/mongo/db/pipeline/document_source_group.cpp b/src/mongo/db/pipeline/document_source_group.cpp index 2d625dc2f1f..c252d61e102 100644 --- a/src/mongo/db/pipeline/document_source_group.cpp +++ b/src/mongo/db/pipeline/document_source_group.cpp @@ -128,25 +128,26 @@ DocumentSource::GetNextResult DocumentSourceGroup::getNextStandard() { DocumentSource::GetNextResult DocumentSourceGroup::getNextStreaming() { // Streaming optimization is active. + auto& variables = pExpCtx->variables; if (!_firstDocOfNextGroup) { auto nextInput = pSource->getNext(); if (!nextInput.isAdvanced()) { return nextInput; } _firstDocOfNextGroup = nextInput.releaseDocument(); + variables.setRoot(*_firstDocOfNextGroup); } Value id; do { // Add to the current accumulator(s). for (size_t i = 0; i < _currentAccumulators.size(); i++) { - _currentAccumulators[i]->process(vpExpression[i]->evaluate(_variables.get()), - _doingMerge); + _currentAccumulators[i]->process(vpExpression[i]->evaluate(), _doingMerge); } // Release our references to the previous input document before asking for the next. This // makes operations like $unwind more efficient. - _variables->clearRoot(); + variables.clearRoot(); // Retrieve the next document. auto nextInput = pSource->getNext(); @@ -156,11 +157,11 @@ DocumentSource::GetNextResult DocumentSourceGroup::getNextStreaming() { _firstDocOfNextGroup = nextInput.releaseDocument(); - _variables->setRoot(*_firstDocOfNextGroup); + variables.setRoot(*_firstDocOfNextGroup); // Compute the id. If it does not match _currentId, we will exit the loop, leaving // _firstDocOfNextGroup set for the next time getNext() is called. - id = computeId(_variables.get()); + id = computeId(); } while (pExpCtx->getValueComparator().evaluate(_currentId == id)); Document out = makeDocument(_currentId, _currentAccumulators, pExpCtx->inShard); @@ -251,7 +252,6 @@ intrusive_ptr<DocumentSourceGroup> DocumentSourceGroup::create( const intrusive_ptr<ExpressionContext>& pExpCtx, const boost::intrusive_ptr<Expression>& groupByExpression, std::vector<AccumulationStatement> accumulationStatements, - Variables::Id numVariables, size_t maxMemoryUsageBytes) { intrusive_ptr<DocumentSourceGroup> groupStage( new DocumentSourceGroup(pExpCtx, maxMemoryUsageBytes)); @@ -259,7 +259,7 @@ intrusive_ptr<DocumentSourceGroup> DocumentSourceGroup::create( for (auto&& statement : accumulationStatements) { groupStage->addAccumulator(statement); } - groupStage->_variables = stdx::make_unique<Variables>(numVariables); + return groupStage; } @@ -339,8 +339,7 @@ intrusive_ptr<DocumentSource> DocumentSourceGroup::createFromBson( BSONObj groupObj(elem.Obj()); BSONObjIterator groupIterator(groupObj); - VariablesIdGenerator idGenerator; - VariablesParseState vps(&idGenerator); + VariablesParseState vps = pExpCtx->variablesParseState; while (groupIterator.more()) { BSONElement groupField(groupIterator.next()); const char* pFieldName = groupField.fieldName(); @@ -362,9 +361,6 @@ intrusive_ptr<DocumentSource> DocumentSourceGroup::createFromBson( } uassert(15955, "a group specification must include an _id", !pGroup->_idExpressions.empty()); - - pGroup->_variables.reset(new Variables(idGenerator.getIdCount())); - return pGroup; } @@ -483,10 +479,10 @@ DocumentSource::GetNextResult DocumentSourceGroup::initialize() { return firstInput; } _firstDocOfNextGroup = firstInput.releaseDocument(); - _variables->setRoot(*_firstDocOfNextGroup); + pExpCtx->variables.setRoot(*_firstDocOfNextGroup); // Compute the _id value. - _currentId = computeId(_variables.get()); + _currentId = computeId(); _initialized = true; return DocumentSource::GetNextResult::makeEOF(); } @@ -505,9 +501,10 @@ DocumentSource::GetNextResult DocumentSourceGroup::initialize() { _memoryUsageBytes = 0; } - _variables->setRoot(input.releaseDocument()); + auto& variables = pExpCtx->variables; + variables.setRoot(input.releaseDocument()); - Value id = computeId(_variables.get()); + Value id = computeId(); // Look for the _id value in the map. If it's not there, add a new entry with a blank // accumulator. This is done in a somewhat odd way in order to avoid hashing 'id' and @@ -533,13 +530,14 @@ DocumentSource::GetNextResult DocumentSourceGroup::initialize() { /* tickle all the accumulators for the group we found */ dassert(numAccumulators == group.size()); + for (size_t i = 0; i < numAccumulators; i++) { - group[i]->process(vpExpression[i]->evaluate(_variables.get()), _doingMerge); + group[i]->process(vpExpression[i]->evaluate(), _doingMerge); _memoryUsageBytes += group[i]->memUsageForSorter(); } // We are done with the ROOT document so release it. - _variables->clearRoot(); + variables.clearRoot(); if (kDebugBuild && !storageGlobalParams.readOnly) { // In debug mode, spill every time we have a duplicate id to stress merge logic. @@ -784,10 +782,10 @@ BSONObjSet DocumentSourceGroup::getOutputSorts() { } -Value DocumentSourceGroup::computeId(Variables* vars) { +Value DocumentSourceGroup::computeId() { // If only one expression, return result directly if (_idExpressions.size() == 1) { - Value retValue = _idExpressions[0]->evaluate(vars); + Value retValue = _idExpressions[0]->evaluate(); return retValue.missing() ? Value(BSONNULL) : std::move(retValue); } @@ -795,7 +793,7 @@ Value DocumentSourceGroup::computeId(Variables* vars) { vector<Value> vals; vals.reserve(_idExpressions.size()); for (size_t i = 0; i < _idExpressions.size(); i++) { - vals.push_back(_idExpressions[i]->evaluate(vars)); + vals.push_back(_idExpressions[i]->evaluate()); } return Value(std::move(vals)); } @@ -850,8 +848,7 @@ intrusive_ptr<DocumentSource> DocumentSourceGroup::getMergeSource() { intrusive_ptr<DocumentSourceGroup> pMerger(new DocumentSourceGroup(pExpCtx)); pMerger->setDoingMerge(true); - VariablesIdGenerator idGenerator; - VariablesParseState vps(&idGenerator); + VariablesParseState vps = pExpCtx->variablesParseState; /* the merger will use the same grouping key */ pMerger->setIdExpression(ExpressionFieldPath::parse(pExpCtx, "$$ROOT._id", vps)); @@ -871,8 +868,6 @@ intrusive_ptr<DocumentSource> DocumentSourceGroup::getMergeSource() { ExpressionFieldPath::parse(pExpCtx, "$$ROOT." + vFieldName[i], vps)}); } - pMerger->_variables.reset(new Variables(idGenerator.getIdCount())); - return pMerger; } } diff --git a/src/mongo/db/pipeline/document_source_group.h b/src/mongo/db/pipeline/document_source_group.h index 643e291415b..474952a3203 100644 --- a/src/mongo/db/pipeline/document_source_group.h +++ b/src/mongo/db/pipeline/document_source_group.h @@ -60,7 +60,6 @@ public: const boost::intrusive_ptr<ExpressionContext>& expCtx, const boost::intrusive_ptr<Expression>& groupByExpression, std::vector<AccumulationStatement> accumulationStatements, - Variables::Id numVariables, size_t maxMemoryUsageBytes = kDefaultMaxMemoryUsageBytes); /** @@ -141,7 +140,7 @@ private: /** * Computes the internal representation of the group key. */ - Value computeId(Variables* vars); + Value computeId(); /** * Converts the internal representation of the group key to the _id shape specified by the @@ -162,7 +161,6 @@ private: bool _doingMerge; size_t _memoryUsageBytes = 0; size_t _maxMemoryUsageBytes; - std::unique_ptr<Variables> _variables; std::vector<std::string> _idFieldNames; // used when id is a document std::vector<boost::intrusive_ptr<Expression>> _idExpressions; diff --git a/src/mongo/db/pipeline/document_source_group_test.cpp b/src/mongo/db/pipeline/document_source_group_test.cpp index 25b10ddcb5b..53234769feb 100644 --- a/src/mongo/db/pipeline/document_source_group_test.cpp +++ b/src/mongo/db/pipeline/document_source_group_test.cpp @@ -76,7 +76,7 @@ TEST_F(DocumentSourceGroupTest, ShouldBeAbleToPauseLoading) { AccumulationStatement::getFactory("$sum"), ExpressionConstant::create(expCtx, Value(1))}; auto group = DocumentSourceGroup::create( - expCtx, ExpressionConstant::create(expCtx, Value(BSONNULL)), {countStatement}, 0); + expCtx, ExpressionConstant::create(expCtx, Value(BSONNULL)), {countStatement}); auto mock = DocumentSourceMock::create({DocumentSource::GetNextResult::makePauseExecution(), Document(), DocumentSource::GetNextResult::makePauseExecution(), @@ -106,14 +106,13 @@ TEST_F(DocumentSourceGroupTest, ShouldBeAbleToPauseLoadingWhileSpilled) { expCtx->extSortAllowed = true; const size_t maxMemoryUsageBytes = 1000; - VariablesIdGenerator idGen; - VariablesParseState vps(&idGen); + VariablesParseState vps = expCtx->variablesParseState; AccumulationStatement pushStatement{"spaceHog", AccumulationStatement::getFactory("$push"), ExpressionFieldPath::parse(expCtx, "$largeStr", vps)}; auto groupByExpression = ExpressionFieldPath::parse(expCtx, "$_id", vps); auto group = DocumentSourceGroup::create( - expCtx, groupByExpression, {pushStatement}, idGen.getIdCount(), maxMemoryUsageBytes); + expCtx, groupByExpression, {pushStatement}, maxMemoryUsageBytes); string largeStr(maxMemoryUsageBytes, 'x'); auto mock = DocumentSourceMock::create({Document{{"_id", 0}, {"largeStr", largeStr}}, @@ -146,14 +145,13 @@ TEST_F(DocumentSourceGroupTest, ShouldErrorIfNotAllowedToSpillToDiskAndResultSet expCtx->inRouter = true; // Disallow external sort. // This is the only way to do this in a debug build. - VariablesIdGenerator idGen; - VariablesParseState vps(&idGen); + VariablesParseState vps = expCtx->variablesParseState; AccumulationStatement pushStatement{"spaceHog", AccumulationStatement::getFactory("$push"), ExpressionFieldPath::parse(expCtx, "$largeStr", vps)}; auto groupByExpression = ExpressionFieldPath::parse(expCtx, "$_id", vps); auto group = DocumentSourceGroup::create( - expCtx, groupByExpression, {pushStatement}, idGen.getIdCount(), maxMemoryUsageBytes); + expCtx, groupByExpression, {pushStatement}, maxMemoryUsageBytes); string largeStr(maxMemoryUsageBytes, 'x'); auto mock = DocumentSourceMock::create({Document{{"_id", 0}, {"largeStr", largeStr}}, @@ -169,14 +167,13 @@ TEST_F(DocumentSourceGroupTest, ShouldCorrectlyTrackMemoryUsageBetweenPauses) { expCtx->inRouter = true; // Disallow external sort. // This is the only way to do this in a debug build. - VariablesIdGenerator idGen; - VariablesParseState vps(&idGen); + VariablesParseState vps = expCtx->variablesParseState; AccumulationStatement pushStatement{"spaceHog", AccumulationStatement::getFactory("$push"), ExpressionFieldPath::parse(expCtx, "$largeStr", vps)}; auto groupByExpression = ExpressionFieldPath::parse(expCtx, "$_id", vps); auto group = DocumentSourceGroup::create( - expCtx, groupByExpression, {pushStatement}, idGen.getIdCount(), maxMemoryUsageBytes); + expCtx, groupByExpression, {pushStatement}, maxMemoryUsageBytes); string largeStr(maxMemoryUsageBytes / 2, 'x'); auto mock = DocumentSourceMock::create({Document{{"_id", 0}, {"largeStr", largeStr}}, diff --git a/src/mongo/db/pipeline/document_source_redact.cpp b/src/mongo/db/pipeline/document_source_redact.cpp index ae79729f2e0..738a55cc056 100644 --- a/src/mongo/db/pipeline/document_source_redact.cpp +++ b/src/mongo/db/pipeline/document_source_redact.cpp @@ -63,8 +63,9 @@ static const Value keepVal = Value("keep"_sd); DocumentSource::GetNextResult DocumentSourceRedact::getNext() { auto nextInput = pSource->getNext(); for (; nextInput.isAdvanced(); nextInput = pSource->getNext()) { - _variables->setRoot(nextInput.getDocument()); - _variables->setValue(_currentId, Value(nextInput.releaseDocument())); + auto& variables = pExpCtx->variables; + variables.setRoot(nextInput.getDocument()); + variables.setValue(_currentId, Value(nextInput.releaseDocument())); if (boost::optional<Document> result = redactObject()) { return std::move(*result); } @@ -99,7 +100,7 @@ Pipeline::SourceContainer::iterator DocumentSourceRedact::doOptimizeAt( Value DocumentSourceRedact::redactValue(const Value& in) { const BSONType valueType = in.getType(); if (valueType == Object) { - _variables->setValue(_currentId, in); + pExpCtx->variables.setValue(_currentId, in); const boost::optional<Document> result = redactObject(); if (result) { return Value(*result); @@ -127,22 +128,23 @@ Value DocumentSourceRedact::redactValue(const Value& in) { } boost::optional<Document> DocumentSourceRedact::redactObject() { - const Value expressionResult = _expression->evaluate(_variables.get()); + auto& variables = pExpCtx->variables; + const Value expressionResult = _expression->evaluate(); ValueComparator simpleValueCmp; if (simpleValueCmp.evaluate(expressionResult == keepVal)) { - return _variables->getDocument(_currentId); + return variables.getDocument(_currentId); } else if (simpleValueCmp.evaluate(expressionResult == pruneVal)) { return boost::optional<Document>(); } else if (simpleValueCmp.evaluate(expressionResult == descendVal)) { - const Document in = _variables->getDocument(_currentId); + const Document in = variables.getDocument(_currentId); MutableDocument out; out.copyMetaDataFrom(in); FieldIterator fields(in); while (fields.more()) { const Document::FieldPair field(fields.next()); - // This changes CURRENT so don't read from _variables after this + // This changes CURRENT so don't read from variables after this const Value val = redactValue(field.second); if (!val.missing()) { out.addField(field.first, val); @@ -169,8 +171,7 @@ Value DocumentSourceRedact::serialize(boost::optional<ExplainOptions::Verbosity> intrusive_ptr<DocumentSource> DocumentSourceRedact::createFromBson( BSONElement elem, const intrusive_ptr<ExpressionContext>& expCtx) { - VariablesIdGenerator idGenerator; - VariablesParseState vps(&idGenerator); + VariablesParseState vps = expCtx->variablesParseState; Variables::Id currentId = vps.defineVariable("CURRENT"); // will differ from ROOT Variables::Id decendId = vps.defineVariable("DESCEND"); Variables::Id pruneId = vps.defineVariable("PRUNE"); @@ -181,10 +182,10 @@ intrusive_ptr<DocumentSource> DocumentSourceRedact::createFromBson( // TODO figure out how much of this belongs in constructor and how much here. // Set up variables. Never need to reset DESCEND, PRUNE, or KEEP. source->_currentId = currentId; - source->_variables.reset(new Variables(idGenerator.getIdCount())); - source->_variables->setValue(decendId, descendVal); - source->_variables->setValue(pruneId, pruneVal); - source->_variables->setValue(keepId, keepVal); + auto& variables = expCtx->variables; + variables.setValue(decendId, descendVal); + variables.setValue(pruneId, pruneVal); + variables.setValue(keepId, keepVal); return source; diff --git a/src/mongo/db/pipeline/document_source_redact.h b/src/mongo/db/pipeline/document_source_redact.h index 8177de12eeb..b894ec9e3f2 100644 --- a/src/mongo/db/pipeline/document_source_redact.h +++ b/src/mongo/db/pipeline/document_source_redact.h @@ -56,12 +56,11 @@ private: DocumentSourceRedact(const boost::intrusive_ptr<ExpressionContext>& expCtx, const boost::intrusive_ptr<Expression>& previsit); - // These both work over _variables + // These both work over pExpCtx->variables. boost::optional<Document> redactObject(); // redacts CURRENT Value redactValue(const Value& in); Variables::Id _currentId; - std::unique_ptr<Variables> _variables; boost::intrusive_ptr<Expression> _expression; }; diff --git a/src/mongo/db/pipeline/document_source_replace_root.cpp b/src/mongo/db/pipeline/document_source_replace_root.cpp index 8f6782dc47e..d089e67d097 100644 --- a/src/mongo/db/pipeline/document_source_replace_root.cpp +++ b/src/mongo/db/pipeline/document_source_replace_root.cpp @@ -49,12 +49,13 @@ class ReplaceRootTransformation final : public DocumentSourceSingleDocumentTransformation::TransformerInterface { public: - ReplaceRootTransformation() {} + ReplaceRootTransformation(const boost::intrusive_ptr<ExpressionContext>& expCtx) + : _expCtx(expCtx) {} Document applyTransformation(Document input) final { // Extract subdocument in the form of a Value. - _variables->setRoot(input); - Value newRoot = _newRoot->evaluate(_variables.get()); + _expCtx->variables.setRoot(input); + Value newRoot = _newRoot->evaluate(); // The newRoot expression, if it exists, must evaluate to an object. uassert(40228, @@ -104,7 +105,7 @@ public: // Create the pointer, parse the stage, and return. std::unique_ptr<ReplaceRootTransformation> parsedReplaceRoot = - stdx::make_unique<ReplaceRootTransformation>(); + stdx::make_unique<ReplaceRootTransformation>(expCtx); parsedReplaceRoot->parse(expCtx, spec); return parsedReplaceRoot; } @@ -112,8 +113,7 @@ public: // Check for valid replaceRoot options, and populate internal state variables. void parse(const boost::intrusive_ptr<ExpressionContext>& expCtx, const BSONElement& spec) { // We need a VariablesParseState in order to parse the 'newRoot' expression. - VariablesIdGenerator idGenerator; - VariablesParseState vps(&idGenerator); + VariablesParseState vps = expCtx->variablesParseState; // Get the options from this stage. Currently the only option is newRoot. for (auto&& argument : spec.Obj()) { @@ -131,12 +131,11 @@ public: // Check that there was a new root specified. uassert(40231, "no newRoot specified for the $replaceRoot stage", _newRoot); - _variables = stdx::make_unique<Variables>(idGenerator.getIdCount()); } private: - std::unique_ptr<Variables> _variables; boost::intrusive_ptr<Expression> _newRoot; + const boost::intrusive_ptr<ExpressionContext> _expCtx; }; REGISTER_DOCUMENT_SOURCE(replaceRoot, diff --git a/src/mongo/db/pipeline/document_source_sort.cpp b/src/mongo/db/pipeline/document_source_sort.cpp index b785c89b347..e757edde217 100644 --- a/src/mongo/db/pipeline/document_source_sort.cpp +++ b/src/mongo/db/pipeline/document_source_sort.cpp @@ -109,8 +109,7 @@ long long DocumentSourceSort::getLimit() const { } void DocumentSourceSort::addKey(StringData fieldPath, bool ascending) { - VariablesIdGenerator idGenerator; - VariablesParseState vps(&idGenerator); + VariablesParseState vps = pExpCtx->variablesParseState; vSortKey.push_back(ExpressionFieldPath::parse(pExpCtx, "$$ROOT." + fieldPath.toString(), vps)); vAscending.push_back(ascending); } @@ -192,8 +191,7 @@ intrusive_ptr<DocumentSourceSort> DocumentSourceSort::create( "$meta is the only expression supported by $sort right now", metaDoc.firstElement().fieldNameStringData() == "$meta"); - VariablesIdGenerator idGen; - VariablesParseState vps(&idGen); + VariablesParseState vps = pExpCtx->variablesParseState; pSort->vSortKey.push_back(ExpressionMeta::parse(pExpCtx, metaDoc.firstElement(), vps)); // If sorting by textScore, sort highest scores first. If sorting by randVal, order @@ -308,15 +306,15 @@ void DocumentSourceSort::populateFromCursors(const vector<DBClientCursor*>& curs } Value DocumentSourceSort::extractKey(const Document& d) const { - Variables vars(0, d); + pExpCtx->variables.setRoot(d); if (vSortKey.size() == 1) { - return vSortKey[0]->evaluate(&vars); + return vSortKey[0]->evaluate(); } vector<Value> keys; keys.reserve(vSortKey.size()); for (size_t i = 0; i < vSortKey.size(); i++) { - keys.push_back(vSortKey[i]->evaluate(&vars)); + keys.push_back(vSortKey[i]->evaluate()); } return Value(std::move(keys)); } diff --git a/src/mongo/db/pipeline/expression.cpp b/src/mongo/db/pipeline/expression.cpp index 9a5b6b7d81e..17156acae47 100644 --- a/src/mongo/db/pipeline/expression.cpp +++ b/src/mongo/db/pipeline/expression.cpp @@ -231,7 +231,7 @@ const char* ExpressionAbs::getOpName() const { /* ------------------------- ExpressionAdd ----------------------------- */ -Value ExpressionAdd::evaluateInternal(Variables* vars) const { +Value ExpressionAdd::evaluateInternal() const { // We'll try to return the narrowest possible result value while avoiding overflow, loss // of precision due to intermediate rounding or implicit use of decimal types. To do that, // compute a compensated sum for non-decimal values and a separate decimal sum for decimal @@ -243,7 +243,7 @@ Value ExpressionAdd::evaluateInternal(Variables* vars) const { const size_t n = vpOperand.size(); for (size_t i = 0; i < n; ++i) { - Value val = vpOperand[i]->evaluateInternal(vars); + Value val = vpOperand[i]->evaluateInternal(); switch (val.getType()) { case NumberDecimal: @@ -313,8 +313,8 @@ const char* ExpressionAdd::getOpName() const { /* ------------------------- ExpressionAllElementsTrue -------------------------- */ -Value ExpressionAllElementsTrue::evaluateInternal(Variables* vars) const { - const Value arr = vpOperand[0]->evaluateInternal(vars); +Value ExpressionAllElementsTrue::evaluateInternal() const { + const Value arr = vpOperand[0]->evaluateInternal(); uassert(17040, str::stream() << getOpName() << "'s argument must be an array, but is " << typeName(arr.getType()), @@ -391,10 +391,10 @@ intrusive_ptr<Expression> ExpressionAnd::optimize() { return pE; } -Value ExpressionAnd::evaluateInternal(Variables* vars) const { +Value ExpressionAnd::evaluateInternal() const { const size_t n = vpOperand.size(); for (size_t i = 0; i < n; ++i) { - Value pValue(vpOperand[i]->evaluateInternal(vars)); + Value pValue(vpOperand[i]->evaluateInternal()); if (!pValue.coerceToBool()) return Value(false); } @@ -409,8 +409,8 @@ const char* ExpressionAnd::getOpName() const { /* ------------------------- ExpressionAnyElementTrue -------------------------- */ -Value ExpressionAnyElementTrue::evaluateInternal(Variables* vars) const { - const Value arr = vpOperand[0]->evaluateInternal(vars); +Value ExpressionAnyElementTrue::evaluateInternal() const { + const Value arr = vpOperand[0]->evaluateInternal(); uassert(17041, str::stream() << getOpName() << "'s argument must be an array, but is " << typeName(arr.getType()), @@ -431,11 +431,11 @@ const char* ExpressionAnyElementTrue::getOpName() const { /* ---------------------- ExpressionArray --------------------------- */ -Value ExpressionArray::evaluateInternal(Variables* vars) const { +Value ExpressionArray::evaluateInternal() const { vector<Value> values; values.reserve(vpOperand.size()); for (auto&& expr : vpOperand) { - Value elemVal = expr->evaluateInternal(vars); + Value elemVal = expr->evaluateInternal(); values.push_back(elemVal.missing() ? Value(BSONNULL) : std::move(elemVal)); } return Value(std::move(values)); @@ -457,9 +457,9 @@ const char* ExpressionArray::getOpName() const { /* ------------------------- ExpressionArrayElemAt -------------------------- */ -Value ExpressionArrayElemAt::evaluateInternal(Variables* vars) const { - const Value array = vpOperand[0]->evaluateInternal(vars); - const Value indexArg = vpOperand[1]->evaluateInternal(vars); +Value ExpressionArrayElemAt::evaluateInternal() const { + const Value array = vpOperand[0]->evaluateInternal(); + const Value indexArg = vpOperand[1]->evaluateInternal(); if (array.nullish() || indexArg.nullish()) { return Value(BSONNULL); @@ -499,8 +499,8 @@ const char* ExpressionArrayElemAt::getOpName() const { /* ------------------------- ExpressionObjectToArray -------------------------- */ -Value ExpressionObjectToArray::evaluateInternal(Variables* vars) const { - const Value targetVal = vpOperand[0]->evaluateInternal(vars); +Value ExpressionObjectToArray::evaluateInternal() const { + const Value targetVal = vpOperand[0]->evaluateInternal(); if (targetVal.nullish()) { return Value(BSONNULL); @@ -531,8 +531,8 @@ const char* ExpressionObjectToArray::getOpName() const { } /* ------------------------- ExpressionArrayToObject -------------------------- */ -Value ExpressionArrayToObject::evaluateInternal(Variables* vars) const { - const Value input = vpOperand[0]->evaluateInternal(vars); +Value ExpressionArrayToObject::evaluateInternal() const { + const Value input = vpOperand[0]->evaluateInternal(); if (input.nullish()) { return Value(BSONNULL); } @@ -680,8 +680,8 @@ void ExpressionCoerceToBool::addDependencies(DepsTracker* deps) const { pExpression->addDependencies(deps); } -Value ExpressionCoerceToBool::evaluateInternal(Variables* vars) const { - Value pResult(pExpression->evaluateInternal(vars)); +Value ExpressionCoerceToBool::evaluateInternal() const { + Value pResult(pExpression->evaluateInternal()); bool b = pResult.coerceToBool(); if (b) return Value(true); @@ -782,9 +782,9 @@ static const CmpLookup cmpLookup[7] = { }; } -Value ExpressionCompare::evaluateInternal(Variables* vars) const { - Value pLeft(vpOperand[0]->evaluateInternal(vars)); - Value pRight(vpOperand[1]->evaluateInternal(vars)); +Value ExpressionCompare::evaluateInternal() const { + Value pLeft(vpOperand[0]->evaluateInternal()); + Value pRight(vpOperand[1]->evaluateInternal()); int cmp = getExpressionContext()->getValueComparator().compare(pLeft, pRight); @@ -810,12 +810,12 @@ const char* ExpressionCompare::getOpName() const { /* ------------------------- ExpressionConcat ----------------------------- */ -Value ExpressionConcat::evaluateInternal(Variables* vars) const { +Value ExpressionConcat::evaluateInternal() const { const size_t n = vpOperand.size(); StringBuilder result; for (size_t i = 0; i < n; ++i) { - Value val = vpOperand[i]->evaluateInternal(vars); + Value val = vpOperand[i]->evaluateInternal(); if (val.nullish()) return Value(BSONNULL); @@ -836,12 +836,12 @@ const char* ExpressionConcat::getOpName() const { /* ------------------------- ExpressionConcatArrays ----------------------------- */ -Value ExpressionConcatArrays::evaluateInternal(Variables* vars) const { +Value ExpressionConcatArrays::evaluateInternal() const { const size_t n = vpOperand.size(); vector<Value> values; for (size_t i = 0; i < n; ++i) { - Value val = vpOperand[i]->evaluateInternal(vars); + Value val = vpOperand[i]->evaluateInternal(); if (val.nullish()) { return Value(BSONNULL); } @@ -864,10 +864,10 @@ const char* ExpressionConcatArrays::getOpName() const { /* ----------------------- ExpressionCond ------------------------------ */ -Value ExpressionCond::evaluateInternal(Variables* vars) const { - Value pCond(vpOperand[0]->evaluateInternal(vars)); +Value ExpressionCond::evaluateInternal() const { + Value pCond(vpOperand[0]->evaluateInternal()); int idx = pCond.coerceToBool() ? 1 : 2; - return vpOperand[idx]->evaluateInternal(vars); + return vpOperand[idx]->evaluateInternal(); } intrusive_ptr<Expression> ExpressionCond::parse( @@ -938,7 +938,7 @@ void ExpressionConstant::addDependencies(DepsTracker* deps) const { /* nothing to do */ } -Value ExpressionConstant::evaluateInternal(Variables* vars) const { +Value ExpressionConstant::evaluateInternal() const { return pValue; } @@ -1008,8 +1008,8 @@ Value ExpressionDateToString::serialize(bool explain) const { DOC("$dateToString" << DOC("format" << _format << "date" << _date->serialize(explain)))); } -Value ExpressionDateToString::evaluateInternal(Variables* vars) const { - const Value date = _date->evaluateInternal(vars); +Value ExpressionDateToString::evaluateInternal() const { + const Value date = _date->evaluateInternal(); if (date.nullish()) { return Value(BSONNULL); @@ -1158,8 +1158,8 @@ void ExpressionDateToString::addDependencies(DepsTracker* deps) const { /* ---------------------- ExpressionDayOfMonth ------------------------- */ -Value ExpressionDayOfMonth::evaluateInternal(Variables* vars) const { - Value pDate(vpOperand[0]->evaluateInternal(vars)); +Value ExpressionDayOfMonth::evaluateInternal() const { + Value pDate(vpOperand[0]->evaluateInternal()); return Value(extract(pDate.coerceToTm())); } @@ -1170,8 +1170,8 @@ const char* ExpressionDayOfMonth::getOpName() const { /* ------------------------- ExpressionDayOfWeek ----------------------------- */ -Value ExpressionDayOfWeek::evaluateInternal(Variables* vars) const { - Value pDate(vpOperand[0]->evaluateInternal(vars)); +Value ExpressionDayOfWeek::evaluateInternal() const { + Value pDate(vpOperand[0]->evaluateInternal()); return Value(extract(pDate.coerceToTm())); } @@ -1182,8 +1182,8 @@ const char* ExpressionDayOfWeek::getOpName() const { /* ------------------------- ExpressionDayOfYear ----------------------------- */ -Value ExpressionDayOfYear::evaluateInternal(Variables* vars) const { - Value pDate(vpOperand[0]->evaluateInternal(vars)); +Value ExpressionDayOfYear::evaluateInternal() const { + Value pDate(vpOperand[0]->evaluateInternal()); return Value(extract(pDate.coerceToTm())); } @@ -1194,9 +1194,9 @@ const char* ExpressionDayOfYear::getOpName() const { /* ----------------------- ExpressionDivide ---------------------------- */ -Value ExpressionDivide::evaluateInternal(Variables* vars) const { - Value lhs = vpOperand[0]->evaluateInternal(vars); - Value rhs = vpOperand[1]->evaluateInternal(vars); +Value ExpressionDivide::evaluateInternal() const { + Value lhs = vpOperand[0]->evaluateInternal(); + Value rhs = vpOperand[1]->evaluateInternal(); auto assertNonZero = [](bool nonZero) { uassert(16608, "can't $divide by zero", nonZero); }; @@ -1295,10 +1295,10 @@ void ExpressionObject::addDependencies(DepsTracker* deps) const { } } -Value ExpressionObject::evaluateInternal(Variables* vars) const { +Value ExpressionObject::evaluateInternal() const { MutableDocument outputDoc; for (auto&& pair : _expressions) { - outputDoc.addField(pair.first, pair.second->evaluateInternal(vars)); + outputDoc.addField(pair.first, pair.second->evaluateInternal()); } return outputDoc.freezeToValue(); } @@ -1408,16 +1408,17 @@ Value ExpressionFieldPath::evaluatePath(size_t index, const Document& input) con } } -Value ExpressionFieldPath::evaluateInternal(Variables* vars) const { +Value ExpressionFieldPath::evaluateInternal() const { + auto& vars = getExpressionContext()->variables; if (_fieldPath.getPathLength() == 1) // get the whole variable - return vars->getValue(_variable); + return vars.getValue(_variable); if (_variable == Variables::kRootId) { // ROOT is always a document so use optimized code path - return evaluatePath(1, vars->getRoot()); + return evaluatePath(1, vars.getRoot()); } - Value var = vars->getValue(_variable); + Value var = vars.getValue(_variable); switch (var.getType()) { case Object: return evaluatePath(1, var.getDocument()); @@ -1511,9 +1512,9 @@ Value ExpressionFilter::serialize(bool explain) const { << _filter->serialize(explain)))); } -Value ExpressionFilter::evaluateInternal(Variables* vars) const { +Value ExpressionFilter::evaluateInternal() const { // We are guaranteed at parse time that this isn't using our _varId. - const Value inputVal = _input->evaluateInternal(vars); + const Value inputVal = _input->evaluateInternal(); if (inputVal.nullish()) return Value(BSONNULL); @@ -1528,10 +1529,11 @@ Value ExpressionFilter::evaluateInternal(Variables* vars) const { return inputVal; vector<Value> output; + auto& vars = getExpressionContext()->variables; for (const auto& elem : input) { - vars->setValue(_varId, elem); + vars.setValue(_varId, elem); - if (_filter->evaluateInternal(vars).coerceToBool()) { + if (_filter->evaluateInternal().coerceToBool()) { output.push_back(std::move(elem)); } } @@ -1644,15 +1646,15 @@ Value ExpressionLet::serialize(bool explain) const { DOC("$let" << DOC("vars" << vars.freeze() << "in" << _subExpression->serialize(explain)))); } -Value ExpressionLet::evaluateInternal(Variables* vars) const { - for (VariableMap::const_iterator it = _variables.begin(), end = _variables.end(); it != end; - ++it) { +Value ExpressionLet::evaluateInternal() const { + for (const auto& item : _variables) { // It is guaranteed at parse-time that these expressions don't use the variable ids we // are setting - vars->setValue(it->first, it->second.expression->evaluateInternal(vars)); + getExpressionContext()->variables.setValue(item.first, + item.second.expression->evaluateInternal()); } - return _subExpression->evaluateInternal(vars); + return _subExpression->evaluateInternal(); } void ExpressionLet::addDependencies(DepsTracker* deps) const { @@ -1737,9 +1739,9 @@ Value ExpressionMap::serialize(bool explain) const { << _each->serialize(explain)))); } -Value ExpressionMap::evaluateInternal(Variables* vars) const { +Value ExpressionMap::evaluateInternal() const { // guaranteed at parse time that this isn't using our _varId - const Value inputVal = _input->evaluateInternal(vars); + const Value inputVal = _input->evaluateInternal(); if (inputVal.nullish()) return Value(BSONNULL); @@ -1755,9 +1757,9 @@ Value ExpressionMap::evaluateInternal(Variables* vars) const { vector<Value> output; output.reserve(input.size()); for (size_t i = 0; i < input.size(); i++) { - vars->setValue(_varId, input[i]); + getExpressionContext()->variables.setValue(_varId, input[i]); - Value toInsert = _each->evaluateInternal(vars); + Value toInsert = _each->evaluateInternal(); if (toInsert.missing()) toInsert = Value(BSONNULL); // can't insert missing values into array @@ -1805,8 +1807,8 @@ Value ExpressionMeta::serialize(bool explain) const { MONGO_UNREACHABLE; } -Value ExpressionMeta::evaluateInternal(Variables* vars) const { - const Document& root = vars->getRoot(); +Value ExpressionMeta::evaluateInternal() const { + const Document& root = getExpressionContext()->variables.getRoot(); switch (_metaType) { case MetaType::TEXT_SCORE: return root.hasTextScore() ? Value(root.getTextScore()) : Value(); @@ -1824,8 +1826,8 @@ void ExpressionMeta::addDependencies(DepsTracker* deps) const { /* ------------------------- ExpressionMillisecond ----------------------------- */ -Value ExpressionMillisecond::evaluateInternal(Variables* vars) const { - Value date(vpOperand[0]->evaluateInternal(vars)); +Value ExpressionMillisecond::evaluateInternal() const { + Value date(vpOperand[0]->evaluateInternal()); return Value(extract(date.coerceToDate())); } @@ -1842,8 +1844,8 @@ const char* ExpressionMillisecond::getOpName() const { /* ------------------------- ExpressionMinute -------------------------- */ -Value ExpressionMinute::evaluateInternal(Variables* vars) const { - Value pDate(vpOperand[0]->evaluateInternal(vars)); +Value ExpressionMinute::evaluateInternal() const { + Value pDate(vpOperand[0]->evaluateInternal()); return Value(extract(pDate.coerceToTm())); } @@ -1854,9 +1856,9 @@ const char* ExpressionMinute::getOpName() const { /* ----------------------- ExpressionMod ---------------------------- */ -Value ExpressionMod::evaluateInternal(Variables* vars) const { - Value lhs = vpOperand[0]->evaluateInternal(vars); - Value rhs = vpOperand[1]->evaluateInternal(vars); +Value ExpressionMod::evaluateInternal() const { + Value lhs = vpOperand[0]->evaluateInternal(); + Value rhs = vpOperand[1]->evaluateInternal(); BSONType leftType = lhs.getType(); BSONType rightType = rhs.getType(); @@ -1911,8 +1913,8 @@ const char* ExpressionMod::getOpName() const { /* ------------------------ ExpressionMonth ----------------------------- */ -Value ExpressionMonth::evaluateInternal(Variables* vars) const { - Value pDate(vpOperand[0]->evaluateInternal(vars)); +Value ExpressionMonth::evaluateInternal() const { + Value pDate(vpOperand[0]->evaluateInternal()); return Value(extract(pDate.coerceToTm())); } @@ -1923,7 +1925,7 @@ const char* ExpressionMonth::getOpName() const { /* ------------------------- ExpressionMultiply ----------------------------- */ -Value ExpressionMultiply::evaluateInternal(Variables* vars) const { +Value ExpressionMultiply::evaluateInternal() const { /* We'll try to return the narrowest possible result value. To do that without creating intermediate Values, do the arithmetic for double @@ -1938,7 +1940,7 @@ Value ExpressionMultiply::evaluateInternal(Variables* vars) const { const size_t n = vpOperand.size(); for (size_t i = 0; i < n; ++i) { - Value val = vpOperand[i]->evaluateInternal(vars); + Value val = vpOperand[i]->evaluateInternal(); if (val.numeric()) { BSONType oldProductType = productType; @@ -1986,8 +1988,8 @@ const char* ExpressionMultiply::getOpName() const { /* ------------------------- ExpressionHour ----------------------------- */ -Value ExpressionHour::evaluateInternal(Variables* vars) const { - Value pDate(vpOperand[0]->evaluateInternal(vars)); +Value ExpressionHour::evaluateInternal() const { + Value pDate(vpOperand[0]->evaluateInternal()); return Value(extract(pDate.coerceToTm())); } @@ -1998,12 +2000,12 @@ const char* ExpressionHour::getOpName() const { /* ----------------------- ExpressionIfNull ---------------------------- */ -Value ExpressionIfNull::evaluateInternal(Variables* vars) const { - Value pLeft(vpOperand[0]->evaluateInternal(vars)); +Value ExpressionIfNull::evaluateInternal() const { + Value pLeft(vpOperand[0]->evaluateInternal()); if (!pLeft.nullish()) return pLeft; - Value pRight(vpOperand[1]->evaluateInternal(vars)); + Value pRight(vpOperand[1]->evaluateInternal()); return pRight; } @@ -2014,9 +2016,9 @@ const char* ExpressionIfNull::getOpName() const { /* ----------------------- ExpressionIn ---------------------------- */ -Value ExpressionIn::evaluateInternal(Variables* vars) const { - Value argument(vpOperand[0]->evaluateInternal(vars)); - Value arrayOfValues(vpOperand[1]->evaluateInternal(vars)); +Value ExpressionIn::evaluateInternal() const { + Value argument(vpOperand[0]->evaluateInternal()); + Value arrayOfValues(vpOperand[1]->evaluateInternal()); uassert(40081, str::stream() << "$in requires an array as a second argument, found: " @@ -2058,8 +2060,8 @@ void uassertIfNotIntegralAndNonNegative(Value val, } // namespace -Value ExpressionIndexOfArray::evaluateInternal(Variables* vars) const { - Value arrayArg = vpOperand[0]->evaluateInternal(vars); +Value ExpressionIndexOfArray::evaluateInternal() const { + Value arrayArg = vpOperand[0]->evaluateInternal(); if (arrayArg.nullish()) { return Value(BSONNULL); @@ -2072,18 +2074,18 @@ Value ExpressionIndexOfArray::evaluateInternal(Variables* vars) const { std::vector<Value> array = arrayArg.getArray(); - Value searchItem = vpOperand[1]->evaluateInternal(vars); + Value searchItem = vpOperand[1]->evaluateInternal(); size_t startIndex = 0; if (vpOperand.size() > 2) { - Value startIndexArg = vpOperand[2]->evaluateInternal(vars); + Value startIndexArg = vpOperand[2]->evaluateInternal(); uassertIfNotIntegralAndNonNegative(startIndexArg, getOpName(), "starting index"); startIndex = static_cast<size_t>(startIndexArg.coerceToInt()); } size_t endIndex = array.size(); if (vpOperand.size() > 3) { - Value endIndexArg = vpOperand[3]->evaluateInternal(vars); + Value endIndexArg = vpOperand[3]->evaluateInternal(); uassertIfNotIntegralAndNonNegative(endIndexArg, getOpName(), "ending index"); // Don't let 'endIndex' exceed the length of the array. endIndex = std::min(array.size(), static_cast<size_t>(endIndexArg.coerceToInt())); @@ -2116,8 +2118,8 @@ bool stringHasTokenAtIndex(size_t index, const std::string& input, const std::st } // namespace -Value ExpressionIndexOfBytes::evaluateInternal(Variables* vars) const { - Value stringArg = vpOperand[0]->evaluateInternal(vars); +Value ExpressionIndexOfBytes::evaluateInternal() const { + Value stringArg = vpOperand[0]->evaluateInternal(); if (stringArg.nullish()) { return Value(BSONNULL); @@ -2129,7 +2131,7 @@ Value ExpressionIndexOfBytes::evaluateInternal(Variables* vars) const { stringArg.getType() == String); const std::string& input = stringArg.getString(); - Value tokenArg = vpOperand[1]->evaluateInternal(vars); + Value tokenArg = vpOperand[1]->evaluateInternal(); uassert(40092, str::stream() << "$indexOfBytes requires a string as the second argument, found: " << typeName(tokenArg.getType()), @@ -2138,14 +2140,14 @@ Value ExpressionIndexOfBytes::evaluateInternal(Variables* vars) const { size_t startIndex = 0; if (vpOperand.size() > 2) { - Value startIndexArg = vpOperand[2]->evaluateInternal(vars); + Value startIndexArg = vpOperand[2]->evaluateInternal(); uassertIfNotIntegralAndNonNegative(startIndexArg, getOpName(), "starting index"); startIndex = static_cast<size_t>(startIndexArg.coerceToInt()); } size_t endIndex = input.size(); if (vpOperand.size() > 3) { - Value endIndexArg = vpOperand[3]->evaluateInternal(vars); + Value endIndexArg = vpOperand[3]->evaluateInternal(); uassertIfNotIntegralAndNonNegative(endIndexArg, getOpName(), "ending index"); // Don't let 'endIndex' exceed the length of the string. endIndex = std::min(input.size(), static_cast<size_t>(endIndexArg.coerceToInt())); @@ -2170,8 +2172,8 @@ const char* ExpressionIndexOfBytes::getOpName() const { /* ----------------------- ExpressionIndexOfCP --------------------- */ -Value ExpressionIndexOfCP::evaluateInternal(Variables* vars) const { - Value stringArg = vpOperand[0]->evaluateInternal(vars); +Value ExpressionIndexOfCP::evaluateInternal() const { + Value stringArg = vpOperand[0]->evaluateInternal(); if (stringArg.nullish()) { return Value(BSONNULL); @@ -2183,7 +2185,7 @@ Value ExpressionIndexOfCP::evaluateInternal(Variables* vars) const { stringArg.getType() == String); const std::string& input = stringArg.getString(); - Value tokenArg = vpOperand[1]->evaluateInternal(vars); + Value tokenArg = vpOperand[1]->evaluateInternal(); uassert(40094, str::stream() << "$indexOfCP requires a string as the second argument, found: " << typeName(tokenArg.getType()), @@ -2192,7 +2194,7 @@ Value ExpressionIndexOfCP::evaluateInternal(Variables* vars) const { size_t startCodePointIndex = 0; if (vpOperand.size() > 2) { - Value startIndexArg = vpOperand[2]->evaluateInternal(vars); + Value startIndexArg = vpOperand[2]->evaluateInternal(); uassertIfNotIntegralAndNonNegative(startIndexArg, getOpName(), "starting index"); startCodePointIndex = static_cast<size_t>(startIndexArg.coerceToInt()); } @@ -2214,7 +2216,7 @@ Value ExpressionIndexOfCP::evaluateInternal(Variables* vars) const { size_t endCodePointIndex = codePointLength; if (vpOperand.size() > 3) { - Value endIndexArg = vpOperand[3]->evaluateInternal(vars); + Value endIndexArg = vpOperand[3]->evaluateInternal(); uassertIfNotIntegralAndNonNegative(endIndexArg, getOpName(), "ending index"); // Don't let 'endCodePointIndex' exceed the number of code points in the string. @@ -2271,9 +2273,9 @@ const char* ExpressionLn::getOpName() const { /* ----------------------- ExpressionLog ---------------------------- */ -Value ExpressionLog::evaluateInternal(Variables* vars) const { - Value argVal = vpOperand[0]->evaluateInternal(vars); - Value baseVal = vpOperand[1]->evaluateInternal(vars); +Value ExpressionLog::evaluateInternal() const { + Value argVal = vpOperand[0]->evaluateInternal(); + Value baseVal = vpOperand[1]->evaluateInternal(); if (argVal.nullish() || baseVal.nullish()) return Value(BSONNULL); @@ -2365,9 +2367,8 @@ intrusive_ptr<Expression> ExpressionNary::optimize() { // If all the operands are constant expressions, collapse the expression into one constant // expression. if (constOperandCount == vpOperand.size()) { - Variables emptyVars; return intrusive_ptr<Expression>( - ExpressionConstant::create(getExpressionContext(), evaluateInternal(&emptyVars))); + ExpressionConstant::create(getExpressionContext(), evaluateInternal())); } // If the expression is associative, we can collapse all the consecutive constant operands into @@ -2410,9 +2411,8 @@ intrusive_ptr<Expression> ExpressionNary::optimize() { if (constExpressions.size() > 1) { ExpressionVector vpOperandSave = std::move(vpOperand); vpOperand = std::move(constExpressions); - Variables emptyVars; - optimizedOperands.emplace_back(ExpressionConstant::create( - getExpressionContext(), evaluateInternal(&emptyVars))); + optimizedOperands.emplace_back( + ExpressionConstant::create(getExpressionContext(), evaluateInternal())); vpOperand = std::move(vpOperandSave); } else { optimizedOperands.insert( @@ -2426,9 +2426,8 @@ intrusive_ptr<Expression> ExpressionNary::optimize() { if (constExpressions.size() > 1) { vpOperand = std::move(constExpressions); - Variables emptyVars; optimizedOperands.emplace_back( - ExpressionConstant::create(getExpressionContext(), evaluateInternal(&emptyVars))); + ExpressionConstant::create(getExpressionContext(), evaluateInternal())); } else { optimizedOperands.insert( optimizedOperands.end(), constExpressions.begin(), constExpressions.end()); @@ -2461,8 +2460,8 @@ Value ExpressionNary::serialize(bool explain) const { /* ------------------------- ExpressionNot ----------------------------- */ -Value ExpressionNot::evaluateInternal(Variables* vars) const { - Value pOp(vpOperand[0]->evaluateInternal(vars)); +Value ExpressionNot::evaluateInternal() const { + Value pOp(vpOperand[0]->evaluateInternal()); bool b = pOp.coerceToBool(); return Value(!b); @@ -2475,10 +2474,10 @@ const char* ExpressionNot::getOpName() const { /* -------------------------- ExpressionOr ----------------------------- */ -Value ExpressionOr::evaluateInternal(Variables* vars) const { +Value ExpressionOr::evaluateInternal() const { const size_t n = vpOperand.size(); for (size_t i = 0; i < n; ++i) { - Value pValue(vpOperand[i]->evaluateInternal(vars)); + Value pValue(vpOperand[i]->evaluateInternal()); if (pValue.coerceToBool()) return Value(true); } @@ -2555,9 +2554,9 @@ intrusive_ptr<Expression> ExpressionPow::create( return expr; } -Value ExpressionPow::evaluateInternal(Variables* vars) const { - Value baseVal = vpOperand[0]->evaluateInternal(vars); - Value expVal = vpOperand[1]->evaluateInternal(vars); +Value ExpressionPow::evaluateInternal() const { + Value baseVal = vpOperand[0]->evaluateInternal(); + Value expVal = vpOperand[1]->evaluateInternal(); if (baseVal.nullish() || expVal.nullish()) return Value(BSONNULL); @@ -2715,9 +2714,9 @@ const char* ExpressionPow::getOpName() const { /* ------------------------- ExpressionRange ------------------------------ */ -Value ExpressionRange::evaluateInternal(Variables* vars) const { - Value startVal(vpOperand[0]->evaluateInternal(vars)); - Value endVal(vpOperand[1]->evaluateInternal(vars)); +Value ExpressionRange::evaluateInternal() const { + Value startVal(vpOperand[0]->evaluateInternal()); + Value endVal(vpOperand[1]->evaluateInternal()); uassert(34443, str::stream() << "$range requires a numeric starting value, found value of type: " @@ -2744,7 +2743,7 @@ Value ExpressionRange::evaluateInternal(Variables* vars) const { int step = 1; if (vpOperand.size() == 3) { // A step was specified by the user. - Value stepVal(vpOperand[2]->evaluateInternal(vars)); + Value stepVal(vpOperand[2]->evaluateInternal()); uassert(34447, str::stream() << "$range requires a numeric step value, found value of type:" @@ -2815,8 +2814,8 @@ intrusive_ptr<Expression> ExpressionReduce::parse( return reduce; } -Value ExpressionReduce::evaluateInternal(Variables* vars) const { - Value inputVal = _input->evaluateInternal(vars); +Value ExpressionReduce::evaluateInternal() const { + Value inputVal = _input->evaluateInternal(); if (inputVal.nullish()) { return Value(BSONNULL); @@ -2827,13 +2826,14 @@ Value ExpressionReduce::evaluateInternal(Variables* vars) const { << inputVal.toString(), inputVal.isArray()); - Value accumulatedValue = _initial->evaluateInternal(vars); + Value accumulatedValue = _initial->evaluateInternal(); + auto& vars = getExpressionContext()->variables; for (auto&& elem : inputVal.getArray()) { - vars->setValue(_thisVar, elem); - vars->setValue(_valueVar, accumulatedValue); + vars.setValue(_thisVar, elem); + vars.setValue(_valueVar, accumulatedValue); - accumulatedValue = _in->evaluateInternal(vars); + accumulatedValue = _in->evaluateInternal(); } return accumulatedValue; @@ -2861,8 +2861,8 @@ Value ExpressionReduce::serialize(bool explain) const { /* ------------------------ ExpressionReverseArray ------------------------ */ -Value ExpressionReverseArray::evaluateInternal(Variables* vars) const { - Value input(vpOperand[0]->evaluateInternal(vars)); +Value ExpressionReverseArray::evaluateInternal() const { + Value input(vpOperand[0]->evaluateInternal()); if (input.nullish()) { return Value(BSONNULL); @@ -2889,8 +2889,8 @@ const char* ExpressionReverseArray::getOpName() const { /* ------------------------- ExpressionSecond ----------------------------- */ -Value ExpressionSecond::evaluateInternal(Variables* vars) const { - Value pDate(vpOperand[0]->evaluateInternal(vars)); +Value ExpressionSecond::evaluateInternal() const { + Value pDate(vpOperand[0]->evaluateInternal()); return Value(extract(pDate.coerceToTm())); } @@ -2910,9 +2910,9 @@ ValueSet arrayToSet(const Value& val, const ValueComparator& valueComparator) { /* ----------------------- ExpressionSetDifference ---------------------------- */ -Value ExpressionSetDifference::evaluateInternal(Variables* vars) const { - const Value lhs = vpOperand[0]->evaluateInternal(vars); - const Value rhs = vpOperand[1]->evaluateInternal(vars); +Value ExpressionSetDifference::evaluateInternal() const { + const Value lhs = vpOperand[0]->evaluateInternal(); + const Value rhs = vpOperand[1]->evaluateInternal(); if (lhs.nullish() || rhs.nullish()) { return Value(BSONNULL); @@ -2956,13 +2956,13 @@ void ExpressionSetEquals::validateArguments(const ExpressionVector& args) const args.size() >= 2); } -Value ExpressionSetEquals::evaluateInternal(Variables* vars) const { +Value ExpressionSetEquals::evaluateInternal() const { const size_t n = vpOperand.size(); const auto& valueComparator = getExpressionContext()->getValueComparator(); ValueSet lhs = valueComparator.makeOrderedValueSet(); for (size_t i = 0; i < n; i++) { - const Value nextEntry = vpOperand[i]->evaluateInternal(vars); + const Value nextEntry = vpOperand[i]->evaluateInternal(); uassert(17044, str::stream() << "All operands of $setEquals must be arrays. One " << "argument is of type: " @@ -2993,12 +2993,12 @@ const char* ExpressionSetEquals::getOpName() const { /* ----------------------- ExpressionSetIntersection ---------------------------- */ -Value ExpressionSetIntersection::evaluateInternal(Variables* vars) const { +Value ExpressionSetIntersection::evaluateInternal() const { const size_t n = vpOperand.size(); const auto& valueComparator = getExpressionContext()->getValueComparator(); ValueSet currentIntersection = valueComparator.makeOrderedValueSet(); for (size_t i = 0; i < n; i++) { - const Value nextEntry = vpOperand[i]->evaluateInternal(vars); + const Value nextEntry = vpOperand[i]->evaluateInternal(); if (nextEntry.nullish()) { return Value(BSONNULL); } @@ -3054,9 +3054,9 @@ Value setIsSubsetHelper(const vector<Value>& lhs, const ValueSet& rhs) { } } -Value ExpressionSetIsSubset::evaluateInternal(Variables* vars) const { - const Value lhs = vpOperand[0]->evaluateInternal(vars); - const Value rhs = vpOperand[1]->evaluateInternal(vars); +Value ExpressionSetIsSubset::evaluateInternal() const { + const Value lhs = vpOperand[0]->evaluateInternal(); + const Value rhs = vpOperand[1]->evaluateInternal(); uassert(17046, str::stream() << "both operands of $setIsSubset must be arrays. First " @@ -3089,8 +3089,8 @@ public: vpOperand = operands; } - virtual Value evaluateInternal(Variables* vars) const { - const Value lhs = vpOperand[0]->evaluateInternal(vars); + virtual Value evaluateInternal() const { + const Value lhs = vpOperand[0]->evaluateInternal(); uassert(17310, str::stream() << "both operands of $setIsSubset must be arrays. First " @@ -3137,11 +3137,11 @@ const char* ExpressionSetIsSubset::getOpName() const { /* ----------------------- ExpressionSetUnion ---------------------------- */ -Value ExpressionSetUnion::evaluateInternal(Variables* vars) const { +Value ExpressionSetUnion::evaluateInternal() const { ValueSet unionedSet = getExpressionContext()->getValueComparator().makeOrderedValueSet(); const size_t n = vpOperand.size(); for (size_t i = 0; i < n; i++) { - const Value newEntries = vpOperand[i]->evaluateInternal(vars); + const Value newEntries = vpOperand[i]->evaluateInternal(); if (newEntries.nullish()) { return Value(BSONNULL); } @@ -3163,8 +3163,8 @@ const char* ExpressionSetUnion::getOpName() const { /* ----------------------- ExpressionIsArray ---------------------------- */ -Value ExpressionIsArray::evaluateInternal(Variables* vars) const { - Value argument = vpOperand[0]->evaluateInternal(vars); +Value ExpressionIsArray::evaluateInternal() const { + Value argument = vpOperand[0]->evaluateInternal(); return Value(argument.isArray()); } @@ -3175,12 +3175,12 @@ const char* ExpressionIsArray::getOpName() const { /* ----------------------- ExpressionSlice ---------------------------- */ -Value ExpressionSlice::evaluateInternal(Variables* vars) const { +Value ExpressionSlice::evaluateInternal() const { const size_t n = vpOperand.size(); - Value arrayVal = vpOperand[0]->evaluateInternal(vars); + Value arrayVal = vpOperand[0]->evaluateInternal(); // Could be either a start index or the length from 0. - Value arg2 = vpOperand[1]->evaluateInternal(vars); + Value arg2 = vpOperand[1]->evaluateInternal(); if (arrayVal.nullish() || arg2.nullish()) { return Value(BSONNULL); @@ -3231,7 +3231,7 @@ Value ExpressionSlice::evaluateInternal(Variables* vars) const { start = std::min(array.size(), size_t(startInt)); } - Value countVal = vpOperand[2]->evaluateInternal(vars); + Value countVal = vpOperand[2]->evaluateInternal(); if (countVal.nullish()) { return Value(BSONNULL); @@ -3266,8 +3266,8 @@ const char* ExpressionSlice::getOpName() const { /* ----------------------- ExpressionSize ---------------------------- */ -Value ExpressionSize::evaluateInternal(Variables* vars) const { - Value array = vpOperand[0]->evaluateInternal(vars); +Value ExpressionSize::evaluateInternal() const { + Value array = vpOperand[0]->evaluateInternal(); uassert(17124, str::stream() << "The argument to $size must be an array, but was of type: " @@ -3283,9 +3283,9 @@ const char* ExpressionSize::getOpName() const { /* ----------------------- ExpressionSplit --------------------------- */ -Value ExpressionSplit::evaluateInternal(Variables* vars) const { - Value inputArg = vpOperand[0]->evaluateInternal(vars); - Value separatorArg = vpOperand[1]->evaluateInternal(vars); +Value ExpressionSplit::evaluateInternal() const { + Value inputArg = vpOperand[0]->evaluateInternal(); + Value separatorArg = vpOperand[1]->evaluateInternal(); if (inputArg.nullish() || separatorArg.nullish()) { return Value(BSONNULL); @@ -3361,9 +3361,9 @@ const char* ExpressionSqrt::getOpName() const { /* ----------------------- ExpressionStrcasecmp ---------------------------- */ -Value ExpressionStrcasecmp::evaluateInternal(Variables* vars) const { - Value pString1(vpOperand[0]->evaluateInternal(vars)); - Value pString2(vpOperand[1]->evaluateInternal(vars)); +Value ExpressionStrcasecmp::evaluateInternal() const { + Value pString1(vpOperand[0]->evaluateInternal()); + Value pString2(vpOperand[1]->evaluateInternal()); /* boost::iequals returns a bool not an int so strings must actually be allocated */ string str1 = boost::to_upper_copy(pString1.coerceToString()); @@ -3385,10 +3385,10 @@ const char* ExpressionStrcasecmp::getOpName() const { /* ----------------------- ExpressionSubstrBytes ---------------------------- */ -Value ExpressionSubstrBytes::evaluateInternal(Variables* vars) const { - Value pString(vpOperand[0]->evaluateInternal(vars)); - Value pLower(vpOperand[1]->evaluateInternal(vars)); - Value pLength(vpOperand[2]->evaluateInternal(vars)); +Value ExpressionSubstrBytes::evaluateInternal() const { + Value pString(vpOperand[0]->evaluateInternal()); + Value pLower(vpOperand[1]->evaluateInternal()); + Value pLength(vpOperand[2]->evaluateInternal()); string str = pString.coerceToString(); uassert(16034, @@ -3438,10 +3438,10 @@ const char* ExpressionSubstrBytes::getOpName() const { /* ----------------------- ExpressionSubstrCP ---------------------------- */ -Value ExpressionSubstrCP::evaluateInternal(Variables* vars) const { - Value inputVal(vpOperand[0]->evaluateInternal(vars)); - Value lowerVal(vpOperand[1]->evaluateInternal(vars)); - Value lengthVal(vpOperand[2]->evaluateInternal(vars)); +Value ExpressionSubstrCP::evaluateInternal() const { + Value inputVal(vpOperand[0]->evaluateInternal()); + Value lowerVal(vpOperand[1]->evaluateInternal()); + Value lengthVal(vpOperand[2]->evaluateInternal()); std::string str = inputVal.coerceToString(); uassert(34450, @@ -3513,8 +3513,8 @@ const char* ExpressionSubstrCP::getOpName() const { /* ----------------------- ExpressionStrLenBytes ------------------------- */ -Value ExpressionStrLenBytes::evaluateInternal(Variables* vars) const { - Value str(vpOperand[0]->evaluateInternal(vars)); +Value ExpressionStrLenBytes::evaluateInternal() const { + Value str(vpOperand[0]->evaluateInternal()); uassert(34473, str::stream() << "$strLenBytes requires a string argument, found: " @@ -3536,8 +3536,8 @@ const char* ExpressionStrLenBytes::getOpName() const { /* ----------------------- ExpressionStrLenCP ------------------------- */ -Value ExpressionStrLenCP::evaluateInternal(Variables* vars) const { - Value val(vpOperand[0]->evaluateInternal(vars)); +Value ExpressionStrLenCP::evaluateInternal() const { + Value val(vpOperand[0]->evaluateInternal()); uassert(34471, str::stream() << "$strLenCP requires a string argument, found: " @@ -3565,9 +3565,9 @@ const char* ExpressionStrLenCP::getOpName() const { /* ----------------------- ExpressionSubtract ---------------------------- */ -Value ExpressionSubtract::evaluateInternal(Variables* vars) const { - Value lhs = vpOperand[0]->evaluateInternal(vars); - Value rhs = vpOperand[1]->evaluateInternal(vars); +Value ExpressionSubtract::evaluateInternal() const { + Value lhs = vpOperand[0]->evaluateInternal(); + Value rhs = vpOperand[1]->evaluateInternal(); BSONType diffType = Value::getWidestNumeric(rhs.getType(), lhs.getType()); @@ -3617,12 +3617,12 @@ const char* ExpressionSubtract::getOpName() const { REGISTER_EXPRESSION(switch, ExpressionSwitch::parse); -Value ExpressionSwitch::evaluateInternal(Variables* vars) const { +Value ExpressionSwitch::evaluateInternal() const { for (auto&& branch : _branches) { - Value caseExpression(branch.first->evaluateInternal(vars)); + Value caseExpression(branch.first->evaluateInternal()); if (caseExpression.coerceToBool()) { - return branch.second->evaluateInternal(vars); + return branch.second->evaluateInternal(); } } @@ -3630,7 +3630,7 @@ Value ExpressionSwitch::evaluateInternal(Variables* vars) const { "$switch could not find a matching branch for an input, and no default was specified.", _default); - return _default->evaluateInternal(vars); + return _default->evaluateInternal(); } boost::intrusive_ptr<Expression> ExpressionSwitch::parse( @@ -3744,8 +3744,8 @@ Value ExpressionSwitch::serialize(bool explain) const { /* ------------------------- ExpressionToLower ----------------------------- */ -Value ExpressionToLower::evaluateInternal(Variables* vars) const { - Value pString(vpOperand[0]->evaluateInternal(vars)); +Value ExpressionToLower::evaluateInternal() const { + Value pString(vpOperand[0]->evaluateInternal()); string str = pString.coerceToString(); boost::to_lower(str); return Value(str); @@ -3758,8 +3758,8 @@ const char* ExpressionToLower::getOpName() const { /* ------------------------- ExpressionToUpper -------------------------- */ -Value ExpressionToUpper::evaluateInternal(Variables* vars) const { - Value pString(vpOperand[0]->evaluateInternal(vars)); +Value ExpressionToUpper::evaluateInternal() const { + Value pString(vpOperand[0]->evaluateInternal()); string str(pString.coerceToString()); boost::to_upper(str); return Value(str); @@ -3792,8 +3792,8 @@ const char* ExpressionTrunc::getOpName() const { /* ------------------------- ExpressionType ----------------------------- */ -Value ExpressionType::evaluateInternal(Variables* vars) const { - Value val(vpOperand[0]->evaluateInternal(vars)); +Value ExpressionType::evaluateInternal() const { + Value val(vpOperand[0]->evaluateInternal()); return Value(StringData(typeName(val.getType()))); } @@ -3804,8 +3804,8 @@ const char* ExpressionType::getOpName() const { /* ------------------------- ExpressionWeek ----------------------------- */ -Value ExpressionWeek::evaluateInternal(Variables* vars) const { - Value pDate(vpOperand[0]->evaluateInternal(vars)); +Value ExpressionWeek::evaluateInternal() const { + Value pDate(vpOperand[0]->evaluateInternal()); return Value(extract(pDate.coerceToTm())); } @@ -3836,8 +3836,8 @@ const char* ExpressionWeek::getOpName() const { /* ------------------------- ExpressionIsoDayOfWeek --------------------- */ -Value ExpressionIsoDayOfWeek::evaluateInternal(Variables* vars) const { - Value date(vpOperand[0]->evaluateInternal(vars)); +Value ExpressionIsoDayOfWeek::evaluateInternal() const { + Value date(vpOperand[0]->evaluateInternal()); return Value(extract(date.coerceToTm())); } @@ -3853,8 +3853,8 @@ const char* ExpressionIsoDayOfWeek::getOpName() const { /* ------------------------- ExpressionIsoWeekYear ---------------------- */ -Value ExpressionIsoWeekYear::evaluateInternal(Variables* vars) const { - Value date(vpOperand[0]->evaluateInternal(vars)); +Value ExpressionIsoWeekYear::evaluateInternal() const { + Value date(vpOperand[0]->evaluateInternal()); return Value(extract(date.coerceToTm())); } @@ -3939,8 +3939,8 @@ int lastWeek(int year) { } } -Value ExpressionIsoWeek::evaluateInternal(Variables* vars) const { - Value date(vpOperand[0]->evaluateInternal(vars)); +Value ExpressionIsoWeek::evaluateInternal() const { + Value date(vpOperand[0]->evaluateInternal()); return Value(extract(date.coerceToTm())); } @@ -3988,8 +3988,8 @@ const char* ExpressionIsoWeek::getOpName() const { /* ------------------------- ExpressionYear ----------------------------- */ -Value ExpressionYear::evaluateInternal(Variables* vars) const { - Value pDate(vpOperand[0]->evaluateInternal(vars)); +Value ExpressionYear::evaluateInternal() const { + Value pDate(vpOperand[0]->evaluateInternal()); return Value(extract(pDate.coerceToTm())); } @@ -4053,7 +4053,7 @@ intrusive_ptr<Expression> ExpressionZip::parse( return std::move(newZip); } -Value ExpressionZip::evaluateInternal(Variables* vars) const { +Value ExpressionZip::evaluateInternal() const { // Evaluate input values. vector<vector<Value>> inputValues; inputValues.reserve(_inputs.size()); @@ -4061,7 +4061,7 @@ Value ExpressionZip::evaluateInternal(Variables* vars) const { size_t minArraySize = 0; size_t maxArraySize = 0; for (size_t i = 0; i < _inputs.size(); i++) { - Value evalExpr = _inputs[i]->evaluateInternal(vars); + Value evalExpr = _inputs[i]->evaluateInternal(); if (evalExpr.nullish()) { return Value(BSONNULL); } @@ -4090,7 +4090,7 @@ Value ExpressionZip::evaluateInternal(Variables* vars) const { // If we need default values, evaluate each expression. if (minArraySize != maxArraySize) { for (size_t i = 0; i < _defaults.size(); i++) { - evaluatedDefaults[i] = _defaults[i]->evaluateInternal(vars); + evaluatedDefaults[i] = _defaults[i]->evaluateInternal(); } } diff --git a/src/mongo/db/pipeline/expression.h b/src/mongo/db/pipeline/expression.h index 62bf6904fc5..0bb366223a3 100644 --- a/src/mongo/db/pipeline/expression.h +++ b/src/mongo/db/pipeline/expression.h @@ -108,19 +108,15 @@ public: * This method should only be used for testing. */ Value evaluate(const Document& root) const { - Variables vars(0, root); - return evaluate(&vars); + getExpressionContext()->variables.setRoot(root); + return evaluate(); } /** - * Evaluate expression with variables given by 'vars', and return the result. - * - * While vars is non-const, a subexpression's modifications to it should not effect outer - * Expressions, since variables defined in the subexpression's scope will be given unique - * variable ids. + * Evaluate expression and return the result. */ - Value evaluate(Variables* vars) const { - return evaluateInternal(vars); + Value evaluate() const { + return evaluateInternal(); } /** @@ -168,12 +164,13 @@ public: */ static std::string removeFieldPrefix(const std::string& prefixedField); - /** Evaluate the subclass Expression using the given Variables as context and return result. + /** + * Evaluate the subclass Expression and return result. * * Should only be called by subclasses, but can't be protected because they need to call * this function on each other. */ - virtual Value evaluateInternal(Variables* vars) const = 0; + virtual Value evaluateInternal() const = 0; /** * Registers an Parser so it can be called from parseExpression. @@ -323,13 +320,13 @@ public: explicit ExpressionFromAccumulator(const boost::intrusive_ptr<ExpressionContext>& expCtx) : ExpressionVariadic<ExpressionFromAccumulator<Accumulator>>(expCtx) {} - Value evaluateInternal(Variables* vars) const final { + Value evaluateInternal() const final { Accumulator accum(this->getExpressionContext()); const size_t n = this->vpOperand.size(); // If a single array arg is given, loop through it passing each member to the accumulator. // If a single, non-array arg is given, pass it directly to the accumulator. if (n == 1) { - Value singleVal = this->vpOperand[0]->evaluateInternal(vars); + Value singleVal = this->vpOperand[0]->evaluateInternal(); if (singleVal.getType() == Array) { for (const Value& val : singleVal.getArray()) { accum.process(val, false); @@ -340,7 +337,7 @@ public: } else { // If multiple arguments are given, pass all arguments to the accumulator. for (auto&& argument : this->vpOperand) { - accum.process(argument->evaluateInternal(vars), false); + accum.process(argument->evaluateInternal(), false); } } return accum.getValue(false); @@ -375,8 +372,8 @@ public: virtual ~ExpressionSingleNumericArg() {} - Value evaluateInternal(Variables* vars) const final { - Value arg = this->vpOperand[0]->evaluateInternal(vars); + Value evaluateInternal() const final { + Value arg = this->vpOperand[0]->evaluateInternal(); if (arg.nullish()) return Value(BSONNULL); @@ -407,7 +404,7 @@ public: explicit ExpressionAdd(const boost::intrusive_ptr<ExpressionContext>& expCtx) : ExpressionVariadic<ExpressionAdd>(expCtx) {} - Value evaluateInternal(Variables* vars) const final; + Value evaluateInternal() const final; const char* getOpName() const final; bool isAssociative() const final { @@ -425,7 +422,7 @@ public: explicit ExpressionAllElementsTrue(const boost::intrusive_ptr<ExpressionContext>& expCtx) : ExpressionFixedArity<ExpressionAllElementsTrue, 1>(expCtx) {} - Value evaluateInternal(Variables* vars) const final; + Value evaluateInternal() const final; const char* getOpName() const final; }; @@ -436,7 +433,7 @@ public: : ExpressionVariadic<ExpressionAnd>(expCtx) {} boost::intrusive_ptr<Expression> optimize() final; - Value evaluateInternal(Variables* vars) const final; + Value evaluateInternal() const final; const char* getOpName() const final; bool isAssociative() const final { @@ -454,7 +451,7 @@ public: explicit ExpressionAnyElementTrue(const boost::intrusive_ptr<ExpressionContext>& expCtx) : ExpressionFixedArity<ExpressionAnyElementTrue, 1>(expCtx) {} - Value evaluateInternal(Variables* vars) const final; + Value evaluateInternal() const final; const char* getOpName() const final; }; @@ -464,7 +461,7 @@ public: explicit ExpressionArray(const boost::intrusive_ptr<ExpressionContext>& expCtx) : ExpressionVariadic<ExpressionArray>(expCtx) {} - Value evaluateInternal(Variables* vars) const final; + Value evaluateInternal() const final; Value serialize(bool explain) const final; const char* getOpName() const final; }; @@ -475,7 +472,7 @@ public: explicit ExpressionArrayElemAt(const boost::intrusive_ptr<ExpressionContext>& expCtx) : ExpressionFixedArity<ExpressionArrayElemAt, 2>(expCtx) {} - Value evaluateInternal(Variables* vars) const final; + Value evaluateInternal() const final; const char* getOpName() const final; }; @@ -484,7 +481,7 @@ public: explicit ExpressionObjectToArray(const boost::intrusive_ptr<ExpressionContext>& expCtx) : ExpressionFixedArity<ExpressionObjectToArray, 1>(expCtx) {} - Value evaluateInternal(Variables* vars) const final; + Value evaluateInternal() const final; const char* getOpName() const final; }; @@ -493,7 +490,7 @@ public: explicit ExpressionArrayToObject(const boost::intrusive_ptr<ExpressionContext>& expCtx) : ExpressionFixedArity<ExpressionArrayToObject, 1>(expCtx) {} - Value evaluateInternal(Variables* vars) const final; + Value evaluateInternal() const final; const char* getOpName() const final; }; @@ -511,7 +508,7 @@ class ExpressionCoerceToBool final : public Expression { public: boost::intrusive_ptr<Expression> optimize() final; void addDependencies(DepsTracker* deps) const final; - Value evaluateInternal(Variables* vars) const final; + Value evaluateInternal() const final; Value serialize(bool explain) const final; static boost::intrusive_ptr<ExpressionCoerceToBool> create( @@ -545,7 +542,7 @@ public: ExpressionCompare(const boost::intrusive_ptr<ExpressionContext>& expCtx, CmpOp cmpOp) : ExpressionFixedArity<ExpressionCompare, 2>(expCtx), cmpOp(cmpOp) {} - Value evaluateInternal(Variables* vars) const final; + Value evaluateInternal() const final; const char* getOpName() const final; static boost::intrusive_ptr<Expression> parse( @@ -570,7 +567,7 @@ public: explicit ExpressionConcat(const boost::intrusive_ptr<ExpressionContext>& expCtx) : ExpressionVariadic<ExpressionConcat>(expCtx) {} - Value evaluateInternal(Variables* vars) const final; + Value evaluateInternal() const final; const char* getOpName() const final; bool isAssociative() const final { @@ -584,7 +581,7 @@ public: explicit ExpressionConcatArrays(const boost::intrusive_ptr<ExpressionContext>& expCtx) : ExpressionVariadic<ExpressionConcatArrays>(expCtx) {} - Value evaluateInternal(Variables* vars) const final; + Value evaluateInternal() const final; const char* getOpName() const final; bool isAssociative() const final { @@ -597,7 +594,7 @@ class ExpressionCond final : public ExpressionFixedArity<ExpressionCond, 3> { public: explicit ExpressionCond(const boost::intrusive_ptr<ExpressionContext>& expCtx) : Base(expCtx) {} - Value evaluateInternal(Variables* vars) const final; + Value evaluateInternal() const final; const char* getOpName() const final; static boost::intrusive_ptr<Expression> parse( @@ -614,7 +611,7 @@ class ExpressionConstant final : public Expression { public: boost::intrusive_ptr<Expression> optimize() final; void addDependencies(DepsTracker* deps) const final; - Value evaluateInternal(Variables* vars) const final; + Value evaluateInternal() const final; Value serialize(bool explain) const final; const char* getOpName() const; @@ -646,7 +643,7 @@ class ExpressionDateToString final : public Expression { public: boost::intrusive_ptr<Expression> optimize() final; Value serialize(bool explain) const final; - Value evaluateInternal(Variables* vars) const final; + Value evaluateInternal() const final; void addDependencies(DepsTracker* deps) const final; static boost::intrusive_ptr<Expression> parse( @@ -677,7 +674,7 @@ public: explicit ExpressionDayOfMonth(const boost::intrusive_ptr<ExpressionContext>& expCtx) : ExpressionFixedArity<ExpressionDayOfMonth, 1>(expCtx) {} - Value evaluateInternal(Variables* vars) const final; + Value evaluateInternal() const final; const char* getOpName() const final; static inline int extract(const tm& tm) { @@ -691,7 +688,7 @@ public: explicit ExpressionDayOfWeek(const boost::intrusive_ptr<ExpressionContext>& expCtx) : ExpressionFixedArity<ExpressionDayOfWeek, 1>(expCtx) {} - Value evaluateInternal(Variables* vars) const final; + Value evaluateInternal() const final; const char* getOpName() const final; // MySQL uses 1-7, tm uses 0-6 @@ -706,7 +703,7 @@ public: explicit ExpressionDayOfYear(const boost::intrusive_ptr<ExpressionContext>& expCtx) : ExpressionFixedArity<ExpressionDayOfYear, 1>(expCtx) {} - Value evaluateInternal(Variables* vars) const final; + Value evaluateInternal() const final; const char* getOpName() const final; // MySQL uses 1-366, tm uses 0-365 @@ -721,7 +718,7 @@ public: explicit ExpressionDivide(const boost::intrusive_ptr<ExpressionContext>& expCtx) : ExpressionFixedArity<ExpressionDivide, 2>(expCtx) {} - Value evaluateInternal(Variables* vars) const final; + Value evaluateInternal() const final; const char* getOpName() const final; }; @@ -740,7 +737,7 @@ class ExpressionFieldPath final : public Expression { public: boost::intrusive_ptr<Expression> optimize() final; void addDependencies(DepsTracker* deps) const final; - Value evaluateInternal(Variables* vars) const final; + Value evaluateInternal() const final; Value serialize(bool explain) const final; /* @@ -805,7 +802,7 @@ class ExpressionFilter final : public Expression { public: boost::intrusive_ptr<Expression> optimize() final; Value serialize(bool explain) const final; - Value evaluateInternal(Variables* vars) const final; + Value evaluateInternal() const final; void addDependencies(DepsTracker* deps) const final; static boost::intrusive_ptr<Expression> parse( @@ -846,7 +843,7 @@ public: explicit ExpressionHour(const boost::intrusive_ptr<ExpressionContext>& expCtx) : ExpressionFixedArity<ExpressionHour, 1>(expCtx) {} - Value evaluateInternal(Variables* vars) const final; + Value evaluateInternal() const final; const char* getOpName() const final; static inline int extract(const tm& tm) { @@ -860,7 +857,7 @@ public: explicit ExpressionIfNull(const boost::intrusive_ptr<ExpressionContext>& expCtx) : ExpressionFixedArity<ExpressionIfNull, 2>(expCtx) {} - Value evaluateInternal(Variables* vars) const final; + Value evaluateInternal() const final; const char* getOpName() const final; }; @@ -870,7 +867,7 @@ public: explicit ExpressionIn(const boost::intrusive_ptr<ExpressionContext>& expCtx) : ExpressionFixedArity<ExpressionIn, 2>(expCtx) {} - Value evaluateInternal(Variables* vars) const final; + Value evaluateInternal() const final; const char* getOpName() const final; }; @@ -880,7 +877,7 @@ public: explicit ExpressionIndexOfArray(const boost::intrusive_ptr<ExpressionContext>& expCtx) : ExpressionRangedArity<ExpressionIndexOfArray, 2, 4>(expCtx) {} - Value evaluateInternal(Variables* vars) const final; + Value evaluateInternal() const final; const char* getOpName() const final; }; @@ -890,7 +887,7 @@ public: explicit ExpressionIndexOfBytes(const boost::intrusive_ptr<ExpressionContext>& expCtx) : ExpressionRangedArity<ExpressionIndexOfBytes, 2, 4>(expCtx) {} - Value evaluateInternal(Variables* vars) const final; + Value evaluateInternal() const final; const char* getOpName() const final; }; @@ -903,7 +900,7 @@ public: explicit ExpressionIndexOfCP(const boost::intrusive_ptr<ExpressionContext>& expCtx) : ExpressionRangedArity<ExpressionIndexOfCP, 2, 4>(expCtx) {} - Value evaluateInternal(Variables* vars) const final; + Value evaluateInternal() const final; const char* getOpName() const final; }; @@ -912,7 +909,7 @@ class ExpressionLet final : public Expression { public: boost::intrusive_ptr<Expression> optimize() final; Value serialize(bool explain) const final; - Value evaluateInternal(Variables* vars) const final; + Value evaluateInternal() const final; void addDependencies(DepsTracker* deps) const final; static boost::intrusive_ptr<Expression> parse( @@ -954,7 +951,7 @@ public: explicit ExpressionLog(const boost::intrusive_ptr<ExpressionContext>& expCtx) : ExpressionFixedArity<ExpressionLog, 2>(expCtx) {} - Value evaluateInternal(Variables* vars) const final; + Value evaluateInternal() const final; const char* getOpName() const final; }; @@ -971,7 +968,7 @@ class ExpressionMap final : public Expression { public: boost::intrusive_ptr<Expression> optimize() final; Value serialize(bool explain) const final; - Value evaluateInternal(Variables* vars) const final; + Value evaluateInternal() const final; void addDependencies(DepsTracker* deps) const final; static boost::intrusive_ptr<Expression> parse( @@ -996,7 +993,7 @@ private: class ExpressionMeta final : public Expression { public: Value serialize(bool explain) const final; - Value evaluateInternal(Variables* vars) const final; + Value evaluateInternal() const final; void addDependencies(DepsTracker* deps) const final; static boost::intrusive_ptr<Expression> parse( @@ -1020,7 +1017,7 @@ public: explicit ExpressionMillisecond(const boost::intrusive_ptr<ExpressionContext>& expCtx) : ExpressionFixedArity<ExpressionMillisecond, 1>(expCtx) {} - Value evaluateInternal(Variables* vars) const final; + Value evaluateInternal() const final; const char* getOpName() const final; static int extract(const long long date); @@ -1032,7 +1029,7 @@ public: explicit ExpressionMinute(const boost::intrusive_ptr<ExpressionContext>& expCtx) : ExpressionFixedArity<ExpressionMinute, 1>(expCtx) {} - Value evaluateInternal(Variables* vars) const final; + Value evaluateInternal() const final; const char* getOpName() const final; static int extract(const tm& tm) { @@ -1046,7 +1043,7 @@ public: explicit ExpressionMod(const boost::intrusive_ptr<ExpressionContext>& expCtx) : ExpressionFixedArity<ExpressionMod, 2>(expCtx) {} - Value evaluateInternal(Variables* vars) const final; + Value evaluateInternal() const final; const char* getOpName() const final; }; @@ -1056,7 +1053,7 @@ public: explicit ExpressionMultiply(const boost::intrusive_ptr<ExpressionContext>& expCtx) : ExpressionVariadic<ExpressionMultiply>(expCtx) {} - Value evaluateInternal(Variables* vars) const final; + Value evaluateInternal() const final; const char* getOpName() const final; bool isAssociative() const final { @@ -1074,7 +1071,7 @@ public: explicit ExpressionMonth(const boost::intrusive_ptr<ExpressionContext>& expCtx) : ExpressionFixedArity<ExpressionMonth, 1>(expCtx) {} - Value evaluateInternal(Variables* vars) const final; + Value evaluateInternal() const final; const char* getOpName() const final; // MySQL uses 1-12, tm uses 0-11 @@ -1089,7 +1086,7 @@ public: explicit ExpressionNot(const boost::intrusive_ptr<ExpressionContext>& expCtx) : ExpressionFixedArity<ExpressionNot, 1>(expCtx) {} - Value evaluateInternal(Variables* vars) const final; + Value evaluateInternal() const final; const char* getOpName() const final; }; @@ -1106,7 +1103,7 @@ class ExpressionObject final : public Expression { public: boost::intrusive_ptr<Expression> optimize() final; void addDependencies(DepsTracker* deps) const final; - Value evaluateInternal(Variables* vars) const final; + Value evaluateInternal() const final; Value serialize(bool explain) const final; static boost::intrusive_ptr<ExpressionObject> create( @@ -1146,7 +1143,7 @@ public: : ExpressionVariadic<ExpressionOr>(expCtx) {} boost::intrusive_ptr<Expression> optimize() final; - Value evaluateInternal(Variables* vars) const final; + Value evaluateInternal() const final; const char* getOpName() const final; bool isAssociative() const final { @@ -1167,7 +1164,7 @@ public: const boost::intrusive_ptr<ExpressionContext>& expCtx, Value base, Value exp); private: - Value evaluateInternal(Variables* vars) const final; + Value evaluateInternal() const final; const char* getOpName() const final; }; @@ -1177,7 +1174,7 @@ public: explicit ExpressionRange(const boost::intrusive_ptr<ExpressionContext>& expCtx) : ExpressionRangedArity<ExpressionRange, 2, 3>(expCtx) {} - Value evaluateInternal(Variables* vars) const final; + Value evaluateInternal() const final; const char* getOpName() const final; }; @@ -1188,7 +1185,7 @@ public: : Expression(expCtx) {} void addDependencies(DepsTracker* deps) const final; - Value evaluateInternal(Variables* vars) const final; + Value evaluateInternal() const final; boost::intrusive_ptr<Expression> optimize() final; static boost::intrusive_ptr<Expression> parse( const boost::intrusive_ptr<ExpressionContext>& expCtx, @@ -1211,7 +1208,7 @@ public: explicit ExpressionSecond(const boost::intrusive_ptr<ExpressionContext>& expCtx) : ExpressionFixedArity<ExpressionSecond, 1>(expCtx) {} - Value evaluateInternal(Variables* vars) const final; + Value evaluateInternal() const final; const char* getOpName() const final; static inline int extract(const tm& tm) { @@ -1225,7 +1222,7 @@ public: explicit ExpressionSetDifference(const boost::intrusive_ptr<ExpressionContext>& expCtx) : ExpressionFixedArity<ExpressionSetDifference, 2>(expCtx) {} - Value evaluateInternal(Variables* vars) const final; + Value evaluateInternal() const final; const char* getOpName() const final; }; @@ -1235,7 +1232,7 @@ public: explicit ExpressionSetEquals(const boost::intrusive_ptr<ExpressionContext>& expCtx) : ExpressionVariadic<ExpressionSetEquals>(expCtx) {} - Value evaluateInternal(Variables* vars) const final; + Value evaluateInternal() const final; const char* getOpName() const final; void validateArguments(const ExpressionVector& args) const final; }; @@ -1246,7 +1243,7 @@ public: explicit ExpressionSetIntersection(const boost::intrusive_ptr<ExpressionContext>& expCtx) : ExpressionVariadic<ExpressionSetIntersection>(expCtx) {} - Value evaluateInternal(Variables* vars) const final; + Value evaluateInternal() const final; const char* getOpName() const final; bool isAssociative() const final { @@ -1266,7 +1263,7 @@ public: : ExpressionFixedArity<ExpressionSetIsSubset, 2>(expCtx) {} boost::intrusive_ptr<Expression> optimize() override; - Value evaluateInternal(Variables* vars) const override; + Value evaluateInternal() const override; const char* getOpName() const final; private: @@ -1279,7 +1276,7 @@ public: explicit ExpressionSetUnion(const boost::intrusive_ptr<ExpressionContext>& expCtx) : ExpressionVariadic<ExpressionSetUnion>(expCtx) {} - Value evaluateInternal(Variables* vars) const final; + Value evaluateInternal() const final; const char* getOpName() const final; bool isAssociative() const final { @@ -1297,7 +1294,7 @@ public: explicit ExpressionSize(const boost::intrusive_ptr<ExpressionContext>& expCtx) : ExpressionFixedArity<ExpressionSize, 1>(expCtx) {} - Value evaluateInternal(Variables* vars) const final; + Value evaluateInternal() const final; const char* getOpName() const final; }; @@ -1307,7 +1304,7 @@ public: explicit ExpressionReverseArray(const boost::intrusive_ptr<ExpressionContext>& expCtx) : ExpressionFixedArity<ExpressionReverseArray, 1>(expCtx) {} - Value evaluateInternal(Variables* vars) const final; + Value evaluateInternal() const final; const char* getOpName() const final; }; @@ -1317,7 +1314,7 @@ public: explicit ExpressionSlice(const boost::intrusive_ptr<ExpressionContext>& expCtx) : ExpressionRangedArity<ExpressionSlice, 2, 3>(expCtx) {} - Value evaluateInternal(Variables* vars) const final; + Value evaluateInternal() const final; const char* getOpName() const final; }; @@ -1327,7 +1324,7 @@ public: explicit ExpressionIsArray(const boost::intrusive_ptr<ExpressionContext>& expCtx) : ExpressionFixedArity<ExpressionIsArray, 1>(expCtx) {} - Value evaluateInternal(Variables* vars) const final; + Value evaluateInternal() const final; const char* getOpName() const final; }; @@ -1337,7 +1334,7 @@ public: explicit ExpressionSplit(const boost::intrusive_ptr<ExpressionContext>& expCtx) : ExpressionFixedArity<ExpressionSplit, 2>(expCtx) {} - Value evaluateInternal(Variables* vars) const final; + Value evaluateInternal() const final; const char* getOpName() const final; }; @@ -1357,7 +1354,7 @@ public: explicit ExpressionStrcasecmp(const boost::intrusive_ptr<ExpressionContext>& expCtx) : ExpressionFixedArity<ExpressionStrcasecmp, 2>(expCtx) {} - Value evaluateInternal(Variables* vars) const final; + Value evaluateInternal() const final; const char* getOpName() const final; }; @@ -1367,7 +1364,7 @@ public: explicit ExpressionSubstrBytes(const boost::intrusive_ptr<ExpressionContext>& expCtx) : ExpressionFixedArity<ExpressionSubstrBytes, 3>(expCtx) {} - Value evaluateInternal(Variables* vars) const final; + Value evaluateInternal() const final; const char* getOpName() const; }; @@ -1377,7 +1374,7 @@ public: explicit ExpressionSubstrCP(const boost::intrusive_ptr<ExpressionContext>& expCtx) : ExpressionFixedArity<ExpressionSubstrCP, 3>(expCtx) {} - Value evaluateInternal(Variables* vars) const final; + Value evaluateInternal() const final; const char* getOpName() const final; }; @@ -1387,7 +1384,7 @@ public: explicit ExpressionStrLenBytes(const boost::intrusive_ptr<ExpressionContext>& expCtx) : ExpressionFixedArity<ExpressionStrLenBytes, 1>(expCtx) {} - Value evaluateInternal(Variables* vars) const final; + Value evaluateInternal() const final; const char* getOpName() const final; }; @@ -1397,7 +1394,7 @@ public: explicit ExpressionStrLenCP(const boost::intrusive_ptr<ExpressionContext>& expCtx) : ExpressionFixedArity<ExpressionStrLenCP, 1>(expCtx) {} - Value evaluateInternal(Variables* vars) const final; + Value evaluateInternal() const final; const char* getOpName() const final; }; @@ -1407,7 +1404,7 @@ public: explicit ExpressionSubtract(const boost::intrusive_ptr<ExpressionContext>& expCtx) : ExpressionFixedArity<ExpressionSubtract, 2>(expCtx) {} - Value evaluateInternal(Variables* vars) const final; + Value evaluateInternal() const final; const char* getOpName() const final; }; @@ -1418,7 +1415,7 @@ public: : Expression(expCtx) {} void addDependencies(DepsTracker* deps) const final; - Value evaluateInternal(Variables* vars) const final; + Value evaluateInternal() const final; boost::intrusive_ptr<Expression> optimize() final; static boost::intrusive_ptr<Expression> parse( const boost::intrusive_ptr<ExpressionContext>& expCtx, @@ -1440,7 +1437,7 @@ public: explicit ExpressionToLower(const boost::intrusive_ptr<ExpressionContext>& expCtx) : ExpressionFixedArity<ExpressionToLower, 1>(expCtx) {} - Value evaluateInternal(Variables* vars) const final; + Value evaluateInternal() const final; const char* getOpName() const final; }; @@ -1450,7 +1447,7 @@ public: explicit ExpressionToUpper(const boost::intrusive_ptr<ExpressionContext>& expCtx) : ExpressionFixedArity<ExpressionToUpper, 1>(expCtx) {} - Value evaluateInternal(Variables* vars) const final; + Value evaluateInternal() const final; const char* getOpName() const final; }; @@ -1470,7 +1467,7 @@ public: explicit ExpressionType(const boost::intrusive_ptr<ExpressionContext>& expCtx) : ExpressionFixedArity<ExpressionType, 1>(expCtx) {} - Value evaluateInternal(Variables* vars) const final; + Value evaluateInternal() const final; const char* getOpName() const final; }; @@ -1480,7 +1477,7 @@ public: explicit ExpressionWeek(const boost::intrusive_ptr<ExpressionContext>& expCtx) : ExpressionFixedArity<ExpressionWeek, 1>(expCtx) {} - Value evaluateInternal(Variables* vars) const final; + Value evaluateInternal() const final; const char* getOpName() const final; static int extract(const tm& tm); @@ -1492,7 +1489,7 @@ public: explicit ExpressionIsoWeekYear(const boost::intrusive_ptr<ExpressionContext>& expCtx) : ExpressionFixedArity<ExpressionIsoWeekYear, 1>(expCtx) {} - Value evaluateInternal(Variables* vars) const final; + Value evaluateInternal() const final; const char* getOpName() const final; static int extract(const tm& tm); @@ -1504,7 +1501,7 @@ public: explicit ExpressionIsoDayOfWeek(const boost::intrusive_ptr<ExpressionContext>& expCtx) : ExpressionFixedArity<ExpressionIsoDayOfWeek, 1>(expCtx) {} - Value evaluateInternal(Variables* vars) const final; + Value evaluateInternal() const final; const char* getOpName() const final; static int extract(const tm& tm); @@ -1516,7 +1513,7 @@ public: explicit ExpressionIsoWeek(const boost::intrusive_ptr<ExpressionContext>& expCtx) : ExpressionFixedArity<ExpressionIsoWeek, 1>(expCtx) {} - Value evaluateInternal(Variables* vars) const final; + Value evaluateInternal() const final; const char* getOpName() const final; static int extract(const tm& tm); @@ -1528,7 +1525,7 @@ public: explicit ExpressionYear(const boost::intrusive_ptr<ExpressionContext>& expCtx) : ExpressionFixedArity<ExpressionYear, 1>(expCtx) {} - Value evaluateInternal(Variables* vars) const final; + Value evaluateInternal() const final; const char* getOpName() const final; // tm_year is years since 1990 @@ -1544,7 +1541,7 @@ public: : Expression(expCtx) {} void addDependencies(DepsTracker* deps) const final; - Value evaluateInternal(Variables* vars) const final; + Value evaluateInternal() const final; boost::intrusive_ptr<Expression> optimize() final; static boost::intrusive_ptr<Expression> parse( const boost::intrusive_ptr<ExpressionContext>& expCtx, diff --git a/src/mongo/db/pipeline/expression_context.cpp b/src/mongo/db/pipeline/expression_context.cpp index 2b149b1af9c..d2a16a6ca87 100644 --- a/src/mongo/db/pipeline/expression_context.cpp +++ b/src/mongo/db/pipeline/expression_context.cpp @@ -50,6 +50,7 @@ ExpressionContext::ExpressionContext(OperationContext* opCtx, ns(request.getNamespaceString()), opCtx(opCtx), collation(request.getCollation()), + variablesParseState(variables.useIdGenerator()), _collator(std::move(collator)), _documentComparator(_collator.get()), _valueComparator(_collator.get()), diff --git a/src/mongo/db/pipeline/expression_context.h b/src/mongo/db/pipeline/expression_context.h index 4d4c0b75f3a..1d0f04dd1a0 100644 --- a/src/mongo/db/pipeline/expression_context.h +++ b/src/mongo/db/pipeline/expression_context.h @@ -40,6 +40,7 @@ #include "mongo/db/pipeline/aggregation_request.h" #include "mongo/db/pipeline/document_comparator.h" #include "mongo/db/pipeline/value_comparator.h" +#include "mongo/db/pipeline/variables.h" #include "mongo/db/query/collation/collator_interface.h" #include "mongo/db/query/explain_options.h" #include "mongo/util/intrusive_counter.h" @@ -117,13 +118,13 @@ public: // collation. BSONObj collation; + Variables variables; + VariablesParseState variablesParseState; + protected: static const int kInterruptCheckPeriod = 128; - /** - * Should only be used by 'ExpressionContextForTest'. - */ - ExpressionContext() = default; + ExpressionContext() : variablesParseState(variables.useIdGenerator()) {} /** * Sets '_collator' and resets '_documentComparator' and '_valueComparator'. diff --git a/src/mongo/db/pipeline/expression_test.cpp b/src/mongo/db/pipeline/expression_test.cpp index 4e35f76931d..a470873058a 100644 --- a/src/mongo/db/pipeline/expression_test.cpp +++ b/src/mongo/db/pipeline/expression_test.cpp @@ -60,8 +60,7 @@ using std::list; static Value evaluateExpression(const string& expressionName, const vector<ImplicitValue>& operands) { intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); - VariablesIdGenerator idGenerator; - VariablesParseState vps(&idGenerator); + VariablesParseState vps = expCtx->variablesParseState; const BSONObj obj = BSON(expressionName << ImplicitValue::convertToValue(operands)); auto expression = Expression::parseExpression(expCtx, obj, vps); Value result = expression->evaluate(Document()); @@ -145,13 +144,15 @@ Value valueFromBson(BSONObj obj) { template <typename T> intrusive_ptr<ExpressionConstant> makeConstant(T&& val) { - return ExpressionConstant::create(nullptr, Value(std::forward<T>(val))); + intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); + return ExpressionConstant::create(expCtx, Value(std::forward<T>(val))); } class ExpressionBaseTest : public unittest::Test { public: void addOperand(intrusive_ptr<ExpressionNary> expr, Value arg) { - expr->addOperand(ExpressionConstant::create(nullptr, arg)); + intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); + expr->addOperand(ExpressionConstant::create(expCtx, arg)); } }; @@ -171,13 +172,13 @@ public: /** A dummy child of ExpressionNary used for testing. */ class Testable : public ExpressionNary { public: - virtual Value evaluateInternal(Variables* vars) const { + virtual Value evaluateInternal() const { // Just put all the values in a list. // By default, this is not associative/commutative so the results will change if // instantiated as commutative or associative and operations are reordered. vector<Value> values; for (ExpressionVector::const_iterator i = vpOperand.begin(); i != vpOperand.end(); ++i) { - values.push_back((*i)->evaluateInternal(vars)); + values.push_back((*i)->evaluateInternal()); } return Value(values); } @@ -238,8 +239,7 @@ protected: void addOperandArrayToExpr(const intrusive_ptr<Testable>& expr, const BSONArray& operands) { intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); - VariablesIdGenerator idGenerator; - VariablesParseState vps(&idGenerator); + VariablesParseState vps = expCtx->variablesParseState; BSONObjIterator i(operands); while (i.more()) { BSONElement element = i.next(); @@ -253,12 +253,14 @@ protected: }; TEST_F(ExpressionNaryTest, AddedConstantOperandIsSerialized) { - _notAssociativeNorCommutative->addOperand(ExpressionConstant::create(nullptr, Value(9))); + intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); + _notAssociativeNorCommutative->addOperand(ExpressionConstant::create(expCtx, Value(9))); assertContents(_notAssociativeNorCommutative, BSON_ARRAY(9)); } TEST_F(ExpressionNaryTest, AddedFieldPathOperandIsSerialized) { - _notAssociativeNorCommutative->addOperand(ExpressionFieldPath::create(nullptr, "ab.c")); + intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); + _notAssociativeNorCommutative->addOperand(ExpressionFieldPath::create(expCtx, "ab.c")); assertContents(_notAssociativeNorCommutative, BSON_ARRAY("$ab.c")); } @@ -267,12 +269,14 @@ TEST_F(ExpressionNaryTest, ValidateEmptyDependencies) { } TEST_F(ExpressionNaryTest, ValidateConstantExpressionDependency) { - _notAssociativeNorCommutative->addOperand(ExpressionConstant::create(nullptr, Value(1))); + intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); + _notAssociativeNorCommutative->addOperand(ExpressionConstant::create(expCtx, Value(1))); assertDependencies(_notAssociativeNorCommutative, BSONArray()); } TEST_F(ExpressionNaryTest, ValidateFieldPathExpressionDependency) { - _notAssociativeNorCommutative->addOperand(ExpressionFieldPath::create(nullptr, "ab.c")); + intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); + _notAssociativeNorCommutative->addOperand(ExpressionFieldPath::create(expCtx, "ab.c")); assertDependencies(_notAssociativeNorCommutative, BSON_ARRAY("ab.c")); } @@ -283,8 +287,7 @@ TEST_F(ExpressionNaryTest, ValidateObjectExpressionDependency) { << "$r")); intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); BSONElement specElement = spec.firstElement(); - VariablesIdGenerator idGenerator; - VariablesParseState vps(&idGenerator); + VariablesParseState vps = expCtx->variablesParseState; _notAssociativeNorCommutative->addOperand( Expression::parseObject(expCtx, specElement.Obj(), vps)); assertDependencies(_notAssociativeNorCommutative, @@ -293,13 +296,15 @@ TEST_F(ExpressionNaryTest, ValidateObjectExpressionDependency) { } TEST_F(ExpressionNaryTest, SerializationToBsonObj) { - _notAssociativeNorCommutative->addOperand(ExpressionConstant::create(nullptr, Value(5))); + intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); + _notAssociativeNorCommutative->addOperand(ExpressionConstant::create(expCtx, Value(5))); ASSERT_BSONOBJ_EQ(BSON("foo" << BSON("$testable" << BSON_ARRAY(BSON("$const" << 5)))), BSON("foo" << _notAssociativeNorCommutative->serialize(false))); } TEST_F(ExpressionNaryTest, SerializationToBsonArr) { - _notAssociativeNorCommutative->addOperand(ExpressionConstant::create(nullptr, Value(5))); + intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); + _notAssociativeNorCommutative->addOperand(ExpressionConstant::create(expCtx, Value(5))); ASSERT_BSONOBJ_EQ(constify(BSON_ARRAY(BSON("$testable" << BSON_ARRAY(5)))), BSON_ARRAY(_notAssociativeNorCommutative->serialize(false))); } @@ -954,7 +959,7 @@ public: void run() { intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); intrusive_ptr<ExpressionNary> expression = new ExpressionAdd(expCtx); - expression->addOperand(ExpressionConstant::create(nullptr, Value(2))); + expression->addOperand(ExpressionConstant::create(expCtx, Value(2))); ASSERT_BSONOBJ_EQ(BSON("" << 2), toBson(expression->evaluate(Document()))); } }; @@ -973,7 +978,7 @@ public: void run() { intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); intrusive_ptr<ExpressionNary> expression = new ExpressionAdd(expCtx); - expression->addOperand(ExpressionConstant::create(nullptr, Value("a"_sd))); + expression->addOperand(ExpressionConstant::create(expCtx, Value("a"_sd))); ASSERT_THROWS(expression->evaluate(Document()), UserException); } }; @@ -984,14 +989,15 @@ public: void run() { intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); intrusive_ptr<ExpressionNary> expression = new ExpressionAdd(expCtx); - expression->addOperand(ExpressionConstant::create(nullptr, Value(true))); + expression->addOperand(ExpressionConstant::create(expCtx, Value(true))); ASSERT_THROWS(expression->evaluate(Document()), UserException); } }; class SingleOperandBase : public ExpectedResultBase { void populateOperands(intrusive_ptr<ExpressionNary>& expression) { - expression->addOperand(ExpressionConstant::create(nullptr, valueFromBson(operand()))); + intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); + expression->addOperand(ExpressionConstant::create(expCtx, valueFromBson(operand()))); } BSONObj expectedResult() { return operand(); @@ -1061,10 +1067,11 @@ public: protected: void populateOperands(intrusive_ptr<ExpressionNary>& expression) { + intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); expression->addOperand( - ExpressionConstant::create(nullptr, valueFromBson(_reverse ? operand2() : operand1()))); + ExpressionConstant::create(expCtx, valueFromBson(_reverse ? operand2() : operand1()))); expression->addOperand( - ExpressionConstant::create(nullptr, valueFromBson(_reverse ? operand1() : operand2()))); + ExpressionConstant::create(expCtx, valueFromBson(_reverse ? operand1() : operand2()))); } virtual BSONObj operand1() = 0; virtual BSONObj operand2() = 0; @@ -1219,8 +1226,7 @@ public: intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); BSONObj specObject = BSON("" << spec()); BSONElement specElement = specObject.firstElement(); - VariablesIdGenerator idGenerator; - VariablesParseState vps(&idGenerator); + VariablesParseState vps = expCtx->variablesParseState; intrusive_ptr<Expression> expression = Expression::parseOperand(expCtx, specElement, vps); ASSERT_BSONOBJ_EQ(constify(spec()), expressionToBson(expression)); ASSERT_BSONOBJ_EQ(BSON("" << expectedResult()), @@ -1242,8 +1248,7 @@ public: intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); BSONObj specObject = BSON("" << spec()); BSONElement specElement = specObject.firstElement(); - VariablesIdGenerator idGenerator; - VariablesParseState vps(&idGenerator); + VariablesParseState vps = expCtx->variablesParseState; intrusive_ptr<Expression> expression = Expression::parseOperand(expCtx, specElement, vps); ASSERT_BSONOBJ_EQ(constify(spec()), expressionToBson(expression)); intrusive_ptr<Expression> optimized = expression->optimize(); @@ -1516,8 +1521,9 @@ namespace CoerceToBool { class EvaluateTrue { public: void run() { - intrusive_ptr<Expression> nested = ExpressionConstant::create(nullptr, Value(5)); - intrusive_ptr<Expression> expression = ExpressionCoerceToBool::create(nullptr, nested); + intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); + intrusive_ptr<Expression> nested = ExpressionConstant::create(expCtx, Value(5)); + intrusive_ptr<Expression> expression = ExpressionCoerceToBool::create(expCtx, nested); ASSERT(expression->evaluate(Document()).getBool()); } }; @@ -1526,8 +1532,9 @@ public: class EvaluateFalse { public: void run() { - intrusive_ptr<Expression> nested = ExpressionConstant::create(nullptr, Value(0)); - intrusive_ptr<Expression> expression = ExpressionCoerceToBool::create(nullptr, nested); + intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); + intrusive_ptr<Expression> nested = ExpressionConstant::create(expCtx, Value(0)); + intrusive_ptr<Expression> expression = ExpressionCoerceToBool::create(expCtx, nested); ASSERT(!expression->evaluate(Document()).getBool()); } }; @@ -1536,8 +1543,9 @@ public: class Dependencies { public: void run() { - intrusive_ptr<Expression> nested = ExpressionFieldPath::create(nullptr, "a.b"); - intrusive_ptr<Expression> expression = ExpressionCoerceToBool::create(nullptr, nested); + intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); + intrusive_ptr<Expression> nested = ExpressionFieldPath::create(expCtx, "a.b"); + intrusive_ptr<Expression> expression = ExpressionCoerceToBool::create(expCtx, nested); DepsTracker dependencies; expression->addDependencies(&dependencies); ASSERT_EQUALS(1U, dependencies.fields.size()); @@ -1551,8 +1559,9 @@ public: class AddToBsonObj { public: void run() { + intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); intrusive_ptr<Expression> expression = - ExpressionCoerceToBool::create(nullptr, ExpressionFieldPath::create(nullptr, "foo")); + ExpressionCoerceToBool::create(expCtx, ExpressionFieldPath::create(expCtx, "foo")); // serialized as $and because CoerceToBool isn't an ExpressionNary assertBinaryEqual(fromjson("{field:{$and:['$foo']}}"), toBsonObj(expression)); @@ -1568,8 +1577,9 @@ private: class AddToBsonArray { public: void run() { + intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); intrusive_ptr<Expression> expression = - ExpressionCoerceToBool::create(nullptr, ExpressionFieldPath::create(nullptr, "foo")); + ExpressionCoerceToBool::create(expCtx, ExpressionFieldPath::create(expCtx, "foo")); // serialized as $and because CoerceToBool isn't an ExpressionNary assertBinaryEqual(BSON_ARRAY(fromjson("{$and:['$foo']}")), toBsonArray(expression)); @@ -1596,9 +1606,8 @@ public: void run() { BSONObj specObject = BSON("" << spec()); BSONElement specElement = specObject.firstElement(); - VariablesIdGenerator idGenerator; - VariablesParseState vps(&idGenerator); intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); + VariablesParseState vps = expCtx->variablesParseState; intrusive_ptr<Expression> expression = Expression::parseOperand(expCtx, specElement, vps); intrusive_ptr<Expression> optimized = expression->optimize(); ASSERT_BSONOBJ_EQ(constify(expectedOptimized()), expressionToBson(optimized)); @@ -1628,9 +1637,8 @@ public: OptimizeBase::run(); BSONObj specObject = BSON("" << spec()); BSONElement specElement = specObject.firstElement(); - VariablesIdGenerator idGenerator; - VariablesParseState vps(&idGenerator); intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); + VariablesParseState vps = expCtx->variablesParseState; intrusive_ptr<Expression> expression = Expression::parseOperand(expCtx, specElement, vps); // Check expression spec round trip. ASSERT_BSONOBJ_EQ(constify(spec()), expressionToBson(expression)); @@ -1670,8 +1678,7 @@ public: intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); BSONObj specObject = BSON("" << spec()); BSONElement specElement = specObject.firstElement(); - VariablesIdGenerator idGenerator; - VariablesParseState vps(&idGenerator); + VariablesParseState vps = expCtx->variablesParseState; ASSERT_THROWS(Expression::parseOperand(expCtx, specElement, vps), UserException); } @@ -1873,9 +1880,8 @@ public: void run() { BSONObj specObject = BSON("" << BSON("$ne" << BSON_ARRAY("a" << 1))); BSONElement specElement = specObject.firstElement(); - VariablesIdGenerator idGenerator; - VariablesParseState vps(&idGenerator); intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); + VariablesParseState vps = expCtx->variablesParseState; intrusive_ptr<Expression> expression = Expression::parseOperand(expCtx, specElement, vps); ASSERT_VALUE_EQ(expression->evaluate(Document()), Value(true)); } @@ -2008,7 +2014,8 @@ namespace Constant { class Create { public: void run() { - intrusive_ptr<Expression> expression = ExpressionConstant::create(nullptr, Value(5)); + intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); + intrusive_ptr<Expression> expression = ExpressionConstant::create(expCtx, Value(5)); assertBinaryEqual(BSON("" << 5), toBson(expression->evaluate(Document()))); } }; @@ -2021,8 +2028,7 @@ public: << "foo"); intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); BSONElement specElement = spec.firstElement(); - VariablesIdGenerator idGenerator; - VariablesParseState vps(&idGenerator); + VariablesParseState vps = expCtx->variablesParseState; intrusive_ptr<Expression> expression = ExpressionConstant::parse(expCtx, specElement, vps); assertBinaryEqual(BSON("" << "foo"), @@ -2034,7 +2040,8 @@ public: class Optimize { public: void run() { - intrusive_ptr<Expression> expression = ExpressionConstant::create(nullptr, Value(5)); + intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); + intrusive_ptr<Expression> expression = ExpressionConstant::create(expCtx, Value(5)); // An attempt to optimize returns the Expression itself. ASSERT_EQUALS(expression, expression->optimize()); } @@ -2044,7 +2051,8 @@ public: class Dependencies { public: void run() { - intrusive_ptr<Expression> expression = ExpressionConstant::create(nullptr, Value(5)); + intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); + intrusive_ptr<Expression> expression = ExpressionConstant::create(expCtx, Value(5)); DepsTracker dependencies; expression->addDependencies(&dependencies); ASSERT_EQUALS(0U, dependencies.fields.size()); @@ -2057,7 +2065,8 @@ public: class AddToBsonObj { public: void run() { - intrusive_ptr<Expression> expression = ExpressionConstant::create(nullptr, Value(5)); + intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); + intrusive_ptr<Expression> expression = ExpressionConstant::create(expCtx, Value(5)); // The constant is replaced with a $ expression. assertBinaryEqual(BSON("field" << BSON("$const" << 5)), toBsonObj(expression)); } @@ -2072,7 +2081,8 @@ private: class AddToBsonArray { public: void run() { - intrusive_ptr<Expression> expression = ExpressionConstant::create(nullptr, Value(5)); + intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); + intrusive_ptr<Expression> expression = ExpressionConstant::create(expCtx, Value(5)); // The constant is copied out as is. assertBinaryEqual(constify(BSON_ARRAY(5)), toBsonArray(expression)); } @@ -2086,12 +2096,14 @@ private: }; TEST(ExpressionConstantTest, ConstantOfValueMissingRemovesField) { - intrusive_ptr<Expression> expression = ExpressionConstant::create(nullptr, Value()); + intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); + intrusive_ptr<Expression> expression = ExpressionConstant::create(expCtx, Value()); assertBinaryEqual(BSONObj(), toBson(expression->evaluate(Document{{"foo", Value("bar"_sd)}}))); } TEST(ExpressionConstantTest, ConstantOfValueMissingSerializesToRemoveSystemVar) { - intrusive_ptr<Expression> expression = ExpressionConstant::create(nullptr, Value()); + intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); + intrusive_ptr<Expression> expression = ExpressionConstant::create(expCtx, Value()); assertBinaryEqual(BSON("field" << "$$REMOVE"), BSON("field" << expression->serialize(false))); @@ -2172,7 +2184,8 @@ namespace FieldPath { class Invalid { public: void run() { - ASSERT_THROWS(ExpressionFieldPath::create(nullptr, ""), UserException); + intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); + ASSERT_THROWS(ExpressionFieldPath::create(expCtx, ""), UserException); } }; @@ -2180,7 +2193,8 @@ public: class Optimize { public: void run() { - intrusive_ptr<Expression> expression = ExpressionFieldPath::create(nullptr, "a"); + intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); + intrusive_ptr<Expression> expression = ExpressionFieldPath::create(expCtx, "a"); // An attempt to optimize returns the Expression itself. ASSERT_EQUALS(expression, expression->optimize()); } @@ -2190,7 +2204,8 @@ public: class Dependencies { public: void run() { - intrusive_ptr<Expression> expression = ExpressionFieldPath::create(nullptr, "a.b"); + intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); + intrusive_ptr<Expression> expression = ExpressionFieldPath::create(expCtx, "a.b"); DepsTracker dependencies; expression->addDependencies(&dependencies); ASSERT_EQUALS(1U, dependencies.fields.size()); @@ -2204,7 +2219,8 @@ public: class Missing { public: void run() { - intrusive_ptr<Expression> expression = ExpressionFieldPath::create(nullptr, "a"); + intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); + intrusive_ptr<Expression> expression = ExpressionFieldPath::create(expCtx, "a"); assertBinaryEqual(fromjson("{}"), toBson(expression->evaluate(Document()))); } }; @@ -2213,7 +2229,8 @@ public: class Present { public: void run() { - intrusive_ptr<Expression> expression = ExpressionFieldPath::create(nullptr, "a"); + intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); + intrusive_ptr<Expression> expression = ExpressionFieldPath::create(expCtx, "a"); assertBinaryEqual(fromjson("{'':123}"), toBson(expression->evaluate(fromBson(BSON("a" << 123))))); } @@ -2223,7 +2240,8 @@ public: class NestedBelowNull { public: void run() { - intrusive_ptr<Expression> expression = ExpressionFieldPath::create(nullptr, "a.b"); + intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); + intrusive_ptr<Expression> expression = ExpressionFieldPath::create(expCtx, "a.b"); assertBinaryEqual(fromjson("{}"), toBson(expression->evaluate(fromBson(fromjson("{a:null}"))))); } @@ -2233,7 +2251,8 @@ public: class NestedBelowUndefined { public: void run() { - intrusive_ptr<Expression> expression = ExpressionFieldPath::create(nullptr, "a.b"); + intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); + intrusive_ptr<Expression> expression = ExpressionFieldPath::create(expCtx, "a.b"); assertBinaryEqual(fromjson("{}"), toBson(expression->evaluate(fromBson(fromjson("{a:undefined}"))))); } @@ -2243,7 +2262,8 @@ public: class NestedBelowMissing { public: void run() { - intrusive_ptr<Expression> expression = ExpressionFieldPath::create(nullptr, "a.b"); + intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); + intrusive_ptr<Expression> expression = ExpressionFieldPath::create(expCtx, "a.b"); assertBinaryEqual(fromjson("{}"), toBson(expression->evaluate(fromBson(fromjson("{z:1}"))))); } @@ -2253,7 +2273,8 @@ public: class NestedBelowInt { public: void run() { - intrusive_ptr<Expression> expression = ExpressionFieldPath::create(nullptr, "a.b"); + intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); + intrusive_ptr<Expression> expression = ExpressionFieldPath::create(expCtx, "a.b"); assertBinaryEqual(fromjson("{}"), toBson(expression->evaluate(fromBson(BSON("a" << 2))))); } }; @@ -2262,7 +2283,8 @@ public: class NestedValue { public: void run() { - intrusive_ptr<Expression> expression = ExpressionFieldPath::create(nullptr, "a.b"); + intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); + intrusive_ptr<Expression> expression = ExpressionFieldPath::create(expCtx, "a.b"); assertBinaryEqual(BSON("" << 55), toBson(expression->evaluate(fromBson(BSON("a" << BSON("b" << 55)))))); } @@ -2272,7 +2294,8 @@ public: class NestedBelowEmptyObject { public: void run() { - intrusive_ptr<Expression> expression = ExpressionFieldPath::create(nullptr, "a.b"); + intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); + intrusive_ptr<Expression> expression = ExpressionFieldPath::create(expCtx, "a.b"); assertBinaryEqual(fromjson("{}"), toBson(expression->evaluate(fromBson(BSON("a" << BSONObj()))))); } @@ -2282,7 +2305,8 @@ public: class NestedBelowEmptyArray { public: void run() { - intrusive_ptr<Expression> expression = ExpressionFieldPath::create(nullptr, "a.b"); + intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); + intrusive_ptr<Expression> expression = ExpressionFieldPath::create(expCtx, "a.b"); assertBinaryEqual(BSON("" << BSONArray()), toBson(expression->evaluate(fromBson(BSON("a" << BSONArray()))))); } @@ -2292,7 +2316,8 @@ public: class NestedBelowArrayWithNull { public: void run() { - intrusive_ptr<Expression> expression = ExpressionFieldPath::create(nullptr, "a.b"); + intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); + intrusive_ptr<Expression> expression = ExpressionFieldPath::create(expCtx, "a.b"); assertBinaryEqual(fromjson("{'':[]}"), toBson(expression->evaluate(fromBson(fromjson("{a:[null]}"))))); } @@ -2302,7 +2327,8 @@ public: class NestedBelowArrayWithUndefined { public: void run() { - intrusive_ptr<Expression> expression = ExpressionFieldPath::create(nullptr, "a.b"); + intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); + intrusive_ptr<Expression> expression = ExpressionFieldPath::create(expCtx, "a.b"); assertBinaryEqual(fromjson("{'':[]}"), toBson(expression->evaluate(fromBson(fromjson("{a:[undefined]}"))))); } @@ -2312,7 +2338,8 @@ public: class NestedBelowArrayWithInt { public: void run() { - intrusive_ptr<Expression> expression = ExpressionFieldPath::create(nullptr, "a.b"); + intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); + intrusive_ptr<Expression> expression = ExpressionFieldPath::create(expCtx, "a.b"); assertBinaryEqual(fromjson("{'':[]}"), toBson(expression->evaluate(fromBson(fromjson("{a:[1]}"))))); } @@ -2322,7 +2349,8 @@ public: class NestedWithinArray { public: void run() { - intrusive_ptr<Expression> expression = ExpressionFieldPath::create(nullptr, "a.b"); + intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); + intrusive_ptr<Expression> expression = ExpressionFieldPath::create(expCtx, "a.b"); assertBinaryEqual(fromjson("{'':[9]}"), toBson(expression->evaluate(fromBson(fromjson("{a:[{b:9}]}"))))); } @@ -2332,7 +2360,8 @@ public: class MultipleArrayValues { public: void run() { - intrusive_ptr<Expression> expression = ExpressionFieldPath::create(nullptr, "a.b"); + intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); + intrusive_ptr<Expression> expression = ExpressionFieldPath::create(expCtx, "a.b"); assertBinaryEqual(fromjson("{'':[9,20]}"), toBson(expression->evaluate( fromBson(fromjson("{a:[{b:9},null,undefined,{g:4},{b:20},{}]}"))))); @@ -2343,7 +2372,8 @@ public: class ExpandNestedArrays { public: void run() { - intrusive_ptr<Expression> expression = ExpressionFieldPath::create(nullptr, "a.b.c"); + intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); + intrusive_ptr<Expression> expression = ExpressionFieldPath::create(expCtx, "a.b.c"); assertBinaryEqual(fromjson("{'':[[1,2],3,[4],[[5]],[6,7]]}"), toBson(expression->evaluate(fromBson(fromjson("{a:[{b:[{c:1},{c:2}]}," "{b:{c:3}}," @@ -2357,7 +2387,8 @@ public: class AddToBsonObj { public: void run() { - intrusive_ptr<Expression> expression = ExpressionFieldPath::create(nullptr, "a.b.c"); + intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); + intrusive_ptr<Expression> expression = ExpressionFieldPath::create(expCtx, "a.b.c"); assertBinaryEqual(BSON("foo" << "$a.b.c"), BSON("foo" << expression->serialize(false))); @@ -2368,7 +2399,8 @@ public: class AddToBsonArray { public: void run() { - intrusive_ptr<Expression> expression = ExpressionFieldPath::create(nullptr, "a.b.c"); + intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); + intrusive_ptr<Expression> expression = ExpressionFieldPath::create(expCtx, "a.b.c"); BSONArrayBuilder bab; bab << expression->serialize(false); assertBinaryEqual(BSON_ARRAY("$a.b.c"), bab.arr()); @@ -2391,16 +2423,14 @@ Document literal(T&& value) { TEST(ExpressionObjectParse, ShouldAcceptEmptyObject) { intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); - VariablesIdGenerator idGen; - VariablesParseState vps(&idGen); + VariablesParseState vps = expCtx->variablesParseState; auto object = ExpressionObject::parse(expCtx, BSONObj(), vps); ASSERT_VALUE_EQ(Value(Document{}), object->serialize(false)); } TEST(ExpressionObjectParse, ShouldAcceptLiteralsAsValues) { intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); - VariablesIdGenerator idGen; - VariablesParseState vps(&idGen); + VariablesParseState vps = expCtx->variablesParseState; auto object = ExpressionObject::parse(expCtx, BSON("a" << 5 << "b" << "string" @@ -2414,8 +2444,7 @@ TEST(ExpressionObjectParse, ShouldAcceptLiteralsAsValues) { TEST(ExpressionObjectParse, ShouldAccept_idAsFieldName) { intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); - VariablesIdGenerator idGen; - VariablesParseState vps(&idGen); + VariablesParseState vps = expCtx->variablesParseState; auto object = ExpressionObject::parse(expCtx, BSON("_id" << 5), vps); auto expectedResult = Value(Document{{"_id", literal(5)}}); ASSERT_VALUE_EQ(expectedResult, object->serialize(false)); @@ -2423,8 +2452,7 @@ TEST(ExpressionObjectParse, ShouldAccept_idAsFieldName) { TEST(ExpressionObjectParse, ShouldAcceptFieldNameContainingDollar) { intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); - VariablesIdGenerator idGen; - VariablesParseState vps(&idGen); + VariablesParseState vps = expCtx->variablesParseState; auto object = ExpressionObject::parse(expCtx, BSON("a$b" << 5), vps); auto expectedResult = Value(Document{{"a$b", literal(5)}}); ASSERT_VALUE_EQ(expectedResult, object->serialize(false)); @@ -2432,8 +2460,7 @@ TEST(ExpressionObjectParse, ShouldAcceptFieldNameContainingDollar) { TEST(ExpressionObjectParse, ShouldAcceptNestedObjects) { intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); - VariablesIdGenerator idGen; - VariablesParseState vps(&idGen); + VariablesParseState vps = expCtx->variablesParseState; auto object = ExpressionObject::parse(expCtx, fromjson("{a: {b: 1}, c: {d: {e: 1, f: 1}}}"), vps); auto expectedResult = @@ -2444,8 +2471,7 @@ TEST(ExpressionObjectParse, ShouldAcceptNestedObjects) { TEST(ExpressionObjectParse, ShouldAcceptArrays) { intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); - VariablesIdGenerator idGen; - VariablesParseState vps(&idGen); + VariablesParseState vps = expCtx->variablesParseState; auto object = ExpressionObject::parse(expCtx, fromjson("{a: [1, 2]}"), vps); auto expectedResult = Value(Document{{"a", vector<Value>{Value(literal(1)), Value(literal(2))}}}); @@ -2454,8 +2480,7 @@ TEST(ExpressionObjectParse, ShouldAcceptArrays) { TEST(ObjectParsing, ShouldAcceptExpressionAsValue) { intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); - VariablesIdGenerator idGen; - VariablesParseState vps(&idGen); + VariablesParseState vps = expCtx->variablesParseState; auto object = ExpressionObject::parse(expCtx, BSON("a" << BSON("$and" << BSONArray())), vps); ASSERT_VALUE_EQ(object->serialize(false), Value(Document{{"a", Document{{"$and", BSONArray()}}}})); @@ -2467,8 +2492,7 @@ TEST(ObjectParsing, ShouldAcceptExpressionAsValue) { TEST(ExpressionObjectParse, ShouldRejectDottedFieldNames) { intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); - VariablesIdGenerator idGen; - VariablesParseState vps(&idGen); + VariablesParseState vps = expCtx->variablesParseState; ASSERT_THROWS(ExpressionObject::parse(expCtx, BSON("a.b" << 1), vps), UserException); ASSERT_THROWS(ExpressionObject::parse(expCtx, BSON("c" << 3 << "a.b" << 1), vps), UserException); @@ -2478,8 +2502,7 @@ TEST(ExpressionObjectParse, ShouldRejectDottedFieldNames) { TEST(ExpressionObjectParse, ShouldRejectDuplicateFieldNames) { intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); - VariablesIdGenerator idGen; - VariablesParseState vps(&idGen); + VariablesParseState vps = expCtx->variablesParseState; ASSERT_THROWS(ExpressionObject::parse(expCtx, BSON("a" << 1 << "a" << 1), vps), UserException); ASSERT_THROWS(ExpressionObject::parse(expCtx, BSON("a" << 1 << "b" << 2 << "a" << 1), vps), UserException); @@ -2493,8 +2516,7 @@ TEST(ExpressionObjectParse, ShouldRejectDuplicateFieldNames) { TEST(ExpressionObjectParse, ShouldRejectInvalidFieldName) { intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); - VariablesIdGenerator idGen; - VariablesParseState vps(&idGen); + VariablesParseState vps = expCtx->variablesParseState; ASSERT_THROWS(ExpressionObject::parse(expCtx, BSON("$a" << 1), vps), UserException); ASSERT_THROWS(ExpressionObject::parse(expCtx, BSON("" << 1), vps), UserException); ASSERT_THROWS(ExpressionObject::parse(expCtx, BSON(std::string("a\0b", 3) << 1), vps), @@ -2503,8 +2525,7 @@ TEST(ExpressionObjectParse, ShouldRejectInvalidFieldName) { TEST(ExpressionObjectParse, ShouldRejectInvalidFieldPathAsValue) { intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); - VariablesIdGenerator idGen; - VariablesParseState vps(&idGen); + VariablesParseState vps = expCtx->variablesParseState; ASSERT_THROWS(ExpressionObject::parse(expCtx, BSON("a" << "$field."), @@ -2514,8 +2535,7 @@ TEST(ExpressionObjectParse, ShouldRejectInvalidFieldPathAsValue) { TEST(ParseObject, ShouldRejectExpressionAsTheSecondField) { intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); - VariablesIdGenerator idGen; - VariablesParseState vps(&idGen); + VariablesParseState vps = expCtx->variablesParseState; ASSERT_THROWS( ExpressionObject::parse( expCtx, BSON("a" << BSON("$and" << BSONArray()) << "$or" << BSONArray()), vps), @@ -2617,8 +2637,7 @@ TEST(ExpressionObjectDependencies, FieldPathsShouldBeAddedToDependencies) { TEST(ExpressionObjectOptimizations, OptimizingAnObjectShouldOptimizeSubExpressions) { // Build up the object {a: {$add: [1, 2]}}. intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); - VariablesIdGenerator idGen; - VariablesParseState vps(&idGen); + VariablesParseState vps = expCtx->variablesParseState; auto addExpression = ExpressionAdd::parse(expCtx, BSON("$add" << BSON_ARRAY(1 << 2)).firstElement(), vps); auto object = ExpressionObject::create(expCtx, {{"a", addExpression}}); @@ -2647,8 +2666,7 @@ public: intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); BSONObj specObject = BSON("" << spec()); BSONElement specElement = specObject.firstElement(); - VariablesIdGenerator idGenerator; - VariablesParseState vps(&idGenerator); + VariablesParseState vps = expCtx->variablesParseState; intrusive_ptr<Expression> expression = Expression::parseOperand(expCtx, specElement, vps); ASSERT_BSONOBJ_EQ(constify(spec()), expressionToBson(expression)); ASSERT_BSONOBJ_EQ(BSON("" << expectedResult()), @@ -2670,8 +2688,7 @@ public: intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); BSONObj specObject = BSON("" << spec()); BSONElement specElement = specObject.firstElement(); - VariablesIdGenerator idGenerator; - VariablesParseState vps(&idGenerator); + VariablesParseState vps = expCtx->variablesParseState; intrusive_ptr<Expression> expression = Expression::parseOperand(expCtx, specElement, vps); ASSERT_BSONOBJ_EQ(constify(spec()), expressionToBson(expression)); intrusive_ptr<Expression> optimized = expression->optimize(); @@ -2948,8 +2965,7 @@ namespace Object { */ boost::intrusive_ptr<Expression> parseObject(BSONObj specification) { intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); - VariablesIdGenerator idGenerator; - VariablesParseState vps(&idGenerator); + VariablesParseState vps = expCtx->variablesParseState; return Expression::parseObject(expCtx, specification, vps); }; @@ -2983,8 +2999,7 @@ using mongo::Expression; */ boost::intrusive_ptr<Expression> parseExpression(BSONObj specification) { const boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); - VariablesIdGenerator idGenerator; - VariablesParseState vps(&idGenerator); + VariablesParseState vps = expCtx->variablesParseState; return Expression::parseExpression(expCtx, specification, vps); } @@ -3091,8 +3106,7 @@ using mongo::Expression; intrusive_ptr<Expression> parseOperand(BSONObj specification) { intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); BSONElement specElement = specification.firstElement(); - VariablesIdGenerator idGenerator; - VariablesParseState vps(&idGenerator); + VariablesParseState vps = expCtx->variablesParseState; return Expression::parseOperand(expCtx, specElement, vps); } @@ -3164,8 +3178,7 @@ public: const Document::FieldPair field(fields.next()); const Value expected = field.second; const BSONObj obj = BSON(field.first << args); - VariablesIdGenerator idGenerator; - VariablesParseState vps(&idGenerator); + VariablesParseState vps = expCtx->variablesParseState; const intrusive_ptr<Expression> expr = Expression::parseExpression(expCtx, obj, vps); Value result = expr->evaluate(Document()); @@ -3188,8 +3201,7 @@ public: size_t n = asserters.size(); for (size_t i = 0; i < n; i++) { const BSONObj obj = BSON(asserters[i].getString() << args); - VariablesIdGenerator idGenerator; - VariablesParseState vps(&idGenerator); + VariablesParseState vps = expCtx->variablesParseState; ASSERT_THROWS( { // NOTE: parse and evaluatation failures are treated the @@ -3475,8 +3487,7 @@ private: intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); BSONObj specObj = BSON("" << spec); BSONElement specElement = specObj.firstElement(); - VariablesIdGenerator idGenerator; - VariablesParseState vps(&idGenerator); + VariablesParseState vps = expCtx->variablesParseState; intrusive_ptr<Expression> expression = Expression::parseOperand(expCtx, specElement, vps); ASSERT_BSONOBJ_EQ(constify(spec), expressionToBson(expression)); ASSERT_BSONOBJ_EQ(BSON("" << expectedResult), toBson(expression->evaluate(Document()))); @@ -3602,8 +3613,7 @@ public: intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); BSONObj specObj = BSON("" << spec()); BSONElement specElement = specObj.firstElement(); - VariablesIdGenerator idGenerator; - VariablesParseState vps(&idGenerator); + VariablesParseState vps = expCtx->variablesParseState; intrusive_ptr<Expression> expression = Expression::parseOperand(expCtx, specElement, vps); ASSERT_BSONOBJ_EQ(constify(spec()), expressionToBson(expression)); ASSERT_BSONOBJ_EQ(BSON("" << expectedResult()), toBson(expression->evaluate(Document()))); @@ -3707,8 +3717,7 @@ namespace SubstrCP { TEST(ExpressionSubstrCPTest, DoesThrowWithBadContinuationByte) { intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); - VariablesIdGenerator idGenerator; - VariablesParseState vps(&idGenerator); + VariablesParseState vps = expCtx->variablesParseState; const auto continuationByte = "\x80\x00"_sd; const auto expr = Expression::parseExpression( @@ -3718,8 +3727,7 @@ TEST(ExpressionSubstrCPTest, DoesThrowWithBadContinuationByte) { TEST(ExpressionSubstrCPTest, DoesThrowWithInvalidLeadingByte) { intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); - VariablesIdGenerator idGenerator; - VariablesParseState vps(&idGenerator); + VariablesParseState vps = expCtx->variablesParseState; const auto leadingByte = "\xFF\x00"_sd; const auto expr = Expression::parseExpression( @@ -3871,27 +3879,27 @@ TEST(BuiltinRemoveVariableTest, LiteralEscapesRemoveVar) { } TEST(BuiltinRemoveVariableTest, RemoveSerializesCorrectly) { - VariablesIdGenerator idGenerator{}; - VariablesParseState vps{&idGenerator}; - auto expression = ExpressionFieldPath::parse(nullptr, "$$REMOVE", vps); + intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); + VariablesParseState vps = expCtx->variablesParseState; + auto expression = ExpressionFieldPath::parse(expCtx, "$$REMOVE", vps); ASSERT_BSONOBJ_EQ(BSON("foo" << "$$REMOVE"), BSON("foo" << expression->serialize(false))); } TEST(BuiltinRemoveVariableTest, RemoveSerializesCorrectlyWithTrailingPath) { - VariablesIdGenerator idGenerator{}; - VariablesParseState vps{&idGenerator}; - auto expression = ExpressionFieldPath::parse(nullptr, "$$REMOVE.a.b", vps); + intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); + VariablesParseState vps = expCtx->variablesParseState; + auto expression = ExpressionFieldPath::parse(expCtx, "$$REMOVE.a.b", vps); ASSERT_BSONOBJ_EQ(BSON("foo" << "$$REMOVE.a.b"), BSON("foo" << expression->serialize(false))); } TEST(BuiltinRemoveVariableTest, RemoveSerializesCorrectlyAfterOptimization) { - VariablesIdGenerator idGenerator{}; - VariablesParseState vps{&idGenerator}; - auto expression = ExpressionFieldPath::parse(nullptr, "$$REMOVE.a.b", vps); + intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); + VariablesParseState vps = expCtx->variablesParseState; + auto expression = ExpressionFieldPath::parse(expCtx, "$$REMOVE.a.b", vps); auto optimizedExpression = expression->optimize(); ASSERT(dynamic_cast<ExpressionConstant*>(optimizedExpression.get())); ASSERT_BSONOBJ_EQ(BSON("foo" @@ -3985,8 +3993,7 @@ public: intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); BSONObj specObj = BSON("" << spec()); BSONElement specElement = specObj.firstElement(); - VariablesIdGenerator idGenerator; - VariablesParseState vps(&idGenerator); + VariablesParseState vps = expCtx->variablesParseState; intrusive_ptr<Expression> expression = Expression::parseOperand(expCtx, specElement, vps); ASSERT_BSONOBJ_EQ(constify(spec()), expressionToBson(expression)); ASSERT_BSONOBJ_EQ(BSON("" << expectedResult()), toBson(expression->evaluate(Document()))); @@ -4043,8 +4050,7 @@ public: intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); BSONObj specObj = BSON("" << spec()); BSONElement specElement = specObj.firstElement(); - VariablesIdGenerator idGenerator; - VariablesParseState vps(&idGenerator); + VariablesParseState vps = expCtx->variablesParseState; intrusive_ptr<Expression> expression = Expression::parseOperand(expCtx, specElement, vps); ASSERT_BSONOBJ_EQ(constify(spec()), expressionToBson(expression)); ASSERT_BSONOBJ_EQ(BSON("" << expectedResult()), toBson(expression->evaluate(Document()))); @@ -4106,8 +4112,7 @@ public: const Document::FieldPair field(fields.next()); const Value expected = field.second; const BSONObj obj = BSON(field.first << args); - VariablesIdGenerator idGenerator; - VariablesParseState vps(&idGenerator); + VariablesParseState vps = expCtx->variablesParseState; const intrusive_ptr<Expression> expr = Expression::parseExpression(expCtx, obj, vps); const Value result = expr->evaluate(Document()); @@ -4127,8 +4132,7 @@ public: size_t n = asserters.size(); for (size_t i = 0; i < n; i++) { const BSONObj obj = BSON(asserters[i].getString() << args); - VariablesIdGenerator idGenerator; - VariablesParseState vps(&idGenerator); + VariablesParseState vps = expCtx->variablesParseState; ASSERT_THROWS( { // NOTE: parse and evaluatation failures are treated the diff --git a/src/mongo/db/pipeline/granularity_rounder_powers_of_two.cpp b/src/mongo/db/pipeline/granularity_rounder_powers_of_two.cpp index 078936e8145..68182291d3a 100644 --- a/src/mongo/db/pipeline/granularity_rounder_powers_of_two.cpp +++ b/src/mongo/db/pipeline/granularity_rounder_powers_of_two.cpp @@ -80,8 +80,7 @@ Value GranularityRounderPowersOfTwo::roundUp(Value value) { exp = Value(63 - countLeadingZeros64(number) + 1); } - Variables vars; - return ExpressionPow::create(getExpCtx(), Value(2), exp)->evaluate(&vars); + return ExpressionPow::create(getExpCtx(), Value(2), exp)->evaluate(); } Value GranularityRounderPowersOfTwo::roundDown(Value value) { @@ -113,8 +112,7 @@ Value GranularityRounderPowersOfTwo::roundDown(Value value) { } } - Variables vars; - return ExpressionPow::create(getExpCtx(), Value(2), exp)->evaluate(&vars); + return ExpressionPow::create(getExpCtx(), Value(2), exp)->evaluate(); } string GranularityRounderPowersOfTwo::getName() { diff --git a/src/mongo/db/pipeline/parsed_add_fields.cpp b/src/mongo/db/pipeline/parsed_add_fields.cpp index ae78664a8a5..18b13c6aa61 100644 --- a/src/mongo/db/pipeline/parsed_add_fields.cpp +++ b/src/mongo/db/pipeline/parsed_add_fields.cpp @@ -46,22 +46,20 @@ std::unique_ptr<ParsedAddFields> ParsedAddFields::create( uasserted(status.location(), str::stream() << "Invalid $addFields specification: " << status.reason()); } - std::unique_ptr<ParsedAddFields> parsedAddFields = stdx::make_unique<ParsedAddFields>(); + std::unique_ptr<ParsedAddFields> parsedAddFields = stdx::make_unique<ParsedAddFields>(expCtx); // Actually parse the specification. - parsedAddFields->parse(expCtx, spec); + parsedAddFields->parse(spec); return parsedAddFields; } -void ParsedAddFields::parse(const boost::intrusive_ptr<ExpressionContext>& expCtx, - const BSONObj& spec, - const VariablesParseState& variablesParseState) { +void ParsedAddFields::parse(const BSONObj& spec) { for (auto elem : spec) { auto fieldName = elem.fieldNameStringData(); if (elem.type() == BSONType::Object) { // This is either an expression, or a nested specification. - if (parseObjectAsExpression(expCtx, fieldName, elem.Obj(), variablesParseState)) { + if (parseObjectAsExpression(fieldName, elem.Obj(), _expCtx->variablesParseState)) { // It was an expression. } else { // The field name might be a dotted path. If so, we need to keep adding children @@ -75,12 +73,13 @@ void ParsedAddFields::parse(const boost::intrusive_ptr<ExpressionContext>& expCt // It is illegal to construct an empty FieldPath, so the above loop ends one // iteration too soon. Add the last path here. child = child->addOrGetChild(remainingPath.fullPath()); - parseSubObject(expCtx, elem.Obj(), variablesParseState, child); + parseSubObject(elem.Obj(), _expCtx->variablesParseState, child); } } else { // This is a literal or regular value. - _root->addComputedField(FieldPath(elem.fieldName()), - Expression::parseOperand(expCtx, elem, variablesParseState)); + _root->addComputedField( + FieldPath(elem.fieldName()), + Expression::parseOperand(_expCtx, elem, _expCtx->variablesParseState)); } } } @@ -92,15 +91,14 @@ Document ParsedAddFields::applyProjection(Document inputDoc, Variables* vars) co // The output doc is the same as the input doc, with the added fields. MutableDocument output(inputDoc); - _root->addComputedFields(&output, vars); + _root->addComputedFields(&output); // Pass through the metadata. output.copyMetaDataFrom(inputDoc); return output.freeze(); } -bool ParsedAddFields::parseObjectAsExpression(const boost::intrusive_ptr<ExpressionContext>& expCtx, - StringData pathToObject, +bool ParsedAddFields::parseObjectAsExpression(StringData pathToObject, const BSONObj& objSpec, const VariablesParseState& variablesParseState) { if (objSpec.firstElementFieldName()[0] == '$') { @@ -108,14 +106,13 @@ bool ParsedAddFields::parseObjectAsExpression(const boost::intrusive_ptr<Express // field. invariant(objSpec.nFields() == 1); _root->addComputedField(pathToObject, - Expression::parseExpression(expCtx, objSpec, variablesParseState)); + Expression::parseExpression(_expCtx, objSpec, variablesParseState)); return true; } return false; } -void ParsedAddFields::parseSubObject(const boost::intrusive_ptr<ExpressionContext>& expCtx, - const BSONObj& subObj, +void ParsedAddFields::parseSubObject(const BSONObj& subObj, const VariablesParseState& variablesParseState, InclusionNode* node) { for (auto&& elem : subObj) { @@ -128,18 +125,17 @@ void ParsedAddFields::parseSubObject(const boost::intrusive_ptr<ExpressionContex // This is either an expression, or a nested specification. auto fieldName = elem.fieldNameStringData().toString(); if (!parseObjectAsExpression( - expCtx, FieldPath::getFullyQualifiedPath(node->getPath(), fieldName), elem.Obj(), variablesParseState)) { // It was a nested subobject auto child = node->addOrGetChild(fieldName); - parseSubObject(expCtx, elem.Obj(), variablesParseState, child); + parseSubObject(elem.Obj(), variablesParseState, child); } } else { // This is a literal or regular value. node->addComputedField(FieldPath(elem.fieldName()), - Expression::parseOperand(expCtx, elem, variablesParseState)); + Expression::parseOperand(_expCtx, elem, variablesParseState)); } } } diff --git a/src/mongo/db/pipeline/parsed_add_fields.h b/src/mongo/db/pipeline/parsed_add_fields.h index f4c1b535f63..261dbb903aa 100644 --- a/src/mongo/db/pipeline/parsed_add_fields.h +++ b/src/mongo/db/pipeline/parsed_add_fields.h @@ -51,7 +51,8 @@ namespace parsed_aggregation_projection { */ class ParsedAddFields : public ParsedAggregationProjection { public: - ParsedAddFields() : ParsedAggregationProjection(), _root(new InclusionNode()) {} + ParsedAddFields(const boost::intrusive_ptr<ExpressionContext>& expCtx) + : ParsedAggregationProjection(expCtx), _root(new InclusionNode()) {} /** * Creates the data needed to perform an AddFields. @@ -68,12 +69,7 @@ public: /** * Parses the addFields specification given by 'spec', populating internal data structures. */ - void parse(const boost::intrusive_ptr<ExpressionContext>& expCtx, const BSONObj& spec) final { - VariablesIdGenerator idGenerator; - VariablesParseState variablesParseState(&idGenerator); - parse(expCtx, spec, variablesParseState); - _variables = stdx::make_unique<Variables>(idGenerator.getIdCount()); - } + void parse(const BSONObj& spec) final; Document serialize(boost::optional<ExplainOptions::Verbosity> explain) const final { MutableDocument output; @@ -115,21 +111,15 @@ public: * with {"0": "hello"}. See SERVER-25200 for more details. */ Document applyProjection(Document inputDoc) const final { - _variables->setRoot(inputDoc); - return applyProjection(inputDoc, _variables.get()); + auto& variables = _expCtx->variables; + variables.setRoot(inputDoc); + return applyProjection(inputDoc, &variables); } Document applyProjection(Document inputDoc, Variables* vars) const; private: /** - * Parses 'spec' to determine which fields to add. - */ - void parse(const boost::intrusive_ptr<ExpressionContext>& expCtx, - const BSONObj& spec, - const VariablesParseState& variablesParseState); - - /** * Attempts to parse 'objSpec' as an expression like {$add: [...]}. Adds a computed field to * '_root' and returns true if it was successfully parsed as an expression. Returns false if it * was not an expression specification. @@ -137,8 +127,7 @@ private: * Throws an error if it was determined to be an expression specification, but failed to parse * as a valid expression. */ - bool parseObjectAsExpression(const boost::intrusive_ptr<ExpressionContext>& expCtx, - StringData pathToObject, + bool parseObjectAsExpression(StringData pathToObject, const BSONObj& objSpec, const VariablesParseState& variablesParseState); @@ -146,17 +135,12 @@ private: * Traverses 'subObj' and parses each field. Adds any computed fields at this level * to 'node'. */ - void parseSubObject(const boost::intrusive_ptr<ExpressionContext>& expCtx, - const BSONObj& subObj, + void parseSubObject(const BSONObj& subObj, const VariablesParseState& variablesParseState, InclusionNode* node); // The InclusionNode tree does most of the execution work once constructed. std::unique_ptr<InclusionNode> _root; - - // This is needed to give the expressions knowledge about the context in which they are being - // executed. - std::unique_ptr<Variables> _variables; }; } // namespace parsed_aggregation_projection } // namespace mongo diff --git a/src/mongo/db/pipeline/parsed_add_fields_test.cpp b/src/mongo/db/pipeline/parsed_add_fields_test.cpp index c9adc2b1a36..9a39b54ae8a 100644 --- a/src/mongo/db/pipeline/parsed_add_fields_test.cpp +++ b/src/mongo/db/pipeline/parsed_add_fields_test.cpp @@ -118,8 +118,8 @@ TEST(ParsedAddFields, DoesNotErrorOnTwoNestedFields) { // Verify that replaced fields are not included as dependencies. TEST(ParsedAddFieldsDeps, RemovesReplaceFieldsFromDependencies) { boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); - ParsedAddFields addition; - addition.parse(expCtx, BSON("a" << true)); + ParsedAddFields addition(expCtx); + addition.parse(BSON("a" << true)); DepsTracker deps; addition.addDependencies(&deps); @@ -132,8 +132,8 @@ TEST(ParsedAddFieldsDeps, RemovesReplaceFieldsFromDependencies) { // Verify that adding nested fields keeps the top-level field as a dependency. TEST(ParsedAddFieldsDeps, IncludesTopLevelFieldInDependenciesWhenAddingNestedFields) { boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); - ParsedAddFields addition; - addition.parse(expCtx, BSON("x.y" << true)); + ParsedAddFields addition(expCtx); + addition.parse(BSON("x.y" << true)); DepsTracker deps; addition.addDependencies(&deps); @@ -147,9 +147,8 @@ TEST(ParsedAddFieldsDeps, IncludesTopLevelFieldInDependenciesWhenAddingNestedFie // Verify that fields that an expression depends on are added to the dependencies. TEST(ParsedAddFieldsDeps, AddsDependenciesForComputedFields) { boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); - ParsedAddFields addition; - addition.parse(expCtx, - BSON("x.y" + ParsedAddFields addition(expCtx); + addition.parse(BSON("x.y" << "$z" << "a" << "$b")); @@ -169,8 +168,8 @@ TEST(ParsedAddFieldsDeps, AddsDependenciesForComputedFields) { // their corresponding $const form. TEST(ParsedAddFieldsSerialize, SerializesToCorrectForm) { boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); - ParsedAddFields addition; - addition.parse(expCtx, fromjson("{a: {$add: ['$a', 2]}, b: {d: 3}, 'x.y': {$literal: 4}}")); + ParsedAddFields addition(expCtx); + addition.parse(fromjson("{a: {$add: ['$a', 2]}, b: {d: 3}, 'x.y': {$literal: 4}}")); auto expectedSerialization = Document( fromjson("{a: {$add: [\"$a\", {$const: 2}]}, b: {d: {$const: 3}}, x: {y: {$const: 4}}}")); @@ -188,8 +187,8 @@ TEST(ParsedAddFieldsSerialize, SerializesToCorrectForm) { // Verify that serialize treats the _id field as any other field: including when explicity included. TEST(ParsedAddFieldsSerialize, AddsIdToSerializeWhenExplicitlyIncluded) { boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); - ParsedAddFields addition; - addition.parse(expCtx, BSON("_id" << false)); + ParsedAddFields addition(expCtx); + addition.parse(BSON("_id" << false)); // Adds explicit "_id" setting field, serializes expressions. auto expectedSerialization = Document(fromjson("{_id: {$const: false}}")); @@ -210,8 +209,8 @@ TEST(ParsedAddFieldsSerialize, AddsIdToSerializeWhenExplicitlyIncluded) { // fail. TEST(ParsedAddFieldsSerialize, OmitsIdFromSerializeWhenNotIncluded) { boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); - ParsedAddFields addition; - addition.parse(expCtx, BSON("a" << true)); + ParsedAddFields addition(expCtx); + addition.parse(BSON("a" << true)); // Does not implicitly include "_id" field. auto expectedSerialization = Document(fromjson("{a: {$const: true}}")); @@ -229,8 +228,8 @@ TEST(ParsedAddFieldsSerialize, OmitsIdFromSerializeWhenNotIncluded) { // Verify that the $addFields stage optimizes expressions into simpler forms when possible. TEST(ParsedAddFieldsOptimize, OptimizesTopLevelExpressions) { boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); - ParsedAddFields addition; - addition.parse(expCtx, BSON("a" << BSON("$add" << BSON_ARRAY(1 << 2)))); + ParsedAddFields addition(expCtx); + addition.parse(BSON("a" << BSON("$add" << BSON_ARRAY(1 << 2)))); addition.optimize(); auto expectedSerialization = Document{{"a", Document{{"$const", 3}}}}; @@ -247,8 +246,8 @@ TEST(ParsedAddFieldsOptimize, OptimizesTopLevelExpressions) { // Verify that the $addFields stage optimizes expressions even when they are nested. TEST(ParsedAddFieldsOptimize, ShouldOptimizeNestedExpressions) { boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); - ParsedAddFields addition; - addition.parse(expCtx, BSON("a.b" << BSON("$add" << BSON_ARRAY(1 << 2)))); + ParsedAddFields addition(expCtx); + addition.parse(BSON("a.b" << BSON("$add" << BSON_ARRAY(1 << 2)))); addition.optimize(); auto expectedSerialization = Document{{"a", Document{{"b", Document{{"$const", 3}}}}}}; @@ -269,8 +268,8 @@ TEST(ParsedAddFieldsOptimize, ShouldOptimizeNestedExpressions) { // Verify that a new field is added to the end of the document. TEST(ParsedAddFieldsExecutionTest, AddsNewFieldToEndOfDocument) { boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); - ParsedAddFields addition; - addition.parse(expCtx, BSON("c" << 3)); + ParsedAddFields addition(expCtx); + addition.parse(BSON("c" << 3)); // There are no fields in the document. auto result = addition.applyProjection(Document{}); @@ -286,8 +285,8 @@ TEST(ParsedAddFieldsExecutionTest, AddsNewFieldToEndOfDocument) { // Verify that an existing field is replaced and stays in the same order in the document. TEST(ParsedAddFieldsExecutionTest, ReplacesFieldThatAlreadyExistsInDocument) { boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); - ParsedAddFields addition; - addition.parse(expCtx, BSON("c" << 3)); + ParsedAddFields addition(expCtx); + addition.parse(BSON("c" << 3)); // Specified field is the only field in the document, and is replaced. auto result = addition.applyProjection(Document{{"c", 1}}); @@ -303,9 +302,8 @@ TEST(ParsedAddFieldsExecutionTest, ReplacesFieldThatAlreadyExistsInDocument) { // Verify that replacing multiple fields preserves the original field order in the document. TEST(ParsedAddFieldsExecutionTest, ReplacesMultipleFieldsWhilePreservingInputFieldOrder) { boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); - ParsedAddFields addition; - addition.parse(expCtx, - BSON("second" + ParsedAddFields addition(expCtx); + addition.parse(BSON("second" << "SECOND" << "first" << "FIRST")); @@ -317,9 +315,8 @@ TEST(ParsedAddFieldsExecutionTest, ReplacesMultipleFieldsWhilePreservingInputFie // Verify that adding multiple fields adds the fields in the order specified. TEST(ParsedAddFieldsExecutionTest, AddsNewFieldsAfterExistingFieldsInOrderSpecified) { boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); - ParsedAddFields addition; - addition.parse(expCtx, - BSON("firstComputed" + ParsedAddFields addition(expCtx); + addition.parse(BSON("firstComputed" << "FIRST" << "secondComputed" << "SECOND")); @@ -336,9 +333,8 @@ TEST(ParsedAddFieldsExecutionTest, AddsNewFieldsAfterExistingFieldsInOrderSpecif // each independently. TEST(ParsedAddFieldsExecutionTest, ReplacesAndAddsNewFieldsWithSameOrderingRulesAsSeparately) { boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); - ParsedAddFields addition; - addition.parse(expCtx, - BSON("firstComputed" + ParsedAddFields addition(expCtx); + addition.parse(BSON("firstComputed" << "FIRST" << "second" << "SECOND")); @@ -352,9 +348,8 @@ TEST(ParsedAddFieldsExecutionTest, ReplacesAndAddsNewFieldsWithSameOrderingRules // input document, when adding new fields. TEST(ParsedAddFieldsExecutionTest, IdFieldIsKeptInOrderItAppearsInInputDocument) { boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); - ParsedAddFields addition; - addition.parse(expCtx, - BSON("newField" + ParsedAddFields addition(expCtx); + addition.parse(BSON("newField" << "computedVal")); auto result = addition.applyProjection(Document{{"_id", "ID"_sd}, {"a", 1}}); auto expectedResult = Document{{"_id", "ID"_sd}, {"a", 1}, {"newField", "computedVal"_sd}}; @@ -368,9 +363,8 @@ TEST(ParsedAddFieldsExecutionTest, IdFieldIsKeptInOrderItAppearsInInputDocument) // Verify that replacing or adding _id works just like any other field. TEST(ParsedAddFieldsExecutionTest, ShouldReplaceIdWithComputedId) { boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); - ParsedAddFields addition; - addition.parse(expCtx, - BSON("_id" + ParsedAddFields addition(expCtx); + addition.parse(BSON("_id" << "newId")); auto result = addition.applyProjection(Document{{"_id", "ID"_sd}, {"a", 1}}); auto expectedResult = Document{{"_id", "newId"_sd}, {"a", 1}}; @@ -392,8 +386,8 @@ TEST(ParsedAddFieldsExecutionTest, ShouldReplaceIdWithComputedId) { // Verify that adding a dotted field keeps the other fields in the subdocument. TEST(ParsedAddFieldsExecutionTest, KeepsExistingSubFieldsWhenAddingSimpleDottedFieldToSubDoc) { boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); - ParsedAddFields addition; - addition.parse(expCtx, BSON("a.b" << true)); + ParsedAddFields addition(expCtx); + addition.parse(BSON("a.b" << true)); // More than one field in sub document. auto result = addition.applyProjection(Document{{"a", Document{{"b", 1}, {"c", 2}}}}); @@ -419,8 +413,8 @@ TEST(ParsedAddFieldsExecutionTest, KeepsExistingSubFieldsWhenAddingSimpleDottedF // Verify that creating a dotted field creates the subdocument structure necessary. TEST(ParsedAddFieldsExecutionTest, CreatesSubDocIfDottedAddedFieldDoesNotExist) { boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); - ParsedAddFields addition; - addition.parse(expCtx, BSON("sub.target" << true)); + ParsedAddFields addition(expCtx); + addition.parse(BSON("sub.target" << true)); // Should add the path if it doesn't exist. auto result = addition.applyProjection(Document{}); @@ -437,8 +431,8 @@ TEST(ParsedAddFieldsExecutionTest, CreatesSubDocIfDottedAddedFieldDoesNotExist) // SERVER-25200: make this agree with $set. TEST(ParsedAddFieldsExecutionTest, AppliesDottedAdditionToEachElementInArray) { boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); - ParsedAddFields addition; - addition.parse(expCtx, BSON("a.b" << true)); + ParsedAddFields addition(expCtx); + addition.parse(BSON("a.b" << true)); vector<Value> nestedValues = {Value(1), Value(Document{}), @@ -463,9 +457,8 @@ TEST(ParsedAddFieldsExecutionTest, AppliesDottedAdditionToEachElementInArray) { // Verify that creation of the subdocument structure works for many layers of nesting. TEST(ParsedAddFieldsExecutionTest, CreatesNestedSubDocumentsAllTheWayToAddedField) { boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); - ParsedAddFields addition; - addition.parse(expCtx, - BSON("a.b.c.d" + ParsedAddFields addition(expCtx); + addition.parse(BSON("a.b.c.d" << "computedVal")); // Should add the path if it doesn't exist. @@ -482,9 +475,8 @@ TEST(ParsedAddFieldsExecutionTest, CreatesNestedSubDocumentsAllTheWayToAddedFiel // Verify that _id is not special: we can add subfields to it as well. TEST(ParsedAddFieldsExecutionTest, AddsSubFieldsOfId) { boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); - ParsedAddFields addition; - addition.parse(expCtx, - BSON("_id.X" << true << "_id.Z" + ParsedAddFields addition(expCtx); + addition.parse(BSON("_id.X" << true << "_id.Z" << "NEW")); auto result = addition.applyProjection(Document{{"_id", Document{{"X", 1}, {"Y", 2}}}}); auto expectedResult = Document{{"_id", Document{{"X", true}, {"Y", 2}, {"Z", "NEW"_sd}}}}; @@ -495,11 +487,10 @@ TEST(ParsedAddFieldsExecutionTest, AddsSubFieldsOfId) { // can be used together in the same specification. TEST(ParsedAddFieldsExecutionTest, ShouldAllowMixedNestedAndDottedFields) { boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); - ParsedAddFields addition; + ParsedAddFields addition(expCtx); // Include all of "a.b", "a.c", "a.d", and "a.e". // Add new computed fields "a.W", "a.X", "a.Y", and "a.Z". - addition.parse(expCtx, - BSON("a.b" << true << "a.c" << true << "a.W" + addition.parse(BSON("a.b" << true << "a.c" << true << "a.W" << "W" << "a.X" << "X" @@ -527,9 +518,8 @@ TEST(ParsedAddFieldsExecutionTest, ShouldAllowMixedNestedAndDottedFields) { // Verify that adding nested fields preserves the addition order in the spec. TEST(ParsedAddFieldsExecutionTest, AddsNestedAddedFieldsInOrderSpecified) { boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); - ParsedAddFields addition; - addition.parse(expCtx, - BSON("b.d" + ParsedAddFields addition(expCtx); + addition.parse(BSON("b.d" << "FIRST" << "b.c" << "SECOND")); @@ -545,8 +535,8 @@ TEST(ParsedAddFieldsExecutionTest, AddsNestedAddedFieldsInOrderSpecified) { // Verify that the metadata is kept from the original input document. TEST(ParsedAddFieldsExecutionTest, AlwaysKeepsMetadataFromOriginalDoc) { boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); - ParsedAddFields addition; - addition.parse(expCtx, BSON("a" << true)); + ParsedAddFields addition(expCtx); + addition.parse(BSON("a" << true)); MutableDocument inputDocBuilder(Document{{"a", 1}}); inputDocBuilder.setRandMetaField(1.0); diff --git a/src/mongo/db/pipeline/parsed_aggregation_projection.cpp b/src/mongo/db/pipeline/parsed_aggregation_projection.cpp index 69b196faab3..26894147b17 100644 --- a/src/mongo/db/pipeline/parsed_aggregation_projection.cpp +++ b/src/mongo/db/pipeline/parsed_aggregation_projection.cpp @@ -278,11 +278,11 @@ std::unique_ptr<ParsedAggregationProjection> ParsedAggregationProjection::create // We can't use make_unique() here, since the branches have different types. std::unique_ptr<ParsedAggregationProjection> parsedProject( projectionType == ProjectionType::kInclusion - ? static_cast<ParsedAggregationProjection*>(new ParsedInclusionProjection()) - : static_cast<ParsedAggregationProjection*>(new ParsedExclusionProjection())); + ? static_cast<ParsedAggregationProjection*>(new ParsedInclusionProjection(expCtx)) + : static_cast<ParsedAggregationProjection*>(new ParsedExclusionProjection(expCtx))); // Actually parse the specification. - parsedProject->parse(expCtx, spec); + parsedProject->parse(spec); return parsedProject; } diff --git a/src/mongo/db/pipeline/parsed_aggregation_projection.h b/src/mongo/db/pipeline/parsed_aggregation_projection.h index 8d92e4ab768..227663a1342 100644 --- a/src/mongo/db/pipeline/parsed_aggregation_projection.h +++ b/src/mongo/db/pipeline/parsed_aggregation_projection.h @@ -133,8 +133,7 @@ public: * inclusions and exclusions. 'variablesParseState' is used by any contained expressions to * track which variables are defined so that they can later be referenced at execution time. */ - virtual void parse(const boost::intrusive_ptr<ExpressionContext>& expCtx, - const BSONObj& spec) = 0; + virtual void parse(const BSONObj& spec) = 0; /** * Optimize any expressions contained within this projection. @@ -156,12 +155,15 @@ public: } protected: - ParsedAggregationProjection() = default; + ParsedAggregationProjection(const boost::intrusive_ptr<ExpressionContext>& expCtx) + : _expCtx(expCtx){}; /** * Apply the projection to 'input'. */ virtual Document applyProjection(Document input) const = 0; + + boost::intrusive_ptr<ExpressionContext> _expCtx; }; } // namespace parsed_aggregation_projection } // namespace mongo diff --git a/src/mongo/db/pipeline/parsed_exclusion_projection.cpp b/src/mongo/db/pipeline/parsed_exclusion_projection.cpp index ae3fa4ba2dc..51809826dfe 100644 --- a/src/mongo/db/pipeline/parsed_exclusion_projection.cpp +++ b/src/mongo/db/pipeline/parsed_exclusion_projection.cpp @@ -144,10 +144,7 @@ Document ParsedExclusionProjection::applyProjection(Document inputDoc) const { return _root->applyProjection(inputDoc); } -void ParsedExclusionProjection::parse(const boost::intrusive_ptr<ExpressionContext>& expCtx, - const BSONObj& spec, - ExclusionNode* node, - size_t depth) { +void ParsedExclusionProjection::parse(const BSONObj& spec, ExclusionNode* node, size_t depth) { for (auto elem : spec) { const auto fieldName = elem.fieldNameStringData().toString(); @@ -192,7 +189,7 @@ void ParsedExclusionProjection::parse(const boost::intrusive_ptr<ExpressionConte child = child->addOrGetChild(fullPath.fullPath()); } - parse(expCtx, elem.Obj(), child, depth + 1); + parse(elem.Obj(), child, depth + 1); break; } default: { MONGO_UNREACHABLE; } diff --git a/src/mongo/db/pipeline/parsed_exclusion_projection.h b/src/mongo/db/pipeline/parsed_exclusion_projection.h index 8f64ac5258e..feb868f08ca 100644 --- a/src/mongo/db/pipeline/parsed_exclusion_projection.h +++ b/src/mongo/db/pipeline/parsed_exclusion_projection.h @@ -97,7 +97,8 @@ private: */ class ParsedExclusionProjection : public ParsedAggregationProjection { public: - ParsedExclusionProjection() : ParsedAggregationProjection(), _root(new ExclusionNode()) {} + ParsedExclusionProjection(const boost::intrusive_ptr<ExpressionContext>& expCtx) + : ParsedAggregationProjection(expCtx), _root(new ExclusionNode()) {} ProjectionType getType() const final { return ProjectionType::kExclusion; @@ -108,8 +109,8 @@ public: /** * Parses the projection specification given by 'spec', populating internal data structures. */ - void parse(const boost::intrusive_ptr<ExpressionContext>& expCtx, const BSONObj& spec) final { - parse(expCtx, spec, _root.get(), 0); + void parse(const BSONObj& spec) final { + parse(spec, _root.get(), 0); } /** @@ -134,10 +135,7 @@ private: * Traverses 'spec' and parses each field. Adds any excluded fields at this level to 'node', * and recurses on any sub-objects. */ - void parse(const boost::intrusive_ptr<ExpressionContext>& expCtx, - const BSONObj& spec, - ExclusionNode* node, - size_t depth); + void parse(const BSONObj& spec, ExclusionNode* node, size_t depth); // The ExclusionNode tree does most of the execution work once constructed. diff --git a/src/mongo/db/pipeline/parsed_exclusion_projection_test.cpp b/src/mongo/db/pipeline/parsed_exclusion_projection_test.cpp index f0e860067f7..a79950a13b3 100644 --- a/src/mongo/db/pipeline/parsed_exclusion_projection_test.cpp +++ b/src/mongo/db/pipeline/parsed_exclusion_projection_test.cpp @@ -57,33 +57,33 @@ using std::vector; DEATH_TEST(ExclusionProjection, ShouldRejectComputedField, "Invariant failure fieldName[0] != '$'") { - ParsedExclusionProjection exclusion; const boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); + ParsedExclusionProjection exclusion(expCtx); // Top-level expression. - exclusion.parse(expCtx, BSON("a" << false << "b" << BSON("$literal" << 1))); + exclusion.parse(BSON("a" << false << "b" << BSON("$literal" << 1))); } DEATH_TEST(ExclusionProjection, ShouldFailWhenGivenIncludedField, "Invariant failure !elem.trueValue()") { - ParsedExclusionProjection exclusion; const boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); - exclusion.parse(expCtx, BSON("a" << true)); + ParsedExclusionProjection exclusion(expCtx); + exclusion.parse(BSON("a" << true)); } DEATH_TEST(ExclusionProjection, ShouldFailWhenGivenIncludedId, "Invariant failure !elem.trueValue()") { - ParsedExclusionProjection exclusion; const boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); - exclusion.parse(expCtx, BSON("_id" << true << "a" << false)); + ParsedExclusionProjection exclusion(expCtx); + exclusion.parse(BSON("_id" << true << "a" << false)); } TEST(ExclusionProjection, ShouldSerializeToEquivalentProjection) { - ParsedExclusionProjection exclusion; const boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); + ParsedExclusionProjection exclusion(expCtx); exclusion.parse( - expCtx, fromjson("{a: 0, b: {c: NumberLong(0), d: 0.0}, 'x.y': false, _id: NumberInt(0)}")); + fromjson("{a: 0, b: {c: NumberLong(0), d: 0.0}, 'x.y': false, _id: NumberInt(0)}")); // Converts numbers to bools, converts dotted paths to nested documents. Note order of excluded // fields is subject to change. @@ -111,10 +111,9 @@ TEST(ExclusionProjection, ShouldNotAddAnyDependencies) { // need to include the "a" in the dependencies of this projection, since it will just be ignored // later. If there are no later stages, then we will finish the dependency computation // cycle without full knowledge of which fields are needed, and thus include all the fields. - ParsedExclusionProjection exclusion; const boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); - exclusion.parse(expCtx, - BSON("_id" << false << "a" << false << "b.c" << false << "x.y.z" << false)); + ParsedExclusionProjection exclusion(expCtx); + exclusion.parse(BSON("_id" << false << "a" << false << "b.c" << false << "x.y.z" << false)); DepsTracker deps; exclusion.addDependencies(&deps); @@ -125,9 +124,9 @@ TEST(ExclusionProjection, ShouldNotAddAnyDependencies) { } TEST(ExclusionProjection, ShouldReportExcludedFieldsAsModified) { - ParsedExclusionProjection exclusion; const boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); - exclusion.parse(expCtx, BSON("_id" << false << "a" << false << "b.c" << false)); + ParsedExclusionProjection exclusion(expCtx); + exclusion.parse(BSON("_id" << false << "a" << false << "b.c" << false)); auto modifiedPaths = exclusion.getModifiedPaths(); ASSERT(modifiedPaths.type == DocumentSource::GetModPathsReturn::Type::kFiniteSet); @@ -138,9 +137,9 @@ TEST(ExclusionProjection, ShouldReportExcludedFieldsAsModified) { } TEST(ExclusionProjection, ShouldReportExcludedFieldsAsModifiedWhenSpecifiedAsNestedObj) { - ParsedExclusionProjection exclusion; const boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); - exclusion.parse(expCtx, BSON("a" << BSON("b" << false << "c" << BSON("d" << false)))); + ParsedExclusionProjection exclusion(expCtx); + exclusion.parse(BSON("a" << BSON("b" << false << "c" << BSON("d" << false)))); auto modifiedPaths = exclusion.getModifiedPaths(); ASSERT(modifiedPaths.type == DocumentSource::GetModPathsReturn::Type::kFiniteSet); @@ -154,9 +153,9 @@ TEST(ExclusionProjection, ShouldReportExcludedFieldsAsModifiedWhenSpecifiedAsNes // TEST(ExclusionProjectionExecutionTest, ShouldExcludeTopLevelField) { - ParsedExclusionProjection exclusion; const boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); - exclusion.parse(expCtx, BSON("a" << false)); + ParsedExclusionProjection exclusion(expCtx); + exclusion.parse(BSON("a" << false)); // More than one field in document. auto result = exclusion.applyProjection(Document{{"a", 1}, {"b", 2}}); @@ -180,10 +179,9 @@ TEST(ExclusionProjectionExecutionTest, ShouldExcludeTopLevelField) { } TEST(ExclusionProjectionExecutionTest, ShouldCoerceNumericsToBools) { - ParsedExclusionProjection exclusion; const boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); - exclusion.parse(expCtx, - BSON("a" << Value(0) << "b" << Value(0LL) << "c" << Value(0.0) << "d" + ParsedExclusionProjection exclusion(expCtx); + exclusion.parse(BSON("a" << Value(0) << "b" << Value(0LL) << "c" << Value(0.0) << "d" << Value(Decimal128(0)))); auto result = @@ -193,36 +191,36 @@ TEST(ExclusionProjectionExecutionTest, ShouldCoerceNumericsToBools) { } TEST(ExclusionProjectionExecutionTest, ShouldPreserveOrderOfExistingFields) { - ParsedExclusionProjection exclusion; const boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); - exclusion.parse(expCtx, BSON("second" << false)); + ParsedExclusionProjection exclusion(expCtx); + exclusion.parse(BSON("second" << false)); auto result = exclusion.applyProjection(Document{{"first", 0}, {"second", 1}, {"third", 2}}); auto expectedResult = Document{{"first", 0}, {"third", 2}}; ASSERT_DOCUMENT_EQ(result, expectedResult); } TEST(ExclusionProjectionExecutionTest, ShouldImplicitlyIncludeId) { - ParsedExclusionProjection exclusion; const boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); - exclusion.parse(expCtx, BSON("a" << false)); + ParsedExclusionProjection exclusion(expCtx); + exclusion.parse(BSON("a" << false)); auto result = exclusion.applyProjection(Document{{"a", 1}, {"b", 2}, {"_id", "ID"_sd}}); auto expectedResult = Document{{"b", 2}, {"_id", "ID"_sd}}; ASSERT_DOCUMENT_EQ(result, expectedResult); } TEST(ExclusionProjectionExecutionTest, ShouldExcludeIdIfExplicitlyExcluded) { - ParsedExclusionProjection exclusion; const boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); - exclusion.parse(expCtx, BSON("a" << false << "_id" << false)); + ParsedExclusionProjection exclusion(expCtx); + exclusion.parse(BSON("a" << false << "_id" << false)); auto result = exclusion.applyProjection(Document{{"a", 1}, {"b", 2}, {"_id", "ID"_sd}}); auto expectedResult = Document{{"b", 2}}; ASSERT_DOCUMENT_EQ(result, expectedResult); } TEST(ExclusionProjectionExecutionTest, ShouldExcludeIdAndKeepAllOtherFields) { - ParsedExclusionProjection exclusion; const boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); - exclusion.parse(expCtx, BSON("_id" << false)); + ParsedExclusionProjection exclusion(expCtx); + exclusion.parse(BSON("_id" << false)); auto result = exclusion.applyProjection(Document{{"a", 1}, {"b", 2}, {"_id", "ID"_sd}}); auto expectedResult = Document{{"a", 1}, {"b", 2}}; ASSERT_DOCUMENT_EQ(result, expectedResult); @@ -233,9 +231,9 @@ TEST(ExclusionProjectionExecutionTest, ShouldExcludeIdAndKeepAllOtherFields) { // TEST(ExclusionProjectionExecutionTest, ShouldExcludeSubFieldsOfId) { - ParsedExclusionProjection exclusion; const boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); - exclusion.parse(expCtx, BSON("_id.x" << false << "_id" << BSON("y" << false))); + ParsedExclusionProjection exclusion(expCtx); + exclusion.parse(BSON("_id.x" << false << "_id" << BSON("y" << false))); auto result = exclusion.applyProjection( Document{{"_id", Document{{"x", 1}, {"y", 2}, {"z", 3}}}, {"a", 1}}); auto expectedResult = Document{{"_id", Document{{"z", 3}}}, {"a", 1}}; @@ -243,9 +241,9 @@ TEST(ExclusionProjectionExecutionTest, ShouldExcludeSubFieldsOfId) { } TEST(ExclusionProjectionExecutionTest, ShouldExcludeSimpleDottedFieldFromSubDoc) { - ParsedExclusionProjection exclusion; const boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); - exclusion.parse(expCtx, BSON("a.b" << false)); + ParsedExclusionProjection exclusion(expCtx); + exclusion.parse(BSON("a.b" << false)); // More than one field in sub document. auto result = exclusion.applyProjection(Document{{"a", Document{{"b", 1}, {"c", 2}}}}); @@ -269,9 +267,9 @@ TEST(ExclusionProjectionExecutionTest, ShouldExcludeSimpleDottedFieldFromSubDoc) } TEST(ExclusionProjectionExecutionTest, ShouldNotCreateSubDocIfDottedExcludedFieldDoesNotExist) { - ParsedExclusionProjection exclusion; const boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); - exclusion.parse(expCtx, BSON("sub.target" << false)); + ParsedExclusionProjection exclusion(expCtx); + exclusion.parse(BSON("sub.target" << false)); // Should not add the path if it doesn't exist. auto result = exclusion.applyProjection(Document{}); @@ -285,9 +283,9 @@ TEST(ExclusionProjectionExecutionTest, ShouldNotCreateSubDocIfDottedExcludedFiel } TEST(ExclusionProjectionExecutionTest, ShouldApplyDottedExclusionToEachElementInArray) { - ParsedExclusionProjection exclusion; const boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); - exclusion.parse(expCtx, BSON("a.b" << false)); + ParsedExclusionProjection exclusion(expCtx); + exclusion.parse(BSON("a.b" << false)); std::vector<Value> nestedValues = { Value(1), @@ -309,11 +307,10 @@ TEST(ExclusionProjectionExecutionTest, ShouldApplyDottedExclusionToEachElementIn } TEST(ExclusionProjectionExecutionTest, ShouldAllowMixedNestedAndDottedFields) { - ParsedExclusionProjection exclusion; const boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); + ParsedExclusionProjection exclusion(expCtx); // Exclude all of "a.b", "a.c", "a.d", and "a.e". exclusion.parse( - expCtx, BSON("a.b" << false << "a.c" << false << "a" << BSON("d" << false << "e" << false))); auto result = exclusion.applyProjection( Document{{"a", Document{{"b", 1}, {"c", 2}, {"d", 3}, {"e", 4}, {"f", 5}}}}); @@ -322,9 +319,9 @@ TEST(ExclusionProjectionExecutionTest, ShouldAllowMixedNestedAndDottedFields) { } TEST(ExclusionProjectionExecutionTest, ShouldAlwaysKeepMetadataFromOriginalDoc) { - ParsedExclusionProjection exclusion; const boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); - exclusion.parse(expCtx, BSON("a" << false)); + ParsedExclusionProjection exclusion(expCtx); + exclusion.parse(BSON("a" << false)); MutableDocument inputDocBuilder(Document{{"_id", "ID"_sd}, {"a", 1}}); inputDocBuilder.setRandMetaField(1.0); diff --git a/src/mongo/db/pipeline/parsed_inclusion_projection.cpp b/src/mongo/db/pipeline/parsed_inclusion_projection.cpp index a2c80238570..765ee6f125b 100644 --- a/src/mongo/db/pipeline/parsed_inclusion_projection.cpp +++ b/src/mongo/db/pipeline/parsed_inclusion_projection.cpp @@ -141,29 +141,29 @@ Value InclusionNode::applyInclusionsToValue(Value inputValue) const { } } -void InclusionNode::addComputedFields(MutableDocument* outputDoc, Variables* vars) const { +void InclusionNode::addComputedFields(MutableDocument* outputDoc) const { for (auto&& field : _orderToProcessAdditionsAndChildren) { auto childIt = _children.find(field); if (childIt != _children.end()) { outputDoc->setField(field, - childIt->second->addComputedFields(outputDoc->peek()[field], vars)); + childIt->second->addComputedFields(outputDoc->peek()[field])); } else { auto expressionIt = _expressions.find(field); invariant(expressionIt != _expressions.end()); - outputDoc->setField(field, expressionIt->second->evaluate(vars)); + outputDoc->setField(field, expressionIt->second->evaluate()); } } } -Value InclusionNode::addComputedFields(Value inputValue, Variables* vars) const { +Value InclusionNode::addComputedFields(Value inputValue) const { if (inputValue.getType() == BSONType::Object) { MutableDocument outputDoc(inputValue.getDocument()); - addComputedFields(&outputDoc, vars); + addComputedFields(&outputDoc); return outputDoc.freezeToValue(); } else if (inputValue.getType() == BSONType::Array) { std::vector<Value> values = inputValue.getArray(); for (auto it = values.begin(); it != values.end(); ++it) { - *it = addComputedFields(*it, vars); + *it = addComputedFields(*it); } return Value(std::move(values)); } else { @@ -172,7 +172,7 @@ Value InclusionNode::addComputedFields(Value inputValue, Variables* vars) const // document of all the computed values. This case represents applying a projection like // {"a.b": {$literal: 1}} to the document {a: 1}. This should yield {a: {b: 1}}. MutableDocument outputDoc; - addComputedFields(&outputDoc, vars); + addComputedFields(&outputDoc); return outputDoc.freezeToValue(); } // We didn't have any expressions, so just return the missing value. @@ -273,9 +273,7 @@ void InclusionNode::addComputedPaths(std::set<std::string>* computedPaths, // ParsedInclusionProjection // -void ParsedInclusionProjection::parse(const boost::intrusive_ptr<ExpressionContext>& expCtx, - const BSONObj& spec, - const VariablesParseState& variablesParseState) { +void ParsedInclusionProjection::parse(const BSONObj& spec) { // It is illegal to specify a projection with no output fields. bool atLeastOneFieldInOutput = false; @@ -311,7 +309,7 @@ void ParsedInclusionProjection::parse(const boost::intrusive_ptr<ExpressionConte } case BSONType::Object: { // This is either an expression, or a nested specification. - if (parseObjectAsExpression(expCtx, fieldName, elem.Obj(), variablesParseState)) { + if (parseObjectAsExpression(fieldName, elem.Obj(), _expCtx->variablesParseState)) { // It was an expression. break; } @@ -328,14 +326,14 @@ void ParsedInclusionProjection::parse(const boost::intrusive_ptr<ExpressionConte // iteration too soon. Add the last path here. child = child->addOrGetChild(remainingPath.fullPath()); - parseSubObject(expCtx, elem.Obj(), variablesParseState, child); + parseSubObject(elem.Obj(), _expCtx->variablesParseState, child); break; } default: { // This is a literal value. _root->addComputedField( FieldPath(elem.fieldName()), - Expression::parseOperand(expCtx, elem, variablesParseState)); + Expression::parseOperand(_expCtx, elem, _expCtx->variablesParseState)); } } } @@ -358,7 +356,7 @@ Document ParsedInclusionProjection::applyProjection(Document inputDoc, Variables MutableDocument output; _root->applyInclusions(inputDoc, &output); - _root->addComputedFields(&output, vars); + _root->addComputedFields(&output); // Always pass through the metadata. output.copyMetaDataFrom(inputDoc); @@ -366,7 +364,6 @@ Document ParsedInclusionProjection::applyProjection(Document inputDoc, Variables } bool ParsedInclusionProjection::parseObjectAsExpression( - const boost::intrusive_ptr<ExpressionContext>& expCtx, StringData pathToObject, const BSONObj& objSpec, const VariablesParseState& variablesParseState) { @@ -375,17 +372,15 @@ bool ParsedInclusionProjection::parseObjectAsExpression( // field. invariant(objSpec.nFields() == 1); _root->addComputedField(pathToObject, - Expression::parseExpression(expCtx, objSpec, variablesParseState)); + Expression::parseExpression(_expCtx, objSpec, variablesParseState)); return true; } return false; } -void ParsedInclusionProjection::parseSubObject( - const boost::intrusive_ptr<ExpressionContext>& expCtx, - const BSONObj& subObj, - const VariablesParseState& variablesParseState, - InclusionNode* node) { +void ParsedInclusionProjection::parseSubObject(const BSONObj& subObj, + const VariablesParseState& variablesParseState, + InclusionNode* node) { for (auto elem : subObj) { invariant(elem.fieldName()[0] != '$'); // Dotted paths in a sub-object have already been disallowed in @@ -407,20 +402,20 @@ void ParsedInclusionProjection::parseSubObject( // This is either an expression, or a nested specification. auto fieldName = elem.fieldNameStringData().toString(); if (parseObjectAsExpression( - expCtx, FieldPath::getFullyQualifiedPath(node->getPath(), fieldName), elem.Obj(), variablesParseState)) { break; } auto child = node->addOrGetChild(fieldName); - parseSubObject(expCtx, elem.Obj(), variablesParseState, child); + parseSubObject(elem.Obj(), variablesParseState, child); break; } default: { // This is a literal value. - node->addComputedField(FieldPath(elem.fieldName()), - Expression::parseOperand(expCtx, elem, variablesParseState)); + node->addComputedField( + FieldPath(elem.fieldName()), + Expression::parseOperand(_expCtx, elem, variablesParseState)); } } } diff --git a/src/mongo/db/pipeline/parsed_inclusion_projection.h b/src/mongo/db/pipeline/parsed_inclusion_projection.h index eb81015f8a4..f86b9970519 100644 --- a/src/mongo/db/pipeline/parsed_inclusion_projection.h +++ b/src/mongo/db/pipeline/parsed_inclusion_projection.h @@ -86,10 +86,9 @@ public: void applyInclusions(Document inputDoc, MutableDocument* outputDoc) const; /** - * Add computed fields to 'outputDoc'. 'vars' is passed through to be used in Expression - * evaluation. + * Add computed fields to 'outputDoc'. */ - void addComputedFields(MutableDocument* outputDoc, Variables* vars) const; + void addComputedFields(MutableDocument* outputDoc) const; /** * Creates the child if it doesn't already exist. 'field' is not allowed to be dotted. @@ -140,7 +139,7 @@ private: // Helpers for the Document versions above. These will apply the transformation recursively to // each element of any arrays, and ensure non-documents are handled appropriately. Value applyInclusionsToValue(Value inputVal) const; - Value addComputedFields(Value inputVal, Variables* vars) const; + Value addComputedFields(Value inputVal) const; /** * Returns nullptr if no such child exists. @@ -185,7 +184,8 @@ private: */ class ParsedInclusionProjection : public ParsedAggregationProjection { public: - ParsedInclusionProjection() : ParsedAggregationProjection(), _root(new InclusionNode()) {} + ParsedInclusionProjection(const boost::intrusive_ptr<ExpressionContext>& expCtx) + : ParsedAggregationProjection(expCtx), _root(new InclusionNode()) {} ProjectionType getType() const final { return ProjectionType::kInclusion; @@ -194,12 +194,7 @@ public: /** * Parses the projection specification given by 'spec', populating internal data structures. */ - void parse(const boost::intrusive_ptr<ExpressionContext>& expCtx, const BSONObj& spec) final { - VariablesIdGenerator idGenerator; - VariablesParseState variablesParseState(&idGenerator); - parse(expCtx, spec, variablesParseState); - _variables = stdx::make_unique<Variables>(idGenerator.getIdCount()); - } + void parse(const BSONObj& spec) final; /** * Serialize the projection. @@ -248,22 +243,15 @@ public: * each element in the array. */ Document applyProjection(Document inputDoc) const final { - _variables->setRoot(inputDoc); - return applyProjection(inputDoc, _variables.get()); + auto& vars = _expCtx->variables; + vars.setRoot(inputDoc); + return applyProjection(inputDoc, &vars); } Document applyProjection(Document inputDoc, Variables* vars) const; private: /** - * Parses 'spec' to determine which fields to include, which are computed, and whether to - * include '_id' or not. - */ - void parse(const boost::intrusive_ptr<ExpressionContext>& expCtx, - const BSONObj& spec, - const VariablesParseState& variablesParseState); - - /** * Attempts to parse 'objSpec' as an expression like {$add: [...]}. Adds a computed field to * '_root' and returns true if it was successfully parsed as an expression. Returns false if it * was not an expression specification. @@ -271,8 +259,7 @@ private: * Throws an error if it was determined to be an expression specification, but failed to parse * as a valid expression. */ - bool parseObjectAsExpression(const boost::intrusive_ptr<ExpressionContext>& expCtx, - StringData pathToObject, + bool parseObjectAsExpression(StringData pathToObject, const BSONObj& objSpec, const VariablesParseState& variablesParseState); @@ -280,8 +267,7 @@ private: * Traverses 'subObj' and parses each field. Adds any included or computed fields at this level * to 'node'. */ - void parseSubObject(const boost::intrusive_ptr<ExpressionContext>& expCtx, - const BSONObj& subObj, + void parseSubObject(const BSONObj& subObj, const VariablesParseState& variablesParseState, InclusionNode* node); @@ -290,10 +276,6 @@ private: // The InclusionNode tree does most of the execution work once constructed. std::unique_ptr<InclusionNode> _root; - - // This is needed to give the expressions knowledge about the context in which they are being - // executed. - std::unique_ptr<Variables> _variables; }; } // namespace parsed_aggregation_projection } // namespace mongo diff --git a/src/mongo/db/pipeline/parsed_inclusion_projection_test.cpp b/src/mongo/db/pipeline/parsed_inclusion_projection_test.cpp index a0e7398ef6f..add0c9e7bb3 100644 --- a/src/mongo/db/pipeline/parsed_inclusion_projection_test.cpp +++ b/src/mongo/db/pipeline/parsed_inclusion_projection_test.cpp @@ -53,24 +53,23 @@ BSONObj wrapInLiteral(const T& arg) { } TEST(InclusionProjection, ShouldThrowWhenParsingInvalidExpression) { - ParsedInclusionProjection inclusion; const boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); - ASSERT_THROWS(inclusion.parse(expCtx, - BSON("a" << BSON("$gt" << BSON("bad" + ParsedInclusionProjection inclusion(expCtx); + ASSERT_THROWS(inclusion.parse(BSON("a" << BSON("$gt" << BSON("bad" << "arguments")))), UserException); } TEST(InclusionProjection, ShouldRejectProjectionWithNoOutputFields) { - ParsedInclusionProjection inclusion; const boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); - ASSERT_THROWS(inclusion.parse(expCtx, BSON("_id" << false)), UserException); + ParsedInclusionProjection inclusion(expCtx); + ASSERT_THROWS(inclusion.parse(BSON("_id" << false)), UserException); } TEST(InclusionProjection, ShouldAddIncludedFieldsToDependencies) { - ParsedInclusionProjection inclusion; const boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); - inclusion.parse(expCtx, BSON("_id" << false << "a" << true << "x.y" << true)); + ParsedInclusionProjection inclusion(expCtx); + inclusion.parse(BSON("_id" << false << "a" << true << "x.y" << true)); DepsTracker deps; inclusion.addDependencies(&deps); @@ -82,9 +81,9 @@ TEST(InclusionProjection, ShouldAddIncludedFieldsToDependencies) { } TEST(InclusionProjection, ShouldAddIdToDependenciesIfNotSpecified) { - ParsedInclusionProjection inclusion; const boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); - inclusion.parse(expCtx, BSON("a" << true)); + ParsedInclusionProjection inclusion(expCtx); + inclusion.parse(BSON("a" << true)); DepsTracker deps; inclusion.addDependencies(&deps); @@ -95,10 +94,9 @@ TEST(InclusionProjection, ShouldAddIdToDependenciesIfNotSpecified) { } TEST(InclusionProjection, ShouldAddDependenciesOfComputedFields) { - ParsedInclusionProjection inclusion; const boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); - inclusion.parse(expCtx, - BSON("a" + ParsedInclusionProjection inclusion(expCtx); + inclusion.parse(BSON("a" << "$a" << "x" << "$z")); @@ -113,10 +111,9 @@ TEST(InclusionProjection, ShouldAddDependenciesOfComputedFields) { } TEST(InclusionProjection, ShouldAddPathToDependenciesForNestedComputedFields) { - ParsedInclusionProjection inclusion; const boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); - inclusion.parse(expCtx, - BSON("x.y" + ParsedInclusionProjection inclusion(expCtx); + inclusion.parse(BSON("x.y" << "$z")); DepsTracker deps; @@ -132,9 +129,9 @@ TEST(InclusionProjection, ShouldAddPathToDependenciesForNestedComputedFields) { } TEST(InclusionProjection, ShouldSerializeToEquivalentProjection) { - ParsedInclusionProjection inclusion; const boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); - inclusion.parse(expCtx, fromjson("{a: {$add: ['$a', 2]}, b: {d: 3}, 'x.y': {$literal: 4}}")); + ParsedInclusionProjection inclusion(expCtx); + inclusion.parse(fromjson("{a: {$add: ['$a', 2]}, b: {d: 3}, 'x.y': {$literal: 4}}")); // Adds implicit "_id" inclusion, converts numbers to bools, serializes expressions. auto expectedSerialization = Document(fromjson( @@ -151,9 +148,9 @@ TEST(InclusionProjection, ShouldSerializeToEquivalentProjection) { } TEST(InclusionProjection, ShouldSerializeExplicitExclusionOfId) { - ParsedInclusionProjection inclusion; const boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); - inclusion.parse(expCtx, BSON("_id" << false << "a" << true)); + ParsedInclusionProjection inclusion(expCtx); + inclusion.parse(BSON("_id" << false << "a" << true)); // Adds implicit "_id" inclusion, converts numbers to bools, serializes expressions. auto expectedSerialization = Document{{"_id", false}, {"a", true}}; @@ -170,9 +167,9 @@ TEST(InclusionProjection, ShouldSerializeExplicitExclusionOfId) { TEST(InclusionProjection, ShouldOptimizeTopLevelExpressions) { - ParsedInclusionProjection inclusion; const boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); - inclusion.parse(expCtx, BSON("a" << BSON("$add" << BSON_ARRAY(1 << 2)))); + ParsedInclusionProjection inclusion(expCtx); + inclusion.parse(BSON("a" << BSON("$add" << BSON_ARRAY(1 << 2)))); inclusion.optimize(); @@ -189,9 +186,9 @@ TEST(InclusionProjection, ShouldOptimizeTopLevelExpressions) { } TEST(InclusionProjection, ShouldOptimizeNestedExpressions) { - ParsedInclusionProjection inclusion; const boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); - inclusion.parse(expCtx, BSON("a.b" << BSON("$add" << BSON_ARRAY(1 << 2)))); + ParsedInclusionProjection inclusion(expCtx); + inclusion.parse(BSON("a.b" << BSON("$add" << BSON_ARRAY(1 << 2)))); inclusion.optimize(); @@ -209,14 +206,12 @@ TEST(InclusionProjection, ShouldOptimizeNestedExpressions) { } TEST(InclusionProjection, ShouldReportThatAllExceptIncludedFieldsAreModified) { - ParsedInclusionProjection inclusion; const boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); - inclusion.parse( - expCtx, - BSON("a" << wrapInLiteral("computedVal") << "b.c" << wrapInLiteral("computedVal") << "d" - << true - << "e.f" - << true)); + ParsedInclusionProjection inclusion(expCtx); + inclusion.parse(BSON( + "a" << wrapInLiteral("computedVal") << "b.c" << wrapInLiteral("computedVal") << "d" << true + << "e.f" + << true)); auto modifiedPaths = inclusion.getModifiedPaths(); ASSERT(modifiedPaths.type == DocumentSource::GetModPathsReturn::Type::kAllExcept); @@ -231,10 +226,9 @@ TEST(InclusionProjection, ShouldReportThatAllExceptIncludedFieldsAreModified) { } TEST(InclusionProjection, ShouldReportThatAllExceptIncludedFieldsAreModifiedWithIdExclusion) { - ParsedInclusionProjection inclusion; const boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); - inclusion.parse(expCtx, - BSON("_id" << false << "a" << wrapInLiteral("computedVal") << "b.c" + ParsedInclusionProjection inclusion(expCtx); + inclusion.parse(BSON("_id" << false << "a" << wrapInLiteral("computedVal") << "b.c" << wrapInLiteral("computedVal") << "d" << true @@ -260,9 +254,9 @@ TEST(InclusionProjection, ShouldReportThatAllExceptIncludedFieldsAreModifiedWith // TEST(InclusionProjectionExecutionTest, ShouldIncludeTopLevelField) { - ParsedInclusionProjection inclusion; const boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); - inclusion.parse(expCtx, BSON("a" << true)); + ParsedInclusionProjection inclusion(expCtx); + inclusion.parse(BSON("a" << true)); // More than one field in document. auto result = inclusion.applyProjection(Document{{"a", 1}, {"b", 2}}); @@ -286,9 +280,9 @@ TEST(InclusionProjectionExecutionTest, ShouldIncludeTopLevelField) { } TEST(InclusionProjectionExecutionTest, ShouldAddComputedTopLevelField) { - ParsedInclusionProjection inclusion; const boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); - inclusion.parse(expCtx, BSON("newField" << wrapInLiteral("computedVal"))); + ParsedInclusionProjection inclusion(expCtx); + inclusion.parse(BSON("newField" << wrapInLiteral("computedVal"))); auto result = inclusion.applyProjection(Document{}); auto expectedResult = Document{{"newField", "computedVal"_sd}}; ASSERT_DOCUMENT_EQ(result, expectedResult); @@ -300,28 +294,27 @@ TEST(InclusionProjectionExecutionTest, ShouldAddComputedTopLevelField) { } TEST(InclusionProjectionExecutionTest, ShouldApplyBothInclusionsAndComputedFields) { - ParsedInclusionProjection inclusion; const boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); - inclusion.parse(expCtx, BSON("a" << true << "newField" << wrapInLiteral("computedVal"))); + ParsedInclusionProjection inclusion(expCtx); + inclusion.parse(BSON("a" << true << "newField" << wrapInLiteral("computedVal"))); auto result = inclusion.applyProjection(Document{{"a", 1}}); auto expectedResult = Document{{"a", 1}, {"newField", "computedVal"_sd}}; ASSERT_DOCUMENT_EQ(result, expectedResult); } TEST(InclusionProjectionExecutionTest, ShouldIncludeFieldsInOrderOfInputDoc) { - ParsedInclusionProjection inclusion; const boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); - inclusion.parse(expCtx, BSON("first" << true << "second" << true << "third" << true)); + ParsedInclusionProjection inclusion(expCtx); + inclusion.parse(BSON("first" << true << "second" << true << "third" << true)); auto inputDoc = Document{{"second", 1}, {"first", 0}, {"third", 2}}; auto result = inclusion.applyProjection(inputDoc); ASSERT_DOCUMENT_EQ(result, inputDoc); } TEST(InclusionProjectionExecutionTest, ShouldApplyComputedFieldsInOrderSpecified) { - ParsedInclusionProjection inclusion; const boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); - inclusion.parse(expCtx, - BSON("firstComputed" << wrapInLiteral("FIRST") << "secondComputed" + ParsedInclusionProjection inclusion(expCtx); + inclusion.parse(BSON("firstComputed" << wrapInLiteral("FIRST") << "secondComputed" << wrapInLiteral("SECOND"))); auto result = inclusion.applyProjection(Document{{"first", 0}, {"second", 1}, {"third", 2}}); auto expectedResult = Document{{"firstComputed", "FIRST"_sd}, {"secondComputed", "SECOND"_sd}}; @@ -329,9 +322,9 @@ TEST(InclusionProjectionExecutionTest, ShouldApplyComputedFieldsInOrderSpecified } TEST(InclusionProjectionExecutionTest, ShouldImplicitlyIncludeId) { - ParsedInclusionProjection inclusion; const boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); - inclusion.parse(expCtx, BSON("a" << true)); + ParsedInclusionProjection inclusion(expCtx); + inclusion.parse(BSON("a" << true)); auto result = inclusion.applyProjection(Document{{"_id", "ID"_sd}, {"a", 1}, {"b", 2}}); auto expectedResult = Document{{"_id", "ID"_sd}, {"a", 1}}; ASSERT_DOCUMENT_EQ(result, expectedResult); @@ -343,18 +336,18 @@ TEST(InclusionProjectionExecutionTest, ShouldImplicitlyIncludeId) { } TEST(InclusionProjectionExecutionTest, ShouldImplicitlyIncludeIdWithComputedFields) { - ParsedInclusionProjection inclusion; const boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); - inclusion.parse(expCtx, BSON("newField" << wrapInLiteral("computedVal"))); + ParsedInclusionProjection inclusion(expCtx); + inclusion.parse(BSON("newField" << wrapInLiteral("computedVal"))); auto result = inclusion.applyProjection(Document{{"_id", "ID"_sd}, {"a", 1}}); auto expectedResult = Document{{"_id", "ID"_sd}, {"newField", "computedVal"_sd}}; ASSERT_DOCUMENT_EQ(result, expectedResult); } TEST(InclusionProjectionExecutionTest, ShouldIncludeIdIfExplicitlyIncluded) { - ParsedInclusionProjection inclusion; const boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); - inclusion.parse(expCtx, BSON("a" << true << "_id" << true << "b" << true)); + ParsedInclusionProjection inclusion(expCtx); + inclusion.parse(BSON("a" << true << "_id" << true << "b" << true)); auto result = inclusion.applyProjection(Document{{"_id", "ID"_sd}, {"a", 1}, {"b", 2}, {"c", 3}}); auto expectedResult = Document{{"_id", "ID"_sd}, {"a", 1}, {"b", 2}}; @@ -362,18 +355,18 @@ TEST(InclusionProjectionExecutionTest, ShouldIncludeIdIfExplicitlyIncluded) { } TEST(InclusionProjectionExecutionTest, ShouldExcludeIdIfExplicitlyExcluded) { - ParsedInclusionProjection inclusion; const boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); - inclusion.parse(expCtx, BSON("a" << true << "_id" << false)); + ParsedInclusionProjection inclusion(expCtx); + inclusion.parse(BSON("a" << true << "_id" << false)); auto result = inclusion.applyProjection(Document{{"a", 1}, {"b", 2}, {"_id", "ID"_sd}}); auto expectedResult = Document{{"a", 1}}; ASSERT_DOCUMENT_EQ(result, expectedResult); } TEST(InclusionProjectionExecutionTest, ShouldReplaceIdWithComputedId) { - ParsedInclusionProjection inclusion; const boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); - inclusion.parse(expCtx, BSON("_id" << wrapInLiteral("newId"))); + ParsedInclusionProjection inclusion(expCtx); + inclusion.parse(BSON("_id" << wrapInLiteral("newId"))); auto result = inclusion.applyProjection(Document{{"a", 1}, {"b", 2}, {"_id", "ID"_sd}}); auto expectedResult = Document{{"_id", "newId"_sd}}; ASSERT_DOCUMENT_EQ(result, expectedResult); @@ -384,9 +377,9 @@ TEST(InclusionProjectionExecutionTest, ShouldReplaceIdWithComputedId) { // TEST(InclusionProjectionExecutionTest, ShouldIncludeSimpleDottedFieldFromSubDoc) { - ParsedInclusionProjection inclusion; const boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); - inclusion.parse(expCtx, BSON("a.b" << true)); + ParsedInclusionProjection inclusion(expCtx); + inclusion.parse(BSON("a.b" << true)); // More than one field in sub document. auto result = inclusion.applyProjection(Document{{"a", Document{{"b", 1}, {"c", 2}}}}); @@ -410,9 +403,9 @@ TEST(InclusionProjectionExecutionTest, ShouldIncludeSimpleDottedFieldFromSubDoc) } TEST(InclusionProjectionExecutionTest, ShouldNotCreateSubDocIfDottedIncludedFieldDoesNotExist) { - ParsedInclusionProjection inclusion; const boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); - inclusion.parse(expCtx, BSON("sub.target" << true)); + ParsedInclusionProjection inclusion(expCtx); + inclusion.parse(BSON("sub.target" << true)); // Should not add the path if it doesn't exist. auto result = inclusion.applyProjection(Document{}); @@ -426,9 +419,9 @@ TEST(InclusionProjectionExecutionTest, ShouldNotCreateSubDocIfDottedIncludedFiel } TEST(InclusionProjectionExecutionTest, ShouldApplyDottedInclusionToEachElementInArray) { - ParsedInclusionProjection inclusion; const boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); - inclusion.parse(expCtx, BSON("a.b" << true)); + ParsedInclusionProjection inclusion(expCtx); + inclusion.parse(BSON("a.b" << true)); vector<Value> nestedValues = {Value(1), Value(Document{}), @@ -451,9 +444,9 @@ TEST(InclusionProjectionExecutionTest, ShouldApplyDottedInclusionToEachElementIn } TEST(InclusionProjectionExecutionTest, ShouldAddComputedDottedFieldToSubDocument) { - ParsedInclusionProjection inclusion; const boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); - inclusion.parse(expCtx, BSON("sub.target" << wrapInLiteral("computedVal"))); + ParsedInclusionProjection inclusion(expCtx); + inclusion.parse(BSON("sub.target" << wrapInLiteral("computedVal"))); // Other fields exist in sub document, one of which is the specified field. auto result = inclusion.applyProjection(Document{{"sub", Document{{"target", 1}, {"c", 2}}}}); @@ -472,9 +465,9 @@ TEST(InclusionProjectionExecutionTest, ShouldAddComputedDottedFieldToSubDocument } TEST(InclusionProjectionExecutionTest, ShouldCreateSubDocIfDottedComputedFieldDoesntExist) { - ParsedInclusionProjection inclusion; const boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); - inclusion.parse(expCtx, BSON("sub.target" << wrapInLiteral("computedVal"))); + ParsedInclusionProjection inclusion(expCtx); + inclusion.parse(BSON("sub.target" << wrapInLiteral("computedVal"))); // Should add the path if it doesn't exist. auto result = inclusion.applyProjection(Document{}); @@ -487,9 +480,9 @@ TEST(InclusionProjectionExecutionTest, ShouldCreateSubDocIfDottedComputedFieldDo } TEST(InclusionProjectionExecutionTest, ShouldCreateNestedSubDocumentsAllTheWayToComputedField) { - ParsedInclusionProjection inclusion; const boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); - inclusion.parse(expCtx, BSON("a.b.c.d" << wrapInLiteral("computedVal"))); + ParsedInclusionProjection inclusion(expCtx); + inclusion.parse(BSON("a.b.c.d" << wrapInLiteral("computedVal"))); // Should add the path if it doesn't exist. auto result = inclusion.applyProjection(Document{}); @@ -503,9 +496,9 @@ TEST(InclusionProjectionExecutionTest, ShouldCreateNestedSubDocumentsAllTheWayTo } TEST(InclusionProjectionExecutionTest, ShouldAddComputedDottedFieldToEachElementInArray) { - ParsedInclusionProjection inclusion; const boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); - inclusion.parse(expCtx, BSON("a.b" << wrapInLiteral("COMPUTED"))); + ParsedInclusionProjection inclusion(expCtx); + inclusion.parse(BSON("a.b" << wrapInLiteral("COMPUTED"))); vector<Value> nestedValues = {Value(1), Value(Document{}), @@ -527,9 +520,9 @@ TEST(InclusionProjectionExecutionTest, ShouldAddComputedDottedFieldToEachElement } TEST(InclusionProjectionExecutionTest, ShouldApplyInclusionsAndAdditionsToEachElementInArray) { - ParsedInclusionProjection inclusion; const boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); - inclusion.parse(expCtx, BSON("a.inc" << true << "a.comp" << wrapInLiteral("COMPUTED"))); + ParsedInclusionProjection inclusion(expCtx); + inclusion.parse(BSON("a.inc" << true << "a.comp" << wrapInLiteral("COMPUTED"))); vector<Value> nestedValues = {Value(1), Value(Document{}), @@ -555,26 +548,24 @@ TEST(InclusionProjectionExecutionTest, ShouldApplyInclusionsAndAdditionsToEachEl } TEST(InclusionProjectionExecutionTest, ShouldAddOrIncludeSubFieldsOfId) { - ParsedInclusionProjection inclusion; const boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); - inclusion.parse(expCtx, BSON("_id.X" << true << "_id.Z" << wrapInLiteral("NEW"))); + ParsedInclusionProjection inclusion(expCtx); + inclusion.parse(BSON("_id.X" << true << "_id.Z" << wrapInLiteral("NEW"))); auto result = inclusion.applyProjection(Document{{"_id", Document{{"X", 1}, {"Y", 2}}}}); auto expectedResult = Document{{"_id", Document{{"X", 1}, {"Z", "NEW"_sd}}}}; ASSERT_DOCUMENT_EQ(result, expectedResult); } TEST(InclusionProjectionExecutionTest, ShouldAllowMixedNestedAndDottedFields) { - ParsedInclusionProjection inclusion; const boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); + ParsedInclusionProjection inclusion(expCtx); // Include all of "a.b", "a.c", "a.d", and "a.e". // Add new computed fields "a.W", "a.X", "a.Y", and "a.Z". - inclusion.parse( - expCtx, - BSON("a.b" << true << "a.c" << true << "a.W" << wrapInLiteral("W") << "a.X" - << wrapInLiteral("X") - << "a" - << BSON("d" << true << "e" << true << "Y" << wrapInLiteral("Y") << "Z" - << wrapInLiteral("Z")))); + inclusion.parse(BSON( + "a.b" << true << "a.c" << true << "a.W" << wrapInLiteral("W") << "a.X" << wrapInLiteral("X") + << "a" + << BSON("d" << true << "e" << true << "Y" << wrapInLiteral("Y") << "Z" + << wrapInLiteral("Z")))); auto result = inclusion.applyProjection(Document{ {"a", Document{{"b", "b"_sd}, {"c", "c"_sd}, {"d", "d"_sd}, {"e", "e"_sd}, {"f", "f"_sd}}}}); @@ -591,19 +582,18 @@ TEST(InclusionProjectionExecutionTest, ShouldAllowMixedNestedAndDottedFields) { } TEST(InclusionProjectionExecutionTest, ShouldApplyNestedComputedFieldsInOrderSpecified) { - ParsedInclusionProjection inclusion; const boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); - inclusion.parse(expCtx, - BSON("a" << wrapInLiteral("FIRST") << "b.c" << wrapInLiteral("SECOND"))); + ParsedInclusionProjection inclusion(expCtx); + inclusion.parse(BSON("a" << wrapInLiteral("FIRST") << "b.c" << wrapInLiteral("SECOND"))); auto result = inclusion.applyProjection(Document{}); auto expectedResult = Document{{"a", "FIRST"_sd}, {"b", Document{{"c", "SECOND"_sd}}}}; ASSERT_DOCUMENT_EQ(result, expectedResult); } TEST(InclusionProjectionExecutionTest, ShouldApplyComputedFieldsAfterAllInclusions) { - ParsedInclusionProjection inclusion; const boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); - inclusion.parse(expCtx, BSON("b.c" << wrapInLiteral("NEW") << "a" << true)); + ParsedInclusionProjection inclusion(expCtx); + inclusion.parse(BSON("b.c" << wrapInLiteral("NEW") << "a" << true)); auto result = inclusion.applyProjection(Document{{"a", 1}}); auto expectedResult = Document{{"a", 1}, {"b", Document{{"c", "NEW"_sd}}}}; ASSERT_DOCUMENT_EQ(result, expectedResult); @@ -621,9 +611,9 @@ TEST(InclusionProjectionExecutionTest, ShouldApplyComputedFieldsAfterAllInclusio } TEST(InclusionProjectionExecutionTest, ComputedFieldReplacingExistingShouldAppearAfterInclusions) { - ParsedInclusionProjection inclusion; const boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); - inclusion.parse(expCtx, BSON("b" << wrapInLiteral("NEW") << "a" << true)); + ParsedInclusionProjection inclusion(expCtx); + inclusion.parse(BSON("b" << wrapInLiteral("NEW") << "a" << true)); auto result = inclusion.applyProjection(Document{{"b", 1}, {"a", 1}}); auto expectedResult = Document{{"a", 1}, {"b", "NEW"_sd}}; ASSERT_DOCUMENT_EQ(result, expectedResult); @@ -637,9 +627,9 @@ TEST(InclusionProjectionExecutionTest, ComputedFieldReplacingExistingShouldAppea // TEST(InclusionProjectionExecutionTest, ShouldAlwaysKeepMetadataFromOriginalDoc) { - ParsedInclusionProjection inclusion; const boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); - inclusion.parse(expCtx, BSON("a" << true)); + ParsedInclusionProjection inclusion(expCtx); + inclusion.parse(BSON("a" << true)); MutableDocument inputDocBuilder(Document{{"a", 1}}); inputDocBuilder.setRandMetaField(1.0); diff --git a/src/mongo/db/pipeline/variables.cpp b/src/mongo/db/pipeline/variables.cpp index 199517b5f7a..ba1b42e4fb1 100644 --- a/src/mongo/db/pipeline/variables.cpp +++ b/src/mongo/db/pipeline/variables.cpp @@ -29,6 +29,7 @@ #include "mongo/platform/basic.h" #include "mongo/db/pipeline/variables.h" +#include "mongo/util/mongoutils/str.h" namespace mongo { @@ -99,9 +100,13 @@ void Variables::uassertValidNameForUserRead(StringData varName) { void Variables::setValue(Id id, const Value& value) { uassert(17199, "can't use Variables::setValue to set a reserved builtin variable", id >= 0); - auto varIndex = static_cast<size_t>(id); - invariant(varIndex < _numVars); - _rest[varIndex] = value; + + const auto idAsSizeT = static_cast<size_t>(id); + if (idAsSizeT >= _valueList.size()) { + _valueList.resize(idAsSizeT + 1); + } + + _valueList[id] = value; } Value Variables::getValue(Id id) const { @@ -117,9 +122,10 @@ Value Variables::getValue(Id id) const { } } - auto varIndex = static_cast<size_t>(id); - invariant(varIndex < _numVars); - return _rest[varIndex]; + uassert(40434, + str::stream() << "Requesting Variables::getValue with an out of range id: " << id, + static_cast<size_t>(id) < _valueList.size()); + return _valueList[id]; } Document Variables::getDocument(Id id) const { @@ -142,7 +148,6 @@ Variables::Id VariablesParseState::defineVariable(StringData name) { Variables::kBuiltinVarNameToId.find(name) == Variables::kBuiltinVarNameToId.end()); Variables::Id id = _idGenerator->generateId(); - invariant(id >= 0); _variables[name] = id; return id; } diff --git a/src/mongo/db/pipeline/variables.h b/src/mongo/db/pipeline/variables.h index 5bbbf4f3d9c..b1e0f0c7deb 100644 --- a/src/mongo/db/pipeline/variables.h +++ b/src/mongo/db/pipeline/variables.h @@ -29,6 +29,8 @@ #pragma once #include "mongo/db/pipeline/document.h" +#include "mongo/stdx/memory.h" +#include "mongo/stdx/unordered_map.h" #include "mongo/util/string_map.h" namespace mongo { @@ -38,27 +40,33 @@ class Variables { MONGO_DISALLOW_COPYING(Variables); public: - /** - * Each unique variable is assigned a unique id of this type. Negative ids are reserved for - * system variables and non-negative ids are allocated for user variables. - */ - typedef int64_t Id; + // Each unique variable is assigned a unique id of this type. Negative ids are reserved for + // system variables and non-negative ids are allocated for user variables. + using Id = int64_t; /** - * Constructs a placeholder for expressions that use no variables (even builtins like ROOT or - * REMOVE). + * Generates Variables::Id and keeps track of the number of Ids handed out. */ - Variables() : _numVars(0) {} + class IdGenerator { + public: + IdGenerator() : _nextId(0) {} + + Variables::Id generateId() { + return _nextId++; + } - explicit Variables(size_t numVars, const Document& root = Document()) - : _root(root), _rest(numVars == 0 ? NULL : new Value[numVars]), _numVars(numVars) {} + private: + Variables::Id _nextId; + }; + + Variables() = default; static void uassertValidNameForUserWrite(StringData varName); static void uassertValidNameForUserRead(StringData varName); // Ids for builtin variables. - static constexpr Id kRootId = Id(-1); - static constexpr Id kRemoveId = Id(-2); + static constexpr Variables::Id kRootId = Id(-1); + static constexpr Variables::Id kRemoveId = Id(-2); // Map from builtin var name to reserved id number. static const StringMap<Id> kBuiltinVarNameToId; @@ -80,45 +88,27 @@ public: * Sets the value of a user-defined variable. Illegal to use with the reserved builtin variables * defined above. */ - void setValue(Id id, const Value& value); + void setValue(Variables::Id id, const Value& value); /** * Gets the value of a user-defined or system variable. */ - Value getValue(Id id) const; + Value getValue(Variables::Id id) const; /** * Returns Document() for non-document values, but otherwise identical to getValue(). */ - Document getDocument(Id id) const; + Document getDocument(Variables::Id id) const; -private: - Document _root; - const std::unique_ptr<Value[]> _rest; - const size_t _numVars; -}; - -/** - * Generates Variables::Ids and keeps track of the number of Ids handed out. - */ -class VariablesIdGenerator { -public: - VariablesIdGenerator() : _nextId(0) {} - - Variables::Id generateId() { - return _nextId++; + IdGenerator* useIdGenerator() { + return &_idGenerator; } - /** - * Returns the number of Ids handed out by this Generator. - * Return value is intended to be passed to Variables constructor. - */ - Variables::Id getIdCount() const { - return _nextId; - } private: - Variables::Id _nextId; + Document _root; + IdGenerator _idGenerator; + std::vector<Value> _valueList; }; /** @@ -130,7 +120,8 @@ private: */ class VariablesParseState { public: - explicit VariablesParseState(VariablesIdGenerator* idGenerator) : _idGenerator(idGenerator) {} + explicit VariablesParseState(Variables::IdGenerator* variableIdGenerator) + : _idGenerator(variableIdGenerator) {} /** * Assigns a named variable a unique Id. This differs from all other variables, even @@ -150,8 +141,10 @@ public: Variables::Id getVariable(StringData name) const; private: + // Not owned here. + Variables::IdGenerator* _idGenerator; + StringMap<Variables::Id> _variables; - VariablesIdGenerator* _idGenerator; }; } // namespace mongo |