diff options
| author | Lucas de Castro Borges <lucas@gnuabordo.com.br> | 2025-02-11 15:07:35 -0300 |
|---|---|---|
| committer | Lucas de Castro Borges <lucas@gnuabordo.com.br> | 2025-02-11 15:07:35 -0300 |
| commit | 4cb8841196d0625dfa3825aa326f071cd27c7b8b (patch) | |
| tree | 1682a647d4463397c119183369ae6f750d5fdcff /src/mongo/db/pipeline/expression.cpp | |
| parent | aa03c6362cbaa767638e6eed9b031d86dd2643d1 (diff) | |
| parent | 8f0827553e09872941945a093b647a4211a9db7f (diff) | |
Update upstream source from tag 'upstream/6.0.0'master
Update to upstream version '6.0.0'
with Debian dir 5604a80ec1c96ca76f25f40d78e6ef855abec322
Diffstat (limited to 'src/mongo/db/pipeline/expression.cpp')
| -rw-r--r-- | src/mongo/db/pipeline/expression.cpp | 942 |
1 files changed, 279 insertions, 663 deletions
diff --git a/src/mongo/db/pipeline/expression.cpp b/src/mongo/db/pipeline/expression.cpp index 658fd9051b4..bf0915deddd 100644 --- a/src/mongo/db/pipeline/expression.cpp +++ b/src/mongo/db/pipeline/expression.cpp @@ -72,25 +72,13 @@ using std::pair; using std::string; using std::vector; -Value ExpressionConstant::serializeConstant(const SerializationOptions& opts, - Value val, - bool wrapRepresentativeValue) { +/// Helper function to easily wrap constants with $const. +static Value serializeConstant(Value val) { if (val.missing()) { return Value("$$REMOVE"_sd); } - // It's safer to wrap constants in $const when generating representative shapes to avoid - // ambiguity when re-parsing (SERVER-88296, SERVER-85376). However, we allow certain expressions - // to override this behavior in order to reduce shape verbosity if the expression takes many - // constant arguments (e.g. variadic expressions - SERVER-84159). - // Debug shapes never wrap constants in $const to reduce shape size (and because re-parsing - // support is not a consideration there). - if ((opts.literalPolicy == LiteralSerializationPolicy::kUnchanged) || - (wrapRepresentativeValue && - opts.literalPolicy == LiteralSerializationPolicy::kToRepresentativeParseableValue)) { - return Value(DOC("$const" << opts.serializeLiteral(val))); - } - return opts.serializeLiteral(val); + return Value(DOC("$const" << val)); } /* --------------------------- Expression ------------------------------ */ @@ -200,7 +188,7 @@ void Expression::registerExpression( parserMap[key] = ParserRegistration{parser, allowedWithApiStrict, allowedWithClientType, requiredMinVersion}; // Add this expression to the global map of operator counters for expressions. - operatorCountersAggExpressions.addCounter(key); + operatorCountersAggExpressions.addAggExpressionCounter(key); } intrusive_ptr<Expression> Expression::parseExpression(ExpressionContext* const expCtx, @@ -320,204 +308,111 @@ const char* ExpressionAbs::getOpName() const { /* ------------------------- ExpressionAdd ----------------------------- */ -namespace { +StatusWith<Value> ExpressionAdd::apply(Value lhs, Value rhs) { + BSONType diffType = Value::getWidestNumeric(rhs.getType(), lhs.getType()); -/** - * We'll try to return the narrowest possible result value while avoiding overflow or implicit use - * of decimal types. To do that, compute separate sums for long, double and decimal values, and - * track the current widest type. The long sum will be converted to double when the first double - * value is seen or when long arithmetic would overflow. - */ -class AddState { -public: - /** - * Update the internal state with another operand. It is up to the caller to validate that the - * operand is of a proper type. - */ - void operator+=(const Value& operand) { - auto oldWidestType = widestType; - // Dates are represented by the long number of milliseconds since the unix epoch, so we can - // treat them as regular numeric values for the purposes of addition after making sure that - // only one date is present in the operand list. - Value valToAdd; - if (operand.getType() == Date) { - uassert(16612, "only one date allowed in an $add expression", !isDate); - Value oldValue = getValue(); - longTotal = 0; - addToDateValue(oldValue); - isDate = true; - valToAdd = Value(operand.getDate().toMillisSinceEpoch()); - } else { - widestType = Value::getWidestNumeric(widestType, operand.getType()); - valToAdd = operand; - } + if (diffType == NumberDecimal) { + Decimal128 left = lhs.coerceToDecimal(); + Decimal128 right = rhs.coerceToDecimal(); + return Value(left.add(right)); + } else if (diffType == NumberDouble) { + double right = rhs.coerceToDouble(); + double left = lhs.coerceToDouble(); + return Value(left + right); + } else if (diffType == NumberLong) { + long long result; - if (isDate) { - addToDateValue(valToAdd); - return; + // If there is an overflow, convert the values to doubles. + if (overflow::add(lhs.coerceToLong(), rhs.coerceToLong(), &result)) { + return Value(lhs.coerceToDouble() + rhs.coerceToDouble()); } + return Value(result); + } else if (diffType == NumberInt) { + long long right = rhs.coerceToLong(); + long long left = lhs.coerceToLong(); + return Value::createIntOrLong(left + right); + } else if (lhs.nullish() || rhs.nullish()) { + return Value(BSONNULL); + } else { + return Status(ErrorCodes::TypeMismatch, + str::stream() << "cannot $add a" << typeName(rhs.getType()) << " from a " + << typeName(lhs.getType())); + } +} - // If this operation widens the return type, perform any necessary type conversions. - if (oldWidestType != widestType) { - switch (widestType) { - case NumberLong: - // Int -> Long is handled by the same sum. - break; - case NumberDouble: - // Int/Long -> Double converts the existing longTotal to a doubleTotal. - doubleTotal = longTotal; - break; - case NumberDecimal: - // Convert the right total to NumberDecimal by looking at the old widest type. - switch (oldWidestType) { - case NumberInt: - case NumberLong: - decimalTotal = Decimal128(longTotal); - break; - case NumberDouble: - decimalTotal = Decimal128(doubleTotal); - break; - default: - MONGO_UNREACHABLE; - } - break; - default: - MONGO_UNREACHABLE; - } - } +Value ExpressionAdd::evaluate(const Document& root, Variables* variables) const { + // We'll try to return the narrowest possible result value while avoiding overflow, loss + // of precision due to intermediate rounding or implicit use of decimal types. To do that, + // compute a compensated sum for non-decimal values and a separate decimal sum for decimal + // values, and track the current narrowest type. + DoubleDoubleSummation nonDecimalTotal; + Decimal128 decimalTotal; + BSONType totalType = NumberInt; + bool haveDate = false; - // Perform the add operation. - switch (widestType) { - case NumberInt: - case NumberLong: - // If the long long arithmetic overflows, promote the result to a NumberDouble and - // start incrementing the doubleTotal. - long long newLongTotal; - if (overflow::add(longTotal, valToAdd.coerceToLong(), &newLongTotal)) { - widestType = NumberDouble; - doubleTotal = longTotal + valToAdd.coerceToDouble(); - } else { - longTotal = newLongTotal; - } + const size_t n = _children.size(); + for (size_t i = 0; i < n; ++i) { + Value val = _children[i]->evaluate(root, variables); + + switch (val.getType()) { + case NumberDecimal: + decimalTotal = decimalTotal.add(val.getDecimal()); + totalType = NumberDecimal; break; case NumberDouble: - doubleTotal += valToAdd.coerceToDouble(); - break; - case NumberDecimal: - decimalTotal = decimalTotal.add(valToAdd.coerceToDecimal()); + nonDecimalTotal.addDouble(val.getDouble()); + if (totalType != NumberDecimal) + totalType = NumberDouble; break; - default: - uasserted(ErrorCodes::TypeMismatch, - str::stream() << "$add only supports numeric or date types, not " - << typeName(valToAdd.getType())); - } - } - - Value getValue() const { - // If one of the operands was a date, then return long value as Date. - if (isDate) { - return Value(Date_t::fromMillisSinceEpoch(longTotal)); - } else { - switch (widestType) { - case NumberInt: - return Value::createIntOrLong(longTotal); - case NumberLong: - return Value(longTotal); - case NumberDouble: - return Value(doubleTotal); - case NumberDecimal: - return Value(decimalTotal); - default: - MONGO_UNREACHABLE; - } - } - } - -private: - // Convert 'valToAdd' into the data type used for dates (long long) and add it to 'longTotal'. - void addToDateValue(Value valToAdd) { - switch (valToAdd.getType()) { - case NumberInt: case NumberLong: - if (overflow::add(longTotal, valToAdd.coerceToLong(), &longTotal)) { - uasserted(ErrorCodes::Overflow, "date overflow"); - } + nonDecimalTotal.addLong(val.getLong()); + if (totalType == NumberInt) + totalType = NumberLong; break; - case NumberDouble: { - using limits = std::numeric_limits<long long>; - double doubleToAdd = valToAdd.coerceToDouble(); - uassert(ErrorCodes::Overflow, - "date overflow", - // The upper bound is exclusive because it rounds up when it is cast to - // a double. - doubleToAdd >= static_cast<double>(limits::min()) && - doubleToAdd < static_cast<double>(limits::max())); - - if (overflow::add(longTotal, llround(doubleToAdd), &longTotal)) { - uasserted(ErrorCodes::Overflow, "date overflow"); - } + case NumberInt: + nonDecimalTotal.addDouble(val.getInt()); break; - } - case NumberDecimal: { - Decimal128 decimalToAdd = valToAdd.coerceToDecimal(); - - std::uint32_t signalingFlags = Decimal128::SignalingFlag::kNoFlag; - std::int64_t longToAdd = decimalToAdd.toLong(&signalingFlags); - if (signalingFlags != Decimal128::SignalingFlag::kNoFlag || - overflow::add(longTotal, longToAdd, &longTotal)) { - uasserted(ErrorCodes::Overflow, "date overflow"); - } + case Date: + uassert(16612, "only one date allowed in an $add expression", !haveDate); + haveDate = true; + nonDecimalTotal.addLong(val.getDate().toMillisSinceEpoch()); break; - } default: - MONGO_UNREACHABLE; + uassert(16554, + str::stream() << "$add only supports numeric or date types, not " + << typeName(val.getType()), + val.nullish()); + return Value(BSONNULL); } } - long long longTotal = 0; - double doubleTotal = 0; - Decimal128 decimalTotal; - BSONType widestType = NumberInt; - bool isDate = false; -}; - -Status checkAddOperandType(Value val) { - if (!val.numeric() && val.getType() != Date) { - return Status(ErrorCodes::TypeMismatch, - str::stream() << "$add only supports numeric or date types, not " - << typeName(val.getType())); + if (haveDate) { + int64_t longTotal; + if (totalType == NumberDecimal) { + longTotal = decimalTotal.add(nonDecimalTotal.getDecimal()).toLong(); + } else { + uassert(ErrorCodes::Overflow, "date overflow in $add", nonDecimalTotal.fitsLong()); + longTotal = nonDecimalTotal.getLong(); + } + return Value(Date_t::fromMillisSinceEpoch(longTotal)); } - - return Status::OK(); -} -} // namespace - -StatusWith<Value> ExpressionAdd::apply(Value lhs, Value rhs) { - if (lhs.nullish()) - return Value(BSONNULL); - if (Status s = checkAddOperandType(lhs); !s.isOK()) - return s; - if (rhs.nullish()) - return Value(BSONNULL); - if (Status s = checkAddOperandType(rhs); !s.isOK()) - return s; - - AddState state; - state += lhs; - state += rhs; - return state.getValue(); -} - -Value ExpressionAdd::evaluate(const Document& root, Variables* variables) const { - AddState state; - for (auto&& child : _children) { - Value val = child->evaluate(root, variables); - if (val.nullish()) - return Value(BSONNULL); - uassertStatusOK(checkAddOperandType(val)); - state += val; + switch (totalType) { + case NumberDecimal: + return Value(decimalTotal.add(nonDecimalTotal.getDecimal())); + case NumberLong: + dassert(nonDecimalTotal.isInteger()); + if (nonDecimalTotal.fitsLong()) + return Value(nonDecimalTotal.getLong()); + // Fallthrough. + case NumberInt: + if (nonDecimalTotal.fitsLong()) + return Value::createIntOrLong(nonDecimalTotal.getLong()); + // Fallthrough. + case NumberDouble: + return Value(nonDecimalTotal.getDouble()); + default: + massert(16417, "$add resulted in a non-numeric type", false); } - return state.getValue(); } REGISTER_STABLE_EXPRESSION(add, ExpressionAdd::parse); @@ -655,16 +550,11 @@ Value ExpressionArray::evaluate(const Document& root, Variables* variables) cons return Value(std::move(values)); } -Value ExpressionArray::serialize(const SerializationOptions& options) const { - if (options.literalPolicy != LiteralSerializationPolicy::kUnchanged && - selfAndChildrenAreConstant()) { - return ExpressionConstant::serializeConstant( - options, evaluate(Document{}, &(getExpressionContext()->variables))); - } +Value ExpressionArray::serialize(bool explain) const { vector<Value> expressions; expressions.reserve(_children.size()); for (auto&& expr : _children) { - expressions.push_back(expr->serialize(options)); + expressions.push_back(expr->serialize(explain)); } return Value(std::move(expressions)); } @@ -687,15 +577,6 @@ intrusive_ptr<Expression> ExpressionArray::optimize() { return this; } -bool ExpressionArray::selfAndChildrenAreConstant() const { - for (auto&& exprPointer : _children) { - if (!exprPointer->selfAndChildrenAreConstant()) { - return false; - } - } - return true; -} - const char* ExpressionArray::getOpName() const { // This should never be called, but is needed to inherit from ExpressionNary. return "$array"; @@ -996,11 +877,11 @@ Value ExpressionCoerceToBool::evaluate(const Document& root, Variables* variable return Value(false); } -Value ExpressionCoerceToBool::serialize(const SerializationOptions& options) const { +Value ExpressionCoerceToBool::serialize(bool explain) const { // When not explaining, serialize to an $and expression. When parsed, the $and expression // will be optimized back into a ExpressionCoerceToBool. - const char* name = options.verbosity ? "$coerceToBool" : "$and"; - return Value(DOC(name << DOC_ARRAY(pExpression->serialize(options)))); + const char* name = explain ? "$coerceToBool" : "$and"; + return Value(DOC(name << DOC_ARRAY(pExpression->serialize(explain)))); } /* ----------------------- ExpressionCompare --------------------------- */ @@ -1249,8 +1130,8 @@ Value ExpressionConstant::evaluate(const Document& root, Variables* variables) c return _value; } -Value ExpressionConstant::serialize(const SerializationOptions& options) const { - return ExpressionConstant::serializeConstant(options, _value); +Value ExpressionConstant::serialize(bool explain) const { + return serializeConstant(_value); } REGISTER_STABLE_EXPRESSION(const, ExpressionConstant::parse); @@ -1464,20 +1345,20 @@ intrusive_ptr<Expression> ExpressionDateFromParts::optimize() { return this; } -Value ExpressionDateFromParts::serialize(const SerializationOptions& options) const { +Value ExpressionDateFromParts::serialize(bool explain) const { return Value(Document{ {"$dateFromParts", - Document{{"year", _year ? _year->serialize(options) : Value()}, - {"month", _month ? _month->serialize(options) : Value()}, - {"day", _day ? _day->serialize(options) : Value()}, - {"hour", _hour ? _hour->serialize(options) : Value()}, - {"minute", _minute ? _minute->serialize(options) : Value()}, - {"second", _second ? _second->serialize(options) : Value()}, - {"millisecond", _millisecond ? _millisecond->serialize(options) : Value()}, - {"isoWeekYear", _isoWeekYear ? _isoWeekYear->serialize(options) : Value()}, - {"isoWeek", _isoWeek ? _isoWeek->serialize(options) : Value()}, - {"isoDayOfWeek", _isoDayOfWeek ? _isoDayOfWeek->serialize(options) : Value()}, - {"timezone", _timeZone ? _timeZone->serialize(options) : Value()}}}}); + Document{{"year", _year ? _year->serialize(explain) : Value()}, + {"month", _month ? _month->serialize(explain) : Value()}, + {"day", _day ? _day->serialize(explain) : Value()}, + {"hour", _hour ? _hour->serialize(explain) : Value()}, + {"minute", _minute ? _minute->serialize(explain) : Value()}, + {"second", _second ? _second->serialize(explain) : Value()}, + {"millisecond", _millisecond ? _millisecond->serialize(explain) : Value()}, + {"isoWeekYear", _isoWeekYear ? _isoWeekYear->serialize(explain) : Value()}, + {"isoWeek", _isoWeek ? _isoWeek->serialize(explain) : Value()}, + {"isoDayOfWeek", _isoDayOfWeek ? _isoDayOfWeek->serialize(explain) : Value()}, + {"timezone", _timeZone ? _timeZone->serialize(explain) : Value()}}}}); } bool ExpressionDateFromParts::evaluateNumberWithDefault(const Document& root, @@ -1721,14 +1602,14 @@ intrusive_ptr<Expression> ExpressionDateFromString::optimize() { return this; } -Value ExpressionDateFromString::serialize(const SerializationOptions& options) const { +Value ExpressionDateFromString::serialize(bool explain) const { return Value( Document{{"$dateFromString", - Document{{"dateString", _dateString->serialize(options)}, - {"timezone", _timeZone ? _timeZone->serialize(options) : Value()}, - {"format", _format ? _format->serialize(options) : Value()}, - {"onNull", _onNull ? _onNull->serialize(options) : Value()}, - {"onError", _onError ? _onError->serialize(options) : Value()}}}}); + Document{{"dateString", _dateString->serialize(explain)}, + {"timezone", _timeZone ? _timeZone->serialize(explain) : Value()}, + {"format", _format ? _format->serialize(explain) : Value()}, + {"onNull", _onNull ? _onNull->serialize(explain) : Value()}, + {"onError", _onError ? _onError->serialize(explain) : Value()}}}}); } Value ExpressionDateFromString::evaluate(const Document& root, Variables* variables) const { @@ -1879,12 +1760,12 @@ intrusive_ptr<Expression> ExpressionDateToParts::optimize() { return this; } -Value ExpressionDateToParts::serialize(const SerializationOptions& options) const { +Value ExpressionDateToParts::serialize(bool explain) const { return Value( Document{{"$dateToParts", - Document{{"date", _date->serialize(options)}, - {"timezone", _timeZone ? _timeZone->serialize(options) : Value()}, - {"iso8601", _iso8601 ? _iso8601->serialize(options) : Value()}}}}); + Document{{"date", _date->serialize(explain)}, + {"timezone", _timeZone ? _timeZone->serialize(explain) : Value()}, + {"iso8601", _iso8601 ? _iso8601->serialize(explain) : Value()}}}}); } boost::optional<int> ExpressionDateToParts::evaluateIso8601Flag(const Document& root, @@ -2037,13 +1918,13 @@ intrusive_ptr<Expression> ExpressionDateToString::optimize() { return this; } -Value ExpressionDateToString::serialize(const SerializationOptions& options) const { +Value ExpressionDateToString::serialize(bool explain) const { return Value( Document{{"$dateToString", - Document{{"date", _date->serialize(options)}, - {"format", _format ? _format->serialize(options) : Value()}, - {"timezone", _timeZone ? _timeZone->serialize(options) : Value()}, - {"onNull", _onNull ? _onNull->serialize(options) : Value()}}}}); + Document{{"date", _date->serialize(explain)}, + {"format", _format ? _format->serialize(explain) : Value()}, + {"timezone", _timeZone ? _timeZone->serialize(explain) : Value()}, + {"onNull", _onNull ? _onNull->serialize(explain) : Value()}}}}); } Value ExpressionDateToString::evaluate(const Document& root, Variables* variables) const { @@ -2184,14 +2065,14 @@ boost::intrusive_ptr<Expression> ExpressionDateDiff::optimize() { return this; }; -Value ExpressionDateDiff::serialize(const SerializationOptions& options) const { +Value ExpressionDateDiff::serialize(bool explain) const { return Value{Document{ {"$dateDiff"_sd, - Document{{"startDate"_sd, _startDate->serialize(options)}, - {"endDate"_sd, _endDate->serialize(options)}, - {"unit"_sd, _unit->serialize(options)}, - {"timezone"_sd, _timeZone ? _timeZone->serialize(options) : Value{}}, - {"startOfWeek"_sd, _startOfWeek ? _startOfWeek->serialize(options) : Value{}}}}}}; + Document{{"startDate"_sd, _startDate->serialize(explain)}, + {"endDate"_sd, _endDate->serialize(explain)}, + {"unit"_sd, _unit->serialize(explain)}, + {"timezone"_sd, _timeZone ? _timeZone->serialize(explain) : Value{}}, + {"startOfWeek"_sd, _startOfWeek ? _startOfWeek->serialize(explain) : Value{}}}}}}; }; Date_t ExpressionDateDiff::convertToDate(const Value& value, StringData parameterName) { @@ -2253,17 +2134,6 @@ void ExpressionDateDiff::_doAddDependencies(DepsTracker* deps) const { } } -monotonic::State ExpressionDateDiff::getMonotonicState(const FieldPath& sortedFieldPath) const { - if (!ExpressionConstant::allNullOrConstant({_unit, _timeZone, _startOfWeek})) { - return monotonic::State::NonMonotonic; - } - // Because the result of this expression can be negative, this works the same way as - // ExpressionSubtract. Edge cases with DST and other timezone changes are handled correctly - // according to dateDiff. - return monotonic::combine(_endDate->getMonotonicState(sortedFieldPath), - monotonic::opposite(_startDate->getMonotonicState(sortedFieldPath))); -} - /* ----------------------- ExpressionDivide ---------------------------- */ Value ExpressionDivide::evaluate(const Document& root, Variables* variables) const { @@ -2405,24 +2275,10 @@ Value ExpressionObject::evaluate(const Document& root, Variables* variables) con return outputDoc.freezeToValue(); } -bool ExpressionObject::selfAndChildrenAreConstant() const { - for (auto&& [_, exprPointer] : _expressions) { - if (!exprPointer->selfAndChildrenAreConstant()) { - return false; - } - } - return true; -} - -Value ExpressionObject::serialize(const SerializationOptions& options) const { - if (options.literalPolicy != LiteralSerializationPolicy::kUnchanged && - selfAndChildrenAreConstant()) { - return ExpressionConstant::serializeConstant(options, Value(Document{})); - } +Value ExpressionObject::serialize(bool explain) const { MutableDocument outputDoc; for (auto&& pair : _expressions) { - outputDoc.addField(options.serializeFieldPathFromString(pair.first), - pair.second->serialize(options)); + outputDoc.addField(pair.first, pair.second->serialize(explain)); } return outputDoc.freezeToValue(); } @@ -2597,34 +2453,14 @@ Value ExpressionFieldPath::evaluate(const Document& root, Variables* variables) } } -namespace { -// Shared among expressions that need to serialize dotted paths and redact the path components. -auto getPrefixAndPath(FieldPath path) { - if (path.getFieldName(0) == "CURRENT" && path.getPathLength() > 1) { +Value ExpressionFieldPath::serialize(bool explain) const { + if (_fieldPath.getFieldName(0) == "CURRENT" && _fieldPath.getPathLength() > 1) { // use short form for "$$CURRENT.foo" but not just "$$CURRENT" - return std::make_pair(std::string("$"), path.tail()); + return Value("$" + _fieldPath.tail().fullPath()); } else { - return std::make_pair(std::string("$$"), path); + return Value("$$" + _fieldPath.fullPath()); } } -} // namespace - -Value ExpressionFieldPath::serialize(const SerializationOptions& options) const { - auto [prefix, path] = getPrefixAndPath(_fieldPath); - // First handles special cases for redaction of system variables. User variables will fall - // through to the default full redaction case. - if (options.transformIdentifiers && prefix.length() == 2) { - if (path.getPathLength() == 1 && Variables::isBuiltin(_variable)) { - // Nothing to redact for builtin variables. - return Value(prefix + path.fullPath()); - } else if (path.getPathLength() > 1 && Variables::isBuiltin(_variable)) { - // The first component of this path is a system variable, so keep that and redact - // the rest. - return Value(prefix + path.front() + "." + options.serializeFieldPath(path.tail())); - } - } - return Value(prefix + options.serializeFieldPath(path)); -} Expression::ComputedPaths ExpressionFieldPath::getComputedPaths(const std::string& exprFieldPath, Variables::Id renamingVar) const { @@ -2671,11 +2507,6 @@ std::unique_ptr<Expression> ExpressionFieldPath::copyWithSubstitution( return nullptr; } -monotonic::State ExpressionFieldPath::getMonotonicState(const FieldPath& sortedFieldPath) const { - return getFieldPathWithoutCurrentPrefix() == sortedFieldPath ? monotonic::State::Increasing - : monotonic::State::NonMonotonic; -} - /* ------------------------- ExpressionFilter ----------------------------- */ REGISTER_STABLE_EXPRESSION(filter, ExpressionFilter::parse); @@ -2765,14 +2596,14 @@ intrusive_ptr<Expression> ExpressionFilter::optimize() { return this; } -Value ExpressionFilter::serialize(const SerializationOptions& options) const { +Value ExpressionFilter::serialize(bool explain) const { if (_limit) { - return Value(DOC("$filter" << DOC("input" << _input->serialize(options) << "as" << _varName - << "cond" << _cond->serialize(options) << "limit" - << (*_limit)->serialize(options)))); + return Value(DOC("$filter" << DOC("input" << _input->serialize(explain) << "as" << _varName + << "cond" << _cond->serialize(explain) << "limit" + << (*_limit)->serialize(explain)))); } - return Value(DOC("$filter" << DOC("input" << _input->serialize(options) << "as" << _varName - << "cond" << _cond->serialize(options)))); + return Value(DOC("$filter" << DOC("input" << _input->serialize(explain) << "as" << _varName + << "cond" << _cond->serialize(explain)))); } Value ExpressionFilter::evaluate(const Document& root, Variables* variables) const { @@ -2964,19 +2795,15 @@ intrusive_ptr<Expression> ExpressionLet::optimize() { return this; } -Value ExpressionLet::serialize(const SerializationOptions& options) const { +Value ExpressionLet::serialize(bool explain) const { MutableDocument vars; for (VariableMap::const_iterator it = _variables.begin(), end = _variables.end(); it != end; ++it) { - auto key = it->second.name; - if (options.transformIdentifiers) { - key = options.transformIdentifiersCallback(key); - } - vars[key] = it->second.expression->serialize(options); + vars[it->second.name] = it->second.expression->serialize(explain); } return Value( - DOC("$let" << DOC("vars" << vars.freeze() << "in" << _subExpression->serialize(options)))); + DOC("$let" << DOC("vars" << vars.freeze() << "in" << _subExpression->serialize(explain)))); } Value ExpressionLet::evaluate(const Document& root, Variables* variables) const { @@ -3070,9 +2897,9 @@ intrusive_ptr<Expression> ExpressionMap::optimize() { return this; } -Value ExpressionMap::serialize(const SerializationOptions& options) const { - return Value(DOC("$map" << DOC("input" << _input->serialize(options) << "as" << _varName << "in" - << _each->serialize(options)))); +Value ExpressionMap::serialize(bool explain) const { + return Value(DOC("$map" << DOC("input" << _input->serialize(explain) << "as" << _varName << "in" + << _each->serialize(explain)))); } Value ExpressionMap::evaluate(const Document& root, Variables* variables) const { @@ -3154,14 +2981,11 @@ const std::string recordIdName = "recordId"; const std::string indexKeyName = "indexKey"; const std::string sortKeyName = "sortKey"; const std::string searchScoreDetailsName = "searchScoreDetails"; -const std::string searchSequenceTokenName = "searchSequenceToken"; const std::string timeseriesBucketMinTimeName = "timeseriesBucketMinTime"; const std::string timeseriesBucketMaxTimeName = "timeseriesBucketMaxTime"; -const std::string vectorSearchScoreName = "vectorSearchScore"; using MetaType = DocumentMetadataFields::MetaType; const StringMap<DocumentMetadataFields::MetaType> kMetaNameToMetaType = { - {vectorSearchScoreName, MetaType::kVectorSearchScore}, {geoNearDistanceName, MetaType::kGeoNearDist}, {geoNearPointName, MetaType::kGeoNearPoint}, {indexKeyName, MetaType::kIndexKey}, @@ -3170,7 +2994,6 @@ const StringMap<DocumentMetadataFields::MetaType> kMetaNameToMetaType = { {searchHighlightsName, MetaType::kSearchHighlights}, {searchScoreName, MetaType::kSearchScore}, {searchScoreDetailsName, MetaType::kSearchScoreDetails}, - {searchSequenceTokenName, MetaType::kSearchSequenceToken}, {sortKeyName, MetaType::kSortKey}, {textScoreName, MetaType::kTextScore}, {timeseriesBucketMinTimeName, MetaType::kTimeseriesBucketMinTime}, @@ -3178,7 +3001,6 @@ const StringMap<DocumentMetadataFields::MetaType> kMetaNameToMetaType = { }; const stdx::unordered_map<DocumentMetadataFields::MetaType, StringData> kMetaTypeToMetaName = { - {MetaType::kVectorSearchScore, vectorSearchScoreName}, {MetaType::kGeoNearDist, geoNearDistanceName}, {MetaType::kGeoNearPoint, geoNearPointName}, {MetaType::kIndexKey, indexKeyName}, @@ -3187,7 +3009,6 @@ const stdx::unordered_map<DocumentMetadataFields::MetaType, StringData> kMetaTyp {MetaType::kSearchHighlights, searchHighlightsName}, {MetaType::kSearchScore, searchScoreName}, {MetaType::kSearchScoreDetails, searchScoreDetailsName}, - {MetaType::kSearchSequenceToken, searchSequenceTokenName}, {MetaType::kSortKey, sortKeyName}, {MetaType::kTextScore, textScoreName}, {MetaType::kTimeseriesBucketMinTime, timeseriesBucketMinTimeName}, @@ -3214,7 +3035,7 @@ ExpressionMeta::ExpressionMeta(ExpressionContext* const expCtx, MetaType metaTyp expCtx->sbeCompatible = false; } -Value ExpressionMeta::serialize(const SerializationOptions& options) const { +Value ExpressionMeta::serialize(bool explain) const { const auto nameIter = kMetaTypeToMetaName.find(_metaType); invariant(nameIter != kMetaTypeToMetaName.end()); return Value(DOC("$meta" << nameIter->second)); @@ -3223,9 +3044,6 @@ Value ExpressionMeta::serialize(const SerializationOptions& options) const { Value ExpressionMeta::evaluate(const Document& root, Variables* variables) const { const auto& metadata = root.metadata(); switch (_metaType) { - case MetaType::kVectorSearchScore: - return metadata.hasVectorSearchScore() ? Value(metadata.getVectorSearchScore()) - : Value(); case MetaType::kTextScore: return metadata.hasTextScore() ? Value(metadata.getTextScore()) : Value(); case MetaType::kRandVal: @@ -3260,9 +3078,6 @@ Value ExpressionMeta::evaluate(const Document& root, Variables* variables) const case MetaType::kSearchScoreDetails: return metadata.hasSearchScoreDetails() ? Value(metadata.getSearchScoreDetails()) : Value(); - case MetaType::kSearchSequenceToken: - return metadata.hasSearchSequenceToken() ? Value(metadata.getSearchSequenceToken()) - : Value(); case MetaType::kTimeseriesBucketMinTime: return metadata.hasTimeseriesBucketMinTime() ? Value(metadata.getTimeseriesBucketMinTime()) @@ -3850,7 +3665,7 @@ Value ExpressionLn::evaluateNumericArg(const Value& numericArg) const { if (numericArg.getType() == NumberDecimal) { Decimal128 argDecimal = numericArg.getDecimal(); if (argDecimal.isGreater(Decimal128::kNormalizedZero)) - return Value(argDecimal.naturalLogarithm()); + return Value(argDecimal.logarithm()); // Fall through for error case. } double argDouble = numericArg.coerceToDouble(); @@ -4004,9 +3819,9 @@ Value toValue(const std::array<std::uint8_t, 32>& buf) { return Value(BSONBinData(vec.data(), vec.size(), BinDataType::Encrypt)); } -Value ExpressionInternalFLEEqual::serialize(const SerializationOptions& options) const { +Value ExpressionInternalFLEEqual::serialize(bool explain) const { return Value(Document{{kInternalFleEq, - Document{{"field", _children[0]->serialize(options)}, + Document{{"field", _children[0]->serialize(explain)}, {"edc", toValue(_edcToken)}, {"counter", Value(static_cast<long long>(_contentionFactor))}, {"server", toValue(_serverToken)}}}}); @@ -4161,22 +3976,13 @@ void ExpressionNary::addOperand(const intrusive_ptr<Expression>& pExpression) { _children.push_back(pExpression); } -Value ExpressionNary::serialize(const SerializationOptions& options) const { +Value ExpressionNary::serialize(bool explain) const { const size_t nOperand = _children.size(); vector<Value> array; /* build up the array */ - for (size_t i = 0; i < nOperand; i++) { - // If this input is a constant, bypass the standard serialization that wraps the - // representative value in $const. This does not lead to ambiguity for variadic operators - // but avoids bloating the representative shape for operators that have many inputs. - ExpressionConstant const* exprConst = dynamic_cast<ExpressionConstant*>(_children[i].get()); - if (exprConst) { - array.push_back(exprConst->serializeConstant( - options, exprConst->getValue(), false /* wrapRepresentativeValue */)); - } else { - array.push_back(_children[i]->serialize(options)); - } - } + for (size_t i = 0; i < nOperand; i++) + array.push_back(_children[i]->serialize(explain)); + return Value(DOC(getOpName() << array)); } @@ -4641,11 +4447,11 @@ void ExpressionReduce::_doAddDependencies(DepsTracker* deps) const { _in->addDependencies(deps); } -Value ExpressionReduce::serialize(const SerializationOptions& options) const { +Value ExpressionReduce::serialize(bool explain) const { return Value(Document{{"$reduce", - Document{{"input", _input->serialize(options)}, - {"initialValue", _initial->serialize(options)}, - {"in", _in->serialize(options)}}}}); + Document{{"input", _input->serialize(explain)}, + {"initialValue", _initial->serialize(explain)}, + {"in", _in->serialize(explain)}}}}); } /* ------------------------ ExpressionReplaceBase ------------------------ */ @@ -4656,11 +4462,11 @@ void ExpressionReplaceBase::_doAddDependencies(DepsTracker* deps) const { _replacement->addDependencies(deps); } -Value ExpressionReplaceBase::serialize(const SerializationOptions& options) const { +Value ExpressionReplaceBase::serialize(bool explain) const { return Value(Document{{getOpName(), - Document{{"input", _input->serialize(options)}, - {"find", _find->serialize(options)}, - {"replacement", _replacement->serialize(options)}}}}); + Document{{"input", _input->serialize(explain)}, + {"find", _find->serialize(explain)}, + {"replacement", _replacement->serialize(explain)}}}}); } namespace { @@ -4953,9 +4759,9 @@ void ExpressionSortArray::_doAddDependencies(DepsTracker* deps) const { _input->addDependencies(deps); } -Value ExpressionSortArray::serialize(const SerializationOptions& options) const { +Value ExpressionSortArray::serialize(bool explain) const { return Value(Document{{kName, - Document{{"input", _input->serialize(options)}, + Document{{"input", _input->serialize(explain)}, {"sortBy", _sortBy.getOriginalElement()}}}}); } @@ -5729,45 +5535,14 @@ StatusWith<Value> ExpressionSubtract::apply(Value lhs, Value rhs) { } else if (lhs.nullish() || rhs.nullish()) { return Value(BSONNULL); } else if (lhs.getType() == Date) { - BSONType rhsType = rhs.getType(); - switch (rhsType) { - case Date: - return Value(durationCount<Milliseconds>(lhs.getDate() - rhs.getDate())); - case NumberInt: - case NumberLong: { - long long longDiff = lhs.getDate().toMillisSinceEpoch(); - if (overflow::sub(longDiff, rhs.coerceToLong(), &longDiff)) { - return Status(ErrorCodes::Overflow, str::stream() << "date overflow"); - } - return Value(Date_t::fromMillisSinceEpoch(longDiff)); - } - case NumberDouble: { - using limits = std::numeric_limits<long long>; - long long longDiff = lhs.getDate().toMillisSinceEpoch(); - double doubleRhs = rhs.coerceToDouble(); - // check the doubleRhs should not exceed int64 limit and result will not overflow - if (doubleRhs >= static_cast<double>(limits::min()) && - doubleRhs < static_cast<double>(limits::max()) && - !overflow::sub(longDiff, llround(doubleRhs), &longDiff)) { - return Value(Date_t::fromMillisSinceEpoch(longDiff)); - } - return Status(ErrorCodes::Overflow, str::stream() << "date overflow"); - } - case NumberDecimal: { - long long longDiff = lhs.getDate().toMillisSinceEpoch(); - Decimal128 decimalRhs = rhs.coerceToDecimal(); - std::uint32_t signalingFlags = Decimal128::SignalingFlag::kNoFlag; - std::int64_t longRhs = decimalRhs.toLong(&signalingFlags); - if (signalingFlags != Decimal128::SignalingFlag::kNoFlag || - overflow::sub(longDiff, longRhs, &longDiff)) { - return Status(ErrorCodes::Overflow, str::stream() << "date overflow"); - } - return Value(Date_t::fromMillisSinceEpoch(longDiff)); - } - default: - return Status(ErrorCodes::TypeMismatch, - str::stream() - << "can't $subtract " << typeName(rhs.getType()) << " from Date"); + if (rhs.getType() == Date) { + return Value(durationCount<Milliseconds>(lhs.getDate() - rhs.getDate())); + } else if (rhs.numeric()) { + return Value(lhs.getDate() - Milliseconds(rhs.coerceToLong())); + } else { + return Status(ErrorCodes::TypeMismatch, + str::stream() + << "can't $subtract " << typeName(rhs.getType()) << " from Date"); } } else { return Status(ErrorCodes::TypeMismatch, @@ -5781,35 +5556,24 @@ const char* ExpressionSubtract::getOpName() const { return "$subtract"; } -monotonic::State ExpressionSubtract::getMonotonicState(const FieldPath& sortedFieldPath) const { - // 1. Get monotonic states of the both children. - // 2. Apply monotonic::opposite to the state of the second child, because it is negated. - // 3. Combine children. Function monotonic::combine correctly handles all the cases where, for - // example, argumemnts are both monotonic, but in the opposite directions. - return monotonic::combine( - getChildren()[0]->getMonotonicState(sortedFieldPath), - monotonic::opposite(getChildren()[1]->getMonotonicState(sortedFieldPath))); -} - /* ------------------------- ExpressionSwitch ------------------------------ */ REGISTER_STABLE_EXPRESSION(switch, ExpressionSwitch::parse); Value ExpressionSwitch::evaluate(const Document& root, Variables* variables) const { - for (int i = 0; i < numBranches(); ++i) { - auto [caseExpr, thenExpr] = getBranch(i); - Value caseResult = caseExpr->evaluate(root, variables); + for (auto&& branch : _branches) { + Value caseExpression(branch.first->evaluate(root, variables)); - if (caseResult.coerceToBool()) { - return thenExpr->evaluate(root, variables); + if (caseExpression.coerceToBool()) { + return branch.second->evaluate(root, variables); } } uassert(40066, "$switch could not find a matching branch for an input, and no default was specified.", - defaultExpr()); + _default); - return defaultExpr()->evaluate(root, variables); + return _default->evaluate(root, variables); } boost::intrusive_ptr<Expression> ExpressionSwitch::parse(ExpressionContext* const expCtx, @@ -5818,7 +5582,7 @@ boost::intrusive_ptr<Expression> ExpressionSwitch::parse(ExpressionContext* cons uassert(40060, str::stream() << "$switch requires an object as an argument, found: " << typeName(expr.type()), - expr.type() == BSONType::Object); + expr.type() == Object); boost::intrusive_ptr<Expression> expDefault; std::vector<boost::intrusive_ptr<Expression>> children; @@ -5830,13 +5594,13 @@ boost::intrusive_ptr<Expression> ExpressionSwitch::parse(ExpressionContext* cons uassert(40061, str::stream() << "$switch expected an array for 'branches', found: " << typeName(elem.type()), - elem.type() == BSONType::Array); + elem.type() == Array); for (auto&& branch : elem.Array()) { uassert(40062, str::stream() << "$switch expected each branch to be an object, found: " << typeName(branch.type()), - branch.type() == BSONType::Object); + branch.type() == Object); boost::intrusive_ptr<Expression> switchCase, switchThen; @@ -5868,96 +5632,98 @@ boost::intrusive_ptr<Expression> ExpressionSwitch::parse(ExpressionContext* cons uasserted(40067, str::stream() << "$switch found an unknown argument: " << field); } } - - // The the 'default' expression is always the final child. If no 'default' expression is - // provided, then the final child is nullptr. 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; + } + } - return new ExpressionSwitch(expCtx, std::move(children)); -} + uassert(40068, "$switch requires at least one branch.", !branches.empty()); -void ExpressionSwitch::deleteBranch(int i) { - invariant(i >= 0); - invariant(i < numBranches()); - // Delete the two elements corresponding to this branch at positions 2i and 2i + 1. - _children.erase(std::next(_children.begin(), i * 2), std::next(_children.begin(), i * 2 + 2)); + return new ExpressionSwitch(expCtx, std::move(children), std::move(branches)); } void ExpressionSwitch::_doAddDependencies(DepsTracker* deps) const { - for (auto&& child : _children) { - // Check for nullptr, since we leave a nullptr as the final child when the 'default' - // expression is missing. - if (child) { - child->addDependencies(deps); - } + for (auto&& branch : _branches) { + branch.first->addDependencies(deps); + branch.second->addDependencies(deps); + } + + if (_default) { + _default->addDependencies(deps); } } boost::intrusive_ptr<Expression> ExpressionSwitch::optimize() { - if (defaultExpr()) { - _children.back() = _children.back()->optimize(); + if (_default) { + _default = _default->optimize(); } - bool trueConst = false; + std::vector<ExpressionPair>::iterator it = _branches.begin(); + bool true_const = false; - int i = 0; - while (!trueConst && i < numBranches()) { - boost::intrusive_ptr<Expression>& caseExpr = _children[i * 2]; - boost::intrusive_ptr<Expression>& thenExpr = _children[i * 2 + 1]; - caseExpr = caseExpr->optimize(); + while (!true_const && it != _branches.end()) { + (it->first) = (it->first)->optimize(); - if (auto* val = dynamic_cast<ExpressionConstant*>(caseExpr.get())) { - if (!val->getValue().coerceToBool()) { + if (auto* val = dynamic_cast<ExpressionConstant*>((it->first).get())) { + if (!((val->getValue()).coerceToBool())) { // Case is constant and evaluates to false, so it is removed. - deleteBranch(i); + it = _branches.erase(it); } else { - // Case optimized to a constant true value. Set the optimized version of the - // corresponding 'then' expression as the new 'default'. Break out of the loop and - // fall through to the logic to remove this and all subsequent branches. - trueConst = true; - _children.back() = thenExpr->optimize(); - break; + // Case is constant and true so it is set to default and then removed. + true_const = true; + + // Optimizing this case's then, so that default will remain optimized. + (it->second) = (it->second)->optimize(); + _default = it->second; + it = _branches.erase(it); } } else { // Since case is not removed from the switch, its then is now optimized. - thenExpr = thenExpr->optimize(); - ++i; + (it->second) = (it->second)->optimize(); + ++it; } } // Erasing the rest of the cases because found a default true value. - if (trueConst) { - while (i < numBranches()) { - deleteBranch(i); - } + if (true_const) { + _branches.erase(it, _branches.end()); } // If there are no cases, make the switch its default. - if (numBranches() == 0) { + if (_branches.size() == 0 && _default) { + return _default; + } else if (_branches.size() == 0) { uassert(40069, - "Cannot execute a switch statement where all the cases evaluate to false " - "without a default", - defaultExpr()); - return _children.back(); + "One cannot execute a switch statement where all the cases evaluate to false " + "without a default.", + _branches.size()); } return this; } -Value ExpressionSwitch::serialize(const SerializationOptions& options) const { +Value ExpressionSwitch::serialize(bool explain) const { std::vector<Value> serializedBranches; - serializedBranches.reserve(numBranches()); + serializedBranches.reserve(_branches.size()); - for (int i = 0; i < numBranches(); ++i) { - auto [caseExpr, thenExpr] = getBranch(i); - serializedBranches.push_back(Value(Document{{"case", caseExpr->serialize(options)}, - {"then", thenExpr->serialize(options)}})); + for (auto&& branch : _branches) { + serializedBranches.push_back(Value(Document{{"case", branch.first->serialize(explain)}, + {"then", branch.second->serialize(explain)}})); } - if (defaultExpr()) { + if (_default) { return Value(Document{{"$switch", Document{{"branches", Value(serializedBranches)}, - {"default", defaultExpr()->serialize(options)}}}}); + {"default", _default->serialize(explain)}}}}); } return Value(Document{{"$switch", Document{{"branches", Value(serializedBranches)}}}}); @@ -6199,11 +5965,11 @@ boost::intrusive_ptr<Expression> ExpressionTrim::optimize() { return this; } -Value ExpressionTrim::serialize(const SerializationOptions& options) const { +Value ExpressionTrim::serialize(bool explain) const { return Value( Document{{_name, - Document{{"input", _input->serialize(options)}, - {"chars", _characters ? _characters->serialize(options) : Value()}}}}); + Document{{"input", _input->serialize(explain)}, + {"chars", _characters ? _characters->serialize(explain) : Value()}}}}); } void ExpressionTrim::_doAddDependencies(DepsTracker* deps) const { @@ -6501,17 +6267,17 @@ boost::intrusive_ptr<Expression> ExpressionZip::optimize() { return this; } -Value ExpressionZip::serialize(const SerializationOptions& options) const { +Value ExpressionZip::serialize(bool explain) const { vector<Value> serializedInput; vector<Value> serializedDefaults; Value serializedUseLongestLength = Value(_useLongestLength); for (auto&& expr : _inputs) { - serializedInput.push_back(expr.get()->serialize(options)); + serializedInput.push_back(expr.get()->serialize(explain)); } for (auto&& expr : _defaults) { - serializedDefaults.push_back(expr.get()->serialize(options)); + serializedDefaults.push_back(expr.get()->serialize(explain)); } return Value(DOC("$zip" << DOC("inputs" << Value(serializedInput) << "defaults" @@ -6905,7 +6671,7 @@ private: } else if (doubleValue == 0.0 && std::signbit(doubleValue)) { return Value("-0"_sd); } else { - return Value(static_cast<std::string>(str::stream() << fmt::format("{}", doubleValue))); + return Value(static_cast<std::string>(str::stream() << doubleValue)); } } @@ -7092,6 +6858,14 @@ boost::intrusive_ptr<Expression> ExpressionConvert::optimize() { return this; } +Value ExpressionConvert::serialize(bool explain) const { + return Value(Document{{"$convert", + Document{{"input", _input->serialize(explain)}, + {"to", _to->serialize(explain)}, + {"onError", _onError ? _onError->serialize(explain) : Value()}, + {"onNull", _onNull ? _onNull->serialize(explain) : Value()}}}}); +} + void ExpressionConvert::_doAddDependencies(DepsTracker* deps) const { _input->addDependencies(deps); _to->addDependencies(deps); @@ -7103,29 +6877,6 @@ void ExpressionConvert::_doAddDependencies(DepsTracker* deps) const { } } -Value ExpressionConvert::serialize(const SerializationOptions& options) const { - // Since the 'to' field is a parameter from a set of valid values and not free user input, - // we want to avoid boiling it down to the representative value in the query shape. The first - // condition is so that we can keep serializing correctly whenever the 'to' field is an - // expression that gets resolved down to a string of a valid type, or its corresponding - // numerical value. If it's just the constant, we want to wrap it in a $const except when the - // serialization policy is debug. - auto constExpr = dynamic_cast<ExpressionConstant*>(_to.get()); - Value toField = Value(); - if (!constExpr) { - toField = _to->serialize(options); - } else if (options.literalPolicy == LiteralSerializationPolicy::kToDebugTypeString) { - toField = constExpr->getValue(); - } else { - toField = Value(DOC("$const" << constExpr->getValue())); - } - return Value(Document{{"$convert", - Document{{"input", _input->serialize(options)}, - {"to", toField}, - {"onError", _onError ? _onError->serialize(options) : Value()}, - {"onNull", _onNull ? _onNull->serialize(options) : Value()}}}}); -} - BSONType ExpressionConvert::computeTargetType(Value targetTypeName) const { BSONType targetType; if (targetTypeName.getType() == BSONType::String) { @@ -7388,12 +7139,12 @@ void ExpressionRegex::_compile(RegexExecutionState* executionState) const { executionState->capturesBuffer.resize((1 + executionState->numCaptures) * 3); } -Value ExpressionRegex::serialize(const SerializationOptions& options) const { +Value ExpressionRegex::serialize(bool explain) const { return Value( Document{{_opName, - Document{{"input", _input->serialize(options)}, - {"regex", _regex->serialize(options)}, - {"options", _options ? _options->serialize(options) : Value()}}}}); + Document{{"input", _input->serialize(explain)}, + {"regex", _regex->serialize(explain)}, + {"options", _options ? _options->serialize(explain) : Value()}}}}); } void ExpressionRegex::_extractInputField(RegexExecutionState* executionState, @@ -7656,7 +7407,7 @@ void ExpressionRandom::_doAddDependencies(DepsTracker* deps) const { deps->needRandomGenerator = true; } -Value ExpressionRandom::serialize(const SerializationOptions& options) const { +Value ExpressionRandom::serialize(const bool explain) const { return Value(DOC(getOpName() << Document())); } @@ -7679,8 +7430,8 @@ Value ExpressionToHashedIndexKey::evaluate(const Document& root, Variables* vari BSONElementHasher::DEFAULT_HASH_SEED)); } -Value ExpressionToHashedIndexKey::serialize(const SerializationOptions& options) const { - return Value(DOC("$toHashedIndexKey" << _children[0]->serialize(options))); +Value ExpressionToHashedIndexKey::serialize(bool explain) const { + return Value(DOC("$toHashedIndexKey" << _children[0]->serialize(explain))); } void ExpressionToHashedIndexKey::_doAddDependencies(DepsTracker* deps) const { @@ -7756,13 +7507,13 @@ boost::intrusive_ptr<Expression> ExpressionDateArithmetics::optimize() { return intrusive_ptr<Expression>(this); } -Value ExpressionDateArithmetics::serialize(const SerializationOptions& options) const { +Value ExpressionDateArithmetics::serialize(bool explain) const { return Value( Document{{_opName, - Document{{"startDate", _startDate->serialize(options)}, - {"unit", _unit->serialize(options)}, - {"amount", _amount->serialize(options)}, - {"timezone", _timeZone ? _timeZone->serialize(options) : Value()}}}}); + Document{{"startDate", _startDate->serialize(explain)}, + {"unit", _unit->serialize(explain)}, + {"amount", _amount->serialize(explain)}, + {"timezone", _timeZone ? _timeZone->serialize(explain) : Value()}}}}); } Value ExpressionDateArithmetics::evaluate(const Document& root, Variables* variables) const { @@ -7800,15 +7551,6 @@ Value ExpressionDateArithmetics::evaluate(const Document& root, Variables* varia startDate.coerceToDate(), unit, amount.coerceToLong(), timezone.get()); } -monotonic::State ExpressionDateArithmetics::getMonotonicState( - const FieldPath& sortedFieldPath) const { - if (!ExpressionConstant::allNullOrConstant({_unit, _timeZone})) { - return monotonic::State::NonMonotonic; - } - return combineMonotonicStateOfArguments(_startDate->getMonotonicState(sortedFieldPath), - _amount->getMonotonicState(sortedFieldPath)); -} - /* ----------------------- ExpressionDateAdd ---------------------------- */ REGISTER_STABLE_EXPRESSION(dateAdd, ExpressionDateAdd::parse); @@ -7833,11 +7575,6 @@ Value ExpressionDateAdd::evaluateDateArithmetics(Date_t date, return Value(dateAdd(date, unit, amount, timezone)); } -monotonic::State ExpressionDateAdd::combineMonotonicStateOfArguments( - monotonic::State startDataMonotonicState, monotonic::State amountMonotonicState) const { - return monotonic::combine(startDataMonotonicState, amountMonotonicState); -} - /* ----------------------- ExpressionDateSubtract ---------------------------- */ REGISTER_STABLE_EXPRESSION(dateSubtract, ExpressionDateSubtract::parse); @@ -7867,11 +7604,6 @@ Value ExpressionDateSubtract::evaluateDateArithmetics(Date_t date, return Value(dateAdd(date, unit, -amount, timezone)); } -monotonic::State ExpressionDateSubtract::combineMonotonicStateOfArguments( - monotonic::State startDataMonotonicState, monotonic::State amountMonotonicState) const { - return monotonic::combine(startDataMonotonicState, amountMonotonicState); -} - /* ----------------------- ExpressionDateTrunc ---------------------------- */ REGISTER_STABLE_EXPRESSION(dateTrunc, ExpressionDateTrunc::parse); @@ -7955,14 +7687,14 @@ boost::intrusive_ptr<Expression> ExpressionDateTrunc::optimize() { return this; }; -Value ExpressionDateTrunc::serialize(const SerializationOptions& options) const { +Value ExpressionDateTrunc::serialize(bool explain) const { return Value{Document{ {"$dateTrunc"_sd, - Document{{"date"_sd, _date->serialize(options)}, - {"unit"_sd, _unit->serialize(options)}, - {"binSize"_sd, _binSize ? _binSize->serialize(options) : Value{}}, - {"timezone"_sd, _timeZone ? _timeZone->serialize(options) : Value{}}, - {"startOfWeek"_sd, _startOfWeek ? _startOfWeek->serialize(options) : Value{}}}}}}; + Document{{"date"_sd, _date->serialize(explain)}, + {"unit"_sd, _unit->serialize(explain)}, + {"binSize"_sd, _binSize ? _binSize->serialize(explain) : Value{}}, + {"timezone"_sd, _timeZone ? _timeZone->serialize(explain) : Value{}}, + {"startOfWeek"_sd, _startOfWeek ? _startOfWeek->serialize(explain) : Value{}}}}}}; }; Date_t ExpressionDateTrunc::convertToDate(const Value& value) { @@ -8044,13 +7776,6 @@ void ExpressionDateTrunc::_doAddDependencies(DepsTracker* deps) const { } } -monotonic::State ExpressionDateTrunc::getMonotonicState(const FieldPath& sortedFieldPath) const { - if (!ExpressionConstant::allNullOrConstant({_unit, _binSize, _timeZone, _startOfWeek})) { - return monotonic::State::NonMonotonic; - } - return _date->getMonotonicState(sortedFieldPath); -} - /* -------------------------- ExpressionGetField ------------------------------ */ REGISTER_EXPRESSION_WITH_MIN_VERSION( getField, @@ -8144,6 +7869,7 @@ Value ExpressionGetField::evaluate(const Document& root, Variables* variables) c return Value(); } + return inputValue.getDocument().getField(fieldValue.getString()); } @@ -8156,22 +7882,10 @@ void ExpressionGetField::_doAddDependencies(DepsTracker* deps) const { _field->addDependencies(deps); } -Value ExpressionGetField::serialize(const SerializationOptions& options) const { - // The parser guarantees that the '_field' expression evaluates to a constant string. - auto strPath = static_cast<ExpressionConstant*>(_field.get())->getValue().getString(); - - Value maybeRedactedPath{options.serializeFieldPathFromString(strPath)}; - // This is a pretty unique option to serialize. It is both a constant and a field path, which - // means that it: - // - should be redacted (if that option is set). - // - should *not* be wrapped in $const iff we are serializing for a debug string - if (options.literalPolicy != LiteralSerializationPolicy::kToDebugTypeString) { - maybeRedactedPath = Value(Document{{"$const"_sd, maybeRedactedPath}}); - } - +Value ExpressionGetField::serialize(const bool explain) const { return Value(Document{{"$getField"_sd, - Document{{"field"_sd, std::move(maybeRedactedPath)}, - {"input"_sd, _input->serialize(options)}}}}); + Document{{"field"_sd, _field->serialize(explain)}, + {"input"_sd, _input->serialize(explain)}}}}); } /* -------------------------- ExpressionSetField ------------------------------ */ @@ -8295,23 +8009,11 @@ void ExpressionSetField::_doAddDependencies(DepsTracker* deps) const { _value->addDependencies(deps); } -Value ExpressionSetField::serialize(const SerializationOptions& options) const { - // The parser guarantees that the '_field' expression evaluates to a constant string. - auto strPath = static_cast<ExpressionConstant*>(_field.get())->getValue().getString(); - - Value maybeRedactedPath{options.serializeFieldPathFromString(strPath)}; - // This is a pretty unique option to serialize. It is both a constant and a field path, which - // means that it: - // - should be redacted (if that option is set). - // - should *not* be wrapped in $const iff we are serializing for a debug string - if (options.literalPolicy != LiteralSerializationPolicy::kToDebugTypeString) { - maybeRedactedPath = Value(Document{{"$const"_sd, maybeRedactedPath}}); - } - +Value ExpressionSetField::serialize(const bool explain) const { return Value(Document{{"$setField"_sd, - Document{{"field"_sd, std::move(maybeRedactedPath)}, - {"input"_sd, _input->serialize(options)}, - {"value"_sd, _value->serialize(options)}}}}); + Document{{"field"_sd, _field->serialize(explain)}, + {"input"_sd, _input->serialize(explain)}, + {"value"_sd, _value->serialize(explain)}}}}); } /* ------------------------- ExpressionTsSecond ----------------------------- */ @@ -8364,90 +8066,4 @@ REGISTER_EXPRESSION_WITH_MIN_VERSION( MONGO_INITIALIZER_GROUP(BeginExpressionRegistration, ("default"), ("EndExpressionRegistration")) MONGO_INITIALIZER_GROUP(EndExpressionRegistration, ("BeginExpressionRegistration"), ()) - -/* ----------------------- ExpressionInternalKeyStringValue ---------------------------- */ - -REGISTER_STABLE_EXPRESSION(_internalKeyStringValue, ExpressionInternalKeyStringValue::parse); - -boost::intrusive_ptr<Expression> ExpressionInternalKeyStringValue::parse( - ExpressionContext* expCtx, BSONElement expr, const VariablesParseState& vps) { - - uassert( - 8281500, - str::stream() << "$_internalKeyStringValue only supports an object as its argument, not " - << typeName(expr.type()), - expr.type() == BSONType::Object); - - boost::intrusive_ptr<Expression> inputExpr; - boost::intrusive_ptr<Expression> collationExpr; - - for (auto&& element : expr.embeddedObject()) { - auto field = element.fieldNameStringData(); - if ("input"_sd == field) { - inputExpr = parseOperand(expCtx, element, vps); - } else if ("collation"_sd == field) { - collationExpr = parseOperand(expCtx, element, vps); - } else { - uasserted(8281501, - str::stream() << "Unrecognized argument to $_internalKeyStringValue: " - << element.fieldName()); - } - } - uassert(8281502, - str::stream() << "$_internalKeyStringValue requires 'input' to be specified", - inputExpr); - - return make_intrusive<ExpressionInternalKeyStringValue>(expCtx, inputExpr, collationExpr); -} - -Value ExpressionInternalKeyStringValue::serialize(const SerializationOptions& options) const { - return Value( - Document{{getOpName(), - Document{{"input", _children[_kInput]->serialize(options)}, - {"collation", - _children[_kCollation] ? _children[_kCollation]->serialize(options) - : Value()}}}}); -} - -Value ExpressionInternalKeyStringValue::evaluate(const Document& root, Variables* variables) const { - const Value input = _children[_kInput]->evaluate(root, variables); - auto inputBson = input.wrap(""); - - std::unique_ptr<CollatorInterface> collator = nullptr; - if (_children[_kCollation]) { - const Value collation = _children[_kCollation]->evaluate(root, variables); - uassert(8281503, - str::stream() << "Collation spec must be an object, not " - << typeName(collation.getType()), - collation.isObject()); - auto collationBson = collation.getDocument().toBson(); - - auto collatorFactory = - CollatorFactoryInterface::get(getExpressionContext()->opCtx->getServiceContext()); - collator = uassertStatusOKWithContext(collatorFactory->makeFromBSON(collationBson), - "Invalid collation spec"); - } - - KeyString::HeapBuilder ksBuilder(KeyString::Version::V1); - if (collator) { - ksBuilder.appendBSONElement(inputBson.firstElement(), [&](StringData str) { - return collator->getComparisonString(str); - }); - } else { - ksBuilder.appendBSONElement(inputBson.firstElement()); - } - auto ksValue = ksBuilder.release(); - - // The result omits the typebits so that the numeric value of different types have the same - // binary representation. - return Value( - BSONBinData{ksValue.getBuffer(), static_cast<int>(ksValue.getSize()), BinDataGeneral}); -} - -void ExpressionInternalKeyStringValue::_doAddDependencies(DepsTracker* deps) const { - _children[_kInput]->addDependencies(deps); - if (_children[_kCollation]) { - _children[_kCollation]->addDependencies(deps); - } -} } // namespace mongo |
