/** * Copyright (C) 2014 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 . * * 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 #include "mongo/base/string_data.h" #include "mongo/db/catalog/database.h" #include "mongo/db/concurrency/d_concurrency.h" #include "mongo/db/namespace_string.h" #include "mongo/db/operation_context.h" #include "mongo/db/views/view.h" #include "mongo/util/timer.h" namespace mongo { class Collection; /** * RAII-style class, which acquires a lock on the specified database in the requested mode and * obtains a reference to the database. Used as a shortcut for calls to dbHolder().get(). * * It is guaranteed that the lock will be released when this object goes out of scope, therefore * the database reference returned by this class should not be retained. */ class AutoGetDb { MONGO_DISALLOW_COPYING(AutoGetDb); public: AutoGetDb(OperationContext* txn, StringData ns, LockMode mode); Database* getDb() const { return _db; } private: const Lock::DBLock _dbLock; Database* const _db; }; /** * RAII-style class, which acquires a locks on the specified database and collection in the * requested mode and obtains references to both. * * It is guaranteed that locks will be released when this object goes out of scope, therefore * the database and the collection references returned by this class should not be retained. */ class AutoGetCollection { MONGO_DISALLOW_COPYING(AutoGetCollection); enum class ViewMode; public: AutoGetCollection(OperationContext* txn, const NamespaceString& nss, LockMode modeAll) : AutoGetCollection(txn, nss, modeAll, modeAll, ViewMode::kViewsForbidden) {} AutoGetCollection(OperationContext* txn, const NamespaceString& nss, LockMode modeDB, LockMode modeColl) : AutoGetCollection(txn, nss, modeDB, modeColl, ViewMode::kViewsForbidden) {} /** * This constructor is inteded for internal use and should not be used outside this file. * AutoGetCollectionForRead and AutoGetCollectionOrViewForRead use ViewMode to determine whether * or not it is permissible to obtain a handle on a view namespace. Use another constructor or * another AutoGet class instead. */ AutoGetCollection(OperationContext* txn, const NamespaceString& nss, LockMode modeDB, LockMode modeColl, ViewMode viewMode); Database* getDb() const { return _autoDb.getDb(); } Collection* getCollection() const { return _coll; } private: enum class ViewMode { kViewsPermitted, kViewsForbidden }; const ViewMode _viewMode; const AutoGetDb _autoDb; const Lock::CollectionLock _collLock; Collection* const _coll; friend class AutoGetCollectionForRead; friend class AutoGetCollectionOrViewForRead; }; /** * RAII-style class, which acquires a lock on the specified database in the requested mode and * obtains a reference to the database, creating it was non-existing. Used as a shortcut for * calls to dbHolder().openDb(), taking care of locking details. The requested mode must be * MODE_IX or MODE_X. If the database needs to be created, the lock will automatically be * reacquired as MODE_X. * * It is guaranteed that locks will be released when this object goes out of scope, therefore * the database reference returned by this class should not be retained. */ class AutoGetOrCreateDb { MONGO_DISALLOW_COPYING(AutoGetOrCreateDb); public: AutoGetOrCreateDb(OperationContext* txn, StringData ns, LockMode mode); Database* getDb() const { return _db; } bool justCreated() const { return _justCreated; } Lock::DBLock& lock() { return _dbLock; } private: ScopedTransaction _transaction; Lock::DBLock _dbLock; // not const, as we may need to relock for implicit create Database* _db; bool _justCreated; }; /** * RAII-style class, which would acquire the appropritate hierarchy of locks for obtaining * a particular collection and would retrieve a reference to the collection. In addition, this * utility validates the shard version for the specified namespace and sets the current operation's * namespace for the duration while this object is alive. * * It is guaranteed that locks will be released when this object goes out of scope, therefore * database and collection references returned by this class should not be retained. */ class AutoGetCollectionForRead { MONGO_DISALLOW_COPYING(AutoGetCollectionForRead); public: AutoGetCollectionForRead(OperationContext* txn, const NamespaceString& nss) : AutoGetCollectionForRead(txn, nss, AutoGetCollection::ViewMode::kViewsForbidden) {} ~AutoGetCollectionForRead(); Database* getDb() const { return _autoColl->getDb(); } Collection* getCollection() const { return _autoColl->getCollection(); } private: void _ensureMajorityCommittedSnapshotIsValid(const NamespaceString& nss); const Timer _timer; OperationContext* const _txn; const ScopedTransaction _transaction; protected: AutoGetCollectionForRead(OperationContext* txn, const NamespaceString& nss, AutoGetCollection::ViewMode viewMode); /** * This protected section must come after the private section because * AutoGetCollectionOrViewForRead needs access to _autoColl, but _autoColl must be initialized * after _transaction. */ boost::optional _autoColl; }; /** * RAII-style class for obtaining a collection or view for reading. The pointer to a view definition * is nullptr if it does not exist. */ class AutoGetCollectionOrViewForRead final : public AutoGetCollectionForRead { MONGO_DISALLOW_COPYING(AutoGetCollectionOrViewForRead); public: AutoGetCollectionOrViewForRead(OperationContext* txn, const NamespaceString& nss); ViewDefinition* getView() const { return _view.get(); } /** * Unlock this view or collection and release all resources. After calling this function, it is * illegal to access this object's database, collection and view pointers. * * TODO(SERVER-24909): Consider having the constructor release locks instead, or otherwise * remove the need for this method. */ void releaseLocksForView() noexcept; private: std::shared_ptr _view; }; /** * Opens the database that we want to use and sets the appropriate namespace on the * current operation. */ class OldClientContext { MONGO_DISALLOW_COPYING(OldClientContext); public: /** this is probably what you want */ OldClientContext(OperationContext* txn, const std::string& ns, bool doVersion = true); /** * Below still calls _finishInit, but assumes database has already been acquired * or just created. */ OldClientContext(OperationContext* txn, const std::string& ns, Database* db, bool justCreated); ~OldClientContext(); Database* db() const { return _db; } /** @return if the db was created by this OldClientContext */ bool justCreated() const { return _justCreated; } private: friend class CurOp; void _finishInit(); void _checkNotStale() const; bool _justCreated; bool _doVersion; const std::string _ns; Database* _db; OperationContext* _txn; Timer _timer; }; class OldClientWriteContext { MONGO_DISALLOW_COPYING(OldClientWriteContext); public: OldClientWriteContext(OperationContext* opCtx, const std::string& ns); Database* db() const { return _c.db(); } Collection* getCollection() const { return _c.db()->getCollection(_nss.ns()); } private: OperationContext* const _txn; const NamespaceString _nss; AutoGetOrCreateDb _autodb; Lock::CollectionLock _collk; OldClientContext _c; Collection* _collection; }; } // namespace mongo