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:13:54 +0000
commitd21dedda973c25da704c9ad416d20bdcf384892f (patch)
treeb5e68562c95912a9a7a93e56a280797e7c5532ee
parent5b725ff6887b53984bda3c59aad26b47fdd8d535 (diff)
downloadmongo-d21dedda973c25da704c9ad416d20bdcf384892f.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 7377ae15eea..61337bb931d 100644
--- a/etc/backports_required_for_multiversion_tests.yml
+++ b/etc/backports_required_for_multiversion_tests.yml
@@ -250,6 +250,8 @@ last-continuous:
ticket: SERVER-72620
- test_file: jstests/core/timeseries/bucket_unpacking_with_sort_extended_range.js
ticket: SERVER-73110
+ - test_file: jstests/sharding/shard_keys_with_dollar_sign.js
+ ticket: SERVER-74124
suites: null
last-lts:
all:
@@ -575,4 +577,6 @@ last-lts:
ticket: SERVER-72620
- test_file: jstests/core/timeseries/bucket_unpacking_with_sort_extended_range.js
ticket: SERVER-73110
+ - 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 c693528de08..65c81a8d447 100644
--- a/src/mongo/s/shard_key_pattern.cpp
+++ b/src/mongo/s/shard_key_pattern.cpp
@@ -90,6 +90,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 67ed42a41ad..cc9f9c273c5 100644
--- a/src/mongo/s/shard_key_pattern_test.cpp
+++ b/src/mongo/s/shard_key_pattern_test.cpp
@@ -99,6 +99,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);
@@ -111,6 +112,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) {
@@ -124,6 +128,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) {
@@ -131,6 +138,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);
@@ -139,6 +147,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) {