/** * Copyright (C) 2016 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/status.h" #include "mongo/db/namespace_string.h" #include "mongo/stdx/unordered_map.h" #include "mongo/stdx/unordered_set.h" #include "mongo/util/string_map.h" namespace mongo { class ViewDefinition; /** * Validates that the graph of view dependencies is acyclic and within the allowed depth. * Each node is represented by an integer id, and stores integer ids for its parents and children * in the graph. * * This is owned and managed by the ViewCatalog. */ class ViewGraph { MONGO_DISALLOW_COPYING(ViewGraph); public: static const int kMaxViewDepth; static const int kMaxViewPipelineSizeBytes; ViewGraph() = default; /** * Called when a view is added to the catalog. 'refs' are a list of namespaces that the view * represented by 'viewNss' references in its viewOn or pipeline. Checks if this view introduces * a cycle, max diameter, or max pipeline size. If an error is detected, it will not insert. */ Status insertAndValidate(const NamespaceString& viewNss, const std::vector& refs, const int pipelineSize); /** * Called when view definitions are being reloaded from the catalog (e.g. on restart of mongod). * Does the same as insertAndValidate except does not check for cycles, max diameter, or max * pipeline size. */ void insertWithoutValidating(const NamespaceString& viewNss, const std::vector& refs, const int pipelineSize); /** * Called when a view is removed from the catalog. If the view does not exist in the graph it is * a no-op. The view may also have a cycle or max diameter through it. */ void remove(const NamespaceString& viewNss); /** * Deletes the view graph and all references to namespaces. */ void clear(); private: // A graph node represents a namespace. The parent-child relation is defined as a parent // references the child either through viewOn or in $lookup/$graphLookup/$facet in its pipeline. // E.g. the command {create: "a", viewOn: "b", pipeline: [{$lookup: {from: "c"}}]} // means the node for "a" is a parent of nodes for "b" and "c" since it references them. struct Node { // Note, a view may refer to the same child more than once, but we only need to know the // set of children and parents, since we do not need to traverse duplicates. stdx::unordered_set parents; stdx::unordered_set children; std::string ns; int size = 0; }; // Bookkeeping for graph traversals. struct NodeStats { bool checked = false; int height = 0; int cumulativeSize = 0; }; using StatsMap = stdx::unordered_map; /** * Recursively traverses parents of this node and computes their heights and sizes. Returns an * error if the maximum depth is exceeded or the pipeline size exceeds the max. */ ErrorCodes::Error _getParentsStats(uint64_t currentId, int currentDepth, StatsMap* statsMap); /** * Recursively traverses children of the starting node and computes their heights and sizes. * Returns an error if the maximum depth is exceeded, a cycle is detected through the starting * node, or the pipeline size exceeds the max. */ ErrorCodes::Error _getChildrenStatsAndCheckCycle(uint64_t startingId, uint64_t currentId, int currentDepth, StatsMap* statsMap, std::vector* cycleIds); /** * Gets the id for this namespace, and creates an id if it doesn't exist. */ uint64_t _getNodeId(const NamespaceString& ns); // Maps namespaces to an internal node id. A mapping exists for every namespace referenced, // i.e. existing views, collections, and non-existing namespaces. StringMap _namespaceIds; // Maps node ids to nodes. There is a 1-1 correspondance with _namespaceIds, hence the lifetime // of a node is the same as the lifetime as its corresponding node id. stdx::unordered_map _graph; static uint64_t _idCounter; }; } // namespace mongo