diff options
Diffstat (limited to 'src/mongo/db/query/sbe_stage_builder_expression.cpp')
-rw-r--r-- | src/mongo/db/query/sbe_stage_builder_expression.cpp | 107 |
1 files changed, 99 insertions, 8 deletions
diff --git a/src/mongo/db/query/sbe_stage_builder_expression.cpp b/src/mongo/db/query/sbe_stage_builder_expression.cpp index 31f7c87ac8a..42b7a086e90 100644 --- a/src/mongo/db/query/sbe_stage_builder_expression.cpp +++ b/src/mongo/db/query/sbe_stage_builder_expression.cpp @@ -298,10 +298,6 @@ void generateStringCaseConversionExpression(ExpressionVisitorContext* _context, sbe::makeE<sbe::ELocalBind>(frameId, std::move(str), std::move(totalCaseConversionExpr))); } -std::unique_ptr<sbe::EExpression> makeNot(std::unique_ptr<sbe::EExpression> e) { - return sbe::makeE<sbe::EPrimUnary>(sbe::EPrimUnary::logicNot, std::move(e)); -} - void buildArrayAccessByConstantIndex(ExpressionVisitorContext* context, const std::string& exprName, int32_t index) { @@ -330,6 +326,21 @@ void buildArrayAccessByConstantIndex(ExpressionVisitorContext* context, sbe::makeE<sbe::ELocalBind>(frameId, std::move(binds), std::move(resultExpr))); } +/** + * Generate an EExpression representing a Regex function result upon null argument(s) depending on + * the type of the function: $regexMatch - false, $regexFind - null, $RegexFindAll - []. + */ +std::unique_ptr<sbe::EExpression> generateRegexNullResponse(StringData exprName) { + if (exprName.toString().compare(std::string("regexMatch")) == 0) { + return sbe::makeE<sbe::EConstant>(sbe::value::TypeTags::Boolean, + sbe::value::bitcastFrom<bool>(false)); + } else if (exprName.toString().compare("regexFindAll") == 0) { + auto [arrTag, arrVal] = sbe::value::makeNewArray(); + return sbe::makeE<sbe::EConstant>(arrTag, arrVal); + } + return sbe::makeE<sbe::EConstant>(sbe::value::TypeTags::Null, 0); +} + class ExpressionPreVisitor final : public ExpressionVisitor { public: ExpressionPreVisitor(ExpressionVisitorContext* context) : _context{context} {} @@ -1136,7 +1147,7 @@ public: // // 2) Check if the value in a given slot is an integral int64. This test is done by // computing a lossless conversion of the value in s1 to an int64. The exposed - // conversion function by the vm returns a value if there is no loss of precsision, + // conversion function by the vm returns a value if there is no loss of precision, // otherwise it returns Nothing. In both the valid or Nothing case, we can store the result // of the conversion in l2.0 of the inner let binding and test for existence. If the // existence check fails we know the conversion is lossy and we can fail the query. @@ -2116,13 +2127,13 @@ public: unsupportedExpression("$convert"); } void visit(ExpressionRegexFind* expr) final { - unsupportedExpression("$regexFind"); + generateRegexExpression(expr, "regexFind"); } void visit(ExpressionRegexFindAll* expr) final { - unsupportedExpression("$regexFind"); + generateRegexExpression(expr, "regexFindAll"); } void visit(ExpressionRegexMatch* expr) final { - unsupportedExpression("$regexFind"); + generateRegexExpression(expr, "regexMatch"); } void visit(ExpressionCosine* expr) final { generateTrigonometricExpressionWithBounds( @@ -2701,6 +2712,86 @@ private: sbe::makeE<sbe::ELocalBind>(frameId, std::move(binds), std::move(setExpr))); } + /** + * Shared expression building logic for regex expressions. + */ + void generateRegexExpression(ExpressionRegex* expr, StringData exprName) { + size_t arity = (expr->hasOptions()) ? 3 : 2; + _context->ensureArity(arity); + + std::unique_ptr<sbe::EExpression> options = + (arity == 3) ? _context->popExpr() : sbe::makeE<sbe::EConstant>(""); + auto pattern = _context->popExpr(); + auto input = _context->popExpr(); + + auto pcreRegexExpr = [&]() { + auto [patternStr, optStr] = expr->getConstantPatternAndOptions(); + if (patternStr) { + // Create the compiled Regex from constant pattern and options. + auto [regexTag, regexVal] = sbe::value::makeNewPcreRegex(patternStr.get(), optStr); + return sbe::makeE<sbe::EConstant>(regexTag, regexVal); + } else { + // Build a call to regexCompile function. + auto frameId = _context->frameIdGenerator->generate(); + auto binds = sbe::makeEs(std::move(pattern)); + sbe::EVariable patternRef(frameId, 0); + + return sbe::makeE<sbe::ELocalBind>( + frameId, + std::move(binds), + buildMultiBranchConditional( + CaseValuePair{generateNullOrMissing(patternRef), + sbe::makeE<sbe::EConstant>(sbe::value::TypeTags::Null, 0)}, + CaseValuePair{generateNonStringCheck(patternRef), + sbe::makeE<sbe::EFail>(ErrorCodes::Error{5073400}, + str::stream() + << "$" << exprName.toString() + << " expects string pattern")}, + sbe::makeE<sbe::EFunction>( + "regexCompile", sbe::makeEs(patternRef.clone(), std::move(options))))); + } + }(); + + auto outerFrameId = _context->frameIdGenerator->generate(); + auto outerBinds = sbe::makeEs(std::move(pcreRegexExpr), std::move(input)); + sbe::EVariable regexRef(outerFrameId, 0); + sbe::EVariable inputRef(outerFrameId, 1); + auto innerFrameId = _context->frameIdGenerator->generate(); + sbe::EVariable resRef(innerFrameId, 0); + + auto regexWithErrorCheck = buildMultiBranchConditional( + CaseValuePair{sbe::makeE<sbe::EPrimBinary>( + sbe::EPrimBinary::logicOr, + generateNullOrMissing(inputRef), + sbe::makeE<sbe::EFunction>("isNull", sbe::makeEs(regexRef.clone()))), + generateRegexNullResponse(exprName)}, + CaseValuePair{generateNonStringCheck(inputRef), + sbe::makeE<sbe::EFail>(ErrorCodes::Error{5073401}, + str::stream() << "$" << exprName.toString() + << " expects input of type string")}, + + CaseValuePair{ + sbe::makeE<sbe::EPrimUnary>( + sbe::EPrimUnary::logicNot, + sbe::makeE<sbe::EFunction>("exists", sbe::makeEs(regexRef.clone()))), + sbe::makeE<sbe::EFail>(ErrorCodes::Error{5073402}, "Invalid regular expression")}, + sbe::makeE<sbe::ELocalBind>( + innerFrameId, + sbe::makeEs(sbe::makeE<sbe::EFunction>( + exprName.toString(), sbe::makeEs(regexRef.clone(), inputRef.clone()))), + sbe::makeE<sbe::EIf>( + sbe::makeE<sbe::EFunction>("exists", sbe::makeEs(resRef.clone())), + resRef.clone(), + sbe::makeE<sbe::EFail>(ErrorCodes::Error{5073403}, + str::stream() + << "Unexpected error occurred while executing " + << exprName.toString() + << ". For more details see the error logs.")))); + + _context->pushExpr(sbe::makeE<sbe::ELocalBind>( + outerFrameId, std::move(outerBinds), std::move(regexWithErrorCheck))); + } + void unsupportedExpression(const char* op) const { uasserted(ErrorCodes::InternalErrorNotSupported, str::stream() << "Expression is not supported in SBE: " << op); |