diff options
author | Cheahuychou Mao <mao.cheahuychou@gmail.com> | 2023-02-22 21:35:58 +0000 |
---|---|---|
committer | Evergreen Agent <no-reply@evergreen.mongodb.com> | 2023-02-23 21:13:54 +0000 |
commit | d21dedda973c25da704c9ad416d20bdcf384892f (patch) | |
tree | b5e68562c95912a9a7a93e56a280797e7c5532ee | |
parent | 5b725ff6887b53984bda3c59aad26b47fdd8d535 (diff) | |
download | mongo-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.yml | 4 | ||||
-rw-r--r-- | jstests/sharding/shard_keys_with_dollar_sign.js | 57 | ||||
-rw-r--r-- | src/mongo/s/shard_key_pattern.cpp | 5 | ||||
-rw-r--r-- | src/mongo/s/shard_key_pattern_test.cpp | 11 |
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) { |