summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/mongo/db/pipeline/expression.cpp24
-rw-r--r--src/mongo/db/pipeline/expression.h14
-rw-r--r--src/mongo/db/query/query_feature_flags.idl3
-rw-r--r--src/mongo/db/update/rename_node_test.cpp3
-rw-r--r--src/mongo/db/update/set_node_test.cpp1
-rw-r--r--src/mongo/db/update/unset_node_test.cpp50
-rw-r--r--src/mongo/dbtests/updatetests.cpp14
7 files changed, 78 insertions, 31 deletions
diff --git a/src/mongo/db/pipeline/expression.cpp b/src/mongo/db/pipeline/expression.cpp
index 729eb11a51a..ffc53961780 100644
--- a/src/mongo/db/pipeline/expression.cpp
+++ b/src/mongo/db/pipeline/expression.cpp
@@ -7226,9 +7226,11 @@ void ExpressionDateTrunc::_doAddDependencies(DepsTracker* deps) const {
}
/* -------------------------- ExpressionGetField ------------------------------ */
-REGISTER_FEATURE_FLAG_GUARDED_EXPRESSION(getField,
- ExpressionGetField::parse,
- feature_flags::gFeatureFlagDotsAndDollars);
+REGISTER_FEATURE_FLAG_GUARDED_EXPRESSION_WITH_MIN_VERSION(
+ getField,
+ ExpressionGetField::parse,
+ feature_flags::gFeatureFlagDotsAndDollars,
+ ServerGlobalParams::FeatureCompatibility::Version::kVersion50);
intrusive_ptr<Expression> ExpressionGetField::parse(ExpressionContext* const expCtx,
BSONElement expr,
@@ -7335,14 +7337,18 @@ Value ExpressionGetField::serialize(const bool explain) const {
}
/* -------------------------- ExpressionSetField ------------------------------ */
-REGISTER_FEATURE_FLAG_GUARDED_EXPRESSION(setField,
- ExpressionSetField::parse,
- feature_flags::gFeatureFlagDotsAndDollars);
+REGISTER_FEATURE_FLAG_GUARDED_EXPRESSION_WITH_MIN_VERSION(
+ setField,
+ ExpressionSetField::parse,
+ feature_flags::gFeatureFlagDotsAndDollars,
+ ServerGlobalParams::FeatureCompatibility::Version::kVersion50);
// $unsetField is syntactic sugar for $setField where value is set to $$REMOVE.
-REGISTER_FEATURE_FLAG_GUARDED_EXPRESSION(unsetField,
- ExpressionSetField::parse,
- feature_flags::gFeatureFlagDotsAndDollars);
+REGISTER_FEATURE_FLAG_GUARDED_EXPRESSION_WITH_MIN_VERSION(
+ unsetField,
+ ExpressionSetField::parse,
+ feature_flags::gFeatureFlagDotsAndDollars,
+ ServerGlobalParams::FeatureCompatibility::Version::kVersion50);
intrusive_ptr<Expression> ExpressionSetField::parse(ExpressionContext* const expCtx,
BSONElement expr,
diff --git a/src/mongo/db/pipeline/expression.h b/src/mongo/db/pipeline/expression.h
index b8d88b0a3c4..0438e2d0eb1 100644
--- a/src/mongo/db/pipeline/expression.h
+++ b/src/mongo/db/pipeline/expression.h
@@ -80,20 +80,20 @@ class DocumentSource;
/**
* Registers a Parser so it can be called from parseExpression and friends (but only if
- * 'featureFlag' is enabled).
+ * 'featureFlag' is enabled) in a feature compatibility version >= X.
*
* As an example, if your expression looks like {"$foo": [1,2,3]} and should be flag-guarded by
- * feature_flags::gFoo, you would add this line:
- * REGISTER_FEATURE_FLAG_GUARDED_EXPRESSION(foo, ExpressionFoo::parse, feature_flags::gFoo);
- *
- * An expression registered this way can be used in any featureCompatibilityVersion.
+ * feature_flags::gFoo and version >= X, you would add this line:
+ * REGISTER_FEATURE_FLAG_GUARDED_EXPRESSION_WITH_MIN_VERSION(
+ * foo, ExpressionFoo::parse, feature_flags::gFoo, X);
*/
-#define REGISTER_FEATURE_FLAG_GUARDED_EXPRESSION(key, parser, featureFlag) \
+#define REGISTER_FEATURE_FLAG_GUARDED_EXPRESSION_WITH_MIN_VERSION( \
+ key, parser, featureFlag, minVersion) \
MONGO_INITIALIZER_GENERAL( \
addToExpressionParserMap_##key, ("default"), ("expressionParserMap")) \
(InitializerContext*) { \
if (featureFlag.isEnabledAndIgnoreFCV()) { \
- Expression::registerExpression("$" #key, (parser), boost::none); \
+ Expression::registerExpression("$" #key, (parser), (minVersion)); \
} \
}
diff --git a/src/mongo/db/query/query_feature_flags.idl b/src/mongo/db/query/query_feature_flags.idl
index 9cc3b882254..2ee336c68e4 100644
--- a/src/mongo/db/query/query_feature_flags.idl
+++ b/src/mongo/db/query/query_feature_flags.idl
@@ -52,4 +52,5 @@ feature_flags:
featureFlagDotsAndDollars:
description: "Feature flag for allowing use of field names containing dots and dollars"
cpp_varname: gFeatureFlagDotsAndDollars
- default: false
+ default: true
+ version: 5.0
diff --git a/src/mongo/db/update/rename_node_test.cpp b/src/mongo/db/update/rename_node_test.cpp
index 1ac12e6254d..d4bf8c85e6b 100644
--- a/src/mongo/db/update/rename_node_test.cpp
+++ b/src/mongo/db/update/rename_node_test.cpp
@@ -36,6 +36,7 @@
#include "mongo/db/json.h"
#include "mongo/db/pipeline/expression_context_for_test.h"
#include "mongo/db/update/update_node_test_fixture.h"
+#include "mongo/idl/server_parameter_test_util.h"
#include "mongo/unittest/death_test.h"
#include "mongo/unittest/unittest.h"
@@ -473,6 +474,7 @@ TEST_F(RenameNodeTest, RenameFromNonExistentPathIsNoOp) {
}
TEST_F(RenameNodeTest, ApplyCannotRemoveRequiredPartOfDBRef) {
+ RAIIServerParameterControllerForTest controller("featureFlagDotsAndDollars", false);
auto update = fromjson("{$rename: {'a.$id': 'b'}}");
boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
RenameNode node;
@@ -579,6 +581,7 @@ TEST_F(RenameNodeTest, ApplyCanRemoveImmutablePathIfNoop) {
}
TEST_F(RenameNodeTest, ApplyCannotCreateDollarPrefixedField) {
+ RAIIServerParameterControllerForTest controller("featureFlagDotsAndDollars", false);
auto update = fromjson("{$rename: {a: '$bad'}}");
boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
RenameNode node;
diff --git a/src/mongo/db/update/set_node_test.cpp b/src/mongo/db/update/set_node_test.cpp
index e1b7598e07b..030a85086b9 100644
--- a/src/mongo/db/update/set_node_test.cpp
+++ b/src/mongo/db/update/set_node_test.cpp
@@ -1053,6 +1053,7 @@ TEST_F(SetNodeTest, ApplyCannotCreateDollarPrefixedFieldInsideSetElement) {
}
TEST_F(SetNodeTest, ApplyCannotCreateDollarPrefixedFieldAtStartOfPath) {
+ RAIIServerParameterControllerForTest controller("featureFlagDotsAndDollars", false);
auto update = fromjson("{$set: {'$bad.a': 1}}");
boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
SetNode node;
diff --git a/src/mongo/db/update/unset_node_test.cpp b/src/mongo/db/update/unset_node_test.cpp
index d45b2472a90..4595183ef0d 100644
--- a/src/mongo/db/update/unset_node_test.cpp
+++ b/src/mongo/db/update/unset_node_test.cpp
@@ -36,6 +36,7 @@
#include "mongo/db/json.h"
#include "mongo/db/pipeline/expression_context_for_test.h"
#include "mongo/db/update/update_node_test_fixture.h"
+#include "mongo/idl/server_parameter_test_util.h"
#include "mongo/unittest/death_test.h"
#include "mongo/unittest/unittest.h"
@@ -369,18 +370,43 @@ TEST_F(UnsetNodeTest, ApplyFieldWithDot) {
}
TEST_F(UnsetNodeTest, ApplyCannotRemoveRequiredPartOfDBRef) {
- auto update = fromjson("{$unset: {'a.$id': true}}");
- boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
- UnsetNode node;
- ASSERT_OK(node.init(update["$unset"]["a.$id"], expCtx));
-
- mutablebson::Document doc(fromjson("{a: {$ref: 'c', $id: 0}}"));
- setPathTaken(makeRuntimeUpdatePathForTest("a.$id"));
- ASSERT_THROWS_CODE_AND_WHAT(
- node.apply(getApplyParams(doc.root()["a"]["$id"]), getUpdateNodeApplyParams()),
- AssertionException,
- ErrorCodes::InvalidDBRef,
- "The DBRef $ref field must be followed by a $id field");
+ {
+ RAIIServerParameterControllerForTest controller("featureFlagDotsAndDollars", false);
+ auto update = fromjson("{$unset: {'a.$id': true}}");
+ boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
+ UnsetNode node;
+ ASSERT_OK(node.init(update["$unset"]["a.$id"], expCtx));
+
+ mutablebson::Document doc(fromjson("{a: {$ref: 'c', $id: 0}}"));
+ setPathTaken(makeRuntimeUpdatePathForTest("a.$id"));
+ ASSERT_THROWS_CODE_AND_WHAT(
+ node.apply(getApplyParams(doc.root()["a"]["$id"]), getUpdateNodeApplyParams()),
+ AssertionException,
+ ErrorCodes::InvalidDBRef,
+ "The DBRef $ref field must be followed by a $id field");
+ }
+ {
+ RAIIServerParameterControllerForTest controller("featureFlagDotsAndDollars", true);
+ auto update = fromjson("{$unset: {'a.$id': true}}");
+ boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
+ UnsetNode node;
+ ASSERT_OK(node.init(update["$unset"]["a.$id"], expCtx));
+
+ mutablebson::Document doc(fromjson("{a: {$ref: 'c', $id: 0}}"));
+ setPathTaken(makeRuntimeUpdatePathForTest("a.$id"));
+ auto result =
+ node.apply(getApplyParams(doc.root()["a"]["$id"]), getUpdateNodeApplyParams());
+ ASSERT_FALSE(result.noop);
+ ASSERT_FALSE(result.indexesAffected);
+ auto updated = BSON("a" << BSON("$ref"
+ << "c"));
+ ASSERT_EQUALS(updated, doc);
+ ASSERT_FALSE(doc.isInPlaceModeEnabled());
+
+ assertOplogEntry(fromjson("{$unset: {'a.$id': true}}"),
+ fromjson("{$v: 2, diff: {sa: {d: {$id: false}}}}"));
+ ASSERT_EQUALS(getModifiedPaths(), "{a.$id}");
+ }
}
TEST_F(UnsetNodeTest, ApplyCanRemoveRequiredPartOfDBRefIfValidateForStorageIsFalse) {
diff --git a/src/mongo/dbtests/updatetests.cpp b/src/mongo/dbtests/updatetests.cpp
index f37dc9adb9b..78ad81498d8 100644
--- a/src/mongo/dbtests/updatetests.cpp
+++ b/src/mongo/dbtests/updatetests.cpp
@@ -44,6 +44,7 @@
#include "mongo/db/lasterror.h"
#include "mongo/db/ops/update.h"
#include "mongo/dbtests/dbtests.h"
+#include "mongo/idl/server_parameter_test_util.h"
namespace UpdateTests {
@@ -1740,8 +1741,17 @@ public:
class CheckNoMods : public SetBase {
public:
void run() {
- _client.update(ns(), BSONObj(), BSON("i" << 5 << "$set" << BSON("q" << 3)), true);
- ASSERT(error());
+ {
+ RAIIServerParameterControllerForTest controller("featureFlagDotsAndDollars", false);
+ _client.update(ns(), BSONObj(), BSON("i" << 5 << "$set" << BSON("q" << 3)), true);
+ ASSERT(error());
+ }
+ {
+ RAIIServerParameterControllerForTest controller("featureFlagDotsAndDollars", true);
+ _client.update(ns(), BSONObj(), BSON("_id" << 52307 << "$set" << BSON("q" << 3)), true);
+ ASSERT_BSONOBJ_EQ(fromjson("{'_id':52307,$set:{q:3}}"),
+ _client.findOne(ns(), Query(BSON("_id" << 52307))));
+ }
}
};