From 17cdb38b6db716dc47485a60ddff3c543e713e3d Mon Sep 17 00:00:00 2001 From: Arun Banala Date: Wed, 29 May 2019 11:36:58 +0100 Subject: SERVER-41065 Make evaluate() thread safe by passing 'Variables' as a parameter --- src/mongo/db/matcher/expression_expr.cpp | 7 +- .../db/pipeline/document_source_bucket_auto.cpp | 5 +- .../db/pipeline/document_source_graph_lookup.cpp | 2 +- src/mongo/db/pipeline/document_source_group.cpp | 11 +- src/mongo/db/pipeline/document_source_lookup.cpp | 2 +- src/mongo/db/pipeline/document_source_merge.h | 2 +- src/mongo/db/pipeline/document_source_redact.cpp | 2 +- .../db/pipeline/document_source_replace_root.cpp | 2 +- src/mongo/db/pipeline/document_source_sort.cpp | 2 +- src/mongo/db/pipeline/expression.cpp | 493 ++++---- src/mongo/db/pipeline/expression.h | 183 +-- src/mongo/db/pipeline/expression_convert_test.cpp | 1300 ++++++++++++-------- src/mongo/db/pipeline/expression_date_test.cpp | 235 ++-- src/mongo/db/pipeline/expression_test.cpp | 291 +++-- .../db/pipeline/expression_trigonometric_test.cpp | 4 +- .../pipeline/granularity_rounder_powers_of_two.cpp | 6 +- .../parsed_aggregation_projection_node.cpp | 5 +- 17 files changed, 1487 insertions(+), 1065 deletions(-) (limited to 'src') diff --git a/src/mongo/db/matcher/expression_expr.cpp b/src/mongo/db/matcher/expression_expr.cpp index f58c1c14ef8..f356c4aa25b 100644 --- a/src/mongo/db/matcher/expression_expr.cpp +++ b/src/mongo/db/matcher/expression_expr.cpp @@ -53,8 +53,13 @@ bool ExprMatchExpression::matches(const MatchableDocument* doc, MatchDetails* de } Document document(doc->toBSON()); + + // 'Variables' is not thread safe, and ExprMatchExpression may be used in a validator which + // processes documents from multiple threads simultaneously. Hence we make a copy of the + // 'Variables' object per-caller. + Variables variables = _expCtx->variables; try { - auto value = _expression->evaluate(document); + auto value = _expression->evaluate(document, &variables); return value.coerceToBool(); } catch (const DBException&) { if (MONGO_FAIL_POINT(ExprMatchExpressionMatchesReturnsFalseOnException)) { diff --git a/src/mongo/db/pipeline/document_source_bucket_auto.cpp b/src/mongo/db/pipeline/document_source_bucket_auto.cpp index ff5eeeb285a..dbbf6c7b1f7 100644 --- a/src/mongo/db/pipeline/document_source_bucket_auto.cpp +++ b/src/mongo/db/pipeline/document_source_bucket_auto.cpp @@ -157,7 +157,7 @@ Value DocumentSourceBucketAuto::extractKey(const Document& doc) { return Value(BSONNULL); } - Value key = _groupByExpression->evaluate(doc); + Value key = _groupByExpression->evaluate(doc, &pExpCtx->variables); if (_granularityRounder) { uassert(40258, @@ -190,7 +190,8 @@ void DocumentSourceBucketAuto::addDocumentToBucket(const pair& const size_t numAccumulators = _accumulatedFields.size(); for (size_t k = 0; k < numAccumulators; k++) { - bucket._accums[k]->process(_accumulatedFields[k].expression->evaluate(entry.second), false); + bucket._accums[k]->process( + _accumulatedFields[k].expression->evaluate(entry.second, &pExpCtx->variables), false); } } diff --git a/src/mongo/db/pipeline/document_source_graph_lookup.cpp b/src/mongo/db/pipeline/document_source_graph_lookup.cpp index 937da2a46cc..7447f8ae51c 100644 --- a/src/mongo/db/pipeline/document_source_graph_lookup.cpp +++ b/src/mongo/db/pipeline/document_source_graph_lookup.cpp @@ -335,7 +335,7 @@ void DocumentSourceGraphLookUp::performSearch() { // Make sure _input is set before calling performSearch(). invariant(_input); - Value startingValue = _startWith->evaluate(*_input); + Value startingValue = _startWith->evaluate(*_input, &pExpCtx->variables); // If _startWith evaluates to an array, treat each value as a separate starting point. if (startingValue.isArray()) { diff --git a/src/mongo/db/pipeline/document_source_group.cpp b/src/mongo/db/pipeline/document_source_group.cpp index dd14584ecc9..ff46f44aba4 100644 --- a/src/mongo/db/pipeline/document_source_group.cpp +++ b/src/mongo/db/pipeline/document_source_group.cpp @@ -73,7 +73,7 @@ Document GroupFromFirstDocumentTransformation::applyTransformation(const Documen MutableDocument output(_accumulatorExprs.size()); for (auto&& expr : _accumulatorExprs) { - auto value = expr.second->evaluate(input); + auto value = expr.second->evaluate(input, &expr.second->getExpressionContext()->variables); output.addField(expr.first, value.missing() ? Value(BSONNULL) : value); } @@ -495,8 +495,9 @@ DocumentSource::GetNextResult DocumentSourceGroup::initialize() { dassert(numAccumulators == group.size()); for (size_t i = 0; i < numAccumulators; i++) { - group[i]->process(_accumulatedFields[i].expression->evaluate(rootDocument), - _doingMerge); + group[i]->process( + _accumulatedFields[i].expression->evaluate(rootDocument, &pExpCtx->variables), + _doingMerge); _memoryUsageBytes += group[i]->memUsageForSorter(); } @@ -611,7 +612,7 @@ shared_ptr::Iterator> DocumentSourceGroup::spill() { Value DocumentSourceGroup::computeId(const Document& root) { // If only one expression, return result directly if (_idExpressions.size() == 1) { - Value retValue = _idExpressions[0]->evaluate(root); + Value retValue = _idExpressions[0]->evaluate(root, &pExpCtx->variables); return retValue.missing() ? Value(BSONNULL) : std::move(retValue); } @@ -619,7 +620,7 @@ Value DocumentSourceGroup::computeId(const Document& root) { vector vals; vals.reserve(_idExpressions.size()); for (size_t i = 0; i < _idExpressions.size(); i++) { - vals.push_back(_idExpressions[i]->evaluate(root)); + vals.push_back(_idExpressions[i]->evaluate(root, &pExpCtx->variables)); } return Value(std::move(vals)); } diff --git a/src/mongo/db/pipeline/document_source_lookup.cpp b/src/mongo/db/pipeline/document_source_lookup.cpp index da994cc3ca7..b2a81168a77 100644 --- a/src/mongo/db/pipeline/document_source_lookup.cpp +++ b/src/mongo/db/pipeline/document_source_lookup.cpp @@ -629,7 +629,7 @@ void DocumentSourceLookUp::resolveLetVariables(const Document& localDoc, Variabl invariant(variables); for (auto& letVar : _letVariables) { - auto value = letVar.expression->evaluate(localDoc); + auto value = letVar.expression->evaluate(localDoc, &pExpCtx->variables); variables->setConstantValue(letVar.id, value); } } diff --git a/src/mongo/db/pipeline/document_source_merge.h b/src/mongo/db/pipeline/document_source_merge.h index 811ebf97591..927c0376245 100644 --- a/src/mongo/db/pipeline/document_source_merge.h +++ b/src/mongo/db/pipeline/document_source_merge.h @@ -181,7 +181,7 @@ private: BSONObjBuilder bob; for (auto && [ name, expr ] : *_letVariables) { - bob << name << expr->evaluate(doc); + bob << name << expr->evaluate(doc, &pExpCtx->variables); } return bob.obj(); } diff --git a/src/mongo/db/pipeline/document_source_redact.cpp b/src/mongo/db/pipeline/document_source_redact.cpp index 8c48726aa2f..3ff60410a95 100644 --- a/src/mongo/db/pipeline/document_source_redact.cpp +++ b/src/mongo/db/pipeline/document_source_redact.cpp @@ -135,7 +135,7 @@ Value DocumentSourceRedact::redactValue(const Value& in, const Document& root) { boost::optional DocumentSourceRedact::redactObject(const Document& root) { auto& variables = pExpCtx->variables; - const Value expressionResult = _expression->evaluate(root); + const Value expressionResult = _expression->evaluate(root, &variables); ValueComparator simpleValueCmp; if (simpleValueCmp.evaluate(expressionResult == keepVal)) { diff --git a/src/mongo/db/pipeline/document_source_replace_root.cpp b/src/mongo/db/pipeline/document_source_replace_root.cpp index 4bbbffdb628..41ff856cdd0 100644 --- a/src/mongo/db/pipeline/document_source_replace_root.cpp +++ b/src/mongo/db/pipeline/document_source_replace_root.cpp @@ -60,7 +60,7 @@ public: Document applyTransformation(const Document& input) final { // Extract subdocument in the form of a Value. - Value newRoot = _newRoot->evaluate(input); + Value newRoot = _newRoot->evaluate(input, &_expCtx->variables); // The newRoot expression, if it exists, must evaluate to an object. uassert(40228, diff --git a/src/mongo/db/pipeline/document_source_sort.cpp b/src/mongo/db/pipeline/document_source_sort.cpp index e9f86b947ed..395d517ce2d 100644 --- a/src/mongo/db/pipeline/document_source_sort.cpp +++ b/src/mongo/db/pipeline/document_source_sort.cpp @@ -423,7 +423,7 @@ StatusWith DocumentSourceSort::extractKeyPart(const Document& doc, plainKey = key.getValue(); } else { invariant(patternPart.expression); - plainKey = patternPart.expression->evaluate(doc); + plainKey = patternPart.expression->evaluate(doc, &pExpCtx->variables); } return getCollationComparisonKey(plainKey); diff --git a/src/mongo/db/pipeline/expression.cpp b/src/mongo/db/pipeline/expression.cpp index d6068398342..c1bf5957066 100644 --- a/src/mongo/db/pipeline/expression.cpp +++ b/src/mongo/db/pipeline/expression.cpp @@ -264,7 +264,7 @@ const char* ExpressionAbs::getOpName() const { /* ------------------------- ExpressionAdd ----------------------------- */ -Value ExpressionAdd::evaluate(const Document& root) const { +Value ExpressionAdd::evaluate(const Document& root, Variables* variables) 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 @@ -276,7 +276,7 @@ Value ExpressionAdd::evaluate(const Document& root) const { const size_t n = _children.size(); for (size_t i = 0; i < n; ++i) { - Value val = _children[i]->evaluate(root); + Value val = _children[i]->evaluate(root, variables); switch (val.getType()) { case NumberDecimal: @@ -346,8 +346,8 @@ const char* ExpressionAdd::getOpName() const { /* ------------------------- ExpressionAllElementsTrue -------------------------- */ -Value ExpressionAllElementsTrue::evaluate(const Document& root) const { - const Value arr = _children[0]->evaluate(root); +Value ExpressionAllElementsTrue::evaluate(const Document& root, Variables* variables) const { + const Value arr = _children[0]->evaluate(root, variables); uassert(17040, str::stream() << getOpName() << "'s argument must be an array, but is " << typeName(arr.getType()), @@ -424,10 +424,10 @@ intrusive_ptr ExpressionAnd::optimize() { return pE; } -Value ExpressionAnd::evaluate(const Document& root) const { +Value ExpressionAnd::evaluate(const Document& root, Variables* variables) const { const size_t n = _children.size(); for (size_t i = 0; i < n; ++i) { - Value pValue(_children[i]->evaluate(root)); + Value pValue(_children[i]->evaluate(root, variables)); if (!pValue.coerceToBool()) return Value(false); } @@ -442,8 +442,8 @@ const char* ExpressionAnd::getOpName() const { /* ------------------------- ExpressionAnyElementTrue -------------------------- */ -Value ExpressionAnyElementTrue::evaluate(const Document& root) const { - const Value arr = _children[0]->evaluate(root); +Value ExpressionAnyElementTrue::evaluate(const Document& root, Variables* variables) const { + const Value arr = _children[0]->evaluate(root, variables); uassert(17041, str::stream() << getOpName() << "'s argument must be an array, but is " << typeName(arr.getType()), @@ -464,11 +464,11 @@ const char* ExpressionAnyElementTrue::getOpName() const { /* ---------------------- ExpressionArray --------------------------- */ -Value ExpressionArray::evaluate(const Document& root) const { +Value ExpressionArray::evaluate(const Document& root, Variables* variables) const { vector values; values.reserve(_children.size()); for (auto&& expr : _children) { - Value elemVal = expr->evaluate(root); + Value elemVal = expr->evaluate(root, variables); values.push_back(elemVal.missing() ? Value(BSONNULL) : std::move(elemVal)); } return Value(std::move(values)); @@ -495,7 +495,8 @@ intrusive_ptr ExpressionArray::optimize() { // If all values in ExpressionArray are constant evaluate to ExpressionConstant. if (allValuesConstant) { - return ExpressionConstant::create(getExpressionContext(), evaluate(Document())); + return ExpressionConstant::create( + getExpressionContext(), evaluate(Document(), &(getExpressionContext()->variables))); } return this; } @@ -507,9 +508,9 @@ const char* ExpressionArray::getOpName() const { /* ------------------------- ExpressionArrayElemAt -------------------------- */ -Value ExpressionArrayElemAt::evaluate(const Document& root) const { - const Value array = _children[0]->evaluate(root); - const Value indexArg = _children[1]->evaluate(root); +Value ExpressionArrayElemAt::evaluate(const Document& root, Variables* variables) const { + const Value array = _children[0]->evaluate(root, variables); + const Value indexArg = _children[1]->evaluate(root, variables); if (array.nullish() || indexArg.nullish()) { return Value(BSONNULL); @@ -549,8 +550,8 @@ const char* ExpressionArrayElemAt::getOpName() const { /* ------------------------- ExpressionObjectToArray -------------------------- */ -Value ExpressionObjectToArray::evaluate(const Document& root) const { - const Value targetVal = _children[0]->evaluate(root); +Value ExpressionObjectToArray::evaluate(const Document& root, Variables* variables) const { + const Value targetVal = _children[0]->evaluate(root, variables); if (targetVal.nullish()) { return Value(BSONNULL); @@ -581,8 +582,8 @@ const char* ExpressionObjectToArray::getOpName() const { } /* ------------------------- ExpressionArrayToObject -------------------------- */ -Value ExpressionArrayToObject::evaluate(const Document& root) const { - const Value input = _children[0]->evaluate(root); +Value ExpressionArrayToObject::evaluate(const Document& root, Variables* variables) const { + const Value input = _children[0]->evaluate(root, variables); if (input.nullish()) { return Value(BSONNULL); } @@ -729,8 +730,8 @@ void ExpressionCoerceToBool::_doAddDependencies(DepsTracker* deps) const { pExpression->addDependencies(deps); } -Value ExpressionCoerceToBool::evaluate(const Document& root) const { - Value pResult(pExpression->evaluate(root)); +Value ExpressionCoerceToBool::evaluate(const Document& root, Variables* variables) const { + Value pResult(pExpression->evaluate(root, variables)); bool b = pResult.coerceToBool(); if (b) return Value(true); @@ -809,9 +810,9 @@ static const CmpLookup cmpLookup[7] = { }; } -Value ExpressionCompare::evaluate(const Document& root) const { - Value pLeft(_children[0]->evaluate(root)); - Value pRight(_children[1]->evaluate(root)); +Value ExpressionCompare::evaluate(const Document& root, Variables* variables) const { + Value pLeft(_children[0]->evaluate(root, variables)); + Value pRight(_children[1]->evaluate(root, variables)); int cmp = getExpressionContext()->getValueComparator().compare(pLeft, pRight); @@ -837,12 +838,12 @@ const char* ExpressionCompare::getOpName() const { /* ------------------------- ExpressionConcat ----------------------------- */ -Value ExpressionConcat::evaluate(const Document& root) const { +Value ExpressionConcat::evaluate(const Document& root, Variables* variables) const { const size_t n = _children.size(); StringBuilder result; for (size_t i = 0; i < n; ++i) { - Value val = _children[i]->evaluate(root); + Value val = _children[i]->evaluate(root, variables); if (val.nullish()) return Value(BSONNULL); @@ -863,12 +864,12 @@ const char* ExpressionConcat::getOpName() const { /* ------------------------- ExpressionConcatArrays ----------------------------- */ -Value ExpressionConcatArrays::evaluate(const Document& root) const { +Value ExpressionConcatArrays::evaluate(const Document& root, Variables* variables) const { const size_t n = _children.size(); vector values; for (size_t i = 0; i < n; ++i) { - Value val = _children[i]->evaluate(root); + Value val = _children[i]->evaluate(root, variables); if (val.nullish()) { return Value(BSONNULL); } @@ -891,10 +892,10 @@ const char* ExpressionConcatArrays::getOpName() const { /* ----------------------- ExpressionCond ------------------------------ */ -Value ExpressionCond::evaluate(const Document& root) const { - Value pCond(_children[0]->evaluate(root)); +Value ExpressionCond::evaluate(const Document& root, Variables* variables) const { + Value pCond(_children[0]->evaluate(root, variables)); int idx = pCond.coerceToBool() ? 1 : 2; - return _children[idx]->evaluate(root); + return _children[idx]->evaluate(root, variables); } intrusive_ptr ExpressionCond::parse( @@ -965,7 +966,7 @@ void ExpressionConstant::_doAddDependencies(DepsTracker* deps) const { /* nothing to do */ } -Value ExpressionConstant::evaluate(const Document& root) const { +Value ExpressionConstant::evaluate(const Document& root, Variables* variables) const { return _value; } @@ -987,14 +988,15 @@ namespace { boost::optional makeTimeZone(const TimeZoneDatabase* tzdb, const Document& root, - const Expression* timeZone) { + const Expression* timeZone, + Variables* variables) { invariant(tzdb); if (!timeZone) { return mongo::TimeZoneDatabase::utcZone(); } - auto timeZoneId = timeZone->evaluate(root); + auto timeZoneId = timeZone->evaluate(root, variables); if (timeZoneId.nullish()) { return boost::none; @@ -1176,8 +1178,10 @@ intrusive_ptr ExpressionDateFromParts::optimize() { _isoWeek, _isoDayOfWeek, _timeZone})) { + // Everything is a constant, so we can turn into a constant. - return ExpressionConstant::create(getExpressionContext(), evaluate(Document{})); + return ExpressionConstant::create( + getExpressionContext(), evaluate(Document{}, &(getExpressionContext()->variables))); } return this; @@ -1203,13 +1207,14 @@ bool ExpressionDateFromParts::evaluateNumberWithDefault(const Document& root, intrusive_ptr field, StringData fieldName, long long defaultValue, - long long* returnValue) const { + long long* returnValue, + Variables* variables) const { if (!field) { *returnValue = defaultValue; return true; } - auto fieldValue = field->evaluate(root); + auto fieldValue = field->evaluate(root, variables); if (fieldValue.nullish()) { return false; @@ -1227,18 +1232,20 @@ bool ExpressionDateFromParts::evaluateNumberWithDefault(const Document& root, return true; } -Value ExpressionDateFromParts::evaluate(const Document& root) const { +Value ExpressionDateFromParts::evaluate(const Document& root, Variables* variables) const { long long hour, minute, second, millisecond; - if (!evaluateNumberWithDefault(root, _hour, "hour"_sd, 0, &hour) || - !evaluateNumberWithDefault(root, _minute, "minute"_sd, 0, &minute) || - !evaluateNumberWithDefault(root, _second, "second"_sd, 0, &second) || - !evaluateNumberWithDefault(root, _millisecond, "millisecond"_sd, 0, &millisecond)) { + if (!evaluateNumberWithDefault(root, _hour, "hour"_sd, 0, &hour, variables) || + !evaluateNumberWithDefault(root, _minute, "minute"_sd, 0, &minute, variables) || + !evaluateNumberWithDefault(root, _second, "second"_sd, 0, &second, variables) || + !evaluateNumberWithDefault( + root, _millisecond, "millisecond"_sd, 0, &millisecond, variables)) { // One of the evaluated inputs in nullish. return Value(BSONNULL); } - auto timeZone = makeTimeZone(getExpressionContext()->timeZoneDatabase, root, _timeZone.get()); + auto timeZone = + makeTimeZone(getExpressionContext()->timeZoneDatabase, root, _timeZone.get(), variables); if (!timeZone) { return Value(BSONNULL); @@ -1247,9 +1254,9 @@ Value ExpressionDateFromParts::evaluate(const Document& root) const { if (_year) { long long year, month, day; - if (!evaluateNumberWithDefault(root, _year, "year"_sd, 1970, &year) || - !evaluateNumberWithDefault(root, _month, "month"_sd, 1, &month) || - !evaluateNumberWithDefault(root, _day, "day"_sd, 1, &day)) { + if (!evaluateNumberWithDefault(root, _year, "year"_sd, 1970, &year, variables) || + !evaluateNumberWithDefault(root, _month, "month"_sd, 1, &month, variables) || + !evaluateNumberWithDefault(root, _day, "day"_sd, 1, &day, variables)) { // One of the evaluated inputs in nullish. return Value(BSONNULL); } @@ -1268,9 +1275,11 @@ Value ExpressionDateFromParts::evaluate(const Document& root) const { if (_isoWeekYear) { long long isoWeekYear, isoWeek, isoDayOfWeek; - if (!evaluateNumberWithDefault(root, _isoWeekYear, "isoWeekYear"_sd, 1970, &isoWeekYear) || - !evaluateNumberWithDefault(root, _isoWeek, "isoWeek"_sd, 1, &isoWeek) || - !evaluateNumberWithDefault(root, _isoDayOfWeek, "isoDayOfWeek"_sd, 1, &isoDayOfWeek)) { + if (!evaluateNumberWithDefault( + root, _isoWeekYear, "isoWeekYear"_sd, 1970, &isoWeekYear, variables) || + !evaluateNumberWithDefault(root, _isoWeek, "isoWeek"_sd, 1, &isoWeek, variables) || + !evaluateNumberWithDefault( + root, _isoDayOfWeek, "isoDayOfWeek"_sd, 1, &isoDayOfWeek, variables)) { // One of the evaluated inputs in nullish. return Value(BSONNULL); } @@ -1405,7 +1414,8 @@ intrusive_ptr ExpressionDateFromString::optimize() { if (ExpressionConstant::allNullOrConstant( {_dateString, _timeZone, _format, _onNull, _onError})) { // Everything is a constant, so we can turn into a constant. - return ExpressionConstant::create(getExpressionContext(), evaluate(Document{})); + return ExpressionConstant::create( + getExpressionContext(), evaluate(Document{}, &(getExpressionContext()->variables))); } return this; } @@ -1420,14 +1430,14 @@ Value ExpressionDateFromString::serialize(bool explain) const { {"onError", _onError ? _onError->serialize(explain) : Value()}}}}); } -Value ExpressionDateFromString::evaluate(const Document& root) const { - const Value dateString = _dateString->evaluate(root); +Value ExpressionDateFromString::evaluate(const Document& root, Variables* variables) const { + const Value dateString = _dateString->evaluate(root, variables); Value formatValue; // Eagerly validate the format parameter, ignoring if nullish since the input string nullish // behavior takes precedence. if (_format) { - formatValue = _format->evaluate(root); + formatValue = _format->evaluate(root, variables); if (!formatValue.nullish()) { uassert(40684, str::stream() << "$dateFromString requires that 'format' be a string, found: " @@ -1442,11 +1452,12 @@ Value ExpressionDateFromString::evaluate(const Document& root) const { // Evaluate the timezone parameter before checking for nullish input, as this will throw an // exception for an invalid timezone string. - auto timeZone = makeTimeZone(getExpressionContext()->timeZoneDatabase, root, _timeZone.get()); + auto timeZone = + makeTimeZone(getExpressionContext()->timeZoneDatabase, root, _timeZone.get(), variables); // Behavior for nullish input takes precedence over other nullish elements. if (dateString.nullish()) { - return _onNull ? _onNull->evaluate(root) : Value(BSONNULL); + return _onNull ? _onNull->evaluate(root, variables) : Value(BSONNULL); } try { @@ -1476,7 +1487,7 @@ Value ExpressionDateFromString::evaluate(const Document& root) const { getExpressionContext()->timeZoneDatabase->fromString(dateTimeString, timeZone.get())); } catch (const ExceptionFor&) { if (_onError) { - return _onError->evaluate(root); + return _onError->evaluate(root, variables); } throw; } @@ -1563,7 +1574,8 @@ intrusive_ptr ExpressionDateToParts::optimize() { if (ExpressionConstant::allNullOrConstant({_date, _iso8601, _timeZone})) { // Everything is a constant, so we can turn into a constant. - return ExpressionConstant::create(getExpressionContext(), evaluate(Document{})); + return ExpressionConstant::create( + getExpressionContext(), evaluate(Document{}, &(getExpressionContext()->variables))); } return this; @@ -1577,12 +1589,13 @@ Value ExpressionDateToParts::serialize(bool explain) const { {"iso8601", _iso8601 ? _iso8601->serialize(explain) : Value()}}}}); } -boost::optional ExpressionDateToParts::evaluateIso8601Flag(const Document& root) const { +boost::optional ExpressionDateToParts::evaluateIso8601Flag(const Document& root, + Variables* variables) const { if (!_iso8601) { return false; } - auto iso8601Output = _iso8601->evaluate(root); + auto iso8601Output = _iso8601->evaluate(root, variables); if (iso8601Output.nullish()) { return boost::none; @@ -1596,15 +1609,16 @@ boost::optional ExpressionDateToParts::evaluateIso8601Flag(const Document& return iso8601Output.getBool(); } -Value ExpressionDateToParts::evaluate(const Document& root) const { - const Value date = _date->evaluate(root); +Value ExpressionDateToParts::evaluate(const Document& root, Variables* variables) const { + const Value date = _date->evaluate(root, variables); - auto timeZone = makeTimeZone(getExpressionContext()->timeZoneDatabase, root, _timeZone.get()); + auto timeZone = + makeTimeZone(getExpressionContext()->timeZoneDatabase, root, _timeZone.get(), variables); if (!timeZone) { return Value(BSONNULL); } - auto iso8601 = evaluateIso8601Flag(root); + auto iso8601 = evaluateIso8601Flag(root, variables); if (!iso8601) { return Value(BSONNULL); } @@ -1718,7 +1732,8 @@ intrusive_ptr ExpressionDateToString::optimize() { if (ExpressionConstant::allNullOrConstant({_date, _format, _timeZone, _onNull})) { // Everything is a constant, so we can turn into a constant. - return ExpressionConstant::create(getExpressionContext(), evaluate(Document{})); + return ExpressionConstant::create( + getExpressionContext(), evaluate(Document{}, &(getExpressionContext()->variables))); } return this; @@ -1733,14 +1748,14 @@ Value ExpressionDateToString::serialize(bool explain) const { {"onNull", _onNull ? _onNull->serialize(explain) : Value()}}}}); } -Value ExpressionDateToString::evaluate(const Document& root) const { - const Value date = _date->evaluate(root); +Value ExpressionDateToString::evaluate(const Document& root, Variables* variables) const { + const Value date = _date->evaluate(root, variables); Value formatValue; // Eagerly validate the format parameter, ignoring if nullish since the input date nullish // behavior takes precedence. if (_format) { - formatValue = _format->evaluate(root); + formatValue = _format->evaluate(root, variables); if (!formatValue.nullish()) { uassert(18533, str::stream() << "$dateToString requires that 'format' be a string, found: " @@ -1755,10 +1770,11 @@ Value ExpressionDateToString::evaluate(const Document& root) const { // Evaluate the timezone parameter before checking for nullish input, as this will throw an // exception for an invalid timezone string. - auto timeZone = makeTimeZone(getExpressionContext()->timeZoneDatabase, root, _timeZone.get()); + auto timeZone = + makeTimeZone(getExpressionContext()->timeZoneDatabase, root, _timeZone.get(), variables); if (date.nullish()) { - return _onNull ? _onNull->evaluate(root) : Value(BSONNULL); + return _onNull ? _onNull->evaluate(root, variables) : Value(BSONNULL); } if (!timeZone) { @@ -1793,9 +1809,9 @@ void ExpressionDateToString::_doAddDependencies(DepsTracker* deps) const { /* ----------------------- ExpressionDivide ---------------------------- */ -Value ExpressionDivide::evaluate(const Document& root) const { - Value lhs = _children[0]->evaluate(root); - Value rhs = _children[1]->evaluate(root); +Value ExpressionDivide::evaluate(const Document& root, Variables* variables) const { + Value lhs = _children[0]->evaluate(root, variables); + Value rhs = _children[1]->evaluate(root, variables); auto assertNonZero = [](bool nonZero) { uassert(16608, "can't $divide by zero", nonZero); }; @@ -1900,7 +1916,8 @@ intrusive_ptr ExpressionObject::optimize() { } // If all values in ExpressionObject are constant evaluate to ExpressionConstant. if (allValuesConstant) { - return ExpressionConstant::create(getExpressionContext(), evaluate(Document())); + return ExpressionConstant::create( + getExpressionContext(), evaluate(Document(), &(getExpressionContext()->variables))); } return this; } @@ -1911,10 +1928,10 @@ void ExpressionObject::_doAddDependencies(DepsTracker* deps) const { } } -Value ExpressionObject::evaluate(const Document& root) const { +Value ExpressionObject::evaluate(const Document& root, Variables* variables) const { MutableDocument outputDoc; for (auto&& pair : _expressions) { - outputDoc.addField(pair.first, pair.second->evaluate(root)); + outputDoc.addField(pair.first, pair.second->evaluate(root, variables)); } return outputDoc.freezeToValue(); } @@ -2005,7 +2022,8 @@ intrusive_ptr ExpressionFieldPath::optimize() { } if (getExpressionContext()->variables.hasConstantValue(_variable)) { - return ExpressionConstant::create(getExpressionContext(), evaluate(Document())); + return ExpressionConstant::create( + getExpressionContext(), evaluate(Document(), &(getExpressionContext()->variables))); } return intrusive_ptr(this); @@ -2072,17 +2090,16 @@ Value ExpressionFieldPath::evaluatePath(size_t index, const Document& input) con } } -Value ExpressionFieldPath::evaluate(const Document& root) const { - auto& vars = getExpressionContext()->variables; +Value ExpressionFieldPath::evaluate(const Document& root, Variables* variables) const { if (_fieldPath.getPathLength() == 1) // get the whole variable - return vars.getValue(_variable, root); + return variables->getValue(_variable, root); if (_variable == Variables::kRootId) { // ROOT is always a document so use optimized code path return evaluatePath(1, root); } - Value var = vars.getValue(_variable, root); + Value var = variables->getValue(_variable, root); switch (var.getType()) { case Object: return evaluatePath(1, var.getDocument()); @@ -2201,9 +2218,9 @@ Value ExpressionFilter::serialize(bool explain) const { << _filter->serialize(explain)))); } -Value ExpressionFilter::evaluate(const Document& root) const { +Value ExpressionFilter::evaluate(const Document& root, Variables* variables) const { // We are guaranteed at parse time that this isn't using our _varId. - const Value inputVal = _input->evaluate(root); + const Value inputVal = _input->evaluate(root, variables); if (inputVal.nullish()) return Value(BSONNULL); @@ -2218,11 +2235,10 @@ Value ExpressionFilter::evaluate(const Document& root) const { return inputVal; vector output; - auto& vars = getExpressionContext()->variables; for (const auto& elem : input) { - vars.setValue(_varId, elem); + variables->setValue(_varId, elem); - if (_filter->evaluate(root).coerceToBool()) { + if (_filter->evaluate(root, variables).coerceToBool()) { output.push_back(std::move(elem)); } } @@ -2345,15 +2361,14 @@ Value ExpressionLet::serialize(bool explain) const { DOC("$let" << DOC("vars" << vars.freeze() << "in" << _subExpression->serialize(explain)))); } -Value ExpressionLet::evaluate(const Document& root) const { +Value ExpressionLet::evaluate(const Document& root, Variables* variables) const { for (const auto& item : _variables) { // It is guaranteed at parse-time that these expressions don't use the variable ids we // are setting - getExpressionContext()->variables.setValue(item.first, - item.second.expression->evaluate(root)); + variables->setValue(item.first, item.second.expression->evaluate(root, variables)); } - return _subExpression->evaluate(root); + return _subExpression->evaluate(root, variables); } void ExpressionLet::_doAddDependencies(DepsTracker* deps) const { @@ -2441,9 +2456,9 @@ Value ExpressionMap::serialize(bool explain) const { << _each->serialize(explain)))); } -Value ExpressionMap::evaluate(const Document& root) const { +Value ExpressionMap::evaluate(const Document& root, Variables* variables) const { // guaranteed at parse time that this isn't using our _varId - const Value inputVal = _input->evaluate(root); + const Value inputVal = _input->evaluate(root, variables); if (inputVal.nullish()) return Value(BSONNULL); @@ -2459,9 +2474,9 @@ Value ExpressionMap::evaluate(const Document& root) const { vector output; output.reserve(input.size()); for (size_t i = 0; i < input.size(); i++) { - getExpressionContext()->variables.setValue(_varId, input[i]); + variables->setValue(_varId, input[i]); - Value toInsert = _each->evaluate(root); + Value toInsert = _each->evaluate(root, variables); if (toInsert.missing()) toInsert = Value(BSONNULL); // can't insert missing values into array @@ -2548,7 +2563,7 @@ Value ExpressionMeta::serialize(bool explain) const { MONGO_UNREACHABLE; } -Value ExpressionMeta::evaluate(const Document& root) const { +Value ExpressionMeta::evaluate(const Document& root, Variables* variables) const { switch (_metaType) { case MetaType::TEXT_SCORE: return root.hasTextScore() ? Value(root.getTextScore()) : Value(); @@ -2573,9 +2588,9 @@ void ExpressionMeta::_doAddDependencies(DepsTracker* deps) const { /* ----------------------- ExpressionMod ---------------------------- */ -Value ExpressionMod::evaluate(const Document& root) const { - Value lhs = _children[0]->evaluate(root); - Value rhs = _children[1]->evaluate(root); +Value ExpressionMod::evaluate(const Document& root, Variables* variables) const { + Value lhs = _children[0]->evaluate(root, variables); + Value rhs = _children[1]->evaluate(root, variables); BSONType leftType = lhs.getType(); BSONType rightType = rhs.getType(); @@ -2630,7 +2645,7 @@ const char* ExpressionMod::getOpName() const { /* ------------------------- ExpressionMultiply ----------------------------- */ -Value ExpressionMultiply::evaluate(const Document& root) const { +Value ExpressionMultiply::evaluate(const Document& root, Variables* variables) const { /* We'll try to return the narrowest possible result value. To do that without creating intermediate Values, do the arithmetic for double @@ -2645,7 +2660,7 @@ Value ExpressionMultiply::evaluate(const Document& root) const { const size_t n = _children.size(); for (size_t i = 0; i < n; ++i) { - Value val = _children[i]->evaluate(root); + Value val = _children[i]->evaluate(root, variables); if (val.numeric()) { BSONType oldProductType = productType; @@ -2693,12 +2708,12 @@ const char* ExpressionMultiply::getOpName() const { /* ----------------------- ExpressionIfNull ---------------------------- */ -Value ExpressionIfNull::evaluate(const Document& root) const { - Value pLeft(_children[0]->evaluate(root)); +Value ExpressionIfNull::evaluate(const Document& root, Variables* variables) const { + Value pLeft(_children[0]->evaluate(root, variables)); if (!pLeft.nullish()) return pLeft; - Value pRight(_children[1]->evaluate(root)); + Value pRight(_children[1]->evaluate(root, variables)); return pRight; } @@ -2709,9 +2724,9 @@ const char* ExpressionIfNull::getOpName() const { /* ----------------------- ExpressionIn ---------------------------- */ -Value ExpressionIn::evaluate(const Document& root) const { - Value argument(_children[0]->evaluate(root)); - Value arrayOfValues(_children[1]->evaluate(root)); +Value ExpressionIn::evaluate(const Document& root, Variables* variables) const { + Value argument(_children[0]->evaluate(root, variables)); + Value arrayOfValues(_children[1]->evaluate(root, variables)); uassert(40081, str::stream() << "$in requires an array as a second argument, found: " @@ -2753,8 +2768,8 @@ void uassertIfNotIntegralAndNonNegative(Value val, } // namespace -Value ExpressionIndexOfArray::evaluate(const Document& root) const { - Value arrayArg = _children[0]->evaluate(root); +Value ExpressionIndexOfArray::evaluate(const Document& root, Variables* variables) const { + Value arrayArg = _children[0]->evaluate(root, variables); if (arrayArg.nullish()) { return Value(BSONNULL); @@ -2766,7 +2781,7 @@ Value ExpressionIndexOfArray::evaluate(const Document& root) const { arrayArg.isArray()); std::vector array = arrayArg.getArray(); - auto args = evaluateAndValidateArguments(root, _children, array.size()); + auto args = evaluateAndValidateArguments(root, _children, array.size(), variables); for (int i = args.startIndex; i < args.endIndex; i++) { if (getExpressionContext()->getValueComparator().evaluate(array[i] == args.targetOfSearch)) { @@ -2779,11 +2794,14 @@ Value ExpressionIndexOfArray::evaluate(const Document& root) const { } ExpressionIndexOfArray::Arguments ExpressionIndexOfArray::evaluateAndValidateArguments( - const Document& root, const ExpressionVector& operands, size_t arrayLength) const { + const Document& root, + const ExpressionVector& operands, + size_t arrayLength, + Variables* variables) const { int startIndex = 0; if (operands.size() > 2) { - Value startIndexArg = operands[2]->evaluate(root); + Value startIndexArg = operands[2]->evaluate(root, variables); uassertIfNotIntegralAndNonNegative(startIndexArg, getOpName(), "starting index"); startIndex = startIndexArg.coerceToInt(); @@ -2791,13 +2809,13 @@ ExpressionIndexOfArray::Arguments ExpressionIndexOfArray::evaluateAndValidateArg int endIndex = arrayLength; if (operands.size() > 3) { - Value endIndexArg = operands[3]->evaluate(root); + Value endIndexArg = operands[3]->evaluate(root, variables); uassertIfNotIntegralAndNonNegative(endIndexArg, getOpName(), "ending index"); // Don't let 'endIndex' exceed the length of the array. endIndex = std::min(static_cast(arrayLength), endIndexArg.coerceToInt()); } - return {_children[1]->evaluate(root), startIndex, endIndex}; + return {_children[1]->evaluate(root, variables), startIndex, endIndex}; } /** @@ -2814,9 +2832,9 @@ public: _children = operands; } - virtual Value evaluate(const Document& root) const { + virtual Value evaluate(const Document& root, Variables* variables) const { - auto args = evaluateAndValidateArguments(root, _children, _indexMap.size()); + auto args = evaluateAndValidateArguments(root, _children, _indexMap.size(), variables); auto indexVec = _indexMap.find(args.targetOfSearch); if (indexVec == _indexMap.end()) @@ -2893,8 +2911,8 @@ bool stringHasTokenAtIndex(size_t index, const std::string& input, const std::st } // namespace -Value ExpressionIndexOfBytes::evaluate(const Document& root) const { - Value stringArg = _children[0]->evaluate(root); +Value ExpressionIndexOfBytes::evaluate(const Document& root, Variables* variables) const { + Value stringArg = _children[0]->evaluate(root, variables); if (stringArg.nullish()) { return Value(BSONNULL); @@ -2906,7 +2924,7 @@ Value ExpressionIndexOfBytes::evaluate(const Document& root) const { stringArg.getType() == String); const std::string& input = stringArg.getString(); - Value tokenArg = _children[1]->evaluate(root); + Value tokenArg = _children[1]->evaluate(root, variables); uassert(40092, str::stream() << "$indexOfBytes requires a string as the second argument, found: " << typeName(tokenArg.getType()), @@ -2915,14 +2933,14 @@ Value ExpressionIndexOfBytes::evaluate(const Document& root) const { size_t startIndex = 0; if (_children.size() > 2) { - Value startIndexArg = _children[2]->evaluate(root); + Value startIndexArg = _children[2]->evaluate(root, variables); uassertIfNotIntegralAndNonNegative(startIndexArg, getOpName(), "starting index"); startIndex = static_cast(startIndexArg.coerceToInt()); } size_t endIndex = input.size(); if (_children.size() > 3) { - Value endIndexArg = _children[3]->evaluate(root); + Value endIndexArg = _children[3]->evaluate(root, variables); uassertIfNotIntegralAndNonNegative(endIndexArg, getOpName(), "ending index"); // Don't let 'endIndex' exceed the length of the string. endIndex = std::min(input.size(), static_cast(endIndexArg.coerceToInt())); @@ -2947,8 +2965,8 @@ const char* ExpressionIndexOfBytes::getOpName() const { /* ----------------------- ExpressionIndexOfCP --------------------- */ -Value ExpressionIndexOfCP::evaluate(const Document& root) const { - Value stringArg = _children[0]->evaluate(root); +Value ExpressionIndexOfCP::evaluate(const Document& root, Variables* variables) const { + Value stringArg = _children[0]->evaluate(root, variables); if (stringArg.nullish()) { return Value(BSONNULL); @@ -2960,7 +2978,7 @@ Value ExpressionIndexOfCP::evaluate(const Document& root) const { stringArg.getType() == String); const std::string& input = stringArg.getString(); - Value tokenArg = _children[1]->evaluate(root); + Value tokenArg = _children[1]->evaluate(root, variables); uassert(40094, str::stream() << "$indexOfCP requires a string as the second argument, found: " << typeName(tokenArg.getType()), @@ -2969,7 +2987,7 @@ Value ExpressionIndexOfCP::evaluate(const Document& root) const { size_t startCodePointIndex = 0; if (_children.size() > 2) { - Value startIndexArg = _children[2]->evaluate(root); + Value startIndexArg = _children[2]->evaluate(root, variables); uassertIfNotIntegralAndNonNegative(startIndexArg, getOpName(), "starting index"); startCodePointIndex = static_cast(startIndexArg.coerceToInt()); } @@ -2992,7 +3010,7 @@ Value ExpressionIndexOfCP::evaluate(const Document& root) const { size_t endCodePointIndex = codePointLength; if (_children.size() > 3) { - Value endIndexArg = _children[3]->evaluate(root); + Value endIndexArg = _children[3]->evaluate(root, variables); uassertIfNotIntegralAndNonNegative(endIndexArg, getOpName(), "ending index"); // Don't let 'endCodePointIndex' exceed the number of code points in the string. @@ -3049,9 +3067,9 @@ const char* ExpressionLn::getOpName() const { /* ----------------------- ExpressionLog ---------------------------- */ -Value ExpressionLog::evaluate(const Document& root) const { - Value argVal = _children[0]->evaluate(root); - Value baseVal = _children[1]->evaluate(root); +Value ExpressionLog::evaluate(const Document& root, Variables* variables) const { + Value argVal = _children[0]->evaluate(root, variables); + Value baseVal = _children[1]->evaluate(root, variables); if (argVal.nullish() || baseVal.nullish()) return Value(BSONNULL); @@ -3143,8 +3161,8 @@ intrusive_ptr ExpressionNary::optimize() { // If all the operands are constant expressions, collapse the expression into one constant // expression. if (constOperandCount == _children.size()) { - return intrusive_ptr( - ExpressionConstant::create(getExpressionContext(), evaluate(Document()))); + return intrusive_ptr(ExpressionConstant::create( + getExpressionContext(), evaluate(Document(), &(getExpressionContext()->variables)))); } // If the expression is associative, we can collapse all the consecutive constant operands into @@ -3187,8 +3205,9 @@ intrusive_ptr ExpressionNary::optimize() { if (constExpressions.size() > 1) { ExpressionVector childrenSave = std::move(_children); _children = std::move(constExpressions); - optimizedOperands.emplace_back( - ExpressionConstant::create(getExpressionContext(), evaluate(Document()))); + optimizedOperands.emplace_back(ExpressionConstant::create( + getExpressionContext(), + evaluate(Document(), &(getExpressionContext()->variables)))); _children = std::move(childrenSave); } else { optimizedOperands.insert( @@ -3202,8 +3221,9 @@ intrusive_ptr ExpressionNary::optimize() { if (constExpressions.size() > 1) { _children = std::move(constExpressions); - optimizedOperands.emplace_back( - ExpressionConstant::create(getExpressionContext(), evaluate(Document()))); + optimizedOperands.emplace_back(ExpressionConstant::create( + getExpressionContext(), + evaluate(Document(), &(getExpressionContext()->variables)))); } else { optimizedOperands.insert( optimizedOperands.end(), constExpressions.begin(), constExpressions.end()); @@ -3236,8 +3256,8 @@ Value ExpressionNary::serialize(bool explain) const { /* ------------------------- ExpressionNot ----------------------------- */ -Value ExpressionNot::evaluate(const Document& root) const { - Value pOp(_children[0]->evaluate(root)); +Value ExpressionNot::evaluate(const Document& root, Variables* variables) const { + Value pOp(_children[0]->evaluate(root, variables)); bool b = pOp.coerceToBool(); return Value(!b); @@ -3250,10 +3270,10 @@ const char* ExpressionNot::getOpName() const { /* -------------------------- ExpressionOr ----------------------------- */ -Value ExpressionOr::evaluate(const Document& root) const { +Value ExpressionOr::evaluate(const Document& root, Variables* variables) const { const size_t n = _children.size(); for (size_t i = 0; i < n; ++i) { - Value pValue(_children[i]->evaluate(root)); + Value pValue(_children[i]->evaluate(root, variables)); if (pValue.coerceToBool()) return Value(true); } @@ -3423,9 +3443,9 @@ intrusive_ptr ExpressionPow::create( return expr; } -Value ExpressionPow::evaluate(const Document& root) const { - Value baseVal = _children[0]->evaluate(root); - Value expVal = _children[1]->evaluate(root); +Value ExpressionPow::evaluate(const Document& root, Variables* variables) const { + Value baseVal = _children[0]->evaluate(root, variables); + Value expVal = _children[1]->evaluate(root, variables); if (baseVal.nullish() || expVal.nullish()) return Value(BSONNULL); @@ -3541,9 +3561,9 @@ const char* ExpressionPow::getOpName() const { /* ------------------------- ExpressionRange ------------------------------ */ -Value ExpressionRange::evaluate(const Document& root) const { - Value startVal(_children[0]->evaluate(root)); - Value endVal(_children[1]->evaluate(root)); +Value ExpressionRange::evaluate(const Document& root, Variables* variables) const { + Value startVal(_children[0]->evaluate(root, variables)); + Value endVal(_children[1]->evaluate(root, variables)); uassert(34443, str::stream() << "$range requires a numeric starting value, found value of type: " @@ -3570,7 +3590,7 @@ Value ExpressionRange::evaluate(const Document& root) const { int step = 1; if (_children.size() == 3) { // A step was specified by the user. - Value stepVal(_children[2]->evaluate(root)); + Value stepVal(_children[2]->evaluate(root, variables)); uassert(34447, str::stream() << "$range requires a numeric step value, found value of type:" @@ -3644,8 +3664,8 @@ intrusive_ptr ExpressionReduce::parse( expCtx, std::move(input), std::move(initial), std::move(in), thisVar, valueVar); } -Value ExpressionReduce::evaluate(const Document& root) const { - Value inputVal = _input->evaluate(root); +Value ExpressionReduce::evaluate(const Document& root, Variables* variables) const { + Value inputVal = _input->evaluate(root, variables); if (inputVal.nullish()) { return Value(BSONNULL); @@ -3656,14 +3676,13 @@ Value ExpressionReduce::evaluate(const Document& root) const { << inputVal.toString(), inputVal.isArray()); - Value accumulatedValue = _initial->evaluate(root); - auto& vars = getExpressionContext()->variables; + Value accumulatedValue = _initial->evaluate(root, variables); for (auto&& elem : inputVal.getArray()) { - vars.setValue(_thisVar, elem); - vars.setValue(_valueVar, accumulatedValue); + variables->setValue(_thisVar, elem); + variables->setValue(_valueVar, accumulatedValue); - accumulatedValue = _in->evaluate(root); + accumulatedValue = _in->evaluate(root, variables); } return accumulatedValue; @@ -3691,8 +3710,8 @@ Value ExpressionReduce::serialize(bool explain) const { /* ------------------------ ExpressionReverseArray ------------------------ */ -Value ExpressionReverseArray::evaluate(const Document& root) const { - Value input(_children[0]->evaluate(root)); +Value ExpressionReverseArray::evaluate(const Document& root, Variables* variables) const { + Value input(_children[0]->evaluate(root, variables)); if (input.nullish()) { return Value(BSONNULL); @@ -3728,9 +3747,9 @@ ValueSet arrayToSet(const Value& val, const ValueComparator& valueComparator) { /* ----------------------- ExpressionSetDifference ---------------------------- */ -Value ExpressionSetDifference::evaluate(const Document& root) const { - const Value lhs = _children[0]->evaluate(root); - const Value rhs = _children[1]->evaluate(root); +Value ExpressionSetDifference::evaluate(const Document& root, Variables* variables) const { + const Value lhs = _children[0]->evaluate(root, variables); + const Value rhs = _children[1]->evaluate(root, variables); if (lhs.nullish() || rhs.nullish()) { return Value(BSONNULL); @@ -3774,13 +3793,13 @@ void ExpressionSetEquals::validateArguments(const ExpressionVector& args) const args.size() >= 2); } -Value ExpressionSetEquals::evaluate(const Document& root) const { +Value ExpressionSetEquals::evaluate(const Document& root, Variables* variables) const { const size_t n = _children.size(); const auto& valueComparator = getExpressionContext()->getValueComparator(); ValueSet lhs = valueComparator.makeOrderedValueSet(); for (size_t i = 0; i < n; i++) { - const Value nextEntry = _children[i]->evaluate(root); + const Value nextEntry = _children[i]->evaluate(root, variables); uassert(17044, str::stream() << "All operands of $setEquals must be arrays. One " << "argument is of type: " @@ -3811,12 +3830,12 @@ const char* ExpressionSetEquals::getOpName() const { /* ----------------------- ExpressionSetIntersection ---------------------------- */ -Value ExpressionSetIntersection::evaluate(const Document& root) const { +Value ExpressionSetIntersection::evaluate(const Document& root, Variables* variables) const { const size_t n = _children.size(); const auto& valueComparator = getExpressionContext()->getValueComparator(); ValueSet currentIntersection = valueComparator.makeOrderedValueSet(); for (size_t i = 0; i < n; i++) { - const Value nextEntry = _children[i]->evaluate(root); + const Value nextEntry = _children[i]->evaluate(root, variables); if (nextEntry.nullish()) { return Value(BSONNULL); } @@ -3872,9 +3891,9 @@ Value setIsSubsetHelper(const vector& lhs, const ValueSet& rhs) { } } -Value ExpressionSetIsSubset::evaluate(const Document& root) const { - const Value lhs = _children[0]->evaluate(root); - const Value rhs = _children[1]->evaluate(root); +Value ExpressionSetIsSubset::evaluate(const Document& root, Variables* variables) const { + const Value lhs = _children[0]->evaluate(root, variables); + const Value rhs = _children[1]->evaluate(root, variables); uassert(17046, str::stream() << "both operands of $setIsSubset must be arrays. First " @@ -3907,8 +3926,8 @@ public: _children = operands; } - virtual Value evaluate(const Document& root) const { - const Value lhs = _children[0]->evaluate(root); + virtual Value evaluate(const Document& root, Variables* variables) const { + const Value lhs = _children[0]->evaluate(root, variables); uassert(17310, str::stream() << "both operands of $setIsSubset must be arrays. First " @@ -3955,11 +3974,11 @@ const char* ExpressionSetIsSubset::getOpName() const { /* ----------------------- ExpressionSetUnion ---------------------------- */ -Value ExpressionSetUnion::evaluate(const Document& root) const { +Value ExpressionSetUnion::evaluate(const Document& root, Variables* variables) const { ValueSet unionedSet = getExpressionContext()->getValueComparator().makeOrderedValueSet(); const size_t n = _children.size(); for (size_t i = 0; i < n; i++) { - const Value newEntries = _children[i]->evaluate(root); + const Value newEntries = _children[i]->evaluate(root, variables); if (newEntries.nullish()) { return Value(BSONNULL); } @@ -3981,8 +4000,8 @@ const char* ExpressionSetUnion::getOpName() const { /* ----------------------- ExpressionIsArray ---------------------------- */ -Value ExpressionIsArray::evaluate(const Document& root) const { - Value argument = _children[0]->evaluate(root); +Value ExpressionIsArray::evaluate(const Document& root, Variables* variables) const { + Value argument = _children[0]->evaluate(root, variables); return Value(argument.isArray()); } @@ -3993,12 +4012,12 @@ const char* ExpressionIsArray::getOpName() const { /* ----------------------- ExpressionSlice ---------------------------- */ -Value ExpressionSlice::evaluate(const Document& root) const { +Value ExpressionSlice::evaluate(const Document& root, Variables* variables) const { const size_t n = _children.size(); - Value arrayVal = _children[0]->evaluate(root); + Value arrayVal = _children[0]->evaluate(root, variables); // Could be either a start index or the length from 0. - Value arg2 = _children[1]->evaluate(root); + Value arg2 = _children[1]->evaluate(root, variables); if (arrayVal.nullish() || arg2.nullish()) { return Value(BSONNULL); @@ -4049,7 +4068,7 @@ Value ExpressionSlice::evaluate(const Document& root) const { start = std::min(array.size(), size_t(startInt)); } - Value countVal = _children[2]->evaluate(root); + Value countVal = _children[2]->evaluate(root, variables); if (countVal.nullish()) { return Value(BSONNULL); @@ -4084,8 +4103,8 @@ const char* ExpressionSlice::getOpName() const { /* ----------------------- ExpressionSize ---------------------------- */ -Value ExpressionSize::evaluate(const Document& root) const { - Value array = _children[0]->evaluate(root); +Value ExpressionSize::evaluate(const Document& root, Variables* variables) const { + Value array = _children[0]->evaluate(root, variables); uassert(17124, str::stream() << "The argument to $size must be an array, but was of type: " @@ -4101,9 +4120,9 @@ const char* ExpressionSize::getOpName() const { /* ----------------------- ExpressionSplit --------------------------- */ -Value ExpressionSplit::evaluate(const Document& root) const { - Value inputArg = _children[0]->evaluate(root); - Value separatorArg = _children[1]->evaluate(root); +Value ExpressionSplit::evaluate(const Document& root, Variables* variables) const { + Value inputArg = _children[0]->evaluate(root, variables); + Value separatorArg = _children[1]->evaluate(root, variables); if (inputArg.nullish() || separatorArg.nullish()) { return Value(BSONNULL); @@ -4179,9 +4198,9 @@ const char* ExpressionSqrt::getOpName() const { /* ----------------------- ExpressionStrcasecmp ---------------------------- */ -Value ExpressionStrcasecmp::evaluate(const Document& root) const { - Value pString1(_children[0]->evaluate(root)); - Value pString2(_children[1]->evaluate(root)); +Value ExpressionStrcasecmp::evaluate(const Document& root, Variables* variables) const { + Value pString1(_children[0]->evaluate(root, variables)); + Value pString2(_children[1]->evaluate(root, variables)); /* boost::iequals returns a bool not an int so strings must actually be allocated */ string str1 = boost::to_upper_copy(pString1.coerceToString()); @@ -4203,10 +4222,10 @@ const char* ExpressionStrcasecmp::getOpName() const { /* ----------------------- ExpressionSubstrBytes ---------------------------- */ -Value ExpressionSubstrBytes::evaluate(const Document& root) const { - Value pString(_children[0]->evaluate(root)); - Value pLower(_children[1]->evaluate(root)); - Value pLength(_children[2]->evaluate(root)); +Value ExpressionSubstrBytes::evaluate(const Document& root, Variables* variables) const { + Value pString(_children[0]->evaluate(root, variables)); + Value pLower(_children[1]->evaluate(root, variables)); + Value pLength(_children[2]->evaluate(root, variables)); string str = pString.coerceToString(); uassert(16034, @@ -4268,10 +4287,10 @@ const char* ExpressionSubstrBytes::getOpName() const { /* ----------------------- ExpressionSubstrCP ---------------------------- */ -Value ExpressionSubstrCP::evaluate(const Document& root) const { - Value inputVal(_children[0]->evaluate(root)); - Value lowerVal(_children[1]->evaluate(root)); - Value lengthVal(_children[2]->evaluate(root)); +Value ExpressionSubstrCP::evaluate(const Document& root, Variables* variables) const { + Value inputVal(_children[0]->evaluate(root, variables)); + Value lowerVal(_children[1]->evaluate(root, variables)); + Value lengthVal(_children[2]->evaluate(root, variables)); std::string str = inputVal.coerceToString(); uassert(34450, @@ -4343,8 +4362,8 @@ const char* ExpressionSubstrCP::getOpName() const { /* ----------------------- ExpressionStrLenBytes ------------------------- */ -Value ExpressionStrLenBytes::evaluate(const Document& root) const { - Value str(_children[0]->evaluate(root)); +Value ExpressionStrLenBytes::evaluate(const Document& root, Variables* variables) const { + Value str(_children[0]->evaluate(root, variables)); uassert(34473, str::stream() << "$strLenBytes requires a string argument, found: " @@ -4366,8 +4385,8 @@ const char* ExpressionStrLenBytes::getOpName() const { /* ----------------------- ExpressionStrLenCP ------------------------- */ -Value ExpressionStrLenCP::evaluate(const Document& root) const { - Value val(_children[0]->evaluate(root)); +Value ExpressionStrLenCP::evaluate(const Document& root, Variables* variables) const { + Value val(_children[0]->evaluate(root, variables)); uassert(34471, str::stream() << "$strLenCP requires a string argument, found: " @@ -4391,9 +4410,9 @@ const char* ExpressionStrLenCP::getOpName() const { /* ----------------------- ExpressionSubtract ---------------------------- */ -Value ExpressionSubtract::evaluate(const Document& root) const { - Value lhs = _children[0]->evaluate(root); - Value rhs = _children[1]->evaluate(root); +Value ExpressionSubtract::evaluate(const Document& root, Variables* variables) const { + Value lhs = _children[0]->evaluate(root, variables); + Value rhs = _children[1]->evaluate(root, variables); BSONType diffType = Value::getWidestNumeric(rhs.getType(), lhs.getType()); @@ -4441,12 +4460,12 @@ const char* ExpressionSubtract::getOpName() const { REGISTER_EXPRESSION(switch, ExpressionSwitch::parse); -Value ExpressionSwitch::evaluate(const Document& root) const { +Value ExpressionSwitch::evaluate(const Document& root, Variables* variables) const { for (auto&& branch : _branches) { - Value caseExpression(branch.first->evaluate(root)); + Value caseExpression(branch.first->evaluate(root, variables)); if (caseExpression.coerceToBool()) { - return branch.second->evaluate(root); + return branch.second->evaluate(root, variables); } } @@ -4454,7 +4473,7 @@ Value ExpressionSwitch::evaluate(const Document& root) const { "$switch could not find a matching branch for an input, and no default was specified.", _default); - return _default->evaluate(root); + return _default->evaluate(root, variables); } boost::intrusive_ptr ExpressionSwitch::parse( @@ -4577,8 +4596,8 @@ Value ExpressionSwitch::serialize(bool explain) const { /* ------------------------- ExpressionToLower ----------------------------- */ -Value ExpressionToLower::evaluate(const Document& root) const { - Value pString(_children[0]->evaluate(root)); +Value ExpressionToLower::evaluate(const Document& root, Variables* variables) const { + Value pString(_children[0]->evaluate(root, variables)); string str = pString.coerceToString(); boost::to_lower(str); return Value(str); @@ -4591,8 +4610,8 @@ const char* ExpressionToLower::getOpName() const { /* ------------------------- ExpressionToUpper -------------------------- */ -Value ExpressionToUpper::evaluate(const Document& root) const { - Value pString(_children[0]->evaluate(root)); +Value ExpressionToUpper::evaluate(const Document& root, Variables* variables) const { + Value pString(_children[0]->evaluate(root, variables)); string str(pString.coerceToString()); boost::to_upper(str); return Value(str); @@ -4714,8 +4733,8 @@ std::vector extractCodePointsFromChars(StringData utf8String, } } // namespace -Value ExpressionTrim::evaluate(const Document& root) const { - auto unvalidatedInput = _input->evaluate(root); +Value ExpressionTrim::evaluate(const Document& root, Variables* variables) const { + auto unvalidatedInput = _input->evaluate(root, variables); if (unvalidatedInput.nullish()) { return Value(BSONNULL); } @@ -4731,7 +4750,7 @@ Value ExpressionTrim::evaluate(const Document& root) const { if (!_characters) { return Value(doTrim(input, kDefaultTrimWhitespaceChars)); } - auto unvalidatedUserChars = _characters->evaluate(root); + auto unvalidatedUserChars = _characters->evaluate(root, variables); if (unvalidatedUserChars.nullish()) { return Value(BSONNULL); } @@ -4810,7 +4829,9 @@ boost::intrusive_ptr ExpressionTrim::optimize() { _characters = _characters->optimize(); } if (ExpressionConstant::allNullOrConstant({_input, _characters})) { - return ExpressionConstant::create(getExpressionContext(), this->evaluate(Document())); + return ExpressionConstant::create( + getExpressionContext(), + this->evaluate(Document(), &(getExpressionContext()->variables))); } return this; } @@ -4849,10 +4870,11 @@ static Value evaluateRoundOrTrunc(const Document& root, const std::vector>& children, const std::string& opName, Decimal128::RoundingMode roundingMode, - double (*doubleOp)(double)) { + double (*doubleOp)(double), + Variables* variables) { constexpr auto maxPrecision = 100LL; constexpr auto minPrecision = -20LL; - auto numericArg = Value(children[0]->evaluate(root)); + auto numericArg = Value(children[0]->evaluate(root, variables)); if (numericArg.nullish()) { return Value(BSONNULL); } @@ -4863,7 +4885,7 @@ static Value evaluateRoundOrTrunc(const Document& root, long long precisionValue = 0; if (children.size() > 1) { - auto precisionArg = Value(children[1]->evaluate(root)); + auto precisionArg = Value(children[1]->evaluate(root, variables)); if (precisionArg.nullish()) { return Value(BSONNULL); } @@ -4919,9 +4941,9 @@ static Value evaluateRoundOrTrunc(const Document& root, } } -Value ExpressionRound::evaluate(const Document& root) const { +Value ExpressionRound::evaluate(const Document& root, Variables* variables) const { return evaluateRoundOrTrunc( - root, _children, getOpName(), Decimal128::kRoundTiesToEven, &std::round); + root, _children, getOpName(), Decimal128::kRoundTiesToEven, &std::round, variables); } REGISTER_EXPRESSION_WITH_MIN_VERSION( @@ -4932,9 +4954,9 @@ const char* ExpressionRound::getOpName() const { return "$round"; } -Value ExpressionTrunc::evaluate(const Document& root) const { +Value ExpressionTrunc::evaluate(const Document& root, Variables* variables) const { return evaluateRoundOrTrunc( - root, _children, getOpName(), Decimal128::kRoundTowardZero, &std::trunc); + root, _children, getOpName(), Decimal128::kRoundTowardZero, &std::trunc, variables); } intrusive_ptr ExpressionTrunc::parse(const intrusive_ptr& expCtx, @@ -4968,8 +4990,8 @@ const char* ExpressionTrunc::getOpName() const { /* ------------------------- ExpressionType ----------------------------- */ -Value ExpressionType::evaluate(const Document& root) const { - Value val(_children[0]->evaluate(root)); +Value ExpressionType::evaluate(const Document& root, Variables* variables) const { + Value val(_children[0]->evaluate(root, variables)); return Value(StringData(typeName(val.getType()))); } @@ -5052,7 +5074,7 @@ intrusive_ptr ExpressionZip::parse( expCtx, useLongestLength, std::move(children), std::move(inputs), std::move(defaults)); } -Value ExpressionZip::evaluate(const Document& root) const { +Value ExpressionZip::evaluate(const Document& root, Variables* variables) const { // Evaluate input values. vector> inputValues; inputValues.reserve(_inputs.size()); @@ -5060,7 +5082,7 @@ Value ExpressionZip::evaluate(const Document& root) const { size_t minArraySize = 0; size_t maxArraySize = 0; for (size_t i = 0; i < _inputs.size(); i++) { - Value evalExpr = _inputs[i].get()->evaluate(root); + Value evalExpr = _inputs[i].get()->evaluate(root, variables); if (evalExpr.nullish()) { return Value(BSONNULL); } @@ -5089,7 +5111,7 @@ Value ExpressionZip::evaluate(const Document& root) 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].get()->evaluate(root); + evaluatedDefaults[i] = _defaults[i].get()->evaluate(root, variables); } } @@ -5673,16 +5695,16 @@ intrusive_ptr ExpressionConvert::parse( expCtx, std::move(input), std::move(to), std::move(onError), std::move(onNull)); } -Value ExpressionConvert::evaluate(const Document& root) const { - auto toValue = _to->evaluate(root); - Value inputValue = _input->evaluate(root); +Value ExpressionConvert::evaluate(const Document& root, Variables* variables) const { + auto toValue = _to->evaluate(root, variables); + Value inputValue = _input->evaluate(root, variables); boost::optional targetType; if (!toValue.nullish()) { targetType = computeTargetType(toValue); } if (inputValue.nullish()) { - return _onNull ? _onNull->evaluate(root) : Value(BSONNULL); + return _onNull ? _onNull->evaluate(root, variables) : Value(BSONNULL); } else if (!targetType) { // "to" evaluated to a nullish value. return Value(BSONNULL); @@ -5692,7 +5714,7 @@ Value ExpressionConvert::evaluate(const Document& root) const { return performConversion(*targetType, inputValue); } catch (const ExceptionFor&) { if (_onError) { - return _onError->evaluate(root); + return _onError->evaluate(root, variables); } else { throw; } @@ -5715,7 +5737,8 @@ boost::intrusive_ptr ExpressionConvert::optimize() { // and _onNull values could still be legally folded if those values are not needed. Support for // that case would add more complexity than it's worth, though. if (ExpressionConstant::allNullOrConstant({_input, _to, _onError, _onNull})) { - return ExpressionConstant::create(getExpressionContext(), evaluate(Document{})); + return ExpressionConstant::create( + getExpressionContext(), evaluate(Document{}, &(getExpressionContext()->variables))); } return this; @@ -5821,10 +5844,10 @@ auto CommonRegexParse(const boost::intrusive_ptr& expCtx, /* -------------------------- ExpressionRegex ------------------------------ */ ExpressionRegex::RegexExecutionState ExpressionRegex::buildInitialState( - const Document& root) const { - Value textInput = _input->evaluate(root); - Value regexPattern = _regex->evaluate(root); - Value regexOptions = _options ? _options->evaluate(root) : Value(BSONNULL); + const Document& root, Variables* variables) const { + Value textInput = _input->evaluate(root, variables); + Value regexPattern = _regex->evaluate(root, variables); + Value regexOptions = _options ? _options->evaluate(root, variables) : Value(BSONNULL); auto executionState = _initialExecStateForConstantRegex.value_or(RegexExecutionState()); @@ -6056,8 +6079,8 @@ boost::intrusive_ptr ExpressionRegexFind::parse( expCtx, std::move(input), std::move(regex), std::move(options), opName); } -Value ExpressionRegexFind::evaluate(const Document& root) const { - auto executionState = buildInitialState(root); +Value ExpressionRegexFind::evaluate(const Document& root, Variables* variables) const { + auto executionState = buildInitialState(root, variables); if (executionState.nullish()) { return Value(BSONNULL); } @@ -6080,9 +6103,9 @@ boost::intrusive_ptr ExpressionRegexFindAll::parse( expCtx, std::move(input), std::move(regex), std::move(options), opName); } -Value ExpressionRegexFindAll::evaluate(const Document& root) const { +Value ExpressionRegexFindAll::evaluate(const Document& root, Variables* variables) const { std::vector output; - auto executionState = buildInitialState(root); + auto executionState = buildInitialState(root, variables); if (executionState.nullish()) { return Value(output); } @@ -6144,8 +6167,8 @@ boost::intrusive_ptr ExpressionRegexMatch::parse( expCtx, std::move(input), std::move(regex), std::move(options), opName); } -Value ExpressionRegexMatch::evaluate(const Document& root) const { - auto executionState = buildInitialState(root); +Value ExpressionRegexMatch::evaluate(const Document& root, Variables* variables) const { + auto executionState = buildInitialState(root, variables); // Return output of execute only if regex is not nullish. return executionState.nullish() ? Value(false) : Value(execute(&executionState) > 0); } diff --git a/src/mongo/db/pipeline/expression.h b/src/mongo/db/pipeline/expression.h index 3ee13d4ea5e..8b7eecf0c72 100644 --- a/src/mongo/db/pipeline/expression.h +++ b/src/mongo/db/pipeline/expression.h @@ -146,9 +146,12 @@ public: virtual Value serialize(bool explain) const = 0; /** - * Evaluate expression with respect to the Document given by 'root', and return the result. + * Evaluate the expression with respect to the Document given by 'root' and the Variables given + * by 'variables'. It is an error to supply a Variables argument whose built-in variables (like + * $$NOW) are not set. This method is thread-safe, so long as the 'variables' passed in here is + * not shared between threads. */ - virtual Value evaluate(const Document& root) const = 0; + virtual Value evaluate(const Document& root, Variables* variables) const = 0; /** * Returns information about the paths computed by this expression. This only needs to be @@ -248,6 +251,10 @@ public: return _children; } + const boost::intrusive_ptr& getExpressionContext() const { + return _expCtx; + } + protected: using ExpressionVector = std::vector>; @@ -261,11 +268,6 @@ protected: } } - - const boost::intrusive_ptr& getExpressionContext() const { - return _expCtx; - } - virtual void _doAddDependencies(DepsTracker* deps) const = 0; /** @@ -404,13 +406,13 @@ public: explicit ExpressionFromAccumulator(const boost::intrusive_ptr& expCtx) : ExpressionVariadic>(expCtx) {} - Value evaluate(const Document& root) const final { + Value evaluate(const Document& root, Variables* variables) const final { Accumulator accum(this->getExpressionContext()); const auto n = this->_children.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->_children[0]->evaluate(root); + Value singleVal = this->_children[0]->evaluate(root, variables); if (singleVal.getType() == Array) { for (const Value& val : singleVal.getArray()) { accum.process(val, false); @@ -421,7 +423,7 @@ public: } else { // If multiple arguments are given, pass all arguments to the accumulator. for (auto&& argument : this->_children) { - accum.process(argument->evaluate(root), false); + accum.process(argument->evaluate(root, variables), false); } } return accum.getValue(false); @@ -460,8 +462,8 @@ public: virtual ~ExpressionSingleNumericArg() = default; - Value evaluate(const Document& root) const final { - Value arg = this->_children[0]->evaluate(root); + Value evaluate(const Document& root, Variables* variables) const final { + Value arg = this->_children[0]->evaluate(root, variables); if (arg.nullish()) return Value(BSONNULL); @@ -494,15 +496,15 @@ public: * 2. If either input is not numeric, it throws an error. * 3. Call evaluateNumericArgs on the two numeric args. */ - Value evaluate(const Document& root) const final { - Value arg1 = this->_children[0]->evaluate(root); + Value evaluate(const Document& root, Variables* variables) const final { + Value arg1 = this->_children[0]->evaluate(root, variables); if (arg1.nullish()) return Value(BSONNULL); uassert(51044, str::stream() << this->getOpName() << " only supports numeric types, not " << typeName(arg1.getType()), arg1.numeric()); - Value arg2 = this->_children[1]->evaluate(root); + Value arg2 = this->_children[1]->evaluate(root, variables); if (arg2.nullish()) return Value(BSONNULL); uassert(51045, @@ -525,7 +527,7 @@ public: class ExpressionConstant final : public Expression { public: boost::intrusive_ptr optimize() final; - Value evaluate(const Document& root) const final; + Value evaluate(const Document& root, Variables* variables) const final; Value serialize(bool explain) const final; const char* getOpName() const; @@ -593,8 +595,8 @@ class DateExpressionAcceptingTimeZone : public Expression { public: virtual ~DateExpressionAcceptingTimeZone() {} - Value evaluate(const Document& root) const final { - auto dateVal = _date->evaluate(root); + Value evaluate(const Document& root, Variables* variables) const final { + auto dateVal = _date->evaluate(root, variables); if (dateVal.nullish()) { return Value(BSONNULL); } @@ -603,7 +605,7 @@ public: if (!_timeZone) { return evaluateDate(date, TimeZoneDatabase::utcZone()); } - auto timeZoneId = _timeZone->evaluate(root); + auto timeZoneId = _timeZone->evaluate(root, variables); if (timeZoneId.nullish()) { return Value(BSONNULL); } @@ -641,7 +643,8 @@ public: } if (ExpressionConstant::allNullOrConstant({_date, _timeZone})) { // Everything is a constant, so we can turn into a constant. - return ExpressionConstant::create(getExpressionContext(), evaluate(Document{})); + return ExpressionConstant::create( + getExpressionContext(), evaluate(Document{}, &(getExpressionContext()->variables))); } return this; } @@ -754,7 +757,7 @@ public: explicit ExpressionAdd(const boost::intrusive_ptr& expCtx) : ExpressionVariadic(expCtx) {} - Value evaluate(const Document& root) const final; + Value evaluate(const Document& root, Variables* variables) const final; const char* getOpName() const final; bool isAssociative() const final { @@ -776,7 +779,7 @@ public: explicit ExpressionAllElementsTrue(const boost::intrusive_ptr& expCtx) : ExpressionFixedArity(expCtx) {} - Value evaluate(const Document& root) const final; + Value evaluate(const Document& root, Variables* variables) const final; const char* getOpName() const final; void acceptVisitor(ExpressionVisitor* visitor) final { @@ -791,7 +794,7 @@ public: : ExpressionVariadic(expCtx) {} boost::intrusive_ptr optimize() final; - Value evaluate(const Document& root) const final; + Value evaluate(const Document& root, Variables* variables) const final; const char* getOpName() const final; bool isAssociative() const final { @@ -813,7 +816,7 @@ public: explicit ExpressionAnyElementTrue(const boost::intrusive_ptr& expCtx) : ExpressionFixedArity(expCtx) {} - Value evaluate(const Document& root) const final; + Value evaluate(const Document& root, Variables* variables) const final; const char* getOpName() const final; void acceptVisitor(ExpressionVisitor* visitor) final { @@ -827,7 +830,7 @@ public: explicit ExpressionArray(const boost::intrusive_ptr& expCtx) : ExpressionVariadic(expCtx) {} - Value evaluate(const Document& root) const final; + Value evaluate(const Document& root, Variables* variables) const final; Value serialize(bool explain) const final; boost::intrusive_ptr optimize() final; const char* getOpName() const final; @@ -843,7 +846,7 @@ public: explicit ExpressionArrayElemAt(const boost::intrusive_ptr& expCtx) : ExpressionFixedArity(expCtx) {} - Value evaluate(const Document& root) const final; + Value evaluate(const Document& root, Variables* variables) const final; const char* getOpName() const final; void acceptVisitor(ExpressionVisitor* visitor) final { @@ -856,7 +859,7 @@ public: explicit ExpressionObjectToArray(const boost::intrusive_ptr& expCtx) : ExpressionFixedArity(expCtx) {} - Value evaluate(const Document& root) const final; + Value evaluate(const Document& root, Variables* variables) const final; const char* getOpName() const final; void acceptVisitor(ExpressionVisitor* visitor) final { @@ -869,7 +872,7 @@ public: explicit ExpressionArrayToObject(const boost::intrusive_ptr& expCtx) : ExpressionFixedArity(expCtx) {} - Value evaluate(const Document& root) const final; + Value evaluate(const Document& root, Variables* variables) const final; const char* getOpName() const final; void acceptVisitor(ExpressionVisitor* visitor) final { @@ -894,7 +897,7 @@ public: class ExpressionCoerceToBool final : public Expression { public: boost::intrusive_ptr optimize() final; - Value evaluate(const Document& root) const final; + Value evaluate(const Document& root, Variables* variables) const final; Value serialize(bool explain) const final; static boost::intrusive_ptr create( @@ -935,7 +938,7 @@ public: ExpressionCompare(const boost::intrusive_ptr& expCtx, CmpOp cmpOp) : ExpressionFixedArity(expCtx), cmpOp(cmpOp) {} - Value evaluate(const Document& root) const final; + Value evaluate(const Document& root, Variables* variables) const final; const char* getOpName() const final; CmpOp getOp() const { @@ -968,7 +971,7 @@ public: explicit ExpressionConcat(const boost::intrusive_ptr& expCtx) : ExpressionVariadic(expCtx) {} - Value evaluate(const Document& root) const final; + Value evaluate(const Document& root, Variables* variables) const final; const char* getOpName() const final; bool isAssociative() const final { @@ -986,7 +989,7 @@ public: explicit ExpressionConcatArrays(const boost::intrusive_ptr& expCtx) : ExpressionVariadic(expCtx) {} - Value evaluate(const Document& root) const final; + Value evaluate(const Document& root, Variables* variables) const final; const char* getOpName() const final; bool isAssociative() const final { @@ -1003,7 +1006,7 @@ class ExpressionCond final : public ExpressionFixedArity { public: explicit ExpressionCond(const boost::intrusive_ptr& expCtx) : Base(expCtx) {} - Value evaluate(const Document& root) const final; + Value evaluate(const Document& root, Variables* variables) const final; const char* getOpName() const final; static boost::intrusive_ptr parse( @@ -1023,7 +1026,7 @@ class ExpressionDateFromString final : public Expression { public: boost::intrusive_ptr optimize() final; Value serialize(bool explain) const final; - Value evaluate(const Document&) const final; + Value evaluate(const Document& root, Variables* variables) const final; static boost::intrusive_ptr parse( const boost::intrusive_ptr& expCtx, @@ -1056,7 +1059,7 @@ class ExpressionDateFromParts final : public Expression { public: boost::intrusive_ptr optimize() final; Value serialize(bool explain) const final; - Value evaluate(const Document& root) const final; + Value evaluate(const Document& root, Variables* variables) const final; static boost::intrusive_ptr parse( const boost::intrusive_ptr& expCtx, @@ -1100,7 +1103,8 @@ private: boost::intrusive_ptr field, StringData fieldName, long long defaultValue, - long long* returnValue) const; + long long* returnValue, + Variables* variables) const; boost::intrusive_ptr& _year; boost::intrusive_ptr& _month; @@ -1119,7 +1123,7 @@ class ExpressionDateToParts final : public Expression { public: boost::intrusive_ptr optimize() final; Value serialize(bool explain) const final; - Value evaluate(const Document& root) const final; + Value evaluate(const Document& root, Variables* variables) const final; static boost::intrusive_ptr parse( const boost::intrusive_ptr& expCtx, @@ -1142,7 +1146,7 @@ private: boost::intrusive_ptr timeZone, boost::intrusive_ptr iso8601); - boost::optional evaluateIso8601Flag(const Document& root) const; + boost::optional evaluateIso8601Flag(const Document& root, Variables* variables) const; boost::intrusive_ptr& _date; boost::intrusive_ptr& _timeZone; @@ -1153,7 +1157,7 @@ class ExpressionDateToString final : public Expression { public: boost::intrusive_ptr optimize() final; Value serialize(bool explain) const final; - Value evaluate(const Document& root) const final; + Value evaluate(const Document& root, Variables* variables) const final; static boost::intrusive_ptr parse( const boost::intrusive_ptr& expCtx, @@ -1239,7 +1243,7 @@ public: explicit ExpressionDivide(const boost::intrusive_ptr& expCtx) : ExpressionFixedArity(expCtx) {} - Value evaluate(const Document& root) const final; + Value evaluate(const Document& root, Variables* variables) const final; const char* getOpName() const final; void acceptVisitor(ExpressionVisitor* visitor) final { @@ -1269,7 +1273,7 @@ public: } boost::intrusive_ptr optimize() final; - Value evaluate(const Document& root) const final; + Value evaluate(const Document& root, Variables* variables) const final; Value serialize(bool explain) const final; /* @@ -1350,7 +1354,7 @@ class ExpressionFilter final : public Expression { public: boost::intrusive_ptr optimize() final; Value serialize(bool explain) const final; - Value evaluate(const Document& root) const final; + Value evaluate(const Document& root, Variables* variables) const final; static boost::intrusive_ptr parse( const boost::intrusive_ptr& expCtx, @@ -1419,7 +1423,7 @@ public: explicit ExpressionIfNull(const boost::intrusive_ptr& expCtx) : ExpressionFixedArity(expCtx) {} - Value evaluate(const Document& root) const final; + Value evaluate(const Document& root, Variables* variables) const final; const char* getOpName() const final; void acceptVisitor(ExpressionVisitor* visitor) final { @@ -1433,7 +1437,7 @@ public: explicit ExpressionIn(const boost::intrusive_ptr& expCtx) : ExpressionFixedArity(expCtx) {} - Value evaluate(const Document& root) const final; + Value evaluate(const Document& root, Variables* variables) const final; const char* getOpName() const final; void acceptVisitor(ExpressionVisitor* visitor) final { @@ -1448,7 +1452,7 @@ public: : ExpressionRangedArity(expCtx) {} - Value evaluate(const Document& root) const; + Value evaluate(const Document& root, Variables* variables) const; boost::intrusive_ptr optimize() final; const char* getOpName() const final; @@ -1475,7 +1479,8 @@ protected: */ Arguments evaluateAndValidateArguments(const Document& root, const ExpressionVector& operands, - size_t arrayLength) const; + size_t arrayLength, + Variables* variables) const; private: class Optimized; @@ -1487,7 +1492,7 @@ public: explicit ExpressionIndexOfBytes(const boost::intrusive_ptr& expCtx) : ExpressionRangedArity(expCtx) {} - Value evaluate(const Document& root) const final; + Value evaluate(const Document& root, Variables* variables) const final; const char* getOpName() const final; void acceptVisitor(ExpressionVisitor* visitor) final { @@ -1504,7 +1509,7 @@ public: explicit ExpressionIndexOfCP(const boost::intrusive_ptr& expCtx) : ExpressionRangedArity(expCtx) {} - Value evaluate(const Document& root) const final; + Value evaluate(const Document& root, Variables* variables) const final; const char* getOpName() const final; void acceptVisitor(ExpressionVisitor* visitor) final { @@ -1517,7 +1522,7 @@ class ExpressionLet final : public Expression { public: boost::intrusive_ptr optimize() final; Value serialize(bool explain) const final; - Value evaluate(const Document& root) const final; + Value evaluate(const Document& root, Variables* variables) const final; static boost::intrusive_ptr parse( const boost::intrusive_ptr& expCtx, @@ -1565,7 +1570,7 @@ public: explicit ExpressionLog(const boost::intrusive_ptr& expCtx) : ExpressionFixedArity(expCtx) {} - Value evaluate(const Document& root) const final; + Value evaluate(const Document& root, Variables* variables) const final; const char* getOpName() const final; void acceptVisitor(ExpressionVisitor* visitor) final { @@ -1590,7 +1595,7 @@ class ExpressionMap final : public Expression { public: boost::intrusive_ptr optimize() final; Value serialize(bool explain) const final; - Value evaluate(const Document& root) const final; + Value evaluate(const Document& root, Variables* variables) const final; static boost::intrusive_ptr parse( const boost::intrusive_ptr& expCtx, @@ -1624,7 +1629,7 @@ private: class ExpressionMeta final : public Expression { public: Value serialize(bool explain) const final; - Value evaluate(const Document& root) const final; + Value evaluate(const Document& root, Variables* variables) const final; static boost::intrusive_ptr parse( const boost::intrusive_ptr& expCtx, @@ -1692,7 +1697,7 @@ public: explicit ExpressionMod(const boost::intrusive_ptr& expCtx) : ExpressionFixedArity(expCtx) {} - Value evaluate(const Document& root) const final; + Value evaluate(const Document& root, Variables* variables) const final; const char* getOpName() const final; void acceptVisitor(ExpressionVisitor* visitor) final { @@ -1706,7 +1711,7 @@ public: explicit ExpressionMultiply(const boost::intrusive_ptr& expCtx) : ExpressionVariadic(expCtx) {} - Value evaluate(const Document& root) const final; + Value evaluate(const Document& root, Variables* variables) const final; const char* getOpName() const final; bool isAssociative() const final { @@ -1746,7 +1751,7 @@ public: explicit ExpressionNot(const boost::intrusive_ptr& expCtx) : ExpressionFixedArity(expCtx) {} - Value evaluate(const Document& root) const final; + Value evaluate(const Document& root, Variables* variables) const final; const char* getOpName() const final; void acceptVisitor(ExpressionVisitor* visitor) final { @@ -1766,7 +1771,7 @@ public: class ExpressionObject final : public Expression { public: boost::intrusive_ptr optimize() final; - Value evaluate(const Document& root) const final; + Value evaluate(const Document& root, Variables* variables) const final; Value serialize(bool explain) const final; static boost::intrusive_ptr create( @@ -1817,7 +1822,7 @@ public: : ExpressionVariadic(expCtx) {} boost::intrusive_ptr optimize() final; - Value evaluate(const Document& root) const final; + Value evaluate(const Document& root, Variables* variables) const final; const char* getOpName() const final; bool isAssociative() const final { @@ -1846,7 +1851,7 @@ public: } private: - Value evaluate(const Document& root) const final; + Value evaluate(const Document& root, Variables* variables) const final; const char* getOpName() const final; }; @@ -1856,7 +1861,7 @@ public: explicit ExpressionRange(const boost::intrusive_ptr& expCtx) : ExpressionRangedArity(expCtx) {} - Value evaluate(const Document& root) const final; + Value evaluate(const Document& root, Variables* variables) const final; const char* getOpName() const final; void acceptVisitor(ExpressionVisitor* visitor) final { @@ -1880,7 +1885,7 @@ public: _thisVar(thisVar), _valueVar(valueVar) {} - Value evaluate(const Document& root) const final; + Value evaluate(const Document& root, Variables* variables) const final; boost::intrusive_ptr optimize() final; static boost::intrusive_ptr parse( const boost::intrusive_ptr& expCtx, @@ -1928,7 +1933,7 @@ public: explicit ExpressionSetDifference(const boost::intrusive_ptr& expCtx) : ExpressionFixedArity(expCtx) {} - Value evaluate(const Document& root) const final; + Value evaluate(const Document& root, Variables* variables) const final; const char* getOpName() const final; void acceptVisitor(ExpressionVisitor* visitor) final { @@ -1942,7 +1947,7 @@ public: explicit ExpressionSetEquals(const boost::intrusive_ptr& expCtx) : ExpressionVariadic(expCtx) {} - Value evaluate(const Document& root) const final; + Value evaluate(const Document& root, Variables* variables) const final; const char* getOpName() const final; void validateArguments(const ExpressionVector& args) const final; @@ -1957,7 +1962,7 @@ public: explicit ExpressionSetIntersection(const boost::intrusive_ptr& expCtx) : ExpressionVariadic(expCtx) {} - Value evaluate(const Document& root) const final; + Value evaluate(const Document& root, Variables* variables) const final; const char* getOpName() const final; bool isAssociative() const final { @@ -1981,7 +1986,7 @@ public: : ExpressionFixedArity(expCtx) {} boost::intrusive_ptr optimize() override; - Value evaluate(const Document& root) const override; + Value evaluate(const Document& root, Variables* variables) const override; const char* getOpName() const final; void acceptVisitor(ExpressionVisitor* visitor) final { @@ -1998,7 +2003,7 @@ public: explicit ExpressionSetUnion(const boost::intrusive_ptr& expCtx) : ExpressionVariadic(expCtx) {} - Value evaluate(const Document& root) const final; + Value evaluate(const Document& root, Variables* variables) const final; const char* getOpName() const final; bool isAssociative() const final { @@ -2020,7 +2025,7 @@ public: explicit ExpressionSize(const boost::intrusive_ptr& expCtx) : ExpressionFixedArity(expCtx) {} - Value evaluate(const Document& root) const final; + Value evaluate(const Document& root, Variables* variables) const final; const char* getOpName() const final; void acceptVisitor(ExpressionVisitor* visitor) final { @@ -2034,7 +2039,7 @@ public: explicit ExpressionReverseArray(const boost::intrusive_ptr& expCtx) : ExpressionFixedArity(expCtx) {} - Value evaluate(const Document& root) const final; + Value evaluate(const Document& root, Variables* variables) const final; const char* getOpName() const final; void acceptVisitor(ExpressionVisitor* visitor) final { @@ -2048,7 +2053,7 @@ public: explicit ExpressionSlice(const boost::intrusive_ptr& expCtx) : ExpressionRangedArity(expCtx) {} - Value evaluate(const Document& root) const final; + Value evaluate(const Document& root, Variables* variables) const final; const char* getOpName() const final; void acceptVisitor(ExpressionVisitor* visitor) final { @@ -2062,7 +2067,7 @@ public: explicit ExpressionIsArray(const boost::intrusive_ptr& expCtx) : ExpressionFixedArity(expCtx) {} - Value evaluate(const Document& root) const final; + Value evaluate(const Document& root, Variables* variables) const final; const char* getOpName() const final; void acceptVisitor(ExpressionVisitor* visitor) final { @@ -2075,7 +2080,7 @@ public: explicit ExpressionRound(const boost::intrusive_ptr& expCtx) : ExpressionRangedArity(expCtx) {} - Value evaluate(const Document& root) const final; + Value evaluate(const Document& root, Variables* variables) const final; const char* getOpName() const final; void acceptVisitor(ExpressionVisitor* visitor) final { @@ -2088,7 +2093,7 @@ public: explicit ExpressionSplit(const boost::intrusive_ptr& expCtx) : ExpressionFixedArity(expCtx) {} - Value evaluate(const Document& root) const final; + Value evaluate(const Document& root, Variables* variables) const final; const char* getOpName() const final; void acceptVisitor(ExpressionVisitor* visitor) final { @@ -2116,7 +2121,7 @@ public: explicit ExpressionStrcasecmp(const boost::intrusive_ptr& expCtx) : ExpressionFixedArity(expCtx) {} - Value evaluate(const Document& root) const final; + Value evaluate(const Document& root, Variables* variables) const final; const char* getOpName() const final; void acceptVisitor(ExpressionVisitor* visitor) final { @@ -2130,7 +2135,7 @@ public: explicit ExpressionSubstrBytes(const boost::intrusive_ptr& expCtx) : ExpressionFixedArity(expCtx) {} - Value evaluate(const Document& root) const final; + Value evaluate(const Document& root, Variables* variables) const final; const char* getOpName() const; void acceptVisitor(ExpressionVisitor* visitor) final { @@ -2144,7 +2149,7 @@ public: explicit ExpressionSubstrCP(const boost::intrusive_ptr& expCtx) : ExpressionFixedArity(expCtx) {} - Value evaluate(const Document& root) const final; + Value evaluate(const Document& root, Variables* variables) const final; const char* getOpName() const final; void acceptVisitor(ExpressionVisitor* visitor) final { @@ -2158,7 +2163,7 @@ public: explicit ExpressionStrLenBytes(const boost::intrusive_ptr& expCtx) : ExpressionFixedArity(expCtx) {} - Value evaluate(const Document& root) const final; + Value evaluate(const Document& root, Variables* variables) const final; const char* getOpName() const final; void acceptVisitor(ExpressionVisitor* visitor) final { @@ -2172,7 +2177,7 @@ public: explicit ExpressionStrLenCP(const boost::intrusive_ptr& expCtx) : ExpressionFixedArity(expCtx) {} - Value evaluate(const Document& root) const final; + Value evaluate(const Document& root, Variables* variables) const final; const char* getOpName() const final; void acceptVisitor(ExpressionVisitor* visitor) final { @@ -2186,7 +2191,7 @@ public: explicit ExpressionSubtract(const boost::intrusive_ptr& expCtx) : ExpressionFixedArity(expCtx) {} - Value evaluate(const Document& root) const final; + Value evaluate(const Document& root, Variables* variables) const final; const char* getOpName() const final; void acceptVisitor(ExpressionVisitor* visitor) final { @@ -2207,7 +2212,7 @@ public: _default(_children.back()), _branches(std::move(branches)) {} - Value evaluate(const Document& root) const final; + Value evaluate(const Document& root, Variables* variables) const final; boost::intrusive_ptr optimize() final; static boost::intrusive_ptr parse( const boost::intrusive_ptr& expCtx, @@ -2233,7 +2238,7 @@ public: explicit ExpressionToLower(const boost::intrusive_ptr& expCtx) : ExpressionFixedArity(expCtx) {} - Value evaluate(const Document& root) const final; + Value evaluate(const Document& root, Variables* variables) const final; const char* getOpName() const final; void acceptVisitor(ExpressionVisitor* visitor) final { @@ -2247,7 +2252,7 @@ public: explicit ExpressionToUpper(const boost::intrusive_ptr& expCtx) : ExpressionFixedArity(expCtx) {} - Value evaluate(const Document& root) const final; + Value evaluate(const Document& root, Variables* variables) const final; const char* getOpName() const final; void acceptVisitor(ExpressionVisitor* visitor) final { @@ -2279,7 +2284,7 @@ public: _input(_children[0]), _characters(_children[1]) {} - Value evaluate(const Document& root) const final; + Value evaluate(const Document& root, Variables* variables) const final; boost::intrusive_ptr optimize() final; static boost::intrusive_ptr parse( const boost::intrusive_ptr& expCtx, @@ -2337,7 +2342,7 @@ public: const boost::intrusive_ptr& expCtx, BSONElement elem, const VariablesParseState& vps); - Value evaluate(const Document& root) const final; + Value evaluate(const Document& root, Variables* variables) const final; const char* getOpName() const final; void acceptVisitor(ExpressionVisitor* visitor) final { @@ -2351,7 +2356,7 @@ public: explicit ExpressionType(const boost::intrusive_ptr& expCtx) : ExpressionFixedArity(expCtx) {} - Value evaluate(const Document& root) const final; + Value evaluate(const Document& root, Variables* variables) const final; const char* getOpName() const final; void acceptVisitor(ExpressionVisitor* visitor) final { @@ -2463,7 +2468,7 @@ public: _inputs(std::move(inputs)), _defaults(std::move(defaults)) {} - Value evaluate(const Document& root) const final; + Value evaluate(const Document& root, Variables* variables) const final; boost::intrusive_ptr optimize() final; static boost::intrusive_ptr parse( const boost::intrusive_ptr& expCtx, @@ -2499,7 +2504,7 @@ public: BSONElement expr, const VariablesParseState& vpsIn); - Value evaluate(const Document& root) const final; + Value evaluate(const Document& root, Variables* variables) const final; boost::intrusive_ptr optimize() final; Value serialize(bool explain) const final; @@ -2567,7 +2572,7 @@ public: * Validates the structure of input passed in 'inputExpr'. If valid, generates an initial * execution state. This returned object can later be used for calling execute() or nextMatch(). */ - RegexExecutionState buildInitialState(const Document& root) const; + RegexExecutionState buildInitialState(const Document& root, Variables* variables) const; /** * Checks if there is a match for the given input and pattern that are part of 'executionState'. @@ -2652,7 +2657,7 @@ public: BSONElement expr, const VariablesParseState& vpsIn); - Value evaluate(const Document& root) const final; + Value evaluate(const Document& root, Variables* variables) const final; void acceptVisitor(ExpressionVisitor* visitor) final { return visitor->visit(this); @@ -2668,7 +2673,7 @@ public: BSONElement expr, const VariablesParseState& vpsIn); - Value evaluate(const Document& root) const final; + Value evaluate(const Document& root, Variables* variables) const final; void acceptVisitor(ExpressionVisitor* visitor) final { return visitor->visit(this); } @@ -2683,7 +2688,7 @@ public: BSONElement expr, const VariablesParseState& vpsIn); - Value evaluate(const Document& root) const final; + Value evaluate(const Document& root, Variables* variables) const final; void acceptVisitor(ExpressionVisitor* visitor) final { return visitor->visit(this); diff --git a/src/mongo/db/pipeline/expression_convert_test.cpp b/src/mongo/db/pipeline/expression_convert_test.cpp index b5f22579438..a0a5c2d4a64 100644 --- a/src/mongo/db/pipeline/expression_convert_test.cpp +++ b/src/mongo/db/pipeline/expression_convert_test.cpp @@ -157,7 +157,7 @@ TEST_F(ExpressionConvertTest, InvalidTypeNameFails) { auto convertExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); - ASSERT_THROWS_WITH_CHECK(convertExp->evaluate(Document()), + ASSERT_THROWS_WITH_CHECK(convertExp->evaluate(Document(), &expCtx->variables), AssertionException, [](const AssertionException& exception) { ASSERT_EQ(exception.code(), ErrorCodes::BadValue); @@ -177,7 +177,7 @@ TEST_F(ExpressionConvertTest, NonIntegralTypeFails) { auto convertExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); - ASSERT_THROWS_WITH_CHECK(convertExp->evaluate(Document()), + ASSERT_THROWS_WITH_CHECK(convertExp->evaluate(Document(), &expCtx->variables), AssertionException, [](const AssertionException& exception) { ASSERT_EQ(exception.code(), ErrorCodes::FailedToParse); @@ -200,7 +200,7 @@ TEST_F(ExpressionConvertTest, NonStringNonNumericalTypeFails) { auto convertExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); - ASSERT_THROWS_WITH_CHECK(convertExp->evaluate(Document()), + ASSERT_THROWS_WITH_CHECK(convertExp->evaluate(Document(), &expCtx->variables), AssertionException, [](const AssertionException& exception) { ASSERT_EQ(exception.code(), ErrorCodes::FailedToParse); @@ -223,7 +223,7 @@ TEST_F(ExpressionConvertTest, InvalidNumericTargetTypeFails) { auto convertExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); ASSERT_THROWS_WITH_CHECK( - convertExp->evaluate(Document()), + convertExp->evaluate(Document(), &expCtx->variables), AssertionException, [](const AssertionException& exception) { ASSERT_EQ(exception.code(), ErrorCodes::FailedToParse); @@ -246,7 +246,7 @@ TEST_F(ExpressionConvertTest, NegativeNumericTargetTypeFails) { auto convertExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); ASSERT_THROWS_WITH_CHECK( - convertExp->evaluate(Document()), + convertExp->evaluate(Document(), &expCtx->variables), AssertionException, [](const AssertionException& exception) { ASSERT_EQ(exception.code(), ErrorCodes::FailedToParse); @@ -304,7 +304,7 @@ TEST_F(ExpressionConvertTest, UnsupportedConversionShouldThrowUnlessOnErrorProvi auto convertExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); - ASSERT_THROWS_WITH_CHECK(convertExp->evaluate(input), + ASSERT_THROWS_WITH_CHECK(convertExp->evaluate(input, &expCtx->variables), AssertionException, [](const AssertionException& exception) { ASSERT_EQ(exception.code(), ErrorCodes::ConversionFailure); @@ -329,7 +329,8 @@ TEST_F(ExpressionConvertTest, UnsupportedConversionShouldThrowUnlessOnErrorProvi auto convertExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); - ASSERT_VALUE_CONTENTS_AND_TYPE(convertExp->evaluate(input), "X"_sd, BSONType::String); + ASSERT_VALUE_CONTENTS_AND_TYPE( + convertExp->evaluate(input, &expCtx->variables), "X"_sd, BSONType::String); } } @@ -346,9 +347,9 @@ TEST_F(ExpressionConvertTest, ConvertNullishInput) { Document undefinedInput{{"path1", BSONUndefined}}; Document missingInput{{"path1", Value()}}; - ASSERT_VALUE_EQ(convertExp->evaluate(nullInput), Value(BSONNULL)); - ASSERT_VALUE_EQ(convertExp->evaluate(undefinedInput), Value(BSONNULL)); - ASSERT_VALUE_EQ(convertExp->evaluate(missingInput), Value(BSONNULL)); + ASSERT_VALUE_EQ(convertExp->evaluate(nullInput, &expCtx->variables), Value(BSONNULL)); + ASSERT_VALUE_EQ(convertExp->evaluate(undefinedInput, &expCtx->variables), Value(BSONNULL)); + ASSERT_VALUE_EQ(convertExp->evaluate(missingInput, &expCtx->variables), Value(BSONNULL)); } TEST_F(ExpressionConvertTest, ConvertNullishInputWithOnNull) { @@ -368,9 +369,9 @@ TEST_F(ExpressionConvertTest, ConvertNullishInputWithOnNull) { Document undefinedInput{{"path1", BSONUndefined}}; Document missingInput{{"path1", Value()}}; - ASSERT_VALUE_EQ(convertExp->evaluate(nullInput), Value("B)"_sd)); - ASSERT_VALUE_EQ(convertExp->evaluate(undefinedInput), Value("B)"_sd)); - ASSERT_VALUE_EQ(convertExp->evaluate(missingInput), Value("B)"_sd)); + ASSERT_VALUE_EQ(convertExp->evaluate(nullInput, &expCtx->variables), Value("B)"_sd)); + ASSERT_VALUE_EQ(convertExp->evaluate(undefinedInput, &expCtx->variables), Value("B)"_sd)); + ASSERT_VALUE_EQ(convertExp->evaluate(missingInput, &expCtx->variables), Value("B)"_sd)); } TEST_F(ExpressionConvertTest, NullishToReturnsNull) { @@ -390,9 +391,9 @@ TEST_F(ExpressionConvertTest, NullishToReturnsNull) { Document undefinedInput{{"path1", BSONUndefined}}; Document missingInput{{"path1", Value()}}; - ASSERT_VALUE_EQ(convertExp->evaluate(nullInput), Value(BSONNULL)); - ASSERT_VALUE_EQ(convertExp->evaluate(undefinedInput), Value(BSONNULL)); - ASSERT_VALUE_EQ(convertExp->evaluate(missingInput), Value(BSONNULL)); + ASSERT_VALUE_EQ(convertExp->evaluate(nullInput, &expCtx->variables), Value(BSONNULL)); + ASSERT_VALUE_EQ(convertExp->evaluate(undefinedInput, &expCtx->variables), Value(BSONNULL)); + ASSERT_VALUE_EQ(convertExp->evaluate(missingInput, &expCtx->variables), Value(BSONNULL)); } TEST_F(ExpressionConvertTest, NullInputOverridesNullTo) { @@ -403,7 +404,8 @@ TEST_F(ExpressionConvertTest, NullInputOverridesNullTo) { << "X")); auto convertExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); - ASSERT_VALUE_CONTENTS_AND_TYPE(convertExp->evaluate(Document{}), "X"_sd, BSONType::String); + ASSERT_VALUE_CONTENTS_AND_TYPE( + convertExp->evaluate(Document{}, &expCtx->variables), "X"_sd, BSONType::String); } TEST_F(ExpressionConvertTest, ConvertOptimizesToExpressionConstant) { @@ -444,20 +446,21 @@ TEST_F(ExpressionConvertTest, DoubleIdentityConversion) { auto convertExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); Document doubleInput{{"path1", 2.4}}; - ASSERT_VALUE_CONTENTS_AND_TYPE(convertExp->evaluate(doubleInput), 2.4, BSONType::NumberDouble); + ASSERT_VALUE_CONTENTS_AND_TYPE( + convertExp->evaluate(doubleInput, &expCtx->variables), 2.4, BSONType::NumberDouble); Document doubleNaN{{"path1", std::numeric_limits::quiet_NaN()}}; - auto result = convertExp->evaluate(doubleNaN); + auto result = convertExp->evaluate(doubleNaN, &expCtx->variables); ASSERT(std::isnan(result.getDouble())); Document doubleInfinity{{"path1", std::numeric_limits::infinity()}}; - result = convertExp->evaluate(doubleInfinity); + result = convertExp->evaluate(doubleInfinity, &expCtx->variables); ASSERT_EQ(result.getType(), BSONType::NumberDouble); ASSERT_GT(result.getDouble(), 0.0); ASSERT(std::isinf(result.getDouble())); Document doubleNegativeInfinity{{"path1", -std::numeric_limits::infinity()}}; - result = convertExp->evaluate(doubleNegativeInfinity); + result = convertExp->evaluate(doubleNegativeInfinity, &expCtx->variables); ASSERT_EQ(result.getType(), BSONType::NumberDouble); ASSERT_LT(result.getDouble(), 0.0); ASSERT(std::isinf(result.getDouble())); @@ -473,10 +476,12 @@ TEST_F(ExpressionConvertTest, BoolIdentityConversion) { auto convertExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); Document trueBoolInput{{"path1", true}}; - ASSERT_VALUE_CONTENTS_AND_TYPE(convertExp->evaluate(trueBoolInput), true, BSONType::Bool); + ASSERT_VALUE_CONTENTS_AND_TYPE( + convertExp->evaluate(trueBoolInput, &expCtx->variables), true, BSONType::Bool); Document falseBoolInput{{"path1", false}}; - ASSERT_VALUE_CONTENTS_AND_TYPE(convertExp->evaluate(falseBoolInput), false, BSONType::Bool); + ASSERT_VALUE_CONTENTS_AND_TYPE( + convertExp->evaluate(falseBoolInput, &expCtx->variables), false, BSONType::Bool); } TEST_F(ExpressionConvertTest, StringIdentityConversion) { @@ -490,7 +495,7 @@ TEST_F(ExpressionConvertTest, StringIdentityConversion) { Document stringInput{{"path1", "More cowbell"_sd}}; ASSERT_VALUE_CONTENTS_AND_TYPE( - convertExp->evaluate(stringInput), "More cowbell"_sd, BSONType::String); + convertExp->evaluate(stringInput, &expCtx->variables), "More cowbell"_sd, BSONType::String); } TEST_F(ExpressionConvertTest, ObjectIdIdentityConversion) { @@ -503,8 +508,9 @@ TEST_F(ExpressionConvertTest, ObjectIdIdentityConversion) { auto convertExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); Document oidInput{{"path1", OID("0123456789abcdef01234567")}}; - ASSERT_VALUE_CONTENTS_AND_TYPE( - convertExp->evaluate(oidInput), OID("0123456789abcdef01234567"), BSONType::jstOID); + ASSERT_VALUE_CONTENTS_AND_TYPE(convertExp->evaluate(oidInput, &expCtx->variables), + OID("0123456789abcdef01234567"), + BSONType::jstOID); } TEST_F(ExpressionConvertTest, DateIdentityConversion) { @@ -517,7 +523,8 @@ TEST_F(ExpressionConvertTest, DateIdentityConversion) { auto convertExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); Document dateInput{{"path1", Date_t{}}}; - ASSERT_VALUE_CONTENTS_AND_TYPE(convertExp->evaluate(dateInput), Date_t{}, BSONType::Date); + ASSERT_VALUE_CONTENTS_AND_TYPE( + convertExp->evaluate(dateInput, &expCtx->variables), Date_t{}, BSONType::Date); } TEST_F(ExpressionConvertTest, IntIdentityConversion) { @@ -530,7 +537,8 @@ TEST_F(ExpressionConvertTest, IntIdentityConversion) { auto convertExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); Document intInput{{"path1", int{123}}}; - ASSERT_VALUE_CONTENTS_AND_TYPE(convertExp->evaluate(intInput), int{123}, BSONType::NumberInt); + ASSERT_VALUE_CONTENTS_AND_TYPE( + convertExp->evaluate(intInput, &expCtx->variables), int{123}, BSONType::NumberInt); } TEST_F(ExpressionConvertTest, LongIdentityConversion) { @@ -543,7 +551,8 @@ TEST_F(ExpressionConvertTest, LongIdentityConversion) { auto convertExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); Document longInput{{"path1", 123LL}}; - ASSERT_VALUE_CONTENTS_AND_TYPE(convertExp->evaluate(longInput), 123LL, BSONType::NumberLong); + ASSERT_VALUE_CONTENTS_AND_TYPE( + convertExp->evaluate(longInput, &expCtx->variables), 123LL, BSONType::NumberLong); } TEST_F(ExpressionConvertTest, DecimalIdentityConversion) { @@ -556,22 +565,25 @@ TEST_F(ExpressionConvertTest, DecimalIdentityConversion) { auto convertExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); Document decimalInput{{"path1", Decimal128("2.4")}}; - ASSERT_VALUE_CONTENTS_AND_TYPE( - convertExp->evaluate(decimalInput), Decimal128("2.4"), BSONType::NumberDecimal); + ASSERT_VALUE_CONTENTS_AND_TYPE(convertExp->evaluate(decimalInput, &expCtx->variables), + Decimal128("2.4"), + BSONType::NumberDecimal); Document decimalNaN{{"path1", Decimal128::kPositiveNaN}}; - ASSERT_VALUE_CONTENTS_AND_TYPE( - convertExp->evaluate(decimalNaN), Decimal128::kPositiveNaN, BSONType::NumberDecimal); + ASSERT_VALUE_CONTENTS_AND_TYPE(convertExp->evaluate(decimalNaN, &expCtx->variables), + Decimal128::kPositiveNaN, + BSONType::NumberDecimal); Document decimalInfinity{{"path1", Decimal128::kPositiveInfinity}}; - ASSERT_VALUE_CONTENTS_AND_TYPE(convertExp->evaluate(decimalInfinity), + ASSERT_VALUE_CONTENTS_AND_TYPE(convertExp->evaluate(decimalInfinity, &expCtx->variables), Decimal128::kPositiveInfinity, BSONType::NumberDecimal); Document decimalNegativeInfinity{{"path1", Decimal128::kNegativeInfinity}}; - ASSERT_VALUE_CONTENTS_AND_TYPE(convertExp->evaluate(decimalNegativeInfinity), - Decimal128::kNegativeInfinity, - BSONType::NumberDecimal); + ASSERT_VALUE_CONTENTS_AND_TYPE( + convertExp->evaluate(decimalNegativeInfinity, &expCtx->variables), + Decimal128::kNegativeInfinity, + BSONType::NumberDecimal); } TEST_F(ExpressionConvertTest, ConvertDateToBool) { @@ -585,7 +597,8 @@ TEST_F(ExpressionConvertTest, ConvertDateToBool) { // All date inputs evaluate as true. Document dateInput{{"path1", Date_t{}}}; - ASSERT_VALUE_CONTENTS_AND_TYPE(convertExp->evaluate(dateInput), true, BSONType::Bool); + ASSERT_VALUE_CONTENTS_AND_TYPE( + convertExp->evaluate(dateInput, &expCtx->variables), true, BSONType::Bool); } TEST_F(ExpressionConvertTest, ConvertIntToBool) { @@ -598,10 +611,12 @@ TEST_F(ExpressionConvertTest, ConvertIntToBool) { auto convertExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); Document trueIntInput{{"path1", int{1}}}; - ASSERT_VALUE_CONTENTS_AND_TYPE(convertExp->evaluate(trueIntInput), true, BSONType::Bool); + ASSERT_VALUE_CONTENTS_AND_TYPE( + convertExp->evaluate(trueIntInput, &expCtx->variables), true, BSONType::Bool); Document falseIntInput{{"path1", int{0}}}; - ASSERT_VALUE_CONTENTS_AND_TYPE(convertExp->evaluate(falseIntInput), false, BSONType::Bool); + ASSERT_VALUE_CONTENTS_AND_TYPE( + convertExp->evaluate(falseIntInput, &expCtx->variables), false, BSONType::Bool); } TEST_F(ExpressionConvertTest, ConvertLongToBool) { @@ -614,10 +629,12 @@ TEST_F(ExpressionConvertTest, ConvertLongToBool) { auto convertExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); Document trueLongInput{{"path1", -1LL}}; - ASSERT_VALUE_CONTENTS_AND_TYPE(convertExp->evaluate(trueLongInput), true, BSONType::Bool); + ASSERT_VALUE_CONTENTS_AND_TYPE( + convertExp->evaluate(trueLongInput, &expCtx->variables), true, BSONType::Bool); Document falseLongInput{{"path1", 0LL}}; - ASSERT_VALUE_CONTENTS_AND_TYPE(convertExp->evaluate(falseLongInput), false, BSONType::Bool); + ASSERT_VALUE_CONTENTS_AND_TYPE( + convertExp->evaluate(falseLongInput, &expCtx->variables), false, BSONType::Bool); } TEST_F(ExpressionConvertTest, ConvertDoubleToBool) { @@ -630,20 +647,24 @@ TEST_F(ExpressionConvertTest, ConvertDoubleToBool) { auto convertExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); Document trueDoubleInput{{"path1", 2.4}}; - ASSERT_VALUE_CONTENTS_AND_TYPE(convertExp->evaluate(trueDoubleInput), true, BSONType::Bool); + ASSERT_VALUE_CONTENTS_AND_TYPE( + convertExp->evaluate(trueDoubleInput, &expCtx->variables), true, BSONType::Bool); Document falseDoubleInput{{"path1", -0.0}}; - ASSERT_VALUE_CONTENTS_AND_TYPE(convertExp->evaluate(falseDoubleInput), false, BSONType::Bool); + ASSERT_VALUE_CONTENTS_AND_TYPE( + convertExp->evaluate(falseDoubleInput, &expCtx->variables), false, BSONType::Bool); Document doubleNaN{{"path1", std::numeric_limits::quiet_NaN()}}; - ASSERT_VALUE_CONTENTS_AND_TYPE(convertExp->evaluate(doubleNaN), true, BSONType::Bool); + ASSERT_VALUE_CONTENTS_AND_TYPE( + convertExp->evaluate(doubleNaN, &expCtx->variables), true, BSONType::Bool); Document doubleInfinity{{"path1", std::numeric_limits::infinity()}}; - ASSERT_VALUE_CONTENTS_AND_TYPE(convertExp->evaluate(doubleInfinity), true, BSONType::Bool); + ASSERT_VALUE_CONTENTS_AND_TYPE( + convertExp->evaluate(doubleInfinity, &expCtx->variables), true, BSONType::Bool); Document doubleNegativeInfinity{{"path1", -std::numeric_limits::infinity()}}; ASSERT_VALUE_CONTENTS_AND_TYPE( - convertExp->evaluate(doubleNegativeInfinity), true, BSONType::Bool); + convertExp->evaluate(doubleNegativeInfinity, &expCtx->variables), true, BSONType::Bool); } TEST_F(ExpressionConvertTest, ConvertDecimalToBool) { @@ -656,29 +677,36 @@ TEST_F(ExpressionConvertTest, ConvertDecimalToBool) { auto convertExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); Document trueDecimalInput{{"path1", Decimal128(5)}}; - ASSERT_VALUE_CONTENTS_AND_TYPE(convertExp->evaluate(trueDecimalInput), true, BSONType::Bool); + ASSERT_VALUE_CONTENTS_AND_TYPE( + convertExp->evaluate(trueDecimalInput, &expCtx->variables), true, BSONType::Bool); Document falseDecimalInput{{"path1", Decimal128(0)}}; - ASSERT_VALUE_CONTENTS_AND_TYPE(convertExp->evaluate(falseDecimalInput), false, BSONType::Bool); + ASSERT_VALUE_CONTENTS_AND_TYPE( + convertExp->evaluate(falseDecimalInput, &expCtx->variables), false, BSONType::Bool); Document preciseZero{{"path1", Decimal128("0.00")}}; - ASSERT_VALUE_CONTENTS_AND_TYPE(convertExp->evaluate(preciseZero), false, BSONType::Bool); + ASSERT_VALUE_CONTENTS_AND_TYPE( + convertExp->evaluate(preciseZero, &expCtx->variables), false, BSONType::Bool); Document negativeZero{{"path1", Decimal128("-0.00")}}; - ASSERT_VALUE_CONTENTS_AND_TYPE(convertExp->evaluate(negativeZero), false, BSONType::Bool); + ASSERT_VALUE_CONTENTS_AND_TYPE( + convertExp->evaluate(negativeZero, &expCtx->variables), false, BSONType::Bool); Document decimalNaN{{"path1", Decimal128::kPositiveNaN}}; - ASSERT_VALUE_CONTENTS_AND_TYPE(convertExp->evaluate(decimalNaN), true, BSONType::Bool); + ASSERT_VALUE_CONTENTS_AND_TYPE( + convertExp->evaluate(decimalNaN, &expCtx->variables), true, BSONType::Bool); Document decimalNegativeNaN{{"path1", Decimal128::kNegativeNaN}}; - ASSERT_VALUE_CONTENTS_AND_TYPE(convertExp->evaluate(decimalNegativeNaN), true, BSONType::Bool); + ASSERT_VALUE_CONTENTS_AND_TYPE( + convertExp->evaluate(decimalNegativeNaN, &expCtx->variables), true, BSONType::Bool); Document decimalInfinity{{"path1", Decimal128::kPositiveInfinity}}; - ASSERT_VALUE_CONTENTS_AND_TYPE(convertExp->evaluate(decimalInfinity), true, BSONType::Bool); + ASSERT_VALUE_CONTENTS_AND_TYPE( + convertExp->evaluate(decimalInfinity, &expCtx->variables), true, BSONType::Bool); Document decimalNegativeInfinity{{"path1", Decimal128::kNegativeInfinity}}; ASSERT_VALUE_CONTENTS_AND_TYPE( - convertExp->evaluate(decimalNegativeInfinity), true, BSONType::Bool); + convertExp->evaluate(decimalNegativeInfinity, &expCtx->variables), true, BSONType::Bool); } TEST_F(ExpressionConvertTest, ConvertStringToBool) { @@ -691,10 +719,12 @@ TEST_F(ExpressionConvertTest, ConvertStringToBool) { auto convertExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); Document stringInput{{"path1", "str"_sd}}; - ASSERT_VALUE_CONTENTS_AND_TYPE(convertExp->evaluate(stringInput), true, BSONType::Bool); + ASSERT_VALUE_CONTENTS_AND_TYPE( + convertExp->evaluate(stringInput, &expCtx->variables), true, BSONType::Bool); Document emptyStringInput{{"path1", ""_sd}}; - ASSERT_VALUE_CONTENTS_AND_TYPE(convertExp->evaluate(emptyStringInput), true, BSONType::Bool); + ASSERT_VALUE_CONTENTS_AND_TYPE( + convertExp->evaluate(emptyStringInput, &expCtx->variables), true, BSONType::Bool); } TEST_F(ExpressionConvertTest, ConvertObjectIdToBool) { @@ -707,7 +737,8 @@ TEST_F(ExpressionConvertTest, ConvertObjectIdToBool) { auto convertExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); Document oidInput{{"path1", OID("59E8A8D8FEDCBA9876543210")}}; - ASSERT_VALUE_CONTENTS_AND_TYPE(convertExp->evaluate(oidInput), true, BSONType::Bool); + ASSERT_VALUE_CONTENTS_AND_TYPE( + convertExp->evaluate(oidInput, &expCtx->variables), true, BSONType::Bool); } TEST_F(ExpressionConvertTest, ConvertMinKeyToBool) { @@ -720,7 +751,8 @@ TEST_F(ExpressionConvertTest, ConvertMinKeyToBool) { auto convertExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); Document minKeyInput{{"path1", MINKEY}}; - ASSERT_VALUE_CONTENTS_AND_TYPE(convertExp->evaluate(minKeyInput), true, BSONType::Bool); + ASSERT_VALUE_CONTENTS_AND_TYPE( + convertExp->evaluate(minKeyInput, &expCtx->variables), true, BSONType::Bool); } TEST_F(ExpressionConvertTest, ConvertObjectToBool) { @@ -733,7 +765,8 @@ TEST_F(ExpressionConvertTest, ConvertObjectToBool) { auto convertExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); Document objectInput{{"path1", Document{{"foo", 1}}}}; - ASSERT_VALUE_CONTENTS_AND_TYPE(convertExp->evaluate(objectInput), true, BSONType::Bool); + ASSERT_VALUE_CONTENTS_AND_TYPE( + convertExp->evaluate(objectInput, &expCtx->variables), true, BSONType::Bool); } TEST_F(ExpressionConvertTest, ConvertArrayToBool) { @@ -746,7 +779,8 @@ TEST_F(ExpressionConvertTest, ConvertArrayToBool) { auto convertExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); Document arrayInput{{"path1", BSON_ARRAY(1)}}; - ASSERT_VALUE_CONTENTS_AND_TYPE(convertExp->evaluate(arrayInput), true, BSONType::Bool); + ASSERT_VALUE_CONTENTS_AND_TYPE( + convertExp->evaluate(arrayInput, &expCtx->variables), true, BSONType::Bool); } TEST_F(ExpressionConvertTest, ConvertBinDataToBool) { @@ -760,7 +794,8 @@ TEST_F(ExpressionConvertTest, ConvertBinDataToBool) { char data[] = "(^_^)"; Document binInput{{"path1", BSONBinData(data, sizeof(data), BinDataType::BinDataGeneral)}}; - ASSERT_VALUE_CONTENTS_AND_TYPE(convertExp->evaluate(binInput), true, BSONType::Bool); + ASSERT_VALUE_CONTENTS_AND_TYPE( + convertExp->evaluate(binInput, &expCtx->variables), true, BSONType::Bool); } TEST_F(ExpressionConvertTest, ConvertRegexToBool) { @@ -773,7 +808,8 @@ TEST_F(ExpressionConvertTest, ConvertRegexToBool) { auto convertExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); Document regexInput{{"path1", BSONRegEx("ab*a"_sd)}}; - ASSERT_VALUE_CONTENTS_AND_TYPE(convertExp->evaluate(regexInput), true, BSONType::Bool); + ASSERT_VALUE_CONTENTS_AND_TYPE( + convertExp->evaluate(regexInput, &expCtx->variables), true, BSONType::Bool); } TEST_F(ExpressionConvertTest, ConvertDBRefToBool) { @@ -786,7 +822,8 @@ TEST_F(ExpressionConvertTest, ConvertDBRefToBool) { auto convertExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); Document refInput{{"path1", BSONDBRef("db.coll"_sd, OID("aaaaaaaaaaaaaaaaaaaaaaaa"))}}; - ASSERT_VALUE_CONTENTS_AND_TYPE(convertExp->evaluate(refInput), true, BSONType::Bool); + ASSERT_VALUE_CONTENTS_AND_TYPE( + convertExp->evaluate(refInput, &expCtx->variables), true, BSONType::Bool); } TEST_F(ExpressionConvertTest, ConvertCodeToBool) { @@ -799,7 +836,8 @@ TEST_F(ExpressionConvertTest, ConvertCodeToBool) { auto convertExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); Document codeInput{{"path1", BSONCode("print('Hello world!');"_sd)}}; - ASSERT_VALUE_CONTENTS_AND_TYPE(convertExp->evaluate(codeInput), true, BSONType::Bool); + ASSERT_VALUE_CONTENTS_AND_TYPE( + convertExp->evaluate(codeInput, &expCtx->variables), true, BSONType::Bool); } TEST_F(ExpressionConvertTest, ConvertSymbolToBool) { @@ -812,7 +850,8 @@ TEST_F(ExpressionConvertTest, ConvertSymbolToBool) { auto convertExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); Document symbolInput{{"path1", BSONSymbol("print"_sd)}}; - ASSERT_VALUE_CONTENTS_AND_TYPE(convertExp->evaluate(symbolInput), true, BSONType::Bool); + ASSERT_VALUE_CONTENTS_AND_TYPE( + convertExp->evaluate(symbolInput, &expCtx->variables), true, BSONType::Bool); } TEST_F(ExpressionConvertTest, ConvertCodeWScopeToBool) { @@ -826,7 +865,8 @@ TEST_F(ExpressionConvertTest, ConvertCodeWScopeToBool) { Document codeWScopeInput{ {"path1", BSONCodeWScope("print('Hello again, world!')"_sd, BSONObj())}}; - ASSERT_VALUE_CONTENTS_AND_TYPE(convertExp->evaluate(codeWScopeInput), true, BSONType::Bool); + ASSERT_VALUE_CONTENTS_AND_TYPE( + convertExp->evaluate(codeWScopeInput, &expCtx->variables), true, BSONType::Bool); } TEST_F(ExpressionConvertTest, ConvertTimestampToBool) { @@ -839,7 +879,8 @@ TEST_F(ExpressionConvertTest, ConvertTimestampToBool) { auto convertExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); Document timestampInput{{"path1", Timestamp()}}; - ASSERT_VALUE_CONTENTS_AND_TYPE(convertExp->evaluate(timestampInput), true, BSONType::Bool); + ASSERT_VALUE_CONTENTS_AND_TYPE( + convertExp->evaluate(timestampInput, &expCtx->variables), true, BSONType::Bool); } TEST_F(ExpressionConvertTest, ConvertMaxKeyToBool) { @@ -852,7 +893,8 @@ TEST_F(ExpressionConvertTest, ConvertMaxKeyToBool) { auto convertExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); Document maxKeyInput{{"path1", MAXKEY}}; - ASSERT_VALUE_CONTENTS_AND_TYPE(convertExp->evaluate(maxKeyInput), true, BSONType::Bool); + ASSERT_VALUE_CONTENTS_AND_TYPE( + convertExp->evaluate(maxKeyInput, &expCtx->variables), true, BSONType::Bool); } TEST_F(ExpressionConvertTest, ConvertNumericToDouble) { @@ -865,39 +907,43 @@ TEST_F(ExpressionConvertTest, ConvertNumericToDouble) { auto convertExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); Document intInput{{"path1", int{1}}}; - ASSERT_VALUE_CONTENTS_AND_TYPE(convertExp->evaluate(intInput), 1.0, BSONType::NumberDouble); + ASSERT_VALUE_CONTENTS_AND_TYPE( + convertExp->evaluate(intInput, &expCtx->variables), 1.0, BSONType::NumberDouble); Document longInput{{"path1", 0xf00000000LL}}; ASSERT_VALUE_CONTENTS_AND_TYPE( - convertExp->evaluate(longInput), 64424509440.0, BSONType::NumberDouble); + convertExp->evaluate(longInput, &expCtx->variables), 64424509440.0, BSONType::NumberDouble); Document decimalInput{{"path1", Decimal128("5.5")}}; - ASSERT_VALUE_CONTENTS_AND_TYPE(convertExp->evaluate(decimalInput), 5.5, BSONType::NumberDouble); + ASSERT_VALUE_CONTENTS_AND_TYPE( + convertExp->evaluate(decimalInput, &expCtx->variables), 5.5, BSONType::NumberDouble); Document boolFalse{{"path1", false}}; - ASSERT_VALUE_CONTENTS_AND_TYPE(convertExp->evaluate(boolFalse), 0.0, BSONType::NumberDouble); + ASSERT_VALUE_CONTENTS_AND_TYPE( + convertExp->evaluate(boolFalse, &expCtx->variables), 0.0, BSONType::NumberDouble); Document boolTrue{{"path1", true}}; - ASSERT_VALUE_CONTENTS_AND_TYPE(convertExp->evaluate(boolTrue), 1.0, BSONType::NumberDouble); + ASSERT_VALUE_CONTENTS_AND_TYPE( + convertExp->evaluate(boolTrue, &expCtx->variables), 1.0, BSONType::NumberDouble); Document decimalNaN{{"path1", Decimal128::kPositiveNaN}}; - auto result = convertExp->evaluate(decimalNaN); + auto result = convertExp->evaluate(decimalNaN, &expCtx->variables); ASSERT_EQ(result.getType(), BSONType::NumberDouble); ASSERT(std::isnan(result.getDouble())); Document decimalNegativeNaN{{"path1", Decimal128::kNegativeNaN}}; - result = convertExp->evaluate(decimalNegativeNaN); + result = convertExp->evaluate(decimalNegativeNaN, &expCtx->variables); ASSERT_EQ(result.getType(), BSONType::NumberDouble); ASSERT(std::isnan(result.getDouble())); Document decimalInfinity{{"path1", Decimal128::kPositiveInfinity}}; - result = convertExp->evaluate(decimalInfinity); + result = convertExp->evaluate(decimalInfinity, &expCtx->variables); ASSERT_EQ(result.getType(), BSONType::NumberDouble); ASSERT_GT(result.getDouble(), 0.0); ASSERT(std::isinf(result.getDouble())); Document decimalNegativeInfinity{{"path1", Decimal128::kNegativeInfinity}}; - result = convertExp->evaluate(decimalNegativeInfinity); + result = convertExp->evaluate(decimalNegativeInfinity, &expCtx->variables); ASSERT_EQ(result.getType(), BSONType::NumberDouble); ASSERT_LT(result.getDouble(), 0.0); ASSERT(std::isinf(result.getDouble())); @@ -905,12 +951,12 @@ TEST_F(ExpressionConvertTest, ConvertNumericToDouble) { // Note that the least significant bits get lost, because the significand of a double is not // wide enough for the original long long value in its entirety. Document largeLongInput{{"path1", 0xf0000000000000fLL}}; - result = convertExp->evaluate(largeLongInput); + result = convertExp->evaluate(largeLongInput, &expCtx->variables); ASSERT_EQ(static_cast(result.getDouble()), 0xf00000000000000LL); // Again, some precision is lost in the conversion from Decimal128 to double. Document preciseDecimalInput{{"path1", Decimal128("1.125000000000000000005")}}; - result = convertExp->evaluate(preciseDecimalInput); + result = convertExp->evaluate(preciseDecimalInput, &expCtx->variables); ASSERT_EQ(result.getDouble(), 1.125); } @@ -924,13 +970,15 @@ TEST_F(ExpressionConvertTest, ConvertDateToDouble) { auto convertExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); Document dateInput{{"path1", Date_t::fromMillisSinceEpoch(123)}}; - ASSERT_VALUE_CONTENTS_AND_TYPE(convertExp->evaluate(dateInput), 123.0, BSONType::NumberDouble); + ASSERT_VALUE_CONTENTS_AND_TYPE( + convertExp->evaluate(dateInput, &expCtx->variables), 123.0, BSONType::NumberDouble); // Note that the least significant bits get lost, because the significand of a double is not // wide enough for the original 64-bit Date_t value in its entirety. Document largeDateInput{{"path1", Date_t::fromMillisSinceEpoch(0xf0000000000000fLL)}}; - ASSERT_VALUE_CONTENTS_AND_TYPE( - convertExp->evaluate(largeDateInput), 0xf00000000000000LL, BSONType::NumberDouble); + ASSERT_VALUE_CONTENTS_AND_TYPE(convertExp->evaluate(largeDateInput, &expCtx->variables), + 0xf00000000000000LL, + BSONType::NumberDouble); } TEST_F(ExpressionConvertTest, ConvertOutOfBoundsDecimalToDouble) { @@ -943,7 +991,7 @@ TEST_F(ExpressionConvertTest, ConvertOutOfBoundsDecimalToDouble) { auto convertExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); Document overflowInput{{"path1", Decimal128("1e309")}}; - ASSERT_THROWS_WITH_CHECK(convertExp->evaluate(overflowInput), + ASSERT_THROWS_WITH_CHECK(convertExp->evaluate(overflowInput, &expCtx->variables), AssertionException, [](const AssertionException& exception) { ASSERT_EQ(exception.code(), ErrorCodes::ConversionFailure); @@ -952,7 +1000,7 @@ TEST_F(ExpressionConvertTest, ConvertOutOfBoundsDecimalToDouble) { }); Document negativeOverflowInput{{"path1", Decimal128("-1e309")}}; - ASSERT_THROWS_WITH_CHECK(convertExp->evaluate(negativeOverflowInput), + ASSERT_THROWS_WITH_CHECK(convertExp->evaluate(negativeOverflowInput, &expCtx->variables), AssertionException, [](const AssertionException& exception) { ASSERT_EQ(exception.code(), ErrorCodes::ConversionFailure); @@ -973,11 +1021,12 @@ TEST_F(ExpressionConvertTest, ConvertOutOfBoundsDecimalToDoubleWithOnError) { auto convertExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); Document overflowInput{{"path1", Decimal128("1e309")}}; - ASSERT_VALUE_CONTENTS_AND_TYPE(convertExp->evaluate(overflowInput), "X"_sd, BSONType::String); + ASSERT_VALUE_CONTENTS_AND_TYPE( + convertExp->evaluate(overflowInput, &expCtx->variables), "X"_sd, BSONType::String); Document negativeOverflowInput{{"path1", Decimal128("-1e309")}}; ASSERT_VALUE_CONTENTS_AND_TYPE( - convertExp->evaluate(negativeOverflowInput), "X"_sd, BSONType::String); + convertExp->evaluate(negativeOverflowInput, &expCtx->variables), "X"_sd, BSONType::String); } TEST_F(ExpressionConvertTest, ConvertNumericToDecimal) { @@ -991,44 +1040,48 @@ TEST_F(ExpressionConvertTest, ConvertNumericToDecimal) { Document intInput{{"path1", int{1}}}; ASSERT_VALUE_CONTENTS_AND_TYPE( - convertExp->evaluate(intInput), Decimal128(1), BSONType::NumberDecimal); + convertExp->evaluate(intInput, &expCtx->variables), Decimal128(1), BSONType::NumberDecimal); Document longInput{{"path1", 0xf00000000LL}}; - ASSERT_VALUE_CONTENTS_AND_TYPE(convertExp->evaluate(longInput), + ASSERT_VALUE_CONTENTS_AND_TYPE(convertExp->evaluate(longInput, &expCtx->variables), Decimal128(std::int64_t{0xf00000000LL}), BSONType::NumberDecimal); Document doubleInput{{"path1", 0.1}}; - ASSERT_VALUE_CONTENTS_AND_TYPE( - convertExp->evaluate(doubleInput), Decimal128("0.1"), BSONType::NumberDecimal); + ASSERT_VALUE_CONTENTS_AND_TYPE(convertExp->evaluate(doubleInput, &expCtx->variables), + Decimal128("0.1"), + BSONType::NumberDecimal); Document boolFalse{{"path1", false}}; - ASSERT_VALUE_CONTENTS_AND_TYPE( - convertExp->evaluate(boolFalse), Decimal128(0), BSONType::NumberDecimal); + ASSERT_VALUE_CONTENTS_AND_TYPE(convertExp->evaluate(boolFalse, &expCtx->variables), + Decimal128(0), + BSONType::NumberDecimal); Document boolTrue{{"path1", true}}; ASSERT_VALUE_CONTENTS_AND_TYPE( - convertExp->evaluate(boolTrue), Decimal128(1), BSONType::NumberDecimal); + convertExp->evaluate(boolTrue, &expCtx->variables), Decimal128(1), BSONType::NumberDecimal); Document doubleNaN{{"path1", std::numeric_limits::quiet_NaN()}}; - ASSERT_VALUE_CONTENTS_AND_TYPE( - convertExp->evaluate(doubleNaN), Decimal128::kPositiveNaN, BSONType::NumberDecimal); + ASSERT_VALUE_CONTENTS_AND_TYPE(convertExp->evaluate(doubleNaN, &expCtx->variables), + Decimal128::kPositiveNaN, + BSONType::NumberDecimal); Document doubleInfinity{{"path1", std::numeric_limits::infinity()}}; - ASSERT_VALUE_CONTENTS_AND_TYPE(convertExp->evaluate(doubleInfinity), + ASSERT_VALUE_CONTENTS_AND_TYPE(convertExp->evaluate(doubleInfinity, &expCtx->variables), Decimal128::kPositiveInfinity, BSONType::NumberDecimal); Document doubleNegativeInfinity{{"path1", -std::numeric_limits::infinity()}}; - ASSERT_VALUE_CONTENTS_AND_TYPE(convertExp->evaluate(doubleNegativeInfinity), + ASSERT_VALUE_CONTENTS_AND_TYPE(convertExp->evaluate(doubleNegativeInfinity, &expCtx->variables), Decimal128::kNegativeInfinity, BSONType::NumberDecimal); // Unlike the similar conversion in ConvertNumericToDouble, there is more than enough precision // to store the exact orignal value in a Decimal128. Document largeLongInput{{"path1", 0xf0000000000000fLL}}; - ASSERT_VALUE_CONTENTS_AND_TYPE( - convertExp->evaluate(largeLongInput), 0xf0000000000000fLL, BSONType::NumberDecimal); + ASSERT_VALUE_CONTENTS_AND_TYPE(convertExp->evaluate(largeLongInput, &expCtx->variables), + 0xf0000000000000fLL, + BSONType::NumberDecimal); } TEST_F(ExpressionConvertTest, ConvertDateToDecimal) { @@ -1041,12 +1094,14 @@ TEST_F(ExpressionConvertTest, ConvertDateToDecimal) { auto convertExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); Document dateInput{{"path1", Date_t::fromMillisSinceEpoch(123)}}; - ASSERT_VALUE_CONTENTS_AND_TYPE( - convertExp->evaluate(dateInput), Decimal128(123), BSONType::NumberDecimal); + ASSERT_VALUE_CONTENTS_AND_TYPE(convertExp->evaluate(dateInput, &expCtx->variables), + Decimal128(123), + BSONType::NumberDecimal); Document largeDateInput{{"path1", Date_t::fromMillisSinceEpoch(0xf0000000000000fLL)}}; - ASSERT_VALUE_CONTENTS_AND_TYPE( - convertExp->evaluate(largeDateInput), 0xf0000000000000fLL, BSONType::NumberDecimal); + ASSERT_VALUE_CONTENTS_AND_TYPE(convertExp->evaluate(largeDateInput, &expCtx->variables), + 0xf0000000000000fLL, + BSONType::NumberDecimal); } TEST_F(ExpressionConvertTest, ConvertDoubleToInt) { @@ -1059,28 +1114,35 @@ TEST_F(ExpressionConvertTest, ConvertDoubleToInt) { auto convertExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); Document simpleInput{{"path1", 1.0}}; - ASSERT_VALUE_CONTENTS_AND_TYPE(convertExp->evaluate(simpleInput), 1, BSONType::NumberInt); + ASSERT_VALUE_CONTENTS_AND_TYPE( + convertExp->evaluate(simpleInput, &expCtx->variables), 1, BSONType::NumberInt); // Conversions to int should always truncate the fraction (i.e., round towards 0). Document nonIntegerInput1{{"path1", 2.1}}; - ASSERT_VALUE_CONTENTS_AND_TYPE(convertExp->evaluate(nonIntegerInput1), 2, BSONType::NumberInt); + ASSERT_VALUE_CONTENTS_AND_TYPE( + convertExp->evaluate(nonIntegerInput1, &expCtx->variables), 2, BSONType::NumberInt); Document nonIntegerInput2{{"path1", 2.9}}; - ASSERT_VALUE_CONTENTS_AND_TYPE(convertExp->evaluate(nonIntegerInput2), 2, BSONType::NumberInt); + ASSERT_VALUE_CONTENTS_AND_TYPE( + convertExp->evaluate(nonIntegerInput2, &expCtx->variables), 2, BSONType::NumberInt); Document nonIntegerInput3{{"path1", -2.1}}; - ASSERT_VALUE_CONTENTS_AND_TYPE(convertExp->evaluate(nonIntegerInput3), -2, BSONType::NumberInt); + ASSERT_VALUE_CONTENTS_AND_TYPE( + convertExp->evaluate(nonIntegerInput3, &expCtx->variables), -2, BSONType::NumberInt); Document nonIntegerInput4{{"path1", -2.9}}; - ASSERT_VALUE_CONTENTS_AND_TYPE(convertExp->evaluate(nonIntegerInput4), -2, BSONType::NumberInt); + ASSERT_VALUE_CONTENTS_AND_TYPE( + convertExp->evaluate(nonIntegerInput4, &expCtx->variables), -2, BSONType::NumberInt); int maxInt = std::numeric_limits::max(); Document maxInput{{"path1", static_cast(maxInt)}}; - ASSERT_VALUE_CONTENTS_AND_TYPE(convertExp->evaluate(maxInput), maxInt, BSONType::NumberInt); + ASSERT_VALUE_CONTENTS_AND_TYPE( + convertExp->evaluate(maxInput, &expCtx->variables), maxInt, BSONType::NumberInt); int minInt = std::numeric_limits::lowest(); Document minInput{{"path1", static_cast(minInt)}}; - ASSERT_VALUE_CONTENTS_AND_TYPE(convertExp->evaluate(minInput), minInt, BSONType::NumberInt); + ASSERT_VALUE_CONTENTS_AND_TYPE( + convertExp->evaluate(minInput, &expCtx->variables), minInt, BSONType::NumberInt); } TEST_F(ExpressionConvertTest, ConvertOutOfBoundsDoubleToInt) { @@ -1096,7 +1158,7 @@ TEST_F(ExpressionConvertTest, ConvertOutOfBoundsDoubleToInt) { double overflowInt = std::nextafter(static_cast(maxInt), std::numeric_limits::max()); Document overflowInput{{"path1", overflowInt}}; - ASSERT_THROWS_WITH_CHECK(convertExp->evaluate(overflowInput), + ASSERT_THROWS_WITH_CHECK(convertExp->evaluate(overflowInput, &expCtx->variables), AssertionException, [](const AssertionException& exception) { ASSERT_EQ(exception.code(), ErrorCodes::ConversionFailure); @@ -1108,7 +1170,7 @@ TEST_F(ExpressionConvertTest, ConvertOutOfBoundsDoubleToInt) { double negativeOverflowInt = std::nextafter(static_cast(minInt), std::numeric_limits::lowest()); Document negativeOverflowInput{{"path1", negativeOverflowInt}}; - ASSERT_THROWS_WITH_CHECK(convertExp->evaluate(negativeOverflowInput), + ASSERT_THROWS_WITH_CHECK(convertExp->evaluate(negativeOverflowInput, &expCtx->variables), AssertionException, [](const AssertionException& exception) { ASSERT_EQ(exception.code(), ErrorCodes::ConversionFailure); @@ -1117,7 +1179,7 @@ TEST_F(ExpressionConvertTest, ConvertOutOfBoundsDoubleToInt) { }); Document nanInput{{"path1", std::numeric_limits::quiet_NaN()}}; - ASSERT_THROWS_WITH_CHECK(convertExp->evaluate(nanInput), + ASSERT_THROWS_WITH_CHECK(convertExp->evaluate(nanInput, &expCtx->variables), AssertionException, [](const AssertionException& exception) { ASSERT_EQ(exception.code(), ErrorCodes::ConversionFailure); @@ -1126,7 +1188,7 @@ TEST_F(ExpressionConvertTest, ConvertOutOfBoundsDoubleToInt) { }); Document doubleInfinity{{"path1", std::numeric_limits::infinity()}}; - ASSERT_THROWS_WITH_CHECK(convertExp->evaluate(doubleInfinity), + ASSERT_THROWS_WITH_CHECK(convertExp->evaluate(doubleInfinity, &expCtx->variables), AssertionException, [](const AssertionException& exception) { ASSERT_EQ(exception.code(), ErrorCodes::ConversionFailure); @@ -1136,7 +1198,7 @@ TEST_F(ExpressionConvertTest, ConvertOutOfBoundsDoubleToInt) { }); Document doubleNegativeInfinity{{"path1", -std::numeric_limits::infinity()}}; - ASSERT_THROWS_WITH_CHECK(convertExp->evaluate(doubleNegativeInfinity), + ASSERT_THROWS_WITH_CHECK(convertExp->evaluate(doubleNegativeInfinity, &expCtx->variables), AssertionException, [](const AssertionException& exception) { ASSERT_EQ(exception.code(), ErrorCodes::ConversionFailure); @@ -1161,24 +1223,27 @@ TEST_F(ExpressionConvertTest, ConvertOutOfBoundsDoubleToIntWithOnError) { double overflowInt = std::nextafter(static_cast(maxInt), std::numeric_limits::max()); Document overflowInput{{"path1", overflowInt}}; - ASSERT_VALUE_CONTENTS_AND_TYPE(convertExp->evaluate(overflowInput), "X"_sd, BSONType::String); + ASSERT_VALUE_CONTENTS_AND_TYPE( + convertExp->evaluate(overflowInput, &expCtx->variables), "X"_sd, BSONType::String); int minInt = std::numeric_limits::lowest(); double negativeOverflowInt = std::nextafter(static_cast(minInt), std::numeric_limits::lowest()); Document negativeOverflowInput{{"path1", negativeOverflowInt}}; ASSERT_VALUE_CONTENTS_AND_TYPE( - convertExp->evaluate(negativeOverflowInput), "X"_sd, BSONType::String); + convertExp->evaluate(negativeOverflowInput, &expCtx->variables), "X"_sd, BSONType::String); Document nanInput{{"path1", std::numeric_limits::quiet_NaN()}}; - ASSERT_VALUE_CONTENTS_AND_TYPE(convertExp->evaluate(nanInput), "X"_sd, BSONType::String); + ASSERT_VALUE_CONTENTS_AND_TYPE( + convertExp->evaluate(nanInput, &expCtx->variables), "X"_sd, BSONType::String); Document doubleInfinity{{"path1", std::numeric_limits::infinity()}}; - ASSERT_VALUE_CONTENTS_AND_TYPE(convertExp->evaluate(doubleInfinity), "X"_sd, BSONType::String); + ASSERT_VALUE_CONTENTS_AND_TYPE( + convertExp->evaluate(doubleInfinity, &expCtx->variables), "X"_sd, BSONType::String); Document doubleNegativeInfinity{{"path1", -std::numeric_limits::infinity()}}; ASSERT_VALUE_CONTENTS_AND_TYPE( - convertExp->evaluate(doubleNegativeInfinity), "X"_sd, BSONType::String); + convertExp->evaluate(doubleNegativeInfinity, &expCtx->variables), "X"_sd, BSONType::String); } TEST_F(ExpressionConvertTest, ConvertDoubleToLong) { @@ -1191,34 +1256,39 @@ TEST_F(ExpressionConvertTest, ConvertDoubleToLong) { auto convertExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); Document simpleInput{{"path1", 1.0}}; - ASSERT_VALUE_CONTENTS_AND_TYPE(convertExp->evaluate(simpleInput), 1, BSONType::NumberLong); + ASSERT_VALUE_CONTENTS_AND_TYPE( + convertExp->evaluate(simpleInput, &expCtx->variables), 1, BSONType::NumberLong); // Conversions to int should always truncate the fraction (i.e., round towards 0). Document nonIntegerInput1{{"path1", 2.1}}; - ASSERT_VALUE_CONTENTS_AND_TYPE(convertExp->evaluate(nonIntegerInput1), 2, BSONType::NumberLong); + ASSERT_VALUE_CONTENTS_AND_TYPE( + convertExp->evaluate(nonIntegerInput1, &expCtx->variables), 2, BSONType::NumberLong); Document nonIntegerInput2{{"path1", 2.9}}; - ASSERT_VALUE_CONTENTS_AND_TYPE(convertExp->evaluate(nonIntegerInput2), 2, BSONType::NumberLong); + ASSERT_VALUE_CONTENTS_AND_TYPE( + convertExp->evaluate(nonIntegerInput2, &expCtx->variables), 2, BSONType::NumberLong); Document nonIntegerInput3{{"path1", -2.1}}; ASSERT_VALUE_CONTENTS_AND_TYPE( - convertExp->evaluate(nonIntegerInput3), -2, BSONType::NumberLong); + convertExp->evaluate(nonIntegerInput3, &expCtx->variables), -2, BSONType::NumberLong); Document nonIntegerInput4{{"path1", -2.9}}; ASSERT_VALUE_CONTENTS_AND_TYPE( - convertExp->evaluate(nonIntegerInput4), -2, BSONType::NumberLong); + convertExp->evaluate(nonIntegerInput4, &expCtx->variables), -2, BSONType::NumberLong); // maxVal is the highest double value that will not overflow long long. double maxVal = std::nextafter(BSONElement::kLongLongMaxPlusOneAsDouble, 0.0); Document maxInput{{"path1", maxVal}}; - ASSERT_VALUE_CONTENTS_AND_TYPE( - convertExp->evaluate(maxInput), static_cast(maxVal), BSONType::NumberLong); + ASSERT_VALUE_CONTENTS_AND_TYPE(convertExp->evaluate(maxInput, &expCtx->variables), + static_cast(maxVal), + BSONType::NumberLong); // minVal is the lowest double value that will not overflow long long. double minVal = static_cast(std::numeric_limits::lowest()); Document minInput{{"path1", minVal}}; - ASSERT_VALUE_CONTENTS_AND_TYPE( - convertExp->evaluate(minInput), static_cast(minVal), BSONType::NumberLong); + ASSERT_VALUE_CONTENTS_AND_TYPE(convertExp->evaluate(minInput, &expCtx->variables), + static_cast(minVal), + BSONType::NumberLong); } TEST_F(ExpressionConvertTest, ConvertOutOfBoundsDoubleToLong) { @@ -1232,7 +1302,7 @@ TEST_F(ExpressionConvertTest, ConvertOutOfBoundsDoubleToLong) { double overflowLong = BSONElement::kLongLongMaxPlusOneAsDouble; Document overflowInput{{"path1", overflowLong}}; - ASSERT_THROWS_WITH_CHECK(convertExp->evaluate(overflowInput), + ASSERT_THROWS_WITH_CHECK(convertExp->evaluate(overflowInput, &expCtx->variables), AssertionException, [](const AssertionException& exception) { ASSERT_EQ(exception.code(), ErrorCodes::ConversionFailure); @@ -1244,7 +1314,7 @@ TEST_F(ExpressionConvertTest, ConvertOutOfBoundsDoubleToLong) { double negativeOverflowLong = std::nextafter(static_cast(minLong), std::numeric_limits::lowest()); Document negativeOverflowInput{{"path1", negativeOverflowLong}}; - ASSERT_THROWS_WITH_CHECK(convertExp->evaluate(negativeOverflowInput), + ASSERT_THROWS_WITH_CHECK(convertExp->evaluate(negativeOverflowInput, &expCtx->variables), AssertionException, [](const AssertionException& exception) { ASSERT_EQ(exception.code(), ErrorCodes::ConversionFailure); @@ -1253,7 +1323,7 @@ TEST_F(ExpressionConvertTest, ConvertOutOfBoundsDoubleToLong) { }); Document nanInput{{"path1", std::numeric_limits::quiet_NaN()}}; - ASSERT_THROWS_WITH_CHECK(convertExp->evaluate(nanInput), + ASSERT_THROWS_WITH_CHECK(convertExp->evaluate(nanInput, &expCtx->variables), AssertionException, [](const AssertionException& exception) { ASSERT_EQ(exception.code(), ErrorCodes::ConversionFailure); @@ -1262,7 +1332,7 @@ TEST_F(ExpressionConvertTest, ConvertOutOfBoundsDoubleToLong) { }); Document doubleInfinity{{"path1", std::numeric_limits::infinity()}}; - ASSERT_THROWS_WITH_CHECK(convertExp->evaluate(doubleInfinity), + ASSERT_THROWS_WITH_CHECK(convertExp->evaluate(doubleInfinity, &expCtx->variables), AssertionException, [](const AssertionException& exception) { ASSERT_EQ(exception.code(), ErrorCodes::ConversionFailure); @@ -1272,7 +1342,7 @@ TEST_F(ExpressionConvertTest, ConvertOutOfBoundsDoubleToLong) { }); Document doubleNegativeInfinity{{"path1", -std::numeric_limits::infinity()}}; - ASSERT_THROWS_WITH_CHECK(convertExp->evaluate(doubleNegativeInfinity), + ASSERT_THROWS_WITH_CHECK(convertExp->evaluate(doubleNegativeInfinity, &expCtx->variables), AssertionException, [](const AssertionException& exception) { ASSERT_EQ(exception.code(), ErrorCodes::ConversionFailure); @@ -1295,24 +1365,27 @@ TEST_F(ExpressionConvertTest, ConvertOutOfBoundsDoubleToLongWithOnError) { double overflowLong = BSONElement::kLongLongMaxPlusOneAsDouble; Document overflowInput{{"path1", overflowLong}}; - ASSERT_VALUE_CONTENTS_AND_TYPE(convertExp->evaluate(overflowInput), "X"_sd, BSONType::String); + ASSERT_VALUE_CONTENTS_AND_TYPE( + convertExp->evaluate(overflowInput, &expCtx->variables), "X"_sd, BSONType::String); double minLong = static_cast(std::numeric_limits::lowest()); double negativeOverflowLong = std::nextafter(static_cast(minLong), std::numeric_limits::lowest()); Document negativeOverflowInput{{"path1", negativeOverflowLong}}; ASSERT_VALUE_CONTENTS_AND_TYPE( - convertExp->evaluate(negativeOverflowInput), "X"_sd, BSONType::String); + convertExp->evaluate(negativeOverflowInput, &expCtx->variables), "X"_sd, BSONType::String); Document nanInput{{"path1", std::numeric_limits::quiet_NaN()}}; - ASSERT_VALUE_CONTENTS_AND_TYPE(convertExp->evaluate(nanInput), "X"_sd, BSONType::String); + ASSERT_VALUE_CONTENTS_AND_TYPE( + convertExp->evaluate(nanInput, &expCtx->variables), "X"_sd, BSONType::String); Document doubleInfinity{{"path1", std::numeric_limits::infinity()}}; - ASSERT_VALUE_CONTENTS_AND_TYPE(convertExp->evaluate(doubleInfinity), "X"_sd, BSONType::String); + ASSERT_VALUE_CONTENTS_AND_TYPE( + convertExp->evaluate(doubleInfinity, &expCtx->variables), "X"_sd, BSONType::String); Document doubleNegativeInfinity{{"path1", -std::numeric_limits::infinity()}}; ASSERT_VALUE_CONTENTS_AND_TYPE( - convertExp->evaluate(doubleNegativeInfinity), "X"_sd, BSONType::String); + convertExp->evaluate(doubleNegativeInfinity, &expCtx->variables), "X"_sd, BSONType::String); } TEST_F(ExpressionConvertTest, ConvertDecimalToInt) { @@ -1325,28 +1398,35 @@ TEST_F(ExpressionConvertTest, ConvertDecimalToInt) { auto convertExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); Document simpleInput{{"path1", Decimal128("1.0")}}; - ASSERT_VALUE_CONTENTS_AND_TYPE(convertExp->evaluate(simpleInput), 1, BSONType::NumberInt); + ASSERT_VALUE_CONTENTS_AND_TYPE( + convertExp->evaluate(simpleInput, &expCtx->variables), 1, BSONType::NumberInt); // Conversions to int should always truncate the fraction (i.e., round towards 0). Document nonIntegerInput1{{"path1", Decimal128("2.1")}}; - ASSERT_VALUE_CONTENTS_AND_TYPE(convertExp->evaluate(nonIntegerInput1), 2, BSONType::NumberInt); + ASSERT_VALUE_CONTENTS_AND_TYPE( + convertExp->evaluate(nonIntegerInput1, &expCtx->variables), 2, BSONType::NumberInt); Document nonIntegerInput2{{"path1", Decimal128("2.9")}}; - ASSERT_VALUE_CONTENTS_AND_TYPE(convertExp->evaluate(nonIntegerInput2), 2, BSONType::NumberInt); + ASSERT_VALUE_CONTENTS_AND_TYPE( + convertExp->evaluate(nonIntegerInput2, &expCtx->variables), 2, BSONType::NumberInt); Document nonIntegerInput3{{"path1", Decimal128("-2.1")}}; - ASSERT_VALUE_CONTENTS_AND_TYPE(convertExp->evaluate(nonIntegerInput3), -2, BSONType::NumberInt); + ASSERT_VALUE_CONTENTS_AND_TYPE( + convertExp->evaluate(nonIntegerInput3, &expCtx->variables), -2, BSONType::NumberInt); Document nonIntegerInput4{{"path1", Decimal128("-2.9")}}; - ASSERT_VALUE_CONTENTS_AND_TYPE(convertExp->evaluate(nonIntegerInput3), -2, BSONType::NumberInt); + ASSERT_VALUE_CONTENTS_AND_TYPE( + convertExp->evaluate(nonIntegerInput3, &expCtx->variables), -2, BSONType::NumberInt); int maxInt = std::numeric_limits::max(); Document maxInput{{"path1", Decimal128(maxInt)}}; - ASSERT_VALUE_CONTENTS_AND_TYPE(convertExp->evaluate(maxInput), maxInt, BSONType::NumberInt); + ASSERT_VALUE_CONTENTS_AND_TYPE( + convertExp->evaluate(maxInput, &expCtx->variables), maxInt, BSONType::NumberInt); int minInt = std::numeric_limits::min(); Document minInput{{"path1", Decimal128(minInt)}}; - ASSERT_VALUE_CONTENTS_AND_TYPE(convertExp->evaluate(minInput), minInt, BSONType::NumberInt); + ASSERT_VALUE_CONTENTS_AND_TYPE( + convertExp->evaluate(minInput, &expCtx->variables), minInt, BSONType::NumberInt); } TEST_F(ExpressionConvertTest, ConvertOutOfBoundsDecimalToInt) { @@ -1360,7 +1440,7 @@ TEST_F(ExpressionConvertTest, ConvertOutOfBoundsDecimalToInt) { int maxInt = std::numeric_limits::max(); Document overflowInput{{"path1", Decimal128(maxInt).add(Decimal128(1))}}; - ASSERT_THROWS_WITH_CHECK(convertExp->evaluate(overflowInput), + ASSERT_THROWS_WITH_CHECK(convertExp->evaluate(overflowInput, &expCtx->variables), AssertionException, [](const AssertionException& exception) { ASSERT_EQ(exception.code(), ErrorCodes::ConversionFailure); @@ -1370,7 +1450,7 @@ TEST_F(ExpressionConvertTest, ConvertOutOfBoundsDecimalToInt) { int minInt = std::numeric_limits::lowest(); Document negativeOverflowInput{{"path1", Decimal128(minInt).subtract(Decimal128(1))}}; - ASSERT_THROWS_WITH_CHECK(convertExp->evaluate(negativeOverflowInput), + ASSERT_THROWS_WITH_CHECK(convertExp->evaluate(negativeOverflowInput, &expCtx->variables), AssertionException, [](const AssertionException& exception) { ASSERT_EQ(exception.code(), ErrorCodes::ConversionFailure); @@ -1379,7 +1459,7 @@ TEST_F(ExpressionConvertTest, ConvertOutOfBoundsDecimalToInt) { }); Document nanInput{{"path1", Decimal128::kPositiveNaN}}; - ASSERT_THROWS_WITH_CHECK(convertExp->evaluate(nanInput), + ASSERT_THROWS_WITH_CHECK(convertExp->evaluate(nanInput, &expCtx->variables), AssertionException, [](const AssertionException& exception) { ASSERT_EQ(exception.code(), ErrorCodes::ConversionFailure); @@ -1388,7 +1468,7 @@ TEST_F(ExpressionConvertTest, ConvertOutOfBoundsDecimalToInt) { }); Document negativeNaNInput{{"path1", Decimal128::kNegativeNaN}}; - ASSERT_THROWS_WITH_CHECK(convertExp->evaluate(negativeNaNInput), + ASSERT_THROWS_WITH_CHECK(convertExp->evaluate(negativeNaNInput, &expCtx->variables), AssertionException, [](const AssertionException& exception) { ASSERT_EQ(exception.code(), ErrorCodes::ConversionFailure); @@ -1397,7 +1477,7 @@ TEST_F(ExpressionConvertTest, ConvertOutOfBoundsDecimalToInt) { }); Document decimalInfinity{{"path1", Decimal128::kPositiveInfinity}}; - ASSERT_THROWS_WITH_CHECK(convertExp->evaluate(decimalInfinity), + ASSERT_THROWS_WITH_CHECK(convertExp->evaluate(decimalInfinity, &expCtx->variables), AssertionException, [](const AssertionException& exception) { ASSERT_EQ(exception.code(), ErrorCodes::ConversionFailure); @@ -1407,7 +1487,7 @@ TEST_F(ExpressionConvertTest, ConvertOutOfBoundsDecimalToInt) { }); Document decimalNegativeInfinity{{"path1", Decimal128::kNegativeInfinity}}; - ASSERT_THROWS_WITH_CHECK(convertExp->evaluate(decimalNegativeInfinity), + ASSERT_THROWS_WITH_CHECK(convertExp->evaluate(decimalNegativeInfinity, &expCtx->variables), AssertionException, [](const AssertionException& exception) { ASSERT_EQ(exception.code(), ErrorCodes::ConversionFailure); @@ -1430,26 +1510,31 @@ TEST_F(ExpressionConvertTest, ConvertOutOfBoundsDecimalToIntWithOnError) { int maxInt = std::numeric_limits::max(); Document overflowInput{{"path1", Decimal128(maxInt).add(Decimal128(1))}}; - ASSERT_VALUE_CONTENTS_AND_TYPE(convertExp->evaluate(overflowInput), "X"_sd, BSONType::String); + ASSERT_VALUE_CONTENTS_AND_TYPE( + convertExp->evaluate(overflowInput, &expCtx->variables), "X"_sd, BSONType::String); int minInt = std::numeric_limits::lowest(); Document negativeOverflowInput{{"path1", Decimal128(minInt).subtract(Decimal128(1))}}; ASSERT_VALUE_CONTENTS_AND_TYPE( - convertExp->evaluate(negativeOverflowInput), "X"_sd, BSONType::String); + convertExp->evaluate(negativeOverflowInput, &expCtx->variables), "X"_sd, BSONType::String); Document nanInput{{"path1", Decimal128::kPositiveNaN}}; - ASSERT_VALUE_CONTENTS_AND_TYPE(convertExp->evaluate(nanInput), "X"_sd, BSONType::String); + ASSERT_VALUE_CONTENTS_AND_TYPE( + convertExp->evaluate(nanInput, &expCtx->variables), "X"_sd, BSONType::String); Document negativeNaNInput{{"path1", Decimal128::kNegativeNaN}}; ASSERT_VALUE_CONTENTS_AND_TYPE( - convertExp->evaluate(negativeNaNInput), "X"_sd, BSONType::String); + convertExp->evaluate(negativeNaNInput, &expCtx->variables), "X"_sd, BSONType::String); Document decimalInfinity{{"path1", Decimal128::kPositiveInfinity}}; - ASSERT_VALUE_CONTENTS_AND_TYPE(convertExp->evaluate(decimalInfinity), "X"_sd, BSONType::String); + ASSERT_VALUE_CONTENTS_AND_TYPE( + convertExp->evaluate(decimalInfinity, &expCtx->variables), "X"_sd, BSONType::String); Document decimalNegativeInfinity{{"path1", Decimal128::kNegativeInfinity}}; ASSERT_VALUE_CONTENTS_AND_TYPE( - convertExp->evaluate(decimalNegativeInfinity), "X"_sd, BSONType::String); + convertExp->evaluate(decimalNegativeInfinity, &expCtx->variables), + "X"_sd, + BSONType::String); } TEST_F(ExpressionConvertTest, ConvertDecimalToLong) { @@ -1462,30 +1547,35 @@ TEST_F(ExpressionConvertTest, ConvertDecimalToLong) { auto convertExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); Document simpleInput{{"path1", Decimal128("1.0")}}; - ASSERT_VALUE_CONTENTS_AND_TYPE(convertExp->evaluate(simpleInput), 1, BSONType::NumberLong); + ASSERT_VALUE_CONTENTS_AND_TYPE( + convertExp->evaluate(simpleInput, &expCtx->variables), 1, BSONType::NumberLong); // Conversions to long should always truncate the fraction (i.e., round towards 0). Document nonIntegerInput1{{"path1", Decimal128("2.1")}}; - ASSERT_VALUE_CONTENTS_AND_TYPE(convertExp->evaluate(nonIntegerInput1), 2, BSONType::NumberLong); + ASSERT_VALUE_CONTENTS_AND_TYPE( + convertExp->evaluate(nonIntegerInput1, &expCtx->variables), 2, BSONType::NumberLong); Document nonIntegerInput2{{"path1", Decimal128("2.9")}}; - ASSERT_VALUE_CONTENTS_AND_TYPE(convertExp->evaluate(nonIntegerInput2), 2, BSONType::NumberLong); + ASSERT_VALUE_CONTENTS_AND_TYPE( + convertExp->evaluate(nonIntegerInput2, &expCtx->variables), 2, BSONType::NumberLong); Document nonIntegerInput3{{"path1", Decimal128("-2.1")}}; ASSERT_VALUE_CONTENTS_AND_TYPE( - convertExp->evaluate(nonIntegerInput3), -2, BSONType::NumberLong); + convertExp->evaluate(nonIntegerInput3, &expCtx->variables), -2, BSONType::NumberLong); Document nonIntegerInput4{{"path1", Decimal128("-2.9")}}; ASSERT_VALUE_CONTENTS_AND_TYPE( - convertExp->evaluate(nonIntegerInput4), -2, BSONType::NumberLong); + convertExp->evaluate(nonIntegerInput4, &expCtx->variables), -2, BSONType::NumberLong); long long maxVal = std::numeric_limits::max(); Document maxInput{{"path1", Decimal128(std::int64_t{maxVal})}}; - ASSERT_VALUE_CONTENTS_AND_TYPE(convertExp->evaluate(maxInput), maxVal, BSONType::NumberLong); + ASSERT_VALUE_CONTENTS_AND_TYPE( + convertExp->evaluate(maxInput, &expCtx->variables), maxVal, BSONType::NumberLong); long long minVal = std::numeric_limits::min(); Document minInput{{"path1", Decimal128(std::int64_t{minVal})}}; - ASSERT_VALUE_CONTENTS_AND_TYPE(convertExp->evaluate(minInput), minVal, BSONType::NumberLong); + ASSERT_VALUE_CONTENTS_AND_TYPE( + convertExp->evaluate(minInput, &expCtx->variables), minVal, BSONType::NumberLong); } TEST_F(ExpressionConvertTest, ConvertOutOfBoundsDecimalToLong) { @@ -1499,7 +1589,7 @@ TEST_F(ExpressionConvertTest, ConvertOutOfBoundsDecimalToLong) { long long maxVal = std::numeric_limits::max(); Document overflowInput{{"path1", Decimal128(std::int64_t{maxVal}).add(Decimal128(1))}}; - ASSERT_THROWS_WITH_CHECK(convertExp->evaluate(overflowInput), + ASSERT_THROWS_WITH_CHECK(convertExp->evaluate(overflowInput, &expCtx->variables), AssertionException, [](const AssertionException& exception) { ASSERT_EQ(exception.code(), ErrorCodes::ConversionFailure); @@ -1510,7 +1600,7 @@ TEST_F(ExpressionConvertTest, ConvertOutOfBoundsDecimalToLong) { long long minVal = std::numeric_limits::lowest(); Document negativeOverflowInput{ {"path1", Decimal128(std::int64_t{minVal}).subtract(Decimal128(1))}}; - ASSERT_THROWS_WITH_CHECK(convertExp->evaluate(negativeOverflowInput), + ASSERT_THROWS_WITH_CHECK(convertExp->evaluate(negativeOverflowInput, &expCtx->variables), AssertionException, [](const AssertionException& exception) { ASSERT_EQ(exception.code(), ErrorCodes::ConversionFailure); @@ -1519,7 +1609,7 @@ TEST_F(ExpressionConvertTest, ConvertOutOfBoundsDecimalToLong) { }); Document nanInput{{"path1", Decimal128::kPositiveNaN}}; - ASSERT_THROWS_WITH_CHECK(convertExp->evaluate(nanInput), + ASSERT_THROWS_WITH_CHECK(convertExp->evaluate(nanInput, &expCtx->variables), AssertionException, [](const AssertionException& exception) { ASSERT_EQ(exception.code(), ErrorCodes::ConversionFailure); @@ -1528,7 +1618,7 @@ TEST_F(ExpressionConvertTest, ConvertOutOfBoundsDecimalToLong) { }); Document negativeNaNInput{{"path1", Decimal128::kNegativeNaN}}; - ASSERT_THROWS_WITH_CHECK(convertExp->evaluate(negativeNaNInput), + ASSERT_THROWS_WITH_CHECK(convertExp->evaluate(negativeNaNInput, &expCtx->variables), AssertionException, [](const AssertionException& exception) { ASSERT_EQ(exception.code(), ErrorCodes::ConversionFailure); @@ -1537,7 +1627,7 @@ TEST_F(ExpressionConvertTest, ConvertOutOfBoundsDecimalToLong) { }); Document decimalInfinity{{"path1", Decimal128::kPositiveInfinity}}; - ASSERT_THROWS_WITH_CHECK(convertExp->evaluate(decimalInfinity), + ASSERT_THROWS_WITH_CHECK(convertExp->evaluate(decimalInfinity, &expCtx->variables), AssertionException, [](const AssertionException& exception) { ASSERT_EQ(exception.code(), ErrorCodes::ConversionFailure); @@ -1547,7 +1637,7 @@ TEST_F(ExpressionConvertTest, ConvertOutOfBoundsDecimalToLong) { }); Document decimalNegativeInfinity{{"path1", Decimal128::kNegativeInfinity}}; - ASSERT_THROWS_WITH_CHECK(convertExp->evaluate(decimalNegativeInfinity), + ASSERT_THROWS_WITH_CHECK(convertExp->evaluate(decimalNegativeInfinity, &expCtx->variables), AssertionException, [](const AssertionException& exception) { ASSERT_EQ(exception.code(), ErrorCodes::ConversionFailure); @@ -1570,27 +1660,32 @@ TEST_F(ExpressionConvertTest, ConvertOutOfBoundsDecimalToLongWithOnError) { long long maxVal = std::numeric_limits::max(); Document overflowInput{{"path1", Decimal128(std::int64_t{maxVal}).add(Decimal128(1))}}; - ASSERT_VALUE_CONTENTS_AND_TYPE(convertExp->evaluate(overflowInput), "X"_sd, BSONType::String); + ASSERT_VALUE_CONTENTS_AND_TYPE( + convertExp->evaluate(overflowInput, &expCtx->variables), "X"_sd, BSONType::String); long long minVal = std::numeric_limits::lowest(); Document negativeOverflowInput{ {"path1", Decimal128(std::int64_t{minVal}).subtract(Decimal128(1))}}; ASSERT_VALUE_CONTENTS_AND_TYPE( - convertExp->evaluate(negativeOverflowInput), "X"_sd, BSONType::String); + convertExp->evaluate(negativeOverflowInput, &expCtx->variables), "X"_sd, BSONType::String); Document nanInput{{"path1", Decimal128::kPositiveNaN}}; - ASSERT_VALUE_CONTENTS_AND_TYPE(convertExp->evaluate(nanInput), "X"_sd, BSONType::String); + ASSERT_VALUE_CONTENTS_AND_TYPE( + convertExp->evaluate(nanInput, &expCtx->variables), "X"_sd, BSONType::String); Document negativeNaNInput{{"path1", Decimal128::kNegativeNaN}}; ASSERT_VALUE_CONTENTS_AND_TYPE( - convertExp->evaluate(negativeNaNInput), "X"_sd, BSONType::String); + convertExp->evaluate(negativeNaNInput, &expCtx->variables), "X"_sd, BSONType::String); Document decimalInfinity{{"path1", Decimal128::kPositiveInfinity}}; - ASSERT_VALUE_CONTENTS_AND_TYPE(convertExp->evaluate(decimalInfinity), "X"_sd, BSONType::String); + ASSERT_VALUE_CONTENTS_AND_TYPE( + convertExp->evaluate(decimalInfinity, &expCtx->variables), "X"_sd, BSONType::String); Document decimalNegativeInfinity{{"path1", Decimal128::kNegativeInfinity}}; ASSERT_VALUE_CONTENTS_AND_TYPE( - convertExp->evaluate(decimalNegativeInfinity), "X"_sd, BSONType::String); + convertExp->evaluate(decimalNegativeInfinity, &expCtx->variables), + "X"_sd, + BSONType::String); } TEST_F(ExpressionConvertTest, ConvertDateToLong) { @@ -1603,7 +1698,8 @@ TEST_F(ExpressionConvertTest, ConvertDateToLong) { auto convertExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); Document dateInput{{"path1", Date_t::fromMillisSinceEpoch(123LL)}}; - ASSERT_VALUE_CONTENTS_AND_TYPE(convertExp->evaluate(dateInput), 123LL, BSONType::NumberLong); + ASSERT_VALUE_CONTENTS_AND_TYPE( + convertExp->evaluate(dateInput, &expCtx->variables), 123LL, BSONType::NumberLong); } TEST_F(ExpressionConvertTest, ConvertIntToLong) { @@ -1616,15 +1712,18 @@ TEST_F(ExpressionConvertTest, ConvertIntToLong) { auto convertExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); Document simpleInput{{"path1", 1}}; - ASSERT_VALUE_CONTENTS_AND_TYPE(convertExp->evaluate(simpleInput), 1LL, BSONType::NumberLong); + ASSERT_VALUE_CONTENTS_AND_TYPE( + convertExp->evaluate(simpleInput, &expCtx->variables), 1LL, BSONType::NumberLong); int maxInt = std::numeric_limits::max(); Document maxInput{{"path1", maxInt}}; - ASSERT_VALUE_CONTENTS_AND_TYPE(convertExp->evaluate(maxInput), maxInt, BSONType::NumberLong); + ASSERT_VALUE_CONTENTS_AND_TYPE( + convertExp->evaluate(maxInput, &expCtx->variables), maxInt, BSONType::NumberLong); int minInt = std::numeric_limits::min(); Document minInput{{"path1", minInt}}; - ASSERT_VALUE_CONTENTS_AND_TYPE(convertExp->evaluate(minInput), minInt, BSONType::NumberLong); + ASSERT_VALUE_CONTENTS_AND_TYPE( + convertExp->evaluate(minInput, &expCtx->variables), minInt, BSONType::NumberLong); } TEST_F(ExpressionConvertTest, ConvertLongToInt) { @@ -1637,15 +1736,18 @@ TEST_F(ExpressionConvertTest, ConvertLongToInt) { auto convertExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); Document simpleInput{{"path1", 1}}; - ASSERT_VALUE_CONTENTS_AND_TYPE(convertExp->evaluate(simpleInput), 1, BSONType::NumberInt); + ASSERT_VALUE_CONTENTS_AND_TYPE( + convertExp->evaluate(simpleInput, &expCtx->variables), 1, BSONType::NumberInt); long long maxInt = std::numeric_limits::max(); Document maxInput{{"path1", maxInt}}; - ASSERT_VALUE_CONTENTS_AND_TYPE(convertExp->evaluate(maxInput), maxInt, BSONType::NumberInt); + ASSERT_VALUE_CONTENTS_AND_TYPE( + convertExp->evaluate(maxInput, &expCtx->variables), maxInt, BSONType::NumberInt); long long minInt = std::numeric_limits::min(); Document minInput{{"path1", minInt}}; - ASSERT_VALUE_CONTENTS_AND_TYPE(convertExp->evaluate(minInput), minInt, BSONType::NumberInt); + ASSERT_VALUE_CONTENTS_AND_TYPE( + convertExp->evaluate(minInput, &expCtx->variables), minInt, BSONType::NumberInt); } TEST_F(ExpressionConvertTest, ConvertOutOfBoundsLongToInt) { @@ -1659,7 +1761,7 @@ TEST_F(ExpressionConvertTest, ConvertOutOfBoundsLongToInt) { long long maxInt = std::numeric_limits::max(); Document overflowInput{{"path1", maxInt + 1}}; - ASSERT_THROWS_WITH_CHECK(convertExp->evaluate(overflowInput), + ASSERT_THROWS_WITH_CHECK(convertExp->evaluate(overflowInput, &expCtx->variables), AssertionException, [](const AssertionException& exception) { ASSERT_EQ(exception.code(), ErrorCodes::ConversionFailure); @@ -1669,7 +1771,7 @@ TEST_F(ExpressionConvertTest, ConvertOutOfBoundsLongToInt) { long long minInt = std::numeric_limits::min(); Document negativeOverflowInput{{"path1", minInt - 1}}; - ASSERT_THROWS_WITH_CHECK(convertExp->evaluate(negativeOverflowInput), + ASSERT_THROWS_WITH_CHECK(convertExp->evaluate(negativeOverflowInput, &expCtx->variables), AssertionException, [](const AssertionException& exception) { ASSERT_EQ(exception.code(), ErrorCodes::ConversionFailure); @@ -1691,12 +1793,13 @@ TEST_F(ExpressionConvertTest, ConvertOutOfBoundsLongToIntWithOnError) { long long maxInt = std::numeric_limits::max(); Document overflowInput{{"path1", maxInt + 1}}; - ASSERT_VALUE_CONTENTS_AND_TYPE(convertExp->evaluate(overflowInput), "X"_sd, BSONType::String); + ASSERT_VALUE_CONTENTS_AND_TYPE( + convertExp->evaluate(overflowInput, &expCtx->variables), "X"_sd, BSONType::String); long long minInt = std::numeric_limits::min(); Document negativeOverflowInput{{"path1", minInt - 1}}; ASSERT_VALUE_CONTENTS_AND_TYPE( - convertExp->evaluate(negativeOverflowInput), "X"_sd, BSONType::String); + convertExp->evaluate(negativeOverflowInput, &expCtx->variables), "X"_sd, BSONType::String); } TEST_F(ExpressionConvertTest, ConvertBoolToInt) { @@ -1709,10 +1812,12 @@ TEST_F(ExpressionConvertTest, ConvertBoolToInt) { auto convertExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); Document boolFalse{{"path1", false}}; - ASSERT_VALUE_CONTENTS_AND_TYPE(convertExp->evaluate(boolFalse), 0, BSONType::NumberInt); + ASSERT_VALUE_CONTENTS_AND_TYPE( + convertExp->evaluate(boolFalse, &expCtx->variables), 0, BSONType::NumberInt); Document boolTrue{{"path1", true}}; - ASSERT_VALUE_CONTENTS_AND_TYPE(convertExp->evaluate(boolTrue), 1, BSONType::NumberInt); + ASSERT_VALUE_CONTENTS_AND_TYPE( + convertExp->evaluate(boolTrue, &expCtx->variables), 1, BSONType::NumberInt); } TEST_F(ExpressionConvertTest, ConvertBoolToLong) { @@ -1725,10 +1830,12 @@ TEST_F(ExpressionConvertTest, ConvertBoolToLong) { auto convertExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); Document boolFalse{{"path1", false}}; - ASSERT_VALUE_CONTENTS_AND_TYPE(convertExp->evaluate(boolFalse), 0LL, BSONType::NumberLong); + ASSERT_VALUE_CONTENTS_AND_TYPE( + convertExp->evaluate(boolFalse, &expCtx->variables), 0LL, BSONType::NumberLong); Document boolTrue{{"path1", true}}; - ASSERT_VALUE_CONTENTS_AND_TYPE(convertExp->evaluate(boolTrue), 1LL, BSONType::NumberLong); + ASSERT_VALUE_CONTENTS_AND_TYPE( + convertExp->evaluate(boolTrue, &expCtx->variables), 1LL, BSONType::NumberLong); } TEST_F(ExpressionConvertTest, ConvertNumberToDate) { @@ -1741,23 +1848,25 @@ TEST_F(ExpressionConvertTest, ConvertNumberToDate) { auto convertExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); Document longInput{{"path1", 0LL}}; - ASSERT_EQ(dateToISOStringUTC(convertExp->evaluate(longInput).getDate()), + ASSERT_EQ(dateToISOStringUTC(convertExp->evaluate(longInput, &expCtx->variables).getDate()), "1970-01-01T00:00:00.000Z"); Document doubleInput{{"path1", 431568000000.0}}; - ASSERT_EQ(dateToISOStringUTC(convertExp->evaluate(doubleInput).getDate()), + ASSERT_EQ(dateToISOStringUTC(convertExp->evaluate(doubleInput, &expCtx->variables).getDate()), "1983-09-05T00:00:00.000Z"); Document doubleInputWithFraction{{"path1", 431568000000.987}}; - ASSERT_EQ(dateToISOStringUTC(convertExp->evaluate(doubleInputWithFraction).getDate()), + ASSERT_EQ(dateToISOStringUTC( + convertExp->evaluate(doubleInputWithFraction, &expCtx->variables).getDate()), "1983-09-05T00:00:00.000Z"); Document decimalInput{{"path1", Decimal128("872835240000")}}; - ASSERT_EQ(dateToISOStringUTC(convertExp->evaluate(decimalInput).getDate()), + ASSERT_EQ(dateToISOStringUTC(convertExp->evaluate(decimalInput, &expCtx->variables).getDate()), "1997-08-29T06:14:00.000Z"); Document decimalInputWithFraction{{"path1", Decimal128("872835240000.987")}}; - ASSERT_EQ(dateToISOStringUTC(convertExp->evaluate(decimalInputWithFraction).getDate()), + ASSERT_EQ(dateToISOStringUTC( + convertExp->evaluate(decimalInputWithFraction, &expCtx->variables).getDate()), "1997-08-29T06:14:00.000Z"); } @@ -1771,7 +1880,7 @@ TEST_F(ExpressionConvertTest, ConvertOutOfBoundsNumberToDate) { auto convertExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); Document doubleOverflowInput{{"path1", 1.0e100}}; - ASSERT_THROWS_WITH_CHECK(convertExp->evaluate(doubleOverflowInput), + ASSERT_THROWS_WITH_CHECK(convertExp->evaluate(doubleOverflowInput, &expCtx->variables), AssertionException, [](const AssertionException& exception) { ASSERT_EQ(exception.code(), ErrorCodes::ConversionFailure); @@ -1780,7 +1889,7 @@ TEST_F(ExpressionConvertTest, ConvertOutOfBoundsNumberToDate) { }); Document doubleNegativeOverflowInput{{"path1", -1.0e100}}; - ASSERT_THROWS_WITH_CHECK(convertExp->evaluate(doubleNegativeOverflowInput), + ASSERT_THROWS_WITH_CHECK(convertExp->evaluate(doubleNegativeOverflowInput, &expCtx->variables), AssertionException, [](const AssertionException& exception) { ASSERT_EQ(exception.code(), ErrorCodes::ConversionFailure); @@ -1789,7 +1898,7 @@ TEST_F(ExpressionConvertTest, ConvertOutOfBoundsNumberToDate) { }); Document doubleNaN{{"path1", std::numeric_limits::quiet_NaN()}}; - ASSERT_THROWS_WITH_CHECK(convertExp->evaluate(doubleNaN), + ASSERT_THROWS_WITH_CHECK(convertExp->evaluate(doubleNaN, &expCtx->variables), AssertionException, [](const AssertionException& exception) { ASSERT_EQ(exception.code(), ErrorCodes::ConversionFailure); @@ -1799,7 +1908,7 @@ TEST_F(ExpressionConvertTest, ConvertOutOfBoundsNumberToDate) { }); Document doubleInfinity{{"path1", std::numeric_limits::infinity()}}; - ASSERT_THROWS_WITH_CHECK(convertExp->evaluate(doubleInfinity), + ASSERT_THROWS_WITH_CHECK(convertExp->evaluate(doubleInfinity, &expCtx->variables), AssertionException, [](const AssertionException& exception) { ASSERT_EQ(exception.code(), ErrorCodes::ConversionFailure); @@ -1809,7 +1918,7 @@ TEST_F(ExpressionConvertTest, ConvertOutOfBoundsNumberToDate) { }); Document doubleNegativeInfinity{{"path1", -std::numeric_limits::infinity()}}; - ASSERT_THROWS_WITH_CHECK(convertExp->evaluate(doubleNegativeInfinity), + ASSERT_THROWS_WITH_CHECK(convertExp->evaluate(doubleNegativeInfinity, &expCtx->variables), AssertionException, [](const AssertionException& exception) { ASSERT_EQ(exception.code(), ErrorCodes::ConversionFailure); @@ -1819,7 +1928,7 @@ TEST_F(ExpressionConvertTest, ConvertOutOfBoundsNumberToDate) { }); Document decimalOverflowInput{{"path1", Decimal128("1.0e100")}}; - ASSERT_THROWS_WITH_CHECK(convertExp->evaluate(decimalOverflowInput), + ASSERT_THROWS_WITH_CHECK(convertExp->evaluate(decimalOverflowInput, &expCtx->variables), AssertionException, [](const AssertionException& exception) { ASSERT_EQ(exception.code(), ErrorCodes::ConversionFailure); @@ -1828,7 +1937,7 @@ TEST_F(ExpressionConvertTest, ConvertOutOfBoundsNumberToDate) { }); Document decimalNegativeOverflowInput{{"path1", Decimal128("1.0e100")}}; - ASSERT_THROWS_WITH_CHECK(convertExp->evaluate(decimalNegativeOverflowInput), + ASSERT_THROWS_WITH_CHECK(convertExp->evaluate(decimalNegativeOverflowInput, &expCtx->variables), AssertionException, [](const AssertionException& exception) { ASSERT_EQ(exception.code(), ErrorCodes::ConversionFailure); @@ -1837,7 +1946,7 @@ TEST_F(ExpressionConvertTest, ConvertOutOfBoundsNumberToDate) { }); Document decimalNaN{{"path1", Decimal128::kPositiveNaN}}; - ASSERT_THROWS_WITH_CHECK(convertExp->evaluate(decimalNaN), + ASSERT_THROWS_WITH_CHECK(convertExp->evaluate(decimalNaN, &expCtx->variables), AssertionException, [](const AssertionException& exception) { ASSERT_EQ(exception.code(), ErrorCodes::ConversionFailure); @@ -1847,7 +1956,7 @@ TEST_F(ExpressionConvertTest, ConvertOutOfBoundsNumberToDate) { }); Document decimalNegativeNaN{{"path1", Decimal128::kNegativeNaN}}; - ASSERT_THROWS_WITH_CHECK(convertExp->evaluate(decimalNegativeNaN), + ASSERT_THROWS_WITH_CHECK(convertExp->evaluate(decimalNegativeNaN, &expCtx->variables), AssertionException, [](const AssertionException& exception) { ASSERT_EQ(exception.code(), ErrorCodes::ConversionFailure); @@ -1857,7 +1966,7 @@ TEST_F(ExpressionConvertTest, ConvertOutOfBoundsNumberToDate) { }); Document decimalInfinity{{"path1", Decimal128::kPositiveInfinity}}; - ASSERT_THROWS_WITH_CHECK(convertExp->evaluate(decimalInfinity), + ASSERT_THROWS_WITH_CHECK(convertExp->evaluate(decimalInfinity, &expCtx->variables), AssertionException, [](const AssertionException& exception) { ASSERT_EQ(exception.code(), ErrorCodes::ConversionFailure); @@ -1867,7 +1976,7 @@ TEST_F(ExpressionConvertTest, ConvertOutOfBoundsNumberToDate) { }); Document decimalNegativeInfinity{{"path1", Decimal128::kNegativeInfinity}}; - ASSERT_THROWS_WITH_CHECK(convertExp->evaluate(decimalNegativeInfinity), + ASSERT_THROWS_WITH_CHECK(convertExp->evaluate(decimalNegativeInfinity, &expCtx->variables), AssertionException, [](const AssertionException& exception) { ASSERT_EQ(exception.code(), ErrorCodes::ConversionFailure); @@ -1890,47 +1999,58 @@ TEST_F(ExpressionConvertTest, ConvertOutOfBoundsNumberToDateWithOnError) { // Int is explicitly disallowed for date conversions. Clients must use 64-bit long instead. Document intInput{{"path1", int{0}}}; - ASSERT_VALUE_CONTENTS_AND_TYPE(convertExp->evaluate(intInput), "X"_sd, BSONType::String); + ASSERT_VALUE_CONTENTS_AND_TYPE( + convertExp->evaluate(intInput, &expCtx->variables), "X"_sd, BSONType::String); Document doubleOverflowInput{{"path1", 1.0e100}}; ASSERT_VALUE_CONTENTS_AND_TYPE( - convertExp->evaluate(doubleOverflowInput), "X"_sd, BSONType::String); + convertExp->evaluate(doubleOverflowInput, &expCtx->variables), "X"_sd, BSONType::String); Document doubleNegativeOverflowInput{{"path1", -1.0e100}}; ASSERT_VALUE_CONTENTS_AND_TYPE( - convertExp->evaluate(doubleNegativeOverflowInput), "X"_sd, BSONType::String); + convertExp->evaluate(doubleNegativeOverflowInput, &expCtx->variables), + "X"_sd, + BSONType::String); Document doubleNaN{{"path1", std::numeric_limits::quiet_NaN()}}; - ASSERT_VALUE_CONTENTS_AND_TYPE(convertExp->evaluate(doubleNaN), "X"_sd, BSONType::String); + ASSERT_VALUE_CONTENTS_AND_TYPE( + convertExp->evaluate(doubleNaN, &expCtx->variables), "X"_sd, BSONType::String); Document doubleInfinity{{"path1", std::numeric_limits::infinity()}}; - ASSERT_VALUE_CONTENTS_AND_TYPE(convertExp->evaluate(doubleInfinity), "X"_sd, BSONType::String); + ASSERT_VALUE_CONTENTS_AND_TYPE( + convertExp->evaluate(doubleInfinity, &expCtx->variables), "X"_sd, BSONType::String); Document doubleNegativeInfinity{{"path1", -std::numeric_limits::infinity()}}; ASSERT_VALUE_CONTENTS_AND_TYPE( - convertExp->evaluate(doubleNegativeInfinity), "X"_sd, BSONType::String); + convertExp->evaluate(doubleNegativeInfinity, &expCtx->variables), "X"_sd, BSONType::String); Document decimalOverflowInput{{"path1", Decimal128("1.0e100")}}; ASSERT_VALUE_CONTENTS_AND_TYPE( - convertExp->evaluate(decimalOverflowInput), "X"_sd, BSONType::String); + convertExp->evaluate(decimalOverflowInput, &expCtx->variables), "X"_sd, BSONType::String); Document decimalNegativeOverflowInput{{"path1", Decimal128("1.0e100")}}; ASSERT_VALUE_CONTENTS_AND_TYPE( - convertExp->evaluate(decimalNegativeOverflowInput), "X"_sd, BSONType::String); + convertExp->evaluate(decimalNegativeOverflowInput, &expCtx->variables), + "X"_sd, + BSONType::String); Document decimalNaN{{"path1", Decimal128::kPositiveNaN}}; - ASSERT_VALUE_CONTENTS_AND_TYPE(convertExp->evaluate(decimalNaN), "X"_sd, BSONType::String); + ASSERT_VALUE_CONTENTS_AND_TYPE( + convertExp->evaluate(decimalNaN, &expCtx->variables), "X"_sd, BSONType::String); Document decimalNegativeNaN{{"path1", Decimal128::kNegativeNaN}}; ASSERT_VALUE_CONTENTS_AND_TYPE( - convertExp->evaluate(decimalNegativeNaN), "X"_sd, BSONType::String); + convertExp->evaluate(decimalNegativeNaN, &expCtx->variables), "X"_sd, BSONType::String); Document decimalInfinity{{"path1", Decimal128::kPositiveInfinity}}; - ASSERT_VALUE_CONTENTS_AND_TYPE(convertExp->evaluate(decimalInfinity), "X"_sd, BSONType::String); + ASSERT_VALUE_CONTENTS_AND_TYPE( + convertExp->evaluate(decimalInfinity, &expCtx->variables), "X"_sd, BSONType::String); Document decimalNegativeInfinity{{"path1", Decimal128::kNegativeInfinity}}; ASSERT_VALUE_CONTENTS_AND_TYPE( - convertExp->evaluate(decimalNegativeInfinity), "X"_sd, BSONType::String); + convertExp->evaluate(decimalNegativeInfinity, &expCtx->variables), + "X"_sd, + BSONType::String); } TEST_F(ExpressionConvertTest, ConvertObjectIdToDate) { @@ -1944,7 +2064,7 @@ TEST_F(ExpressionConvertTest, ConvertObjectIdToDate) { Document oidInput{{"path1", OID("59E8A8D8FEDCBA9876543210")}}; - ASSERT_EQ(dateToISOStringUTC(convertExp->evaluate(oidInput).getDate()), + ASSERT_EQ(dateToISOStringUTC(convertExp->evaluate(oidInput, &expCtx->variables).getDate()), "2017-10-19T13:30:00.000Z"); } @@ -1953,11 +2073,13 @@ TEST_F(ExpressionConvertTest, ConvertStringToInt) { auto spec = fromjson("{$convert: {input: '5', to: 'int'}}"); auto convertExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); - ASSERT_VALUE_CONTENTS_AND_TYPE(convertExp->evaluate({}), 5, BSONType::NumberInt); + ASSERT_VALUE_CONTENTS_AND_TYPE( + convertExp->evaluate({}, &expCtx->variables), 5, BSONType::NumberInt); spec = fromjson("{$convert: {input: '" + std::to_string(kIntMax) + "', to: 'int'}}"); convertExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); - ASSERT_VALUE_CONTENTS_AND_TYPE(convertExp->evaluate({}), kIntMax, BSONType::NumberInt); + ASSERT_VALUE_CONTENTS_AND_TYPE( + convertExp->evaluate({}, &expCtx->variables), kIntMax, BSONType::NumberInt); } TEST_F(ExpressionConvertTest, ConvertStringToIntOverflow) { @@ -1965,19 +2087,21 @@ TEST_F(ExpressionConvertTest, ConvertStringToIntOverflow) { auto spec = fromjson("{$convert: {input: '" + std::to_string(kIntMax + 1) + "', to: 'int'}}"); auto convertExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); - ASSERT_THROWS_WITH_CHECK( - convertExp->evaluate({}), AssertionException, [](const AssertionException& exception) { - ASSERT_EQ(exception.code(), ErrorCodes::ConversionFailure); - ASSERT_STRING_CONTAINS(exception.reason(), "Overflow"); - }); + ASSERT_THROWS_WITH_CHECK(convertExp->evaluate({}, &expCtx->variables), + AssertionException, + [](const AssertionException& exception) { + ASSERT_EQ(exception.code(), ErrorCodes::ConversionFailure); + ASSERT_STRING_CONTAINS(exception.reason(), "Overflow"); + }); spec = fromjson("{$convert: {input: '" + std::to_string(kIntMin - 1) + "', to: 'int'}}"); convertExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); - ASSERT_THROWS_WITH_CHECK( - convertExp->evaluate({}), AssertionException, [](const AssertionException& exception) { - ASSERT_EQ(exception.code(), ErrorCodes::ConversionFailure); - ASSERT_STRING_CONTAINS(exception.reason(), "Overflow"); - }); + ASSERT_THROWS_WITH_CHECK(convertExp->evaluate({}, &expCtx->variables), + AssertionException, + [](const AssertionException& exception) { + ASSERT_EQ(exception.code(), ErrorCodes::ConversionFailure); + ASSERT_STRING_CONTAINS(exception.reason(), "Overflow"); + }); } TEST_F(ExpressionConvertTest, ConvertStringToIntOverflowWithOnError) { @@ -1987,12 +2111,14 @@ TEST_F(ExpressionConvertTest, ConvertStringToIntOverflowWithOnError) { auto spec = fromjson("{$convert: {input: '" + std::to_string(kIntMax + 1) + "', to: 'int', onError: '" + onErrorValue + "'}}"); auto convertExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); - ASSERT_VALUE_CONTENTS_AND_TYPE(convertExp->evaluate({}), onErrorValue, BSONType::String); + ASSERT_VALUE_CONTENTS_AND_TYPE( + convertExp->evaluate({}, &expCtx->variables), onErrorValue, BSONType::String); spec = fromjson("{$convert: {input: '" + std::to_string(kIntMin - 1) + "', to: 'int', onError: '" + onErrorValue + "'}}"); convertExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); - ASSERT_VALUE_CONTENTS_AND_TYPE(convertExp->evaluate({}), onErrorValue, BSONType::String); + ASSERT_VALUE_CONTENTS_AND_TYPE( + convertExp->evaluate({}, &expCtx->variables), onErrorValue, BSONType::String); } TEST_F(ExpressionConvertTest, ConvertStringToLong) { @@ -2000,11 +2126,13 @@ TEST_F(ExpressionConvertTest, ConvertStringToLong) { auto spec = fromjson("{$convert: {input: '5', to: 'long'}}"); auto convertExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); - ASSERT_VALUE_CONTENTS_AND_TYPE(convertExp->evaluate({}), 5LL, BSONType::NumberLong); + ASSERT_VALUE_CONTENTS_AND_TYPE( + convertExp->evaluate({}, &expCtx->variables), 5LL, BSONType::NumberLong); spec = fromjson("{$convert: {input: '" + std::to_string(kLongMax) + "', to: 'long'}}"); convertExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); - ASSERT_VALUE_CONTENTS_AND_TYPE(convertExp->evaluate({}), kLongMax, BSONType::NumberLong); + ASSERT_VALUE_CONTENTS_AND_TYPE( + convertExp->evaluate({}, &expCtx->variables), kLongMax, BSONType::NumberLong); } TEST_F(ExpressionConvertTest, ConvertStringToLongOverflow) { @@ -2015,22 +2143,24 @@ TEST_F(ExpressionConvertTest, ConvertStringToLongOverflow) { auto spec = fromjson("{$convert: {input: '" + longMaxPlusOneAsString + "', to: 'long'}}"); auto convertExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); - ASSERT_THROWS_WITH_CHECK( - convertExp->evaluate({}), AssertionException, [](const AssertionException& exception) { - ASSERT_EQ(exception.code(), ErrorCodes::ConversionFailure); - ASSERT_STRING_CONTAINS(exception.reason(), "Overflow"); - }); + ASSERT_THROWS_WITH_CHECK(convertExp->evaluate({}, &expCtx->variables), + AssertionException, + [](const AssertionException& exception) { + ASSERT_EQ(exception.code(), ErrorCodes::ConversionFailure); + ASSERT_STRING_CONTAINS(exception.reason(), "Overflow"); + }); auto longMinMinusOneAsString = std::to_string(kLongNegativeOverflow); longMinMinusOneAsString = longMinMinusOneAsString.substr(0, longMinMinusOneAsString.find('.')); spec = fromjson("{$convert: {input: '" + longMinMinusOneAsString + "', to: 'long'}}"); convertExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); - ASSERT_THROWS_WITH_CHECK( - convertExp->evaluate({}), AssertionException, [](const AssertionException& exception) { - ASSERT_EQ(exception.code(), ErrorCodes::ConversionFailure); - ASSERT_STRING_CONTAINS(exception.reason(), "Overflow"); - }); + ASSERT_THROWS_WITH_CHECK(convertExp->evaluate({}, &expCtx->variables), + AssertionException, + [](const AssertionException& exception) { + ASSERT_EQ(exception.code(), ErrorCodes::ConversionFailure); + ASSERT_STRING_CONTAINS(exception.reason(), "Overflow"); + }); } TEST_F(ExpressionConvertTest, ConvertStringToLongFailsForFloats) { @@ -2038,19 +2168,21 @@ TEST_F(ExpressionConvertTest, ConvertStringToLongFailsForFloats) { auto spec = fromjson("{$convert: {input: '5.5', to: 'long'}}"); auto convertExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); - ASSERT_THROWS_WITH_CHECK( - convertExp->evaluate({}), AssertionException, [](const AssertionException& exception) { - ASSERT_EQ(exception.code(), ErrorCodes::ConversionFailure); - ASSERT_STRING_CONTAINS(exception.reason(), "Bad digit \".\""); - }); + ASSERT_THROWS_WITH_CHECK(convertExp->evaluate({}, &expCtx->variables), + AssertionException, + [](const AssertionException& exception) { + ASSERT_EQ(exception.code(), ErrorCodes::ConversionFailure); + ASSERT_STRING_CONTAINS(exception.reason(), "Bad digit \".\""); + }); spec = fromjson("{$convert: {input: '5.0', to: 'long'}}"); convertExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); - ASSERT_THROWS_WITH_CHECK( - convertExp->evaluate({}), AssertionException, [](const AssertionException& exception) { - ASSERT_EQ(exception.code(), ErrorCodes::ConversionFailure); - ASSERT_STRING_CONTAINS(exception.reason(), "Bad digit \".\""); - }); + ASSERT_THROWS_WITH_CHECK(convertExp->evaluate({}, &expCtx->variables), + AssertionException, + [](const AssertionException& exception) { + ASSERT_EQ(exception.code(), ErrorCodes::ConversionFailure); + ASSERT_STRING_CONTAINS(exception.reason(), "Bad digit \".\""); + }); } TEST_F(ExpressionConvertTest, ConvertStringToLongWithOnError) { @@ -2063,7 +2195,8 @@ TEST_F(ExpressionConvertTest, ConvertStringToLongWithOnError) { auto spec = fromjson("{$convert: {input: '" + longMaxPlusOneAsString + "', to: 'long', onError: '" + onErrorValue + "'}}"); auto convertExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); - ASSERT_VALUE_CONTENTS_AND_TYPE(convertExp->evaluate({}), onErrorValue, BSONType::String); + ASSERT_VALUE_CONTENTS_AND_TYPE( + convertExp->evaluate({}, &expCtx->variables), onErrorValue, BSONType::String); auto longMinMinusOneAsString = std::to_string(kLongNegativeOverflow); longMinMinusOneAsString = longMinMinusOneAsString.substr(0, longMinMinusOneAsString.find('.')); @@ -2071,15 +2204,18 @@ TEST_F(ExpressionConvertTest, ConvertStringToLongWithOnError) { spec = fromjson("{$convert: {input: '" + longMinMinusOneAsString + "', to: 'long', onError: '" + onErrorValue + "'}}"); convertExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); - ASSERT_VALUE_CONTENTS_AND_TYPE(convertExp->evaluate({}), onErrorValue, BSONType::String); + ASSERT_VALUE_CONTENTS_AND_TYPE( + convertExp->evaluate({}, &expCtx->variables), onErrorValue, BSONType::String); spec = fromjson("{$convert: {input: '5.5', to: 'long', onError: '" + onErrorValue + "'}}"); convertExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); - ASSERT_VALUE_CONTENTS_AND_TYPE(convertExp->evaluate({}), onErrorValue, BSONType::String); + ASSERT_VALUE_CONTENTS_AND_TYPE( + convertExp->evaluate({}, &expCtx->variables), onErrorValue, BSONType::String); spec = fromjson("{$convert: {input: '5.0', to: 'long', onError: '" + onErrorValue + "'}}"); convertExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); - ASSERT_VALUE_CONTENTS_AND_TYPE(convertExp->evaluate({}), onErrorValue, BSONType::String); + ASSERT_VALUE_CONTENTS_AND_TYPE( + convertExp->evaluate({}, &expCtx->variables), onErrorValue, BSONType::String); } TEST_F(ExpressionConvertTest, ConvertStringToDouble) { @@ -2087,23 +2223,28 @@ TEST_F(ExpressionConvertTest, ConvertStringToDouble) { auto spec = fromjson("{$convert: {input: '5', to: 'double'}}"); auto convertExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); - ASSERT_VALUE_CONTENTS_AND_TYPE(convertExp->evaluate({}), 5.0, BSONType::NumberDouble); + ASSERT_VALUE_CONTENTS_AND_TYPE( + convertExp->evaluate({}, &expCtx->variables), 5.0, BSONType::NumberDouble); spec = fromjson("{$convert: {input: '5.5', to: 'double'}}"); convertExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); - ASSERT_VALUE_CONTENTS_AND_TYPE(convertExp->evaluate({}), 5.5, BSONType::NumberDouble); + ASSERT_VALUE_CONTENTS_AND_TYPE( + convertExp->evaluate({}, &expCtx->variables), 5.5, BSONType::NumberDouble); spec = fromjson("{$convert: {input: '.5', to: 'double'}}"); convertExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); - ASSERT_VALUE_CONTENTS_AND_TYPE(convertExp->evaluate({}), 0.5, BSONType::NumberDouble); + ASSERT_VALUE_CONTENTS_AND_TYPE( + convertExp->evaluate({}, &expCtx->variables), 0.5, BSONType::NumberDouble); spec = fromjson("{$convert: {input: '+5', to: 'double'}}"); convertExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); - ASSERT_VALUE_CONTENTS_AND_TYPE(convertExp->evaluate({}), 5.0, BSONType::NumberDouble); + ASSERT_VALUE_CONTENTS_AND_TYPE( + convertExp->evaluate({}, &expCtx->variables), 5.0, BSONType::NumberDouble); spec = fromjson("{$convert: {input: '+5.0e42', to: 'double'}}"); convertExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); - ASSERT_VALUE_CONTENTS_AND_TYPE(convertExp->evaluate({}), 5.0e42, BSONType::NumberDouble); + ASSERT_VALUE_CONTENTS_AND_TYPE( + convertExp->evaluate({}, &expCtx->variables), 5.0e42, BSONType::NumberDouble); } TEST_F(ExpressionConvertTest, ConvertStringToDoubleWithPrecisionLoss) { @@ -2113,12 +2254,14 @@ TEST_F(ExpressionConvertTest, ConvertStringToDoubleWithPrecisionLoss) { // wide enough for the given input string in its entirety. auto spec = fromjson("{$convert: {input: '10000000000000000001', to: 'double'}}"); auto convertExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); - ASSERT_VALUE_CONTENTS_AND_TYPE(convertExp->evaluate({}), 1e19, BSONType::NumberDouble); + ASSERT_VALUE_CONTENTS_AND_TYPE( + convertExp->evaluate({}, &expCtx->variables), 1e19, BSONType::NumberDouble); // Again, some precision is lost in the conversion to double. spec = fromjson("{$convert: {input: '1.125000000000000000005', to: 'double'}}"); convertExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); - ASSERT_VALUE_CONTENTS_AND_TYPE(convertExp->evaluate({}), 1.125, BSONType::NumberDouble); + ASSERT_VALUE_CONTENTS_AND_TYPE( + convertExp->evaluate({}, &expCtx->variables), 1.125, BSONType::NumberDouble); } TEST_F(ExpressionConvertTest, ConvertStringToDoubleFailsForInvalidFloats) { @@ -2126,19 +2269,23 @@ TEST_F(ExpressionConvertTest, ConvertStringToDoubleFailsForInvalidFloats) { auto spec = fromjson("{$convert: {input: '.5.', to: 'double'}}"); auto convertExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); - ASSERT_THROWS_WITH_CHECK( - convertExp->evaluate({}), AssertionException, [](const AssertionException& exception) { - ASSERT_EQ(exception.code(), ErrorCodes::ConversionFailure); - ASSERT_STRING_CONTAINS(exception.reason(), "Did not consume whole number"); - }); + ASSERT_THROWS_WITH_CHECK(convertExp->evaluate({}, &expCtx->variables), + AssertionException, + [](const AssertionException& exception) { + ASSERT_EQ(exception.code(), ErrorCodes::ConversionFailure); + ASSERT_STRING_CONTAINS(exception.reason(), + "Did not consume whole number"); + }); spec = fromjson("{$convert: {input: '5.5f', to: 'double'}}"); convertExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); - ASSERT_THROWS_WITH_CHECK( - convertExp->evaluate({}), AssertionException, [](const AssertionException& exception) { - ASSERT_EQ(exception.code(), ErrorCodes::ConversionFailure); - ASSERT_STRING_CONTAINS(exception.reason(), "Did not consume whole number"); - }); + ASSERT_THROWS_WITH_CHECK(convertExp->evaluate({}, &expCtx->variables), + AssertionException, + [](const AssertionException& exception) { + ASSERT_EQ(exception.code(), ErrorCodes::ConversionFailure); + ASSERT_STRING_CONTAINS(exception.reason(), + "Did not consume whole number"); + }); } TEST_F(ExpressionConvertTest, ConvertInfinityStringsToDouble) { @@ -2147,39 +2294,48 @@ TEST_F(ExpressionConvertTest, ConvertInfinityStringsToDouble) { auto spec = fromjson("{$convert: {input: 'Infinity', to: 'double'}}"); auto convertExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); - ASSERT_VALUE_CONTENTS_AND_TYPE(convertExp->evaluate({}), infValue, BSONType::NumberDouble); + ASSERT_VALUE_CONTENTS_AND_TYPE( + convertExp->evaluate({}, &expCtx->variables), infValue, BSONType::NumberDouble); spec = fromjson("{$convert: {input: 'INF', to: 'double'}}"); convertExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); - ASSERT_VALUE_CONTENTS_AND_TYPE(convertExp->evaluate({}), infValue, BSONType::NumberDouble); + ASSERT_VALUE_CONTENTS_AND_TYPE( + convertExp->evaluate({}, &expCtx->variables), infValue, BSONType::NumberDouble); spec = fromjson("{$convert: {input: 'infinity', to: 'double'}}"); convertExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); - ASSERT_VALUE_CONTENTS_AND_TYPE(convertExp->evaluate({}), infValue, BSONType::NumberDouble); + ASSERT_VALUE_CONTENTS_AND_TYPE( + convertExp->evaluate({}, &expCtx->variables), infValue, BSONType::NumberDouble); spec = fromjson("{$convert: {input: '+InFiNiTy', to: 'double'}}"); convertExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); - ASSERT_VALUE_CONTENTS_AND_TYPE(convertExp->evaluate({}), infValue, BSONType::NumberDouble); + ASSERT_VALUE_CONTENTS_AND_TYPE( + convertExp->evaluate({}, &expCtx->variables), infValue, BSONType::NumberDouble); spec = fromjson("{$convert: {input: '-Infinity', to: 'double'}}"); convertExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); - ASSERT_VALUE_CONTENTS_AND_TYPE(convertExp->evaluate({}), -infValue, BSONType::NumberDouble); + ASSERT_VALUE_CONTENTS_AND_TYPE( + convertExp->evaluate({}, &expCtx->variables), -infValue, BSONType::NumberDouble); spec = fromjson("{$convert: {input: '-INF', to: 'double'}}"); convertExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); - ASSERT_VALUE_CONTENTS_AND_TYPE(convertExp->evaluate({}), -infValue, BSONType::NumberDouble); + ASSERT_VALUE_CONTENTS_AND_TYPE( + convertExp->evaluate({}, &expCtx->variables), -infValue, BSONType::NumberDouble); spec = fromjson("{$convert: {input: '-InFiNiTy', to: 'double'}}"); convertExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); - ASSERT_VALUE_CONTENTS_AND_TYPE(convertExp->evaluate({}), -infValue, BSONType::NumberDouble); + ASSERT_VALUE_CONTENTS_AND_TYPE( + convertExp->evaluate({}, &expCtx->variables), -infValue, BSONType::NumberDouble); spec = fromjson("{$convert: {input: '-inf', to: 'double'}}"); convertExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); - ASSERT_VALUE_CONTENTS_AND_TYPE(convertExp->evaluate({}), -infValue, BSONType::NumberDouble); + ASSERT_VALUE_CONTENTS_AND_TYPE( + convertExp->evaluate({}, &expCtx->variables), -infValue, BSONType::NumberDouble); spec = fromjson("{$convert: {input: '-infinity', to: 'double'}}"); convertExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); - ASSERT_VALUE_CONTENTS_AND_TYPE(convertExp->evaluate({}), -infValue, BSONType::NumberDouble); + ASSERT_VALUE_CONTENTS_AND_TYPE( + convertExp->evaluate({}, &expCtx->variables), -infValue, BSONType::NumberDouble); } TEST_F(ExpressionConvertTest, ConvertZeroStringsToDouble) { @@ -2187,25 +2343,25 @@ TEST_F(ExpressionConvertTest, ConvertZeroStringsToDouble) { auto spec = fromjson("{$convert: {input: '-0', to: 'double'}}"); auto convertExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); - auto result = convertExp->evaluate({}); + auto result = convertExp->evaluate({}, &expCtx->variables); ASSERT_VALUE_CONTENTS_AND_TYPE(result, 0, BSONType::NumberDouble); ASSERT_TRUE(std::signbit(result.getDouble())); spec = fromjson("{$convert: {input: '-0.0', to: 'double'}}"); convertExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); - result = convertExp->evaluate({}); + result = convertExp->evaluate({}, &expCtx->variables); ASSERT_VALUE_CONTENTS_AND_TYPE(result, 0, BSONType::NumberDouble); ASSERT_TRUE(std::signbit(result.getDouble())); spec = fromjson("{$convert: {input: '+0', to: 'double'}}"); convertExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); - result = convertExp->evaluate({}); + result = convertExp->evaluate({}, &expCtx->variables); ASSERT_VALUE_CONTENTS_AND_TYPE(result, 0, BSONType::NumberDouble); ASSERT_FALSE(std::signbit(result.getDouble())); spec = fromjson("{$convert: {input: '+0.0', to: 'double'}}"); convertExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); - result = convertExp->evaluate({}); + result = convertExp->evaluate({}, &expCtx->variables); ASSERT_VALUE_CONTENTS_AND_TYPE(result, 0, BSONType::NumberDouble); ASSERT_FALSE(std::signbit(result.getDouble())); } @@ -2216,28 +2372,29 @@ TEST_F(ExpressionConvertTest, ConvertNanStringsToDouble) { auto spec = fromjson("{$convert: {input: 'nan', to: 'double'}}"); auto convertExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); - auto result = convertExp->evaluate({}); + auto result = convertExp->evaluate({}, &expCtx->variables); ASSERT_TRUE(std::isnan(result.getDouble())); - ASSERT_VALUE_CONTENTS_AND_TYPE(convertExp->evaluate({}), nanValue, BSONType::NumberDouble); + ASSERT_VALUE_CONTENTS_AND_TYPE( + convertExp->evaluate({}, &expCtx->variables), nanValue, BSONType::NumberDouble); spec = fromjson("{$convert: {input: 'Nan', to: 'double'}}"); convertExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); - result = convertExp->evaluate({}); + result = convertExp->evaluate({}, &expCtx->variables); ASSERT_TRUE(std::isnan(result.getDouble())); spec = fromjson("{$convert: {input: 'NaN', to: 'double'}}"); convertExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); - result = convertExp->evaluate({}); + result = convertExp->evaluate({}, &expCtx->variables); ASSERT_TRUE(std::isnan(result.getDouble())); spec = fromjson("{$convert: {input: '-NAN', to: 'double'}}"); convertExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); - result = convertExp->evaluate({}); + result = convertExp->evaluate({}, &expCtx->variables); ASSERT_TRUE(std::isnan(result.getDouble())); spec = fromjson("{$convert: {input: '+NaN', to: 'double'}}"); convertExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); - result = convertExp->evaluate({}); + result = convertExp->evaluate({}, &expCtx->variables); ASSERT_TRUE(std::isnan(result.getDouble())); } @@ -2246,20 +2403,22 @@ TEST_F(ExpressionConvertTest, ConvertStringToDoubleOverflow) { auto spec = fromjson("{$convert: {input: '" + kDoubleOverflow.toString() + "', to: 'double'}}"); auto convertExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); - ASSERT_THROWS_WITH_CHECK( - convertExp->evaluate({}), AssertionException, [](const AssertionException& exception) { - ASSERT_EQ(exception.code(), ErrorCodes::ConversionFailure); - ASSERT_STRING_CONTAINS(exception.reason(), "Out of range"); - }); + ASSERT_THROWS_WITH_CHECK(convertExp->evaluate({}, &expCtx->variables), + AssertionException, + [](const AssertionException& exception) { + ASSERT_EQ(exception.code(), ErrorCodes::ConversionFailure); + ASSERT_STRING_CONTAINS(exception.reason(), "Out of range"); + }); spec = fromjson("{$convert: {input: '" + kDoubleNegativeOverflow.toString() + "', to: 'double'}}"); convertExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); - ASSERT_THROWS_WITH_CHECK( - convertExp->evaluate({}), AssertionException, [](const AssertionException& exception) { - ASSERT_EQ(exception.code(), ErrorCodes::ConversionFailure); - ASSERT_STRING_CONTAINS(exception.reason(), "Out of range"); - }); + ASSERT_THROWS_WITH_CHECK(convertExp->evaluate({}, &expCtx->variables), + AssertionException, + [](const AssertionException& exception) { + ASSERT_EQ(exception.code(), ErrorCodes::ConversionFailure); + ASSERT_STRING_CONTAINS(exception.reason(), "Out of range"); + }); } TEST_F(ExpressionConvertTest, ConvertStringToDoubleUnderflow) { @@ -2268,7 +2427,9 @@ TEST_F(ExpressionConvertTest, ConvertStringToDoubleUnderflow) { auto spec = fromjson("{$convert: {input: '1E-1000', to: 'double'}}"); auto convertExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); ASSERT_THROWS_WITH_CHECK( - convertExp->evaluate({}), AssertionException, [](const AssertionException& exception) { + convertExp->evaluate({}, &expCtx->variables), + AssertionException, + [](const AssertionException& exception) { ASSERT_EQ(exception.code(), ErrorCodes::ConversionFailure); ASSERT_STRING_CONTAINS( exception.reason(), @@ -2278,7 +2439,9 @@ TEST_F(ExpressionConvertTest, ConvertStringToDoubleUnderflow) { spec = fromjson("{$convert: {input: '-1E-1000', to: 'double'}}"); convertExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); ASSERT_THROWS_WITH_CHECK( - convertExp->evaluate({}), AssertionException, [](const AssertionException& exception) { + convertExp->evaluate({}, &expCtx->variables), + AssertionException, + [](const AssertionException& exception) { ASSERT_EQ(exception.code(), ErrorCodes::ConversionFailure); ASSERT_STRING_CONTAINS(exception.reason(), "Failed to parse number '-1E-1000' in $convert with no onError " @@ -2293,20 +2456,24 @@ TEST_F(ExpressionConvertTest, ConvertStringToDoubleWithOnError) { auto spec = fromjson("{$convert: {input: '" + kDoubleOverflow.toString() + "', to: 'double', onError: '" + onErrorValue + "'}}"); auto convertExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); - ASSERT_VALUE_CONTENTS_AND_TYPE(convertExp->evaluate({}), onErrorValue, BSONType::String); + ASSERT_VALUE_CONTENTS_AND_TYPE( + convertExp->evaluate({}, &expCtx->variables), onErrorValue, BSONType::String); spec = fromjson("{$convert: {input: '" + kDoubleNegativeOverflow.toString() + "', to: 'double', onError: '" + onErrorValue + "'}}"); convertExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); - ASSERT_VALUE_CONTENTS_AND_TYPE(convertExp->evaluate({}), onErrorValue, BSONType::String); + ASSERT_VALUE_CONTENTS_AND_TYPE( + convertExp->evaluate({}, &expCtx->variables), onErrorValue, BSONType::String); spec = fromjson("{$convert: {input: '.5.', to: 'double', onError: '" + onErrorValue + "'}}"); convertExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); - ASSERT_VALUE_CONTENTS_AND_TYPE(convertExp->evaluate({}), onErrorValue, BSONType::String); + ASSERT_VALUE_CONTENTS_AND_TYPE( + convertExp->evaluate({}, &expCtx->variables), onErrorValue, BSONType::String); spec = fromjson("{$convert: {input: '5.5f', to: 'double', onError: '" + onErrorValue + "'}}"); convertExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); - ASSERT_VALUE_CONTENTS_AND_TYPE(convertExp->evaluate({}), onErrorValue, BSONType::String); + ASSERT_VALUE_CONTENTS_AND_TYPE( + convertExp->evaluate({}, &expCtx->variables), onErrorValue, BSONType::String); } TEST_F(ExpressionConvertTest, ConvertStringToDecimal) { @@ -2314,29 +2481,33 @@ TEST_F(ExpressionConvertTest, ConvertStringToDecimal) { auto spec = fromjson("{$convert: {input: '5', to: 'decimal'}}"); auto convertExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); - ASSERT_VALUE_CONTENTS_AND_TYPE(convertExp->evaluate({}), 5, BSONType::NumberDecimal); + ASSERT_VALUE_CONTENTS_AND_TYPE( + convertExp->evaluate({}, &expCtx->variables), 5, BSONType::NumberDecimal); spec = fromjson("{$convert: {input: '2.02', to: 'decimal'}}"); convertExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); ASSERT_VALUE_CONTENTS_AND_TYPE( - convertExp->evaluate({}), Decimal128("2.02"), BSONType::NumberDecimal); + convertExp->evaluate({}, &expCtx->variables), Decimal128("2.02"), BSONType::NumberDecimal); spec = fromjson("{$convert: {input: '2.02E200', to: 'decimal'}}"); convertExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); - ASSERT_VALUE_CONTENTS_AND_TYPE( - convertExp->evaluate({}), Decimal128("2.02E200"), BSONType::NumberDecimal); + ASSERT_VALUE_CONTENTS_AND_TYPE(convertExp->evaluate({}, &expCtx->variables), + Decimal128("2.02E200"), + BSONType::NumberDecimal); spec = fromjson("{$convert: {input: '" + Decimal128::kLargestPositive.toString() + "', to: 'decimal'}}"); convertExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); - ASSERT_VALUE_CONTENTS_AND_TYPE( - convertExp->evaluate({}), Decimal128::kLargestPositive, BSONType::NumberDecimal); + ASSERT_VALUE_CONTENTS_AND_TYPE(convertExp->evaluate({}, &expCtx->variables), + Decimal128::kLargestPositive, + BSONType::NumberDecimal); spec = fromjson("{$convert: {input: '" + Decimal128::kLargestNegative.toString() + "', to: 'decimal'}}"); convertExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); - ASSERT_VALUE_CONTENTS_AND_TYPE( - convertExp->evaluate({}), Decimal128::kLargestNegative, BSONType::NumberDecimal); + ASSERT_VALUE_CONTENTS_AND_TYPE(convertExp->evaluate({}, &expCtx->variables), + Decimal128::kLargestNegative, + BSONType::NumberDecimal); } TEST_F(ExpressionConvertTest, ConvertInfinityStringsToDecimal) { @@ -2346,39 +2517,48 @@ TEST_F(ExpressionConvertTest, ConvertInfinityStringsToDecimal) { auto spec = fromjson("{$convert: {input: 'Infinity', to: 'decimal'}}"); auto convertExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); - ASSERT_VALUE_CONTENTS_AND_TYPE(convertExp->evaluate({}), infValue, BSONType::NumberDecimal); + ASSERT_VALUE_CONTENTS_AND_TYPE( + convertExp->evaluate({}, &expCtx->variables), infValue, BSONType::NumberDecimal); spec = fromjson("{$convert: {input: 'INF', to: 'decimal'}}"); convertExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); - ASSERT_VALUE_CONTENTS_AND_TYPE(convertExp->evaluate({}), infValue, BSONType::NumberDecimal); + ASSERT_VALUE_CONTENTS_AND_TYPE( + convertExp->evaluate({}, &expCtx->variables), infValue, BSONType::NumberDecimal); spec = fromjson("{$convert: {input: 'infinity', to: 'decimal'}}"); convertExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); - ASSERT_VALUE_CONTENTS_AND_TYPE(convertExp->evaluate({}), infValue, BSONType::NumberDecimal); + ASSERT_VALUE_CONTENTS_AND_TYPE( + convertExp->evaluate({}, &expCtx->variables), infValue, BSONType::NumberDecimal); spec = fromjson("{$convert: {input: '+InFiNiTy', to: 'decimal'}}"); convertExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); - ASSERT_VALUE_CONTENTS_AND_TYPE(convertExp->evaluate({}), infValue, BSONType::NumberDecimal); + ASSERT_VALUE_CONTENTS_AND_TYPE( + convertExp->evaluate({}, &expCtx->variables), infValue, BSONType::NumberDecimal); spec = fromjson("{$convert: {input: '-Infinity', to: 'decimal'}}"); convertExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); - ASSERT_VALUE_CONTENTS_AND_TYPE(convertExp->evaluate({}), negInfValue, BSONType::NumberDecimal); + ASSERT_VALUE_CONTENTS_AND_TYPE( + convertExp->evaluate({}, &expCtx->variables), negInfValue, BSONType::NumberDecimal); spec = fromjson("{$convert: {input: '-INF', to: 'decimal'}}"); convertExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); - ASSERT_VALUE_CONTENTS_AND_TYPE(convertExp->evaluate({}), negInfValue, BSONType::NumberDecimal); + ASSERT_VALUE_CONTENTS_AND_TYPE( + convertExp->evaluate({}, &expCtx->variables), negInfValue, BSONType::NumberDecimal); spec = fromjson("{$convert: {input: '-InFiNiTy', to: 'decimal'}}"); convertExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); - ASSERT_VALUE_CONTENTS_AND_TYPE(convertExp->evaluate({}), negInfValue, BSONType::NumberDecimal); + ASSERT_VALUE_CONTENTS_AND_TYPE( + convertExp->evaluate({}, &expCtx->variables), negInfValue, BSONType::NumberDecimal); spec = fromjson("{$convert: {input: '-inf', to: 'decimal'}}"); convertExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); - ASSERT_VALUE_CONTENTS_AND_TYPE(convertExp->evaluate({}), negInfValue, BSONType::NumberDecimal); + ASSERT_VALUE_CONTENTS_AND_TYPE( + convertExp->evaluate({}, &expCtx->variables), negInfValue, BSONType::NumberDecimal); spec = fromjson("{$convert: {input: '-infinity', to: 'decimal'}}"); convertExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); - ASSERT_VALUE_CONTENTS_AND_TYPE(convertExp->evaluate({}), negInfValue, BSONType::NumberDecimal); + ASSERT_VALUE_CONTENTS_AND_TYPE( + convertExp->evaluate({}, &expCtx->variables), negInfValue, BSONType::NumberDecimal); } TEST_F(ExpressionConvertTest, ConvertNanStringsToDecimal) { @@ -2388,31 +2568,38 @@ TEST_F(ExpressionConvertTest, ConvertNanStringsToDecimal) { auto spec = fromjson("{$convert: {input: 'nan', to: 'decimal'}}"); auto convertExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); - ASSERT_VALUE_CONTENTS_AND_TYPE(convertExp->evaluate({}), positiveNan, BSONType::NumberDecimal); + ASSERT_VALUE_CONTENTS_AND_TYPE( + convertExp->evaluate({}, &expCtx->variables), positiveNan, BSONType::NumberDecimal); spec = fromjson("{$convert: {input: 'Nan', to: 'decimal'}}"); convertExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); - ASSERT_VALUE_CONTENTS_AND_TYPE(convertExp->evaluate({}), positiveNan, BSONType::NumberDecimal); + ASSERT_VALUE_CONTENTS_AND_TYPE( + convertExp->evaluate({}, &expCtx->variables), positiveNan, BSONType::NumberDecimal); spec = fromjson("{$convert: {input: 'NaN', to: 'decimal'}}"); convertExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); - ASSERT_VALUE_CONTENTS_AND_TYPE(convertExp->evaluate({}), positiveNan, BSONType::NumberDecimal); + ASSERT_VALUE_CONTENTS_AND_TYPE( + convertExp->evaluate({}, &expCtx->variables), positiveNan, BSONType::NumberDecimal); spec = fromjson("{$convert: {input: '+NaN', to: 'decimal'}}"); convertExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); - ASSERT_VALUE_CONTENTS_AND_TYPE(convertExp->evaluate({}), positiveNan, BSONType::NumberDecimal); + ASSERT_VALUE_CONTENTS_AND_TYPE( + convertExp->evaluate({}, &expCtx->variables), positiveNan, BSONType::NumberDecimal); spec = fromjson("{$convert: {input: '-NAN', to: 'decimal'}}"); convertExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); - ASSERT_VALUE_CONTENTS_AND_TYPE(convertExp->evaluate({}), negativeNan, BSONType::NumberDecimal); + ASSERT_VALUE_CONTENTS_AND_TYPE( + convertExp->evaluate({}, &expCtx->variables), negativeNan, BSONType::NumberDecimal); spec = fromjson("{$convert: {input: '-nan', to: 'decimal'}}"); convertExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); - ASSERT_VALUE_CONTENTS_AND_TYPE(convertExp->evaluate({}), negativeNan, BSONType::NumberDecimal); + ASSERT_VALUE_CONTENTS_AND_TYPE( + convertExp->evaluate({}, &expCtx->variables), negativeNan, BSONType::NumberDecimal); spec = fromjson("{$convert: {input: '-NaN', to: 'decimal'}}"); convertExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); - ASSERT_VALUE_CONTENTS_AND_TYPE(convertExp->evaluate({}), negativeNan, BSONType::NumberDecimal); + ASSERT_VALUE_CONTENTS_AND_TYPE( + convertExp->evaluate({}, &expCtx->variables), negativeNan, BSONType::NumberDecimal); } TEST_F(ExpressionConvertTest, ConvertZeroStringsToDecimal) { @@ -2420,28 +2607,28 @@ TEST_F(ExpressionConvertTest, ConvertZeroStringsToDecimal) { auto spec = fromjson("{$convert: {input: '-0', to: 'decimal'}}"); auto convertExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); - auto result = convertExp->evaluate({}); + auto result = convertExp->evaluate({}, &expCtx->variables); ASSERT_VALUE_CONTENTS_AND_TYPE(result, 0, BSONType::NumberDecimal); ASSERT_TRUE(result.getDecimal().isZero()); ASSERT_TRUE(result.getDecimal().isNegative()); spec = fromjson("{$convert: {input: '-0.0', to: 'decimal'}}"); convertExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); - result = convertExp->evaluate({}); + result = convertExp->evaluate({}, &expCtx->variables); ASSERT_VALUE_CONTENTS_AND_TYPE(result, 0, BSONType::NumberDecimal); ASSERT_TRUE(result.getDecimal().isZero()); ASSERT_TRUE(result.getDecimal().isNegative()); spec = fromjson("{$convert: {input: '+0', to: 'decimal'}}"); convertExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); - result = convertExp->evaluate({}); + result = convertExp->evaluate({}, &expCtx->variables); ASSERT_VALUE_CONTENTS_AND_TYPE(result, 0, BSONType::NumberDecimal); ASSERT_TRUE(result.getDecimal().isZero()); ASSERT_FALSE(result.getDecimal().isNegative()); spec = fromjson("{$convert: {input: '+0.0', to: 'decimal'}}"); convertExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); - result = convertExp->evaluate({}); + result = convertExp->evaluate({}, &expCtx->variables); ASSERT_VALUE_CONTENTS_AND_TYPE(result, 0, BSONType::NumberDecimal); ASSERT_TRUE(result.getDecimal().isZero()); ASSERT_FALSE(result.getDecimal().isNegative()); @@ -2452,21 +2639,25 @@ TEST_F(ExpressionConvertTest, ConvertStringToDecimalOverflow) { auto spec = fromjson("{$convert: {input: '1E6145', to: 'decimal'}}"); auto convertExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); - ASSERT_THROWS_WITH_CHECK( - convertExp->evaluate({}), AssertionException, [](const AssertionException& exception) { - ASSERT_EQ(exception.code(), ErrorCodes::ConversionFailure); - ASSERT_STRING_CONTAINS(exception.reason(), - "Conversion from string to decimal would overflow"); - }); + ASSERT_THROWS_WITH_CHECK(convertExp->evaluate({}, &expCtx->variables), + AssertionException, + [](const AssertionException& exception) { + ASSERT_EQ(exception.code(), ErrorCodes::ConversionFailure); + ASSERT_STRING_CONTAINS( + exception.reason(), + "Conversion from string to decimal would overflow"); + }); spec = fromjson("{$convert: {input: '-1E6145', to: 'decimal'}}"); convertExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); - ASSERT_THROWS_WITH_CHECK( - convertExp->evaluate({}), AssertionException, [](const AssertionException& exception) { - ASSERT_EQ(exception.code(), ErrorCodes::ConversionFailure); - ASSERT_STRING_CONTAINS(exception.reason(), - "Conversion from string to decimal would overflow"); - }); + ASSERT_THROWS_WITH_CHECK(convertExp->evaluate({}, &expCtx->variables), + AssertionException, + [](const AssertionException& exception) { + ASSERT_EQ(exception.code(), ErrorCodes::ConversionFailure); + ASSERT_STRING_CONTAINS( + exception.reason(), + "Conversion from string to decimal would overflow"); + }); } TEST_F(ExpressionConvertTest, ConvertStringToDecimalUnderflow) { @@ -2474,21 +2665,25 @@ TEST_F(ExpressionConvertTest, ConvertStringToDecimalUnderflow) { auto spec = fromjson("{$convert: {input: '1E-6178', to: 'decimal'}}"); auto convertExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); - ASSERT_THROWS_WITH_CHECK( - convertExp->evaluate({}), AssertionException, [](const AssertionException& exception) { - ASSERT_EQ(exception.code(), ErrorCodes::ConversionFailure); - ASSERT_STRING_CONTAINS(exception.reason(), - "Conversion from string to decimal would underflow"); - }); + ASSERT_THROWS_WITH_CHECK(convertExp->evaluate({}, &expCtx->variables), + AssertionException, + [](const AssertionException& exception) { + ASSERT_EQ(exception.code(), ErrorCodes::ConversionFailure); + ASSERT_STRING_CONTAINS( + exception.reason(), + "Conversion from string to decimal would underflow"); + }); spec = fromjson("{$convert: {input: '-1E-6177', to: 'decimal'}}"); convertExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); - ASSERT_THROWS_WITH_CHECK( - convertExp->evaluate({}), AssertionException, [](const AssertionException& exception) { - ASSERT_EQ(exception.code(), ErrorCodes::ConversionFailure); - ASSERT_STRING_CONTAINS(exception.reason(), - "Conversion from string to decimal would underflow"); - }); + ASSERT_THROWS_WITH_CHECK(convertExp->evaluate({}, &expCtx->variables), + AssertionException, + [](const AssertionException& exception) { + ASSERT_EQ(exception.code(), ErrorCodes::ConversionFailure); + ASSERT_STRING_CONTAINS( + exception.reason(), + "Conversion from string to decimal would underflow"); + }); } TEST_F(ExpressionConvertTest, ConvertStringToDecimalWithPrecisionLoss) { @@ -2498,12 +2693,12 @@ TEST_F(ExpressionConvertTest, ConvertStringToDecimalWithPrecisionLoss) { fromjson("{$convert: {input: '10000000000000000000000000000000001', to: 'decimal'}}"); auto convertExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); ASSERT_VALUE_CONTENTS_AND_TYPE( - convertExp->evaluate({}), Decimal128("1e34"), BSONType::NumberDecimal); + convertExp->evaluate({}, &expCtx->variables), Decimal128("1e34"), BSONType::NumberDecimal); spec = fromjson("{$convert: {input: '1.1250000000000000000000000000000001', to: 'decimal'}}"); convertExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); ASSERT_VALUE_CONTENTS_AND_TYPE( - convertExp->evaluate({}), Decimal128("1.125"), BSONType::NumberDecimal); + convertExp->evaluate({}, &expCtx->variables), Decimal128("1.125"), BSONType::NumberDecimal); } TEST_F(ExpressionConvertTest, ConvertStringToDecimalWithOnError) { @@ -2513,12 +2708,14 @@ TEST_F(ExpressionConvertTest, ConvertStringToDecimalWithOnError) { auto spec = fromjson("{$convert: {input: '1E6145', to: 'decimal', onError: '" + onErrorValue + "'}}"); auto convertExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); - ASSERT_VALUE_CONTENTS_AND_TYPE(convertExp->evaluate({}), onErrorValue, BSONType::String); + ASSERT_VALUE_CONTENTS_AND_TYPE( + convertExp->evaluate({}, &expCtx->variables), onErrorValue, BSONType::String); spec = fromjson("{$convert: {input: '-1E-6177', to: 'decimal', onError: '" + onErrorValue + "'}}"); convertExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); - ASSERT_VALUE_CONTENTS_AND_TYPE(convertExp->evaluate({}), onErrorValue, BSONType::String); + ASSERT_VALUE_CONTENTS_AND_TYPE( + convertExp->evaluate({}, &expCtx->variables), onErrorValue, BSONType::String); } TEST_F(ExpressionConvertTest, ConvertStringToNumberFailsForHexStrings) { @@ -2530,43 +2727,53 @@ TEST_F(ExpressionConvertTest, ConvertStringToNumberFailsForHexStrings) { auto spec = fromjson("{$convert: {input: '0xFF', to: 'int'}}"); auto convertExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); - ASSERT_THROWS_WITH_CHECK(convertExp->evaluate({}), AssertionException, invalidHexFailure); + ASSERT_THROWS_WITH_CHECK( + convertExp->evaluate({}, &expCtx->variables), AssertionException, invalidHexFailure); spec = fromjson("{$convert: {input: '0xFF', to: 'long'}}"); convertExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); - ASSERT_THROWS_WITH_CHECK(convertExp->evaluate({}), AssertionException, invalidHexFailure); + ASSERT_THROWS_WITH_CHECK( + convertExp->evaluate({}, &expCtx->variables), AssertionException, invalidHexFailure); spec = fromjson("{$convert: {input: '0xFF', to: 'double'}}"); convertExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); - ASSERT_THROWS_WITH_CHECK(convertExp->evaluate({}), AssertionException, invalidHexFailure); + ASSERT_THROWS_WITH_CHECK( + convertExp->evaluate({}, &expCtx->variables), AssertionException, invalidHexFailure); spec = fromjson("{$convert: {input: '0xFF', to: 'decimal'}}"); convertExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); - ASSERT_THROWS_WITH_CHECK(convertExp->evaluate({}), AssertionException, invalidHexFailure); + ASSERT_THROWS_WITH_CHECK( + convertExp->evaluate({}, &expCtx->variables), AssertionException, invalidHexFailure); spec = fromjson("{$convert: {input: '0x00', to: 'int'}}"); convertExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); - ASSERT_THROWS_WITH_CHECK(convertExp->evaluate({}), AssertionException, invalidHexFailure); + ASSERT_THROWS_WITH_CHECK( + convertExp->evaluate({}, &expCtx->variables), AssertionException, invalidHexFailure); spec = fromjson("{$convert: {input: '0x00', to: 'decimal'}}"); convertExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); - ASSERT_THROWS_WITH_CHECK(convertExp->evaluate({}), AssertionException, invalidHexFailure); + ASSERT_THROWS_WITH_CHECK( + convertExp->evaluate({}, &expCtx->variables), AssertionException, invalidHexFailure); spec = fromjson("{$convert: {input: 'FF', to: 'double'}}"); convertExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); - ASSERT_THROWS_WITH_CHECK( - convertExp->evaluate({}), AssertionException, [](const AssertionException& exception) { - ASSERT_EQ(exception.code(), ErrorCodes::ConversionFailure); - ASSERT_STRING_CONTAINS(exception.reason(), "Did not consume whole number"); - }); + ASSERT_THROWS_WITH_CHECK(convertExp->evaluate({}, &expCtx->variables), + AssertionException, + [](const AssertionException& exception) { + ASSERT_EQ(exception.code(), ErrorCodes::ConversionFailure); + ASSERT_STRING_CONTAINS(exception.reason(), + "Did not consume whole number"); + }); spec = fromjson("{$convert: {input: 'FF', to: 'decimal'}}"); convertExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); - ASSERT_THROWS_WITH_CHECK( - convertExp->evaluate({}), AssertionException, [](const AssertionException& exception) { - ASSERT_EQ(exception.code(), ErrorCodes::ConversionFailure); - ASSERT_STRING_CONTAINS(exception.reason(), "Failed to parse string to decimal"); - }); + ASSERT_THROWS_WITH_CHECK(convertExp->evaluate({}, &expCtx->variables), + AssertionException, + [](const AssertionException& exception) { + ASSERT_EQ(exception.code(), ErrorCodes::ConversionFailure); + ASSERT_STRING_CONTAINS(exception.reason(), + "Failed to parse string to decimal"); + }); } TEST_F(ExpressionConvertTest, ConvertStringToOID) { @@ -2575,12 +2782,14 @@ TEST_F(ExpressionConvertTest, ConvertStringToOID) { auto spec = fromjson("{$convert: {input: '" + oid.toString() + "', to: 'objectId'}}"); auto convertExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); - ASSERT_VALUE_CONTENTS_AND_TYPE(convertExp->evaluate({}), oid, BSONType::jstOID); + ASSERT_VALUE_CONTENTS_AND_TYPE( + convertExp->evaluate({}, &expCtx->variables), oid, BSONType::jstOID); spec = fromjson("{$convert: {input: '123456789abcdef123456789', to: 'objectId'}}"); convertExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); - ASSERT_VALUE_CONTENTS_AND_TYPE( - convertExp->evaluate({}), OID("123456789abcdef123456789"), BSONType::jstOID); + ASSERT_VALUE_CONTENTS_AND_TYPE(convertExp->evaluate({}, &expCtx->variables), + OID("123456789abcdef123456789"), + BSONType::jstOID); } TEST_F(ExpressionConvertTest, ConvertStringToOIDFailsForInvalidHexStrings) { @@ -2588,27 +2797,33 @@ TEST_F(ExpressionConvertTest, ConvertStringToOIDFailsForInvalidHexStrings) { auto spec = fromjson("{$convert: {input: 'InvalidHexButSizeCorrect', to: 'objectId'}}"); auto convertExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); - ASSERT_THROWS_WITH_CHECK( - convertExp->evaluate({}), AssertionException, [](const AssertionException& exception) { - ASSERT_EQ(exception.code(), ErrorCodes::ConversionFailure); - ASSERT_STRING_CONTAINS(exception.reason(), "Invalid character found in hex string"); - }); + ASSERT_THROWS_WITH_CHECK(convertExp->evaluate({}, &expCtx->variables), + AssertionException, + [](const AssertionException& exception) { + ASSERT_EQ(exception.code(), ErrorCodes::ConversionFailure); + ASSERT_STRING_CONTAINS(exception.reason(), + "Invalid character found in hex string"); + }); spec = fromjson("{$convert: {input: 'InvalidSize', to: 'objectId'}}"); convertExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); - ASSERT_THROWS_WITH_CHECK( - convertExp->evaluate({}), AssertionException, [](const AssertionException& exception) { - ASSERT_EQ(exception.code(), ErrorCodes::ConversionFailure); - ASSERT_STRING_CONTAINS(exception.reason(), "Invalid string length for parsing to OID"); - }); + ASSERT_THROWS_WITH_CHECK(convertExp->evaluate({}, &expCtx->variables), + AssertionException, + [](const AssertionException& exception) { + ASSERT_EQ(exception.code(), ErrorCodes::ConversionFailure); + ASSERT_STRING_CONTAINS(exception.reason(), + "Invalid string length for parsing to OID"); + }); spec = fromjson("{$convert: {input: '0x123456789abcdef123456789', to: 'objectId'}}"); convertExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); - ASSERT_THROWS_WITH_CHECK( - convertExp->evaluate({}), AssertionException, [](const AssertionException& exception) { - ASSERT_EQ(exception.code(), ErrorCodes::ConversionFailure); - ASSERT_STRING_CONTAINS(exception.reason(), "Invalid string length for parsing to OID"); - }); + ASSERT_THROWS_WITH_CHECK(convertExp->evaluate({}, &expCtx->variables), + AssertionException, + [](const AssertionException& exception) { + ASSERT_EQ(exception.code(), ErrorCodes::ConversionFailure); + ASSERT_STRING_CONTAINS(exception.reason(), + "Invalid string length for parsing to OID"); + }); } TEST_F(ExpressionConvertTest, ConvertStringToOIDWithOnError) { @@ -2619,17 +2834,20 @@ TEST_F(ExpressionConvertTest, ConvertStringToOIDWithOnError) { fromjson("{$convert: {input: 'InvalidHexButSizeCorrect', to: 'objectId', onError: '" + onErrorValue + "'}}"); auto convertExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); - ASSERT_VALUE_CONTENTS_AND_TYPE(convertExp->evaluate({}), onErrorValue, BSONType::String); + ASSERT_VALUE_CONTENTS_AND_TYPE( + convertExp->evaluate({}, &expCtx->variables), onErrorValue, BSONType::String); spec = fromjson("{$convert: {input: 'InvalidSize', to: 'objectId', onError: '" + onErrorValue + "'}}"); convertExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); - ASSERT_VALUE_CONTENTS_AND_TYPE(convertExp->evaluate({}), onErrorValue, BSONType::String); + ASSERT_VALUE_CONTENTS_AND_TYPE( + convertExp->evaluate({}, &expCtx->variables), onErrorValue, BSONType::String); spec = fromjson("{$convert: {input: '0x123456789abcdef123456789', to: 'objectId', onError: '" + onErrorValue + "'}}"); convertExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); - ASSERT_VALUE_CONTENTS_AND_TYPE(convertExp->evaluate({}), onErrorValue, BSONType::String); + ASSERT_VALUE_CONTENTS_AND_TYPE( + convertExp->evaluate({}, &expCtx->variables), onErrorValue, BSONType::String); } TEST_F(ExpressionConvertTest, ConvertStringToDateRejectsUnparsableString) { @@ -2637,11 +2855,15 @@ TEST_F(ExpressionConvertTest, ConvertStringToDateRejectsUnparsableString) { auto spec = fromjson("{$convert: {input: '60.Monday1770/06:59', to: 'date'}}"); auto convertExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); - ASSERT_THROWS_CODE(convertExp->evaluate({}), AssertionException, ErrorCodes::ConversionFailure); + ASSERT_THROWS_CODE(convertExp->evaluate({}, &expCtx->variables), + AssertionException, + ErrorCodes::ConversionFailure); spec = fromjson("{$convert: {input: 'Definitely not a date', to: 'date'}}"); convertExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); - ASSERT_THROWS_CODE(convertExp->evaluate({}), AssertionException, ErrorCodes::ConversionFailure); + ASSERT_THROWS_CODE(convertExp->evaluate({}, &expCtx->variables), + AssertionException, + ErrorCodes::ConversionFailure); } TEST_F(ExpressionConvertTest, ConvertStringToDateRejectsTimezoneNameInString) { @@ -2649,11 +2871,15 @@ TEST_F(ExpressionConvertTest, ConvertStringToDateRejectsTimezoneNameInString) { auto spec = fromjson("{$convert: {input: '2017-07-13T10:02:57 Europe/London', to: 'date'}}"); auto convertExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); - ASSERT_THROWS_CODE(convertExp->evaluate({}), AssertionException, ErrorCodes::ConversionFailure); + ASSERT_THROWS_CODE(convertExp->evaluate({}, &expCtx->variables), + AssertionException, + ErrorCodes::ConversionFailure); spec = fromjson("{$convert: {input: 'July 4, 2017 Europe/London', to: 'date'}}"); convertExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); - ASSERT_THROWS_CODE(convertExp->evaluate({}), AssertionException, ErrorCodes::ConversionFailure); + ASSERT_THROWS_CODE(convertExp->evaluate({}, &expCtx->variables), + AssertionException, + ErrorCodes::ConversionFailure); } TEST_F(ExpressionConvertTest, ConvertStringToDate) { @@ -2662,15 +2888,17 @@ TEST_F(ExpressionConvertTest, ConvertStringToDate) { auto spec = fromjson("{$convert: {input: '$path1', to: 'date'}}"); auto convertExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); - auto result = convertExp->evaluate({{"path1", Value("2017-07-06T12:35:37Z"_sd)}}); + auto result = + convertExp->evaluate({{"path1", Value("2017-07-06T12:35:37Z"_sd)}}, &expCtx->variables); ASSERT_EQ(result.getType(), BSONType::Date); ASSERT_EQ("2017-07-06T12:35:37.000Z", result.toString()); - result = convertExp->evaluate({{"path1", Value("2017-07-06T12:35:37.513Z"_sd)}}); + result = + convertExp->evaluate({{"path1", Value("2017-07-06T12:35:37.513Z"_sd)}}, &expCtx->variables); ASSERT_EQ(result.getType(), BSONType::Date); ASSERT_EQ("2017-07-06T12:35:37.513Z", result.toString()); - result = convertExp->evaluate({{"path1", Value("2017-07-06"_sd)}}); + result = convertExp->evaluate({{"path1", Value("2017-07-06"_sd)}}, &expCtx->variables); ASSERT_EQ(result.getType(), BSONType::Date); ASSERT_EQ("2017-07-06T00:00:00.000Z", result.toString()); } @@ -2681,11 +2909,13 @@ TEST_F(ExpressionConvertTest, ConvertStringWithTimezoneToDate) { auto spec = fromjson("{$convert: {input: '$path1', to: 'date'}}"); auto convertExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); - auto result = convertExp->evaluate({{"path1", Value("2017-07-14T12:02:44.771 GMT+02:00"_sd)}}); + auto result = convertExp->evaluate({{"path1", Value("2017-07-14T12:02:44.771 GMT+02:00"_sd)}}, + &expCtx->variables); ASSERT_EQ(result.getType(), BSONType::Date); ASSERT_EQ("2017-07-14T10:02:44.771Z", result.toString()); - result = convertExp->evaluate({{"path1", Value("2017-07-14T12:02:44.771 A"_sd)}}); + result = convertExp->evaluate({{"path1", Value("2017-07-14T12:02:44.771 A"_sd)}}, + &expCtx->variables); ASSERT_EQ(result.getType(), BSONType::Date); ASSERT_EQ("2017-07-14T11:02:44.771Z", result.toString()); } @@ -2696,15 +2926,15 @@ TEST_F(ExpressionConvertTest, ConvertVerbalStringToDate) { auto spec = fromjson("{$convert: {input: '$path1', to: 'date'}}"); auto convertExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); - auto result = convertExp->evaluate({{"path1", Value("July 4th, 2017"_sd)}}); + auto result = convertExp->evaluate({{"path1", Value("July 4th, 2017"_sd)}}, &expCtx->variables); ASSERT_EQ(result.getType(), BSONType::Date); ASSERT_EQ("2017-07-04T00:00:00.000Z", result.toString()); - result = convertExp->evaluate({{"path1", Value("July 4th, 2017 12pm"_sd)}}); + result = convertExp->evaluate({{"path1", Value("July 4th, 2017 12pm"_sd)}}, &expCtx->variables); ASSERT_EQ(result.getType(), BSONType::Date); ASSERT_EQ("2017-07-04T12:00:00.000Z", result.toString()); - result = convertExp->evaluate({{"path1", Value("2017-Jul-04 noon"_sd)}}); + result = convertExp->evaluate({{"path1", Value("2017-Jul-04 noon"_sd)}}, &expCtx->variables); ASSERT_EQ(result.getType(), BSONType::Date); ASSERT_EQ("2017-07-04T12:00:00.000Z", result.toString()); } @@ -2717,13 +2947,14 @@ TEST_F(ExpressionConvertTest, ConvertStringToDateWithOnError) { fromjson("{$convert: {input: '$path1', to: 'date', onError: '" + onErrorValue + "'}}"); auto convertExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); - auto result = convertExp->evaluate({{"path1", Value("Not a date"_sd)}}); + auto result = convertExp->evaluate({{"path1", Value("Not a date"_sd)}}, &expCtx->variables); ASSERT_VALUE_CONTENTS_AND_TYPE(result, onErrorValue, BSONType::String); - result = convertExp->evaluate({{"path1", Value("60.Monday1770/06:59"_sd)}}); + result = convertExp->evaluate({{"path1", Value("60.Monday1770/06:59"_sd)}}, &expCtx->variables); ASSERT_VALUE_CONTENTS_AND_TYPE(result, onErrorValue, BSONType::String); - result = convertExp->evaluate({{"path1", Value("2017-07-13T10:02:57 Europe/London"_sd)}}); + result = convertExp->evaluate({{"path1", Value("2017-07-13T10:02:57 Europe/London"_sd)}}, + &expCtx->variables); ASSERT_VALUE_CONTENTS_AND_TYPE(result, onErrorValue, BSONType::String); } @@ -2735,10 +2966,10 @@ TEST_F(ExpressionConvertTest, ConvertStringToDateWithOnNull) { fromjson("{$convert: {input: '$path1', to: 'date', onNull: '" + onNullValue + "'}}"); auto convertExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); - auto result = convertExp->evaluate({}); + auto result = convertExp->evaluate({}, &expCtx->variables); ASSERT_VALUE_CONTENTS_AND_TYPE(result, onNullValue, BSONType::String); - result = convertExp->evaluate({{"path1", Value(BSONNULL)}}); + result = convertExp->evaluate({{"path1", Value(BSONNULL)}}, &expCtx->variables); ASSERT_VALUE_CONTENTS_AND_TYPE(result, onNullValue, BSONType::String); } @@ -2752,54 +2983,67 @@ TEST_F(ExpressionConvertTest, FormatDouble) { auto convertExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); Document zeroInput{{"path1", 0.0}}; - ASSERT_VALUE_CONTENTS_AND_TYPE(convertExp->evaluate(zeroInput), "0"_sd, BSONType::String); + ASSERT_VALUE_CONTENTS_AND_TYPE( + convertExp->evaluate(zeroInput, &expCtx->variables), "0"_sd, BSONType::String); Document negativeZeroInput{{"path1", -0.0}}; ASSERT_VALUE_CONTENTS_AND_TYPE( - convertExp->evaluate(negativeZeroInput), "-0"_sd, BSONType::String); + convertExp->evaluate(negativeZeroInput, &expCtx->variables), "-0"_sd, BSONType::String); Document positiveIntegerInput{{"path1", 1337.0}}; - ASSERT_VALUE_CONTENTS_AND_TYPE( - convertExp->evaluate(positiveIntegerInput), "1337"_sd, BSONType::String); + ASSERT_VALUE_CONTENTS_AND_TYPE(convertExp->evaluate(positiveIntegerInput, &expCtx->variables), + "1337"_sd, + BSONType::String); Document negativeIntegerInput{{"path1", -1337.0}}; - ASSERT_VALUE_CONTENTS_AND_TYPE( - convertExp->evaluate(negativeIntegerInput), "-1337"_sd, BSONType::String); + ASSERT_VALUE_CONTENTS_AND_TYPE(convertExp->evaluate(negativeIntegerInput, &expCtx->variables), + "-1337"_sd, + BSONType::String); Document positiveFractionalInput{{"path1", 0.1337}}; ASSERT_VALUE_CONTENTS_AND_TYPE( - convertExp->evaluate(positiveFractionalInput), "0.1337"_sd, BSONType::String); + convertExp->evaluate(positiveFractionalInput, &expCtx->variables), + "0.1337"_sd, + BSONType::String); Document negativeFractionalInput{{"path1", -0.1337}}; ASSERT_VALUE_CONTENTS_AND_TYPE( - convertExp->evaluate(negativeFractionalInput), "-0.1337"_sd, BSONType::String); + convertExp->evaluate(negativeFractionalInput, &expCtx->variables), + "-0.1337"_sd, + BSONType::String); Document positiveLargeInput{{"path1", 1.3e37}}; - ASSERT_VALUE_CONTENTS_AND_TYPE( - convertExp->evaluate(positiveLargeInput), "1.3e+37"_sd, BSONType::String); + ASSERT_VALUE_CONTENTS_AND_TYPE(convertExp->evaluate(positiveLargeInput, &expCtx->variables), + "1.3e+37"_sd, + BSONType::String); Document negativeLargeInput{{"path1", -1.3e37}}; - ASSERT_VALUE_CONTENTS_AND_TYPE( - convertExp->evaluate(negativeLargeInput), "-1.3e+37"_sd, BSONType::String); + ASSERT_VALUE_CONTENTS_AND_TYPE(convertExp->evaluate(negativeLargeInput, &expCtx->variables), + "-1.3e+37"_sd, + BSONType::String); Document positiveTinyInput{{"path1", 1.3e-37}}; - ASSERT_VALUE_CONTENTS_AND_TYPE( - convertExp->evaluate(positiveTinyInput), "1.3e-37"_sd, BSONType::String); + ASSERT_VALUE_CONTENTS_AND_TYPE(convertExp->evaluate(positiveTinyInput, &expCtx->variables), + "1.3e-37"_sd, + BSONType::String); Document negativeTinyInput{{"path1", -1.3e-37}}; - ASSERT_VALUE_CONTENTS_AND_TYPE( - convertExp->evaluate(negativeTinyInput), "-1.3e-37"_sd, BSONType::String); + ASSERT_VALUE_CONTENTS_AND_TYPE(convertExp->evaluate(negativeTinyInput, &expCtx->variables), + "-1.3e-37"_sd, + BSONType::String); Document infinityInput{{"path1", std::numeric_limits::infinity()}}; ASSERT_VALUE_CONTENTS_AND_TYPE( - convertExp->evaluate(infinityInput), "Infinity"_sd, BSONType::String); + convertExp->evaluate(infinityInput, &expCtx->variables), "Infinity"_sd, BSONType::String); Document negativeInfinityInput{{"path1", -std::numeric_limits::infinity()}}; - ASSERT_VALUE_CONTENTS_AND_TYPE( - convertExp->evaluate(negativeInfinityInput), "-Infinity"_sd, BSONType::String); + ASSERT_VALUE_CONTENTS_AND_TYPE(convertExp->evaluate(negativeInfinityInput, &expCtx->variables), + "-Infinity"_sd, + BSONType::String); Document nanInput{{"path1", std::numeric_limits::quiet_NaN()}}; - ASSERT_VALUE_CONTENTS_AND_TYPE(convertExp->evaluate(nanInput), "NaN"_sd, BSONType::String); + ASSERT_VALUE_CONTENTS_AND_TYPE( + convertExp->evaluate(nanInput, &expCtx->variables), "NaN"_sd, BSONType::String); } TEST_F(ExpressionConvertTest, FormatObjectId) { @@ -2812,12 +3056,14 @@ TEST_F(ExpressionConvertTest, FormatObjectId) { auto convertExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); Document zeroInput{{"path1", OID("000000000000000000000000")}}; - ASSERT_VALUE_CONTENTS_AND_TYPE( - convertExp->evaluate(zeroInput), "000000000000000000000000"_sd, BSONType::String); + ASSERT_VALUE_CONTENTS_AND_TYPE(convertExp->evaluate(zeroInput, &expCtx->variables), + "000000000000000000000000"_sd, + BSONType::String); Document simpleInput{{"path1", OID("0123456789abcdef01234567")}}; - ASSERT_VALUE_CONTENTS_AND_TYPE( - convertExp->evaluate(simpleInput), "0123456789abcdef01234567"_sd, BSONType::String); + ASSERT_VALUE_CONTENTS_AND_TYPE(convertExp->evaluate(simpleInput, &expCtx->variables), + "0123456789abcdef01234567"_sd, + BSONType::String); } TEST_F(ExpressionConvertTest, FormatBool) { @@ -2830,10 +3076,12 @@ TEST_F(ExpressionConvertTest, FormatBool) { auto convertExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); Document trueInput{{"path1", true}}; - ASSERT_VALUE_CONTENTS_AND_TYPE(convertExp->evaluate(trueInput), "true"_sd, BSONType::String); + ASSERT_VALUE_CONTENTS_AND_TYPE( + convertExp->evaluate(trueInput, &expCtx->variables), "true"_sd, BSONType::String); Document falseInput{{"path1", false}}; - ASSERT_VALUE_CONTENTS_AND_TYPE(convertExp->evaluate(falseInput), "false"_sd, BSONType::String); + ASSERT_VALUE_CONTENTS_AND_TYPE( + convertExp->evaluate(falseInput, &expCtx->variables), "false"_sd, BSONType::String); } TEST_F(ExpressionConvertTest, FormatDate) { @@ -2846,12 +3094,14 @@ TEST_F(ExpressionConvertTest, FormatDate) { auto convertExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); Document epochInput{{"path1", Date_t::fromMillisSinceEpoch(0LL)}}; - ASSERT_VALUE_CONTENTS_AND_TYPE( - convertExp->evaluate(epochInput), "1970-01-01T00:00:00.000Z"_sd, BSONType::String); + ASSERT_VALUE_CONTENTS_AND_TYPE(convertExp->evaluate(epochInput, &expCtx->variables), + "1970-01-01T00:00:00.000Z"_sd, + BSONType::String); Document dateInput{{"path1", Date_t::fromMillisSinceEpoch(872835240000)}}; - ASSERT_VALUE_CONTENTS_AND_TYPE( - convertExp->evaluate(dateInput), "1997-08-29T06:14:00.000Z"_sd, BSONType::String); + ASSERT_VALUE_CONTENTS_AND_TYPE(convertExp->evaluate(dateInput, &expCtx->variables), + "1997-08-29T06:14:00.000Z"_sd, + BSONType::String); } TEST_F(ExpressionConvertTest, FormatInt) { @@ -2864,15 +3114,16 @@ TEST_F(ExpressionConvertTest, FormatInt) { auto convertExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); Document zeroInput{{"path1", int{0}}}; - ASSERT_VALUE_CONTENTS_AND_TYPE(convertExp->evaluate(zeroInput), "0"_sd, BSONType::String); + ASSERT_VALUE_CONTENTS_AND_TYPE( + convertExp->evaluate(zeroInput, &expCtx->variables), "0"_sd, BSONType::String); Document positiveInput{{"path1", int{1337}}}; ASSERT_VALUE_CONTENTS_AND_TYPE( - convertExp->evaluate(positiveInput), "1337"_sd, BSONType::String); + convertExp->evaluate(positiveInput, &expCtx->variables), "1337"_sd, BSONType::String); Document negativeInput{{"path1", int{-1337}}}; ASSERT_VALUE_CONTENTS_AND_TYPE( - convertExp->evaluate(negativeInput), "-1337"_sd, BSONType::String); + convertExp->evaluate(negativeInput, &expCtx->variables), "-1337"_sd, BSONType::String); } TEST_F(ExpressionConvertTest, FormatLong) { @@ -2885,15 +3136,18 @@ TEST_F(ExpressionConvertTest, FormatLong) { auto convertExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); Document zeroInput{{"path1", 0LL}}; - ASSERT_VALUE_CONTENTS_AND_TYPE(convertExp->evaluate(zeroInput), "0"_sd, BSONType::String); + ASSERT_VALUE_CONTENTS_AND_TYPE( + convertExp->evaluate(zeroInput, &expCtx->variables), "0"_sd, BSONType::String); Document positiveInput{{"path1", 1337133713371337LL}}; - ASSERT_VALUE_CONTENTS_AND_TYPE( - convertExp->evaluate(positiveInput), "1337133713371337"_sd, BSONType::String); + ASSERT_VALUE_CONTENTS_AND_TYPE(convertExp->evaluate(positiveInput, &expCtx->variables), + "1337133713371337"_sd, + BSONType::String); Document negativeInput{{"path1", -1337133713371337LL}}; - ASSERT_VALUE_CONTENTS_AND_TYPE( - convertExp->evaluate(negativeInput), "-1337133713371337"_sd, BSONType::String); + ASSERT_VALUE_CONTENTS_AND_TYPE(convertExp->evaluate(negativeInput, &expCtx->variables), + "-1337133713371337"_sd, + BSONType::String); } TEST_F(ExpressionConvertTest, FormatDecimal) { @@ -2906,83 +3160,103 @@ TEST_F(ExpressionConvertTest, FormatDecimal) { auto convertExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); Document zeroInput{{"path1", Decimal128("0")}}; - ASSERT_VALUE_CONTENTS_AND_TYPE(convertExp->evaluate(zeroInput), "0"_sd, BSONType::String); + ASSERT_VALUE_CONTENTS_AND_TYPE( + convertExp->evaluate(zeroInput, &expCtx->variables), "0"_sd, BSONType::String); Document negativeZeroInput{{"path1", Decimal128("-0")}}; ASSERT_VALUE_CONTENTS_AND_TYPE( - convertExp->evaluate(negativeZeroInput), "-0"_sd, BSONType::String); + convertExp->evaluate(negativeZeroInput, &expCtx->variables), "-0"_sd, BSONType::String); Document preciseZeroInput{{"path1", Decimal128("0.0")}}; ASSERT_VALUE_CONTENTS_AND_TYPE( - convertExp->evaluate(preciseZeroInput), "0.0"_sd, BSONType::String); + convertExp->evaluate(preciseZeroInput, &expCtx->variables), "0.0"_sd, BSONType::String); Document negativePreciseZeroInput{{"path1", Decimal128("-0.0")}}; ASSERT_VALUE_CONTENTS_AND_TYPE( - convertExp->evaluate(negativePreciseZeroInput), "-0.0"_sd, BSONType::String); + convertExp->evaluate(negativePreciseZeroInput, &expCtx->variables), + "-0.0"_sd, + BSONType::String); Document extraPreciseZeroInput{{"path1", Decimal128("0.0000")}}; - ASSERT_VALUE_CONTENTS_AND_TYPE( - convertExp->evaluate(extraPreciseZeroInput), "0.0000"_sd, BSONType::String); + ASSERT_VALUE_CONTENTS_AND_TYPE(convertExp->evaluate(extraPreciseZeroInput, &expCtx->variables), + "0.0000"_sd, + BSONType::String); Document positiveIntegerInput{{"path1", Decimal128("1337")}}; - ASSERT_VALUE_CONTENTS_AND_TYPE( - convertExp->evaluate(positiveIntegerInput), "1337"_sd, BSONType::String); + ASSERT_VALUE_CONTENTS_AND_TYPE(convertExp->evaluate(positiveIntegerInput, &expCtx->variables), + "1337"_sd, + BSONType::String); Document largeIntegerInput{{"path1", Decimal128("13370000000000000000000000000000000")}}; - ASSERT_VALUE_CONTENTS_AND_TYPE(convertExp->evaluate(largeIntegerInput), + ASSERT_VALUE_CONTENTS_AND_TYPE(convertExp->evaluate(largeIntegerInput, &expCtx->variables), "1.337000000000000000000000000000000E+34"_sd, BSONType::String); Document negativeIntegerInput{{"path1", Decimal128("-1337")}}; - ASSERT_VALUE_CONTENTS_AND_TYPE( - convertExp->evaluate(negativeIntegerInput), "-1337"_sd, BSONType::String); + ASSERT_VALUE_CONTENTS_AND_TYPE(convertExp->evaluate(negativeIntegerInput, &expCtx->variables), + "-1337"_sd, + BSONType::String); Document positiveFractionalInput{{"path1", Decimal128("0.1337")}}; ASSERT_VALUE_CONTENTS_AND_TYPE( - convertExp->evaluate(positiveFractionalInput), "0.1337"_sd, BSONType::String); + convertExp->evaluate(positiveFractionalInput, &expCtx->variables), + "0.1337"_sd, + BSONType::String); Document positivePreciseFractionalInput{{"path1", Decimal128("0.133700")}}; ASSERT_VALUE_CONTENTS_AND_TYPE( - convertExp->evaluate(positivePreciseFractionalInput), "0.133700"_sd, BSONType::String); + convertExp->evaluate(positivePreciseFractionalInput, &expCtx->variables), + "0.133700"_sd, + BSONType::String); Document negativeFractionalInput{{"path1", Decimal128("-0.1337")}}; ASSERT_VALUE_CONTENTS_AND_TYPE( - convertExp->evaluate(negativeFractionalInput), "-0.1337"_sd, BSONType::String); + convertExp->evaluate(negativeFractionalInput, &expCtx->variables), + "-0.1337"_sd, + BSONType::String); Document negativePreciseFractionalInput{{"path1", Decimal128("-0.133700")}}; ASSERT_VALUE_CONTENTS_AND_TYPE( - convertExp->evaluate(negativePreciseFractionalInput), "-0.133700"_sd, BSONType::String); + convertExp->evaluate(negativePreciseFractionalInput, &expCtx->variables), + "-0.133700"_sd, + BSONType::String); Document positiveLargeInput{{"path1", Decimal128("1.3e37")}}; - ASSERT_VALUE_CONTENTS_AND_TYPE( - convertExp->evaluate(positiveLargeInput), "1.3E+37"_sd, BSONType::String); + ASSERT_VALUE_CONTENTS_AND_TYPE(convertExp->evaluate(positiveLargeInput, &expCtx->variables), + "1.3E+37"_sd, + BSONType::String); Document negativeLargeInput{{"path1", Decimal128("-1.3e37")}}; - ASSERT_VALUE_CONTENTS_AND_TYPE( - convertExp->evaluate(negativeLargeInput), "-1.3E+37"_sd, BSONType::String); + ASSERT_VALUE_CONTENTS_AND_TYPE(convertExp->evaluate(negativeLargeInput, &expCtx->variables), + "-1.3E+37"_sd, + BSONType::String); Document positiveTinyInput{{"path1", Decimal128("1.3e-37")}}; - ASSERT_VALUE_CONTENTS_AND_TYPE( - convertExp->evaluate(positiveTinyInput), "1.3E-37"_sd, BSONType::String); + ASSERT_VALUE_CONTENTS_AND_TYPE(convertExp->evaluate(positiveTinyInput, &expCtx->variables), + "1.3E-37"_sd, + BSONType::String); Document negativeTinyInput{{"path1", Decimal128("-1.3e-37")}}; - ASSERT_VALUE_CONTENTS_AND_TYPE( - convertExp->evaluate(negativeTinyInput), "-1.3E-37"_sd, BSONType::String); + ASSERT_VALUE_CONTENTS_AND_TYPE(convertExp->evaluate(negativeTinyInput, &expCtx->variables), + "-1.3E-37"_sd, + BSONType::String); Document infinityInput{{"path1", Decimal128::kPositiveInfinity}}; ASSERT_VALUE_CONTENTS_AND_TYPE( - convertExp->evaluate(infinityInput), "Infinity"_sd, BSONType::String); + convertExp->evaluate(infinityInput, &expCtx->variables), "Infinity"_sd, BSONType::String); Document negativeInfinityInput{{"path1", Decimal128::kNegativeInfinity}}; - ASSERT_VALUE_CONTENTS_AND_TYPE( - convertExp->evaluate(negativeInfinityInput), "-Infinity"_sd, BSONType::String); + ASSERT_VALUE_CONTENTS_AND_TYPE(convertExp->evaluate(negativeInfinityInput, &expCtx->variables), + "-Infinity"_sd, + BSONType::String); Document nanInput{{"path1", Decimal128::kPositiveNaN}}; - ASSERT_VALUE_CONTENTS_AND_TYPE(convertExp->evaluate(nanInput), "NaN"_sd, BSONType::String); + ASSERT_VALUE_CONTENTS_AND_TYPE( + convertExp->evaluate(nanInput, &expCtx->variables), "NaN"_sd, BSONType::String); Document negativeNaNInput{{"path1", Decimal128::kNegativeNaN}}; ASSERT_VALUE_CONTENTS_AND_TYPE( - convertExp->evaluate(negativeNaNInput), "NaN"_sd, BSONType::String); + convertExp->evaluate(negativeNaNInput, &expCtx->variables), "NaN"_sd, BSONType::String); } } // namespace ExpressionConvertTest @@ -3039,7 +3313,9 @@ TEST_F(ExpressionConvertShortcutsTest, AcceptsSingleArgumentInArrayOrByItself) { spec = BSON("$toInt" << BSON_ARRAY(BSON_ARRAY("1"))); convert = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); - ASSERT_THROWS_CODE(convert->evaluate({}), AssertionException, ErrorCodes::ConversionFailure); + ASSERT_THROWS_CODE(convert->evaluate({}, &expCtx->variables), + AssertionException, + ErrorCodes::ConversionFailure); } TEST_F(ExpressionConvertShortcutsTest, ConvertsToInts) { @@ -3049,7 +3325,8 @@ TEST_F(ExpressionConvertShortcutsTest, ConvertsToInts) { << "1"); auto convert = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); ASSERT_TRUE(dynamic_cast(convert.get())); - ASSERT_VALUE_CONTENTS_AND_TYPE(convert->evaluate({}), Value(1), BSONType::NumberInt); + ASSERT_VALUE_CONTENTS_AND_TYPE( + convert->evaluate({}, &expCtx->variables), Value(1), BSONType::NumberInt); } TEST_F(ExpressionConvertShortcutsTest, ConvertsToLongs) { @@ -3059,7 +3336,8 @@ TEST_F(ExpressionConvertShortcutsTest, ConvertsToLongs) { << "1"); auto convert = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); ASSERT_TRUE(dynamic_cast(convert.get())); - ASSERT_VALUE_CONTENTS_AND_TYPE(convert->evaluate({}), Value(1), BSONType::NumberLong); + ASSERT_VALUE_CONTENTS_AND_TYPE( + convert->evaluate({}, &expCtx->variables), Value(1), BSONType::NumberLong); } TEST_F(ExpressionConvertShortcutsTest, ConvertsToDoubles) { @@ -3069,7 +3347,8 @@ TEST_F(ExpressionConvertShortcutsTest, ConvertsToDoubles) { << "1"); auto convert = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); ASSERT_TRUE(dynamic_cast(convert.get())); - ASSERT_VALUE_CONTENTS_AND_TYPE(convert->evaluate({}), Value(1), BSONType::NumberDouble); + ASSERT_VALUE_CONTENTS_AND_TYPE( + convert->evaluate({}, &expCtx->variables), Value(1), BSONType::NumberDouble); } TEST_F(ExpressionConvertShortcutsTest, ConvertsToDecimals) { @@ -3079,7 +3358,8 @@ TEST_F(ExpressionConvertShortcutsTest, ConvertsToDecimals) { << "1"); auto convert = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); ASSERT_TRUE(dynamic_cast(convert.get())); - ASSERT_VALUE_CONTENTS_AND_TYPE(convert->evaluate({}), Value(1), BSONType::NumberDecimal); + ASSERT_VALUE_CONTENTS_AND_TYPE( + convert->evaluate({}, &expCtx->variables), Value(1), BSONType::NumberDecimal); } TEST_F(ExpressionConvertShortcutsTest, ConvertsToDates) { @@ -3088,8 +3368,9 @@ TEST_F(ExpressionConvertShortcutsTest, ConvertsToDates) { BSONObj spec = BSON("$toDate" << 0LL); auto convert = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); ASSERT_TRUE(dynamic_cast(convert.get())); - ASSERT_VALUE_CONTENTS_AND_TYPE( - convert->evaluate({}), Value(Date_t::fromMillisSinceEpoch(0)), BSONType::Date); + ASSERT_VALUE_CONTENTS_AND_TYPE(convert->evaluate({}, &expCtx->variables), + Value(Date_t::fromMillisSinceEpoch(0)), + BSONType::Date); } TEST_F(ExpressionConvertShortcutsTest, ConvertsToObjectIds) { @@ -3099,8 +3380,9 @@ TEST_F(ExpressionConvertShortcutsTest, ConvertsToObjectIds) { BSONObj spec = BSON("$toObjectId" << hexString); auto convert = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); ASSERT_TRUE(dynamic_cast(convert.get())); - ASSERT_VALUE_CONTENTS_AND_TYPE( - convert->evaluate({}), Value(OID::createFromString(hexString)), BSONType::jstOID); + ASSERT_VALUE_CONTENTS_AND_TYPE(convert->evaluate({}, &expCtx->variables), + Value(OID::createFromString(hexString)), + BSONType::jstOID); } TEST_F(ExpressionConvertShortcutsTest, ConvertsToString) { @@ -3109,7 +3391,8 @@ TEST_F(ExpressionConvertShortcutsTest, ConvertsToString) { BSONObj spec = BSON("$toString" << 1); auto convert = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); ASSERT_TRUE(dynamic_cast(convert.get())); - ASSERT_VALUE_CONTENTS_AND_TYPE(convert->evaluate({}), Value("1"_sd), BSONType::String); + ASSERT_VALUE_CONTENTS_AND_TYPE( + convert->evaluate({}, &expCtx->variables), Value("1"_sd), BSONType::String); } TEST_F(ExpressionConvertShortcutsTest, ConvertsToBool) { @@ -3118,7 +3401,8 @@ TEST_F(ExpressionConvertShortcutsTest, ConvertsToBool) { BSONObj spec = BSON("$toBool" << 1); auto convert = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); ASSERT_TRUE(dynamic_cast(convert.get())); - ASSERT_VALUE_CONTENTS_AND_TYPE(convert->evaluate({}), Value(true), BSONType::Bool); + ASSERT_VALUE_CONTENTS_AND_TYPE( + convert->evaluate({}, &expCtx->variables), Value(true), BSONType::Bool); } TEST_F(ExpressionConvertShortcutsTest, ReturnsNullOnNullishInput) { @@ -3127,13 +3411,15 @@ TEST_F(ExpressionConvertShortcutsTest, ReturnsNullOnNullishInput) { BSONObj spec = BSON("$toBool" << BSONNULL); auto convert = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); ASSERT_TRUE(dynamic_cast(convert.get())); - ASSERT_VALUE_CONTENTS_AND_TYPE(convert->evaluate({}), Value(BSONNULL), BSONType::jstNULL); + ASSERT_VALUE_CONTENTS_AND_TYPE( + convert->evaluate({}, &expCtx->variables), Value(BSONNULL), BSONType::jstNULL); spec = BSON("$toInt" << "$missing"); convert = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); ASSERT_TRUE(dynamic_cast(convert.get())); - ASSERT_VALUE_CONTENTS_AND_TYPE(convert->evaluate({}), Value(BSONNULL), BSONType::jstNULL); + ASSERT_VALUE_CONTENTS_AND_TYPE( + convert->evaluate({}, &expCtx->variables), Value(BSONNULL), BSONType::jstNULL); } TEST_F(ExpressionConvertShortcutsTest, ThrowsOnConversionFailure) { @@ -3143,13 +3429,17 @@ TEST_F(ExpressionConvertShortcutsTest, ThrowsOnConversionFailure) { << "not an int"); auto convert = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); ASSERT_TRUE(dynamic_cast(convert.get())); - ASSERT_THROWS_CODE(convert->evaluate({}), AssertionException, ErrorCodes::ConversionFailure); + ASSERT_THROWS_CODE(convert->evaluate({}, &expCtx->variables), + AssertionException, + ErrorCodes::ConversionFailure); spec = BSON("$toObjectId" << "not all hex values"); convert = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); ASSERT_TRUE(dynamic_cast(convert.get())); - ASSERT_THROWS_CODE(convert->evaluate({}), AssertionException, ErrorCodes::ConversionFailure); + ASSERT_THROWS_CODE(convert->evaluate({}, &expCtx->variables), + AssertionException, + ErrorCodes::ConversionFailure); } } // namespace ExpressionConvertShortcutsTest diff --git a/src/mongo/db/pipeline/expression_date_test.cpp b/src/mongo/db/pipeline/expression_date_test.cpp index 528c0da9541..67e798d17af 100644 --- a/src/mongo/db/pipeline/expression_date_test.cpp +++ b/src/mongo/db/pipeline/expression_date_test.cpp @@ -143,27 +143,27 @@ TEST_F(ExpressionDateFromPartsTest, TestThatOutOfRangeValuesRollOver) { auto spec = BSON("$dateFromParts" << BSON("year" << 2017 << "month" << -1)); auto dateExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); auto dateVal = Date_t::fromMillisSinceEpoch(1477958400000); // 11/1/2016 in ms. - ASSERT_VALUE_EQ(Value(dateVal), dateExp->evaluate(Document{})); + ASSERT_VALUE_EQ(Value(dateVal), dateExp->evaluate({}, &expCtx->variables)); spec = BSON("$dateFromParts" << BSON("year" << 2017 << "day" << -1)); dateExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); dateVal = Date_t::fromMillisSinceEpoch(1483056000000); // 12/30/2016 - ASSERT_VALUE_EQ(Value(dateVal), dateExp->evaluate(Document{})); + ASSERT_VALUE_EQ(Value(dateVal), dateExp->evaluate({}, &expCtx->variables)); spec = BSON("$dateFromParts" << BSON("year" << 2017 << "hour" << 25)); dateExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); dateVal = Date_t::fromMillisSinceEpoch(1483318800000); // 1/2/2017 01:00:00 - ASSERT_VALUE_EQ(Value(dateVal), dateExp->evaluate(Document{})); + ASSERT_VALUE_EQ(Value(dateVal), dateExp->evaluate({}, &expCtx->variables)); spec = BSON("$dateFromParts" << BSON("year" << 2017 << "minute" << 61)); dateExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); dateVal = Date_t::fromMillisSinceEpoch(1483232460000); // 1/1/2017 01:01:00 - ASSERT_VALUE_EQ(Value(dateVal), dateExp->evaluate(Document{})); + ASSERT_VALUE_EQ(Value(dateVal), dateExp->evaluate({}, &expCtx->variables)); spec = BSON("$dateFromParts" << BSON("year" << 2017 << "second" << 61)); dateExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); dateVal = Date_t::fromMillisSinceEpoch(1483228861000); // 1/1/2017 00:01:01 - ASSERT_VALUE_EQ(Value(dateVal), dateExp->evaluate(Document{})); + ASSERT_VALUE_EQ(Value(dateVal), dateExp->evaluate({}, &expCtx->variables)); } } // namespace ExpressionDateFromPartsTest @@ -354,14 +354,16 @@ TEST_F(DateExpressionTest, RejectsArraysWithinObjectSpecification) { // It will parse as an ExpressionArray, and fail at runtime. auto dateExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); auto contextDoc = Document{{"_id", 0}}; - ASSERT_THROWS_CODE(dateExp->evaluate(contextDoc), AssertionException, 16006); + ASSERT_THROWS_CODE( + dateExp->evaluate(contextDoc, &expCtx->variables), AssertionException, 16006); // Test that it rejects an array for the timezone option. spec = BSON(expName << BSON("date" << Date_t{} << "timezone" << BSON_ARRAY("Europe/London"))); dateExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); contextDoc = Document{{"_id", 0}}; - ASSERT_THROWS_CODE(dateExp->evaluate(contextDoc), AssertionException, 40533); + ASSERT_THROWS_CODE( + dateExp->evaluate(contextDoc, &expCtx->variables), AssertionException, 40533); } } @@ -371,7 +373,8 @@ TEST_F(DateExpressionTest, RejectsTypesThatCannotCoerceToDate) { BSONObj spec = BSON(expName << "$stringField"); auto dateExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); auto contextDoc = Document{{"stringField", "string"_sd}}; - ASSERT_THROWS_CODE(dateExp->evaluate(contextDoc), AssertionException, 16006); + ASSERT_THROWS_CODE( + dateExp->evaluate(contextDoc, &expCtx->variables), AssertionException, 16006); } } @@ -381,7 +384,7 @@ TEST_F(DateExpressionTest, AcceptsObjectIds) { BSONObj spec = BSON(expName << "$oid"); auto dateExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); auto contextDoc = Document{{"oid", OID::gen()}}; - dateExp->evaluate(contextDoc); // Should not throw. + dateExp->evaluate(contextDoc, &expCtx->variables); // Should not throw. } } @@ -391,7 +394,7 @@ TEST_F(DateExpressionTest, AcceptsTimestamps) { BSONObj spec = BSON(expName << "$ts"); auto dateExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); auto contextDoc = Document{{"ts", Timestamp{Date_t{}}}}; - dateExp->evaluate(contextDoc); // Should not throw. + dateExp->evaluate(contextDoc, &expCtx->variables); // Should not throw. } } @@ -402,7 +405,8 @@ TEST_F(DateExpressionTest, RejectsNonStringTimezone) { << "$intField")); auto dateExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); auto contextDoc = Document{{"intField", 4}}; - ASSERT_THROWS_CODE(dateExp->evaluate(contextDoc), AssertionException, 40533); + ASSERT_THROWS_CODE( + dateExp->evaluate(contextDoc, &expCtx->variables), AssertionException, 40533); } } @@ -413,7 +417,8 @@ TEST_F(DateExpressionTest, RejectsUnrecognizedTimeZoneSpecification) { << "UNRECOGNIZED!")); auto dateExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); auto contextDoc = Document{{"_id", 0}}; - ASSERT_THROWS_CODE(dateExp->evaluate(contextDoc), AssertionException, 40485); + ASSERT_THROWS_CODE( + dateExp->evaluate(contextDoc, &expCtx->variables), AssertionException, 40485); } } @@ -498,7 +503,7 @@ TEST_F(DateExpressionTest, DoesRespectTimeZone) { << "America/New_York")); auto dateExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); auto contextDoc = Document{{"_id", 0}}; - dateExp->evaluate(contextDoc); // Should not throw. + dateExp->evaluate(contextDoc, &expCtx->variables); // Should not throw. } // Make sure the time zone is used during evaluation. @@ -506,14 +511,14 @@ TEST_F(DateExpressionTest, DoesRespectTimeZone) { auto specWithoutTimezone = BSON("$hour" << BSON("date" << date)); auto hourWithoutTimezone = Expression::parseExpression(expCtx, specWithoutTimezone, expCtx->variablesParseState) - ->evaluate({}); + ->evaluate({}, &expCtx->variables); ASSERT_VALUE_EQ(hourWithoutTimezone, Value(19)); auto specWithTimezone = BSON("$hour" << BSON("date" << date << "timezone" << "America/New_York")); auto hourWithTimezone = Expression::parseExpression(expCtx, specWithTimezone, expCtx->variablesParseState) - ->evaluate({}); + ->evaluate({}, &expCtx->variables); ASSERT_VALUE_EQ(hourWithTimezone, Value(15)); } @@ -528,30 +533,30 @@ TEST_F(DateExpressionTest, DoesResultInNullIfGivenNullishInput) { auto spec = BSON(expName << BSON("date" << "$missing")); auto dateExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); - ASSERT_VALUE_EQ(Value(BSONNULL), dateExp->evaluate(contextDoc)); + ASSERT_VALUE_EQ(Value(BSONNULL), dateExp->evaluate(contextDoc, &expCtx->variables)); spec = BSON(expName << BSON("date" << BSONNULL)); dateExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); - ASSERT_VALUE_EQ(Value(BSONNULL), dateExp->evaluate(contextDoc)); + ASSERT_VALUE_EQ(Value(BSONNULL), dateExp->evaluate(contextDoc, &expCtx->variables)); spec = BSON(expName << BSON("date" << BSONUndefined)); dateExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); - ASSERT_VALUE_EQ(Value(BSONNULL), dateExp->evaluate(contextDoc)); + ASSERT_VALUE_EQ(Value(BSONNULL), dateExp->evaluate(contextDoc, &expCtx->variables)); // Test that the expression results in null if the date is present but the timezone is // nullish. spec = BSON(expName << BSON("date" << Date_t{} << "timezone" << "$missing")); dateExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); - ASSERT_VALUE_EQ(Value(BSONNULL), dateExp->evaluate(contextDoc)); + ASSERT_VALUE_EQ(Value(BSONNULL), dateExp->evaluate(contextDoc, &expCtx->variables)); spec = BSON(expName << BSON("date" << Date_t{} << "timezone" << BSONNULL)); dateExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); - ASSERT_VALUE_EQ(Value(BSONNULL), dateExp->evaluate(contextDoc)); + ASSERT_VALUE_EQ(Value(BSONNULL), dateExp->evaluate(contextDoc, &expCtx->variables)); spec = BSON(expName << BSON("date" << Date_t{} << "timezone" << BSONUndefined)); dateExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); - ASSERT_VALUE_EQ(Value(BSONNULL), dateExp->evaluate(contextDoc)); + ASSERT_VALUE_EQ(Value(BSONNULL), dateExp->evaluate(contextDoc, &expCtx->variables)); // Test that the expression results in null if the date and timezone both nullish. spec = BSON(expName << BSON("date" @@ -559,7 +564,7 @@ TEST_F(DateExpressionTest, DoesResultInNullIfGivenNullishInput) { << "timezone" << BSONUndefined)); dateExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); - ASSERT_VALUE_EQ(Value(BSONNULL), dateExp->evaluate(contextDoc)); + ASSERT_VALUE_EQ(Value(BSONNULL), dateExp->evaluate(contextDoc, &expCtx->variables)); // Test that the expression results in null if the date is nullish and timezone is present. spec = BSON(expName << BSON("date" @@ -567,7 +572,7 @@ TEST_F(DateExpressionTest, DoesResultInNullIfGivenNullishInput) { << "timezone" << "Europe/London")); dateExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); - ASSERT_VALUE_EQ(Value(BSONNULL), dateExp->evaluate(contextDoc)); + ASSERT_VALUE_EQ(Value(BSONNULL), dateExp->evaluate(contextDoc, &expCtx->variables)); } } @@ -707,7 +712,7 @@ TEST_F(ExpressionDateToStringTest, ReturnsOnNullValueWhenInputIsNullish) { << "onNull" << "null default")); auto dateExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); - ASSERT_VALUE_EQ(Value("null default"_sd), dateExp->evaluate(Document{})); + ASSERT_VALUE_EQ(Value("null default"_sd), dateExp->evaluate({}, &expCtx->variables)); spec = BSON("$dateToString" << BSON("format" << "%Y-%m-%d" @@ -716,7 +721,7 @@ TEST_F(ExpressionDateToStringTest, ReturnsOnNullValueWhenInputIsNullish) { << "onNull" << BSONNULL)); dateExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); - ASSERT_VALUE_EQ(Value(BSONNULL), dateExp->evaluate(Document{})); + ASSERT_VALUE_EQ(Value(BSONNULL), dateExp->evaluate({}, &expCtx->variables)); spec = BSON("$dateToString" << BSON("format" << "%Y-%m-%d" @@ -725,7 +730,7 @@ TEST_F(ExpressionDateToStringTest, ReturnsOnNullValueWhenInputIsNullish) { << "onNull" << "null default")); dateExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); - ASSERT_VALUE_EQ(Value("null default"_sd), dateExp->evaluate(Document{})); + ASSERT_VALUE_EQ(Value("null default"_sd), dateExp->evaluate({}, &expCtx->variables)); spec = BSON("$dateToString" << BSON("format" << "%Y-%m-%d" @@ -734,7 +739,7 @@ TEST_F(ExpressionDateToStringTest, ReturnsOnNullValueWhenInputIsNullish) { << "onNull" << "$alsoMissing")); dateExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); - ASSERT_VALUE_EQ(Value(), dateExp->evaluate(Document{})); + ASSERT_VALUE_EQ(Value(), dateExp->evaluate({}, &expCtx->variables)); } TEST_F(ExpressionDateToStringTest, ReturnsNullIfInputDateIsNullish) { @@ -742,8 +747,9 @@ TEST_F(ExpressionDateToStringTest, ReturnsNullIfInputDateIsNullish) { auto spec = fromjson("{$dateToString: {date: '$date'}}"); auto dateExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); - ASSERT_VALUE_EQ(Value(BSONNULL), dateExp->evaluate(Document{{"date", BSONNULL}})); - ASSERT_VALUE_EQ(Value(BSONNULL), dateExp->evaluate(Document{})); + ASSERT_VALUE_EQ(Value(BSONNULL), + dateExp->evaluate(Document{{"date", BSONNULL}}, &expCtx->variables)); + ASSERT_VALUE_EQ(Value(BSONNULL), dateExp->evaluate({}, &expCtx->variables)); } TEST_F(ExpressionDateToStringTest, ReturnsNullIfFormatIsNullish) { @@ -751,9 +757,11 @@ TEST_F(ExpressionDateToStringTest, ReturnsNullIfFormatIsNullish) { auto spec = fromjson("{$dateToString: {date: '$date', format: '$format'}}"); auto dateExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); + ASSERT_VALUE_EQ( + Value(BSONNULL), + dateExp->evaluate(Document{{"date", Date_t{}}, {"format", BSONNULL}}, &expCtx->variables)); ASSERT_VALUE_EQ(Value(BSONNULL), - dateExp->evaluate(Document{{"date", Date_t{}}, {"format", BSONNULL}})); - ASSERT_VALUE_EQ(Value(BSONNULL), dateExp->evaluate(Document{{"date", Date_t{}}})); + dateExp->evaluate(Document{{"date", Date_t{}}}, &expCtx->variables)); } TEST_F(ExpressionDateToStringTest, UsesDefaultFormatIfNoneSpecified) { @@ -762,7 +770,7 @@ TEST_F(ExpressionDateToStringTest, UsesDefaultFormatIfNoneSpecified) { auto spec = fromjson("{$dateToString: {date: '$date'}}"); auto dateExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); ASSERT_VALUE_EQ(Value("1970-01-01T00:00:00.000Z"_sd), - dateExp->evaluate(Document{{"date", Date_t{}}})); + dateExp->evaluate(Document{{"date", Date_t{}}}, &expCtx->variables)); } TEST_F(ExpressionDateToStringTest, FailsForInvalidTimezoneRegardlessOfInputDate) { @@ -770,11 +778,14 @@ TEST_F(ExpressionDateToStringTest, FailsForInvalidTimezoneRegardlessOfInputDate) auto spec = fromjson("{$dateToString: {date: '$date', timezone: '$tz'}}"); auto dateExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); - ASSERT_THROWS_CODE(dateExp->evaluate(Document{{"date", BSONNULL}, {"tz", "invalid"_sd}}), - AssertionException, - 40485); ASSERT_THROWS_CODE( - dateExp->evaluate(Document{{"date", BSONNULL}, {"tz", 5}}), AssertionException, 40517); + dateExp->evaluate(Document{{"date", BSONNULL}, {"tz", "invalid"_sd}}, &expCtx->variables), + AssertionException, + 40485); + ASSERT_THROWS_CODE( + dateExp->evaluate(Document{{"date", BSONNULL}, {"tz", 5}}, &expCtx->variables), + AssertionException, + 40517); } TEST_F(ExpressionDateToStringTest, FailsForInvalidFormatStrings) { @@ -783,12 +794,12 @@ TEST_F(ExpressionDateToStringTest, FailsForInvalidFormatStrings) { auto spec = BSON("$dateToString" << BSON("date" << Date_t{} << "format" << "%n")); auto dateExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); - ASSERT_THROWS_CODE(dateExp->evaluate({}), AssertionException, 18536); + ASSERT_THROWS_CODE(dateExp->evaluate({}, &expCtx->variables), AssertionException, 18536); spec = BSON("$dateToString" << BSON("date" << Date_t{} << "format" << "%Y%")); dateExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); - ASSERT_THROWS_CODE(dateExp->evaluate({}), AssertionException, 18535); + ASSERT_THROWS_CODE(dateExp->evaluate({}, &expCtx->variables), AssertionException, 18535); } TEST_F(ExpressionDateToStringTest, FailsForInvalidFormatRegardlessOfInputDate) { @@ -797,16 +808,21 @@ TEST_F(ExpressionDateToStringTest, FailsForInvalidFormatRegardlessOfInputDate) { auto spec = fromjson("{$dateToString: {date: '$date', format: '$format', onNull: 0}}"); auto dateExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); ASSERT_THROWS_CODE( - dateExp->evaluate(Document{{"date", BSONNULL}, {"format", 5}}), AssertionException, 18533); - ASSERT_THROWS_CODE(dateExp->evaluate(Document{{"date", BSONNULL}, {"format", "%n"_sd}}), - AssertionException, - 18536); - ASSERT_THROWS_CODE(dateExp->evaluate(Document{{"date", BSONNULL}, {"format", "%"_sd}}), - AssertionException, - 18535); - ASSERT_THROWS_CODE(dateExp->evaluate(Document{{"date", "Invalid date"_sd}, {"format", 5}}), - AssertionException, - 18533); + dateExp->evaluate(Document{{"date", BSONNULL}, {"format", 5}}, &expCtx->variables), + AssertionException, + 18533); + ASSERT_THROWS_CODE( + dateExp->evaluate(Document{{"date", BSONNULL}, {"format", "%n"_sd}}, &expCtx->variables), + AssertionException, + 18536); + ASSERT_THROWS_CODE( + dateExp->evaluate(Document{{"date", BSONNULL}, {"format", "%"_sd}}, &expCtx->variables), + AssertionException, + 18535); + ASSERT_THROWS_CODE( + dateExp->evaluate(Document{{"date", "Invalid date"_sd}, {"format", 5}}, &expCtx->variables), + AssertionException, + 18533); } } // namespace ExpressionDateToStringTest @@ -894,7 +910,7 @@ TEST_F(ExpressionDateFromStringTest, OptimizesToConstantIfAllInputsAreConstant) ASSERT(dynamic_cast(dateExp->optimize().get())); Date_t dateVal = Date_t::fromMillisSinceEpoch(1499173797000); - ASSERT_VALUE_EQ(Value(dateVal), dateExp->evaluate(Document{})); + ASSERT_VALUE_EQ(Value(dateVal), dateExp->evaluate({}, &expCtx->variables)); spec = BSON("$dateFromString" << BSON("dateString" << "2017-07-04T13:09:57" @@ -914,7 +930,7 @@ TEST_F(ExpressionDateFromStringTest, OptimizesToConstantIfAllInputsAreConstant) ASSERT(dynamic_cast(dateExp->optimize().get())); dateVal = Date_t::fromMillisSinceEpoch(1499170197000); - ASSERT_VALUE_EQ(Value(dateVal), dateExp->evaluate(Document{})); + ASSERT_VALUE_EQ(Value(dateVal), dateExp->evaluate({}, &expCtx->variables)); spec = BSON("$dateFromString" << BSON("dateString" << "2017-07-04T13:09:57" @@ -986,7 +1002,9 @@ TEST_F(ExpressionDateFromStringTest, RejectsUnparsableString) { auto spec = BSON("$dateFromString" << BSON("dateString" << "60.Monday1770/06:59")); auto dateExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); - ASSERT_THROWS_CODE(dateExp->evaluate({}), AssertionException, ErrorCodes::ConversionFailure); + ASSERT_THROWS_CODE(dateExp->evaluate({}, &expCtx->variables), + AssertionException, + ErrorCodes::ConversionFailure); } TEST_F(ExpressionDateFromStringTest, RejectsTimeZoneInString) { @@ -995,12 +1013,16 @@ TEST_F(ExpressionDateFromStringTest, RejectsTimeZoneInString) { auto spec = BSON("$dateFromString" << BSON("dateString" << "2017-07-13T10:02:57 Europe/London")); auto dateExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); - ASSERT_THROWS_CODE(dateExp->evaluate({}), AssertionException, ErrorCodes::ConversionFailure); + ASSERT_THROWS_CODE(dateExp->evaluate({}, &expCtx->variables), + AssertionException, + ErrorCodes::ConversionFailure); spec = BSON("$dateFromString" << BSON("dateString" << "July 4, 2017 Europe/London")); dateExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); - ASSERT_THROWS_CODE(dateExp->evaluate({}), AssertionException, ErrorCodes::ConversionFailure); + ASSERT_THROWS_CODE(dateExp->evaluate({}, &expCtx->variables), + AssertionException, + ErrorCodes::ConversionFailure); } TEST_F(ExpressionDateFromStringTest, RejectsTimeZoneInStringAndArgument) { @@ -1012,7 +1034,9 @@ TEST_F(ExpressionDateFromStringTest, RejectsTimeZoneInStringAndArgument) { << "timezone" << "Europe/London")); auto dateExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); - ASSERT_THROWS_CODE(dateExp->evaluate({}), AssertionException, ErrorCodes::ConversionFailure); + ASSERT_THROWS_CODE(dateExp->evaluate({}, &expCtx->variables), + AssertionException, + ErrorCodes::ConversionFailure); // Test with timezone abbreviation and timezone spec = BSON("$dateFromString" << BSON("dateString" @@ -1020,7 +1044,9 @@ TEST_F(ExpressionDateFromStringTest, RejectsTimeZoneInStringAndArgument) { << "timezone" << "Europe/London")); dateExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); - ASSERT_THROWS_CODE(dateExp->evaluate({}), AssertionException, ErrorCodes::ConversionFailure); + ASSERT_THROWS_CODE(dateExp->evaluate({}, &expCtx->variables), + AssertionException, + ErrorCodes::ConversionFailure); // Test with GMT offset and timezone spec = BSON("$dateFromString" << BSON("dateString" @@ -1028,7 +1054,9 @@ TEST_F(ExpressionDateFromStringTest, RejectsTimeZoneInStringAndArgument) { << "timezone" << "Europe/London")); dateExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); - ASSERT_THROWS_CODE(dateExp->evaluate({}), AssertionException, ErrorCodes::ConversionFailure); + ASSERT_THROWS_CODE(dateExp->evaluate({}, &expCtx->variables), + AssertionException, + ErrorCodes::ConversionFailure); // Test with GMT offset and GMT timezone spec = BSON("$dateFromString" << BSON("dateString" @@ -1036,7 +1064,9 @@ TEST_F(ExpressionDateFromStringTest, RejectsTimeZoneInStringAndArgument) { << "timezone" << "GMT")); dateExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); - ASSERT_THROWS_CODE(dateExp->evaluate({}), AssertionException, ErrorCodes::ConversionFailure); + ASSERT_THROWS_CODE(dateExp->evaluate({}, &expCtx->variables), + AssertionException, + ErrorCodes::ConversionFailure); } TEST_F(ExpressionDateFromStringTest, RejectsNonStringFormat) { @@ -1047,14 +1077,14 @@ TEST_F(ExpressionDateFromStringTest, RejectsNonStringFormat) { << "format" << 2)); auto dateExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); - ASSERT_THROWS_CODE(dateExp->evaluate({}), AssertionException, 40684); + ASSERT_THROWS_CODE(dateExp->evaluate({}, &expCtx->variables), AssertionException, 40684); spec = BSON("$dateFromString" << BSON("dateString" << "July 4, 2017" << "format" << true)); dateExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); - ASSERT_THROWS_CODE(dateExp->evaluate({}), AssertionException, 40684); + ASSERT_THROWS_CODE(dateExp->evaluate({}, &expCtx->variables), AssertionException, 40684); } TEST_F(ExpressionDateFromStringTest, RejectsStringsThatDoNotMatchFormat) { @@ -1065,14 +1095,18 @@ TEST_F(ExpressionDateFromStringTest, RejectsStringsThatDoNotMatchFormat) { << "format" << "%Y-%m-%d")); auto dateExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); - ASSERT_THROWS_CODE(dateExp->evaluate({}), AssertionException, ErrorCodes::ConversionFailure); + ASSERT_THROWS_CODE(dateExp->evaluate({}, &expCtx->variables), + AssertionException, + ErrorCodes::ConversionFailure); spec = BSON("$dateFromString" << BSON("dateString" << "2017-07" << "format" << "%m-%Y")); dateExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); - ASSERT_THROWS_CODE(dateExp->evaluate({}), AssertionException, ErrorCodes::ConversionFailure); + ASSERT_THROWS_CODE(dateExp->evaluate({}, &expCtx->variables), + AssertionException, + ErrorCodes::ConversionFailure); } TEST_F(ExpressionDateFromStringTest, EscapeCharacterAllowsPrefixUsage) { @@ -1083,7 +1117,7 @@ TEST_F(ExpressionDateFromStringTest, EscapeCharacterAllowsPrefixUsage) { << "format" << "%Y %% %m %% %d")); auto dateExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); - ASSERT_EQ("2017-01-01T00:00:00.000Z", dateExp->evaluate(Document{}).toString()); + ASSERT_EQ("2017-01-01T00:00:00.000Z", dateExp->evaluate({}, &expCtx->variables).toString()); } @@ -1095,21 +1129,21 @@ TEST_F(ExpressionDateFromStringTest, EvaluatesToNullIfFormatIsNullish) { << "format" << BSONNULL)); auto dateExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); - ASSERT_VALUE_EQ(Value(BSONNULL), dateExp->evaluate(Document{})); + ASSERT_VALUE_EQ(Value(BSONNULL), dateExp->evaluate({}, &expCtx->variables)); spec = BSON("$dateFromString" << BSON("dateString" << "1/1/2017" << "format" << "$missing")); dateExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); - ASSERT_VALUE_EQ(Value(BSONNULL), dateExp->evaluate(Document{})); + ASSERT_VALUE_EQ(Value(BSONNULL), dateExp->evaluate({}, &expCtx->variables)); spec = BSON("$dateFromString" << BSON("dateString" << "1/1/2017" << "format" << BSONUndefined)); dateExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); - ASSERT_VALUE_EQ(Value(BSONNULL), dateExp->evaluate(Document{})); + ASSERT_VALUE_EQ(Value(BSONNULL), dateExp->evaluate({}, &expCtx->variables)); } TEST_F(ExpressionDateFromStringTest, ReadWithUTCOffset) { @@ -1120,35 +1154,35 @@ TEST_F(ExpressionDateFromStringTest, ReadWithUTCOffset) { << "timezone" << "-01:00")); auto dateExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); - ASSERT_EQ("2017-07-28T11:47:52.912Z", dateExp->evaluate(Document{}).toString()); + ASSERT_EQ("2017-07-28T11:47:52.912Z", dateExp->evaluate({}, &expCtx->variables).toString()); spec = BSON("$dateFromString" << BSON("dateString" << "2017-07-28T10:47:52.912" << "timezone" << "+01:00")); dateExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); - ASSERT_EQ("2017-07-28T09:47:52.912Z", dateExp->evaluate(Document{}).toString()); + ASSERT_EQ("2017-07-28T09:47:52.912Z", dateExp->evaluate({}, &expCtx->variables).toString()); spec = BSON("$dateFromString" << BSON("dateString" << "2017-07-28T10:47:52.912" << "timezone" << "+0445")); dateExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); - ASSERT_EQ("2017-07-28T06:02:52.912Z", dateExp->evaluate(Document{}).toString()); + ASSERT_EQ("2017-07-28T06:02:52.912Z", dateExp->evaluate({}, &expCtx->variables).toString()); spec = BSON("$dateFromString" << BSON("dateString" << "2017-07-28T10:47:52.912" << "timezone" << "+10:45")); dateExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); - ASSERT_EQ("2017-07-28T00:02:52.912Z", dateExp->evaluate(Document{}).toString()); + ASSERT_EQ("2017-07-28T00:02:52.912Z", dateExp->evaluate({}, &expCtx->variables).toString()); spec = BSON("$dateFromString" << BSON("dateString" << "1945-07-28T10:47:52.912" << "timezone" << "-08:00")); dateExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); - ASSERT_EQ("1945-07-28T18:47:52.912Z", dateExp->evaluate(Document{}).toString()); + ASSERT_EQ("1945-07-28T18:47:52.912Z", dateExp->evaluate({}, &expCtx->variables).toString()); } TEST_F(ExpressionDateFromStringTest, ConvertStringWithUTCOffsetAndFormat) { @@ -1161,7 +1195,7 @@ TEST_F(ExpressionDateFromStringTest, ConvertStringWithUTCOffsetAndFormat) { << "format" << "%H:%M:%S.%L on %m/%d/%Y")); auto dateExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); - ASSERT_EQ("2017-07-28T11:47:52.912Z", dateExp->evaluate(Document{}).toString()); + ASSERT_EQ("2017-07-28T11:47:52.912Z", dateExp->evaluate({}, &expCtx->variables).toString()); spec = BSON("$dateFromString" << BSON("dateString" << "10:47:52.912 on 7/28/2017" @@ -1170,7 +1204,7 @@ TEST_F(ExpressionDateFromStringTest, ConvertStringWithUTCOffsetAndFormat) { << "format" << "%H:%M:%S.%L on %m/%d/%Y")); dateExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); - ASSERT_EQ("2017-07-28T09:47:52.912Z", dateExp->evaluate(Document{}).toString()); + ASSERT_EQ("2017-07-28T09:47:52.912Z", dateExp->evaluate({}, &expCtx->variables).toString()); } TEST_F(ExpressionDateFromStringTest, ConvertStringWithISODateFormat) { @@ -1181,7 +1215,7 @@ TEST_F(ExpressionDateFromStringTest, ConvertStringWithISODateFormat) { << "format" << "Day %u Week %V Year %G")); auto dateExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); - ASSERT_EQ("2018-01-07T00:00:00.000Z", dateExp->evaluate(Document{}).toString()); + ASSERT_EQ("2018-01-07T00:00:00.000Z", dateExp->evaluate({}, &expCtx->variables).toString()); // Week and day of week default to '1' if not specified. spec = BSON("$dateFromString" << BSON("dateString" @@ -1189,14 +1223,14 @@ TEST_F(ExpressionDateFromStringTest, ConvertStringWithISODateFormat) { << "format" << "Week %V Year %G")); dateExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); - ASSERT_EQ("2018-01-01T00:00:00.000Z", dateExp->evaluate(Document{}).toString()); + ASSERT_EQ("2018-01-01T00:00:00.000Z", dateExp->evaluate({}, &expCtx->variables).toString()); spec = BSON("$dateFromString" << BSON("dateString" << "Day 7 Year 2017" << "format" << "Day %u Year %G")); dateExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); - ASSERT_EQ("2017-01-08T00:00:00.000Z", dateExp->evaluate(Document{}).toString()); + ASSERT_EQ("2017-01-08T00:00:00.000Z", dateExp->evaluate({}, &expCtx->variables).toString()); } TEST_F(ExpressionDateFromStringTest, ReturnsOnNullForNullishInput) { @@ -1205,25 +1239,25 @@ TEST_F(ExpressionDateFromStringTest, ReturnsOnNullForNullishInput) { auto spec = BSON("$dateFromString" << BSON("dateString" << BSONNULL << "onNull" << "Null default")); auto dateExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); - ASSERT_VALUE_EQ(Value("Null default"_sd), dateExp->evaluate(Document{})); + ASSERT_VALUE_EQ(Value("Null default"_sd), dateExp->evaluate({}, &expCtx->variables)); spec = BSON("$dateFromString" << BSON("dateString" << "$missing" << "onNull" << "Null default")); dateExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); - ASSERT_VALUE_EQ(Value("Null default"_sd), dateExp->evaluate(Document{})); + ASSERT_VALUE_EQ(Value("Null default"_sd), dateExp->evaluate({}, &expCtx->variables)); spec = BSON("$dateFromString" << BSON("dateString" << "$missing" << "onNull" << "$alsoMissing")); dateExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); - ASSERT_VALUE_EQ(Value(), dateExp->evaluate(Document{})); + ASSERT_VALUE_EQ(Value(), dateExp->evaluate({}, &expCtx->variables)); spec = BSON("$dateFromString" << BSON("dateString" << BSONNULL << "onNull" << BSONNULL)); dateExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); - ASSERT_VALUE_EQ(Value(BSONNULL), dateExp->evaluate(Document{})); + ASSERT_VALUE_EQ(Value(BSONNULL), dateExp->evaluate({}, &expCtx->variables)); } TEST_F(ExpressionDateFromStringTest, InvalidFormatTakesPrecedenceOverOnNull) { @@ -1234,14 +1268,14 @@ TEST_F(ExpressionDateFromStringTest, InvalidFormatTakesPrecedenceOverOnNull) { << "format" << 5)); auto dateExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); - ASSERT_THROWS_CODE(dateExp->evaluate({}), AssertionException, 40684); + ASSERT_THROWS_CODE(dateExp->evaluate({}, &expCtx->variables), AssertionException, 40684); spec = BSON("$dateFromString" << BSON("dateString" << BSONNULL << "onNull" << "Null default" << "format" << "%")); dateExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); - ASSERT_THROWS_CODE(dateExp->evaluate({}), AssertionException, 18535); + ASSERT_THROWS_CODE(dateExp->evaluate({}, &expCtx->variables), AssertionException, 18535); } TEST_F(ExpressionDateFromStringTest, InvalidFormatTakesPrecedenceOverOnError) { @@ -1254,14 +1288,14 @@ TEST_F(ExpressionDateFromStringTest, InvalidFormatTakesPrecedenceOverOnError) { << "format" << 5)); auto dateExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); - ASSERT_THROWS_CODE(dateExp->evaluate({}), AssertionException, 40684); + ASSERT_THROWS_CODE(dateExp->evaluate({}, &expCtx->variables), AssertionException, 40684); spec = BSON("$dateFromString" << BSON("dateString" << 5 << "onError" << "Not used default" << "format" << "%")); dateExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); - ASSERT_THROWS_CODE(dateExp->evaluate({}), AssertionException, 18535); + ASSERT_THROWS_CODE(dateExp->evaluate({}, &expCtx->variables), AssertionException, 18535); } TEST_F(ExpressionDateFromStringTest, InvalidTimezoneTakesPrecedenceOverOnNull) { @@ -1272,14 +1306,14 @@ TEST_F(ExpressionDateFromStringTest, InvalidTimezoneTakesPrecedenceOverOnNull) { << "timezone" << 5)); auto dateExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); - ASSERT_THROWS_CODE(dateExp->evaluate({}), AssertionException, 40517); + ASSERT_THROWS_CODE(dateExp->evaluate({}, &expCtx->variables), AssertionException, 40517); spec = BSON("$dateFromString" << BSON("dateString" << BSONNULL << "onNull" << "Null default" << "timezone" << "invalid timezone string")); dateExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); - ASSERT_THROWS_CODE(dateExp->evaluate({}), AssertionException, 40485); + ASSERT_THROWS_CODE(dateExp->evaluate({}, &expCtx->variables), AssertionException, 40485); } TEST_F(ExpressionDateFromStringTest, InvalidTimezoneTakesPrecedenceOverOnError) { @@ -1292,14 +1326,14 @@ TEST_F(ExpressionDateFromStringTest, InvalidTimezoneTakesPrecedenceOverOnError) << "timezone" << 5)); auto dateExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); - ASSERT_THROWS_CODE(dateExp->evaluate({}), AssertionException, 40517); + ASSERT_THROWS_CODE(dateExp->evaluate({}, &expCtx->variables), AssertionException, 40517); spec = BSON("$dateFromString" << BSON("dateString" << 5 << "onError" << "On error default" << "timezone" << "invalid timezone string")); dateExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); - ASSERT_THROWS_CODE(dateExp->evaluate({}), AssertionException, 40485); + ASSERT_THROWS_CODE(dateExp->evaluate({}, &expCtx->variables), AssertionException, 40485); } TEST_F(ExpressionDateFromStringTest, OnNullTakesPrecedenceOverOtherNullishParameters) { @@ -1310,14 +1344,14 @@ TEST_F(ExpressionDateFromStringTest, OnNullTakesPrecedenceOverOtherNullishParame << "timezone" << BSONNULL)); auto dateExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); - ASSERT_VALUE_EQ(Value("Null default"_sd), dateExp->evaluate(Document{})); + ASSERT_VALUE_EQ(Value("Null default"_sd), dateExp->evaluate({}, &expCtx->variables)); spec = BSON("$dateFromString" << BSON("dateString" << BSONNULL << "onNull" << "Null default" << "format" << BSONNULL)); dateExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); - ASSERT_VALUE_EQ(Value("Null default"_sd), dateExp->evaluate(Document{})); + ASSERT_VALUE_EQ(Value("Null default"_sd), dateExp->evaluate({}, &expCtx->variables)); } TEST_F(ExpressionDateFromStringTest, OnNullOnlyUsedIfInputStringIsNullish) { @@ -1330,7 +1364,7 @@ TEST_F(ExpressionDateFromStringTest, OnNullOnlyUsedIfInputStringIsNullish) { << "timezone" << BSONNULL)); auto dateExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); - ASSERT_VALUE_EQ(Value(BSONNULL), dateExp->evaluate(Document{})); + ASSERT_VALUE_EQ(Value(BSONNULL), dateExp->evaluate({}, &expCtx->variables)); spec = BSON("$dateFromString" << BSON("dateString" << "2018-02-14" @@ -1339,7 +1373,7 @@ TEST_F(ExpressionDateFromStringTest, OnNullOnlyUsedIfInputStringIsNullish) { << "format" << BSONNULL)); dateExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); - ASSERT_VALUE_EQ(Value(BSONNULL), dateExp->evaluate(Document{})); + ASSERT_VALUE_EQ(Value(BSONNULL), dateExp->evaluate({}, &expCtx->variables)); } TEST_F(ExpressionDateFromStringTest, ReturnsOnErrorForParseFailures) { @@ -1351,7 +1385,7 @@ TEST_F(ExpressionDateFromStringTest, ReturnsOnErrorForParseFailures) { auto spec = BSON("$dateFromString" << BSON("dateString" << date << "onError" << "Error default")); auto dateExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); - ASSERT_VALUE_EQ(Value("Error default"_sd), dateExp->evaluate(Document{})); + ASSERT_VALUE_EQ(Value("Error default"_sd), dateExp->evaluate({}, &expCtx->variables)); } } @@ -1365,7 +1399,7 @@ TEST_F(ExpressionDateFromStringTest, ReturnsOnErrorForFormatMismatch) { BSON("$dateFromString" << BSON("dateString" << date << "format" << format << "onError" << "Error default")); auto dateExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); - ASSERT_VALUE_EQ(Value("Error default"_sd), dateExp->evaluate(Document{})); + ASSERT_VALUE_EQ(Value("Error default"_sd), dateExp->evaluate({}, &expCtx->variables)); } } @@ -1377,9 +1411,10 @@ TEST_F(ExpressionDateFromStringTest, OnNullEvaluatedLazily) { << "onNull" << BSON("$divide" << BSON_ARRAY(1 << 0)))); auto dateExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); - ASSERT_EQ("2018-02-14T00:00:00.000Z", - dateExp->evaluate(Document{{"date", "2018-02-14"_sd}}).toString()); - ASSERT_THROWS_CODE(dateExp->evaluate(Document{}), AssertionException, 16608); + ASSERT_EQ( + "2018-02-14T00:00:00.000Z", + dateExp->evaluate(Document{{"date", "2018-02-14"_sd}}, &expCtx->variables).toString()); + ASSERT_THROWS_CODE(dateExp->evaluate({}, &expCtx->variables), AssertionException, 16608); } TEST_F(ExpressionDateFromStringTest, OnErrorEvaluatedLazily) { @@ -1390,9 +1425,11 @@ TEST_F(ExpressionDateFromStringTest, OnErrorEvaluatedLazily) { << "onError" << BSON("$divide" << BSON_ARRAY(1 << 0)))); auto dateExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); - ASSERT_EQ("2018-02-14T00:00:00.000Z", - dateExp->evaluate(Document{{"date", "2018-02-14"_sd}}).toString()); - ASSERT_THROWS_CODE(dateExp->evaluate(Document{{"date", 5}}), AssertionException, 16608); + ASSERT_EQ( + "2018-02-14T00:00:00.000Z", + dateExp->evaluate(Document{{"date", "2018-02-14"_sd}}, &expCtx->variables).toString()); + ASSERT_THROWS_CODE( + dateExp->evaluate(Document{{"date", 5}}, &expCtx->variables), AssertionException, 16608); } } // namespace ExpressionDateFromStringTest diff --git a/src/mongo/db/pipeline/expression_test.cpp b/src/mongo/db/pipeline/expression_test.cpp index 029320c1b2f..8552a8137cf 100644 --- a/src/mongo/db/pipeline/expression_test.cpp +++ b/src/mongo/db/pipeline/expression_test.cpp @@ -65,7 +65,7 @@ static Value evaluateExpression(const string& expressionName, VariablesParseState vps = expCtx->variablesParseState; const BSONObj obj = BSON(expressionName << ImplicitValue::convertToValue(operands)); auto expression = Expression::parseExpression(expCtx, obj, vps); - Value result = expression->evaluate(Document()); + Value result = expression->evaluate({}, &expCtx->variables); return result; } @@ -78,7 +78,7 @@ static Value evaluateNamedArgExpression(const string& expressionName, const Docu VariablesParseState vps = expCtx->variablesParseState; const BSONObj obj = BSON(expressionName << operand); auto expression = Expression::parseExpression(expCtx, obj, vps); - Value result = expression->evaluate(Document()); + Value result = expression->evaluate({}, &expCtx->variables); return result; } @@ -175,8 +175,9 @@ class ExpressionNaryTestOneArg : public ExpressionBaseTest { public: virtual void assertEvaluates(Value input, Value output) { addOperand(_expr, input); - ASSERT_VALUE_EQ(output, _expr->evaluate(Document())); - ASSERT_EQUALS(output.getType(), _expr->evaluate(Document()).getType()); + ASSERT_VALUE_EQ(output, _expr->evaluate({}, &_expr->getExpressionContext()->variables)); + ASSERT_EQUALS(output.getType(), + _expr->evaluate({}, &_expr->getExpressionContext()->variables).getType()); } intrusive_ptr _expr; @@ -187,8 +188,9 @@ public: virtual void assertEvaluates(Value input1, Value input2, Value output) { addOperand(_expr, input1); addOperand(_expr, input2); - ASSERT_VALUE_EQ(output, _expr->evaluate(Document())); - ASSERT_EQUALS(output.getType(), _expr->evaluate(Document()).getType()); + ASSERT_VALUE_EQ(output, _expr->evaluate({}, &_expr->getExpressionContext()->variables)); + ASSERT_EQUALS(output.getType(), + _expr->evaluate({}, &_expr->getExpressionContext()->variables).getType()); } intrusive_ptr _expr; @@ -199,13 +201,13 @@ public: /** A dummy child of ExpressionNary used for testing. */ class Testable : public ExpressionNary { public: - virtual Value evaluate(const Document& root) const { + virtual Value evaluate(const Document& root, Variables* variables) 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 values; for (auto&& child : _children) - values.push_back(child->evaluate(root)); + values.push_back(child->evaluate(root, variables)); return Value(values); } @@ -1380,7 +1382,7 @@ public: intrusive_ptr expCtx(new ExpressionContextForTest()); intrusive_ptr expression = new ExpressionAdd(expCtx); populateOperands(expression); - ASSERT_BSONOBJ_EQ(expectedResult(), toBson(expression->evaluate(Document()))); + ASSERT_BSONOBJ_EQ(expectedResult(), toBson(expression->evaluate({}, &expCtx->variables))); } protected: @@ -1396,7 +1398,7 @@ public: intrusive_ptr expCtx(new ExpressionContextForTest()); intrusive_ptr expression = new ExpressionAdd(expCtx); expression->addOperand(ExpressionConstant::create(expCtx, Value(2))); - ASSERT_BSONOBJ_EQ(BSON("" << 2), toBson(expression->evaluate(Document()))); + ASSERT_BSONOBJ_EQ(BSON("" << 2), toBson(expression->evaluate({}, &expCtx->variables))); } }; @@ -1415,7 +1417,7 @@ public: intrusive_ptr expCtx(new ExpressionContextForTest()); intrusive_ptr expression = new ExpressionAdd(expCtx); expression->addOperand(ExpressionConstant::create(expCtx, Value("a"_sd))); - ASSERT_THROWS(expression->evaluate(Document()), AssertionException); + ASSERT_THROWS(expression->evaluate({}, &expCtx->variables), AssertionException); } }; @@ -1426,7 +1428,7 @@ public: intrusive_ptr expCtx(new ExpressionContextForTest()); intrusive_ptr expression = new ExpressionAdd(expCtx); expression->addOperand(ExpressionConstant::create(expCtx, Value(true))); - ASSERT_THROWS(expression->evaluate(Document()), AssertionException); + ASSERT_THROWS(expression->evaluate({}, &expCtx->variables), AssertionException); } }; @@ -1665,11 +1667,13 @@ public: VariablesParseState vps = expCtx->variablesParseState; intrusive_ptr expression = Expression::parseOperand(expCtx, specElement, vps); ASSERT_BSONOBJ_EQ(constify(spec()), expressionToBson(expression)); - ASSERT_BSONOBJ_EQ(BSON("" << expectedResult()), - toBson(expression->evaluate(fromBson(BSON("a" << 1))))); + ASSERT_BSONOBJ_EQ( + BSON("" << expectedResult()), + toBson(expression->evaluate(fromBson(BSON("a" << 1)), &expCtx->variables))); intrusive_ptr optimized = expression->optimize(); - ASSERT_BSONOBJ_EQ(BSON("" << expectedResult()), - toBson(optimized->evaluate(fromBson(BSON("a" << 1))))); + ASSERT_BSONOBJ_EQ( + BSON("" << expectedResult()), + toBson(optimized->evaluate(fromBson(BSON("a" << 1)), &expCtx->variables))); } protected: @@ -1960,7 +1964,7 @@ public: intrusive_ptr expCtx(new ExpressionContextForTest()); intrusive_ptr nested = ExpressionConstant::create(expCtx, Value(5)); intrusive_ptr expression = ExpressionCoerceToBool::create(expCtx, nested); - ASSERT(expression->evaluate(Document()).getBool()); + ASSERT(expression->evaluate({}, &expCtx->variables).getBool()); } }; @@ -1971,7 +1975,7 @@ public: intrusive_ptr expCtx(new ExpressionContextForTest()); intrusive_ptr nested = ExpressionConstant::create(expCtx, Value(0)); intrusive_ptr expression = ExpressionCoerceToBool::create(expCtx, nested); - ASSERT(!expression->evaluate(Document()).getBool()); + ASSERT(!expression->evaluate({}, &expCtx->variables).getBool()); } }; @@ -2079,10 +2083,10 @@ public: // Check expression spec round trip. ASSERT_BSONOBJ_EQ(constify(spec()), expressionToBson(expression)); // Check evaluation result. - ASSERT_BSONOBJ_EQ(expectedResult(), toBson(expression->evaluate(Document()))); + ASSERT_BSONOBJ_EQ(expectedResult(), toBson(expression->evaluate({}, &expCtx->variables))); // Check that the result is the same after optimizing. intrusive_ptr optimized = expression->optimize(); - ASSERT_BSONOBJ_EQ(expectedResult(), toBson(optimized->evaluate(Document()))); + ASSERT_BSONOBJ_EQ(expectedResult(), toBson(optimized->evaluate({}, &expCtx->variables))); } protected: @@ -2319,7 +2323,7 @@ public: intrusive_ptr expCtx(new ExpressionContextForTest()); VariablesParseState vps = expCtx->variablesParseState; intrusive_ptr expression = Expression::parseOperand(expCtx, specElement, vps); - ASSERT_VALUE_EQ(expression->evaluate(Document()), Value(true)); + ASSERT_VALUE_EQ(expression->evaluate({}, &expCtx->variables), Value(true)); } }; @@ -2452,7 +2456,7 @@ public: void run() { intrusive_ptr expCtx(new ExpressionContextForTest()); intrusive_ptr expression = ExpressionConstant::create(expCtx, Value(5)); - assertBinaryEqual(BSON("" << 5), toBson(expression->evaluate(Document()))); + assertBinaryEqual(BSON("" << 5), toBson(expression->evaluate({}, &expCtx->variables))); } }; @@ -2468,7 +2472,7 @@ public: intrusive_ptr expression = ExpressionConstant::parse(expCtx, specElement, vps); assertBinaryEqual(BSON("" << "foo"), - toBson(expression->evaluate(Document()))); + toBson(expression->evaluate({}, &expCtx->variables))); } }; @@ -2534,7 +2538,9 @@ private: TEST(ExpressionConstantTest, ConstantOfValueMissingRemovesField) { intrusive_ptr expCtx(new ExpressionContextForTest()); intrusive_ptr expression = ExpressionConstant::create(expCtx, Value()); - assertBinaryEqual(BSONObj(), toBson(expression->evaluate(Document{{"foo", Value("bar"_sd)}}))); + assertBinaryEqual( + BSONObj(), + toBson(expression->evaluate(Document{{"foo", Value("bar"_sd)}}, &expCtx->variables))); } TEST(ExpressionConstantTest, ConstantOfValueMissingSerializesToRemoveSystemVar) { @@ -2637,11 +2643,11 @@ TEST(ExpressionPowTest, ThrowsWhenBaseZeroAndExpNegative) { VariablesParseState vps = expCtx->variablesParseState; const auto expr = Expression::parseExpression(expCtx, BSON("$pow" << BSON_ARRAY(0 << -5)), vps); - ASSERT_THROWS([&] { expr->evaluate(Document()); }(), AssertionException); + ASSERT_THROWS([&] { expr->evaluate({}, &expCtx->variables); }(), AssertionException); const auto exprWithLong = Expression::parseExpression(expCtx, BSON("$pow" << BSON_ARRAY(0LL << -5LL)), vps); - ASSERT_THROWS([&] { expr->evaluate(Document()); }(), AssertionException); + ASSERT_THROWS([&] { expr->evaluate({}, &expCtx->variables); }(), AssertionException); } TEST(ExpressionPowTest, LargeExponentValuesWithBaseOfOne) { @@ -2792,21 +2798,35 @@ TEST(ExpressionIndexOfArray, fromjson("{ $indexOfArray : [ [0, 1, 2, 3, 4, 5, 'val'] , '$x'] }"), expCtx->variablesParseState); auto optimizedIndexOfArray = expIndexOfArray->optimize(); - ASSERT_VALUE_EQ(Value(0), optimizedIndexOfArray->evaluate(Document{{"x", 0}})); - ASSERT_VALUE_EQ(Value(1), optimizedIndexOfArray->evaluate(Document{{"x", 1}})); - ASSERT_VALUE_EQ(Value(2), optimizedIndexOfArray->evaluate(Document{{"x", 2}})); - ASSERT_VALUE_EQ(Value(3), optimizedIndexOfArray->evaluate(Document{{"x", 3}})); - ASSERT_VALUE_EQ(Value(4), optimizedIndexOfArray->evaluate(Document{{"x", 4}})); - ASSERT_VALUE_EQ(Value(5), optimizedIndexOfArray->evaluate(Document{{"x", 5}})); - ASSERT_VALUE_EQ(Value(6), optimizedIndexOfArray->evaluate(Document{{"x", string("val")}})); + ASSERT_VALUE_EQ(Value(0), + optimizedIndexOfArray->evaluate(Document{{"x", 0}}, &expCtx->variables)); + ASSERT_VALUE_EQ(Value(1), + optimizedIndexOfArray->evaluate(Document{{"x", 1}}, &expCtx->variables)); + ASSERT_VALUE_EQ(Value(2), + optimizedIndexOfArray->evaluate(Document{{"x", 2}}, &expCtx->variables)); + ASSERT_VALUE_EQ(Value(3), + optimizedIndexOfArray->evaluate(Document{{"x", 3}}, &expCtx->variables)); + ASSERT_VALUE_EQ(Value(4), + optimizedIndexOfArray->evaluate(Document{{"x", 4}}, &expCtx->variables)); + ASSERT_VALUE_EQ(Value(5), + optimizedIndexOfArray->evaluate(Document{{"x", 5}}, &expCtx->variables)); + ASSERT_VALUE_EQ( + Value(6), + optimizedIndexOfArray->evaluate(Document{{"x", string("val")}}, &expCtx->variables)); auto optimizedIndexNotFound = optimizedIndexOfArray->optimize(); // Should evaluate to -1 if not found. - ASSERT_VALUE_EQ(Value(-1), optimizedIndexNotFound->evaluate(Document{{"x", 10}})); - ASSERT_VALUE_EQ(Value(-1), optimizedIndexNotFound->evaluate(Document{{"x", 100}})); - ASSERT_VALUE_EQ(Value(-1), optimizedIndexNotFound->evaluate(Document{{"x", 1000}})); - ASSERT_VALUE_EQ(Value(-1), optimizedIndexNotFound->evaluate(Document{{"x", string("string")}})); - ASSERT_VALUE_EQ(Value(-1), optimizedIndexNotFound->evaluate(Document{{"x", -1}})); + ASSERT_VALUE_EQ(Value(-1), + optimizedIndexNotFound->evaluate(Document{{"x", 10}}, &expCtx->variables)); + ASSERT_VALUE_EQ(Value(-1), + optimizedIndexNotFound->evaluate(Document{{"x", 100}}, &expCtx->variables)); + ASSERT_VALUE_EQ(Value(-1), + optimizedIndexNotFound->evaluate(Document{{"x", 1000}}, &expCtx->variables)); + ASSERT_VALUE_EQ( + Value(-1), + optimizedIndexNotFound->evaluate(Document{{"x", string("string")}}, &expCtx->variables)); + ASSERT_VALUE_EQ(Value(-1), + optimizedIndexNotFound->evaluate(Document{{"x", -1}}, &expCtx->variables)); } TEST(ExpressionIndexOfArray, @@ -2819,10 +2839,12 @@ TEST(ExpressionIndexOfArray, fromjson("{ $indexOfArray : [ [0, 1, 2, 3, 4, 5] , '$x', 3, 5] }"), expCtx->variablesParseState); auto optimizedIndexOfArray = expIndexOfArray->optimize(); - ASSERT_VALUE_EQ(Value(4), optimizedIndexOfArray->evaluate(Document{{"x", 4}})); + ASSERT_VALUE_EQ(Value(4), + optimizedIndexOfArray->evaluate(Document{{"x", 4}}, &expCtx->variables)); // Should evaluate to -1 if not found in range. - ASSERT_VALUE_EQ(Value(-1), optimizedIndexOfArray->evaluate(Document{{"x", 0}})); + ASSERT_VALUE_EQ(Value(-1), + optimizedIndexOfArray->evaluate(Document{{"x", 0}}, &expCtx->variables)); } TEST(ExpressionIndexOfArray, @@ -2835,8 +2857,9 @@ TEST(ExpressionIndexOfArray, fromjson("{ $indexOfArray : [ [0, 1, 2, 2, 3, 4, 5] , '$x'] }"), expCtx->variablesParseState); auto optimizedIndexOfArrayWithDuplicateValues = expIndexOfArrayWithDuplicateValues->optimize(); - ASSERT_VALUE_EQ(Value(2), - optimizedIndexOfArrayWithDuplicateValues->evaluate(Document{{"x", 2}})); + ASSERT_VALUE_EQ( + Value(2), + optimizedIndexOfArrayWithDuplicateValues->evaluate(Document{{"x", 2}}, &expCtx->variables)); // Duplicate Values in a range. auto expIndexInRangeWithhDuplicateValues = Expression::parseExpression( expCtx, @@ -2845,8 +2868,9 @@ TEST(ExpressionIndexOfArray, expCtx->variablesParseState); auto optimizedIndexInRangeWithDuplcateValues = expIndexInRangeWithhDuplicateValues->optimize(); // Should evaluate to 4. - ASSERT_VALUE_EQ(Value(4), - optimizedIndexInRangeWithDuplcateValues->evaluate(Document{{"x", 2}})); + ASSERT_VALUE_EQ( + Value(4), + optimizedIndexInRangeWithDuplcateValues->evaluate(Document{{"x", 2}}, &expCtx->variables)); } namespace FieldPath { @@ -2885,7 +2909,9 @@ TEST(FieldPath, RemoveOptimizesToMissingValue) { auto optimizedExpr = expression->optimize(); - ASSERT_VALUE_EQ(Value(), optimizedExpr->evaluate(Document(BSON("x" << BSON("y" << 123))))); + ASSERT_VALUE_EQ( + Value(), + optimizedExpr->evaluate(Document(BSON("x" << BSON("y" << 123))), &expCtx->variables)); } TEST(FieldPath, NoOptimizationOnNormalPath) { @@ -3007,7 +3033,7 @@ public: void run() { intrusive_ptr expCtx(new ExpressionContextForTest()); intrusive_ptr expression = ExpressionFieldPath::create(expCtx, "a"); - assertBinaryEqual(fromjson("{}"), toBson(expression->evaluate(Document()))); + assertBinaryEqual(fromjson("{}"), toBson(expression->evaluate({}, &expCtx->variables))); } }; @@ -3017,8 +3043,9 @@ public: void run() { intrusive_ptr expCtx(new ExpressionContextForTest()); intrusive_ptr expression = ExpressionFieldPath::create(expCtx, "a"); - assertBinaryEqual(fromjson("{'':123}"), - toBson(expression->evaluate(fromBson(BSON("a" << 123))))); + assertBinaryEqual( + fromjson("{'':123}"), + toBson(expression->evaluate(fromBson(BSON("a" << 123)), &expCtx->variables))); } }; @@ -3028,8 +3055,9 @@ public: void run() { intrusive_ptr expCtx(new ExpressionContextForTest()); intrusive_ptr expression = ExpressionFieldPath::create(expCtx, "a.b"); - assertBinaryEqual(fromjson("{}"), - toBson(expression->evaluate(fromBson(fromjson("{a:null}"))))); + assertBinaryEqual( + fromjson("{}"), + toBson(expression->evaluate(fromBson(fromjson("{a:null}")), &expCtx->variables))); } }; @@ -3039,8 +3067,9 @@ public: void run() { intrusive_ptr expCtx(new ExpressionContextForTest()); intrusive_ptr expression = ExpressionFieldPath::create(expCtx, "a.b"); - assertBinaryEqual(fromjson("{}"), - toBson(expression->evaluate(fromBson(fromjson("{a:undefined}"))))); + assertBinaryEqual( + fromjson("{}"), + toBson(expression->evaluate(fromBson(fromjson("{a:undefined}")), &expCtx->variables))); } }; @@ -3050,8 +3079,9 @@ public: void run() { intrusive_ptr expCtx(new ExpressionContextForTest()); intrusive_ptr expression = ExpressionFieldPath::create(expCtx, "a.b"); - assertBinaryEqual(fromjson("{}"), - toBson(expression->evaluate(fromBson(fromjson("{z:1}"))))); + assertBinaryEqual( + fromjson("{}"), + toBson(expression->evaluate(fromBson(fromjson("{z:1}")), &expCtx->variables))); } }; @@ -3061,7 +3091,9 @@ public: void run() { intrusive_ptr expCtx(new ExpressionContextForTest()); intrusive_ptr expression = ExpressionFieldPath::create(expCtx, "a.b"); - assertBinaryEqual(fromjson("{}"), toBson(expression->evaluate(fromBson(BSON("a" << 2))))); + assertBinaryEqual( + fromjson("{}"), + toBson(expression->evaluate(fromBson(BSON("a" << 2)), &expCtx->variables))); } }; @@ -3072,7 +3104,8 @@ public: intrusive_ptr expCtx(new ExpressionContextForTest()); intrusive_ptr expression = ExpressionFieldPath::create(expCtx, "a.b"); assertBinaryEqual(BSON("" << 55), - toBson(expression->evaluate(fromBson(BSON("a" << BSON("b" << 55)))))); + toBson(expression->evaluate(fromBson(BSON("a" << BSON("b" << 55))), + &expCtx->variables))); } }; @@ -3082,8 +3115,9 @@ public: void run() { intrusive_ptr expCtx(new ExpressionContextForTest()); intrusive_ptr expression = ExpressionFieldPath::create(expCtx, "a.b"); - assertBinaryEqual(fromjson("{}"), - toBson(expression->evaluate(fromBson(BSON("a" << BSONObj()))))); + assertBinaryEqual( + fromjson("{}"), + toBson(expression->evaluate(fromBson(BSON("a" << BSONObj())), &expCtx->variables))); } }; @@ -3093,8 +3127,9 @@ public: void run() { intrusive_ptr expCtx(new ExpressionContextForTest()); intrusive_ptr expression = ExpressionFieldPath::create(expCtx, "a.b"); - assertBinaryEqual(BSON("" << BSONArray()), - toBson(expression->evaluate(fromBson(BSON("a" << BSONArray()))))); + assertBinaryEqual( + BSON("" << BSONArray()), + toBson(expression->evaluate(fromBson(BSON("a" << BSONArray())), &expCtx->variables))); } }; @@ -3104,8 +3139,9 @@ public: void run() { intrusive_ptr expCtx(new ExpressionContextForTest()); intrusive_ptr expression = ExpressionFieldPath::create(expCtx, "a.b"); - assertBinaryEqual(fromjson("{'':[]}"), - toBson(expression->evaluate(fromBson(fromjson("{a:[null]}"))))); + assertBinaryEqual( + fromjson("{'':[]}"), + toBson(expression->evaluate(fromBson(fromjson("{a:[null]}")), &expCtx->variables))); } }; @@ -3116,7 +3152,8 @@ public: intrusive_ptr expCtx(new ExpressionContextForTest()); intrusive_ptr expression = ExpressionFieldPath::create(expCtx, "a.b"); assertBinaryEqual(fromjson("{'':[]}"), - toBson(expression->evaluate(fromBson(fromjson("{a:[undefined]}"))))); + toBson(expression->evaluate(fromBson(fromjson("{a:[undefined]}")), + &expCtx->variables))); } }; @@ -3126,8 +3163,9 @@ public: void run() { intrusive_ptr expCtx(new ExpressionContextForTest()); intrusive_ptr expression = ExpressionFieldPath::create(expCtx, "a.b"); - assertBinaryEqual(fromjson("{'':[]}"), - toBson(expression->evaluate(fromBson(fromjson("{a:[1]}"))))); + assertBinaryEqual( + fromjson("{'':[]}"), + toBson(expression->evaluate(fromBson(fromjson("{a:[1]}")), &expCtx->variables))); } }; @@ -3137,8 +3175,9 @@ public: void run() { intrusive_ptr expCtx(new ExpressionContextForTest()); intrusive_ptr expression = ExpressionFieldPath::create(expCtx, "a.b"); - assertBinaryEqual(fromjson("{'':[9]}"), - toBson(expression->evaluate(fromBson(fromjson("{a:[{b:9}]}"))))); + assertBinaryEqual( + fromjson("{'':[9]}"), + toBson(expression->evaluate(fromBson(fromjson("{a:[{b:9}]}")), &expCtx->variables))); } }; @@ -3150,7 +3189,8 @@ public: intrusive_ptr expression = ExpressionFieldPath::create(expCtx, "a.b"); assertBinaryEqual(fromjson("{'':[9,20]}"), toBson(expression->evaluate( - fromBson(fromjson("{a:[{b:9},null,undefined,{g:4},{b:20},{}]}"))))); + fromBson(fromjson("{a:[{b:9},null,undefined,{g:4},{b:20},{}]}")), + &expCtx->variables))); } }; @@ -3165,7 +3205,8 @@ public: "{b:{c:3}}," "{b:[{c:4}]}," "{b:[{c:[5]}]}," - "{b:{c:[6,7]}}]}"))))); + "{b:{c:[6,7]}}]}")), + &expCtx->variables))); } }; @@ -3359,19 +3400,22 @@ auto expressionObjectCreateHelper( TEST(ExpressionObjectEvaluate, EmptyObjectShouldEvaluateToEmptyDocument) { intrusive_ptr expCtx(new ExpressionContextForTest()); auto object = expressionObjectCreateHelper(expCtx, {}); - ASSERT_VALUE_EQ(Value(Document()), object->evaluate(Document())); - ASSERT_VALUE_EQ(Value(Document()), object->evaluate(Document{{"a", 1}})); - ASSERT_VALUE_EQ(Value(Document()), object->evaluate(Document{{"_id", "ID"_sd}})); + ASSERT_VALUE_EQ(Value(Document()), object->evaluate(Document(), &(expCtx->variables))); + ASSERT_VALUE_EQ(Value(Document()), object->evaluate(Document{{"a", 1}}, &(expCtx->variables))); + ASSERT_VALUE_EQ(Value(Document()), + object->evaluate(Document{{"_id", "ID"_sd}}, &(expCtx->variables))); } TEST(ExpressionObjectEvaluate, ShouldEvaluateEachField) { intrusive_ptr expCtx(new ExpressionContextForTest()); auto object = expressionObjectCreateHelper(expCtx, {{"a", makeConstant(1)}, {"b", makeConstant(5)}}); - ASSERT_VALUE_EQ(Value(Document{{"a", 1}, {"b", 5}}), object->evaluate(Document())); - ASSERT_VALUE_EQ(Value(Document{{"a", 1}, {"b", 5}}), object->evaluate(Document{{"a", 1}})); ASSERT_VALUE_EQ(Value(Document{{"a", 1}, {"b", 5}}), - object->evaluate(Document{{"_id", "ID"_sd}})); + object->evaluate(Document(), &(expCtx->variables))); + ASSERT_VALUE_EQ(Value(Document{{"a", 1}, {"b", 5}}), + object->evaluate(Document{{"a", 1}}, &(expCtx->variables))); + ASSERT_VALUE_EQ(Value(Document{{"a", 1}, {"b", 5}}), + object->evaluate(Document{{"_id", "ID"_sd}}, &(expCtx->variables))); } TEST(ExpressionObjectEvaluate, OrderOfFieldsInOutputShouldMatchOrderInSpecification) { @@ -3382,7 +3426,8 @@ TEST(ExpressionObjectEvaluate, OrderOfFieldsInOutputShouldMatchOrderInSpecificat {"c", ExpressionFieldPath::create(expCtx, "c")}}); ASSERT_VALUE_EQ( Value(Document{{"a", "A"_sd}, {"b", "B"_sd}, {"c", "C"_sd}}), - object->evaluate(Document{{"c", "C"_sd}, {"a", "A"_sd}, {"b", "B"_sd}, {"_id", "ID"_sd}})); + object->evaluate(Document{{"c", "C"_sd}, {"a", "A"_sd}, {"b", "B"_sd}, {"_id", "ID"_sd}}, + &(expCtx->variables))); } TEST(ExpressionObjectEvaluate, ShouldRemoveFieldsThatHaveMissingValues) { @@ -3391,8 +3436,8 @@ TEST(ExpressionObjectEvaluate, ShouldRemoveFieldsThatHaveMissingValues) { expressionObjectCreateHelper(expCtx, {{"a", ExpressionFieldPath::create(expCtx, "a.b")}, {"b", ExpressionFieldPath::create(expCtx, "missing")}}); - ASSERT_VALUE_EQ(Value(Document{}), object->evaluate(Document())); - ASSERT_VALUE_EQ(Value(Document{}), object->evaluate(Document{{"a", 1}})); + ASSERT_VALUE_EQ(Value(Document{}), object->evaluate(Document(), &(expCtx->variables))); + ASSERT_VALUE_EQ(Value(Document{}), object->evaluate(Document{{"a", 1}}, &(expCtx->variables))); } TEST(ExpressionObjectEvaluate, ShouldEvaluateFieldsWithinNestedObject) { @@ -3403,20 +3448,21 @@ TEST(ExpressionObjectEvaluate, ShouldEvaluateFieldsWithinNestedObject) { expressionObjectCreateHelper( expCtx, {{"b", makeConstant(1)}, {"c", ExpressionFieldPath::create(expCtx, "_id")}})}}); - ASSERT_VALUE_EQ(Value(Document{{"a", Document{{"b", 1}}}}), object->evaluate(Document())); + ASSERT_VALUE_EQ(Value(Document{{"a", Document{{"b", 1}}}}), + object->evaluate(Document(), &(expCtx->variables))); ASSERT_VALUE_EQ(Value(Document{{"a", Document{{"b", 1}, {"c", "ID"_sd}}}}), - object->evaluate(Document{{"_id", "ID"_sd}})); + object->evaluate(Document{{"_id", "ID"_sd}}, &(expCtx->variables))); } TEST(ExpressionObjectEvaluate, ShouldEvaluateToEmptyDocumentIfAllFieldsAreMissing) { intrusive_ptr expCtx(new ExpressionContextForTest()); auto object = expressionObjectCreateHelper( expCtx, {{"a", ExpressionFieldPath::create(expCtx, "missing")}}); - ASSERT_VALUE_EQ(Value(Document{}), object->evaluate(Document())); + ASSERT_VALUE_EQ(Value(Document{}), object->evaluate(Document(), &(expCtx->variables))); auto objectWithNestedObject = expressionObjectCreateHelper(expCtx, {{"nested", object}}); ASSERT_VALUE_EQ(Value(Document{{"nested", Document{}}}), - objectWithNestedObject->evaluate(Document())); + objectWithNestedObject->evaluate(Document(), &(expCtx->variables))); } // @@ -3519,7 +3565,8 @@ TEST(ExpressionObjectOptimizations, OptimizingAnObjectShouldOptimizeSubExpressio auto optimized = object->optimize(); auto optimizedObject = dynamic_cast(optimized.get()); ASSERT_TRUE(optimizedObject); - ASSERT_VALUE_EQ(optimizedObject->evaluate(Document()), Value(BSON("a" << 3))); + ASSERT_VALUE_EQ(optimizedObject->evaluate(Document(), &(expCtx->variables)), + Value(BSON("a" << 3))); }; TEST(ExpressionObjectOptimizations, @@ -3552,7 +3599,7 @@ TEST(ExpressionObjectOptimizations, auto optimizedWithConstant = expressionWithConstantObject->optimize(); auto optimizedObject = dynamic_cast(optimizedWithConstant.get()); ASSERT_TRUE(optimizedObject); - ASSERT_VALUE_EQ(optimizedObject->evaluate(Document()), + ASSERT_VALUE_EQ(optimizedObject->evaluate(Document(), &expCtx->variables), Value(BSON("willBeConstant" << 3 << "alreadyConstant" << "string"))); }; @@ -3571,11 +3618,13 @@ public: VariablesParseState vps = expCtx->variablesParseState; intrusive_ptr expression = Expression::parseOperand(expCtx, specElement, vps); ASSERT_BSONOBJ_EQ(constify(spec()), expressionToBson(expression)); - ASSERT_BSONOBJ_EQ(BSON("" << expectedResult()), - toBson(expression->evaluate(fromBson(BSON("a" << 1))))); + ASSERT_BSONOBJ_EQ( + BSON("" << expectedResult()), + toBson(expression->evaluate(fromBson(BSON("a" << 1)), &expCtx->variables))); intrusive_ptr optimized = expression->optimize(); - ASSERT_BSONOBJ_EQ(BSON("" << expectedResult()), - toBson(optimized->evaluate(fromBson(BSON("a" << 1))))); + ASSERT_BSONOBJ_EQ( + BSON("" << expectedResult()), + toBson(optimized->evaluate(fromBson(BSON("a" << 1)), &expCtx->variables))); } protected: @@ -4083,7 +4132,7 @@ public: VariablesParseState vps = expCtx->variablesParseState; const intrusive_ptr expr = Expression::parseExpression(expCtx, obj, vps); - Value result = expr->evaluate(Document()); + Value result = expr->evaluate({}, &expCtx->variables); if (result.getType() == Array) { result = sortSet(result); } @@ -4110,7 +4159,7 @@ public: // same const intrusive_ptr expr = Expression::parseExpression(expCtx, obj, vps); - expr->evaluate(Document()); + expr->evaluate({}, &expCtx->variables); }(), AssertionException); } @@ -4392,7 +4441,8 @@ private: VariablesParseState vps = expCtx->variablesParseState; intrusive_ptr expression = Expression::parseOperand(expCtx, specElement, vps); ASSERT_BSONOBJ_EQ(constify(spec), expressionToBson(expression)); - ASSERT_BSONOBJ_EQ(BSON("" << expectedResult), toBson(expression->evaluate(Document()))); + ASSERT_BSONOBJ_EQ(BSON("" << expectedResult), + toBson(expression->evaluate({}, &expCtx->variables))); } }; @@ -4518,7 +4568,8 @@ public: VariablesParseState vps = expCtx->variablesParseState; intrusive_ptr expression = Expression::parseOperand(expCtx, specElement, vps); ASSERT_BSONOBJ_EQ(constify(spec()), expressionToBson(expression)); - ASSERT_BSONOBJ_EQ(BSON("" << expectedResult()), toBson(expression->evaluate(Document()))); + ASSERT_BSONOBJ_EQ(BSON("" << expectedResult()), + toBson(expression->evaluate({}, &expCtx->variables))); } protected: @@ -4636,7 +4687,7 @@ TEST(ExpressionSubstrTest, ThrowsWithNegativeStart) { const auto str = "abcdef"_sd; const auto expr = Expression::parseExpression(expCtx, BSON("$substrCP" << BSON_ARRAY(str << -5 << 1)), vps); - ASSERT_THROWS([&] { expr->evaluate(Document()); }(), AssertionException); + ASSERT_THROWS([&] { expr->evaluate({}, &expCtx->variables); }(), AssertionException); } } // namespace Substr @@ -4650,7 +4701,7 @@ TEST(ExpressionSubstrCPTest, DoesThrowWithBadContinuationByte) { const auto continuationByte = "\x80\x00"_sd; const auto expr = Expression::parseExpression( expCtx, BSON("$substrCP" << BSON_ARRAY(continuationByte << 0 << 1)), vps); - ASSERT_THROWS([&] { expr->evaluate(Document()); }(), AssertionException); + ASSERT_THROWS([&] { expr->evaluate({}, &expCtx->variables); }(), AssertionException); } TEST(ExpressionSubstrCPTest, DoesThrowWithInvalidLeadingByte) { @@ -4660,7 +4711,7 @@ TEST(ExpressionSubstrCPTest, DoesThrowWithInvalidLeadingByte) { const auto leadingByte = "\xFF\x00"_sd; const auto expr = Expression::parseExpression( expCtx, BSON("$substrCP" << BSON_ARRAY(leadingByte << 0 << 1)), vps); - ASSERT_THROWS([&] { expr->evaluate(Document()); }(), AssertionException); + ASSERT_THROWS([&] { expr->evaluate({}, &expCtx->variables); }(), AssertionException); } TEST(ExpressionSubstrCPTest, WithStandardValue) { @@ -5110,7 +5161,7 @@ TEST(ExpressionTrimTest, TrimComparisonsShouldNotRespectCollation) { << "x")), expCtx->variablesParseState); - ASSERT_VALUE_EQ(trim->evaluate(Document()), Value("XX"_sd)); + ASSERT_VALUE_EQ(trim->evaluate({}, &expCtx->variables), Value("XX"_sd)); } TEST(ExpressionTrimTest, ShouldRejectInvalidUTFInCharsArgument) { @@ -5383,7 +5434,7 @@ TEST(ExpressionTrimTest, DoesSerializeCorrectly) { // Make sure we can re-parse it and evaluate it. auto reparsedTrim = Expression::parseExpression( expCtx, trim->serialize(false).getDocument().toBson(), expCtx->variablesParseState); - ASSERT_VALUE_EQ(reparsedTrim->evaluate(Document()), Value("abc"_sd)); + ASSERT_VALUE_EQ(reparsedTrim->evaluate({}, &expCtx->variables), Value("abc"_sd)); // Use $ltrim, and specify the 'chars' option. trim = Expression::parseExpression(expCtx, @@ -5399,7 +5450,8 @@ TEST(ExpressionTrimTest, DoesSerializeCorrectly) { // Make sure we can re-parse it and evaluate it. reparsedTrim = Expression::parseExpression( expCtx, trim->serialize(false).getDocument().toBson(), expCtx->variablesParseState); - ASSERT_VALUE_EQ(reparsedTrim->evaluate(Document{{"inputField", " , 4"_sd}, {"a", " ,"_sd}}), + ASSERT_VALUE_EQ(reparsedTrim->evaluate(Document{{"inputField", " , 4"_sd}, {"a", " ,"_sd}}, + &expCtx->variables), Value("4"_sd)); } } // namespace Trim @@ -5627,7 +5679,8 @@ public: VariablesParseState vps = expCtx->variablesParseState; intrusive_ptr expression = Expression::parseOperand(expCtx, specElement, vps); ASSERT_BSONOBJ_EQ(constify(spec()), expressionToBson(expression)); - ASSERT_BSONOBJ_EQ(BSON("" << expectedResult()), toBson(expression->evaluate(Document()))); + ASSERT_BSONOBJ_EQ(BSON("" << expectedResult()), + toBson(expression->evaluate({}, &expCtx->variables))); } protected: @@ -5684,7 +5737,8 @@ public: VariablesParseState vps = expCtx->variablesParseState; intrusive_ptr expression = Expression::parseOperand(expCtx, specElement, vps); ASSERT_BSONOBJ_EQ(constify(spec()), expressionToBson(expression)); - ASSERT_BSONOBJ_EQ(BSON("" << expectedResult()), toBson(expression->evaluate(Document()))); + ASSERT_BSONOBJ_EQ(BSON("" << expectedResult()), + toBson(expression->evaluate({}, &expCtx->variables))); } protected: @@ -5746,7 +5800,7 @@ public: VariablesParseState vps = expCtx->variablesParseState; const intrusive_ptr expr = Expression::parseExpression(expCtx, obj, vps); - const Value result = expr->evaluate(Document()); + const Value result = expr->evaluate({}, &expCtx->variables); if (ValueComparator().evaluate(result != expected)) { string errMsg = str::stream() << "for expression " << field.first.toString() << " with argument " @@ -5770,7 +5824,7 @@ public: // same const intrusive_ptr expr = Expression::parseExpression(expCtx, obj, vps); - expr->evaluate(Document()); + expr->evaluate({}, &expCtx->variables); }(), AssertionException); } @@ -6014,7 +6068,7 @@ TEST(ExpressionMetaTest, ExpressionMetaSearchScore) { MutableDocument doc; doc.setSearchScore(1.234); - Value val = expressionMeta->evaluate(doc.freeze()); + Value val = expressionMeta->evaluate(doc.freeze(), &expCtx->variables); ASSERT_EQ(val.getDouble(), 1.234); } @@ -6028,7 +6082,7 @@ TEST(ExpressionMetaTest, ExpressionMetaSearchHighlights) { Document highlights = DOC("this part" << 1 << "is opaque to the server" << 1); doc.setSearchHighlights(Value(highlights)); - Value val = expressionMeta->evaluate(doc.freeze()); + Value val = expressionMeta->evaluate(doc.freeze(), &expCtx->variables); ASSERT_DOCUMENT_EQ(val.getDocument(), highlights); } } // namespace expression_meta_test @@ -6038,8 +6092,9 @@ namespace ExpressionRegexTest { class ExpressionRegexTest { public: template - static intrusive_ptr generateOptimizedExpression(const BSONObj& input) { - intrusive_ptr expCtx(new ExpressionContextForTest()); + static intrusive_ptr generateOptimizedExpression( + const BSONObj& input, intrusive_ptr expCtx) { + auto expression = ExpressionRegexSubClass::parse( expCtx, input.firstElement(), expCtx->variablesParseState); return expression->optimize(); @@ -6048,31 +6103,31 @@ public: static void testAllExpressions(const BSONObj& input, bool optimized, const std::vector& expectedFindAllOutput) { + + intrusive_ptr expCtx(new ExpressionContextForTest()); { // For $regexFindAll. - auto expression = generateOptimizedExpression(input); + auto expression = generateOptimizedExpression(input, expCtx); auto regexFindAllExpr = dynamic_cast(expression.get()); ASSERT_EQ(regexFindAllExpr->hasConstantRegex(), optimized); - Value output = regexFindAllExpr->evaluate(Document()); + Value output = expression->evaluate({}, &expCtx->variables); ASSERT_VALUE_EQ(output, Value(expectedFindAllOutput)); } - { // For $regexFind. - auto expression = generateOptimizedExpression(input); + auto expression = generateOptimizedExpression(input, expCtx); auto regexFindExpr = dynamic_cast(expression.get()); ASSERT_EQ(regexFindExpr->hasConstantRegex(), optimized); - Value output = regexFindExpr->evaluate(Document()); + Value output = expression->evaluate({}, &expCtx->variables); ASSERT_VALUE_EQ( output, expectedFindAllOutput.empty() ? Value(BSONNULL) : expectedFindAllOutput[0]); } - { // For $regexMatch. - auto expression = generateOptimizedExpression(input); + auto expression = generateOptimizedExpression(input, expCtx); auto regexMatchExpr = dynamic_cast(expression.get()); ASSERT_EQ(regexMatchExpr->hasConstantRegex(), optimized); - Value output = regexMatchExpr->evaluate(Document()); + Value output = expression->evaluate({}, &expCtx->variables); ASSERT_VALUE_EQ(output, expectedFindAllOutput.empty() ? Value(false) : Value(true)); } } @@ -6380,14 +6435,14 @@ TEST(NowAndClusterTime, BasicTest) { // $$NOW is the Date type. { auto expression = ExpressionFieldPath::parse(expCtx, "$$NOW", expCtx->variablesParseState); - Value result = expression->evaluate(Document()); + Value result = expression->evaluate(Document(), &(expCtx->variables)); ASSERT_EQ(result.getType(), Date); } // $$CLUSTER_TIME is the timestamp type. { auto expression = ExpressionFieldPath::parse(expCtx, "$$CLUSTER_TIME", expCtx->variablesParseState); - Value result = expression->evaluate(Document()); + Value result = expression->evaluate(Document(), &(expCtx->variables)); ASSERT_EQ(result.getType(), bsonTimestamp); } @@ -6395,7 +6450,7 @@ TEST(NowAndClusterTime, BasicTest) { { auto expression = Expression::parseExpression( expCtx, fromjson("{$eq: [\"$$NOW\", \"$$NOW\"]}"), expCtx->variablesParseState); - Value result = expression->evaluate(Document()); + Value result = expression->evaluate(Document(), &(expCtx->variables)); ASSERT_VALUE_EQ(result, Value{true}); } @@ -6405,7 +6460,7 @@ TEST(NowAndClusterTime, BasicTest) { Expression::parseExpression(expCtx, fromjson("{$eq: [\"$$CLUSTER_TIME\", \"$$CLUSTER_TIME\"]}"), expCtx->variablesParseState); - Value result = expression->evaluate(Document()); + Value result = expression->evaluate(Document(), &(expCtx->variables)); ASSERT_VALUE_EQ(result, Value{true}); } diff --git a/src/mongo/db/pipeline/expression_trigonometric_test.cpp b/src/mongo/db/pipeline/expression_trigonometric_test.cpp index 35756404dd4..49ea60e1f9b 100644 --- a/src/mongo/db/pipeline/expression_trigonometric_test.cpp +++ b/src/mongo/db/pipeline/expression_trigonometric_test.cpp @@ -58,7 +58,7 @@ static void assertEvaluates(const std::string& expressionName, Value input, Valu auto obj = BSON(expressionName << BSON_ARRAY(input)); auto vps = expCtx->variablesParseState; auto expression = Expression::parseExpression(expCtx, obj, vps); - Value result = expression->evaluate(Document()); + Value result = expression->evaluate({}, &expCtx->variables); ASSERT_EQUALS(result.getType(), output.getType()); assertApproxEq(result, output); } @@ -73,7 +73,7 @@ static void assertEvaluates(const std::string& expressionName, auto obj = BSON(expressionName << BSON_ARRAY(input1 << input2)); auto vps = expCtx->variablesParseState; auto expression = Expression::parseExpression(expCtx, obj, vps); - Value result = expression->evaluate(Document()); + Value result = expression->evaluate({}, &expCtx->variables); ASSERT_EQUALS(result.getType(), output.getType()); assertApproxEq(result, output); } 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 3a033cc034f..aa622f9b014 100644 --- a/src/mongo/db/pipeline/granularity_rounder_powers_of_two.cpp +++ b/src/mongo/db/pipeline/granularity_rounder_powers_of_two.cpp @@ -81,7 +81,8 @@ Value GranularityRounderPowersOfTwo::roundUp(Value value) { exp = Value(63 - countLeadingZeros64(number) + 1); } - return ExpressionPow::create(getExpCtx(), Value(2), exp)->evaluate(Document()); + return ExpressionPow::create(getExpCtx(), Value(2), exp) + ->evaluate(Document(), &getExpCtx()->variables); } Value GranularityRounderPowersOfTwo::roundDown(Value value) { @@ -113,7 +114,8 @@ Value GranularityRounderPowersOfTwo::roundDown(Value value) { } } - return ExpressionPow::create(getExpCtx(), Value(2), exp)->evaluate(Document()); + return ExpressionPow::create(getExpCtx(), Value(2), exp) + ->evaluate(Document(), &getExpCtx()->variables); } string GranularityRounderPowersOfTwo::getName() { diff --git a/src/mongo/db/pipeline/parsed_aggregation_projection_node.cpp b/src/mongo/db/pipeline/parsed_aggregation_projection_node.cpp index 18b6e243f09..65637d9144b 100644 --- a/src/mongo/db/pipeline/parsed_aggregation_projection_node.cpp +++ b/src/mongo/db/pipeline/parsed_aggregation_projection_node.cpp @@ -172,7 +172,10 @@ void ProjectionNode::applyExpressions(const Document& root, MutableDocument* out } else { auto expressionIt = _expressions.find(field); invariant(expressionIt != _expressions.end()); - outputDoc->setField(field, expressionIt->second->evaluate(root)); + outputDoc->setField( + field, + expressionIt->second->evaluate( + root, &expressionIt->second->getExpressionContext()->variables)); } } } -- cgit v1.2.1