diff options
Diffstat (limited to 'src/mongo')
-rw-r--r-- | src/mongo/db/catalog/SConscript | 13 | ||||
-rw-r--r-- | src/mongo/db/catalog/collection.h | 48 | ||||
-rw-r--r-- | src/mongo/db/catalog/database_holder.cpp | 207 | ||||
-rw-r--r-- | src/mongo/db/catalog/database_holder.h | 95 | ||||
-rw-r--r-- | src/mongo/db/catalog/database_holder_impl.cpp | 249 | ||||
-rw-r--r-- | src/mongo/db/catalog/database_holder_impl.h | 98 |
6 files changed, 480 insertions, 230 deletions
diff --git a/src/mongo/db/catalog/SConscript b/src/mongo/db/catalog/SConscript index dd2e2ddebd7..0776c567bc3 100644 --- a/src/mongo/db/catalog/SConscript +++ b/src/mongo/db/catalog/SConscript @@ -92,6 +92,16 @@ env.Library( LIBDEPS=[ ], ) + +env.Library( + target='database_holder', + source=[ + "database_holder.cpp", + ], + LIBDEPS=[ + ], +) + env.Library( target='collection_info_cache', source=[ @@ -113,7 +123,7 @@ env.Library( "create_collection.cpp", "cursor_manager.cpp", "database_impl.cpp", - "database_holder.cpp", + "database_holder_impl.cpp", "drop_collection.cpp", "drop_database.cpp", "drop_indexes.cpp", @@ -127,6 +137,7 @@ env.Library( 'collection_info_cache', 'collection_options', 'database', + 'database_holder', 'index_catalog', 'index_catalog_entry', 'index_key_validate', diff --git a/src/mongo/db/catalog/collection.h b/src/mongo/db/catalog/collection.h index 5be5529499e..1f4bd4bf6bb 100644 --- a/src/mongo/db/catalog/collection.h +++ b/src/mongo/db/catalog/collection.h @@ -319,30 +319,6 @@ public: }; private: - std::unique_ptr<Impl> _pimpl; - - // This structure exists to give us a customization point to decide how to force users of this - // class to depend upon the corresponding `index_catalog_entry.cpp` Translation Unit (TU). All - // public forwarding functions call `_impl(), and `_impl` creates an instance of this structure. - struct TUHook { - static void hook() noexcept; - - explicit inline TUHook() noexcept { - if (kDebugBuild) - this->hook(); - } - }; - - inline const Impl& _impl() const { - TUHook{}; - return *this->_pimpl; - } - - inline Impl& _impl() { - TUHook{}; - return *this->_pimpl; - } - static std::unique_ptr<Impl> makeImpl(Collection* _this, OperationContext* opCtx, StringData fullNS, @@ -739,6 +715,30 @@ private: return this->_impl().recordStoreGoingToUpdateInPlace(opCtx, loc); } + // This structure exists to give us a customization point to decide how to force users of this + // class to depend upon the corresponding `collection.cpp` Translation Unit (TU). All public + // forwarding functions call `_impl(), and `_impl` creates an instance of this structure. + struct TUHook { + static void hook() noexcept; + + explicit inline TUHook() noexcept { + if (kDebugBuild) + this->hook(); + } + }; + + inline const Impl& _impl() const { + TUHook{}; + return *this->_pimpl; + } + + inline Impl& _impl() { + TUHook{}; + return *this->_pimpl; + } + + std::unique_ptr<Impl> _pimpl; + friend class DatabaseImpl; friend class IndexCatalogImpl; }; diff --git a/src/mongo/db/catalog/database_holder.cpp b/src/mongo/db/catalog/database_holder.cpp index bf13aea1860..0b0c4336662 100644 --- a/src/mongo/db/catalog/database_holder.cpp +++ b/src/mongo/db/catalog/database_holder.cpp @@ -1,5 +1,5 @@ /** - * Copyright (C) 2012-2014 MongoDB Inc. + * Copyright (C) 2017 MongoDB Inc. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, @@ -32,205 +32,36 @@ #include "mongo/db/catalog/database_holder.h" -#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/service_context.h" -#include "mongo/db/storage/storage_engine.h" -#include "mongo/util/log.h" -#include "mongo/util/scopeguard.h" - namespace mongo { -using std::set; -using std::string; -using std::stringstream; +DatabaseHolder::Impl::~Impl() = default; namespace { - -StringData _todb(StringData ns) { - size_t i = ns.find('.'); - if (i == std::string::npos) { - uassert(13074, "db name can't be empty", ns.size()); - return ns; - } - - uassert(13075, "db name can't be empty", i > 0); - - const StringData d = ns.substr(0, i); - uassert(13280, - "invalid db name: " + ns.toString(), - NamespaceString::validDBName(d, NamespaceString::DollarInDbNameBehavior::Allow)); - - return d; -} - - -DatabaseHolder _dbHolder; - +stdx::function<DatabaseHolder::factory_function_type> factory; } // namespace - -DatabaseHolder& dbHolder() { - return _dbHolder; -} - - -Database* DatabaseHolder::get(OperationContext* opCtx, StringData ns) const { - const StringData db = _todb(ns); - invariant(opCtx->lockState()->isDbLockedForMode(db, MODE_IS)); - - stdx::lock_guard<SimpleMutex> lk(_m); - DBs::const_iterator it = _dbs.find(db); - if (it != _dbs.end()) { - return it->second; - } - - 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; +void DatabaseHolder::registerFactory(decltype(factory) newFactory) { + factory = std::move(newFactory); } -std::set<std::string> DatabaseHolder::getNamesWithConflictingCasing(StringData name) { - stdx::lock_guard<SimpleMutex> lk(_m); - return _getNamesWithConflictingCasing_inlock(name); -} - -Database* DatabaseHolder::openDb(OperationContext* opCtx, StringData ns, bool* justCreated) { - const StringData dbname = _todb(ns); - invariant(opCtx->lockState()->isDbLockedForMode(dbname, MODE_X)); - - if (justCreated) - *justCreated = false; // Until proven otherwise. - - stdx::unique_lock<SimpleMutex> lk(_m); - - // 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; - - // 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(opCtx, dbname); - - if (!entry->exists()) { - audit::logCreateDatabase(&cc(), dbname); - if (justCreated) - *justCreated = true; - } - - auto newDb = stdx::make_unique<Database>(opCtx, dbname, entry); - - // 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); - it->second = newDb.release(); - invariant(_getNamesWithConflictingCasing_inlock(dbname.toString()).empty()); - - return it->second; +auto DatabaseHolder::makeImpl() -> std::unique_ptr<Impl> { + return factory(); } -void DatabaseHolder::close(OperationContext* opCtx, StringData ns, const std::string& reason) { - invariant(opCtx->lockState()->isW()); - - const StringData dbName = _todb(ns); - - stdx::lock_guard<SimpleMutex> lk(_m); +void DatabaseHolder::TUHook::hook() noexcept {} - DBs::const_iterator it = _dbs.find(dbName); - if (it == _dbs.end()) { - return; - } - - it->second->close(opCtx, reason); - delete it->second; - _dbs.erase(it); +namespace { +stdx::function<decltype(dbHolder)> dbHolderImpl; +} // namespace +} // namespace mongo - getGlobalServiceContext()->getGlobalStorageEngine()->closeDatabase(opCtx, dbName.toString()); +// The `mongo::` prefix is necessary to placate MSVC -- it is unable to properly identify anonymous +// nested namespace members in `decltype` expressions when defining functions using scope-resolution +// syntax. +void mongo::registerDbHolderImpl(decltype(mongo::dbHolderImpl) impl) { + dbHolderImpl = std::move(impl); } -bool DatabaseHolder::closeAll(OperationContext* opCtx, - BSONObjBuilder& result, - bool force, - const std::string& reason) { - invariant(opCtx->lockState()->isW()); - - stdx::lock_guard<SimpleMutex> lk(_m); - - set<string> dbs; - for (DBs::const_iterator i = _dbs.begin(); i != _dbs.end(); ++i) { - dbs.insert(i->first); - } - - BSONArrayBuilder bb(result.subarrayStart("dbs")); - int nNotClosed = 0; - for (set<string>::iterator i = dbs.begin(); i != dbs.end(); ++i) { - string name = *i; - - LOG(2) << "DatabaseHolder::closeAll name:" << name; - - if (!force && BackgroundOperation::inProgForDb(name)) { - log() << "WARNING: can't close database " << name - << " because a bg job is in progress - try killOp command"; - nNotClosed++; - continue; - } - - Database* db = _dbs[name]; - db->close(opCtx, reason); - delete db; - - _dbs.erase(name); - - getGlobalServiceContext()->getGlobalStorageEngine()->closeDatabase(opCtx, name); - - bb.append(name); - } - - bb.done(); - if (nNotClosed) { - result.append("nNotClosed", nNotClosed); - } - - return true; -} +auto mongo::dbHolder() -> DatabaseHolder& { + return dbHolderImpl(); } diff --git a/src/mongo/db/catalog/database_holder.h b/src/mongo/db/catalog/database_holder.h index ecde0496b76..58af1747218 100644 --- a/src/mongo/db/catalog/database_holder.h +++ b/src/mongo/db/catalog/database_holder.h @@ -1,5 +1,5 @@ /** - * Copyright (C) 2012-2014 MongoDB Inc. + * Copyright (C) 2017 MongoDB Inc. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, @@ -33,12 +33,12 @@ #include "mongo/base/string_data.h" #include "mongo/db/namespace_string.h" +#include "mongo/stdx/functional.h" #include "mongo/stdx/mutex.h" #include "mongo/util/concurrency/mutex.h" #include "mongo/util/string_map.h" namespace mongo { - class Database; class OperationContext; @@ -47,13 +47,43 @@ class OperationContext; */ class DatabaseHolder { public: - DatabaseHolder() = default; + class Impl { + public: + virtual ~Impl() = 0; + + virtual Database* get(OperationContext* opCtx, StringData ns) const = 0; + + virtual Database* openDb(OperationContext* opCtx, StringData ns, bool* justCreated) = 0; + + virtual void close(OperationContext* opCtx, StringData ns, const std::string& reason) = 0; + + virtual bool closeAll(OperationContext* opCtx, + BSONObjBuilder& result, + bool force, + const std::string& reason) = 0; + + virtual std::set<std::string> getNamesWithConflictingCasing(StringData name) = 0; + }; + +private: + static std::unique_ptr<Impl> makeImpl(); + +public: + using factory_function_type = decltype(makeImpl); + + static void registerFactory(stdx::function<factory_function_type> factory); + + inline ~DatabaseHolder() = default; + + inline explicit DatabaseHolder() : _pimpl(makeImpl()) {} /** * Retrieves an already opened database or returns NULL. Must be called with the database * locked in at least IS-mode. */ - Database* get(OperationContext* opCtx, StringData ns) const; + inline Database* get(OperationContext* const opCtx, const StringData ns) const { + return this->_impl().get(opCtx, ns); + } /** * Retrieves a database reference if it is already opened, or opens it if it hasn't been @@ -62,12 +92,20 @@ public: * @param justCreated Returns whether the database was newly created (true) or it already * existed (false). Can be NULL if this information is not necessary. */ - Database* openDb(OperationContext* opCtx, StringData ns, bool* justCreated = NULL); + inline Database* openDb(OperationContext* const opCtx, + const StringData ns, + bool* const justCreated = nullptr) { + return this->_impl().openDb(opCtx, ns, justCreated); + } /** * Closes the specified database. Must be called with the database locked in X-mode. */ - void close(OperationContext* opCtx, StringData ns, const std::string& reason); + inline void close(OperationContext* const opCtx, + const StringData ns, + const std::string& reason) { + return this->_impl().close(opCtx, ns, reason); + } /** * Closes all opened databases. Must be called with the global lock acquired in X-mode. @@ -76,23 +114,46 @@ public: * @param force Force close even if something underway - use at shutdown * @param reason The reason for close. */ - bool closeAll(OperationContext* opCtx, - BSONObjBuilder& result, - bool force, - const std::string& reason); + inline bool closeAll(OperationContext* const opCtx, + BSONObjBuilder& result, + const bool force, + const std::string& reason) { + return this->_impl().closeAll(opCtx, result, force, reason); + } /** * Returns the set of existing database names that differ only in casing. */ - std::set<std::string> getNamesWithConflictingCasing(StringData name); + inline std::set<std::string> getNamesWithConflictingCasing(const StringData name) { + return this->_impl().getNamesWithConflictingCasing(name); + } private: - std::set<std::string> _getNamesWithConflictingCasing_inlock(StringData name); + // This structure exists to give us a customization point to decide how to force users of this + // class to depend upon the corresponding `database_holder.cpp` Translation Unit (TU). All + // public forwarding functions call `_impl(), and `_impl` creates an instance of this structure. + struct TUHook { + static void hook() noexcept; + + explicit inline TUHook() noexcept { + if (kDebugBuild) + this->hook(); + } + }; + + inline const Impl& _impl() const { + TUHook{}; + return *this->_pimpl; + } + + inline Impl& _impl() { + TUHook{}; + return *this->_pimpl; + } - typedef StringMap<Database*> DBs; - mutable SimpleMutex _m; - DBs _dbs; + std::unique_ptr<Impl> _pimpl; }; -DatabaseHolder& dbHolder(); -} +extern DatabaseHolder& dbHolder(); +extern void registerDbHolderImpl(stdx::function<decltype(dbHolder)> impl); +} // namespace mongo diff --git a/src/mongo/db/catalog/database_holder_impl.cpp b/src/mongo/db/catalog/database_holder_impl.cpp new file mode 100644 index 00000000000..a9a7573eccc --- /dev/null +++ b/src/mongo/db/catalog/database_holder_impl.cpp @@ -0,0 +1,249 @@ +/** + * Copyright (C) 2017 MongoDB Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * As a special exception, the copyright holders give permission to link the + * code of portions of this program with the OpenSSL library under certain + * conditions as described in each individual source file and distribute + * linked combinations including the program with the OpenSSL library. You + * must comply with the GNU Affero General Public License in all respects for + * all of the code used other than as permitted herein. If you modify file(s) + * with this exception, you may extend this exception to your version of the + * file(s), but you are not obligated to do so. If you do not wish to do so, + * delete this exception statement from your version. If you delete this + * exception statement from all source files in the program, then also delete + * it in the license file. + */ + +#define MONGO_LOG_DEFAULT_COMPONENT ::mongo::logger::LogComponent::kStorage + +#include "mongo/platform/basic.h" + +#include "mongo/db/catalog/database_holder_impl.h" + +#include "mongo/base/init.h" +#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/service_context.h" +#include "mongo/db/storage/storage_engine.h" +#include "mongo/util/log.h" +#include "mongo/util/scopeguard.h" + +namespace mongo { +namespace { + +DatabaseHolder& dbHolderImpl() { + static DatabaseHolder _dbHolder; + return _dbHolder; +} + +MONGO_INITIALIZER_WITH_PREREQUISITES(InitializeDbHolderimpl, ("InitializeDatabaseHolderFactory")) +(InitializerContext* const) { + registerDbHolderImpl(dbHolderImpl); + return Status::OK(); +} + +MONGO_INITIALIZER(InitializeDatabaseHolderFactory)(InitializerContext* const) { + DatabaseHolder::registerFactory([] { return stdx::make_unique<DatabaseHolderImpl>(); }); + return Status::OK(); +} + +} // namespace + +using std::set; +using std::size_t; +using std::string; +using std::stringstream; + +namespace { + +StringData _todb(StringData ns) { + size_t i = ns.find('.'); + if (i == std::string::npos) { + uassert(13074, "db name can't be empty", ns.size()); + return ns; + } + + uassert(13075, "db name can't be empty", i > 0); + + const StringData d = ns.substr(0, i); + uassert(13280, + "invalid db name: " + ns.toString(), + NamespaceString::validDBName(d, NamespaceString::DollarInDbNameBehavior::Allow)); + + return d; +} + +} // namespace + + +Database* DatabaseHolderImpl::get(OperationContext* opCtx, StringData ns) const { + const StringData db = _todb(ns); + invariant(opCtx->lockState()->isDbLockedForMode(db, MODE_IS)); + + stdx::lock_guard<SimpleMutex> lk(_m); + DBs::const_iterator it = _dbs.find(db); + if (it != _dbs.end()) { + return it->second; + } + + return NULL; +} + +std::set<std::string> DatabaseHolderImpl::_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> DatabaseHolderImpl::getNamesWithConflictingCasing(StringData name) { + stdx::lock_guard<SimpleMutex> lk(_m); + return _getNamesWithConflictingCasing_inlock(name); +} + +Database* DatabaseHolderImpl::openDb(OperationContext* opCtx, StringData ns, bool* justCreated) { + const StringData dbname = _todb(ns); + invariant(opCtx->lockState()->isDbLockedForMode(dbname, MODE_X)); + + if (justCreated) + *justCreated = false; // Until proven otherwise. + + stdx::unique_lock<SimpleMutex> lk(_m); + + // 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; + + // 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(opCtx, dbname); + + if (!entry->exists()) { + audit::logCreateDatabase(&cc(), dbname); + if (justCreated) + *justCreated = true; + } + + auto newDb = stdx::make_unique<Database>(opCtx, dbname, entry); + + // 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); + it->second = newDb.release(); + invariant(_getNamesWithConflictingCasing_inlock(dbname.toString()).empty()); + + return it->second; +} + +void DatabaseHolderImpl::close(OperationContext* opCtx, StringData ns, const std::string& reason) { + invariant(opCtx->lockState()->isW()); + + const StringData dbName = _todb(ns); + + stdx::lock_guard<SimpleMutex> lk(_m); + + DBs::const_iterator it = _dbs.find(dbName); + if (it == _dbs.end()) { + return; + } + + it->second->close(opCtx, reason); + delete it->second; + _dbs.erase(it); + + getGlobalServiceContext()->getGlobalStorageEngine()->closeDatabase(opCtx, dbName.toString()); +} + +bool DatabaseHolderImpl::closeAll(OperationContext* opCtx, + BSONObjBuilder& result, + bool force, + const std::string& reason) { + invariant(opCtx->lockState()->isW()); + + stdx::lock_guard<SimpleMutex> lk(_m); + + set<string> dbs; + for (DBs::const_iterator i = _dbs.begin(); i != _dbs.end(); ++i) { + dbs.insert(i->first); + } + + BSONArrayBuilder bb(result.subarrayStart("dbs")); + int nNotClosed = 0; + for (set<string>::iterator i = dbs.begin(); i != dbs.end(); ++i) { + string name = *i; + + LOG(2) << "DatabaseHolder::closeAll name:" << name; + + if (!force && BackgroundOperation::inProgForDb(name)) { + log() << "WARNING: can't close database " << name + << " because a bg job is in progress - try killOp command"; + nNotClosed++; + continue; + } + + Database* db = _dbs[name]; + db->close(opCtx, reason); + delete db; + + _dbs.erase(name); + + getGlobalServiceContext()->getGlobalStorageEngine()->closeDatabase(opCtx, name); + + bb.append(name); + } + + bb.done(); + if (nNotClosed) { + result.append("nNotClosed", nNotClosed); + } + + return true; +} +} // namespace mongo diff --git a/src/mongo/db/catalog/database_holder_impl.h b/src/mongo/db/catalog/database_holder_impl.h new file mode 100644 index 00000000000..c60d0043123 --- /dev/null +++ b/src/mongo/db/catalog/database_holder_impl.h @@ -0,0 +1,98 @@ +/** + * Copyright (C) 2017 MongoDB Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * As a special exception, the copyright holders give permission to link the + * code of portions of this program with the OpenSSL library under certain + * conditions as described in each individual source file and distribute + * linked combinations including the program with the OpenSSL library. You + * must comply with the GNU Affero General Public License in all respects for + * all of the code used other than as permitted herein. If you modify file(s) + * with this exception, you may extend this exception to your version of the + * file(s), but you are not obligated to do so. If you do not wish to do so, + * delete this exception statement from your version. If you delete this + * exception statement from all source files in the program, then also delete + * it in the license file. + */ + +#pragma once + +#include "mongo/db/catalog/database_holder.h" + +#include <set> +#include <string> + +#include "mongo/base/string_data.h" +#include "mongo/db/namespace_string.h" +#include "mongo/stdx/mutex.h" +#include "mongo/util/concurrency/mutex.h" +#include "mongo/util/string_map.h" + +namespace mongo { + +class Database; +class OperationContext; + +/** + * Registry of opened databases. + */ +class DatabaseHolderImpl : public DatabaseHolder::Impl { +public: + DatabaseHolderImpl() = default; + + /** + * Retrieves an already opened database or returns NULL. Must be called with the database + * locked in at least IS-mode. + */ + Database* get(OperationContext* opCtx, StringData ns) const override; + + /** + * Retrieves a database reference if it is already opened, or opens it if it hasn't been + * opened/created yet. Must be called with the database locked in X-mode. + * + * @param justCreated Returns whether the database was newly created (true) or it already + * existed (false). Can be NULL if this information is not necessary. + */ + Database* openDb(OperationContext* opCtx, StringData ns, bool* justCreated = nullptr) override; + + /** + * Closes the specified database. Must be called with the database locked in X-mode. + */ + void close(OperationContext* opCtx, StringData ns, const std::string& reason) override; + + /** + * Closes all opened databases. Must be called with the global lock acquired in X-mode. + * + * @param result Populated with the names of the databases, which were closed. + * @param force Force close even if something underway - use at shutdown + * @param reason The reason for close. + */ + bool closeAll(OperationContext* opCtx, + BSONObjBuilder& result, + bool force, + const std::string& reason) override; + + /** + * Returns the set of existing database names that differ only in casing. + */ + std::set<std::string> getNamesWithConflictingCasing(StringData name) override; + +private: + std::set<std::string> _getNamesWithConflictingCasing_inlock(StringData name); + + typedef StringMap<Database*> DBs; + mutable SimpleMutex _m; + DBs _dbs; +}; +} // namespace mongo |