summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/mongo/db/SConscript1
-rw-r--r--src/mongo/db/namespace_string.cpp72
-rw-r--r--src/mongo/db/namespace_string.h24
-rw-r--r--src/mongo/db/namespace_string_test.cpp85
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"));