summaryrefslogtreecommitdiff
path: root/src/mongo/db
diff options
context:
space:
mode:
authorIrina Yatsenko <irina.yatsenko@mongodb.com>2021-07-07 15:44:54 +0000
committerEvergreen Agent <no-reply@evergreen.mongodb.com>2021-07-16 05:38:55 +0000
commitbeeab6beaf18232e52bb3094f5f31fe83fbae2a4 (patch)
treedce5b9fefa813283212757dcf16f59e4b8bffe9e /src/mongo/db
parent23ecc48f89f4ec03d7b42e637c5969802efdb261 (diff)
downloadmongo-beeab6beaf18232e52bb3094f5f31fe83fbae2a4.tar.gz
SERVER-57391 Return error response to OP_QUERY and OP_GET_MORE messages
Diffstat (limited to 'src/mongo/db')
-rw-r--r--src/mongo/db/audit.cpp14
-rw-r--r--src/mongo/db/audit.h16
-rw-r--r--src/mongo/db/curop.cpp75
-rw-r--r--src/mongo/db/curop.h21
-rw-r--r--src/mongo/db/dbmessage.cpp63
-rw-r--r--src/mongo/db/dbmessage.h88
-rw-r--r--src/mongo/db/ops/write_ops_parsers_test.cpp30
-rw-r--r--src/mongo/db/query/canonical_query.cpp18
-rw-r--r--src/mongo/db/query/canonical_query.h17
-rw-r--r--src/mongo/db/query/find.cpp673
-rw-r--r--src/mongo/db/query/find.h16
-rw-r--r--src/mongo/db/query/get_executor.cpp16
-rw-r--r--src/mongo/db/query/get_executor.h8
-rw-r--r--src/mongo/db/service_entry_point_common.cpp210
-rw-r--r--src/mongo/db/service_entry_point_common.h1
15 files changed, 75 insertions, 1191 deletions
diff --git a/src/mongo/db/audit.cpp b/src/mongo/db/audit.cpp
index 8b0867289d3..2f62a0d26b9 100644
--- a/src/mongo/db/audit.cpp
+++ b/src/mongo/db/audit.cpp
@@ -54,13 +54,6 @@ void logCommandAuthzCheck(Client* client,
invariant(client);
}
-void logGetMoreAuthzCheck(Client* client,
- const NamespaceString& ns,
- long long cursorId,
- ErrorCodes::Error result) {
- invariant(client);
-}
-
void logKillCursorsAuthzCheck(Client* client,
const NamespaceString& ns,
long long cursorId,
@@ -68,13 +61,6 @@ void logKillCursorsAuthzCheck(Client* client,
invariant(client);
}
-void logQueryAuthzCheck(Client* client,
- const NamespaceString& ns,
- const BSONObj& query,
- ErrorCodes::Error result) {
- invariant(client);
-}
-
void logCreateUser(Client* client,
const UserName& username,
bool password,
diff --git a/src/mongo/db/audit.h b/src/mongo/db/audit.h
index 57bdfe600fd..2e4904e09df 100644
--- a/src/mongo/db/audit.h
+++ b/src/mongo/db/audit.h
@@ -172,14 +172,6 @@ void logCommandAuthzCheck(Client* client,
ErrorCodes::Error result);
/**
- * Logs the result of an authorization check for an OP_GET_MORE wire protocol message.
- */
-void logGetMoreAuthzCheck(Client* client,
- const NamespaceString& ns,
- long long cursorId,
- ErrorCodes::Error result);
-
-/**
* Logs the result of an authorization check for a killCursors command.
*/
void logKillCursorsAuthzCheck(Client* client,
@@ -188,14 +180,6 @@ void logKillCursorsAuthzCheck(Client* client,
ErrorCodes::Error result);
/**
- * Logs the result of an authorization check for an OP_QUERY wire protocol message.
- */
-void logQueryAuthzCheck(Client* client,
- const NamespaceString& ns,
- const BSONObj& query,
- ErrorCodes::Error result);
-
-/**
* Logs the result of a createUser command.
*/
void logCreateUser(Client* client,
diff --git a/src/mongo/db/curop.cpp b/src/mongo/db/curop.cpp
index 2ad744aefd5..aba407a169a 100644
--- a/src/mongo/db/curop.cpp
+++ b/src/mongo/db/curop.cpp
@@ -67,87 +67,12 @@ using std::string;
namespace {
-// Lists the $-prefixed query options that can be passed alongside a wrapped query predicate for
-// OP_QUERY find. The $orderby field is omitted because "orderby" (no dollar sign) is also allowed,
-// and this requires special handling.
-const std::vector<const char*> kDollarQueryModifiers = {
- "$hint",
- "$comment",
- "$max",
- "$min",
- "$returnKey",
- "$showDiskLoc",
- "$snapshot",
- "$maxTimeMS",
-};
-
TimerStats oplogGetMoreStats;
ServerStatusMetricField<TimerStats> displayBatchesReceived("repl.network.oplogGetMoresProcessed",
&oplogGetMoreStats);
} // namespace
-BSONObj upconvertQueryEntry(const BSONObj& query,
- const NamespaceString& nss,
- int ntoreturn,
- int ntoskip) {
- BSONObjBuilder bob;
-
- bob.append("find", nss.coll());
-
- // Whether or not the query predicate is wrapped inside a "query" or "$query" field so that
- // other options can be passed alongside the predicate.
- bool predicateIsWrapped = false;
-
- // Extract the query predicate.
- BSONObj filter;
- if (query["query"].isABSONObj()) {
- predicateIsWrapped = true;
- bob.appendAs(query["query"], "filter");
- } else if (query["$query"].isABSONObj()) {
- predicateIsWrapped = true;
- bob.appendAs(query["$query"], "filter");
- } else if (!query.isEmpty()) {
- bob.append("filter", query);
- }
-
- if (ntoskip) {
- bob.append("skip", ntoskip);
- }
- if (ntoreturn) {
- bob.append("ntoreturn", ntoreturn);
- }
-
- // The remainder of the query options are only available if the predicate is passed in wrapped
- // form. If the predicate is not wrapped, we're done.
- if (!predicateIsWrapped) {
- return bob.obj();
- }
-
- // Extract the sort.
- if (auto elem = query["orderby"]) {
- bob.appendAs(elem, "sort");
- } else if (auto elem = query["$orderby"]) {
- bob.appendAs(elem, "sort");
- }
-
- // Add $-prefixed OP_QUERY modifiers, like $hint.
- for (auto modifier : kDollarQueryModifiers) {
- if (auto elem = query[modifier]) {
- // Use "+ 1" to omit the leading dollar sign.
- bob.appendAs(elem, modifier + 1);
- }
- }
-
- return bob.obj();
-}
-
-BSONObj upconvertGetMoreEntry(const NamespaceString& nss, CursorId cursorId, int ntoreturn) {
- GetMoreCommandRequest getMoreRequest(cursorId, nss.coll().toString());
- getMoreRequest.setBatchSize(ntoreturn);
- return getMoreRequest.toBSON({});
-}
-
/**
* This type decorates a Client object with a stack of active CurOp objects.
*
diff --git a/src/mongo/db/curop.h b/src/mongo/db/curop.h
index 904f41ff73a..d95b56e41e9 100644
--- a/src/mongo/db/curop.h
+++ b/src/mongo/db/curop.h
@@ -512,13 +512,6 @@ public:
}
/**
- * Returns true if this CurOp represents a non-command OP_QUERY request.
- */
- bool isLegacyQuery() const {
- return _networkOp == NetworkOp::dbQuery && !isCommand();
- }
-
- /**
* Returns true if the current operation is known to be a command.
*/
bool isCommand() const {
@@ -855,18 +848,4 @@ private:
TickSource* _tickSource = nullptr;
};
-
-/**
- * Upconverts a legacy query object such that it matches the format of the find command.
- */
-BSONObj upconvertQueryEntry(const BSONObj& query,
- const NamespaceString& nss,
- int ntoreturn,
- int ntoskip);
-
-/**
- * Generates a getMore command object from the specified namespace, cursor ID and batchsize.
- */
-BSONObj upconvertGetMoreEntry(const NamespaceString& nss, CursorId cursorId, int ntoreturn);
-
} // namespace mongo
diff --git a/src/mongo/db/dbmessage.cpp b/src/mongo/db/dbmessage.cpp
index 0fcb10aab3e..b8d94d2f866 100644
--- a/src/mongo/db/dbmessage.cpp
+++ b/src/mongo/db/dbmessage.cpp
@@ -154,12 +154,12 @@ Message makeMessage(NetworkOp op, Func&& bodyBuilder) {
}
} // namespace
-Message makeQueryMessage(StringData ns,
- BSONObj query,
- int nToReturn,
- int nToSkip,
- const BSONObj* fieldsToReturn,
- int queryOptions) {
+Message makeDeprecatedQueryMessage(StringData ns,
+ BSONObj query,
+ int nToReturn,
+ int nToSkip,
+ const BSONObj* fieldsToReturn,
+ int queryOptions) {
return makeMessage(dbQuery, [&](BufBuilder& b) {
b.appendNum(queryOptions);
b.appendStr(ns);
@@ -171,7 +171,7 @@ Message makeQueryMessage(StringData ns,
});
}
-Message makeInsertMessage(StringData ns, const BSONObj* objs, size_t count, int flags) {
+Message makeDeprecatedInsertMessage(StringData ns, const BSONObj* objs, size_t count, int flags) {
return makeMessage(dbInsert, [&](BufBuilder& b) {
int reservedFlags = 0;
if (flags & InsertOption_ContinueOnError)
@@ -186,7 +186,7 @@ Message makeInsertMessage(StringData ns, const BSONObj* objs, size_t count, int
});
}
-Message makeUpdateMessage(StringData ns, BSONObj query, BSONObj update, int flags) {
+Message makeDeprecatedUpdateMessage(StringData ns, BSONObj query, BSONObj update, int flags) {
return makeMessage(dbUpdate, [&](BufBuilder& b) {
const int reservedFlags = 0;
b.appendNum(reservedFlags);
@@ -198,7 +198,7 @@ Message makeUpdateMessage(StringData ns, BSONObj query, BSONObj update, int flag
});
}
-Message makeRemoveMessage(StringData ns, BSONObj query, int flags) {
+Message makeDeprecatedRemoveMessage(StringData ns, BSONObj query, int flags) {
return makeMessage(dbDelete, [&](BufBuilder& b) {
const int reservedFlags = 0;
b.appendNum(reservedFlags);
@@ -209,7 +209,7 @@ Message makeRemoveMessage(StringData ns, BSONObj query, int flags) {
});
}
-Message makeKillCursorsMessage(long long cursorId) {
+Message makeDeprecatedKillCursorsMessage(long long cursorId) {
return makeMessage(dbKillCursors, [&](BufBuilder& b) {
b.appendNum((int)0); // reserved
b.appendNum((int)1); // number
@@ -217,7 +217,7 @@ Message makeKillCursorsMessage(long long cursorId) {
});
}
-Message makeGetMoreMessage(StringData ns, long long cursorId, int nToReturn, int flags) {
+Message makeDeprecatedGetMoreMessage(StringData ns, long long cursorId, int nToReturn, int flags) {
return makeMessage(dbGetMore, [&](BufBuilder& b) {
b.appendNum(flags);
b.appendStr(ns);
@@ -226,32 +226,25 @@ Message makeGetMoreMessage(StringData ns, long long cursorId, int nToReturn, int
});
}
-OpQueryReplyBuilder::OpQueryReplyBuilder() : _buffer(32768) {
- _buffer.skip(sizeof(QueryResult::Value));
-}
+DbResponse makeErrorResponseToDeprecatedOpQuery(StringData errorMsg) {
+ BSONObjBuilder err;
+ err.append("$err", errorMsg);
+ err.append("code", 5739101);
+ err.append("ok", 0.0);
+ BSONObj errObj = err.done();
+
+ BufBuilder buffer(sizeof(QueryResult::Value) + errObj.objsize());
+ buffer.skip(sizeof(QueryResult::Value));
+ buffer.appendBuf(errObj.objdata(), errObj.objsize());
-Message OpQueryReplyBuilder::toQueryReply(int queryResultFlags,
- int nReturned,
- int startingFrom,
- long long cursorId) {
- QueryResult::View qr = _buffer.buf();
- qr.setResultFlags(queryResultFlags);
- qr.msgdata().setLen(_buffer.len());
+ QueryResult::View qr = buffer.buf();
+ qr.setResultFlags(ResultFlag_ErrSet);
+ qr.msgdata().setLen(buffer.len());
qr.msgdata().setOperation(opReply);
- qr.setCursorId(cursorId);
- qr.setStartingFrom(startingFrom);
- qr.setNReturned(nReturned);
- return Message(_buffer.release());
-}
+ qr.setCursorId(0);
+ qr.setStartingFrom(0);
+ qr.setNReturned(1);
-DbResponse replyToQuery(int queryResultFlags,
- const void* data,
- int size,
- int nReturned,
- int startingFrom,
- long long cursorId) {
- OpQueryReplyBuilder reply;
- reply.bufBuilderForResults().appendBuf(data, size);
- return DbResponse{reply.toQueryReply(queryResultFlags, nReturned, startingFrom, cursorId)};
+ return DbResponse{Message(buffer.release())};
}
} // namespace mongo
diff --git a/src/mongo/db/dbmessage.h b/src/mongo/db/dbmessage.h
index 992142f3ef1..63c78c261cb 100644
--- a/src/mongo/db/dbmessage.h
+++ b/src/mongo/db/dbmessage.h
@@ -387,12 +387,12 @@ public:
/**
* Builds a legacy OP_QUERY message.
*/
-Message makeQueryMessage(StringData ns,
- BSONObj query,
- int nToReturn,
- int nToSkip,
- const BSONObj* fieldsToReturn,
- int queryOptions);
+Message makeDeprecatedQueryMessage(StringData ns,
+ BSONObj query,
+ int nToReturn,
+ int nToSkip,
+ const BSONObj* fieldsToReturn,
+ int queryOptions);
enum InsertOptions {
/** With muli-insert keep processing inserts if one fails */
@@ -402,7 +402,10 @@ enum InsertOptions {
/**
* Builds a legacy OP_INSERT message.
*/
-Message makeInsertMessage(StringData ns, const BSONObj* objs, size_t count, int flags = 0);
+Message makeDeprecatedInsertMessage(StringData ns,
+ const BSONObj* objs,
+ size_t count,
+ int flags = 0);
enum UpdateOptions {
/** Upsert - that is, insert the item if no matching item is found. */
@@ -419,7 +422,7 @@ enum UpdateOptions {
/**
* Builds a legacy OP_UPDATE message.
*/
-Message makeUpdateMessage(StringData ns, BSONObj query, BSONObj update, int flags = 0);
+Message makeDeprecatedUpdateMessage(StringData ns, BSONObj query, BSONObj update, int flags = 0);
enum RemoveOptions {
/** only delete one option */
@@ -432,17 +435,20 @@ enum RemoveOptions {
/**
* Builds a legacy OP_REMOVE message.
*/
-Message makeRemoveMessage(StringData ns, BSONObj query, int flags = 0);
+Message makeDeprecatedRemoveMessage(StringData ns, BSONObj query, int flags = 0);
/**
* Builds a legacy OP_KILLCURSORS message.
*/
-Message makeKillCursorsMessage(long long cursorId);
+Message makeDeprecatedKillCursorsMessage(long long cursorId);
/**
- * Builds a legacy OP_GETMORE message.
+ * Builds a legacy OP_GET_MORE message.
*/
-Message makeGetMoreMessage(StringData ns, long long cursorId, int nToReturn, int flags = 0);
+Message makeDeprecatedGetMoreMessage(StringData ns,
+ long long cursorId,
+ int nToReturn,
+ int flags = 0);
/**
* A response to a DbMessage.
@@ -462,61 +468,7 @@ struct DbResponse {
};
/**
- * Prepares query replies to legacy finds (opReply to dbQuery) in place. This is also used for
- * command responses that don't use the new dbMsg protocol.
+ * Helper to build an error DbResponse for OP_QUERY and OP_GET_MORE.
*/
-class OpQueryReplyBuilder {
- OpQueryReplyBuilder(const OpQueryReplyBuilder&) = delete;
- OpQueryReplyBuilder& operator=(const OpQueryReplyBuilder&) = delete;
-
-public:
- OpQueryReplyBuilder();
-
- /**
- * Returns the BufBuilder that should be used for placing result objects. It will be positioned
- * where the first (or next) object should go.
- *
- * You must finish the BSONObjBuilder that uses this (by destruction or calling doneFast())
- * before calling any more methods on this object.
- */
- BufBuilder& bufBuilderForResults() {
- return _buffer;
- }
-
- /**
- * Finishes the reply and returns the message buffer.
- */
- Message toQueryReply(int queryResultFlags,
- int nReturned,
- int startingFrom = 0,
- long long cursorId = 0);
-
- /**
- * Similar to toQueryReply() but used for replying to a command.
- */
- Message toCommandReply() {
- return toQueryReply(0, 1);
- }
-
-private:
- BufBuilder _buffer;
-};
-
-/**
- * Helper to build a DbResponse from a buffer containing an OP_QUERY response.
- */
-DbResponse replyToQuery(int queryResultFlags,
- const void* data,
- int size,
- int nReturned,
- int startingFrom = 0,
- long long cursorId = 0);
-
-
-/**
- * Helper to build a DbRespose for OP_QUERY with a single reply object.
- */
-inline DbResponse replyToQuery(const BSONObj& obj, int queryResultFlags = 0) {
- return replyToQuery(queryResultFlags, obj.objdata(), obj.objsize(), /*nReturned*/ 1);
-}
+DbResponse makeErrorResponseToDeprecatedOpQuery(StringData errorMsg);
} // namespace mongo
diff --git a/src/mongo/db/ops/write_ops_parsers_test.cpp b/src/mongo/db/ops/write_ops_parsers_test.cpp
index d3dc3ebd63a..e8abac496fd 100644
--- a/src/mongo/db/ops/write_ops_parsers_test.cpp
+++ b/src/mongo/db/ops/write_ops_parsers_test.cpp
@@ -404,8 +404,8 @@ TEST(LegacyWriteOpsParsers, SingleInsert) {
const std::string ns = "test.foo";
const BSONObj obj = BSON("x" << 1);
for (bool continueOnError : {false, true}) {
- auto message =
- makeInsertMessage(ns, &obj, 1, continueOnError ? InsertOption_ContinueOnError : 0);
+ auto message = makeDeprecatedInsertMessage(
+ ns, &obj, 1, continueOnError ? InsertOption_ContinueOnError : 0);
const auto op = InsertOp::parseLegacy(message);
ASSERT_EQ(op.getNamespace().ns(), ns);
ASSERT(!op.getWriteCommandRequestBase().getBypassDocumentValidation());
@@ -419,7 +419,7 @@ TEST(LegacyWriteOpsParsers, EmptyMultiInsertFails) {
const std::string ns = "test.foo";
for (bool continueOnError : {false, true}) {
auto objs = std::vector<BSONObj>{};
- auto message = makeInsertMessage(
+ auto message = makeDeprecatedInsertMessage(
ns, objs.data(), objs.size(), (continueOnError ? InsertOption_ContinueOnError : 0));
ASSERT_THROWS_CODE(
InsertOp::parseLegacy(message), AssertionException, ErrorCodes::InvalidLength);
@@ -432,7 +432,7 @@ TEST(LegacyWriteOpsParsers, RealMultiInsert) {
const BSONObj obj1 = BSON("x" << 1);
for (bool continueOnError : {false, true}) {
auto objs = std::vector<BSONObj>{obj0, obj1};
- auto message = makeInsertMessage(
+ auto message = makeDeprecatedInsertMessage(
ns, objs.data(), objs.size(), continueOnError ? InsertOption_ContinueOnError : 0);
const auto op = InsertOp::parseLegacy(message);
ASSERT_EQ(op.getNamespace().ns(), ns);
@@ -450,11 +450,11 @@ TEST(LegacyWriteOpsParsers, UpdateCommandRequest) {
const BSONObj update = BSON("$inc" << BSON("x" << 1));
for (bool upsert : {false, true}) {
for (bool multi : {false, true}) {
- auto message = makeUpdateMessage(ns,
- query,
- update,
- (upsert ? UpdateOption_Upsert : 0) |
- (multi ? UpdateOption_Multi : 0));
+ auto message = makeDeprecatedUpdateMessage(ns,
+ query,
+ update,
+ (upsert ? UpdateOption_Upsert : 0) |
+ (multi ? UpdateOption_Multi : 0));
const auto op = UpdateOp::parseLegacy(message);
ASSERT_EQ(op.getNamespace().ns(), ns);
ASSERT(!op.getWriteCommandRequestBase().getBypassDocumentValidation());
@@ -478,11 +478,11 @@ TEST(LegacyWriteOpsParsers, UpdateWithArrayUpdateFieldIsParsedAsReplacementStyle
const BSONObj update = BSON_ARRAY(BSON("$addFields" << BSON("x" << 1)));
for (bool upsert : {false, true}) {
for (bool multi : {false, true}) {
- auto message = makeUpdateMessage(ns,
- query,
- update,
- (upsert ? UpdateOption_Upsert : 0) |
- (multi ? UpdateOption_Multi : 0));
+ auto message = makeDeprecatedUpdateMessage(ns,
+ query,
+ update,
+ (upsert ? UpdateOption_Upsert : 0) |
+ (multi ? UpdateOption_Multi : 0));
const auto op = UpdateOp::parseLegacy(message);
ASSERT_EQ(op.getNamespace().ns(), ns);
ASSERT(!op.getWriteCommandRequestBase().getBypassDocumentValidation());
@@ -502,7 +502,7 @@ TEST(LegacyWriteOpsParsers, Remove) {
const std::string ns = "test.foo";
const BSONObj query = BSON("x" << 1);
for (bool multi : {false, true}) {
- auto message = makeRemoveMessage(ns, query, (multi ? 0 : RemoveOption_JustOne));
+ auto message = makeDeprecatedRemoveMessage(ns, query, (multi ? 0 : RemoveOption_JustOne));
const auto op = DeleteOp::parseLegacy(message);
ASSERT_EQ(op.getNamespace().ns(), ns);
ASSERT(!op.getWriteCommandRequestBase().getBypassDocumentValidation());
diff --git a/src/mongo/db/query/canonical_query.cpp b/src/mongo/db/query/canonical_query.cpp
index e7cfed36f53..b714624bd34 100644
--- a/src/mongo/db/query/canonical_query.cpp
+++ b/src/mongo/db/query/canonical_query.cpp
@@ -61,24 +61,6 @@ bool parsingCanProduceNoopMatchNodes(const ExtensionsCallback& extensionsCallbac
// static
StatusWith<std::unique_ptr<CanonicalQuery>> CanonicalQuery::canonicalize(
OperationContext* opCtx,
- const QueryMessage& qm,
- const boost::intrusive_ptr<ExpressionContext>& expCtx,
- const ExtensionsCallback& extensionsCallback,
- MatchExpressionParser::AllowedFeatureSet allowedFeatures) {
- bool explain = false;
- // Make FindCommandRequest.
- auto status = query_request_helper::fromLegacyQueryMessage(qm, &explain);
- if (!status.isOK()) {
- return status.getStatus();
- }
-
- return CanonicalQuery::canonicalize(
- opCtx, std::move(status.getValue()), explain, expCtx, extensionsCallback, allowedFeatures);
-}
-
-// static
-StatusWith<std::unique_ptr<CanonicalQuery>> CanonicalQuery::canonicalize(
- OperationContext* opCtx,
std::unique_ptr<FindCommandRequest> findCommand,
bool explain,
const boost::intrusive_ptr<ExpressionContext>& expCtx,
diff --git a/src/mongo/db/query/canonical_query.h b/src/mongo/db/query/canonical_query.h
index d79d92e4068..1e2a29d6def 100644
--- a/src/mongo/db/query/canonical_query.h
+++ b/src/mongo/db/query/canonical_query.h
@@ -58,23 +58,6 @@ public:
*
* 'opCtx' must point to a valid OperationContext, but 'opCtx' does not need to outlive the
* returned CanonicalQuery.
- *
- * Used for legacy find through the OP_QUERY message.
- */
- static StatusWith<std::unique_ptr<CanonicalQuery>> canonicalize(
- OperationContext* opCtx,
- const QueryMessage& qm,
- const boost::intrusive_ptr<ExpressionContext>& expCtx = nullptr,
- const ExtensionsCallback& extensionsCallback = ExtensionsCallbackNoop(),
- MatchExpressionParser::AllowedFeatureSet allowedFeatures =
- MatchExpressionParser::kDefaultSpecialFeatures);
-
- /**
- * If parsing succeeds, returns a std::unique_ptr<CanonicalQuery> representing the parsed
- * query (which will never be NULL). If parsing fails, returns an error Status.
- *
- * 'opCtx' must point to a valid OperationContext, but 'opCtx' does not need to outlive the
- * returned CanonicalQuery.
*/
static StatusWith<std::unique_ptr<CanonicalQuery>> canonicalize(
OperationContext* opCtx,
diff --git a/src/mongo/db/query/find.cpp b/src/mongo/db/query/find.cpp
index c4dac17cfec..0f791f2d11e 100644
--- a/src/mongo/db/query/find.cpp
+++ b/src/mongo/db/query/find.cpp
@@ -78,9 +78,6 @@ using std::unique_ptr;
// Failpoint for checking whether we've received a getmore.
MONGO_FAIL_POINT_DEFINE(failReceivedGetmore);
-// Failpoint to keep a cursor pinned.
-MONGO_FAIL_POINT_DEFINE(legacyGetMoreWaitWithCursor)
-
bool shouldSaveCursor(OperationContext* opCtx,
const CollectionPtr& collection,
PlanExecutor::ExecState finalState,
@@ -148,674 +145,4 @@ void endQueryOp(OperationContext* opCtx,
}
}
-namespace {
-
-/**
- * Uses 'cursor' to fill out 'bb' with the batch of result documents to be returned by this getMore.
- *
- * Returns the number of documents in the batch in 'numResults', which must be initialized to
- * zero by the caller. Returns the final ExecState returned by the cursor in *state.
- *
- * Throws an exception if the PlanExecutor encounters a failure.
- */
-void generateBatch(int ntoreturn,
- ClientCursor* cursor,
- BufBuilder* bb,
- std::uint64_t* numResults,
- ResourceConsumption::DocumentUnitCounter* docUnitsReturned,
- PlanExecutor::ExecState* state) {
- PlanExecutor* exec = cursor->getExecutor();
-
- try {
- BSONObj obj;
- while (!FindCommon::enoughForGetMore(ntoreturn, *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.
- if (!FindCommon::haveSpaceForNext(obj, *numResults, bb->len())) {
- exec->enqueue(obj);
- break;
- }
-
- // Add result to output buffer.
- bb->appendBuf((void*)obj.objdata(), obj.objsize());
-
- // Count the result.
- (*numResults)++;
-
- docUnitsReturned->observeOne(obj.objsize());
- }
- } catch (DBException& exception) {
- auto&& explainer = exec->getPlanExplainer();
- auto&& [stats, _] = explainer.getWinningPlanStats(ExplainOptions::Verbosity::kExecStats);
- LOGV2_ERROR(20918, "getMore executor error", "stats"_attr = redact(stats));
- exception.addContext("Executor error during OP_GET_MORE");
- throw;
- }
-}
-
-Message makeCursorNotFoundResponse() {
- const int initialBufSize = 512 + sizeof(QueryResult::Value);
- BufBuilder bb(initialBufSize);
- bb.skip(sizeof(QueryResult::Value));
- QueryResult::View qr = bb.buf();
- qr.msgdata().setLen(bb.len());
- qr.msgdata().setOperation(opReply);
- qr.setResultFlags(ResultFlag_CursorNotFound);
- qr.setCursorId(0);
- qr.setStartingFrom(0);
- qr.setNReturned(0);
- return Message(bb.release());
-}
-
-} // namespace
-
-/**
- * Called by db/instance.cpp. This is the getMore entry point.
- */
-Message getMore(OperationContext* opCtx,
- const char* ns,
- int ntoreturn,
- long long cursorid,
- bool* exhaust,
- bool* isCursorAuthorized) {
- invariant(ntoreturn >= 0);
-
- LOGV2_DEBUG(20909, 5, "Running getMore", "cursorId"_attr = cursorid);
-
- CurOp& curOp = *CurOp::get(opCtx);
- curOp.ensureStarted();
-
- // For testing, we may want to fail if we receive a getmore.
- if (MONGO_unlikely(failReceivedGetmore.shouldFail())) {
- MONGO_UNREACHABLE;
- }
-
- *exhaust = false;
-
- const NamespaceString nss(ns);
-
- ResourceConsumption::ScopedMetricsCollector scopedMetrics(opCtx, nss.db().toString());
-
- // Cursors come in one of two flavors:
- //
- // - Cursors which read from a single collection, such as those generated via the find command.
- // For these cursors, we hold the appropriate collection lock for the duration of the getMore
- // using AutoGetCollectionForRead. These cursors have the 'kLockExternally' lock policy.
- //
- // - Cursors which may read from many collections, e.g. those generated via the aggregate
- // command, or which do not read from a collection at all, e.g. those generated by the
- // listIndexes command. We don't need to acquire locks to use these cursors, since they either
- // manage locking themselves or don't access data protected by collection locks. These cursors
- // have the 'kLocksInternally' lock policy.
- //
- // While we only need to acquire locks for 'kLockExternally' cursors, we need to create an
- // AutoStatsTracker in either case. This is responsible for updating statistics in CurOp and
- // Top. We avoid using AutoGetCollectionForReadCommand because we may need to drop and reacquire
- // locks when the cursor is awaitData, but we don't want to update the stats twice.
- UninterruptibleLockGuard noInterrupt(opCtx->lockState());
- boost::optional<AutoGetCollectionForReadMaybeLockFree> readLock;
- boost::optional<AutoStatsTracker> statsTracker;
-
- // These are set in the QueryResult msg we return.
- int resultFlags = ResultFlag_AwaitCapable;
-
- auto cursorManager = CursorManager::get(opCtx);
- auto statusWithCursorPin = cursorManager->pinCursor(opCtx, cursorid);
- if (statusWithCursorPin == ErrorCodes::CursorNotFound) {
- return makeCursorNotFoundResponse();
- }
- uassertStatusOK(statusWithCursorPin.getStatus());
- auto cursorPin = std::move(statusWithCursorPin.getValue());
-
- // Set kMajorityCommitted before we instantiate readLock. We should not override readSource
- // after storage snapshot is setup.
- const auto replicationMode = repl::ReplicationCoordinator::get(opCtx)->getReplicationMode();
- if (replicationMode == repl::ReplicationCoordinator::modeReplSet &&
- cursorPin->getReadConcernArgs().getLevel() ==
- repl::ReadConcernLevel::kMajorityReadConcern) {
- opCtx->recoveryUnit()->setTimestampReadSource(RecoveryUnit::ReadSource::kMajorityCommitted);
- uassertStatusOK(opCtx->recoveryUnit()->majorityCommittedSnapshotAvailable());
- }
-
- opCtx->setExhaust(cursorPin->queryOptions() & QueryOption_Exhaust);
-
- if (cursorPin->getExecutor()->lockPolicy() == PlanExecutor::LockPolicy::kLocksInternally) {
- if (!nss.isCollectionlessCursorNamespace()) {
- AutoGetDb autoDb(opCtx, nss.db(), MODE_IS);
- statsTracker.emplace(opCtx,
- nss,
- Top::LockType::NotLocked,
- AutoStatsTracker::LogMode::kUpdateTopAndCurOp,
- CollectionCatalog::get(opCtx)->getDatabaseProfileLevel(nss.db()));
- auto view =
- autoDb.getDb() ? ViewCatalog::get(autoDb.getDb())->lookup(opCtx, nss) : nullptr;
- uassert(
- ErrorCodes::CommandNotSupportedOnView,
- str::stream() << "Namespace " << nss.ns()
- << " is a view. OP_GET_MORE operations are not supported on views. "
- << "Only clients which support the getMore command can be used to "
- "query views.",
- !view);
- }
- } else {
- readLock.emplace(opCtx, nss);
- statsTracker.emplace(opCtx,
- nss,
- Top::LockType::ReadLocked,
- AutoStatsTracker::LogMode::kUpdateTopAndCurOp,
- CollectionCatalog::get(opCtx)->getDatabaseProfileLevel(nss.db()));
-
- // This checks to make sure the operation is allowed on a replicated node. Since we are not
- // passing in a query object (necessary to check SlaveOK query option), we allow reads
- // whether we are PRIMARY or SECONDARY.
- uassertStatusOK(
- repl::ReplicationCoordinator::get(opCtx)->checkCanServeReadsFor(opCtx, nss, true));
- }
-
- std::uint64_t numResults = 0;
- int startingResult = 0;
- ResourceConsumption::DocumentUnitCounter docUnitsReturned;
-
- const int initialBufSize =
- 512 + sizeof(QueryResult::Value) + FindCommon::kMaxBytesToReturnToClientAtOnce;
-
- BufBuilder bb(initialBufSize);
- bb.skip(sizeof(QueryResult::Value));
-
- // Check for spoofing of the ns such that it does not match the one originally there for the
- // cursor.
- uassert(ErrorCodes::Unauthorized,
- str::stream() << "Requested getMore on namespace " << ns << ", but cursor " << cursorid
- << " belongs to namespace " << cursorPin->nss().ns(),
- nss == cursorPin->nss());
-
- // A user can only call getMore on their own cursor. If there were multiple users authenticated
- // when the cursor was created, then at least one of them must be authenticated in order to run
- // getMore on the cursor.
- uassert(ErrorCodes::Unauthorized,
- str::stream() << "cursor id " << cursorid
- << " was not created by the authenticated user",
- AuthorizationSession::get(opCtx->getClient())
- ->isCoauthorizedWith(cursorPin->getAuthenticatedUsers()));
-
- *isCursorAuthorized = true;
-
- // Only used by the failpoints.
- std::function<void()> dropAndReaquireReadLock = [&] {
- // Make sure an interrupted operation does not prevent us from reacquiring the lock.
- UninterruptibleLockGuard noInterrupt(opCtx->lockState());
-
- readLock.reset();
- readLock.emplace(opCtx, nss);
- };
-
- // On early return, get rid of the cursor.
- auto cursorFreer = makeGuard([&] { cursorPin.deleteUnderlying(); });
-
- // If the 'waitAfterPinningCursorBeforeGetMoreBatch' fail point is enabled, set the
- // 'msg' field of this operation's CurOp to signal that we've hit this point and then
- // repeatedly release and re-acquire the collection readLock at regular intervals until
- // the failpoint is released. This is done in order to avoid deadlocks caused by the
- // pinned-cursor failpoints in this file (see SERVER-21997).
- waitAfterPinningCursorBeforeGetMoreBatch.execute([&](const BSONObj& data) {
- if (data["shouldNotdropLock"].booleanSafe()) {
- dropAndReaquireReadLock = []() {};
- }
-
- CurOpFailpointHelpers::waitWhileFailPointEnabled(&waitAfterPinningCursorBeforeGetMoreBatch,
- opCtx,
- "waitAfterPinningCursorBeforeGetMoreBatch",
- dropAndReaquireReadLock,
- nss);
- });
-
- uassert(40548,
- "OP_GET_MORE operations are not supported on tailable aggregations. Only clients "
- "which support the getMore command can be used on tailable aggregations.",
- readLock || !cursorPin->isAwaitData());
- uassert(
- 31124,
- str::stream()
- << "OP_GET_MORE does not support cursors with a write concern other than the default."
- " Use the getMore command instead. Write concern was: "
- << cursorPin->getWriteConcernOptions().toBSON(),
- cursorPin->getWriteConcernOptions().isImplicitDefaultWriteConcern());
-
- // If the operation that spawned this cursor had a time limit set, apply leftover time to this
- // getmore.
- if (cursorPin->getLeftoverMaxTimeMicros() < Microseconds::max()) {
- uassert(40136,
- "Illegal attempt to set operation deadline within DBDirectClient",
- !opCtx->getClient()->isInDirectClient());
- opCtx->setDeadlineAfterNowBy(cursorPin->getLeftoverMaxTimeMicros(),
- ErrorCodes::MaxTimeMSExpired);
- }
- opCtx->checkForInterrupt(); // May trigger maxTimeAlwaysTimeOut fail point.
-
- // What number result are we starting at? Used to fill out the reply.
- startingResult = cursorPin->nReturnedSoFar();
-
- uint64_t notifierVersion = 0;
- std::shared_ptr<CappedInsertNotifier> notifier;
- if (cursorPin->isAwaitData()) {
- invariant(readLock->getCollection()->isCapped());
- // Retrieve the notifier which we will wait on until new data arrives. We make sure to do
- // this in the lock because once we drop the lock it is possible for the collection to
- // become invalid. The notifier itself will outlive the collection if the collection is
- // dropped, as we keep a shared_ptr to it.
- notifier = readLock->getCollection()->getCappedInsertNotifier();
-
- // Must get the version before we call generateBatch in case a write comes in after that
- // call and before we call wait on the notifier.
- notifierVersion = notifier->getVersion();
- }
-
- PlanExecutor* exec = cursorPin->getExecutor();
- exec->reattachToOperationContext(opCtx);
- exec->restoreState(readLock ? &readLock->getCollection() : nullptr);
-
- auto planSummary = exec->getPlanExplainer().getPlanSummary();
- {
- stdx::lock_guard<Client> lk(*opCtx->getClient());
- curOp.setPlanSummary_inlock(planSummary);
-
- // Ensure that the original query object is available in the slow query log, profiler and
- // currentOp. Upconvert _query to resemble a getMore command, and set the original command
- // or upconverted legacy query in the originatingCommand field.
- curOp.setOpDescription_inlock(upconvertGetMoreEntry(nss, cursorid, ntoreturn));
- curOp.setOriginatingCommand_inlock(cursorPin->getOriginatingCommandObj());
- // Update the generic cursor in curOp.
- curOp.setGenericCursor_inlock(cursorPin->toGenericCursor());
- }
-
- // If the 'failGetMoreAfterCursorCheckout' failpoint is enabled, throw an exception with the
- // specified 'errorCode' value, or ErrorCodes::InternalError if 'errorCode' is omitted.
- failGetMoreAfterCursorCheckout.executeIf(
- [](const BSONObj& data) {
- auto errorCode = (data["errorCode"] ? data["errorCode"].safeNumberLong()
- : ErrorCodes::InternalError);
- uasserted(errorCode, "Hit the 'failGetMoreAfterCursorCheckout' failpoint");
- },
- [&opCtx, &nss](const BSONObj& data) {
- auto dataForFailCommand =
- data.addField(BSON("failCommands" << BSON_ARRAY("getMore")).firstElement());
- auto* getMoreCommand = CommandHelpers::findCommand("getMore");
- return CommandHelpers::shouldActivateFailCommandFailPoint(
- dataForFailCommand, nss, getMoreCommand, opCtx->getClient());
- });
-
- PlanExecutor::ExecState state;
-
- // We report keysExamined and docsExamined to OpDebug for a given getMore operation. To obtain
- // these values we need to take a diff of the pre-execution and post-execution metrics, as they
- // accumulate over the course of a cursor's lifetime.
- PlanSummaryStats preExecutionStats;
- exec->getPlanExplainer().getSummaryStats(&preExecutionStats);
- if (MONGO_unlikely(waitWithPinnedCursorDuringGetMoreBatch.shouldFail())) {
- CurOpFailpointHelpers::waitWhileFailPointEnabled(&waitWithPinnedCursorDuringGetMoreBatch,
- opCtx,
- "waitWithPinnedCursorDuringGetMoreBatch",
- nullptr);
- }
-
- generateBatch(ntoreturn, cursorPin.getCursor(), &bb, &numResults, &docUnitsReturned, &state);
-
- // If this is an await data cursor, and we hit EOF without generating any results, then we block
- // waiting for new data to arrive.
- if (cursorPin->isAwaitData() && state == PlanExecutor::IS_EOF && numResults == 0) {
- // Save the PlanExecutor and drop our locks.
- exec->saveState();
- readLock.reset();
-
- // Block waiting for data for up to 1 second. Time spent blocking is not counted towards the
- // total operation latency.
- curOp.pauseTimer();
- Seconds timeout(1);
- notifier->waitUntil(notifierVersion,
- opCtx->getServiceContext()->getPreciseClockSource()->now() + timeout);
- notifier.reset();
- curOp.resumeTimer();
-
- // Reacquiring locks.
- readLock.emplace(opCtx, nss);
- exec->restoreState(&readLock->getCollection());
-
- // We woke up because either the timed_wait expired, or there was more data. Either way,
- // attempt to generate another batch of results.
- generateBatch(
- ntoreturn, cursorPin.getCursor(), &bb, &numResults, &docUnitsReturned, &state);
- }
-
- PlanSummaryStats postExecutionStats;
- auto&& explainer = exec->getPlanExplainer();
- explainer.getSummaryStats(&postExecutionStats);
- postExecutionStats.totalKeysExamined -= preExecutionStats.totalKeysExamined;
- postExecutionStats.totalDocsExamined -= preExecutionStats.totalDocsExamined;
- curOp.debug().setPlanSummaryMetrics(postExecutionStats);
-
- // We do not report 'execStats' for aggregation or other cursors with the 'kLocksInternally'
- // policy, both in the original request and subsequent getMore. It would be useful to have this
- // info for an aggregation, but the source PlanExecutor could be destroyed before we know if we
- // need 'execStats' and we do not want to generate the stats eagerly for all operations due to
- // cost.
- if (cursorPin->getExecutor()->lockPolicy() != PlanExecutor::LockPolicy::kLocksInternally &&
- curOp.shouldDBProfile(opCtx)) {
- auto&& [stats, _] = explainer.getWinningPlanStats(ExplainOptions::Verbosity::kExecStats);
-
- curOp.debug().execStats = std::move(stats);
- }
-
- // Our two possible ClientCursorPin cleanup paths are:
- // 1) If the cursor is not going to be saved, we call deleteUnderlying() on the pin.
- // 2) If the cursor is going to be saved, we simply let the pin go out of scope. In this case,
- // the pin's destructor will be invoked, which will call release() on the pin. Because our
- // ClientCursorPin is declared after our lock is declared, this will happen under the lock if
- // any locking was necessary.
- if (!shouldSaveCursorGetMore(exec, cursorPin->isTailable())) {
- // cc is now invalid, as is the executor
- cursorid = 0;
- curOp.debug().cursorExhausted = true;
-
- LOGV2_DEBUG(20910,
- 5,
- "getMore NOT saving client cursor",
- "planExecutorState"_attr = PlanExecutor::stateToStr(state));
- } else {
- cursorFreer.dismiss();
- // Continue caching the ClientCursor.
- cursorPin->incNReturnedSoFar(numResults);
- cursorPin->incNBatches();
- exec->saveState();
- exec->detachFromOperationContext();
- LOGV2_DEBUG(20911,
- 5,
- "getMore saving client cursor",
- "planExecutorState"_attr = PlanExecutor::stateToStr(state));
-
- // Set 'exhaust' if the client requested exhaust and the cursor is not exhausted.
- *exhaust = opCtx->isExhaust();
-
- // We assume that cursors created through a DBDirectClient are always used from their
- // original OperationContext, so we do not need to move time to and from the cursor.
- if (!opCtx->getClient()->isInDirectClient()) {
- // If the getmore had a time limit, remaining time is "rolled over" back to the cursor
- // (for use by future getmore ops).
- cursorPin->setLeftoverMaxTimeMicros(opCtx->getRemainingMaxTimeMicros());
- }
- }
-
- // We're about to unpin or delete the cursor as the ClientCursorPin goes out of scope.
- // If the 'waitBeforeUnpinningOrDeletingCursorAfterGetMoreBatch' failpoint is active, we
- // set the 'msg' field of this operation's CurOp to signal that we've hit this point and
- // then spin until the failpoint is released.
- if (MONGO_unlikely(waitBeforeUnpinningOrDeletingCursorAfterGetMoreBatch.shouldFail())) {
- CurOpFailpointHelpers::waitWhileFailPointEnabled(
- &waitBeforeUnpinningOrDeletingCursorAfterGetMoreBatch,
- opCtx,
- "waitBeforeUnpinningOrDeletingCursorAfterGetMoreBatch",
- dropAndReaquireReadLock);
- }
-
- // Increment this metric once the command succeeds and we know it will return documents.
- auto& metricsCollector = ResourceConsumption::MetricsCollector::get(opCtx);
- metricsCollector.incrementDocUnitsReturned(docUnitsReturned);
-
- QueryResult::View qr = bb.buf();
- qr.msgdata().setLen(bb.len());
- qr.msgdata().setOperation(opReply);
- qr.setResultFlags(resultFlags);
- qr.setCursorId(cursorid);
- qr.setStartingFrom(startingResult);
- qr.setNReturned(numResults);
- LOGV2_DEBUG(20912, 5, "getMore returned results", "numResults"_attr = numResults);
- return Message(bb.release());
-}
-
-bool runQuery(OperationContext* opCtx,
- QueryMessage& q,
- const NamespaceString& nss,
- Message& result) {
- CurOp& curOp = *CurOp::get(opCtx);
- curOp.ensureStarted();
-
- uassert(ErrorCodes::InvalidNamespace,
- str::stream() << "Invalid ns [" << nss.ns() << "]",
- nss.isValid());
- invariant(!nss.isCommand());
-
- ResourceConsumption::ScopedMetricsCollector scopedMetrics(opCtx, nss.db().toString());
-
- // Set CurOp information.
- const auto upconvertedQuery = upconvertQueryEntry(q.query, nss, q.ntoreturn, q.ntoskip);
-
- // Extract the 'comment' parameter from the upconverted query, if it exists.
- if (auto commentField = upconvertedQuery["comment"]) {
- opCtx->setComment(commentField.wrap());
- }
-
- beginQueryOp(opCtx, nss, upconvertedQuery, q.ntoreturn, q.ntoskip);
-
- // Parse the qm into a CanonicalQuery.
- const boost::intrusive_ptr<ExpressionContext> expCtx =
- make_intrusive<ExpressionContext>(opCtx, nullptr /* collator */, nss);
- auto cq = uassertStatusOKWithContext(
- CanonicalQuery::canonicalize(opCtx,
- q,
- expCtx,
- ExtensionsCallbackReal(opCtx, &nss),
- MatchExpressionParser::kAllowAllSpecialFeatures),
- "Can't canonicalize query");
- invariant(cq.get());
-
- LOGV2_DEBUG(20913, 5, "Running query", "query"_attr = redact(cq->toString()));
- LOGV2_DEBUG(20914, 2, "Running query", "query"_attr = redact(cq->toStringShort()));
-
- // Parse, canonicalize, plan, transcribe, and get a plan executor.
- AutoGetCollectionForReadCommandMaybeLockFree collection(
- opCtx, nss, AutoGetCollectionViewMode::kViewsForbidden);
-
- 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 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()
- : isSecondaryOk;
- uassertStatusOK(repl::ReplicationCoordinator::get(opCtx)->checkCanServeReadsFor(
- opCtx, nss, secondaryOk));
- }
-
- const FindCommandRequest& findCommand = cq->getFindCommandRequest();
- // Get the execution plan for the query.
- constexpr auto verbosity = ExplainOptions::Verbosity::kExecAllPlans;
- 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 (isExplain) {
- BufBuilder bb;
- bb.skip(sizeof(QueryResult::Value));
-
- BSONObjBuilder explainBob;
- Explain::explainStages(exec.get(),
- collection.getCollection(),
- verbosity,
- BSONObj(),
- upconvertedQuery,
- &explainBob);
-
- // Add the resulting object to the return buffer.
- BSONObj explainObj = explainBob.obj();
- bb.appendBuf((void*)explainObj.objdata(), explainObj.objsize());
-
- // Set query result fields.
- QueryResult::View qr = bb.buf();
- qr.setResultFlagsToOk();
- qr.msgdata().setLen(bb.len());
- curOp.debug().responseLength = bb.len();
- qr.msgdata().setOperation(opReply);
- qr.setCursorId(0);
- qr.setStartingFrom(0);
- qr.setNReturned(1);
- result.setData(bb.release());
- return false;
- }
-
- int maxTimeMS = findCommand.getMaxTimeMS() ? static_cast<int>(*findCommand.getMaxTimeMS()) : 0;
- // Handle query option $maxTimeMS (not used with commands).
- if (maxTimeMS > 0) {
- uassert(40116,
- "Illegal attempt to set operation deadline within DBDirectClient",
- !opCtx->getClient()->isInDirectClient());
- opCtx->setDeadlineAfterNowBy(Milliseconds{maxTimeMS}, ErrorCodes::MaxTimeMSExpired);
- }
- opCtx->checkForInterrupt(); // May trigger maxTimeAlwaysTimeOut fail point.
-
- FindCommon::waitInFindBeforeMakingBatch(opCtx, *exec->getCanonicalQuery());
-
- // Run the query.
- // bb is used to hold query results
- // this buffer should contain either requested documents per query or
- // explain information, but not both
- BufBuilder bb(FindCommon::kInitReplyBufferSize);
- bb.skip(sizeof(QueryResult::Value));
-
- // How many results have we obtained from the executor?
- int numResults = 0;
- ResourceConsumption::DocumentUnitCounter docUnitsReturned;
-
- BSONObj obj;
- PlanExecutor::ExecState state;
-
- // Get summary info about which plan the executor is using.
- {
- stdx::lock_guard<Client> lk(*opCtx->getClient());
- curOp.setPlanSummary_inlock(exec->getPlanExplainer().getPlanSummary());
- }
-
- try {
- while (PlanExecutor::ADVANCED == (state = exec->getNext(&obj, nullptr))) {
- // If we can't fit this result inside the current batch, then we stash it for later.
- if (!FindCommon::haveSpaceForNext(obj, numResults, bb.len())) {
- exec->enqueue(obj);
- break;
- }
-
- // Add result to output buffer.
- bb.appendBuf((void*)obj.objdata(), obj.objsize());
-
- // Count the result.
- ++numResults;
-
- docUnitsReturned.observeOne(obj.objsize());
-
- if (FindCommon::enoughForFirstBatch(findCommand, numResults)) {
- LOGV2_DEBUG(20915,
- 5,
- "Enough for first batch",
- "wantMore"_attr = !findCommand.getSingleBatch(),
- "numToReturn"_attr = findCommand.getNtoreturn().value_or(0),
- "numResults"_attr = numResults);
- break;
- }
- }
- } catch (DBException& exception) {
- auto&& explainer = exec->getPlanExplainer();
- auto&& [stats, _] = explainer.getWinningPlanStats(ExplainOptions::Verbosity::kExecStats);
- LOGV2_ERROR(20919,
- "Plan executor error during find",
- "error"_attr = redact(exception.toStatus()),
- "stats"_attr = redact(stats));
-
- exception.addContext("Executor error during find");
- throw;
- }
-
- // Fill out CurOp based on query results. If we have a cursorid, we will fill out CurOp with
- // this cursorid later.
- long long ccId = 0;
-
- if (shouldSaveCursor(opCtx, collection.getCollection(), state, exec.get())) {
- // We won't use the executor until it's getMore'd.
- exec->saveState();
- exec->detachFromOperationContext();
-
- const auto& readConcernArgs = repl::ReadConcernArgs::get(opCtx);
- // Allocate a new ClientCursor and register it with the cursor manager.
- ClientCursorPin pinnedCursor = CursorManager::get(opCtx)->registerCursor(
- opCtx,
- {std::move(exec),
- nss,
- AuthorizationSession::get(opCtx->getClient())->getAuthenticatedUserNames(),
- APIParameters::get(opCtx),
- opCtx->getWriteConcern(),
- readConcernArgs,
- upconvertedQuery,
- {Privilege(ResourcePattern::forExactNamespace(nss), ActionType::find)}});
- ccId = pinnedCursor.getCursor()->cursorid();
-
- LOGV2_DEBUG(20916,
- 5,
- "Caching executor after returning results",
- "cursorId"_attr = ccId,
- "numResults"_attr = numResults);
-
- // Set curOp.debug().exhaust if the client requested exhaust and the cursor is not
- // exhausted.
- if (opCtx->isExhaust()) {
- curOp.debug().exhaust = true;
- }
-
- pinnedCursor.getCursor()->setNReturnedSoFar(numResults);
- pinnedCursor.getCursor()->incNBatches();
-
- // We assume that cursors created through a DBDirectClient are always used from their
- // original OperationContext, so we do not need to move time to and from the cursor.
- if (!opCtx->getClient()->isInDirectClient()) {
- // If the query had a time limit, remaining time is "rolled over" to the cursor (for
- // use by future getmore ops).
- pinnedCursor.getCursor()->setLeftoverMaxTimeMicros(opCtx->getRemainingMaxTimeMicros());
- }
-
- endQueryOp(opCtx,
- collection.getCollection(),
- *pinnedCursor.getCursor()->getExecutor(),
- numResults,
- ccId);
- } else {
- LOGV2_DEBUG(
- 20917, 5, "Not caching executor but returning results", "numResults"_attr = numResults);
- endQueryOp(opCtx, collection.getCollection(), *exec, numResults, ccId);
- }
-
- // Increment this metric once it has succeeded and we know it will return documents.
- auto& metricsCollector = ResourceConsumption::MetricsCollector::get(opCtx);
- metricsCollector.incrementDocUnitsReturned(docUnitsReturned);
-
- // Fill out the output buffer's header.
- QueryResult::View queryResultView = bb.buf();
- queryResultView.setCursorId(ccId);
- queryResultView.setResultFlagsToOk();
- queryResultView.msgdata().setLen(bb.len());
- queryResultView.msgdata().setOperation(opReply);
- queryResultView.setStartingFrom(0);
- queryResultView.setNReturned(numResults);
-
- // Add the results from the query into the output buffer.
- result.setData(bb.release());
-
- // curOp.debug().exhaust is set above if the client requested exhaust and the cursor is not
- // exhausted.
- return curOp.debug().exhaust;
-}
-
} // namespace mongo
diff --git a/src/mongo/db/query/find.h b/src/mongo/db/query/find.h
index 6913404cebb..65ff1ed01f5 100644
--- a/src/mongo/db/query/find.h
+++ b/src/mongo/db/query/find.h
@@ -84,20 +84,4 @@ void endQueryOp(OperationContext* opCtx,
long long numResults,
CursorId cursorId);
-/**
- * Called from the getMore entry point in ops/query.cpp.
- * Returned buffer is the message to return to the client.
- */
-Message getMore(OperationContext* opCtx,
- const char* ns,
- int ntoreturn,
- long long cursorid,
- bool* exhaust,
- bool* isCursorAuthorized);
-
-/**
- * Run the query 'q' and place the result in 'result'. Returns true if in exhaust mode.
- */
-bool runQuery(OperationContext* opCtx, QueryMessage& q, const NamespaceString& ns, Message& result);
-
} // namespace mongo
diff --git a/src/mongo/db/query/get_executor.cpp b/src/mongo/db/query/get_executor.cpp
index cdb4375ffef..c62c497d7ba 100644
--- a/src/mongo/db/query/get_executor.cpp
+++ b/src/mongo/db/query/get_executor.cpp
@@ -1186,13 +1186,10 @@ inline bool isQuerySbeCompatible(OperationContext* opCtx,
// ENSURE_SORTED stage.
const bool doesNotNeedEnsureSorted = !cq->getFindCommandRequest().getNtoreturn();
- // OP_QUERY style find commands are not currently supported by SBE.
- const bool isNotLegacy = !CurOp::get(opCtx)->isLegacyQuery();
-
// Queries against a time-series collection are not currently supported by SBE.
const bool isQueryNotAgainstTimeseriesCollection = !(cq->nss().isTimeseriesBucketsCollection());
return allExpressionsSupported && isNotCount && doesNotContainMetadataRequirements &&
- isNotLegacy && doesNotNeedEnsureSorted && isQueryNotAgainstTimeseriesCollection &&
+ doesNotNeedEnsureSorted && isQueryNotAgainstTimeseriesCollection &&
doesNotSortOnMetaOrPathWithNumericComponents && isNotOplog;
}
} // namespace
@@ -1245,17 +1242,6 @@ StatusWith<std::unique_ptr<PlanExecutor, PlanExecutor::Deleter>> getExecutorFind
opCtx, collection, std::move(canonicalQuery), yieldPolicy, plannerOptions);
}
-StatusWith<std::unique_ptr<PlanExecutor, PlanExecutor::Deleter>> getExecutorLegacyFind(
- OperationContext* opCtx,
- const CollectionPtr* collection,
- std::unique_ptr<CanonicalQuery> canonicalQuery) {
- return _getExecutorFind(opCtx,
- collection,
- std::move(canonicalQuery),
- PlanYieldPolicy::YieldPolicy::YIELD_AUTO,
- QueryPlannerParams::DEFAULT);
-}
-
namespace {
/**
diff --git a/src/mongo/db/query/get_executor.h b/src/mongo/db/query/get_executor.h
index 26f420fd938..b3d8c5d332a 100644
--- a/src/mongo/db/query/get_executor.h
+++ b/src/mongo/db/query/get_executor.h
@@ -144,14 +144,6 @@ StatusWith<std::unique_ptr<PlanExecutor, PlanExecutor::Deleter>> getExecutorFind
size_t plannerOptions = QueryPlannerParams::DEFAULT);
/**
- * Returns a plan executor for a legacy OP_QUERY find.
- */
-StatusWith<std::unique_ptr<PlanExecutor, PlanExecutor::Deleter>> getExecutorLegacyFind(
- OperationContext* opCtx,
- const CollectionPtr* collection,
- std::unique_ptr<CanonicalQuery> canonicalQuery);
-
-/**
* If possible, turn the provided QuerySolution into a QuerySolution that uses a DistinctNode
* to provide results for the distinct command.
*
diff --git a/src/mongo/db/service_entry_point_common.cpp b/src/mongo/db/service_entry_point_common.cpp
index d64fb735b6e..12898e85ceb 100644
--- a/src/mongo/db/service_entry_point_common.cpp
+++ b/src/mongo/db/service_entry_point_common.cpp
@@ -111,7 +111,6 @@
namespace mongo {
-MONGO_FAIL_POINT_DEFINE(rsStopGetMore);
MONGO_FAIL_POINT_DEFINE(respondWithNotPrimaryInCommandDispatch);
MONGO_FAIL_POINT_DEFINE(skipCheckingForNotPrimaryInCommandDispatch);
MONGO_FAIL_POINT_DEFINE(sleepMillisAfterCommandExecutionBegins);
@@ -225,66 +224,6 @@ struct HandleRequest {
std::shared_ptr<ExecutionContext> executionContext;
};
-void generateLegacyQueryErrorResponse(const AssertionException& exception,
- const QueryMessage& queryMessage,
- CurOp* curop,
- Message* response) {
- curop->debug().errInfo = exception.toStatus();
-
- if (queryMessage.query.valid())
- LOGV2_OPTIONS(51777,
- {logv2::LogComponent::kQuery},
- "Assertion {error} ns: {namespace} query: {query}",
- "Assertion for valid query",
- "error"_attr = exception,
- "namespace"_attr = queryMessage.ns,
- "query"_attr = redact(queryMessage.query));
- else
- LOGV2_OPTIONS(51778,
- {logv2::LogComponent::kQuery},
- "Assertion {error} ns: {namespace} query object is corrupt",
- "Assertion for query with corrupted object",
- "error"_attr = exception,
- "namespace"_attr = queryMessage.ns);
-
- if (queryMessage.ntoskip || queryMessage.ntoreturn) {
- LOGV2_OPTIONS(21952,
- {logv2::LogComponent::kQuery},
- "Query's nToSkip = {nToSkip} and nToReturn = {nToReturn}",
- "Assertion for query with nToSkip and/or nToReturn",
- "nToSkip"_attr = queryMessage.ntoskip,
- "nToReturn"_attr = queryMessage.ntoreturn);
- }
-
- BSONObjBuilder err;
- err.append("$err", exception.reason());
- err.append("code", exception.code());
- err.append("ok", 0.0);
- auto const extraInfo = exception.extraInfo();
- if (extraInfo) {
- extraInfo->serialize(&err);
- }
- BSONObj errObj = err.done();
-
- invariant(!exception.isA<ErrorCategory::StaleShardVersionError>() &&
- exception.code() != ErrorCodes::StaleDbVersion);
-
- BufBuilder bb;
- bb.skip(sizeof(QueryResult::Value));
- bb.appendBuf((void*)errObj.objdata(), errObj.objsize());
-
- // TODO: call replyToQuery() from here instead of this!!! see dbmessage.h
- QueryResult::View msgdata = bb.buf();
- QueryResult::View qr = msgdata;
- qr.setResultFlags(ResultFlag_ErrSet);
- qr.msgdata().setLen(bb.len());
- qr.msgdata().setOperation(opReply);
- qr.setCursorId(0);
- qr.setStartingFrom(0);
- qr.setNReturned(1);
- response->setData(bb.release());
-}
-
void registerError(OperationContext* opCtx, const Status& status) {
LastError::get(opCtx->getClient()).setLastError(status.code(), status.reason());
CurOp::get(opCtx)->debug().errInfo = status;
@@ -1989,132 +1928,6 @@ Future<DbResponse> receivedCommands(std::shared_ptr<HandleRequest::ExecutionCont
.then([execContext]() mutable { return makeCommandResponse(std::move(execContext)); });
}
-DbResponse receivedQuery(OperationContext* opCtx,
- const NamespaceString& nss,
- Client& c,
- const Message& m,
- const ServiceEntryPointCommon::Hooks& behaviors) {
- invariant(!nss.isCommand());
-
- // The legacy opcodes should be counted twice: as part of the overall opcodes' counts and on
- // their own to highlight that they are being used.
- globalOpCounters.gotQuery();
- globalOpCounters.gotQueryDeprecated();
-
- if (!opCtx->getClient()->isInDirectClient()) {
- ServerReadConcernMetrics::get(opCtx)->recordReadConcern(repl::ReadConcernArgs::get(opCtx),
- false /* isTransaction */);
- }
-
- DbMessage d(m);
- QueryMessage q(d);
-
- CurOp& op = *CurOp::get(opCtx);
- DbResponse dbResponse;
-
- try {
- warnDeprecation(c, networkOpToString(m.operation()));
- Client* client = opCtx->getClient();
- Status status = auth::checkAuthForFind(AuthorizationSession::get(client), nss, false);
- audit::logQueryAuthzCheck(client, nss, q.query, status.code());
- uassertStatusOK(status);
-
- dbResponse.shouldRunAgainForExhaust = runQuery(opCtx, q, nss, dbResponse.response);
- } catch (const AssertionException& e) {
- dbResponse.response.reset();
- generateLegacyQueryErrorResponse(e, q, &op, &dbResponse.response);
- }
-
- op.debug().responseLength = dbResponse.response.header().dataLen();
- return dbResponse;
-}
-
-DbResponse receivedGetMore(OperationContext* opCtx,
- const Message& m,
- CurOp& curop,
- bool* shouldLogOpDebug) {
- // The legacy opcodes should be counted twice: as part of the overall opcodes' counts and on
- // their own to highlight that they are being used.
- globalOpCounters.gotGetMore();
- globalOpCounters.gotGetMoreDeprecated();
- DbMessage d(m);
-
- const char* ns = d.getns();
- int ntoreturn = d.pullInt();
- uassert(
- 34419, str::stream() << "Invalid ntoreturn for OP_GET_MORE: " << ntoreturn, ntoreturn >= 0);
- long long cursorid = d.pullInt64();
-
- curop.debug().ntoreturn = ntoreturn;
- curop.debug().cursorid = cursorid;
-
- {
- stdx::lock_guard<Client> lk(*opCtx->getClient());
- CurOp::get(opCtx)->setNS_inlock(ns);
- }
-
- bool exhaust = false;
- bool isCursorAuthorized = false;
-
- DbResponse dbresponse;
- try {
- warnDeprecation(*opCtx->getClient(), networkOpToString(m.operation()));
- const NamespaceString nsString(ns);
- uassert(ErrorCodes::InvalidNamespace,
- str::stream() << "Invalid ns [" << ns << "]",
- nsString.isValid());
-
- Status status = auth::checkAuthForGetMore(
- AuthorizationSession::get(opCtx->getClient()), nsString, cursorid, false);
- audit::logGetMoreAuthzCheck(opCtx->getClient(), nsString, cursorid, status.code());
- uassertStatusOK(status);
-
- while (MONGO_unlikely(rsStopGetMore.shouldFail())) {
- sleepmillis(0);
- }
-
- dbresponse.response =
- getMore(opCtx, ns, ntoreturn, cursorid, &exhaust, &isCursorAuthorized);
- } catch (AssertionException& e) {
- if (isCursorAuthorized) {
- // Make sure that killCursorGlobal does not throw an exception if it is interrupted.
- UninterruptibleLockGuard noInterrupt(opCtx->lockState());
-
- // If an error was thrown prior to auth checks, then the cursor should remain alive
- // in order to prevent an unauthorized user from resulting in the death of a cursor.
- // In other error cases, the cursor is dead and should be cleaned up.
- //
- // If killing the cursor fails, ignore the error and don't try again. The cursor
- // should be reaped by the client cursor timeout thread.
- CursorManager::get(opCtx)->killCursor(opCtx, cursorid).ignore();
- }
-
- BSONObjBuilder err;
- err.append("$err", e.reason());
- err.append("code", e.code());
- BSONObj errObj = err.obj();
-
- curop.debug().errInfo = e.toStatus();
-
- dbresponse = replyToQuery(errObj, ResultFlag_ErrSet);
- curop.debug().responseLength = dbresponse.response.header().dataLen();
- curop.debug().nreturned = 1;
- *shouldLogOpDebug = true;
- return dbresponse;
- }
-
- curop.debug().responseLength = dbresponse.response.header().dataLen();
- auto queryResult = QueryResult::ConstView(dbresponse.response.buf());
- curop.debug().nreturned = queryResult.getNReturned();
-
- if (exhaust) {
- curop.debug().exhaust = true;
- dbresponse.shouldRunAgainForExhaust = true;
- }
-
- return dbresponse;
-}
-
struct CommandOpRunner : HandleRequest::OpRunner {
using HandleRequest::OpRunner::OpRunner;
Future<DbResponse> run() override {
@@ -2134,23 +1947,20 @@ struct SynchronousOpRunner : HandleRequest::OpRunner {
struct QueryOpRunner : SynchronousOpRunner {
using SynchronousOpRunner::SynchronousOpRunner;
DbResponse runSync() override {
- auto opCtx = executionContext->getOpCtx();
- opCtx->markKillOnClientDisconnect();
- return receivedQuery(opCtx,
- executionContext->nsString(),
- executionContext->client(),
- executionContext->getMessage(),
- *executionContext->behaviors);
+ invariant(!executionContext->nsString().isCommand());
+
+ globalOpCounters.gotQueryDeprecated();
+ warnDeprecation(executionContext->client(), networkOpToString(dbQuery));
+ return makeErrorResponseToDeprecatedOpQuery("OP_QUERY is no longer supported");
}
};
struct GetMoreOpRunner : SynchronousOpRunner {
using SynchronousOpRunner::SynchronousOpRunner;
DbResponse runSync() override {
- return receivedGetMore(executionContext->getOpCtx(),
- executionContext->getMessage(),
- executionContext->currentOp(),
- &executionContext->forceLog);
+ globalOpCounters.gotGetMoreDeprecated();
+ warnDeprecation(executionContext->client(), networkOpToString(dbGetMore));
+ return makeErrorResponseToDeprecatedOpQuery("OP_GET_MORE is no longer supported");
}
};
@@ -2220,7 +2030,9 @@ std::unique_ptr<HandleRequest::OpRunner> HandleRequest::makeOpRunner() {
case dbQuery:
if (!executionContext->nsString().isCommand())
return std::make_unique<QueryOpRunner>(this);
- // FALLTHROUGH: it's a query containing a command
+ // FALLTHROUGH: it's a query containing a command. Ideally, we'd like to let through
+ // only hello|isMaster commands but at this point the command hasn't been parsed yet, so
+ // we don't know what it is.
case dbMsg:
return std::make_unique<CommandOpRunner>(this);
case dbGetMore:
diff --git a/src/mongo/db/service_entry_point_common.h b/src/mongo/db/service_entry_point_common.h
index 53de36e4829..89d4cc529f6 100644
--- a/src/mongo/db/service_entry_point_common.h
+++ b/src/mongo/db/service_entry_point_common.h
@@ -40,7 +40,6 @@
namespace mongo {
-extern FailPoint rsStopGetMore;
extern FailPoint respondWithNotPrimaryInCommandDispatch;
// When active, we won't check if we are primary in command dispatch. Activate this if you want to