summaryrefslogtreecommitdiff
path: root/src/mongo
diff options
context:
space:
mode:
authorNick Zolnierz <nicholas.zolnierz@mongodb.com>2017-10-17 17:41:26 -0400
committerNick Zolnierz <nicholas.zolnierz@mongodb.com>2017-10-23 16:12:40 -0400
commitec7af3523d4aa5130c56a05d76169755d9b5a611 (patch)
tree82c73c32e697eaebe00faca69ab62f573bdc283c /src/mongo
parent6475f004ad0db96d907e996dd24bad9a2228d3f6 (diff)
downloadmongo-ec7af3523d4aa5130c56a05d76169755d9b5a611.tar.gz
SERVER-17846: Forbid $isolated outside of update/delete user operations
Diffstat (limited to 'src/mongo')
-rw-r--r--src/mongo/db/catalog/SConscript2
-rw-r--r--src/mongo/db/catalog/database_impl.cpp2
-rw-r--r--src/mongo/db/catalog/index_catalog_entry_impl.cpp11
-rw-r--r--src/mongo/db/catalog/index_catalog_impl.cpp8
-rw-r--r--src/mongo/db/catalog/index_key_validate.cpp30
-rw-r--r--src/mongo/db/catalog/index_key_validate.h1
-rw-r--r--src/mongo/db/catalog/index_spec_validate_test.cpp171
-rw-r--r--src/mongo/db/commands/create_indexes.cpp5
-rw-r--r--src/mongo/db/commands/dbcommands.cpp2
-rw-r--r--src/mongo/db/commands/find_cmd.cpp6
-rw-r--r--src/mongo/db/commands/geo_near_cmd.cpp3
-rw-r--r--src/mongo/db/commands/mr.cpp18
-rw-r--r--src/mongo/db/commands/plan_cache_commands_test.cpp19
-rw-r--r--src/mongo/db/dbhelpers.cpp3
-rw-r--r--src/mongo/db/matcher/expression_parser.cpp7
-rw-r--r--src/mongo/db/matcher/expression_parser.h1
-rw-r--r--src/mongo/db/matcher/expression_parser_test.cpp55
-rw-r--r--src/mongo/db/matcher/schema/expression_parser_schema_test.cpp6
-rw-r--r--src/mongo/db/query/canonical_query_test.cpp25
-rw-r--r--src/mongo/db/query/find.cpp3
-rw-r--r--src/mongo/db/query/get_executor.cpp6
-rw-r--r--src/mongo/db/query/parsed_distinct.cpp3
-rw-r--r--src/mongo/db/system_index.cpp14
23 files changed, 310 insertions, 91 deletions
diff --git a/src/mongo/db/catalog/SConscript b/src/mongo/db/catalog/SConscript
index 6480a091972..8a92c492aa6 100644
--- a/src/mongo/db/catalog/SConscript
+++ b/src/mongo/db/catalog/SConscript
@@ -63,6 +63,7 @@ env.Library(
'$BUILD_DIR/mongo/db/common',
'$BUILD_DIR/mongo/db/index/index_descriptor',
'$BUILD_DIR/mongo/db/index_names',
+ '$BUILD_DIR/mongo/db/matcher/expressions',
'$BUILD_DIR/mongo/db/query/collation/collator_factory_interface',
'$BUILD_DIR/mongo/util/fail_point',
],
@@ -76,6 +77,7 @@ env.CppUnitTest(
],
LIBDEPS=[
'$BUILD_DIR/mongo/db/query/query_test_service_context',
+ '$BUILD_DIR/mongo/db/matcher/expressions',
'index_key_validate',
],
)
diff --git a/src/mongo/db/catalog/database_impl.cpp b/src/mongo/db/catalog/database_impl.cpp
index 47ee32c4158..1ccf0f50b6b 100644
--- a/src/mongo/db/catalog/database_impl.cpp
+++ b/src/mongo/db/catalog/database_impl.cpp
@@ -1046,7 +1046,7 @@ auto mongo::userCreateNSImpl(OperationContext* opCtx,
// enough, so we rewrite it here.
return {ErrorCodes::QueryFeatureNotAllowed,
str::stream() << "The featureCompatibilityVersion must be 3.6 to create a "
- "collection validator using 3.6 query featuress. See "
+ "collection validator using 3.6 query features. See "
<< feature_compatibility_version::kDochubLink
<< "."};
} else {
diff --git a/src/mongo/db/catalog/index_catalog_entry_impl.cpp b/src/mongo/db/catalog/index_catalog_entry_impl.cpp
index afa073f7ce0..fdaee922b71 100644
--- a/src/mongo/db/catalog/index_catalog_entry_impl.cpp
+++ b/src/mongo/db/catalog/index_catalog_entry_impl.cpp
@@ -129,9 +129,16 @@ IndexCatalogEntryImpl::IndexCatalogEntryImpl(IndexCatalogEntry* const this_,
BSONObj filter = filterElement.Obj();
boost::intrusive_ptr<ExpressionContext> expCtx(
new ExpressionContext(opCtx, _collator.get()));
+
+ // Parsing the partial filter expression is not expected to fail here since the
+ // expression would have been successfully parsed upstream during index creation. However,
+ // filters that were allowed in partial filter expressions prior to 3.6 may be present in
+ // the index catalog and must also successfully parse.
StatusWithMatchExpression statusWithMatcher =
- MatchExpressionParser::parse(filter, std::move(expCtx));
- // this should be checked in create, so can blow up here
+ MatchExpressionParser::parse(filter,
+ std::move(expCtx),
+ ExtensionsCallbackNoop(),
+ MatchExpressionParser::AllowedFeatures::kIsolated);
invariantOK(statusWithMatcher.getStatus());
_filterExpression = std::move(statusWithMatcher.getValue());
LOG(2) << "have filter expression for " << _ns << " " << _descriptor->indexName() << " "
diff --git a/src/mongo/db/catalog/index_catalog_impl.cpp b/src/mongo/db/catalog/index_catalog_impl.cpp
index 578291a06e9..071a134963a 100644
--- a/src/mongo/db/catalog/index_catalog_impl.cpp
+++ b/src/mongo/db/catalog/index_catalog_impl.cpp
@@ -667,11 +667,17 @@ Status IndexCatalogImpl::_isSpecOk(OperationContext* opCtx, const BSONObj& spec)
// The collator must outlive the constructed MatchExpression.
boost::intrusive_ptr<ExpressionContext> expCtx(
new ExpressionContext(opCtx, collator.get()));
+
+ // Parsing the partial filter expression is not expected to fail here since the
+ // expression would have been successfully parsed upstream during index creation. However,
+ // filters that were allowed in partial filter expressions prior to 3.6 may be present in
+ // the index catalog and must also successfully parse (e.g., partial index filters with the
+ // $isolated/$atomic option).
StatusWithMatchExpression statusWithMatcher =
MatchExpressionParser::parse(filterElement.Obj(),
std::move(expCtx),
ExtensionsCallbackNoop(),
- MatchExpressionParser::kBanAllSpecialFeatures);
+ MatchExpressionParser::kIsolated);
if (!statusWithMatcher.isOK()) {
return statusWithMatcher.getStatus();
}
diff --git a/src/mongo/db/catalog/index_key_validate.cpp b/src/mongo/db/catalog/index_key_validate.cpp
index 0a8c30263b5..ca5ac2aaea5 100644
--- a/src/mongo/db/catalog/index_key_validate.cpp
+++ b/src/mongo/db/catalog/index_key_validate.cpp
@@ -41,6 +41,7 @@
#include "mongo/db/index/index_descriptor.h"
#include "mongo/db/index_names.h"
#include "mongo/db/jsobj.h"
+#include "mongo/db/matcher/expression_parser.h"
#include "mongo/db/namespace_string.h"
#include "mongo/db/query/collation/collator_factory_interface.h"
#include "mongo/db/service_context.h"
@@ -209,6 +210,7 @@ Status validateKeyPattern(const BSONObj& key, IndexDescriptor::IndexVersion inde
}
StatusWith<BSONObj> validateIndexSpec(
+ OperationContext* opCtx,
const BSONObj& indexSpec,
const NamespaceString& expectedNamespace,
const ServerGlobalParams::FeatureCompatibility& featureCompatibility) {
@@ -333,6 +335,34 @@ StatusWith<BSONObj> validateIndexSpec(
}
hasCollationField = true;
+ } else if (IndexDescriptor::kPartialFilterExprFieldName == indexSpecElemFieldName) {
+ if (indexSpecElem.type() != BSONType::Object) {
+ return {ErrorCodes::TypeMismatch,
+ str::stream() << "The field '"
+ << IndexDescriptor::kPartialFilterExprFieldName
+ << "' must be an object, but got "
+ << typeName(indexSpecElem.type())};
+ }
+
+ // Just use the simple collator, even though the index may have a separate collation
+ // specified or may inherit the default collation from the collection. It's legal to
+ // parse with the wrong collation, since the collation can be set on a MatchExpression
+ // after the fact. Here, we don't bother checking the collation after the fact, since
+ // this invocation of the parser is just for validity checking.
+ auto simpleCollator = nullptr;
+ boost::intrusive_ptr<ExpressionContext> expCtx(
+ new ExpressionContext(opCtx, simpleCollator));
+
+ // Special match expression features (e.g. $jsonSchema, $expr, ...) are not allowed in
+ // a partialFilterExpression on index creation.
+ auto statusWithMatcher =
+ MatchExpressionParser::parse(indexSpecElem.Obj(),
+ std::move(expCtx),
+ ExtensionsCallbackNoop(),
+ MatchExpressionParser::kBanAllSpecialFeatures);
+ if (!statusWithMatcher.isOK()) {
+ return statusWithMatcher.getStatus();
+ }
} else {
// We can assume field name is valid at this point. Validation of fieldname is handled
// prior to this in validateIndexSpecFieldNames().
diff --git a/src/mongo/db/catalog/index_key_validate.h b/src/mongo/db/catalog/index_key_validate.h
index 9ee07df7dc5..d8d1b544041 100644
--- a/src/mongo/db/catalog/index_key_validate.h
+++ b/src/mongo/db/catalog/index_key_validate.h
@@ -53,6 +53,7 @@ Status validateKeyPattern(const BSONObj& key, IndexDescriptor::IndexVersion inde
* status is returned.
*/
StatusWith<BSONObj> validateIndexSpec(
+ OperationContext* opCtx,
const BSONObj& indexSpec,
const NamespaceString& expectedNamespace,
const ServerGlobalParams::FeatureCompatibility& featureCompatibility);
diff --git a/src/mongo/db/catalog/index_spec_validate_test.cpp b/src/mongo/db/catalog/index_spec_validate_test.cpp
index ca9179df5c4..3b8634c68ef 100644
--- a/src/mongo/db/catalog/index_spec_validate_test.cpp
+++ b/src/mongo/db/catalog/index_spec_validate_test.cpp
@@ -50,6 +50,7 @@ using index_key_validate::validateIdIndexSpec;
using index_key_validate::validateIndexSpecCollation;
const NamespaceString kTestNamespace("test", "index_spec_validate");
+constexpr OperationContext* kDefaultOpCtx = nullptr;
/**
* Helper function used to return the fields of a BSONObj in a consistent order.
@@ -69,19 +70,22 @@ TEST(IndexSpecValidateTest, ReturnsAnErrorIfKeyPatternIsNotAnObject) {
featureCompatibility.validateFeaturesAsMaster.store(true);
ASSERT_EQ(ErrorCodes::TypeMismatch,
- validateIndexSpec(BSON("key" << 1 << "name"
+ validateIndexSpec(kDefaultOpCtx,
+ BSON("key" << 1 << "name"
<< "indexName"),
kTestNamespace,
featureCompatibility));
ASSERT_EQ(ErrorCodes::TypeMismatch,
- validateIndexSpec(BSON("key"
+ validateIndexSpec(kDefaultOpCtx,
+ BSON("key"
<< "not an object"
<< "name"
<< "indexName"),
kTestNamespace,
featureCompatibility));
ASSERT_EQ(ErrorCodes::TypeMismatch,
- validateIndexSpec(BSON("key" << BSONArray() << "name"
+ validateIndexSpec(kDefaultOpCtx,
+ BSON("key" << BSONArray() << "name"
<< "indexName"),
kTestNamespace,
featureCompatibility));
@@ -93,12 +97,14 @@ TEST(IndexSpecValidateTest, ReturnsAnErrorIfFieldRepeatedInKeyPattern) {
featureCompatibility.validateFeaturesAsMaster.store(true);
ASSERT_EQ(ErrorCodes::BadValue,
- validateIndexSpec(BSON("key" << BSON("field" << 1 << "field" << 1) << "name"
+ validateIndexSpec(kDefaultOpCtx,
+ BSON("key" << BSON("field" << 1 << "field" << 1) << "name"
<< "indexName"),
kTestNamespace,
featureCompatibility));
ASSERT_EQ(ErrorCodes::BadValue,
- validateIndexSpec(BSON("key" << BSON("field" << 1 << "otherField" << -1 << "field"
+ validateIndexSpec(kDefaultOpCtx,
+ BSON("key" << BSON("field" << 1 << "otherField" << -1 << "field"
<< "2dsphere")
<< "name"
<< "indexName"),
@@ -112,7 +118,8 @@ TEST(IndexSpecValidateTest, ReturnsAnErrorIfKeyPatternIsNotPresent) {
featureCompatibility.validateFeaturesAsMaster.store(true);
ASSERT_EQ(ErrorCodes::FailedToParse,
- validateIndexSpec(BSON("name"
+ validateIndexSpec(kDefaultOpCtx,
+ BSON("name"
<< "indexName"),
kTestNamespace,
featureCompatibility));
@@ -124,7 +131,8 @@ TEST(IndexSpecValidateTest, ReturnsAnErrorIfNameIsNotAString) {
featureCompatibility.validateFeaturesAsMaster.store(true);
ASSERT_EQ(ErrorCodes::TypeMismatch,
- validateIndexSpec(BSON("key" << BSON("field" << 1) << "name" << 1),
+ validateIndexSpec(kDefaultOpCtx,
+ BSON("key" << BSON("field" << 1) << "name" << 1),
kTestNamespace,
featureCompatibility));
}
@@ -134,9 +142,11 @@ TEST(IndexSpecValidateTest, ReturnsAnErrorIfNameIsNotPresent) {
featureCompatibility.setVersion(ServerGlobalParams::FeatureCompatibility::Version::k36);
featureCompatibility.validateFeaturesAsMaster.store(true);
- ASSERT_EQ(
- ErrorCodes::FailedToParse,
- validateIndexSpec(BSON("key" << BSON("field" << 1)), kTestNamespace, featureCompatibility));
+ ASSERT_EQ(ErrorCodes::FailedToParse,
+ validateIndexSpec(kDefaultOpCtx,
+ BSON("key" << BSON("field" << 1)),
+ kTestNamespace,
+ featureCompatibility));
}
TEST(IndexSpecValidateTest, ReturnsAnErrorIfNamespaceIsNotAString) {
@@ -145,14 +155,16 @@ TEST(IndexSpecValidateTest, ReturnsAnErrorIfNamespaceIsNotAString) {
featureCompatibility.validateFeaturesAsMaster.store(true);
ASSERT_EQ(ErrorCodes::TypeMismatch,
- validateIndexSpec(BSON("key" << BSON("field" << 1) << "name"
+ validateIndexSpec(kDefaultOpCtx,
+ BSON("key" << BSON("field" << 1) << "name"
<< "indexName"
<< "ns"
<< 1),
kTestNamespace,
featureCompatibility));
ASSERT_EQ(ErrorCodes::TypeMismatch,
- validateIndexSpec(BSON("key" << BSON("field" << 1) << "name"
+ validateIndexSpec(kDefaultOpCtx,
+ BSON("key" << BSON("field" << 1) << "name"
<< "indexName"
<< "ns"
<< BSONObj()),
@@ -166,7 +178,8 @@ TEST(IndexSpecValidateTest, ReturnsAnErrorIfNamespaceIsEmptyString) {
featureCompatibility.validateFeaturesAsMaster.store(true);
ASSERT_EQ(ErrorCodes::BadValue,
- validateIndexSpec(BSON("key" << BSON("field" << 1) << "name"
+ validateIndexSpec(kDefaultOpCtx,
+ BSON("key" << BSON("field" << 1) << "name"
<< "indexName"
<< "ns"
<< ""),
@@ -180,7 +193,8 @@ TEST(IndexSpecValidateTest, ReturnsAnErrorIfNamespaceDoesNotMatch) {
featureCompatibility.validateFeaturesAsMaster.store(true);
ASSERT_EQ(ErrorCodes::BadValue,
- validateIndexSpec(BSON("key" << BSON("field" << 1) << "name"
+ validateIndexSpec(kDefaultOpCtx,
+ BSON("key" << BSON("field" << 1) << "name"
<< "indexName"
<< "ns"
<< "some string"),
@@ -190,7 +204,8 @@ TEST(IndexSpecValidateTest, ReturnsAnErrorIfNamespaceDoesNotMatch) {
// Verify that we reject the index specification when the "ns" field only contains the
// collection name.
ASSERT_EQ(ErrorCodes::BadValue,
- validateIndexSpec(BSON("key" << BSON("field" << 1) << "name"
+ validateIndexSpec(kDefaultOpCtx,
+ BSON("key" << BSON("field" << 1) << "name"
<< "indexName"
<< "ns"
<< kTestNamespace.coll()),
@@ -203,7 +218,8 @@ TEST(IndexSpecValidateTest, ReturnsIndexSpecWithNamespaceFilledInIfItIsNotPresen
featureCompatibility.setVersion(ServerGlobalParams::FeatureCompatibility::Version::k36);
featureCompatibility.validateFeaturesAsMaster.store(true);
- auto result = validateIndexSpec(BSON("key" << BSON("field" << 1) << "name"
+ auto result = validateIndexSpec(kDefaultOpCtx,
+ BSON("key" << BSON("field" << 1) << "name"
<< "indexName"
<< "v"
<< 1),
@@ -221,7 +237,8 @@ TEST(IndexSpecValidateTest, ReturnsIndexSpecWithNamespaceFilledInIfItIsNotPresen
sorted(result.getValue()));
// Verify that the index specification we returned is still considered valid.
- ASSERT_OK(validateIndexSpec(result.getValue(), kTestNamespace, featureCompatibility));
+ ASSERT_OK(
+ validateIndexSpec(kDefaultOpCtx, result.getValue(), kTestNamespace, featureCompatibility));
}
TEST(IndexSpecValidateTest, ReturnsIndexSpecUnchangedIfNamespaceAndVersionArePresent) {
@@ -229,7 +246,8 @@ TEST(IndexSpecValidateTest, ReturnsIndexSpecUnchangedIfNamespaceAndVersionArePre
featureCompatibility.setVersion(ServerGlobalParams::FeatureCompatibility::Version::k36);
featureCompatibility.validateFeaturesAsMaster.store(true);
- auto result = validateIndexSpec(BSON("key" << BSON("field" << 1) << "name"
+ auto result = validateIndexSpec(kDefaultOpCtx,
+ BSON("key" << BSON("field" << 1) << "name"
<< "indexName"
<< "ns"
<< kTestNamespace.ns()
@@ -255,14 +273,16 @@ TEST(IndexSpecValidateTest, ReturnsAnErrorIfVersionIsNotANumber) {
featureCompatibility.validateFeaturesAsMaster.store(true);
ASSERT_EQ(ErrorCodes::TypeMismatch,
- validateIndexSpec(BSON("key" << BSON("field" << 1) << "name"
+ validateIndexSpec(kDefaultOpCtx,
+ BSON("key" << BSON("field" << 1) << "name"
<< "indexName"
<< "v"
<< "not a number"),
kTestNamespace,
featureCompatibility));
ASSERT_EQ(ErrorCodes::TypeMismatch,
- validateIndexSpec(BSON("key" << BSON("field" << 1) << "name"
+ validateIndexSpec(kDefaultOpCtx,
+ BSON("key" << BSON("field" << 1) << "name"
<< "indexName"
<< "v"
<< BSONObj()),
@@ -276,28 +296,32 @@ TEST(IndexSpecValidateTest, ReturnsAnErrorIfVersionIsNotRepresentableAsInt) {
featureCompatibility.validateFeaturesAsMaster.store(true);
ASSERT_EQ(ErrorCodes::BadValue,
- validateIndexSpec(BSON("key" << BSON("field" << 1) << "name"
+ validateIndexSpec(kDefaultOpCtx,
+ BSON("key" << BSON("field" << 1) << "name"
<< "indexName"
<< "v"
<< 2.2),
kTestNamespace,
featureCompatibility));
ASSERT_EQ(ErrorCodes::BadValue,
- validateIndexSpec(BSON("key" << BSON("field" << 1) << "name"
+ validateIndexSpec(kDefaultOpCtx,
+ BSON("key" << BSON("field" << 1) << "name"
<< "indexName"
<< "v"
<< std::nan("1")),
kTestNamespace,
featureCompatibility));
ASSERT_EQ(ErrorCodes::BadValue,
- validateIndexSpec(BSON("key" << BSON("field" << 1) << "name"
+ validateIndexSpec(kDefaultOpCtx,
+ BSON("key" << BSON("field" << 1) << "name"
<< "indexName"
<< "v"
<< std::numeric_limits<double>::infinity()),
kTestNamespace,
featureCompatibility));
ASSERT_EQ(ErrorCodes::BadValue,
- validateIndexSpec(BSON("key" << BSON("field" << 1) << "name"
+ validateIndexSpec(kDefaultOpCtx,
+ BSON("key" << BSON("field" << 1) << "name"
<< "indexName"
<< "v"
<< std::numeric_limits<long long>::max()),
@@ -311,7 +335,8 @@ TEST(IndexSpecValidateTest, ReturnsAnErrorIfVersionIsV0) {
featureCompatibility.validateFeaturesAsMaster.store(true);
ASSERT_EQ(ErrorCodes::CannotCreateIndex,
- validateIndexSpec(BSON("key" << BSON("field" << 1) << "name"
+ validateIndexSpec(kDefaultOpCtx,
+ BSON("key" << BSON("field" << 1) << "name"
<< "indexName"
<< "v"
<< 0),
@@ -325,7 +350,8 @@ TEST(IndexSpecValidateTest, ReturnsAnErrorIfVersionIsUnsupported) {
featureCompatibility.validateFeaturesAsMaster.store(true);
ASSERT_EQ(ErrorCodes::CannotCreateIndex,
- validateIndexSpec(BSON("key" << BSON("field" << 1) << "name"
+ validateIndexSpec(kDefaultOpCtx,
+ BSON("key" << BSON("field" << 1) << "name"
<< "indexName"
<< "v"
<< 3
@@ -336,7 +362,8 @@ TEST(IndexSpecValidateTest, ReturnsAnErrorIfVersionIsUnsupported) {
featureCompatibility));
ASSERT_EQ(ErrorCodes::CannotCreateIndex,
- validateIndexSpec(BSON("key" << BSON("field" << 1) << "name"
+ validateIndexSpec(kDefaultOpCtx,
+ BSON("key" << BSON("field" << 1) << "name"
<< "indexName"
<< "v"
<< -3LL),
@@ -349,7 +376,8 @@ TEST(IndexSpecValidateTest, AcceptsIndexVersionsThatAreAllowedForCreation) {
featureCompatibility.setVersion(ServerGlobalParams::FeatureCompatibility::Version::k36);
featureCompatibility.validateFeaturesAsMaster.store(true);
- auto result = validateIndexSpec(BSON("key" << BSON("field" << 1) << "name"
+ auto result = validateIndexSpec(kDefaultOpCtx,
+ BSON("key" << BSON("field" << 1) << "name"
<< "indexName"
<< "v"
<< 1),
@@ -366,7 +394,8 @@ TEST(IndexSpecValidateTest, AcceptsIndexVersionsThatAreAllowedForCreation) {
<< 1)),
sorted(result.getValue()));
- result = validateIndexSpec(BSON("key" << BSON("field" << 1) << "name"
+ result = validateIndexSpec(kDefaultOpCtx,
+ BSON("key" << BSON("field" << 1) << "name"
<< "indexName"
<< "v"
<< 2LL),
@@ -389,7 +418,8 @@ TEST(IndexSpecValidateTest, DefaultIndexVersionIsV2) {
featureCompatibility.setVersion(ServerGlobalParams::FeatureCompatibility::Version::k36);
featureCompatibility.validateFeaturesAsMaster.store(true);
- auto result = validateIndexSpec(BSON("key" << BSON("field" << 1) << "name"
+ auto result = validateIndexSpec(kDefaultOpCtx,
+ BSON("key" << BSON("field" << 1) << "name"
<< "indexName"
<< "ns"
<< kTestNamespace.ns()),
@@ -407,7 +437,8 @@ TEST(IndexSpecValidateTest, DefaultIndexVersionIsV2) {
sorted(result.getValue()));
// Verify that the index specification we returned is still considered valid.
- ASSERT_OK(validateIndexSpec(result.getValue(), kTestNamespace, featureCompatibility));
+ ASSERT_OK(
+ validateIndexSpec(kDefaultOpCtx, result.getValue(), kTestNamespace, featureCompatibility));
}
TEST(IndexSpecValidateTest, AcceptsIndexVersionV1) {
@@ -415,7 +446,8 @@ TEST(IndexSpecValidateTest, AcceptsIndexVersionV1) {
featureCompatibility.setVersion(ServerGlobalParams::FeatureCompatibility::Version::k36);
featureCompatibility.validateFeaturesAsMaster.store(true);
- auto result = validateIndexSpec(BSON("key" << BSON("field" << 1) << "name"
+ auto result = validateIndexSpec(kDefaultOpCtx,
+ BSON("key" << BSON("field" << 1) << "name"
<< "indexName"
<< "v"
<< 1),
@@ -439,21 +471,24 @@ TEST(IndexSpecValidateTest, ReturnsAnErrorIfCollationIsNotAnObject) {
featureCompatibility.validateFeaturesAsMaster.store(true);
ASSERT_EQ(ErrorCodes::TypeMismatch,
- validateIndexSpec(BSON("key" << BSON("field" << 1) << "name"
+ validateIndexSpec(kDefaultOpCtx,
+ BSON("key" << BSON("field" << 1) << "name"
<< "indexName"
<< "collation"
<< 1),
kTestNamespace,
featureCompatibility));
ASSERT_EQ(ErrorCodes::TypeMismatch,
- validateIndexSpec(BSON("key" << BSON("field" << 1) << "name"
+ validateIndexSpec(kDefaultOpCtx,
+ BSON("key" << BSON("field" << 1) << "name"
<< "indexName"
<< "collation"
<< "not an object"),
kTestNamespace,
featureCompatibility));
ASSERT_EQ(ErrorCodes::TypeMismatch,
- validateIndexSpec(BSON("key" << BSON("field" << 1) << "name"
+ validateIndexSpec(kDefaultOpCtx,
+ BSON("key" << BSON("field" << 1) << "name"
<< "indexName"
<< "collation"
<< BSONArray()),
@@ -467,7 +502,8 @@ TEST(IndexSpecValidateTest, ReturnsAnErrorIfCollationIsEmpty) {
featureCompatibility.validateFeaturesAsMaster.store(true);
ASSERT_EQ(ErrorCodes::BadValue,
- validateIndexSpec(BSON("key" << BSON("field" << 1) << "name"
+ validateIndexSpec(kDefaultOpCtx,
+ BSON("key" << BSON("field" << 1) << "name"
<< "indexName"
<< "collation"
<< BSONObj()),
@@ -481,7 +517,8 @@ TEST(IndexSpecValidateTest, ReturnsAnErrorIfCollationIsPresentAndVersionIsLessTh
featureCompatibility.validateFeaturesAsMaster.store(true);
ASSERT_EQ(ErrorCodes::CannotCreateIndex,
- validateIndexSpec(BSON("key" << BSON("field" << 1) << "name"
+ validateIndexSpec(kDefaultOpCtx,
+ BSON("key" << BSON("field" << 1) << "name"
<< "indexName"
<< "collation"
<< BSON("locale"
@@ -497,7 +534,8 @@ TEST(IndexSpecValidateTest, AcceptsAnyNonEmptyObjectValueForCollation) {
featureCompatibility.setVersion(ServerGlobalParams::FeatureCompatibility::Version::k36);
featureCompatibility.validateFeaturesAsMaster.store(true);
- auto result = validateIndexSpec(BSON("key" << BSON("field" << 1) << "name"
+ auto result = validateIndexSpec(kDefaultOpCtx,
+ BSON("key" << BSON("field" << 1) << "name"
<< "indexName"
<< "v"
<< 2
@@ -520,7 +558,8 @@ TEST(IndexSpecValidateTest, AcceptsAnyNonEmptyObjectValueForCollation) {
<< "simple"))),
sorted(result.getValue()));
- result = validateIndexSpec(BSON("key" << BSON("field" << 1) << "name"
+ result = validateIndexSpec(kDefaultOpCtx,
+ BSON("key" << BSON("field" << 1) << "name"
<< "indexName"
<< "v"
<< 2
@@ -547,7 +586,8 @@ TEST(IndexSpecValidateTest, AcceptsIndexSpecIfCollationIsPresentAndVersionIsEqua
featureCompatibility.setVersion(ServerGlobalParams::FeatureCompatibility::Version::k36);
featureCompatibility.validateFeaturesAsMaster.store(true);
- auto result = validateIndexSpec(BSON("key" << BSON("field" << 1) << "name"
+ auto result = validateIndexSpec(kDefaultOpCtx,
+ BSON("key" << BSON("field" << 1) << "name"
<< "indexName"
<< "v"
<< 2
@@ -576,7 +616,8 @@ TEST(IndexSpecValidateTest, ReturnsAnErrorIfUnknownFieldIsPresentInSpecV2) {
featureCompatibility.setVersion(ServerGlobalParams::FeatureCompatibility::Version::k36);
featureCompatibility.validateFeaturesAsMaster.store(true);
- auto result = validateIndexSpec(BSON("key" << BSON("field" << 1) << "name"
+ auto result = validateIndexSpec(kDefaultOpCtx,
+ BSON("key" << BSON("field" << 1) << "name"
<< "indexName"
<< "v"
<< 2
@@ -592,7 +633,8 @@ TEST(IndexSpecValidateTest, ReturnsAnErrorIfUnknownFieldIsPresentInSpecV1) {
featureCompatibility.setVersion(ServerGlobalParams::FeatureCompatibility::Version::k36);
featureCompatibility.validateFeaturesAsMaster.store(true);
- auto result = validateIndexSpec(BSON("key" << BSON("field" << 1) << "name"
+ auto result = validateIndexSpec(kDefaultOpCtx,
+ BSON("key" << BSON("field" << 1) << "name"
<< "indexName"
<< "v"
<< 1
@@ -824,5 +866,50 @@ TEST(IndexSpecCollationValidateTest, FillsInCollationFieldWithCollectionDefaultI
sorted(result.getValue()));
}
+TEST(IndexSpecPartialFilterTest, FailsIfPartialFilterIsNotAnObject) {
+ ServerGlobalParams::FeatureCompatibility featureCompatibility;
+ featureCompatibility.setVersion(ServerGlobalParams::FeatureCompatibility::Version::k36);
+ featureCompatibility.validateFeaturesAsMaster.store(true);
+
+ auto result = validateIndexSpec(kDefaultOpCtx,
+ BSON("key" << BSON("field" << 1) << "name"
+ << "indexName"
+ << "partialFilterExpression"
+ << 1),
+ kTestNamespace,
+ featureCompatibility);
+ ASSERT_EQ(result.getStatus(), ErrorCodes::TypeMismatch);
+}
+
+TEST(IndexSpecPartialFilterTest, FailsIfPartialFilterContainsBannedFeature) {
+ ServerGlobalParams::FeatureCompatibility featureCompatibility;
+ featureCompatibility.setVersion(ServerGlobalParams::FeatureCompatibility::Version::k36);
+ featureCompatibility.validateFeaturesAsMaster.store(true);
+
+ auto result = validateIndexSpec(kDefaultOpCtx,
+ BSON("key" << BSON("field" << 1) << "name"
+ << "indexName"
+ << "partialFilterExpression"
+ << BSON("$isolated" << 1)),
+ kTestNamespace,
+ featureCompatibility);
+ ASSERT_EQ(result.getStatus(), ErrorCodes::QueryFeatureNotAllowed);
+}
+
+TEST(IndexSpecPartialFilterTest, AcceptsValidPartialFilterExpression) {
+ ServerGlobalParams::FeatureCompatibility featureCompatibility;
+ featureCompatibility.setVersion(ServerGlobalParams::FeatureCompatibility::Version::k36);
+ featureCompatibility.validateFeaturesAsMaster.store(true);
+
+ auto result = validateIndexSpec(kDefaultOpCtx,
+ BSON("key" << BSON("field" << 1) << "name"
+ << "indexName"
+ << "partialFilterExpression"
+ << BSON("a" << 1)),
+ kTestNamespace,
+ featureCompatibility);
+ ASSERT_OK(result.getStatus());
+}
+
} // namespace
} // namespace mongo
diff --git a/src/mongo/db/commands/create_indexes.cpp b/src/mongo/db/commands/create_indexes.cpp
index f34680a7382..01f37ae4ac6 100644
--- a/src/mongo/db/commands/create_indexes.cpp
+++ b/src/mongo/db/commands/create_indexes.cpp
@@ -73,6 +73,7 @@ const StringData kCommandName = "createIndexes"_sd;
* malformed, then an error status is returned.
*/
StatusWith<std::vector<BSONObj>> parseAndValidateIndexSpecs(
+ OperationContext* opCtx,
const NamespaceString& ns,
const BSONObj& cmdObj,
const ServerGlobalParams::FeatureCompatibility& featureCompatibility) {
@@ -99,7 +100,7 @@ StatusWith<std::vector<BSONObj>> parseAndValidateIndexSpecs(
}
auto indexSpecStatus = index_key_validate::validateIndexSpec(
- indexesElem.Obj(), ns, featureCompatibility);
+ opCtx, indexesElem.Obj(), ns, featureCompatibility);
if (!indexSpecStatus.isOK()) {
return indexSpecStatus.getStatus();
}
@@ -242,7 +243,7 @@ public:
return appendCommandStatus(result, status);
auto specsWithStatus =
- parseAndValidateIndexSpecs(ns, cmdObj, serverGlobalParams.featureCompatibility);
+ parseAndValidateIndexSpecs(opCtx, ns, cmdObj, serverGlobalParams.featureCompatibility);
if (!specsWithStatus.isOK()) {
return appendCommandStatus(result, specsWithStatus.getStatus());
}
diff --git a/src/mongo/db/commands/dbcommands.cpp b/src/mongo/db/commands/dbcommands.cpp
index 15b1841b580..693bb58d5a0 100644
--- a/src/mongo/db/commands/dbcommands.cpp
+++ b/src/mongo/db/commands/dbcommands.cpp
@@ -514,7 +514,7 @@ public:
// Perform index spec validation.
idIndexSpec = uassertStatusOK(index_key_validate::validateIndexSpec(
- idIndexSpec, ns, serverGlobalParams.featureCompatibility));
+ opCtx, idIndexSpec, ns, serverGlobalParams.featureCompatibility));
uassertStatusOK(index_key_validate::validateIdIndexSpec(idIndexSpec));
// Validate or fill in _id index collation.
diff --git a/src/mongo/db/commands/find_cmd.cpp b/src/mongo/db/commands/find_cmd.cpp
index 3ef407c1d3f..e4473be4b48 100644
--- a/src/mongo/db/commands/find_cmd.cpp
+++ b/src/mongo/db/commands/find_cmd.cpp
@@ -163,7 +163,8 @@ public:
std::move(qrStatus.getValue()),
expCtx,
extensionsCallback,
- MatchExpressionParser::kAllowAllSpecialFeatures);
+ MatchExpressionParser::kAllowAllSpecialFeatures &
+ ~MatchExpressionParser::AllowedFeatures::kIsolated);
if (!statusWithCQ.isOK()) {
return statusWithCQ.getStatus();
}
@@ -280,7 +281,8 @@ public:
std::move(qr),
expCtx,
extensionsCallback,
- MatchExpressionParser::kAllowAllSpecialFeatures);
+ MatchExpressionParser::kAllowAllSpecialFeatures &
+ ~MatchExpressionParser::AllowedFeatures::kIsolated);
if (!statusWithCQ.isOK()) {
return appendCommandStatus(result, statusWithCQ.getStatus());
}
diff --git a/src/mongo/db/commands/geo_near_cmd.cpp b/src/mongo/db/commands/geo_near_cmd.cpp
index 0df75717f42..0389d550618 100644
--- a/src/mongo/db/commands/geo_near_cmd.cpp
+++ b/src/mongo/db/commands/geo_near_cmd.cpp
@@ -221,7 +221,8 @@ public:
std::move(qr),
expCtx,
extensionsCallback,
- MatchExpressionParser::kAllowAllSpecialFeatures);
+ MatchExpressionParser::kAllowAllSpecialFeatures &
+ ~MatchExpressionParser::AllowedFeatures::kIsolated);
if (!statusWithCQ.isOK()) {
errmsg = "Can't parse filter / create query";
return false;
diff --git a/src/mongo/db/commands/mr.cpp b/src/mongo/db/commands/mr.cpp
index f8882ec158f..c6ff02b154c 100644
--- a/src/mongo/db/commands/mr.cpp
+++ b/src/mongo/db/commands/mr.cpp
@@ -431,7 +431,7 @@ void State::prepTempCollection() {
BSON("key" << BSON("0" << 1) << "ns" << _config.incLong.ns() << "name"
<< "_temp_0");
auto indexSpec = uassertStatusOK(index_key_validate::validateIndexSpec(
- rawIndexSpec, _config.incLong, serverGlobalParams.featureCompatibility));
+ _opCtx, rawIndexSpec, _config.incLong, serverGlobalParams.featureCompatibility));
Status status = incColl->getIndexCatalog()
->createIndexOnEmptyCollection(_opCtx, indexSpec)
@@ -1122,7 +1122,8 @@ void State::finalReduce(OperationContext* opCtx, CurOp* curOp, ProgressMeterHold
std::move(qr),
expCtx,
extensionsCallback,
- MatchExpressionParser::kAllowAllSpecialFeatures);
+ MatchExpressionParser::kAllowAllSpecialFeatures &
+ ~MatchExpressionParser::AllowedFeatures::kIsolated);
verify(statusWithCQ.isOK());
std::unique_ptr<CanonicalQuery> cq = std::move(statusWithCQ.getValue());
@@ -1488,12 +1489,13 @@ public:
const ExtensionsCallbackReal extensionsCallback(opCtx, &config.nss);
const boost::intrusive_ptr<ExpressionContext> expCtx;
- auto statusWithCQ =
- CanonicalQuery::canonicalize(opCtx,
- std::move(qr),
- expCtx,
- extensionsCallback,
- MatchExpressionParser::kAllowAllSpecialFeatures);
+ auto statusWithCQ = CanonicalQuery::canonicalize(
+ opCtx,
+ std::move(qr),
+ expCtx,
+ extensionsCallback,
+ MatchExpressionParser::kAllowAllSpecialFeatures &
+ ~MatchExpressionParser::AllowedFeatures::kIsolated);
if (!statusWithCQ.isOK()) {
uasserted(17238, "Can't canonicalize query " + config.filter.toString());
return 0;
diff --git a/src/mongo/db/commands/plan_cache_commands_test.cpp b/src/mongo/db/commands/plan_cache_commands_test.cpp
index e8a80bf2266..b93b9da43e2 100644
--- a/src/mongo/db/commands/plan_cache_commands_test.cpp
+++ b/src/mongo/db/commands/plan_cache_commands_test.cpp
@@ -231,7 +231,6 @@ TEST(PlanCacheCommandsTest, Canonicalize) {
ASSERT_OK(statusWithCQ.getStatus());
unique_ptr<CanonicalQuery> query = std::move(statusWithCQ.getValue());
-
// Equivalent query should generate same key.
statusWithCQ =
PlanCacheCommand::canonicalize(opCtx.get(), nss.ns(), fromjson("{query: {b: 1, a: 1}}"));
@@ -268,6 +267,24 @@ TEST(PlanCacheCommandsTest, Canonicalize) {
ASSERT_NOT_EQUALS(planCache.computeKey(*query), planCache.computeKey(*projectionQuery));
}
+TEST(PlanCacheCommandsTest, PlanCacheIgnoresIsolated) {
+ PlanCache planCache;
+ QueryTestServiceContext serviceContext;
+ auto opCtx = serviceContext.makeOperationContext();
+
+ // Query with $isolated should generate the same key as a query without $siolated.
+ auto statusWithCQ =
+ PlanCacheCommand::canonicalize(opCtx.get(), nss.ns(), fromjson("{query: {a: 1, b: 1}}"));
+ ASSERT_OK(statusWithCQ.getStatus());
+ unique_ptr<CanonicalQuery> query = std::move(statusWithCQ.getValue());
+
+ statusWithCQ = PlanCacheCommand::canonicalize(
+ opCtx.get(), nss.ns(), fromjson("{query: {a: 1, b: 1}, $isolated: 1}"));
+ ASSERT_OK(statusWithCQ.getStatus());
+ unique_ptr<CanonicalQuery> queryWithIsolated = std::move(statusWithCQ.getValue());
+ ASSERT_EQUALS(planCache.computeKey(*query), planCache.computeKey(*queryWithIsolated));
+}
+
/**
* Tests for planCacheClear (single query shape)
*/
diff --git a/src/mongo/db/dbhelpers.cpp b/src/mongo/db/dbhelpers.cpp
index f3ed7df4f97..158e4b727a6 100644
--- a/src/mongo/db/dbhelpers.cpp
+++ b/src/mongo/db/dbhelpers.cpp
@@ -119,7 +119,8 @@ RecordId Helpers::findOne(OperationContext* opCtx,
std::move(qr),
expCtx,
extensionsCallback,
- MatchExpressionParser::kAllowAllSpecialFeatures);
+ MatchExpressionParser::kAllowAllSpecialFeatures &
+ ~MatchExpressionParser::AllowedFeatures::kIsolated);
massert(17244, "Could not canonicalize " + query.toString(), statusWithCQ.isOK());
unique_ptr<CanonicalQuery> cq = std::move(statusWithCQ.getValue());
diff --git a/src/mongo/db/matcher/expression_parser.cpp b/src/mongo/db/matcher/expression_parser.cpp
index 4f3de904667..0cd9ec13a53 100644
--- a/src/mongo/db/matcher/expression_parser.cpp
+++ b/src/mongo/db/matcher/expression_parser.cpp
@@ -400,8 +400,13 @@ StatusWithMatchExpression parseAtomicOrIsolated(
const ExtensionsCallback* extensionsCallback,
MatchExpressionParser::AllowedFeatureSet allowedFeatures,
DocumentParseLevel currentLevel) {
+ if ((allowedFeatures & MatchExpressionParser::AllowedFeatures::kIsolated) == 0u) {
+ return {Status(ErrorCodes::QueryFeatureNotAllowed,
+ "$isolated ($atomic) is not allowed in this context")};
+ }
if (currentLevel != DocumentParseLevel::kPredicateTopLevel) {
- return {Status(ErrorCodes::FailedToParse, "$atomic/$isolated has to be at the top level")};
+ return {
+ Status(ErrorCodes::FailedToParse, "$isolated ($atomic) has to be at the top level")};
}
return {nullptr};
}
diff --git a/src/mongo/db/matcher/expression_parser.h b/src/mongo/db/matcher/expression_parser.h
index 11c74438e51..f997b4c1a49 100644
--- a/src/mongo/db/matcher/expression_parser.h
+++ b/src/mongo/db/matcher/expression_parser.h
@@ -96,6 +96,7 @@ public:
kJavascript = 1 << 2,
kExpr = 1 << 3,
kJSONSchema = 1 << 4,
+ kIsolated = 1 << 5,
};
using AllowedFeatureSet = unsigned long long;
static constexpr AllowedFeatureSet kBanAllSpecialFeatures = 0;
diff --git a/src/mongo/db/matcher/expression_parser_test.cpp b/src/mongo/db/matcher/expression_parser_test.cpp
index e25a322248f..669986004f4 100644
--- a/src/mongo/db/matcher/expression_parser_test.cpp
+++ b/src/mongo/db/matcher/expression_parser_test.cpp
@@ -65,19 +65,54 @@ TEST(MatchExpressionParserTest, Multiple1) {
ASSERT(!result.getValue()->matchesBSON(BSON("x" << 5 << "y" << 4)));
}
-TEST(AtomicMatchExpressionTest, AtomicOperator1) {
- BSONObj query = BSON("x" << 5 << "$atomic" << BSON("$gt" << 5 << "$lt" << 8));
+TEST(AtomicMatchExpressionTest, AtomicOperatorSuccessfullyParsesWhenFeatureBitIsSet) {
+ auto query = BSON("x" << 5 << "$atomic" << 1);
boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
- StatusWithMatchExpression result = MatchExpressionParser::parse(query, expCtx);
- ASSERT_TRUE(result.isOK());
+ auto result = MatchExpressionParser::parse(
+ query, expCtx, ExtensionsCallbackNoop(), MatchExpressionParser::AllowedFeatures::kIsolated);
+ ASSERT_OK(result.getStatus());
+}
- query = BSON("x" << 5 << "$isolated" << 1);
- result = MatchExpressionParser::parse(query, expCtx);
- ASSERT_TRUE(result.isOK());
+TEST(AtomicMatchExpressionTest, AtomicOperatorFailsToParseIfNotTopLevel) {
+ boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
+ auto query = BSON("x" << 5 << "y" << BSON("$atomic" << 1));
+ auto result = MatchExpressionParser::parse(
+ query, expCtx, ExtensionsCallbackNoop(), MatchExpressionParser::AllowedFeatures::kIsolated);
+ ASSERT_NOT_OK(result.getStatus());
+ ASSERT_EQ(ErrorCodes::BadValue, result.getStatus());
+}
- query = BSON("x" << 5 << "y" << BSON("$isolated" << 1));
- result = MatchExpressionParser::parse(query, expCtx);
- ASSERT_FALSE(result.isOK());
+TEST(AtomicMatchExpressionTest, AtomicOperatorFailsToParseIfFeatureBitIsNotSet) {
+ auto query = BSON("x" << 5 << "$atomic" << 1);
+ boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
+ auto result = MatchExpressionParser::parse(query, expCtx);
+ ASSERT_EQ(ErrorCodes::QueryFeatureNotAllowed, result.getStatus());
+}
+
+TEST(IsolatedMatchExpressionTest, IsolatedOperatorSuccessfullyParsesWhenFeatureBitIsSet) {
+ auto query = BSON("x" << 5 << "$isolated" << 1);
+ boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
+ auto result = MatchExpressionParser::parse(
+ query, expCtx, ExtensionsCallbackNoop(), MatchExpressionParser::AllowedFeatures::kIsolated);
+ ASSERT_OK(result.getStatus());
+}
+
+TEST(IsolatedMatchExpressionTest, IsolatedOperatorFailsToParseIfFeatureBitIsNotSet) {
+ // Query parsing fails if $isolated is not in the allowed feature set.
+ auto query = BSON("x" << 5 << "$isolated" << 1);
+ boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
+ auto result = MatchExpressionParser::parse(query, expCtx);
+ ASSERT_NOT_OK(result.getStatus());
+ ASSERT_EQ(ErrorCodes::QueryFeatureNotAllowed, result.getStatus());
+}
+
+TEST(IsolatedMatchExpressionTest, IsolatedOperatorFailsToParseIfNotTopLevel) {
+ boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
+ auto query = BSON("x" << 5 << "y" << BSON("$isolated" << 1));
+ auto result = MatchExpressionParser::parse(
+ query, expCtx, ExtensionsCallbackNoop(), MatchExpressionParser::AllowedFeatures::kIsolated);
+ ASSERT_NOT_OK(result.getStatus());
+ ASSERT_EQ(ErrorCodes::BadValue, result.getStatus());
}
TEST(MatchExpressionParserTest, MinDistanceWithoutNearFailsToParse) {
diff --git a/src/mongo/db/matcher/schema/expression_parser_schema_test.cpp b/src/mongo/db/matcher/schema/expression_parser_schema_test.cpp
index 13f3d9e1f6c..89ca1824b10 100644
--- a/src/mongo/db/matcher/schema/expression_parser_schema_test.cpp
+++ b/src/mongo/db/matcher/schema/expression_parser_schema_test.cpp
@@ -231,14 +231,14 @@ TEST(MatchExpressionParserSchemaTest, ObjectMatchCorrectlyParsesNestedObjectMatc
result.getValue()->matchesBSON(fromjson("{a: [{b: 0}, {b: [{c: 0}, {c: 'string'}]}]}")));
}
-TEST(MatchExpressionParserSchemaTest, ObjectMatchSubExprRejectsTopLevelOperators) {
+TEST(MatchExpressionParserSchemaTest, ObjectMatchSubExprRejectsPathlessOperators) {
auto query = fromjson(
"{a: {$_internalSchemaObjectMatch: {"
- " $isolated: 1"
+ " $expr: {$eq: ['$a', 5]}"
"}}}");
boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
auto result = MatchExpressionParser::parse(query, expCtx);
- ASSERT_EQ(result.getStatus(), ErrorCodes::FailedToParse);
+ ASSERT_EQ(result.getStatus(), ErrorCodes::BadValue);
}
//
diff --git a/src/mongo/db/query/canonical_query_test.cpp b/src/mongo/db/query/canonical_query_test.cpp
index ebef44cebc4..a745b06a8ef 100644
--- a/src/mongo/db/query/canonical_query_test.cpp
+++ b/src/mongo/db/query/canonical_query_test.cpp
@@ -175,13 +175,18 @@ TEST(CanonicalQueryTest, SortTreeNumChildrenComparison) {
/**
* Utility function to create a CanonicalQuery
*/
-unique_ptr<CanonicalQuery> canonicalize(const char* queryStr) {
+unique_ptr<CanonicalQuery> canonicalize(const char* queryStr,
+ MatchExpressionParser::AllowedFeatureSet allowedFeatures =
+ MatchExpressionParser::kDefaultSpecialFeatures) {
QueryTestServiceContext serviceContext;
auto opCtx = serviceContext.makeOperationContext();
auto qr = stdx::make_unique<QueryRequest>(nss);
qr->setFilter(fromjson(queryStr));
- auto statusWithCQ = CanonicalQuery::canonicalize(opCtx.get(), std::move(qr));
+
+ auto statusWithCQ = CanonicalQuery::canonicalize(
+ opCtx.get(), std::move(qr), nullptr, ExtensionsCallbackNoop(), allowedFeatures);
+
ASSERT_OK(statusWithCQ.getStatus());
return std::move(statusWithCQ.getValue());
}
@@ -205,22 +210,30 @@ std::unique_ptr<CanonicalQuery> canonicalize(const char* queryStr,
* Test that CanonicalQuery::isIsolated() returns correctly.
*/
TEST(CanonicalQueryTest, IsIsolatedReturnsTrueWithIsolated) {
- unique_ptr<CanonicalQuery> cq = canonicalize("{$isolated: 1, x: 3}");
+ unique_ptr<CanonicalQuery> cq = canonicalize("{$isolated: 1, x: 3}",
+ MatchExpressionParser::kDefaultSpecialFeatures |
+ MatchExpressionParser::kIsolated);
ASSERT_TRUE(cq->isIsolated());
}
TEST(CanonicalQueryTest, IsIsolatedReturnsTrueWithAtomic) {
- unique_ptr<CanonicalQuery> cq = canonicalize("{$atomic: 1, x: 3}");
+ unique_ptr<CanonicalQuery> cq = canonicalize("{$atomic: 1, x: 3}",
+ MatchExpressionParser::kDefaultSpecialFeatures |
+ MatchExpressionParser::kIsolated);
ASSERT_TRUE(cq->isIsolated());
}
TEST(CanonicalQueryTest, IsIsolatedReturnsFalseWithIsolated) {
- unique_ptr<CanonicalQuery> cq = canonicalize("{$isolated: 0, x: 3}");
+ unique_ptr<CanonicalQuery> cq = canonicalize("{$isolated: 0, x: 3}",
+ MatchExpressionParser::kDefaultSpecialFeatures |
+ MatchExpressionParser::kIsolated);
ASSERT_FALSE(cq->isIsolated());
}
TEST(CanonicalQueryTest, IsIsolatedReturnsFalseWithAtomic) {
- unique_ptr<CanonicalQuery> cq = canonicalize("{$atomic: 0, x: 3}");
+ unique_ptr<CanonicalQuery> cq = canonicalize("{$atomic: 0, x: 3}",
+ MatchExpressionParser::kDefaultSpecialFeatures |
+ MatchExpressionParser::kIsolated);
ASSERT_FALSE(cq->isIsolated());
}
diff --git a/src/mongo/db/query/find.cpp b/src/mongo/db/query/find.cpp
index 26a249f7928..e3419187390 100644
--- a/src/mongo/db/query/find.cpp
+++ b/src/mongo/db/query/find.cpp
@@ -533,7 +533,8 @@ std::string runQuery(OperationContext* opCtx,
q,
expCtx,
ExtensionsCallbackReal(opCtx, &nss),
- MatchExpressionParser::kAllowAllSpecialFeatures);
+ MatchExpressionParser::kAllowAllSpecialFeatures &
+ ~MatchExpressionParser::AllowedFeatures::kIsolated);
if (!statusWithCQ.isOK()) {
uasserted(17287,
str::stream() << "Can't canonicalize query: "
diff --git a/src/mongo/db/query/get_executor.cpp b/src/mongo/db/query/get_executor.cpp
index e818b7429c4..ad9553046dc 100644
--- a/src/mongo/db/query/get_executor.cpp
+++ b/src/mongo/db/query/get_executor.cpp
@@ -1248,7 +1248,8 @@ StatusWith<unique_ptr<PlanExecutor, PlanExecutor::Deleter>> getExecutorCount(
collection ? static_cast<const ExtensionsCallback&>(
ExtensionsCallbackReal(opCtx, &collection->ns()))
: static_cast<const ExtensionsCallback&>(ExtensionsCallbackNoop()),
- MatchExpressionParser::kAllowAllSpecialFeatures);
+ MatchExpressionParser::kAllowAllSpecialFeatures &
+ ~MatchExpressionParser::AllowedFeatures::kIsolated);
if (!statusWithCQ.isOK()) {
return statusWithCQ.getStatus();
@@ -1505,7 +1506,8 @@ StatusWith<unique_ptr<PlanExecutor, PlanExecutor::Deleter>> getExecutorDistinct(
std::move(qr),
expCtx,
extensionsCallback,
- MatchExpressionParser::kAllowAllSpecialFeatures);
+ MatchExpressionParser::kAllowAllSpecialFeatures &
+ ~MatchExpressionParser::AllowedFeatures::kIsolated);
if (!statusWithCQ.isOK()) {
return statusWithCQ.getStatus();
}
diff --git a/src/mongo/db/query/parsed_distinct.cpp b/src/mongo/db/query/parsed_distinct.cpp
index 915636dd98c..7fbb38648b2 100644
--- a/src/mongo/db/query/parsed_distinct.cpp
+++ b/src/mongo/db/query/parsed_distinct.cpp
@@ -206,7 +206,8 @@ StatusWith<ParsedDistinct> ParsedDistinct::parse(OperationContext* opCtx,
std::move(qr),
expCtx,
extensionsCallback,
- MatchExpressionParser::kAllowAllSpecialFeatures);
+ MatchExpressionParser::kAllowAllSpecialFeatures &
+ ~MatchExpressionParser::AllowedFeatures::kIsolated);
if (!cq.isOK()) {
return cq.getStatus();
}
diff --git a/src/mongo/db/system_index.cpp b/src/mongo/db/system_index.cpp
index ccc6ccab06c..9a910570f51 100644
--- a/src/mongo/db/system_index.cpp
+++ b/src/mongo/db/system_index.cpp
@@ -99,7 +99,7 @@ void generateSystemIndexForExistingCollection(OperationContext* opCtx,
const IndexSpec& spec) {
try {
auto indexSpecStatus = index_key_validate::validateIndexSpec(
- spec.toBSON(), ns, serverGlobalParams.featureCompatibility);
+ opCtx, spec.toBSON(), ns, serverGlobalParams.featureCompatibility);
BSONObj indexSpec = fassertStatusOK(40452, indexSpecStatus);
log() << "No authorization index detected on " << ns
@@ -198,16 +198,20 @@ void createSystemIndexes(OperationContext* opCtx, Collection* collection) {
if (ns == AuthorizationManager::usersCollectionNamespace) {
auto indexSpec = fassertStatusOK(
40455,
- index_key_validate::validateIndexSpec(
- v3SystemUsersIndexSpec.toBSON(), ns, serverGlobalParams.featureCompatibility));
+ index_key_validate::validateIndexSpec(opCtx,
+ v3SystemUsersIndexSpec.toBSON(),
+ ns,
+ serverGlobalParams.featureCompatibility));
fassertStatusOK(
40456, collection->getIndexCatalog()->createIndexOnEmptyCollection(opCtx, indexSpec));
} else if (ns == AuthorizationManager::rolesCollectionNamespace) {
auto indexSpec = fassertStatusOK(
40457,
- index_key_validate::validateIndexSpec(
- v3SystemRolesIndexSpec.toBSON(), ns, serverGlobalParams.featureCompatibility));
+ index_key_validate::validateIndexSpec(opCtx,
+ v3SystemRolesIndexSpec.toBSON(),
+ ns,
+ serverGlobalParams.featureCompatibility));
fassertStatusOK(
40458, collection->getIndexCatalog()->createIndexOnEmptyCollection(opCtx, indexSpec));