StatusWith<ResolvedView> ViewCatalog::resolveView(OperationContext* txn, const NamespaceString& nss) { stdx::lock_guard<stdx::mutex> lk(_mutex); const NamespaceString* resolvedNss = &nss; std::vector<BSONObj> resolvedPipeline; for (int i = 0; i < ViewGraph::kMaxViewDepth; i++) { auto view = _lookup_inlock(txn, resolvedNss->ns()); if (!view) return StatusWith<ResolvedView>({*resolvedNss, resolvedPipeline}); resolvedNss = &(view->viewOn()); // Prepend the underlying view's pipeline to the current working pipeline. const std::vector<BSONObj>& toPrepend = view->pipeline(); resolvedPipeline.insert(resolvedPipeline.begin(), toPrepend.begin(), toPrepend.end()); // If the first stage is a $collStats, then we return early with the viewOn namespace. if (toPrepend.size() > 0 && !toPrepend[0]["$collStats"].eoo()) { return StatusWith<ResolvedView>({*resolvedNss, resolvedPipeline}); } } return {ErrorCodes::ViewDepthLimitExceeded, str::stream() << "View depth too deep or view cycle detected; maximum depth is " << ViewGraph::kMaxViewDepth}; }
StatusWith<ResolvedView> ViewCatalog::resolveView(OperationContext* opCtx, const NamespaceString& nss) { stdx::lock_guard<stdx::mutex> lk(_mutex); const NamespaceString* resolvedNss = &nss; std::vector<BSONObj> resolvedPipeline; BSONObj collation; for (int i = 0; i < ViewGraph::kMaxViewDepth; i++) { auto view = _lookup_inlock(opCtx, resolvedNss->ns()); if (!view) { // Return error status if pipeline is too large. int pipelineSize = 0; for (auto obj : resolvedPipeline) { pipelineSize += obj.objsize(); } if (pipelineSize > ViewGraph::kMaxViewPipelineSizeBytes) { return {ErrorCodes::ViewPipelineMaxSizeExceeded, str::stream() << "View pipeline exceeds maximum size; maximum size is " << ViewGraph::kMaxViewPipelineSizeBytes}; } return StatusWith<ResolvedView>( {*resolvedNss, std::move(resolvedPipeline), std::move(collation)}); } resolvedNss = &(view->viewOn()); collation = view->defaultCollator() ? view->defaultCollator()->getSpec().toBSON() : CollationSpec::kSimpleSpec; // Prepend the underlying view's pipeline to the current working pipeline. const std::vector<BSONObj>& toPrepend = view->pipeline(); resolvedPipeline.insert(resolvedPipeline.begin(), toPrepend.begin(), toPrepend.end()); // If the first stage is a $collStats, then we return early with the viewOn namespace. if (toPrepend.size() > 0 && !toPrepend[0]["$collStats"].eoo()) { return StatusWith<ResolvedView>( {*resolvedNss, std::move(resolvedPipeline), std::move(collation)}); } } return {ErrorCodes::ViewDepthLimitExceeded, str::stream() << "View depth too deep or view cycle detected; maximum depth is " << ViewGraph::kMaxViewDepth}; }
StatusWith<ResolvedView> ViewCatalog::resolveView(OperationContext* opCtx, const NamespaceString& nss) { stdx::unique_lock<stdx::mutex> lock(_mutex); // Keep looping until the resolution completes. If the catalog is invalidated during the // resolution, we start over from the beginning. while (true) { // Points to the name of the most resolved namespace. const NamespaceString* resolvedNss = &nss; // Holds the combination of all the resolved views. std::vector<BSONObj> resolvedPipeline; // If the catalog has not been tampered with, all views seen during the resolution will have // the same collation. As an optimization, we fill out the collation spec only once. boost::optional<BSONObj> collation; // The last seen view definition, which owns the NamespaceString pointed to by // 'resolvedNss'. std::shared_ptr<ViewDefinition> lastViewDefinition; int depth = 0; for (; depth < ViewGraph::kMaxViewDepth; depth++) { // If the catalog has been invalidated, bail and restart. if (!_valid.load()) { uassertStatusOK(_reloadIfNeeded(lock, opCtx)); break; } auto view = _lookup(lock, opCtx, resolvedNss->ns()); if (!view) { // Return error status if pipeline is too large. int pipelineSize = 0; for (auto obj : resolvedPipeline) { pipelineSize += obj.objsize(); } if (pipelineSize > ViewGraph::kMaxViewPipelineSizeBytes) { return {ErrorCodes::ViewPipelineMaxSizeExceeded, str::stream() << "View pipeline exceeds maximum size; maximum size is " << ViewGraph::kMaxViewPipelineSizeBytes}; } return StatusWith<ResolvedView>( {*resolvedNss, std::move(resolvedPipeline), std::move(collation.get())}); } resolvedNss = &view->viewOn(); if (!collation) { collation = view->defaultCollator() ? view->defaultCollator()->getSpec().toBSON() : CollationSpec::kSimpleSpec; } // Prepend the underlying view's pipeline to the current working pipeline. const std::vector<BSONObj>& toPrepend = view->pipeline(); resolvedPipeline.insert(resolvedPipeline.begin(), toPrepend.begin(), toPrepend.end()); // If the first stage is a $collStats, then we return early with the viewOn namespace. if (toPrepend.size() > 0 && !toPrepend[0]["$collStats"].eoo()) { return StatusWith<ResolvedView>( {*resolvedNss, std::move(resolvedPipeline), std::move(collation.get())}); } } if (depth >= ViewGraph::kMaxViewDepth) { return {ErrorCodes::ViewDepthLimitExceeded, str::stream() << "View depth too deep or view cycle detected; maximum depth is " << ViewGraph::kMaxViewDepth}; } }; MONGO_UNREACHABLE; }