diff options
Diffstat (limited to 'src/mongo/db/catalog/database_holder.cpp')
-rw-r--r-- | src/mongo/db/catalog/database_holder.cpp | 100 |
1 files changed, 36 insertions, 64 deletions
diff --git a/src/mongo/db/catalog/database_holder.cpp b/src/mongo/db/catalog/database_holder.cpp index c780bcaf471..f4a2cf62970 100644 --- a/src/mongo/db/catalog/database_holder.cpp +++ b/src/mongo/db/catalog/database_holder.cpp @@ -35,15 +35,14 @@ #include "mongo/db/audit.h" #include "mongo/db/auth/auth_index_d.h" #include "mongo/db/background.h" -#include "mongo/db/catalog/database.h" -#include "mongo/db/catalog/database_catalog_entry.h" #include "mongo/db/client.h" #include "mongo/db/clientcursor.h" -#include "mongo/db/operation_context.h" +#include "mongo/db/catalog/database.h" +#include "mongo/db/catalog/database_catalog_entry.h" #include "mongo/db/service_context.h" +#include "mongo/db/operation_context.h" #include "mongo/db/storage/storage_engine.h" #include "mongo/util/log.h" -#include "mongo/util/scopeguard.h" namespace mongo { @@ -92,83 +91,56 @@ Database* DatabaseHolder::get(OperationContext* txn, StringData ns) const { return NULL; } -std::set<std::string> DatabaseHolder::_getNamesWithConflictingCasing_inlock(StringData name) { - std::set<std::string> duplicates; - - for (const auto& nameAndPointer : _dbs) { - // A name that's equal with case-insensitive match must be identical, or it's a duplicate. - if (name.equalCaseInsensitive(nameAndPointer.first) && name != nameAndPointer.first) - duplicates.insert(nameAndPointer.first); - } - return duplicates; -} - -std::set<std::string> DatabaseHolder::getNamesWithConflictingCasing(StringData name) { - stdx::lock_guard<SimpleMutex> lk(_m); - return _getNamesWithConflictingCasing_inlock(name); -} - Database* DatabaseHolder::openDb(OperationContext* txn, StringData ns, bool* justCreated) { const StringData dbname = _todb(ns); invariant(txn->lockState()->isDbLockedForMode(dbname, MODE_X)); - if (justCreated) - *justCreated = false; // Until proven otherwise. - - stdx::unique_lock<SimpleMutex> lk(_m); + Database* db = get(txn, ns); + if (db) { + if (justCreated) { + *justCreated = false; + } - // The following will insert a nullptr for dbname, which will treated the same as a non- - // existant database by the get method, yet still counts in getNamesWithConflictingCasing. - if (auto db = _dbs[dbname]) return db; + } + + // Check casing + const string duplicate = Database::duplicateUncasedName(dbname.toString()); + if (!duplicate.empty()) { + stringstream ss; + ss << "db already exists with different case already have: [" << duplicate + << "] trying to create [" << dbname.toString() << "]"; + uasserted(ErrorCodes::DatabaseDifferCase, ss.str()); + } - // We've inserted a nullptr entry for dbname: make sure to remove it on unsuccessful exit. - auto removeDbGuard = MakeGuard([this, &lk, dbname] { - if (!lk.owns_lock()) - lk.lock(); - _dbs.erase(dbname); - }); - - // Check casing in lock to avoid transient duplicates. - auto duplicates = _getNamesWithConflictingCasing_inlock(dbname); - uassert(ErrorCodes::DatabaseDifferCase, - str::stream() << "db already exists with different case already have: [" - << *duplicates.cbegin() - << "] trying to create [" - << dbname.toString() - << "]", - duplicates.empty()); - - - // Do the catalog lookup and database creation outside of the scoped lock, because these may - // block. Only one thread can be inside this method for the same DB name, because of the - // requirement for X-lock on the database when we enter. So there is no way we can insert two - // different databases for the same name. - lk.unlock(); StorageEngine* storageEngine = getGlobalServiceContext()->getGlobalStorageEngine(); - DatabaseCatalogEntry* entry = storageEngine->getDatabaseCatalogEntry(txn, dbname); + invariant(storageEngine); - if (!entry->exists()) { + DatabaseCatalogEntry* entry = storageEngine->getDatabaseCatalogEntry(txn, dbname); + invariant(entry); + const bool exists = entry->exists(); + if (!exists) { audit::logCreateDatabase(&cc(), dbname); - if (justCreated) - *justCreated = true; } - auto newDb = stdx::make_unique<Database>(txn, dbname, entry); + if (justCreated) { + *justCreated = !exists; + } - // Finally replace our nullptr entry with the new Database pointer. - removeDbGuard.Dismiss(); - lk.lock(); - auto it = _dbs.find(dbname); - invariant(it != _dbs.end() && it->second == nullptr); - Database* newDbPointer = newDb.release(); - _dbs[dbname] = newDbPointer; - invariant(_getNamesWithConflictingCasing_inlock(dbname.toString()).empty()); + // Do this outside of the scoped lock, because database creation does transactional + // operations which may block. Only one thread can be inside this method for the same DB + // name, because of the requirement for X-lock on the database when we enter. So there is + // no way we can insert two different databases for the same name. + db = new Database(txn, dbname, entry); + + stdx::lock_guard<SimpleMutex> lk(_m); + _dbs[dbname] = db; - return newDbPointer; + return db; } void DatabaseHolder::close(OperationContext* txn, StringData ns) { + // TODO: This should be fine if only a DB X-lock invariant(txn->lockState()->isW()); const StringData dbName = _todb(ns); |