/** * Copyright (C) 2018-present MongoDB, Inc. * * This program is free software: you can redistribute it and/or modify * it under the terms of the Server Side Public License, version 1, * as published by MongoDB, Inc. * * 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 * Server Side Public License for more details. * * You should have received a copy of the Server Side 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 Server Side 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/collection.h" #include "mongo/db/catalog/collection_catalog.h" #include "mongo/db/catalog/views_for_database.h" #include "mongo/db/database_name.h" #include "mongo/db/views/view.h" #include "mongo/util/uuid.h" namespace mongo { /** * Decoration on Snapshot to store cloned Collections until they are committed or rolled back. */ class UncommittedCatalogUpdates { public: struct Entry { enum class Action { // Created collection instance kCreatedCollection, // Writable clone kWritableCollection, // Marker to indicate that the namespace has been renamed kRenamedCollection, // Dropped collection instance kDroppedCollection, // Recreated collection after drop kRecreatedCollection, // Replaced views for a particular database kReplacedViewsForDatabase, // Add a view resource kAddViewResource, // Remove a view resource kRemoveViewResource, // Dropped index instance kDroppedIndex, }; boost::optional uuid() const { if (action == Action::kCreatedCollection || action == Action::kWritableCollection || action == Action::kRenamedCollection) return collection->uuid(); return externalUUID; } // Type of action this entry has stored. Members below may or may not be set depending on // this member. Action action; // Storage for the actual collection. // Set for actions kWritableCollection, kCreatedCollection, kRecreatedCollection and nullptr // otherwise. std::shared_ptr collection; // Store namespace separately to handle rename and drop without making writable first. // Set for all actions. NamespaceString nss; // External uuid when not accessible via collection. // Set for actions kDroppedCollection, kRecreatedCollection. boost::none otherwise. boost::optional externalUUID; // New namespace this collection has been renamed to. // Set for action kRenamedCollection. Default constructed otherwise. NamespaceString renameTo; // New set of view information for a database. // Set for action kReplacedViewsForDatabase, boost::none otherwise. boost::optional viewsForDb; // Storage for the actual index entry. // Set for action kDroppedIndex and nullptr otherwise. std::shared_ptr indexEntry; // Whether the collection or index entry is drop pending. // Set for actions kDroppedCollection and kDroppedIndex, boost::none otherwise. boost::optional isDropPending; }; struct CollectionLookupResult { // True if the collection is currently being managed in this transaction. bool found; // Storage for the actual collection. // Set for actions kWritableCollection, kCreatedCollection, and kRecreatedCollection. std::shared_ptr collection; // True if the collection was created during this transaction for the first time. bool newColl; }; UncommittedCatalogUpdates() {} ~UncommittedCatalogUpdates() = default; /** * Determine if an entry is associated with a collection action (as opposed to a view action). */ static bool isCollectionEntry(const Entry& entry) { return (entry.action == Entry::Action::kCreatedCollection || entry.action == Entry::Action::kWritableCollection || entry.action == Entry::Action::kRenamedCollection || entry.action == Entry::Action::kDroppedCollection || entry.action == Entry::Action::kRecreatedCollection); } /** * Determine if an entry uses two-phase commit to write into the CollectionCatalog. * kCreatedCollection is also committed using two-phase commit but using a separate system and * is excluded from this list. kDroppedIndex is covered by kWritableCollection as a writable * collection must be used to drop an index. */ static bool isTwoPhaseCommitEntry(const Entry& entry) { return (entry.action == Entry::Action::kWritableCollection || entry.action == Entry::Action::kRenamedCollection || entry.action == Entry::Action::kDroppedCollection || entry.action == Entry::Action::kRecreatedCollection); } /** * Lookup of Collection by UUID describing whether this namespace is managed, a managed * Collection pointer (may be returned as nullptr, which indicates a drop), and if it was * created in this transaction. */ static UncommittedCatalogUpdates::CollectionLookupResult lookupCollection( OperationContext* opCtx, UUID uuid); /** * Lookup of Collection by Namestring describing whether this namespace is managed, a managed * Collection pointer (may be returned as nullptr, which indicates a drop), and if it was * created in this transaction. */ static CollectionLookupResult lookupCollection(OperationContext* opCtx, const NamespaceString& nss); boost::optional getViewsForDatabase(const DatabaseName& dbName) const; /** * Add collection to entries and register RecoveryUnit preCommitHook to throw a * `WriteConflictException` if there is a NamespaceString conflict in the catalog. */ void createCollection(OperationContext* opCtx, std::shared_ptr coll); /** * Wraps 'createCollection' and does not register a preCommitHook in order to defer committing a * collection after a collection drop. */ void recreateCollection(OperationContext* opCtx, std::shared_ptr coll); /** * Manage the lifetime of uncommitted writable collection. */ void writableCollection(std::shared_ptr collection); /** * Manage an uncommitted rename, pointer must have made writable first and should exist in entry * list. */ void renameCollection(const Collection* collection, const NamespaceString& from); /** * Manages an uncommitted index entry drop. */ void dropIndex(const NamespaceString& nss, std::shared_ptr indexEntry, bool isDropPending); /** * Manage an uncommitted collection drop. */ void dropCollection(const Collection* collection, bool isDropPending); /** * Replace the ViewsForDatabase instance assocated with database `dbName` with `vfdb`. This is * the primary low-level write method to alter any information about the views associated with a * given database. */ void replaceViewsForDatabase(const DatabaseName& dbName, ViewsForDatabase&& vfdb); /** * Adds a ResourceID associated with a view namespace, and registers a preCommitHook to do * conflict-checking on the view namespace. */ void addView(OperationContext* opCtx, const NamespaceString& nss); /** * Removes the ResourceID associated with a view namespace. */ void removeView(const NamespaceString& nss); /** * Returns all entries without releasing them. */ const std::vector& entries() const; /** * Releases all entries, needs to be done when WriteUnitOfWork commits or rolls back. */ std::vector releaseEntries(); /** * The catalog needs to ignore external view changes for its own modifications. This method * should be used by DDL operations to prevent op observers from triggering additional catalog * operations. */ void setIgnoreExternalViewChanges(const DatabaseName& dbName, bool value); /** * The catalog needs to ignore external view changes for its own modifications. This method can * be used by methods called by op observers (e.g. 'CollectionCatalog::reload()') to distinguish * between an external write to 'system.views' and one initiated through the proper view DDL * operations. */ bool shouldIgnoreExternalViewChanges(const DatabaseName& dbName) const; /** * Checks if there is an entry with the nss `nss` and the * 'kCreatedCollection'/'kRecreatedCollection' action type. */ static bool isCreatedCollection(OperationContext* opCtx, const NamespaceString& nss); bool isEmpty() { return _entries.empty(); } static UncommittedCatalogUpdates& get(OperationContext* opCtx); private: /** * Adds a created or recreated collection to the entries vector and registers rollback handlers * (in addition to a preCommitHook for newly created collections). */ void _createCollection(OperationContext* opCtx, std::shared_ptr coll, Entry::Action action); /** * Store entries in vector, we will do linear search to find what we're looking for but it will * be very few entries so it should be fine. */ std::vector _entries; stdx::unordered_set _ignoreExternalViewChanges; }; /** * Decoration on Snapshot to store Collections instantiated from durable catalog data. Lifetime tied * to Snapshot lifetime. */ class OpenedCollections { public: static OpenedCollections& get(OperationContext* opCtx); /** * Lookup collection instance by namespace. * * May return nullptr which indicates that the namespace does not exist in the snapshot. * * Returns boost::none if this namespace is unknown to OpenedCollections. */ boost::optional> lookupByNamespace( const NamespaceString& ns) const; /** * Lookup collection instance by UUID. * * May return nullptr which indicates that the UUID does not exist in the snapshot. * * Returns boost::none if this UUID is unknown to OpenedCollections. */ boost::optional> lookupByUUID(UUID uuid) const; /** * Stores a Collection instance. Lifetime of instance will be tied to lifetime of opened storage * snapshot. * * Collection instance may be nullptr to indicate that the namespace and/or UUID does not exist * in the snapshot. */ void store(std::shared_ptr coll, boost::optional nss, boost::optional uuid); private: struct Entry { std::shared_ptr collection; boost::optional nss; boost::optional uuid; }; // Static storage for one entry. The expected common case is that only a single collection will // be needed so we optimize for that. boost::container::small_vector _collections; }; } // namespace mongo