/** * Validates the jump instruction node. * * The actual checks are delegated polymorphically. * * \return A `ValidationResult` indicating the validity of the node. */ ValidationResult validate(MemoryAccess& memoryAccess) const override { auto result = _validateNumberOfChildren(); if (!result.isSuccess()) return result; result = _validateChildren(memoryAccess); if (!result.isSuccess()) return result; result = _validateOperandTypes(); if (!result.isSuccess()) return result; result = _validateOffset(memoryAccess); if (!result.isSuccess()) return result; return ValidationResult::success(); }
Status ViewGraph::insertAndValidate(const ViewDefinition& view, const std::vector<NamespaceString>& refs, int pipelineSize) { insertWithoutValidating(view, refs, pipelineSize); // Perform validation on this newly inserted view. Note, if the graph was put in an invalid // state through unvalidated inserts (e.g. if the user manually edits system.views) // this may not necessarily be detected. We only check for errors introduced by this view. const auto& viewNss = view.name(); uint64_t nodeId = _getNodeId(viewNss); // If the graph fails validation for any reason, the insert is automatically rolled back on // exiting this method. auto rollBackInsert = [&]() -> auto { remove(viewNss); }; auto guard = MakeGuard(rollBackInsert); // Check for cycles and get the height of the children. StatsMap statsMap; std::vector<uint64_t> cycleVertices; cycleVertices.reserve(kMaxViewDepth); auto childRes = _validateChildren(nodeId, nodeId, 0, &statsMap, &cycleVertices); if (!childRes.isOK()) { return childRes; } // Subtract one since the child height includes the non-view leaf node(s). int childrenHeight = statsMap[nodeId].height - 1; int childrenSize = statsMap[nodeId].cumulativeSize; // Get the height of the parents to obtain the diameter through this node, as well as the size // of the pipeline to check if if the combined pipeline exceeds the max size. statsMap.clear(); auto parentRes = _validateParents(nodeId, 0, &statsMap); if (!parentRes.isOK()) { return parentRes; } // Check the combined heights of the children and parents. int parentsHeight = statsMap[nodeId].height; // Subtract one since the parent and children heights include the current node. int diameter = parentsHeight + childrenHeight - 1; if (diameter > kMaxViewDepth) { return {ErrorCodes::ViewDepthLimitExceeded, str::stream() << "View depth limit exceeded; maximum depth is " << kMaxViewDepth}; } // Check the combined sizes of the children and parents. int parentsSize = statsMap[nodeId].cumulativeSize; // Subtract the current node's size since the parent and children sizes include the current // node. const Node& currentNode = _graph[nodeId]; int pipelineTotalSize = parentsSize + childrenSize - currentNode.size; if (pipelineTotalSize > kMaxViewPipelineSizeBytes) { return {ErrorCodes::ViewPipelineMaxSizeExceeded, str::stream() << "Operation would result in a resolved view pipeline that exceeds " "the maximum size of " << kMaxViewPipelineSizeBytes << " bytes"}; } guard.Dismiss(); return Status::OK(); }
Status ViewGraph::_validateChildren(uint64_t startingId, uint64_t currentId, int currentDepth, StatsMap* statsMap, std::vector<uint64_t>* traversalIds) { const Node& currentNode = _graph[currentId]; traversalIds->push_back(currentId); // If we've encountered the id of the starting node, we've found a cycle in the graph. if (currentDepth > 0 && currentId == startingId) { auto iterator = traversalIds->rbegin(); auto errmsg = StringBuilder(); errmsg << "View cycle detected: "; errmsg << _graph[*iterator].ns; for (; iterator != traversalIds->rend(); ++iterator) { errmsg << " => " << _graph[*iterator].ns; } return {ErrorCodes::GraphContainsCycle, errmsg.str()}; } // Return early if we've already exceeded the maximum depth. This will also be triggered if // we're traversing a cycle introduced through unvalidated inserts. if (currentDepth > kMaxViewDepth) { return {ErrorCodes::ViewDepthLimitExceeded, str::stream() << "View depth limit exceeded; maximum depth is " << ViewGraph::kMaxViewDepth}; } int maxHeightOfChildren = 0; int maxSizeOfChildren = 0; for (uint64_t childId : currentNode.children) { if ((*statsMap)[childId].checked) { continue; } const auto& childNode = _graph[childId]; if (childNode.isView() && !CollatorInterface::collatorsMatch(currentNode.collator.get(), childNode.collator.get())) { return {ErrorCodes::OptionNotSupportedOnView, str::stream() << "View " << currentNode.ns << " has a collation that does not match the collation of view " << childNode.ns}; } auto res = _validateChildren(startingId, childId, currentDepth + 1, statsMap, traversalIds); if (!res.isOK()) { return res; } maxHeightOfChildren = std::max(maxHeightOfChildren, (*statsMap)[childId].height); maxSizeOfChildren = std::max(maxSizeOfChildren, (*statsMap)[childId].cumulativeSize); } traversalIds->pop_back(); (*statsMap)[currentId].checked = true; (*statsMap)[currentId].height = maxHeightOfChildren + 1; (*statsMap)[currentId].cumulativeSize += maxSizeOfChildren + currentNode.size; return Status::OK(); }