diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/mongo/db/pipeline/expression.cpp | 52 | ||||
-rw-r--r-- | src/mongo/db/pipeline/expression.h | 12 | ||||
-rw-r--r-- | src/mongo/db/pipeline/expression_test.cpp | 53 |
3 files changed, 117 insertions, 0 deletions
diff --git a/src/mongo/db/pipeline/expression.cpp b/src/mongo/db/pipeline/expression.cpp index 7dc91bdd4fe..6b4fd5ac6ee 100644 --- a/src/mongo/db/pipeline/expression.cpp +++ b/src/mongo/db/pipeline/expression.cpp @@ -3166,6 +3166,58 @@ const char* ExpressionSubstrCP::getOpName() const { return "$substrCP"; } +/* ----------------------- ExpressionStrLenBytes ------------------------- */ + +Value ExpressionStrLenBytes::evaluateInternal(Variables* vars) const { + Value str(vpOperand[0]->evaluateInternal(vars)); + + uassert(34473, + str::stream() << "$strLenBytes requires a string argument, found: " + << typeName(str.getType()), + str.getType() == String); + + size_t strLen = str.getString().size(); + + uassert(34470, + "string length could not be represented as an int.", + strLen <= std::numeric_limits<int>::max()); + return Value(static_cast<int>(strLen)); +} + +REGISTER_EXPRESSION(strLenBytes, ExpressionStrLenBytes::parse); +const char* ExpressionStrLenBytes::getOpName() const { + return "$strLenBytes"; +} + +/* ----------------------- ExpressionStrLenCP ------------------------- */ + +Value ExpressionStrLenCP::evaluateInternal(Variables* vars) const { + Value val(vpOperand[0]->evaluateInternal(vars)); + + uassert( + 34471, + str::stream() << "$strLenCP requires a string argument, found: " << typeName(val.getType()), + val.getType() == String); + + std::string stringVal = val.getString(); + + size_t strLen = 0; + for (char byte : stringVal) { + strLen += !isContinuationByte(byte); + } + + uassert(34472, + "string length could not be represented as an int.", + strLen <= std::numeric_limits<int>::max()); + + return Value(static_cast<int>(strLen)); +} + +REGISTER_EXPRESSION(strLenCP, ExpressionStrLenCP::parse); +const char* ExpressionStrLenCP::getOpName() const { + return "$strLenCP"; +} + /* ----------------------- ExpressionSubtract ---------------------------- */ Value ExpressionSubtract::evaluateInternal(Variables* vars) const { diff --git a/src/mongo/db/pipeline/expression.h b/src/mongo/db/pipeline/expression.h index fe3e57b0d4a..0939bcbcdc7 100644 --- a/src/mongo/db/pipeline/expression.h +++ b/src/mongo/db/pipeline/expression.h @@ -1259,6 +1259,18 @@ public: }; +class ExpressionStrLenBytes final : public ExpressionFixedArity<ExpressionStrLenBytes, 1> { + Value evaluateInternal(Variables* vars) const final; + const char* getOpName() const final; +}; + + +class ExpressionStrLenCP final : public ExpressionFixedArity<ExpressionStrLenCP, 1> { + Value evaluateInternal(Variables* vars) const final; + const char* getOpName() const final; +}; + + class ExpressionSubtract final : public ExpressionFixedArity<ExpressionSubtract, 2> { public: Value evaluateInternal(Variables* vars) const final; diff --git a/src/mongo/db/pipeline/expression_test.cpp b/src/mongo/db/pipeline/expression_test.cpp index ed6625cc594..457cc1e4d8c 100644 --- a/src/mongo/db/pipeline/expression_test.cpp +++ b/src/mongo/db/pipeline/expression_test.cpp @@ -4294,6 +4294,59 @@ class NullMiddleGt : public ExpectedResultBase { } // namespace Strcasecmp +namespace StrLenBytes { + +TEST(ExpressionStrLenBytes, ComputesLengthOfString) { + assertExpectedResults("$strLenBytes", {{{Value("abc")}, Value(3)}}); +} + +TEST(ExpressionStrLenBytes, ComputesLengthOfEmptyString) { + assertExpectedResults("$strLenBytes", {{{Value("")}, Value(0)}}); +} + +TEST(ExpressionStrLenBytes, ComputesLengthOfStringWithNull) { + assertExpectedResults("$strLenBytes", + {{{Value(StringData("ab\0c", StringData::LiteralTag()))}, Value(4)}}); +} + +TEST(ExpressionStrLenCP, ComputesLengthOfStringWithNullAtEnd) { + assertExpectedResults("$strLenBytes", + {{{Value(StringData("abc\0", StringData::LiteralTag()))}, Value(4)}}); +} + +} // namespace StrLenBytes + +namespace StrLenCP { + +TEST(ExpressionStrLenCP, ComputesLengthOfASCIIString) { + assertExpectedResults("$strLenCP", {{{Value("abc")}, Value(3)}}); +} + +TEST(ExpressionStrLenCP, ComputesLengthOfEmptyString) { + assertExpectedResults("$strLenCP", {{{Value("")}, Value(0)}}); +} + +TEST(ExpressionStrLenCP, ComputesLengthOfStringWithNull) { + assertExpectedResults("$strLenCP", + {{{Value(StringData("ab\0c", StringData::LiteralTag()))}, Value(4)}}); +} + +TEST(ExpressionStrLenCP, ComputesLengthOfStringWithNullAtEnd) { + assertExpectedResults("$strLenCP", + {{{Value(StringData("abc\0", StringData::LiteralTag()))}, Value(4)}}); +} + +TEST(ExpressionStrLenCP, ComputesLengthOfStringWithAccent) { + assertExpectedResults("$strLenCP", + {{{Value(StringData("a\0bâ", StringData::LiteralTag()))}, Value(4)}}); +} + +TEST(ExpressionStrLenCP, ComputesLengthOfStringWithSpecialCharacters) { + assertExpectedResults("$strLenCP", {{{Value("ºabøåß")}, Value(6)}}); +} + +} // namespace StrLenCP + namespace SubstrBytes { class ExpectedResultBase { |