/**
* Copyright (C) 2017 10gen 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
#include "mongo/base/string_data.h"
#include "mongo/bson/bsonobj.h"
#include "mongo/db/catalog/collection.h"
#include "mongo/db/catalog/collection_options.h"
#include "mongo/db/namespace_string.h"
#include "mongo/db/repl/optime.h"
#include "mongo/db/storage/storage_options.h"
#include "mongo/db/views/view.h"
#include "mongo/db/views/view_catalog.h"
#include "mongo/stdx/functional.h"
#include "mongo/util/mongoutils/str.h"
#include "mongo/util/string_map.h"
namespace mongo {
/**
* Represents a logical database containing Collections.
*
* The semantics for a const Database are that you can mutate individual collections but not add or
* remove them.
*/
class Database {
public:
typedef StringMap CollectionMap;
class Impl {
public:
virtual ~Impl() = 0;
virtual void init(OperationContext* opCtx) = 0;
virtual void close(OperationContext* opCtx, const std::string& reason) = 0;
virtual const std::string& name() const = 0;
virtual void clearTmpCollections(OperationContext* opCtx) = 0;
virtual Status setProfilingLevel(OperationContext* opCtx, int newLevel) = 0;
virtual int getProfilingLevel() const = 0;
virtual const char* getProfilingNS() const = 0;
virtual void setDropPending(OperationContext* opCtx, bool dropPending) = 0;
virtual bool isDropPending(OperationContext* opCtx) const = 0;
virtual void getStats(OperationContext* opCtx, BSONObjBuilder* output, double scale) = 0;
virtual const DatabaseCatalogEntry* getDatabaseCatalogEntry() const = 0;
virtual Status dropCollection(OperationContext* opCtx,
StringData fullns,
repl::OpTime dropOpTime) = 0;
virtual Status dropCollectionEvenIfSystem(OperationContext* opCtx,
const NamespaceString& fullns,
repl::OpTime dropOpTime) = 0;
virtual Status dropView(OperationContext* opCtx, StringData fullns) = 0;
virtual Collection* createCollection(OperationContext* opCtx,
StringData ns,
const CollectionOptions& options,
bool createDefaultIndexes,
const BSONObj& idIndex) = 0;
virtual Status createView(OperationContext* opCtx,
StringData viewName,
const CollectionOptions& options) = 0;
virtual Collection* getCollection(OperationContext* opCtx, StringData ns) const = 0;
virtual ViewCatalog* getViewCatalog() = 0;
virtual Collection* getOrCreateCollection(OperationContext* opCtx,
const NamespaceString& nss) = 0;
virtual Status renameCollection(OperationContext* opCtx,
StringData fromNS,
StringData toNS,
bool stayTemp) = 0;
virtual const NamespaceString& getSystemIndexesName() const = 0;
virtual const std::string& getSystemViewsName() const = 0;
virtual StatusWith makeUniqueCollectionNamespace(
OperationContext* opCtx, StringData collectionNameModel) = 0;
virtual CollectionMap& collections() = 0;
virtual const CollectionMap& collections() const = 0;
};
private:
static std::unique_ptr makeImpl(Database* _this,
OperationContext* opCtx,
StringData name,
DatabaseCatalogEntry* dbEntry);
public:
using factory_function_type = decltype(makeImpl);
static void registerFactory(stdx::function factory);
/**
* Iterating over a Database yields Collection* pointers.
*/
class iterator {
public:
using iterator_category = std::forward_iterator_tag;
using value_type = Collection*;
using pointer = const value_type*;
using reference = const value_type&;
using difference_type = ptrdiff_t;
explicit inline iterator() = default;
inline iterator(CollectionMap::const_iterator it) : _it(std::move(it)) {}
inline reference operator*() const {
return _it->second;
}
inline pointer operator->() const {
return &_it->second;
}
inline friend bool operator==(const iterator& lhs, const iterator& rhs) {
return lhs._it == rhs._it;
}
inline friend bool operator!=(const iterator& lhs, const iterator& rhs) {
return !(lhs == rhs);
}
inline iterator& operator++() {
++_it;
return *this;
}
inline iterator operator++(int) {
auto oldPosition = *this;
++_it;
return oldPosition;
}
private:
CollectionMap::const_iterator _it;
};
explicit inline Database(OperationContext* const opCtx,
const StringData name,
DatabaseCatalogEntry* const dbEntry)
: _pimpl(makeImpl(this, opCtx, name, dbEntry)) {
this->_impl().init(opCtx);
}
// must call close first
inline ~Database() = default;
inline Database(Database&&) = delete;
inline Database& operator=(Database&&) = delete;
inline iterator begin() const {
return iterator(this->_impl().collections().begin());
}
inline iterator end() const {
return iterator(this->_impl().collections().end());
}
// closes files and other cleanup see below.
inline void close(OperationContext* const opCtx, const std::string& reason) {
return this->_impl().close(opCtx, reason);
}
inline const std::string& name() const {
return this->_impl().name();
}
inline void clearTmpCollections(OperationContext* const opCtx) {
return this->_impl().clearTmpCollections(opCtx);
}
/**
* Sets a new profiling level for the database and returns the outcome.
*
* @param opCtx Operation context which to use for creating the profiling collection.
* @param newLevel New profiling level to use.
*/
inline Status setProfilingLevel(OperationContext* const opCtx, const int newLevel) {
return this->_impl().setProfilingLevel(opCtx, newLevel);
}
inline int getProfilingLevel() const {
return this->_impl().getProfilingLevel();
}
inline const char* getProfilingNS() const {
return this->_impl().getProfilingNS();
}
/**
* Sets the 'drop-pending' state of this Database.
* This is done at the beginning of a dropDatabase operation and is used to reject subsequent
* collection creation requests on this database.
* Throws a UserAssertion if this is called on a Database that is already in a 'drop-pending'
* state.
* The database must be locked in MODE_X when calling this function.
*/
inline void setDropPending(OperationContext* opCtx, bool dropPending) {
this->_impl().setDropPending(opCtx, dropPending);
}
/**
* Returns the 'drop-pending' state of this Database.
* The database must be locked in MODE_X when calling this function.
*/
inline bool isDropPending(OperationContext* opCtx) const {
return this->_impl().isDropPending(opCtx);
}
inline void getStats(OperationContext* const opCtx,
BSONObjBuilder* const output,
const double scale = 1) {
return this->_impl().getStats(opCtx, output, scale);
}
inline const DatabaseCatalogEntry* getDatabaseCatalogEntry() const {
return this->_impl().getDatabaseCatalogEntry();
}
/**
* dropCollection() will refuse to drop system collections. Use dropCollectionEvenIfSystem() if
* that is required.
*
* If we are applying a 'drop' oplog entry on a secondary, 'dropOpTime' will contain the optime
* of the oplog entry.
*/
inline Status dropCollection(OperationContext* const opCtx,
const StringData fullns,
repl::OpTime dropOpTime = {}) {
return this->_impl().dropCollection(opCtx, fullns, dropOpTime);
}
inline Status dropCollectionEvenIfSystem(OperationContext* const opCtx,
const NamespaceString& fullns,
repl::OpTime dropOpTime = {}) {
return this->_impl().dropCollectionEvenIfSystem(opCtx, fullns, dropOpTime);
}
inline Status dropView(OperationContext* const opCtx, const StringData fullns) {
return this->_impl().dropView(opCtx, fullns);
}
inline Collection* createCollection(OperationContext* const opCtx,
StringData ns,
const CollectionOptions& options = CollectionOptions(),
const bool createDefaultIndexes = true,
const BSONObj& idIndex = BSONObj()) {
return this->_impl().createCollection(opCtx, ns, options, createDefaultIndexes, idIndex);
}
inline Status createView(OperationContext* const opCtx,
const StringData viewName,
const CollectionOptions& options) {
return this->_impl().createView(opCtx, viewName, options);
}
/**
* @param ns - this is fully qualified, which is maybe not ideal ???
*/
inline Collection* getCollection(OperationContext* opCtx, const StringData ns) const {
return this->_impl().getCollection(opCtx, ns);
}
inline Collection* getCollection(OperationContext* opCtx, const NamespaceString& ns) const {
return this->_impl().getCollection(opCtx, ns.ns());
}
/**
* Get the view catalog, which holds the definition for all views created on this database. You
* must be holding a database lock to use this accessor.
*/
inline ViewCatalog* getViewCatalog() {
return this->_impl().getViewCatalog();
}
inline Collection* getOrCreateCollection(OperationContext* const opCtx,
const NamespaceString& nss) {
return this->_impl().getOrCreateCollection(opCtx, nss);
}
inline Status renameCollection(OperationContext* const opCtx,
const StringData fromNS,
const StringData toNS,
const bool stayTemp) {
return this->_impl().renameCollection(opCtx, fromNS, toNS, stayTemp);
}
/**
* Physically drops the specified opened database and removes it from the server's metadata. It
* doesn't notify the replication subsystem or do any other consistency checks, so it should
* not be used directly from user commands.
*
* Must be called with the specified database locked in X mode.
*/
static void dropDatabase(OperationContext* opCtx, Database* db);
/**
* Registers an implementation of `Database::dropDatabase` for use by library clients.
* This is necessary to allow `catalog/database` to be a vtable edge.
* @param impl Implementation of `dropDatabase` to install.
* @note This call is not thread safe.
*/
static void registerDropDatabaseImpl(stdx::function impl);
// static Status validateDBName( StringData dbname );
inline const NamespaceString& getSystemIndexesName() const {
return this->_impl().getSystemIndexesName();
}
inline const std::string& getSystemViewsName() const {
return this->_impl().getSystemViewsName();
}
/**
* Generates a collection namespace suitable for creating a temporary collection.
* The namespace is based on a model that replaces each percent sign in 'collectionNameModel' by
* a random character in the range [0-9A-Za-z].
* Returns FailedToParse if 'collectionNameModel' does not contain any percent signs.
* Returns NamespaceExists if we are unable to generate a collection name that does not conflict
* with an existing collection in this database.
*
* The database must be locked in MODE_X when calling this function.
*/
inline StatusWith makeUniqueCollectionNamespace(
OperationContext* opCtx, StringData collectionNameModel) {
return this->_impl().makeUniqueCollectionNamespace(opCtx, collectionNameModel);
}
private:
// This structure exists to give us a customization point to decide how to force users of this
// class to depend upon the corresponding `database.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 _pimpl;
};
void dropAllDatabasesExceptLocal(OperationContext* opCtx);
/**
* Registers an implementation of `dropAllDatabaseExceptLocal` for use by library clients.
* This is necessary to allow `catalog/database` to be a vtable edge.
* @param impl Implementation of `dropAllDatabaseExceptLocal` to install.
* @note This call is not thread safe.
*/
void registerDropAllDatabasesExceptLocalImpl(
stdx::function impl);
/**
* Creates the namespace 'ns' in the database 'db' according to 'options'. If 'createDefaultIndexes'
* is true, creates the _id index for the collection (and the system indexes, in the case of system
* collections). Creates the collection's _id index according to 'idIndex', if it is non-empty. When
* 'idIndex' is empty, creates the default _id index.
*/
Status userCreateNS(OperationContext* opCtx,
Database* db,
StringData ns,
BSONObj options,
CollectionOptions::ParseKind parseKind = CollectionOptions::parseForCommand,
bool createDefaultIndexes = true,
const BSONObj& idIndex = BSONObj());
/**
* Registers an implementation of `userCreateNS` for use by library clients.
* This is necessary to allow `catalog/database` to be a vtable edge.
* @param impl Implementation of `userCreateNS` to install.
* @note This call is not thread safe.
*/
void registerUserCreateNSImpl(stdx::function impl);
} // namespace mongo