diff options
author | Nick Zolnierz <nicholas.zolnierz@mongodb.com> | 2018-10-11 17:55:57 -0400 |
---|---|---|
committer | Nick Zolnierz <nicholas.zolnierz@mongodb.com> | 2018-10-11 17:55:57 -0400 |
commit | 11cd6942b2ec32e9079ad1b979c6eba544beda41 (patch) | |
tree | f40388d0784ace38c0518a6ed5b5879c7f2a7d3a | |
parent | eb8d989fd8d5b628b7e424953072a3d2ef852a1f (diff) | |
download | mongo-11cd6942b2ec32e9079ad1b979c6eba544beda41.tar.gz |
SERVER-37058: Update with numeric field names inside an array can cause validation to fail
-rw-r--r-- | jstests/core/update_numeric_field_name.js | 29 | ||||
-rw-r--r-- | src/mongo/db/update_index_data.cpp | 25 | ||||
-rw-r--r-- | src/mongo/db/update_index_data_test.cpp | 22 |
3 files changed, 75 insertions, 1 deletions
diff --git a/jstests/core/update_numeric_field_name.js b/jstests/core/update_numeric_field_name.js new file mode 100644 index 00000000000..bc165a7ca78 --- /dev/null +++ b/jstests/core/update_numeric_field_name.js @@ -0,0 +1,29 @@ +// Test that update operations correctly fail if they violate the "ambiguous field name in array" +// constraint for indexes. This is designed to reproduce SERVER-37058. +(function() { + "use strict"; + + const coll = db.coll; + coll.drop(); + + assert.writeOK(coll.insert({_id: 0, 'a': [{}]})); + assert.commandWorked(coll.createIndex({'a.0.c': 1})); + + // Attempt to insert a field name '0'. The first '0' refers to the first element of the array + // 'a'. + assert.writeErrorWithCode(coll.update({_id: 0}, {$set: {'a.0.0': 1}}), 16746); + + // Verify that the indexes were not affected. + let res = assert.commandWorked(coll.validate(true)); + assert(res.valid, tojson(res)); + + assert.writeErrorWithCode(coll.update({_id: 0}, {$set: {'a.0.0.b': 1}}), 16746); + res = assert.commandWorked(coll.validate(true)); + assert(res.valid, tojson(res)); + + // An update which does not violate the ambiguous field name in array constraint should succeed. + assert.writeOK(coll.update({_id: 0}, {$set: {'a.1.b.0.0': 1}})); + + res = assert.commandWorked(coll.validate(true)); + assert(res.valid, tojson(res)); +})(); diff --git a/src/mongo/db/update_index_data.cpp b/src/mongo/db/update_index_data.cpp index 2b1144d40a3..ade76f0c50d 100644 --- a/src/mongo/db/update_index_data.cpp +++ b/src/mongo/db/update_index_data.cpp @@ -149,12 +149,35 @@ bool getCanonicalIndexField(StringData fullName, string* out) { while (j + 1 < fullName.size() && isdigit(fullName[j + 1])) j++; - if (j + 1 == fullName.size() || fullName[j + 1] == '.') { + if (j + 1 == fullName.size()) { // only digits found, skip forward i = j; modified = true; continue; } + + // Check for consecutive digits separated by a period. + if (fullName[j + 1] == '.') { + // Peek ahead to see if the next set of characters are also numeric. + size_t k = j + 2; + while (k < fullName.size() && isdigit(fullName[k])) { + k++; + } + + // The second set of digits may end at the end of the path or a '.'. + if (k == fullName.size() || fullName[k] == '.') { + // Found consecutive numerical path components. Since this implies a numeric + // field name, return the prefix as the canonical index field. This is meant to + // fix SERVER-37058. + modified = true; + break; + } + + // Only one numerical path component, skip forward. + i = j; + modified = true; + continue; + } } buf << c; diff --git a/src/mongo/db/update_index_data_test.cpp b/src/mongo/db/update_index_data_test.cpp index 167cb632ab1..f01f495ed1f 100644 --- a/src/mongo/db/update_index_data_test.cpp +++ b/src/mongo/db/update_index_data_test.cpp @@ -126,4 +126,26 @@ TEST(UpdateIndexDataTest, getCanonicalIndexField1) { ASSERT_FALSE(getCanonicalIndexField("a.", &x)); } + +TEST(UpdateIndexDataTest, CanonicalIndexFieldForConsecutiveDigits) { + std::string indexField; + + ASSERT_TRUE(getCanonicalIndexField("a.0.0", &indexField)); + ASSERT_EQ(indexField, "a"); + + ASSERT_TRUE(getCanonicalIndexField("a.55.01", &indexField)); + ASSERT_EQ(indexField, "a"); + + ASSERT_TRUE(getCanonicalIndexField("a.0.0.b.1", &indexField)); + ASSERT_EQ(indexField, "a"); + + ASSERT_TRUE(getCanonicalIndexField("a.0b.1", &indexField)); + ASSERT_EQ(indexField, "a.0b"); + + ASSERT_TRUE(getCanonicalIndexField("a.0.b.1.2", &indexField)); + ASSERT_EQ(indexField, "a.b"); + + ASSERT_TRUE(getCanonicalIndexField("a.0.11b", &indexField)); + ASSERT_EQ(indexField, "a.11b"); +} } |