Status ViewCatalog::modifyView(OperationContext* opCtx, const NamespaceString& viewName, const NamespaceString& viewOn, const BSONArray& pipeline) { stdx::lock_guard<stdx::mutex> lk(_mutex); if (viewName.db() != viewOn.db()) return Status(ErrorCodes::BadValue, "View must be created on a view or collection in the same database"); auto viewPtr = _lookup(lk, opCtx, viewName.ns()); if (!viewPtr) return Status(ErrorCodes::NamespaceNotFound, str::stream() << "cannot modify missing view " << viewName.ns()); if (!NamespaceString::validCollectionName(viewOn.coll())) return Status(ErrorCodes::InvalidNamespace, str::stream() << "invalid name for 'viewOn': " << viewOn.coll()); ViewDefinition savedDefinition = *viewPtr; opCtx->recoveryUnit()->onRollback([this, viewName, savedDefinition]() { this->_viewMap[viewName.ns()] = std::make_shared<ViewDefinition>(savedDefinition); }); return _createOrUpdateView(lk, opCtx, viewName, viewOn, pipeline, CollatorInterface::cloneCollator(savedDefinition.defaultCollator())); }
StatusWith<stdx::unordered_set<NamespaceString>> ViewCatalog::_validatePipeline_inlock( OperationContext* opCtx, const ViewDefinition& viewDef) const { AggregationRequest request(viewDef.viewOn(), viewDef.pipeline()); const LiteParsedPipeline liteParsedPipeline(request); const auto involvedNamespaces = liteParsedPipeline.getInvolvedNamespaces(); // Verify that this is a legitimate pipeline specification by making sure it parses // correctly. In order to parse a pipeline we need to resolve any namespaces involved to a // collection and a pipeline, but in this case we don't need this map to be accurate since // we will not be evaluating the pipeline. StringMap<ExpressionContext::ResolvedNamespace> resolvedNamespaces; for (auto&& nss : involvedNamespaces) { resolvedNamespaces[nss.coll()] = {nss, {}}; } boost::intrusive_ptr<ExpressionContext> expCtx = new ExpressionContext(opCtx, request, CollatorInterface::cloneCollator(viewDef.defaultCollator()), // We can use a stub MongoProcessInterface because we are only parsing // the Pipeline for validation here. We won't do anything with the // pipeline that will require a real implementation. std::make_shared<StubMongoProcessInterface>(), std::move(resolvedNamespaces)); // Save this to a variable to avoid reading the atomic variable multiple times. auto currentFCV = serverGlobalParams.featureCompatibility.getVersion(); // If the feature compatibility version is not 4.0, and we are validating features as master, // ban the use of new agg features introduced in 4.0 to prevent them from being persisted in the // catalog. if (serverGlobalParams.validateFeaturesAsMaster.load() && currentFCV != ServerGlobalParams::FeatureCompatibility::Version::kFullyUpgradedTo40) { expCtx->maxFeatureCompatibilityVersion = currentFCV; } auto pipelineStatus = Pipeline::parse(viewDef.pipeline(), std::move(expCtx)); if (!pipelineStatus.isOK()) { return pipelineStatus.getStatus(); } // Validate that the view pipeline does not contain any ineligible stages. auto sources = pipelineStatus.getValue()->getSources(); if (!sources.empty() && sources.front()->constraints().isChangeStreamStage()) { return {ErrorCodes::OptionNotSupportedOnView, "$changeStream cannot be used in a view definition"}; } return std::move(involvedNamespaces); }
Status ViewCatalog::_validateCollation_inlock(OperationContext* opCtx, const ViewDefinition& view, const std::vector<NamespaceString>& refs) { for (auto&& potentialViewNss : refs) { auto otherView = _lookup_inlock(opCtx, potentialViewNss.ns()); if (otherView && !CollatorInterface::collatorsMatch(view.defaultCollator(), otherView->defaultCollator())) { return {ErrorCodes::OptionNotSupportedOnView, str::stream() << "View " << view.name().toString() << " has conflicting collation with view " << otherView->name().toString()}; } } return Status::OK(); }
void ViewGraph::insertWithoutValidating(const ViewDefinition& view, const std::vector<NamespaceString>& refs, int pipelineSize) { uint64_t nodeId = _getNodeId(view.name()); // Note, the parent pointers of this node are set when the parents are inserted. // This sets the children pointers of the node for this view, as well as the parent // pointers for its children. Node* node = &(_graph[nodeId]); invariant(node->children.empty()); invariant(!static_cast<bool>(node->collator)); node->size = pipelineSize; node->collator = view.defaultCollator(); for (const NamespaceString& childNss : refs) { uint64_t childId = _getNodeId(childNss); node->children.insert(childId); _graph[childId].parents.insert(nodeId); } }
StatusWith<stdx::unordered_set<NamespaceString>> ViewCatalog::_validatePipeline_inlock( OperationContext* opCtx, const ViewDefinition& viewDef) const { AggregationRequest request(viewDef.viewOn(), viewDef.pipeline()); const LiteParsedPipeline liteParsedPipeline(request); const auto involvedNamespaces = liteParsedPipeline.getInvolvedNamespaces(); // Verify that this is a legitimate pipeline specification by making sure it parses // correctly. In order to parse a pipeline we need to resolve any namespaces involved to a // collection and a pipeline, but in this case we don't need this map to be accurate since // we will not be evaluating the pipeline. StringMap<ExpressionContext::ResolvedNamespace> resolvedNamespaces; for (auto&& nss : involvedNamespaces) { resolvedNamespaces[nss.coll()] = {nss, {}}; } boost::intrusive_ptr<ExpressionContext> expCtx = new ExpressionContext(opCtx, request, CollatorInterface::cloneCollator(viewDef.defaultCollator()), // We can use a stub MongoProcessInterface because we are only parsing // the Pipeline for validation here. We won't do anything with the // pipeline that will require a real implementation. std::make_shared<StubMongoProcessInterface>(), std::move(resolvedNamespaces)); auto pipelineStatus = Pipeline::parse(viewDef.pipeline(), std::move(expCtx)); if (!pipelineStatus.isOK()) { return pipelineStatus.getStatus(); } // Validate that the view pipeline does not contain any ineligible stages. auto sources = pipelineStatus.getValue()->getSources(); if (!sources.empty() && sources.front()->constraints().isChangeStreamStage()) { return {ErrorCodes::OptionNotSupportedOnView, "$changeStream cannot be used in a view definition"}; } return std::move(involvedNamespaces); }