diff options
author | Zixuan Zhuang <zixuan.zhuang@mongodb.com> | 2023-03-20 21:34:38 +0000 |
---|---|---|
committer | Evergreen Agent <no-reply@evergreen.mongodb.com> | 2023-03-20 23:12:17 +0000 |
commit | 8a524486d458d0fad010d4821423f3a77780ee50 (patch) | |
tree | 7ab9e516e5206d750783768bcfd8c55a1f778457 /src/mongo | |
parent | 598ebfee8e441253efed2ee4118ec8a045f75479 (diff) | |
download | mongo-8a524486d458d0fad010d4821423f3a77780ee50.tar.gz |
SERVER-74264 Fix incorrect $$NOW behavior in projection of find
Diffstat (limited to 'src/mongo')
-rw-r--r-- | src/mongo/db/matcher/expression_expr_test.cpp | 40 | ||||
-rw-r--r-- | src/mongo/db/pipeline/expression.cpp | 9 | ||||
-rw-r--r-- | src/mongo/db/pipeline/expression_date_test.cpp | 3 | ||||
-rw-r--r-- | src/mongo/db/pipeline/expression_field_path_test.cpp | 27 |
4 files changed, 40 insertions, 39 deletions
diff --git a/src/mongo/db/matcher/expression_expr_test.cpp b/src/mongo/db/matcher/expression_expr_test.cpp index 276c4ea1eb9..07e18df4660 100644 --- a/src/mongo/db/matcher/expression_expr_test.cpp +++ b/src/mongo/db/matcher/expression_expr_test.cpp @@ -832,48 +832,12 @@ TEST_F(ExprMatchTest, ExprRedactsCorrectly) { createMatcher(fromjson("{$expr: {$eq: [\"$a\", \"$$NOW\"]}}")); ASSERT_BSONOBJ_EQ_AUTO( // NOLINT - R"({ - "$and": [ - { - "HASH<a>": { - "$_internalExprEq": "?" - } - }, - { - "$expr": { - "$eq": [ - "$HASH<a>", - { - "$const": "?" - } - ] - } - } - ] - })", + R"({"$expr":{"$eq":["$HASH<a>","$$NOW"]}})", serialize(opts)); createMatcher(fromjson("{$expr: {$eq: [\"$a\", \"$$NOW\"]}}")); ASSERT_BSONOBJ_EQ_AUTO( // NOLINT - R"({ - "$and": [ - { - "HASH<a>": { - "$_internalExprEq": "?" - } - }, - { - "$expr": { - "$eq": [ - "$HASH<a>", - { - "$const": "?" - } - ] - } - } - ] - })", + R"({"$expr":{"$eq":["$HASH<a>","$$NOW"]}})", serialize(opts)); createMatcher(fromjson("{$expr: {$getField: {field: \"b\", input: {a: 1, b: 2}}}}")); diff --git a/src/mongo/db/pipeline/expression.cpp b/src/mongo/db/pipeline/expression.cpp index 6cdc08bca0c..125a14619a1 100644 --- a/src/mongo/db/pipeline/expression.cpp +++ b/src/mongo/db/pipeline/expression.cpp @@ -2501,6 +2501,15 @@ intrusive_ptr<Expression> ExpressionFieldPath::optimize() { return ExpressionConstant::create(getExpressionContext(), Value()); } + if (_variable == Variables::kNowId || _variable == Variables::kClusterTimeId || + _variable == Variables::kUserRolesId) { + // The agg system is allowed to replace the ExpressionFieldPath with an ExpressionConstant, + // which in turn would result in a plan cache entry that inlines the value of a system + // variable. However, the values of these system variables are not guaranteed to be constant + // across different executions of the same query shape, so we prohibit the optimization. + return intrusive_ptr<Expression>(this); + } + if (getExpressionContext()->variables.hasConstantValue(_variable)) { return ExpressionConstant::create( getExpressionContext(), evaluate(Document(), &(getExpressionContext()->variables))); diff --git a/src/mongo/db/pipeline/expression_date_test.cpp b/src/mongo/db/pipeline/expression_date_test.cpp index 1a76e1e1838..419028b9f06 100644 --- a/src/mongo/db/pipeline/expression_date_test.cpp +++ b/src/mongo/db/pipeline/expression_date_test.cpp @@ -2137,13 +2137,14 @@ TEST_F(ExpressionDateArithmeticsTest, OptimizesToConstant) { dateAddExp = Expression::parseExpression(expCtx.get(), doc, expCtx->variablesParseState); ASSERT(dynamic_cast<ExpressionConstant*>(dateAddExp->optimize().get())); + // Test that $$NOW will not be optimized as constant. doc = BSON(expName << BSON("startDate" << "$$NOW" << "unit" << "day" << "amount" << 1)); dateAddExp = Expression::parseExpression(expCtx.get(), doc, expCtx->variablesParseState); - ASSERT(dynamic_cast<ExpressionConstant*>(dateAddExp->optimize().get())); + ASSERT_FALSE(dynamic_cast<ExpressionConstant*>(dateAddExp->optimize().get())); // Test that expression does not optimize to constant if some of the parameters is not a diff --git a/src/mongo/db/pipeline/expression_field_path_test.cpp b/src/mongo/db/pipeline/expression_field_path_test.cpp index 3ad2c5d7262..4fba45c34a2 100644 --- a/src/mongo/db/pipeline/expression_field_path_test.cpp +++ b/src/mongo/db/pipeline/expression_field_path_test.cpp @@ -190,6 +190,33 @@ TEST(FieldPath, NoOptimizationOnVariableWithMissingValue) { ASSERT_FALSE(dynamic_cast<ExpressionConstant*>(optimizedExpr.get())); } +TEST(FieldPath, NoOptimizationOnCertainVariables) { + auto expCtx = ExpressionContextForTest{}; + + { + auto expr = ExpressionFieldPath::parse(&expCtx, "$$NOW", expCtx.variablesParseState); + ASSERT_TRUE(dynamic_cast<ExpressionFieldPath*>(expr.get())); + + auto optimizedExpr = expr->optimize(); + ASSERT_FALSE(dynamic_cast<ExpressionConstant*>(optimizedExpr.get())); + } + { + auto expr = + ExpressionFieldPath::parse(&expCtx, "$$CLUSTER_TIME", expCtx.variablesParseState); + ASSERT_TRUE(dynamic_cast<ExpressionFieldPath*>(expr.get())); + + auto optimizedExpr = expr->optimize(); + ASSERT_FALSE(dynamic_cast<ExpressionConstant*>(optimizedExpr.get())); + } + { + auto expr = ExpressionFieldPath::parse(&expCtx, "$$USER_ROLES", expCtx.variablesParseState); + ASSERT_TRUE(dynamic_cast<ExpressionFieldPath*>(expr.get())); + + auto optimizedExpr = expr->optimize(); + ASSERT_FALSE(dynamic_cast<ExpressionConstant*>(optimizedExpr.get())); + } +} + TEST(FieldPath, ScalarVariableWithDottedFieldPathOptimizesToConstantMissingValue) { auto expCtx = ExpressionContextForTest{}; auto varId = expCtx.variablesParseState.defineVariable("userVar"); |