summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCheahuychou Mao <mao.cheahuychou@gmail.com>2023-02-22 21:35:58 +0000
committerEvergreen Agent <no-reply@evergreen.mongodb.com>2023-02-23 21:18:09 +0000
commit8f3946c68aa48d85472ff005a4bd03d9df2ba4b9 (patch)
tree3c4341d42c16604edf32cd9d89bad443af7b650e
parent1330f5552e6655a94edc1f422bf8d033f2853534 (diff)
downloadmongo-8f3946c68aa48d85472ff005a4bd03d9df2ba4b9.tar.gz
SERVER-74124 Disallow shard key pattern with fields that have parts that start with '$'
(cherry picked from commit 71372698d18785a374aa09bdd646d3c5dc2f227a)
-rw-r--r--etc/backports_required_for_multiversion_tests.yml4
-rw-r--r--jstests/sharding/shard_keys_with_dollar_sign.js57
-rw-r--r--src/mongo/s/shard_key_pattern.cpp5
-rw-r--r--src/mongo/s/shard_key_pattern_test.cpp11
4 files changed, 77 insertions, 0 deletions
diff --git a/etc/backports_required_for_multiversion_tests.yml b/etc/backports_required_for_multiversion_tests.yml
index b936889ae67..16df2fce80a 100644
--- a/etc/backports_required_for_multiversion_tests.yml
+++ b/etc/backports_required_for_multiversion_tests.yml
@@ -310,6 +310,8 @@ last-continuous:
ticket: SERVER-73822
- test_file: jstests/sharding/internal_txns/incomplete_transaction_history_during_migration.js
ticket: SERVER-73938
+ - test_file: jstests/sharding/shard_keys_with_dollar_sign.js
+ ticket: SERVER-74124
suites: null
last-lts:
all:
@@ -695,4 +697,6 @@ last-lts:
ticket: SERVER-73822
- test_file: jstests/sharding/internal_txns/incomplete_transaction_history_during_migration.js
ticket: SERVER-73938
+ - test_file: jstests/sharding/shard_keys_with_dollar_sign.js
+ ticket: SERVER-74124
suites: null
diff --git a/jstests/sharding/shard_keys_with_dollar_sign.js b/jstests/sharding/shard_keys_with_dollar_sign.js
new file mode 100644
index 00000000000..5d18d6ab7ab
--- /dev/null
+++ b/jstests/sharding/shard_keys_with_dollar_sign.js
@@ -0,0 +1,57 @@
+/**
+ * Tests that the shardCollection command and reshardCollection command correctly reject a shard key
+ * that has a field name with parts that start with '$'.
+ */
+(function() {
+"use strict";
+
+const st = new ShardingTest({shards: 1});
+
+const dbName = "testDb";
+const ns0 = dbName + ".testColl0";
+const ns1 = dbName + ".testColl1";
+const ns2 = dbName + ".testColl2";
+const db = st.s.getDB(dbName);
+
+function testValidation(key, {isValidIndexKey, isValidShardKey}) {
+ jsTest.log(`Testing ${tojson({key, isValidIndexKey, isValidShardKey})}`);
+ assert.commandWorked(st.s.adminCommand({enableSharding: dbName}));
+ st.ensurePrimaryShard(dbName, st.shard0.name);
+
+ const createIndexRes = db.getCollection(ns0).createIndex(key);
+ if (isValidIndexKey) {
+ assert.commandWorked(createIndexRes);
+ } else {
+ assert.commandFailedWithCode(createIndexRes, ErrorCodes.CannotCreateIndex);
+ }
+
+ const shardCollectionRes = st.s.adminCommand({shardCollection: ns1, key});
+ if (isValidShardKey) {
+ assert.commandWorked(shardCollectionRes);
+ } else {
+ assert.commandFailedWithCode(shardCollectionRes, ErrorCodes.BadValue);
+ }
+
+ assert.commandWorked(st.s.adminCommand({shardCollection: ns2, key: {_id: 1}}));
+ const reshardCollectionRes = st.s.adminCommand({reshardCollection: ns2, key});
+ if (isValidShardKey) {
+ assert.commandWorked(reshardCollectionRes);
+ } else {
+ assert.commandFailedWithCode(reshardCollectionRes, ErrorCodes.BadValue);
+ }
+
+ assert.commandWorked(db.dropDatabase());
+}
+
+testValidation({"$x": 1}, {isValidIndexKey: false, isValidShardKey: false});
+testValidation({"x.$y": 1}, {isValidIndexKey: false, isValidShardKey: false});
+testValidation({"$**": 1}, {isValidIndexKey: true, isValidShardKey: false});
+testValidation({"x.$**": 1}, {isValidIndexKey: true, isValidShardKey: false});
+testValidation({"$": 1}, {isValidIndexKey: false, isValidShardKey: false});
+
+testValidation({"x$": 1}, {isValidIndexKey: true, isValidShardKey: true});
+testValidation({"x$.y": 1}, {isValidIndexKey: true, isValidShardKey: true});
+testValidation({"x.y$": 1}, {isValidIndexKey: true, isValidShardKey: true});
+
+st.stop();
+})();
diff --git a/src/mongo/s/shard_key_pattern.cpp b/src/mongo/s/shard_key_pattern.cpp
index db4dce94066..b782f426cc6 100644
--- a/src/mongo/s/shard_key_pattern.cpp
+++ b/src/mongo/s/shard_key_pattern.cpp
@@ -76,6 +76,11 @@ std::vector<std::unique_ptr<FieldRef>> parseShardKeyPattern(const BSONObj& keyPa
str::stream() << "Field " << patternEl.fieldNameStringData()
<< " contains empty parts",
!newFieldRef->getPart(i).empty());
+
+ uassert(ErrorCodes::BadValue,
+ str::stream() << "Field " << patternEl.fieldNameStringData()
+ << " contains parts that start with '$'",
+ newFieldRef->getPart(i).find("$") != 0);
}
// Numeric and ascending (1.0), or "hashed" with exactly hashed field.
diff --git a/src/mongo/s/shard_key_pattern_test.cpp b/src/mongo/s/shard_key_pattern_test.cpp
index a59f0494f73..bbbf6136ec7 100644
--- a/src/mongo/s/shard_key_pattern_test.cpp
+++ b/src/mongo/s/shard_key_pattern_test.cpp
@@ -66,6 +66,7 @@ TEST_F(ShardKeyPatternTest, SingleFieldShardKeyPatternsValidityCheck) {
ShardKeyPattern s3(BSON("a" << (long long)1L));
ShardKeyPattern s4(BSON("a"
<< "hashed"));
+ ShardKeyPattern s5(BSON("a$" << 1));
ASSERT_THROWS(ShardKeyPattern(BSONObj()), DBException);
ASSERT_THROWS(ShardKeyPattern(BSON("a" << -1)), DBException);
@@ -78,6 +79,9 @@ TEST_F(ShardKeyPatternTest, SingleFieldShardKeyPatternsValidityCheck) {
DBException);
ASSERT_THROWS(ShardKeyPattern(BSON("" << 1)), DBException);
ASSERT_THROWS(ShardKeyPattern(BSON("." << 1)), DBException);
+ ASSERT_THROWS(ShardKeyPattern(BSON("$" << 1)), DBException);
+ ASSERT_THROWS(ShardKeyPattern(BSON("$a" << 1)), DBException);
+ ASSERT_THROWS(ShardKeyPattern(BSON("$**" << 1)), DBException);
}
TEST_F(ShardKeyPatternTest, CompositeShardKeyPatternsValidityCheck) {
@@ -91,6 +95,9 @@ TEST_F(ShardKeyPatternTest, CompositeShardKeyPatternsValidityCheck) {
DBException);
ASSERT_THROWS(ShardKeyPattern(BSON("a" << 1 << "b." << 1.0)), DBException);
ASSERT_THROWS(ShardKeyPattern(BSON("a" << 1 << "" << 1.0)), DBException);
+ ASSERT_THROWS(ShardKeyPattern(BSON("a" << 1 << "$" << 1.0)), DBException);
+ ASSERT_THROWS(ShardKeyPattern(BSON("a" << 1 << "$b" << 1.0)), DBException);
+ ASSERT_THROWS(ShardKeyPattern(BSON("a" << 1 << "$**" << 1.0)), DBException);
}
TEST_F(ShardKeyPatternTest, NestedShardKeyPatternsValidtyCheck) {
@@ -98,6 +105,7 @@ TEST_F(ShardKeyPatternTest, NestedShardKeyPatternsValidtyCheck) {
ShardKeyPattern s2(BSON("a.b.c.d" << 1.0));
ShardKeyPattern s3(BSON("a" << 1 << "c.d" << 1.0 << "e.f.g" << 1.0f));
ShardKeyPattern s4(BSON("a" << 1 << "a.b" << 1.0 << "a.b.c" << 1.0f));
+ ShardKeyPattern s6(BSON("a.b$" << 1));
ASSERT_THROWS(ShardKeyPattern(BSON("a.b" << -1)), DBException);
ASSERT_THROWS(ShardKeyPattern(BSON("a" << BSON("b" << 1))), DBException);
@@ -106,6 +114,9 @@ TEST_F(ShardKeyPatternTest, NestedShardKeyPatternsValidtyCheck) {
ASSERT_THROWS(ShardKeyPattern(BSON("a..b" << 1)), DBException);
ASSERT_THROWS(ShardKeyPattern(BSON("a" << 1 << "a.b." << 1.0)), DBException);
ASSERT_THROWS(ShardKeyPattern(BSON("a" << BSON("b" << 1) << "c.d" << 1.0)), DBException);
+ ASSERT_THROWS(ShardKeyPattern(BSON("a.$" << 1)), DBException);
+ ASSERT_THROWS(ShardKeyPattern(BSON("a.$b" << 1)), DBException);
+ ASSERT_THROWS(ShardKeyPattern(BSON("a.$**" << 1)), DBException);
}
TEST_F(ShardKeyPatternTest, IsShardKey) {