summaryrefslogtreecommitdiff
path: root/src/mongo/db/commands/run_aggregate.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/mongo/db/commands/run_aggregate.cpp')
-rw-r--r--src/mongo/db/commands/run_aggregate.cpp187
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.