summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--jstests/core/create_indexes.js14
-rw-r--r--jstests/noPassthrough/geo_full.js7
-rw-r--r--src/mongo/db/catalog/index_key_validate.cpp51
-rw-r--r--src/mongo/db/catalog/index_key_validate.h7
-rw-r--r--src/mongo/db/catalog/index_spec_validate_test.cpp16
-rw-r--r--src/mongo/db/commands/create_indexes.cpp22
-rw-r--r--src/mongo/db/index/index_descriptor.cpp11
-rw-r--r--src/mongo/db/index/index_descriptor.h11
8 files changed, 130 insertions, 9 deletions
diff --git a/jstests/core/create_indexes.js b/jstests/core/create_indexes.js
index a6cdb99ea89..685dfb2e65e 100644
--- a/jstests/core/create_indexes.js
+++ b/jstests/core/create_indexes.js
@@ -138,4 +138,18 @@
res = t.runCommand('createIndexes', {indexes: [{key: {d: 1}, name: 'd_1', v: 1}]});
assert.commandWorked(res, 'v1 index creation should succeed');
+ // Test that index creation fails with an invalid top-level field.
+ res = t.runCommand('createIndexes', {indexes: [{key: {e: 1}, name: 'e_1'}], 'invalidField': 1});
+ assert.commandFailedWithCode(res, ErrorCodes.BadValue);
+
+ // Test that index creation fails with an invalid field in the index spec for index version V2.
+ res = t.runCommand('createIndexes',
+ {indexes: [{key: {e: 1}, name: 'e_1', 'v': 2, 'invalidField': 1}]});
+ assert.commandFailedWithCode(res, ErrorCodes.BadValue);
+
+ // Test that index creation fails with an invalid field in the index spec for index version V1.
+ res = t.runCommand('createIndexes',
+ {indexes: [{key: {e: 1}, name: 'e_1', 'v': 1, 'invalidField': 1}]});
+ assert.commandFailedWithCode(res, ErrorCodes.BadValue);
+
}());
diff --git a/jstests/noPassthrough/geo_full.js b/jstests/noPassthrough/geo_full.js
index bdd4b973b79..8b5048e0b29 100644
--- a/jstests/noPassthrough/geo_full.js
+++ b/jstests/noPassthrough/geo_full.js
@@ -411,7 +411,12 @@ for (var test = 0; test < numTests; test++) {
var indexDoc = {"locs.loc": "2d"};
randIndexAdditions(indexDoc);
- t.ensureIndex(indexDoc, env);
+
+ // "earth" is used to drive test setup and not a valid createIndexes option or required at this
+ // point. It must be removed before calling ensureIndexes().
+ delete env.earth;
+
+ assert.commandWorked(t.ensureIndex(indexDoc, env));
assert.isnull(db.getLastError());
var padding = "x";
diff --git a/src/mongo/db/catalog/index_key_validate.cpp b/src/mongo/db/catalog/index_key_validate.cpp
index 986454911cc..d83f8fafd4c 100644
--- a/src/mongo/db/catalog/index_key_validate.cpp
+++ b/src/mongo/db/catalog/index_key_validate.cpp
@@ -33,6 +33,7 @@
#include <boost/optional.hpp>
#include <cmath>
#include <limits>
+#include <set>
#include "mongo/base/status.h"
#include "mongo/base/status_with.h"
@@ -147,6 +148,11 @@ StatusWith<BSONObj> validateIndexSpec(
bool hasVersionField = false;
bool hasCollationField = false;
+ auto fieldNamesValidStatus = validateIndexSpecFieldNames(indexSpec);
+ if (!fieldNamesValidStatus.isOK()) {
+ return fieldNamesValidStatus;
+ }
+
boost::optional<IndexVersion> resolvedIndexVersion;
for (auto&& indexSpecElem : indexSpec) {
@@ -235,7 +241,8 @@ StatusWith<BSONObj> validateIndexSpec(
hasCollationField = true;
} else {
- // TODO SERVER-769: Validate index options specified in the "createIndexes" command.
+ // We can assume field name is valid at this point. Validation of fieldname is handled
+ // prior to this in validateIndexSpecFieldNames().
continue;
}
}
@@ -283,4 +290,46 @@ StatusWith<BSONObj> validateIndexSpec(
return indexSpec;
}
+
+Status validateIndexSpecFieldNames(const BSONObj& indexSpec) {
+ const std::set<StringData> allowedFieldNames = {
+ IndexDescriptor::k2dIndexMaxFieldName,
+ IndexDescriptor::k2dIndexBitsFieldName,
+ IndexDescriptor::k2dIndexMaxFieldName,
+ IndexDescriptor::k2dIndexMinFieldName,
+ IndexDescriptor::k2dsphereCoarsestIndexedLevel,
+ IndexDescriptor::k2dsphereFinestIndexedLevel,
+ IndexDescriptor::k2dsphereVersionFieldName,
+ IndexDescriptor::kBackgroundFieldName,
+ IndexDescriptor::kCollationFieldName,
+ IndexDescriptor::kDefaultLanguageFieldName,
+ IndexDescriptor::kDropDuplicatesFieldName,
+ IndexDescriptor::kExpireAfterSecondsFieldName,
+ IndexDescriptor::kGeoHaystackBucketSize,
+ IndexDescriptor::kIndexNameFieldName,
+ IndexDescriptor::kIndexVersionFieldName,
+ IndexDescriptor::kKeyPatternFieldName,
+ IndexDescriptor::kLanguageOverrideFieldName,
+ IndexDescriptor::kNamespaceFieldName,
+ IndexDescriptor::kPartialFilterExprFieldName,
+ IndexDescriptor::kSparseFieldName,
+ IndexDescriptor::kStorageEngineFieldName,
+ IndexDescriptor::kTextVersionFieldName,
+ IndexDescriptor::kUniqueFieldName,
+ IndexDescriptor::kWeightsFieldName,
+ // Index creation under legacy writeMode can result in an index spec with an _id field.
+ "_id"};
+
+ for (auto&& indexSpecElem : indexSpec) {
+ auto indexSpecElemFieldName = indexSpecElem.fieldNameStringData();
+ if (!allowedFieldNames.count(indexSpecElemFieldName)) {
+ return {ErrorCodes::BadValue,
+ str::stream() << "The field '" << indexSpecElemFieldName
+ << "' is not valid for an index specification"};
+ }
+ }
+
+ return Status::OK();
+}
+
} // namespace mongo
diff --git a/src/mongo/db/catalog/index_key_validate.h b/src/mongo/db/catalog/index_key_validate.h
index 036eaa4103d..f8e0e8b3e3c 100644
--- a/src/mongo/db/catalog/index_key_validate.h
+++ b/src/mongo/db/catalog/index_key_validate.h
@@ -51,4 +51,11 @@ StatusWith<BSONObj> validateIndexSpec(
const BSONObj& indexSpec,
const NamespaceString& expectedNamespace,
ServerGlobalParams::FeatureCompatibility::Version featureCompatibilityVersion);
+
+/**
+ * Confirms that 'indexSpec' contains only valid field names. Returns an error if an unexpected
+ * field name is found.
+ */
+Status validateIndexSpecFieldNames(const BSONObj& indexSpec);
+
} // namespace mongo
diff --git a/src/mongo/db/catalog/index_spec_validate_test.cpp b/src/mongo/db/catalog/index_spec_validate_test.cpp
index c41bc920cad..c1731e08f10 100644
--- a/src/mongo/db/catalog/index_spec_validate_test.cpp
+++ b/src/mongo/db/catalog/index_spec_validate_test.cpp
@@ -370,5 +370,21 @@ TEST(IndexSpecValidateTest, AcceptsIndexSpecIfCollationIsPresentAndVersionIsEqua
sorted(result.getValue()));
}
+TEST(IndexSpecValidateTest, ReturnsAnErrorIfUnknownFieldIsPresentInSpecV2) {
+ auto result =
+ validateIndexSpec(BSON("key" << BSON("field" << 1) << "v" << 2 << "unknownField" << 1),
+ kTestNamespace,
+ ServerGlobalParams::FeatureCompatibility::Version::k34);
+ ASSERT_EQ(ErrorCodes::BadValue, result);
+}
+
+TEST(IndexSpecValidateTest, ReturnsAnErrorIfUnknownFieldIsPresentInSpecV1) {
+ auto result =
+ validateIndexSpec(BSON("key" << BSON("field" << 1) << "v" << 1 << "unknownField" << 1),
+ kTestNamespace,
+ ServerGlobalParams::FeatureCompatibility::Version::k34);
+ ASSERT_EQ(ErrorCodes::BadValue, result);
+}
+
} // namespace
} // namespace mongo
diff --git a/src/mongo/db/commands/create_indexes.cpp b/src/mongo/db/commands/create_indexes.cpp
index 5d660f024ce..f4537fe45f9 100644
--- a/src/mongo/db/commands/create_indexes.cpp
+++ b/src/mongo/db/commands/create_indexes.cpp
@@ -65,6 +65,8 @@ using IndexVersion = IndexDescriptor::IndexVersion;
namespace {
const StringData kIndexesFieldName = "indexes"_sd;
+const StringData kCommandName = "createIndexes"_sd;
+const StringData kWriteConcern = "writeConcern"_sd;
/**
* Parses the index specifications from 'cmdObj', validates them, and returns equivalent index
@@ -106,16 +108,22 @@ StatusWith<std::vector<BSONObj>> parseAndValidateIndexSpecs(
}
hasIndexesField = true;
- } else {
- // TODO SERVER-769: Validate top-level options to the "createIndexes" command.
+ } else if (kCommandName == cmdElemFieldName || kWriteConcern == cmdElemFieldName) {
+ // Both the command name and writeConcern are valid top-level fields.
continue;
+ } else {
+ return {ErrorCodes::BadValue,
+ str::stream() << "Invalid field specified for " << kCommandName << " command: "
+ << cmdElemFieldName};
}
}
if (!hasIndexesField) {
return {ErrorCodes::FailedToParse,
str::stream() << "The '" << kIndexesFieldName
- << "' field is a required argument of the createIndexes command"};
+ << "' field is a required argument of the "
+ << kCommandName
+ << " command"};
}
if (indexSpecs.empty()) {
@@ -205,7 +213,7 @@ StatusWith<std::vector<BSONObj>> resolveCollectionDefaultProperties(
*/
class CmdCreateIndex : public Command {
public:
- CmdCreateIndex() : Command("createIndexes") {}
+ CmdCreateIndex() : Command(kCommandName) {}
virtual bool supportsWriteConcern(const BSONObj& cmd) const override {
return true;
@@ -276,7 +284,7 @@ public:
invariant(collection);
wunit.commit();
}
- MONGO_WRITE_CONFLICT_RETRY_LOOP_END(txn, "createIndexes", ns.ns());
+ MONGO_WRITE_CONFLICT_RETRY_LOOP_END(txn, kCommandName, ns.ns());
result.appendBool("createdCollectionAutomatically", true);
}
@@ -328,7 +336,7 @@ public:
MONGO_WRITE_CONFLICT_RETRY_LOOP_BEGIN {
indexInfoObjs = uassertStatusOK(indexer.init(specs));
}
- MONGO_WRITE_CONFLICT_RETRY_LOOP_END(txn, "createIndexes", ns.ns());
+ MONGO_WRITE_CONFLICT_RETRY_LOOP_END(txn, kCommandName, ns.ns());
// If we're a background index, replace exclusive db lock with an intent lock, so that
// other readers and writers can proceed during this phase.
@@ -401,7 +409,7 @@ public:
wunit.commit();
}
- MONGO_WRITE_CONFLICT_RETRY_LOOP_END(txn, "createIndexes", ns.ns());
+ MONGO_WRITE_CONFLICT_RETRY_LOOP_END(txn, kCommandName, ns.ns());
result.append("numIndexesAfter", collection->getIndexCatalog()->numIndexesTotal(txn));
diff --git a/src/mongo/db/index/index_descriptor.cpp b/src/mongo/db/index/index_descriptor.cpp
index fca84057727..ff7b615dd0b 100644
--- a/src/mongo/db/index/index_descriptor.cpp
+++ b/src/mongo/db/index/index_descriptor.cpp
@@ -70,18 +70,29 @@ void populateOptionsMap(std::map<StringData, BSONElement>& theMap, const BSONObj
}
} // namespace
+constexpr StringData IndexDescriptor::k2dIndexBitsFieldName;
+constexpr StringData IndexDescriptor::k2dIndexMaxFieldName;
+constexpr StringData IndexDescriptor::k2dIndexMinFieldName;
+constexpr StringData IndexDescriptor::k2dsphereCoarsestIndexedLevel;
+constexpr StringData IndexDescriptor::k2dsphereFinestIndexedLevel;
constexpr StringData IndexDescriptor::k2dsphereVersionFieldName;
constexpr StringData IndexDescriptor::kBackgroundFieldName;
constexpr StringData IndexDescriptor::kCollationFieldName;
+constexpr StringData IndexDescriptor::kDefaultLanguageFieldName;
constexpr StringData IndexDescriptor::kDropDuplicatesFieldName;
+constexpr StringData IndexDescriptor::kExpireAfterSecondsFieldName;
+constexpr StringData IndexDescriptor::kGeoHaystackBucketSize;
constexpr StringData IndexDescriptor::kIndexNameFieldName;
constexpr StringData IndexDescriptor::kIndexVersionFieldName;
constexpr StringData IndexDescriptor::kKeyPatternFieldName;
+constexpr StringData IndexDescriptor::kLanguageOverrideFieldName;
constexpr StringData IndexDescriptor::kNamespaceFieldName;
constexpr StringData IndexDescriptor::kPartialFilterExprFieldName;
constexpr StringData IndexDescriptor::kSparseFieldName;
+constexpr StringData IndexDescriptor::kStorageEngineFieldName;
constexpr StringData IndexDescriptor::kTextVersionFieldName;
constexpr StringData IndexDescriptor::kUniqueFieldName;
+constexpr StringData IndexDescriptor::kWeightsFieldName;
bool IndexDescriptor::isIndexVersionSupported(IndexVersion indexVersion) {
switch (indexVersion) {
diff --git a/src/mongo/db/index/index_descriptor.h b/src/mongo/db/index/index_descriptor.h
index d271e7e5f5a..82ee02766de 100644
--- a/src/mongo/db/index/index_descriptor.h
+++ b/src/mongo/db/index/index_descriptor.h
@@ -56,18 +56,29 @@ class IndexDescriptor {
public:
enum class IndexVersion { kV0 = 0, kV1 = 1, kV2 = 2 };
+ static constexpr StringData k2dIndexBitsFieldName = "bits"_sd;
+ static constexpr StringData k2dIndexMinFieldName = "min"_sd;
+ static constexpr StringData k2dIndexMaxFieldName = "max"_sd;
+ static constexpr StringData k2dsphereCoarsestIndexedLevel = "coarsestIndexedLevel"_sd;
+ static constexpr StringData k2dsphereFinestIndexedLevel = "finestIndexedLevel"_sd;
static constexpr StringData k2dsphereVersionFieldName = "2dsphereIndexVersion"_sd;
static constexpr StringData kBackgroundFieldName = "background"_sd;
static constexpr StringData kCollationFieldName = "collation"_sd;
+ static constexpr StringData kDefaultLanguageFieldName = "default_language"_sd;
static constexpr StringData kDropDuplicatesFieldName = "dropDups"_sd;
+ static constexpr StringData kExpireAfterSecondsFieldName = "expireAfterSeconds"_sd;
+ static constexpr StringData kGeoHaystackBucketSize = "bucketSize"_sd;
static constexpr StringData kIndexNameFieldName = "name"_sd;
static constexpr StringData kIndexVersionFieldName = "v"_sd;
static constexpr StringData kKeyPatternFieldName = "key"_sd;
+ static constexpr StringData kLanguageOverrideFieldName = "language_override"_sd;
static constexpr StringData kNamespaceFieldName = "ns"_sd;
static constexpr StringData kPartialFilterExprFieldName = "partialFilterExpression"_sd;
static constexpr StringData kSparseFieldName = "sparse"_sd;
+ static constexpr StringData kStorageEngineFieldName = "storageEngine"_sd;
static constexpr StringData kTextVersionFieldName = "textIndexVersion"_sd;
static constexpr StringData kUniqueFieldName = "unique"_sd;
+ static constexpr StringData kWeightsFieldName = "weights"_sd;
/**
* OnDiskIndexData is a pointer to the memory mapped per-index data.