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