diff options
Diffstat (limited to 'src/mongo/db/commands/run_aggregate.cpp')
-rw-r--r-- | src/mongo/db/commands/run_aggregate.cpp | 187 |
1 files changed, 107 insertions, 80 deletions
diff --git a/src/mongo/db/commands/run_aggregate.cpp b/src/mongo/db/commands/run_aggregate.cpp index e08e6b09e30..6cb86f131da 100644 --- a/src/mongo/db/commands/run_aggregate.cpp +++ b/src/mongo/db/commands/run_aggregate.cpp @@ -656,6 +656,95 @@ std::vector<std::unique_ptr<PlanExecutor, PlanExecutor::Deleter>> createLegacyEx return execs; } +Status runAggregateOnView(OperationContext* opCtx, + const NamespaceString& origNss, + const AggregateCommandRequest& request, + const MultipleCollectionAccessor& collections, + boost::optional<std::unique_ptr<CollatorInterface>> collatorToUse, + const ViewDefinition* view, + const boost::intrusive_ptr<ExpressionContext>& expCtx, + std::shared_ptr<const CollectionCatalog> catalog, + const PrivilegeVector& privileges, + CurOp* curOp, + rpc::ReplyBuilderInterface* result, + const std::function<void(void)>& resetContextFn) { + auto nss = request.getNamespace(); + checkCollectionUUIDMismatch( + opCtx, nss, collections.getMainCollection(), request.getCollectionUUID()); + + uassert(ErrorCodes::CommandNotSupportedOnView, + "mapReduce on a view is not supported", + !request.getIsMapReduceCommand()); + + // Check that the default collation of 'view' is compatible with the operation's + // collation. The check is skipped if the request did not specify a collation. + if (!request.getCollation().get_value_or(BSONObj()).isEmpty()) { + invariant(collatorToUse); // Should already be resolved at this point. + if (!CollatorInterface::collatorsMatch(view->defaultCollator(), collatorToUse->get()) && + !view->timeseries()) { + + return {ErrorCodes::OptionNotSupportedOnView, + "Cannot override a view's default collation"}; + } + } + + // Queries on timeseries views may specify non-default collation whereas queries + // on all other types of views must match the default collator (the collation use + // to originally create that collections). Thus in the case of operations on TS + // views, we use the request's collation. + auto timeSeriesCollator = view->timeseries() ? request.getCollation() : boost::none; + + auto resolvedView = + uassertStatusOK(view_catalog_helpers::resolveView(opCtx, catalog, nss, timeSeriesCollator)); + + // With the view & collation resolved, we can relinquish locks. + resetContextFn(); + + // Set this operation's shard version for the underlying collection to unsharded. + // This is prerequisite for future shard versioning checks. + boost::optional<ScopedSetShardRole> scopeSetShardRole; + if (!serverGlobalParams.clusterRole.has(ClusterRole::None)) { + scopeSetShardRole.emplace(opCtx, + resolvedView.getNamespace(), + ShardVersion::UNSHARDED() /* shardVersion */, + boost::none /* databaseVersion */); + }; + uassert(std::move(resolvedView), + "Explain of a resolved view must be executed by mongos", + !ShardingState::get(opCtx)->enabled() || !request.getExplain()); + + // Parse the resolved view into a new aggregation request. + auto newRequest = resolvedView.asExpandedViewAggregation(request); + auto newCmd = aggregation_request_helper::serializeToCommandObj(newRequest); + + auto status{Status::OK()}; + try { + status = runAggregate(opCtx, origNss, newRequest, newCmd, privileges, result); + } catch (const ExceptionForCat<ErrorCategory::StaleShardVersionError>& ex) { + // Since we expect the view to be UNSHARDED, if we reached to this point there are + // two possibilities: + // 1. The shard doesn't know what its shard version/state is and needs to recover + // it (in which case we throw so that the shard can run recovery) + // 2. The collection references by the view is actually SHARDED, in which case the + // router must execute it + if (const auto staleInfo{ex.extraInfo<StaleConfigInfo>()}) { + uassert(std::move(resolvedView), + "Resolved views on sharded collections must be executed by mongos", + !staleInfo->getVersionWanted()); + } + throw; + } + + { + // Set the namespace of the curop back to the view namespace so ctx records + // stats on this view namespace on destruction. + stdx::lock_guard<Client> lk(*opCtx->getClient()); + curOp->setNS_inlock(nss); + } + + return status; +} + } // namespace Status runAggregate(OperationContext* opCtx, @@ -887,86 +976,24 @@ Status runAggregate(OperationContext* opCtx, // recursively calling runAggregate(), which will re-acquire locks on the underlying // collection. (The lock must be released because recursively acquiring locks on the // database will prohibit yielding.) - if (ctx && ctx->getView() && !liteParsedPipeline.startsWithCollStats()) { - invariant(nss != NamespaceString::kRsOplogNamespace); - invariant(!nss.isCollectionlessAggregateNS()); - - checkCollectionUUIDMismatch( - opCtx, nss, collections.getMainCollection(), request.getCollectionUUID()); - - uassert(ErrorCodes::CommandNotSupportedOnView, - "mapReduce on a view is not supported", - !request.getIsMapReduceCommand()); - - // Check that the default collation of 'view' is compatible with the operation's - // collation. The check is skipped if the request did not specify a collation. - if (!request.getCollation().get_value_or(BSONObj()).isEmpty()) { - invariant(collatorToUse); // Should already be resolved at this point. - if (!CollatorInterface::collatorsMatch(ctx->getView()->defaultCollator(), - collatorToUse->get()) && - !ctx->getView()->timeseries()) { - - return {ErrorCodes::OptionNotSupportedOnView, - "Cannot override a view's default collation"}; - } - } - - // Queries on timeseries views may specify non-default collation whereas queries - // on all other types of views must match the default collator (the collation use - // to originally create that collections). Thus in the case of operations on TS - // views, we use the request's collation. - auto timeSeriesCollator = - ctx->getView()->timeseries() ? request.getCollation() : boost::none; - - auto resolvedView = uassertStatusOK( - view_catalog_helpers::resolveView(opCtx, catalog, nss, timeSeriesCollator)); - - // With the view & collation resolved, we can relinquish locks. - resetContext(); - - // Set this operation's shard version for the underlying collection to unsharded. - // This is prerequisite for future shard versioning checks. - boost::optional<ScopedSetShardRole> scopeSetShardRole; - if (!serverGlobalParams.clusterRole.has(ClusterRole::None)) { - scopeSetShardRole.emplace(opCtx, - resolvedView.getNamespace(), - ShardVersion::UNSHARDED() /* shardVersion */, - boost::none /* databaseVersion */); - }; - uassert(std::move(resolvedView), - "Explain of a resolved view must be executed by mongos", - !ShardingState::get(opCtx)->enabled() || !request.getExplain()); - - // Parse the resolved view into a new aggregation request. - auto newRequest = resolvedView.asExpandedViewAggregation(request); - auto newCmd = aggregation_request_helper::serializeToCommandObj(newRequest); - - auto status{Status::OK()}; - try { - status = runAggregate(opCtx, origNss, newRequest, newCmd, privileges, result); - } catch (const ExceptionForCat<ErrorCategory::StaleShardVersionError>& ex) { - // Since we expect the view to be UNSHARDED, if we reached to this point there are - // two possibilities: - // 1. The shard doesn't know what its shard version/state is and needs to recover - // it (in which case we throw so that the shard can run recovery) - // 2. The collection references by the view is actually SHARDED, in which case the - // router must execute it - if (const auto staleInfo{ex.extraInfo<StaleConfigInfo>()}) { - uassert(std::move(resolvedView), - "Resolved views on sharded collections must be executed by mongos", - !staleInfo->getVersionWanted()); - } - throw; - } - - { - // Set the namespace of the curop back to the view namespace so ctx records - // stats on this view namespace on destruction. - stdx::lock_guard<Client> lk(*opCtx->getClient()); - curOp->setNS_inlock(nss); - } - - return status; + // We do not need to expand the view pipeline when there is a $collStats stage, as + // $collStats is supported on a view namespace. For a time-series collection, however, the + // view is abstracted out for the users, so we needed to resolve the namespace to get the + // underlying bucket collection. + if (ctx && ctx->getView() && + (!liteParsedPipeline.startsWithCollStats() || ctx->getView()->timeseries())) { + return runAggregateOnView(opCtx, + origNss, + request, + collections, + std::move(collatorToUse), + ctx->getView(), + expCtx, + catalog, + privileges, + curOp, + result, + resetContext); } // If collectionUUID was provided, verify the collection exists and has the expected UUID. |