diff options
Diffstat (limited to 'src/mongo/db/commands/find_cmd.cpp')
| -rw-r--r-- | src/mongo/db/commands/find_cmd.cpp | 211 |
1 files changed, 92 insertions, 119 deletions
diff --git a/src/mongo/db/commands/find_cmd.cpp b/src/mongo/db/commands/find_cmd.cpp index df96e09e2ce..f7ac781404f 100644 --- a/src/mongo/db/commands/find_cmd.cpp +++ b/src/mongo/db/commands/find_cmd.cpp @@ -36,7 +36,6 @@ #include "mongo/db/catalog/collection_uuid_mismatch.h" #include "mongo/db/client.h" #include "mongo/db/clientcursor.h" -#include "mongo/db/collection_type.h" #include "mongo/db/commands.h" #include "mongo/db/commands/run_aggregate.h" #include "mongo/db/commands/test_commands_enabled.h" @@ -55,10 +54,6 @@ #include "mongo/db/query/find_common.h" #include "mongo/db/query/get_executor.h" #include "mongo/db/query/query_knobs_gen.h" -#include "mongo/db/query/query_shape/query_shape.h" -#include "mongo/db/query/query_stats/find_key.h" -#include "mongo/db/query/query_stats/key.h" -#include "mongo/db/query/query_stats/query_stats.h" #include "mongo/db/repl/replication_coordinator.h" #include "mongo/db/service_context.h" #include "mongo/db/stats/counters.h" @@ -116,28 +111,66 @@ std::unique_ptr<FindCommandRequest> translateNtoReturnToLimitOrBatchSize( return findCmd; } +// Parses the command object to a FindCommandRequest. If the client request did not specify any +// runtime constants, make them available to the query here. +std::unique_ptr<FindCommandRequest> parseCmdObjectToFindCommandRequest(OperationContext* opCtx, + NamespaceString nss, + BSONObj cmdObj) { + auto findCommand = query_request_helper::makeFromFindCommand( + std::move(cmdObj), + std::move(nss), + APIParameters::get(opCtx).getAPIStrict().value_or(false)); + + // Rewrite any FLE find payloads that exist in the query if this is a FLE 2 query. + if (shouldDoFLERewrite(findCommand)) { + invariant(findCommand->getNamespaceOrUUID().nss()); + processFLEFindD(opCtx, findCommand->getNamespaceOrUUID().nss().get(), findCommand.get()); + } + + return translateNtoReturnToLimitOrBatchSize(std::move(findCommand)); +} + boost::intrusive_ptr<ExpressionContext> makeExpressionContext( OperationContext* opCtx, const FindCommandRequest& findCommand, - const CollectionPtr& collPtr, boost::optional<ExplainOptions::Verbosity> verbosity) { std::unique_ptr<CollatorInterface> collator; if (!findCommand.getCollation().isEmpty()) { collator = uassertStatusOK(CollatorFactoryInterface::get(opCtx->getServiceContext()) ->makeFromBSON(findCommand.getCollation())); - } else if (collPtr && collPtr->getDefaultCollator()) { - // The 'collPtr' will be null for views, but we don't need to worry about views here. The - // views will get rewritten into aggregate command and will regenerate the - // ExpressionContext. - collator = collPtr->getDefaultCollator()->clone(); } - auto expCtx = - make_intrusive<ExpressionContext>(opCtx, - findCommand, - std::move(collator), - CurOp::get(opCtx)->dbProfileLevel() > 0, // mayDbProfile - verbosity, - allowDiskUseByDefault.load()); + + // Although both 'find' and 'aggregate' commands have an ExpressionContext, some of the data + // members in the ExpressionContext are used exclusively by the aggregation subsystem. This + // includes the following fields which here we simply initialize to some meaningless default + // value: + // - explain + // - fromMongos + // - needsMerge + // - bypassDocumentValidation + // - mongoProcessInterface + // - resolvedNamespaces + // - uuid + // + // As we change the code to make the find and agg systems more tightly coupled, it would make + // sense to start initializing these fields for find operations as well. + auto expCtx = make_intrusive<ExpressionContext>( + opCtx, + verbosity, + false, // fromMongos + false, // needsMerge + findCommand.getAllowDiskUse().value_or(allowDiskUseByDefault.load()), + false, // bypassDocumentValidation + false, // isMapReduceCommand + findCommand.getNamespaceOrUUID().nss().value_or(NamespaceString()), + findCommand.getLegacyRuntimeConstants(), + std::move(collator), + nullptr, // mongoProcessInterface + StringMap<ExpressionContext::ResolvedNamespace>{}, + boost::none, // uuid + findCommand.getLet(), // let + CurOp::get(opCtx)->dbProfileLevel() > 0 // mayDbProfile + ); if (storageGlobalParams.readOnly) { // Disallow disk use if in read-only mode. expCtx->allowDiskUse = false; @@ -159,45 +192,6 @@ void beginQueryOp(OperationContext* opCtx, const NamespaceString& nss, const BSO } /** - * Parses the grammar elements like 'filter', 'sort', and 'projection' from the raw - * 'FindCommandRequest', and tracks internal state like begining the operation's timer and recording - * query shape stats (if enabled). - */ -std::unique_ptr<CanonicalQuery> parseQueryAndBeginOperation( - OperationContext* opCtx, - const AutoGetCollectionForReadCommandMaybeLockFree& ctx, - const NamespaceString& nss, - BSONObj requestBody, - std::unique_ptr<FindCommandRequest> findCommand, - const CollectionPtr& collection) { - // Fill out curop information. - beginQueryOp(opCtx, nss, requestBody); - // Finish the parsing step by using the FindCommandRequest to create a CanonicalQuery. - const ExtensionsCallbackReal extensionsCallback(opCtx, &nss); - - auto expCtx = - makeExpressionContext(opCtx, *findCommand, collection, boost::none /* verbosity */); - - auto parsedRequest = uassertStatusOK( - parsed_find_command::parse(expCtx, - std::move(findCommand), - extensionsCallback, - MatchExpressionParser::kAllowAllSpecialFeatures)); - - // Register query stats collection. Exclude queries against collections with encrypted fields. - // It is important to do this before canonicalizing and optimizing the query, each of which - // would alter the query shape. - if (!(collection && collection.get()->getCollectionOptions().encryptedFieldConfig)) { - query_stats::registerRequest(opCtx, nss, [&]() { - return std::make_unique<query_stats::FindKey>( - expCtx, *parsedRequest, ctx.getCollectionType()); - }); - } - - return uassertStatusOK( - CanonicalQuery::canonicalize(std::move(expCtx), std::move(parsedRequest))); -} -/** * A command for running .find() queries. */ class FindCmd final : public Command { @@ -250,7 +244,7 @@ public: return false; } - bool shouldAffectReadOptionCounters() const override { + bool shouldAffectReadConcernCounter() const override { return true; } @@ -322,20 +316,11 @@ public: const auto nss = ctx->getNss(); // Parse the command BSON to a FindCommandRequest. - auto findCommand = _parseCmdObjectToFindCommandRequest(opCtx, nss, _request.body); + auto findCommand = parseCmdObjectToFindCommandRequest(opCtx, nss, _request.body); // Finish the parsing step by using the FindCommandRequest to create a CanonicalQuery. const ExtensionsCallbackReal extensionsCallback(opCtx, &nss); - - // The collection may be NULL. If so, getExecutor() should handle it by returning an - // execution tree with an EOFStage. - const auto& collection = ctx->getCollection(); - if (!ctx->getView()) { - const bool isClusteredCollection = collection && collection->isClustered(); - uassertStatusOK(query_request_helper::validateResumeAfter( - findCommand->getResumeAfter(), isClusteredCollection)); - } - auto expCtx = makeExpressionContext(opCtx, *findCommand, collection, verbosity); + auto expCtx = makeExpressionContext(opCtx, *findCommand, verbosity); const bool isExplain = true; auto cq = uassertStatusOK( CanonicalQuery::canonicalize(opCtx, @@ -372,8 +357,8 @@ public: try { // An empty PrivilegeVector is acceptable because these privileges are only // checked on getMore and explain will not open a cursor. - uassertStatusOK( - runAggregate(opCtx, aggRequest, viewAggCmd, PrivilegeVector(), result)); + uassertStatusOK(runAggregate( + opCtx, nss, aggRequest, viewAggCmd, PrivilegeVector(), result)); } catch (DBException& error) { if (error.code() == ErrorCodes::InvalidPipelineOperator) { uasserted(ErrorCodes::InvalidPipelineOperator, @@ -385,6 +370,10 @@ public: return; } + // The collection may be NULL. If so, getExecutor() should handle it by returning an + // execution tree with an EOFStage. + const auto& collection = ctx->getCollection(); + // Get the execution plan for the query. bool permitYield = true; auto exec = @@ -419,10 +408,10 @@ public: // Parse the command BSON to a FindCommandRequest. Pass in the parsedNss in case cmdObj // does not have a UUID. auto parsedNss = NamespaceString{CommandHelpers::parseNsFromCommand(_dbName, cmdObj)}; + const bool isExplain = false; const bool isOplogNss = (parsedNss == NamespaceString::kRsOplogNamespace); auto findCommand = - _parseCmdObjectToFindCommandRequest(opCtx, std::move(parsedNss), cmdObj); - CurOp::get(opCtx)->beginQueryPlanningTimer(); + parseCmdObjectToFindCommandRequest(opCtx, std::move(parsedNss), cmdObj); // Only allow speculative majority for internal commands that specify the correct flag. uassert(ErrorCodes::ReadConcernMajorityNotEnabled, @@ -516,16 +505,14 @@ public: } // Tailing a replicated capped clustered collection requires majority read concern. - const auto& collection = ctx->getCollection(); - - bool isClusteredCollection = false; - if (collection) { + const auto coll = ctx->getCollection().get(); + if (coll) { const bool isTailable = findCommand->getTailable(); const bool isMajorityReadConcern = repl::ReadConcernArgs::get(opCtx).getLevel() == repl::ReadConcernLevel::kMajorityReadConcern; - isClusteredCollection = collection->isClustered(); - const bool isCapped = collection->isCapped(); - const bool isReplicated = collection->ns().isReplicated(); + const bool isClusteredCollection = coll->isClustered(); + const bool isCapped = coll->isCapped(); + const bool isReplicated = coll->ns().isReplicated(); if (isClusteredCollection && isCapped && isReplicated && isTailable) { uassert(ErrorCodes::Error(6049203), "A tailable cursor on a capped clustered collection requires majority " @@ -534,16 +521,19 @@ public: } } - // Views use the aggregation system and the $_resumeAfter parameter is not allowed. A - // more descriptive error will be raised later, but we want to validate this parameter - // before beginning the operation. - if (!ctx->getView()) { - uassertStatusOK(query_request_helper::validateResumeAfter( - findCommand->getResumeAfter(), isClusteredCollection)); - } + // Fill out curop information. + beginQueryOp(opCtx, nss, _request.body); - auto cq = parseQueryAndBeginOperation( - opCtx, *ctx, nss, _request.body, std::move(findCommand), collection); + // Finish the parsing step by using the FindCommandRequest to create a CanonicalQuery. + const ExtensionsCallbackReal extensionsCallback(opCtx, &nss); + auto expCtx = makeExpressionContext(opCtx, *findCommand, boost::none /* verbosity */); + auto cq = uassertStatusOK( + CanonicalQuery::canonicalize(opCtx, + std::move(findCommand), + isExplain, + std::move(expCtx), + extensionsCallback, + MatchExpressionParser::kAllowAllSpecialFeatures)); // If we are running a query against a view, or if we are trying to test the new // optimizer, redirect this query through the aggregation system. @@ -560,9 +550,6 @@ public: auto viewAggregationCommand = uassertStatusOK(query_request_helper::asAggregationCommand(findCommand)); - // This doesn't directly call 'runAggregate()' so it doesn't need to adapt to the - // new API on v6.0. @Alyssa this suggests we should look into view performance more - // carefully on v6.0. The perf of this code path may have different characteristics? BSONObj aggResult = CommandHelpers::runCommandDirectly( opCtx, OpMsgRequest::fromDBAndBody(_dbName, std::move(viewAggregationCommand))); auto status = getStatusFromCommandResult(aggResult); @@ -583,6 +570,8 @@ public: uassertStatusOK(replCoord->checkCanServeReadsFor( opCtx, nss, ReadPreferenceSetting::get(opCtx).canRunOnSecondary())); + const auto& collection = ctx->getCollection(); + if (cq->getFindCommandRequest().getReadOnce()) { // The readOnce option causes any storage-layer cursors created during plan // execution to assume read data will not be needed again and need not be cached. @@ -616,7 +605,7 @@ public: // there is no ClientCursor id, and then return. const long long numResults = 0; const CursorId cursorId = 0; - endQueryOp(opCtx, collection, *exec, numResults, boost::none, cmdObj); + endQueryOp(opCtx, collection, *exec, numResults, cursorId); auto bodyBuilder = result->getBodyBuilder(); appendCursorResponseObject( cursorId, nss.ns(), BSONArray(), boost::none, &bodyBuilder); @@ -667,6 +656,8 @@ public: auto&& [stats, _] = explainer.getWinningPlanStats(ExplainOptions::Verbosity::kExecStats); LOGV2_WARNING(23798, + "Plan executor error during find command: {error}, " + "stats: {stats}, cmd: {cmd}", "Plan executor error during find command", "error"_attr = exception.toStatus(), "stats"_attr = redact(stats), @@ -715,24 +706,26 @@ public: pinnedCursor.getCursor()->setLeftoverMaxTimeMicros( opCtx->getRemainingMaxTimeMicros()); } + pinnedCursor.getCursor()->setNReturnedSoFar(numResults); + pinnedCursor.getCursor()->incNBatches(); // Fill out curop based on the results. - endQueryOp(opCtx, collection, *cursorExec, numResults, pinnedCursor, cmdObj); + endQueryOp(opCtx, collection, *cursorExec, numResults, cursorId); if (stashResourcesForGetMore) { // Collect storage stats now before we stash the recovery unit. These stats are // normally collected in the service entry point layer just before a command - // ends, but they must be collected before stashing the RecoveryUnit. Otherwise, - // the service entry point layer will collect the stats from the new - // RecoveryUnit, which wasn't actually used for the query. + // ends, but they must be collected before stashing the + // RecoveryUnit. Otherwise, the service entry point layer will collect the + // stats from the new RecoveryUnit, which wasn't actually used for the query. // // The stats collected here will not get overwritten, as the service entry // point layer will only set these stats when they're not empty. CurOp::get(opCtx)->debug().storageStats = - opCtx->recoveryUnit()->computeOperationStatisticsSinceLastCall(); + opCtx->recoveryUnit()->getOperationStatistics(); } } else { - endQueryOp(opCtx, collection, *exec, numResults, boost::none, cmdObj); + endQueryOp(opCtx, collection, *exec, numResults, cursorId); } // Generate the response object to send to the client. @@ -759,7 +752,6 @@ public: keyBob.append("min", 1); keyBob.append("max", 1); keyBob.append("shardVersion", 1); - keyBob.append("databaseVersion", 1); return keyBob.obj(); }(); @@ -773,25 +765,6 @@ public: private: const OpMsgRequest _request; const StringData _dbName; - - // Parses the command object to a FindCommandRequest. If the client request did not specify - // any runtime constants, make them available to the query here. - std::unique_ptr<FindCommandRequest> _parseCmdObjectToFindCommandRequest( - OperationContext* opCtx, NamespaceString nss, BSONObj cmdObj) { - auto findCommand = query_request_helper::makeFromFindCommand( - std::move(cmdObj), - std::move(nss), - APIParameters::get(opCtx).getAPIStrict().value_or(false)); - - // Rewrite any FLE find payloads that exist in the query if this is a FLE 2 query. - if (shouldDoFLERewrite(findCommand)) { - invariant(findCommand->getNamespaceOrUUID().nss()); - processFLEFindD( - opCtx, findCommand->getNamespaceOrUUID().nss().value(), findCommand.get()); - } - - return translateNtoReturnToLimitOrBatchSize(std::move(findCommand)); - } }; } findCmd; |
