summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--buildscripts/resmokeconfig/suites/replica_sets_multi_stmt_txn_kill_primary_jscore_passthrough.yml3
-rw-r--r--buildscripts/resmokeconfig/suites/replica_sets_multi_stmt_txn_stepdown_jscore_passthrough.yml3
-rw-r--r--buildscripts/resmokeconfig/suites/replica_sets_multi_stmt_txn_terminate_primary_jscore_passthrough.yml4
-rw-r--r--jstests/core/api_version_test_expression.js115
-rw-r--r--src/mongo/db/pipeline/SConscript3
-rw-r--r--src/mongo/db/pipeline/expression.h15
-rw-r--r--src/mongo/db/pipeline/expression_context.cpp2
-rw-r--r--src/mongo/db/pipeline/expression_context.h6
-rw-r--r--src/mongo/db/pipeline/expression_test_api_version.cpp97
-rw-r--r--src/mongo/db/pipeline/expression_test_api_version.h64
-rw-r--r--src/mongo/db/pipeline/expression_test_api_version_test.cpp123
-rw-r--r--src/mongo/db/pipeline/expression_visitor.h2
-rw-r--r--src/mongo/db/query/sbe_stage_builder_expression.cpp5
13 files changed, 442 insertions, 0 deletions
diff --git a/buildscripts/resmokeconfig/suites/replica_sets_multi_stmt_txn_kill_primary_jscore_passthrough.yml b/buildscripts/resmokeconfig/suites/replica_sets_multi_stmt_txn_kill_primary_jscore_passthrough.yml
index 25013723167..fb0535caf22 100644
--- a/buildscripts/resmokeconfig/suites/replica_sets_multi_stmt_txn_kill_primary_jscore_passthrough.yml
+++ b/buildscripts/resmokeconfig/suites/replica_sets_multi_stmt_txn_kill_primary_jscore_passthrough.yml
@@ -330,6 +330,9 @@ selector:
# operation and cluster times aren't shared between shells.
# "Cowardly refusing to run test with network retries enabled when it uses startParallelShell()"
- uses_parallel_shell
+ # Transaction-continuing commands cannot specify API parameters, so tests that use API parameters
+ # cannot be run with transactions.
+ - uses_api_parameters
executor:
archive:
diff --git a/buildscripts/resmokeconfig/suites/replica_sets_multi_stmt_txn_stepdown_jscore_passthrough.yml b/buildscripts/resmokeconfig/suites/replica_sets_multi_stmt_txn_stepdown_jscore_passthrough.yml
index 446dc151877..34f856bfe8d 100644
--- a/buildscripts/resmokeconfig/suites/replica_sets_multi_stmt_txn_stepdown_jscore_passthrough.yml
+++ b/buildscripts/resmokeconfig/suites/replica_sets_multi_stmt_txn_stepdown_jscore_passthrough.yml
@@ -312,6 +312,9 @@ selector:
# operation and cluster times aren't shared between shells.
# "Cowardly refusing to run test with network retries enabled when it uses startParallelShell()"
- uses_parallel_shell
+ # Transaction-continuing commands cannot specify API parameters, so tests that use API parameters
+ # cannot be run with transactions.
+ - uses_api_parameters
executor:
archive:
diff --git a/buildscripts/resmokeconfig/suites/replica_sets_multi_stmt_txn_terminate_primary_jscore_passthrough.yml b/buildscripts/resmokeconfig/suites/replica_sets_multi_stmt_txn_terminate_primary_jscore_passthrough.yml
index 2312c1a1260..815a7ccd853 100644
--- a/buildscripts/resmokeconfig/suites/replica_sets_multi_stmt_txn_terminate_primary_jscore_passthrough.yml
+++ b/buildscripts/resmokeconfig/suites/replica_sets_multi_stmt_txn_terminate_primary_jscore_passthrough.yml
@@ -319,6 +319,10 @@ selector:
# operation and cluster times aren't shared between shells.
# "Cowardly refusing to run test with network retries enabled when it uses startParallelShell()"
- uses_parallel_shell
+ # Transaction-continuing commands cannot specify API parameters, so tests that use API parameters
+ # cannot be run with transactions.
+ - uses_api_parameters
+
executor:
archive:
diff --git a/jstests/core/api_version_test_expression.js b/jstests/core/api_version_test_expression.js
new file mode 100644
index 00000000000..ca3d2b6b231
--- /dev/null
+++ b/jstests/core/api_version_test_expression.js
@@ -0,0 +1,115 @@
+/**
+ * Checks that the $_testApiVersion expression used for API versioning testing
+ * throws errors as expected.
+ *
+ * Tests which create views aren't expected to work when collections are implicitly sharded.
+ * @tags: [requires_fcv_47, uses_api_parameters, assumes_unsharded_collection, sbe_incompatible]
+ */
+
+(function() {
+"use strict";
+
+const collName = "api_version_test_expression";
+const coll = db[collName];
+coll.drop();
+
+for (let i = 0; i < 5; i++) {
+ assert.commandWorked(coll.insert({num: i}));
+}
+
+// Assert error thrown when command specifies {apiStrict: true} and expression specifies {unstable:
+// true}.
+let pipeline = [{$project: {v: {$_testApiVersion: {unstable: true}}}}];
+assert.commandFailedWithCode(
+ db.runCommand(
+ {aggregate: collName, pipeline: pipeline, cursor: {}, apiStrict: true, apiVersion: "1"}),
+ ErrorCodes.APIStrictError);
+
+// Assert error thrown when command specifies {apiDeprecationErrors: true} and expression specifies
+// {deprecated: true}
+pipeline = [{$project: {v: {$_testApiVersion: {deprecated: true}}}}];
+assert.commandFailedWithCode(db.runCommand({
+ aggregate: collName,
+ pipeline: pipeline,
+ cursor: {},
+ apiDeprecationErrors: true,
+ apiVersion: "1"
+}),
+ ErrorCodes.APIDeprecationError);
+
+// Test that command successfully runs to completion without any API parameters.
+pipeline = [{$project: {v: {$_testApiVersion: {unstable: true}}}}];
+assert.commandWorked(db.runCommand({aggregate: collName, pipeline: pipeline, cursor: {}}));
+
+// Create a view with {apiStrict: true}.
+db.view.drop();
+assert.commandWorked(db.runCommand(
+ {create: "view", viewOn: collName, pipeline: [], apiStrict: true, apiVersion: "1"}));
+// This command will work because API parameters are not inherited from views.
+assert.commandWorked(db.runCommand({aggregate: "view", pipeline: pipeline, cursor: {}}));
+assert.commandFailedWithCode(
+ db.runCommand(
+ {aggregate: "view", pipeline: pipeline, cursor: {}, apiVersion: "1", apiStrict: true}),
+ ErrorCodes.APIStrictError);
+
+// Create a view with {unstable: true}.
+db.unstableView.drop();
+assert.commandWorked(db.runCommand({
+ create: "unstableView",
+ viewOn: collName,
+ pipeline: pipeline,
+ apiStrict: true,
+ apiVersion: "1"
+}));
+assert.commandWorked(db.runCommand({aggregate: "unstableView", pipeline: [], cursor: {}}));
+// This commmand will fail even with the empty pipeline because of the view.
+assert.commandFailedWithCode(
+ db.runCommand(
+ {aggregate: "unstableView", pipeline: [], cursor: {}, apiVersion: "1", apiStrict: true}),
+ ErrorCodes.APIStrictError);
+
+// Create a validator containing the unstable test expression.
+let validator = {$expr: {$_testApiVersion: {unstable: true}}};
+let validatedCollName = collName + "_validated";
+
+// Create the collection with the unstable validator, setting apiStrict: true does not have an
+// effect.
+db[validatedCollName].drop();
+assert.commandWorked(db.runCommand(
+ {create: validatedCollName, validator: validator, apiVersion: "1", apiStrict: true}));
+
+// Run an insert command without any API version and verify that it is successful.
+assert.commandWorked(
+ db[validatedCollName].runCommand({insert: validatedCollName, documents: [{num: 1}]}));
+
+// TODO SERVER-53218: Specifying apiStrict: true results in an error.
+assert.commandWorked(db[validatedCollName].runCommand(
+ {insert: validatedCollName, documents: [{num: 1}], apiVersion: "1", apiStrict: true}));
+
+// Recreate the validator containing a deprecated test expression.
+db[validatedCollName].drop();
+validator = {
+ $expr: {$_testApiVersion: {deprecated: true}}
+};
+
+// Create the collection with the unstable validator, setting apiDeprecationErrors : true does not
+// have an effect.
+assert.commandWorked(db.runCommand({
+ create: validatedCollName,
+ validator: validator,
+ apiVersion: "1",
+ apiDeprecationErrors: true,
+}));
+
+// Run an insert command without any API version and verify that it is successful.
+assert.commandWorked(
+ db[validatedCollName].runCommand({insert: validatedCollName, documents: [{num: 1}]}));
+
+// TODO SERVER-53218: Specifying apiDeprecationErrors: true results in an error.
+assert.commandWorked(db[validatedCollName].runCommand({
+ insert: validatedCollName,
+ documents: [{num: 1}],
+ apiVersion: "1",
+ apiDeprecationErrors: true
+}));
+})();
diff --git a/src/mongo/db/pipeline/SConscript b/src/mongo/db/pipeline/SConscript
index 41c30aecb02..9a5adb2ce1f 100644
--- a/src/mongo/db/pipeline/SConscript
+++ b/src/mongo/db/pipeline/SConscript
@@ -70,12 +70,14 @@ env.Library(
'expression_context.cpp',
'expression_function.cpp',
'expression_js_emit.cpp',
+ 'expression_test_api_version.cpp',
'expression_trigonometric.cpp',
'javascript_execution.cpp',
'make_js_function.cpp',
'variables.cpp',
],
LIBDEPS=[
+ '$BUILD_DIR/mongo/db/commands/test_commands_enabled',
'$BUILD_DIR/mongo/db/exec/document_value/document_value',
'$BUILD_DIR/mongo/db/query/collation/collator_factory_interface',
'$BUILD_DIR/mongo/db/query/datetime/date_time_support',
@@ -389,6 +391,7 @@ env.CppUnitTest(
'expression_or_test.cpp',
'expression_replace_test.cpp',
'expression_test.cpp',
+ 'expression_test_api_version_test.cpp',
'expression_trigonometric_test.cpp',
'expression_trim_test.cpp',
'expression_walker_test.cpp',
diff --git a/src/mongo/db/pipeline/expression.h b/src/mongo/db/pipeline/expression.h
index cbcf74dd618..647f7a24527 100644
--- a/src/mongo/db/pipeline/expression.h
+++ b/src/mongo/db/pipeline/expression.h
@@ -41,6 +41,7 @@
#include <vector>
#include "mongo/base/init.h"
+#include "mongo/db/commands/test_commands_enabled.h"
#include "mongo/db/exec/document_value/document.h"
#include "mongo/db/exec/document_value/value.h"
#include "mongo/db/pipeline/dependencies.h"
@@ -91,6 +92,20 @@ class DocumentSource;
return Status::OK(); \
}
+/**
+ * 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, parser) \
+ MONGO_INITIALIZER_GENERAL( \
+ addToExpressionParserMap_##key, ("EndStartupOptionHandling"), ("expressionParserMap")) \
+ (InitializerContext*) { \
+ if (getTestCommandsEnabled()) { \
+ Expression::registerExpression("$" #key, (parser), boost::none); \
+ } \
+ return Status::OK(); \
+ }
+
class Expression : public RefCountable {
public:
using Parser = std::function<boost::intrusive_ptr<Expression>(
diff --git a/src/mongo/db/pipeline/expression_context.cpp b/src/mongo/db/pipeline/expression_context.cpp
index a27d8ca64b1..0ca09f7a27c 100644
--- a/src/mongo/db/pipeline/expression_context.cpp
+++ b/src/mongo/db/pipeline/expression_context.cpp
@@ -125,6 +125,8 @@ ExpressionContext::ExpressionContext(
}
if (letParameters)
variables.seedVariablesWithLetParameters(this, *letParameters);
+ if (opCtx)
+ apiParameters = APIParameters::get(opCtx);
}
ExpressionContext::ExpressionContext(
diff --git a/src/mongo/db/pipeline/expression_context.h b/src/mongo/db/pipeline/expression_context.h
index c6a20a28ab9..721cf6ffe01 100644
--- a/src/mongo/db/pipeline/expression_context.h
+++ b/src/mongo/db/pipeline/expression_context.h
@@ -37,6 +37,7 @@
#include "mongo/base/string_data.h"
#include "mongo/bson/bsonobj.h"
+#include "mongo/db/api_parameters.h"
#include "mongo/db/exec/document_value/document_comparator.h"
#include "mongo/db/exec/document_value/value_comparator.h"
#include "mongo/db/namespace_string.h"
@@ -363,6 +364,11 @@ public:
// construction.
const bool mayDbProfile = true;
+ // API Parameters pulled from OperationContext upon object creation.
+ // This may become stale if OperationContext changes after object creation.
+ // Expressions should reach APIParameters with this variable instead of using the decorator.
+ APIParameters apiParameters;
+
protected:
static const int kInterruptCheckPeriod = 128;
diff --git a/src/mongo/db/pipeline/expression_test_api_version.cpp b/src/mongo/db/pipeline/expression_test_api_version.cpp
new file mode 100644
index 00000000000..c501d8b9292
--- /dev/null
+++ b/src/mongo/db/pipeline/expression_test_api_version.cpp
@@ -0,0 +1,97 @@
+/**
+ * Copyright (C) 2020-present MongoDB, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the Server Side Public License, version 1,
+ * as published by MongoDB, Inc.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * Server Side Public License for more details.
+ *
+ * You should have received a copy of the Server Side Public License
+ * along with this program. If not, see
+ * <http://www.mongodb.com/licensing/server-side-public-license>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the Server Side Public License in all respects for
+ * all of the code used other than as permitted herein. If you modify file(s)
+ * with this exception, you may extend this exception to your version of the
+ * file(s), but you are not obligated to do so. If you do not wish to do so,
+ * delete this exception statement from your version. If you delete this
+ * exception statement from all source files in the program, then also delete
+ * it in the license file.
+ */
+#include "mongo/platform/basic.h"
+
+#include "mongo/db/api_parameters.h"
+#include "mongo/db/pipeline/expression_test_api_version.h"
+
+namespace mongo {
+
+REGISTER_TEST_EXPRESSION(_testApiVersion, ExpressionTestApiVersion::parse);
+
+ExpressionTestApiVersion::ExpressionTestApiVersion(ExpressionContext* const expCtx,
+ bool unstable,
+ bool deprecated)
+ : Expression(expCtx), _unstable(unstable), _deprecated(deprecated) {}
+
+boost::intrusive_ptr<Expression> ExpressionTestApiVersion::parse(ExpressionContext* const expCtx,
+ BSONElement expr,
+ const VariablesParseState& vps) {
+ uassert(5161700,
+ "$_testApiVersion only supports an object as its argument",
+ expr.type() == BSONType::Object);
+
+ const BSONObj params = expr.embeddedObject();
+ uassert(5161701,
+ "$_testApiVersion only accepts an object with a single field.",
+ params.nFields() == 1);
+
+ bool unstableField = false;
+ bool deprecatedField = false;
+
+ auto field = params.firstElementFieldNameStringData();
+ if (field == kUnstableField) {
+ uassert(5161702, "unstable must be a boolean", params.firstElement().isBoolean());
+ unstableField = params.firstElement().boolean();
+ } else if (field == kDeprecatedField) {
+ uassert(5161703, "deprecated must be a boolean", params.firstElement().isBoolean());
+ deprecatedField = params.firstElement().boolean();
+ } else {
+ uasserted(5161704,
+ str::stream() << field << " is not a valid argument for $_testApiVersion");
+ }
+
+ return new ExpressionTestApiVersion(expCtx, unstableField, deprecatedField);
+}
+
+Value ExpressionTestApiVersion::serialize(bool explain) const {
+ return Value(Document{{"$_testApiVersion",
+ Document{{"unstable", _unstable ? Value(_unstable) : Value()},
+ {"deprecated", _deprecated ? Value(_deprecated) : Value()}}}});
+}
+
+Value ExpressionTestApiVersion::evaluate(const Document& root, Variables* variables) const {
+ APIParameters apiParams = getExpressionContext()->apiParameters;
+
+ if (apiParams.getAPIStrict() && _unstable) {
+ uasserted(ErrorCodes::APIStrictError,
+ "Provided apiStrict is true with an unstable command.");
+ }
+
+ if (apiParams.getAPIDeprecationErrors() && _deprecated) {
+ uasserted(ErrorCodes::APIDeprecationError,
+ "Provided apiDeprecatedErrors is true with a deprecated command.");
+ }
+
+ return Value(1);
+}
+
+void ExpressionTestApiVersion::_doAddDependencies(DepsTracker* deps) const {}
+
+} // namespace mongo \ No newline at end of file
diff --git a/src/mongo/db/pipeline/expression_test_api_version.h b/src/mongo/db/pipeline/expression_test_api_version.h
new file mode 100644
index 00000000000..ede9a8378d4
--- /dev/null
+++ b/src/mongo/db/pipeline/expression_test_api_version.h
@@ -0,0 +1,64 @@
+/**
+ * Copyright (C) 2020-present MongoDB, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the Server Side Public License, version 1,
+ * as published by MongoDB, Inc.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * Server Side Public License for more details.
+ *
+ * You should have received a copy of the Server Side Public License
+ * along with this program. If not, see
+ * <http://www.mongodb.com/licensing/server-side-public-license>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the Server Side Public License in all respects for
+ * all of the code used other than as permitted herein. If you modify file(s)
+ * with this exception, you may extend this exception to your version of the
+ * file(s), but you are not obligated to do so. If you do not wish to do so,
+ * delete this exception statement from your version. If you delete this
+ * exception statement from all source files in the program, then also delete
+ * it in the license file.
+ */
+
+#pragma once
+
+#include "mongo/db/pipeline/expression.h"
+
+namespace mongo {
+/**
+ * This expression will be used to validate that versioning code is working as expected.
+ * $_testApiVersion should only take one parameter, either {unstable: true} or {deprecated: true}.
+ * If no error is thrown, this expression will return an integer value.
+ */
+class ExpressionTestApiVersion final : public Expression {
+public:
+ static constexpr auto kUnstableField = "unstable";
+ static constexpr auto kDeprecatedField = "deprecated";
+
+ static boost::intrusive_ptr<Expression> parse(ExpressionContext* const expCtx,
+ BSONElement expr,
+ const VariablesParseState& vps);
+
+ Value evaluate(const Document& root, Variables* variables) const final;
+
+ Value serialize(bool explain) const final;
+
+ void acceptVisitor(ExpressionVisitor* visitor) final {
+ return visitor->visit(this);
+ }
+
+private:
+ ExpressionTestApiVersion(ExpressionContext* const expCtx, bool unstable, bool deprecated);
+ void _doAddDependencies(DepsTracker* deps) const final override;
+
+ bool _unstable;
+ bool _deprecated;
+};
+} // namespace mongo
diff --git a/src/mongo/db/pipeline/expression_test_api_version_test.cpp b/src/mongo/db/pipeline/expression_test_api_version_test.cpp
new file mode 100644
index 00000000000..5dc4b93493e
--- /dev/null
+++ b/src/mongo/db/pipeline/expression_test_api_version_test.cpp
@@ -0,0 +1,123 @@
+/**
+ * Copyright (C) 2020-present MongoDB, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the Server Side Public License, version 1,
+ * as published by MongoDB, Inc.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * Server Side Public License for more details.
+ *
+ * You should have received a copy of the Server Side Public License
+ * along with this program. If not, see
+ * <http://www.mongodb.com/licensing/server-side-public-license>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the Server Side Public License in all respects for
+ * all of the code used other than as permitted herein. If you modify file(s)
+ * with this exception, you may extend this exception to your version of the
+ * file(s), but you are not obligated to do so. If you do not wish to do so,
+ * delete this exception statement from your version. If you delete this
+ * exception statement from all source files in the program, then also delete
+ * it in the license file.
+ */
+
+#include "mongo/db/exec/document_value/document_value_test_util.h"
+#include "mongo/db/pipeline/aggregation_context_fixture.h"
+#include "mongo/db/pipeline/expression_context_for_test.h"
+#include "mongo/platform/basic.h"
+
+#include "mongo/db/pipeline/expression_test_api_version.h"
+#include "mongo/unittest/unittest.h"
+
+namespace mongo {
+namespace {
+
+using TestApiVersion = AggregationContextFixture;
+
+TEST_F(TestApiVersion, UnstableAcceptsBooleanValue) {
+ auto expCtx = getExpCtx();
+ boost::intrusive_ptr<Expression> expression = ExpressionTestApiVersion::parse(
+ expCtx.get(),
+ BSON("$_testApiVersion" << BSON("unstable" << true)).firstElement(),
+ expCtx->variablesParseState);
+
+ ASSERT_VALUE_EQ(Value(DOC("$_testApiVersion" << DOC("unstable" << true))),
+ expression->serialize(false));
+}
+
+TEST_F(TestApiVersion, UnstableDoesNotAcceptNumericValue) {
+ auto expCtx = getExpCtx();
+
+ ASSERT_THROWS_CODE(ExpressionTestApiVersion::parse(
+ expCtx.get(),
+ BSON("$_testApiVersion" << BSON("unstable" << 1)).firstElement(),
+ expCtx->variablesParseState),
+ AssertionException,
+ 5161702);
+}
+
+TEST_F(TestApiVersion, DeprecatedAcceptsBooleanValue) {
+ auto expCtx = getExpCtx();
+ boost::intrusive_ptr<Expression> expression = ExpressionTestApiVersion::parse(
+ expCtx.get(),
+ BSON("$_testApiVersion" << BSON("deprecated" << true)).firstElement(),
+ expCtx->variablesParseState);
+
+ ASSERT_VALUE_EQ(Value(DOC("$_testApiVersion" << DOC("deprecated" << true))),
+ expression->serialize(false));
+}
+
+TEST_F(TestApiVersion, DeprecatedDoesNotAcceptNumericValue) {
+ auto expCtx = getExpCtx();
+
+ ASSERT_THROWS_CODE(ExpressionTestApiVersion::parse(
+ expCtx.get(),
+ BSON("$_testApiVersion" << BSON("deprecated" << 1)).firstElement(),
+ expCtx->variablesParseState),
+ AssertionException,
+ 5161703);
+}
+
+TEST_F(TestApiVersion, DoesNotAcceptInvalidParameter) {
+ auto expCtx = getExpCtx();
+
+ ASSERT_THROWS_CODE(
+ ExpressionTestApiVersion::parse(
+ expCtx.get(),
+ BSON("$_testApiVersion" << BSON("invalidParameter" << true)).firstElement(),
+ expCtx->variablesParseState),
+ AssertionException,
+ 5161704);
+}
+
+TEST_F(TestApiVersion, OnlyTakesOneParameter) {
+ auto expCtx = getExpCtx();
+
+ ASSERT_THROWS_CODE(
+ ExpressionTestApiVersion::parse(
+ expCtx.get(),
+ BSON("$_testApiVersion" << BSON("deprecated" << true << "unstable" << true))
+ .firstElement(),
+ expCtx->variablesParseState),
+ AssertionException,
+ 5161701);
+}
+
+TEST_F(TestApiVersion, DoesNotAcceptEmptyDocument) {
+ auto expCtx = getExpCtx();
+
+ ASSERT_THROWS_CODE(
+ ExpressionTestApiVersion::parse(expCtx.get(),
+ BSON("$_testApiVersion" << BSONObj()).firstElement(),
+ expCtx->variablesParseState),
+ AssertionException,
+ 5161701);
+}
+} // namespace
+} // namespace mongo \ No newline at end of file
diff --git a/src/mongo/db/pipeline/expression_visitor.h b/src/mongo/db/pipeline/expression_visitor.h
index de4b32908e3..51225289b26 100644
--- a/src/mongo/db/pipeline/expression_visitor.h
+++ b/src/mongo/db/pipeline/expression_visitor.h
@@ -117,6 +117,7 @@ class ExpressionBinarySize;
class ExpressionStrLenCP;
class ExpressionSubtract;
class ExpressionSwitch;
+class ExpressionTestApiVersion;
class ExpressionToLower;
class ExpressionToUpper;
class ExpressionTrim;
@@ -185,6 +186,7 @@ public:
virtual void visit(ExpressionAllElementsTrue*) = 0;
virtual void visit(ExpressionAnd*) = 0;
virtual void visit(ExpressionAnyElementTrue*) = 0;
+ virtual void visit(ExpressionTestApiVersion*) = 0;
virtual void visit(ExpressionArray*) = 0;
virtual void visit(ExpressionArrayElemAt*) = 0;
virtual void visit(ExpressionFirst*) = 0;
diff --git a/src/mongo/db/query/sbe_stage_builder_expression.cpp b/src/mongo/db/query/sbe_stage_builder_expression.cpp
index c46454c1935..cd653deb016 100644
--- a/src/mongo/db/query/sbe_stage_builder_expression.cpp
+++ b/src/mongo/db/query/sbe_stage_builder_expression.cpp
@@ -427,6 +427,7 @@ public:
void visit(ExpressionSwitch* expr) final {
_context->evalStack.emplaceFrame(EvalStage{});
}
+ void visit(ExpressionTestApiVersion* expr) final {}
void visit(ExpressionToLower* expr) final {}
void visit(ExpressionToUpper* expr) final {}
void visit(ExpressionTrim* expr) final {}
@@ -622,6 +623,7 @@ public:
void visit(ExpressionSwitch* expr) final {
_context->evalStack.emplaceFrame(EvalStage{});
}
+ void visit(ExpressionTestApiVersion* expr) final {}
void visit(ExpressionToLower* expr) final {}
void visit(ExpressionToUpper* expr) final {}
void visit(ExpressionTrim* expr) final {}
@@ -2324,6 +2326,9 @@ public:
void visit(ExpressionSwitch* expr) final {
visitConditionalExpression(expr);
}
+ void visit(ExpressionTestApiVersion* expr) final {
+ unsupportedExpression("$_testApiVersion");
+ }
void visit(ExpressionToLower* expr) final {
generateStringCaseConversionExpression(_context, "toLower");
}