summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorUladzimir Makouski <uladzimir.makouski@mongodb.com>2021-09-14 05:53:17 +0000
committerEvergreen Agent <no-reply@evergreen.mongodb.com>2021-09-14 06:34:50 +0000
commitc42a89a1f0d5c001f1b2b875cc9659da65ecd9ce (patch)
tree77c93c90d7566ffc04e47360d5a6a2aecbf4285c /src
parentf77b07713c6ca6e10485ca58e40f9ac2fa44d0b2 (diff)
downloadmongo-c42a89a1f0d5c001f1b2b875cc9659da65ecd9ce.tar.gz
Revert "SERVER-58379 Extend REGISTER macros to support API version, FCV, and conditional checks for accumulators, expressions, and window functions"
This reverts commit 15c51de0d4700d490c71ca70f961c3c44c1d4a1c.
Diffstat (limited to 'src')
-rw-r--r--src/mongo/db/pipeline/accumulation_statement.cpp45
-rw-r--r--src/mongo/db/pipeline/accumulation_statement.h71
-rw-r--r--src/mongo/db/pipeline/accumulator_multi.cpp99
-rw-r--r--src/mongo/db/pipeline/document_source_group_test.cpp8
-rw-r--r--src/mongo/db/pipeline/expression.h71
-rw-r--r--src/mongo/db/pipeline/expression_test.cpp112
-rw-r--r--src/mongo/db/pipeline/sharded_union_test.cpp6
-rw-r--r--src/mongo/db/pipeline/window_function/window_function_expression.cpp50
-rw-r--r--src/mongo/db/pipeline/window_function/window_function_expression.h47
9 files changed, 187 insertions, 322 deletions
diff --git a/src/mongo/db/pipeline/accumulation_statement.cpp b/src/mongo/db/pipeline/accumulation_statement.cpp
index d5afb1f83d0..18cca9fd32a 100644
--- a/src/mongo/db/pipeline/accumulation_statement.cpp
+++ b/src/mongo/db/pipeline/accumulation_statement.cpp
@@ -37,7 +37,6 @@
#include "mongo/db/commands/feature_compatibility_version_documentation.h"
#include "mongo/db/exec/document_value/value.h"
#include "mongo/db/pipeline/accumulator.h"
-#include "mongo/db/query/allowed_contexts.h"
#include "mongo/util/assert_util.h"
#include "mongo/util/str.h"
#include "mongo/util/string_map.h"
@@ -47,29 +46,39 @@ namespace mongo {
using std::string;
namespace {
-// Used to keep track of which Accumulators are registered under which name and in which contexts
-// they can be used in.
-static StringMap<AccumulationStatement::ParserRegistration> parserMap;
+// Used to keep track of which Accumulators are registered under which name.
+using ParserRegistration = std::pair<AccumulationStatement::Parser,
+ boost::optional<multiversion::FeatureCompatibilityVersion>>;
+static StringMap<ParserRegistration> parserMap;
} // namespace
void AccumulationStatement::registerAccumulator(
std::string name,
AccumulationStatement::Parser parser,
- AllowedWithApiStrict allowedWithApiStrict,
- AllowedWithClientType allowedWithClientType,
boost::optional<multiversion::FeatureCompatibilityVersion> requiredMinVersion) {
auto it = parserMap.find(name);
massert(28722,
str::stream() << "Duplicate accumulator (" << name << ") registered.",
it == parserMap.end());
- parserMap[name] = {parser, allowedWithApiStrict, allowedWithClientType, requiredMinVersion};
+ parserMap[name] = {parser, requiredMinVersion};
}
-AccumulationStatement::ParserRegistration& AccumulationStatement::getParser(StringData name) {
+AccumulationStatement::Parser& AccumulationStatement::getParser(
+ StringData name, boost::optional<multiversion::FeatureCompatibilityVersion> allowedMaxVersion) {
auto it = parserMap.find(name);
uassert(
15952, str::stream() << "unknown group operator '" << name << "'", it != parserMap.end());
- return it->second;
+ auto& [parser, requiredMinVersion] = it->second;
+ uassert(ErrorCodes::QueryFeatureNotAllowed,
+ // We would like to include the current version and the required minimum version in this
+ // error message, but using FeatureCompatibilityVersion::toString() would introduce a
+ // dependency cycle (see SERVER-31968).
+ str::stream() << name
+ << " is not allowed in the current feature compatibility version. See "
+ << feature_compatibility_version_documentation::kCompatibilityLink
+ << " for more information.",
+ !requiredMinVersion || !allowedMaxVersion || *requiredMinVersion <= *allowedMaxVersion);
+ return parser;
}
boost::intrusive_ptr<AccumulatorState> AccumulationStatement::makeAccumulator() const {
@@ -102,22 +111,8 @@ AccumulationStatement AccumulationStatement::parseAccumulationStatement(
str::stream() << "The " << accName << " accumulator is a unary operator",
specElem.type() != BSONType::Array);
- auto&& [parser, allowedWithApiStrict, allowedWithClientType, requiredMinVersion] =
- AccumulationStatement::getParser(accName);
- auto allowedMaxVersion = expCtx->maxFeatureCompatibilityVersion;
- uassert(ErrorCodes::QueryFeatureNotAllowed,
- // We would like to include the current version and the required minimum version in this
- // error message, but using FeatureCompatibilityVersion::toString() would introduce a
- // dependency cycle (see SERVER-31968).
- str::stream() << accName
- << " is not allowed in the current feature compatibility version. See "
- << feature_compatibility_version_documentation::kCompatibilityLink
- << " for more information.",
- !requiredMinVersion || !allowedMaxVersion || *requiredMinVersion <= *allowedMaxVersion);
-
- tassert(5837900, "Accumulators should only appear in a user operation", expCtx->opCtx);
- assertLanguageFeatureIsAllowed(
- expCtx->opCtx, accName.toString(), allowedWithApiStrict, allowedWithClientType);
+ auto&& parser =
+ AccumulationStatement::getParser(accName, expCtx->maxFeatureCompatibilityVersion);
auto accExpr = parser(expCtx, specElem, vps);
return AccumulationStatement(fieldName.toString(), std::move(accExpr));
diff --git a/src/mongo/db/pipeline/accumulation_statement.h b/src/mongo/db/pipeline/accumulation_statement.h
index b056b3ca2d9..e473d5d7ea1 100644
--- a/src/mongo/db/pipeline/accumulation_statement.h
+++ b/src/mongo/db/pipeline/accumulation_statement.h
@@ -43,45 +43,15 @@ namespace mongo {
* you would add this line:
* REGISTER_ACCUMULATOR(foo, AccumulatorFoo::create);
*/
-#define REGISTER_ACCUMULATOR(key, factory) \
- REGISTER_ACCUMULATOR_CONDITIONALLY(key, \
- factory, \
- AllowedWithApiStrict::kAlways, \
- AllowedWithClientType::kAny, \
- boost::none, \
- true)
-
-#define REGISTER_ACCUMULATOR_WITH_MIN_VERSION(key, factory, minVersion) \
- REGISTER_ACCUMULATOR_CONDITIONALLY(key, \
- factory, \
- AllowedWithApiStrict::kAlways, \
- AllowedWithClientType::kAny, \
- minVersion, \
- true)
-
-/**
- * Like REGISTER_ACCUMULATOR_WITH_MIN_VERSION, except you can also specify a condition,
- * evaluated during startup, that decides whether to register the parser.
- *
- * For example, you could check a feature flag, and register the parser only when it's enabled.
- *
- * Note that the condition is evaluated only once, during a MONGO_INITIALIZER. Don't specify
- * a condition that can change at runtime, such as FCV. (Feature flags are ok, because they
- * cannot be toggled at runtime.)
- *
- * This is the most general REGISTER_ACCUMULATOR* macro, which all others should delegate to.
- */
-#define REGISTER_ACCUMULATOR_CONDITIONALLY( \
- key, factory, allowedWithApiStrict, allowedClientType, minVersion, ...) \
- MONGO_INITIALIZER_GENERAL(addToAccumulatorFactoryMap_##key, \
- ("BeginAccumulatorRegistration"), \
- ("EndAccumulatorRegistration")) \
- (InitializerContext*) { \
- if (!(__VA_ARGS__)) { \
- return; \
- } \
- AccumulationStatement::registerAccumulator( \
- "$" #key, (factory), (allowedWithApiStrict), (allowedClientType), (minVersion)); \
+#define REGISTER_ACCUMULATOR(key, factory) \
+ REGISTER_ACCUMULATOR_WITH_MIN_VERSION(key, factory, boost::none)
+
+#define REGISTER_ACCUMULATOR_WITH_MIN_VERSION(key, factory, minVersion) \
+ MONGO_INITIALIZER_GENERAL(addToAccumulatorFactoryMap_##key, \
+ ("BeginAccumulatorRegistration"), \
+ ("EndAccumulatorRegistration")) \
+ (InitializerContext*) { \
+ AccumulationStatement::registerAccumulator("$" #key, (factory), (minVersion)); \
}
/**
@@ -209,16 +179,6 @@ public:
using Parser = std::function<AccumulationExpression(
ExpressionContext* const, BSONElement, VariablesParseState)>;
- /**
- * Associates a Parser with information regarding which contexts it can be used in, including
- * API Version and FCV.
- */
- using ParserRegistration =
- std::tuple<Parser,
- AllowedWithApiStrict,
- AllowedWithClientType,
- boost::optional<multiversion::FeatureCompatibilityVersion>>;
-
AccumulationStatement(std::string fieldName, AccumulationExpression expr)
: fieldName(std::move(fieldName)), expr(std::move(expr)) {}
@@ -226,9 +186,7 @@ public:
* Parses a BSONElement that is an accumulated field, and returns an AccumulationStatement for
* that accumulated field.
*
- * Throws an AssertionException if parsing fails, if the configured API version is not
- * compatible with this AccumulationStatement, or if the Parser is registered under an FCV
- * greater than the specified maximum allowed FCV.
+ * Throws an AssertionException if parsing fails.
*/
static AccumulationStatement parseAccumulationStatement(ExpressionContext* expCtx,
const BSONElement& elem,
@@ -245,15 +203,16 @@ public:
static void registerAccumulator(
std::string name,
Parser parser,
- AllowedWithApiStrict allowedWithApiStrict,
- AllowedWithClientType allowedWithClientType,
boost::optional<multiversion::FeatureCompatibilityVersion> requiredMinVersion);
/**
* Retrieves the Parser for the accumulator specified by the given name, and raises an error if
- * there is no such Parser registered.
+ * there is no such Parser registered, or the Parser is registered under an FCV greater than the
+ * specified maximum allowed FCV.
*/
- static ParserRegistration& getParser(StringData name);
+ static Parser& getParser(
+ StringData name,
+ boost::optional<multiversion::FeatureCompatibilityVersion> allowedMaxVersion);
// The field name is used to store the results of the accumulation in a result document.
std::string fieldName;
diff --git a/src/mongo/db/pipeline/accumulator_multi.cpp b/src/mongo/db/pipeline/accumulator_multi.cpp
index 912d7c56569..78bd99e740d 100644
--- a/src/mongo/db/pipeline/accumulator_multi.cpp
+++ b/src/mongo/db/pipeline/accumulator_multi.cpp
@@ -34,64 +34,39 @@ namespace mongo {
using FirstLastSense = AccumulatorFirstLastN::Sense;
using MinMaxSense = AccumulatorMinMax::Sense;
-// TODO SERVER-52247 Replace boost::none with 'gFeatureFlagExactTopNAccumulator.getVersion()' below
-// once 'gFeatureFlagExactTopNAccumulator' is set to true by default and is configured with an FCV.
-REGISTER_ACCUMULATOR_CONDITIONALLY(
- maxN,
- AccumulatorMinMaxN::parseMinMaxN<MinMaxSense::kMax>,
- AllowedWithApiStrict::kNeverInVersion1,
- AllowedWithClientType::kAny,
- boost::none,
- feature_flags::gFeatureFlagExactTopNAccumulator.isEnabledAndIgnoreFCV());
-REGISTER_ACCUMULATOR_CONDITIONALLY(
- minN,
- AccumulatorMinMaxN::parseMinMaxN<MinMaxSense::kMin>,
- AllowedWithApiStrict::kNeverInVersion1,
- AllowedWithClientType::kAny,
- boost::none,
- feature_flags::gFeatureFlagExactTopNAccumulator.isEnabledAndIgnoreFCV());
-REGISTER_EXPRESSION_CONDITIONALLY(
- maxN,
- AccumulatorMinMaxN::parseExpression<MinMaxSense::kMax>,
- AllowedWithApiStrict::kNeverInVersion1,
- AllowedWithClientType::kAny,
- boost::none,
- feature_flags::gFeatureFlagExactTopNAccumulator.isEnabledAndIgnoreFCV());
-REGISTER_EXPRESSION_CONDITIONALLY(
- minN,
- AccumulatorMinMaxN::parseExpression<MinMaxSense::kMin>,
- AllowedWithApiStrict::kNeverInVersion1,
- AllowedWithClientType::kAny,
- boost::none,
- feature_flags::gFeatureFlagExactTopNAccumulator.isEnabledAndIgnoreFCV());
-REGISTER_ACCUMULATOR_CONDITIONALLY(
+REGISTER_ACCUMULATOR_WITH_MIN_VERSION(maxN,
+ AccumulatorMinMaxN::parseMinMaxN<MinMaxSense::kMax>,
+ multiversion::FeatureCompatibilityVersion::kVersion_5_1);
+REGISTER_ACCUMULATOR_WITH_MIN_VERSION(minN,
+ AccumulatorMinMaxN::parseMinMaxN<MinMaxSense::kMin>,
+ multiversion::FeatureCompatibilityVersion::kVersion_5_1);
+REGISTER_EXPRESSION_WITH_MIN_VERSION(maxN,
+ AccumulatorMinMaxN::parseExpression<MinMaxSense::kMax>,
+ AllowedWithApiStrict::kNeverInVersion1,
+ AllowedWithClientType::kAny,
+ multiversion::FeatureCompatibilityVersion::kVersion_5_1);
+REGISTER_EXPRESSION_WITH_MIN_VERSION(minN,
+ AccumulatorMinMaxN::parseExpression<MinMaxSense::kMin>,
+ AllowedWithApiStrict::kNeverInVersion1,
+ AllowedWithClientType::kAny,
+ multiversion::FeatureCompatibilityVersion::kVersion_5_1);
+REGISTER_ACCUMULATOR_WITH_MIN_VERSION(
firstN,
AccumulatorFirstLastN::parseFirstLastN<FirstLastSense::kFirst>,
- AllowedWithApiStrict::kNeverInVersion1,
- AllowedWithClientType::kAny,
- boost::none,
- feature_flags::gFeatureFlagExactTopNAccumulator.isEnabledAndIgnoreFCV());
-REGISTER_ACCUMULATOR_CONDITIONALLY(
- lastN,
- AccumulatorFirstLastN::parseFirstLastN<FirstLastSense::kLast>,
- AllowedWithApiStrict::kNeverInVersion1,
- AllowedWithClientType::kAny,
- boost::none,
- feature_flags::gFeatureFlagExactTopNAccumulator.isEnabledAndIgnoreFCV());
-REGISTER_EXPRESSION_CONDITIONALLY(
- firstN,
- AccumulatorFirstLastN::parseExpression<FirstLastSense::kFirst>,
- AllowedWithApiStrict::kNeverInVersion1,
- AllowedWithClientType::kAny,
- boost::none,
- feature_flags::gFeatureFlagExactTopNAccumulator.isEnabledAndIgnoreFCV());
-REGISTER_EXPRESSION_CONDITIONALLY(
- lastN,
- AccumulatorFirstLastN::parseExpression<FirstLastSense::kLast>,
- AllowedWithApiStrict::kNeverInVersion1,
- AllowedWithClientType::kAny,
- boost::none,
- feature_flags::gFeatureFlagExactTopNAccumulator.isEnabledAndIgnoreFCV());
+ multiversion::FeatureCompatibilityVersion::kVersion_5_1);
+REGISTER_ACCUMULATOR_WITH_MIN_VERSION(lastN,
+ AccumulatorFirstLastN::parseFirstLastN<FirstLastSense::kLast>,
+ multiversion::FeatureCompatibilityVersion::kVersion_5_1);
+REGISTER_EXPRESSION_WITH_MIN_VERSION(firstN,
+ AccumulatorFirstLastN::parseExpression<FirstLastSense::kFirst>,
+ AllowedWithApiStrict::kNeverInVersion1,
+ AllowedWithClientType::kAny,
+ multiversion::FeatureCompatibilityVersion::kVersion_5_1);
+REGISTER_EXPRESSION_WITH_MIN_VERSION(lastN,
+ AccumulatorFirstLastN::parseExpression<FirstLastSense::kLast>,
+ AllowedWithApiStrict::kNeverInVersion1,
+ AllowedWithClientType::kAny,
+ multiversion::FeatureCompatibilityVersion::kVersion_5_1);
// TODO SERVER-57884 Add $firstN/$lastN as window functions.
AccumulatorN::AccumulatorN(ExpressionContext* const expCtx)
@@ -207,6 +182,12 @@ AccumulationExpression AccumulatorMinMaxN::parseMinMaxN(ExpressionContext* const
}
}();
+ // TODO SERVER-58379 Remove this uassert once the FCV constants are upgraded and the REGISTER
+ // macros above are updated accordingly.
+ uassert(5787909,
+ str::stream() << "Cannot create " << name << " accumulator if feature flag is disabled",
+ feature_flags::gFeatureFlagExactTopNAccumulator.isEnabled(
+ serverGlobalParams.featureCompatibility));
uassert(5787900,
str::stream() << "specification must be an object; found " << elem,
elem.type() == BSONType::Object);
@@ -298,6 +279,12 @@ AccumulationExpression AccumulatorFirstLastN::parseFirstLastN(ExpressionContext*
}
}();
+ // TODO SERVER-58379 Remove this uassert once the FCV constants are upgraded and the REGISTER
+ // macros above are updated accordingly.
+ uassert(5787800,
+ str::stream() << "Cannot create " << name << " accumulator if feature flag is disabled",
+ feature_flags::gFeatureFlagExactTopNAccumulator.isEnabled(
+ serverGlobalParams.featureCompatibility));
uassert(5787801,
str::stream() << "specification must be an object; found " << elem,
elem.type() == BSONType::Object);
diff --git a/src/mongo/db/pipeline/document_source_group_test.cpp b/src/mongo/db/pipeline/document_source_group_test.cpp
index 9d62e0206a2..de6252449eb 100644
--- a/src/mongo/db/pipeline/document_source_group_test.cpp
+++ b/src/mongo/db/pipeline/document_source_group_test.cpp
@@ -74,7 +74,7 @@ TEST_F(DocumentSourceGroupTest, ShouldBeAbleToPauseLoading) {
auto expCtx = getExpCtx();
expCtx->inMongos = true; // Disallow external sort.
// This is the only way to do this in a debug build.
- auto&& [parser, _1, _2, _3] = AccumulationStatement::getParser("$sum");
+ auto&& parser = AccumulationStatement::getParser("$sum", boost::none);
auto accumulatorArg = BSON("" << 1);
auto accExpr = parser(expCtx.get(), accumulatorArg.firstElement(), expCtx->variablesParseState);
AccumulationStatement countStatement{"count", accExpr};
@@ -111,7 +111,7 @@ TEST_F(DocumentSourceGroupTest, ShouldBeAbleToPauseLoadingWhileSpilled) {
expCtx->allowDiskUse = true;
const size_t maxMemoryUsageBytes = 1000;
- auto&& [parser, _1, _2, _3] = AccumulationStatement::getParser("$push");
+ auto&& parser = AccumulationStatement::getParser("$push", boost::none);
auto accumulatorArg = BSON(""
<< "$largeStr");
auto accExpr = parser(expCtx.get(), accumulatorArg.firstElement(), expCtx->variablesParseState);
@@ -154,7 +154,7 @@ TEST_F(DocumentSourceGroupTest, ShouldErrorIfNotAllowedToSpillToDiskAndResultSet
expCtx->inMongos = true; // Disallow external sort.
// This is the only way to do this in a debug build.
- auto&& [parser, _1, _2, _3] = AccumulationStatement::getParser("$push");
+ auto&& parser = AccumulationStatement::getParser("$push", boost::none);
auto accumulatorArg = BSON(""
<< "$largeStr");
auto accExpr = parser(expCtx.get(), accumulatorArg.firstElement(), expCtx->variablesParseState);
@@ -180,7 +180,7 @@ TEST_F(DocumentSourceGroupTest, ShouldCorrectlyTrackMemoryUsageBetweenPauses) {
expCtx->inMongos = true; // Disallow external sort.
// This is the only way to do this in a debug build.
- auto&& [parser, _1, _2, _3] = AccumulationStatement::getParser("$push");
+ auto&& parser = AccumulationStatement::getParser("$push", boost::none);
auto accumulatorArg = BSON(""
<< "$largeStr");
auto accExpr = parser(expCtx.get(), accumulatorArg.firstElement(), expCtx->variablesParseState);
diff --git a/src/mongo/db/pipeline/expression.h b/src/mongo/db/pipeline/expression.h
index cb68e7cef5b..a0a2d08bdee 100644
--- a/src/mongo/db/pipeline/expression.h
+++ b/src/mongo/db/pipeline/expression.h
@@ -73,37 +73,16 @@ class DocumentSource;
* An expression registered this way can be used in any featureCompatibilityVersion and will be
* considered part of the stable API.
*/
-#define REGISTER_STABLE_EXPRESSION(key, parser) \
- REGISTER_EXPRESSION_CONDITIONALLY(key, \
- parser, \
- AllowedWithApiStrict::kAlways, \
- AllowedWithClientType::kAny, \
- boost::none, \
- true);
-
-/**
- * Like REGISTER_EXPRESSION_WITH_MIN_VERSION, except you can also specify a condition,
- * evaluated during startup, that decides whether to register the parser.
- *
- * For example, you could check a feature flag, and register the parser only when it's enabled.
- *
- * Note that the condition is evaluated only once, during a MONGO_INITIALIZER. Don't specify
- * a condition that can change at runtime, such as FCV. (Feature flags are ok, because they
- * cannot be toggled at runtime.)
- *
- * This is the most general REGISTER_EXPRESSION* macro, which all others should delegate to.
- */
-#define REGISTER_EXPRESSION_CONDITIONALLY( \
- key, parser, allowedWithApiStrict, allowedClientType, minVersion, ...) \
- MONGO_INITIALIZER_GENERAL(addToExpressionParserMap_##key, \
- ("BeginExpressionRegistration"), \
- ("EndExpressionRegistration")) \
- (InitializerContext*) { \
- if (!(__VA_ARGS__)) { \
- return; \
- } \
- Expression::registerExpression( \
- "$" #key, (parser), (allowedWithApiStrict), (allowedClientType), (minVersion)); \
+#define REGISTER_STABLE_EXPRESSION(key, parser) \
+ MONGO_INITIALIZER_GENERAL(addToExpressionParserMap_##key, \
+ ("BeginExpressionRegistration"), \
+ ("EndExpressionRegistration")) \
+ (InitializerContext*) { \
+ Expression::registerExpression("$" #key, \
+ (parser), \
+ AllowedWithApiStrict::kAlways, \
+ AllowedWithClientType::kAny, \
+ boost::none); \
}
/**
@@ -127,22 +106,30 @@ class DocumentSource;
* parser and enforce the 'sometimes' behavior during that invocation. No extra validation will be
* done here.
*/
-#define REGISTER_EXPRESSION_WITH_MIN_VERSION( \
- key, parser, allowedWithApiStrict, allowedClientType, minVersion) \
- REGISTER_EXPRESSION_CONDITIONALLY( \
- key, parser, allowedWithApiStrict, allowedClientType, minVersion, true)
+#define REGISTER_EXPRESSION_WITH_MIN_VERSION( \
+ key, parser, allowedWithApiStrict, allowedClientType, minVersion) \
+ MONGO_INITIALIZER_GENERAL(addToExpressionParserMap_##key, \
+ ("BeginExpressionRegistration"), \
+ ("EndExpressionRegistration")) \
+ (InitializerContext*) { \
+ Expression::registerExpression( \
+ "$" #key, (parser), (allowedWithApiStrict), (allowedClientType), (minVersion)); \
+ }
/**
* Registers a Parser only if test commands are enabled. Use this if your expression is only used
* for testing purposes.
*/
-#define REGISTER_TEST_EXPRESSION(key, allowedWithApiStrict, allowedClientType, parser) \
- REGISTER_EXPRESSION_CONDITIONALLY(key, \
- parser, \
- allowedWithApiStrict, \
- allowedClientType, \
- boost::none, \
- getTestCommandsEnabled());
+#define REGISTER_TEST_EXPRESSION(key, allowedWithApiStrict, allowedClientType, parser) \
+ MONGO_INITIALIZER_GENERAL(addToExpressionParserMap_##key, \
+ ("BeginExpressionRegistration"), \
+ ("EndExpressionRegistration")) \
+ (InitializerContext*) { \
+ if (getTestCommandsEnabled()) { \
+ Expression::registerExpression( \
+ "$" #key, (parser), (allowedWithApiStrict), (allowedClientType), boost::none); \
+ } \
+ }
class Expression : public RefCountable {
public:
diff --git a/src/mongo/db/pipeline/expression_test.cpp b/src/mongo/db/pipeline/expression_test.cpp
index e579eece98c..6dd4c4b9974 100644
--- a/src/mongo/db/pipeline/expression_test.cpp
+++ b/src/mongo/db/pipeline/expression_test.cpp
@@ -40,7 +40,6 @@
#include "mongo/db/jsobj.h"
#include "mongo/db/json.h"
#include "mongo/db/pipeline/accumulator.h"
-#include "mongo/db/pipeline/accumulator_multi.h"
#include "mongo/db/pipeline/expression.h"
#include "mongo/db/pipeline/expression_context_for_test.h"
#include "mongo/db/query/collation/collator_interface_mock.h"
@@ -155,22 +154,6 @@ void assertExpectedArray(const BSONObj& spec, const BSONArray& expected) {
ASSERT_VALUE_EQ(result, Value(expected));
};
-/**
- * Given 'parseFn', parses and evaluates 'spec' and verifies that the result is equal to
- * 'expected'. Useful when the parser for an expression is unavailable in certain contexts (for
- * instance, when evaluating an expression that's guarded by a feature flag that's off by default).
- */
-void parseAndVerifyResults(
- const std::function<boost::intrusive_ptr<Expression>(
- ExpressionContext* const, BSONElement, const VariablesParseState&)>& parseFn,
- const BSONElement& elem,
- Value expected) {
- auto expCtx = ExpressionContextForTest{};
- VariablesParseState vps = expCtx.variablesParseState;
- auto expr = parseFn(&expCtx, elem, vps);
- ASSERT_VALUE_EQ(expr->evaluate({}, &expCtx.variables), expected);
-}
-
/* ------------------------- ExpressionArrayToObject -------------------------- */
TEST(ExpressionArrayToObjectTest, KVFormatSimple) {
@@ -748,38 +731,31 @@ TEST(ExpressionFromAccumulators, Avg) {
}
TEST(ExpressionFromAccumulators, FirstNLastN) {
- using Sense = AccumulatorFirstLastN::Sense;
+ RAIIServerParameterControllerForTest controller("featureFlagExactTopNAccumulator", true);
// $firstN
- auto firstNFn = [&](const BSONObj& spec, const BSONArray& expected) {
- parseAndVerifyResults(AccumulatorFirstLastN::parseExpression<Sense::kFirst>,
- spec.firstElement(),
- Value(expected));
- };
- firstNFn(fromjson("{$firstN: {n: 3, output: [19, 7, 28, 3, 5]}}"),
- BSONArray(fromjson("[19, 7, 28]")));
- firstNFn(fromjson("{$firstN: {n: 6, output: [19, 7, 28, 3, 5]}}"),
- BSONArray(fromjson("[19, 7, 28, 3, 5]")));
- firstNFn(fromjson("{$firstN: {n: 3, output: [1,2,3,4,5,6]}}"), BSONArray(fromjson("[1,2,3]")));
- firstNFn(fromjson("{$firstN: {n: 3, output: [1,2,null,null]}}"),
- BSONArray(fromjson("[1,2,null]")));
- firstNFn(fromjson("{$firstN: {n: 3, output: [1.1, 2.713, 3, 3.4]}}"),
- BSONArray(fromjson("[1.1, 2.713, 3]")));
+ assertExpectedArray(fromjson("{$firstN: {n: 3, output: [19, 7, 28, 3, 5]}}"),
+ BSONArray(fromjson("[19, 7, 28]")));
+ assertExpectedArray(fromjson("{$firstN: {n: 6, output: [19, 7, 28, 3, 5]}}"),
+ BSONArray(fromjson("[19, 7, 28, 3, 5]")));
+ assertExpectedArray(fromjson("{$firstN: {n: 3, output: [1,2,3,4,5,6]}}"),
+ BSONArray(fromjson("[1,2,3]")));
+ assertExpectedArray(fromjson("{$firstN: {n: 3, output: [1,2,null,null]}}"),
+ BSONArray(fromjson("[1,2,null]")));
+ assertExpectedArray(fromjson("{$firstN: {n: 3, output: [1.1, 2.713, 3, 3.4]}}"),
+ BSONArray(fromjson("[1.1, 2.713, 3]")));
// $lastN
- auto lastNFn = [&](const BSONObj& spec, const BSONArray& expected) {
- parseAndVerifyResults(AccumulatorFirstLastN::parseExpression<Sense::kLast>,
- spec.firstElement(),
- Value(expected));
- };
- lastNFn(fromjson("{$lastN: {n: 3, output: [19, 7, 28, 3, 5]}}"),
- BSONArray(fromjson("[28,3,5]")));
- lastNFn(fromjson("{$lastN: {n: 6, output: [19, 7, 28, 3, 5]}}"),
- BSONArray(fromjson("[19, 7, 28, 3, 5]")));
- lastNFn(fromjson("{$lastN: {n: 3, output: [3,2,1,4,5,6]}}"), BSONArray(fromjson("[4,5,6]")));
- lastNFn(fromjson("{$lastN: {n: 3, output: [1,2,null,3]}}"), BSONArray(fromjson("[2,null,3]")));
- lastNFn(fromjson("{$lastN: {n: 3, output: [3, 2.713, 1.1, 2.7]}}"),
- BSONArray(fromjson("[2.713, 1.1, 2.7]")));
+ assertExpectedArray(fromjson("{$lastN: {n: 3, output: [19, 7, 28, 3, 5]}}"),
+ BSONArray(fromjson("[28,3,5]")));
+ assertExpectedArray(fromjson("{$lastN: {n: 6, output: [19, 7, 28, 3, 5]}}"),
+ BSONArray(fromjson("[19, 7, 28, 3, 5]")));
+ assertExpectedArray(fromjson("{$lastN: {n: 3, output: [3,2,1,4,5,6]}}"),
+ BSONArray(fromjson("[4,5,6]")));
+ assertExpectedArray(fromjson("{$lastN: {n: 3, output: [1,2,null,3]}}"),
+ BSONArray(fromjson("[2,null,3]")));
+ assertExpectedArray(fromjson("{$lastN: {n: 3, output: [3, 2.713, 1.1, 2.7]}}"),
+ BSONArray(fromjson("[2.713, 1.1, 2.7]")));
}
TEST(ExpressionFromAccumulators, Max) {
@@ -806,35 +782,31 @@ TEST(ExpressionFromAccumulators, Min) {
}
TEST(ExpressionFromAccumulators, MinNMaxN) {
- using Sense = AccumulatorMinMax::Sense;
- auto maxNFn = [&](const BSONObj& spec, const BSONArray& expected) {
- parseAndVerifyResults(
- AccumulatorMinMaxN::parseExpression<Sense::kMax>, spec.firstElement(), Value(expected));
- };
+ RAIIServerParameterControllerForTest controller("featureFlagExactTopNAccumulator", true);
// $maxN
- maxNFn(fromjson("{$maxN: {n: 3, output: [19, 7, 28, 3, 5]}}"),
- BSONArray(fromjson("[28, 19, 7]")));
- maxNFn(fromjson("{$maxN: {n: 6, output: [19, 7, 28, 3, 5]}}"),
- BSONArray(fromjson("[28, 19, 7, 5, 3]")));
- maxNFn(fromjson("{$maxN: {n: 3, output: [1,2,3]}}"), BSONArray(fromjson("[3,2,1]")));
- maxNFn(fromjson("{$maxN: {n: 3, output: [1,2,null]}}"), BSONArray(fromjson("[2,1]")));
- maxNFn(fromjson("{$maxN: {n: 3, output: [1.1, 2.713, 3]}}"),
- BSONArray(fromjson("[3, 2.713, 1.1]")));
-
- auto minNFn = [&](const BSONObj& spec, const BSONArray& expected) {
- parseAndVerifyResults(
- AccumulatorMinMaxN::parseExpression<Sense::kMin>, spec.firstElement(), Value(expected));
- };
+ assertExpectedArray(fromjson("{$maxN: {n: 3, output: [19, 7, 28, 3, 5]}}"),
+ BSONArray(fromjson("[28, 19, 7]")));
+ assertExpectedArray(fromjson("{$maxN: {n: 6, output: [19, 7, 28, 3, 5]}}"),
+ BSONArray(fromjson("[28, 19, 7, 5, 3]")));
+ assertExpectedArray(fromjson("{$maxN: {n: 3, output: [1,2,3]}}"),
+ BSONArray(fromjson("[3,2,1]")));
+ assertExpectedArray(fromjson("{$maxN: {n: 3, output: [1,2,null]}}"),
+ BSONArray(fromjson("[2,1]")));
+ assertExpectedArray(fromjson("{$maxN: {n: 3, output: [1.1, 2.713, 3]}}"),
+ BSONArray(fromjson("[3, 2.713, 1.1]")));
// $minN
- minNFn(fromjson("{$minN: {n: 3, output: [19, 7, 28, 3, 5]}}"), BSONArray(fromjson("[3,5,7]")));
- minNFn(fromjson("{$minN: {n: 6, output: [19, 7, 28, 3, 5]}}"),
- BSONArray(fromjson("[3,5,7,19, 28]")));
- minNFn(fromjson("{$minN: {n: 3, output: [3,2,1]}}"), BSONArray(fromjson("[1,2,3]")));
- minNFn(fromjson("{$minN: {n: 3, output: [1,2,null]}}"), BSONArray(fromjson("[1,2]")));
- minNFn(fromjson("{$minN: {n: 3, output: [3, 2.713, 1.1]}}"),
- BSONArray(fromjson("[1.1, 2.713, 3]")));
+ assertExpectedArray(fromjson("{$minN: {n: 3, output: [19, 7, 28, 3, 5]}}"),
+ BSONArray(fromjson("[3,5,7]")));
+ assertExpectedArray(fromjson("{$minN: {n: 6, output: [19, 7, 28, 3, 5]}}"),
+ BSONArray(fromjson("[3,5,7,19,28]")));
+ assertExpectedArray(fromjson("{$minN: {n: 3, output: [3,2,1]}}"),
+ BSONArray(fromjson("[1,2,3]")));
+ assertExpectedArray(fromjson("{$minN: {n: 3, output: [1,2,null]}}"),
+ BSONArray(fromjson("[1,2]")));
+ assertExpectedArray(fromjson("{$minN: {n: 3, output: [3, 2.713, 1.1]}}"),
+ BSONArray(fromjson("[1.1, 2.713, 3]")));
}
TEST(ExpressionFromAccumulators, Sum) {
diff --git a/src/mongo/db/pipeline/sharded_union_test.cpp b/src/mongo/db/pipeline/sharded_union_test.cpp
index 395990e5309..e9748aa0a57 100644
--- a/src/mongo/db/pipeline/sharded_union_test.cpp
+++ b/src/mongo/db/pipeline/sharded_union_test.cpp
@@ -205,7 +205,7 @@ TEST_F(ShardedUnionTest, CorrectlySplitsSubPipelineIfRefreshedDistributionRequir
auto shards = setupNShards(2);
const auto cm = loadRoutingTableWithTwoChunksAndTwoShards(kTestAggregateNss);
- auto&& [parser, _1, _2, _3] = AccumulationStatement::getParser("$sum");
+ auto&& parser = AccumulationStatement::getParser("$sum", boost::none);
auto accumulatorArg = BSON("" << 1);
auto sumStatement =
parser(expCtx().get(), accumulatorArg.firstElement(), expCtx()->variablesParseState);
@@ -293,7 +293,7 @@ TEST_F(ShardedUnionTest, AvoidsSplittingSubPipelineIfRefreshedDistributionDoesNo
auto shards = setupNShards(2);
const auto cm = loadRoutingTableWithTwoChunksAndTwoShards(kTestAggregateNss);
- auto&& [parser, _1, _2, _3] = AccumulationStatement::getParser("$sum");
+ auto&& parser = AccumulationStatement::getParser("$sum", boost::none);
auto accumulatorArg = BSON("" << 1);
auto sumStatement =
parser(expCtx().get(), accumulatorArg.firstElement(), expCtx()->variablesParseState);
@@ -428,7 +428,7 @@ TEST_F(ShardedUnionTest, ForwardsReadConcernToRemotes) {
setupNShards(2);
loadRoutingTableWithTwoChunksAndTwoShards(kTestAggregateNss);
- auto&& [parser, _1, _2, _3] = AccumulationStatement::getParser("$sum");
+ auto&& parser = AccumulationStatement::getParser("$sum", boost::none);
auto accumulatorArg = BSON("" << 1);
auto sumExpression =
parser(expCtx().get(), accumulatorArg.firstElement(), expCtx()->variablesParseState);
diff --git a/src/mongo/db/pipeline/window_function/window_function_expression.cpp b/src/mongo/db/pipeline/window_function/window_function_expression.cpp
index 729d47aad0d..118db84f280 100644
--- a/src/mongo/db/pipeline/window_function/window_function_expression.cpp
+++ b/src/mongo/db/pipeline/window_function/window_function_expression.cpp
@@ -29,7 +29,6 @@
#include "mongo/platform/basic.h"
-#include "mongo/db/commands/feature_compatibility_version_documentation.h"
#include "mongo/db/pipeline/accumulation_statement.h"
#include "mongo/db/pipeline/document_source_add_fields.h"
#include "mongo/db/pipeline/document_source_project.h"
@@ -54,45 +53,26 @@ using MinMaxSense = AccumulatorMinMax::Sense;
REGISTER_WINDOW_FUNCTION(derivative, ExpressionDerivative::parse);
REGISTER_WINDOW_FUNCTION(first, ExpressionFirst::parse);
REGISTER_WINDOW_FUNCTION(last, ExpressionLast::parse);
+REGISTER_WINDOW_FUNCTION(minN, ExpressionMinMaxN<MinMaxSense::kMin>::parse);
+REGISTER_WINDOW_FUNCTION(maxN, ExpressionMinMaxN<MinMaxSense::kMax>::parse);
-// TODO SERVER-52247 Replace boost::none with 'gFeatureFlagExactTopNAccumulator.getVersion()' below
-// once 'gFeatureFlagExactTopNAccumulator' is set to true by default and is configured with an FCV.
-REGISTER_WINDOW_FUNCTION_CONDITIONALLY(
- minN,
- ExpressionMinMaxN<MinMaxSense::kMin>::parse,
- boost::none,
- feature_flags::gFeatureFlagExactTopNAccumulator.isEnabledAndIgnoreFCV());
-REGISTER_WINDOW_FUNCTION_CONDITIONALLY(
- maxN,
- ExpressionMinMaxN<MinMaxSense::kMax>::parse,
- boost::none,
- feature_flags::gFeatureFlagExactTopNAccumulator.isEnabledAndIgnoreFCV());
-
-StringMap<Expression::ExpressionParserRegistration> Expression::parserMap;
+StringMap<Expression::Parser> Expression::parserMap;
intrusive_ptr<Expression> Expression::parse(BSONObj obj,
const optional<SortPattern>& sortBy,
ExpressionContext* expCtx) {
+
for (const auto& field : obj) {
// Check if window function is $-prefixed.
auto fieldName = field.fieldNameStringData();
if (fieldName.startsWith("$"_sd)) {
- auto exprName = field.fieldNameStringData();
- if (auto parserFCV = parserMap.find(exprName); parserFCV != parserMap.end()) {
+
+ if (auto parser = parserMap.find(field.fieldNameStringData());
+ parser != parserMap.end()) {
// Found one valid window function. If there are multiple window functions they will
// be caught as invalid arguments to the Expression parser later.
- const auto& parser = parserFCV->second.first;
- auto fcv = parserFCV->second.second;
- uassert(ErrorCodes::QueryFeatureNotAllowed,
- str::stream()
- << exprName
- << " is not allowed in the current feature compatibility version. See "
- << feature_compatibility_version_documentation::kCompatibilityLink
- << " for more information.",
- !expCtx->maxFeatureCompatibilityVersion || !fcv ||
- (*fcv <= *expCtx->maxFeatureCompatibilityVersion));
- return parser(obj, sortBy, expCtx);
+ return parser->second(obj, sortBy, expCtx);
}
// The window function provided in the window function expression is invalid.
@@ -119,13 +99,9 @@ intrusive_ptr<Expression> Expression::parse(BSONObj obj,
: ", "s + obj.firstElementFieldNameStringData()));
}
-void Expression::registerParser(
- std::string functionName,
- Parser parser,
- boost::optional<multiversion::FeatureCompatibilityVersion> requiredMinVersion) {
+void Expression::registerParser(std::string functionName, Parser parser) {
invariant(parserMap.find(functionName) == parserMap.end());
- ExpressionParserRegistration r(parser, requiredMinVersion);
- parserMap.emplace(std::move(functionName), std::move(r));
+ parserMap.emplace(std::move(functionName), std::move(parser));
}
@@ -253,6 +229,12 @@ boost::intrusive_ptr<Expression> ExpressionMinMaxN<S>::parse(
return AccumulatorMaxN::getName();
}
}();
+ uassert(5788502,
+ str::stream() << "Cannot create " << name
+ << " accumulator in $setWindowFields"
+ " if feature flag is disabled",
+ feature_flags::gFeatureFlagExactTopNAccumulator.isEnabled(
+ serverGlobalParams.featureCompatibility));
boost::intrusive_ptr<::mongo::Expression> nExpr;
boost::intrusive_ptr<::mongo::Expression> outputExpr;
diff --git a/src/mongo/db/pipeline/window_function/window_function_expression.h b/src/mongo/db/pipeline/window_function/window_function_expression.h
index ab2675f1dbd..4cbbd88914e 100644
--- a/src/mongo/db/pipeline/window_function/window_function_expression.h
+++ b/src/mongo/db/pipeline/window_function/window_function_expression.h
@@ -46,33 +46,21 @@ class WindowFunctionExec;
class PartitionIterator;
} // namespace mongo
-#define REGISTER_WINDOW_FUNCTION(name, parser) \
- REGISTER_WINDOW_FUNCTION_CONDITIONALLY(name, parser, boost::none, true)
-
-#define REGISTER_WINDOW_FUNCTION_WITH_MIN_VERSION(name, parser, minVersion) \
- REGISTER_WINDOW_FUNCTION_CONDITIONALLY(name, parser, minVersion, true)
-
-#define REGISTER_WINDOW_FUNCTION_CONDITIONALLY(name, parser, minVersion, ...) \
- MONGO_INITIALIZER_GENERAL(addToWindowFunctionMap_##name, \
- ("BeginWindowFunctionRegistration"), \
- ("EndWindowFunctionRegistration")) \
- (InitializerContext*) { \
- if (!(__VA_ARGS__)) { \
- return; \
- } \
- ::mongo::window_function::Expression::registerParser("$" #name, parser, minVersion); \
+#define REGISTER_WINDOW_FUNCTION(name, parser) \
+ MONGO_INITIALIZER_GENERAL(addToWindowFunctionMap_##name, \
+ ("BeginWindowFunctionRegistration"), \
+ ("EndWindowFunctionRegistration")) \
+ (InitializerContext*) { \
+ ::mongo::window_function::Expression::registerParser("$" #name, parser); \
}
-
-#define REGISTER_REMOVABLE_WINDOW_FUNCTION(name, accumClass, wfClass) \
- MONGO_INITIALIZER_GENERAL(addToWindowFunctionMap_##name, \
- ("BeginWindowFunctionRegistration"), \
- ("EndWindowFunctionRegistration")) \
- (InitializerContext*) { \
- ::mongo::window_function::Expression::registerParser( \
- "$" #name, \
- ::mongo::window_function::ExpressionRemovable<accumClass, wfClass>::parse, \
- boost::none); \
+#define REGISTER_REMOVABLE_WINDOW_FUNCTION(name, accumClass, wfClass) \
+ MONGO_INITIALIZER_GENERAL(addToWindowFunctionMap_##name, \
+ ("BeginWindowFunctionRegistration"), \
+ ("EndWindowFunctionRegistration")) \
+ (InitializerContext*) { \
+ ::mongo::window_function::Expression::registerParser( \
+ "$" #name, ::mongo::window_function::ExpressionRemovable<accumClass, wfClass>::parse); \
}
@@ -119,12 +107,7 @@ public:
* described above, because some parsers need to switch on the function name.
*/
using Parser = std::function<decltype(parse)>;
- using ExpressionParserRegistration =
- std::pair<Parser, boost::optional<multiversion::FeatureCompatibilityVersion>>;
- static void registerParser(
- std::string functionName,
- Parser parser,
- boost::optional<multiversion::FeatureCompatibilityVersion> requiredMinVersion);
+ static void registerParser(std::string functionName, Parser parser);
/**
* Is this a function that the parser knows about?
@@ -198,7 +181,7 @@ protected:
* In these cases, this field is ignored.
*/
WindowBounds _bounds;
- static StringMap<ExpressionParserRegistration> parserMap;
+ static StringMap<Parser> parserMap;
};
template <typename NonRemovableType>