/**
   * 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();
  }
Example #2
0
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();
}
Example #3
0
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();
}