diff options
100 files changed, 2152 insertions, 2470 deletions
diff --git a/src/mongo/client/dbclient_cursor.cpp b/src/mongo/client/dbclient_cursor.cpp index 999a382b6ff..4dab6959f26 100644 --- a/src/mongo/client/dbclient_cursor.cpp +++ b/src/mongo/client/dbclient_cursor.cpp @@ -47,7 +47,7 @@ #include "mongo/db/pipeline/aggregation_request_helper.h" #include "mongo/db/query/cursor_response.h" #include "mongo/db/query/getmore_request.h" -#include "mongo/db/query/query_request.h" +#include "mongo/db/query/query_request_helper.h" #include "mongo/logv2/log.h" #include "mongo/rpc/factory.h" #include "mongo/rpc/get_status_from_command_result.h" @@ -120,43 +120,46 @@ Message DBClientCursor::_assembleInit() { } else if (_useFindCommand) { // The caller supplies a 'query' object which may have $-prefixed directives in the format // expected for a legacy OP_QUERY. Therefore, we use the legacy parsing code supplied by - // QueryRequest. When actually issuing the request to the remote node, we will assemble a - // find command. - auto qr = QueryRequest::fromLegacyQuery(_nsOrUuid, - query, - fieldsToReturn ? *fieldsToReturn : BSONObj(), - nToSkip, - nextBatchSize(), - opts); - if (qr.isOK() && !qr.getValue()->isExplain()) { + // query_request_helper. When actually issuing the request to the remote node, we will + // assemble a find command. + bool explain = false; + auto findCommand = + query_request_helper::fromLegacyQuery(_nsOrUuid, + query, + fieldsToReturn ? *fieldsToReturn : BSONObj(), + nToSkip, + nextBatchSize(), + opts, + &explain); + if (findCommand.isOK() && !explain) { if (query.getBoolField("$readOnce")) { // Legacy queries don't handle readOnce. - qr.getValue()->setReadOnce(true); + findCommand.getValue()->setReadOnce(true); } if (query.getBoolField(FindCommand::kRequestResumeTokenFieldName)) { // Legacy queries don't handle requestResumeToken. - qr.getValue()->setRequestResumeToken(true); + findCommand.getValue()->setRequestResumeToken(true); } if (query.hasField(FindCommand::kResumeAfterFieldName)) { // Legacy queries don't handle resumeAfter. - qr.getValue()->setResumeAfter( + findCommand.getValue()->setResumeAfter( query.getObjectField(FindCommand::kResumeAfterFieldName)); } if (auto replTerm = query[FindCommand::kTermFieldName]) { // Legacy queries don't handle term. - qr.getValue()->setReplicationTerm(replTerm.numberLong()); + findCommand.getValue()->setTerm(replTerm.numberLong()); } // Legacy queries don't handle readConcern. // We prioritize the readConcern parsed from the query object over '_readConcernObj'. if (auto readConcern = query[repl::ReadConcernArgs::kReadConcernFieldName]) { - qr.getValue()->setReadConcern(readConcern.Obj()); + findCommand.getValue()->setReadConcern(readConcern.Obj()); } else if (_readConcernObj) { - qr.getValue()->setReadConcern(*_readConcernObj); + findCommand.getValue()->setReadConcern(_readConcernObj); } - BSONObj cmd = qr.getValue()->asFindCommand(); + BSONObj cmd = findCommand.getValue()->toBSON(BSONObj()); if (auto readPref = query["$readPreference"]) { - // QueryRequest doesn't handle $readPreference. + // FindCommand doesn't handle $readPreference. cmd = BSONObjBuilder(std::move(cmd)).append(readPref).obj(); } return assembleCommandRequest(_client, ns.db(), opts, std::move(cmd)); @@ -165,7 +168,7 @@ Message DBClientCursor::_assembleInit() { // Legacy OP_QUERY request does not support UUIDs. if (_nsOrUuid.uuid()) { // If there was a problem building the query request, report that. - uassertStatusOK(qr.getStatus()); + uassertStatusOK(findCommand.getStatus()); // Otherwise it must have been explain. uasserted(50937, "Query by UUID is not supported for explain queries."); } diff --git a/src/mongo/db/clientcursor.h b/src/mongo/db/clientcursor.h index 745b03157f3..4e6137751f0 100644 --- a/src/mongo/db/clientcursor.h +++ b/src/mongo/db/clientcursor.h @@ -68,9 +68,7 @@ struct ClientCursorParams { apiParameters(std::move(apiParameters)), writeConcernOptions(std::move(writeConcernOptions)), readConcernArgs(std::move(readConcernArgs)), - queryOptions(exec->getCanonicalQuery() - ? exec->getCanonicalQuery()->getQueryRequest().getOptions() - : 0), + queryOptions(exec->getCanonicalQuery() ? exec->getCanonicalQuery()->getOptions() : 0), originatingCommandObj(originatingCommandObj.getOwned()), originatingPrivileges(std::move(originatingPrivileges)) { while (authenticatedUsersIter.more()) { diff --git a/src/mongo/db/commands/dbcommands_d.cpp b/src/mongo/db/commands/dbcommands_d.cpp index 66b175fd6bb..6ec683052f9 100644 --- a/src/mongo/db/commands/dbcommands_d.cpp +++ b/src/mongo/db/commands/dbcommands_d.cpp @@ -289,11 +289,11 @@ public: BSONObj sort = BSON("files_id" << 1 << "n" << 1); return writeConflictRetry(opCtx, "filemd5", dbname, [&] { - auto qr = std::make_unique<QueryRequest>(nss); - qr->setFilter(query); - qr->setSort(sort); + auto findCommand = std::make_unique<FindCommand>(nss); + findCommand->setFilter(query.getOwned()); + findCommand->setSort(sort.getOwned()); - auto statusWithCQ = CanonicalQuery::canonicalize(opCtx, std::move(qr)); + auto statusWithCQ = CanonicalQuery::canonicalize(opCtx, std::move(findCommand)); if (!statusWithCQ.isOK()) { uasserted(17240, "Can't canonicalize query " + query.toString()); return false; diff --git a/src/mongo/db/commands/find_cmd.cpp b/src/mongo/db/commands/find_cmd.cpp index 2b7665c8638..e315f1c314f 100644 --- a/src/mongo/db/commands/find_cmd.cpp +++ b/src/mongo/db/commands/find_cmd.cpp @@ -65,31 +65,29 @@ namespace { const auto kTermField = "term"_sd; -// Parses the command object to a QueryRequest. If the client request did not specify any runtime +// Parses the command object to a FindCommand. If the client request did not specify any runtime // constants, make them available to the query here. -std::unique_ptr<QueryRequest> parseCmdObjectToQueryRequest(OperationContext* opCtx, - NamespaceString nss, - BSONObj cmdObj, - bool isExplain) { - auto qr = - QueryRequest::makeFromFindCommand(std::move(cmdObj), - isExplain, - std::move(nss), - APIParameters::get(opCtx).getAPIStrict().value_or(false)); - if (!qr->getLegacyRuntimeConstants()) { - qr->setLegacyRuntimeConstants(Variables::generateRuntimeConstants(opCtx)); +std::unique_ptr<FindCommand> parseCmdObjectToFindCommand(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)); + if (!findCommand->getLegacyRuntimeConstants()) { + findCommand->setLegacyRuntimeConstants(Variables::generateRuntimeConstants(opCtx)); } - return qr; + return findCommand; } boost::intrusive_ptr<ExpressionContext> makeExpressionContext( OperationContext* opCtx, - const QueryRequest& queryRequest, + const FindCommand& findCommand, boost::optional<ExplainOptions::Verbosity> verbosity) { std::unique_ptr<CollatorInterface> collator; - if (!queryRequest.getCollation().isEmpty()) { + if (!findCommand.getCollation().isEmpty()) { collator = uassertStatusOK(CollatorFactoryInterface::get(opCtx->getServiceContext()) - ->makeFromBSON(queryRequest.getCollation())); + ->makeFromBSON(findCommand.getCollation())); } // Although both 'find' and 'aggregate' commands have an ExpressionContext, some of the data @@ -106,23 +104,23 @@ boost::intrusive_ptr<ExpressionContext> makeExpressionContext( // // 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 - queryRequest.allowDiskUse(), - false, // bypassDocumentValidation - false, // isMapReduceCommand - queryRequest.nss(), - queryRequest.getLegacyRuntimeConstants(), - std::move(collator), - nullptr, // mongoProcessInterface - StringMap<ExpressionContext::ResolvedNamespace>{}, - boost::none, // uuid - queryRequest.getLetParameters(), // let - CurOp::get(opCtx)->dbProfileLevel() > 0 // mayDbProfile - ); + auto expCtx = make_intrusive<ExpressionContext>( + opCtx, + verbosity, + false, // fromMongos + false, // needsMerge + findCommand.getAllowDiskUse(), + 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 + ); expCtx->tempDir = storageGlobalParams.dbpath + "/_tmp"; return expCtx; } @@ -250,16 +248,17 @@ public: AutoGetCollectionViewMode::kViewsPermitted); const auto nss = ctx->getNss(); - // Parse the command BSON to a QueryRequest. - const bool isExplain = true; - auto qr = parseCmdObjectToQueryRequest(opCtx, nss, _request.body, isExplain); + // Parse the command BSON to a FindCommand. + auto findCommand = parseCmdObjectToFindCommand(opCtx, nss, _request.body); - // Finish the parsing step by using the QueryRequest to create a CanonicalQuery. + // Finish the parsing step by using the FindCommand to create a CanonicalQuery. const ExtensionsCallbackReal extensionsCallback(opCtx, &nss); - auto expCtx = makeExpressionContext(opCtx, *qr, verbosity); + auto expCtx = makeExpressionContext(opCtx, *findCommand, verbosity); + const bool isExplain = true; auto cq = uassertStatusOK( CanonicalQuery::canonicalize(opCtx, - std::move(qr), + std::move(findCommand), + isExplain, std::move(expCtx), extensionsCallback, MatchExpressionParser::kAllowAllSpecialFeatures)); @@ -270,8 +269,9 @@ public: // Convert the find command into an aggregation using $match (and other stages, as // necessary), if possible. - const auto& qr = cq->getQueryRequest(); - auto viewAggregationCommand = uassertStatusOK(qr.asAggregationCommand()); + const auto& findCommand = cq->getFindCommand(); + auto viewAggregationCommand = + uassertStatusOK(query_request_helper::asAggregationCommand(findCommand)); auto viewAggCmd = OpMsgRequest::fromDBAndBody(_dbName, viewAggregationCommand).body; // Create the agg request equivalent of the find operation, with the explain @@ -329,31 +329,32 @@ public: const BSONObj& cmdObj = _request.body; - // Parse the command BSON to a QueryRequest. Pass in the parsedNss in case cmdObj does + // Parse the command BSON to a FindCommand. 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 qr = parseCmdObjectToQueryRequest(opCtx, std::move(parsedNss), cmdObj, isExplain); + auto findCommand = parseCmdObjectToFindCommand(opCtx, std::move(parsedNss), cmdObj); // Only allow speculative majority for internal commands that specify the correct flag. uassert(ErrorCodes::ReadConcernMajorityNotEnabled, "Majority read concern is not enabled.", !(repl::ReadConcernArgs::get(opCtx).isSpeculativeMajority() && - !qr->allowSpeculativeMajorityRead())); + !findCommand->getAllowSpeculativeMajorityRead())); auto replCoord = repl::ReplicationCoordinator::get(opCtx); const auto txnParticipant = TransactionParticipant::get(opCtx); uassert(ErrorCodes::InvalidOptions, "It is illegal to open a tailable cursor in a transaction", - !(opCtx->inMultiDocumentTransaction() && qr->isTailable())); + !(opCtx->inMultiDocumentTransaction() && findCommand->getTailable())); uassert(ErrorCodes::OperationNotSupportedInTransaction, "The 'readOnce' option is not supported within a transaction.", - !txnParticipant || !opCtx->inMultiDocumentTransaction() || !qr->isReadOnce()); + !txnParticipant || !opCtx->inMultiDocumentTransaction() || + !findCommand->getReadOnce()); // Validate term before acquiring locks, if provided. - auto term = qr->getReplicationTerm(); + auto term = findCommand->getTerm(); if (term) { // Note: updateTerm returns ok if term stayed the same. uassertStatusOK(replCoord->updateTerm(opCtx, *term)); @@ -379,13 +380,13 @@ public: const auto& nss = ctx->getNss(); uassert(ErrorCodes::NamespaceNotFound, - str::stream() << "UUID " << qr->uuid().get() + str::stream() << "UUID " << findCommand->getNamespaceOrUUID().uuid().get() << " specified in query request not found", - ctx || !qr->uuid()); + ctx || !findCommand->getNamespaceOrUUID().uuid()); // Set the namespace if a collection was found, as opposed to nothing or a view. if (ctx) { - qr->refreshNSS(ctx->getNss()); + query_request_helper::refreshNSS(ctx->getNss(), findCommand.get()); } // Check whether we are allowed to read from this node after acquiring our locks. @@ -401,12 +402,13 @@ public: const int ntoskip = -1; beginQueryOp(opCtx, nss, _request.body, ntoreturn, ntoskip); - // Finish the parsing step by using the QueryRequest to create a CanonicalQuery. + // Finish the parsing step by using the FindCommand to create a CanonicalQuery. const ExtensionsCallbackReal extensionsCallback(opCtx, &nss); - auto expCtx = makeExpressionContext(opCtx, *qr, boost::none /* verbosity */); + auto expCtx = makeExpressionContext(opCtx, *findCommand, boost::none /* verbosity */); auto cq = uassertStatusOK( CanonicalQuery::canonicalize(opCtx, - std::move(qr), + std::move(findCommand), + isExplain, std::move(expCtx), extensionsCallback, MatchExpressionParser::kAllowAllSpecialFeatures)); @@ -417,8 +419,9 @@ public: // Convert the find command into an aggregation using $match (and other stages, as // necessary), if possible. - const auto& qr = cq->getQueryRequest(); - auto viewAggregationCommand = uassertStatusOK(qr.asAggregationCommand()); + const auto& findCommand = cq->getFindCommand(); + auto viewAggregationCommand = + uassertStatusOK(query_request_helper::asAggregationCommand(findCommand)); BSONObj aggResult = CommandHelpers::runCommandDirectly( opCtx, OpMsgRequest::fromDBAndBody(_dbName, std::move(viewAggregationCommand))); @@ -434,7 +437,7 @@ public: const auto& collection = ctx->getCollection(); - if (cq->getQueryRequest().isReadOnce()) { + if (cq->getFindCommand().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. opCtx->recoveryUnit()->setReadOnce(true); @@ -463,7 +466,7 @@ public: FindCommon::waitInFindBeforeMakingBatch(opCtx, *exec->getCanonicalQuery()); - const QueryRequest& originalQR = exec->getCanonicalQuery()->getQueryRequest(); + const FindCommand& originalFC = exec->getCanonicalQuery()->getFindCommand(); // Stream query results, adding them to a BSONArray as we go. CursorResponseBuilder::Options options; @@ -479,7 +482,7 @@ public: ResourceConsumption::DocumentUnitCounter docUnitsReturned; try { - while (!FindCommon::enoughForFirstBatch(originalQR, numResults) && + while (!FindCommon::enoughForFirstBatch(originalFC, numResults) && PlanExecutor::ADVANCED == (state = exec->getNext(&obj, nullptr))) { // If we can't fit this result inside the current batch, then we stash it for // later. diff --git a/src/mongo/db/commands/getmore_cmd.cpp b/src/mongo/db/commands/getmore_cmd.cpp index 50c742a612d..50242d38565 100644 --- a/src/mongo/db/commands/getmore_cmd.cpp +++ b/src/mongo/db/commands/getmore_cmd.cpp @@ -521,7 +521,7 @@ public: PlanExecutor* exec = cursorPin->getExecutor(); const auto* cq = exec->getCanonicalQuery(); - if (cq && cq->getQueryRequest().isReadOnce()) { + if (cq && cq->getFindCommand().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. opCtx->recoveryUnit()->setReadOnce(true); diff --git a/src/mongo/db/commands/index_filter_commands.cpp b/src/mongo/db/commands/index_filter_commands.cpp index c9a82127980..286b3595056 100644 --- a/src/mongo/db/commands/index_filter_commands.cpp +++ b/src/mongo/db/commands/index_filter_commands.cpp @@ -303,15 +303,16 @@ Status ClearFilters::clear(OperationContext* opCtx, AllowedIndexEntry entry = *i; // Create canonical query. - auto qr = std::make_unique<QueryRequest>(nss); - qr->setFilter(entry.query); - qr->setSort(entry.sort); - qr->setProj(entry.projection); - qr->setCollation(entry.collation); + auto findCommand = std::make_unique<FindCommand>(nss); + findCommand->setFilter(entry.query); + findCommand->setSort(entry.sort); + findCommand->setProjection(entry.projection); + findCommand->setCollation(entry.collation); const boost::intrusive_ptr<ExpressionContext> expCtx; auto statusWithCQ = CanonicalQuery::canonicalize(opCtx, - std::move(qr), + std::move(findCommand), + false, expCtx, extensionsCallback, MatchExpressionParser::kAllowAllSpecialFeatures); diff --git a/src/mongo/db/commands/index_filter_commands_test.cpp b/src/mongo/db/commands/index_filter_commands_test.cpp index b1b7e7b7e29..760ccfbf9d2 100644 --- a/src/mongo/db/commands/index_filter_commands_test.cpp +++ b/src/mongo/db/commands/index_filter_commands_test.cpp @@ -130,12 +130,12 @@ void addQueryShapeToPlanCache(OperationContext* opCtx, const char* projectionStr, const char* collationStr) { // Create canonical query. - auto qr = std::make_unique<QueryRequest>(nss); - qr->setFilter(fromjson(queryStr)); - qr->setSort(fromjson(sortStr)); - qr->setProj(fromjson(projectionStr)); - qr->setCollation(fromjson(collationStr)); - auto statusWithCQ = CanonicalQuery::canonicalize(opCtx, std::move(qr)); + auto findCommand = std::make_unique<FindCommand>(nss); + findCommand->setFilter(fromjson(queryStr)); + findCommand->setSort(fromjson(sortStr)); + findCommand->setProjection(fromjson(projectionStr)); + findCommand->setCollation(fromjson(collationStr)); + auto statusWithCQ = CanonicalQuery::canonicalize(opCtx, std::move(findCommand)); ASSERT_OK(statusWithCQ.getStatus()); std::unique_ptr<CanonicalQuery> cq = std::move(statusWithCQ.getValue()); @@ -161,12 +161,12 @@ bool planCacheContains(OperationContext* opCtx, const char* collationStr) { // Create canonical query. - auto qr = std::make_unique<QueryRequest>(nss); - qr->setFilter(fromjson(queryStr)); - qr->setSort(fromjson(sortStr)); - qr->setProj(fromjson(projectionStr)); - qr->setCollation(fromjson(collationStr)); - auto statusWithInputQuery = CanonicalQuery::canonicalize(opCtx, std::move(qr)); + auto findCommand = std::make_unique<FindCommand>(nss); + findCommand->setFilter(fromjson(queryStr)); + findCommand->setSort(fromjson(sortStr)); + findCommand->setProjection(fromjson(projectionStr)); + findCommand->setCollation(fromjson(collationStr)); + auto statusWithInputQuery = CanonicalQuery::canonicalize(opCtx, std::move(findCommand)); ASSERT_OK(statusWithInputQuery.getStatus()); unique_ptr<CanonicalQuery> inputQuery = std::move(statusWithInputQuery.getValue()); @@ -181,12 +181,12 @@ bool planCacheContains(OperationContext* opCtx, // Canonicalize the query shape stored in the cache entry in order to get the plan cache // key. - auto qr = std::make_unique<QueryRequest>(nss); - qr->setFilter(createdFromQuery.filter); - qr->setSort(createdFromQuery.sort); - qr->setProj(createdFromQuery.projection); - qr->setCollation(createdFromQuery.collation); - auto statusWithCurrentQuery = CanonicalQuery::canonicalize(opCtx, std::move(qr)); + auto findCommand = std::make_unique<FindCommand>(nss); + findCommand->setFilter(createdFromQuery.filter); + findCommand->setSort(createdFromQuery.sort); + findCommand->setProjection(createdFromQuery.projection); + findCommand->setCollation(createdFromQuery.collation); + auto statusWithCurrentQuery = CanonicalQuery::canonicalize(opCtx, std::move(findCommand)); ASSERT_OK(statusWithCurrentQuery.getStatus()); unique_ptr<CanonicalQuery> currentQuery = std::move(statusWithCurrentQuery.getValue()); diff --git a/src/mongo/db/commands/plan_cache_clear_command.cpp b/src/mongo/db/commands/plan_cache_clear_command.cpp index 5dc3822bd4a..69c632bdc0a 100644 --- a/src/mongo/db/commands/plan_cache_clear_command.cpp +++ b/src/mongo/db/commands/plan_cache_clear_command.cpp @@ -86,9 +86,9 @@ Status clear(OperationContext* opCtx, "Query shape doesn't exist in PlanCache", "namespace"_attr = ns, "query"_attr = redact(cq->getQueryObj()), - "sort"_attr = cq->getQueryRequest().getSort(), - "projection"_attr = cq->getQueryRequest().getProj(), - "collation"_attr = cq->getQueryRequest().getCollation()); + "sort"_attr = cq->getFindCommand().getSort(), + "projection"_attr = cq->getFindCommand().getProjection(), + "collation"_attr = cq->getFindCommand().getCollation()); return Status::OK(); } @@ -99,9 +99,9 @@ Status clear(OperationContext* opCtx, "Removed plan cache entry", "namespace"_attr = ns, "query"_attr = redact(cq->getQueryObj()), - "sort"_attr = cq->getQueryRequest().getSort(), - "projection"_attr = cq->getQueryRequest().getProj(), - "collation"_attr = cq->getQueryRequest().getCollation()); + "sort"_attr = cq->getFindCommand().getSort(), + "projection"_attr = cq->getFindCommand().getProjection(), + "collation"_attr = cq->getFindCommand().getCollation()); return Status::OK(); } diff --git a/src/mongo/db/commands/plan_cache_commands.cpp b/src/mongo/db/commands/plan_cache_commands.cpp index 443d3ecc4dc..7ac23d5911b 100644 --- a/src/mongo/db/commands/plan_cache_commands.cpp +++ b/src/mongo/db/commands/plan_cache_commands.cpp @@ -85,16 +85,18 @@ StatusWith<std::unique_ptr<CanonicalQuery>> canonicalize(OperationContext* opCtx } // Create canonical query - auto qr = std::make_unique<QueryRequest>(NamespaceString{ns}); - qr->setFilter(queryObj); - qr->setSort(sortObj); - qr->setProj(projObj); - qr->setCollation(collationObj); - const ExtensionsCallbackReal extensionsCallback(opCtx, &qr->nss()); + auto findCommand = std::make_unique<FindCommand>(NamespaceString{ns}); + findCommand->setFilter(queryObj.getOwned()); + findCommand->setSort(sortObj.getOwned()); + findCommand->setProjection(projObj.getOwned()); + findCommand->setCollation(collationObj.getOwned()); + const ExtensionsCallbackReal extensionsCallback( + opCtx, findCommand->getNamespaceOrUUID().nss().get_ptr()); const boost::intrusive_ptr<ExpressionContext> expCtx; auto statusWithCQ = CanonicalQuery::canonicalize(opCtx, - std::move(qr), + std::move(findCommand), + false, expCtx, extensionsCallback, MatchExpressionParser::kAllowAllSpecialFeatures); diff --git a/src/mongo/db/db_raii_test.cpp b/src/mongo/db/db_raii_test.cpp index 72a9fab784b..cf142ce0222 100644 --- a/src/mongo/db/db_raii_test.cpp +++ b/src/mongo/db/db_raii_test.cpp @@ -70,8 +70,9 @@ public: std::unique_ptr<PlanExecutor, PlanExecutor::Deleter> makeTailableQueryPlan( OperationContext* opCtx, const CollectionPtr& collection) { - auto qr = std::make_unique<QueryRequest>(collection->ns()); - qr->setTailableMode(TailableModeEnum::kTailableAndAwaitData); + auto findCommand = std::make_unique<FindCommand>(collection->ns()); + query_request_helper::setTailableMode(TailableModeEnum::kTailableAndAwaitData, + findCommand.get()); awaitDataState(opCtx).shouldWaitForInserts = true; awaitDataState(opCtx).waitForInsertsDeadline = @@ -81,7 +82,8 @@ std::unique_ptr<PlanExecutor, PlanExecutor::Deleter> makeTailableQueryPlan( const boost::intrusive_ptr<ExpressionContext> expCtx; auto statusWithCQ = CanonicalQuery::canonicalize(opCtx, - std::move(qr), + std::move(findCommand), + false, expCtx, ExtensionsCallbackNoop(), MatchExpressionParser::kBanAllSpecialFeatures); diff --git a/src/mongo/db/dbhelpers.cpp b/src/mongo/db/dbhelpers.cpp index 94d5ef3e3d3..3fc790e9bec 100644 --- a/src/mongo/db/dbhelpers.cpp +++ b/src/mongo/db/dbhelpers.cpp @@ -81,14 +81,14 @@ RecordId Helpers::findOne(OperationContext* opCtx, if (!collection) return RecordId(); - auto qr = std::make_unique<QueryRequest>(collection->ns()); - qr->setFilter(query); - return findOne(opCtx, collection, std::move(qr), requireIndex); + auto findCommand = std::make_unique<FindCommand>(collection->ns()); + findCommand->setFilter(query); + return findOne(opCtx, collection, std::move(findCommand), requireIndex); } RecordId Helpers::findOne(OperationContext* opCtx, const CollectionPtr& collection, - std::unique_ptr<QueryRequest> qr, + std::unique_ptr<FindCommand> findCommand, bool requireIndex) { if (!collection) return RecordId(); @@ -98,7 +98,8 @@ RecordId Helpers::findOne(OperationContext* opCtx, const boost::intrusive_ptr<ExpressionContext> expCtx; auto statusWithCQ = CanonicalQuery::canonicalize(opCtx, - std::move(qr), + std::move(findCommand), + false, expCtx, extensionsCallback, MatchExpressionParser::kAllowAllSpecialFeatures); diff --git a/src/mongo/db/dbhelpers.h b/src/mongo/db/dbhelpers.h index 54fe7635ce4..284e1b4d051 100644 --- a/src/mongo/db/dbhelpers.h +++ b/src/mongo/db/dbhelpers.h @@ -39,7 +39,7 @@ class Collection; class CollectionPtr; class Database; class OperationContext; -class QueryRequest; +class FindCommand; /** * db helpers are helper functions and classes that let us easily manipulate the local @@ -75,7 +75,7 @@ struct Helpers { bool requireIndex); static RecordId findOne(OperationContext* opCtx, const CollectionPtr& collection, - std::unique_ptr<QueryRequest> qr, + std::unique_ptr<FindCommand> qr, bool requireIndex); /** diff --git a/src/mongo/db/exec/idhack.cpp b/src/mongo/db/exec/idhack.cpp index af8edac2a9b..db3a668bf27 100644 --- a/src/mongo/db/exec/idhack.cpp +++ b/src/mongo/db/exec/idhack.cpp @@ -58,7 +58,7 @@ IDHackStage::IDHackStage(ExpressionContext* expCtx, _workingSet(ws), _key(query->getQueryObj()["_id"].wrap()) { _specificStats.indexName = descriptor->indexName(); - _addKeyMetadata = query->getQueryRequest().returnKey(); + _addKeyMetadata = query->getFindCommand().getReturnKey(); } IDHackStage::IDHackStage(ExpressionContext* expCtx, diff --git a/src/mongo/db/exec/sbe_cmd.cpp b/src/mongo/db/exec/sbe_cmd.cpp index 680a5ae60f8..41451657e87 100644 --- a/src/mongo/db/exec/sbe_cmd.cpp +++ b/src/mongo/db/exec/sbe_cmd.cpp @@ -67,7 +67,7 @@ public: CommandHelpers::handleMarkKillOnClientDisconnect(opCtx); long long batchSize; uassertStatusOK(CursorRequest::parseCommandCursorOptions( - cmdObj, QueryRequest::kDefaultBatchSize, &batchSize)); + cmdObj, query_request_helper::kDefaultBatchSize, &batchSize)); sbe::Parser parser; auto root = parser.parse(opCtx, dbname, cmdObj["sbe"].String()); diff --git a/src/mongo/db/exec/subplan.cpp b/src/mongo/db/exec/subplan.cpp index c97c85d361e..9aaea0bb41d 100644 --- a/src/mongo/db/exec/subplan.cpp +++ b/src/mongo/db/exec/subplan.cpp @@ -72,28 +72,28 @@ SubplanStage::SubplanStage(ExpressionContext* expCtx, } bool SubplanStage::canUseSubplanning(const CanonicalQuery& query) { - const QueryRequest& qr = query.getQueryRequest(); + const FindCommand& findCommand = query.getFindCommand(); const MatchExpression* expr = query.root(); // Hint provided - if (!qr.getHint().isEmpty()) { + if (!findCommand.getHint().isEmpty()) { return false; } // Min provided // Min queries are a special case of hinted queries. - if (!qr.getMin().isEmpty()) { + if (!findCommand.getMin().isEmpty()) { return false; } // Max provided // Similar to min, max queries are a special case of hinted queries. - if (!qr.getMax().isEmpty()) { + if (!findCommand.getMax().isEmpty()) { return false; } // Tailable cursors won't get cached, just turn into collscans. - if (query.getQueryRequest().isTailable()) { + if (findCommand.getTailable()) { return false; } diff --git a/src/mongo/db/exec/trial_period_utils.cpp b/src/mongo/db/exec/trial_period_utils.cpp index 4369ff40a08..9a7bbe8bc41 100644 --- a/src/mongo/db/exec/trial_period_utils.cpp +++ b/src/mongo/db/exec/trial_period_utils.cpp @@ -54,11 +54,11 @@ size_t getTrialPeriodNumToReturn(const CanonicalQuery& query) { // Determine the number of results which we will produce during the plan ranking phase before // stopping. size_t numResults = static_cast<size_t>(internalQueryPlanEvaluationMaxResults.load()); - if (query.getQueryRequest().getNToReturn()) { + if (query.getFindCommand().getNtoreturn()) { numResults = - std::min(static_cast<size_t>(*query.getQueryRequest().getNToReturn()), numResults); - } else if (query.getQueryRequest().getLimit()) { - numResults = std::min(static_cast<size_t>(*query.getQueryRequest().getLimit()), numResults); + std::min(static_cast<size_t>(*query.getFindCommand().getNtoreturn()), numResults); + } else if (query.getFindCommand().getLimit()) { + numResults = std::min(static_cast<size_t>(*query.getFindCommand().getLimit()), numResults); } return numResults; diff --git a/src/mongo/db/matcher/expression_optimize_test.cpp b/src/mongo/db/matcher/expression_optimize_test.cpp index e46b9aaddba..2141c210a35 100644 --- a/src/mongo/db/matcher/expression_optimize_test.cpp +++ b/src/mongo/db/matcher/expression_optimize_test.cpp @@ -69,20 +69,20 @@ MatchExpression* parseMatchExpression(const BSONObj& obj) { * (expression tree, query request) tuple passes CanonicalQuery::isValid(). * Returns Status::OK() if the tuple is valid, else returns an error Status. */ -Status isValid(const std::string& queryStr, const QueryRequest& qrRaw) { +Status isValid(const std::string& queryStr, const FindCommand& findCommand) { BSONObj queryObj = fromjson(queryStr); std::unique_ptr<MatchExpression> me(parseMatchExpression(queryObj)); me = MatchExpression::optimize(std::move(me)); - return CanonicalQuery::isValid(me.get(), qrRaw).getStatus(); + return CanonicalQuery::isValid(me.get(), findCommand).getStatus(); } TEST(ExpressionOptimizeTest, IsValidText) { - // Filter inside QueryRequest is not used. - auto qr = std::make_unique<QueryRequest>(nss); - ASSERT_OK(qr->validate()); + // Filter inside FindCommand is not used. + auto findCommand = std::make_unique<FindCommand>(nss); + ASSERT_OK(query_request_helper::validateFindCommand(*findCommand)); // Valid: regular TEXT. - ASSERT_OK(isValid("{$text: {$search: 's'}}", *qr)); + ASSERT_OK(isValid("{$text: {$search: 's'}}", *findCommand)); // Valid: TEXT inside OR. ASSERT_OK( @@ -90,13 +90,13 @@ TEST(ExpressionOptimizeTest, IsValidText) { " {$text: {$search: 's'}}," " {a: 1}" "]}", - *qr)); + *findCommand)); // Valid: TEXT outside NOR. - ASSERT_OK(isValid("{$text: {$search: 's'}, $nor: [{a: 1}, {b: 1}]}", *qr)); + ASSERT_OK(isValid("{$text: {$search: 's'}, $nor: [{a: 1}, {b: 1}]}", *findCommand)); // Invalid: TEXT inside NOR. - ASSERT_NOT_OK(isValid("{$nor: [{$text: {$search: 's'}}, {a: 1}]}", *qr)); + ASSERT_NOT_OK(isValid("{$nor: [{$text: {$search: 's'}}, {a: 1}]}", *findCommand)); // Invalid: TEXT inside NOR. ASSERT_NOT_OK( @@ -107,7 +107,7 @@ TEST(ExpressionOptimizeTest, IsValidText) { " ]}," " {a: 2}" "]}", - *qr)); + *findCommand)); // Invalid: >1 TEXT. ASSERT_NOT_OK( @@ -115,7 +115,7 @@ TEST(ExpressionOptimizeTest, IsValidText) { " {$text: {$search: 's'}}," " {$text: {$search: 't'}}" "]}", - *qr)); + *findCommand)); // Invalid: >1 TEXT. ASSERT_NOT_OK( @@ -129,26 +129,26 @@ TEST(ExpressionOptimizeTest, IsValidText) { " {b: 1}" " ]}" "]}", - *qr)); + *findCommand)); } TEST(ExpressionOptimizeTest, IsValidTextTailable) { - // Filter inside QueryRequest is not used. - auto qr = std::make_unique<QueryRequest>(nss); - qr->setTailableMode(TailableModeEnum::kTailable); - ASSERT_OK(qr->validate()); + // Filter inside FindCommand is not used. + auto findCommand = std::make_unique<FindCommand>(nss); + query_request_helper::setTailableMode(TailableModeEnum::kTailable, findCommand.get()); + ASSERT_OK(query_request_helper::validateFindCommand(*findCommand)); // Invalid: TEXT and tailable. - ASSERT_NOT_OK(isValid("{$text: {$search: 's'}}", *qr)); + ASSERT_NOT_OK(isValid("{$text: {$search: 's'}}", *findCommand)); } TEST(ExpressionOptimizeTest, IsValidGeo) { - // Filter inside QueryRequest is not used. - auto qr = std::make_unique<QueryRequest>(nss); - ASSERT_OK(qr->validate()); + // Filter inside FindCommand is not used. + auto findCommand = std::make_unique<FindCommand>(nss); + ASSERT_OK(query_request_helper::validateFindCommand(*findCommand)); // Valid: regular GEO_NEAR. - ASSERT_OK(isValid("{a: {$near: [0, 0]}}", *qr)); + ASSERT_OK(isValid("{a: {$near: [0, 0]}}", *findCommand)); // Valid: GEO_NEAR inside nested AND. ASSERT_OK( @@ -159,7 +159,7 @@ TEST(ExpressionOptimizeTest, IsValidGeo) { " ]}," " {c: 1}" "]}", - *qr)); + *findCommand)); // Invalid: >1 GEO_NEAR. ASSERT_NOT_OK( @@ -167,7 +167,7 @@ TEST(ExpressionOptimizeTest, IsValidGeo) { " {a: {$near: [0, 0]}}," " {b: {$near: [0, 0]}}" "]}", - *qr)); + *findCommand)); // Invalid: >1 GEO_NEAR. ASSERT_NOT_OK( @@ -175,7 +175,7 @@ TEST(ExpressionOptimizeTest, IsValidGeo) { " {a: {$geoNear: [0, 0]}}," " {b: {$near: [0, 0]}}" "]}", - *qr)); + *findCommand)); // Invalid: >1 GEO_NEAR. ASSERT_NOT_OK( @@ -189,7 +189,7 @@ TEST(ExpressionOptimizeTest, IsValidGeo) { " {d: 1}" " ]}" "]}", - *qr)); + *findCommand)); // Invalid: GEO_NEAR inside NOR. ASSERT_NOT_OK( @@ -197,7 +197,7 @@ TEST(ExpressionOptimizeTest, IsValidGeo) { " {a: {$near: [0, 0]}}," " {b: 1}" "]}", - *qr)); + *findCommand)); // Invalid: GEO_NEAR inside OR. ASSERT_NOT_OK( @@ -205,19 +205,19 @@ TEST(ExpressionOptimizeTest, IsValidGeo) { " {a: {$near: [0, 0]}}," " {b: 1}" "]}", - *qr)); + *findCommand)); } TEST(ExpressionOptimizeTest, IsValidTextAndGeo) { - // Filter inside QueryRequest is not used. - auto qr = std::make_unique<QueryRequest>(nss); - ASSERT_OK(qr->validate()); + // Filter inside FindCommand is not used. + auto findCommand = std::make_unique<FindCommand>(nss); + ASSERT_OK(query_request_helper::validateFindCommand(*findCommand)); // Invalid: TEXT and GEO_NEAR. - ASSERT_NOT_OK(isValid("{$text: {$search: 's'}, a: {$near: [0, 0]}}", *qr)); + ASSERT_NOT_OK(isValid("{$text: {$search: 's'}, a: {$near: [0, 0]}}", *findCommand)); // Invalid: TEXT and GEO_NEAR. - ASSERT_NOT_OK(isValid("{$text: {$search: 's'}, a: {$geoNear: [0, 0]}}", *qr)); + ASSERT_NOT_OK(isValid("{$text: {$search: 's'}, a: {$geoNear: [0, 0]}}", *findCommand)); // Invalid: TEXT and GEO_NEAR. ASSERT_NOT_OK( @@ -226,89 +226,85 @@ TEST(ExpressionOptimizeTest, IsValidTextAndGeo) { " {a: 1}" " ]," " b: {$near: [0, 0]}}", - *qr)); + *findCommand)); } TEST(ExpressionOptimizeTest, IsValidTextAndNaturalAscending) { - // Filter inside QueryRequest is not used. - auto qr = std::make_unique<QueryRequest>(nss); - qr->setSort(fromjson("{$natural: 1}")); - ASSERT_OK(qr->validate()); + // Filter inside FindCommand is not used. + auto findCommand = std::make_unique<FindCommand>(nss); + findCommand->setSort(fromjson("{$natural: 1}")); + ASSERT_OK(query_request_helper::validateFindCommand(*findCommand)); // Invalid: TEXT and {$natural: 1} sort order. - ASSERT_NOT_OK(isValid("{$text: {$search: 's'}}", *qr)); + ASSERT_NOT_OK(isValid("{$text: {$search: 's'}}", *findCommand)); } TEST(ExpressionOptimizeTest, IsValidTextAndNaturalDescending) { - // Filter inside QueryRequest is not used. - auto qr = std::make_unique<QueryRequest>(nss); - qr->setSort(fromjson("{$natural: -1}")); - ASSERT_OK(qr->validate()); + // Filter inside FindCommand is not used. + auto findCommand = std::make_unique<FindCommand>(nss); + findCommand->setSort(fromjson("{$natural: -1}")); + ASSERT_OK(query_request_helper::validateFindCommand(*findCommand)); // Invalid: TEXT and {$natural: -1} sort order. - ASSERT_NOT_OK(isValid("{$text: {$search: 's'}}", *qr)); + ASSERT_NOT_OK(isValid("{$text: {$search: 's'}}", *findCommand)); } TEST(ExpressionOptimizeTest, IsValidTextAndHint) { - // Filter inside QueryRequest is not used. - auto qr = std::make_unique<QueryRequest>(nss); - qr->setHint(fromjson("{a: 1}")); - ASSERT_OK(qr->validate()); + // Filter inside FindCommand is not used. + auto findCommand = std::make_unique<FindCommand>(nss); + findCommand->setHint(fromjson("{a: 1}")); + ASSERT_OK(query_request_helper::validateFindCommand(*findCommand)); // Invalid: TEXT and {$natural: -1} sort order. - ASSERT_NOT_OK(isValid("{$text: {$search: 's'}}", *qr)); + ASSERT_NOT_OK(isValid("{$text: {$search: 's'}}", *findCommand)); } // SERVER-14366 TEST(ExpressionOptimizeTest, IsValidGeoNearNaturalSort) { - // Filter inside QueryRequest is not used. - auto qr = std::make_unique<QueryRequest>(nss); - qr->setSort(fromjson("{$natural: 1}")); - ASSERT_OK(qr->validate()); + // Filter inside FindCommand is not used. + auto findCommand = std::make_unique<FindCommand>(nss); + findCommand->setSort(fromjson("{$natural: 1}")); + ASSERT_OK(query_request_helper::validateFindCommand(*findCommand)); // Invalid: GEO_NEAR and {$natural: 1} sort order. - ASSERT_NOT_OK(isValid("{a: {$near: {$geometry: {type: 'Point', coordinates: [0, 0]}}}}", *qr)); + ASSERT_NOT_OK( + isValid("{a: {$near: {$geometry: {type: 'Point', coordinates: [0, 0]}}}}", *findCommand)); } // SERVER-14366 TEST(ExpressionOptimizeTest, IsValidGeoNearNaturalHint) { - // Filter inside QueryRequest is not used. - auto qr = std::make_unique<QueryRequest>(nss); - qr->setHint(fromjson("{$natural: 1}")); - ASSERT_OK(qr->validate()); + // Filter inside FindCommand is not used. + auto findCommand = std::make_unique<FindCommand>(nss); + findCommand->setHint(fromjson("{$natural: 1}")); + ASSERT_OK(query_request_helper::validateFindCommand(*findCommand)); // Invalid: GEO_NEAR and {$natural: 1} hint. - ASSERT_NOT_OK(isValid("{a: {$near: {$geometry: {type: 'Point', coordinates: [0, 0]}}}}", *qr)); + ASSERT_NOT_OK( + isValid("{a: {$near: {$geometry: {type: 'Point', coordinates: [0, 0]}}}}", *findCommand)); } TEST(ExpressionOptimizeTest, IsValidNaturalSortIndexHint) { - const bool isExplain = false; - auto qr = QueryRequest::makeFromFindCommandForTests( - fromjson("{find: 'testcoll', sort: {$natural: 1}, hint: {a: 1}, '$db': 'test'}"), - isExplain); + auto findCommand = query_request_helper::makeFromFindCommandForTests( + fromjson("{find: 'testcoll', sort: {$natural: 1}, hint: {a: 1}, '$db': 'test'}")); // Invalid: {$natural: 1} sort order and index hint. - ASSERT_NOT_OK(isValid("{}", *qr)); + ASSERT_NOT_OK(isValid("{}", *findCommand)); } TEST(ExpressionOptimizeTest, IsValidNaturalSortNaturalHint) { - const bool isExplain = false; - auto qr = QueryRequest::makeFromFindCommandForTests( - fromjson("{find: 'testcoll', sort: {$natural: 1}, hint: {$natural: 1}, '$db': 'test'}"), - isExplain); + auto findCommand = query_request_helper::makeFromFindCommandForTests( + fromjson("{find: 'testcoll', sort: {$natural: 1}, hint: {$natural: 1}, '$db': 'test'}")); // Valid: {$natural: 1} sort order and {$natural: 1} hint. - ASSERT_OK(isValid("{}", *qr)); + ASSERT_OK(isValid("{}", *findCommand)); } TEST(ExpressionOptimizeTest, IsValidNaturalSortNaturalHintDifferentDirections) { - const bool isExplain = false; - auto qr = QueryRequest::makeFromFindCommandForTests( - fromjson("{find: 'testcoll', sort: {$natural: 1}, hint: {$natural: -1}, '$db': 'test'}"), - isExplain); + auto findCommand = query_request_helper::makeFromFindCommandForTests( + fromjson("{find: 'testcoll', sort: {$natural: 1}, hint: {$natural: -1}, '$db': 'test'}")); // Invalid: {$natural: 1} sort order and {$natural: -1} hint. - ASSERT_NOT_OK(isValid("{}", *qr)); + ASSERT_NOT_OK(isValid("{}", *findCommand)); } TEST(ExpressionOptimizeTest, NormalizeWithInPreservesTags) { diff --git a/src/mongo/db/ops/parsed_delete.cpp b/src/mongo/db/ops/parsed_delete.cpp index e2bc0f0432f..0837f51addc 100644 --- a/src/mongo/db/ops/parsed_delete.cpp +++ b/src/mongo/db/ops/parsed_delete.cpp @@ -90,12 +90,11 @@ Status ParsedDelete::parseQueryToCQ() { // The projection needs to be applied after the delete operation, so we do not specify a // projection during canonicalization. - auto qr = std::make_unique<QueryRequest>(_request->getNsString()); - qr->setFilter(_request->getQuery()); - qr->setSort(_request->getSort()); - qr->setCollation(_request->getCollation()); - qr->setExplain(_request->getIsExplain()); - qr->setHint(_request->getHint()); + auto findCommand = std::make_unique<FindCommand>(_request->getNsString()); + findCommand->setFilter(_request->getQuery().getOwned()); + findCommand->setSort(_request->getSort().getOwned()); + findCommand->setCollation(_request->getCollation().getOwned()); + findCommand->setHint(_request->getHint()); // Limit should only used for the findAndModify command when a sort is specified. If a sort // is requested, we want to use a top-k sort for efficiency reasons, so should pass the @@ -104,19 +103,20 @@ Status ParsedDelete::parseQueryToCQ() { // has not actually deleted a document. This behavior is fine for findAndModify, but should // not apply to deletes in general. if (!_request->getMulti() && !_request->getSort().isEmpty()) { - qr->setLimit(1); + findCommand->setLimit(1); } // If the delete request has runtime constants or let parameters attached to it, pass them to - // the QueryRequest. + // the FindCommand. if (auto& runtimeConstants = _request->getLegacyRuntimeConstants()) - qr->setLegacyRuntimeConstants(*runtimeConstants); + findCommand->setLegacyRuntimeConstants(*runtimeConstants); if (auto& letParams = _request->getLet()) - qr->setLetParameters(*letParams); + findCommand->setLet(*letParams); auto statusWithCQ = CanonicalQuery::canonicalize(_opCtx, - std::move(qr), + std::move(findCommand), + _request->getIsExplain(), _expCtx, extensionsCallback, MatchExpressionParser::kAllowAllSpecialFeatures); diff --git a/src/mongo/db/ops/parsed_update.cpp b/src/mongo/db/ops/parsed_update.cpp index c21933d9876..fbdc4e0a43e 100644 --- a/src/mongo/db/ops/parsed_update.cpp +++ b/src/mongo/db/ops/parsed_update.cpp @@ -122,15 +122,14 @@ Status ParsedUpdate::parseQueryToCQ() { // The projection needs to be applied after the update operation, so we do not specify a // projection during canonicalization. - auto qr = std::make_unique<QueryRequest>(_request->getNamespaceString()); - qr->setFilter(_request->getQuery()); - qr->setSort(_request->getSort()); - qr->setExplain(static_cast<bool>(_request->explain())); - qr->setHint(_request->getHint()); + auto findCommand = std::make_unique<FindCommand>(_request->getNamespaceString()); + findCommand->setFilter(_request->getQuery()); + findCommand->setSort(_request->getSort()); + findCommand->setHint(_request->getHint()); // We get the collation off the ExpressionContext because it may contain a collection-default // collator if no collation was included in the user's request. - qr->setCollation(_expCtx->getCollatorBSON()); + findCommand->setCollation(_expCtx->getCollatorBSON()); // Limit should only used for the findAndModify command when a sort is specified. If a sort // is requested, we want to use a top-k sort for efficiency reasons, so should pass the @@ -139,7 +138,7 @@ Status ParsedUpdate::parseQueryToCQ() { // has not actually updated a document. This behavior is fine for findAndModify, but should // not apply to update in general. if (!_request->isMulti() && !_request->getSort().isEmpty()) { - qr->setLimit(1); + findCommand->setLimit(1); } // $expr is not allowed in the query for an upsert, since it is not clear what the equality @@ -151,16 +150,20 @@ Status ParsedUpdate::parseQueryToCQ() { } // If the update request has runtime constants or let parameters attached to it, pass them to - // the QueryRequest. + // the FindCommand. if (auto& runtimeConstants = _request->getLegacyRuntimeConstants()) { - qr->setLegacyRuntimeConstants(*runtimeConstants); + findCommand->setLegacyRuntimeConstants(*runtimeConstants); } if (auto& letParams = _request->getLetParameters()) { - qr->setLetParameters(*letParams); + findCommand->setLet(*letParams); } - auto statusWithCQ = CanonicalQuery::canonicalize( - _opCtx, std::move(qr), _expCtx, _extensionsCallback, allowedMatcherFeatures); + auto statusWithCQ = CanonicalQuery::canonicalize(_opCtx, + std::move(findCommand), + static_cast<bool>(_request->explain()), + _expCtx, + _extensionsCallback, + allowedMatcherFeatures); if (statusWithCQ.isOK()) { _canonicalQuery = std::move(statusWithCQ.getValue()); } diff --git a/src/mongo/db/pipeline/aggregation_request_helper.cpp b/src/mongo/db/pipeline/aggregation_request_helper.cpp index 6d575759482..a3b574566bd 100644 --- a/src/mongo/db/pipeline/aggregation_request_helper.cpp +++ b/src/mongo/db/pipeline/aggregation_request_helper.cpp @@ -37,7 +37,7 @@ #include "mongo/db/exec/document_value/document.h" #include "mongo/db/exec/document_value/value.h" #include "mongo/db/query/cursor_request.h" -#include "mongo/db/query/query_request.h" +#include "mongo/db/query/query_request_helper.h" #include "mongo/db/repl/read_concern_args.h" #include "mongo/db/storage/storage_options.h" #include "mongo/idl/command_generic_argument.h" diff --git a/src/mongo/db/pipeline/aggregation_request_test.cpp b/src/mongo/db/pipeline/aggregation_request_test.cpp index 8dac4105fb3..3adb9219d20 100644 --- a/src/mongo/db/pipeline/aggregation_request_test.cpp +++ b/src/mongo/db/pipeline/aggregation_request_test.cpp @@ -39,7 +39,7 @@ #include "mongo/db/exec/document_value/document_value_test_util.h" #include "mongo/db/exec/document_value/value.h" #include "mongo/db/namespace_string.h" -#include "mongo/db/query/query_request.h" +#include "mongo/db/query/query_request_helper.h" #include "mongo/db/repl/read_concern_args.h" #include "mongo/unittest/unittest.h" #include "mongo/util/assert_util.h" @@ -221,7 +221,7 @@ TEST(AggregationRequestTest, ShouldSerializeOptionalValuesIfSet) { {AggregateCommand::kPipelineFieldName, std::vector<Value>{}}, {AggregateCommand::kAllowDiskUseFieldName, true}, {AggregateCommand::kCursorFieldName, Value(Document({{kBatchSizeFieldName, 10}}))}, - {QueryRequest::cmdOptionMaxTimeMS, 10}, + {query_request_helper::cmdOptionMaxTimeMS, 10}, {AggregateCommand::kBypassDocumentValidationFieldName, true}, {repl::ReadConcernArgs::kReadConcernFieldName, readConcernObj}, {AggregateCommand::kCollationFieldName, collationObj}, @@ -229,7 +229,7 @@ TEST(AggregationRequestTest, ShouldSerializeOptionalValuesIfSet) { {AggregateCommand::kLetFieldName, letParamsObj}, {AggregateCommand::kNeedsMergeFieldName, true}, {AggregateCommand::kFromMongosFieldName, true}, - {QueryRequest::kUnwrappedReadPrefField, readPrefObj}, + {query_request_helper::kUnwrappedReadPrefField, readPrefObj}, {AggregateCommand::kRequestReshardingResumeTokenFieldName, true}, {AggregateCommand::kIsMapReduceCommandFieldName, true}, {AggregateCommand::kCollectionUUIDFieldName, uuid}}; diff --git a/src/mongo/db/pipeline/document_source_merge_cursors_test.cpp b/src/mongo/db/pipeline/document_source_merge_cursors_test.cpp index 2c06c7c3497..e2ea85834e0 100644 --- a/src/mongo/db/pipeline/document_source_merge_cursors_test.cpp +++ b/src/mongo/db/pipeline/document_source_merge_cursors_test.cpp @@ -45,7 +45,7 @@ #include "mongo/db/query/cursor_response.h" #include "mongo/db/query/getmore_request.h" #include "mongo/db/query/query_knobs_gen.h" -#include "mongo/db/query/query_request.h" +#include "mongo/db/query/query_request_helper.h" #include "mongo/executor/network_interface_mock.h" #include "mongo/executor/task_executor.h" #include "mongo/executor/thread_pool_task_executor_test_fixture.h" diff --git a/src/mongo/db/pipeline/pipeline_d.cpp b/src/mongo/db/pipeline/pipeline_d.cpp index 059217598a8..bf2923130ed 100644 --- a/src/mongo/db/pipeline/pipeline_d.cpp +++ b/src/mongo/db/pipeline/pipeline_d.cpp @@ -203,32 +203,34 @@ StatusWith<std::unique_ptr<PlanExecutor, PlanExecutor::Deleter>> attemptToGetExe const AggregateCommand* aggRequest, const size_t plannerOpts, const MatchExpressionParser::AllowedFeatureSet& matcherFeatures) { - auto qr = std::make_unique<QueryRequest>(nss); - qr->setTailableMode(expCtx->tailableMode); - qr->setFilter(queryObj); - qr->setProj(projectionObj); - qr->setSort(sortObj); + auto findCommand = std::make_unique<FindCommand>(nss); + query_request_helper::setTailableMode(expCtx->tailableMode, findCommand.get()); + findCommand->setFilter(queryObj.getOwned()); + findCommand->setProjection(projectionObj.getOwned()); + findCommand->setSort(sortObj.getOwned()); if (auto skip = skipThenLimit.getSkip()) { - qr->setSkip(static_cast<std::int64_t>(*skip)); + findCommand->setSkip(static_cast<std::int64_t>(*skip)); } if (auto limit = skipThenLimit.getLimit()) { - qr->setLimit(static_cast<std::int64_t>(*limit)); + findCommand->setLimit(static_cast<std::int64_t>(*limit)); } + bool isExplain = false; if (aggRequest) { - qr->setExplain(static_cast<bool>(aggRequest->getExplain())); - qr->setHint(aggRequest->getHint().value_or(BSONObj())); + findCommand->setHint(aggRequest->getHint().value_or(BSONObj()).getOwned()); + isExplain = static_cast<bool>(aggRequest->getExplain()); } // The collation on the ExpressionContext has been resolved to either the user-specified // collation or the collection default. This BSON should never be empty even if the resolved // collator is simple. - qr->setCollation(expCtx->getCollatorBSON()); + findCommand->setCollation(expCtx->getCollatorBSON().getOwned()); const ExtensionsCallbackReal extensionsCallback(expCtx->opCtx, &nss); auto cq = CanonicalQuery::canonicalize(expCtx->opCtx, - std::move(qr), + std::move(findCommand), + isExplain, expCtx, extensionsCallback, matcherFeatures, diff --git a/src/mongo/db/pipeline/process_interface/replica_set_node_process_interface.cpp b/src/mongo/db/pipeline/process_interface/replica_set_node_process_interface.cpp index 6d48014b8ed..b07e0b3caec 100644 --- a/src/mongo/db/pipeline/process_interface/replica_set_node_process_interface.cpp +++ b/src/mongo/db/pipeline/process_interface/replica_set_node_process_interface.cpp @@ -233,7 +233,8 @@ void ReplicaSetNodeProcessInterface::_attachGenericCommandArgs(OperationContext* auto maxTimeMS = opCtx->getRemainingMaxTimeMillis(); if (maxTimeMS != Milliseconds::max()) { - cmd->append(QueryRequest::cmdOptionMaxTimeMS, durationCount<Milliseconds>(maxTimeMS)); + cmd->append(query_request_helper::cmdOptionMaxTimeMS, + durationCount<Milliseconds>(maxTimeMS)); } logical_session_id_helpers::serializeLsidAndTxnNumber(opCtx, cmd); diff --git a/src/mongo/db/pipeline/sharded_agg_helpers.cpp b/src/mongo/db/pipeline/sharded_agg_helpers.cpp index ec72dceff96..3bec7700e30 100644 --- a/src/mongo/db/pipeline/sharded_agg_helpers.cpp +++ b/src/mongo/db/pipeline/sharded_agg_helpers.cpp @@ -79,8 +79,8 @@ Document wrapAggAsExplain(Document aggregateCommand, ExplainOptions::Verbosity v MutableDocument explainCommandBuilder; explainCommandBuilder["explain"] = Value(aggregateCommand); // Downstream host targeting code expects queryOptions at the top level of the command object. - explainCommandBuilder[QueryRequest::kUnwrappedReadPrefField] = - Value(aggregateCommand[QueryRequest::kUnwrappedReadPrefField]); + explainCommandBuilder[query_request_helper::kUnwrappedReadPrefField] = + Value(aggregateCommand[query_request_helper::kUnwrappedReadPrefField]); // readConcern needs to be promoted to the top-level of the request. explainCommandBuilder[repl::ReadConcernArgs::kReadConcernFieldName] = diff --git a/src/mongo/db/query/README.md b/src/mongo/db/query/README.md index ede094cc3d4..ffb98c9fe7a 100644 --- a/src/mongo/db/query/README.md +++ b/src/mongo/db/query/README.md @@ -215,9 +215,9 @@ Once we have parsed the command and checked authorization, we move on to parsing parts of the query. Once again, we will focus on the find and aggregate commands. ### Find command parsing -The find command is parsed entirely by the IDL. Initially the IDL parser creates a QueryRequest. As +The find command is parsed entirely by the IDL. Initially the IDL parser creates a FindCommand. As mentioned above, the IDL parser does all of the required type checking and stores all options for -the query. The QueryRequest is then turned into a CanonicalQuery. The CanonicalQuery +the query. The FindCommand is then turned into a CanonicalQuery. The CanonicalQuery parses the collation and the filter while just holding the rest of the IDL parsed fields. The parsing of the collation is straightforward: for each field that is allowed to be in the object, we check for that field and then build the collation from the parsed fields. @@ -270,7 +270,7 @@ give a summary of how each is parsed, but not get into the same level of detail. * count : Parsed by IDL and then turned into a CountStage which can be executed in a similar way to a find command. * distinct : The distinct specific arguments are parsed by IDL, and the generic command arguments - are parsed by custom code. They are then combined into a QueryRequest (mentioned above), + are parsed by custom code. They are then combined into a FindCommand (mentioned above), canonicalized, packaged into a ParsedDistinct, which is eventually turned into an executable stage. * mapReduce : Parsed by IDL and then turned into an equivalent aggregation command. diff --git a/src/mongo/db/query/SConscript b/src/mongo/db/query/SConscript index ed2627eab26..6672d91e65d 100644 --- a/src/mongo/db/query/SConscript +++ b/src/mongo/db/query/SConscript @@ -177,7 +177,7 @@ env.Library( source=[ "distinct_command.idl", "find_command.idl", - "query_request.cpp", + "query_request_helper.cpp", "max_time_ms_parser.cpp", "tailable_mode.cpp", "tailable_mode.idl", diff --git a/src/mongo/db/query/canonical_query.cpp b/src/mongo/db/query/canonical_query.cpp index 26824999f04..2b24dac9f9a 100644 --- a/src/mongo/db/query/canonical_query.cpp +++ b/src/mongo/db/query/canonical_query.cpp @@ -65,33 +65,35 @@ StatusWith<std::unique_ptr<CanonicalQuery>> CanonicalQuery::canonicalize( const boost::intrusive_ptr<ExpressionContext>& expCtx, const ExtensionsCallback& extensionsCallback, MatchExpressionParser::AllowedFeatureSet allowedFeatures) { - // Make QueryRequest. - auto qrStatus = QueryRequest::fromLegacyQueryMessage(qm); - if (!qrStatus.isOK()) { - return qrStatus.getStatus(); + bool explain = false; + // Make FindCommand. + auto status = query_request_helper::fromLegacyQueryMessage(qm, &explain); + if (!status.isOK()) { + return status.getStatus(); } return CanonicalQuery::canonicalize( - opCtx, std::move(qrStatus.getValue()), expCtx, extensionsCallback, allowedFeatures); + opCtx, std::move(status.getValue()), explain, expCtx, extensionsCallback, allowedFeatures); } // static StatusWith<std::unique_ptr<CanonicalQuery>> CanonicalQuery::canonicalize( OperationContext* opCtx, - std::unique_ptr<QueryRequest> qr, + std::unique_ptr<FindCommand> findCommand, + bool explain, const boost::intrusive_ptr<ExpressionContext>& expCtx, const ExtensionsCallback& extensionsCallback, MatchExpressionParser::AllowedFeatureSet allowedFeatures, const ProjectionPolicies& projectionPolicies) { - auto qrStatus = qr->validate(); - if (!qrStatus.isOK()) { - return qrStatus; + auto status = query_request_helper::validateFindCommand(*findCommand); + if (!status.isOK()) { + return status; } std::unique_ptr<CollatorInterface> collator; - if (!qr->getCollation().isEmpty()) { + if (!findCommand->getCollation().isEmpty()) { auto statusWithCollator = CollatorFactoryInterface::get(opCtx->getServiceContext()) - ->makeFromBSON(qr->getCollation()); + ->makeFromBSON(findCommand->getCollation()); if (!statusWithCollator.isOK()) { return statusWithCollator.getStatus(); } @@ -101,14 +103,15 @@ StatusWith<std::unique_ptr<CanonicalQuery>> CanonicalQuery::canonicalize( // Make MatchExpression. boost::intrusive_ptr<ExpressionContext> newExpCtx; if (!expCtx.get()) { + invariant(findCommand->getNamespaceOrUUID().nss()); newExpCtx = make_intrusive<ExpressionContext>(opCtx, std::move(collator), - qr->nss(), - qr->getLegacyRuntimeConstants(), - qr->getLetParameters()); + *findCommand->getNamespaceOrUUID().nss(), + findCommand->getLegacyRuntimeConstants(), + findCommand->getLet()); } else { newExpCtx = expCtx; - // A collator can enter through both the QueryRequest and ExpressionContext arguments. + // A collator can enter through both the FindCommand and ExpressionContext arguments. // This invariant ensures that both collators are the same because downstream we // pull the collator from only one of the ExpressionContext carrier. if (collator.get() && expCtx->getCollator()) { @@ -118,17 +121,19 @@ StatusWith<std::unique_ptr<CanonicalQuery>> CanonicalQuery::canonicalize( // Make the CQ we'll hopefully return. std::unique_ptr<CanonicalQuery> cq(new CanonicalQuery()); + cq->setExplain(explain); StatusWithMatchExpression statusWithMatcher = [&]() -> StatusWithMatchExpression { if (getTestCommandsEnabled() && internalQueryEnableCSTParser.load()) { try { - return cst::parseToMatchExpression(qr->getFilter(), newExpCtx, extensionsCallback); + return cst::parseToMatchExpression( + findCommand->getFilter(), newExpCtx, extensionsCallback); } catch (const DBException& ex) { return ex.toStatus(); } } else { return MatchExpressionParser::parse( - qr->getFilter(), newExpCtx, extensionsCallback, allowedFeatures); + findCommand->getFilter(), newExpCtx, extensionsCallback, allowedFeatures); } }(); if (!statusWithMatcher.isOK()) { @@ -139,7 +144,7 @@ StatusWith<std::unique_ptr<CanonicalQuery>> CanonicalQuery::canonicalize( Status initStatus = cq->init(opCtx, std::move(newExpCtx), - std::move(qr), + std::move(findCommand), parsingCanProduceNoopMatchNodes(extensionsCallback, allowedFeatures), std::move(me), projectionPolicies); @@ -153,24 +158,24 @@ StatusWith<std::unique_ptr<CanonicalQuery>> CanonicalQuery::canonicalize( // static StatusWith<std::unique_ptr<CanonicalQuery>> CanonicalQuery::canonicalize( OperationContext* opCtx, const CanonicalQuery& baseQuery, MatchExpression* root) { - auto qr = std::make_unique<QueryRequest>(baseQuery.nss()); + auto findCommand = std::make_unique<FindCommand>(baseQuery.nss()); BSONObjBuilder builder; root->serialize(&builder, true); - qr->setFilter(builder.obj()); - qr->setProj(baseQuery.getQueryRequest().getProj()); - qr->setSort(baseQuery.getQueryRequest().getSort()); - qr->setCollation(baseQuery.getQueryRequest().getCollation()); - qr->setExplain(baseQuery.getQueryRequest().isExplain()); - auto qrStatus = qr->validate(); - if (!qrStatus.isOK()) { - return qrStatus; + findCommand->setFilter(builder.obj()); + findCommand->setProjection(baseQuery.getFindCommand().getProjection().getOwned()); + findCommand->setSort(baseQuery.getFindCommand().getSort().getOwned()); + findCommand->setCollation(baseQuery.getFindCommand().getCollation().getOwned()); + auto status = query_request_helper::validateFindCommand(*findCommand); + if (!status.isOK()) { + return status; } // Make the CQ we'll hopefully return. std::unique_ptr<CanonicalQuery> cq(new CanonicalQuery()); + cq->setExplain(baseQuery.getExplain()); Status initStatus = cq->init(opCtx, baseQuery.getExpCtx(), - std::move(qr), + std::move(findCommand), baseQuery.canHaveNoopMatchNodes(), root->shallowClone(), ProjectionPolicies::findProjectionPolicies()); @@ -183,28 +188,31 @@ StatusWith<std::unique_ptr<CanonicalQuery>> CanonicalQuery::canonicalize( Status CanonicalQuery::init(OperationContext* opCtx, boost::intrusive_ptr<ExpressionContext> expCtx, - std::unique_ptr<QueryRequest> qr, + std::unique_ptr<FindCommand> findCommand, bool canHaveNoopMatchNodes, std::unique_ptr<MatchExpression> root, const ProjectionPolicies& projectionPolicies) { _expCtx = expCtx; - _qr = std::move(qr); + _findCommand = std::move(findCommand); _canHaveNoopMatchNodes = canHaveNoopMatchNodes; // Normalize and validate tree. _root = MatchExpression::normalize(std::move(root)); - auto validStatus = isValid(_root.get(), *_qr); + auto validStatus = isValid(_root.get(), *_findCommand); if (!validStatus.isOK()) { return validStatus.getStatus(); } auto unavailableMetadata = validStatus.getValue(); // Validate the projection if there is one. - if (!_qr->getProj().isEmpty()) { + if (!_findCommand->getProjection().isEmpty()) { try { - _proj.emplace(projection_ast::parse( - expCtx, _qr->getProj(), _root.get(), _qr->getFilter(), projectionPolicies)); + _proj.emplace(projection_ast::parse(expCtx, + _findCommand->getProjection(), + _root.get(), + _findCommand->getFilter(), + projectionPolicies)); // Fail if any of the projection's dependencies are unavailable. DepsTracker{unavailableMetadata}.requestMetadata(_proj->metadataDeps()); @@ -216,7 +224,7 @@ Status CanonicalQuery::init(OperationContext* opCtx, } if (_proj && _proj->metadataDeps()[DocumentMetadataFields::kSortKey] && - _qr->getSort().isEmpty()) { + _findCommand->getSort().isEmpty()) { return Status(ErrorCodes::BadValue, "cannot use sortKey $meta projection without a sort"); } @@ -228,7 +236,7 @@ Status CanonicalQuery::init(OperationContext* opCtx, } // If the 'returnKey' option is set, then the plan should produce index key metadata. - if (_qr->returnKey()) { + if (_findCommand->getReturnKey()) { _metadataDeps.set(DocumentMetadataFields::kIndexKey); } @@ -236,7 +244,7 @@ Status CanonicalQuery::init(OperationContext* opCtx, } void CanonicalQuery::initSortPattern(QueryMetadataBitSet unavailableMetadata) { - if (_qr->getSort().isEmpty()) { + if (_findCommand->getSort().isEmpty()) { return; } @@ -246,15 +254,15 @@ void CanonicalQuery::initSortPattern(QueryMetadataBitSet unavailableMetadata) { // We have already validated that if there is a $natural sort and a hint, that the hint // also specifies $natural with the same direction. Therefore, it is safe to clear the $natural // sort and rewrite it as a $natural hint. - if (_qr->getSort()[QueryRequest::kNaturalSortField]) { - _qr->setHint(_qr->getSort()); - _qr->setSort(BSONObj{}); + if (_findCommand->getSort()[query_request_helper::kNaturalSortField]) { + _findCommand->setHint(_findCommand->getSort().getOwned()); + _findCommand->setSort(BSONObj{}); } if (getTestCommandsEnabled() && internalQueryEnableCSTParser.load()) { - _sortPattern = cst::parseToSortPattern(_qr->getSort(), _expCtx); + _sortPattern = cst::parseToSortPattern(_findCommand->getSort(), _expCtx); } else { - _sortPattern = SortPattern{_qr->getSort(), _expCtx}; + _sortPattern = SortPattern{_findCommand->getSort(), _expCtx}; } _metadataDeps |= _sortPattern->metadataDeps(unavailableMetadata); @@ -334,7 +342,7 @@ bool hasNodeInSubtree(MatchExpression* root, } StatusWith<QueryMetadataBitSet> CanonicalQuery::isValid(MatchExpression* root, - const QueryRequest& request) { + const FindCommand& findCommand) { QueryMetadataBitSet unavailableMetadata{}; // There can only be one TEXT. If there is a TEXT, it cannot appear inside a NOR. @@ -378,9 +386,9 @@ StatusWith<QueryMetadataBitSet> CanonicalQuery::isValid(MatchExpression* root, unavailableMetadata |= DepsTracker::kAllGeoNearData; } - const BSONObj& sortObj = request.getSort(); + const BSONObj& sortObj = findCommand.getSort(); BSONElement sortNaturalElt = sortObj["$natural"]; - const BSONObj& hintObj = request.getHint(); + const BSONObj& hintObj = findCommand.getHint(); BSONElement hintNaturalElt = hintObj["$natural"]; if (sortNaturalElt && sortObj.nFields() != 1) { @@ -422,12 +430,12 @@ StatusWith<QueryMetadataBitSet> CanonicalQuery::isValid(MatchExpression* root, } // TEXT and tailable are incompatible. - if (numText > 0 && request.isTailable()) { + if (numText > 0 && findCommand.getTailable()) { return Status(ErrorCodes::BadValue, "text and tailable cursor not allowed in same query"); } // NEAR and tailable are incompatible. - if (numGeoNear > 0 && request.isTailable()) { + if (numGeoNear > 0 && findCommand.getTailable()) { return Status(ErrorCodes::BadValue, "Tailable cursors and geo $near cannot be used together"); } @@ -448,59 +456,78 @@ StatusWith<QueryMetadataBitSet> CanonicalQuery::isValid(MatchExpression* root, return unavailableMetadata; } +int CanonicalQuery::getOptions() const { + int options = 0; + if (_findCommand->getTailable()) { + options |= QueryOption_CursorTailable; + } + if (_findCommand->getAwaitData()) { + options |= QueryOption_AwaitData; + } + if (_findCommand->getNoCursorTimeout()) { + options |= QueryOption_NoCursorTimeout; + } + if (_findCommand->getAllowPartialResults()) { + options |= QueryOption_PartialResults; + } + return options; +} + std::string CanonicalQuery::toString() const { str::stream ss; - ss << "ns=" << _qr->nss().ns(); + ss << "ns=" << _findCommand->getNamespaceOrUUID().nss().value_or(NamespaceString()).ns(); - if (_qr->getBatchSize()) { - ss << " batchSize=" << *_qr->getBatchSize(); + if (_findCommand->getBatchSize()) { + ss << " batchSize=" << *_findCommand->getBatchSize(); } - if (_qr->getLimit()) { - ss << " limit=" << *_qr->getLimit(); + if (_findCommand->getLimit()) { + ss << " limit=" << *_findCommand->getLimit(); } - if (_qr->getSkip()) { - ss << " skip=" << *_qr->getSkip(); + if (_findCommand->getSkip()) { + ss << " skip=" << *_findCommand->getSkip(); } - if (_qr->getNToReturn()) { - ss << " ntoreturn=" << *_qr->getNToReturn() << '\n'; + if (_findCommand->getNtoreturn()) { + ss << " ntoreturn=" << *_findCommand->getNtoreturn() << '\n'; } // The expression tree puts an endl on for us. ss << "Tree: " << _root->debugString(); - ss << "Sort: " << _qr->getSort().toString() << '\n'; - ss << "Proj: " << _qr->getProj().toString() << '\n'; - if (!_qr->getCollation().isEmpty()) { - ss << "Collation: " << _qr->getCollation().toString() << '\n'; + ss << "Sort: " << _findCommand->getSort().toString() << '\n'; + ss << "Proj: " << _findCommand->getProjection().toString() << '\n'; + if (!_findCommand->getCollation().isEmpty()) { + ss << "Collation: " << _findCommand->getCollation().toString() << '\n'; } return ss; } std::string CanonicalQuery::toStringShort() const { str::stream ss; - ss << "ns: " << _qr->nss().ns() << " query: " << _qr->getFilter().toString() - << " sort: " << _qr->getSort().toString() << " projection: " << _qr->getProj().toString(); + ss << "ns: " << _findCommand->getNamespaceOrUUID().nss().value_or(NamespaceString()).ns() + << " query: " << _findCommand->getFilter().toString() + << " sort: " << _findCommand->getSort().toString() + << " projection: " << _findCommand->getProjection().toString(); - if (!_qr->getCollation().isEmpty()) { - ss << " collation: " << _qr->getCollation().toString(); + if (!_findCommand->getCollation().isEmpty()) { + ss << " collation: " << _findCommand->getCollation().toString(); } - if (_qr->getBatchSize()) { - ss << " batchSize: " << *_qr->getBatchSize(); + if (_findCommand->getBatchSize()) { + ss << " batchSize: " << *_findCommand->getBatchSize(); } - if (_qr->getLimit()) { - ss << " limit: " << *_qr->getLimit(); + if (_findCommand->getLimit()) { + ss << " limit: " << *_findCommand->getLimit(); } - if (_qr->getSkip()) { - ss << " skip: " << *_qr->getSkip(); + if (_findCommand->getSkip()) { + ss << " skip: " << *_findCommand->getSkip(); } - if (_qr->getNToReturn()) { - ss << " ntoreturn=" << *_qr->getNToReturn(); + if (_findCommand->getNtoreturn()) { + ss << " ntoreturn=" << *_findCommand->getNtoreturn(); } return ss; diff --git a/src/mongo/db/query/canonical_query.h b/src/mongo/db/query/canonical_query.h index ddcd5c731d7..4b148557710 100644 --- a/src/mongo/db/query/canonical_query.h +++ b/src/mongo/db/query/canonical_query.h @@ -38,7 +38,7 @@ #include "mongo/db/query/collation/collator_interface.h" #include "mongo/db/query/projection.h" #include "mongo/db/query/projection_policies.h" -#include "mongo/db/query/query_request.h" +#include "mongo/db/query/query_request_helper.h" #include "mongo/db/query/sort_pattern.h" namespace mongo { @@ -77,7 +77,8 @@ public: */ static StatusWith<std::unique_ptr<CanonicalQuery>> canonicalize( OperationContext* opCtx, - std::unique_ptr<QueryRequest> qr, + std::unique_ptr<FindCommand> findCommand, + bool explain = false, const boost::intrusive_ptr<ExpressionContext>& expCtx = nullptr, const ExtensionsCallback& extensionsCallback = ExtensionsCallbackNoop(), MatchExpressionParser::AllowedFeatureSet allowedFeatures = @@ -117,13 +118,14 @@ public: * error. */ static StatusWith<QueryMetadataBitSet> isValid(MatchExpression* root, - const QueryRequest& request); + const FindCommand& findCommand); - const NamespaceString& nss() const { - return _qr->nss(); + const NamespaceString nss() const { + invariant(_findCommand->getNamespaceOrUUID().nss()); + return *_findCommand->getNamespaceOrUUID().nss(); } - const std::string& ns() const { - return _qr->nss().ns(); + const std::string ns() const { + return nss().ns(); } // @@ -133,10 +135,10 @@ public: return _root.get(); } const BSONObj& getQueryObj() const { - return _qr->getFilter(); + return _findCommand->getFilter(); } - const QueryRequest& getQueryRequest() const { - return *_qr; + const FindCommand& getFindCommand() const { + return *_findCommand; } /** @@ -209,6 +211,19 @@ public: return _canHaveNoopMatchNodes; } + /** + * Return options as a bit vector. + */ + int getOptions() const; + + bool getExplain() const { + return _explain; + } + + void setExplain(bool explain) { + _explain = explain; + } + auto& getExpCtx() const { return _expCtx; } @@ -222,7 +237,7 @@ private: Status init(OperationContext* opCtx, boost::intrusive_ptr<ExpressionContext> expCtx, - std::unique_ptr<QueryRequest> qr, + std::unique_ptr<FindCommand> findCommand, bool canHaveNoopMatchNodes, std::unique_ptr<MatchExpression> root, const ProjectionPolicies& projectionPolicies); @@ -235,7 +250,7 @@ private: boost::intrusive_ptr<ExpressionContext> _expCtx; - std::unique_ptr<QueryRequest> _qr; + std::unique_ptr<FindCommand> _findCommand; std::unique_ptr<MatchExpression> _root; @@ -247,6 +262,8 @@ private: QueryMetadataBitSet _metadataDeps; bool _canHaveNoopMatchNodes = false; + + bool _explain = false; }; } // namespace mongo diff --git a/src/mongo/db/query/canonical_query_encoder.cpp b/src/mongo/db/query/canonical_query_encoder.cpp index e8ef4308158..41e7af395f8 100644 --- a/src/mongo/db/query/canonical_query_encoder.cpp +++ b/src/mongo/db/query/canonical_query_encoder.cpp @@ -480,7 +480,7 @@ void encodeKeyForMatch(const MatchExpression* tree, StringBuilder* keyBuilder) { /** * Encodes sort order into cache key. * Sort order is normalized because it provided by - * QueryRequest. + * FindCommand. */ void encodeKeyForSort(const BSONObj& sortObj, StringBuilder* keyBuilder) { if (sortObj.isEmpty()) { @@ -493,7 +493,7 @@ void encodeKeyForSort(const BSONObj& sortObj, StringBuilder* keyBuilder) { while (it.more()) { BSONElement elt = it.next(); // $meta text score - if (QueryRequest::isTextScoreMeta(elt)) { + if (query_request_helper::isTextScoreMeta(elt)) { *keyBuilder << "t"; } // Ascending @@ -567,7 +567,7 @@ namespace canonical_query_encoder { CanonicalQuery::QueryShapeString encode(const CanonicalQuery& cq) { StringBuilder keyBuilder; encodeKeyForMatch(cq.root(), &keyBuilder); - encodeKeyForSort(cq.getQueryRequest().getSort(), &keyBuilder); + encodeKeyForSort(cq.getFindCommand().getSort(), &keyBuilder); encodeKeyForProj(cq.getProj(), &keyBuilder); encodeCollation(cq.getCollator(), &keyBuilder); diff --git a/src/mongo/db/query/canonical_query_encoder_test.cpp b/src/mongo/db/query/canonical_query_encoder_test.cpp index f10c0c17e64..f54f7b7398b 100644 --- a/src/mongo/db/query/canonical_query_encoder_test.cpp +++ b/src/mongo/db/query/canonical_query_encoder_test.cpp @@ -55,15 +55,16 @@ unique_ptr<CanonicalQuery> canonicalize(BSONObj query, QueryTestServiceContext serviceContext; auto opCtx = serviceContext.makeOperationContext(); - auto qr = std::make_unique<QueryRequest>(nss); - qr->setFilter(query); - qr->setSort(sort); - qr->setProj(proj); - qr->setCollation(collation); + auto findCommand = std::make_unique<FindCommand>(nss); + findCommand->setFilter(query.getOwned()); + findCommand->setSort(sort.getOwned()); + findCommand->setProjection(proj.getOwned()); + findCommand->setCollation(collation.getOwned()); const boost::intrusive_ptr<ExpressionContext> expCtx; auto statusWithCQ = CanonicalQuery::canonicalize(opCtx.get(), - std::move(qr), + std::move(findCommand), + false, expCtx, ExtensionsCallbackNoop(), MatchExpressionParser::kAllowAllSpecialFeatures); diff --git a/src/mongo/db/query/canonical_query_test.cpp b/src/mongo/db/query/canonical_query_test.cpp index b40f31d6f60..2f0f2e02d4a 100644 --- a/src/mongo/db/query/canonical_query_test.cpp +++ b/src/mongo/db/query/canonical_query_test.cpp @@ -98,22 +98,18 @@ TEST(CanonicalQueryTest, IsValidSortKeyMetaProjection) { // Passing a sortKey meta-projection without a sort is an error. { - const bool isExplain = false; - auto qr = QueryRequest::makeFromFindCommandForTests( - fromjson("{find: 'testcoll', projection: {foo: {$meta: 'sortKey'}}, '$db': 'test'}"), - isExplain); - auto cq = CanonicalQuery::canonicalize(opCtx.get(), std::move(qr)); + auto findCommand = query_request_helper::makeFromFindCommandForTests( + fromjson("{find: 'testcoll', projection: {foo: {$meta: 'sortKey'}}, '$db': 'test'}")); + auto cq = CanonicalQuery::canonicalize(opCtx.get(), std::move(findCommand)); ASSERT_NOT_OK(cq.getStatus()); } // Should be able to successfully create a CQ when there is a sort. { - const bool isExplain = false; - auto qr = QueryRequest::makeFromFindCommandForTests( + auto findCommand = query_request_helper::makeFromFindCommandForTests( fromjson("{find: 'testcoll', projection: {foo: {$meta: 'sortKey'}}, sort: {bar: 1}, " - "'$db': 'test'}"), - isExplain); - auto cq = CanonicalQuery::canonicalize(opCtx.get(), std::move(qr)); + "'$db': 'test'}")); + auto cq = CanonicalQuery::canonicalize(opCtx.get(), std::move(findCommand)); ASSERT_OK(cq.getStatus()); } } @@ -183,11 +179,15 @@ unique_ptr<CanonicalQuery> canonicalize(const char* queryStr, QueryTestServiceContext serviceContext; auto opCtx = serviceContext.makeOperationContext(); - auto qr = std::make_unique<QueryRequest>(nss); - qr->setFilter(fromjson(queryStr)); + auto findCommand = std::make_unique<FindCommand>(nss); + findCommand->setFilter(fromjson(queryStr)); - auto statusWithCQ = CanonicalQuery::canonicalize( - opCtx.get(), std::move(qr), nullptr, ExtensionsCallbackNoop(), allowedFeatures); + auto statusWithCQ = CanonicalQuery::canonicalize(opCtx.get(), + std::move(findCommand), + false, + nullptr, + ExtensionsCallbackNoop(), + allowedFeatures); ASSERT_OK(statusWithCQ.getStatus()); return std::move(statusWithCQ.getValue()); @@ -199,11 +199,11 @@ std::unique_ptr<CanonicalQuery> canonicalize(const char* queryStr, QueryTestServiceContext serviceContext; auto opCtx = serviceContext.makeOperationContext(); - auto qr = std::make_unique<QueryRequest>(nss); - qr->setFilter(fromjson(queryStr)); - qr->setSort(fromjson(sortStr)); - qr->setProj(fromjson(projStr)); - auto statusWithCQ = CanonicalQuery::canonicalize(opCtx.get(), std::move(qr)); + auto findCommand = std::make_unique<FindCommand>(nss); + findCommand->setFilter(fromjson(queryStr)); + findCommand->setSort(fromjson(sortStr)); + findCommand->setProjection(fromjson(projStr)); + auto statusWithCQ = CanonicalQuery::canonicalize(opCtx.get(), std::move(findCommand)); ASSERT_OK(statusWithCQ.getStatus()); return std::move(statusWithCQ.getValue()); } @@ -273,27 +273,29 @@ TEST(CanonicalQueryTest, CanonicalizeFromBaseQuery) { const std::string cmdStr = "{find:'bogusns', filter:{$or:[{a:1,b:1},{a:1,c:1}]}, projection:{a:1}, sort:{b:1}, '$db': " "'test'}"; - auto qr = QueryRequest::makeFromFindCommandForTests(fromjson(cmdStr), isExplain); - auto baseCq = assertGet(CanonicalQuery::canonicalize(opCtx.get(), std::move(qr))); + auto findCommand = query_request_helper::makeFromFindCommandForTests(fromjson(cmdStr)); + auto baseCq = + assertGet(CanonicalQuery::canonicalize(opCtx.get(), std::move(findCommand), isExplain)); MatchExpression* firstClauseExpr = baseCq->root()->getChild(0); auto childCq = assertGet(CanonicalQuery::canonicalize(opCtx.get(), *baseCq, firstClauseExpr)); BSONObjBuilder expectedFilter; firstClauseExpr->serialize(&expectedFilter); - ASSERT_BSONOBJ_EQ(childCq->getQueryRequest().getFilter(), expectedFilter.obj()); + ASSERT_BSONOBJ_EQ(childCq->getFindCommand().getFilter(), expectedFilter.obj()); - ASSERT_BSONOBJ_EQ(childCq->getQueryRequest().getProj(), baseCq->getQueryRequest().getProj()); - ASSERT_BSONOBJ_EQ(childCq->getQueryRequest().getSort(), baseCq->getQueryRequest().getSort()); - ASSERT_TRUE(childCq->getQueryRequest().isExplain()); + ASSERT_BSONOBJ_EQ(childCq->getFindCommand().getProjection(), + baseCq->getFindCommand().getProjection()); + ASSERT_BSONOBJ_EQ(childCq->getFindCommand().getSort(), baseCq->getFindCommand().getSort()); + ASSERT_TRUE(childCq->getExplain()); } TEST(CanonicalQueryTest, CanonicalQueryFromQRWithNoCollation) { QueryTestServiceContext serviceContext; auto opCtx = serviceContext.makeOperationContext(); - auto qr = std::make_unique<QueryRequest>(nss); - auto cq = assertGet(CanonicalQuery::canonicalize(opCtx.get(), std::move(qr))); + auto findCommand = std::make_unique<FindCommand>(nss); + auto cq = assertGet(CanonicalQuery::canonicalize(opCtx.get(), std::move(findCommand))); ASSERT_TRUE(cq->getCollator() == nullptr); } @@ -301,10 +303,10 @@ TEST(CanonicalQueryTest, CanonicalQueryFromQRWithCollation) { QueryTestServiceContext serviceContext; auto opCtx = serviceContext.makeOperationContext(); - auto qr = std::make_unique<QueryRequest>(nss); - qr->setCollation(BSON("locale" - << "reverse")); - auto cq = assertGet(CanonicalQuery::canonicalize(opCtx.get(), std::move(qr))); + auto findCommand = std::make_unique<FindCommand>(nss); + findCommand->setCollation(BSON("locale" + << "reverse")); + auto cq = assertGet(CanonicalQuery::canonicalize(opCtx.get(), std::move(findCommand))); CollatorInterfaceMock collator(CollatorInterfaceMock::MockType::kReverseString); ASSERT_TRUE(CollatorInterface::collatorsMatch(cq->getCollator(), &collator)); } @@ -313,9 +315,9 @@ TEST(CanonicalQueryTest, CanonicalQueryFromBaseQueryWithNoCollation) { QueryTestServiceContext serviceContext; auto opCtx = serviceContext.makeOperationContext(); - auto qr = std::make_unique<QueryRequest>(nss); - qr->setFilter(fromjson("{$or:[{a:1,b:1},{a:1,c:1}]}")); - auto baseCq = assertGet(CanonicalQuery::canonicalize(opCtx.get(), std::move(qr))); + auto findCommand = std::make_unique<FindCommand>(nss); + findCommand->setFilter(fromjson("{$or:[{a:1,b:1},{a:1,c:1}]}")); + auto baseCq = assertGet(CanonicalQuery::canonicalize(opCtx.get(), std::move(findCommand))); MatchExpression* firstClauseExpr = baseCq->root()->getChild(0); auto childCq = assertGet(CanonicalQuery::canonicalize(opCtx.get(), *baseCq, firstClauseExpr)); ASSERT_TRUE(baseCq->getCollator() == nullptr); @@ -326,11 +328,11 @@ TEST(CanonicalQueryTest, CanonicalQueryFromBaseQueryWithCollation) { QueryTestServiceContext serviceContext; auto opCtx = serviceContext.makeOperationContext(); - auto qr = std::make_unique<QueryRequest>(nss); - qr->setFilter(fromjson("{$or:[{a:1,b:1},{a:1,c:1}]}")); - qr->setCollation(BSON("locale" - << "reverse")); - auto baseCq = assertGet(CanonicalQuery::canonicalize(opCtx.get(), std::move(qr))); + auto findCommand = std::make_unique<FindCommand>(nss); + findCommand->setFilter(fromjson("{$or:[{a:1,b:1},{a:1,c:1}]}")); + findCommand->setCollation(BSON("locale" + << "reverse")); + auto baseCq = assertGet(CanonicalQuery::canonicalize(opCtx.get(), std::move(findCommand))); MatchExpression* firstClauseExpr = baseCq->root()->getChild(0); auto childCq = assertGet(CanonicalQuery::canonicalize(opCtx.get(), *baseCq, firstClauseExpr)); ASSERT(baseCq->getCollator()); @@ -342,9 +344,9 @@ TEST(CanonicalQueryTest, SettingCollatorUpdatesCollatorAndMatchExpression) { QueryTestServiceContext serviceContext; auto opCtx = serviceContext.makeOperationContext(); - auto qr = std::make_unique<QueryRequest>(nss); - qr->setFilter(fromjson("{a: 'foo', b: {$in: ['bar', 'baz']}}")); - auto cq = assertGet(CanonicalQuery::canonicalize(opCtx.get(), std::move(qr))); + auto findCommand = std::make_unique<FindCommand>(nss); + findCommand->setFilter(fromjson("{a: 'foo', b: {$in: ['bar', 'baz']}}")); + auto cq = assertGet(CanonicalQuery::canonicalize(opCtx.get(), std::move(findCommand))); ASSERT_EQUALS(2U, cq->root()->numChildren()); auto firstChild = cq->root()->getChild(0); auto secondChild = cq->root()->getChild(1); @@ -396,12 +398,13 @@ void assertValidSortOrder(BSONObj sort, BSONObj filter = BSONObj{}) { QueryTestServiceContext serviceContext; auto opCtx = serviceContext.makeOperationContext(); - auto qr = std::make_unique<QueryRequest>(nss); - qr->setFilter(filter); - qr->setSort(sort); + auto findCommand = std::make_unique<FindCommand>(nss); + findCommand->setFilter(filter); + findCommand->setSort(sort); auto statusWithCQ = CanonicalQuery::canonicalize(opCtx.get(), - std::move(qr), + std::move(findCommand), + false, nullptr, ExtensionsCallbackNoop(), MatchExpressionParser::kAllowAllSpecialFeatures); @@ -421,9 +424,9 @@ void assertInvalidSortOrder(BSONObj sort) { QueryTestServiceContext serviceContext; auto opCtx = serviceContext.makeOperationContext(); - auto qr = std::make_unique<QueryRequest>(nss); - qr->setSort(sort); - auto statusWithCQ = CanonicalQuery::canonicalize(opCtx.get(), std::move(qr)); + auto findCommand = std::make_unique<FindCommand>(nss); + findCommand->setSort(sort); + auto statusWithCQ = CanonicalQuery::canonicalize(opCtx.get(), std::move(findCommand)); ASSERT_NOT_OK(statusWithCQ.getStatus()); } diff --git a/src/mongo/db/query/classic_stage_builder.cpp b/src/mongo/db/query/classic_stage_builder.cpp index a70ff51848f..5a5d2f311dd 100644 --- a/src/mongo/db/query/classic_stage_builder.cpp +++ b/src/mongo/db/query/classic_stage_builder.cpp @@ -161,7 +161,7 @@ std::unique_ptr<PlanStage> ClassicStageBuilder::build(const QuerySolutionNode* r auto pn = static_cast<const ProjectionNodeDefault*>(root); auto childStage = build(pn->children[0]); return std::make_unique<ProjectionStageDefault>(_cq.getExpCtx(), - _cq.getQueryRequest().getProj(), + _cq.getFindCommand().getProjection(), _cq.getProj(), _ws, std::move(childStage)); @@ -170,7 +170,7 @@ std::unique_ptr<PlanStage> ClassicStageBuilder::build(const QuerySolutionNode* r auto pn = static_cast<const ProjectionNodeCovered*>(root); auto childStage = build(pn->children[0]); return std::make_unique<ProjectionStageCovered>(_cq.getExpCtxRaw(), - _cq.getQueryRequest().getProj(), + _cq.getFindCommand().getProjection(), _cq.getProj(), _ws, std::move(childStage), @@ -180,7 +180,7 @@ std::unique_ptr<PlanStage> ClassicStageBuilder::build(const QuerySolutionNode* r auto pn = static_cast<const ProjectionNodeSimple*>(root); auto childStage = build(pn->children[0]); return std::make_unique<ProjectionStageSimple>(_cq.getExpCtxRaw(), - _cq.getQueryRequest().getProj(), + _cq.getFindCommand().getProjection(), _cq.getProj(), _ws, std::move(childStage)); diff --git a/src/mongo/db/query/classic_stage_builder_test.cpp b/src/mongo/db/query/classic_stage_builder_test.cpp index 0fd2f44faa0..b7761ef474f 100644 --- a/src/mongo/db/query/classic_stage_builder_test.cpp +++ b/src/mongo/db/query/classic_stage_builder_test.cpp @@ -68,9 +68,10 @@ public: * Builds a PlanStage using the given WorkingSet and QuerySolution. */ std::unique_ptr<PlanStage> buildPlanStage(std::unique_ptr<QuerySolution> querySolution) { - auto qr = std::make_unique<QueryRequest>(kNss); + auto findCommand = std::make_unique<FindCommand>(kNss); auto expCtx = make_intrusive<ExpressionContext>(opCtx(), nullptr, kNss); - auto statusWithCQ = CanonicalQuery::canonicalize(opCtx(), std::move(qr), expCtx); + auto statusWithCQ = + CanonicalQuery::canonicalize(opCtx(), std::move(findCommand), false, expCtx); ASSERT_OK(statusWithCQ.getStatus()); stage_builder::ClassicStageBuilder builder{ diff --git a/src/mongo/db/query/count_command_as_aggregation_command.cpp b/src/mongo/db/query/count_command_as_aggregation_command.cpp index 418fe629a6a..668baeb7c6b 100644 --- a/src/mongo/db/query/count_command_as_aggregation_command.cpp +++ b/src/mongo/db/query/count_command_as_aggregation_command.cpp @@ -31,7 +31,7 @@ #include "mongo/db/query/count_command_as_aggregation_command.h" -#include "mongo/db/query/query_request.h" +#include "mongo/db/query/query_request_helper.h" #include "mongo/util/str.h" namespace mongo { @@ -101,7 +101,8 @@ StatusWith<BSONObj> countCommandAsAggregationCommand(const CountCommand& cmd, if (auto unwrapped = cmd.getQueryOptions()) { if (!unwrapped->isEmpty()) { - aggregationBuilder.append(QueryRequest::kUnwrappedReadPrefField, unwrapped.get()); + aggregationBuilder.append(query_request_helper::kUnwrappedReadPrefField, + unwrapped.get()); } } diff --git a/src/mongo/db/query/count_request.cpp b/src/mongo/db/query/count_request.cpp index 93a645841e3..0b6f1a68a71 100644 --- a/src/mongo/db/query/count_request.cpp +++ b/src/mongo/db/query/count_request.cpp @@ -33,7 +33,7 @@ #include "mongo/bson/bsonobjbuilder.h" #include "mongo/db/matcher/expression_parser.h" -#include "mongo/db/query/query_request.h" +#include "mongo/db/query/query_request_helper.h" namespace mongo { namespace count_request { diff --git a/src/mongo/db/query/find.cpp b/src/mongo/db/query/find.cpp index 67e439e7e53..6e919f51a1a 100644 --- a/src/mongo/db/query/find.cpp +++ b/src/mongo/db/query/find.cpp @@ -85,8 +85,8 @@ bool shouldSaveCursor(OperationContext* opCtx, const CollectionPtr& collection, PlanExecutor::ExecState finalState, PlanExecutor* exec) { - const QueryRequest& qr = exec->getCanonicalQuery()->getQueryRequest(); - if (qr.isSingleBatch()) { + const FindCommand& findCommand = exec->getCanonicalQuery()->getFindCommand(); + if (findCommand.getSingleBatch()) { return false; } @@ -96,7 +96,7 @@ bool shouldSaveCursor(OperationContext* opCtx, // SERVER-13955: we should be able to create a tailable cursor that waits on // an empty collection. Right now we do not keep a cursor if the collection // has zero records. - if (qr.isTailable()) { + if (findCommand.getTailable()) { return collection && collection->numRecords(opCtx) != 0U; } @@ -614,30 +614,34 @@ bool runQuery(OperationContext* opCtx, // Parse, canonicalize, plan, transcribe, and get a plan executor. AutoGetCollectionForReadCommandMaybeLockFree collection( opCtx, nss, AutoGetCollectionViewMode::kViewsForbidden); - const QueryRequest& qr = cq->getQueryRequest(); - opCtx->setExhaust(qr.isExhaust()); + const bool isExhaust = (q.queryOptions & QueryOption_Exhaust) != 0; + opCtx->setExhaust(isExhaust); { // Allow the query to run on secondaries if the read preference permits it. If no read // preference was specified, allow the query to run iff slaveOk has been set. - const bool slaveOK = qr.hasReadPref() + const bool isSecondaryOk = (q.queryOptions & QueryOption_SecondaryOk) != 0; + const bool hasReadPref = q.query.hasField(query_request_helper::kWrappedReadPrefField); + const bool secondaryOk = hasReadPref ? uassertStatusOK(ReadPreferenceSetting::fromContainingBSON(q.query)) .canRunOnSecondary() - : qr.isSlaveOk(); - uassertStatusOK( - repl::ReplicationCoordinator::get(opCtx)->checkCanServeReadsFor(opCtx, nss, slaveOK)); + : isSecondaryOk; + uassertStatusOK(repl::ReplicationCoordinator::get(opCtx)->checkCanServeReadsFor( + opCtx, nss, secondaryOk)); } + const FindCommand& findCommand = cq->getFindCommand(); // Get the execution plan for the query. constexpr auto verbosity = ExplainOptions::Verbosity::kExecAllPlans; - expCtx->explain = qr.isExplain() ? boost::make_optional(verbosity) : boost::none; + const bool isExplain = cq->getExplain(); + expCtx->explain = isExplain ? boost::make_optional(verbosity) : boost::none; auto exec = uassertStatusOK(getExecutorLegacyFind(opCtx, &collection.getCollection(), std::move(cq))); // If it's actually an explain, do the explain and return rather than falling through // to the normal query execution loop. - if (qr.isExplain()) { + if (isExplain) { BufBuilder bb; bb.skip(sizeof(QueryResult::Value)); @@ -666,12 +670,13 @@ bool runQuery(OperationContext* opCtx, return false; } + int maxTimeMS = findCommand.getMaxTimeMS() ? static_cast<int>(*findCommand.getMaxTimeMS()) : 0; // Handle query option $maxTimeMS (not used with commands). - if (qr.getMaxTimeMS() > 0) { + if (maxTimeMS > 0) { uassert(40116, "Illegal attempt to set operation deadline within DBDirectClient", !opCtx->getClient()->isInDirectClient()); - opCtx->setDeadlineAfterNowBy(Milliseconds{qr.getMaxTimeMS()}, ErrorCodes::MaxTimeMSExpired); + opCtx->setDeadlineAfterNowBy(Milliseconds{maxTimeMS}, ErrorCodes::MaxTimeMSExpired); } opCtx->checkForInterrupt(); // May trigger maxTimeAlwaysTimeOut fail point. @@ -713,12 +718,12 @@ bool runQuery(OperationContext* opCtx, docUnitsReturned.observeOne(obj.objsize()); - if (FindCommon::enoughForFirstBatch(qr, numResults)) { + if (FindCommon::enoughForFirstBatch(findCommand, numResults)) { LOGV2_DEBUG(20915, 5, "Enough for first batch", - "wantMore"_attr = !qr.isSingleBatch(), - "numToReturn"_attr = qr.getNToReturn().value_or(0), + "wantMore"_attr = !findCommand.getSingleBatch(), + "numToReturn"_attr = findCommand.getNtoreturn().value_or(0), "numResults"_attr = numResults); break; } diff --git a/src/mongo/db/query/find_common.cpp b/src/mongo/db/query/find_common.cpp index 048683f677b..02191c367eb 100644 --- a/src/mongo/db/query/find_common.cpp +++ b/src/mongo/db/query/find_common.cpp @@ -37,7 +37,7 @@ #include "mongo/db/curop.h" #include "mongo/db/curop_failpoint_helpers.h" #include "mongo/db/query/canonical_query.h" -#include "mongo/db/query/query_request.h" +#include "mongo/db/query/query_request_helper.h" #include "mongo/logv2/log.h" #include "mongo/util/assert_util.h" @@ -58,13 +58,15 @@ MONGO_FAIL_POINT_DEFINE(failGetMoreAfterCursorCheckout); const OperationContext::Decoration<AwaitDataState> awaitDataState = OperationContext::declareDecoration<AwaitDataState>(); -bool FindCommon::enoughForFirstBatch(const QueryRequest& qr, long long numDocs) { - if (!qr.getEffectiveBatchSize()) { +bool FindCommon::enoughForFirstBatch(const FindCommand& findCommand, long long numDocs) { + auto effectiveBatchSize = + findCommand.getBatchSize() ? findCommand.getBatchSize() : findCommand.getNtoreturn(); + if (!effectiveBatchSize) { // We enforce a default batch size for the initial find if no batch size is specified. - return numDocs >= QueryRequest::kDefaultBatchSize; + return numDocs >= query_request_helper::kDefaultBatchSize; } - return numDocs >= qr.getEffectiveBatchSize().value(); + return numDocs >= effectiveBatchSize.value(); } bool FindCommon::haveSpaceForNext(const BSONObj& nextDoc, long long numDocs, int bytesBuffered) { diff --git a/src/mongo/db/query/find_common.h b/src/mongo/db/query/find_common.h index 44b941f7667..3fc2b9b0e76 100644 --- a/src/mongo/db/query/find_common.h +++ b/src/mongo/db/query/find_common.h @@ -54,7 +54,7 @@ extern const OperationContext::Decoration<AwaitDataState> awaitDataState; class BSONObj; class CanonicalQuery; -class QueryRequest; +class FindCommand; // Failpoint for making find hang. extern FailPoint waitInFindBeforeMakingBatch; @@ -101,7 +101,7 @@ public: * * If 'qr' does not have a batchSize, the default batchSize is respected. */ - static bool enoughForFirstBatch(const QueryRequest& qr, long long numDocs); + static bool enoughForFirstBatch(const FindCommand& findCommand, long long numDocs); /** * Returns true if the batchSize for the getMore has been satisfied. diff --git a/src/mongo/db/query/get_executor.cpp b/src/mongo/db/query/get_executor.cpp index 19e068ca39c..2251683e71d 100644 --- a/src/mongo/db/query/get_executor.cpp +++ b/src/mongo/db/query/get_executor.cpp @@ -147,11 +147,11 @@ bool turnIxscanIntoCount(QuerySolution* soln); * Returns 'true' if 'query' on the given 'collection' can be answered using a special IDHACK plan. */ bool isIdHackEligibleQuery(const CollectionPtr& collection, const CanonicalQuery& query) { - return !query.getQueryRequest().showRecordId() && query.getQueryRequest().getHint().isEmpty() && - query.getQueryRequest().getMin().isEmpty() && query.getQueryRequest().getMax().isEmpty() && - !query.getQueryRequest().getSkip() && - CanonicalQuery::isSimpleIdQuery(query.getQueryRequest().getFilter()) && - !query.getQueryRequest().isTailable() && + const auto& findCommand = query.getFindCommand(); + return !findCommand.getShowRecordId() && findCommand.getHint().isEmpty() && + findCommand.getMin().isEmpty() && findCommand.getMax().isEmpty() && + !findCommand.getSkip() && CanonicalQuery::isSimpleIdQuery(findCommand.getFilter()) && + !findCommand.getTailable() && CollatorInterface::collatorsMatch(query.getCollator(), collection->getDefaultCollator()); } } // namespace @@ -351,7 +351,7 @@ void fillOutPlannerParams(OperationContext* opCtx, plannerParams->options |= QueryPlannerParams::SPLIT_LIMITED_SORT; if (shouldWaitForOplogVisibility( - opCtx, collection, canonicalQuery->getQueryRequest().isTailable())) { + opCtx, collection, canonicalQuery->getFindCommand().getTailable())) { plannerParams->options |= QueryPlannerParams::OPLOG_SCAN_WAIT_FOR_VISIBLE; } } @@ -578,7 +578,7 @@ public: // If the canonical query does not have a user-specified collation and no one has given the // CanonicalQuery a collation already, set it from the collection default. - if (_cq->getQueryRequest().getCollation().isEmpty() && _cq->getCollator() == nullptr && + if (_cq->getFindCommand().getCollation().isEmpty() && _cq->getCollator() == nullptr && _collection->getDefaultCollator()) { _cq->setCollator(_collection->getDefaultCollator()->clone()); } @@ -598,7 +598,7 @@ public: } // Tailable: If the query requests tailable the collection must be capped. - if (_cq->getQueryRequest().isTailable() && !_collection->isCapped()) { + if (_cq->getFindCommand().getTailable() && !_collection->isCapped()) { return Status(ErrorCodes::BadValue, str::stream() << "error processing query: " << _cq->toString() << " tailable cursor requested on non capped collection"); @@ -799,10 +799,10 @@ protected: // Add a SortKeyGeneratorStage if the query requested sortKey metadata. if (_cq->metadataDeps()[DocumentMetadataFields::kSortKey]) { stage = std::make_unique<SortKeyGeneratorStage>( - _cq->getExpCtxRaw(), std::move(stage), _ws, _cq->getQueryRequest().getSort()); + _cq->getExpCtxRaw(), std::move(stage), _ws, _cq->getFindCommand().getSort()); } - if (_cq->getQueryRequest().returnKey()) { + if (_cq->getFindCommand().getReturnKey()) { // If returnKey was requested, add ReturnKeyStage to return only the index keys in // the resulting documents. If a projection was also specified, it will be ignored, // with the exception the $meta sortKey projection, which can be used along with the @@ -820,17 +820,19 @@ protected: // simple inclusion fast path. // Stuff the right data into the params depending on what proj impl we use. if (!cqProjection->isSimple()) { - stage = std::make_unique<ProjectionStageDefault>(_cq->getExpCtxRaw(), - _cq->getQueryRequest().getProj(), - _cq->getProj(), - _ws, - std::move(stage)); + stage = + std::make_unique<ProjectionStageDefault>(_cq->getExpCtxRaw(), + _cq->getFindCommand().getProjection(), + _cq->getProj(), + _ws, + std::move(stage)); } else { - stage = std::make_unique<ProjectionStageSimple>(_cq->getExpCtxRaw(), - _cq->getQueryRequest().getProj(), - _cq->getProj(), - _ws, - std::move(stage)); + stage = + std::make_unique<ProjectionStageSimple>(_cq->getExpCtxRaw(), + _cq->getFindCommand().getProjection(), + _cq->getProj(), + _ws, + std::move(stage)); } } @@ -1112,7 +1114,7 @@ inline bool isQuerySbeCompatible(const CanonicalQuery* const cq, size_t plannerO const bool isNotCount = !(plannerOptions & QueryPlannerParams::IS_COUNT); // Specifying 'ntoreturn' in an OP_QUERY style find may result in a QuerySolution with // ENSURE_SORTED node, which is currently not supported by SBE. - const bool doesNotNeedEnsureSorted = !cq->getQueryRequest().getNToReturn(); + const bool doesNotNeedEnsureSorted = !cq->getFindCommand().getNtoreturn(); const bool doesNotContainMetadataRequirements = cq->metadataDeps().none(); const bool doesNotSortOnDottedPath = !sortPattern || std::all_of(sortPattern->begin(), sortPattern->end(), [](auto&& part) { @@ -1716,16 +1718,16 @@ StatusWith<std::unique_ptr<PlanExecutor, PlanExecutor::Deleter>> getExecutorCoun OperationContext* opCtx = expCtx->opCtx; std::unique_ptr<WorkingSet> ws = std::make_unique<WorkingSet>(); - auto qr = std::make_unique<QueryRequest>(nss); - qr->setFilter(request.getQuery()); + auto findCommand = std::make_unique<FindCommand>(nss); + findCommand->setFilter(request.getQuery()); auto collation = request.getCollation().value_or(BSONObj()); - qr->setCollation(collation); - qr->setHint(request.getHint()); - qr->setExplain(explain); + findCommand->setCollation(collation); + findCommand->setHint(request.getHint()); auto statusWithCQ = CanonicalQuery::canonicalize( opCtx, - std::move(qr), + std::move(findCommand), + explain, expCtx, collection ? static_cast<const ExtensionsCallback&>( ExtensionsCallbackReal(opCtx, &collection->ns())) @@ -1998,7 +2000,7 @@ QueryPlannerParams fillOutPlannerParamsForDistinct(OperationContext* opCtx, const bool mayUnwindArrays = !(plannerOptions & QueryPlannerParams::STRICT_DISTINCT_ONLY); std::unique_ptr<IndexCatalog::IndexIterator> ii = collection->getIndexCatalog()->getIndexIterator(opCtx, false); - auto query = parsedDistinct.getQuery()->getQueryRequest().getFilter(); + auto query = parsedDistinct.getQuery()->getFindCommand().getFilter(); while (ii->more()) { const IndexCatalogEntry* ice = ii->next(); const IndexDescriptor* desc = ice->descriptor(); @@ -2043,7 +2045,7 @@ QueryPlannerParams fillOutPlannerParamsForDistinct(OperationContext* opCtx, } const CanonicalQuery* canonicalQuery = parsedDistinct.getQuery(); - const BSONObj& hint = canonicalQuery->getQueryRequest().getHint(); + const BSONObj& hint = canonicalQuery->getFindCommand().getHint(); applyIndexFilters(collection, *canonicalQuery, &plannerParams); @@ -2083,7 +2085,7 @@ StatusWith<std::unique_ptr<PlanExecutor, PlanExecutor::Deleter>> getExecutorForS // If there's no query, we can just distinct-scan one of the indices. Not every index in // plannerParams.indices may be suitable. Refer to getDistinctNodeIndex(). size_t distinctNodeIndex = 0; - if (!parsedDistinct->getQuery()->getQueryRequest().getFilter().isEmpty() || + if (!parsedDistinct->getQuery()->getFindCommand().getFilter().isEmpty() || parsedDistinct->getQuery()->getSortPattern() || !getDistinctNodeIndex( plannerParams.indices, parsedDistinct->getKey(), collator, &distinctNodeIndex)) { @@ -2204,14 +2206,15 @@ StatusWith<std::unique_ptr<PlanExecutor, PlanExecutor::Deleter>> getExecutorWith size_t plannerOptions) { const auto& collection = *coll; - auto qr = std::make_unique<QueryRequest>(cq->getQueryRequest()); - qr->setProj(BSONObj()); + auto findCommand = std::make_unique<FindCommand>(cq->getFindCommand()); + findCommand->setProjection(BSONObj()); const boost::intrusive_ptr<ExpressionContext> expCtx; const ExtensionsCallbackReal extensionsCallback(opCtx, &collection->ns()); auto cqWithoutProjection = CanonicalQuery::canonicalize(opCtx, - std::move(qr), + std::move(findCommand), + cq->getExplain(), expCtx, extensionsCallback, MatchExpressionParser::kAllowAllSpecialFeatures); diff --git a/src/mongo/db/query/get_executor.h b/src/mongo/db/query/get_executor.h index 21637b03b88..00ed169447c 100644 --- a/src/mongo/db/query/get_executor.h +++ b/src/mongo/db/query/get_executor.h @@ -176,7 +176,7 @@ bool turnIxscanIntoDistinctIxscan(QuerySolution* soln, * A $group stage on a single field behaves similarly to a distinct command. If it has no * accumulators or only $first accumulators, the $group command only needs to visit one document for * each distinct value of the grouped-by (_id) field to compute its result. When there is a sort - * order specified in parsedDistinct->getQuery()->getQueryRequest.getSort(), the DISTINCT_SCAN will + * order specified in parsedDistinct->getQuery()->getFindCommand().getSort(), the DISTINCT_SCAN will * follow that sort order, ensuring that it chooses the correct document from each group to compute * any $first accumulators. * diff --git a/src/mongo/db/query/get_executor_test.cpp b/src/mongo/db/query/get_executor_test.cpp index 40da9d86e8e..e3ac2411131 100644 --- a/src/mongo/db/query/get_executor_test.cpp +++ b/src/mongo/db/query/get_executor_test.cpp @@ -74,11 +74,11 @@ unique_ptr<CanonicalQuery> canonicalize(const char* queryStr, QueryTestServiceContext serviceContext; auto opCtx = serviceContext.makeOperationContext(); - auto qr = std::make_unique<QueryRequest>(nss); - qr->setFilter(fromjson(queryStr)); - qr->setSort(fromjson(sortStr)); - qr->setProj(fromjson(projStr)); - auto statusWithCQ = CanonicalQuery::canonicalize(opCtx.get(), std::move(qr)); + auto findCommand = std::make_unique<FindCommand>(nss); + findCommand->setFilter(fromjson(queryStr)); + findCommand->setSort(fromjson(sortStr)); + findCommand->setProjection(fromjson(projStr)); + auto statusWithCQ = CanonicalQuery::canonicalize(opCtx.get(), std::move(findCommand)); ASSERT_OK(statusWithCQ.getStatus()); return std::move(statusWithCQ.getValue()); } diff --git a/src/mongo/db/query/parsed_distinct.cpp b/src/mongo/db/query/parsed_distinct.cpp index dc751b3eead..e797f9650c2 100644 --- a/src/mongo/db/query/parsed_distinct.cpp +++ b/src/mongo/db/query/parsed_distinct.cpp @@ -37,7 +37,7 @@ #include "mongo/bson/util/bson_extract.h" #include "mongo/db/query/canonical_query.h" #include "mongo/db/query/distinct_command_gen.h" -#include "mongo/db/query/query_request.h" +#include "mongo/db/query/query_request_helper.h" #include "mongo/db/repl/read_concern_args.h" #include "mongo/idl/idl_parser.h" #include "mongo/util/str.h" @@ -171,8 +171,9 @@ StatusWith<BSONObj> ParsedDistinct::asAggregationCommand() const { BSONObjBuilder aggregationBuilder; invariant(_query); - const QueryRequest& qr = _query->getQueryRequest(); - aggregationBuilder.append("aggregate", qr.nss().coll()); + const FindCommand& findCommand = _query->getFindCommand(); + aggregationBuilder.append( + "aggregate", findCommand.getNamespaceOrUUID().nss().value_or(NamespaceString()).coll()); // Build a pipeline that accomplishes the distinct request. The building code constructs a // pipeline that looks like this, assuming the distinct is on the key "a.b.c" @@ -201,9 +202,9 @@ StatusWith<BSONObj> ParsedDistinct::asAggregationCommand() const { // Any arrays remaining after the $unwinds must have been nested arrays, so in order to match // the behavior of the distinct() command, we filter them out before the $group. BSONArrayBuilder pipelineBuilder(aggregationBuilder.subarrayStart("pipeline")); - if (!qr.getFilter().isEmpty()) { + if (!findCommand.getFilter().isEmpty()) { BSONObjBuilder matchStageBuilder(pipelineBuilder.subobjStart()); - matchStageBuilder.append("$match", qr.getFilter()); + matchStageBuilder.append("$match", findCommand.getFilter()); matchStageBuilder.doneFast(); } @@ -225,19 +226,21 @@ StatusWith<BSONObj> ParsedDistinct::asAggregationCommand() const { groupStageBuilder.doneFast(); pipelineBuilder.doneFast(); - aggregationBuilder.append(kCollationField, qr.getCollation()); + aggregationBuilder.append(kCollationField, findCommand.getCollation()); - if (qr.getMaxTimeMS() > 0) { - aggregationBuilder.append(QueryRequest::cmdOptionMaxTimeMS, qr.getMaxTimeMS()); + int maxTimeMS = findCommand.getMaxTimeMS() ? static_cast<int>(*findCommand.getMaxTimeMS()) : 0; + if (maxTimeMS > 0) { + aggregationBuilder.append(query_request_helper::cmdOptionMaxTimeMS, maxTimeMS); } - if (qr.getReadConcern() && !qr.getReadConcern()->isEmpty()) { + if (findCommand.getReadConcern() && !findCommand.getReadConcern()->isEmpty()) { aggregationBuilder.append(repl::ReadConcernArgs::kReadConcernFieldName, - *qr.getReadConcern()); + *findCommand.getReadConcern()); } - if (!qr.getUnwrappedReadPref().isEmpty()) { - aggregationBuilder.append(QueryRequest::kUnwrappedReadPrefField, qr.getUnwrappedReadPref()); + if (!findCommand.getUnwrappedReadPref().isEmpty()) { + aggregationBuilder.append(query_request_helper::kUnwrappedReadPrefField, + findCommand.getUnwrappedReadPref()); } // Specify the 'cursor' option so that aggregation uses the cursor interface. @@ -261,7 +264,7 @@ StatusWith<ParsedDistinct> ParsedDistinct::parse(OperationContext* opCtx, return exceptionToStatus(); } - auto qr = std::make_unique<QueryRequest>(nss); + auto findCommand = std::make_unique<FindCommand>(nss); if (parsedDistinct.getKey().find('\0') != std::string::npos) { return Status(ErrorCodes::Error(31032), "Key field cannot contain an embedded null byte"); @@ -269,14 +272,14 @@ StatusWith<ParsedDistinct> ParsedDistinct::parse(OperationContext* opCtx, // Create a projection on the fields needed by the distinct command, so that the query planner // will produce a covered plan if possible. - qr->setProj(getDistinctProjection(std::string(parsedDistinct.getKey()))); + findCommand->setProjection(getDistinctProjection(std::string(parsedDistinct.getKey()))); if (auto query = parsedDistinct.getQuery()) { - qr->setFilter(query.get()); + findCommand->setFilter(query.get().getOwned()); } if (auto collation = parsedDistinct.getCollation()) { - qr->setCollation(collation.get()); + findCommand->setCollation(collation.get().getOwned()); } // The IDL parser above does not handle generic command arguments. Since the underlying query @@ -289,33 +292,32 @@ StatusWith<ParsedDistinct> ParsedDistinct::parse(OperationContext* opCtx, << "\" had the wrong type. Expected " << typeName(BSONType::Object) << ", found " << typeName(readConcernElt.type())); } - qr->setReadConcern(readConcernElt.embeddedObject()); + findCommand->setReadConcern(readConcernElt.embeddedObject().getOwned()); } - if (auto queryOptionsElt = cmdObj[QueryRequest::kUnwrappedReadPrefField]) { + if (auto queryOptionsElt = cmdObj[query_request_helper::kUnwrappedReadPrefField]) { if (queryOptionsElt.type() != BSONType::Object) { return Status(ErrorCodes::TypeMismatch, str::stream() - << "\"" << QueryRequest::kUnwrappedReadPrefField + << "\"" << query_request_helper::kUnwrappedReadPrefField << "\" had the wrong type. Expected " << typeName(BSONType::Object) << ", found " << typeName(queryOptionsElt.type())); } - qr->setUnwrappedReadPref(queryOptionsElt.embeddedObject()); + findCommand->setUnwrappedReadPref(queryOptionsElt.embeddedObject().getOwned()); } - if (auto maxTimeMSElt = cmdObj[QueryRequest::cmdOptionMaxTimeMS]) { + if (auto maxTimeMSElt = cmdObj[query_request_helper::cmdOptionMaxTimeMS]) { auto maxTimeMS = parseMaxTimeMS(maxTimeMSElt); if (!maxTimeMS.isOK()) { return maxTimeMS.getStatus(); } - qr->setMaxTimeMS(static_cast<unsigned int>(maxTimeMS.getValue())); + findCommand->setMaxTimeMS(static_cast<unsigned int>(maxTimeMS.getValue())); } - qr->setExplain(isExplain); - const boost::intrusive_ptr<ExpressionContext> expCtx; auto cq = CanonicalQuery::canonicalize(opCtx, - std::move(qr), + std::move(findCommand), + isExplain, expCtx, extensionsCallback, MatchExpressionParser::kAllowAllSpecialFeatures); @@ -323,7 +325,7 @@ StatusWith<ParsedDistinct> ParsedDistinct::parse(OperationContext* opCtx, return cq.getStatus(); } - if (cq.getValue()->getQueryRequest().getCollation().isEmpty() && defaultCollator) { + if (cq.getValue()->getFindCommand().getCollation().isEmpty() && defaultCollator) { cq.getValue()->setCollator(defaultCollator->clone()); } diff --git a/src/mongo/db/query/plan_cache.cpp b/src/mongo/db/query/plan_cache.cpp index f29c2925b6b..6a78e017dee 100644 --- a/src/mongo/db/query/plan_cache.cpp +++ b/src/mongo/db/query/plan_cache.cpp @@ -121,7 +121,7 @@ StringBuilder& operator<<(StringBuilder& builder, const PlanCacheKey& key) { // bool PlanCache::shouldCacheQuery(const CanonicalQuery& query) { - const QueryRequest& qr = query.getQueryRequest(); + const FindCommand& findCommand = query.getFindCommand(); const MatchExpression* expr = query.root(); // Collection scan @@ -132,19 +132,19 @@ bool PlanCache::shouldCacheQuery(const CanonicalQuery& query) { } // Hint provided - if (!qr.getHint().isEmpty()) { + if (!findCommand.getHint().isEmpty()) { return false; } // Min provided // Min queries are a special case of hinted queries. - if (!qr.getMin().isEmpty()) { + if (!findCommand.getMin().isEmpty()) { return false; } // Max provided // Similar to min, max queries are a special case of hinted queries. - if (!qr.getMax().isEmpty()) { + if (!findCommand.getMax().isEmpty()) { return false; } @@ -152,12 +152,12 @@ bool PlanCache::shouldCacheQuery(const CanonicalQuery& query) { // that explain queries don't affect cache state, and it also makes // sure that we can always generate information regarding rejected plans // and/or trial period execution of candidate plans. - if (qr.isExplain()) { + if (query.getExplain()) { return false; } // Tailable cursors won't get cached, just turn into collscans. - if (query.getQueryRequest().isTailable()) { + if (query.getFindCommand().getTailable()) { return false; } @@ -204,9 +204,9 @@ std::unique_ptr<PlanCacheEntry> PlanCacheEntry::create( if (includeDebugInfo) { // Strip projections on $-prefixed fields, as these are added by internal callers of the // system and are not considered part of the user projection. - const QueryRequest& qr = query.getQueryRequest(); + const FindCommand& findCommand = query.getFindCommand(); BSONObjBuilder projBuilder; - for (auto elem : qr.getProj()) { + for (auto elem : findCommand.getProjection()) { if (elem.fieldName()[0] == '$') { continue; } @@ -214,8 +214,8 @@ std::unique_ptr<PlanCacheEntry> PlanCacheEntry::create( } CreatedFromQuery createdFromQuery{ - qr.getFilter(), - qr.getSort(), + findCommand.getFilter(), + findCommand.getSort(), projBuilder.obj(), query.getCollator() ? query.getCollator()->getSpec().toBSON() : BSONObj()}; debugInfo.emplace(std::move(createdFromQuery), std::move(decision)); diff --git a/src/mongo/db/query/plan_cache_test.cpp b/src/mongo/db/query/plan_cache_test.cpp index 9df05d614bf..6d2bb24bd0a 100644 --- a/src/mongo/db/query/plan_cache_test.cpp +++ b/src/mongo/db/query/plan_cache_test.cpp @@ -74,12 +74,13 @@ unique_ptr<CanonicalQuery> canonicalize(const BSONObj& queryObj) { QueryTestServiceContext serviceContext; auto opCtx = serviceContext.makeOperationContext(); - auto qr = std::make_unique<QueryRequest>(nss); - qr->setFilter(queryObj); + auto findCommand = std::make_unique<FindCommand>(nss); + findCommand->setFilter(queryObj); const boost::intrusive_ptr<ExpressionContext> expCtx; auto statusWithCQ = CanonicalQuery::canonicalize(opCtx.get(), - std::move(qr), + std::move(findCommand), + false, expCtx, ExtensionsCallbackNoop(), MatchExpressionParser::kAllowAllSpecialFeatures); @@ -99,15 +100,16 @@ unique_ptr<CanonicalQuery> canonicalize(BSONObj query, QueryTestServiceContext serviceContext; auto opCtx = serviceContext.makeOperationContext(); - auto qr = std::make_unique<QueryRequest>(nss); - qr->setFilter(query); - qr->setSort(sort); - qr->setProj(proj); - qr->setCollation(collation); + auto findCommand = std::make_unique<FindCommand>(nss); + findCommand->setFilter(query); + findCommand->setSort(sort); + findCommand->setProjection(proj); + findCommand->setCollation(collation); const boost::intrusive_ptr<ExpressionContext> expCtx; auto statusWithCQ = CanonicalQuery::canonicalize(opCtx.get(), - std::move(qr), + std::move(findCommand), + false, expCtx, ExtensionsCallbackNoop(), MatchExpressionParser::kAllowAllSpecialFeatures); @@ -134,23 +136,24 @@ unique_ptr<CanonicalQuery> canonicalize(const char* queryStr, QueryTestServiceContext serviceContext; auto opCtx = serviceContext.makeOperationContext(); - auto qr = std::make_unique<QueryRequest>(nss); - qr->setFilter(fromjson(queryStr)); - qr->setSort(fromjson(sortStr)); - qr->setProj(fromjson(projStr)); + auto findCommand = std::make_unique<FindCommand>(nss); + findCommand->setFilter(fromjson(queryStr)); + findCommand->setSort(fromjson(sortStr)); + findCommand->setProjection(fromjson(projStr)); if (skip) { - qr->setSkip(skip); + findCommand->setSkip(skip); } if (limit) { - qr->setLimit(limit); + findCommand->setLimit(limit); } - qr->setHint(fromjson(hintStr)); - qr->setMin(fromjson(minStr)); - qr->setMax(fromjson(maxStr)); + findCommand->setHint(fromjson(hintStr)); + findCommand->setMin(fromjson(minStr)); + findCommand->setMax(fromjson(maxStr)); const boost::intrusive_ptr<ExpressionContext> expCtx; auto statusWithCQ = CanonicalQuery::canonicalize(opCtx.get(), - std::move(qr), + std::move(findCommand), + false, expCtx, ExtensionsCallbackNoop(), MatchExpressionParser::kAllowAllSpecialFeatures); @@ -170,24 +173,24 @@ unique_ptr<CanonicalQuery> canonicalize(const char* queryStr, QueryTestServiceContext serviceContext; auto opCtx = serviceContext.makeOperationContext(); - auto qr = std::make_unique<QueryRequest>(nss); - qr->setFilter(fromjson(queryStr)); - qr->setSort(fromjson(sortStr)); - qr->setProj(fromjson(projStr)); + auto findCommand = std::make_unique<FindCommand>(nss); + findCommand->setFilter(fromjson(queryStr)); + findCommand->setSort(fromjson(sortStr)); + findCommand->setProjection(fromjson(projStr)); if (skip) { - qr->setSkip(skip); + findCommand->setSkip(skip); } if (limit) { - qr->setLimit(limit); + findCommand->setLimit(limit); } - qr->setHint(fromjson(hintStr)); - qr->setMin(fromjson(minStr)); - qr->setMax(fromjson(maxStr)); - qr->setExplain(explain); + findCommand->setHint(fromjson(hintStr)); + findCommand->setMin(fromjson(minStr)); + findCommand->setMax(fromjson(maxStr)); const boost::intrusive_ptr<ExpressionContext> expCtx; auto statusWithCQ = CanonicalQuery::canonicalize(opCtx.get(), - std::move(qr), + std::move(findCommand), + explain, expCtx, ExtensionsCallbackNoop(), MatchExpressionParser::kAllowAllSpecialFeatures); @@ -487,8 +490,7 @@ TEST(PlanCacheTest, ShouldNotCacheQueryExplain) { "{}", // min, max true // explain )); - const QueryRequest& qr = cq->getQueryRequest(); - ASSERT_TRUE(qr.isExplain()); + ASSERT_TRUE(cq->getExplain()); assertShouldNotCacheQuery(*cq); } @@ -1015,23 +1017,24 @@ protected: // Clean up any previous state from a call to runQueryFull or runQueryAsCommand. solns.clear(); - auto qr = std::make_unique<QueryRequest>(nss); - qr->setFilter(query); - qr->setSort(sort); - qr->setProj(proj); + auto findCommand = std::make_unique<FindCommand>(nss); + findCommand->setFilter(query); + findCommand->setSort(sort); + findCommand->setProjection(proj); if (skip) { - qr->setSkip(skip); + findCommand->setSkip(skip); } if (limit) { - qr->setLimit(limit); + findCommand->setLimit(limit); } - qr->setHint(hint); - qr->setMin(minObj); - qr->setMax(maxObj); + findCommand->setHint(hint); + findCommand->setMin(minObj); + findCommand->setMax(maxObj); const boost::intrusive_ptr<ExpressionContext> expCtx; auto statusWithCQ = CanonicalQuery::canonicalize(opCtx.get(), - std::move(qr), + std::move(findCommand), + false, expCtx, ExtensionsCallbackNoop(), MatchExpressionParser::kAllowAllSpecialFeatures); @@ -1049,13 +1052,14 @@ protected: solns.clear(); const bool isExplain = false; - std::unique_ptr<QueryRequest> qr( - QueryRequest::makeFromFindCommandForTests(cmdObj, isExplain)); + std::unique_ptr<FindCommand> findCommand( + query_request_helper::makeFromFindCommandForTests(cmdObj)); const boost::intrusive_ptr<ExpressionContext> expCtx; auto statusWithCQ = CanonicalQuery::canonicalize(opCtx.get(), - std::move(qr), + std::move(findCommand), + isExplain, expCtx, ExtensionsCallbackNoop(), MatchExpressionParser::kAllowAllSpecialFeatures); @@ -1121,15 +1125,16 @@ protected: QueryTestServiceContext serviceContext; auto opCtx = serviceContext.makeOperationContext(); - auto qr = std::make_unique<QueryRequest>(nss); - qr->setFilter(query); - qr->setSort(sort); - qr->setProj(proj); - qr->setCollation(collation); + auto findCommand = std::make_unique<FindCommand>(nss); + findCommand->setFilter(query); + findCommand->setSort(sort); + findCommand->setProjection(proj); + findCommand->setCollation(collation); const boost::intrusive_ptr<ExpressionContext> expCtx; auto statusWithCQ = CanonicalQuery::canonicalize(opCtx.get(), - std::move(qr), + std::move(findCommand), + false, expCtx, ExtensionsCallbackNoop(), MatchExpressionParser::kAllowAllSpecialFeatures); diff --git a/src/mongo/db/query/plan_executor_impl.cpp b/src/mongo/db/query/plan_executor_impl.cpp index 5ebe5455ad3..a5c13f4cf6e 100644 --- a/src/mongo/db/query/plan_executor_impl.cpp +++ b/src/mongo/db/query/plan_executor_impl.cpp @@ -150,7 +150,7 @@ PlanExecutorImpl::PlanExecutorImpl(OperationContext* opCtx, _nss = collection->ns(); } else { invariant(_cq); - _nss = _cq->getQueryRequest().nss(); + _nss = _cq->getFindCommand().getNamespaceOrUUID().nss().value_or(NamespaceString()); } uassertStatusOK(_pickBestPlan()); diff --git a/src/mongo/db/query/plan_insert_listener.cpp b/src/mongo/db/query/plan_insert_listener.cpp index b1140df2641..8591004401f 100644 --- a/src/mongo/db/query/plan_insert_listener.cpp +++ b/src/mongo/db/query/plan_insert_listener.cpp @@ -45,7 +45,7 @@ MONGO_FAIL_POINT_DEFINE(planExecutorHangWhileYieldedInWaitForInserts); } bool shouldListenForInserts(OperationContext* opCtx, CanonicalQuery* cq) { - return cq && cq->getQueryRequest().isTailableAndAwaitData() && + return cq && cq->getFindCommand().getTailable() && cq->getFindCommand().getAwaitData() && awaitDataState(opCtx).shouldWaitForInserts && opCtx->checkForInterruptNoAssert().isOK() && awaitDataState(opCtx).waitForInsertsDeadline > opCtx->getServiceContext()->getPreciseClockSource()->now(); diff --git a/src/mongo/db/query/planner_access.cpp b/src/mongo/db/query/planner_access.cpp index 2f66961a084..194110865fd 100644 --- a/src/mongo/db/query/planner_access.cpp +++ b/src/mongo/db/query/planner_access.cpp @@ -223,9 +223,9 @@ std::unique_ptr<QuerySolutionNode> QueryPlannerAccess::makeCollectionScan( params.options & QueryPlannerParams::OPLOG_SCAN_WAIT_FOR_VISIBLE; // If the hint is {$natural: +-1} this changes the direction of the collection scan. - const BSONObj& hint = query.getQueryRequest().getHint(); + const BSONObj& hint = query.getFindCommand().getHint(); if (!hint.isEmpty()) { - BSONElement natural = hint[QueryRequest::kNaturalSortField]; + BSONElement natural = hint[query_request_helper::kNaturalSortField]; if (natural) { csn->direction = natural.numberInt() >= 0 ? 1 : -1; } @@ -234,13 +234,13 @@ std::unique_ptr<QuerySolutionNode> QueryPlannerAccess::makeCollectionScan( // If the client requested a resume token and we are scanning the oplog, prepare // the collection scan to return timestamp-based tokens. Otherwise, we should // return generic RecordId-based tokens. - if (query.getQueryRequest().getRequestResumeToken()) { + if (query.getFindCommand().getRequestResumeToken()) { csn->shouldTrackLatestOplogTimestamp = query.nss().isOplog(); csn->requestResumeToken = !query.nss().isOplog(); } // Extract and assign the RecordId from the 'resumeAfter' token, if present. - const BSONObj& resumeAfterObj = query.getQueryRequest().getResumeAfter(); + const BSONObj& resumeAfterObj = query.getFindCommand().getResumeAfter(); if (!resumeAfterObj.isEmpty()) { BSONElement recordIdElem = resumeAfterObj["$recordId"]; switch (recordIdElem.type()) { @@ -1129,7 +1129,7 @@ std::unique_ptr<QuerySolutionNode> QueryPlannerAccess::buildIndexedAnd( for (size_t i = 0; i < andResult->children.size(); ++i) { andResult->children[i]->computeProperties(); if (andResult->children[i]->providedSorts().contains( - query.getQueryRequest().getSort())) { + query.getFindCommand().getSort())) { std::swap(andResult->children[i], andResult->children.back()); break; } @@ -1230,7 +1230,7 @@ std::unique_ptr<QuerySolutionNode> QueryPlannerAccess::buildIndexedOr( // If all ixscanNodes can provide the sort, shouldReverseScan is populated with which // scans to reverse. shouldReverseScan = - canProvideSortWithMergeSort(ixscanNodes, query.getQueryRequest().getSort()); + canProvideSortWithMergeSort(ixscanNodes, query.getFindCommand().getSort()); } if (!shouldReverseScan.empty()) { @@ -1244,7 +1244,7 @@ std::unique_ptr<QuerySolutionNode> QueryPlannerAccess::buildIndexedOr( } auto msn = std::make_unique<MergeSortNode>(); - msn->sort = query.getQueryRequest().getSort(); + msn->sort = query.getFindCommand().getSort(); msn->addChildren(std::move(ixscanNodes)); orResult = std::move(msn); } else { diff --git a/src/mongo/db/query/planner_analysis.cpp b/src/mongo/db/query/planner_analysis.cpp index f61f7fc3264..a0de5bd01bd 100644 --- a/src/mongo/db/query/planner_analysis.cpp +++ b/src/mongo/db/query/planner_analysis.cpp @@ -372,7 +372,7 @@ std::unique_ptr<QuerySolutionNode> addSortKeyGeneratorStageIfNeeded( const CanonicalQuery& query, bool hasSortStage, std::unique_ptr<QuerySolutionNode> solnRoot) { if (!hasSortStage && query.metadataDeps()[DocumentMetadataFields::kSortKey]) { auto keyGenNode = std::make_unique<SortKeyGeneratorNode>(); - keyGenNode->sortSpec = query.getQueryRequest().getSort(); + keyGenNode->sortSpec = query.getFindCommand().getSort(); keyGenNode->children.push_back(solnRoot.release()); return keyGenNode; } @@ -539,8 +539,8 @@ std::unique_ptr<QuerySolutionNode> tryPushdownProjectBeneathSort( bool canUseSimpleSort(const QuerySolutionNode& solnRoot, const CanonicalQuery& cq, const QueryPlannerParams& plannerParams) { - const bool splitLimitedSortEligible = cq.getQueryRequest().getNToReturn() && - !cq.getQueryRequest().isSingleBatch() && + const bool splitLimitedSortEligible = cq.getFindCommand().getNtoreturn() && + !cq.getFindCommand().getSingleBatch() && plannerParams.options & QueryPlannerParams::SPLIT_LIMITED_SORT; // The simple sort stage discards any metadata other than sort key metadata. It can only be used @@ -622,7 +622,7 @@ bool QueryPlannerAnalysis::explodeForSort(const CanonicalQuery& query, // Find explodable nodes in the subtree rooted at 'toReplace'. getExplodableNodes(toReplace, &explodableNodes); - const BSONObj& desiredSort = query.getQueryRequest().getSort(); + const BSONObj& desiredSort = query.getFindCommand().getSort(); // How many scan leaves will result from our expansion? size_t totalNumScans = 0; @@ -764,8 +764,8 @@ QuerySolutionNode* QueryPlannerAnalysis::analyzeSort(const CanonicalQuery& query bool* blockingSortOut) { *blockingSortOut = false; - const QueryRequest& qr = query.getQueryRequest(); - const BSONObj& sortObj = qr.getSort(); + const FindCommand& findCommand = query.getFindCommand(); + const BSONObj& sortObj = findCommand.getSort(); if (sortObj.isEmpty()) { return solnRoot; @@ -776,7 +776,7 @@ QuerySolutionNode* QueryPlannerAnalysis::analyzeSort(const CanonicalQuery& query // If the sort is $natural, we ignore it, assuming that the caller has detected that and // outputted a collscan to satisfy the desired order. - if (sortObj[QueryRequest::kNaturalSortField]) { + if (sortObj[query_request_helper::kNaturalSortField]) { return solnRoot; } @@ -838,19 +838,19 @@ QuerySolutionNode* QueryPlannerAnalysis::analyzeSort(const CanonicalQuery& query // When setting the limit on the sort, we need to consider both // the limit N and skip count M. The sort should return an ordered list // N + M items so that the skip stage can discard the first M results. - if (qr.getLimit()) { + if (findCommand.getLimit()) { // We have a true limit. The limit can be combined with the SORT stage. - sortNodeRaw->limit = - static_cast<size_t>(*qr.getLimit()) + static_cast<size_t>(qr.getSkip().value_or(0)); - } else if (qr.getNToReturn()) { + sortNodeRaw->limit = static_cast<size_t>(*findCommand.getLimit()) + + static_cast<size_t>(findCommand.getSkip().value_or(0)); + } else if (findCommand.getNtoreturn()) { // We have an ntoreturn specified by an OP_QUERY style find. This is used // by clients to mean both batchSize and limit. // // Overflow here would be bad and could cause a nonsense limit. Cast // skip and limit values to unsigned ints to make sure that the // sum is never stored as signed. (See SERVER-13537). - sortNodeRaw->limit = - static_cast<size_t>(*qr.getNToReturn()) + static_cast<size_t>(qr.getSkip().value_or(0)); + sortNodeRaw->limit = static_cast<size_t>(*findCommand.getNtoreturn()) + + static_cast<size_t>(findCommand.getSkip().value_or(0)); // This is a SORT with a limit. The wire protocol has a single quantity called "numToReturn" // which could mean either limit or batchSize. We have no idea what the client intended. @@ -876,7 +876,8 @@ QuerySolutionNode* QueryPlannerAnalysis::analyzeSort(const CanonicalQuery& query // // Not allowed for geo or text, because we assume elsewhere that those stages appear just // once. - if (!qr.isSingleBatch() && params.options & QueryPlannerParams::SPLIT_LIMITED_SORT && + if (!findCommand.getSingleBatch() && + params.options & QueryPlannerParams::SPLIT_LIMITED_SORT && !QueryPlannerCommon::hasNode(query.root(), MatchExpression::TEXT) && !QueryPlannerCommon::hasNode(query.root(), MatchExpression::GEO) && !QueryPlannerCommon::hasNode(query.root(), MatchExpression::GEO_NEAR)) { @@ -973,17 +974,17 @@ std::unique_ptr<QuerySolution> QueryPlannerAnalysis::analyzeDataAccess( bool hasAndHashStage = solnRoot->hasNode(STAGE_AND_HASH); soln->hasBlockingStage = hasSortStage || hasAndHashStage; - const QueryRequest& qr = query.getQueryRequest(); + const FindCommand& findCommand = query.getFindCommand(); - if (qr.getSkip()) { + if (findCommand.getSkip()) { auto skip = std::make_unique<SkipNode>(); - skip->skip = *qr.getSkip(); + skip->skip = *findCommand.getSkip(); skip->children.push_back(solnRoot.release()); solnRoot = std::move(skip); } // Project the results. - if (qr.returnKey()) { + if (findCommand.getReturnKey()) { // We don't need a projection stage if returnKey was requested since the intended behavior // is that the projection is ignored when returnKey is specified. solnRoot = std::make_unique<ReturnKeyNode>( @@ -1012,16 +1013,16 @@ std::unique_ptr<QuerySolution> QueryPlannerAnalysis::analyzeDataAccess( if (!hasSortStage) { // We don't have a sort stage. This means that, if there is a limit, we will have // to enforce it ourselves since it's not handled inside SORT. - if (qr.getLimit()) { + if (findCommand.getLimit()) { LimitNode* limit = new LimitNode(); - limit->limit = *qr.getLimit(); + limit->limit = *findCommand.getLimit(); limit->children.push_back(solnRoot.release()); solnRoot.reset(limit); - } else if (qr.getNToReturn() && qr.isSingleBatch()) { + } else if (findCommand.getNtoreturn() && findCommand.getSingleBatch()) { // We have a "legacy limit", i.e. a negative ntoreturn value from an OP_QUERY style // find. LimitNode* limit = new LimitNode(); - limit->limit = *qr.getNToReturn(); + limit->limit = *findCommand.getNtoreturn(); limit->children.push_back(solnRoot.release()); solnRoot.reset(limit); } diff --git a/src/mongo/db/query/query_planner.cpp b/src/mongo/db/query/query_planner.cpp index 70f62ecc418..fb902758eb5 100644 --- a/src/mongo/db/query/query_planner.cpp +++ b/src/mongo/db/query/query_planner.cpp @@ -341,7 +341,7 @@ std::unique_ptr<QuerySolution> buildWholeIXSoln(const IndexEntry& index, } bool providesSort(const CanonicalQuery& query, const BSONObj& kp) { - return query.getQueryRequest().getSort().isPrefixOf(kp, SimpleBSONElementComparator::kInstance); + return query.getFindCommand().getSort().isPrefixOf(kp, SimpleBSONElementComparator::kInstance); } StatusWith<std::unique_ptr<PlanCacheIndexTree>> QueryPlanner::cacheDataFromTaggedTree( @@ -609,7 +609,7 @@ StatusWith<std::vector<std::unique_ptr<QuerySolution>>> QueryPlanner::plan( } const bool canTableScan = !(params.options & QueryPlannerParams::NO_TABLE_SCAN); - const bool isTailable = query.getQueryRequest().isTailable(); + const bool isTailable = query.getFindCommand().getTailable(); // If the query requests a tailable cursor, the only solution is a collscan + filter with // tailable set on the collscan. @@ -632,16 +632,16 @@ StatusWith<std::vector<std::unique_ptr<QuerySolution>>> QueryPlanner::plan( // The hint can be {$natural: +/-1}. If this happens, output a collscan. We expect any $natural // sort to have been normalized to a $natural hint upstream. - if (!query.getQueryRequest().getHint().isEmpty()) { - const BSONObj& hintObj = query.getQueryRequest().getHint(); - if (hintObj[QueryRequest::kNaturalSortField]) { + if (!query.getFindCommand().getHint().isEmpty()) { + const BSONObj& hintObj = query.getFindCommand().getHint(); + if (hintObj[query_request_helper::kNaturalSortField]) { LOGV2_DEBUG(20969, 5, "Forcing a table scan due to hinted $natural"); if (!canTableScan) { return Status(ErrorCodes::NoQueryExecutionPlans, "hint $natural is not allowed, because 'notablescan' is enabled"); } - if (!query.getQueryRequest().getMin().isEmpty() || - !query.getQueryRequest().getMax().isEmpty()) { + if (!query.getFindCommand().getMin().isEmpty() || + !query.getFindCommand().getMax().isEmpty()) { return Status(ErrorCodes::NoQueryExecutionPlans, "min and max are incompatible with $natural"); } @@ -661,7 +661,7 @@ StatusWith<std::vector<std::unique_ptr<QuerySolution>>> QueryPlanner::plan( // requested in the query. BSONObj hintedIndex; if (!params.indexFiltersApplied) { - hintedIndex = query.getQueryRequest().getHint(); + hintedIndex = query.getFindCommand().getHint(); } // Either the list of indices passed in by the caller, or the list of indices filtered according @@ -717,16 +717,15 @@ StatusWith<std::vector<std::unique_ptr<QuerySolution>>> QueryPlanner::plan( // Deal with the .min() and .max() query options. If either exist we can only use an index // that matches the object inside. - if (!query.getQueryRequest().getMin().isEmpty() || - !query.getQueryRequest().getMax().isEmpty()) { + if (!query.getFindCommand().getMin().isEmpty() || !query.getFindCommand().getMax().isEmpty()) { if (!hintedIndexEntry) { return Status(ErrorCodes::Error(51173), "When using min()/max() a hint of which index to use must be provided"); } - BSONObj minObj = query.getQueryRequest().getMin(); - BSONObj maxObj = query.getQueryRequest().getMax(); + BSONObj minObj = query.getFindCommand().getMin(); + BSONObj maxObj = query.getFindCommand().getMax(); if ((!minObj.isEmpty() && !indexCompatibleMaxMin(minObj, query.getCollator(), *hintedIndexEntry)) || @@ -788,7 +787,7 @@ StatusWith<std::vector<std::unique_ptr<QuerySolution>>> QueryPlanner::plan( // // TEXT and GEO_NEAR are special because they require the use of a text/geo index in order // to be evaluated correctly. Stripping these "mandatory assignments" is therefore invalid. - if (query.getQueryRequest().getProj().isEmpty() && + if (query.getFindCommand().getProjection().isEmpty() && !QueryPlannerCommon::hasNode(query.root(), MatchExpression::GEO_NEAR) && !QueryPlannerCommon::hasNode(query.root(), MatchExpression::TEXT)) { QueryPlannerIXSelect::stripUnneededAssignments(query.root(), relevantIndices); diff --git a/src/mongo/db/query/query_planner_operator_test.cpp b/src/mongo/db/query/query_planner_operator_test.cpp index 2acb55d5687..017fae0a40c 100644 --- a/src/mongo/db/query/query_planner_operator_test.cpp +++ b/src/mongo/db/query/query_planner_operator_test.cpp @@ -802,7 +802,7 @@ TEST_F(QueryPlannerTest, CompoundIndexWithEqualityPredicatesProvidesSort) { // TEST_F(QueryPlannerTest, SortLimit) { - // Negative limit indicates hard limit - see query_request.cpp + // Negative limit indicates hard limit - see query_request_helper.cpp runQuerySortProjSkipNToReturn(BSONObj(), fromjson("{a: 1}"), BSONObj(), 0, -3); assertNumSolutions(1U); assertSolutionExists( diff --git a/src/mongo/db/query/query_planner_options_test.cpp b/src/mongo/db/query/query_planner_options_test.cpp index 460433db511..3ff3392fcae 100644 --- a/src/mongo/db/query/query_planner_options_test.cpp +++ b/src/mongo/db/query/query_planner_options_test.cpp @@ -828,9 +828,9 @@ TEST_F(QueryPlannerTest, CacheDataFromTaggedTreeFailsOnBadInput) { // No relevant index matching the index tag. relevantIndices.push_back(buildSimpleIndexEntry(BSON("a" << 1), "a_1")); - auto qr = std::make_unique<QueryRequest>(NamespaceString("test.collection")); - qr->setFilter(BSON("a" << 3)); - auto statusWithCQ = CanonicalQuery::canonicalize(opCtx.get(), std::move(qr)); + auto findCommand = std::make_unique<FindCommand>(NamespaceString("test.collection")); + findCommand->setFilter(BSON("a" << 3)); + auto statusWithCQ = CanonicalQuery::canonicalize(opCtx.get(), std::move(findCommand)); ASSERT_OK(statusWithCQ.getStatus()); std::unique_ptr<CanonicalQuery> scopedCq = std::move(statusWithCQ.getValue()); scopedCq->root()->setTag(new IndexTag(1)); @@ -842,9 +842,9 @@ TEST_F(QueryPlannerTest, CacheDataFromTaggedTreeFailsOnBadInput) { TEST_F(QueryPlannerTest, TagAccordingToCacheFailsOnBadInput) { const NamespaceString nss("test.collection"); - auto qr = std::make_unique<QueryRequest>(nss); - qr->setFilter(BSON("a" << 3)); - auto statusWithCQ = CanonicalQuery::canonicalize(opCtx.get(), std::move(qr)); + auto findCommand = std::make_unique<FindCommand>(nss); + findCommand->setFilter(BSON("a" << 3)); + auto statusWithCQ = CanonicalQuery::canonicalize(opCtx.get(), std::move(findCommand)); ASSERT_OK(statusWithCQ.getStatus()); std::unique_ptr<CanonicalQuery> scopedCq = std::move(statusWithCQ.getValue()); @@ -871,7 +871,7 @@ TEST_F(QueryPlannerTest, TagAccordingToCacheFailsOnBadInput) { ASSERT_OK(s); // Regenerate canonical query in order to clear tags. - auto newQR = std::make_unique<QueryRequest>(nss); + auto newQR = std::make_unique<FindCommand>(nss); newQR->setFilter(BSON("a" << 3)); statusWithCQ = CanonicalQuery::canonicalize(opCtx.get(), std::move(newQR)); ASSERT_OK(statusWithCQ.getStatus()); diff --git a/src/mongo/db/query/query_planner_test_fixture.cpp b/src/mongo/db/query/query_planner_test_fixture.cpp index 520544baedc..1a222b2e91c 100644 --- a/src/mongo/db/query/query_planner_test_fixture.cpp +++ b/src/mongo/db/query/query_planner_test_fixture.cpp @@ -321,27 +321,28 @@ void QueryPlannerTest::runQueryFull(const BSONObj& query, const BSONObj& maxObj) { clearState(); - auto qr = std::make_unique<QueryRequest>(nss); - qr->setFilter(query); - qr->setSort(sort); - qr->setProj(proj); + auto findCommand = std::make_unique<FindCommand>(nss); + findCommand->setFilter(query); + findCommand->setSort(sort); + findCommand->setProjection(proj); if (skip) { - qr->setSkip(skip); + findCommand->setSkip(skip); } if (ntoreturn) { if (ntoreturn < 0) { ASSERT_NE(ntoreturn, std::numeric_limits<long long>::min()); ntoreturn = -ntoreturn; - qr->setSingleBatchField(true); + findCommand->setSingleBatch(true); } - qr->setNToReturn(ntoreturn); + findCommand->setNtoreturn(ntoreturn); } - qr->setHint(hint); - qr->setMin(minObj); - qr->setMax(maxObj); + findCommand->setHint(hint); + findCommand->setMin(minObj); + findCommand->setMax(maxObj); auto statusWithCQ = CanonicalQuery::canonicalize(opCtx.get(), - std::move(qr), + std::move(findCommand), + false, expCtx, ExtensionsCallbackNoop(), MatchExpressionParser::kAllowAllSpecialFeatures); @@ -401,27 +402,28 @@ void QueryPlannerTest::runInvalidQueryFull(const BSONObj& query, const BSONObj& maxObj) { clearState(); - auto qr = std::make_unique<QueryRequest>(nss); - qr->setFilter(query); - qr->setSort(sort); - qr->setProj(proj); + auto findCommand = std::make_unique<FindCommand>(nss); + findCommand->setFilter(query); + findCommand->setSort(sort); + findCommand->setProjection(proj); if (skip) { - qr->setSkip(skip); + findCommand->setSkip(skip); } if (ntoreturn) { if (ntoreturn < 0) { ASSERT_NE(ntoreturn, std::numeric_limits<long long>::min()); ntoreturn = -ntoreturn; - qr->setSingleBatchField(true); + findCommand->setSingleBatch(true); } - qr->setNToReturn(ntoreturn); + findCommand->setNtoreturn(ntoreturn); } - qr->setHint(hint); - qr->setMin(minObj); - qr->setMax(maxObj); + findCommand->setHint(hint); + findCommand->setMin(minObj); + findCommand->setMax(maxObj); auto statusWithCQ = CanonicalQuery::canonicalize(opCtx.get(), - std::move(qr), + std::move(findCommand), + false, expCtx, ExtensionsCallbackNoop(), MatchExpressionParser::kAllowAllSpecialFeatures); @@ -443,12 +445,13 @@ void QueryPlannerTest::runQueryAsCommand(const BSONObj& cmdObj) { // If there is no '$db', append it. auto cmd = OpMsgRequest::fromDBAndBody(nss.db(), cmdObj).body; - std::unique_ptr<QueryRequest> qr( - QueryRequest::makeFromFindCommandForTests(cmd, isExplain, nss)); + std::unique_ptr<FindCommand> findCommand( + query_request_helper::makeFromFindCommandForTests(cmd, nss)); auto statusWithCQ = CanonicalQuery::canonicalize(opCtx.get(), - std::move(qr), + std::move(findCommand), + isExplain, expCtx, ExtensionsCallbackNoop(), MatchExpressionParser::kAllowAllSpecialFeatures); @@ -469,12 +472,13 @@ void QueryPlannerTest::runInvalidQueryAsCommand(const BSONObj& cmdObj) { // If there is no '$db', append it. auto cmd = OpMsgRequest::fromDBAndBody(nss.db(), cmdObj).body; - std::unique_ptr<QueryRequest> qr( - QueryRequest::makeFromFindCommandForTests(cmd, isExplain, nss)); + std::unique_ptr<FindCommand> findCommand( + query_request_helper::makeFromFindCommandForTests(cmd, nss)); auto statusWithCQ = CanonicalQuery::canonicalize(opCtx.get(), - std::move(qr), + std::move(findCommand), + isExplain, expCtx, ExtensionsCallbackNoop(), MatchExpressionParser::kAllowAllSpecialFeatures); diff --git a/src/mongo/db/query/query_request.h b/src/mongo/db/query/query_request.h deleted file mode 100644 index bb06302c612..00000000000 --- a/src/mongo/db/query/query_request.h +++ /dev/null @@ -1,524 +0,0 @@ -/** - * Copyright (C) 2018-present MongoDB, Inc. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the Server Side Public License, version 1, - * as published by MongoDB, Inc. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * Server Side Public License for more details. - * - * You should have received a copy of the Server Side Public License - * along with this program. If not, see - * <http://www.mongodb.com/licensing/server-side-public-license>. - * - * As a special exception, the copyright holders give permission to link the - * code of portions of this program with the OpenSSL library under certain - * conditions as described in each individual source file and distribute - * linked combinations including the program with the OpenSSL library. You - * must comply with the Server Side Public License in all respects for - * all of the code used other than as permitted herein. If you modify file(s) - * with this exception, you may extend this exception to your version of the - * file(s), but you are not obligated to do so. If you do not wish to do so, - * delete this exception statement from your version. If you delete this - * exception statement from all source files in the program, then also delete - * it in the license file. - */ - -#pragma once - -#include <boost/optional.hpp> -#include <string> - -#include "mongo/db/catalog/collection_options.h" -#include "mongo/db/jsobj.h" -#include "mongo/db/namespace_string.h" -#include "mongo/db/operation_context.h" -#include "mongo/db/pipeline/legacy_runtime_constants_gen.h" -#include "mongo/db/query/find_command_gen.h" -#include "mongo/db/query/tailable_mode.h" - -namespace mongo { - -class QueryMessage; -class Status; -template <typename T> -class StatusWith; - -/** - * Parses the QueryMessage or find command received from the user and makes the various fields - * more easily accessible. - */ -class QueryRequest { -public: - static constexpr auto kMaxTimeMSOpOnlyField = "maxTimeMSOpOnly"; - - // Field names for sorting options. - static constexpr auto kNaturalSortField = "$natural"; - - static constexpr auto kShardVersionField = "shardVersion"; - - explicit QueryRequest(NamespaceStringOrUUID nss, bool preferNssForSerialization = true); - explicit QueryRequest(FindCommand findCommand); - - /** - * Returns a non-OK status if any property of the QR has a bad value (e.g. a negative skip - * value) or if there is a bad combination of options (e.g. awaitData is illegal without - * tailable). - */ - Status validate() const; - - /** - * Parses a find command object, 'cmdObj'. Caller must indicate whether or not this lite - * parsed query is an explained query or not via 'isExplain'. Accepts a NSS with which - * to initialize the QueryRequest if there is no UUID in cmdObj. - * - * Returns a heap allocated QueryRequest on success or an error if 'cmdObj' is not well - * formed. - */ - static std::unique_ptr<QueryRequest> makeFromFindCommand(const BSONObj& cmdObj, - bool isExplain, - boost::optional<NamespaceString> nss, - bool apiStrict); - - static std::unique_ptr<QueryRequest> makeFromFindCommandForTests( - const BSONObj& cmdObj, - bool isExplain, - boost::optional<NamespaceString> nss = boost::none, - bool apiStrict = false); - - /** - * If _uuid exists for this QueryRequest, update the value of _nss. - */ - void refreshNSS(const NamespaceString& nss); - - void setNSS(const NamespaceString& nss) { - auto& nssOrUuid = _findCommand.getNamespaceOrUUID(); - nssOrUuid.setNss(nss); - } - - /** - * Converts this QR into a find command. - * The withUuid variants make a UUID-based find command instead of a namespace-based ones. - */ - BSONObj asFindCommand() const; - - /** - * Common code for UUID and namespace-based find commands. - */ - void asFindCommand(BSONObjBuilder* cmdBuilder) const; - - /** - * Converts this QR into an aggregation using $match. If this QR has options that cannot be - * satisfied by aggregation, a non-OK status is returned and 'cmdBuilder' is not modified. - */ - StatusWith<BSONObj> asAggregationCommand() const; - - /** - * Helper function to identify text search sort key - * Example: {a: {$meta: "textScore"}} - */ - static bool isTextScoreMeta(BSONElement elt); - - // Read preference is attached to commands in "wrapped" form, e.g. - // { $query: { <cmd>: ... } , <kWrappedReadPrefField>: { ... } } - // - // However, mongos internally "unwraps" the read preference and adds it as a parameter to the - // command, e.g. - // { <cmd>: ... , <kUnwrappedReadPrefField>: { <kWrappedReadPrefField>: { ... } } } - static constexpr auto kWrappedReadPrefField = "$readPreference"; - static constexpr auto kUnwrappedReadPrefField = "$queryOptions"; - - // Names of the maxTimeMS command and query option. - // Char arrays because they are used in static initialization. - static constexpr auto cmdOptionMaxTimeMS = "maxTimeMS"; - static constexpr auto queryOptionMaxTimeMS = "$maxTimeMS"; - - // Names of the $meta projection values. - static constexpr auto metaGeoNearDistance = "geoNearDistance"; - static constexpr auto metaGeoNearPoint = "geoNearPoint"; - static constexpr auto metaRecordId = "recordId"; - static constexpr auto metaSortKey = "sortKey"; - static constexpr auto metaTextScore = "textScore"; - - // Allow using disk during the find command. - static constexpr auto kAllowDiskUseField = "allowDiskUse"; - - // A constant by which 'maxTimeMSOpOnly' values are allowed to exceed the max allowed value for - // 'maxTimeMS'. This is because mongod and mongos server processes add a small amount to the - // 'maxTimeMS' value they are given before passing it on as 'maxTimeMSOpOnly', to allow for - // clock precision. - static constexpr auto kMaxTimeMSOpOnlyMaxPadding = 100LL; - - const NamespaceString& nss() const { - if (_findCommand.getNamespaceOrUUID().nss()) { - return *_findCommand.getNamespaceOrUUID().nss(); - } else { - static NamespaceString nss = NamespaceString(); - return nss; - } - } - - boost::optional<UUID> uuid() const { - return _findCommand.getNamespaceOrUUID().uuid(); - } - - const BSONObj& getFilter() const { - return _findCommand.getFilter(); - } - - void setFilter(BSONObj filter) { - _findCommand.setFilter(filter.getOwned()); - } - - const BSONObj& getProj() const { - return _findCommand.getProjection(); - } - - void setProj(BSONObj proj) { - _findCommand.setProjection(proj.getOwned()); - } - - const BSONObj& getSort() const { - return _findCommand.getSort(); - } - - void setSort(BSONObj sort) { - _findCommand.setSort(sort.getOwned()); - } - - const BSONObj& getHint() const { - return _findCommand.getHint(); - } - - void setHint(BSONObj hint) { - _findCommand.setHint(hint.getOwned()); - } - - boost::optional<BSONObj> getReadConcern() const { - return _findCommand.getReadConcern(); - } - - void setReadConcern(BSONObj readConcern) { - _findCommand.setReadConcern(readConcern.getOwned()); - } - - const BSONObj& getCollation() const { - return _findCommand.getCollation(); - } - - void setCollation(BSONObj collation) { - _findCommand.setCollation(collation.getOwned()); - } - - static constexpr auto kDefaultBatchSize = 101ll; - - boost::optional<std::int64_t> getSkip() const { - return _findCommand.getSkip(); - } - - void setSkip(boost::optional<std::int64_t> skip) { - _findCommand.setSkip(skip); - } - - boost::optional<std::int64_t> getLimit() const { - return _findCommand.getLimit(); - } - - void setLimit(boost::optional<std::int64_t> limit) { - _findCommand.setLimit(limit); - } - - boost::optional<std::int64_t> getBatchSize() const { - return _findCommand.getBatchSize(); - } - - void setBatchSize(boost::optional<std::int64_t> batchSize) { - _findCommand.setBatchSize(batchSize); - } - - boost::optional<std::int64_t> getNToReturn() const { - return _findCommand.getNtoreturn(); - } - - void setNToReturn(boost::optional<std::int64_t> ntoreturn) { - _findCommand.setNtoreturn(ntoreturn); - } - - /** - * Returns batchSize or ntoreturn value if either is set. If neither is set, - * returns boost::none. - */ - boost::optional<std::int64_t> getEffectiveBatchSize() const; - - bool isSingleBatch() const { - return _findCommand.getSingleBatch(); - } - - void setSingleBatchField(bool singleBatch) { - _findCommand.setSingleBatch(singleBatch); - } - - bool allowDiskUse() const { - return _findCommand.getAllowDiskUse(); - } - - void setAllowDiskUse(bool allowDiskUse) { - _findCommand.setAllowDiskUse(allowDiskUse); - } - - bool isExplain() const { - return _explain; - } - - void setExplain(bool explain) { - _explain = explain; - } - - const BSONObj& getUnwrappedReadPref() const { - return _findCommand.getUnwrappedReadPref(); - } - - void setUnwrappedReadPref(BSONObj unwrappedReadPref) { - _findCommand.setUnwrappedReadPref(unwrappedReadPref.getOwned()); - } - - int getMaxTimeMS() const { - return _findCommand.getMaxTimeMS() ? static_cast<int>(*_findCommand.getMaxTimeMS()) : 0; - } - - void setMaxTimeMS(int maxTimeMS) { - _findCommand.setMaxTimeMS(maxTimeMS); - } - - const BSONObj& getMin() const { - return _findCommand.getMin(); - } - - void setMin(BSONObj min) { - _findCommand.setMin(min.getOwned()); - } - - const BSONObj& getMax() const { - return _findCommand.getMax(); - } - - void setMax(BSONObj max) { - _findCommand.setMax(max.getOwned()); - } - - bool returnKey() const { - return _findCommand.getReturnKey(); - } - - void setReturnKey(bool returnKey) { - _findCommand.setReturnKey(returnKey); - } - - bool showRecordId() const { - return _findCommand.getShowRecordId(); - } - - void setShowRecordId(bool showRecordId) { - _findCommand.setShowRecordId(showRecordId); - } - - bool hasReadPref() const { - return _hasReadPref; - } - - void setHasReadPref(bool hasReadPref) { - _hasReadPref = hasReadPref; - } - - bool isTailable() const { - return _tailableMode == TailableModeEnum::kTailable || - _tailableMode == TailableModeEnum::kTailableAndAwaitData; - } - - bool isTailableAndAwaitData() const { - return _tailableMode == TailableModeEnum::kTailableAndAwaitData; - } - - void setTailableMode(TailableModeEnum tailableMode) { - _tailableMode = tailableMode; - if (_tailableMode == TailableModeEnum::kTailableAndAwaitData) { - _findCommand.setAwaitData(true); - _findCommand.setTailable(true); - } else if (_tailableMode == TailableModeEnum::kTailable) { - _findCommand.setTailable(true); - } - } - - TailableModeEnum getTailableMode() const { - return _tailableMode; - } - - void setLegacyRuntimeConstants(LegacyRuntimeConstants runtimeConstants) { - _findCommand.setLegacyRuntimeConstants(std::move(runtimeConstants)); - } - - const boost::optional<LegacyRuntimeConstants>& getLegacyRuntimeConstants() const { - return _findCommand.getLegacyRuntimeConstants(); - } - - bool getTailable() const { - return _findCommand.getTailable(); - } - - bool getAwaitData() const { - return _findCommand.getAwaitData(); - } - - void setLetParameters(BSONObj letParams) { - _findCommand.setLet(std::move(letParams)); - } - - const boost::optional<BSONObj>& getLetParameters() const { - return _findCommand.getLet(); - } - - bool isSlaveOk() const { - return _slaveOk; - } - - void setSlaveOk(bool slaveOk) { - _slaveOk = slaveOk; - } - - bool isNoCursorTimeout() const { - return _findCommand.getNoCursorTimeout(); - } - - void setNoCursorTimeout(bool noCursorTimeout) { - _findCommand.setNoCursorTimeout(noCursorTimeout); - } - - bool isExhaust() const { - return _exhaust; - } - - void setExhaust(bool exhaust) { - _exhaust = exhaust; - } - - bool isAllowPartialResults() const { - return _findCommand.getAllowPartialResults(); - } - - void setAllowPartialResults(bool allowPartialResults) { - _findCommand.setAllowPartialResults(allowPartialResults); - } - - boost::optional<std::int64_t> getReplicationTerm() const { - return _findCommand.getTerm(); - } - - void setReplicationTerm(boost::optional<std::int64_t> replicationTerm) { - _findCommand.setTerm(replicationTerm); - } - - bool isReadOnce() const { - return _findCommand.getReadOnce(); - } - - void setReadOnce(bool readOnce) { - _findCommand.setReadOnce(readOnce); - } - - void setAllowSpeculativeMajorityRead(bool allowSpeculativeMajorityRead) { - _findCommand.setAllowSpeculativeMajorityRead(allowSpeculativeMajorityRead); - } - - bool allowSpeculativeMajorityRead() const { - return _findCommand.getAllowSpeculativeMajorityRead(); - } - - bool getRequestResumeToken() const { - return _findCommand.getRequestResumeToken(); - } - - void setRequestResumeToken(bool requestResumeToken) { - _findCommand.setRequestResumeToken(requestResumeToken); - } - - const BSONObj& getResumeAfter() const { - return _findCommand.getResumeAfter(); - } - - void setResumeAfter(BSONObj resumeAfter) { - _findCommand.setResumeAfter(resumeAfter.getOwned()); - } - - /** - * Return options as a bit vector. - */ - int getOptions() const; - - // - // Old parsing code: SOON TO BE DEPRECATED. - // - - /** - * Parse the provided QueryMessage and return a heap constructed QueryRequest, which - * represents it or an error. - */ - static StatusWith<std::unique_ptr<QueryRequest>> fromLegacyQueryMessage(const QueryMessage& qm); - - /** - * Parse the provided legacy query object and parameters to construct a QueryRequest. - */ - static StatusWith<std::unique_ptr<QueryRequest>> fromLegacyQuery(NamespaceStringOrUUID nsOrUuid, - const BSONObj& queryObj, - const BSONObj& proj, - int ntoskip, - int ntoreturn, - int queryOptions); - -private: - static StatusWith<std::unique_ptr<QueryRequest>> parseFromFindCommand( - std::unique_ptr<QueryRequest> qr, const BSONObj& cmdObj, bool isExplain); - Status init(int ntoskip, - int ntoreturn, - int queryOptions, - const BSONObj& queryObj, - const BSONObj& proj, - bool fromQueryMessage); - - Status initFullQuery(const BSONObj& top); - - /** - * Updates the projection object with a $meta projection for the showRecordId option. - */ - void addShowRecordIdMetaProj(); - - /** - * Initializes options based on the value of the 'options' bit vector. - * - * This contains flags such as tailable, exhaust, and noCursorTimeout. - */ - void initFromInt(int options); - - /** - * Add the meta projection to this object if needed. - */ - void addMetaProjection(); - - // TODO SERVER-53060: This additional nesting can be avoided if we move the below fields - // (_explain, _tailableMode, etc.) into the CanonicalQuery class. - FindCommand _findCommand; - - bool _explain = false; - - // Options that can be specified in the OP_QUERY 'flags' header. - TailableModeEnum _tailableMode = TailableModeEnum::kNormal; - bool _slaveOk = false; - bool _exhaust = false; - - // Parameters used only by the legacy query request. - bool _hasReadPref = false; -}; - -} // namespace mongo diff --git a/src/mongo/db/query/query_request.cpp b/src/mongo/db/query/query_request_helper.cpp index a823d734085..ec12a47c821 100644 --- a/src/mongo/db/query/query_request.cpp +++ b/src/mongo/db/query/query_request_helper.cpp @@ -29,193 +29,361 @@ #include "mongo/platform/basic.h" -#include "mongo/db/query/query_request.h" +#include "mongo/db/query/query_request_helper.h" #include <memory> #include "mongo/base/status.h" #include "mongo/base/status_with.h" #include "mongo/bson/simple_bsonobj_comparator.h" -#include "mongo/db/catalog/collection_catalog.h" -#include "mongo/db/commands.h" #include "mongo/db/dbmessage.h" -#include "mongo/db/namespace_string.h" -#include "mongo/db/repl/read_concern_args.h" -#include "mongo/idl/command_generic_argument.h" -#include "mongo/util/assert_util.h" -#include "mongo/util/stacktrace.h" -#include "mongo/util/str.h" namespace mongo { -QueryRequest::QueryRequest(NamespaceStringOrUUID nssOrUuid, bool preferNssForSerialization) - : _findCommand(std::move(nssOrUuid)) { - if (preferNssForSerialization) { - _findCommand.getNamespaceOrUUID().preferNssForSerialization(); +namespace query_request_helper { +namespace { + +/** + * Initializes options based on the value of the 'options' bit vector. + * + * This contains flags such as tailable, exhaust, and noCursorTimeout. + */ +void initFromInt(int options, FindCommand* findCommand) { + bool tailable = (options & QueryOption_CursorTailable) != 0; + bool awaitData = (options & QueryOption_AwaitData) != 0; + if (awaitData) { + findCommand->setAwaitData(true); + } + if (tailable) { + findCommand->setTailable(true); + } + + if ((options & QueryOption_NoCursorTimeout) != 0) { + findCommand->setNoCursorTimeout(true); + } + if ((options & QueryOption_PartialResults) != 0) { + findCommand->setAllowPartialResults(true); } } -QueryRequest::QueryRequest(FindCommand findCommand) : _findCommand(std::move(findCommand)) { - _findCommand.getNamespaceOrUUID().preferNssForSerialization(); + +/** + * Updates the projection object with a $meta projection for the showRecordId option. + */ +void addShowRecordIdMetaProj(FindCommand* findCommand) { + if (findCommand->getProjection()["$recordId"]) { + // There's already some projection on $recordId. Don't overwrite it. + return; + } + + BSONObjBuilder projBob; + projBob.appendElements(findCommand->getProjection()); + BSONObj metaRecordId = BSON("$recordId" << BSON("$meta" << query_request_helper::metaRecordId)); + projBob.append(metaRecordId.firstElement()); + findCommand->setProjection(projBob.obj()); } -void QueryRequest::refreshNSS(const NamespaceString& nss) { - if (_findCommand.getNamespaceOrUUID().uuid()) { - auto& nssOrUUID = _findCommand.getNamespaceOrUUID(); - nssOrUUID.setNss(nss); +/** + * Add the meta projection to this object if needed. + */ +void addMetaProjection(FindCommand* findCommand) { + if (findCommand->getShowRecordId()) { + addShowRecordIdMetaProj(findCommand); } - invariant(_findCommand.getNamespaceOrUUID().nss()); } -// static -std::unique_ptr<QueryRequest> QueryRequest::makeFromFindCommand( - const BSONObj& cmdObj, bool isExplain, boost::optional<NamespaceString> nss, bool apiStrict) { +Status initFullQuery(const BSONObj& top, FindCommand* findCommand, bool* explain) { + BSONObjIterator i(top); - auto qr = std::make_unique<QueryRequest>( - FindCommand::parse(IDLParserErrorContext("FindCommand", apiStrict), cmdObj)); + while (i.more()) { + BSONElement e = i.next(); + StringData name = e.fieldNameStringData(); - // If there is an explicit namespace specified overwite it. - if (nss) { - qr->setNSS(*nss); - } + if (name == "$orderby" || name == "orderby") { + if (Object == e.type()) { + findCommand->setSort(e.embeddedObject().getOwned()); + } else if (Array == e.type()) { + findCommand->setSort(e.embeddedObject()); - qr->_tailableMode = - uassertStatusOK(tailableModeFromBools(qr->getTailable(), qr->getAwaitData())); + // TODO: Is this ever used? I don't think so. + // Quote: + // This is for languages whose "objects" are not well ordered (JSON is well + // ordered). + // [ { a : ... } , { b : ... } ] -> { a : ..., b : ... } + // note: this is slow, but that is ok as order will have very few pieces + BSONObjBuilder b; + char p[2] = "0"; - qr->_explain = isExplain; - qr->addMetaProjection(); + while (1) { + BSONObj j = findCommand->getSort().getObjectField(p); + if (j.isEmpty()) { + break; + } + BSONElement e = j.firstElement(); + if (e.eoo()) { + return Status(ErrorCodes::BadValue, "bad order array"); + } + if (!e.isNumber()) { + return Status(ErrorCodes::BadValue, "bad order array [2]"); + } + b.append(e); + (*p)++; + if (!(*p <= '9')) { + return Status(ErrorCodes::BadValue, "too many ordering elements"); + } + } - if (qr->getSkip() && *qr->getSkip() == 0) { - qr->setSkip(boost::none); - } - if (qr->getLimit() && *qr->getLimit() == 0) { - qr->setLimit(boost::none); + findCommand->setSort(b.obj()); + } else { + return Status(ErrorCodes::BadValue, "sort must be object or array"); + } + } else if (name.startsWith("$")) { + name = name.substr(1); // chop first char + if (name == "explain") { + // Won't throw. + *explain = e.trueValue(); + } else if (name == "min") { + if (!e.isABSONObj()) { + return Status(ErrorCodes::BadValue, "$min must be a BSONObj"); + } + findCommand->setMin(e.embeddedObject().getOwned()); + } else if (name == "max") { + if (!e.isABSONObj()) { + return Status(ErrorCodes::BadValue, "$max must be a BSONObj"); + } + findCommand->setMax(e.embeddedObject().getOwned()); + } else if (name == "hint") { + if (e.isABSONObj()) { + findCommand->setHint(e.embeddedObject().getOwned()); + } else if (String == e.type()) { + findCommand->setHint(e.wrap()); + } else { + return Status(ErrorCodes::BadValue, + "$hint must be either a string or nested object"); + } + } else if (name == "returnKey") { + // Won't throw. + if (e.trueValue()) { + findCommand->setReturnKey(true); + } + } else if (name == "showDiskLoc") { + // Won't throw. + if (e.trueValue()) { + findCommand->setShowRecordId(true); + addShowRecordIdMetaProj(findCommand); + } + } else if (name == "maxTimeMS") { + StatusWith<int> maxTimeMS = parseMaxTimeMS(e); + if (!maxTimeMS.isOK()) { + return maxTimeMS.getStatus(); + } + findCommand->setMaxTimeMS(maxTimeMS.getValue()); + } + } } - uassertStatusOK(qr->validate()); - return qr; -} -std::unique_ptr<QueryRequest> QueryRequest::makeFromFindCommandForTests( - const BSONObj& cmdObj, bool isExplain, boost::optional<NamespaceString> nss, bool apiStrict) { - return makeFromFindCommand(cmdObj, isExplain, nss, apiStrict); + return Status::OK(); } -BSONObj QueryRequest::asFindCommand() const { - BSONObjBuilder bob; - asFindCommand(&bob); - return bob.obj(); -} +Status initFindCommand(int ntoskip, + int ntoreturn, + int queryOptions, + const BSONObj& queryObj, + const BSONObj& proj, + bool fromQueryMessage, + FindCommand* findCommand, + bool* explain) { + if (!proj.isEmpty()) { + findCommand->setProjection(proj.getOwned()); + } + if (ntoskip) { + findCommand->setSkip(ntoskip); + } -void QueryRequest::asFindCommand(BSONObjBuilder* cmdBuilder) const { - _findCommand.serialize(BSONObj(), cmdBuilder); -} + if (ntoreturn) { + if (ntoreturn < 0) { + if (ntoreturn == std::numeric_limits<int>::min()) { + // ntoreturn is negative but can't be negated. + return Status(ErrorCodes::BadValue, "bad ntoreturn value in query"); + } + findCommand->setNtoreturn(-ntoreturn); + findCommand->setSingleBatch(true); + } else { + findCommand->setNtoreturn(ntoreturn); + } + } -void QueryRequest::addShowRecordIdMetaProj() { - if (getProj()["$recordId"]) { - // There's already some projection on $recordId. Don't overwrite it. - return; + // An ntoreturn of 1 is special because it also means to return at most one batch. + if (findCommand->getNtoreturn().value_or(0) == 1) { + findCommand->setSingleBatch(true); } - BSONObjBuilder projBob; - projBob.appendElements(getProj()); - BSONObj metaRecordId = BSON("$recordId" << BSON("$meta" << QueryRequest::metaRecordId)); - projBob.append(metaRecordId.firstElement()); - setProj(projBob.obj()); + // Initialize flags passed as 'queryOptions' bit vector. + initFromInt(queryOptions, findCommand); + + if (fromQueryMessage) { + BSONElement queryField = queryObj["query"]; + if (!queryField.isABSONObj()) { + queryField = queryObj["$query"]; + } + if (queryField.isABSONObj()) { + findCommand->setFilter(queryField.embeddedObject().getOwned()); + Status status = initFullQuery(queryObj, findCommand, explain); + if (!status.isOK()) { + return status; + } + } else { + findCommand->setFilter(queryObj.getOwned()); + } + // It's not possible to specify readConcern in a legacy query message, so initialize it to + // an empty readConcern object, ie. equivalent to `readConcern: {}`. This ensures that + // mongos passes this empty readConcern to shards. + findCommand->setReadConcern(BSONObj()); + } else { + // This is the debugging code path. + findCommand->setFilter(queryObj.getOwned()); + } + + return validateFindCommand(*findCommand); } -Status QueryRequest::validate() const { +} // namespace + +Status validateFindCommand(const FindCommand& findCommand) { // Min and Max objects must have the same fields. - if (!getMin().isEmpty() && !getMax().isEmpty()) { - if (!getMin().isFieldNamePrefixOf(getMax()) || (getMin().nFields() != getMax().nFields())) { + if (!findCommand.getMin().isEmpty() && !findCommand.getMax().isEmpty()) { + if (!findCommand.getMin().isFieldNamePrefixOf(findCommand.getMax()) || + (findCommand.getMin().nFields() != findCommand.getMax().nFields())) { return Status(ErrorCodes::Error(51176), "min and max must have the same field names"); } } - if ((getLimit() || getBatchSize()) && getNToReturn()) { + if ((findCommand.getLimit() || findCommand.getBatchSize()) && findCommand.getNtoreturn()) { return Status(ErrorCodes::BadValue, "'limit' or 'batchSize' fields can not be set with 'ntoreturn' field."); } // TODO SERVER-53060: When legacy query request is seperated, these validations can be moved to // IDL. - if (getSkip() && *getSkip() < 0) { + if (findCommand.getSkip() && *findCommand.getSkip() < 0) { return Status(ErrorCodes::BadValue, - str::stream() - << "Skip value must be non-negative, but received: " << *getSkip()); + str::stream() << "Skip value must be non-negative, but received: " + << *findCommand.getSkip()); } - if (getLimit() && *getLimit() < 0) { + if (findCommand.getLimit() && *findCommand.getLimit() < 0) { return Status(ErrorCodes::BadValue, - str::stream() - << "Limit value must be non-negative, but received: " << *getLimit()); + str::stream() << "Limit value must be non-negative, but received: " + << *findCommand.getLimit()); } - if (getBatchSize() && *getBatchSize() < 0) { + if (findCommand.getBatchSize() && *findCommand.getBatchSize() < 0) { return Status(ErrorCodes::BadValue, str::stream() << "BatchSize value must be non-negative, but received: " - << *getBatchSize()); + << *findCommand.getBatchSize()); } - if (getNToReturn() && *getNToReturn() < 0) { + if (findCommand.getNtoreturn() && *findCommand.getNtoreturn() < 0) { return Status(ErrorCodes::BadValue, str::stream() << "NToReturn value must be non-negative, but received: " - << *getNToReturn()); + << *findCommand.getNtoreturn()); } - if (getMaxTimeMS() < 0) { + int maxTimeMS = findCommand.getMaxTimeMS() ? static_cast<int>(*findCommand.getMaxTimeMS()) : 0; + if (maxTimeMS < 0) { return Status(ErrorCodes::BadValue, - str::stream() << "MaxTimeMS value must be non-negative, but received: " - << getMaxTimeMS()); + str::stream() + << "MaxTimeMS value must be non-negative, but received: " << maxTimeMS); } - if (_tailableMode != TailableModeEnum::kNormal) { + if (query_request_helper::getTailableMode(findCommand) != TailableModeEnum::kNormal) { // Tailable cursors cannot have any sort other than {$natural: 1}. - const BSONObj expectedSort = BSON(kNaturalSortField << 1); - if (!getSort().isEmpty() && - SimpleBSONObjComparator::kInstance.evaluate(getSort() != expectedSort)) { + const BSONObj expectedSort = BSON(query_request_helper::kNaturalSortField << 1); + if (!findCommand.getSort().isEmpty() && + SimpleBSONObjComparator::kInstance.evaluate(findCommand.getSort() != expectedSort)) { return Status(ErrorCodes::BadValue, "cannot use tailable option with a sort other than {$natural: 1}"); } // Cannot indicate that you want a 'singleBatch' if the cursor is tailable. - if (isSingleBatch()) { + if (findCommand.getSingleBatch()) { return Status(ErrorCodes::BadValue, "cannot use tailable option with the 'singleBatch' option"); } } - if (getRequestResumeToken()) { - if (SimpleBSONObjComparator::kInstance.evaluate(getHint() != - BSON(kNaturalSortField << 1))) { + if (findCommand.getRequestResumeToken()) { + if (SimpleBSONObjComparator::kInstance.evaluate( + findCommand.getHint() != BSON(query_request_helper::kNaturalSortField << 1))) { return Status(ErrorCodes::BadValue, "hint must be {$natural:1} if 'requestResumeToken' is enabled"); } - if (!getSort().isEmpty() && - SimpleBSONObjComparator::kInstance.evaluate(getSort() != - BSON(kNaturalSortField << 1))) { + if (!findCommand.getSort().isEmpty() && + SimpleBSONObjComparator::kInstance.evaluate( + findCommand.getSort() != BSON(query_request_helper::kNaturalSortField << 1))) { return Status(ErrorCodes::BadValue, "sort must be unset or {$natural:1} if 'requestResumeToken' is enabled"); } - if (!getResumeAfter().isEmpty()) { - if (getResumeAfter().nFields() != 1 || - (getResumeAfter()["$recordId"].type() != BSONType::NumberLong && - getResumeAfter()["$recordId"].type() != BSONType::jstOID && - getResumeAfter()["$recordId"].type() != BSONType::jstNULL)) { + if (!findCommand.getResumeAfter().isEmpty()) { + if (findCommand.getResumeAfter().nFields() != 1 || + (findCommand.getResumeAfter()["$recordId"].type() != BSONType::NumberLong && + findCommand.getResumeAfter()["$recordId"].type() != BSONType::jstOID && + findCommand.getResumeAfter()["$recordId"].type() != BSONType::jstNULL)) { return Status( ErrorCodes::BadValue, "Malformed resume token: the '_resumeAfter' object must contain" " exactly one field named '$recordId', of type NumberLong, jstOID or jstNULL."); } } - } else if (!getResumeAfter().isEmpty()) { + } else if (!findCommand.getResumeAfter().isEmpty()) { return Status(ErrorCodes::BadValue, "'requestResumeToken' must be true if 'resumeAfter' is" " specified"); } + return Status::OK(); } -// static -bool QueryRequest::isTextScoreMeta(BSONElement elt) { +void refreshNSS(const NamespaceString& nss, FindCommand* findCommand) { + if (findCommand->getNamespaceOrUUID().uuid()) { + auto& nssOrUUID = findCommand->getNamespaceOrUUID(); + nssOrUUID.setNss(nss); + } + invariant(findCommand->getNamespaceOrUUID().nss()); +} + +std::unique_ptr<FindCommand> makeFromFindCommand(const BSONObj& cmdObj, + boost::optional<NamespaceString> nss, + bool apiStrict) { + + auto findCommand = std::make_unique<FindCommand>( + FindCommand::parse(IDLParserErrorContext("FindCommand", apiStrict), cmdObj)); + + // If there is an explicit namespace specified overwite it. + if (nss) { + auto& nssOrUuid = findCommand->getNamespaceOrUUID(); + nssOrUuid.setNss(*nss); + } + + addMetaProjection(findCommand.get()); + + if (findCommand->getSkip() && *findCommand->getSkip() == 0) { + findCommand->setSkip(boost::none); + } + if (findCommand->getLimit() && *findCommand->getLimit() == 0) { + findCommand->setLimit(boost::none); + } + uassertStatusOK(validateFindCommand(*findCommand)); + + return findCommand; +} + +std::unique_ptr<FindCommand> makeFromFindCommandForTests(const BSONObj& cmdObj, + boost::optional<NamespaceString> nss, + bool apiStrict) { + return makeFromFindCommand(cmdObj, nss, apiStrict); +} + +bool isTextScoreMeta(BSONElement elt) { // elt must be foo: {$meta: "textScore"} if (mongo::Object != elt.type()) { return false; @@ -233,7 +401,7 @@ bool QueryRequest::isTextScoreMeta(BSONElement elt) { if (mongo::String != metaElt.type()) { return false; } - if (StringData{metaElt.valuestr()} != QueryRequest::metaTextScore) { + if (StringData{metaElt.valuestr()} != metaTextScore) { return false; } // must have exactly 1 element @@ -243,323 +411,134 @@ bool QueryRequest::isTextScoreMeta(BSONElement elt) { return true; } +void setTailableMode(TailableModeEnum tailableMode, FindCommand* findCommand) { + if (tailableMode == TailableModeEnum::kTailableAndAwaitData) { + findCommand->setAwaitData(true); + findCommand->setTailable(true); + } else if (tailableMode == TailableModeEnum::kTailable) { + findCommand->setTailable(true); + } +} + +TailableModeEnum getTailableMode(const FindCommand& findCommand) { + return uassertStatusOK( + tailableModeFromBools(findCommand.getTailable(), findCommand.getAwaitData())); +} + // // Old QueryRequest parsing code: SOON TO BE DEPRECATED. // -// static -StatusWith<std::unique_ptr<QueryRequest>> QueryRequest::fromLegacyQueryMessage( - const QueryMessage& qm) { - auto qr = std::make_unique<QueryRequest>(NamespaceString(qm.ns)); - - Status status = qr->init(qm.ntoskip, qm.ntoreturn, qm.queryOptions, qm.query, qm.fields, true); +StatusWith<std::unique_ptr<FindCommand>> fromLegacyQueryMessage(const QueryMessage& qm, + bool* explain) { + auto findCommand = std::make_unique<FindCommand>(NamespaceString(qm.ns)); + + Status status = initFindCommand(qm.ntoskip, + qm.ntoreturn, + qm.queryOptions, + qm.query, + qm.fields, + true, + findCommand.get(), + explain); if (!status.isOK()) { return status; } - return std::move(qr); + return std::move(findCommand); } -StatusWith<std::unique_ptr<QueryRequest>> QueryRequest::fromLegacyQuery( - NamespaceStringOrUUID nsOrUuid, - const BSONObj& queryObj, - const BSONObj& proj, - int ntoskip, - int ntoreturn, - int queryOptions) { - // Legacy command should prefer to serialize with UUID. - auto qr = std::make_unique<QueryRequest>(std::move(nsOrUuid), false); - - Status status = qr->init(ntoskip, ntoreturn, queryOptions, queryObj, proj, true); +StatusWith<std::unique_ptr<FindCommand>> fromLegacyQuery(NamespaceStringOrUUID nssOrUuid, + const BSONObj& queryObj, + const BSONObj& proj, + int ntoskip, + int ntoreturn, + int queryOptions, + bool* explain) { + auto findCommand = std::make_unique<FindCommand>(std::move(nssOrUuid)); + + Status status = initFindCommand( + ntoskip, ntoreturn, queryOptions, queryObj, proj, true, findCommand.get(), explain); if (!status.isOK()) { return status; } - return std::move(qr); -} - -Status QueryRequest::init(int ntoskip, - int ntoreturn, - int queryOptions, - const BSONObj& queryObj, - const BSONObj& proj, - bool fromQueryMessage) { - if (!proj.isEmpty()) { - _findCommand.setProjection(proj.getOwned()); - } - if (ntoskip) { - _findCommand.setSkip(ntoskip); - } - - if (ntoreturn) { - if (ntoreturn < 0) { - if (ntoreturn == std::numeric_limits<int>::min()) { - // ntoreturn is negative but can't be negated. - return Status(ErrorCodes::BadValue, "bad ntoreturn value in query"); - } - _findCommand.setNtoreturn(-ntoreturn); - setSingleBatchField(true); - } else { - _findCommand.setNtoreturn(ntoreturn); - } - } - - // An ntoreturn of 1 is special because it also means to return at most one batch. - if (getNToReturn().value_or(0) == 1) { - setSingleBatchField(true); - } - - // Initialize flags passed as 'queryOptions' bit vector. - initFromInt(queryOptions); - - if (fromQueryMessage) { - BSONElement queryField = queryObj["query"]; - if (!queryField.isABSONObj()) { - queryField = queryObj["$query"]; - } - if (queryField.isABSONObj()) { - _findCommand.setFilter(queryField.embeddedObject().getOwned()); - Status status = initFullQuery(queryObj); - if (!status.isOK()) { - return status; - } - } else { - _findCommand.setFilter(queryObj.getOwned()); - } - // It's not possible to specify readConcern in a legacy query message, so initialize it to - // an empty readConcern object, ie. equivalent to `readConcern: {}`. This ensures that - // mongos passes this empty readConcern to shards. - setReadConcern(BSONObj()); - } else { - // This is the debugging code path. - _findCommand.setFilter(queryObj.getOwned()); - } - - _hasReadPref = queryObj.hasField("$readPreference"); - - return validate(); + return std::move(findCommand); } -Status QueryRequest::initFullQuery(const BSONObj& top) { - BSONObjIterator i(top); - - while (i.more()) { - BSONElement e = i.next(); - StringData name = e.fieldNameStringData(); - - if (name == "$orderby" || name == "orderby") { - if (Object == e.type()) { - setSort(e.embeddedObject().getOwned()); - } else if (Array == e.type()) { - setSort(e.embeddedObject()); - - // TODO: Is this ever used? I don't think so. - // Quote: - // This is for languages whose "objects" are not well ordered (JSON is well - // ordered). - // [ { a : ... } , { b : ... } ] -> { a : ..., b : ... } - // note: this is slow, but that is ok as order will have very few pieces - BSONObjBuilder b; - char p[2] = "0"; - - while (1) { - BSONObj j = getSort().getObjectField(p); - if (j.isEmpty()) { - break; - } - BSONElement e = j.firstElement(); - if (e.eoo()) { - return Status(ErrorCodes::BadValue, "bad order array"); - } - if (!e.isNumber()) { - return Status(ErrorCodes::BadValue, "bad order array [2]"); - } - b.append(e); - (*p)++; - if (!(*p <= '9')) { - return Status(ErrorCodes::BadValue, "too many ordering elements"); - } - } - - setSort(b.obj()); - } else { - return Status(ErrorCodes::BadValue, "sort must be object or array"); - } - } else if (name.startsWith("$")) { - name = name.substr(1); // chop first char - if (name == "explain") { - // Won't throw. - _explain = e.trueValue(); - } else if (name == "min") { - if (!e.isABSONObj()) { - return Status(ErrorCodes::BadValue, "$min must be a BSONObj"); - } - setMin(e.embeddedObject().getOwned()); - } else if (name == "max") { - if (!e.isABSONObj()) { - return Status(ErrorCodes::BadValue, "$max must be a BSONObj"); - } - setMax(e.embeddedObject().getOwned()); - } else if (name == "hint") { - if (e.isABSONObj()) { - setHint(e.embeddedObject().getOwned()); - } else if (String == e.type()) { - setHint(e.wrap()); - } else { - return Status(ErrorCodes::BadValue, - "$hint must be either a string or nested object"); - } - } else if (name == "returnKey") { - // Won't throw. - if (e.trueValue()) { - setReturnKey(true); - } - } else if (name == "showDiskLoc") { - // Won't throw. - if (e.trueValue()) { - setShowRecordId(true); - addShowRecordIdMetaProj(); - } - } else if (name == "maxTimeMS") { - StatusWith<int> maxTimeMS = parseMaxTimeMS(e); - if (!maxTimeMS.isOK()) { - return maxTimeMS.getStatus(); - } - setMaxTimeMS(maxTimeMS.getValue()); - } - } - } - - return Status::OK(); -} - -int QueryRequest::getOptions() const { - int options = 0; - if (_tailableMode == TailableModeEnum::kTailable) { - options |= QueryOption_CursorTailable; - } else if (_tailableMode == TailableModeEnum::kTailableAndAwaitData) { - options |= QueryOption_CursorTailable; - options |= QueryOption_AwaitData; - } - if (_slaveOk) { - options |= QueryOption_SecondaryOk; - } - if (isNoCursorTimeout()) { - options |= QueryOption_NoCursorTimeout; - } - if (_exhaust) { - options |= QueryOption_Exhaust; - } - if (isAllowPartialResults()) { - options |= QueryOption_PartialResults; - } - return options; -} - -void QueryRequest::initFromInt(int options) { - bool tailable = (options & QueryOption_CursorTailable) != 0; - bool awaitData = (options & QueryOption_AwaitData) != 0; - if (awaitData) { - _findCommand.setAwaitData(true); - } - if (tailable) { - _findCommand.setTailable(true); - } - _tailableMode = uassertStatusOK(tailableModeFromBools(tailable, awaitData)); - _slaveOk = (options & QueryOption_SecondaryOk) != 0; - _exhaust = (options & QueryOption_Exhaust) != 0; - - if ((options & QueryOption_NoCursorTimeout) != 0) { - setNoCursorTimeout(true); - } - if ((options & QueryOption_PartialResults) != 0) { - setAllowPartialResults(true); - } -} - -void QueryRequest::addMetaProjection() { - if (showRecordId()) { - addShowRecordIdMetaProj(); - } -} - -boost::optional<int64_t> QueryRequest::getEffectiveBatchSize() const { - return getBatchSize() ? getBatchSize() : getNToReturn(); -} - -StatusWith<BSONObj> QueryRequest::asAggregationCommand() const { +StatusWith<BSONObj> asAggregationCommand(const FindCommand& findCommand) { BSONObjBuilder aggregationBuilder; // First, check if this query has options that are not supported in aggregation. - if (!getMin().isEmpty()) { + if (!findCommand.getMin().isEmpty()) { return {ErrorCodes::InvalidPipelineOperator, str::stream() << "Option " << FindCommand::kMinFieldName << " not supported in aggregation."}; } - if (!getMax().isEmpty()) { + if (!findCommand.getMax().isEmpty()) { return {ErrorCodes::InvalidPipelineOperator, str::stream() << "Option " << FindCommand::kMaxFieldName << " not supported in aggregation."}; } - if (returnKey()) { + if (findCommand.getReturnKey()) { return {ErrorCodes::InvalidPipelineOperator, str::stream() << "Option " << FindCommand::kReturnKeyFieldName << " not supported in aggregation."}; } - if (showRecordId()) { + if (findCommand.getShowRecordId()) { return {ErrorCodes::InvalidPipelineOperator, str::stream() << "Option " << FindCommand::kShowRecordIdFieldName << " not supported in aggregation."}; } - if (isTailable()) { + if (findCommand.getTailable()) { return {ErrorCodes::InvalidPipelineOperator, "Tailable cursors are not supported in aggregation."}; } - if (isNoCursorTimeout()) { + if (findCommand.getNoCursorTimeout()) { return {ErrorCodes::InvalidPipelineOperator, str::stream() << "Option " << FindCommand::kNoCursorTimeoutFieldName << " not supported in aggregation."}; } - if (isAllowPartialResults()) { + if (findCommand.getAllowPartialResults()) { return {ErrorCodes::InvalidPipelineOperator, str::stream() << "Option " << FindCommand::kAllowPartialResultsFieldName << " not supported in aggregation."}; } - if (getNToReturn()) { + if (findCommand.getNtoreturn()) { return {ErrorCodes::BadValue, str::stream() << "Cannot convert to an aggregation if ntoreturn is set."}; } - if (getSort()[kNaturalSortField]) { + if (findCommand.getSort()[query_request_helper::kNaturalSortField]) { return {ErrorCodes::InvalidPipelineOperator, - str::stream() << "Sort option " << kNaturalSortField + str::stream() << "Sort option " << query_request_helper::kNaturalSortField << " not supported in aggregation."}; } // The aggregation command normally does not support the 'singleBatch' option, but we make a // special exception if 'limit' is set to 1. - if (isSingleBatch() && getLimit().value_or(0) != 1LL) { + if (findCommand.getSingleBatch() && findCommand.getLimit().value_or(0) != 1LL) { return {ErrorCodes::InvalidPipelineOperator, str::stream() << "Option " << FindCommand::kSingleBatchFieldName << " not supported in aggregation."}; } - if (isReadOnce()) { + if (findCommand.getReadOnce()) { return {ErrorCodes::InvalidPipelineOperator, str::stream() << "Option " << FindCommand::kReadOnceFieldName << " not supported in aggregation."}; } - if (allowSpeculativeMajorityRead()) { + if (findCommand.getAllowSpeculativeMajorityRead()) { return {ErrorCodes::InvalidPipelineOperator, str::stream() << "Option " << FindCommand::kAllowSpeculativeMajorityReadFieldName << " not supported in aggregation."}; } - if (getRequestResumeToken()) { + if (findCommand.getRequestResumeToken()) { return {ErrorCodes::InvalidPipelineOperator, str::stream() << "Option " << FindCommand::kRequestResumeTokenFieldName << " not supported in aggregation."}; } - if (!getResumeAfter().isEmpty()) { + if (!findCommand.getResumeAfter().isEmpty()) { return {ErrorCodes::InvalidPipelineOperator, str::stream() << "Option " << FindCommand::kResumeAfterFieldName << " not supported in aggregation."}; @@ -567,72 +546,77 @@ StatusWith<BSONObj> QueryRequest::asAggregationCommand() const { // Now that we've successfully validated this QR, begin building the aggregation command. aggregationBuilder.append("aggregate", - _findCommand.getNamespaceOrUUID().nss() - ? _findCommand.getNamespaceOrUUID().nss()->coll() + findCommand.getNamespaceOrUUID().nss() + ? findCommand.getNamespaceOrUUID().nss()->coll() : ""); // Construct an aggregation pipeline that finds the equivalent documents to this query request. BSONArrayBuilder pipelineBuilder(aggregationBuilder.subarrayStart("pipeline")); - if (!getFilter().isEmpty()) { + if (!findCommand.getFilter().isEmpty()) { BSONObjBuilder matchBuilder(pipelineBuilder.subobjStart()); - matchBuilder.append("$match", getFilter()); + matchBuilder.append("$match", findCommand.getFilter()); matchBuilder.doneFast(); } - if (!getSort().isEmpty()) { + if (!findCommand.getSort().isEmpty()) { BSONObjBuilder sortBuilder(pipelineBuilder.subobjStart()); - sortBuilder.append("$sort", getSort()); + sortBuilder.append("$sort", findCommand.getSort()); sortBuilder.doneFast(); } - if (getSkip()) { + if (findCommand.getSkip()) { BSONObjBuilder skipBuilder(pipelineBuilder.subobjStart()); - skipBuilder.append("$skip", *getSkip()); + skipBuilder.append("$skip", *findCommand.getSkip()); skipBuilder.doneFast(); } - if (getLimit()) { + if (findCommand.getLimit()) { BSONObjBuilder limitBuilder(pipelineBuilder.subobjStart()); - limitBuilder.append("$limit", *getLimit()); + limitBuilder.append("$limit", *findCommand.getLimit()); limitBuilder.doneFast(); } - if (!getProj().isEmpty()) { + if (!findCommand.getProjection().isEmpty()) { BSONObjBuilder projectBuilder(pipelineBuilder.subobjStart()); - projectBuilder.append("$project", getProj()); + projectBuilder.append("$project", findCommand.getProjection()); projectBuilder.doneFast(); } pipelineBuilder.doneFast(); // The aggregation 'cursor' option is always set, regardless of the presence of batchSize. BSONObjBuilder batchSizeBuilder(aggregationBuilder.subobjStart("cursor")); - if (getBatchSize()) { - batchSizeBuilder.append(FindCommand::kBatchSizeFieldName, *getBatchSize()); + if (findCommand.getBatchSize()) { + batchSizeBuilder.append(FindCommand::kBatchSizeFieldName, *findCommand.getBatchSize()); } batchSizeBuilder.doneFast(); // Other options. - aggregationBuilder.append("collation", getCollation()); - if (getMaxTimeMS() > 0) { - aggregationBuilder.append(cmdOptionMaxTimeMS, getMaxTimeMS()); + aggregationBuilder.append("collation", findCommand.getCollation()); + int maxTimeMS = findCommand.getMaxTimeMS() ? static_cast<int>(*findCommand.getMaxTimeMS()) : 0; + if (maxTimeMS > 0) { + aggregationBuilder.append(cmdOptionMaxTimeMS, maxTimeMS); } - if (!getHint().isEmpty()) { - aggregationBuilder.append(FindCommand::kHintFieldName, getHint()); + if (!findCommand.getHint().isEmpty()) { + aggregationBuilder.append(FindCommand::kHintFieldName, findCommand.getHint()); } - if (getReadConcern()) { - aggregationBuilder.append("readConcern", *getReadConcern()); + if (findCommand.getReadConcern()) { + aggregationBuilder.append("readConcern", *findCommand.getReadConcern()); } - if (!getUnwrappedReadPref().isEmpty()) { - aggregationBuilder.append(FindCommand::kUnwrappedReadPrefFieldName, getUnwrappedReadPref()); + if (!findCommand.getUnwrappedReadPref().isEmpty()) { + aggregationBuilder.append(FindCommand::kUnwrappedReadPrefFieldName, + findCommand.getUnwrappedReadPref()); } - if (allowDiskUse()) { - aggregationBuilder.append(FindCommand::kAllowDiskUseFieldName, allowDiskUse()); + if (findCommand.getAllowDiskUse()) { + aggregationBuilder.append(FindCommand::kAllowDiskUseFieldName, + static_cast<bool>(findCommand.getAllowDiskUse())); } - if (getLegacyRuntimeConstants()) { + if (findCommand.getLegacyRuntimeConstants()) { BSONObjBuilder rtcBuilder( aggregationBuilder.subobjStart(FindCommand::kLegacyRuntimeConstantsFieldName)); - getLegacyRuntimeConstants()->serialize(&rtcBuilder); + findCommand.getLegacyRuntimeConstants()->serialize(&rtcBuilder); rtcBuilder.doneFast(); } - if (getLetParameters()) { - aggregationBuilder.append(FindCommand::kLetFieldName, *getLetParameters()); + if (findCommand.getLet()) { + aggregationBuilder.append(FindCommand::kLetFieldName, *findCommand.getLet()); } return StatusWith<BSONObj>(aggregationBuilder.obj()); } + +} // namespace query_request_helper } // namespace mongo diff --git a/src/mongo/db/query/query_request_helper.h b/src/mongo/db/query/query_request_helper.h new file mode 100644 index 00000000000..00497369379 --- /dev/null +++ b/src/mongo/db/query/query_request_helper.h @@ -0,0 +1,166 @@ +/** + * Copyright (C) 2018-present MongoDB, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the Server Side Public License, version 1, + * as published by MongoDB, Inc. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * Server Side Public License for more details. + * + * You should have received a copy of the Server Side Public License + * along with this program. If not, see + * <http://www.mongodb.com/licensing/server-side-public-license>. + * + * As a special exception, the copyright holders give permission to link the + * code of portions of this program with the OpenSSL library under certain + * conditions as described in each individual source file and distribute + * linked combinations including the program with the OpenSSL library. You + * must comply with the Server Side Public License in all respects for + * all of the code used other than as permitted herein. If you modify file(s) + * with this exception, you may extend this exception to your version of the + * file(s), but you are not obligated to do so. If you do not wish to do so, + * delete this exception statement from your version. If you delete this + * exception statement from all source files in the program, then also delete + * it in the license file. + */ + +#pragma once + +#include <boost/optional.hpp> +#include <string> + +#include "mongo/db/catalog/collection_options.h" +#include "mongo/db/jsobj.h" +#include "mongo/db/operation_context.h" +#include "mongo/db/query/find_command_gen.h" +#include "mongo/db/query/tailable_mode.h" + +namespace mongo { + +class QueryMessage; +class Status; +template <typename T> +class StatusWith; + +/** + * Parses the QueryMessage or find command received from the user and makes the various fields + * more easily accessible. + */ +namespace query_request_helper { + +static constexpr auto kMaxTimeMSOpOnlyField = "maxTimeMSOpOnly"; + +// Field names for sorting options. +static constexpr auto kNaturalSortField = "$natural"; + +static constexpr auto kShardVersionField = "shardVersion"; + +/** + * Returns a non-OK status if any property of the QR has a bad value (e.g. a negative skip + * value) or if there is a bad combination of options (e.g. awaitData is illegal without + * tailable). + */ +Status validateFindCommand(const FindCommand& findCommand); + +/** + * Parses a find command object, 'cmdObj'. Caller must indicate whether or not this lite + * parsed query is an explained query or not via 'isExplain'. Accepts a NSS with which + * to initialize the FindCommand if there is no UUID in cmdObj. + * + * Returns a heap allocated FindCommand on success or an error if 'cmdObj' is not well + * formed. + */ +std::unique_ptr<FindCommand> makeFromFindCommand(const BSONObj& cmdObj, + boost::optional<NamespaceString> nss, + bool apiStrict); + +std::unique_ptr<FindCommand> makeFromFindCommandForTests( + const BSONObj& cmdObj, + boost::optional<NamespaceString> nss = boost::none, + bool apiStrict = false); + +/** + * If _uuid exists for this FindCommand, update the value of _nss. + */ +void refreshNSS(const NamespaceString& nss, FindCommand* findCommand); + +/** + * Converts this FindCommand into an aggregation using $match. If this FindCommand has options + * that cannot be satisfied by aggregation, a non-OK status is returned and 'cmdBuilder' is not + * modified. + */ +StatusWith<BSONObj> asAggregationCommand(const FindCommand& findCommand); + +/** + * Helper function to identify text search sort key + * Example: {a: {$meta: "textScore"}} + */ +bool isTextScoreMeta(BSONElement elt); + +// Read preference is attached to commands in "wrapped" form, e.g. +// { $query: { <cmd>: ... } , <kWrappedReadPrefField>: { ... } } +// +// However, mongos internally "unwraps" the read preference and adds it as a parameter to the +// command, e.g. +// { <cmd>: ... , <kUnwrappedReadPrefField>: { <kWrappedReadPrefField>: { ... } } } +static constexpr auto kWrappedReadPrefField = "$readPreference"; +static constexpr auto kUnwrappedReadPrefField = "$queryOptions"; + +// Names of the maxTimeMS command and query option. +// Char arrays because they are used in static initialization. +static constexpr auto cmdOptionMaxTimeMS = "maxTimeMS"; +static constexpr auto queryOptionMaxTimeMS = "$maxTimeMS"; + +// Names of the $meta projection values. +static constexpr auto metaGeoNearDistance = "geoNearDistance"; +static constexpr auto metaGeoNearPoint = "geoNearPoint"; +static constexpr auto metaRecordId = "recordId"; +static constexpr auto metaSortKey = "sortKey"; +static constexpr auto metaTextScore = "textScore"; + +// A constant by which 'maxTimeMSOpOnly' values are allowed to exceed the max allowed value for +// 'maxTimeMS'. This is because mongod and mongos server processes add a small amount to the +// 'maxTimeMS' value they are given before passing it on as 'maxTimeMSOpOnly', to allow for +// clock precision. +static constexpr auto kMaxTimeMSOpOnlyMaxPadding = 100LL; + +static constexpr auto kDefaultBatchSize = 101ll; + +void setTailableMode(TailableModeEnum tailableMode, FindCommand* findCommand); + +TailableModeEnum getTailableMode(const FindCommand& findCommand); + +// +// Old parsing code: SOON TO BE DEPRECATED. +// + +/** + * Parse the provided QueryMessage and return a heap constructed FindCommand, which + * represents it or an error. + */ +StatusWith<std::unique_ptr<FindCommand>> fromLegacyQueryMessage(const QueryMessage& qm, + bool* explain); + +/** + * Parse the provided legacy query object and parameters to construct a FindCommand. + */ +StatusWith<std::unique_ptr<FindCommand>> fromLegacyQuery(NamespaceStringOrUUID nsOrUuid, + const BSONObj& queryObj, + const BSONObj& proj, + int ntoskip, + int ntoreturn, + int queryOptions, + bool* explain); + +StatusWith<std::unique_ptr<FindCommand>> fromLegacyQueryFindCommand(NamespaceStringOrUUID nsOrUuid, + const BSONObj& queryObj, + const BSONObj& proj, + int ntoskip, + int ntoreturn, + int queryOptions); + +} // namespace query_request_helper +} // namespace mongo diff --git a/src/mongo/db/query/query_request_test.cpp b/src/mongo/db/query/query_request_test.cpp index adee5c43cfc..adb40fa212f 100644 --- a/src/mongo/db/query/query_request_test.cpp +++ b/src/mongo/db/query/query_request_test.cpp @@ -40,7 +40,7 @@ #include "mongo/db/json.h" #include "mongo/db/namespace_string.h" #include "mongo/db/pipeline/aggregation_request_helper.h" -#include "mongo/db/query/query_request.h" +#include "mongo/db/query/query_request_helper.h" #include "mongo/db/service_context_test_fixture.h" #include "mongo/unittest/unittest.h" @@ -53,142 +53,142 @@ using unittest::assertGet; static const NamespaceString testns("testdb.testcoll"); TEST(QueryRequestTest, LimitWithNToReturn) { - QueryRequest qr(testns); - qr.setLimit(1); - qr.setNToReturn(0); - ASSERT_NOT_OK(qr.validate()); + FindCommand findCommand(testns); + findCommand.setLimit(1); + findCommand.setNtoreturn(0); + ASSERT_NOT_OK(query_request_helper::validateFindCommand(findCommand)); } TEST(QueryRequestTest, BatchSizeWithNToReturn) { - QueryRequest qr(testns); - qr.setBatchSize(0); - qr.setNToReturn(0); - ASSERT_NOT_OK(qr.validate()); + FindCommand findCommand(testns); + findCommand.setBatchSize(0); + findCommand.setNtoreturn(0); + ASSERT_NOT_OK(query_request_helper::validateFindCommand(findCommand)); } TEST(QueryRequestTest, NegativeSkip) { - QueryRequest qr(testns); - qr.setSkip(-1); - ASSERT_NOT_OK(qr.validate()); + FindCommand findCommand(testns); + findCommand.setSkip(-1); + ASSERT_NOT_OK(query_request_helper::validateFindCommand(findCommand)); } TEST(QueryRequestTest, ZeroSkip) { - QueryRequest qr(testns); - qr.setSkip(0); - ASSERT_OK(qr.validate()); + FindCommand findCommand(testns); + findCommand.setSkip(0); + ASSERT_OK(query_request_helper::validateFindCommand(findCommand)); } TEST(QueryRequestTest, PositiveSkip) { - QueryRequest qr(testns); - qr.setSkip(1); - ASSERT_OK(qr.validate()); + FindCommand findCommand(testns); + findCommand.setSkip(1); + ASSERT_OK(query_request_helper::validateFindCommand(findCommand)); } TEST(QueryRequestTest, NegativeLimit) { - QueryRequest qr(testns); - qr.setLimit(-1); - ASSERT_NOT_OK(qr.validate()); + FindCommand findCommand(testns); + findCommand.setLimit(-1); + ASSERT_NOT_OK(query_request_helper::validateFindCommand(findCommand)); } TEST(QueryRequestTest, ZeroLimit) { - QueryRequest qr(testns); - qr.setLimit(0); - ASSERT_OK(qr.validate()); + FindCommand findCommand(testns); + findCommand.setLimit(0); + ASSERT_OK(query_request_helper::validateFindCommand(findCommand)); } TEST(QueryRequestTest, PositiveLimit) { - QueryRequest qr(testns); - qr.setLimit(1); - ASSERT_OK(qr.validate()); + FindCommand findCommand(testns); + findCommand.setLimit(1); + ASSERT_OK(query_request_helper::validateFindCommand(findCommand)); } TEST(QueryRequestTest, NegativeBatchSize) { - QueryRequest qr(testns); - qr.setBatchSize(-1); - ASSERT_NOT_OK(qr.validate()); + FindCommand findCommand(testns); + findCommand.setBatchSize(-1); + ASSERT_NOT_OK(query_request_helper::validateFindCommand(findCommand)); } TEST(QueryRequestTest, ZeroBatchSize) { - QueryRequest qr(testns); - qr.setBatchSize(0); - ASSERT_OK(qr.validate()); + FindCommand findCommand(testns); + findCommand.setBatchSize(0); + ASSERT_OK(query_request_helper::validateFindCommand(findCommand)); } TEST(QueryRequestTest, PositiveBatchSize) { - QueryRequest qr(testns); - qr.setBatchSize(1); - ASSERT_OK(qr.validate()); + FindCommand findCommand(testns); + findCommand.setBatchSize(1); + ASSERT_OK(query_request_helper::validateFindCommand(findCommand)); } TEST(QueryRequestTest, NegativeNToReturn) { - QueryRequest qr(testns); - qr.setNToReturn(-1); - ASSERT_NOT_OK(qr.validate()); + FindCommand findCommand(testns); + findCommand.setNtoreturn(-1); + ASSERT_NOT_OK(query_request_helper::validateFindCommand(findCommand)); } TEST(QueryRequestTest, ZeroNToReturn) { - QueryRequest qr(testns); - qr.setNToReturn(0); - ASSERT_OK(qr.validate()); + FindCommand findCommand(testns); + findCommand.setNtoreturn(0); + ASSERT_OK(query_request_helper::validateFindCommand(findCommand)); } TEST(QueryRequestTest, PositiveNToReturn) { - QueryRequest qr(testns); - qr.setNToReturn(1); - ASSERT_OK(qr.validate()); + FindCommand findCommand(testns); + findCommand.setNtoreturn(1); + ASSERT_OK(query_request_helper::validateFindCommand(findCommand)); } TEST(QueryRequestTest, NegativeMaxTimeMS) { - QueryRequest qr(testns); - qr.setMaxTimeMS(-1); - ASSERT_NOT_OK(qr.validate()); + FindCommand findCommand(testns); + findCommand.setMaxTimeMS(-1); + ASSERT_NOT_OK(query_request_helper::validateFindCommand(findCommand)); } TEST(QueryRequestTest, ZeroMaxTimeMS) { - QueryRequest qr(testns); - qr.setMaxTimeMS(0); - ASSERT_OK(qr.validate()); + FindCommand findCommand(testns); + findCommand.setMaxTimeMS(0); + ASSERT_OK(query_request_helper::validateFindCommand(findCommand)); } TEST(QueryRequestTest, PositiveMaxTimeMS) { - QueryRequest qr(testns); - qr.setMaxTimeMS(1); - ASSERT_OK(qr.validate()); + FindCommand findCommand(testns); + findCommand.setMaxTimeMS(1); + ASSERT_OK(query_request_helper::validateFindCommand(findCommand)); } TEST(QueryRequestTest, ValidSortOrder) { - QueryRequest qr(testns); - qr.setSort(fromjson("{a: 1}")); - ASSERT_OK(qr.validate()); + FindCommand findCommand(testns); + findCommand.setSort(fromjson("{a: 1}")); + ASSERT_OK(query_request_helper::validateFindCommand(findCommand)); } TEST(QueryRequestTest, DoesNotErrorOnInvalidSortPattern) { - QueryRequest qr(testns); - qr.setSort(fromjson("{a: \"\"}")); - // QueryRequest isn't responsible for validating the sort pattern, so it is considered valid + FindCommand findCommand(testns); + findCommand.setSort(fromjson("{a: \"\"}")); + // FindCommand isn't responsible for validating the sort pattern, so it is considered valid // even though the sort pattern {a: ""} is not well-formed. - ASSERT_OK(qr.validate()); + ASSERT_OK(query_request_helper::validateFindCommand(findCommand)); } TEST(QueryRequestTest, MinFieldsNotPrefixOfMax) { - QueryRequest qr(testns); - qr.setMin(fromjson("{a: 1}")); - qr.setMax(fromjson("{b: 1}")); - ASSERT_NOT_OK(qr.validate()); + FindCommand findCommand(testns); + findCommand.setMin(fromjson("{a: 1}")); + findCommand.setMax(fromjson("{b: 1}")); + ASSERT_NOT_OK(query_request_helper::validateFindCommand(findCommand)); } TEST(QueryRequestTest, MinFieldsMoreThanMax) { - QueryRequest qr(testns); - qr.setMin(fromjson("{a: 1, b: 1}")); - qr.setMax(fromjson("{a: 1}")); - ASSERT_NOT_OK(qr.validate()); + FindCommand findCommand(testns); + findCommand.setMin(fromjson("{a: 1, b: 1}")); + findCommand.setMax(fromjson("{a: 1}")); + ASSERT_NOT_OK(query_request_helper::validateFindCommand(findCommand)); } TEST(QueryRequestTest, MinFieldsLessThanMax) { - QueryRequest qr(testns); - qr.setMin(fromjson("{a: 1}")); - qr.setMax(fromjson("{a: 1, b: 1}")); - ASSERT_NOT_OK(qr.validate()); + FindCommand findCommand(testns); + findCommand.setMin(fromjson("{a: 1}")); + findCommand.setMax(fromjson("{a: 1, b: 1}")); + ASSERT_NOT_OK(query_request_helper::validateFindCommand(findCommand)); } TEST(QueryRequestTest, ForbidTailableWithNonNaturalSort) { @@ -197,8 +197,7 @@ TEST(QueryRequestTest, ForbidTailableWithNonNaturalSort) { "tailable: true," "sort: {a: 1}, '$db': 'test'}"); - bool isExplain = false; - ASSERT_THROWS_CODE(QueryRequest::makeFromFindCommandForTests(cmdObj, isExplain), + ASSERT_THROWS_CODE(query_request_helper::makeFromFindCommandForTests(cmdObj), DBException, ErrorCodes::BadValue); } @@ -209,8 +208,7 @@ TEST(QueryRequestTest, ForbidTailableWithSingleBatch) { "tailable: true," "singleBatch: true, '$db': 'test'}"); - bool isExplain = false; - ASSERT_THROWS_CODE(QueryRequest::makeFromFindCommandForTests(cmdObj, isExplain), + ASSERT_THROWS_CODE(query_request_helper::makeFromFindCommandForTests(cmdObj), DBException, ErrorCodes::BadValue); } @@ -221,10 +219,9 @@ TEST(QueryRequestTest, AllowTailableWithNaturalSort) { "tailable: true," "sort: {$natural: 1}, '$db': 'test'}"); - bool isExplain = false; - auto qr = QueryRequest::makeFromFindCommandForTests(cmdObj, isExplain); - ASSERT_TRUE(qr->isTailable()); - ASSERT_BSONOBJ_EQ(qr->getSort(), BSON("$natural" << 1)); + auto findCommand = query_request_helper::makeFromFindCommandForTests(cmdObj); + ASSERT_TRUE(findCommand->getTailable()); + ASSERT_BSONOBJ_EQ(findCommand->getSort(), BSON("$natural" << 1)); } // @@ -232,122 +229,122 @@ TEST(QueryRequestTest, AllowTailableWithNaturalSort) { // TEST(QueryRequestTest, ValidSortProj) { - QueryRequest qr(testns); - qr.setProj(fromjson("{a: 1}")); - qr.setSort(fromjson("{a: 1}")); - ASSERT_OK(qr.validate()); + FindCommand findCommand(testns); + findCommand.setProjection(fromjson("{a: 1}")); + findCommand.setSort(fromjson("{a: 1}")); + ASSERT_OK(query_request_helper::validateFindCommand(findCommand)); - QueryRequest metaQR(testns); - metaQR.setProj(fromjson("{a: {$meta: \"textScore\"}}")); - metaQR.setSort(fromjson("{a: {$meta: \"textScore\"}}")); - ASSERT_OK(metaQR.validate()); + FindCommand metaFC(testns); + metaFC.setProjection(fromjson("{a: {$meta: \"textScore\"}}")); + metaFC.setSort(fromjson("{a: {$meta: \"textScore\"}}")); + ASSERT_OK(query_request_helper::validateFindCommand(metaFC)); } TEST(QueryRequestTest, TextScoreMetaSortOnFieldDoesNotRequireMetaProjection) { - QueryRequest qr(testns); - qr.setProj(fromjson("{b: 1}")); - qr.setSort(fromjson("{a: {$meta: 'textScore'}}")); - ASSERT_OK(qr.validate()); + FindCommand findCommand(testns); + findCommand.setProjection(fromjson("{b: 1}")); + findCommand.setSort(fromjson("{a: {$meta: 'textScore'}}")); + ASSERT_OK(query_request_helper::validateFindCommand(findCommand)); } TEST(QueryRequestTest, TextScoreMetaProjectionDoesNotRequireTextScoreMetaSort) { - QueryRequest qr(testns); - qr.setProj(fromjson("{a: {$meta: \"textScore\"}}")); - qr.setSort(fromjson("{b: 1}")); - ASSERT_OK(qr.validate()); + FindCommand findCommand(testns); + findCommand.setProjection(fromjson("{a: {$meta: \"textScore\"}}")); + findCommand.setSort(fromjson("{b: 1}")); + ASSERT_OK(query_request_helper::validateFindCommand(findCommand)); } TEST(QueryRequestTest, RequestResumeTokenWithHint) { - QueryRequest qr(testns); - qr.setRequestResumeToken(true); - ASSERT_NOT_OK(qr.validate()); - qr.setHint(fromjson("{a: 1}")); - ASSERT_NOT_OK(qr.validate()); - qr.setHint(fromjson("{$natural: 1}")); - ASSERT_OK(qr.validate()); + FindCommand findCommand(testns); + findCommand.setRequestResumeToken(true); + ASSERT_NOT_OK(query_request_helper::validateFindCommand(findCommand)); + findCommand.setHint(fromjson("{a: 1}")); + ASSERT_NOT_OK(query_request_helper::validateFindCommand(findCommand)); + findCommand.setHint(fromjson("{$natural: 1}")); + ASSERT_OK(query_request_helper::validateFindCommand(findCommand)); } TEST(QueryRequestTest, RequestResumeTokenWithSort) { - QueryRequest qr(testns); - qr.setRequestResumeToken(true); + FindCommand findCommand(testns); + findCommand.setRequestResumeToken(true); // Hint must be explicitly set for the query request to validate. - qr.setHint(fromjson("{$natural: 1}")); - ASSERT_OK(qr.validate()); - qr.setSort(fromjson("{a: 1}")); - ASSERT_NOT_OK(qr.validate()); - qr.setSort(fromjson("{$natural: 1}")); - ASSERT_OK(qr.validate()); + findCommand.setHint(fromjson("{$natural: 1}")); + ASSERT_OK(query_request_helper::validateFindCommand(findCommand)); + findCommand.setSort(fromjson("{a: 1}")); + ASSERT_NOT_OK(query_request_helper::validateFindCommand(findCommand)); + findCommand.setSort(fromjson("{$natural: 1}")); + ASSERT_OK(query_request_helper::validateFindCommand(findCommand)); } TEST(QueryRequestTest, InvalidResumeAfterWrongRecordIdType) { - QueryRequest qr(testns); + FindCommand findCommand(testns); BSONObj resumeAfter = BSON("$recordId" << 1); - qr.setResumeAfter(resumeAfter); - qr.setRequestResumeToken(true); + findCommand.setResumeAfter(resumeAfter); + findCommand.setRequestResumeToken(true); // Hint must be explicitly set for the query request to validate. - qr.setHint(fromjson("{$natural: 1}")); - ASSERT_NOT_OK(qr.validate()); + findCommand.setHint(fromjson("{$natural: 1}")); + ASSERT_NOT_OK(query_request_helper::validateFindCommand(findCommand)); resumeAfter = BSON("$recordId" << 1LL); - qr.setResumeAfter(resumeAfter); - ASSERT_OK(qr.validate()); + findCommand.setResumeAfter(resumeAfter); + ASSERT_OK(query_request_helper::validateFindCommand(findCommand)); } TEST(QueryRequestTest, InvalidResumeAfterExtraField) { - QueryRequest qr(testns); + FindCommand findCommand(testns); BSONObj resumeAfter = BSON("$recordId" << 1LL << "$extra" << 1); - qr.setResumeAfter(resumeAfter); - qr.setRequestResumeToken(true); + findCommand.setResumeAfter(resumeAfter); + findCommand.setRequestResumeToken(true); // Hint must be explicitly set for the query request to validate. - qr.setHint(fromjson("{$natural: 1}")); - ASSERT_NOT_OK(qr.validate()); + findCommand.setHint(fromjson("{$natural: 1}")); + ASSERT_NOT_OK(query_request_helper::validateFindCommand(findCommand)); } TEST(QueryRequestTest, ResumeAfterWithHint) { - QueryRequest qr(testns); + FindCommand findCommand(testns); BSONObj resumeAfter = BSON("$recordId" << 1LL); - qr.setResumeAfter(resumeAfter); - qr.setRequestResumeToken(true); - ASSERT_NOT_OK(qr.validate()); - qr.setHint(fromjson("{a: 1}")); - ASSERT_NOT_OK(qr.validate()); - qr.setHint(fromjson("{$natural: 1}")); - ASSERT_OK(qr.validate()); + findCommand.setResumeAfter(resumeAfter); + findCommand.setRequestResumeToken(true); + ASSERT_NOT_OK(query_request_helper::validateFindCommand(findCommand)); + findCommand.setHint(fromjson("{a: 1}")); + ASSERT_NOT_OK(query_request_helper::validateFindCommand(findCommand)); + findCommand.setHint(fromjson("{$natural: 1}")); + ASSERT_OK(query_request_helper::validateFindCommand(findCommand)); } TEST(QueryRequestTest, ResumeAfterWithSort) { - QueryRequest qr(testns); + FindCommand findCommand(testns); BSONObj resumeAfter = BSON("$recordId" << 1LL); - qr.setResumeAfter(resumeAfter); - qr.setRequestResumeToken(true); + findCommand.setResumeAfter(resumeAfter); + findCommand.setRequestResumeToken(true); // Hint must be explicitly set for the query request to validate. - qr.setHint(fromjson("{$natural: 1}")); - ASSERT_OK(qr.validate()); - qr.setSort(fromjson("{a: 1}")); - ASSERT_NOT_OK(qr.validate()); - qr.setSort(fromjson("{$natural: 1}")); - ASSERT_OK(qr.validate()); + findCommand.setHint(fromjson("{$natural: 1}")); + ASSERT_OK(query_request_helper::validateFindCommand(findCommand)); + findCommand.setSort(fromjson("{a: 1}")); + ASSERT_NOT_OK(query_request_helper::validateFindCommand(findCommand)); + findCommand.setSort(fromjson("{$natural: 1}")); + ASSERT_OK(query_request_helper::validateFindCommand(findCommand)); } TEST(QueryRequestTest, ResumeNoSpecifiedRequestResumeToken) { - QueryRequest qr(testns); + FindCommand findCommand(testns); BSONObj resumeAfter = BSON("$recordId" << 1LL); - qr.setResumeAfter(resumeAfter); + findCommand.setResumeAfter(resumeAfter); // Hint must be explicitly set for the query request to validate. - qr.setHint(fromjson("{$natural: 1}")); - ASSERT_NOT_OK(qr.validate()); - qr.setRequestResumeToken(true); - ASSERT_OK(qr.validate()); + findCommand.setHint(fromjson("{$natural: 1}")); + ASSERT_NOT_OK(query_request_helper::validateFindCommand(findCommand)); + findCommand.setRequestResumeToken(true); + ASSERT_OK(query_request_helper::validateFindCommand(findCommand)); } TEST(QueryRequestTest, ExplicitEmptyResumeAfter) { - QueryRequest qr(NamespaceString::kRsOplogNamespace); + FindCommand findCommand(NamespaceString::kRsOplogNamespace); BSONObj resumeAfter = fromjson("{}"); // Hint must be explicitly set for the query request to validate. - qr.setHint(fromjson("{$natural: 1}")); - qr.setResumeAfter(resumeAfter); - ASSERT_OK(qr.validate()); - qr.setRequestResumeToken(true); - ASSERT_OK(qr.validate()); + findCommand.setHint(fromjson("{$natural: 1}")); + findCommand.setResumeAfter(resumeAfter); + ASSERT_OK(query_request_helper::validateFindCommand(findCommand)); + findCommand.setRequestResumeToken(true); + ASSERT_OK(query_request_helper::validateFindCommand(findCommand)); } // @@ -357,7 +354,7 @@ TEST(QueryRequestTest, ExplicitEmptyResumeAfter) { bool isFirstElementTextScoreMeta(const char* sortStr) { BSONObj sortObj = fromjson(sortStr); BSONElement elt = sortObj.firstElement(); - bool result = QueryRequest::isTextScoreMeta(elt); + bool result = query_request_helper::isTextScoreMeta(elt); return result; } @@ -384,8 +381,7 @@ TEST(QueryRequestTest, ParseFromCommandBasic) { "sort: {a: 1}," "projection: {_id: 0, a: 1}, '$db': 'test'}"); - bool isExplain = false; - QueryRequest::makeFromFindCommandForTests(cmdObj, isExplain); + query_request_helper::makeFromFindCommandForTests(cmdObj); } TEST(QueryRequestTest, ParseFromCommandWithOptions) { @@ -396,11 +392,10 @@ TEST(QueryRequestTest, ParseFromCommandWithOptions) { "projection: {_id: 0, a: 1}," "showRecordId: true, '$db': 'test'}"); - bool isExplain = false; - unique_ptr<QueryRequest> qr(QueryRequest::makeFromFindCommandForTests(cmdObj, isExplain)); + unique_ptr<FindCommand> findCommand(query_request_helper::makeFromFindCommandForTests(cmdObj)); // Make sure the values from the command BSON are reflected in the QR. - ASSERT(qr->showRecordId()); + ASSERT(findCommand->getShowRecordId()); } TEST(QueryRequestTest, ParseFromCommandHintAsString) { @@ -409,10 +404,9 @@ TEST(QueryRequestTest, ParseFromCommandHintAsString) { "filter: {a: 1}," "hint: 'foo_1', '$db': 'test'}"); - bool isExplain = false; - unique_ptr<QueryRequest> qr(QueryRequest::makeFromFindCommandForTests(cmdObj, isExplain)); + unique_ptr<FindCommand> findCommand(query_request_helper::makeFromFindCommandForTests(cmdObj)); - BSONObj hintObj = qr->getHint(); + BSONObj hintObj = findCommand->getHint(); ASSERT_BSONOBJ_EQ(BSON("$hint" << "foo_1"), hintObj); @@ -424,8 +418,7 @@ TEST(QueryRequestTest, ParseFromCommandValidSortProj) { "projection: {a: 1}," "sort: {a: 1}, '$db': 'test'}"); - bool isExplain = false; - QueryRequest::makeFromFindCommandForTests(cmdObj, isExplain); + query_request_helper::makeFromFindCommandForTests(cmdObj); } TEST(QueryRequestTest, ParseFromCommandValidSortProjMeta) { @@ -434,8 +427,7 @@ TEST(QueryRequestTest, ParseFromCommandValidSortProjMeta) { "projection: {a: {$meta: 'textScore'}}," "sort: {a: {$meta: 'textScore'}}, '$db': 'test'}"); - bool isExplain = false; - QueryRequest::makeFromFindCommandForTests(cmdObj, isExplain); + query_request_helper::makeFromFindCommandForTests(cmdObj); } TEST(QueryRequestTest, ParseFromCommandAllFlagsTrue) { @@ -448,17 +440,15 @@ TEST(QueryRequestTest, ParseFromCommandAllFlagsTrue) { "readOnce: true," "allowSpeculativeMajorityRead: true, '$db': 'test'}"); - bool isExplain = false; - unique_ptr<QueryRequest> qr(QueryRequest::makeFromFindCommandForTests(cmdObj, isExplain)); + unique_ptr<FindCommand> findCommand(query_request_helper::makeFromFindCommandForTests(cmdObj)); // Test that all the flags got set to true. - ASSERT(qr->isTailable()); - ASSERT(!qr->isSlaveOk()); - ASSERT(qr->isNoCursorTimeout()); - ASSERT(qr->isTailableAndAwaitData()); - ASSERT(qr->isAllowPartialResults()); - ASSERT(qr->isReadOnce()); - ASSERT(qr->allowSpeculativeMajorityRead()); + ASSERT(findCommand->getTailable()); + ASSERT(findCommand->getNoCursorTimeout()); + ASSERT(findCommand->getTailable() && findCommand->getAwaitData()); + ASSERT(findCommand->getAllowPartialResults()); + ASSERT(findCommand->getReadOnce()); + ASSERT(findCommand->getAllowSpeculativeMajorityRead()); } TEST(QueryRequestTest, OplogReplayFlagIsAllowedButIgnored) { @@ -466,12 +456,11 @@ TEST(QueryRequestTest, OplogReplayFlagIsAllowedButIgnored) { << "testns" << "oplogReplay" << true << "tailable" << true << "$db" << "test"); - const bool isExplain = false; const NamespaceString nss{"test.testns"}; - auto qr = QueryRequest::makeFromFindCommandForTests(cmdObj, isExplain); + auto findCommand = query_request_helper::makeFromFindCommandForTests(cmdObj); // Verify that the 'oplogReplay' flag does not appear if we reserialize the request. - auto reserialized = qr->asFindCommand(); + auto reserialized = findCommand->toBSON(BSONObj()); ASSERT_BSONOBJ_EQ(reserialized, BSON("find" << "testns" @@ -481,9 +470,8 @@ TEST(QueryRequestTest, OplogReplayFlagIsAllowedButIgnored) { TEST(QueryRequestTest, ParseFromCommandReadOnceDefaultsToFalse) { BSONObj cmdObj = fromjson("{find: 'testns', '$db': 'test'}"); - bool isExplain = false; - unique_ptr<QueryRequest> qr(QueryRequest::makeFromFindCommandForTests(cmdObj, isExplain)); - ASSERT(!qr->isReadOnce()); + unique_ptr<FindCommand> findCommand(query_request_helper::makeFromFindCommandForTests(cmdObj)); + ASSERT(!findCommand->getReadOnce()); } TEST(QueryRequestTest, ParseFromCommandValidMinMax) { @@ -493,12 +481,11 @@ TEST(QueryRequestTest, ParseFromCommandValidMinMax) { "min: {a: 1}," "max: {a: 2}, '$db': 'test'}"); - bool isExplain = false; - unique_ptr<QueryRequest> qr(QueryRequest::makeFromFindCommandForTests(cmdObj, isExplain)); + unique_ptr<FindCommand> findCommand(query_request_helper::makeFromFindCommandForTests(cmdObj)); BSONObj expectedMin = BSON("a" << 1); - ASSERT_EQUALS(0, expectedMin.woCompare(qr->getMin())); + ASSERT_EQUALS(0, expectedMin.woCompare(findCommand->getMin())); BSONObj expectedMax = BSON("a" << 2); - ASSERT_EQUALS(0, expectedMax.woCompare(qr->getMax())); + ASSERT_EQUALS(0, expectedMax.woCompare(findCommand->getMax())); } TEST(QueryRequestTest, ParseFromCommandAllNonOptionFields) { @@ -519,32 +506,31 @@ TEST(QueryRequestTest, ParseFromCommandAllNonOptionFields) { "singleBatch: false, '$db': 'test'}") .addField(rtcObj["runtimeConstants"]); - bool isExplain = false; - unique_ptr<QueryRequest> qr(QueryRequest::makeFromFindCommandForTests(cmdObj, isExplain)); + unique_ptr<FindCommand> findCommand(query_request_helper::makeFromFindCommandForTests(cmdObj)); // Check the values inside the QR. BSONObj expectedQuery = BSON("a" << 1); - ASSERT_EQUALS(0, expectedQuery.woCompare(qr->getFilter())); + ASSERT_EQUALS(0, expectedQuery.woCompare(findCommand->getFilter())); BSONObj expectedSort = BSON("b" << 1); - ASSERT_EQUALS(0, expectedSort.woCompare(qr->getSort())); + ASSERT_EQUALS(0, expectedSort.woCompare(findCommand->getSort())); BSONObj expectedProj = BSON("c" << 1); - ASSERT_EQUALS(0, expectedProj.woCompare(qr->getProj())); + ASSERT_EQUALS(0, expectedProj.woCompare(findCommand->getProjection())); BSONObj expectedHint = BSON("d" << 1); - ASSERT_EQUALS(0, expectedHint.woCompare(qr->getHint())); + ASSERT_EQUALS(0, expectedHint.woCompare(findCommand->getHint())); BSONObj expectedReadConcern = BSON("e" << 1); - ASSERT(qr->getReadConcern()); - ASSERT_BSONOBJ_EQ(expectedReadConcern, *qr->getReadConcern()); + ASSERT(findCommand->getReadConcern()); + ASSERT_BSONOBJ_EQ(expectedReadConcern, *findCommand->getReadConcern()); BSONObj expectedUnwrappedReadPref = BSON("$readPreference" << "secondary"); - ASSERT_EQUALS(0, expectedUnwrappedReadPref.woCompare(qr->getUnwrappedReadPref())); + ASSERT_EQUALS(0, expectedUnwrappedReadPref.woCompare(findCommand->getUnwrappedReadPref())); BSONObj expectedCollation = BSON("f" << 1); - ASSERT_EQUALS(0, expectedCollation.woCompare(qr->getCollation())); - ASSERT_EQUALS(3, *qr->getLimit()); - ASSERT_EQUALS(5, *qr->getSkip()); - ASSERT_EQUALS(90, *qr->getBatchSize()); - ASSERT(qr->getLegacyRuntimeConstants().has_value()); - ASSERT_EQUALS(qr->getLegacyRuntimeConstants()->getLocalNow(), rtc.getLocalNow()); - ASSERT_EQUALS(qr->getLegacyRuntimeConstants()->getClusterTime(), rtc.getClusterTime()); - ASSERT(!qr->isSingleBatch()); + ASSERT_EQUALS(0, expectedCollation.woCompare(findCommand->getCollation())); + ASSERT_EQUALS(3, *findCommand->getLimit()); + ASSERT_EQUALS(5, *findCommand->getSkip()); + ASSERT_EQUALS(90, *findCommand->getBatchSize()); + ASSERT(findCommand->getLegacyRuntimeConstants().has_value()); + ASSERT_EQUALS(findCommand->getLegacyRuntimeConstants()->getLocalNow(), rtc.getLocalNow()); + ASSERT_EQUALS(findCommand->getLegacyRuntimeConstants()->getClusterTime(), rtc.getClusterTime()); + ASSERT(!findCommand->getSingleBatch()); } TEST(QueryRequestTest, ParseFromCommandLargeLimit) { @@ -553,10 +539,9 @@ TEST(QueryRequestTest, ParseFromCommandLargeLimit) { "filter: {a: 1}," "limit: 8000000000, '$db': 'test'}"); // 8 * 1000 * 1000 * 1000 - const bool isExplain = false; - unique_ptr<QueryRequest> qr(QueryRequest::makeFromFindCommandForTests(cmdObj, isExplain)); + unique_ptr<FindCommand> findCommand(query_request_helper::makeFromFindCommandForTests(cmdObj)); - ASSERT_EQUALS(8LL * 1000 * 1000 * 1000, *qr->getLimit()); + ASSERT_EQUALS(8LL * 1000 * 1000 * 1000, *findCommand->getLimit()); } TEST(QueryRequestTest, ParseFromCommandLargeBatchSize) { @@ -565,10 +550,9 @@ TEST(QueryRequestTest, ParseFromCommandLargeBatchSize) { "filter: {a: 1}," "batchSize: 8000000000, '$db': 'test'}"); // 8 * 1000 * 1000 * 1000 - const bool isExplain = false; - unique_ptr<QueryRequest> qr(QueryRequest::makeFromFindCommandForTests(cmdObj, isExplain)); + unique_ptr<FindCommand> findCommand(query_request_helper::makeFromFindCommandForTests(cmdObj)); - ASSERT_EQUALS(8LL * 1000 * 1000 * 1000, *qr->getBatchSize()); + ASSERT_EQUALS(8LL * 1000 * 1000 * 1000, *findCommand->getBatchSize()); } TEST(QueryRequestTest, ParseFromCommandLargeSkip) { @@ -577,10 +561,9 @@ TEST(QueryRequestTest, ParseFromCommandLargeSkip) { "filter: {a: 1}," "skip: 8000000000, '$db': 'test'}"); // 8 * 1000 * 1000 * 1000 - const bool isExplain = false; - unique_ptr<QueryRequest> qr(QueryRequest::makeFromFindCommandForTests(cmdObj, isExplain)); + unique_ptr<FindCommand> findCommand(query_request_helper::makeFromFindCommandForTests(cmdObj)); - ASSERT_EQUALS(8LL * 1000 * 1000 * 1000, *qr->getSkip()); + ASSERT_EQUALS(8LL * 1000 * 1000 * 1000, *findCommand->getSkip()); } // @@ -592,8 +575,7 @@ TEST(QueryRequestTest, ParseFromCommandQueryWrongType) { "{find: 'testns'," "filter: 3, '$db': 'test'}"); - bool isExplain = false; - ASSERT_THROWS_CODE(QueryRequest::makeFromFindCommandForTests(cmdObj, isExplain), + ASSERT_THROWS_CODE(query_request_helper::makeFromFindCommandForTests(cmdObj), DBException, ErrorCodes::TypeMismatch); } @@ -604,8 +586,7 @@ TEST(QueryRequestTest, ParseFromCommandSortWrongType) { "filter: {a: 1}," "sort: 3, '$db': 'test'}"); - bool isExplain = false; - ASSERT_THROWS_CODE(QueryRequest::makeFromFindCommandForTests(cmdObj, isExplain), + ASSERT_THROWS_CODE(query_request_helper::makeFromFindCommandForTests(cmdObj), DBException, ErrorCodes::TypeMismatch); } @@ -617,8 +598,7 @@ TEST(QueryRequestTest, ParseFromCommandProjWrongType) { "filter: {a: 1}," "projection: 'foo', '$db': 'test'}"); - bool isExplain = false; - ASSERT_THROWS_CODE(QueryRequest::makeFromFindCommandForTests(cmdObj, isExplain), + ASSERT_THROWS_CODE(query_request_helper::makeFromFindCommandForTests(cmdObj), DBException, ErrorCodes::TypeMismatch); } @@ -631,8 +611,7 @@ TEST(QueryRequestTest, ParseFromCommandSkipWrongType) { "skip: '5'," "projection: {a: 1}, '$db': 'test'}"); - bool isExplain = false; - ASSERT_THROWS_CODE(QueryRequest::makeFromFindCommandForTests(cmdObj, isExplain), + ASSERT_THROWS_CODE(query_request_helper::makeFromFindCommandForTests(cmdObj), DBException, ErrorCodes::TypeMismatch); } @@ -645,8 +624,7 @@ TEST(QueryRequestTest, ParseFromCommandLimitWrongType) { "limit: '5'," "projection: {a: 1}, '$db': 'test'}"); - bool isExplain = false; - ASSERT_THROWS_CODE(QueryRequest::makeFromFindCommandForTests(cmdObj, isExplain), + ASSERT_THROWS_CODE(query_request_helper::makeFromFindCommandForTests(cmdObj), DBException, ErrorCodes::TypeMismatch); } @@ -659,8 +637,7 @@ TEST(QueryRequestTest, ParseFromCommandSingleBatchWrongType) { "singleBatch: 'false'," "projection: {a: 1}, '$db': 'test'}"); - bool isExplain = false; - ASSERT_THROWS_CODE(QueryRequest::makeFromFindCommandForTests(cmdObj, isExplain), + ASSERT_THROWS_CODE(query_request_helper::makeFromFindCommandForTests(cmdObj), DBException, ErrorCodes::TypeMismatch); } @@ -672,8 +649,7 @@ TEST(QueryRequestTest, ParseFromCommandUnwrappedReadPrefWrongType) { "filter: {a: 1}," "$queryOptions: 1, '$db': 'test'}"); - bool isExplain = false; - ASSERT_THROWS_CODE(QueryRequest::makeFromFindCommandForTests(cmdObj, isExplain), + ASSERT_THROWS_CODE(query_request_helper::makeFromFindCommandForTests(cmdObj), DBException, ErrorCodes::TypeMismatch); } @@ -685,8 +661,7 @@ TEST(QueryRequestTest, ParseFromCommandMaxTimeMSWrongType) { "filter: {a: 1}," "maxTimeMS: true, '$db': 'test'}"); - bool isExplain = false; - ASSERT_THROWS_CODE(QueryRequest::makeFromFindCommandForTests(cmdObj, isExplain), + ASSERT_THROWS_CODE(query_request_helper::makeFromFindCommandForTests(cmdObj), DBException, ErrorCodes::BadValue); } @@ -698,8 +673,7 @@ TEST(QueryRequestTest, ParseFromCommandMaxWrongType) { "filter: {a: 1}," "max: 3, '$db': 'test'}"); - bool isExplain = false; - ASSERT_THROWS_CODE(QueryRequest::makeFromFindCommandForTests(cmdObj, isExplain), + ASSERT_THROWS_CODE(query_request_helper::makeFromFindCommandForTests(cmdObj), DBException, ErrorCodes::TypeMismatch); } @@ -711,8 +685,7 @@ TEST(QueryRequestTest, ParseFromCommandMinWrongType) { "filter: {a: 1}," "min: 3, '$db': 'test'}"); - bool isExplain = false; - ASSERT_THROWS_CODE(QueryRequest::makeFromFindCommandForTests(cmdObj, isExplain), + ASSERT_THROWS_CODE(query_request_helper::makeFromFindCommandForTests(cmdObj), DBException, ErrorCodes::TypeMismatch); } @@ -723,8 +696,7 @@ TEST(QueryRequestTest, ParseFromCommandReturnKeyWrongType) { "filter: {a: 1}," "returnKey: 3, '$db': 'test'}"); - bool isExplain = false; - ASSERT_THROWS_CODE(QueryRequest::makeFromFindCommandForTests(cmdObj, isExplain), + ASSERT_THROWS_CODE(query_request_helper::makeFromFindCommandForTests(cmdObj), DBException, ErrorCodes::TypeMismatch); } @@ -735,8 +707,7 @@ TEST(QueryRequestTest, ParseFromCommandShowRecordIdWrongType) { "filter: {a: 1}," "showRecordId: 3, '$db': 'test'}"); - bool isExplain = false; - ASSERT_THROWS_CODE(QueryRequest::makeFromFindCommandForTests(cmdObj, isExplain), + ASSERT_THROWS_CODE(query_request_helper::makeFromFindCommandForTests(cmdObj), DBException, ErrorCodes::TypeMismatch); } @@ -747,8 +718,7 @@ TEST(QueryRequestTest, ParseFromCommandTailableWrongType) { "filter: {a: 1}," "tailable: 3, '$db': 'test'}"); - bool isExplain = false; - ASSERT_THROWS_CODE(QueryRequest::makeFromFindCommandForTests(cmdObj, isExplain), + ASSERT_THROWS_CODE(query_request_helper::makeFromFindCommandForTests(cmdObj), DBException, ErrorCodes::TypeMismatch); } @@ -759,9 +729,8 @@ TEST(QueryRequestTest, ParseFromCommandSlaveOkWrongType) { "filter: {a: 1}," "slaveOk: 3, '$db': 'test'}"); - bool isExplain = false; ASSERT_THROWS_CODE( - QueryRequest::makeFromFindCommandForTests(cmdObj, isExplain), DBException, 40415); + query_request_helper::makeFromFindCommandForTests(cmdObj), DBException, 40415); } TEST(QueryRequestTest, ParseFromCommandOplogReplayWrongType) { @@ -770,8 +739,7 @@ TEST(QueryRequestTest, ParseFromCommandOplogReplayWrongType) { "filter: {a: 1}," "oplogReplay: 3, '$db': 'test'}"); - bool isExplain = false; - ASSERT_THROWS_CODE(QueryRequest::makeFromFindCommandForTests(cmdObj, isExplain), + ASSERT_THROWS_CODE(query_request_helper::makeFromFindCommandForTests(cmdObj), DBException, ErrorCodes::TypeMismatch); } @@ -782,8 +750,7 @@ TEST(QueryRequestTest, ParseFromCommandNoCursorTimeoutWrongType) { "filter: {a: 1}," "noCursorTimeout: 3, '$db': 'test'}"); - bool isExplain = false; - ASSERT_THROWS_CODE(QueryRequest::makeFromFindCommandForTests(cmdObj, isExplain), + ASSERT_THROWS_CODE(query_request_helper::makeFromFindCommandForTests(cmdObj), DBException, ErrorCodes::TypeMismatch); } @@ -795,8 +762,7 @@ TEST(QueryRequestTest, ParseFromCommandAwaitDataWrongType) { "tailable: true," "awaitData: 3, '$db': 'test'}"); - bool isExplain = false; - ASSERT_THROWS_CODE(QueryRequest::makeFromFindCommandForTests(cmdObj, isExplain), + ASSERT_THROWS_CODE(query_request_helper::makeFromFindCommandForTests(cmdObj), DBException, ErrorCodes::TypeMismatch); } @@ -808,9 +774,8 @@ TEST(QueryRequestTest, ParseFromCommandExhaustWrongType) { "filter: {a: 1}," "exhaust: 3, '$db': 'test'}"); - bool isExplain = false; ASSERT_THROWS_CODE( - QueryRequest::makeFromFindCommandForTests(cmdObj, isExplain), DBException, 40415); + query_request_helper::makeFromFindCommandForTests(cmdObj), DBException, 40415); } @@ -820,8 +785,7 @@ TEST(QueryRequestTest, ParseFromCommandPartialWrongType) { "filter: {a: 1}," "allowPartialResults: 3, '$db': 'test'}"); - bool isExplain = false; - ASSERT_THROWS_CODE(QueryRequest::makeFromFindCommandForTests(cmdObj, isExplain), + ASSERT_THROWS_CODE(query_request_helper::makeFromFindCommandForTests(cmdObj), DBException, ErrorCodes::TypeMismatch); } @@ -832,8 +796,7 @@ TEST(QueryRequestTest, ParseFromCommandReadConcernWrongType) { "filter: {a: 1}," "readConcern: 'foo', '$db': 'test'}"); - bool isExplain = false; - ASSERT_THROWS_CODE(QueryRequest::makeFromFindCommandForTests(cmdObj, isExplain), + ASSERT_THROWS_CODE(query_request_helper::makeFromFindCommandForTests(cmdObj), DBException, ErrorCodes::TypeMismatch); } @@ -844,8 +807,7 @@ TEST(QueryRequestTest, ParseFromCommandCollationWrongType) { "filter: {a: 1}," "collation: 'foo', '$db': 'test'}"); - bool isExplain = false; - ASSERT_THROWS_CODE(QueryRequest::makeFromFindCommandForTests(cmdObj, isExplain), + ASSERT_THROWS_CODE(query_request_helper::makeFromFindCommandForTests(cmdObj), DBException, ErrorCodes::TypeMismatch); } @@ -855,8 +817,7 @@ TEST(QueryRequestTest, ParseFromCommandReadOnceWrongType) { "{find: 'testns'," "readOnce: 1, '$db': 'test'}"); - bool isExplain = false; - ASSERT_THROWS_CODE(QueryRequest::makeFromFindCommandForTests(cmdObj, isExplain), + ASSERT_THROWS_CODE(query_request_helper::makeFromFindCommandForTests(cmdObj), DBException, ErrorCodes::TypeMismatch); } @@ -869,8 +830,7 @@ TEST(QueryRequestTest, ParseFromCommandLegacyRuntimeConstantsWrongType) { << "$db" << "test"); - bool isExplain = false; - ASSERT_THROWS_CODE(QueryRequest::makeFromFindCommandForTests(cmdObj, isExplain), + ASSERT_THROWS_CODE(query_request_helper::makeFromFindCommandForTests(cmdObj), DBException, ErrorCodes::TypeMismatch); } @@ -885,8 +845,7 @@ TEST(QueryRequestTest, ParseFromCommandLegacyRuntimeConstantsSubfieldsWrongType) << "shouldBeTimestamp") << "$db" << "test"); - bool isExplain = false; - ASSERT_THROWS_CODE(QueryRequest::makeFromFindCommandForTests(cmdObj, isExplain), + ASSERT_THROWS_CODE(query_request_helper::makeFromFindCommandForTests(cmdObj), AssertionException, ErrorCodes::TypeMismatch); } @@ -900,8 +859,7 @@ TEST(QueryRequestTest, ParseFromCommandNegativeSkipError) { "{find: 'testns'," "skip: -3," "filter: {a: 3}, '$db': 'test'}"); - bool isExplain = false; - ASSERT_THROWS_CODE(QueryRequest::makeFromFindCommandForTests(cmdObj, isExplain), + ASSERT_THROWS_CODE(query_request_helper::makeFromFindCommandForTests(cmdObj), DBException, ErrorCodes::BadValue); } @@ -911,10 +869,9 @@ TEST(QueryRequestTest, ParseFromCommandSkipIsZero) { "{find: 'testns'," "skip: 0," "filter: {a: 3}, '$db': 'test'}"); - bool isExplain = false; - unique_ptr<QueryRequest> qr(QueryRequest::makeFromFindCommandForTests(cmdObj, isExplain)); - ASSERT_BSONOBJ_EQ(BSON("a" << 3), qr->getFilter()); - ASSERT_FALSE(qr->getSkip()); + unique_ptr<FindCommand> findCommand(query_request_helper::makeFromFindCommandForTests(cmdObj)); + ASSERT_BSONOBJ_EQ(BSON("a" << 3), findCommand->getFilter()); + ASSERT_FALSE(findCommand->getSkip()); } TEST(QueryRequestTest, ParseFromCommandNegativeLimitError) { @@ -922,8 +879,7 @@ TEST(QueryRequestTest, ParseFromCommandNegativeLimitError) { "{find: 'testns'," "limit: -3," "filter: {a: 3}, '$db': 'test'}"); - bool isExplain = false; - ASSERT_THROWS_CODE(QueryRequest::makeFromFindCommandForTests(cmdObj, isExplain), + ASSERT_THROWS_CODE(query_request_helper::makeFromFindCommandForTests(cmdObj), DBException, ErrorCodes::BadValue); } @@ -933,10 +889,9 @@ TEST(QueryRequestTest, ParseFromCommandLimitIsZero) { "{find: 'testns'," "limit: 0," "filter: {a: 3}, '$db': 'test'}"); - bool isExplain = false; - unique_ptr<QueryRequest> qr(QueryRequest::makeFromFindCommandForTests(cmdObj, isExplain)); - ASSERT_BSONOBJ_EQ(BSON("a" << 3), qr->getFilter()); - ASSERT_FALSE(qr->getLimit()); + unique_ptr<FindCommand> findCommand(query_request_helper::makeFromFindCommandForTests(cmdObj)); + ASSERT_BSONOBJ_EQ(BSON("a" << 3), findCommand->getFilter()); + ASSERT_FALSE(findCommand->getLimit()); } TEST(QueryRequestTest, ParseFromCommandNegativeBatchSizeError) { @@ -944,29 +899,26 @@ TEST(QueryRequestTest, ParseFromCommandNegativeBatchSizeError) { "{find: 'testns'," "batchSize: -10," "filter: {a: 3}, '$db': 'test'}"); - bool isExplain = false; - ASSERT_THROWS_CODE(QueryRequest::makeFromFindCommandForTests(cmdObj, isExplain), + ASSERT_THROWS_CODE(query_request_helper::makeFromFindCommandForTests(cmdObj), DBException, ErrorCodes::BadValue); } TEST(QueryRequestTest, ParseFromCommandBatchSizeZero) { BSONObj cmdObj = fromjson("{find: 'testns', batchSize: 0, '$db': 'test'}"); - bool isExplain = false; - unique_ptr<QueryRequest> qr(QueryRequest::makeFromFindCommandForTests(cmdObj, isExplain)); + unique_ptr<FindCommand> findCommand(query_request_helper::makeFromFindCommandForTests(cmdObj)); - ASSERT(qr->getBatchSize()); - ASSERT_EQ(0, *qr->getBatchSize()); - ASSERT(!qr->getLimit()); + ASSERT(findCommand->getBatchSize()); + ASSERT_EQ(0, *findCommand->getBatchSize()); + ASSERT(!findCommand->getLimit()); } TEST(QueryRequestTest, ParseFromCommandDefaultBatchSize) { BSONObj cmdObj = fromjson("{find: 'testns', '$db': 'test'}"); - bool isExplain = false; - unique_ptr<QueryRequest> qr(QueryRequest::makeFromFindCommandForTests(cmdObj, isExplain)); + unique_ptr<FindCommand> findCommand(query_request_helper::makeFromFindCommandForTests(cmdObj)); - ASSERT(!qr->getBatchSize()); - ASSERT(!qr->getLimit()); + ASSERT(!findCommand->getBatchSize()); + ASSERT(!findCommand->getLimit()); } TEST(QueryRequestTest, ParseFromCommandRequestResumeToken) { @@ -976,9 +928,8 @@ TEST(QueryRequestTest, ParseFromCommandRequestResumeToken) { << "$_requestResumeToken" << true << "$db" << "test"); - bool isExplain = false; - unique_ptr<QueryRequest> qr(QueryRequest::makeFromFindCommandForTests(cmdObj, isExplain)); - ASSERT(qr->getRequestResumeToken()); + unique_ptr<FindCommand> findCommand(query_request_helper::makeFromFindCommandForTests(cmdObj)); + ASSERT(findCommand->getRequestResumeToken()); } TEST(QueryRequestTest, ParseFromCommandResumeToken) { @@ -989,10 +940,9 @@ TEST(QueryRequestTest, ParseFromCommandResumeToken) { << BSON("$recordId" << 1LL) << "$db" << "test"); - bool isExplain = false; - unique_ptr<QueryRequest> qr(QueryRequest::makeFromFindCommandForTests(cmdObj, isExplain)); - ASSERT(!qr->getResumeAfter().isEmpty()); - ASSERT(qr->getRequestResumeToken()); + unique_ptr<FindCommand> findCommand(query_request_helper::makeFromFindCommandForTests(cmdObj)); + ASSERT(!findCommand->getResumeAfter().isEmpty()); + ASSERT(findCommand->getRequestResumeToken()); } TEST(QueryRequestTest, ParseFromCommandEmptyResumeToken) { @@ -1004,14 +954,13 @@ TEST(QueryRequestTest, ParseFromCommandEmptyResumeToken) { << "$_requestResumeToken" << true << "$_resumeAfter" << resumeAfter << "$db" << "test"); - bool isExplain = false; - unique_ptr<QueryRequest> qr(QueryRequest::makeFromFindCommandForTests(cmdObj, isExplain)); - ASSERT(qr->getRequestResumeToken()); - ASSERT(qr->getResumeAfter().isEmpty()); + unique_ptr<FindCommand> findCommand(query_request_helper::makeFromFindCommandForTests(cmdObj)); + ASSERT(findCommand->getRequestResumeToken()); + ASSERT(findCommand->getResumeAfter().isEmpty()); } // -// Test asFindCommand ns and uuid variants. +// Test FindCommand object ns and uuid variants. // TEST(QueryRequestTest, AsFindCommandAllNonOptionFields) { @@ -1031,9 +980,8 @@ TEST(QueryRequestTest, AsFindCommandAllNonOptionFields) { "readConcern: {e: 1}, '$db': 'test'}") .addField(storage["runtimeConstants"]); - bool isExplain = false; - unique_ptr<QueryRequest> qr(QueryRequest::makeFromFindCommandForTests(cmdObj, isExplain)); - ASSERT_BSONOBJ_EQ(cmdObj.removeField("$db"), qr->asFindCommand()); + unique_ptr<FindCommand> findCommand(query_request_helper::makeFromFindCommandForTests(cmdObj)); + ASSERT_BSONOBJ_EQ(cmdObj.removeField("$db"), findCommand->toBSON(BSONObj())); } TEST(QueryRequestTest, AsFindCommandWithUuidAllNonOptionFields) { @@ -1055,17 +1003,16 @@ TEST(QueryRequestTest, AsFindCommandWithUuidAllNonOptionFields) { "readConcern: {e: 1}, '$db': 'test'}") .addField(storage["runtimeConstants"]); - bool isExplain = false; - unique_ptr<QueryRequest> qr(QueryRequest::makeFromFindCommandForTests(cmdObj, isExplain)); - ASSERT_BSONOBJ_EQ(cmdObj.removeField("$db"), qr->asFindCommand()); + unique_ptr<FindCommand> findCommand(query_request_helper::makeFromFindCommandForTests(cmdObj)); + ASSERT_BSONOBJ_EQ(cmdObj.removeField("$db"), findCommand->toBSON(BSONObj())); } TEST(QueryRequestTest, AsFindCommandWithUuidNoAvailableNamespace) { BSONObj cmdObj = fromjson("{find: { \"$binary\" : \"ASNFZ4mrze/ty6mHZUMhAQ==\", \"$type\" : \"04\" }}"); - QueryRequest qr(NamespaceStringOrUUID( + FindCommand findCommand(NamespaceStringOrUUID( "test", UUID::parse("01234567-89ab-cdef-edcb-a98765432101").getValue())); - ASSERT_BSONOBJ_EQ(cmdObj.removeField("$db"), qr.asFindCommand()); + ASSERT_BSONOBJ_EQ(cmdObj.removeField("$db"), findCommand.toBSON(BSONObj())); } TEST(QueryRequestTest, AsFindCommandWithResumeToken) { @@ -1076,9 +1023,8 @@ TEST(QueryRequestTest, AsFindCommandWithResumeToken) { << BSON("$recordId" << 1LL) << "$db" << "test"); - bool isExplain = false; - unique_ptr<QueryRequest> qr(QueryRequest::makeFromFindCommandForTests(cmdObj, isExplain)); - ASSERT_BSONOBJ_EQ(cmdObj.removeField("$db"), qr->asFindCommand()); + unique_ptr<FindCommand> findCommand(query_request_helper::makeFromFindCommandForTests(cmdObj)); + ASSERT_BSONOBJ_EQ(cmdObj.removeField("$db"), findCommand->toBSON(BSONObj())); } TEST(QueryRequestTest, AsFindCommandWithEmptyResumeToken) { @@ -1089,14 +1035,13 @@ TEST(QueryRequestTest, AsFindCommandWithEmptyResumeToken) { << "hint" << BSON("$natural" << 1) << "sort" << BSON("$natural" << 1) << "$_requestResumeToken" << true << "$_resumeAfter" << resumeAfter << "$db" << "test"); - bool isExplain = false; - unique_ptr<QueryRequest> qr(QueryRequest::makeFromFindCommandForTests(cmdObj, isExplain)); - ASSERT(qr->asFindCommand().getField("$_resumeAftr").eoo()); + unique_ptr<FindCommand> findCommand(query_request_helper::makeFromFindCommandForTests(cmdObj)); + ASSERT(findCommand->toBSON(BSONObj()).getField("$_resumeAftr").eoo()); } // // -// Errors checked in QueryRequest::validate(). +// Errors checked in query_request_helper::validateFindCommand(). // TEST(QueryRequestTest, ParseFromCommandMinMaxDifferentFieldsError) { @@ -1104,9 +1049,8 @@ TEST(QueryRequestTest, ParseFromCommandMinMaxDifferentFieldsError) { "{find: 'testns'," "min: {a: 3}," "max: {b: 4}, '$db': 'test'}"); - bool isExplain = false; ASSERT_THROWS_CODE( - QueryRequest::makeFromFindCommandForTests(cmdObj, isExplain), DBException, 51176); + query_request_helper::makeFromFindCommandForTests(cmdObj), DBException, 51176); } TEST(QueryRequestTest, ParseCommandAllowNonMetaSortOnFieldWithMetaProject) { @@ -1116,14 +1060,13 @@ TEST(QueryRequestTest, ParseCommandAllowNonMetaSortOnFieldWithMetaProject) { "{find: 'testns'," "projection: {a: {$meta: 'textScore'}}," "sort: {a: 1}, '$db': 'test'}"); - bool isExplain = false; - QueryRequest::makeFromFindCommandForTests(cmdObj, isExplain); + query_request_helper::makeFromFindCommandForTests(cmdObj); cmdObj = fromjson( "{find: 'testns'," "projection: {a: {$meta: 'textScore'}}," "sort: {b: 1}, '$db': 'test'}"); - QueryRequest::makeFromFindCommandForTests(cmdObj, isExplain); + query_request_helper::makeFromFindCommandForTests(cmdObj); } TEST(QueryRequestTest, ParseCommandAllowMetaSortOnFieldWithoutMetaProject) { @@ -1134,95 +1077,84 @@ TEST(QueryRequestTest, ParseCommandAllowMetaSortOnFieldWithoutMetaProject) { "projection: {a: 1}," "sort: {a: {$meta: 'textScore'}}, '$db': 'test'}"); - bool isExplain = false; - auto qr = QueryRequest::makeFromFindCommandForTests(cmdObj, isExplain); + auto findCommand = query_request_helper::makeFromFindCommandForTests(cmdObj); cmdObj = fromjson( "{find: 'testns'," "projection: {b: 1}," "sort: {a: {$meta: 'textScore'}}, '$db': 'test'}"); - qr = QueryRequest::makeFromFindCommandForTests(cmdObj, isExplain); + findCommand = query_request_helper::makeFromFindCommandForTests(cmdObj); } TEST(QueryRequestTest, ParseCommandForbidExhaust) { BSONObj cmdObj = fromjson("{find: 'testns', exhaust: true, '$db': 'test'}"); - bool isExplain = false; ASSERT_THROWS_CODE( - QueryRequest::makeFromFindCommandForTests(cmdObj, isExplain), DBException, 40415); + query_request_helper::makeFromFindCommandForTests(cmdObj), DBException, 40415); } TEST(QueryRequestTest, ParseCommandIsFromFindCommand) { BSONObj cmdObj = fromjson("{find: 'testns', '$db': 'test'}"); - bool isExplain = false; - unique_ptr<QueryRequest> qr(QueryRequest::makeFromFindCommandForTests(cmdObj, isExplain)); + unique_ptr<FindCommand> findCommand(query_request_helper::makeFromFindCommandForTests(cmdObj)); - ASSERT_FALSE(qr->getNToReturn()); + ASSERT_FALSE(findCommand->getNtoreturn()); } TEST(QueryRequestTest, ParseCommandAwaitDataButNotTailable) { BSONObj cmdObj = fromjson("{find: 'testns', awaitData: true, '$db': 'test'}"); - bool isExplain = false; - ASSERT_THROWS_CODE(QueryRequest::makeFromFindCommandForTests(cmdObj, isExplain), + ASSERT_THROWS_CODE(query_request_helper::makeFromFindCommandForTests(cmdObj), DBException, ErrorCodes::FailedToParse); } TEST(QueryRequestTest, ParseCommandFirstFieldNotString) { BSONObj cmdObj = fromjson("{find: 1, '$db': 'test'}"); - bool isExplain = false; - ASSERT_THROWS_CODE(QueryRequest::makeFromFindCommandForTests(cmdObj, isExplain), + ASSERT_THROWS_CODE(query_request_helper::makeFromFindCommandForTests(cmdObj), DBException, ErrorCodes::BadValue); } TEST(QueryRequestTest, ParseCommandIgnoreShardVersionField) { BSONObj cmdObj = fromjson("{find: 'test.testns', shardVersion: 'foo', '$db': 'test'}"); - bool isExplain = false; - QueryRequest::makeFromFindCommandForTests(cmdObj, isExplain); + query_request_helper::makeFromFindCommandForTests(cmdObj); } TEST(QueryRequestTest, DefaultQueryParametersCorrect) { BSONObj cmdObj = fromjson("{find: 'testns', '$db': 'test'}"); - std::unique_ptr<QueryRequest> qr(QueryRequest::makeFromFindCommandForTests(cmdObj, false)); + std::unique_ptr<FindCommand> findCommand( + query_request_helper::makeFromFindCommandForTests(cmdObj)); - ASSERT_FALSE(qr->getSkip()); - ASSERT_FALSE(qr->getLimit()); + ASSERT_FALSE(findCommand->getSkip()); + ASSERT_FALSE(findCommand->getLimit()); - ASSERT_FALSE(qr->isSingleBatch()); - ASSERT_FALSE(qr->getNToReturn()); - ASSERT_EQUALS(false, qr->isExplain()); - ASSERT_EQUALS(0, qr->getMaxTimeMS()); - ASSERT_EQUALS(false, qr->returnKey()); - ASSERT_EQUALS(false, qr->showRecordId()); - ASSERT_EQUALS(false, qr->hasReadPref()); - ASSERT_EQUALS(false, qr->isTailable()); - ASSERT_EQUALS(false, qr->isSlaveOk()); - ASSERT_EQUALS(false, qr->isNoCursorTimeout()); - ASSERT_EQUALS(false, qr->isTailableAndAwaitData()); - ASSERT_EQUALS(false, qr->isExhaust()); - ASSERT_EQUALS(false, qr->isAllowPartialResults()); - ASSERT_EQUALS(false, qr->getLegacyRuntimeConstants().has_value()); - ASSERT_EQUALS(false, qr->allowDiskUse()); + ASSERT_FALSE(findCommand->getSingleBatch()); + ASSERT_FALSE(findCommand->getNtoreturn()); + ASSERT_EQUALS(0, findCommand->getMaxTimeMS().value_or(0)); + ASSERT_EQUALS(false, findCommand->getReturnKey()); + ASSERT_EQUALS(false, findCommand->getShowRecordId()); + ASSERT_EQUALS(false, findCommand->getTailable()); + ASSERT_EQUALS(false, findCommand->getNoCursorTimeout()); + ASSERT_EQUALS(false, findCommand->getTailable() && findCommand->getAwaitData()); + ASSERT_EQUALS(false, findCommand->getAllowPartialResults()); + ASSERT_EQUALS(false, findCommand->getLegacyRuntimeConstants().has_value()); + ASSERT_EQUALS(false, findCommand->getAllowDiskUse()); } TEST(QueryRequestTest, ParseCommandAllowDiskUseTrue) { BSONObj cmdObj = fromjson("{find: 'testns', allowDiskUse: true, '$db': 'test'}"); - const bool isExplain = false; - auto result = QueryRequest::makeFromFindCommandForTests(cmdObj, isExplain); + auto result = query_request_helper::makeFromFindCommandForTests(cmdObj); - ASSERT_EQ(true, result->allowDiskUse()); + ASSERT_EQ(true, result->getAllowDiskUse()); } TEST(QueryRequestTest, ParseCommandAllowDiskUseFalse) { BSONObj cmdObj = fromjson("{find: 'testns', allowDiskUse: false, '$db': 'test'}"); - const bool isExplain = false; - auto result = QueryRequest::makeFromFindCommandForTests(cmdObj, isExplain); + auto result = query_request_helper::makeFromFindCommandForTests(cmdObj); - ASSERT_EQ(false, result->allowDiskUse()); + ASSERT_EQ(false, result->getAllowDiskUse()); } // @@ -1234,9 +1166,8 @@ TEST(QueryRequestTest, ParseFromCommandForbidExtraField) { "{find: 'testns'," "foo: {a: 1}, '$db': 'test'}"); - bool isExplain = false; ASSERT_THROWS_CODE( - QueryRequest::makeFromFindCommandForTests(cmdObj, isExplain), DBException, 40415); + query_request_helper::makeFromFindCommandForTests(cmdObj), DBException, 40415); } TEST(QueryRequestTest, ParseFromCommandForbidExtraOption) { @@ -1244,48 +1175,47 @@ TEST(QueryRequestTest, ParseFromCommandForbidExtraOption) { "{find: 'testns'," "foo: true, '$db': 'test'}"); - bool isExplain = false; ASSERT_THROWS_CODE( - QueryRequest::makeFromFindCommandForTests(cmdObj, isExplain), DBException, 40415); + query_request_helper::makeFromFindCommandForTests(cmdObj), DBException, 40415); } TEST(QueryRequestTest, ParseMaxTimeMSStringValueFails) { - BSONObj maxTimeObj = BSON(QueryRequest::cmdOptionMaxTimeMS << "foo"); - ASSERT_NOT_OK(parseMaxTimeMS(maxTimeObj[QueryRequest::cmdOptionMaxTimeMS])); + BSONObj maxTimeObj = BSON(query_request_helper::cmdOptionMaxTimeMS << "foo"); + ASSERT_NOT_OK(parseMaxTimeMS(maxTimeObj[query_request_helper::cmdOptionMaxTimeMS])); } TEST(QueryRequestTest, ParseMaxTimeMSNonIntegralValueFails) { - BSONObj maxTimeObj = BSON(QueryRequest::cmdOptionMaxTimeMS << 100.3); - ASSERT_NOT_OK(parseMaxTimeMS(maxTimeObj[QueryRequest::cmdOptionMaxTimeMS])); + BSONObj maxTimeObj = BSON(query_request_helper::cmdOptionMaxTimeMS << 100.3); + ASSERT_NOT_OK(parseMaxTimeMS(maxTimeObj[query_request_helper::cmdOptionMaxTimeMS])); } TEST(QueryRequestTest, ParseMaxTimeMSOutOfRangeDoubleFails) { - BSONObj maxTimeObj = BSON(QueryRequest::cmdOptionMaxTimeMS << 1e200); - ASSERT_NOT_OK(parseMaxTimeMS(maxTimeObj[QueryRequest::cmdOptionMaxTimeMS])); + BSONObj maxTimeObj = BSON(query_request_helper::cmdOptionMaxTimeMS << 1e200); + ASSERT_NOT_OK(parseMaxTimeMS(maxTimeObj[query_request_helper::cmdOptionMaxTimeMS])); } TEST(QueryRequestTest, ParseMaxTimeMSNegativeValueFails) { - BSONObj maxTimeObj = BSON(QueryRequest::cmdOptionMaxTimeMS << -400); - ASSERT_NOT_OK(parseMaxTimeMS(maxTimeObj[QueryRequest::cmdOptionMaxTimeMS])); + BSONObj maxTimeObj = BSON(query_request_helper::cmdOptionMaxTimeMS << -400); + ASSERT_NOT_OK(parseMaxTimeMS(maxTimeObj[query_request_helper::cmdOptionMaxTimeMS])); } TEST(QueryRequestTest, ParseMaxTimeMSZeroSucceeds) { - BSONObj maxTimeObj = BSON(QueryRequest::cmdOptionMaxTimeMS << 0); - auto maxTime = parseMaxTimeMS(maxTimeObj[QueryRequest::cmdOptionMaxTimeMS]); + BSONObj maxTimeObj = BSON(query_request_helper::cmdOptionMaxTimeMS << 0); + auto maxTime = parseMaxTimeMS(maxTimeObj[query_request_helper::cmdOptionMaxTimeMS]); ASSERT_OK(maxTime); ASSERT_EQ(maxTime.getValue(), 0); } TEST(QueryRequestTest, ParseMaxTimeMSPositiveInRangeSucceeds) { - BSONObj maxTimeObj = BSON(QueryRequest::cmdOptionMaxTimeMS << 300); - auto maxTime = parseMaxTimeMS(maxTimeObj[QueryRequest::cmdOptionMaxTimeMS]); + BSONObj maxTimeObj = BSON(query_request_helper::cmdOptionMaxTimeMS << 300); + auto maxTime = parseMaxTimeMS(maxTimeObj[query_request_helper::cmdOptionMaxTimeMS]); ASSERT_OK(maxTime); ASSERT_EQ(maxTime.getValue(), 300); } TEST(QueryRequestTest, ConvertToAggregationSucceeds) { - QueryRequest qr(testns); - auto agg = qr.asAggregationCommand(); + FindCommand findCommand(testns); + auto agg = query_request_helper::asAggregationCommand(findCommand); ASSERT_OK(agg); auto aggCmd = OpMsgRequest::fromDBAndBody(testns.db(), agg.getValue()).body; @@ -1301,9 +1231,8 @@ TEST(QueryRequestTest, ConvertToAggregationSucceeds) { } TEST(QueryRequestTest, ConvertToAggregationOmitsExplain) { - QueryRequest qr(testns); - qr.setExplain(true); - auto agg = qr.asAggregationCommand(); + FindCommand findCommand(testns); + auto agg = query_request_helper::asAggregationCommand(findCommand); ASSERT_OK(agg); auto aggCmd = OpMsgRequest::fromDBAndBody(testns.db(), agg.getValue()).body; @@ -1316,113 +1245,113 @@ TEST(QueryRequestTest, ConvertToAggregationOmitsExplain) { } TEST(QueryRequestTest, ConvertToAggregationWithHintSucceeds) { - QueryRequest qr(testns); - qr.setHint(fromjson("{a_1: -1}")); - const auto agg = qr.asAggregationCommand(); + FindCommand findCommand(testns); + findCommand.setHint(fromjson("{a_1: -1}")); + const auto agg = query_request_helper::asAggregationCommand(findCommand); ASSERT_OK(agg); auto aggCmd = OpMsgRequest::fromDBAndBody(testns.db(), agg.getValue()).body; auto ar = aggregation_request_helper::parseFromBSONForTests(testns, aggCmd); ASSERT_OK(ar.getStatus()); - ASSERT_BSONOBJ_EQ(qr.getHint(), ar.getValue().getHint().value_or(BSONObj())); + ASSERT_BSONOBJ_EQ(findCommand.getHint(), ar.getValue().getHint().value_or(BSONObj())); } TEST(QueryRequestTest, ConvertToAggregationWithMinFails) { - QueryRequest qr(testns); - qr.setMin(fromjson("{a: 1}")); - ASSERT_NOT_OK(qr.asAggregationCommand()); + FindCommand findCommand(testns); + findCommand.setMin(fromjson("{a: 1}")); + ASSERT_NOT_OK(query_request_helper::asAggregationCommand(findCommand)); } TEST(QueryRequestTest, ConvertToAggregationWithMaxFails) { - QueryRequest qr(testns); - qr.setMax(fromjson("{a: 1}")); - ASSERT_NOT_OK(qr.asAggregationCommand()); + FindCommand findCommand(testns); + findCommand.setMax(fromjson("{a: 1}")); + ASSERT_NOT_OK(query_request_helper::asAggregationCommand(findCommand)); } TEST(QueryRequestTest, ConvertToAggregationWithSingleBatchFieldFails) { - QueryRequest qr(testns); - qr.setSingleBatchField(true); - ASSERT_NOT_OK(qr.asAggregationCommand()); + FindCommand findCommand(testns); + findCommand.setSingleBatch(true); + ASSERT_NOT_OK(query_request_helper::asAggregationCommand(findCommand)); } TEST(QueryRequestTest, ConvertToAggregationWithSingleBatchFieldAndLimitFails) { - QueryRequest qr(testns); - qr.setSingleBatchField(true); - qr.setLimit(7); - ASSERT_NOT_OK(qr.asAggregationCommand()); + FindCommand findCommand(testns); + findCommand.setSingleBatch(true); + findCommand.setLimit(7); + ASSERT_NOT_OK(query_request_helper::asAggregationCommand(findCommand)); } TEST(QueryRequestTest, ConvertToAggregationWithSingleBatchFieldLimitOneSucceeds) { - QueryRequest qr(testns); - qr.setSingleBatchField(true); - qr.setLimit(1); - ASSERT_OK(qr.asAggregationCommand()); + FindCommand findCommand(testns); + findCommand.setSingleBatch(true); + findCommand.setLimit(1); + ASSERT_OK(query_request_helper::asAggregationCommand(findCommand)); } TEST(QueryRequestTest, ConvertToAggregationWithReturnKeyFails) { - QueryRequest qr(testns); - qr.setReturnKey(true); - ASSERT_NOT_OK(qr.asAggregationCommand()); + FindCommand findCommand(testns); + findCommand.setReturnKey(true); + ASSERT_NOT_OK(query_request_helper::asAggregationCommand(findCommand)); } TEST(QueryRequestTest, ConvertToAggregationWithShowRecordIdFails) { - QueryRequest qr(testns); - qr.setShowRecordId(true); - ASSERT_NOT_OK(qr.asAggregationCommand()); + FindCommand findCommand(testns); + findCommand.setShowRecordId(true); + ASSERT_NOT_OK(query_request_helper::asAggregationCommand(findCommand)); } TEST(QueryRequestTest, ConvertToAggregationWithTailableFails) { - QueryRequest qr(testns); - qr.setTailableMode(TailableModeEnum::kTailable); - ASSERT_NOT_OK(qr.asAggregationCommand()); + FindCommand findCommand(testns); + query_request_helper::setTailableMode(TailableModeEnum::kTailable, &findCommand); + ASSERT_NOT_OK(query_request_helper::asAggregationCommand(findCommand)); } TEST(QueryRequestTest, ConvertToAggregationWithNoCursorTimeoutFails) { - QueryRequest qr(testns); - qr.setNoCursorTimeout(true); - ASSERT_NOT_OK(qr.asAggregationCommand()); + FindCommand findCommand(testns); + findCommand.setNoCursorTimeout(true); + ASSERT_NOT_OK(query_request_helper::asAggregationCommand(findCommand)); } TEST(QueryRequestTest, ConvertToAggregationWithAwaitDataFails) { - QueryRequest qr(testns); - qr.setTailableMode(TailableModeEnum::kTailableAndAwaitData); - ASSERT_NOT_OK(qr.asAggregationCommand()); + FindCommand findCommand(testns); + query_request_helper::setTailableMode(TailableModeEnum::kTailableAndAwaitData, &findCommand); + ASSERT_NOT_OK(query_request_helper::asAggregationCommand(findCommand)); } TEST(QueryRequestTest, ConvertToAggregationWithAllowPartialResultsFails) { - QueryRequest qr(testns); - qr.setAllowPartialResults(true); - ASSERT_NOT_OK(qr.asAggregationCommand()); + FindCommand findCommand(testns); + findCommand.setAllowPartialResults(true); + ASSERT_NOT_OK(query_request_helper::asAggregationCommand(findCommand)); } TEST(QueryRequestTest, ConvertToAggregationWithNToReturnFails) { - QueryRequest qr(testns); - qr.setNToReturn(7); - ASSERT_NOT_OK(qr.asAggregationCommand()); + FindCommand findCommand(testns); + findCommand.setNtoreturn(7); + ASSERT_NOT_OK(query_request_helper::asAggregationCommand(findCommand)); } TEST(QueryRequestTest, ConvertToAggregationWithRequestResumeTokenFails) { - QueryRequest qr(testns); - qr.setRequestResumeToken(true); - ASSERT_NOT_OK(qr.asAggregationCommand()); + FindCommand findCommand(testns); + findCommand.setRequestResumeToken(true); + ASSERT_NOT_OK(query_request_helper::asAggregationCommand(findCommand)); } TEST(QueryRequestTest, ConvertToAggregationWithResumeAfterFails) { - QueryRequest qr(testns); + FindCommand findCommand(testns); BSONObj resumeAfter = BSON("$recordId" << 1LL); - qr.setResumeAfter(resumeAfter); - ASSERT_NOT_OK(qr.asAggregationCommand()); + findCommand.setResumeAfter(resumeAfter); + ASSERT_NOT_OK(query_request_helper::asAggregationCommand(findCommand)); } TEST(QueryRequestTest, ConvertToAggregationWithPipeline) { - QueryRequest qr(testns); - qr.setFilter(BSON("x" << 1)); - qr.setSort(BSON("y" << -1)); - qr.setLimit(3); - qr.setSkip(7); - qr.setProj(BSON("z" << 0)); - - auto agg = qr.asAggregationCommand(); + FindCommand findCommand(testns); + findCommand.setFilter(BSON("x" << 1)); + findCommand.setSort(BSON("y" << -1)); + findCommand.setLimit(3); + findCommand.setSkip(7); + findCommand.setProjection(BSON("z" << 0)); + + auto agg = query_request_helper::asAggregationCommand(findCommand); ASSERT_OK(agg); auto aggCmd = OpMsgRequest::fromDBAndBody(testns.db(), agg.getValue()).body; @@ -1447,10 +1376,10 @@ TEST(QueryRequestTest, ConvertToAggregationWithPipeline) { } TEST(QueryRequestTest, ConvertToAggregationWithBatchSize) { - QueryRequest qr(testns); - qr.setBatchSize(4); + FindCommand findCommand(testns); + findCommand.setBatchSize(4); - auto agg = qr.asAggregationCommand(); + auto agg = query_request_helper::asAggregationCommand(findCommand); ASSERT_OK(agg); auto aggCmd = OpMsgRequest::fromDBAndBody(testns.db(), agg.getValue()).body; @@ -1465,10 +1394,10 @@ TEST(QueryRequestTest, ConvertToAggregationWithBatchSize) { } TEST(QueryRequestTest, ConvertToAggregationWithMaxTimeMS) { - QueryRequest qr(testns); - qr.setMaxTimeMS(9); + FindCommand findCommand(testns); + findCommand.setMaxTimeMS(9); - auto agg = qr.asAggregationCommand(); + auto agg = query_request_helper::asAggregationCommand(findCommand); ASSERT_OK(agg); const BSONObj cmdObj = agg.getValue(); @@ -1486,9 +1415,9 @@ TEST(QueryRequestTest, ConvertToAggregationWithMaxTimeMS) { } TEST(QueryRequestTest, ConvertToAggregationWithCollationSucceeds) { - QueryRequest qr(testns); - qr.setCollation(BSON("f" << 1)); - auto agg = qr.asAggregationCommand(); + FindCommand findCommand(testns); + findCommand.setCollation(BSON("f" << 1)); + auto agg = query_request_helper::asAggregationCommand(findCommand); ASSERT_OK(agg); auto aggCmd = OpMsgRequest::fromDBAndBody(testns.db(), agg.getValue()).body; @@ -1504,24 +1433,24 @@ TEST(QueryRequestTest, ConvertToAggregationWithCollationSucceeds) { } TEST(QueryRequestTest, ConvertToAggregationWithReadOnceFails) { - QueryRequest qr(testns); - qr.setReadOnce(true); - const auto aggCmd = qr.asAggregationCommand(); + FindCommand findCommand(testns); + findCommand.setReadOnce(true); + const auto aggCmd = query_request_helper::asAggregationCommand(findCommand); ASSERT_EQ(ErrorCodes::InvalidPipelineOperator, aggCmd.getStatus().code()); } TEST(QueryRequestTest, ConvertToAggregationWithAllowSpeculativeMajorityReadFails) { - QueryRequest qr(testns); - qr.setAllowSpeculativeMajorityRead(true); - const auto aggCmd = qr.asAggregationCommand(); + FindCommand findCommand(testns); + findCommand.setAllowSpeculativeMajorityRead(true); + const auto aggCmd = query_request_helper::asAggregationCommand(findCommand); ASSERT_EQ(ErrorCodes::InvalidPipelineOperator, aggCmd.getStatus().code()); } TEST(QueryRequestTest, ConvertToAggregationWithLegacyRuntimeConstantsSucceeds) { LegacyRuntimeConstants rtc{Date_t::now(), Timestamp(1, 1)}; - QueryRequest qr(testns); - qr.setLegacyRuntimeConstants(rtc); - auto agg = qr.asAggregationCommand(); + FindCommand findCommand(testns); + findCommand.setLegacyRuntimeConstants(rtc); + auto agg = query_request_helper::asAggregationCommand(findCommand); ASSERT_OK(agg); auto aggCmd = OpMsgRequest::fromDBAndBody(testns.db(), agg.getValue()).body; @@ -1533,9 +1462,9 @@ TEST(QueryRequestTest, ConvertToAggregationWithLegacyRuntimeConstantsSucceeds) { } TEST(QueryRequestTest, ConvertToAggregationWithAllowDiskUseTrueSucceeds) { - QueryRequest qr(testns); - qr.setAllowDiskUse(true); - const auto agg = qr.asAggregationCommand(); + FindCommand findCommand(testns); + findCommand.setAllowDiskUse(true); + const auto agg = query_request_helper::asAggregationCommand(findCommand); ASSERT_OK(agg.getStatus()); auto aggCmd = OpMsgRequest::fromDBAndBody(testns.db(), agg.getValue()).body; @@ -1545,9 +1474,9 @@ TEST(QueryRequestTest, ConvertToAggregationWithAllowDiskUseTrueSucceeds) { } TEST(QueryRequestTest, ConvertToAggregationWithAllowDiskUseFalseSucceeds) { - QueryRequest qr(testns); - qr.setAllowDiskUse(false); - const auto agg = qr.asAggregationCommand(); + FindCommand findCommand(testns); + findCommand.setAllowDiskUse(false); + const auto agg = query_request_helper::asAggregationCommand(findCommand); ASSERT_OK(agg.getStatus()); auto aggCmd = OpMsgRequest::fromDBAndBody(testns.db(), agg.getValue()).body; @@ -1557,21 +1486,21 @@ TEST(QueryRequestTest, ConvertToAggregationWithAllowDiskUseFalseSucceeds) { } TEST(QueryRequestTest, ConvertToFindWithAllowDiskUseTrueSucceeds) { - QueryRequest qr(testns); - qr.setAllowDiskUse(true); - const auto findCmd = qr.asFindCommand(); + FindCommand findCommand(testns); + findCommand.setAllowDiskUse(true); + const auto findCmd = findCommand.toBSON(BSONObj()); - BSONElement elem = findCmd[QueryRequest::kAllowDiskUseField]; + BSONElement elem = findCmd[FindCommand::kAllowDiskUseFieldName]; ASSERT_EQ(true, elem.isBoolean()); ASSERT_EQ(true, elem.Bool()); } TEST(QueryRequestTest, ConvertToFindWithAllowDiskUseFalseSucceeds) { - QueryRequest qr(testns); - qr.setAllowDiskUse(false); - const auto findCmd = qr.asFindCommand(); + FindCommand findCommand(testns); + findCommand.setAllowDiskUse(false); + const auto findCmd = findCommand.toBSON(BSONObj()); - ASSERT_FALSE(findCmd[QueryRequest::kAllowDiskUseField].booleanSafe()); + ASSERT_FALSE(findCmd[FindCommand::kAllowDiskUseFieldName].booleanSafe()); } TEST(QueryRequestTest, ParseFromLegacyQuery) { @@ -1586,28 +1515,25 @@ TEST(QueryRequestTest, ParseFromLegacyQuery) { $min: {x: 'min'}, $max: {x: 'max'} })"); - unique_ptr<QueryRequest> qr( - std::move(QueryRequest::fromLegacyQuery( - nss, queryObj, BSON("proj" << 1), kSkip, kNToReturn, QueryOption_Exhaust) - .getValue())); - - ASSERT_EQ(qr->nss(), nss); - ASSERT_BSONOBJ_EQ(qr->getFilter(), fromjson("{query: 1}")); - ASSERT_BSONOBJ_EQ(qr->getProj(), fromjson("{proj: 1}")); - ASSERT_BSONOBJ_EQ(qr->getSort(), fromjson("{sort: 1}")); - ASSERT_BSONOBJ_EQ(qr->getHint(), fromjson("{hint: 1}")); - ASSERT_BSONOBJ_EQ(qr->getMin(), fromjson("{x: 'min'}")); - ASSERT_BSONOBJ_EQ(qr->getMax(), fromjson("{x: 'max'}")); - ASSERT_EQ(qr->getSkip(), boost::optional<int64_t>(kSkip)); - ASSERT_EQ(qr->getNToReturn(), boost::optional<int64_t>(kNToReturn)); - ASSERT_EQ(qr->isSingleBatch(), false); - ASSERT_EQ(qr->isExplain(), false); - ASSERT_EQ(qr->isSlaveOk(), false); - ASSERT_EQ(qr->isNoCursorTimeout(), false); - ASSERT_EQ(qr->isTailable(), false); - ASSERT_EQ(qr->isExhaust(), true); - ASSERT_EQ(qr->isAllowPartialResults(), false); - ASSERT_EQ(qr->getOptions(), QueryOption_Exhaust); + + bool explain = false; + unique_ptr<FindCommand> findCommand(assertGet(query_request_helper::fromLegacyQuery( + nss, queryObj, BSON("proj" << 1), kSkip, kNToReturn, QueryOption_Exhaust, &explain))); + + ASSERT_EQ(*findCommand->getNamespaceOrUUID().nss(), nss); + ASSERT_EQ(explain, false); + ASSERT_BSONOBJ_EQ(findCommand->getFilter(), fromjson("{query: 1}")); + ASSERT_BSONOBJ_EQ(findCommand->getProjection(), fromjson("{proj: 1}")); + ASSERT_BSONOBJ_EQ(findCommand->getSort(), fromjson("{sort: 1}")); + ASSERT_BSONOBJ_EQ(findCommand->getHint(), fromjson("{hint: 1}")); + ASSERT_BSONOBJ_EQ(findCommand->getMin(), fromjson("{x: 'min'}")); + ASSERT_BSONOBJ_EQ(findCommand->getMax(), fromjson("{x: 'max'}")); + ASSERT_EQ(findCommand->getSkip(), boost::optional<int64_t>(kSkip)); + ASSERT_EQ(findCommand->getNtoreturn(), boost::optional<int64_t>(kNToReturn)); + ASSERT_EQ(findCommand->getSingleBatch(), false); + ASSERT_EQ(findCommand->getNoCursorTimeout(), false); + ASSERT_EQ(findCommand->getTailable(), false); + ASSERT_EQ(findCommand->getAllowPartialResults(), false); } TEST(QueryRequestTest, ParseFromLegacyQueryOplogReplayFlagAllowed) { @@ -1620,13 +1546,16 @@ TEST(QueryRequestTest, ParseFromLegacyQueryOplogReplayFlagAllowed) { // Test that parsing succeeds even if the oplog replay bit is set in the OP_QUERY message. This // flag may be set by old clients. auto options = QueryOption_OplogReplay_DEPRECATED; - auto qr = - QueryRequest::fromLegacyQuery(nss, queryObj, projectionObj, nToSkip, nToReturn, options); - ASSERT_OK(qr.getStatus()); + bool explain = false; + unique_ptr<FindCommand> findCommand(assertGet(query_request_helper::fromLegacyQuery( + nss, queryObj, projectionObj, nToSkip, nToReturn, options, &explain))); - // Verify that if we reserialize the QueryRequest as a find command, the 'oplogReplay' field + // Verify that if we reserialize the find command, the 'oplogReplay' field // does not appear. - auto reserialized = qr.getValue()->asFindCommand(); + BSONObjBuilder bob; + findCommand->serialize(BSONObj(), &bob); + auto reserialized = bob.obj(); + ASSERT_BSONOBJ_EQ(reserialized, BSON("find" << "testns" @@ -1639,11 +1568,12 @@ TEST(QueryRequestTest, ParseFromLegacyQueryUnwrapped) { foo: 1 })"); const NamespaceString nss("test.testns"); - unique_ptr<QueryRequest> qr(assertGet( - QueryRequest::fromLegacyQuery(nss, queryObj, BSONObj(), 0, 0, QueryOption_Exhaust))); + bool explain = false; + unique_ptr<FindCommand> findCommand(assertGet(query_request_helper::fromLegacyQuery( + nss, queryObj, BSONObj(), 0, 0, QueryOption_Exhaust, &explain))); - ASSERT_EQ(qr->nss(), nss); - ASSERT_BSONOBJ_EQ(qr->getFilter(), fromjson("{foo: 1}")); + ASSERT_EQ(*findCommand->getNamespaceOrUUID().nss(), nss); + ASSERT_BSONOBJ_EQ(findCommand->getFilter(), fromjson("{foo: 1}")); } TEST(QueryRequestTest, ParseFromLegacyQueryTooNegativeNToReturn) { @@ -1652,10 +1582,15 @@ TEST(QueryRequestTest, ParseFromLegacyQueryTooNegativeNToReturn) { })"); const NamespaceString nss("test.testns"); - ASSERT_NOT_OK( - QueryRequest::fromLegacyQuery( - nss, queryObj, BSONObj(), 0, std::numeric_limits<int>::min(), QueryOption_Exhaust) - .getStatus()); + bool explain = false; + ASSERT_NOT_OK(query_request_helper::fromLegacyQuery(nss, + queryObj, + BSONObj(), + 0, + std::numeric_limits<int>::min(), + QueryOption_Exhaust, + &explain) + .getStatus()); } class QueryRequestTest : public ServiceContextTest {}; @@ -1665,11 +1600,11 @@ TEST_F(QueryRequestTest, ParseFromUUID) { NamespaceStringOrUUID nssOrUUID("test", uuid); - QueryRequest qr(nssOrUUID); + FindCommand findCommand(nssOrUUID); const NamespaceString nss("test.testns"); // Ensure a call to refreshNSS succeeds. - qr.refreshNSS(nss); - ASSERT_EQ(nss, qr.nss()); + query_request_helper::refreshNSS(nss, &findCommand); + ASSERT_EQ(nss, *findCommand.getNamespaceOrUUID().nss()); } } // namespace diff --git a/src/mongo/db/query/query_settings.cpp b/src/mongo/db/query/query_settings.cpp index 5060d6d9ac8..d3552edd3c0 100644 --- a/src/mongo/db/query/query_settings.cpp +++ b/src/mongo/db/query/query_settings.cpp @@ -101,10 +101,10 @@ std::vector<AllowedIndexEntry> QuerySettings::getAllAllowedIndices() const { void QuerySettings::setAllowedIndices(const CanonicalQuery& canonicalQuery, const BSONObjSet& indexKeyPatterns, const stdx::unordered_set<std::string>& indexNames) { - const QueryRequest& qr = canonicalQuery.getQueryRequest(); - const BSONObj& query = qr.getFilter(); - const BSONObj& sort = qr.getSort(); - const BSONObj& projection = qr.getProj(); + const FindCommand& findCommand = canonicalQuery.getFindCommand(); + const BSONObj& query = findCommand.getFilter(); + const BSONObj& sort = findCommand.getSort(); + const BSONObj& projection = findCommand.getProjection(); const auto key = canonicalQuery.encodeKey(); const BSONObj collation = canonicalQuery.getCollator() ? canonicalQuery.getCollator()->getSpec().toBSON() : BSONObj(); diff --git a/src/mongo/db/query/sbe_stage_builder.cpp b/src/mongo/db/query/sbe_stage_builder.cpp index fc041266768..c7da41d5daa 100644 --- a/src/mongo/db/query/sbe_stage_builder.cpp +++ b/src/mongo/db/query/sbe_stage_builder.cpp @@ -1416,7 +1416,7 @@ std::pair<std::unique_ptr<sbe::PlanStage>, PlanStageSlots> SlotBasedStageBuilder case STAGE_COLLSCAN: case STAGE_LIMIT: case STAGE_SKIP: - if (_cq.getQueryRequest().isTailable() && + if (_cq.getFindCommand().getTailable() && !reqs.getIsBuildingUnionForTailableCollScan()) { auto childReqs = reqs; childReqs.setIsBuildingUnionForTailableCollScan(true); diff --git a/src/mongo/db/query/sbe_stage_builder_test_fixture.cpp b/src/mongo/db/query/sbe_stage_builder_test_fixture.cpp index d832a3e30b6..fad3a4cd63a 100644 --- a/src/mongo/db/query/sbe_stage_builder_test_fixture.cpp +++ b/src/mongo/db/query/sbe_stage_builder_test_fixture.cpp @@ -49,9 +49,10 @@ SbeStageBuilderTestFixture::buildPlanStage( std::unique_ptr<QuerySolution> querySolution, bool hasRecordId, std::unique_ptr<ShardFiltererFactoryInterface> shardFiltererInterface) { - auto qr = std::make_unique<QueryRequest>(_nss); + auto findCommand = std::make_unique<FindCommand>(_nss); const boost::intrusive_ptr<ExpressionContext> expCtx(new ExpressionContextForTest(_nss)); - auto statusWithCQ = CanonicalQuery::canonicalize(opCtx(), std::move(qr), expCtx); + auto statusWithCQ = + CanonicalQuery::canonicalize(opCtx(), std::move(findCommand), false, expCtx); ASSERT_OK(statusWithCQ.getStatus()); stage_builder::SlotBasedStageBuilder builder{opCtx(), diff --git a/src/mongo/db/s/config/config_server_test_fixture.cpp b/src/mongo/db/s/config/config_server_test_fixture.cpp index a2fba05a49b..228ac592d20 100644 --- a/src/mongo/db/s/config/config_server_test_fixture.cpp +++ b/src/mongo/db/s/config/config_server_test_fixture.cpp @@ -45,7 +45,7 @@ #include "mongo/db/op_observer.h" #include "mongo/db/ops/write_ops.h" #include "mongo/db/query/cursor_response.h" -#include "mongo/db/query/query_request.h" +#include "mongo/db/query/query_request_helper.h" #include "mongo/db/repl/oplog.h" #include "mongo/db/repl/read_concern_args.h" #include "mongo/db/repl/repl_settings.h" diff --git a/src/mongo/db/s/config/sharding_catalog_manager.cpp b/src/mongo/db/s/config/sharding_catalog_manager.cpp index 271fc1d52b8..ff00b91fbeb 100644 --- a/src/mongo/db/s/config/sharding_catalog_manager.cpp +++ b/src/mongo/db/s/config/sharding_catalog_manager.cpp @@ -40,7 +40,7 @@ #include "mongo/db/error_labels.h" #include "mongo/db/operation_context.h" #include "mongo/db/ops/write_ops.h" -#include "mongo/db/query/query_request.h" +#include "mongo/db/query/query_request_helper.h" #include "mongo/db/s/balancer/type_migration.h" #include "mongo/db/s/type_lockpings.h" #include "mongo/db/s/type_locks.h" @@ -101,15 +101,14 @@ OpMsg runCommandInLocalTxn(OperationContext* opCtx, void startTransactionWithNoopFind(OperationContext* opCtx, const NamespaceString& nss, TxnNumber txnNumber) { - BSONObjBuilder findCmdBuilder; - QueryRequest qr(nss); - qr.setBatchSize(0); - qr.setSingleBatchField(true); - qr.asFindCommand(&findCmdBuilder); - - auto res = runCommandInLocalTxn( - opCtx, nss.db(), true /*startTransaction*/, txnNumber, findCmdBuilder.done()) - .body; + FindCommand findCommand(nss); + findCommand.setBatchSize(0); + findCommand.setSingleBatch(true); + + auto res = + runCommandInLocalTxn( + opCtx, nss.db(), true /*startTransaction*/, txnNumber, findCommand.toBSON(BSONObj())) + .body; uassertStatusOK(getStatusFromCommandResult(res)); } diff --git a/src/mongo/db/s/config/sharding_catalog_manager_create_database_test.cpp b/src/mongo/db/s/config/sharding_catalog_manager_create_database_test.cpp index 8b213721ba5..2d0c3ea0b36 100644 --- a/src/mongo/db/s/config/sharding_catalog_manager_create_database_test.cpp +++ b/src/mongo/db/s/config/sharding_catalog_manager_create_database_test.cpp @@ -34,7 +34,7 @@ #include "mongo/client/remote_command_targeter_factory_mock.h" #include "mongo/client/remote_command_targeter_mock.h" #include "mongo/db/commands.h" -#include "mongo/db/query/query_request.h" +#include "mongo/db/query/query_request_helper.h" #include "mongo/db/repl/read_concern_args.h" #include "mongo/db/s/config/config_server_test_fixture.h" #include "mongo/db/s/config/sharding_catalog_manager.h" diff --git a/src/mongo/db/s/config/sharding_catalog_manager_enable_sharding_test.cpp b/src/mongo/db/s/config/sharding_catalog_manager_enable_sharding_test.cpp index 6a09a6f1358..ae41929f63e 100644 --- a/src/mongo/db/s/config/sharding_catalog_manager_enable_sharding_test.cpp +++ b/src/mongo/db/s/config/sharding_catalog_manager_enable_sharding_test.cpp @@ -34,7 +34,7 @@ #include "mongo/client/remote_command_targeter_factory_mock.h" #include "mongo/client/remote_command_targeter_mock.h" #include "mongo/db/commands.h" -#include "mongo/db/query/query_request.h" +#include "mongo/db/query/query_request_helper.h" #include "mongo/db/repl/read_concern_args.h" #include "mongo/db/s/config/config_server_test_fixture.h" #include "mongo/db/s/config/sharding_catalog_manager.h" diff --git a/src/mongo/db/s/resharding/resharding_collection_cloner.cpp b/src/mongo/db/s/resharding/resharding_collection_cloner.cpp index 4be972ee221..947c9834139 100644 --- a/src/mongo/db/s/resharding/resharding_collection_cloner.cpp +++ b/src/mongo/db/s/resharding/resharding_collection_cloner.cpp @@ -49,7 +49,7 @@ #include "mongo/db/pipeline/document_source_match.h" #include "mongo/db/pipeline/document_source_replace_root.h" #include "mongo/db/pipeline/sharded_agg_helpers.h" -#include "mongo/db/query/query_request.h" +#include "mongo/db/query/query_request_helper.h" #include "mongo/db/s/resharding/resharding_server_parameters_gen.h" #include "mongo/db/s/resharding_util.h" #include "mongo/db/service_context.h" @@ -218,11 +218,12 @@ Value ReshardingCollectionCloner::_findHighestInsertedId(OperationContext* opCtx << "' did not already exist", outputColl); - auto qr = std::make_unique<QueryRequest>(_outputNss); - qr->setLimit(1); - qr->setSort(BSON("_id" << -1)); + auto findCommand = std::make_unique<FindCommand>(_outputNss); + findCommand->setLimit(1); + findCommand->setSort(BSON("_id" << -1)); - auto recordId = Helpers::findOne(opCtx, *outputColl, std::move(qr), true /* requireIndex */); + auto recordId = + Helpers::findOne(opCtx, *outputColl, std::move(findCommand), true /* requireIndex */); if (recordId.isNull()) { return Value{}; } diff --git a/src/mongo/db/s/sharding_mongod_test_fixture.cpp b/src/mongo/db/s/sharding_mongod_test_fixture.cpp index 7de0b0b5825..11529d01e58 100644 --- a/src/mongo/db/s/sharding_mongod_test_fixture.cpp +++ b/src/mongo/db/s/sharding_mongod_test_fixture.cpp @@ -43,7 +43,7 @@ #include "mongo/db/namespace_string.h" #include "mongo/db/op_observer_registry.h" #include "mongo/db/query/cursor_response.h" -#include "mongo/db/query/query_request.h" +#include "mongo/db/query/query_request_helper.h" #include "mongo/db/repl/drop_pending_collection_reaper.h" #include "mongo/db/repl/oplog.h" #include "mongo/db/repl/read_concern_args.h" diff --git a/src/mongo/db/service_entry_point_common.cpp b/src/mongo/db/service_entry_point_common.cpp index e918fb3d592..79466de4bb5 100644 --- a/src/mongo/db/service_entry_point_common.cpp +++ b/src/mongo/db/service_entry_point_common.cpp @@ -1343,9 +1343,9 @@ void ExecCommandDatabase::_initiateCommand() { StringMap<int> topLevelFields; for (auto&& element : request.body) { StringData fieldName = element.fieldNameStringData(); - if (fieldName == QueryRequest::cmdOptionMaxTimeMS) { + if (fieldName == query_request_helper::cmdOptionMaxTimeMS) { cmdOptionMaxTimeMSField = element; - } else if (fieldName == QueryRequest::kMaxTimeMSOpOnlyField) { + } else if (fieldName == query_request_helper::kMaxTimeMSOpOnlyField) { uassert(ErrorCodes::InvalidOptions, "Can not specify maxTimeMSOpOnly for non internal clients", _isInternalClient()); @@ -1356,7 +1356,7 @@ void ExecCommandDatabase::_initiateCommand() { helpField = element; } else if (fieldName == "comment") { opCtx->setComment(element.wrap()); - } else if (fieldName == QueryRequest::queryOptionMaxTimeMS) { + } else if (fieldName == query_request_helper::queryOptionMaxTimeMS) { uasserted(ErrorCodes::InvalidOptions, "no such command option $maxTimeMs; use maxTimeMS instead"); } diff --git a/src/mongo/db/transaction_history_iterator.cpp b/src/mongo/db/transaction_history_iterator.cpp index e0584763df7..70f38a7ed01 100644 --- a/src/mongo/db/transaction_history_iterator.cpp +++ b/src/mongo/db/transaction_history_iterator.cpp @@ -35,7 +35,7 @@ #include "mongo/db/namespace_string.h" #include "mongo/db/operation_context.h" #include "mongo/db/query/get_executor.h" -#include "mongo/db/query/query_request.h" +#include "mongo/db/query/query_request_helper.h" #include "mongo/db/repl/local_oplog_info.h" #include "mongo/db/repl/oplog_entry.h" #include "mongo/db/transaction_history_iterator.h" @@ -56,18 +56,19 @@ BSONObj findOneOplogEntry(OperationContext* opCtx, BSONObj oplogBSON; invariant(!opTime.isNull()); - auto qr = std::make_unique<QueryRequest>(NamespaceString::kRsOplogNamespace); - qr->setFilter(opTime.asQuery()); + auto findCommand = std::make_unique<FindCommand>(NamespaceString::kRsOplogNamespace); + findCommand->setFilter(opTime.asQuery()); if (prevOpOnly) { - qr->setProj( + findCommand->setProjection( BSON("_id" << 0 << repl::OplogEntry::kPrevWriteOpTimeInTransactionFieldName << 1LL)); } const boost::intrusive_ptr<ExpressionContext> expCtx; auto statusWithCQ = CanonicalQuery::canonicalize(opCtx, - std::move(qr), + std::move(findCommand), + false, expCtx, ExtensionsCallbackNoop(), MatchExpressionParser::kBanAllSpecialFeatures); diff --git a/src/mongo/db/ttl.cpp b/src/mongo/db/ttl.cpp index 993af2e55bf..93be4678360 100644 --- a/src/mongo/db/ttl.cpp +++ b/src/mongo/db/ttl.cpp @@ -366,9 +366,9 @@ private: const char* keyFieldName = key.firstElement().fieldName(); BSONObj query = BSON(keyFieldName << BSON("$gte" << kDawnOfTime << "$lte" << expirationTime)); - auto qr = std::make_unique<QueryRequest>(collectionNSS); - qr->setFilter(query); - auto canonicalQuery = CanonicalQuery::canonicalize(opCtx, std::move(qr)); + auto findCommand = std::make_unique<FindCommand>(collectionNSS); + findCommand->setFilter(query); + auto canonicalQuery = CanonicalQuery::canonicalize(opCtx, std::move(findCommand)); invariant(canonicalQuery.getStatus()); auto params = std::make_unique<DeleteStageParams>(); diff --git a/src/mongo/db/update/update_driver.cpp b/src/mongo/db/update/update_driver.cpp index 1c402f2a24d..156830388a8 100644 --- a/src/mongo/db/update/update_driver.cpp +++ b/src/mongo/db/update/update_driver.cpp @@ -195,14 +195,15 @@ Status UpdateDriver::populateDocumentWithQueryFields(OperationContext* opCtx, // We canonicalize the query to collapse $and/$or, and the namespace is not needed. Also, // because this is for the upsert case, where we insert a new document if one was not found, the // $where/$text clauses do not make sense, hence empty ExtensionsCallback. - auto qr = std::make_unique<QueryRequest>(NamespaceString("")); - qr->setFilter(query); + auto findCommand = std::make_unique<FindCommand>(NamespaceString("")); + findCommand->setFilter(query); const boost::intrusive_ptr<ExpressionContext> expCtx; // $expr is not allowed in the query for an upsert, since it is not clear what the equality // extraction behavior for $expr should be. auto statusWithCQ = CanonicalQuery::canonicalize(opCtx, - std::move(qr), + std::move(findCommand), + false, expCtx, ExtensionsCallbackNoop(), MatchExpressionParser::kAllowAllSpecialFeatures & diff --git a/src/mongo/dbtests/documentsourcetests.cpp b/src/mongo/dbtests/documentsourcetests.cpp index 60cb1965eba..811e8bf6882 100644 --- a/src/mongo/dbtests/documentsourcetests.cpp +++ b/src/mongo/dbtests/documentsourcetests.cpp @@ -93,11 +93,11 @@ protected: dbtests::WriteContextForTests ctx(opCtx(), nss.ns()); _coll = ctx.getCollection(); - auto qr = std::make_unique<QueryRequest>(nss); + auto findCommand = std::make_unique<FindCommand>(nss); if (hint) { - qr->setHint(*hint); + findCommand->setHint(*hint); } - auto cq = uassertStatusOK(CanonicalQuery::canonicalize(opCtx(), std::move(qr))); + auto cq = uassertStatusOK(CanonicalQuery::canonicalize(opCtx(), std::move(findCommand))); auto exec = uassertStatusOK(getExecutor(opCtx(), &_coll, @@ -312,11 +312,12 @@ TEST_F(DocumentSourceCursorTest, TailableAwaitDataCursorShouldErrorAfterTimeout) collScanParams, workingSet.get(), matchExpression.get()); - auto queryRequest = std::make_unique<QueryRequest>(nss); - queryRequest->setFilter(filter); - queryRequest->setTailableMode(TailableModeEnum::kTailableAndAwaitData); + auto findCommand = std::make_unique<FindCommand>(nss); + findCommand->setFilter(filter); + query_request_helper::setTailableMode(TailableModeEnum::kTailableAndAwaitData, + findCommand.get()); auto canonicalQuery = unittest::assertGet( - CanonicalQuery::canonicalize(opCtx(), std::move(queryRequest), nullptr)); + CanonicalQuery::canonicalize(opCtx(), std::move(findCommand), false, nullptr)); auto planExecutor = uassertStatusOK(plan_executor_factory::make(std::move(canonicalQuery), std::move(workingSet), @@ -355,10 +356,10 @@ TEST_F(DocumentSourceCursorTest, NonAwaitDataCursorShouldErrorAfterTimeout) { collScanParams, workingSet.get(), matchExpression.get()); - auto queryRequest = std::make_unique<QueryRequest>(nss); - queryRequest->setFilter(filter); + auto findCommand = std::make_unique<FindCommand>(nss); + findCommand->setFilter(filter); auto canonicalQuery = unittest::assertGet( - CanonicalQuery::canonicalize(opCtx(), std::move(queryRequest), nullptr)); + CanonicalQuery::canonicalize(opCtx(), std::move(findCommand), false, nullptr)); auto planExecutor = uassertStatusOK(plan_executor_factory::make(std::move(canonicalQuery), std::move(workingSet), @@ -407,11 +408,12 @@ TEST_F(DocumentSourceCursorTest, TailableAwaitDataCursorShouldErrorAfterBeingKil collScanParams, workingSet.get(), matchExpression.get()); - auto queryRequest = std::make_unique<QueryRequest>(nss); - queryRequest->setFilter(filter); - queryRequest->setTailableMode(TailableModeEnum::kTailableAndAwaitData); + auto findCommand = std::make_unique<FindCommand>(nss); + findCommand->setFilter(filter); + query_request_helper::setTailableMode(TailableModeEnum::kTailableAndAwaitData, + findCommand.get()); auto canonicalQuery = unittest::assertGet( - CanonicalQuery::canonicalize(opCtx(), std::move(queryRequest), nullptr)); + CanonicalQuery::canonicalize(opCtx(), std::move(findCommand), false, nullptr)); auto planExecutor = uassertStatusOK( plan_executor_factory::make(std::move(canonicalQuery), std::move(workingSet), @@ -449,10 +451,10 @@ TEST_F(DocumentSourceCursorTest, NormalCursorShouldErrorAfterBeingKilled) { collScanParams, workingSet.get(), matchExpression.get()); - auto queryRequest = std::make_unique<QueryRequest>(nss); - queryRequest->setFilter(filter); + auto findCommand = std::make_unique<FindCommand>(nss); + findCommand->setFilter(filter); auto canonicalQuery = unittest::assertGet( - CanonicalQuery::canonicalize(opCtx(), std::move(queryRequest), nullptr)); + CanonicalQuery::canonicalize(opCtx(), std::move(findCommand), false, nullptr)); auto planExecutor = uassertStatusOK( plan_executor_factory::make(std::move(canonicalQuery), std::move(workingSet), diff --git a/src/mongo/dbtests/plan_executor_invalidation_test.cpp b/src/mongo/dbtests/plan_executor_invalidation_test.cpp index a3a92ab4854..e77c5d59c1a 100644 --- a/src/mongo/dbtests/plan_executor_invalidation_test.cpp +++ b/src/mongo/dbtests/plan_executor_invalidation_test.cpp @@ -81,8 +81,8 @@ public: new CollectionScan(_expCtx.get(), collection(), params, ws.get(), nullptr)); // Create a plan executor to hold it - auto qr = std::make_unique<QueryRequest>(nss); - auto statusWithCQ = CanonicalQuery::canonicalize(&_opCtx, std::move(qr)); + auto findCommand = std::make_unique<FindCommand>(nss); + auto statusWithCQ = CanonicalQuery::canonicalize(&_opCtx, std::move(findCommand)); ASSERT_OK(statusWithCQ.getStatus()); std::unique_ptr<CanonicalQuery> cq = std::move(statusWithCQ.getValue()); diff --git a/src/mongo/dbtests/plan_ranking.cpp b/src/mongo/dbtests/plan_ranking.cpp index 79f977d1409..461fc617b12 100644 --- a/src/mongo/dbtests/plan_ranking.cpp +++ b/src/mongo/dbtests/plan_ranking.cpp @@ -230,10 +230,10 @@ public: addIndex(BSON("d" << 1)); // Query: find({a: 1}).sort({d: 1}) - auto qr = std::make_unique<QueryRequest>(nss); - qr->setFilter(BSON("a" << 1)); - qr->setSort(BSON("d" << 1)); - auto statusWithCQ = CanonicalQuery::canonicalize(opCtx(), std::move(qr)); + auto findCommand = std::make_unique<FindCommand>(nss); + findCommand->setFilter(BSON("a" << 1)); + findCommand->setSort(BSON("d" << 1)); + auto statusWithCQ = CanonicalQuery::canonicalize(opCtx(), std::move(findCommand)); ASSERT_OK(statusWithCQ.getStatus()); unique_ptr<CanonicalQuery> cq = std::move(statusWithCQ.getValue()); ASSERT(cq); @@ -287,9 +287,9 @@ public: // Run the query {a:4, b:1}. { - auto qr = std::make_unique<QueryRequest>(nss); - qr->setFilter(BSON("a" << 100 << "b" << 1)); - auto statusWithCQ = CanonicalQuery::canonicalize(opCtx(), std::move(qr)); + auto findCommand = std::make_unique<FindCommand>(nss); + findCommand->setFilter(BSON("a" << 100 << "b" << 1)); + auto statusWithCQ = CanonicalQuery::canonicalize(opCtx(), std::move(findCommand)); verify(statusWithCQ.isOK()); cq = std::move(statusWithCQ.getValue()); ASSERT(cq.get()); @@ -306,9 +306,9 @@ public: // And run the same query again. { - auto qr = std::make_unique<QueryRequest>(nss); - qr->setFilter(BSON("a" << 100 << "b" << 1)); - auto statusWithCQ = CanonicalQuery::canonicalize(opCtx(), std::move(qr)); + auto findCommand = std::make_unique<FindCommand>(nss); + findCommand->setFilter(BSON("a" << 100 << "b" << 1)); + auto statusWithCQ = CanonicalQuery::canonicalize(opCtx(), std::move(findCommand)); verify(statusWithCQ.isOK()); cq = std::move(statusWithCQ.getValue()); } @@ -341,9 +341,9 @@ public: addIndex(BSON("b" << 1)); // Run the query {a:1, b:{$gt:1}. - auto qr = std::make_unique<QueryRequest>(nss); - qr->setFilter(BSON("a" << 1 << "b" << BSON("$gt" << 1))); - auto statusWithCQ = CanonicalQuery::canonicalize(opCtx(), std::move(qr)); + auto findCommand = std::make_unique<FindCommand>(nss); + findCommand->setFilter(BSON("a" << 1 << "b" << BSON("$gt" << 1))); + auto statusWithCQ = CanonicalQuery::canonicalize(opCtx(), std::move(findCommand)); verify(statusWithCQ.isOK()); unique_ptr<CanonicalQuery> cq = std::move(statusWithCQ.getValue()); ASSERT(nullptr != cq.get()); @@ -381,10 +381,10 @@ public: addIndex(BSON("a" << 1 << "b" << 1)); // Query for a==27 with projection that wants 'a' and 'b'. - auto qr = std::make_unique<QueryRequest>(nss); - qr->setFilter(BSON("a" << 27)); - qr->setProj(BSON("_id" << 0 << "a" << 1 << "b" << 1)); - auto statusWithCQ = CanonicalQuery::canonicalize(opCtx(), std::move(qr)); + auto findCommand = std::make_unique<FindCommand>(nss); + findCommand->setFilter(BSON("a" << 27)); + findCommand->setProjection(BSON("_id" << 0 << "a" << 1 << "b" << 1)); + auto statusWithCQ = CanonicalQuery::canonicalize(opCtx(), std::move(findCommand)); ASSERT_OK(statusWithCQ.getStatus()); unique_ptr<CanonicalQuery> cq = std::move(statusWithCQ.getValue()); ASSERT(nullptr != cq.get()); @@ -416,9 +416,9 @@ public: addIndex(BSON("b" << 1)); // There is no data that matches this query but we don't know that until EOF. - auto qr = std::make_unique<QueryRequest>(nss); - qr->setFilter(BSON("a" << 1 << "b" << 1 << "c" << 99)); - auto statusWithCQ = CanonicalQuery::canonicalize(opCtx(), std::move(qr)); + auto findCommand = std::make_unique<FindCommand>(nss); + findCommand->setFilter(BSON("a" << 1 << "b" << 1 << "c" << 99)); + auto statusWithCQ = CanonicalQuery::canonicalize(opCtx(), std::move(findCommand)); ASSERT_OK(statusWithCQ.getStatus()); unique_ptr<CanonicalQuery> cq = std::move(statusWithCQ.getValue()); ASSERT(nullptr != cq.get()); @@ -453,11 +453,11 @@ public: // There is no data that matches this query ({a:2}). Both scans will hit EOF before // returning any data. - auto qr = std::make_unique<QueryRequest>(nss); - qr->setFilter(BSON("a" << 2)); - qr->setProj(BSON("_id" << 0 << "a" << 1 << "b" << 1)); + auto findCommand = std::make_unique<FindCommand>(nss); + findCommand->setFilter(BSON("a" << 2)); + findCommand->setProjection(BSON("_id" << 0 << "a" << 1 << "b" << 1)); - auto statusWithCQ = CanonicalQuery::canonicalize(opCtx(), std::move(qr)); + auto statusWithCQ = CanonicalQuery::canonicalize(opCtx(), std::move(findCommand)); ASSERT_OK(statusWithCQ.getStatus()); unique_ptr<CanonicalQuery> cq = std::move(statusWithCQ.getValue()); ASSERT(nullptr != cq.get()); @@ -488,9 +488,9 @@ public: addIndex(BSON("b" << 1)); // Run the query {a:N+1, b:1}. (No such document.) - auto qr = std::make_unique<QueryRequest>(nss); - qr->setFilter(BSON("a" << N + 1 << "b" << 1)); - auto statusWithCQ = CanonicalQuery::canonicalize(opCtx(), std::move(qr)); + auto findCommand = std::make_unique<FindCommand>(nss); + findCommand->setFilter(BSON("a" << N + 1 << "b" << 1)); + auto statusWithCQ = CanonicalQuery::canonicalize(opCtx(), std::move(findCommand)); verify(statusWithCQ.isOK()); unique_ptr<CanonicalQuery> cq = std::move(statusWithCQ.getValue()); ASSERT(nullptr != cq.get()); @@ -524,9 +524,9 @@ public: addIndex(BSON("b" << 1)); // Run the query {a:N+1, b:1}. (No such document.) - auto qr = std::make_unique<QueryRequest>(nss); - qr->setFilter(BSON("a" << BSON("$gte" << N + 1) << "b" << 1)); - auto statusWithCQ = CanonicalQuery::canonicalize(opCtx(), std::move(qr)); + auto findCommand = std::make_unique<FindCommand>(nss); + findCommand->setFilter(BSON("a" << BSON("$gte" << N + 1) << "b" << 1)); + auto statusWithCQ = CanonicalQuery::canonicalize(opCtx(), std::move(findCommand)); verify(statusWithCQ.isOK()); unique_ptr<CanonicalQuery> cq = std::move(statusWithCQ.getValue()); ASSERT(nullptr != cq.get()); @@ -553,10 +553,10 @@ public: // Run a query with a sort. The blocking sort won't produce any data during the // evaluation period. - auto qr = std::make_unique<QueryRequest>(nss); - qr->setFilter(BSON("_id" << BSON("$gte" << 20 << "$lte" << 200))); - qr->setSort(BSON("c" << 1)); - auto statusWithCQ = CanonicalQuery::canonicalize(opCtx(), std::move(qr)); + auto findCommand = std::make_unique<FindCommand>(nss); + findCommand->setFilter(BSON("_id" << BSON("$gte" << 20 << "$lte" << 200))); + findCommand->setSort(BSON("c" << 1)); + auto statusWithCQ = CanonicalQuery::canonicalize(opCtx(), std::move(findCommand)); ASSERT_OK(statusWithCQ.getStatus()); unique_ptr<CanonicalQuery> cq = std::move(statusWithCQ.getValue()); @@ -583,9 +583,9 @@ public: } // Look for A Space Odyssey. - auto qr = std::make_unique<QueryRequest>(nss); - qr->setFilter(BSON("foo" << 2001)); - auto statusWithCQ = CanonicalQuery::canonicalize(opCtx(), std::move(qr)); + auto findCommand = std::make_unique<FindCommand>(nss); + findCommand->setFilter(BSON("foo" << 2001)); + auto statusWithCQ = CanonicalQuery::canonicalize(opCtx(), std::move(findCommand)); verify(statusWithCQ.isOK()); unique_ptr<CanonicalQuery> cq = std::move(statusWithCQ.getValue()); ASSERT(nullptr != cq.get()); @@ -616,10 +616,10 @@ public: addIndex(BSON("d" << 1 << "e" << 1)); // Query: find({a: 1}).sort({d: 1}) - auto qr = std::make_unique<QueryRequest>(nss); - qr->setFilter(BSON("a" << 1)); - qr->setSort(BSON("d" << 1)); - auto statusWithCQ = CanonicalQuery::canonicalize(opCtx(), std::move(qr)); + auto findCommand = std::make_unique<FindCommand>(nss); + findCommand->setFilter(BSON("a" << 1)); + findCommand->setSort(BSON("d" << 1)); + auto statusWithCQ = CanonicalQuery::canonicalize(opCtx(), std::move(findCommand)); ASSERT_OK(statusWithCQ.getStatus()); unique_ptr<CanonicalQuery> cq = std::move(statusWithCQ.getValue()); ASSERT(nullptr != cq.get()); @@ -654,9 +654,9 @@ public: // Solutions using either 'a' or 'b' will take a long time to start producing // results. However, an index scan on 'b' will start producing results sooner // than an index scan on 'a'. - auto qr = std::make_unique<QueryRequest>(nss); - qr->setFilter(fromjson("{a: 1, b: 1, c: {$gte: 5000}}")); - auto statusWithCQ = CanonicalQuery::canonicalize(opCtx(), std::move(qr)); + auto findCommand = std::make_unique<FindCommand>(nss); + findCommand->setFilter(fromjson("{a: 1, b: 1, c: {$gte: 5000}}")); + auto statusWithCQ = CanonicalQuery::canonicalize(opCtx(), std::move(findCommand)); ASSERT_OK(statusWithCQ.getStatus()); unique_ptr<CanonicalQuery> cq = std::move(statusWithCQ.getValue()); ASSERT(nullptr != cq.get()); @@ -686,9 +686,9 @@ public: addIndex(BSON("b" << 1 << "c" << 1)); addIndex(BSON("a" << 1)); - auto qr = std::make_unique<QueryRequest>(nss); - qr->setFilter(fromjson("{a: 9, b: {$ne: 10}, c: 9}")); - auto statusWithCQ = CanonicalQuery::canonicalize(opCtx(), std::move(qr)); + auto findCommand = std::make_unique<FindCommand>(nss); + findCommand->setFilter(fromjson("{a: 9, b: {$ne: 10}, c: 9}")); + auto statusWithCQ = CanonicalQuery::canonicalize(opCtx(), std::move(findCommand)); ASSERT_OK(statusWithCQ.getStatus()); unique_ptr<CanonicalQuery> cq = std::move(statusWithCQ.getValue()); ASSERT(nullptr != cq.get()); diff --git a/src/mongo/dbtests/query_plan_executor.cpp b/src/mongo/dbtests/query_plan_executor.cpp index 4d027200708..570a0cc37b2 100644 --- a/src/mongo/dbtests/query_plan_executor.cpp +++ b/src/mongo/dbtests/query_plan_executor.cpp @@ -106,10 +106,10 @@ public: unique_ptr<WorkingSet> ws(new WorkingSet()); // Canonicalize the query. - auto qr = std::make_unique<QueryRequest>(nss); - qr->setFilter(filterObj); - qr->setTailableMode(tailableMode); - auto statusWithCQ = CanonicalQuery::canonicalize(&_opCtx, std::move(qr)); + auto findCommand = std::make_unique<FindCommand>(nss); + findCommand->setFilter(filterObj); + query_request_helper::setTailableMode(tailableMode, findCommand.get()); + auto statusWithCQ = CanonicalQuery::canonicalize(&_opCtx, std::move(findCommand)); ASSERT_OK(statusWithCQ.getStatus()); unique_ptr<CanonicalQuery> cq = std::move(statusWithCQ.getValue()); verify(nullptr != cq.get()); @@ -155,8 +155,8 @@ public: unique_ptr<PlanStage> root = std::make_unique<FetchStage>(_expCtx.get(), ws.get(), std::move(ixscan), nullptr, coll); - auto qr = std::make_unique<QueryRequest>(nss); - auto statusWithCQ = CanonicalQuery::canonicalize(&_opCtx, std::move(qr)); + auto findCommand = std::make_unique<FindCommand>(nss); + auto statusWithCQ = CanonicalQuery::canonicalize(&_opCtx, std::move(findCommand)); verify(statusWithCQ.isOK()); unique_ptr<CanonicalQuery> cq = std::move(statusWithCQ.getValue()); verify(nullptr != cq.get()); diff --git a/src/mongo/dbtests/query_stage_cached_plan.cpp b/src/mongo/dbtests/query_stage_cached_plan.cpp index 4625bd6ebf9..f79bcb15763 100644 --- a/src/mongo/dbtests/query_stage_cached_plan.cpp +++ b/src/mongo/dbtests/query_stage_cached_plan.cpp @@ -60,9 +60,9 @@ namespace { std::unique_ptr<CanonicalQuery> canonicalQueryFromFilterObj(OperationContext* opCtx, const NamespaceString& nss, BSONObj filter) { - auto qr = std::make_unique<QueryRequest>(nss); - qr->setFilter(filter); - auto statusWithCQ = CanonicalQuery::canonicalize(opCtx, std::move(qr)); + auto findCommand = std::make_unique<FindCommand>(nss); + findCommand->setFilter(filter); + auto statusWithCQ = CanonicalQuery::canonicalize(opCtx, std::move(findCommand)); uassertStatusOK(statusWithCQ.getStatus()); return std::move(statusWithCQ.getValue()); } @@ -187,9 +187,9 @@ TEST_F(QueryStageCachedPlan, QueryStageCachedPlanFailureMemoryLimitExceeded) { ASSERT(collection); // Query can be answered by either index on "a" or index on "b". - auto qr = std::make_unique<QueryRequest>(nss); - qr->setFilter(fromjson("{a: {$gte: 8}, b: 1}")); - auto statusWithCQ = CanonicalQuery::canonicalize(opCtx(), std::move(qr)); + auto findCommand = std::make_unique<FindCommand>(nss); + findCommand->setFilter(fromjson("{a: {$gte: 8}, b: 1}")); + auto statusWithCQ = CanonicalQuery::canonicalize(opCtx(), std::move(findCommand)); ASSERT_OK(statusWithCQ.getStatus()); const std::unique_ptr<CanonicalQuery> cq = std::move(statusWithCQ.getValue()); @@ -237,9 +237,9 @@ TEST_F(QueryStageCachedPlan, QueryStageCachedPlanHitMaxWorks) { ASSERT(collection); // Query can be answered by either index on "a" or index on "b". - auto qr = std::make_unique<QueryRequest>(nss); - qr->setFilter(fromjson("{a: {$gte: 8}, b: 1}")); - auto statusWithCQ = CanonicalQuery::canonicalize(opCtx(), std::move(qr)); + auto findCommand = std::make_unique<FindCommand>(nss); + findCommand->setFilter(fromjson("{a: {$gte: 8}, b: 1}")); + auto statusWithCQ = CanonicalQuery::canonicalize(opCtx(), std::move(findCommand)); ASSERT_OK(statusWithCQ.getStatus()); const std::unique_ptr<CanonicalQuery> cq = std::move(statusWithCQ.getValue()); @@ -448,8 +448,8 @@ TEST_F(QueryStageCachedPlan, ThrowsOnYieldRecoveryWhenIndexIsDroppedBeforePlanSe ASSERT(collection); // Query can be answered by either index on "a" or index on "b". - auto qr = std::make_unique<QueryRequest>(nss); - auto statusWithCQ = CanonicalQuery::canonicalize(opCtx(), std::move(qr)); + auto findCommand = std::make_unique<FindCommand>(nss); + auto statusWithCQ = CanonicalQuery::canonicalize(opCtx(), std::move(findCommand)); ASSERT_OK(statusWithCQ.getStatus()); const std::unique_ptr<CanonicalQuery> cq = std::move(statusWithCQ.getValue()); @@ -492,8 +492,8 @@ TEST_F(QueryStageCachedPlan, DoesNotThrowOnYieldRecoveryWhenIndexIsDroppedAferPl ASSERT(collection); // Query can be answered by either index on "a" or index on "b". - auto qr = std::make_unique<QueryRequest>(nss); - auto statusWithCQ = CanonicalQuery::canonicalize(opCtx(), std::move(qr)); + auto findCommand = std::make_unique<FindCommand>(nss); + auto statusWithCQ = CanonicalQuery::canonicalize(opCtx(), std::move(findCommand)); ASSERT_OK(statusWithCQ.getStatus()); const std::unique_ptr<CanonicalQuery> cq = std::move(statusWithCQ.getValue()); diff --git a/src/mongo/dbtests/query_stage_delete.cpp b/src/mongo/dbtests/query_stage_delete.cpp index 03b7834e079..93fbd082916 100644 --- a/src/mongo/dbtests/query_stage_delete.cpp +++ b/src/mongo/dbtests/query_stage_delete.cpp @@ -104,9 +104,9 @@ public: } unique_ptr<CanonicalQuery> canonicalize(const BSONObj& query) { - auto qr = std::make_unique<QueryRequest>(nss); - qr->setFilter(query); - auto statusWithCQ = CanonicalQuery::canonicalize(&_opCtx, std::move(qr)); + auto findCommand = std::make_unique<FindCommand>(nss); + findCommand->setFilter(query); + auto statusWithCQ = CanonicalQuery::canonicalize(&_opCtx, std::move(findCommand)); ASSERT_OK(statusWithCQ.getStatus()); return std::move(statusWithCQ.getValue()); } diff --git a/src/mongo/dbtests/query_stage_multiplan.cpp b/src/mongo/dbtests/query_stage_multiplan.cpp index f4300805bd2..5ef147ed590 100644 --- a/src/mongo/dbtests/query_stage_multiplan.cpp +++ b/src/mongo/dbtests/query_stage_multiplan.cpp @@ -128,9 +128,9 @@ protected: std::unique_ptr<CanonicalQuery> makeCanonicalQuery(OperationContext* opCtx, NamespaceString nss, BSONObj filter) { - auto qr = std::make_unique<QueryRequest>(nss); - qr->setFilter(filter); - auto statusWithCQ = CanonicalQuery::canonicalize(opCtx, std::move(qr)); + auto findCommand = std::make_unique<FindCommand>(nss); + findCommand->setFilter(filter); + auto statusWithCQ = CanonicalQuery::canonicalize(opCtx, std::move(findCommand)); ASSERT_OK(statusWithCQ.getStatus()); unique_ptr<CanonicalQuery> cq = std::move(statusWithCQ.getValue()); ASSERT(cq); @@ -374,10 +374,10 @@ TEST_F(QueryStageMultiPlanTest, MPSBackupPlan) { AutoGetCollectionForReadCommand collection(_opCtx.get(), nss); // Query for both 'a' and 'b' and sort on 'b'. - auto qr = std::make_unique<QueryRequest>(nss); - qr->setFilter(BSON("a" << 1 << "b" << 1)); - qr->setSort(BSON("b" << 1)); - auto statusWithCQ = CanonicalQuery::canonicalize(opCtx(), std::move(qr)); + auto findCommand = std::make_unique<FindCommand>(nss); + findCommand->setFilter(BSON("a" << 1 << "b" << 1)); + findCommand->setSort(BSON("b" << 1)); + auto statusWithCQ = CanonicalQuery::canonicalize(opCtx(), std::move(findCommand)); verify(statusWithCQ.isOK()); unique_ptr<CanonicalQuery> cq = std::move(statusWithCQ.getValue()); ASSERT(nullptr != cq.get()); @@ -486,9 +486,9 @@ TEST_F(QueryStageMultiPlanTest, MPSExplainAllPlans) { AutoGetCollectionForReadCommand ctx(_opCtx.get(), nss); - auto qr = std::make_unique<QueryRequest>(nss); - qr->setFilter(BSON("x" << 1)); - auto cq = uassertStatusOK(CanonicalQuery::canonicalize(opCtx(), std::move(qr))); + auto findCommand = std::make_unique<FindCommand>(nss); + findCommand->setFilter(BSON("x" << 1)); + auto cq = uassertStatusOK(CanonicalQuery::canonicalize(opCtx(), std::move(findCommand))); unique_ptr<MultiPlanStage> mps = std::make_unique<MultiPlanStage>(_expCtx.get(), ctx.getCollection(), cq.get()); @@ -558,9 +558,9 @@ TEST_F(QueryStageMultiPlanTest, MPSSummaryStats) { const CollectionPtr& coll = ctx.getCollection(); // Create the executor (Matching all documents). - auto qr = std::make_unique<QueryRequest>(nss); - qr->setFilter(BSON("foo" << BSON("$gte" << 0))); - auto cq = uassertStatusOK(CanonicalQuery::canonicalize(opCtx(), std::move(qr))); + auto findCommand = std::make_unique<FindCommand>(nss); + findCommand->setFilter(BSON("foo" << BSON("$gte" << 0))); + auto cq = uassertStatusOK(CanonicalQuery::canonicalize(opCtx(), std::move(findCommand))); auto exec = uassertStatusOK( getExecutor(opCtx(), &coll, std::move(cq), PlanYieldPolicy::YieldPolicy::NO_YIELD, 0)); @@ -611,10 +611,10 @@ TEST_F(QueryStageMultiPlanTest, ShouldReportErrorIfExceedsTimeLimitDuringPlannin getCollScanPlan(_expCtx.get(), coll.getCollection(), sharedWs.get(), filter.get()); - auto queryRequest = std::make_unique<QueryRequest>(nss); - queryRequest->setFilter(filterObj); + auto findCommand = std::make_unique<FindCommand>(nss); + findCommand->setFilter(filterObj); auto canonicalQuery = - uassertStatusOK(CanonicalQuery::canonicalize(opCtx(), std::move(queryRequest))); + uassertStatusOK(CanonicalQuery::canonicalize(opCtx(), std::move(findCommand))); MultiPlanStage multiPlanStage( _expCtx.get(), coll.getCollection(), canonicalQuery.get(), PlanCachingMode::NeverCache); multiPlanStage.addPlan(createQuerySolution(), std::move(ixScanRoot), sharedWs.get()); @@ -651,10 +651,10 @@ TEST_F(QueryStageMultiPlanTest, ShouldReportErrorIfKilledDuringPlanning) { unique_ptr<PlanStage> collScanRoot = getCollScanPlan(_expCtx.get(), coll.getCollection(), sharedWs.get(), filter.get()); - auto queryRequest = std::make_unique<QueryRequest>(nss); - queryRequest->setFilter(BSON("foo" << BSON("$gte" << 0))); + auto findCommand = std::make_unique<FindCommand>(nss); + findCommand->setFilter(BSON("foo" << BSON("$gte" << 0))); auto canonicalQuery = - uassertStatusOK(CanonicalQuery::canonicalize(opCtx(), std::move(queryRequest))); + uassertStatusOK(CanonicalQuery::canonicalize(opCtx(), std::move(findCommand))); MultiPlanStage multiPlanStage( _expCtx.get(), coll.getCollection(), canonicalQuery.get(), PlanCachingMode::NeverCache); multiPlanStage.addPlan(createQuerySolution(), std::move(ixScanRoot), sharedWs.get()); @@ -694,11 +694,11 @@ TEST_F(QueryStageMultiPlanTest, AddsContextDuringException) { insert(BSON("foo" << 10)); AutoGetCollectionForReadCommand ctx(_opCtx.get(), nss); - auto queryRequest = std::make_unique<QueryRequest>(nss); - queryRequest->setFilter(BSON("fake" - << "query")); + auto findCommand = std::make_unique<FindCommand>(nss); + findCommand->setFilter(BSON("fake" + << "query")); auto canonicalQuery = - uassertStatusOK(CanonicalQuery::canonicalize(opCtx(), std::move(queryRequest))); + uassertStatusOK(CanonicalQuery::canonicalize(opCtx(), std::move(findCommand))); MultiPlanStage multiPlanStage( _expCtx.get(), ctx.getCollection(), canonicalQuery.get(), PlanCachingMode::NeverCache); unique_ptr<WorkingSet> sharedWs(new WorkingSet()); diff --git a/src/mongo/dbtests/query_stage_subplan.cpp b/src/mongo/dbtests/query_stage_subplan.cpp index afbfc280c15..1a63e655cf9 100644 --- a/src/mongo/dbtests/query_stage_subplan.cpp +++ b/src/mongo/dbtests/query_stage_subplan.cpp @@ -97,11 +97,13 @@ protected: bool isExplain = false; // If there is no '$db', append it. auto cmd = OpMsgRequest::fromDBAndBody("test", cmdObj).body; - auto qr = QueryRequest::makeFromFindCommandForTests(cmd, isExplain, NamespaceString()); + auto findCommand = + query_request_helper::makeFromFindCommandForTests(cmd, NamespaceString()); auto cq = unittest::assertGet( CanonicalQuery::canonicalize(opCtx(), - std::move(qr), + std::move(findCommand), + isExplain, expCtx(), ExtensionsCallbackNoop(), MatchExpressionParser::kAllowAllSpecialFeatures)); @@ -135,9 +137,9 @@ TEST_F(QueryStageSubplanTest, QueryStageSubplanGeo2dOr) { "{$or: [{a: {$geoWithin: {$centerSphere: [[0,0],10]}}}," "{a: {$geoWithin: {$centerSphere: [[1,1],10]}}}]}"); - auto qr = std::make_unique<QueryRequest>(nss); - qr->setFilter(query); - auto statusWithCQ = CanonicalQuery::canonicalize(opCtx(), std::move(qr)); + auto findCommand = std::make_unique<FindCommand>(nss); + findCommand->setFilter(query); + auto statusWithCQ = CanonicalQuery::canonicalize(opCtx(), std::move(findCommand)); ASSERT_OK(statusWithCQ.getStatus()); std::unique_ptr<CanonicalQuery> cq = std::move(statusWithCQ.getValue()); @@ -172,9 +174,9 @@ void assertSubplanFromCache(QueryStageSubplanTest* test, const dbtests::WriteCon CollectionPtr collection = ctx.getCollection(); - auto qr = std::make_unique<QueryRequest>(nss); - qr->setFilter(query); - auto statusWithCQ = CanonicalQuery::canonicalize(test->opCtx(), std::move(qr)); + auto findCommand = std::make_unique<FindCommand>(nss); + findCommand->setFilter(query); + auto statusWithCQ = CanonicalQuery::canonicalize(test->opCtx(), std::move(findCommand)); ASSERT_OK(statusWithCQ.getStatus()); std::unique_ptr<CanonicalQuery> cq = std::move(statusWithCQ.getValue()); @@ -256,9 +258,9 @@ TEST_F(QueryStageSubplanTest, QueryStageSubplanDontCacheZeroResults) { CollectionPtr collection = ctx.getCollection(); - auto qr = std::make_unique<QueryRequest>(nss); - qr->setFilter(query); - auto statusWithCQ = CanonicalQuery::canonicalize(opCtx(), std::move(qr)); + auto findCommand = std::make_unique<FindCommand>(nss); + findCommand->setFilter(query); + auto statusWithCQ = CanonicalQuery::canonicalize(opCtx(), std::move(findCommand)); ASSERT_OK(statusWithCQ.getStatus()); std::unique_ptr<CanonicalQuery> cq = std::move(statusWithCQ.getValue()); @@ -312,9 +314,9 @@ TEST_F(QueryStageSubplanTest, QueryStageSubplanDontCacheTies) { CollectionPtr collection = ctx.getCollection(); - auto qr = std::make_unique<QueryRequest>(nss); - qr->setFilter(query); - auto statusWithCQ = CanonicalQuery::canonicalize(opCtx(), std::move(qr)); + auto findCommand = std::make_unique<FindCommand>(nss); + findCommand->setFilter(query); + auto statusWithCQ = CanonicalQuery::canonicalize(opCtx(), std::move(findCommand)); ASSERT_OK(statusWithCQ.getStatus()); std::unique_ptr<CanonicalQuery> cq = std::move(statusWithCQ.getValue()); @@ -484,10 +486,10 @@ TEST_F(QueryStageSubplanTest, QueryStageSubplanPlanRootedOrNE) { insert(BSON("_id" << 3 << "a" << 3)); insert(BSON("_id" << 4)); - auto qr = std::make_unique<QueryRequest>(nss); - qr->setFilter(fromjson("{$or: [{a: 1}, {a: {$ne:1}}]}")); - qr->setSort(BSON("d" << 1)); - auto cq = unittest::assertGet(CanonicalQuery::canonicalize(opCtx(), std::move(qr))); + auto findCommand = std::make_unique<FindCommand>(nss); + findCommand->setFilter(fromjson("{$or: [{a: 1}, {a: {$ne:1}}]}")); + findCommand->setSort(BSON("d" << 1)); + auto cq = unittest::assertGet(CanonicalQuery::canonicalize(opCtx(), std::move(findCommand))); CollectionPtr collection = ctx.getCollection(); @@ -517,10 +519,10 @@ TEST_F(QueryStageSubplanTest, QueryStageSubplanPlanRootedOrNE) { TEST_F(QueryStageSubplanTest, ShouldReportErrorIfExceedsTimeLimitDuringPlanning) { dbtests::WriteContextForTests ctx(opCtx(), nss.ns()); // Build a query with a rooted $or. - auto queryRequest = std::make_unique<QueryRequest>(nss); - queryRequest->setFilter(BSON("$or" << BSON_ARRAY(BSON("p1" << 1) << BSON("p2" << 2)))); + auto findCommand = std::make_unique<FindCommand>(nss); + findCommand->setFilter(BSON("$or" << BSON_ARRAY(BSON("p1" << 1) << BSON("p2" << 2)))); auto canonicalQuery = - uassertStatusOK(CanonicalQuery::canonicalize(opCtx(), std::move(queryRequest))); + uassertStatusOK(CanonicalQuery::canonicalize(opCtx(), std::move(findCommand))); // Add 4 indices: 2 for each predicate to choose from. addIndex(BSON("p1" << 1 << "opt1" << 1)); @@ -550,10 +552,10 @@ TEST_F(QueryStageSubplanTest, ShouldReportErrorIfExceedsTimeLimitDuringPlanning) TEST_F(QueryStageSubplanTest, ShouldReportErrorIfKilledDuringPlanning) { dbtests::WriteContextForTests ctx(opCtx(), nss.ns()); // Build a query with a rooted $or. - auto queryRequest = std::make_unique<QueryRequest>(nss); - queryRequest->setFilter(BSON("$or" << BSON_ARRAY(BSON("p1" << 1) << BSON("p2" << 2)))); + auto findCommand = std::make_unique<FindCommand>(nss); + findCommand->setFilter(BSON("$or" << BSON_ARRAY(BSON("p1" << 1) << BSON("p2" << 2)))); auto canonicalQuery = - uassertStatusOK(CanonicalQuery::canonicalize(opCtx(), std::move(queryRequest))); + uassertStatusOK(CanonicalQuery::canonicalize(opCtx(), std::move(findCommand))); // Add 4 indices: 2 for each predicate to choose from. addIndex(BSON("p1" << 1 << "opt1" << 1)); @@ -587,10 +589,10 @@ TEST_F(QueryStageSubplanTest, ShouldThrowOnRestoreIfIndexDroppedBeforePlanSelect } // Build a query with a rooted $or. - auto queryRequest = std::make_unique<QueryRequest>(nss); - queryRequest->setFilter(BSON("$or" << BSON_ARRAY(BSON("p1" << 1) << BSON("p2" << 2)))); + auto findCommand = std::make_unique<FindCommand>(nss); + findCommand->setFilter(BSON("$or" << BSON_ARRAY(BSON("p1" << 1) << BSON("p2" << 2)))); auto canonicalQuery = - uassertStatusOK(CanonicalQuery::canonicalize(opCtx(), std::move(queryRequest))); + uassertStatusOK(CanonicalQuery::canonicalize(opCtx(), std::move(findCommand))); boost::optional<AutoGetCollectionForReadCommand> collLock; collLock.emplace(opCtx(), nss); @@ -633,10 +635,10 @@ TEST_F(QueryStageSubplanTest, ShouldNotThrowOnRestoreIfIndexDroppedAfterPlanSele } // Build a query with a rooted $or. - auto queryRequest = std::make_unique<QueryRequest>(nss); - queryRequest->setFilter(BSON("$or" << BSON_ARRAY(BSON("p1" << 1) << BSON("p2" << 2)))); + auto findCommand = std::make_unique<FindCommand>(nss); + findCommand->setFilter(BSON("$or" << BSON_ARRAY(BSON("p1" << 1) << BSON("p2" << 2)))); auto canonicalQuery = - uassertStatusOK(CanonicalQuery::canonicalize(opCtx(), std::move(queryRequest))); + uassertStatusOK(CanonicalQuery::canonicalize(opCtx(), std::move(findCommand))); boost::optional<AutoGetCollectionForReadCommand> collLock; collLock.emplace(opCtx(), nss); diff --git a/src/mongo/dbtests/query_stage_update.cpp b/src/mongo/dbtests/query_stage_update.cpp index c55d7bd17f1..4ef593dfa76 100644 --- a/src/mongo/dbtests/query_stage_update.cpp +++ b/src/mongo/dbtests/query_stage_update.cpp @@ -99,9 +99,9 @@ public: } unique_ptr<CanonicalQuery> canonicalize(const BSONObj& query) { - auto qr = std::make_unique<QueryRequest>(nss); - qr->setFilter(query); - auto statusWithCQ = CanonicalQuery::canonicalize(&_opCtx, std::move(qr)); + auto findCommand = std::make_unique<FindCommand>(nss); + findCommand->setFilter(query); + auto statusWithCQ = CanonicalQuery::canonicalize(&_opCtx, std::move(findCommand)); ASSERT_OK(statusWithCQ.getStatus()); return std::move(statusWithCQ.getValue()); } diff --git a/src/mongo/executor/remote_command_request.cpp b/src/mongo/executor/remote_command_request.cpp index 8eb623cde61..fc2af299615 100644 --- a/src/mongo/executor/remote_command_request.cpp +++ b/src/mongo/executor/remote_command_request.cpp @@ -36,7 +36,7 @@ #include "mongo/bson/simple_bsonobj_comparator.h" #include "mongo/db/api_parameters.h" #include "mongo/db/operation_context.h" -#include "mongo/db/query/query_request.h" +#include "mongo/db/query/query_request_helper.h" #include "mongo/platform/atomic_word.h" #include "mongo/util/str.h" @@ -79,8 +79,8 @@ RemoteCommandRequestBase::RemoteCommandRequestBase(RequestId requestId, // the OpCtx. It should never be specified explicitly. uassert(4924403, str::stream() << "Command request object should not manually specify " - << QueryRequest::kMaxTimeMSOpOnlyField, - !cmdObj.hasField(QueryRequest::kMaxTimeMSOpOnlyField)); + << query_request_helper::kMaxTimeMSOpOnlyField, + !cmdObj.hasField(query_request_helper::kMaxTimeMSOpOnlyField)); if (hedgeOptions) { operationKey.emplace(UUID::gen()); diff --git a/src/mongo/s/balancer_configuration_test.cpp b/src/mongo/s/balancer_configuration_test.cpp index 3d4af2a4fbb..546b7f67d4f 100644 --- a/src/mongo/s/balancer_configuration_test.cpp +++ b/src/mongo/s/balancer_configuration_test.cpp @@ -38,7 +38,7 @@ #include "mongo/bson/bsonmisc.h" #include "mongo/client/remote_command_targeter_mock.h" #include "mongo/db/namespace_string.h" -#include "mongo/db/query/query_request.h" +#include "mongo/db/query/query_request_helper.h" #include "mongo/executor/remote_command_request.h" #include "mongo/rpc/metadata/repl_set_metadata.h" #include "mongo/rpc/metadata/tracking_metadata.h" @@ -80,10 +80,10 @@ protected: rpc::TrackingMetadata::removeTrackingData(request.metadata)); auto opMsg = OpMsgRequest::fromDBAndBody(request.dbname, request.cmdObj); - auto query = QueryRequest::makeFromFindCommandForTests(opMsg.body, false); + auto findCommand = query_request_helper::makeFromFindCommandForTests(opMsg.body); - ASSERT_EQ(query->nss().ns(), "config.settings"); - ASSERT_BSONOBJ_EQ(query->getFilter(), BSON("_id" << key)); + ASSERT_EQ(findCommand->getNamespaceOrUUID().nss()->ns(), "config.settings"); + ASSERT_BSONOBJ_EQ(findCommand->getFilter(), BSON("_id" << key)); checkReadConcern(request.cmdObj, Timestamp(0, 0), repl::OpTime::kUninitializedTerm); diff --git a/src/mongo/s/catalog/sharding_catalog_client_test.cpp b/src/mongo/s/catalog/sharding_catalog_client_test.cpp index 8a4aa9e2ad4..676db551c23 100644 --- a/src/mongo/s/catalog/sharding_catalog_client_test.cpp +++ b/src/mongo/s/catalog/sharding_catalog_client_test.cpp @@ -37,7 +37,7 @@ #include "mongo/client/remote_command_targeter_mock.h" #include "mongo/db/commands.h" #include "mongo/db/ops/write_ops.h" -#include "mongo/db/query/query_request.h" +#include "mongo/db/query/query_request_helper.h" #include "mongo/db/repl/read_concern_args.h" #include "mongo/db/time_proof_service.h" #include "mongo/executor/task_executor.h" @@ -101,10 +101,11 @@ TEST_F(ShardingCatalogClientTest, GetCollectionExisting) { rpc::TrackingMetadata::removeTrackingData(request.metadata)); auto opMsg = OpMsgRequest::fromDBAndBody(request.dbname, request.cmdObj); - auto query = QueryRequest::makeFromFindCommandForTests(opMsg.body, false); + auto query = query_request_helper::makeFromFindCommandForTests(opMsg.body); // Ensure the query is correct - ASSERT_EQ(query->nss(), CollectionType::ConfigNS); + ASSERT_EQ(query->getNamespaceOrUUID().nss().value_or(NamespaceString()), + CollectionType::ConfigNS); ASSERT_BSONOBJ_EQ(query->getFilter(), BSON(CollectionType::kNssFieldName << expectedColl.getNss().ns())); ASSERT_BSONOBJ_EQ(query->getSort(), BSONObj()); @@ -171,9 +172,10 @@ TEST_F(ShardingCatalogClientTest, GetDatabaseExisting) { rpc::TrackingMetadata::removeTrackingData(request.metadata)); auto opMsg = OpMsgRequest::fromDBAndBody(request.dbname, request.cmdObj); - auto query = QueryRequest::makeFromFindCommandForTests(opMsg.body, false); + auto query = query_request_helper::makeFromFindCommandForTests(opMsg.body); - ASSERT_EQ(query->nss(), DatabaseType::ConfigNS); + ASSERT_EQ(query->getNamespaceOrUUID().nss().value_or(NamespaceString()), + DatabaseType::ConfigNS); ASSERT_BSONOBJ_EQ(query->getFilter(), BSON(DatabaseType::name(expectedDb.getName()))); ASSERT_BSONOBJ_EQ(query->getSort(), BSONObj()); ASSERT(!query->getLimit()); @@ -300,9 +302,10 @@ TEST_F(ShardingCatalogClientTest, GetAllShardsValid) { rpc::TrackingMetadata::removeTrackingData(request.metadata)); auto opMsg = OpMsgRequest::fromDBAndBody(request.dbname, request.cmdObj); - auto query = QueryRequest::makeFromFindCommandForTests(opMsg.body, false); + auto query = query_request_helper::makeFromFindCommandForTests(opMsg.body); - ASSERT_EQ(query->nss(), ShardType::ConfigNS); + ASSERT_EQ(query->getNamespaceOrUUID().nss().value_or(NamespaceString()), + ShardType::ConfigNS); ASSERT_BSONOBJ_EQ(query->getFilter(), BSONObj()); ASSERT_BSONOBJ_EQ(query->getSort(), BSONObj()); ASSERT_FALSE(query->getLimit().is_initialized()); @@ -397,9 +400,10 @@ TEST_F(ShardingCatalogClientTest, GetChunksForNSWithSortAndLimit) { rpc::TrackingMetadata::removeTrackingData(request.metadata)); auto opMsg = OpMsgRequest::fromDBAndBody(request.dbname, request.cmdObj); - auto query = QueryRequest::makeFromFindCommandForTests(opMsg.body, false); + auto query = query_request_helper::makeFromFindCommandForTests(opMsg.body); - ASSERT_EQ(query->nss(), ChunkType::ConfigNS); + ASSERT_EQ(query->getNamespaceOrUUID().nss().value_or(NamespaceString()), + ChunkType::ConfigNS); ASSERT_BSONOBJ_EQ(query->getFilter(), chunksQuery); ASSERT_BSONOBJ_EQ(query->getSort(), BSON(ChunkType::lastmod() << -1)); ASSERT_EQ(query->getLimit().get(), 1); @@ -454,9 +458,10 @@ TEST_F(ShardingCatalogClientTest, GetChunksForNSNoSortNoLimit) { rpc::TrackingMetadata::removeTrackingData(request.metadata)); auto opMsg = OpMsgRequest::fromDBAndBody(request.dbname, request.cmdObj); - auto query = QueryRequest::makeFromFindCommandForTests(opMsg.body, false); + auto query = query_request_helper::makeFromFindCommandForTests(opMsg.body); - ASSERT_EQ(query->nss(), ChunkType::ConfigNS); + ASSERT_EQ(query->getNamespaceOrUUID().nss().value_or(NamespaceString()), + ChunkType::ConfigNS); ASSERT_BSONOBJ_EQ(query->getFilter(), chunksQuery); ASSERT_BSONOBJ_EQ(query->getSort(), BSONObj()); ASSERT_FALSE(query->getLimit().is_initialized()); @@ -766,9 +771,10 @@ TEST_F(ShardingCatalogClientTest, GetCollectionsValidResultsNoDb) { rpc::TrackingMetadata::removeTrackingData(request.metadata)); auto opMsg = OpMsgRequest::fromDBAndBody(request.dbname, request.cmdObj); - auto query = QueryRequest::makeFromFindCommandForTests(opMsg.body, false); + auto query = query_request_helper::makeFromFindCommandForTests(opMsg.body); - ASSERT_EQ(query->nss(), CollectionType::ConfigNS); + ASSERT_EQ(query->getNamespaceOrUUID().nss().value_or(NamespaceString()), + CollectionType::ConfigNS); ASSERT_BSONOBJ_EQ(query->getFilter(), BSONObj()); ASSERT_BSONOBJ_EQ(query->getSort(), BSONObj()); @@ -813,9 +819,10 @@ TEST_F(ShardingCatalogClientTest, GetCollectionsValidResultsWithDb) { rpc::TrackingMetadata::removeTrackingData(request.metadata)); auto opMsg = OpMsgRequest::fromDBAndBody(request.dbname, request.cmdObj); - auto query = QueryRequest::makeFromFindCommandForTests(opMsg.body, false); + auto query = query_request_helper::makeFromFindCommandForTests(opMsg.body); - ASSERT_EQ(query->nss(), CollectionType::ConfigNS); + ASSERT_EQ(query->getNamespaceOrUUID().nss().value_or(NamespaceString()), + CollectionType::ConfigNS); { BSONObjBuilder b; b.appendRegex(CollectionType::kNssFieldName, "^test\\."); @@ -850,9 +857,10 @@ TEST_F(ShardingCatalogClientTest, GetCollectionsInvalidCollectionType) { rpc::TrackingMetadata::removeTrackingData(request.metadata)); auto opMsg = OpMsgRequest::fromDBAndBody(request.dbname, request.cmdObj); - auto query = QueryRequest::makeFromFindCommandForTests(opMsg.body, false); + auto query = query_request_helper::makeFromFindCommandForTests(opMsg.body); - ASSERT_EQ(query->nss(), CollectionType::ConfigNS); + ASSERT_EQ(query->getNamespaceOrUUID().nss().value_or(NamespaceString()), + CollectionType::ConfigNS); { BSONObjBuilder b; b.appendRegex(CollectionType::kNssFieldName, "^test\\."); @@ -886,9 +894,10 @@ TEST_F(ShardingCatalogClientTest, GetDatabasesForShardValid) { rpc::TrackingMetadata::removeTrackingData(request.metadata)); auto opMsg = OpMsgRequest::fromDBAndBody(request.dbname, request.cmdObj); - auto query = QueryRequest::makeFromFindCommandForTests(opMsg.body, false); + auto query = query_request_helper::makeFromFindCommandForTests(opMsg.body); - ASSERT_EQ(query->nss(), DatabaseType::ConfigNS); + ASSERT_EQ(query->getNamespaceOrUUID().nss().value_or(NamespaceString()), + DatabaseType::ConfigNS); ASSERT_BSONOBJ_EQ(query->getFilter(), BSON(DatabaseType::primary(dbt1.getPrimary().toString()))); ASSERT_BSONOBJ_EQ(query->getSort(), BSONObj()); @@ -954,9 +963,10 @@ TEST_F(ShardingCatalogClientTest, GetTagsForCollection) { rpc::TrackingMetadata::removeTrackingData(request.metadata)); auto opMsg = OpMsgRequest::fromDBAndBody(request.dbname, request.cmdObj); - auto query = QueryRequest::makeFromFindCommandForTests(opMsg.body, false); + auto query = query_request_helper::makeFromFindCommandForTests(opMsg.body); - ASSERT_EQ(query->nss(), TagsType::ConfigNS); + ASSERT_EQ(query->getNamespaceOrUUID().nss().value_or(NamespaceString()), + TagsType::ConfigNS); ASSERT_BSONOBJ_EQ(query->getFilter(), BSON(TagsType::ns("TestDB.TestColl"))); ASSERT_BSONOBJ_EQ(query->getSort(), BSON(TagsType::min() << 1)); @@ -1301,14 +1311,15 @@ TEST_F(ShardingCatalogClientTest, GetNewKeys) { ASSERT_EQ("admin", request.dbname); auto opMsg = OpMsgRequest::fromDBAndBody(request.dbname, request.cmdObj); - auto query = QueryRequest::makeFromFindCommandForTests(opMsg.body, false); + auto query = query_request_helper::makeFromFindCommandForTests(opMsg.body); BSONObj expectedQuery( fromjson("{purpose: 'none'," "expiresAt: {$gt: {$timestamp: {t: 1234, i: 5678}}}}")); - ASSERT_EQ(NamespaceString::kKeysCollectionNamespace, query->nss()); + ASSERT_EQ(NamespaceString::kKeysCollectionNamespace, + query->getNamespaceOrUUID().nss().value_or(NamespaceString())); ASSERT_BSONOBJ_EQ(expectedQuery, query->getFilter()); ASSERT_BSONOBJ_EQ(BSON("expiresAt" << 1), query->getSort()); ASSERT_FALSE(query->getLimit().is_initialized()); @@ -1353,13 +1364,14 @@ TEST_F(ShardingCatalogClientTest, GetNewKeysWithEmptyCollection) { ASSERT_EQ("admin", request.dbname); auto opMsg = OpMsgRequest::fromDBAndBody(request.dbname, request.cmdObj); - auto query = QueryRequest::makeFromFindCommandForTests(opMsg.body, false); + auto query = query_request_helper::makeFromFindCommandForTests(opMsg.body); BSONObj expectedQuery( fromjson("{purpose: 'none'," "expiresAt: {$gt: {$timestamp: {t: 1234, i: 5678}}}}")); - ASSERT_EQ(NamespaceString::kKeysCollectionNamespace, query->nss()); + ASSERT_EQ(NamespaceString::kKeysCollectionNamespace, + query->getNamespaceOrUUID().nss().value_or(NamespaceString())); ASSERT_BSONOBJ_EQ(expectedQuery, query->getFilter()); ASSERT_BSONOBJ_EQ(BSON("expiresAt" << 1), query->getSort()); ASSERT_FALSE(query->getLimit().is_initialized()); diff --git a/src/mongo/s/catalog/sharding_catalog_write_retry_test.cpp b/src/mongo/s/catalog/sharding_catalog_write_retry_test.cpp index 849d094032e..a1cb36b7c4b 100644 --- a/src/mongo/s/catalog/sharding_catalog_write_retry_test.cpp +++ b/src/mongo/s/catalog/sharding_catalog_write_retry_test.cpp @@ -40,7 +40,7 @@ #include "mongo/db/client.h" #include "mongo/db/commands.h" #include "mongo/db/ops/write_ops.h" -#include "mongo/db/query/query_request.h" +#include "mongo/db/query/query_request_helper.h" #include "mongo/db/storage/duplicate_key_error_info.h" #include "mongo/db/write_concern.h" #include "mongo/executor/task_executor.h" @@ -149,7 +149,7 @@ TEST_F(InsertRetryTest, RetryOnNetworkErrorFails) { void assertFindRequestHasFilter(const RemoteCommandRequest& request, BSONObj filter) { // If there is no '$db', append it. auto cmd = OpMsgRequest::fromDBAndBody(request.dbname, request.cmdObj).body; - auto query = QueryRequest::makeFromFindCommandForTests(cmd, false); + auto query = query_request_helper::makeFromFindCommandForTests(cmd); ASSERT_BSONOBJ_EQ(filter, query->getFilter()); } diff --git a/src/mongo/s/catalog_cache_refresh_test.cpp b/src/mongo/s/catalog_cache_refresh_test.cpp index b259aa94929..eafa39348ae 100644 --- a/src/mongo/s/catalog_cache_refresh_test.cpp +++ b/src/mongo/s/catalog_cache_refresh_test.cpp @@ -32,7 +32,7 @@ #include "mongo/platform/basic.h" #include "mongo/db/concurrency/locker_noop.h" -#include "mongo/db/query/query_request.h" +#include "mongo/db/query/query_request_helper.h" #include "mongo/s/catalog/type_chunk.h" #include "mongo/s/catalog/type_collection.h" #include "mongo/s/catalog/type_database.h" @@ -610,7 +610,7 @@ TEST_F(CatalogCacheRefreshTest, ChunkEpochChangeDuringIncrementalLoadRecoveryAft expectGetCollection(oldVersion.epoch(), shardKeyPattern); onFindCommand([&](const RemoteCommandRequest& request) { auto opMsg = OpMsgRequest::fromDBAndBody(request.dbname, request.cmdObj); - auto diffQuery = QueryRequest::makeFromFindCommandForTests(opMsg.body, false); + auto diffQuery = query_request_helper::makeFromFindCommandForTests(opMsg.body); ASSERT_BSONOBJ_EQ(BSON("ns" << kNss.ns() << "lastmod" << BSON("$gte" << Timestamp(oldVersion.majorVersion(), oldVersion.minorVersion()))), @@ -642,7 +642,7 @@ TEST_F(CatalogCacheRefreshTest, ChunkEpochChangeDuringIncrementalLoadRecoveryAft // Ensure it is a differential query but starting from version zero (to fetch all the // chunks) since the incremental refresh above produced a different version auto opMsg = OpMsgRequest::fromDBAndBody(request.dbname, request.cmdObj); - auto diffQuery = QueryRequest::makeFromFindCommandForTests(opMsg.body, false); + auto diffQuery = query_request_helper::makeFromFindCommandForTests(opMsg.body); ASSERT_BSONOBJ_EQ(BSON("ns" << kNss.ns() << "lastmod" << BSON("$gte" << Timestamp(0, 0))), diffQuery->getFilter()); @@ -696,7 +696,7 @@ TEST_F(CatalogCacheRefreshTest, IncrementalLoadAfterCollectionEpochChange) { onFindCommand([&](const RemoteCommandRequest& request) { // Ensure it is a differential query but starting from version zero auto opMsg = OpMsgRequest::fromDBAndBody(request.dbname, request.cmdObj); - auto diffQuery = QueryRequest::makeFromFindCommandForTests(opMsg.body, false); + auto diffQuery = query_request_helper::makeFromFindCommandForTests(opMsg.body); ASSERT_BSONOBJ_EQ(BSON("ns" << kNss.ns() << "lastmod" << BSON("$gte" << Timestamp(0, 0))), diffQuery->getFilter()); @@ -742,7 +742,7 @@ TEST_F(CatalogCacheRefreshTest, IncrementalLoadAfterSplit) { onFindCommand([&](const RemoteCommandRequest& request) { // Ensure it is a differential query auto opMsg = OpMsgRequest::fromDBAndBody(request.dbname, request.cmdObj); - auto diffQuery = QueryRequest::makeFromFindCommandForTests(opMsg.body, false); + auto diffQuery = query_request_helper::makeFromFindCommandForTests(opMsg.body); ASSERT_BSONOBJ_EQ( BSON("ns" << kNss.ns() << "lastmod" << BSON("$gte" << Timestamp(version.majorVersion(), version.minorVersion()))), diff --git a/src/mongo/s/chunk_manager.cpp b/src/mongo/s/chunk_manager.cpp index 4eca62e5395..1ddbccfe938 100644 --- a/src/mongo/s/chunk_manager.cpp +++ b/src/mongo/s/chunk_manager.cpp @@ -384,23 +384,24 @@ void ChunkManager::getShardIdsForQuery(boost::intrusive_ptr<ExpressionContext> e const BSONObj& query, const BSONObj& collation, std::set<ShardId>* shardIds) const { - auto qr = std::make_unique<QueryRequest>(_rt->optRt->nss()); - qr->setFilter(query); + auto findCommand = std::make_unique<FindCommand>(_rt->optRt->nss()); + findCommand->setFilter(query.getOwned()); if (auto uuid = getUUID()) expCtx->uuid = uuid; if (!collation.isEmpty()) { - qr->setCollation(collation); + findCommand->setCollation(collation.getOwned()); } else if (_rt->optRt->getDefaultCollator()) { auto defaultCollator = _rt->optRt->getDefaultCollator(); - qr->setCollation(defaultCollator->getSpec().toBSON()); + findCommand->setCollation(defaultCollator->getSpec().toBSON()); expCtx->setCollator(defaultCollator->clone()); } auto cq = uassertStatusOK( CanonicalQuery::canonicalize(expCtx->opCtx, - std::move(qr), + std::move(findCommand), + false, /* isExplain */ expCtx, ExtensionsCallbackNoop(), MatchExpressionParser::kAllowAllSpecialFeatures)); diff --git a/src/mongo/s/chunk_manager_index_bounds_test.cpp b/src/mongo/s/chunk_manager_index_bounds_test.cpp index 8b8c490fd92..611c50fe13f 100644 --- a/src/mongo/s/chunk_manager_index_bounds_test.cpp +++ b/src/mongo/s/chunk_manager_index_bounds_test.cpp @@ -56,13 +56,14 @@ protected: std::unique_ptr<CanonicalQuery> canonicalize(const char* queryStr) { BSONObj queryObj = fromjson(queryStr); const NamespaceString nss("test.foo"); - auto qr = std::make_unique<QueryRequest>(nss); - qr->setFilter(queryObj); + auto findCommand = std::make_unique<FindCommand>(nss); + findCommand->setFilter(queryObj); boost::intrusive_ptr<ExpressionContextForTest> expCtx( new ExpressionContextForTest(operationContext())); auto statusWithCQ = CanonicalQuery::canonicalize(operationContext(), - std::move(qr), + std::move(findCommand), + false, expCtx, ExtensionsCallbackNoop(), MatchExpressionParser::kAllowAllSpecialFeatures); diff --git a/src/mongo/s/client/shard_remote.cpp b/src/mongo/s/client/shard_remote.cpp index a9d12cf30b3..77250817f40 100644 --- a/src/mongo/s/client/shard_remote.cpp +++ b/src/mongo/s/client/shard_remote.cpp @@ -44,7 +44,7 @@ #include "mongo/db/jsobj.h" #include "mongo/db/logical_time.h" #include "mongo/db/operation_context.h" -#include "mongo/db/query/query_request.h" +#include "mongo/db/query/query_request_helper.h" #include "mongo/db/repl/read_concern_args.h" #include "mongo/executor/task_executor_pool.h" #include "mongo/logv2/log.h" @@ -79,13 +79,13 @@ BSONObj appendMaxTimeToCmdObj(Milliseconds maxTimeMSOverride, const BSONObj& cmd // Remove the user provided maxTimeMS so we can attach the one from the override for (const auto& elem : cmdObj) { - if (elem.fieldNameStringData() != QueryRequest::cmdOptionMaxTimeMS) { + if (elem.fieldNameStringData() != query_request_helper::cmdOptionMaxTimeMS) { updatedCmdBuilder.append(elem); } } if (maxTimeMSOverride < Milliseconds::max()) { - updatedCmdBuilder.append(QueryRequest::cmdOptionMaxTimeMS, + updatedCmdBuilder.append(query_request_helper::cmdOptionMaxTimeMS, durationCount<Milliseconds>(maxTimeMSOverride)); } @@ -376,20 +376,21 @@ StatusWith<Shard::QueryResponse> ShardRemote::_exhaustiveFindOnConfig( BSONObjBuilder findCmdBuilder; { - QueryRequest qr(nss); - qr.setFilter(query); - qr.setSort(sort); - qr.setReadConcern(readConcernObj); - qr.setLimit(limit ? static_cast<boost::optional<std::int64_t>>(*limit) : boost::none); + FindCommand findCommand(nss); + findCommand.setFilter(query.getOwned()); + findCommand.setSort(sort.getOwned()); + findCommand.setReadConcern(readConcernObj.getOwned()); + findCommand.setLimit(limit ? static_cast<boost::optional<std::int64_t>>(*limit) + : boost::none); if (hint) { - qr.setHint(*hint); + findCommand.setHint(*hint); } if (maxTimeMS < Milliseconds::max()) { - qr.setMaxTimeMS(durationCount<Milliseconds>(maxTimeMS)); + findCommand.setMaxTimeMS(durationCount<Milliseconds>(maxTimeMS)); } - qr.asFindCommand(&findCmdBuilder); + findCommand.serialize(BSONObj(), &findCmdBuilder); } return _runExhaustiveCursorCommand(opCtx, diff --git a/src/mongo/s/cluster_identity_loader_test.cpp b/src/mongo/s/cluster_identity_loader_test.cpp index bf35b79990d..b9f7ba1f029 100644 --- a/src/mongo/s/cluster_identity_loader_test.cpp +++ b/src/mongo/s/cluster_identity_loader_test.cpp @@ -35,7 +35,7 @@ #include "mongo/client/remote_command_targeter_mock.h" #include "mongo/db/commands.h" -#include "mongo/db/query/query_request.h" +#include "mongo/db/query/query_request_helper.h" #include "mongo/db/service_context.h" #include "mongo/executor/task_executor.h" #include "mongo/rpc/metadata/repl_set_metadata.h" @@ -77,9 +77,9 @@ public: rpc::TrackingMetadata::removeTrackingData(request.metadata)); auto opMsg = OpMsgRequest::fromDBAndBody(request.dbname, request.cmdObj); - auto query = QueryRequest::makeFromFindCommandForTests(opMsg.body, false); + auto query = query_request_helper::makeFromFindCommandForTests(opMsg.body); - ASSERT_EQ(query->nss().ns(), "config.version"); + ASSERT_EQ(query->getNamespaceOrUUID().nss()->ns(), "config.version"); ASSERT_BSONOBJ_EQ(query->getFilter(), BSONObj()); ASSERT_FALSE(query->getLimit().is_initialized()); diff --git a/src/mongo/s/commands/cluster_find_cmd.cpp b/src/mongo/s/commands/cluster_find_cmd.cpp index 0403314e50b..20508a7fc66 100644 --- a/src/mongo/s/commands/cluster_find_cmd.cpp +++ b/src/mongo/s/commands/cluster_find_cmd.cpp @@ -56,31 +56,29 @@ using std::vector; const char kTermField[] = "term"; -// Parses the command object to a QueryRequest, validates that no runtime constants were supplied +// Parses the command object to a FindCommand, validates that no runtime constants were supplied // with the command, and sets the constant runtime values that will be forwarded to each shard. -std::unique_ptr<QueryRequest> parseCmdObjectToQueryRequest(OperationContext* opCtx, - NamespaceString nss, - BSONObj cmdObj, - bool isExplain) { - auto qr = - QueryRequest::makeFromFindCommand(std::move(cmdObj), - isExplain, - std::move(nss), - APIParameters::get(opCtx).getAPIStrict().value_or(false)); - if (!qr->getReadConcern()) { +std::unique_ptr<FindCommand> parseCmdObjectToFindCommand(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)); + if (!findCommand->getReadConcern()) { if (opCtx->isStartingMultiDocumentTransaction() || !opCtx->inMultiDocumentTransaction()) { // If there is no explicit readConcern in the cmdObj, and this is either the first // operation in a transaction, or not running in a transaction, then use the readConcern // from the opCtx (which may be a cluster-wide default). const auto& readConcernArgs = repl::ReadConcernArgs::get(opCtx); - qr->setReadConcern(readConcernArgs.toBSONInner()); + findCommand->setReadConcern(readConcernArgs.toBSONInner()); } } uassert(51202, "Cannot specify runtime constants option to a mongos", - !qr->getLegacyRuntimeConstants()); - qr->setLegacyRuntimeConstants(Variables::generateRuntimeConstants(opCtx)); - return qr; + !findCommand->getLegacyRuntimeConstants()); + findCommand->setLegacyRuntimeConstants(Variables::generateRuntimeConstants(opCtx)); + return findCommand; } /** @@ -153,31 +151,31 @@ public: void explain(OperationContext* opCtx, ExplainOptions::Verbosity verbosity, rpc::ReplyBuilderInterface* result) override { - // Parse the command BSON to a QueryRequest. - const bool isExplain = true; - auto qr = parseCmdObjectToQueryRequest(opCtx, ns(), _request.body, isExplain); + // Parse the command BSON to a FindCommand. + auto findCommand = parseCmdObjectToFindCommand(opCtx, ns(), _request.body); try { const auto explainCmd = - ClusterExplain::wrapAsExplain(qr->asFindCommand(), verbosity); + ClusterExplain::wrapAsExplain(findCommand->toBSON(BSONObj()), verbosity); long long millisElapsed; std::vector<AsyncRequestsSender::Response> shardResponses; // We will time how long it takes to run the commands on the shards. Timer timer; - const auto routingInfo = uassertStatusOK( - Grid::get(opCtx)->catalogCache()->getCollectionRoutingInfo(opCtx, qr->nss())); - shardResponses = - scatterGatherVersionedTargetByRoutingTable(opCtx, - qr->nss().db(), - qr->nss(), - routingInfo, - explainCmd, - ReadPreferenceSetting::get(opCtx), - Shard::RetryPolicy::kIdempotent, - qr->getFilter(), - qr->getCollation()); + const auto routingInfo = + uassertStatusOK(Grid::get(opCtx)->catalogCache()->getCollectionRoutingInfo( + opCtx, *findCommand->getNamespaceOrUUID().nss())); + shardResponses = scatterGatherVersionedTargetByRoutingTable( + opCtx, + findCommand->getNamespaceOrUUID().nss()->db(), + *findCommand->getNamespaceOrUUID().nss(), + routingInfo, + explainCmd, + ReadPreferenceSetting::get(opCtx), + Shard::RetryPolicy::kIdempotent, + findCommand->getFilter(), + findCommand->getCollation()); millisElapsed = timer.millis(); const char* mongosStageName = @@ -195,7 +193,8 @@ public: auto bodyBuilder = result->getBodyBuilder(); bodyBuilder.resetToEmpty(); - auto aggCmdOnView = uassertStatusOK(qr->asAggregationCommand()); + auto aggCmdOnView = + uassertStatusOK(query_request_helper::asAggregationCommand(*findCommand)); auto viewAggregationCommand = OpMsgRequest::fromDBAndBody(_dbName, aggCmdOnView).body; @@ -226,13 +225,13 @@ public: opCtx, mongo::LogicalOp::opQuery); }); - const bool isExplain = false; - auto qr = parseCmdObjectToQueryRequest(opCtx, ns(), _request.body, isExplain); + auto findCommand = parseCmdObjectToFindCommand(opCtx, ns(), _request.body); const boost::intrusive_ptr<ExpressionContext> expCtx; auto cq = uassertStatusOK( CanonicalQuery::canonicalize(opCtx, - std::move(qr), + std::move(findCommand), + false, /* isExplain */ expCtx, ExtensionsCallbackNoop(), MatchExpressionParser::kAllowAllSpecialFeatures)); @@ -261,7 +260,8 @@ public: } catch (const ExceptionFor<ErrorCodes::CommandOnShardedViewNotSupportedOnMongod>& ex) { result->reset(); - auto aggCmdOnView = uassertStatusOK(cq->getQueryRequest().asAggregationCommand()); + auto aggCmdOnView = uassertStatusOK( + query_request_helper::asAggregationCommand(cq->getFindCommand())); auto viewAggregationCommand = OpMsgRequest::fromDBAndBody(_dbName, aggCmdOnView).body; diff --git a/src/mongo/s/commands/strategy.cpp b/src/mongo/s/commands/strategy.cpp index bb97a416c7d..f4db2effe4b 100644 --- a/src/mongo/s/commands/strategy.cpp +++ b/src/mongo/s/commands/strategy.cpp @@ -58,7 +58,7 @@ #include "mongo/db/ops/write_ops.h" #include "mongo/db/query/find_common.h" #include "mongo/db/query/getmore_request.h" -#include "mongo/db/query/query_request.h" +#include "mongo/db/query/query_request_helper.h" #include "mongo/db/read_write_concern_defaults.h" #include "mongo/db/stats/api_version_metrics.h" #include "mongo/db/stats/counters.h" @@ -498,9 +498,9 @@ void ParseAndRunCommand::_parseCommand() { // maxTimeMS altogether on a getMore command. uassert(ErrorCodes::InvalidOptions, "no such command option $maxTimeMs; use maxTimeMS instead", - request.body[QueryRequest::queryOptionMaxTimeMS].eoo()); + request.body[query_request_helper::queryOptionMaxTimeMS].eoo()); const int maxTimeMS = - uassertStatusOK(parseMaxTimeMS(request.body[QueryRequest::cmdOptionMaxTimeMS])); + uassertStatusOK(parseMaxTimeMS(request.body[query_request_helper::cmdOptionMaxTimeMS])); if (maxTimeMS > 0 && command->getLogicalOp() != LogicalOp::opGetMore) { opCtx->setDeadlineAfterNowBy(Milliseconds{maxTimeMS}, ErrorCodes::MaxTimeMSExpired); } @@ -1096,29 +1096,29 @@ DbResponse Strategy::queryOp(OperationContext* opCtx, const NamespaceString& nss ExtensionsCallbackNoop(), MatchExpressionParser::kAllowAllSpecialFeatures)); - const QueryRequest& queryRequest = canonicalQuery->getQueryRequest(); + const FindCommand& findCommand = canonicalQuery->getFindCommand(); // Handle query option $maxTimeMS (not used with commands). - if (queryRequest.getMaxTimeMS() > 0) { + if (findCommand.getMaxTimeMS().value_or(0) > 0) { uassert(50749, "Illegal attempt to set operation deadline within DBDirectClient", !opCtx->getClient()->isInDirectClient()); - opCtx->setDeadlineAfterNowBy(Milliseconds{queryRequest.getMaxTimeMS()}, + opCtx->setDeadlineAfterNowBy(Milliseconds{findCommand.getMaxTimeMS().value_or(0)}, ErrorCodes::MaxTimeMSExpired); } opCtx->checkForInterrupt(); // May trigger maxTimeAlwaysTimeOut fail point. // If the $explain flag was set, we must run the operation on the shards as an explain command // rather than a find command. - if (queryRequest.isExplain()) { - const BSONObj findCommand = queryRequest.asFindCommand(); + if (canonicalQuery->getExplain()) { + const BSONObj findCommandObj = findCommand.toBSON(BSONObj()); // We default to allPlansExecution verbosity. const auto verbosity = ExplainOptions::Verbosity::kExecAllPlans; BSONObjBuilder explainBuilder; Strategy::explainFind(opCtx, + findCommandObj, findCommand, - queryRequest, verbosity, ReadPreferenceSetting::get(opCtx), &explainBuilder); @@ -1444,12 +1444,12 @@ void Strategy::writeOp(std::shared_ptr<RequestExecutionContext> rec) { } void Strategy::explainFind(OperationContext* opCtx, - const BSONObj& findCommand, - const QueryRequest& qr, + const BSONObj& findCommandObj, + const FindCommand& findCommand, ExplainOptions::Verbosity verbosity, const ReadPreferenceSetting& readPref, BSONObjBuilder* out) { - const auto explainCmd = ClusterExplain::wrapAsExplain(findCommand, verbosity); + const auto explainCmd = ClusterExplain::wrapAsExplain(findCommandObj, verbosity); long long millisElapsed; std::vector<AsyncRequestsSender::Response> shardResponses; @@ -1460,18 +1460,20 @@ void Strategy::explainFind(OperationContext* opCtx, // We will time how long it takes to run the commands on the shards. Timer timer; try { - const auto routingInfo = uassertStatusOK( - Grid::get(opCtx)->catalogCache()->getCollectionRoutingInfo(opCtx, qr.nss())); - shardResponses = - scatterGatherVersionedTargetByRoutingTable(opCtx, - qr.nss().db(), - qr.nss(), - routingInfo, - explainCmd, - readPref, - Shard::RetryPolicy::kIdempotent, - qr.getFilter(), - qr.getCollation()); + invariant(findCommand.getNamespaceOrUUID().nss()); + const auto routingInfo = + uassertStatusOK(Grid::get(opCtx)->catalogCache()->getCollectionRoutingInfo( + opCtx, *findCommand.getNamespaceOrUUID().nss())); + shardResponses = scatterGatherVersionedTargetByRoutingTable( + opCtx, + findCommand.getNamespaceOrUUID().nss()->db(), + *findCommand.getNamespaceOrUUID().nss(), + routingInfo, + explainCmd, + readPref, + Shard::RetryPolicy::kIdempotent, + findCommand.getFilter(), + findCommand.getCollation()); millisElapsed = timer.millis(); break; } catch (ExceptionFor<ErrorCodes::ShardInvalidatedForTargeting>&) { @@ -1524,9 +1526,9 @@ void Strategy::explainFind(OperationContext* opCtx, } const char* mongosStageName = - ClusterExplain::getStageNameForReadOp(shardResponses.size(), findCommand); + ClusterExplain::getStageNameForReadOp(shardResponses.size(), findCommandObj); uassertStatusOK(ClusterExplain::buildExplainResult( - opCtx, shardResponses, mongosStageName, millisElapsed, findCommand, out)); + opCtx, shardResponses, mongosStageName, millisElapsed, findCommandObj, out)); } } // namespace mongo diff --git a/src/mongo/s/commands/strategy.h b/src/mongo/s/commands/strategy.h index 95f3f8c9449..4ea30690bf2 100644 --- a/src/mongo/s/commands/strategy.h +++ b/src/mongo/s/commands/strategy.h @@ -43,7 +43,7 @@ struct DbResponse; class Message; class NamespaceString; class OperationContext; -class QueryRequest; +class FindCommand; /** * Legacy interface for processing client read/write/cmd requests. @@ -93,8 +93,8 @@ public: * $explain modifier. */ static void explainFind(OperationContext* opCtx, - const BSONObj& findCommand, - const QueryRequest& qr, + const BSONObj& findCommandObj, + const FindCommand& findCommand, ExplainOptions::Verbosity verbosity, const ReadPreferenceSetting& readPref, BSONObjBuilder* out); diff --git a/src/mongo/s/query/async_results_merger_test.cpp b/src/mongo/s/query/async_results_merger_test.cpp index 00112add13b..fd156343e2a 100644 --- a/src/mongo/s/query/async_results_merger_test.cpp +++ b/src/mongo/s/query/async_results_merger_test.cpp @@ -38,7 +38,7 @@ #include "mongo/db/pipeline/resume_token.h" #include "mongo/db/query/cursor_response.h" #include "mongo/db/query/getmore_request.h" -#include "mongo/db/query/query_request.h" +#include "mongo/db/query/query_request_helper.h" #include "mongo/executor/task_executor.h" #include "mongo/s/catalog/type_shard.h" #include "mongo/s/client/shard_registry.h" diff --git a/src/mongo/s/query/cluster_find.cpp b/src/mongo/s/query/cluster_find.cpp index 7fac4105e93..02c4b570ac9 100644 --- a/src/mongo/s/query/cluster_find.cpp +++ b/src/mongo/s/query/cluster_find.cpp @@ -84,86 +84,94 @@ static const int kPerDocumentOverheadBytesUpperBound = 10; const char kFindCmdName[] = "find"; /** - * Given the QueryRequest 'qr' being executed by mongos, returns a copy of the query which is - * suitable for forwarding to the targeted hosts. + * Given the FindCommand 'findCommand' being executed by mongos, returns a copy of the query which + * is suitable for forwarding to the targeted hosts. */ -StatusWith<std::unique_ptr<QueryRequest>> transformQueryForShards( - const QueryRequest& qr, bool appendGeoNearDistanceProjection) { +StatusWith<std::unique_ptr<FindCommand>> transformQueryForShards( + const FindCommand& findCommand, bool appendGeoNearDistanceProjection) { // If there is a limit, we forward the sum of the limit and the skip. boost::optional<int64_t> newLimit; - if (qr.getLimit()) { + if (findCommand.getLimit()) { long long newLimitValue; - if (overflow::add(*qr.getLimit(), qr.getSkip().value_or(0), &newLimitValue)) { + if (overflow::add( + *findCommand.getLimit(), findCommand.getSkip().value_or(0), &newLimitValue)) { return Status( ErrorCodes::Overflow, str::stream() << "sum of limit and skip cannot be represented as a 64-bit integer, limit: " - << *qr.getLimit() << ", skip: " << qr.getSkip().value_or(0)); + << *findCommand.getLimit() << ", skip: " << findCommand.getSkip().value_or(0)); } newLimit = newLimitValue; } // Similarly, if nToReturn is set, we forward the sum of nToReturn and the skip. boost::optional<int64_t> newNToReturn; - if (qr.getNToReturn()) { + if (findCommand.getNtoreturn()) { // 'singleBatch' and ntoreturn mean the same as 'singleBatch' and limit, so perform the // conversion. - if (qr.isSingleBatch()) { + if (findCommand.getSingleBatch()) { int64_t newLimitValue; - if (overflow::add(*qr.getNToReturn(), qr.getSkip().value_or(0), &newLimitValue)) { + if (overflow::add(*findCommand.getNtoreturn(), + findCommand.getSkip().value_or(0), + &newLimitValue)) { return Status(ErrorCodes::Overflow, str::stream() << "sum of ntoreturn and skip cannot be represented as a 64-bit " "integer, ntoreturn: " - << *qr.getNToReturn() << ", skip: " << qr.getSkip().value_or(0)); + << *findCommand.getNtoreturn() + << ", skip: " << findCommand.getSkip().value_or(0)); } newLimit = newLimitValue; } else { int64_t newNToReturnValue; - if (overflow::add(*qr.getNToReturn(), qr.getSkip().value_or(0), &newNToReturnValue)) { + if (overflow::add(*findCommand.getNtoreturn(), + findCommand.getSkip().value_or(0), + &newNToReturnValue)) { return Status(ErrorCodes::Overflow, str::stream() << "sum of ntoreturn and skip cannot be represented as a 64-bit " "integer, ntoreturn: " - << *qr.getNToReturn() << ", skip: " << qr.getSkip().value_or(0)); + << *findCommand.getNtoreturn() + << ", skip: " << findCommand.getSkip().value_or(0)); } newNToReturn = newNToReturnValue; } } // If there is a sort other than $natural, we send a sortKey meta-projection to the remote node. - BSONObj newProjection = qr.getProj(); - if (!qr.getSort().isEmpty() && !qr.getSort()[QueryRequest::kNaturalSortField]) { + BSONObj newProjection = findCommand.getProjection(); + if (!findCommand.getSort().isEmpty() && + !findCommand.getSort()[query_request_helper::kNaturalSortField]) { BSONObjBuilder projectionBuilder; - projectionBuilder.appendElements(qr.getProj()); + projectionBuilder.appendElements(findCommand.getProjection()); projectionBuilder.append(AsyncResultsMerger::kSortKeyField, kSortKeyMetaProjection); newProjection = projectionBuilder.obj(); } if (appendGeoNearDistanceProjection) { - invariant(qr.getSort().isEmpty()); + invariant(findCommand.getSort().isEmpty()); BSONObjBuilder projectionBuilder; - projectionBuilder.appendElements(qr.getProj()); + projectionBuilder.appendElements(findCommand.getProjection()); projectionBuilder.append(AsyncResultsMerger::kSortKeyField, kGeoNearDistanceMetaProjection); newProjection = projectionBuilder.obj(); } - auto newQR = std::make_unique<QueryRequest>(qr); - newQR->setProj(newProjection); + auto newQR = std::make_unique<FindCommand>(findCommand); + newQR->setProjection(newProjection); newQR->setSkip(boost::none); newQR->setLimit(newLimit); - newQR->setNToReturn(newNToReturn); + newQR->setNtoreturn(newNToReturn); // Even if the client sends us singleBatch=true, we may need to retrieve // multiple batches from a shard in order to return the single requested batch to the client. // Therefore, we must always send singleBatch=false to the shards. - newQR->setSingleBatchField(false); + newQR->setSingleBatch(false); // Any expansion of the 'showRecordId' flag should have already happened on mongos. - if (newQR->showRecordId()) + if (newQR->getShowRecordId()) newQR->setShowRecordId(false); - invariant(newQR->validate()); + uassertStatusOK(query_request_helper::validateFindCommand(*newQR)); return std::move(newQR); } @@ -178,20 +186,20 @@ std::vector<std::pair<ShardId, BSONObj>> constructRequestsForShards( const CanonicalQuery& query, bool appendGeoNearDistanceProjection) { - std::unique_ptr<QueryRequest> qrToForward; + std::unique_ptr<FindCommand> findCommandToForward; if (shardIds.size() > 1) { - qrToForward = uassertStatusOK( - transformQueryForShards(query.getQueryRequest(), appendGeoNearDistanceProjection)); + findCommandToForward = uassertStatusOK( + transformQueryForShards(query.getFindCommand(), appendGeoNearDistanceProjection)); } else { - // Forwards the QueryRequest as is to a single shard so that limit and skip can + // Forwards the FindCommand as is to a single shard so that limit and skip can // be applied on mongod. - qrToForward = std::make_unique<QueryRequest>(query.getQueryRequest()); + findCommandToForward = std::make_unique<FindCommand>(query.getFindCommand()); } auto& readConcernArgs = repl::ReadConcernArgs::get(opCtx); if (readConcernArgs.wasAtClusterTimeSelected()) { // If mongos selected atClusterTime or received it from client, transmit it to shard. - qrToForward->setReadConcern(readConcernArgs.toBSONInner()); + findCommandToForward->setReadConcern(readConcernArgs.toBSONInner()); } auto shardRegistry = Grid::get(opCtx)->shardRegistry(); @@ -201,7 +209,7 @@ std::vector<std::pair<ShardId, BSONObj>> constructRequestsForShards( invariant(!shard->isConfig() || shard->getConnString()); BSONObjBuilder cmdBuilder; - qrToForward->asFindCommand(&cmdBuilder); + findCommandToForward->serialize(BSONObj(), &cmdBuilder); if (cm.isSharded()) { cm.getVersion(shardId).appendToCommand(&cmdBuilder); @@ -240,20 +248,20 @@ CursorId runQueryWithoutRetrying(OperationContext* opCtx, const ChunkManager& cm, std::vector<BSONObj>* results, bool* partialResultsReturned) { + auto findCommand = query.getFindCommand(); // Get the set of shards on which we will run the query. - auto shardIds = getTargetedShardsForQuery(query.getExpCtx(), - cm, - query.getQueryRequest().getFilter(), - query.getQueryRequest().getCollation()); + auto shardIds = getTargetedShardsForQuery( + query.getExpCtx(), cm, findCommand.getFilter(), findCommand.getCollation()); // Construct the query and parameters. Defer setting skip and limit here until // we determine if the query is targeting multi-shards or a single shard below. ClusterClientCursorParams params( query.nss(), APIParameters::get(opCtx), readPref, ReadConcernArgs::get(opCtx)); params.originatingCommandObj = CurOp::get(opCtx)->opDescription().getOwned(); - params.batchSize = query.getQueryRequest().getEffectiveBatchSize(); - params.tailableMode = query.getQueryRequest().getTailableMode(); - params.isAllowPartialResults = query.getQueryRequest().isAllowPartialResults(); + params.batchSize = + findCommand.getBatchSize() ? findCommand.getBatchSize() : findCommand.getNtoreturn(); + params.tailableMode = query_request_helper::getTailableMode(findCommand); + params.isAllowPartialResults = findCommand.getAllowPartialResults(); params.lsid = opCtx->getLogicalSessionId(); params.txnNumber = opCtx->getTxnNumber(); params.originatingPrivileges = { @@ -274,8 +282,7 @@ CursorId runQueryWithoutRetrying(OperationContext* opCtx, // sort on mongos. Including a $natural anywhere in the sort spec results in the whole sort // being considered a hint to use a collection scan. BSONObj sortComparatorObj; - if (query.getSortPattern() && - !query.getQueryRequest().getSort()[QueryRequest::kNaturalSortField]) { + if (query.getSortPattern() && !findCommand.getSort()[query_request_helper::kNaturalSortField]) { // We have already validated the input sort object. Serialize the raw sort spec into one // suitable for use as the ordering specification in BSONObj::woCompare(). In particular, we // want to eliminate sorts using expressions (like $meta) and replace them with a @@ -303,7 +310,7 @@ CursorId runQueryWithoutRetrying(OperationContext* opCtx, // Tailable cursors can't have a sort, which should have already been validated. tassert(4457013, "tailable cursor unexpectedly has a sort", - sortComparatorObj.isEmpty() || !query.getQueryRequest().isTailable()); + sortComparatorObj.isEmpty() || !findCommand.getTailable()); // Construct the requests that we will use to establish cursors on the targeted shards, // attaching the shardVersion and txnNumber, if necessary. @@ -317,7 +324,7 @@ CursorId runQueryWithoutRetrying(OperationContext* opCtx, query.nss(), readPref, requests, - query.getQueryRequest().isAllowPartialResults()); + findCommand.getAllowPartialResults()); // Determine whether the cursor we may eventually register will be single- or multi-target. @@ -328,9 +335,8 @@ CursorId runQueryWithoutRetrying(OperationContext* opCtx, // Only set skip, limit and sort to be applied to on the router for the multi-shard case. For // the single-shard case skip/limit as well as sorts are appled on mongod. if (cursorType == ClusterCursorManager::CursorType::MultiTarget) { - const auto qr = query.getQueryRequest(); - params.skipToApplyOnRouter = qr.getSkip(); - params.limit = qr.getLimit(); + params.skipToApplyOnRouter = findCommand.getSkip(); + params.limit = findCommand.getLimit(); params.sortToApplyOnRouter = sortComparatorObj; params.compareWholeSortKeyOnRouter = compareWholeSortKeyOnRouter; } @@ -350,7 +356,7 @@ CursorId runQueryWithoutRetrying(OperationContext* opCtx, // This loop will not result in actually calling getMore against shards, but just loading // results from the initial batches (that were obtained while establishing cursors) into // 'results'. - while (!FindCommon::enoughForFirstBatch(query.getQueryRequest(), results->size())) { + while (!FindCommon::enoughForFirstBatch(findCommand, results->size())) { auto next = uassertStatusOK(ccc->next(RouterExecStage::ExecContext::kInitialFind)); if (next.isEOF()) { @@ -381,7 +387,7 @@ CursorId runQueryWithoutRetrying(OperationContext* opCtx, ccc->detachFromOperationContext(); - if (query.getQueryRequest().isSingleBatch() && !ccc->isTailable()) { + if (findCommand.getSingleBatch() && !ccc->isTailable()) { cursorState = ClusterCursorManager::CursorState::Exhausted; } @@ -408,7 +414,7 @@ CursorId runQueryWithoutRetrying(OperationContext* opCtx, // Register the cursor with the cursor manager for subsequent getMore's. auto cursorManager = Grid::get(opCtx)->getCursorManager(); - const auto cursorLifetime = query.getQueryRequest().isNoCursorTimeout() + const auto cursorLifetime = findCommand.getNoCursorTimeout() ? ClusterCursorManager::CursorLifetime::Immortal : ClusterCursorManager::CursorLifetime::Mortal; auto authUsers = AuthorizationSession::get(opCtx->getClient())->getAuthenticatedUserNames(); @@ -488,19 +494,19 @@ CursorId ClusterFind::runQuery(OperationContext* opCtx, // We must always have a BSONObj vector into which to output our results. invariant(results); + auto findCommand = query.getFindCommand(); // Projection on the reserved sort key field is illegal in mongos. - if (query.getQueryRequest().getProj().hasField(AsyncResultsMerger::kSortKeyField)) { + if (findCommand.getProjection().hasField(AsyncResultsMerger::kSortKeyField)) { uasserted(ErrorCodes::BadValue, str::stream() << "Projection contains illegal field '" << AsyncResultsMerger::kSortKeyField - << "': " << query.getQueryRequest().getProj()); + << "': " << findCommand.getProjection()); } // Attempting to establish a resumable query through mongoS is illegal. uassert(ErrorCodes::BadValue, "Queries on mongoS may not request or provide a resume token", - !query.getQueryRequest().getRequestResumeToken() && - query.getQueryRequest().getResumeAfter().isEmpty()); + !findCommand.getRequestResumeToken() && findCommand.getResumeAfter().isEmpty()); auto const catalogCache = Grid::get(opCtx)->catalogCache(); diff --git a/src/mongo/s/query/results_merger_test_fixture.h b/src/mongo/s/query/results_merger_test_fixture.h index d653bfe0bfa..10978c11cb7 100644 --- a/src/mongo/s/query/results_merger_test_fixture.h +++ b/src/mongo/s/query/results_merger_test_fixture.h @@ -72,22 +72,22 @@ protected: if (findCmd) { // If there is no '$db', append it. auto cmd = OpMsgRequest::fromDBAndBody(kTestNss.db(), *findCmd).body; - const auto qr = - QueryRequest::makeFromFindCommandForTests(cmd, false /* isExplain */, kTestNss); - if (!qr->getSort().isEmpty()) { - params.setSort(qr->getSort().getOwned()); + const auto findCommand = + query_request_helper::makeFromFindCommandForTests(cmd, kTestNss); + if (!findCommand->getSort().isEmpty()) { + params.setSort(findCommand->getSort().getOwned()); } if (getMoreBatchSize) { params.setBatchSize(getMoreBatchSize); } else { - params.setBatchSize(qr->getBatchSize() - ? boost::optional<std::int64_t>( - static_cast<std::int64_t>(*qr->getBatchSize())) + params.setBatchSize(findCommand->getBatchSize() + ? boost::optional<std::int64_t>(static_cast<std::int64_t>( + *findCommand->getBatchSize())) : boost::none); } - params.setTailableMode(qr->getTailableMode()); - params.setAllowPartialResults(qr->isAllowPartialResults()); + params.setTailableMode(query_request_helper::getTailableMode(*findCommand)); + params.setAllowPartialResults(findCommand->getAllowPartialResults()); } OperationSessionInfoFromClient sessionInfo; diff --git a/src/mongo/s/request_types/set_shard_version_request.cpp b/src/mongo/s/request_types/set_shard_version_request.cpp index de6d902b6cf..808f480a725 100644 --- a/src/mongo/s/request_types/set_shard_version_request.cpp +++ b/src/mongo/s/request_types/set_shard_version_request.cpp @@ -35,7 +35,7 @@ #include "mongo/bson/bsonobj.h" #include "mongo/bson/bsonobjbuilder.h" #include "mongo/bson/util/bson_extract.h" -#include "mongo/db/query/query_request.h" +#include "mongo/db/query/query_request_helper.h" #include "mongo/util/assert_util.h" #include "mongo/util/str.h" diff --git a/src/mongo/s/sessions_collection_sharded.cpp b/src/mongo/s/sessions_collection_sharded.cpp index 76946cbd900..c986fabc535 100644 --- a/src/mongo/s/sessions_collection_sharded.cpp +++ b/src/mongo/s/sessions_collection_sharded.cpp @@ -34,7 +34,7 @@ #include "mongo/db/matcher/extensions_callback_noop.h" #include "mongo/db/operation_context.h" #include "mongo/db/query/canonical_query.h" -#include "mongo/db/query/query_request.h" +#include "mongo/db/query/query_request_helper.h" #include "mongo/db/sessions_collection_rs.h" #include "mongo/rpc/get_status_from_command_result.h" #include "mongo/rpc/op_msg.h" @@ -172,13 +172,14 @@ LogicalSessionIdSet SessionsCollectionSharded::findRemovedSessions( toSend = OpMsgRequest::fromDBAndBody(NamespaceString::kLogicalSessionsNamespace.db(), toSend) .body; - auto qr = QueryRequest::makeFromFindCommand( - toSend, false, NamespaceString::kLogicalSessionsNamespace, apiStrict); + auto findCommand = query_request_helper::makeFromFindCommand( + toSend, NamespaceString::kLogicalSessionsNamespace, apiStrict); const boost::intrusive_ptr<ExpressionContext> expCtx; auto cq = uassertStatusOK( CanonicalQuery::canonicalize(opCtx, - std::move(qr), + std::move(findCommand), + false, expCtx, ExtensionsCallbackNoop(), MatchExpressionParser::kBanAllSpecialFeatures)); diff --git a/src/mongo/s/shard_key_pattern.cpp b/src/mongo/s/shard_key_pattern.cpp index 94e6db3dbea..96156876552 100644 --- a/src/mongo/s/shard_key_pattern.cpp +++ b/src/mongo/s/shard_key_pattern.cpp @@ -390,13 +390,14 @@ BSONObj ShardKeyPattern::emplaceMissingShardKeyValuesForDocument(const BSONObj d StatusWith<BSONObj> ShardKeyPattern::extractShardKeyFromQuery(OperationContext* opCtx, const NamespaceString& nss, const BSONObj& basicQuery) const { - auto qr = std::make_unique<QueryRequest>(nss); - qr->setFilter(basicQuery); + auto findCommand = std::make_unique<FindCommand>(nss); + findCommand->setFilter(basicQuery.getOwned()); const boost::intrusive_ptr<ExpressionContext> expCtx; auto statusWithCQ = CanonicalQuery::canonicalize(opCtx, - std::move(qr), + std::move(findCommand), + false, /* isExplain */ expCtx, ExtensionsCallbackNoop(), MatchExpressionParser::kAllowAllSpecialFeatures); @@ -409,15 +410,16 @@ StatusWith<BSONObj> ShardKeyPattern::extractShardKeyFromQuery(OperationContext* StatusWith<BSONObj> ShardKeyPattern::extractShardKeyFromQuery( boost::intrusive_ptr<ExpressionContext> expCtx, const BSONObj& basicQuery) const { - auto qr = std::make_unique<QueryRequest>(expCtx->ns); - qr->setFilter(basicQuery); + auto findCommand = std::make_unique<FindCommand>(expCtx->ns); + findCommand->setFilter(basicQuery.getOwned()); if (!expCtx->getCollatorBSON().isEmpty()) { - qr->setCollation(expCtx->getCollatorBSON()); + findCommand->setCollation(expCtx->getCollatorBSON().getOwned()); } auto statusWithCQ = CanonicalQuery::canonicalize(expCtx->opCtx, - std::move(qr), + std::move(findCommand), + false, /* isExplain */ expCtx, ExtensionsCallbackNoop(), MatchExpressionParser::kAllowAllSpecialFeatures); diff --git a/src/mongo/s/sharding_router_test_fixture.cpp b/src/mongo/s/sharding_router_test_fixture.cpp index 3e42317a93e..8c7af5b2001 100644 --- a/src/mongo/s/sharding_router_test_fixture.cpp +++ b/src/mongo/s/sharding_router_test_fixture.cpp @@ -44,7 +44,7 @@ #include "mongo/db/namespace_string.h" #include "mongo/db/ops/write_ops.h" #include "mongo/db/query/collation/collator_factory_mock.h" -#include "mongo/db/query/query_request.h" +#include "mongo/db/query/query_request_helper.h" #include "mongo/db/repl/read_concern_args.h" #include "mongo/db/vector_clock_metadata_hook.h" #include "mongo/executor/task_executor_pool.h" @@ -257,8 +257,8 @@ void ShardingTestFixture::expectGetShards(const std::vector<ShardType>& shards) // If there is no '$db', append it. auto cmd = OpMsgRequest::fromDBAndBody(nss.db(), request.cmdObj).body; - auto query = QueryRequest::makeFromFindCommandForTests(cmd, false, nss); - ASSERT_EQ(query->nss(), ShardType::ConfigNS); + auto query = query_request_helper::makeFromFindCommandForTests(cmd, nss); + ASSERT_EQ(*query->getNamespaceOrUUID().nss(), ShardType::ConfigNS); ASSERT_BSONOBJ_EQ(query->getFilter(), BSONObj()); ASSERT_BSONOBJ_EQ(query->getSort(), BSONObj()); diff --git a/src/mongo/s/write_ops/chunk_manager_targeter.cpp b/src/mongo/s/write_ops/chunk_manager_targeter.cpp index 7c3b2fb8a27..a6770cc03c7 100644 --- a/src/mongo/s/write_ops/chunk_manager_targeter.cpp +++ b/src/mongo/s/write_ops/chunk_manager_targeter.cpp @@ -180,7 +180,7 @@ bool isExactIdQuery(OperationContext* opCtx, const CanonicalQuery& query, const } if (CollationIndexKey::isCollatableType(idElt.type()) && cm.isSharded() && - !query.getQueryRequest().getCollation().isEmpty() && + !query.getFindCommand().getCollation().isEmpty() && !CollatorInterface::collatorsMatch(query.getCollator(), cm.getDefaultCollator())) { // The collation applies to the _id field, but the user specified a collation which doesn't @@ -196,13 +196,14 @@ bool isExactIdQuery(OperationContext* opCtx, const BSONObj query, const BSONObj collation, const ChunkManager& cm) { - auto qr = std::make_unique<QueryRequest>(nss); - qr->setFilter(query); + auto findCommand = std::make_unique<FindCommand>(nss); + findCommand->setFilter(query); if (!collation.isEmpty()) { - qr->setCollation(collation); + findCommand->setCollation(collation); } const auto cq = CanonicalQuery::canonicalize(opCtx, - std::move(qr), + std::move(findCommand), + false, /* isExplain */ nullptr, ExtensionsCallbackNoop(), MatchExpressionParser::kAllowAllSpecialFeatures); @@ -506,14 +507,15 @@ std::vector<ShardEndpoint> ChunkManagerTargeter::targetDelete(OperationContext* // We failed to target a single shard. // Parse delete query. - auto qr = std::make_unique<QueryRequest>(_nss); - qr->setFilter(deleteOp.getQ()); + auto findCommand = std::make_unique<FindCommand>(_nss); + findCommand->setFilter(deleteOp.getQ()); if (!collation.isEmpty()) { - qr->setCollation(collation); + findCommand->setCollation(collation); } auto cq = uassertStatusOKWithContext( CanonicalQuery::canonicalize(opCtx, - std::move(qr), + std::move(findCommand), + false, /* isExplain */ expCtx, ExtensionsCallbackNoop(), MatchExpressionParser::kAllowAllSpecialFeatures), diff --git a/src/mongo/shell/bench.cpp b/src/mongo/shell/bench.cpp index 6aaf8a679ad..5585c1483a5 100644 --- a/src/mongo/shell/bench.cpp +++ b/src/mongo/shell/bench.cpp @@ -40,7 +40,7 @@ #include "mongo/db/namespace_string.h" #include "mongo/db/query/cursor_response.h" #include "mongo/db/query/getmore_request.h" -#include "mongo/db/query/query_request.h" +#include "mongo/db/query/query_request_helper.h" #include "mongo/logv2/log.h" #include "mongo/scripting/bson_template_evaluator.h" #include "mongo/stdx/thread.h" @@ -207,23 +207,24 @@ void abortTransaction(DBClientBase* conn, } /** - * Issues the query 'qr' against 'conn' using read commands. Returns the size of the result set - * returned by the query. + * Issues the query 'findCommand' against 'conn' using read commands. Returns the size of the result + * set returned by the query. * - * If 'qr' has the 'wantMore' flag set to false and the 'limit' option set to 1LL, then the caller - * may optionally specify a pointer to an object in 'objOut', which will be filled in with the - * single object in the query result set (or the empty object, if the result set is empty). - * If 'qr' doesn't have these options set, then nullptr must be passed for 'objOut'. + * If 'findCommand' has the 'wantMore' flag set to false and the 'limit' option set to 1LL, then the + * caller may optionally specify a pointer to an object in 'objOut', which will be filled in with + * the single object in the query result set (or the empty object, if the result set is empty). If + * 'findCommand' doesn't have these options set, then nullptr must be passed for 'objOut'. * * On error, throws a AssertionException. */ int runQueryWithReadCommands(DBClientBase* conn, const boost::optional<LogicalSessionIdToClient>& lsid, boost::optional<TxnNumber> txnNumber, - std::unique_ptr<QueryRequest> qr, + std::unique_ptr<FindCommand> findCommand, Milliseconds delayBeforeGetMore, BSONObj* objOut) { - const auto dbName = qr->nss().db().toString(); + const auto dbName = + findCommand->getNamespaceOrUUID().nss().value_or(NamespaceString()).db().toString(); BSONObj findCommandResult; uassert(ErrorCodes::CommandFailed, @@ -231,7 +232,7 @@ int runQueryWithReadCommands(DBClientBase* conn, runCommandWithSession( conn, dbName, - qr->asFindCommand(), + findCommand->toBSON(BSONObj()), // read command with txnNumber implies performing reads in a // multi-statement transaction txnNumber ? kStartTransactionOption | kMultiStatementTransactionOption : kNoOptions, @@ -244,7 +245,8 @@ int runQueryWithReadCommands(DBClientBase* conn, int count = cursorResponse.getBatch().size(); if (objOut) { - invariant(qr->getLimit() && *qr->getLimit() == 1 && qr->isSingleBatch()); + invariant(findCommand->getLimit() && *findCommand->getLimit() == 1 && + findCommand->getSingleBatch()); // Since this is a "single batch" query, we can simply grab the first item in the result set // and return here. *objOut = (count > 0) ? cursorResponse.getBatch()[0] : BSONObj(); @@ -255,11 +257,11 @@ int runQueryWithReadCommands(DBClientBase* conn, sleepFor(delayBeforeGetMore); GetMoreRequest getMoreRequest( - qr->nss(), + findCommand->getNamespaceOrUUID().nss().value_or(NamespaceString()), cursorResponse.getCursorId(), - qr->getBatchSize() - ? boost::optional<std::int64_t>(static_cast<std::int64_t>(*qr->getBatchSize())) - : boost::none, + findCommand->getBatchSize() ? boost::optional<std::int64_t>(static_cast<std::int64_t>( + *findCommand->getBatchSize())) + : boost::none, boost::none, // maxTimeMS boost::none, // term boost::none); // lastKnownCommittedOpTime @@ -291,16 +293,17 @@ void doNothing(const BSONObj&) {} Timestamp getLatestClusterTime(DBClientBase* conn) { // Sort by descending 'ts' in the query to the oplog collection. The first entry will have the // latest cluster time. - auto qr = std::make_unique<QueryRequest>(NamespaceString("local.oplog.rs")); - qr->setSort(BSON("$natural" << -1)); - qr->setLimit(1LL); - qr->setSingleBatchField(true); - invariant(qr->validate()); - const auto dbName = qr->nss().db().toString(); + auto findCommand = std::make_unique<FindCommand>(NamespaceString("local.oplog.rs")); + findCommand->setSort(BSON("$natural" << -1)); + findCommand->setLimit(1LL); + findCommand->setSingleBatch(true); + invariant(query_request_helper::validateFindCommand(*findCommand)); + const auto dbName = + findCommand->getNamespaceOrUUID().nss().value_or(NamespaceString()).db().toString(); BSONObj oplogResult; int count = runQueryWithReadCommands( - conn, boost::none, boost::none, std::move(qr), Milliseconds(0), &oplogResult); + conn, boost::none, boost::none, std::move(findCommand), Milliseconds(0), &oplogResult); uassert(ErrorCodes::OperationFailed, str::stream() << "Find cmd on the oplog collection failed; reply was: " << oplogResult, count == 1); @@ -991,15 +994,15 @@ void BenchRunOp::executeOnce(DBClientBase* conn, BSONObj fixedQuery = fixQuery(this->query, *state->bsonTemplateEvaluator); BSONObj result; if (this->useReadCmd) { - auto qr = std::make_unique<QueryRequest>(NamespaceString(this->ns)); - qr->setFilter(fixedQuery); - qr->setProj(this->projection); - qr->setLimit(1LL); - qr->setSingleBatchField(true); + auto findCommand = std::make_unique<FindCommand>(NamespaceString(this->ns)); + findCommand->setFilter(fixedQuery); + findCommand->setProjection(this->projection); + findCommand->setLimit(1LL); + findCommand->setSingleBatch(true); if (config.useSnapshotReads) { - qr->setReadConcern(readConcernSnapshot); + findCommand->setReadConcern(readConcernSnapshot); } - invariant(qr->validate()); + invariant(query_request_helper::validateFindCommand(*findCommand)); BenchRunEventTrace _bret(&state->stats->findOneCounter); boost::optional<TxnNumber> txnNumberForOp; @@ -1009,7 +1012,7 @@ void BenchRunOp::executeOnce(DBClientBase* conn, state->inProgressMultiStatementTxn = true; } runQueryWithReadCommands( - conn, lsid, txnNumberForOp, std::move(qr), Milliseconds(0), &result); + conn, lsid, txnNumberForOp, std::move(findCommand), Milliseconds(0), &result); } else { BenchRunEventTrace _bret(&state->stats->findOneCounter); result = conn->findOne( @@ -1074,17 +1077,17 @@ void BenchRunOp::executeOnce(DBClientBase* conn, "cannot use 'options' in combination with read commands", !this->options); - auto qr = std::make_unique<QueryRequest>(NamespaceString(this->ns)); - qr->setFilter(fixedQuery); - qr->setProj(this->projection); + auto findCommand = std::make_unique<FindCommand>(NamespaceString(this->ns)); + findCommand->setFilter(fixedQuery); + findCommand->setProjection(this->projection); if (this->skip) { - qr->setSkip(this->skip); + findCommand->setSkip(this->skip); } if (this->limit) { - qr->setLimit(this->limit); + findCommand->setLimit(this->limit); } if (this->batchSize) { - qr->setBatchSize(this->batchSize); + findCommand->setBatchSize(this->batchSize); } BSONObjBuilder readConcernBuilder; if (config.useSnapshotReads) { @@ -1098,9 +1101,9 @@ void BenchRunOp::executeOnce(DBClientBase* conn, conn, state->rng->nextInt32(this->useAClusterTimeWithinPastSeconds)); readConcernBuilder.append("atClusterTime", atClusterTime); } - qr->setReadConcern(readConcernBuilder.obj()); + findCommand->setReadConcern(readConcernBuilder.obj()); - invariant(qr->validate()); + invariant(query_request_helper::validateFindCommand(*findCommand)); BenchRunEventTrace _bret(&state->stats->queryCounter); boost::optional<TxnNumber> txnNumberForOp; @@ -1117,7 +1120,7 @@ void BenchRunOp::executeOnce(DBClientBase* conn, count = runQueryWithReadCommands(conn, lsid, txnNumberForOp, - std::move(qr), + std::move(findCommand), Milliseconds(delayBeforeGetMore), nullptr); } else { |