summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/mongo/db/database_name.h131
-rw-r--r--src/mongo/db/namespace_string.h112
-rw-r--r--src/mongo/db/namespace_string_bm.cpp22
-rw-r--r--src/mongo/db/namespace_string_test.cpp94
-rw-r--r--src/mongo/util/namespace_string_util_test.cpp23
5 files changed, 300 insertions, 82 deletions
diff --git a/src/mongo/db/database_name.h b/src/mongo/db/database_name.h
index a16c9a0092d..f728c691362 100644
--- a/src/mongo/db/database_name.h
+++ b/src/mongo/db/database_name.h
@@ -79,7 +79,7 @@ public:
return *_dbName;
}
- private:
+ protected:
StringData _db;
mutable std::once_flag _once;
mutable const DatabaseName* _dbName = nullptr;
@@ -102,7 +102,7 @@ public:
return _get().toString();
}
- private:
+ protected:
const DatabaseName& _get() const {
return _sharedState->get();
}
@@ -123,6 +123,26 @@ public:
DatabaseName() = default;
/**
+ * Construct a new DatabaseName from a reference. This reference could be a NamespaceString so
+ * only use the discriminator, tenant id and database name from its data.
+ */
+ DatabaseName(const DatabaseName& dbName)
+ : _data(dbName.view().substr(0, dbName.sizeWithTenant() + kDataOffset)) {}
+
+ DatabaseName(DatabaseName&& dbName) = default;
+
+ DatabaseName& operator=(DatabaseName&& dbName) = default;
+
+ /**
+ * Copy assignment operator. dbName could be a NamespaceString so only use the discriminator,
+ * tenant id and database name from its data.
+ */
+ DatabaseName& operator=(const DatabaseName& dbName) {
+ _data = dbName.view().substr(0, dbName.sizeWithTenant() + kDataOffset).toString();
+ return *this;
+ }
+
+ /**
* This function constructs a DatabaseName without checking for presence of TenantId.
*
* MUST only be used for tests.
@@ -133,16 +153,18 @@ public:
}
boost::optional<TenantId> tenantId() const {
- if (!_hasTenantId()) {
+ if (!hasTenantId()) {
return boost::none;
}
return TenantId{OID::from(&_data[kDataOffset])};
}
+ /**
+ * Returns the size of the name of this Database.
+ */
size_t size() const {
- auto offset = _hasTenantId() ? kDataOffset + OID::kOIDSize : kDataOffset;
- return _data.size() - offset;
+ return static_cast<uint8_t>(_data.front()) & kDatabaseNameOffsetEndMask;
}
bool isEmpty() const {
@@ -253,31 +275,31 @@ public:
return toString();
}
+ /**
+ * Returns true if the db names of `this` and `other` are equal, ignoring case, *and* they both
+ * refer to the same tenant ID (or none).
+ * The tenant comparison *is* case-sensitive.
+ */
bool equalCaseInsensitive(const DatabaseName& other) const {
- return StringData{_data.data() + kDataOffset, _data.size() - kDataOffset}
- .equalCaseInsensitive(
- StringData{other._data.data() + kDataOffset, other._data.size() - kDataOffset});
+ return tenantIdView() == other.tenantIdView() && db().equalCaseInsensitive(other.db());
}
int compare(const DatabaseName& other) const {
- if (_hasTenantId() && !other._hasTenantId()) {
+ if (hasTenantId() && !other.hasTenantId()) {
return 1;
}
- if (other._hasTenantId() && !_hasTenantId()) {
+ if (other.hasTenantId() && !hasTenantId()) {
return -1;
}
- return StringData{_data.data() + kDataOffset, _data.size() - kDataOffset}.compare(
- StringData{other._data.data() + kDataOffset, other._data.size() - kDataOffset});
+ return StringData{_data.data() + kDataOffset, sizeWithTenant()}.compare(
+ StringData{other._data.data() + kDataOffset, other.sizeWithTenant()});
}
friend bool operator==(const DatabaseName& lhs, const DatabaseName& rhs) {
- return lhs._data == rhs._data;
- }
-
- friend bool operator!=(const DatabaseName& lhs, const DatabaseName& rhs) {
- return lhs._data != rhs._data;
+ return lhs.view().substr(kDataOffset, lhs.sizeWithTenant()) ==
+ rhs.view().substr(kDataOffset, rhs.sizeWithTenant());
}
friend bool operator<(const DatabaseName& lhs, const DatabaseName& rhs) {
@@ -298,14 +320,17 @@ public:
template <typename H>
friend H AbslHashValue(H h, const DatabaseName& obj) {
- return H::combine(std::move(h), obj._data);
+ // _data might contain a collection : only hash the discriminator, tenant and database.
+ return H::combine(
+ std::move(h),
+ std::string_view{obj.view().substr(0, obj.sizeWithTenant() + kDataOffset)});
}
friend auto logAttrs(const DatabaseName& obj) {
return "db"_attr = obj;
}
-private:
+protected:
friend class NamespaceString;
friend class NamespaceStringOrUUID;
friend class DatabaseNameUtil;
@@ -315,6 +340,34 @@ private:
friend class AuthName;
/**
+ * Returns the size of the optional tenant id.
+ */
+ size_t tenantIdSize() const {
+ return hasTenantId() ? OID::kOIDSize : 0;
+ }
+
+ /**
+ * Returns the size of database name plus the size of the tenant.
+ */
+ size_t sizeWithTenant() const {
+ return size() + tenantIdSize();
+ }
+
+ /**
+ * Returns the offset from the start of _data to the start of the database name.
+ */
+ size_t dbNameOffsetStart() const {
+ return kDataOffset + tenantIdSize();
+ }
+
+ /**
+ * Returns a view of the internal string.
+ */
+ StringData view() const {
+ return _data;
+ }
+
+ /**
* Constructs a DatabaseName from the given tenantId and database name.
* "dbString" is expected only consist of a db name. It is the caller's responsibility to ensure
* the dbString is a valid db name.
@@ -350,8 +403,15 @@ private:
}
StringData db() const {
- auto offset = _hasTenantId() ? kDataOffset + OID::kOIDSize : kDataOffset;
- return StringData{_data.data() + offset, _data.size() - offset};
+ return view().substr(dbNameOffsetStart(), size());
+ }
+
+ StringData tenantIdView() const {
+ if (!hasTenantId()) {
+ return {};
+ }
+
+ return view().substr(kDataOffset, OID::kOIDSize);
}
std::string toString() const {
@@ -359,7 +419,7 @@ private:
}
std::string toStringWithTenantId() const {
- if (_hasTenantId()) {
+ if (hasTenantId()) {
auto tenantId = TenantId{OID::from(&_data[kDataOffset])};
return str::stream() << tenantId.toString() << "_" << db();
}
@@ -371,7 +431,7 @@ private:
static constexpr uint8_t kTenantIdMask = 0x80;
static constexpr uint8_t kDatabaseNameOffsetEndMask = 0x7F;
- inline bool _hasTenantId() const {
+ bool hasTenantId() const {
return static_cast<uint8_t>(_data.front()) & kTenantIdMask;
}
@@ -379,7 +439,30 @@ private:
struct TrustedInitTag {};
DatabaseName(std::string data, TrustedInitTag) : _data(std::move(data)) {}
- // Same in-memory layout as NamespaceString, see documentation in its header
+ /**
+ * We pack all possible namespaces data into a string consisting of these concatenated parts:
+ *
+ * Length Name Description
+ * ---------------------------------------------------------------------------------------------
+ * 1 discriminator Consists of bit fields:
+ * [0..6] : dbSize : length of the database part (cannot exceed
+ * 64 characters). 7 bits.
+ * [7] : hasTenant : MSB. 1 if there is a tenant.
+ *
+ * 12 tenant ID OID that uniquely identify the tenant for this namespace.
+ * Optional, only if hasTenant == 1.
+ *
+ * dbSize database Database name, size equal to the dbSize component of the
+ * discriminator.
+ *
+ * 1 dot Dot character between the database and the collection.
+ * Optional, only if `this` is a NamespaceString and there is
+ * space left in the string.
+ *
+ * ... collection Collection name.
+ * Optional, only if `this` is a NamespaceString and there is space
+ * left in the string.
+ */
std::string _data{'\0'};
};
diff --git a/src/mongo/db/namespace_string.h b/src/mongo/db/namespace_string.h
index 0c5d929cb1d..3872e221b5c 100644
--- a/src/mongo/db/namespace_string.h
+++ b/src/mongo/db/namespace_string.h
@@ -62,7 +62,7 @@
namespace mongo {
-class NamespaceString {
+class NamespaceString : private DatabaseName {
public:
/**
* The NamespaceString reserved constants are actually this `ConstantProxy`
@@ -210,7 +210,34 @@ public:
/**
* Constructs a NamespaceString for the given database.
*/
- explicit NamespaceString(DatabaseName dbName) : _data(std::move(dbName._data)) {}
+ explicit NamespaceString(DatabaseName dbName)
+ : DatabaseName(std::move(dbName._data), DatabaseName::TrustedInitTag{}) {
+ // Given that NamespaceString is a DatabaseName, it is possible the `dbName` parameter is
+ // created from another `NamespaceString` and contains a collection. In this case we want to
+ // resize the underlying `data` to discard the previous collection.
+ const size_t expectedSize = DatabaseName::sizeWithTenant();
+ _data.resize(expectedSize + kDataOffset);
+ }
+
+ /**
+ * Construct a NamespaceString from a const reference. This constructor is required to avoid
+ * invoking DatabaseName(const DatabaseName&..) which would discard the collection from the
+ * underlying data.
+ */
+ NamespaceString(NamespaceString&& ns) = default;
+
+ NamespaceString(const NamespaceString& ns) : DatabaseName(ns._data, TrustedInitTag{}) {}
+
+ NamespaceString& operator=(NamespaceString&& ns) = default;
+
+ /**
+ * Copy assignment operator. This cannot be defaulted as we must avoid calling DatabaseName copy
+ * assignment operator which would discard the collection from _data.
+ */
+ NamespaceString& operator=(const NamespaceString& ns) {
+ _data = ns._data;
+ return *this;
+ }
/**
* Constructs a NamespaceString in the global config db, "config.<collName>".
@@ -358,7 +385,7 @@ public:
boost::optional<TenantId> tenantId() const {
- if (!_hasTenantId()) {
+ if (!hasTenantId()) {
return boost::none;
}
@@ -376,19 +403,15 @@ public:
* This function must only be used in unit tests.
*/
StringData db_forTest() const {
- auto offset = _hasTenantId() ? kDataOffset + OID::kOIDSize : kDataOffset;
- return StringData{_data.data() + offset, _dbNameOffsetEnd()};
+ return db_deprecated();
}
- DatabaseName dbName() const {
- auto offset = _hasTenantId() ? kDataOffset + OID::kOIDSize : kDataOffset;
- return DatabaseName{_data.substr(0, offset + _dbNameOffsetEnd()),
- DatabaseName::TrustedInitTag{}};
+ const DatabaseName& dbName() const {
+ return *this;
}
StringData coll() const {
- const auto offset =
- kDataOffset + _dbNameOffsetEnd() + 1 + (_hasTenantId() ? OID::kOIDSize : 0);
+ const auto offset = kDataOffset + dbSize() + 1 + tenantIdSize();
if (offset > _data.size()) {
return {};
}
@@ -458,13 +481,16 @@ public:
return nss.toStringWithTenantId();
}
+ /**
+ * Returns the size of the database and collection (including the 'dot').
+ */
size_t size() const {
- auto offset = _hasTenantId() ? kDataOffset + OID::kOIDSize : kDataOffset;
+ auto offset = kDataOffset + tenantIdSize();
return _data.size() - offset;
}
size_t dbSize() const {
- return _dbNameOffsetEnd();
+ return DatabaseName::size();
}
bool isEmpty() const {
@@ -541,8 +567,7 @@ public:
* foo.a = false
*/
bool isDbOnly() const {
- auto offset = _hasTenantId() ? kDataOffset + OID::kOIDSize : kDataOffset;
- return offset + _dbNameOffsetEnd() == _data.size();
+ return kDataOffset + DatabaseName::sizeWithTenant() == _data.size();
}
/**
@@ -761,11 +786,11 @@ public:
static bool validCollectionName(StringData coll);
int compare(const NamespaceString& other) const {
- if (_hasTenantId() && !other._hasTenantId()) {
+ if (hasTenantId() && !other.hasTenantId()) {
return 1;
}
- if (other._hasTenantId() && !_hasTenantId()) {
+ if (other.hasTenantId() && !hasTenantId()) {
return -1;
}
@@ -796,10 +821,6 @@ public:
return lhs._data == rhs._data;
}
- friend bool operator!=(const NamespaceString& lhs, const NamespaceString& rhs) {
- return lhs._data != rhs._data;
- }
-
friend bool operator<(const NamespaceString& lhs, const NamespaceString& rhs) {
return lhs.compare(rhs) < 0;
}
@@ -838,8 +859,8 @@ private:
* Constructs a NamespaceString from the fully qualified namespace named in "ns" and the
* tenantId. "ns" is NOT expected to contain the tenantId.
*/
- explicit NamespaceString(boost::optional<TenantId> tenantId, StringData ns)
- : _data(makeData(tenantId, ns)) {}
+ NamespaceString(boost::optional<TenantId> tenantId, StringData ns)
+ : DatabaseName(makeData(tenantId, ns), DatabaseName::TrustedInitTag{}) {}
/**
* Constructs a NamespaceString for the given database and collection names.
@@ -853,12 +874,13 @@ private:
"namespaces cannot have embedded null characters",
collectionName.find('\0') == std::string::npos);
- _data.resize(collectionName.empty() ? dbName._data.size()
- : dbName._data.size() + 1 + collectionName.size());
- std::memcpy(_data.data(), dbName._data.data(), dbName._data.size());
+ const size_t dbAndDiscriminatorSize = dbName.sizeWithTenant() + kDataOffset;
+ _data.resize(collectionName.empty() ? dbAndDiscriminatorSize
+ : dbAndDiscriminatorSize + 1 + collectionName.size());
+ std::memcpy(_data.data(), dbName._data.data(), dbAndDiscriminatorSize);
if (!collectionName.empty()) {
- *reinterpret_cast<uint8_t*>(_data.data() + dbName._data.size()) = '.';
- std::memcpy(_data.data() + dbName._data.size() + 1,
+ *reinterpret_cast<uint8_t*>(_data.data() + dbAndDiscriminatorSize) = '.';
+ std::memcpy(_data.data() + dbAndDiscriminatorSize + 1,
collectionName.rawData(),
collectionName.size());
}
@@ -870,14 +892,14 @@ private:
* NOT expected to contain a tenantId.
*/
NamespaceString(boost::optional<TenantId> tenantId, StringData db, StringData collectionName)
- : _data(makeData(tenantId, db, collectionName)) {}
+ : DatabaseName(makeData(tenantId, db, collectionName), DatabaseName::TrustedInitTag{}) {}
std::string toString() const {
return ns().toString();
}
std::string toStringWithTenantId() const {
- if (_hasTenantId()) {
+ if (hasTenantId()) {
return str::stream() << TenantId{OID::from(&_data[kDataOffset])} << "_" << ns();
}
@@ -889,7 +911,7 @@ private:
* test needing access to ns().
*/
StringData ns() const {
- auto offset = _hasTenantId() ? kDataOffset + OID::kOIDSize : kDataOffset;
+ auto offset = kDataOffset + tenantIdSize();
return StringData{_data.data() + offset, _data.size() - offset};
}
@@ -900,22 +922,13 @@ private:
* at the DatabaseNameUtil::serialize method which takes in a DatabaseName object.
*/
StringData db_deprecated() const {
- auto offset = _hasTenantId() ? kDataOffset + OID::kOIDSize : kDataOffset;
- return StringData{_data.data() + offset, _dbNameOffsetEnd()};
+ return dbName().db();
}
static constexpr size_t kDataOffset = sizeof(uint8_t);
static constexpr uint8_t kTenantIdMask = 0x80;
static constexpr uint8_t kDatabaseNameOffsetEndMask = 0x7F;
- inline bool _hasTenantId() const {
- return static_cast<uint8_t>(_data.front()) & kTenantIdMask;
- }
-
- inline size_t _dbNameOffsetEnd() const {
- return static_cast<uint8_t>(_data.front()) & kDatabaseNameOffsetEndMask;
- }
-
std::string makeData(boost::optional<TenantId> tenantId,
StringData db,
StringData collectionName) {
@@ -969,21 +982,6 @@ private:
return makeData(tenantId, ns.substr(0, dotIndex), ns.substr(dotIndex + 1, ns.size()));
}
-
- // In order to reduce the size of a NamespaceString, we pack all possible namespace data
- // into a single std::string with the following in-memory layout:
- //
- // 1 byte 12 byte optional tenant id remaining bytes
- // discriminator (see more below) namespace
- // |<------------->|<--------------------------->|<-------------------------------------->|
- // [---------------|----|----|----|----|----|----|----|----|----|----|----|----|----|----|]
- // 0 1 12 ??
- //
- // The MSB of the discriminator tells us whether a tenant id is present, and the remaining
- // bits store the offset of end of the databaes component of the namespace. Database names
- // must be 64 characters or shorter, so we can be confident the length will fit in three bits.
-
- std::string _data{'\0'};
};
/**
@@ -1020,7 +1018,7 @@ public:
/**
* Returns the database name.
*/
- DatabaseName dbName() const {
+ const DatabaseName& dbName() const {
if (holds_alternative<NamespaceString>(_nssOrUUID)) {
return get<NamespaceString>(_nssOrUUID).dbName();
}
diff --git a/src/mongo/db/namespace_string_bm.cpp b/src/mongo/db/namespace_string_bm.cpp
index 0a1f1448841..4a6799e92bb 100644
--- a/src/mongo/db/namespace_string_bm.cpp
+++ b/src/mongo/db/namespace_string_bm.cpp
@@ -97,6 +97,24 @@ void BM_CreateLongNsFromConstexpr(benchmark::State& state) {
}
}
+void BM_NamespaceStringShortDbName(benchmark::State& state) {
+ const auto dbName = "short"_sd;
+ NamespaceString ns = makeNS(dbName, kMapCollName);
+
+ for (auto _ : state) {
+ benchmark::DoNotOptimize(ns.dbName());
+ }
+}
+
+void BM_NamespaceStringLongDbName(benchmark::State& state) {
+ const std::string dbName(60, 'x');
+ NamespaceString ns = makeNS(dbName, kMapCollName);
+
+ for (auto _ : state) {
+ benchmark::DoNotOptimize(ns.dbName());
+ }
+}
+
void searchInMap(benchmark::State& state, const NamespaceString& ns) {
immutable::map<DatabaseName, std::shared_ptr<int>> dbMap;
@@ -149,6 +167,10 @@ BENCHMARK(BM_CreateShortNsFromConstexpr);
BENCHMARK(BM_CreateLongNsFromConstexpr);
+BENCHMARK(BM_NamespaceStringShortDbName);
+
+BENCHMARK(BM_NamespaceStringLongDbName);
+
BENCHMARK(BM_NamespaceStringShortDbNameMapLookupExist);
BENCHMARK(BM_NamespaceStringLongDbNameMapLookupExist);
diff --git a/src/mongo/db/namespace_string_test.cpp b/src/mongo/db/namespace_string_test.cpp
index 65490efe159..60c6634bb24 100644
--- a/src/mongo/db/namespace_string_test.cpp
+++ b/src/mongo/db/namespace_string_test.cpp
@@ -56,10 +56,18 @@ protected:
return NamespaceString(tenantId, ns);
}
+ NamespaceString makeNamespaceString(const DatabaseName& dbName) {
+ return NamespaceString(dbName);
+ }
+
NamespaceString makeNamespaceString(const DatabaseName& dbName, StringData coll) {
return NamespaceString(dbName, coll);
}
+ NamespaceString makeNamespaceString(StringData dbName, StringData coll) {
+ return NamespaceString(boost::none, dbName, coll);
+ }
+
NamespaceString makeNamespaceString(boost::optional<TenantId> tenantId,
StringData db,
StringData coll) {
@@ -306,6 +314,11 @@ TEST_F(NamespaceStringTest, CollectionValidNames) {
ASSERT(!NamespaceString::validCollectionName("a\0b"_sd));
}
+TEST_F(NamespaceStringTest, DbForSharding) {
+ ASSERT_EQ(makeNamespaceString("foo", "bar").db_forSharding(), "foo");
+ ASSERT_EQ(makeNamespaceString("foo", "").db_forSharding(), "foo");
+}
+
TEST_F(NamespaceStringTest, nsToDatabase1) {
ASSERT_EQUALS("foo", nsToDatabaseSubstring("foo.bar"));
ASSERT_EQUALS("foo", nsToDatabaseSubstring("foo"));
@@ -371,6 +384,8 @@ TEST_F(NamespaceStringTest, EmptyDbWithColl) {
NamespaceString nss = makeNamespaceString(boost::none, "", "coll");
ASSERT_EQ(nss.db_forTest(), StringData{});
ASSERT_EQ(nss.coll(), "coll");
+ ASSERT_EQ(nss.dbName(), DatabaseName::kEmpty);
+ ASSERT_EQ(nss.dbName().compare(DatabaseName::kEmpty), 0);
}
TEST_F(NamespaceStringTest, NSSWithTenantId) {
@@ -379,13 +394,17 @@ TEST_F(NamespaceStringTest, NSSWithTenantId) {
{
std::string tenantNsStr = str::stream() << tenantId.toString() << "_foo.bar";
NamespaceString nss = makeNamespaceString(tenantId, "foo.bar");
+ DatabaseName db = DatabaseName::createDatabaseName_forTest(tenantId, "foo");
+
ASSERT_EQ(nss.size(), 7);
ASSERT_EQ(nss.ns_forTest(), "foo.bar");
ASSERT_EQ(nss.toString_forTest(), "foo.bar");
ASSERT_EQ(nss.toStringWithTenantId_forTest(), tenantNsStr);
ASSERT_EQ(nss.db_forTest(), "foo");
ASSERT_EQ(nss.coll(), "bar");
+ ASSERT_EQ(nss.dbName(), db);
ASSERT_EQ(nss.dbName().toString_forTest(), "foo");
+ ASSERT_EQ(nss.dbName().size(), db.size());
ASSERT_EQ(nss.size(), 7);
ASSERT(nss.tenantId());
ASSERT(nss.dbName().tenantId());
@@ -607,6 +626,80 @@ TEST_F(NamespaceStringTest, EmptyNamespaceString) {
ASSERT_EQ(emptyNss.toStringForErrorMsg(), "");
}
+TEST_F(NamespaceStringTest, NsFromDbOnly) {
+ DatabaseName db = DatabaseName::createDatabaseName_forTest(boost::none, "testdb");
+ NamespaceString ns(db);
+
+ ASSERT_EQ(ns.size(), 6);
+ ASSERT_EQ(ns, makeNamespaceString(boost::none, "testdb", ""));
+}
+
+TEST_F(NamespaceStringTest, ConstRefAssignmentOperator) {
+ NamespaceString expected{makeNamespaceString("foo", "bar")};
+
+ NamespaceString actual;
+ actual = expected;
+
+ ASSERT_EQ(actual, expected);
+ ASSERT_EQ(actual.coll(), "bar");
+}
+
+// Verify we can create a new NamespaceString with a DatabaseName created by ns.dbName(). We must
+// ensure we discard the collection from `ns` and we don't end up with `db.collection.collection`.
+TEST_F(NamespaceStringTest, NamespaceToDatabaseRoundtrip) {
+ auto dbName = "test"_sd;
+ auto collName = "foo"_sd;
+ auto otherCollName = "othername"_sd;
+
+ NamespaceString ns = makeNamespaceString(boost::none, dbName, collName);
+ NamespaceString nsDbOnly =
+ makeNamespaceString(DatabaseName::createDatabaseName_forTest(boost::none, dbName));
+ NamespaceString nsWithOtherColl = makeNamespaceString(boost::none, dbName, otherCollName);
+
+ auto runChecks = [&](const DatabaseName& db) {
+ ASSERT_EQ(db.size(), 4);
+
+ auto nsWithEmptyColl = makeNamespaceString(db);
+ ASSERT_EQ(nsWithEmptyColl, nsDbOnly);
+ ASSERT_EQ(nsWithEmptyColl.compare(nsDbOnly), 0);
+
+ auto newNs = makeNamespaceString(db, collName);
+ ASSERT_EQ(newNs.size(), 8);
+ ASSERT_EQ(ns, newNs);
+ ASSERT_EQ(ns.compare(newNs), 0);
+
+ auto anotherColl = makeNamespaceString(db, otherCollName);
+ ASSERT_EQ(anotherColl.size(), 14);
+ ASSERT_EQ(anotherColl.coll(), otherCollName);
+ ASSERT_EQ(anotherColl, nsWithOtherColl);
+ ASSERT_EQ(anotherColl.compare(nsWithOtherColl), 0);
+ };
+
+ const DatabaseName& dbRef = ns.dbName();
+ runChecks(dbRef);
+
+ const DatabaseName dbVal(ns.dbName());
+ runChecks(dbVal);
+}
+
+TEST_F(NamespaceStringTest, HashTest) {
+ auto ns = NamespaceString::createNamespaceString_forTest(boost::none, "foo", "bar");
+ auto ns2 = NamespaceString::createNamespaceString_forTest(boost::none, "foo", "bar");
+
+ auto db = DatabaseName::createDatabaseName_forTest(boost::none, "foo");
+
+ using NsHash = absl::Hash<NamespaceString>;
+ using DbHash = absl::Hash<DatabaseName>;
+
+ ASSERT_EQ(DbHash()(db), DbHash()(ns.dbName()));
+ ASSERT_EQ(NsHash()(ns), NsHash()(ns2));
+ ASSERT_EQ(NsHash()(ns2),
+ NsHash()(NamespaceString::createNamespaceString_forTest(ns.dbName(), "bar")));
+
+ ASSERT_EQ(NsHash()(NamespaceString(ns.dbName())),
+ NsHash()(NamespaceString::createNamespaceString_forTest(boost::none, "foo", "")));
+}
+
TEST_F(NamespaceStringTest, isDbOnly) {
TenantId tenantId(OID::gen());
@@ -636,7 +729,6 @@ TEST_F(NamespaceStringTest, isDbOnly) {
DatabaseName::createDatabaseName_forTest(tenantId, "foo"), "bar")};
for (const auto& nss : dbOnlyNamespaces) {
- std::cout << "nss=" << nss.toStringForErrorMsg() << std::endl;
ASSERT_TRUE(nss.isDbOnly());
}
diff --git a/src/mongo/util/namespace_string_util_test.cpp b/src/mongo/util/namespace_string_util_test.cpp
index 0e226c86ddd..260699095ed 100644
--- a/src/mongo/util/namespace_string_util_test.cpp
+++ b/src/mongo/util/namespace_string_util_test.cpp
@@ -36,6 +36,7 @@
#include "mongo/bson/bsonobjbuilder.h"
#include "mongo/bson/oid.h"
#include "mongo/db/namespace_string.h"
+#include "mongo/db/tenant_id.h"
#include "mongo/idl/server_parameter_test_util.h"
#include "mongo/unittest/assert.h"
#include "mongo/unittest/framework.h"
@@ -156,6 +157,28 @@ TEST(NamespaceStringUtilTest,
ASSERT_EQ(nss.dbName().toString_forTest(), dbNameStr);
}
+TEST(NamespaceStringUtilTest, NamespaceStringToDatabaseNameRoundTrip) {
+ struct Scenario {
+ bool multitenancy;
+ boost::optional<TenantId> tenant;
+ std::string database;
+ };
+
+ for (auto& scenario : {
+ Scenario{false, boost::none, "foo"},
+ Scenario{true, boost::none, "config"},
+ Scenario{true, TenantId{OID::gen()}, "foo"},
+ }) {
+ RAIIServerParameterControllerForTest mc("multitenancySupport", scenario.multitenancy);
+
+ auto expected = NamespaceString::createNamespaceString_forTest(
+ scenario.tenant, scenario.database, "bar");
+ auto actual = NamespaceStringUtil::deserialize(expected.dbName(), "bar");
+
+ ASSERT_EQ(actual, expected);
+ }
+}
+
// Deserialize NamespaceString when multitenancySupport and featureFlagRequireTenantID are disabled.
TEST(NamespaceStringUtilTest, DeserializeMultitenancySupportOffFeatureFlagRequireTenantIDOff) {
RAIIServerParameterControllerForTest multitenanyController("multitenancySupport", false);