diff options
-rw-r--r-- | src/mongo/db/SConscript | 1 | ||||
-rw-r--r-- | src/mongo/db/namespace_string.cpp | 72 | ||||
-rw-r--r-- | src/mongo/db/namespace_string.h | 24 | ||||
-rw-r--r-- | src/mongo/db/namespace_string_test.cpp | 85 |
4 files changed, 182 insertions, 0 deletions
diff --git a/src/mongo/db/SConscript b/src/mongo/db/SConscript index 2ca28fe8c39..a998f4c8c64 100644 --- a/src/mongo/db/SConscript +++ b/src/mongo/db/SConscript @@ -262,6 +262,7 @@ env.Library( ], LIBDEPS=[ '$BUILD_DIR/mongo/base', + '$BUILD_DIR/mongo/db/repl/optime', ], ) diff --git a/src/mongo/db/namespace_string.cpp b/src/mongo/db/namespace_string.cpp index 0a453546a61..d86878e4aef 100644 --- a/src/mongo/db/namespace_string.cpp +++ b/src/mongo/db/namespace_string.cpp @@ -34,6 +34,7 @@ #include "mongo/db/namespace_string.h" +#include "mongo/base/parse_number.h" #include "mongo/util/mongoutils/str.h" namespace mongo { @@ -74,6 +75,7 @@ const char kConfigCollection[] = "admin.system.version"; constexpr auto listCollectionsCursorCol = "$cmd.listCollections"_sd; constexpr auto listIndexesCursorNSPrefix = "$cmd.listIndexes."_sd; +constexpr auto dropPendingNSPrefix = "system.drop."_sd; } // namespace @@ -148,6 +150,76 @@ boost::optional<NamespaceString> NamespaceString::getTargetNSForGloballyManagedN return NamespaceString{db(), coll().substr(indexOfNextDot + 1)}; } +bool NamespaceString::isDropPendingNamespace() const { + return coll().startsWith(dropPendingNSPrefix); +} + +NamespaceString NamespaceString::makeDropPendingNamespace(const repl::OpTime& opTime) const { + mongo::StringBuilder ss; + ss << db() << "." << dropPendingNSPrefix; + ss << opTime.getSecs() << "i" << opTime.getTimestamp().getInc() << "t" << opTime.getTerm(); + ss << "." << coll(); + return NamespaceString(ss.stringData().substr(0, MaxNsCollectionLen)); +} + +StatusWith<repl::OpTime> NamespaceString::getDropPendingNamespaceOpTime() const { + if (!isDropPendingNamespace()) { + return Status(ErrorCodes::BadValue, + str::stream() << "Not a drop-pending namespace: " << _ns); + } + + auto collectionName = coll(); + auto opTimeBeginIndex = dropPendingNSPrefix.size(); + auto opTimeEndIndex = collectionName.find('.', opTimeBeginIndex); + auto opTimeStr = std::string::npos == opTimeEndIndex + ? collectionName.substr(opTimeBeginIndex) + : collectionName.substr(opTimeBeginIndex, opTimeEndIndex - opTimeBeginIndex); + + auto incrementSeparatorIndex = opTimeStr.find('i'); + if (std::string::npos == incrementSeparatorIndex) { + return Status(ErrorCodes::FailedToParse, + str::stream() << "Missing 'i' separator in drop-pending namespace: " << _ns); + } + + auto termSeparatorIndex = opTimeStr.find('t', incrementSeparatorIndex); + if (std::string::npos == termSeparatorIndex) { + return Status(ErrorCodes::FailedToParse, + str::stream() << "Missing 't' separator in drop-pending namespace: " << _ns); + } + + long long seconds; + auto status = parseNumberFromString(opTimeStr.substr(0, incrementSeparatorIndex), &seconds); + if (!status.isOK()) { + return Status( + status.code(), + str::stream() << "Invalid timestamp seconds in drop-pending namespace: " << _ns << ": " + << status.reason()); + } + + unsigned int increment; + status = + parseNumberFromString(opTimeStr.substr(incrementSeparatorIndex + 1, + termSeparatorIndex - (incrementSeparatorIndex + 1)), + &increment); + if (!status.isOK()) { + return Status(status.code(), + str::stream() << "Invalid timestamp increment in drop-pending namespace: " + << _ns + << ": " + << status.reason()); + } + + long long term; + status = mongo::parseNumberFromString(opTimeStr.substr(termSeparatorIndex + 1), &term); + if (!status.isOK()) { + return Status(status.code(), + str::stream() << "Invalid term in drop-pending namespace: " << _ns << ": " + << status.reason()); + } + + return repl::OpTime(Timestamp(Seconds(seconds), increment), term); +} + string NamespaceString::escapeDbName(const StringData dbname) { std::string escapedDbName; diff --git a/src/mongo/db/namespace_string.h b/src/mongo/db/namespace_string.h index 9a88f67a9a3..ec7ce4953b8 100644 --- a/src/mongo/db/namespace_string.h +++ b/src/mongo/db/namespace_string.h @@ -35,7 +35,9 @@ #include <iosfwd> #include <string> +#include "mongo/base/status_with.h" #include "mongo/base/string_data.h" +#include "mongo/db/repl/optime.h" #include "mongo/platform/hash_namespace.h" #include "mongo/util/assert_util.h" @@ -222,6 +224,28 @@ public: boost::optional<NamespaceString> getTargetNSForGloballyManagedNamespace() const; /** + * Returns true if this namespace refers to a drop-pending collection. + */ + bool isDropPendingNamespace() const; + + /** + * Returns the drop-pending namespace name for this namespace, provided the given optime. + * + * Example: + * test.foo -> test.system.drop.<timestamp seconds>i<timestamp increment>t<term>.foo + * + * Original collection name may be truncated so that the generated namespace length does not + * exceed MaxNsCollectionLen. + */ + NamespaceString makeDropPendingNamespace(const repl::OpTime& opTime) const; + + /** + * Returns the optime used to generate the drop-pending namespace. + * Returns an error if this namespace is not drop-pending. + */ + StatusWith<repl::OpTime> getDropPendingNamespaceOpTime() const; + + /** * Given a NamespaceString for which isListIndexesCursorNS() returns true, returns the * NamespaceString for the collection that the "listIndexes" targets. */ diff --git a/src/mongo/db/namespace_string_test.cpp b/src/mongo/db/namespace_string_test.cpp index 3e4b489b508..9de487b1327 100644 --- a/src/mongo/db/namespace_string_test.cpp +++ b/src/mongo/db/namespace_string_test.cpp @@ -30,6 +30,7 @@ #include "mongo/unittest/unittest.h" #include "mongo/db/namespace_string.h" +#include "mongo/db/repl/optime.h" namespace mongo { @@ -208,6 +209,90 @@ TEST(NamespaceStringTest, GetTargetNSForGloballyManagedNamespace) { NamespaceString{"test.$cmd.otherCommand"}.getTargetNSForGloballyManagedNamespace()); } +TEST(NamespaceStringTest, IsDropPendingNamespace) { + ASSERT_TRUE(NamespaceString{"test.system.drop.0i0t-1.foo"}.isDropPendingNamespace()); + ASSERT_TRUE(NamespaceString{"test.system.drop.1234567i8t9.foo"}.isDropPendingNamespace()); + ASSERT_TRUE(NamespaceString{"test.system.drop.1234.foo"}.isDropPendingNamespace()); + ASSERT_TRUE(NamespaceString{"test.system.drop.foo"}.isDropPendingNamespace()); + + ASSERT_FALSE(NamespaceString{"test.system.drop"}.isDropPendingNamespace()); + ASSERT_FALSE(NamespaceString{"test.drop.1234.foo"}.isDropPendingNamespace()); + ASSERT_FALSE(NamespaceString{"test.drop.foo"}.isDropPendingNamespace()); + ASSERT_FALSE(NamespaceString{"test.foo"}.isDropPendingNamespace()); + ASSERT_FALSE(NamespaceString{"test.$cmd"}.isDropPendingNamespace()); + + ASSERT_FALSE(NamespaceString{"$cmd.aggregate.foo"}.isDropPendingNamespace()); + ASSERT_FALSE(NamespaceString{"$cmd.listCollections"}.isDropPendingNamespace()); +} + +TEST(NamespaceStringTest, MakeDropPendingNamespace) { + ASSERT_EQUALS(NamespaceString{"test.system.drop.0i0t-1.foo"}, + NamespaceString{"test.foo"}.makeDropPendingNamespace(repl::OpTime())); + ASSERT_EQUALS(NamespaceString{"test.system.drop.1234567i8t9.foo"}, + NamespaceString{"test.foo"}.makeDropPendingNamespace( + repl::OpTime(Timestamp(Seconds(1234567), 8U), 9LL))); + // If the collection name is too long to fit in the generated drop pending namespace, it will be + // truncated. + std::string dbName("test"); + std::string collName(std::size_t(NamespaceString::MaxNsCollectionLen) - dbName.size() - 1, 't'); + NamespaceString nss(dbName, collName); + auto dropPendingNss = + nss.makeDropPendingNamespace(repl::OpTime(Timestamp(Seconds(1234567), 8U), 9LL)); + ASSERT_EQUALS(std::size_t(NamespaceString::MaxNsCollectionLen), dropPendingNss.size()); +} + +TEST(NamespaceStringTest, GetDropPendingNamespaceOpTime) { + // Null optime is acceptable. + ASSERT_EQUALS( + repl::OpTime(), + unittest::assertGet( + NamespaceString{"test.system.drop.0i0t-1.foo"}.getDropPendingNamespaceOpTime())); + + // Valid optime. + ASSERT_EQUALS( + repl::OpTime(Timestamp(Seconds(1234567), 8U), 9LL), + unittest::assertGet( + NamespaceString{"test.system.drop.1234567i8t9.foo"}.getDropPendingNamespaceOpTime())); + + // Original collection name is optional. + ASSERT_EQUALS( + repl::OpTime(Timestamp(Seconds(1234567), 8U), 9LL), + unittest::assertGet( + NamespaceString{"test.system.drop.1234567i8t9"}.getDropPendingNamespaceOpTime())); + + // No system.drop. prefix. + ASSERT_EQUALS(ErrorCodes::BadValue, + NamespaceString{"test.1234.foo"}.getDropPendingNamespaceOpTime()); + + // Missing 'i' separator. + ASSERT_EQUALS(ErrorCodes::FailedToParse, + NamespaceString{"test.system.drop.1234t8.foo"}.getDropPendingNamespaceOpTime()); + + // Missing 't' separator. + ASSERT_EQUALS(ErrorCodes::FailedToParse, + NamespaceString{"test.system.drop.1234i56.foo"}.getDropPendingNamespaceOpTime()); + + // Timestamp seconds is not a number. + ASSERT_EQUALS( + ErrorCodes::FailedToParse, + NamespaceString{"test.system.drop.wwwi56t123.foo"}.getDropPendingNamespaceOpTime()); + + // Timestamp increment is not a number. + ASSERT_EQUALS( + ErrorCodes::FailedToParse, + NamespaceString{"test.system.drop.1234iaaat123.foo"}.getDropPendingNamespaceOpTime()); + + // Timestamp increment must be an unsigned number. + ASSERT_EQUALS( + ErrorCodes::FailedToParse, + NamespaceString{"test.system.drop.1234i-100t123.foo"}.getDropPendingNamespaceOpTime()); + + // Term is not a number. + ASSERT_EQUALS( + ErrorCodes::FailedToParse, + NamespaceString{"test.system.drop.1234i111taaa.foo"}.getDropPendingNamespaceOpTime()); +} + TEST(NamespaceStringTest, CollectionComponentValidNames) { ASSERT(NamespaceString::validCollectionComponent("a.b")); ASSERT(NamespaceString::validCollectionComponent("a.b")); |