diff options
author | Jacob Evans <jacob.evans@10gen.com> | 2019-04-12 12:21:15 -0400 |
---|---|---|
committer | Jacob Evans <jacob.evans@10gen.com> | 2019-05-03 14:36:58 -0400 |
commit | d226d5d236f2542bcece138c3c2337d67348d84d (patch) | |
tree | 125a55de39cbff86909a64f0d9e6f2d7e9ba1dca /src/mongo/db/pipeline | |
parent | 734266e9ec2f8cbf0b75bd7797f8b573262af471 (diff) | |
download | mongo-d226d5d236f2542bcece138c3c2337d67348d84d.tar.gz |
SERVER-40526 Make agg Expressions walkable
Diffstat (limited to 'src/mongo/db/pipeline')
-rw-r--r-- | src/mongo/db/pipeline/document_source_group_test.cpp | 15 | ||||
-rw-r--r-- | src/mongo/db/pipeline/expression.cpp | 693 | ||||
-rw-r--r-- | src/mongo/db/pipeline/expression.h | 418 | ||||
-rw-r--r-- | src/mongo/db/pipeline/expression_test.cpp | 63 |
4 files changed, 699 insertions, 490 deletions
diff --git a/src/mongo/db/pipeline/document_source_group_test.cpp b/src/mongo/db/pipeline/document_source_group_test.cpp index 111a2db01f2..da25b97d397 100644 --- a/src/mongo/db/pipeline/document_source_group_test.cpp +++ b/src/mongo/db/pipeline/document_source_group_test.cpp @@ -207,7 +207,20 @@ TEST_F(DocumentSourceGroupTest, ShouldReportMultipleFieldGroupKeysAsARename) { VariablesParseState vps = expCtx->variablesParseState; auto x = ExpressionFieldPath::parse(expCtx, "$x", vps); auto y = ExpressionFieldPath::parse(expCtx, "$y", vps); - auto groupByExpression = ExpressionObject::create(expCtx, {{"x", x}, {"y", y}}); + auto groupByExpression = [&]() { + std::vector<boost::intrusive_ptr<Expression>> children; + std::vector<std::pair<std::string, boost::intrusive_ptr<Expression>&>> expressions; + auto doc = std::vector<std::pair<std::string, boost::intrusive_ptr<Expression>>>{{"x", x}, + {"y", y}}; + for (auto & [ unused, expression ] : doc) + children.push_back(std::move(expression)); + std::vector<boost::intrusive_ptr<Expression>>::size_type index = 0; + for (auto & [ fieldName, unused ] : doc) { + expressions.emplace_back(fieldName, children[index]); + ++index; + } + return ExpressionObject::create(expCtx, std::move(children), std::move(expressions)); + }(); auto group = DocumentSourceGroup::create(expCtx, groupByExpression, {}); auto modifiedPathsRet = group->getModifiedPaths(); ASSERT(modifiedPathsRet.type == DocumentSource::GetModPathsReturn::Type::kAllExcept); diff --git a/src/mongo/db/pipeline/expression.cpp b/src/mongo/db/pipeline/expression.cpp index 0084f870d30..8895e4a98ac 100644 --- a/src/mongo/db/pipeline/expression.cpp +++ b/src/mongo/db/pipeline/expression.cpp @@ -37,6 +37,7 @@ #include <boost/algorithm/string.hpp> #include <cstdio> #include <pcrecpp.h> +#include <utility> #include <vector> #include "mongo/db/commands/feature_compatibility_version_documentation.h" @@ -93,7 +94,7 @@ intrusive_ptr<Expression> Expression::parseObject( BSONObj obj, const VariablesParseState& vps) { if (obj.isEmpty()) { - return ExpressionObject::create(expCtx, {}); + return ExpressionObject::create(expCtx, {}, {}); } if (obj.firstElementFieldName()[0] == '$') { @@ -273,9 +274,9 @@ Value ExpressionAdd::evaluate(const Document& root) const { BSONType totalType = NumberInt; bool haveDate = false; - const size_t n = vpOperand.size(); + const size_t n = _children.size(); for (size_t i = 0; i < n; ++i) { - Value val = vpOperand[i]->evaluate(root); + Value val = _children[i]->evaluate(root); switch (val.getType()) { case NumberDecimal: @@ -346,7 +347,7 @@ const char* ExpressionAdd::getOpName() const { /* ------------------------- ExpressionAllElementsTrue -------------------------- */ Value ExpressionAllElementsTrue::evaluate(const Document& root) const { - const Value arr = vpOperand[0]->evaluate(root); + const Value arr = _children[0]->evaluate(root); uassert(17040, str::stream() << getOpName() << "'s argument must be an array, but is " << typeName(arr.getType()), @@ -381,10 +382,10 @@ intrusive_ptr<Expression> ExpressionAnd::optimize() { promised by ExpressionNary::optimize(),) then there's nothing we can do. */ - const size_t n = pAnd->vpOperand.size(); + const size_t n = pAnd->_children.size(); // ExpressionNary::optimize() generates an ExpressionConstant for {$and:[]}. verify(n > 0); - intrusive_ptr<Expression> pLast(pAnd->vpOperand[n - 1]); + intrusive_ptr<Expression> pLast(pAnd->_children[n - 1]); const ExpressionConstant* pConst = dynamic_cast<ExpressionConstant*>(pLast.get()); if (!pConst) return pE; @@ -408,7 +409,7 @@ intrusive_ptr<Expression> ExpressionAnd::optimize() { */ if (n == 2) { intrusive_ptr<Expression> pFinal( - ExpressionCoerceToBool::create(getExpressionContext(), pAnd->vpOperand[0])); + ExpressionCoerceToBool::create(getExpressionContext(), std::move(pAnd->_children[0]))); return pFinal; } @@ -419,14 +420,14 @@ intrusive_ptr<Expression> ExpressionAnd::optimize() { Note that because of any implicit conversions, we may need to apply an implicit boolean conversion. */ - pAnd->vpOperand.resize(n - 1); + pAnd->_children.resize(n - 1); return pE; } Value ExpressionAnd::evaluate(const Document& root) const { - const size_t n = vpOperand.size(); + const size_t n = _children.size(); for (size_t i = 0; i < n; ++i) { - Value pValue(vpOperand[i]->evaluate(root)); + Value pValue(_children[i]->evaluate(root)); if (!pValue.coerceToBool()) return Value(false); } @@ -442,7 +443,7 @@ const char* ExpressionAnd::getOpName() const { /* ------------------------- ExpressionAnyElementTrue -------------------------- */ Value ExpressionAnyElementTrue::evaluate(const Document& root) const { - const Value arr = vpOperand[0]->evaluate(root); + const Value arr = _children[0]->evaluate(root); uassert(17041, str::stream() << getOpName() << "'s argument must be an array, but is " << typeName(arr.getType()), @@ -465,8 +466,8 @@ const char* ExpressionAnyElementTrue::getOpName() const { Value ExpressionArray::evaluate(const Document& root) const { vector<Value> values; - values.reserve(vpOperand.size()); - for (auto&& expr : vpOperand) { + values.reserve(_children.size()); + for (auto&& expr : _children) { Value elemVal = expr->evaluate(root); values.push_back(elemVal.missing() ? Value(BSONNULL) : std::move(elemVal)); } @@ -475,8 +476,8 @@ Value ExpressionArray::evaluate(const Document& root) const { Value ExpressionArray::serialize(bool explain) const { vector<Value> expressions; - expressions.reserve(vpOperand.size()); - for (auto&& expr : vpOperand) { + expressions.reserve(_children.size()); + for (auto&& expr : _children) { expressions.push_back(expr->serialize(explain)); } return Value(std::move(expressions)); @@ -485,7 +486,7 @@ Value ExpressionArray::serialize(bool explain) const { intrusive_ptr<Expression> ExpressionArray::optimize() { bool allValuesConstant = true; - for (auto&& expr : vpOperand) { + for (auto&& expr : _children) { expr = expr->optimize(); if (!dynamic_cast<ExpressionConstant*>(expr.get())) { allValuesConstant = false; @@ -507,8 +508,8 @@ const char* ExpressionArray::getOpName() const { /* ------------------------- ExpressionArrayElemAt -------------------------- */ Value ExpressionArrayElemAt::evaluate(const Document& root) const { - const Value array = vpOperand[0]->evaluate(root); - const Value indexArg = vpOperand[1]->evaluate(root); + const Value array = _children[0]->evaluate(root); + const Value indexArg = _children[1]->evaluate(root); if (array.nullish() || indexArg.nullish()) { return Value(BSONNULL); @@ -549,7 +550,7 @@ const char* ExpressionArrayElemAt::getOpName() const { /* ------------------------- ExpressionObjectToArray -------------------------- */ Value ExpressionObjectToArray::evaluate(const Document& root) const { - const Value targetVal = vpOperand[0]->evaluate(root); + const Value targetVal = _children[0]->evaluate(root); if (targetVal.nullish()) { return Value(BSONNULL); @@ -581,7 +582,7 @@ const char* ExpressionObjectToArray::getOpName() const { /* ------------------------- ExpressionArrayToObject -------------------------- */ Value ExpressionArrayToObject::evaluate(const Document& root) const { - const Value input = vpOperand[0]->evaluate(root); + const Value input = _children[0]->evaluate(root); if (input.nullish()) { return Value(BSONNULL); } @@ -702,14 +703,13 @@ const char* ExpressionCeil::getOpName() const { /* -------------------- ExpressionCoerceToBool ------------------------- */ intrusive_ptr<ExpressionCoerceToBool> ExpressionCoerceToBool::create( - const intrusive_ptr<ExpressionContext>& expCtx, const intrusive_ptr<Expression>& pExpression) { - intrusive_ptr<ExpressionCoerceToBool> pNew(new ExpressionCoerceToBool(expCtx, pExpression)); - return pNew; + const intrusive_ptr<ExpressionContext>& expCtx, intrusive_ptr<Expression> pExpression) { + return new ExpressionCoerceToBool(expCtx, std::move(pExpression)); } ExpressionCoerceToBool::ExpressionCoerceToBool(const intrusive_ptr<ExpressionContext>& expCtx, - const intrusive_ptr<Expression>& pTheExpression) - : Expression(expCtx), pExpression(pTheExpression) {} + intrusive_ptr<Expression> pExpression) + : Expression(expCtx, {std::move(pExpression)}), pExpression(_children[0]) {} intrusive_ptr<Expression> ExpressionCoerceToBool::optimize() { /* optimize the operand */ @@ -774,7 +774,7 @@ intrusive_ptr<Expression> ExpressionCompare::parse( intrusive_ptr<ExpressionCompare> expr = new ExpressionCompare(expCtx, op); ExpressionVector args = parseArguments(expCtx, bsonExpr, vps); expr->validateArguments(args); - expr->vpOperand = args; + expr->_children = args; return expr; } @@ -784,7 +784,7 @@ boost::intrusive_ptr<ExpressionCompare> ExpressionCompare::create( const boost::intrusive_ptr<Expression>& exprLeft, const boost::intrusive_ptr<Expression>& exprRight) { boost::intrusive_ptr<ExpressionCompare> expr = new ExpressionCompare(expCtx, cmpOp); - expr->vpOperand = {exprLeft, exprRight}; + expr->_children = {exprLeft, exprRight}; return expr; } @@ -810,8 +810,8 @@ static const CmpLookup cmpLookup[7] = { } Value ExpressionCompare::evaluate(const Document& root) const { - Value pLeft(vpOperand[0]->evaluate(root)); - Value pRight(vpOperand[1]->evaluate(root)); + Value pLeft(_children[0]->evaluate(root)); + Value pRight(_children[1]->evaluate(root)); int cmp = getExpressionContext()->getValueComparator().compare(pLeft, pRight); @@ -838,11 +838,11 @@ const char* ExpressionCompare::getOpName() const { /* ------------------------- ExpressionConcat ----------------------------- */ Value ExpressionConcat::evaluate(const Document& root) const { - const size_t n = vpOperand.size(); + const size_t n = _children.size(); StringBuilder result; for (size_t i = 0; i < n; ++i) { - Value val = vpOperand[i]->evaluate(root); + Value val = _children[i]->evaluate(root); if (val.nullish()) return Value(BSONNULL); @@ -864,11 +864,11 @@ const char* ExpressionConcat::getOpName() const { /* ------------------------- ExpressionConcatArrays ----------------------------- */ Value ExpressionConcatArrays::evaluate(const Document& root) const { - const size_t n = vpOperand.size(); + const size_t n = _children.size(); vector<Value> values; for (size_t i = 0; i < n; ++i) { - Value val = vpOperand[i]->evaluate(root); + Value val = _children[i]->evaluate(root); if (val.nullish()) { return Value(BSONNULL); } @@ -892,9 +892,9 @@ const char* ExpressionConcatArrays::getOpName() const { /* ----------------------- ExpressionCond ------------------------------ */ Value ExpressionCond::evaluate(const Document& root) const { - Value pCond(vpOperand[0]->evaluate(root)); + Value pCond(_children[0]->evaluate(root)); int idx = pCond.coerceToBool() ? 1 : 2; - return vpOperand[idx]->evaluate(root); + return _children[idx]->evaluate(root); } intrusive_ptr<Expression> ExpressionCond::parse( @@ -907,25 +907,25 @@ intrusive_ptr<Expression> ExpressionCond::parse( verify(expr.fieldNameStringData() == "$cond"); intrusive_ptr<ExpressionCond> ret = new ExpressionCond(expCtx); - ret->vpOperand.resize(3); + ret->_children.resize(3); const BSONObj args = expr.embeddedObject(); BSONForEach(arg, args) { if (arg.fieldNameStringData() == "if") { - ret->vpOperand[0] = parseOperand(expCtx, arg, vps); + ret->_children[0] = parseOperand(expCtx, arg, vps); } else if (arg.fieldNameStringData() == "then") { - ret->vpOperand[1] = parseOperand(expCtx, arg, vps); + ret->_children[1] = parseOperand(expCtx, arg, vps); } else if (arg.fieldNameStringData() == "else") { - ret->vpOperand[2] = parseOperand(expCtx, arg, vps); + ret->_children[2] = parseOperand(expCtx, arg, vps); } else { uasserted(17083, str::stream() << "Unrecognized parameter to $cond: " << arg.fieldName()); } } - uassert(17080, "Missing 'if' parameter to $cond", ret->vpOperand[0]); - uassert(17081, "Missing 'then' parameter to $cond", ret->vpOperand[1]); - uassert(17082, "Missing 'else' parameter to $cond", ret->vpOperand[2]); + uassert(17080, "Missing 'if' parameter to $cond", ret->_children[0]); + uassert(17081, "Missing 'then' parameter to $cond", ret->_children[1]); + uassert(17082, "Missing 'else' parameter to $cond", ret->_children[2]); return ret; } @@ -1106,18 +1106,29 @@ ExpressionDateFromParts::ExpressionDateFromParts( intrusive_ptr<Expression> isoWeek, intrusive_ptr<Expression> isoDayOfWeek, intrusive_ptr<Expression> timeZone) - : Expression(expCtx), - _year(std::move(year)), - _month(std::move(month)), - _day(std::move(day)), - _hour(std::move(hour)), - _minute(std::move(minute)), - _second(std::move(second)), - _millisecond(std::move(millisecond)), - _isoWeekYear(std::move(isoWeekYear)), - _isoWeek(std::move(isoWeek)), - _isoDayOfWeek(std::move(isoDayOfWeek)), - _timeZone(std::move(timeZone)) {} + : Expression(expCtx, + {std::move(year), + std::move(month), + std::move(day), + std::move(hour), + std::move(minute), + std::move(second), + std::move(millisecond), + std::move(isoWeekYear), + std::move(isoWeek), + std::move(isoDayOfWeek), + std::move(timeZone)}), + _year(_children[0]), + _month(_children[1]), + _day(_children[2]), + _hour(_children[3]), + _minute(_children[4]), + _second(_children[5]), + _millisecond(_children[6]), + _isoWeekYear(_children[7]), + _isoWeek(_children[8]), + _isoDayOfWeek(_children[9]), + _timeZone(_children[10]) {} intrusive_ptr<Expression> ExpressionDateFromParts::optimize() { if (_year) { @@ -1361,12 +1372,17 @@ ExpressionDateFromString::ExpressionDateFromString( intrusive_ptr<Expression> format, intrusive_ptr<Expression> onNull, intrusive_ptr<Expression> onError) - : Expression(expCtx), - _dateString(std::move(dateString)), - _timeZone(std::move(timeZone)), - _format(std::move(format)), - _onNull(std::move(onNull)), - _onError(std::move(onError)) {} + : Expression(expCtx, + {std::move(dateString), + std::move(timeZone), + std::move(format), + std::move(onNull), + std::move(onError)}), + _dateString(_children[0]), + _timeZone(_children[1]), + _format(_children[2]), + _onNull(_children[3]), + _onError(_children[4]) {} intrusive_ptr<Expression> ExpressionDateFromString::optimize() { _dateString = _dateString->optimize(); @@ -1531,10 +1547,10 @@ ExpressionDateToParts::ExpressionDateToParts(const boost::intrusive_ptr<Expressi intrusive_ptr<Expression> date, intrusive_ptr<Expression> timeZone, intrusive_ptr<Expression> iso8601) - : Expression(expCtx), - _date(std::move(date)), - _timeZone(std::move(timeZone)), - _iso8601(std::move(iso8601)) {} + : Expression(expCtx, {std::move(date), std::move(timeZone), std::move(iso8601)}), + _date(_children[0]), + _timeZone(_children[1]), + _iso8601(_children[2]) {} intrusive_ptr<Expression> ExpressionDateToParts::optimize() { _date = _date->optimize(); @@ -1679,11 +1695,12 @@ ExpressionDateToString::ExpressionDateToString( intrusive_ptr<Expression> format, intrusive_ptr<Expression> timeZone, intrusive_ptr<Expression> onNull) - : Expression(expCtx), - _format(std::move(format)), - _date(std::move(date)), - _timeZone(std::move(timeZone)), - _onNull(std::move(onNull)) {} + : Expression(expCtx, + {std::move(format), std::move(date), std::move(timeZone), std::move(onNull)}), + _format(_children[0]), + _date(_children[1]), + _timeZone(_children[2]), + _onNull(_children[3]) {} intrusive_ptr<Expression> ExpressionDateToString::optimize() { _date = _date->optimize(); @@ -1777,8 +1794,8 @@ void ExpressionDateToString::_doAddDependencies(DepsTracker* deps) const { /* ----------------------- ExpressionDivide ---------------------------- */ Value ExpressionDivide::evaluate(const Document& root) const { - Value lhs = vpOperand[0]->evaluate(root); - Value rhs = vpOperand[1]->evaluate(root); + Value lhs = _children[0]->evaluate(root); + Value rhs = _children[1]->evaluate(root); auto assertNonZero = [](bool nonZero) { uassert(16608, "can't $divide by zero", nonZero); }; @@ -1830,13 +1847,15 @@ const char* ExpressionExp::getOpName() const { /* ---------------------- ExpressionObject --------------------------- */ ExpressionObject::ExpressionObject(const boost::intrusive_ptr<ExpressionContext>& expCtx, - vector<pair<string, intrusive_ptr<Expression>>>&& expressions) - : Expression(expCtx), _expressions(std::move(expressions)) {} + std::vector<boost::intrusive_ptr<Expression>> _children, + vector<pair<string, intrusive_ptr<Expression>&>>&& expressions) + : Expression(expCtx, std::move(_children)), _expressions(std::move(expressions)) {} intrusive_ptr<ExpressionObject> ExpressionObject::create( const boost::intrusive_ptr<ExpressionContext>& expCtx, - vector<pair<string, intrusive_ptr<Expression>>>&& expressions) { - return new ExpressionObject(expCtx, std::move(expressions)); + std::vector<boost::intrusive_ptr<Expression>> _children, + vector<pair<string, intrusive_ptr<Expression>&>>&& expressions) { + return new ExpressionObject(expCtx, std::move(_children), std::move(expressions)); } intrusive_ptr<ExpressionObject> ExpressionObject::parse( @@ -1846,7 +1865,8 @@ intrusive_ptr<ExpressionObject> ExpressionObject::parse( // Make sure we don't have any duplicate field names. stdx::unordered_set<string> specifiedFields; - vector<pair<string, intrusive_ptr<Expression>>> expressions; + std::vector<boost::intrusive_ptr<Expression>> children; + vector<pair<string, intrusive_ptr<Expression>&>> expressions; for (auto&& elem : obj) { // Make sure this element has a valid field name. Use StringData here so that we can detect // if the field name contains a null byte. @@ -1858,10 +1878,16 @@ intrusive_ptr<ExpressionObject> ExpressionObject::parse( << obj.toString(), specifiedFields.find(fieldName) == specifiedFields.end()); specifiedFields.insert(fieldName); - expressions.emplace_back(fieldName, parseOperand(expCtx, elem, vps)); + children.push_back(parseOperand(expCtx, elem, vps)); } - return new ExpressionObject{expCtx, std::move(expressions)}; + std::vector<boost::intrusive_ptr<Expression>>::size_type index = 0; + for (auto&& elem : obj) { + expressions.emplace_back(elem.fieldName(), children[index]); + ++index; + } + + return new ExpressionObject{expCtx, std::move(children), std::move(expressions)}; } intrusive_ptr<Expression> ExpressionObject::optimize() { @@ -1880,8 +1906,8 @@ intrusive_ptr<Expression> ExpressionObject::optimize() { } void ExpressionObject::_doAddDependencies(DepsTracker* deps) const { - for (auto&& pair : _expressions) { - pair.second->addDependencies(deps); + for (auto&& child : _children) { + child->addDependencies(deps); } } @@ -2156,11 +2182,11 @@ ExpressionFilter::ExpressionFilter(const boost::intrusive_ptr<ExpressionContext> Variables::Id varId, intrusive_ptr<Expression> input, intrusive_ptr<Expression> filter) - : Expression(expCtx), + : Expression(expCtx, {std::move(input), std::move(filter)}), _varName(std::move(varName)), _varId(varId), - _input(std::move(input)), - _filter(std::move(filter)) {} + _input(_children[0]), + _filter(_children[1]) {} intrusive_ptr<Expression> ExpressionFilter::optimize() { // TODO handle when _input is constant. @@ -2245,7 +2271,7 @@ intrusive_ptr<Expression> ExpressionLet::parse( // varsElem must be parsed before inElem regardless of BSON order. BSONElement varsElem; BSONElement inElem; - BSONForEach(arg, args) { + for (auto&& arg : args) { if (arg.fieldNameStringData() == "vars") { varsElem = arg; } else if (arg.fieldNameStringData() == "in") { @@ -2262,25 +2288,36 @@ intrusive_ptr<Expression> ExpressionLet::parse( // parse "vars" VariablesParseState vpsSub(vpsIn); // vpsSub gets our vars, vpsIn doesn't. VariableMap vars; - BSONForEach(varElem, varsElem.embeddedObjectUserCheck()) { + std::vector<boost::intrusive_ptr<Expression>> children; + auto&& varsObj = varsElem.embeddedObjectUserCheck(); + for (auto&& varElem : varsObj) + children.push_back(parseOperand(expCtx, varElem, vpsIn)); + + // Make a place in the vector for "in". + auto& inPtr = children.emplace_back(nullptr); + + std::vector<boost::intrusive_ptr<Expression>>::size_type index = 0; + for (auto&& varElem : varsObj) { const string varName = varElem.fieldName(); Variables::uassertValidNameForUserWrite(varName); Variables::Id id = vpsSub.defineVariable(varName); - vars[id] = NameAndExpression(varName, - parseOperand(expCtx, varElem, vpsIn)); // only has outer vars + vars.emplace(id, NameAndExpression{varName, children[index]}); // only has outer vars + ++index; } // parse "in" - intrusive_ptr<Expression> subExpression = parseOperand(expCtx, inElem, vpsSub); // has our vars + inPtr = parseOperand(expCtx, inElem, vpsSub); // has our vars - return new ExpressionLet(expCtx, vars, subExpression); + return new ExpressionLet(expCtx, std::move(vars), std::move(children)); } ExpressionLet::ExpressionLet(const boost::intrusive_ptr<ExpressionContext>& expCtx, - const VariableMap& vars, - intrusive_ptr<Expression> subExpression) - : Expression(expCtx), _variables(vars), _subExpression(subExpression) {} + VariableMap&& vars, + std::vector<boost::intrusive_ptr<Expression>> children) + : Expression(expCtx, std::move(children)), + _variables(std::move(vars)), + _subExpression(_children.back()) {} intrusive_ptr<Expression> ExpressionLet::optimize() { if (_variables.empty()) { @@ -2386,7 +2423,11 @@ ExpressionMap::ExpressionMap(const boost::intrusive_ptr<ExpressionContext>& expC Variables::Id varId, intrusive_ptr<Expression> input, intrusive_ptr<Expression> each) - : Expression(expCtx), _varName(varName), _varId(varId), _input(input), _each(each) {} + : Expression(expCtx, {std::move(input), std::move(each)}), + _varName(varName), + _varId(varId), + _input(_children[0]), + _each(_children[1]) {} intrusive_ptr<Expression> ExpressionMap::optimize() { // TODO handle when _input is constant @@ -2516,8 +2557,8 @@ void ExpressionMeta::_doAddDependencies(DepsTracker* deps) const { /* ----------------------- ExpressionMod ---------------------------- */ Value ExpressionMod::evaluate(const Document& root) const { - Value lhs = vpOperand[0]->evaluate(root); - Value rhs = vpOperand[1]->evaluate(root); + Value lhs = _children[0]->evaluate(root); + Value rhs = _children[1]->evaluate(root); BSONType leftType = lhs.getType(); BSONType rightType = rhs.getType(); @@ -2585,9 +2626,9 @@ Value ExpressionMultiply::evaluate(const Document& root) const { BSONType productType = NumberInt; - const size_t n = vpOperand.size(); + const size_t n = _children.size(); for (size_t i = 0; i < n; ++i) { - Value val = vpOperand[i]->evaluate(root); + Value val = _children[i]->evaluate(root); if (val.numeric()) { BSONType oldProductType = productType; @@ -2636,11 +2677,11 @@ const char* ExpressionMultiply::getOpName() const { /* ----------------------- ExpressionIfNull ---------------------------- */ Value ExpressionIfNull::evaluate(const Document& root) const { - Value pLeft(vpOperand[0]->evaluate(root)); + Value pLeft(_children[0]->evaluate(root)); if (!pLeft.nullish()) return pLeft; - Value pRight(vpOperand[1]->evaluate(root)); + Value pRight(_children[1]->evaluate(root)); return pRight; } @@ -2652,8 +2693,8 @@ const char* ExpressionIfNull::getOpName() const { /* ----------------------- ExpressionIn ---------------------------- */ Value ExpressionIn::evaluate(const Document& root) const { - Value argument(vpOperand[0]->evaluate(root)); - Value arrayOfValues(vpOperand[1]->evaluate(root)); + Value argument(_children[0]->evaluate(root)); + Value arrayOfValues(_children[1]->evaluate(root)); uassert(40081, str::stream() << "$in requires an array as a second argument, found: " @@ -2696,7 +2737,7 @@ void uassertIfNotIntegralAndNonNegative(Value val, } // namespace Value ExpressionIndexOfArray::evaluate(const Document& root) const { - Value arrayArg = vpOperand[0]->evaluate(root); + Value arrayArg = _children[0]->evaluate(root); if (arrayArg.nullish()) { return Value(BSONNULL); @@ -2708,7 +2749,7 @@ Value ExpressionIndexOfArray::evaluate(const Document& root) const { arrayArg.isArray()); std::vector<Value> array = arrayArg.getArray(); - auto args = evaluateAndValidateArguments(root, vpOperand, array.size()); + auto args = evaluateAndValidateArguments(root, _children, array.size()); for (int i = args.startIndex; i < args.endIndex; i++) { if (getExpressionContext()->getValueComparator().evaluate(array[i] == args.targetOfSearch)) { @@ -2739,7 +2780,7 @@ ExpressionIndexOfArray::Arguments ExpressionIndexOfArray::evaluateAndValidateArg endIndex = std::min(static_cast<int>(arrayLength), endIndexArg.coerceToInt()); } - return {vpOperand[1]->evaluate(root), startIndex, endIndex}; + return {_children[1]->evaluate(root), startIndex, endIndex}; } /** @@ -2753,12 +2794,12 @@ public: const ValueUnorderedMap<vector<int>>& indexMap, const ExpressionVector& operands) : ExpressionIndexOfArray(expCtx), _indexMap(std::move(indexMap)) { - vpOperand = operands; + _children = operands; } virtual Value evaluate(const Document& root) const { - auto args = evaluateAndValidateArguments(root, vpOperand, _indexMap.size()); + auto args = evaluateAndValidateArguments(root, _children, _indexMap.size()); auto indexVec = _indexMap.find(args.targetOfSearch); if (indexVec == _indexMap.end()) @@ -2789,7 +2830,7 @@ intrusive_ptr<Expression> ExpressionIndexOfArray::optimize() { // If the input array is an ExpressionConstant we can optimize using a unordered_map instead of // an // array. - if (auto constantArray = dynamic_cast<ExpressionConstant*>(vpOperand[0].get())) { + if (auto constantArray = dynamic_cast<ExpressionConstant*>(_children[0].get())) { const Value valueArray = constantArray->getValue(); if (valueArray.nullish()) { return ExpressionConstant::create(getExpressionContext(), Value(BSONNULL)); @@ -2812,7 +2853,7 @@ intrusive_ptr<Expression> ExpressionIndexOfArray::optimize() { } indexMap[arr[i]].push_back(i); } - return new Optimized(getExpressionContext(), indexMap, vpOperand); + return new Optimized(getExpressionContext(), indexMap, _children); } return this; } @@ -2836,7 +2877,7 @@ bool stringHasTokenAtIndex(size_t index, const std::string& input, const std::st } // namespace Value ExpressionIndexOfBytes::evaluate(const Document& root) const { - Value stringArg = vpOperand[0]->evaluate(root); + Value stringArg = _children[0]->evaluate(root); if (stringArg.nullish()) { return Value(BSONNULL); @@ -2848,7 +2889,7 @@ Value ExpressionIndexOfBytes::evaluate(const Document& root) const { stringArg.getType() == String); const std::string& input = stringArg.getString(); - Value tokenArg = vpOperand[1]->evaluate(root); + Value tokenArg = _children[1]->evaluate(root); uassert(40092, str::stream() << "$indexOfBytes requires a string as the second argument, found: " << typeName(tokenArg.getType()), @@ -2856,15 +2897,15 @@ Value ExpressionIndexOfBytes::evaluate(const Document& root) const { const std::string& token = tokenArg.getString(); size_t startIndex = 0; - if (vpOperand.size() > 2) { - Value startIndexArg = vpOperand[2]->evaluate(root); + if (_children.size() > 2) { + Value startIndexArg = _children[2]->evaluate(root); uassertIfNotIntegralAndNonNegative(startIndexArg, getOpName(), "starting index"); startIndex = static_cast<size_t>(startIndexArg.coerceToInt()); } size_t endIndex = input.size(); - if (vpOperand.size() > 3) { - Value endIndexArg = vpOperand[3]->evaluate(root); + if (_children.size() > 3) { + Value endIndexArg = _children[3]->evaluate(root); uassertIfNotIntegralAndNonNegative(endIndexArg, getOpName(), "ending index"); // Don't let 'endIndex' exceed the length of the string. endIndex = std::min(input.size(), static_cast<size_t>(endIndexArg.coerceToInt())); @@ -2890,7 +2931,7 @@ const char* ExpressionIndexOfBytes::getOpName() const { /* ----------------------- ExpressionIndexOfCP --------------------- */ Value ExpressionIndexOfCP::evaluate(const Document& root) const { - Value stringArg = vpOperand[0]->evaluate(root); + Value stringArg = _children[0]->evaluate(root); if (stringArg.nullish()) { return Value(BSONNULL); @@ -2902,7 +2943,7 @@ Value ExpressionIndexOfCP::evaluate(const Document& root) const { stringArg.getType() == String); const std::string& input = stringArg.getString(); - Value tokenArg = vpOperand[1]->evaluate(root); + Value tokenArg = _children[1]->evaluate(root); uassert(40094, str::stream() << "$indexOfCP requires a string as the second argument, found: " << typeName(tokenArg.getType()), @@ -2910,8 +2951,8 @@ Value ExpressionIndexOfCP::evaluate(const Document& root) const { const std::string& token = tokenArg.getString(); size_t startCodePointIndex = 0; - if (vpOperand.size() > 2) { - Value startIndexArg = vpOperand[2]->evaluate(root); + if (_children.size() > 2) { + Value startIndexArg = _children[2]->evaluate(root); uassertIfNotIntegralAndNonNegative(startIndexArg, getOpName(), "starting index"); startCodePointIndex = static_cast<size_t>(startIndexArg.coerceToInt()); } @@ -2933,8 +2974,8 @@ Value ExpressionIndexOfCP::evaluate(const Document& root) const { } size_t endCodePointIndex = codePointLength; - if (vpOperand.size() > 3) { - Value endIndexArg = vpOperand[3]->evaluate(root); + if (_children.size() > 3) { + Value endIndexArg = _children[3]->evaluate(root); uassertIfNotIntegralAndNonNegative(endIndexArg, getOpName(), "ending index"); // Don't let 'endCodePointIndex' exceed the number of code points in the string. @@ -2992,8 +3033,8 @@ const char* ExpressionLn::getOpName() const { /* ----------------------- ExpressionLog ---------------------------- */ Value ExpressionLog::evaluate(const Document& root) const { - Value argVal = vpOperand[0]->evaluate(root); - Value baseVal = vpOperand[1]->evaluate(root); + Value argVal = _children[0]->evaluate(root); + Value baseVal = _children[1]->evaluate(root); if (argVal.nullish() || baseVal.nullish()) return Value(BSONNULL); @@ -3076,7 +3117,7 @@ const char* ExpressionLog10::getOpName() const { intrusive_ptr<Expression> ExpressionNary::optimize() { uint32_t constOperandCount = 0; - for (auto& operand : vpOperand) { + for (auto& operand : _children) { operand = operand->optimize(); if (dynamic_cast<ExpressionConstant*>(operand.get())) { ++constOperandCount; @@ -3084,7 +3125,7 @@ intrusive_ptr<Expression> ExpressionNary::optimize() { } // If all the operands are constant expressions, collapse the expression into one constant // expression. - if (constOperandCount == vpOperand.size()) { + if (constOperandCount == _children.size()) { return intrusive_ptr<Expression>( ExpressionConstant::create(getExpressionContext(), evaluate(Document()))); } @@ -3097,8 +3138,8 @@ intrusive_ptr<Expression> ExpressionNary::optimize() { if (isAssociative()) { ExpressionVector constExpressions; ExpressionVector optimizedOperands; - for (size_t i = 0; i < vpOperand.size();) { - intrusive_ptr<Expression> operand = vpOperand[i]; + for (size_t i = 0; i < _children.size();) { + intrusive_ptr<Expression> operand = _children[i]; // If the operand is a constant one, add it to the current list of consecutive constant // operands. if (dynamic_cast<ExpressionConstant*>(operand.get())) { @@ -3112,10 +3153,10 @@ intrusive_ptr<Expression> ExpressionNary::optimize() { // E.g: sum(a, b, sum(c, d), e) => sum(a, b, c, d, e) ExpressionNary* nary = dynamic_cast<ExpressionNary*>(operand.get()); if (nary && !strcmp(nary->getOpName(), getOpName()) && nary->isAssociative()) { - invariant(!nary->vpOperand.empty()); - vpOperand[i] = std::move(nary->vpOperand[0]); - vpOperand.insert( - vpOperand.begin() + i + 1, nary->vpOperand.begin() + 1, nary->vpOperand.end()); + invariant(!nary->_children.empty()); + _children[i] = std::move(nary->_children[0]); + _children.insert( + _children.begin() + i + 1, nary->_children.begin() + 1, nary->_children.end()); continue; } @@ -3127,11 +3168,11 @@ intrusive_ptr<Expression> ExpressionNary::optimize() { // back the operands. if (!isCommutative()) { if (constExpressions.size() > 1) { - ExpressionVector vpOperandSave = std::move(vpOperand); - vpOperand = std::move(constExpressions); + ExpressionVector childrenSave = std::move(_children); + _children = std::move(constExpressions); optimizedOperands.emplace_back( ExpressionConstant::create(getExpressionContext(), evaluate(Document()))); - vpOperand = std::move(vpOperandSave); + _children = std::move(childrenSave); } else { optimizedOperands.insert( optimizedOperands.end(), constExpressions.begin(), constExpressions.end()); @@ -3143,7 +3184,7 @@ intrusive_ptr<Expression> ExpressionNary::optimize() { } if (constExpressions.size() > 1) { - vpOperand = std::move(constExpressions); + _children = std::move(constExpressions); optimizedOperands.emplace_back( ExpressionConstant::create(getExpressionContext(), evaluate(Document()))); } else { @@ -3151,27 +3192,27 @@ intrusive_ptr<Expression> ExpressionNary::optimize() { optimizedOperands.end(), constExpressions.begin(), constExpressions.end()); } - vpOperand = std::move(optimizedOperands); + _children = std::move(optimizedOperands); } return this; } void ExpressionNary::_doAddDependencies(DepsTracker* deps) const { - for (auto&& operand : vpOperand) { + for (auto&& operand : _children) { operand->addDependencies(deps); } } void ExpressionNary::addOperand(const intrusive_ptr<Expression>& pExpression) { - vpOperand.push_back(pExpression); + _children.push_back(pExpression); } Value ExpressionNary::serialize(bool explain) const { - const size_t nOperand = vpOperand.size(); + const size_t nOperand = _children.size(); vector<Value> array; /* build up the array */ for (size_t i = 0; i < nOperand; i++) - array.push_back(vpOperand[i]->serialize(explain)); + array.push_back(_children[i]->serialize(explain)); return Value(DOC(getOpName() << array)); } @@ -3179,7 +3220,7 @@ Value ExpressionNary::serialize(bool explain) const { /* ------------------------- ExpressionNot ----------------------------- */ Value ExpressionNot::evaluate(const Document& root) const { - Value pOp(vpOperand[0]->evaluate(root)); + Value pOp(_children[0]->evaluate(root)); bool b = pOp.coerceToBool(); return Value(!b); @@ -3193,9 +3234,9 @@ const char* ExpressionNot::getOpName() const { /* -------------------------- ExpressionOr ----------------------------- */ Value ExpressionOr::evaluate(const Document& root) const { - const size_t n = vpOperand.size(); + const size_t n = _children.size(); for (size_t i = 0; i < n; ++i) { - Value pValue(vpOperand[i]->evaluate(root)); + Value pValue(_children[i]->evaluate(root)); if (pValue.coerceToBool()) return Value(true); } @@ -3217,10 +3258,10 @@ intrusive_ptr<Expression> ExpressionOr::optimize() { promised by ExpressionNary::optimize(),) then there's nothing we can do. */ - const size_t n = pOr->vpOperand.size(); + const size_t n = pOr->_children.size(); // ExpressionNary::optimize() generates an ExpressionConstant for {$or:[]}. verify(n > 0); - intrusive_ptr<Expression> pLast(pOr->vpOperand[n - 1]); + intrusive_ptr<Expression> pLast(pOr->_children[n - 1]); const ExpressionConstant* pConst = dynamic_cast<ExpressionConstant*>(pLast.get()); if (!pConst) return pE; @@ -3244,14 +3285,14 @@ intrusive_ptr<Expression> ExpressionOr::optimize() { */ if (n == 2) { intrusive_ptr<Expression> pFinal( - ExpressionCoerceToBool::create(getExpressionContext(), pOr->vpOperand[0])); + ExpressionCoerceToBool::create(getExpressionContext(), std::move(pOr->_children[0]))); return pFinal; } /* Remove the final "false" value, and return the new expression. */ - pOr->vpOperand.resize(n - 1); + pOr->_children.resize(n - 1); return pE; } @@ -3358,16 +3399,16 @@ bool representableAsLong(long long base, long long exp) { intrusive_ptr<Expression> ExpressionPow::create( const boost::intrusive_ptr<ExpressionContext>& expCtx, Value base, Value exp) { intrusive_ptr<ExpressionPow> expr(new ExpressionPow(expCtx)); - expr->vpOperand.push_back( + expr->_children.push_back( ExpressionConstant::create(expr->getExpressionContext(), std::move(base))); - expr->vpOperand.push_back( + expr->_children.push_back( ExpressionConstant::create(expr->getExpressionContext(), std::move(exp))); return expr; } Value ExpressionPow::evaluate(const Document& root) const { - Value baseVal = vpOperand[0]->evaluate(root); - Value expVal = vpOperand[1]->evaluate(root); + Value baseVal = _children[0]->evaluate(root); + Value expVal = _children[1]->evaluate(root); if (baseVal.nullish() || expVal.nullish()) return Value(BSONNULL); @@ -3484,8 +3525,8 @@ const char* ExpressionPow::getOpName() const { /* ------------------------- ExpressionRange ------------------------------ */ Value ExpressionRange::evaluate(const Document& root) const { - Value startVal(vpOperand[0]->evaluate(root)); - Value endVal(vpOperand[1]->evaluate(root)); + Value startVal(_children[0]->evaluate(root)); + Value endVal(_children[1]->evaluate(root)); uassert(34443, str::stream() << "$range requires a numeric starting value, found value of type: " @@ -3510,9 +3551,9 @@ Value ExpressionRange::evaluate(const Document& root) const { int end = endVal.coerceToInt(); int step = 1; - if (vpOperand.size() == 3) { + if (_children.size() == 3) { // A step was specified by the user. - Value stepVal(vpOperand[2]->evaluate(root)); + Value stepVal(_children[2]->evaluate(root)); uassert(34447, str::stream() << "$range requires a numeric step value, found value of type:" @@ -3555,32 +3596,35 @@ intrusive_ptr<Expression> ExpressionReduce::parse( << typeName(expr.type()), expr.type() == Object); - intrusive_ptr<ExpressionReduce> reduce(new ExpressionReduce(expCtx)); // vpsSub is used only to parse 'in', which must have access to $$this and $$value. VariablesParseState vpsSub(vps); - reduce->_thisVar = vpsSub.defineVariable("this"); - reduce->_valueVar = vpsSub.defineVariable("value"); + auto thisVar = vpsSub.defineVariable("this"); + auto valueVar = vpsSub.defineVariable("value"); + boost::intrusive_ptr<Expression> input; + boost::intrusive_ptr<Expression> initial; + boost::intrusive_ptr<Expression> in; for (auto&& elem : expr.Obj()) { auto field = elem.fieldNameStringData(); if (field == "input") { - reduce->_input = parseOperand(expCtx, elem, vps); + input = parseOperand(expCtx, elem, vps); } else if (field == "initialValue") { - reduce->_initial = parseOperand(expCtx, elem, vps); + initial = parseOperand(expCtx, elem, vps); } else if (field == "in") { - reduce->_in = parseOperand(expCtx, elem, vpsSub); + in = parseOperand(expCtx, elem, vpsSub); } else { uasserted(40076, str::stream() << "$reduce found an unknown argument: " << field); } } - uassert(40077, "$reduce requires 'input' to be specified", reduce->_input); - uassert(40078, "$reduce requires 'initialValue' to be specified", reduce->_initial); - uassert(40079, "$reduce requires 'in' to be specified", reduce->_in); + uassert(40077, "$reduce requires 'input' to be specified", input); + uassert(40078, "$reduce requires 'initialValue' to be specified", initial); + uassert(40079, "$reduce requires 'in' to be specified", in); - return reduce; + return new ExpressionReduce( + expCtx, std::move(input), std::move(initial), std::move(in), thisVar, valueVar); } Value ExpressionReduce::evaluate(const Document& root) const { @@ -3631,7 +3675,7 @@ Value ExpressionReduce::serialize(bool explain) const { /* ------------------------ ExpressionReverseArray ------------------------ */ Value ExpressionReverseArray::evaluate(const Document& root) const { - Value input(vpOperand[0]->evaluate(root)); + Value input(_children[0]->evaluate(root)); if (input.nullish()) { return Value(BSONNULL); @@ -3668,8 +3712,8 @@ ValueSet arrayToSet(const Value& val, const ValueComparator& valueComparator) { /* ----------------------- ExpressionSetDifference ---------------------------- */ Value ExpressionSetDifference::evaluate(const Document& root) const { - const Value lhs = vpOperand[0]->evaluate(root); - const Value rhs = vpOperand[1]->evaluate(root); + const Value lhs = _children[0]->evaluate(root); + const Value rhs = _children[1]->evaluate(root); if (lhs.nullish() || rhs.nullish()) { return Value(BSONNULL); @@ -3714,12 +3758,12 @@ void ExpressionSetEquals::validateArguments(const ExpressionVector& args) const } Value ExpressionSetEquals::evaluate(const Document& root) const { - const size_t n = vpOperand.size(); + 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 = vpOperand[i]->evaluate(root); + const Value nextEntry = _children[i]->evaluate(root); uassert(17044, str::stream() << "All operands of $setEquals must be arrays. One " << "argument is of type: " @@ -3751,11 +3795,11 @@ const char* ExpressionSetEquals::getOpName() const { /* ----------------------- ExpressionSetIntersection ---------------------------- */ Value ExpressionSetIntersection::evaluate(const Document& root) const { - const size_t n = vpOperand.size(); + 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 = vpOperand[i]->evaluate(root); + const Value nextEntry = _children[i]->evaluate(root); if (nextEntry.nullish()) { return Value(BSONNULL); } @@ -3812,8 +3856,8 @@ Value setIsSubsetHelper(const vector<Value>& lhs, const ValueSet& rhs) { } Value ExpressionSetIsSubset::evaluate(const Document& root) const { - const Value lhs = vpOperand[0]->evaluate(root); - const Value rhs = vpOperand[1]->evaluate(root); + const Value lhs = _children[0]->evaluate(root); + const Value rhs = _children[1]->evaluate(root); uassert(17046, str::stream() << "both operands of $setIsSubset must be arrays. First " @@ -3843,11 +3887,11 @@ public: const ValueSet& cachedRhsSet, const ExpressionVector& operands) : ExpressionSetIsSubset(expCtx), _cachedRhsSet(cachedRhsSet) { - vpOperand = operands; + _children = operands; } virtual Value evaluate(const Document& root) const { - const Value lhs = vpOperand[0]->evaluate(root); + const Value lhs = _children[0]->evaluate(root); uassert(17310, str::stream() << "both operands of $setIsSubset must be arrays. First " @@ -3870,7 +3914,7 @@ intrusive_ptr<Expression> ExpressionSetIsSubset::optimize() { if (optimized.get() != this) return optimized; - if (ExpressionConstant* ec = dynamic_cast<ExpressionConstant*>(vpOperand[1].get())) { + if (ExpressionConstant* ec = dynamic_cast<ExpressionConstant*>(_children[1].get())) { const Value rhs = ec->getValue(); uassert(17311, str::stream() << "both operands of $setIsSubset must be arrays. Second " @@ -3881,7 +3925,7 @@ intrusive_ptr<Expression> ExpressionSetIsSubset::optimize() { intrusive_ptr<Expression> optimizedWithConstant( new Optimized(this->getExpressionContext(), arrayToSet(rhs, getExpressionContext()->getValueComparator()), - vpOperand)); + _children)); return optimizedWithConstant; } return optimized; @@ -3896,9 +3940,9 @@ const char* ExpressionSetIsSubset::getOpName() const { Value ExpressionSetUnion::evaluate(const Document& root) const { ValueSet unionedSet = getExpressionContext()->getValueComparator().makeOrderedValueSet(); - const size_t n = vpOperand.size(); + const size_t n = _children.size(); for (size_t i = 0; i < n; i++) { - const Value newEntries = vpOperand[i]->evaluate(root); + const Value newEntries = _children[i]->evaluate(root); if (newEntries.nullish()) { return Value(BSONNULL); } @@ -3921,7 +3965,7 @@ const char* ExpressionSetUnion::getOpName() const { /* ----------------------- ExpressionIsArray ---------------------------- */ Value ExpressionIsArray::evaluate(const Document& root) const { - Value argument = vpOperand[0]->evaluate(root); + Value argument = _children[0]->evaluate(root); return Value(argument.isArray()); } @@ -3933,11 +3977,11 @@ const char* ExpressionIsArray::getOpName() const { /* ----------------------- ExpressionSlice ---------------------------- */ Value ExpressionSlice::evaluate(const Document& root) const { - const size_t n = vpOperand.size(); + const size_t n = _children.size(); - Value arrayVal = vpOperand[0]->evaluate(root); + Value arrayVal = _children[0]->evaluate(root); // Could be either a start index or the length from 0. - Value arg2 = vpOperand[1]->evaluate(root); + Value arg2 = _children[1]->evaluate(root); if (arrayVal.nullish() || arg2.nullish()) { return Value(BSONNULL); @@ -3988,7 +4032,7 @@ Value ExpressionSlice::evaluate(const Document& root) const { start = std::min(array.size(), size_t(startInt)); } - Value countVal = vpOperand[2]->evaluate(root); + Value countVal = _children[2]->evaluate(root); if (countVal.nullish()) { return Value(BSONNULL); @@ -4024,7 +4068,7 @@ const char* ExpressionSlice::getOpName() const { /* ----------------------- ExpressionSize ---------------------------- */ Value ExpressionSize::evaluate(const Document& root) const { - Value array = vpOperand[0]->evaluate(root); + Value array = _children[0]->evaluate(root); uassert(17124, str::stream() << "The argument to $size must be an array, but was of type: " @@ -4041,8 +4085,8 @@ const char* ExpressionSize::getOpName() const { /* ----------------------- ExpressionSplit --------------------------- */ Value ExpressionSplit::evaluate(const Document& root) const { - Value inputArg = vpOperand[0]->evaluate(root); - Value separatorArg = vpOperand[1]->evaluate(root); + Value inputArg = _children[0]->evaluate(root); + Value separatorArg = _children[1]->evaluate(root); if (inputArg.nullish() || separatorArg.nullish()) { return Value(BSONNULL); @@ -4119,8 +4163,8 @@ const char* ExpressionSqrt::getOpName() const { /* ----------------------- ExpressionStrcasecmp ---------------------------- */ Value ExpressionStrcasecmp::evaluate(const Document& root) const { - Value pString1(vpOperand[0]->evaluate(root)); - Value pString2(vpOperand[1]->evaluate(root)); + Value pString1(_children[0]->evaluate(root)); + Value pString2(_children[1]->evaluate(root)); /* boost::iequals returns a bool not an int so strings must actually be allocated */ string str1 = boost::to_upper_copy(pString1.coerceToString()); @@ -4143,9 +4187,9 @@ const char* ExpressionStrcasecmp::getOpName() const { /* ----------------------- ExpressionSubstrBytes ---------------------------- */ Value ExpressionSubstrBytes::evaluate(const Document& root) const { - Value pString(vpOperand[0]->evaluate(root)); - Value pLower(vpOperand[1]->evaluate(root)); - Value pLength(vpOperand[2]->evaluate(root)); + Value pString(_children[0]->evaluate(root)); + Value pLower(_children[1]->evaluate(root)); + Value pLength(_children[2]->evaluate(root)); string str = pString.coerceToString(); uassert(16034, @@ -4208,9 +4252,9 @@ const char* ExpressionSubstrBytes::getOpName() const { /* ----------------------- ExpressionSubstrCP ---------------------------- */ Value ExpressionSubstrCP::evaluate(const Document& root) const { - Value inputVal(vpOperand[0]->evaluate(root)); - Value lowerVal(vpOperand[1]->evaluate(root)); - Value lengthVal(vpOperand[2]->evaluate(root)); + Value inputVal(_children[0]->evaluate(root)); + Value lowerVal(_children[1]->evaluate(root)); + Value lengthVal(_children[2]->evaluate(root)); std::string str = inputVal.coerceToString(); uassert(34450, @@ -4283,7 +4327,7 @@ const char* ExpressionSubstrCP::getOpName() const { /* ----------------------- ExpressionStrLenBytes ------------------------- */ Value ExpressionStrLenBytes::evaluate(const Document& root) const { - Value str(vpOperand[0]->evaluate(root)); + Value str(_children[0]->evaluate(root)); uassert(34473, str::stream() << "$strLenBytes requires a string argument, found: " @@ -4306,7 +4350,7 @@ const char* ExpressionStrLenBytes::getOpName() const { /* ----------------------- ExpressionStrLenCP ------------------------- */ Value ExpressionStrLenCP::evaluate(const Document& root) const { - Value val(vpOperand[0]->evaluate(root)); + Value val(_children[0]->evaluate(root)); uassert(34471, str::stream() << "$strLenCP requires a string argument, found: " @@ -4331,8 +4375,8 @@ const char* ExpressionStrLenCP::getOpName() const { /* ----------------------- ExpressionSubtract ---------------------------- */ Value ExpressionSubtract::evaluate(const Document& root) const { - Value lhs = vpOperand[0]->evaluate(root); - Value rhs = vpOperand[1]->evaluate(root); + Value lhs = _children[0]->evaluate(root); + Value rhs = _children[1]->evaluate(root); BSONType diffType = Value::getWidestNumeric(rhs.getType(), lhs.getType()); @@ -4405,8 +4449,8 @@ boost::intrusive_ptr<Expression> ExpressionSwitch::parse( << typeName(expr.type()), expr.type() == Object); - intrusive_ptr<ExpressionSwitch> expression(new ExpressionSwitch(expCtx)); - + boost::intrusive_ptr<Expression> expDefault; + std::vector<boost::intrusive_ptr<Expression>> children; for (auto&& elem : expr.Obj()) { auto field = elem.fieldNameStringData(); @@ -4423,15 +4467,15 @@ boost::intrusive_ptr<Expression> ExpressionSwitch::parse( << typeName(branch.type()), branch.type() == Object); - ExpressionPair branchExpression; + boost::intrusive_ptr<Expression> switchCase, switchThen; for (auto&& branchElement : branch.Obj()) { auto branchField = branchElement.fieldNameStringData(); if (branchField == "case") { - branchExpression.first = parseOperand(expCtx, branchElement, vps); + switchCase = parseOperand(expCtx, branchElement, vps); } else if (branchField == "then") { - branchExpression.second = parseOperand(expCtx, branchElement, vps); + switchThen = parseOperand(expCtx, branchElement, vps); } else { uasserted(40063, str::stream() << "$switch found an unknown argument to a branch: " @@ -4439,26 +4483,37 @@ boost::intrusive_ptr<Expression> ExpressionSwitch::parse( } } - uassert(40064, - "$switch requires each branch have a 'case' expression", - branchExpression.first); - uassert(40065, - "$switch requires each branch have a 'then' expression.", - branchExpression.second); + uassert(40064, "$switch requires each branch have a 'case' expression", switchCase); + uassert( + 40065, "$switch requires each branch have a 'then' expression.", switchThen); - expression->_branches.push_back(branchExpression); + children.push_back(std::move(switchCase)); + children.push_back(std::move(switchThen)); } } else if (field == "default") { // Optional, arbitrary expression. - expression->_default = parseOperand(expCtx, elem, vps); + expDefault = parseOperand(expCtx, elem, vps); } else { uasserted(40067, str::stream() << "$switch found an unknown argument: " << field); } } + children.push_back(std::move(expDefault)); + // Obtain references to the case and branch expressions two-by-two from the children vector, + // ignore the last. + std::vector<ExpressionPair> branches; + boost::optional<boost::intrusive_ptr<Expression>&> first; + for (auto&& child : children) { + if (first) { + branches.emplace_back(*first, child); + first = boost::none; + } else { + first = child; + } + } - uassert(40068, "$switch requires at least one branch.", !expression->_branches.empty()); + uassert(40068, "$switch requires at least one branch.", !branches.empty()); - return expression; + return new ExpressionSwitch(expCtx, std::move(children), std::move(branches)); } void ExpressionSwitch::_doAddDependencies(DepsTracker* deps) const { @@ -4477,12 +4532,10 @@ boost::intrusive_ptr<Expression> ExpressionSwitch::optimize() { _default = _default->optimize(); } - std::transform(_branches.begin(), - _branches.end(), - _branches.begin(), - [](ExpressionPair branch) -> ExpressionPair { - return {branch.first->optimize(), branch.second->optimize()}; - }); + for (auto&& [switchCase, switchThen] : _branches) { + switchCase = switchCase->optimize(); + switchThen = switchThen->optimize(); + } return this; } @@ -4508,7 +4561,7 @@ Value ExpressionSwitch::serialize(bool explain) const { /* ------------------------- ExpressionToLower ----------------------------- */ Value ExpressionToLower::evaluate(const Document& root) const { - Value pString(vpOperand[0]->evaluate(root)); + Value pString(_children[0]->evaluate(root)); string str = pString.coerceToString(); boost::to_lower(str); return Value(str); @@ -4522,7 +4575,7 @@ const char* ExpressionToLower::getOpName() const { /* ------------------------- ExpressionToUpper -------------------------- */ Value ExpressionToUpper::evaluate(const Document& root) const { - Value pString(vpOperand[0]->evaluate(root)); + Value pString(_children[0]->evaluate(root)); string str(pString.coerceToString()); boost::to_upper(str); return Value(str); @@ -4776,13 +4829,13 @@ void assertFlagsValid(uint32_t flags, } static Value evaluateRoundOrTrunc(const Document& root, - const std::vector<boost::intrusive_ptr<Expression>>& vpOperand, + const std::vector<boost::intrusive_ptr<Expression>>& children, const std::string& opName, Decimal128::RoundingMode roundingMode, double (*doubleOp)(double)) { constexpr auto maxPrecision = 100LL; constexpr auto minPrecision = -20LL; - auto numericArg = Value(vpOperand[0]->evaluate(root)); + auto numericArg = Value(children[0]->evaluate(root)); if (numericArg.nullish()) { return Value(BSONNULL); } @@ -4790,7 +4843,7 @@ static Value evaluateRoundOrTrunc(const Document& root, str::stream() << opName << " only supports numeric types, not " << typeName(numericArg.getType()), numericArg.numeric()); - if (vpOperand.size() == 1) { + if (children.size() == 1) { switch (numericArg.getType()) { case BSONType::NumberDecimal: return Value( @@ -4804,7 +4857,7 @@ static Value evaluateRoundOrTrunc(const Document& root, } } // Else, if precision is specified, round to the specified precision. - auto precisionArg = Value(vpOperand[1]->evaluate(root)); + auto precisionArg = Value(children[1]->evaluate(root)); if (precisionArg.nullish()) { return Value(BSONNULL); } @@ -4859,7 +4912,7 @@ static Value evaluateRoundOrTrunc(const Document& root, Value ExpressionRound::evaluate(const Document& root) const { return evaluateRoundOrTrunc( - root, vpOperand, getOpName(), Decimal128::kRoundTiesToEven, &std::round); + root, _children, getOpName(), Decimal128::kRoundTiesToEven, &std::round); } REGISTER_EXPRESSION(round, ExpressionRound::parse); @@ -4869,7 +4922,7 @@ const char* ExpressionRound::getOpName() const { Value ExpressionTrunc::evaluate(const Document& root) const { return evaluateRoundOrTrunc( - root, vpOperand, getOpName(), Decimal128::kRoundTowardZero, &std::trunc); + root, _children, getOpName(), Decimal128::kRoundTowardZero, &std::trunc); } REGISTER_EXPRESSION(trunc, ExpressionTrunc::parse); @@ -4880,7 +4933,7 @@ const char* ExpressionTrunc::getOpName() const { /* ------------------------- ExpressionType ----------------------------- */ Value ExpressionType::evaluate(const Document& root) const { - Value val(vpOperand[0]->evaluate(root)); + Value val(_children[0]->evaluate(root)); return Value(StringData(typeName(val.getType()))); } @@ -4901,7 +4954,11 @@ intrusive_ptr<Expression> ExpressionZip::parse( << typeName(expr.type()), expr.type() == Object); - intrusive_ptr<ExpressionZip> newZip(new ExpressionZip(expCtx)); + auto useLongestLength = false; + std::vector<boost::intrusive_ptr<Expression>> children; + // We need to ensure defaults appear after inputs so we build them seperately and then + // concatenate them. + std::vector<boost::intrusive_ptr<Expression>> tempDefaultChildren; for (auto&& elem : expr.Obj()) { const auto field = elem.fieldNameStringData(); @@ -4911,7 +4968,7 @@ intrusive_ptr<Expression> ExpressionZip::parse( << typeName(elem.type()), elem.type() == Array); for (auto&& subExpr : elem.Array()) { - newZip->_inputs.push_back(parseOperand(expCtx, subExpr, vps)); + children.push_back(parseOperand(expCtx, subExpr, vps)); } } else if (field == "defaults") { uassert(34462, @@ -4919,29 +4976,44 @@ intrusive_ptr<Expression> ExpressionZip::parse( << typeName(elem.type()), elem.type() == Array); for (auto&& subExpr : elem.Array()) { - newZip->_defaults.push_back(parseOperand(expCtx, subExpr, vps)); + tempDefaultChildren.push_back(parseOperand(expCtx, subExpr, vps)); } } else if (field == "useLongestLength") { uassert(34463, str::stream() << "useLongestLength must be a bool, found " << typeName(expr.type()), elem.type() == Bool); - newZip->_useLongestLength = elem.Bool(); + useLongestLength = elem.Bool(); } else { uasserted(34464, str::stream() << "$zip found an unknown argument: " << elem.fieldName()); } } - uassert(34465, "$zip requires at least one input array", !newZip->_inputs.empty()); + auto numInputs = children.size(); + std::move(tempDefaultChildren.begin(), tempDefaultChildren.end(), std::back_inserter(children)); + + std::vector<std::reference_wrapper<boost::intrusive_ptr<Expression>>> inputs; + std::vector<std::reference_wrapper<boost::intrusive_ptr<Expression>>> defaults; + for (auto&& child : children) { + if (numInputs == 0) { + defaults.push_back(child); + } else { + inputs.push_back(child); + numInputs--; + } + } + + uassert(34465, "$zip requires at least one input array", !inputs.empty()); uassert(34466, "cannot specify defaults unless useLongestLength is true", - (newZip->_useLongestLength || newZip->_defaults.empty())); + (useLongestLength || defaults.empty())); uassert(34467, "defaults and inputs must have the same length", - (newZip->_defaults.empty() || newZip->_defaults.size() == newZip->_inputs.size())); + (defaults.empty() || defaults.size() == inputs.size())); - return std::move(newZip); + return new ExpressionZip( + expCtx, useLongestLength, std::move(children), std::move(inputs), std::move(defaults)); } Value ExpressionZip::evaluate(const Document& root) const { @@ -4952,7 +5024,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]->evaluate(root); + Value evalExpr = _inputs[i].get()->evaluate(root); if (evalExpr.nullish()) { return Value(BSONNULL); } @@ -4981,7 +5053,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]->evaluate(root); + evaluatedDefaults[i] = _defaults[i].get()->evaluate(root); } } @@ -5014,20 +5086,10 @@ Value ExpressionZip::evaluate(const Document& root) const { } boost::intrusive_ptr<Expression> ExpressionZip::optimize() { - std::transform(_inputs.begin(), - _inputs.end(), - _inputs.begin(), - [](intrusive_ptr<Expression> inputExpression) -> intrusive_ptr<Expression> { - return inputExpression->optimize(); - }); - - std::transform(_defaults.begin(), - _defaults.end(), - _defaults.begin(), - [](intrusive_ptr<Expression> defaultExpression) -> intrusive_ptr<Expression> { - return defaultExpression->optimize(); - }); - + for (auto&& input : _inputs) + input.get() = input.get()->optimize(); + for (auto&& zipDefault : _defaults) + zipDefault.get() = zipDefault.get()->optimize(); return this; } @@ -5037,11 +5099,11 @@ Value ExpressionZip::serialize(bool explain) const { Value serializedUseLongestLength = Value(_useLongestLength); for (auto&& expr : _inputs) { - serializedInput.push_back(expr->serialize(explain)); + serializedInput.push_back(expr.get()->serialize(explain)); } for (auto&& expr : _defaults) { - serializedDefaults.push_back(expr->serialize(explain)); + serializedDefaults.push_back(expr.get()->serialize(explain)); } return Value(DOC("$zip" << DOC("inputs" << Value(serializedInput) << "defaults" @@ -5496,7 +5558,7 @@ Expression::Parser makeConversionAlias(const StringData shortcutName, BSONType t str::stream() << shortcutName << " requires a single argument, got " << operands.size(), operands.size() == 1); - return ExpressionConvert::create(expCtx, operands[0], toType); + return ExpressionConvert::create(expCtx, std::move(operands[0]), toType); }; } @@ -5517,17 +5579,26 @@ REGISTER_EXPRESSION(toBool, makeConversionAlias("$toBool"_sd, BSONType::Bool)); boost::intrusive_ptr<Expression> ExpressionConvert::create( const boost::intrusive_ptr<ExpressionContext>& expCtx, - const boost::intrusive_ptr<Expression>& input, + boost::intrusive_ptr<Expression> input, BSONType toType) { - return new ExpressionConvert(expCtx, input, toType); + return new ExpressionConvert( + expCtx, + std::move(input), + ExpressionConstant::create(expCtx, Value(StringData(typeName(toType)))), + nullptr, + nullptr); } ExpressionConvert::ExpressionConvert(const boost::intrusive_ptr<ExpressionContext>& expCtx, - const boost::intrusive_ptr<Expression>& input, - BSONType toType) - : Expression(expCtx), - _input(input), - _to(ExpressionConstant::create(expCtx, Value(StringData(typeName(toType))))) {} + boost::intrusive_ptr<Expression> input, + boost::intrusive_ptr<Expression> to, + boost::intrusive_ptr<Expression> onError, + boost::intrusive_ptr<Expression> onNull) + : Expression(expCtx, {std::move(input), std::move(to), std::move(onError), std::move(onNull)}), + _input(_children[0]), + _to(_children[1]), + _onError(_children[2]), + _onNull(_children[3]) {} intrusive_ptr<Expression> ExpressionConvert::parse( const boost::intrusive_ptr<ExpressionContext>& expCtx, @@ -5538,18 +5609,20 @@ intrusive_ptr<Expression> ExpressionConvert::parse( << typeName(expr.type()), expr.type() == BSONType::Object); - intrusive_ptr<ExpressionConvert> newConvert(new ExpressionConvert(expCtx)); - + boost::intrusive_ptr<Expression> input; + boost::intrusive_ptr<Expression> to; + boost::intrusive_ptr<Expression> onError; + boost::intrusive_ptr<Expression> onNull; for (auto&& elem : expr.embeddedObject()) { const auto field = elem.fieldNameStringData(); if (field == "input"_sd) { - newConvert->_input = parseOperand(expCtx, elem, vps); + input = parseOperand(expCtx, elem, vps); } else if (field == "to"_sd) { - newConvert->_to = parseOperand(expCtx, elem, vps); + to = parseOperand(expCtx, elem, vps); } else if (field == "onError"_sd) { - newConvert->_onError = parseOperand(expCtx, elem, vps); + onError = parseOperand(expCtx, elem, vps); } else if (field == "onNull"_sd) { - newConvert->_onNull = parseOperand(expCtx, elem, vps); + onNull = parseOperand(expCtx, elem, vps); } else { uasserted(ErrorCodes::FailedToParse, str::stream() << "$convert found an unknown argument: " @@ -5557,10 +5630,11 @@ intrusive_ptr<Expression> ExpressionConvert::parse( } } - uassert(ErrorCodes::FailedToParse, "Missing 'input' parameter to $convert", newConvert->_input); - uassert(ErrorCodes::FailedToParse, "Missing 'to' parameter to $convert", newConvert->_to); + uassert(ErrorCodes::FailedToParse, "Missing 'input' parameter to $convert", input); + uassert(ErrorCodes::FailedToParse, "Missing 'to' parameter to $convert", to); - return std::move(newConvert); + return new ExpressionConvert( + expCtx, std::move(input), std::move(to), std::move(onError), std::move(onNull)); } Value ExpressionConvert::evaluate(const Document& root) const { @@ -5670,36 +5744,46 @@ Value ExpressionConvert::performConversion(BSONType targetType, Value inputValue return table.findConversionFunc(inputType, targetType)(getExpressionContext(), inputValue); } -/* -------------------------- ExpressionRegex ------------------------------ */ +namespace { -ExpressionRegex::ExpressionRegex(const boost::intrusive_ptr<ExpressionContext>& expCtx, - BSONElement expr, - const VariablesParseState& vpsIn, - const std::string& opName) - : Expression(expCtx), _opName(std::move(opName)) { +auto CommonRegexParse(const boost::intrusive_ptr<ExpressionContext>& expCtx, + BSONElement expr, + const VariablesParseState& vpsIn, + StringData opName) { uassert(51103, - str::stream() << _opName << " expects an object of named arguments but found: " + str::stream() << opName << " expects an object of named arguments but found: " << expr.type(), expr.type() == BSONType::Object); + struct { + boost::intrusive_ptr<Expression> input; + boost::intrusive_ptr<Expression> regex; + boost::intrusive_ptr<Expression> options; + } parsed; for (auto&& elem : expr.embeddedObject()) { const auto field = elem.fieldNameStringData(); if (field == "input"_sd) { - _input = parseOperand(expCtx, elem, vpsIn); + parsed.input = Expression::parseOperand(expCtx, elem, vpsIn); } else if (field == "regex"_sd) { - _regex = parseOperand(expCtx, elem, vpsIn); + parsed.regex = Expression::parseOperand(expCtx, elem, vpsIn); } else if (field == "options"_sd) { - _options = parseOperand(expCtx, elem, vpsIn); + parsed.options = Expression::parseOperand(expCtx, elem, vpsIn); } else { uasserted(31024, - str::stream() << _opName << " found an unknown argument: " + str::stream() << opName << " found an unknown argument: " << elem.fieldNameStringData()); } } - uassert(31022, str::stream() << _opName << " requires 'input' parameter", _input); - uassert(31023, str::stream() << _opName << " requires 'regex' parameter", _regex); + uassert(31022, str::stream() << opName << " requires 'input' parameter", parsed.input); + uassert(31023, str::stream() << opName << " requires 'regex' parameter", parsed.regex); + + return parsed; } +} // namespace + +/* -------------------------- ExpressionRegex ------------------------------ */ + ExpressionRegex::RegexExecutionState ExpressionRegex::buildInitialState( const Document& root) const { Value textInput = _input->evaluate(root); @@ -5922,6 +6006,17 @@ void ExpressionRegex::_doAddDependencies(DepsTracker* deps) const { /* -------------------------- ExpressionRegexFind ------------------------------ */ +REGISTER_EXPRESSION(regexFind, ExpressionRegexFind::parse); +boost::intrusive_ptr<Expression> ExpressionRegexFind::parse( + const boost::intrusive_ptr<ExpressionContext>& expCtx, + BSONElement expr, + const VariablesParseState& vpsIn) { + auto opName = "$regexFind"_sd; + auto[input, regex, options] = CommonRegexParse(expCtx, expr, vpsIn, opName); + return new ExpressionRegexFind( + expCtx, std::move(input), std::move(regex), std::move(options), opName); +} + Value ExpressionRegexFind::evaluate(const Document& root) const { auto executionState = buildInitialState(root); if (executionState.nullish()) { @@ -5930,10 +6025,19 @@ Value ExpressionRegexFind::evaluate(const Document& root) const { return nextMatch(&executionState); } -REGISTER_EXPRESSION(regexFind, ExpressionRegexFind::parse); - /* -------------------------- ExpressionRegexFindAll ------------------------------ */ +REGISTER_EXPRESSION(regexFindAll, ExpressionRegexFindAll::parse); +boost::intrusive_ptr<Expression> ExpressionRegexFindAll::parse( + const boost::intrusive_ptr<ExpressionContext>& expCtx, + BSONElement expr, + const VariablesParseState& vpsIn) { + auto opName = "$regexFindAll"_sd; + auto[input, regex, options] = CommonRegexParse(expCtx, expr, vpsIn, opName); + return new ExpressionRegexFindAll( + expCtx, std::move(input), std::move(regex), std::move(options), opName); +} + Value ExpressionRegexFindAll::evaluate(const Document& root) const { std::vector<Value> output; auto executionState = buildInitialState(root); @@ -5982,16 +6086,23 @@ Value ExpressionRegexFindAll::evaluate(const Document& root) const { return Value(output); } -REGISTER_EXPRESSION(regexFindAll, ExpressionRegexFindAll::parse); - /* -------------------------- ExpressionRegexMatch ------------------------------ */ +REGISTER_EXPRESSION(regexMatch, ExpressionRegexMatch::parse); +boost::intrusive_ptr<Expression> ExpressionRegexMatch::parse( + const boost::intrusive_ptr<ExpressionContext>& expCtx, + BSONElement expr, + const VariablesParseState& vpsIn) { + auto opName = "$regexMatch"_sd; + auto[input, regex, options] = CommonRegexParse(expCtx, expr, vpsIn, opName); + return new ExpressionRegexMatch( + expCtx, std::move(input), std::move(regex), std::move(options), opName); +} + Value ExpressionRegexMatch::evaluate(const Document& root) const { auto executionState = buildInitialState(root); // Return output of execute only if regex is not nullish. return executionState.nullish() ? Value(false) : Value(execute(&executionState) > 0); } -REGISTER_EXPRESSION(regexMatch, ExpressionRegexMatch::parse); - } // namespace mongo diff --git a/src/mongo/db/pipeline/expression.h b/src/mongo/db/pipeline/expression.h index 9bf881ea241..47829732ee4 100644 --- a/src/mongo/db/pipeline/expression.h +++ b/src/mongo/db/pipeline/expression.h @@ -36,6 +36,7 @@ #include <map> #include <pcre.h> #include <string> +#include <utility> #include <vector> #include "mongo/base/init.h" @@ -240,15 +241,23 @@ public: Parser parser, boost::optional<ServerGlobalParams::FeatureCompatibility::Version> requiredMinVersion); + const auto& getChildren() const { + return _children; + } + protected: - Expression(const boost::intrusive_ptr<ExpressionContext>& expCtx) : _expCtx(expCtx) { + using ExpressionVector = std::vector<boost::intrusive_ptr<Expression>>; + + Expression(const boost::intrusive_ptr<ExpressionContext>& expCtx) : Expression(expCtx, {}) {} + + Expression(const boost::intrusive_ptr<ExpressionContext>& expCtx, ExpressionVector&& children) + : _children(std::move(children)), _expCtx(expCtx) { auto varIds = _expCtx->variablesParseState.getDefinedVariableIDs(); if (!varIds.empty()) { _boundaryVariableId = *std::prev(varIds.end()); } } - typedef std::vector<boost::intrusive_ptr<Expression>> ExpressionVector; const boost::intrusive_ptr<ExpressionContext>& getExpressionContext() const { return _expCtx; @@ -256,12 +265,25 @@ protected: virtual void _doAddDependencies(DepsTracker* deps) const = 0; + /** + * Owning container for all sub-Expressions. + * + * Some derived classes contain named fields since they orginate from user syntax containing + * field names. These classes contain alternate data structures or object members for accessing + * children. These structures or object memebers are expected to reference this data structure. + * In addition this structure should not be modified by named-field derivied classes to avoid + * invalidating references. + */ + ExpressionVector _children; + private: boost::optional<Variables::Id> _boundaryVariableId; boost::intrusive_ptr<ExpressionContext> _expCtx; }; -// Inherit from ExpressionVariadic or ExpressionFixedArity instead of directly from this class. +/** + * Inherit from ExpressionVariadic or ExpressionFixedArity instead of directly from this class. + */ class ExpressionNary : public Expression { public: boost::intrusive_ptr<Expression> optimize() override; @@ -282,16 +304,8 @@ public: return false; } - /* - Get the name of the operator. - - @returns the name of the operator; this std::string belongs to the class - implementation, and should not be deleted - and should not - */ virtual const char* getOpName() const = 0; - /// Allow subclasses the opportunity to validate arguments at parse time. virtual void validateArguments(const ExpressionVector& args) const {} static ExpressionVector parseArguments(const boost::intrusive_ptr<ExpressionContext>& expCtx, @@ -299,7 +313,7 @@ public: const VariablesParseState& vps); const ExpressionVector& getOperandList() const { - return vpOperand; + return _children; } protected: @@ -307,8 +321,6 @@ protected: : Expression(expCtx) {} void _doAddDependencies(DepsTracker* deps) const override; - - ExpressionVector vpOperand; }; /// Inherit from ExpressionVariadic or ExpressionFixedArity instead of directly from this class. @@ -322,7 +334,7 @@ public: auto expr = make_intrusive<SubClass>(expCtx); ExpressionVector args = parseArguments(expCtx, bsonExpr, vps); expr->validateArguments(args); - expr->vpOperand = args; + expr->_children = std::move(args); return std::move(expr); } @@ -391,11 +403,11 @@ public: Value evaluate(const Document& root) const final { Accumulator accum(this->getExpressionContext()); - const size_t n = this->vpOperand.size(); + 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->vpOperand[0]->evaluate(root); + Value singleVal = this->_children[0]->evaluate(root); if (singleVal.getType() == Array) { for (const Value& val : singleVal.getArray()) { accum.process(val, false); @@ -405,7 +417,7 @@ public: } } else { // If multiple arguments are given, pass all arguments to the accumulator. - for (auto&& argument : this->vpOperand) { + for (auto&& argument : this->_children) { accum.process(argument->evaluate(root), false); } } @@ -415,7 +427,7 @@ public: bool isAssociative() const final { // Return false if a single argument is given to avoid a single array argument being treated // as an array instead of as a list of arguments. - if (this->vpOperand.size() == 1) { + if (this->_children.size() == 1) { return false; } return Accumulator(this->getExpressionContext()).isAssociative(); @@ -446,7 +458,7 @@ public: virtual ~ExpressionSingleNumericArg() = default; Value evaluate(const Document& root) const final { - Value arg = this->vpOperand[0]->evaluate(root); + Value arg = this->_children[0]->evaluate(root); if (arg.nullish()) return Value(BSONNULL); @@ -480,14 +492,14 @@ public: * 3. Call evaluateNumericArgs on the two numeric args. */ Value evaluate(const Document& root) const final { - Value arg1 = this->vpOperand[0]->evaluate(root); + Value arg1 = this->_children[0]->evaluate(root); 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->vpOperand[1]->evaluate(root); + Value arg2 = this->_children[1]->evaluate(root); if (arg2.nullish()) return Value(BSONNULL); uassert(51045, @@ -631,26 +643,26 @@ public: const boost::intrusive_ptr<ExpressionContext>& expCtx, BSONElement operatorElem, const VariablesParseState& variablesParseState) { - boost::intrusive_ptr<DateExpressionAcceptingTimeZone> dateExpression{new SubClass(expCtx)}; if (operatorElem.type() == BSONType::Object) { if (operatorElem.embeddedObject().firstElementFieldName()[0] == '$') { // Assume this is an expression specification representing the date argument // like {$add: [<date>, 1000]}. - dateExpression->_date = Expression::parseObject( - expCtx, operatorElem.embeddedObject(), variablesParseState); - return dateExpression; + return new SubClass(expCtx, + Expression::parseObject(expCtx, + operatorElem.embeddedObject(), + variablesParseState)); } else { // It's an object specifying the date and timezone options like {date: <date>, // timezone: <timezone>}. auto opName = operatorElem.fieldNameStringData(); + boost::intrusive_ptr<Expression> date; + boost::intrusive_ptr<Expression> timeZone; for (const auto& subElem : operatorElem.embeddedObject()) { auto argName = subElem.fieldNameStringData(); if (argName == "date"_sd) { - dateExpression->_date = - Expression::parseOperand(expCtx, subElem, variablesParseState); + date = Expression::parseOperand(expCtx, subElem, variablesParseState); } else if (argName == "timezone"_sd) { - dateExpression->_timeZone = - Expression::parseOperand(expCtx, subElem, variablesParseState); + timeZone = Expression::parseOperand(expCtx, subElem, variablesParseState); } else { uasserted(40535, str::stream() << "unrecognized option to " << opName << ": \"" @@ -661,31 +673,37 @@ public: uassert(40539, str::stream() << "missing 'date' argument to " << opName << ", provided: " << operatorElem, - dateExpression->_date); + date); + return new SubClass(expCtx, std::move(date), std::move(timeZone)); } - return dateExpression; } else if (operatorElem.type() == BSONType::Array) { auto elems = operatorElem.Array(); uassert( 40536, - str::stream() << dateExpression->_opName + str::stream() << operatorElem.fieldNameStringData() << " accepts exactly one argument if given an array, but was given " << elems.size(), elems.size() == 1); // We accept an argument wrapped in a single array. For example, either {$week: <date>} // or {$week: [<date>]} are valid, but not {$week: [{date: <date>}]}. - operatorElem = elems[0]; + return new SubClass(expCtx, + Expression::parseOperand(expCtx, elems[0], variablesParseState)); } - // It's some literal value, or the first element of a user-specified array. Either way it - // should be treated as the date argument. - dateExpression->_date = Expression::parseOperand(expCtx, operatorElem, variablesParseState); - return dateExpression; + // Exhausting the other possibilities, we are left with a literal value which should be + // treated as the date argument. + return new SubClass(expCtx, + Expression::parseOperand(expCtx, operatorElem, variablesParseState)); } protected: - explicit DateExpressionAcceptingTimeZone(StringData opName, - const boost::intrusive_ptr<ExpressionContext>& expCtx) - : Expression(expCtx), _opName(opName) {} + explicit DateExpressionAcceptingTimeZone(const boost::intrusive_ptr<ExpressionContext>& expCtx, + const StringData opName, + boost::intrusive_ptr<Expression> date, + boost::intrusive_ptr<Expression> timeZone) + : Expression(expCtx, {date, timeZone}), + _opName(opName), + _date(_children[0]), + _timeZone(_children[1]) {} void _doAddDependencies(DepsTracker* deps) const final { _date->addDependencies(deps); @@ -706,9 +724,9 @@ private: StringData _opName; // The expression representing the date argument. - boost::intrusive_ptr<Expression> _date; - // The expression representing the timezone argument, nullptr if not specified. - boost::intrusive_ptr<Expression> _timeZone = nullptr; + boost::intrusive_ptr<Expression>& _date; + // The expression representing the timezone argument. + boost::intrusive_ptr<Expression>& _timeZone; }; class ExpressionAbs final : public ExpressionSingleNumericArg<ExpressionAbs> { @@ -874,7 +892,7 @@ public: static boost::intrusive_ptr<ExpressionCoerceToBool> create( const boost::intrusive_ptr<ExpressionContext>& expCtx, - const boost::intrusive_ptr<Expression>& pExpression); + boost::intrusive_ptr<Expression> pExpression); void acceptVisitor(ExpressionVisitor* visitor) final { return visitor->visit(this); @@ -885,9 +903,9 @@ protected: private: ExpressionCoerceToBool(const boost::intrusive_ptr<ExpressionContext>& expCtx, - const boost::intrusive_ptr<Expression>& pExpression); + boost::intrusive_ptr<Expression> pExpression); - boost::intrusive_ptr<Expression> pExpression; + boost::intrusive_ptr<Expression>& pExpression; }; @@ -1020,11 +1038,11 @@ private: boost::intrusive_ptr<Expression> onNull, boost::intrusive_ptr<Expression> onError); - boost::intrusive_ptr<Expression> _dateString; - boost::intrusive_ptr<Expression> _timeZone; - boost::intrusive_ptr<Expression> _format; - boost::intrusive_ptr<Expression> _onNull; - boost::intrusive_ptr<Expression> _onError; + boost::intrusive_ptr<Expression>& _dateString; + boost::intrusive_ptr<Expression>& _timeZone; + boost::intrusive_ptr<Expression>& _format; + boost::intrusive_ptr<Expression>& _onNull; + boost::intrusive_ptr<Expression>& _onError; }; class ExpressionDateFromParts final : public Expression { @@ -1077,17 +1095,17 @@ private: long long defaultValue, long long* returnValue) const; - boost::intrusive_ptr<Expression> _year; - boost::intrusive_ptr<Expression> _month; - boost::intrusive_ptr<Expression> _day; - boost::intrusive_ptr<Expression> _hour; - boost::intrusive_ptr<Expression> _minute; - boost::intrusive_ptr<Expression> _second; - boost::intrusive_ptr<Expression> _millisecond; - boost::intrusive_ptr<Expression> _isoWeekYear; - boost::intrusive_ptr<Expression> _isoWeek; - boost::intrusive_ptr<Expression> _isoDayOfWeek; - boost::intrusive_ptr<Expression> _timeZone; + boost::intrusive_ptr<Expression>& _year; + boost::intrusive_ptr<Expression>& _month; + boost::intrusive_ptr<Expression>& _day; + boost::intrusive_ptr<Expression>& _hour; + boost::intrusive_ptr<Expression>& _minute; + boost::intrusive_ptr<Expression>& _second; + boost::intrusive_ptr<Expression>& _millisecond; + boost::intrusive_ptr<Expression>& _isoWeekYear; + boost::intrusive_ptr<Expression>& _isoWeek; + boost::intrusive_ptr<Expression>& _isoDayOfWeek; + boost::intrusive_ptr<Expression>& _timeZone; }; class ExpressionDateToParts final : public Expression { @@ -1119,9 +1137,9 @@ private: boost::optional<int> evaluateIso8601Flag(const Document& root) const; - boost::intrusive_ptr<Expression> _date; - boost::intrusive_ptr<Expression> _timeZone; - boost::intrusive_ptr<Expression> _iso8601; + boost::intrusive_ptr<Expression>& _date; + boost::intrusive_ptr<Expression>& _timeZone; + boost::intrusive_ptr<Expression>& _iso8601; }; class ExpressionDateToString final : public Expression { @@ -1149,16 +1167,19 @@ private: boost::intrusive_ptr<Expression> timeZone, boost::intrusive_ptr<Expression> onNull); - boost::intrusive_ptr<Expression> _format; - boost::intrusive_ptr<Expression> _date; - boost::intrusive_ptr<Expression> _timeZone; - boost::intrusive_ptr<Expression> _onNull; + boost::intrusive_ptr<Expression>& _format; + boost::intrusive_ptr<Expression>& _date; + boost::intrusive_ptr<Expression>& _timeZone; + boost::intrusive_ptr<Expression>& _onNull; }; class ExpressionDayOfMonth final : public DateExpressionAcceptingTimeZone<ExpressionDayOfMonth> { public: - explicit ExpressionDayOfMonth(const boost::intrusive_ptr<ExpressionContext>& expCtx) - : DateExpressionAcceptingTimeZone<ExpressionDayOfMonth>("$dayOfMonth", expCtx) {} + explicit ExpressionDayOfMonth(const boost::intrusive_ptr<ExpressionContext>& expCtx, + boost::intrusive_ptr<Expression> date, + boost::intrusive_ptr<Expression> timeZone = nullptr) + : DateExpressionAcceptingTimeZone<ExpressionDayOfMonth>( + expCtx, "$dayOfMonth", std::move(date), std::move(timeZone)) {} Value evaluateDate(Date_t date, const TimeZone& timeZone) const final { return Value(timeZone.dateParts(date).dayOfMonth); @@ -1172,8 +1193,11 @@ public: class ExpressionDayOfWeek final : public DateExpressionAcceptingTimeZone<ExpressionDayOfWeek> { public: - explicit ExpressionDayOfWeek(const boost::intrusive_ptr<ExpressionContext>& expCtx) - : DateExpressionAcceptingTimeZone<ExpressionDayOfWeek>("$dayOfWeek", expCtx) {} + explicit ExpressionDayOfWeek(const boost::intrusive_ptr<ExpressionContext>& expCtx, + boost::intrusive_ptr<Expression> date, + boost::intrusive_ptr<Expression> timeZone = nullptr) + : DateExpressionAcceptingTimeZone<ExpressionDayOfWeek>( + expCtx, "$dayOfWeek", std::move(date), std::move(timeZone)) {} Value evaluateDate(Date_t date, const TimeZone& timeZone) const final { return Value(timeZone.dayOfWeek(date)); @@ -1187,8 +1211,11 @@ public: class ExpressionDayOfYear final : public DateExpressionAcceptingTimeZone<ExpressionDayOfYear> { public: - explicit ExpressionDayOfYear(const boost::intrusive_ptr<ExpressionContext>& expCtx) - : DateExpressionAcceptingTimeZone<ExpressionDayOfYear>("$dayOfYear", expCtx) {} + explicit ExpressionDayOfYear(const boost::intrusive_ptr<ExpressionContext>& expCtx, + boost::intrusive_ptr<Expression> date, + boost::intrusive_ptr<Expression> timeZone = nullptr) + : DateExpressionAcceptingTimeZone<ExpressionDayOfYear>( + expCtx, "$dayOfYear", std::move(date), std::move(timeZone)) {} Value evaluateDate(Date_t date, const TimeZone& timeZone) const final { return Value(timeZone.dayOfYear(date)); @@ -1338,9 +1365,9 @@ private: // The id of the variable to set. Variables::Id _varId; // The array to iterate over. - boost::intrusive_ptr<Expression> _input; + boost::intrusive_ptr<Expression>& _input; // The expression determining whether each element should be present in the result array. - boost::intrusive_ptr<Expression> _filter; + boost::intrusive_ptr<Expression>& _filter; }; @@ -1360,8 +1387,11 @@ public: class ExpressionHour final : public DateExpressionAcceptingTimeZone<ExpressionHour> { public: - explicit ExpressionHour(const boost::intrusive_ptr<ExpressionContext>& expCtx) - : DateExpressionAcceptingTimeZone<ExpressionHour>("$hour", expCtx) {} + explicit ExpressionHour(const boost::intrusive_ptr<ExpressionContext>& expCtx, + boost::intrusive_ptr<Expression> date, + boost::intrusive_ptr<Expression> timeZone = nullptr) + : DateExpressionAcceptingTimeZone<ExpressionHour>( + expCtx, "$hour", std::move(date), std::move(timeZone)) {} Value evaluateDate(Date_t date, const TimeZone& timeZone) const final { return Value(timeZone.dateParts(date).hour); @@ -1484,12 +1514,8 @@ public: const VariablesParseState& vps); struct NameAndExpression { - NameAndExpression() {} - NameAndExpression(std::string name, boost::intrusive_ptr<Expression> expression) - : name(name), expression(expression) {} - std::string name; - boost::intrusive_ptr<Expression> expression; + boost::intrusive_ptr<Expression>& expression; }; typedef std::map<Variables::Id, NameAndExpression> VariableMap; @@ -1503,11 +1529,11 @@ protected: private: ExpressionLet(const boost::intrusive_ptr<ExpressionContext>& expCtx, - const VariableMap& vars, - boost::intrusive_ptr<Expression> subExpression); + VariableMap&& vars, + std::vector<boost::intrusive_ptr<Expression>> children); VariableMap _variables; - boost::intrusive_ptr<Expression> _subExpression; + boost::intrusive_ptr<Expression>& _subExpression; }; class ExpressionLn final : public ExpressionSingleNumericArg<ExpressionLn> { @@ -1580,8 +1606,8 @@ private: std::string _varName; Variables::Id _varId; - boost::intrusive_ptr<Expression> _input; - boost::intrusive_ptr<Expression> _each; + boost::intrusive_ptr<Expression>& _input; + boost::intrusive_ptr<Expression>& _each; }; class ExpressionMeta final : public Expression { @@ -1614,8 +1640,11 @@ private: class ExpressionMillisecond final : public DateExpressionAcceptingTimeZone<ExpressionMillisecond> { public: - explicit ExpressionMillisecond(const boost::intrusive_ptr<ExpressionContext>& expCtx) - : DateExpressionAcceptingTimeZone<ExpressionMillisecond>("$millisecond", expCtx) {} + explicit ExpressionMillisecond(const boost::intrusive_ptr<ExpressionContext>& expCtx, + boost::intrusive_ptr<Expression> date, + boost::intrusive_ptr<Expression> timeZone = nullptr) + : DateExpressionAcceptingTimeZone<ExpressionMillisecond>( + expCtx, "$millisecond", std::move(date), std::move(timeZone)) {} Value evaluateDate(Date_t date, const TimeZone& timeZone) const final { return Value(timeZone.dateParts(date).millisecond); @@ -1629,8 +1658,11 @@ public: class ExpressionMinute final : public DateExpressionAcceptingTimeZone<ExpressionMinute> { public: - explicit ExpressionMinute(const boost::intrusive_ptr<ExpressionContext>& expCtx) - : DateExpressionAcceptingTimeZone<ExpressionMinute>("$minute", expCtx) {} + explicit ExpressionMinute(const boost::intrusive_ptr<ExpressionContext>& expCtx, + boost::intrusive_ptr<Expression> date, + boost::intrusive_ptr<Expression> timeZone = nullptr) + : DateExpressionAcceptingTimeZone<ExpressionMinute>( + expCtx, "$minute", std::move(date), std::move(timeZone)) {} Value evaluateDate(Date_t date, const TimeZone& timeZone) const final { return Value(timeZone.dateParts(date).minute); @@ -1680,8 +1712,11 @@ public: class ExpressionMonth final : public DateExpressionAcceptingTimeZone<ExpressionMonth> { public: - explicit ExpressionMonth(const boost::intrusive_ptr<ExpressionContext>& expCtx) - : DateExpressionAcceptingTimeZone<ExpressionMonth>("$month", expCtx) {} + explicit ExpressionMonth(const boost::intrusive_ptr<ExpressionContext>& expCtx, + boost::intrusive_ptr<Expression> date, + boost::intrusive_ptr<Expression> timeZone = nullptr) + : DateExpressionAcceptingTimeZone<ExpressionMonth>( + expCtx, "$month", std::move(date), std::move(timeZone)) {} Value evaluateDate(Date_t date, const TimeZone& timeZone) const final { return Value(timeZone.dateParts(date).month); @@ -1723,7 +1758,8 @@ public: static boost::intrusive_ptr<ExpressionObject> create( const boost::intrusive_ptr<ExpressionContext>& expCtx, - std::vector<std::pair<std::string, boost::intrusive_ptr<Expression>>>&& expressions); + std::vector<boost::intrusive_ptr<Expression>> children, + std::vector<std::pair<std::string, boost::intrusive_ptr<Expression>&>>&& expressions); /** * Parses and constructs an ExpressionObject from 'obj'. @@ -1736,8 +1772,7 @@ public: /** * This ExpressionObject must outlive the returned vector. */ - const std::vector<std::pair<std::string, boost::intrusive_ptr<Expression>>>& - getChildExpressions() const { + const auto& getChildExpressions() const { return _expressions; } @@ -1754,11 +1789,12 @@ protected: private: ExpressionObject( const boost::intrusive_ptr<ExpressionContext>& expCtx, - std::vector<std::pair<std::string, boost::intrusive_ptr<Expression>>>&& expressions); + std::vector<boost::intrusive_ptr<Expression>> children, + std::vector<std::pair<std::string, boost::intrusive_ptr<Expression>&>>&& expressions); // The mapping from field name to expression within this object. This needs to respect the order // in which the fields were specified in the input BSON. - std::vector<std::pair<std::string, boost::intrusive_ptr<Expression>>> _expressions; + std::vector<std::pair<std::string, boost::intrusive_ptr<Expression>&>> _expressions; }; @@ -1818,15 +1854,25 @@ public: class ExpressionReduce final : public Expression { public: - explicit ExpressionReduce(const boost::intrusive_ptr<ExpressionContext>& expCtx) - : Expression(expCtx) {} + ExpressionReduce(const boost::intrusive_ptr<ExpressionContext>& expCtx, + boost::intrusive_ptr<Expression> input, + boost::intrusive_ptr<Expression> initial, + boost::intrusive_ptr<Expression> in, + Variables::Id thisVar, + Variables::Id valueVar) + : Expression(expCtx, {std::move(input), std::move(initial), std::move(in)}), + _input(_children[0]), + _initial(_children[1]), + _in(_children[2]), + _thisVar(thisVar), + _valueVar(valueVar) {} Value evaluate(const Document& root) const final; boost::intrusive_ptr<Expression> optimize() final; static boost::intrusive_ptr<Expression> parse( const boost::intrusive_ptr<ExpressionContext>& expCtx, BSONElement expr, - const VariablesParseState& vpsIn); + const VariablesParseState& vps); Value serialize(bool explain) const final; void acceptVisitor(ExpressionVisitor* visitor) final { @@ -1837,19 +1883,22 @@ protected: void _doAddDependencies(DepsTracker* deps) const final; private: - boost::intrusive_ptr<Expression> _input; - boost::intrusive_ptr<Expression> _initial; - boost::intrusive_ptr<Expression> _in; + boost::intrusive_ptr<Expression>& _input; + boost::intrusive_ptr<Expression>& _initial; + boost::intrusive_ptr<Expression>& _in; - Variables::Id _valueVar; Variables::Id _thisVar; + Variables::Id _valueVar; }; class ExpressionSecond final : public DateExpressionAcceptingTimeZone<ExpressionSecond> { public: - explicit ExpressionSecond(const boost::intrusive_ptr<ExpressionContext>& expCtx) - : DateExpressionAcceptingTimeZone<ExpressionSecond>("$second", expCtx) {} + ExpressionSecond(const boost::intrusive_ptr<ExpressionContext>& expCtx, + boost::intrusive_ptr<Expression> date, + boost::intrusive_ptr<Expression> timeZone = nullptr) + : DateExpressionAcceptingTimeZone<ExpressionSecond>( + expCtx, "$second", std::move(date), std::move(timeZone)) {} Value evaluateDate(Date_t date, const TimeZone& timeZone) const final { return Value(timeZone.dateParts(date).second); @@ -2135,8 +2184,15 @@ public: class ExpressionSwitch final : public Expression { public: - explicit ExpressionSwitch(const boost::intrusive_ptr<ExpressionContext>& expCtx) - : Expression(expCtx) {} + using ExpressionPair = + std::pair<boost::intrusive_ptr<Expression>&, boost::intrusive_ptr<Expression>&>; + + ExpressionSwitch(const boost::intrusive_ptr<ExpressionContext>& expCtx, + std::vector<boost::intrusive_ptr<Expression>> children, + std::vector<ExpressionPair> branches) + : Expression(expCtx, std::move(children)), + _default(_children.back()), + _branches(std::move(branches)) {} Value evaluate(const Document& root) const final; boost::intrusive_ptr<Expression> optimize() final; @@ -2154,10 +2210,7 @@ protected: void _doAddDependencies(DepsTracker* deps) const final; private: - using ExpressionPair = - std::pair<boost::intrusive_ptr<Expression>, boost::intrusive_ptr<Expression>>; - - boost::intrusive_ptr<Expression> _default; + boost::intrusive_ptr<Expression>& _default; std::vector<ExpressionPair> _branches; }; @@ -2205,13 +2258,13 @@ public: ExpressionTrim(const boost::intrusive_ptr<ExpressionContext>& expCtx, TrimType trimType, StringData name, - const boost::intrusive_ptr<Expression>& input, - const boost::intrusive_ptr<Expression>& charactersToTrim) - : Expression(expCtx), + boost::intrusive_ptr<Expression> input, + boost::intrusive_ptr<Expression> charactersToTrim) + : Expression(expCtx, {std::move(input), std::move(charactersToTrim)}), _trimType(trimType), _name(name.toString()), - _input(input), - _characters(charactersToTrim) {} + _input(_children[0]), + _characters(_children[1]) {} Value evaluate(const Document& root) const final; boost::intrusive_ptr<Expression> optimize() final; @@ -2257,8 +2310,8 @@ private: TrimType _trimType; std::string _name; // "$trim", "$ltrim", or "$rtrim". - boost::intrusive_ptr<Expression> _input; - boost::intrusive_ptr<Expression> _characters; // Optional, null if not specified. + boost::intrusive_ptr<Expression>& _input; + boost::intrusive_ptr<Expression>& _characters; // Optional, null if not specified. }; @@ -2292,8 +2345,11 @@ public: class ExpressionWeek final : public DateExpressionAcceptingTimeZone<ExpressionWeek> { public: - explicit ExpressionWeek(const boost::intrusive_ptr<ExpressionContext>& expCtx) - : DateExpressionAcceptingTimeZone<ExpressionWeek>("$week"_sd, expCtx) {} + ExpressionWeek(const boost::intrusive_ptr<ExpressionContext>& expCtx, + boost::intrusive_ptr<Expression> date, + boost::intrusive_ptr<Expression> timeZone = nullptr) + : DateExpressionAcceptingTimeZone<ExpressionWeek>( + expCtx, "$week", std::move(date), std::move(timeZone)) {} Value evaluateDate(Date_t date, const TimeZone& timeZone) const final { return Value(timeZone.week(date)); @@ -2307,8 +2363,11 @@ public: class ExpressionIsoWeekYear final : public DateExpressionAcceptingTimeZone<ExpressionIsoWeekYear> { public: - explicit ExpressionIsoWeekYear(const boost::intrusive_ptr<ExpressionContext>& expCtx) - : DateExpressionAcceptingTimeZone<ExpressionIsoWeekYear>("$isoWeekYear"_sd, expCtx) {} + explicit ExpressionIsoWeekYear(const boost::intrusive_ptr<ExpressionContext>& expCtx, + boost::intrusive_ptr<Expression> date, + boost::intrusive_ptr<Expression> timeZone = nullptr) + : DateExpressionAcceptingTimeZone<ExpressionIsoWeekYear>( + expCtx, "$isoWeekYear", std::move(date), std::move(timeZone)) {} Value evaluateDate(Date_t date, const TimeZone& timeZone) const final { return Value(timeZone.isoYear(date)); @@ -2323,8 +2382,11 @@ public: class ExpressionIsoDayOfWeek final : public DateExpressionAcceptingTimeZone<ExpressionIsoDayOfWeek> { public: - explicit ExpressionIsoDayOfWeek(const boost::intrusive_ptr<ExpressionContext>& expCtx) - : DateExpressionAcceptingTimeZone<ExpressionIsoDayOfWeek>("$isoDayOfWeek"_sd, expCtx) {} + ExpressionIsoDayOfWeek(const boost::intrusive_ptr<ExpressionContext>& expCtx, + boost::intrusive_ptr<Expression> date, + boost::intrusive_ptr<Expression> timeZone = nullptr) + : DateExpressionAcceptingTimeZone<ExpressionIsoDayOfWeek>( + expCtx, "$isoDayOfWeek", std::move(date), std::move(timeZone)) {} Value evaluateDate(Date_t date, const TimeZone& timeZone) const final { return Value(timeZone.isoDayOfWeek(date)); @@ -2338,8 +2400,11 @@ public: class ExpressionIsoWeek final : public DateExpressionAcceptingTimeZone<ExpressionIsoWeek> { public: - explicit ExpressionIsoWeek(const boost::intrusive_ptr<ExpressionContext>& expCtx) - : DateExpressionAcceptingTimeZone<ExpressionIsoWeek>("$isoWeek", expCtx) {} + ExpressionIsoWeek(const boost::intrusive_ptr<ExpressionContext>& expCtx, + boost::intrusive_ptr<Expression> date, + boost::intrusive_ptr<Expression> timeZone = nullptr) + : DateExpressionAcceptingTimeZone<ExpressionIsoWeek>( + expCtx, "$isoWeek", std::move(date), std::move(timeZone)) {} Value evaluateDate(Date_t date, const TimeZone& timeZone) const final { return Value(timeZone.isoWeek(date)); @@ -2353,8 +2418,11 @@ public: class ExpressionYear final : public DateExpressionAcceptingTimeZone<ExpressionYear> { public: - explicit ExpressionYear(const boost::intrusive_ptr<ExpressionContext>& expCtx) - : DateExpressionAcceptingTimeZone<ExpressionYear>("$year", expCtx) {} + ExpressionYear(const boost::intrusive_ptr<ExpressionContext>& expCtx, + boost::intrusive_ptr<Expression> date, + boost::intrusive_ptr<Expression> timeZone = nullptr) + : DateExpressionAcceptingTimeZone<ExpressionYear>( + expCtx, "$year", std::move(date), std::move(timeZone)) {} Value evaluateDate(Date_t date, const TimeZone& timeZone) const final { return Value(timeZone.dateParts(date).year); @@ -2368,8 +2436,15 @@ public: class ExpressionZip final : public Expression { public: - explicit ExpressionZip(const boost::intrusive_ptr<ExpressionContext>& expCtx) - : Expression(expCtx) {} + ExpressionZip(const boost::intrusive_ptr<ExpressionContext>& expCtx, + bool useLongestLength, + std::vector<boost::intrusive_ptr<Expression>> children, + std::vector<std::reference_wrapper<boost::intrusive_ptr<Expression>>> inputs, + std::vector<std::reference_wrapper<boost::intrusive_ptr<Expression>>> defaults) + : Expression(expCtx, std::move(children)), + _useLongestLength(useLongestLength), + _inputs(std::move(inputs)), + _defaults(std::move(defaults)) {} Value evaluate(const Document& root) const final; boost::intrusive_ptr<Expression> optimize() final; @@ -2387,9 +2462,9 @@ protected: void _doAddDependencies(DepsTracker* deps) const final; private: - bool _useLongestLength = false; - ExpressionVector _inputs; - ExpressionVector _defaults; + bool _useLongestLength; + std::vector<std::reference_wrapper<boost::intrusive_ptr<Expression>>> _inputs; + std::vector<std::reference_wrapper<boost::intrusive_ptr<Expression>>> _defaults; }; class ExpressionConvert final : public Expression { @@ -2399,7 +2474,7 @@ public: * 'onNull' and 'onError' unspecified. */ static boost::intrusive_ptr<Expression> create(const boost::intrusive_ptr<ExpressionContext>&, - const boost::intrusive_ptr<Expression>& input, + boost::intrusive_ptr<Expression> input, BSONType toType); static boost::intrusive_ptr<Expression> parse( @@ -2407,8 +2482,6 @@ public: BSONElement expr, const VariablesParseState& vpsIn); - explicit ExpressionConvert(const boost::intrusive_ptr<ExpressionContext>& expCtx) - : Expression(expCtx) {} Value evaluate(const Document& root) const final; boost::intrusive_ptr<Expression> optimize() final; Value serialize(bool explain) const final; @@ -2422,16 +2495,18 @@ protected: private: ExpressionConvert(const boost::intrusive_ptr<ExpressionContext>&, - const boost::intrusive_ptr<Expression>& input, - BSONType toType); + boost::intrusive_ptr<Expression> input, + boost::intrusive_ptr<Expression> to, + boost::intrusive_ptr<Expression> onError, + boost::intrusive_ptr<Expression> onNull); BSONType computeTargetType(Value typeName) const; Value performConversion(BSONType targetType, Value inputValue) const; - boost::intrusive_ptr<Expression> _input; - boost::intrusive_ptr<Expression> _to; - boost::intrusive_ptr<Expression> _onError; - boost::intrusive_ptr<Expression> _onNull; + boost::intrusive_ptr<Expression>& _input; + boost::intrusive_ptr<Expression>& _to; + boost::intrusive_ptr<Expression>& _onError; + boost::intrusive_ptr<Expression>& _onNull; }; class ExpressionRegex : public Expression { @@ -2511,11 +2586,16 @@ public: return _opName; } -protected: ExpressionRegex(const boost::intrusive_ptr<ExpressionContext>& expCtx, - BSONElement expr, - const VariablesParseState& vpsIn, - const std::string& opName); + boost::intrusive_ptr<Expression> input, + boost::intrusive_ptr<Expression> regex, + boost::intrusive_ptr<Expression> options, + const StringData opName) + : Expression(expCtx, {std::move(input), std::move(regex), std::move(options)}), + _input(_children[0]), + _regex(_children[1]), + _options(_children[2]), + _opName(opName) {} private: void _extractInputField(RegexExecutionState* executionState, const Value& textInput) const; @@ -2531,9 +2611,9 @@ private: * Expressions which, when evaluated for a given document, produce the the regex pattern, the * regex option flags, and the input text to which the regex should be applied. */ - boost::intrusive_ptr<Expression> _input; - boost::intrusive_ptr<Expression> _regex; - boost::intrusive_ptr<Expression> _options; + boost::intrusive_ptr<Expression>& _input; + boost::intrusive_ptr<Expression>& _regex; + boost::intrusive_ptr<Expression>& _options; /** * This variable will be set when the $regex* expressions have constant values for their 'regex' @@ -2553,9 +2633,7 @@ public: static boost::intrusive_ptr<Expression> parse( const boost::intrusive_ptr<ExpressionContext>& expCtx, BSONElement expr, - const VariablesParseState& vpsIn) { - return new ExpressionRegexFind(expCtx, expr, vpsIn); - } + const VariablesParseState& vpsIn); Value evaluate(const Document& root) const final; @@ -2563,11 +2641,7 @@ public: return visitor->visit(this); } -private: - ExpressionRegexFind(const boost::intrusive_ptr<ExpressionContext>& expCtx, - BSONElement expr, - const VariablesParseState& vpsIn) - : ExpressionRegex(expCtx, expr, vpsIn, "$regexFind") {} + using ExpressionRegex::ExpressionRegex; }; class ExpressionRegexFindAll final : public ExpressionRegex { @@ -2575,20 +2649,14 @@ public: static boost::intrusive_ptr<Expression> parse( const boost::intrusive_ptr<ExpressionContext>& expCtx, BSONElement expr, - const VariablesParseState& vpsIn) { - return new ExpressionRegexFindAll(expCtx, expr, vpsIn); - } + const VariablesParseState& vpsIn); Value evaluate(const Document& root) const final; void acceptVisitor(ExpressionVisitor* visitor) final { return visitor->visit(this); } -private: - ExpressionRegexFindAll(const boost::intrusive_ptr<ExpressionContext>& expCtx, - BSONElement expr, - const VariablesParseState& vpsIn) - : ExpressionRegex(expCtx, expr, vpsIn, "$regexFindAll") {} + using ExpressionRegex::ExpressionRegex; }; class ExpressionRegexMatch final : public ExpressionRegex { @@ -2596,9 +2664,7 @@ public: static boost::intrusive_ptr<Expression> parse( const boost::intrusive_ptr<ExpressionContext>& expCtx, BSONElement expr, - const VariablesParseState& vpsIn) { - return new ExpressionRegexMatch(expCtx, expr, vpsIn); - } + const VariablesParseState& vpsIn); Value evaluate(const Document& root) const final; @@ -2606,10 +2672,6 @@ public: return visitor->visit(this); } -private: - ExpressionRegexMatch(const boost::intrusive_ptr<ExpressionContext>& expCtx, - BSONElement expr, - const VariablesParseState& vpsIn) - : ExpressionRegex(expCtx, expr, vpsIn, "$regexMatch") {} + using ExpressionRegex::ExpressionRegex; }; } diff --git a/src/mongo/db/pipeline/expression_test.cpp b/src/mongo/db/pipeline/expression_test.cpp index 02bd8a55d8a..ceff19672a2 100644 --- a/src/mongo/db/pipeline/expression_test.cpp +++ b/src/mongo/db/pipeline/expression_test.cpp @@ -204,9 +204,8 @@ public: // By default, this is not associative/commutative so the results will change if // instantiated as commutative or associative and operations are reordered. vector<Value> values; - for (ExpressionVector::const_iterator i = vpOperand.begin(); i != vpOperand.end(); ++i) { - values.push_back((*i)->evaluate(root)); - } + for (auto&& child : _children) + values.push_back(child->evaluate(root)); return Value(values); } @@ -3306,9 +3305,32 @@ TEST(ParseObject, ShouldRejectExpressionAsTheSecondField) { // Evaluation. // +namespace { +/** + * ExpressionObject builds two vectors within it's ::parse() method, one owning and one with names + * and references to the former. Since the ::create() method bypasses this step, we have to mimic + * the behavior here. + */ +auto expressionObjectCreateHelper( + const boost::intrusive_ptr<ExpressionContext>& expCtx, + std::vector<std::pair<std::string, boost::intrusive_ptr<Expression>>>&& + expressionsWithChildrenInPlace) { + std::vector<boost::intrusive_ptr<Expression>> children; + std::vector<std::pair<std::string, boost::intrusive_ptr<Expression>&>> expressions; + for (auto & [ unused, expression ] : expressionsWithChildrenInPlace) + children.push_back(std::move(expression)); + std::vector<boost::intrusive_ptr<Expression>>::size_type index = 0; + for (auto & [ fieldName, unused ] : expressionsWithChildrenInPlace) { + expressions.emplace_back(fieldName, children[index]); + ++index; + } + return ExpressionObject::create(expCtx, std::move(children), std::move(expressions)); +} +} // namespace + TEST(ExpressionObjectEvaluate, EmptyObjectShouldEvaluateToEmptyDocument) { intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); - auto object = ExpressionObject::create(expCtx, {}); + 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}})); @@ -3317,7 +3339,7 @@ TEST(ExpressionObjectEvaluate, EmptyObjectShouldEvaluateToEmptyDocument) { TEST(ExpressionObjectEvaluate, ShouldEvaluateEachField) { intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); auto object = - ExpressionObject::create(expCtx, {{"a", makeConstant(1)}, {"b", makeConstant(5)}}); + 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}}), @@ -3326,10 +3348,10 @@ TEST(ExpressionObjectEvaluate, ShouldEvaluateEachField) { TEST(ExpressionObjectEvaluate, OrderOfFieldsInOutputShouldMatchOrderInSpecification) { intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); - auto object = ExpressionObject::create(expCtx, - {{"a", ExpressionFieldPath::create(expCtx, "a")}, - {"b", ExpressionFieldPath::create(expCtx, "b")}, - {"c", ExpressionFieldPath::create(expCtx, "c")}}); + auto object = expressionObjectCreateHelper(expCtx, + {{"a", ExpressionFieldPath::create(expCtx, "a")}, + {"b", ExpressionFieldPath::create(expCtx, "b")}, + {"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}})); @@ -3337,19 +3359,20 @@ TEST(ExpressionObjectEvaluate, OrderOfFieldsInOutputShouldMatchOrderInSpecificat TEST(ExpressionObjectEvaluate, ShouldRemoveFieldsThatHaveMissingValues) { intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); - auto object = ExpressionObject::create(expCtx, - {{"a", ExpressionFieldPath::create(expCtx, "a.b")}, - {"b", ExpressionFieldPath::create(expCtx, "missing")}}); + auto object = + 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}})); } TEST(ExpressionObjectEvaluate, ShouldEvaluateFieldsWithinNestedObject) { intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); - auto object = ExpressionObject::create( + auto object = expressionObjectCreateHelper( expCtx, {{"a", - ExpressionObject::create( + expressionObjectCreateHelper( expCtx, {{"b", makeConstant(1)}, {"c", ExpressionFieldPath::create(expCtx, "_id")}})}}); ASSERT_VALUE_EQ(Value(Document{{"a", Document{{"b", 1}}}}), object->evaluate(Document())); @@ -3359,11 +3382,11 @@ TEST(ExpressionObjectEvaluate, ShouldEvaluateFieldsWithinNestedObject) { TEST(ExpressionObjectEvaluate, ShouldEvaluateToEmptyDocumentIfAllFieldsAreMissing) { intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); - auto object = - ExpressionObject::create(expCtx, {{"a", ExpressionFieldPath::create(expCtx, "missing")}}); + auto object = expressionObjectCreateHelper( + expCtx, {{"a", ExpressionFieldPath::create(expCtx, "missing")}}); ASSERT_VALUE_EQ(Value(Document{}), object->evaluate(Document())); - auto objectWithNestedObject = ExpressionObject::create(expCtx, {{"nested", object}}); + auto objectWithNestedObject = expressionObjectCreateHelper(expCtx, {{"nested", object}}); ASSERT_VALUE_EQ(Value(Document{{"nested", Document{}}}), objectWithNestedObject->evaluate(Document())); } @@ -3374,7 +3397,7 @@ TEST(ExpressionObjectEvaluate, ShouldEvaluateToEmptyDocumentIfAllFieldsAreMissin TEST(ExpressionObjectDependencies, ConstantValuesShouldNotBeAddedToDependencies) { intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); - auto object = ExpressionObject::create(expCtx, {{"a", makeConstant(5)}}); + auto object = expressionObjectCreateHelper(expCtx, {{"a", makeConstant(5)}}); DepsTracker deps; object->addDependencies(&deps); ASSERT_EQ(deps.fields.size(), 0UL); @@ -3383,7 +3406,7 @@ TEST(ExpressionObjectDependencies, ConstantValuesShouldNotBeAddedToDependencies) TEST(ExpressionObjectDependencies, FieldPathsShouldBeAddedToDependencies) { intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); auto object = - ExpressionObject::create(expCtx, {{"x", ExpressionFieldPath::create(expCtx, "c.d")}}); + expressionObjectCreateHelper(expCtx, {{"x", ExpressionFieldPath::create(expCtx, "c.d")}}); DepsTracker deps; object->addDependencies(&deps); ASSERT_EQ(deps.fields.size(), 1UL); @@ -3462,7 +3485,7 @@ TEST(ExpressionObjectOptimizations, OptimizingAnObjectShouldOptimizeSubExpressio VariablesParseState vps = expCtx->variablesParseState; auto addExpression = ExpressionAdd::parse(expCtx, BSON("$add" << BSON_ARRAY(1 << 2)).firstElement(), vps); - auto object = ExpressionObject::create(expCtx, {{"a", addExpression}}); + auto object = expressionObjectCreateHelper(expCtx, {{"a", addExpression}}); ASSERT_EQ(object->getChildExpressions().size(), 1UL); auto optimized = object->optimize(); |