summaryrefslogtreecommitdiff
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
parent23ecc48f89f4ec03d7b42e637c5969802efdb261 (diff)
downloadmongo-beeab6beaf18232e52bb3094f5f31fe83fbae2a4.tar.gz
SERVER-57391 Return error response to OP_QUERY and OP_GET_MORE messages
-rw-r--r--jstests/noPassthroughWithMongod/getmore_error.js36
-rw-r--r--src/mongo/client/dbclient_base.h2
-rw-r--r--src/mongo/client/dbclient_cursor.cpp9
-rw-r--r--src/mongo/client/query.cpp5
-rw-r--r--src/mongo/client/query.h6
-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
-rw-r--r--src/mongo/dbtests/querytests.cpp30
-rw-r--r--src/mongo/rpc/op_legacy_integration_test.cpp138
-rw-r--r--src/mongo/s/commands/strategy.cpp259
-rw-r--r--src/mongo/s/commands/strategy.h41
-rw-r--r--src/mongo/s/service_entry_point_mongos.cpp99
25 files changed, 189 insertions, 1702 deletions
diff --git a/jstests/noPassthroughWithMongod/getmore_error.js b/jstests/noPassthroughWithMongod/getmore_error.js
deleted file mode 100644
index 4fed6c38d3d..00000000000
--- a/jstests/noPassthroughWithMongod/getmore_error.js
+++ /dev/null
@@ -1,36 +0,0 @@
-// ensure errors in getmore are properly reported to users
-
-var t = db.getmore_error;
-
-for (var i = 0; i < 10; i++) {
- t.insert({_id: i});
-}
-
-var cursor = t.find().batchSize(2); // 1 is a special case
-
-// first batch (only one from OP_QUERY)
-assert.eq(cursor.next(), {_id: 0});
-assert.eq(cursor.next(), {_id: 1});
-assert.eq(cursor.objsLeftInBatch(), 0);
-
-// second batch (first from OP_GETMORE)
-assert.eq(cursor.next(), {_id: 2});
-assert.eq(cursor.next(), {_id: 3});
-assert.eq(cursor.objsLeftInBatch(), 0);
-
-/*
-// QUERY_MIGRATION disabling this because it's hard to have a failpoint in 2 parallel
-// systems
-// make the next OP_GETMORE fail
-assert.commandWorked(
- db.adminCommand({configureFailPoint: 'getMoreError', mode: {times: 1}})
-);
-
-// attempt to get next batch should fail with a failpoint error
-var error = assert.throws(function(){cursor.next();});
-if (!error.search(/failpoint/))
- assert(false, "got a non-failpoint error: " + error);
-*/
-
-// make sure we won't break other tests by breaking getmore for them
-assert.eq(t.find().batchSize(2).itcount(), 10);
diff --git a/src/mongo/client/dbclient_base.h b/src/mongo/client/dbclient_base.h
index 68e2101d259..c8beea97aff 100644
--- a/src/mongo/client/dbclient_base.h
+++ b/src/mongo/client/dbclient_base.h
@@ -615,7 +615,7 @@ public:
/** Uses QueryOption_Exhaust, when available and specified in 'queryOptions'.
Exhaust mode sends back all data queries as fast as possible, with no back-and-forth for
- OP_GETMORE. If you are certain you will exhaust the query, it could be useful. If
+ OP_GET_MORE. If you are certain you will exhaust the query, it could be useful. If
exhaust mode is not specified in 'queryOptions' or not available, this call transparently
falls back to using ordinary getMores.
diff --git a/src/mongo/client/dbclient_cursor.cpp b/src/mongo/client/dbclient_cursor.cpp
index 6c5db1bd6d1..db9cc55d6f3 100644
--- a/src/mongo/client/dbclient_cursor.cpp
+++ b/src/mongo/client/dbclient_cursor.cpp
@@ -109,7 +109,7 @@ Message DBClientCursor::_assembleInit() {
// so we need to allow the shell to send invalid options so that we can
// test that the server rejects them. Thus, to allow generating commands with
// invalid options, we validate them here, and fall back to generating an OP_QUERY
- // through makeQueryMessage() if the options are invalid.
+ // through makeDeprecatedQueryMessage() if the options are invalid.
bool hasValidNToReturnForCommand = (nToReturn == 1 || nToReturn == -1);
bool hasValidFlagsForCommand = !(opts & mongo::QueryOption_Exhaust);
bool hasInvalidMaxTimeMs = query.hasField("$maxTimeMS");
@@ -175,7 +175,8 @@ Message DBClientCursor::_assembleInit() {
}
_useFindCommand = false; // Make sure we handle the reply correctly.
- return makeQueryMessage(ns.ns(), query, nextBatchSize(), nToSkip, fieldsToReturn, opts);
+ return makeDeprecatedQueryMessage(
+ ns.ns(), query, nextBatchSize(), nToSkip, fieldsToReturn, opts);
}
Message DBClientCursor::_assembleGetMore() {
@@ -200,7 +201,7 @@ Message DBClientCursor::_assembleGetMore() {
return msg;
} else {
// Assemble a legacy getMore request.
- return makeGetMoreMessage(ns.ns(), cursorId, nextBatchSize(), opts);
+ return makeDeprecatedGetMoreMessage(ns.ns(), cursorId, nextBatchSize(), opts);
}
}
@@ -642,7 +643,7 @@ void DBClientCursor::kill() {
if (_useFindCommand) {
conn->killCursor(ns, cursorId);
} else {
- auto toSend = makeKillCursorsMessage(cursorId);
+ auto toSend = makeDeprecatedKillCursorsMessage(cursorId);
conn->say(toSend);
}
};
diff --git a/src/mongo/client/query.cpp b/src/mongo/client/query.cpp
index 105d0a8169b..6f8ae2e3615 100644
--- a/src/mongo/client/query.cpp
+++ b/src/mongo/client/query.cpp
@@ -79,11 +79,6 @@ Query& Query::hint(BSONObj keyPattern) {
return *this;
}
-Query& Query::explain() {
- appendComplex("$explain", true);
- return *this;
-}
-
Query& Query::minKey(const BSONObj& val) {
appendComplex("$min", val);
return *this;
diff --git a/src/mongo/client/query.h b/src/mongo/client/query.h
index 63843079c9c..f295873d98e 100644
--- a/src/mongo/client/query.h
+++ b/src/mongo/client/query.h
@@ -93,12 +93,6 @@ public:
*/
Query& maxKey(const BSONObj& val);
- /** Return explain information about execution of this query instead of the actual query
- * results.
- * Normally it is easier to use the mongo shell to run db.find(...).explain().
- */
- Query& explain();
-
/** Queries to the Mongo database support a $where parameter option which contains
a javascript function that is evaluated to see whether objects being queried match
its criteria. Use this helper to append such a function to a query object.
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
diff --git a/src/mongo/dbtests/querytests.cpp b/src/mongo/dbtests/querytests.cpp
index f4e28010795..89b52a3f172 100644
--- a/src/mongo/dbtests/querytests.cpp
+++ b/src/mongo/dbtests/querytests.cpp
@@ -768,22 +768,22 @@ public:
insert(ns, BSON("ts" << Timestamp(1000, 0)));
insert(ns, BSON("ts" << Timestamp(1000, 1)));
insert(ns, BSON("ts" << Timestamp(1000, 2)));
- unique_ptr<DBClientCursor> c = _client.query(
- NamespaceString(ns),
- QUERY("ts" << GT << Timestamp(1000, 1)).hint(BSON("$natural" << 1)).explain(),
- 0,
- 0,
- nullptr);
- ASSERT(c->more());
- // Check number of results and filterSet flag in explain.
- // filterSet is not available in oplog replay mode.
- BSONObj explainObj = c->next();
- ASSERT(explainObj.hasField("executionStats")) << explainObj;
- BSONObj execStats = explainObj["executionStats"].Obj();
- ASSERT_EQUALS(1, execStats.getIntField("nReturned"));
-
- ASSERT(!c->more());
+ BSONObj explainCmdObj =
+ BSON("explain" << BSON("find"
+ << "oplog.querytests.OplogScanGtTsExplain"
+ << "filter" << BSON("ts" << GT << Timestamp(1000, 1)) << "hint"
+ << BSON("$natural" << 1))
+ << "verbosity"
+ << "executionStats");
+
+ auto reply = _client.runCommand(OpMsgRequest::fromDBAndBody("local", explainCmdObj));
+ BSONObj explainCmdReplyBody = reply->getCommandReply();
+ ASSERT_OK(getStatusFromCommandResult(explainCmdReplyBody));
+
+ ASSERT(explainCmdReplyBody.hasField("executionStats")) << explainCmdReplyBody;
+ BSONObj execStats = explainCmdReplyBody["executionStats"].Obj();
+ ASSERT_EQUALS(1, execStats.getIntField("nReturned")) << explainCmdReplyBody;
}
private:
diff --git a/src/mongo/rpc/op_legacy_integration_test.cpp b/src/mongo/rpc/op_legacy_integration_test.cpp
index 8eb7776c3de..4fda3fb8fbc 100644
--- a/src/mongo/rpc/op_legacy_integration_test.cpp
+++ b/src/mongo/rpc/op_legacy_integration_test.cpp
@@ -27,8 +27,11 @@
* it in the license file.
*/
+#define MONGO_LOGV2_DEFAULT_COMPONENT ::mongo::logv2::LogComponent::kTest
+
#include "mongo/client/dbclient_connection.h"
#include "mongo/db/dbmessage.h"
+#include "mongo/db/query/cursor_response.h"
#include "mongo/rpc/get_status_from_command_result.h"
#include "mongo/unittest/integration_test.h"
#include "mongo/unittest/unittest.h"
@@ -51,13 +54,26 @@ long getDeprecatedOpCount(BSONObj serverStatus, const char* opName) {
return deprecatedOpcounters ? deprecatedOpcounters[opName].Long() : 0;
}
-bool isStandaloneMongod(DBClientBase* conn) {
- return !conn->isReplicaSetMember() && !conn->isMongos();
+// Issue a find command request so we can use cursor id from it to test the deprecated getMore
+// and killCursors ops.
+int64_t getValidCursorIdFromFindCmd(DBClientBase* conn, const char* collName) {
+ Message findCmdRequest =
+ OpMsgRequest::fromDBAndBody("testOpLegacy", BSON("find" << collName << "batchSize" << 2))
+ .serialize();
+ Message findCmdReply;
+ ASSERT(conn->call(findCmdRequest, findCmdReply));
+ BSONObj findCmdReplyBody = OpMsg::parse(findCmdReply).body;
+ auto cr = CursorResponse::parseFromBSON(findCmdReplyBody.getOwned());
+ ASSERT_OK(cr.getStatus());
+ const int64_t cursorId = cr.getValue().getCursorId();
+ ASSERT_NE(0, cursorId);
+
+ return cursorId;
}
TEST(OpLegacy, DeprecatedWriteOpsCounters) {
auto conn = getIntegrationTestConnection();
- const std::string ns = "test.DeprecatedWriteOpsCounters";
+ const std::string ns = "testOpLegacy.DeprecatedWriteOpsCounters";
// Cache the counters prior to running the deprecated requests.
auto serverStatusCmd = fromjson("{serverStatus: 1}");
@@ -73,13 +89,13 @@ TEST(OpLegacy, DeprecatedWriteOpsCounters) {
// Issue the requests. They are expected to fail but should still be counted.
Message ignore;
- auto opInsert = makeInsertMessage(ns, insert, 2, 0 /*continue on error*/);
+ auto opInsert = makeDeprecatedInsertMessage(ns, insert, 2, 0 /*continue on error*/);
ASSERT_THROWS(conn->call(opInsert, ignore), ExceptionForCat<ErrorCategory::NetworkError>);
- auto opUpdate = makeUpdateMessage(ns, query, update, 0 /*no upsert, no multi*/);
+ auto opUpdate = makeDeprecatedUpdateMessage(ns, query, update, 0 /*no upsert, no multi*/);
ASSERT_THROWS(conn->call(opUpdate, ignore), ExceptionForCat<ErrorCategory::NetworkError>);
- auto opDelete = makeRemoveMessage(ns, query, 0 /*limit*/);
+ auto opDelete = makeDeprecatedRemoveMessage(ns, query, 0 /*limit*/);
ASSERT_THROWS(conn->call(opDelete, ignore), ExceptionForCat<ErrorCategory::NetworkError>);
// Check the opcounters after running the deprecated operations.
@@ -99,40 +115,57 @@ TEST(OpLegacy, DeprecatedWriteOpsCounters) {
getDeprecatedOpCount(serverStatusReply, "total"));
}
+void assertFailure(const Message response, StringData expectedErr) {
+ QueryResult::ConstView qr = response.singleData().view2ptr();
+ BufReader responseData(qr.data(), qr.dataLen());
+ BSONObj responseBody = responseData.read<BSONObj>();
+
+ ASSERT_FALSE(responseBody["ok"].trueValue()) << responseBody;
+ ASSERT_EQ(5739101, responseBody["code"].Int()) << responseBody;
+ ASSERT_NE(getErrField(responseBody).checkAndGetStringData().find(expectedErr),
+ std::string::npos)
+ << responseBody;
+}
+
TEST(OpLegacy, DeprecatedReadOpsCounters) {
auto conn = getIntegrationTestConnection();
- const std::string ns = "test.DeprecatedReadOpsCounters";
+ const std::string ns = "testOpLegacy.DeprecatedReadOpsCounters";
BSONObj insert = fromjson(R"({
insert: "DeprecatedReadOpsCounters",
documents: [ {a: 1},{a: 2},{a: 3},{a: 4},{a: 5},{a: 6},{a: 7} ]
})");
BSONObj ignoreResponse;
- ASSERT(conn->runCommand("test", insert, ignoreResponse));
+ ASSERT(conn->runCommand("testOpLegacy", insert, ignoreResponse));
// Cache the counters prior to running the deprecated requests.
auto serverStatusCmd = fromjson("{serverStatus: 1}");
BSONObj serverStatusReplyPrior;
ASSERT(conn->runCommand("admin", serverStatusCmd, serverStatusReplyPrior));
- // Issue the deprecated requests.
- const BSONObj query = fromjson("{a: {$lt: 42}}");
-
- auto opQuery = makeQueryMessage(
- ns, query, 2 /*nToReturn*/, 0 /*nToSkip*/, nullptr /*fieldsToReturn*/, 0 /*queryOptions*/);
- Message replyQuery;
- ASSERT(conn->call(opQuery, replyQuery));
- QueryResult::ConstView qr = replyQuery.singleData().view2ptr();
- int64_t cursorId = qr.getCursorId();
- ASSERT_NE(0, cursorId);
-
- auto opGetMore = makeGetMoreMessage(ns, cursorId, 2 /*nToReturn*/, 0 /*flags*/);
- Message replyGetMore;
- ASSERT(conn->call(opGetMore, replyGetMore));
-
- auto opKillCursors = makeKillCursorsMessage(cursorId);
- Message ignore;
- ASSERT_THROWS(conn->call(opKillCursors, ignore), ExceptionForCat<ErrorCategory::NetworkError>);
+ // Issue the deprecated requests. They all should fail one way or another.
+ Message opQueryRequest = makeDeprecatedQueryMessage(ns,
+ fromjson("{}"),
+ 2 /*nToReturn*/,
+ 0 /*nToSkip*/,
+ nullptr /*fieldsToReturn*/,
+ 0 /*queryOptions*/);
+ Message opQueryReply;
+ ASSERT(conn->call(opQueryRequest, opQueryReply));
+ assertFailure(opQueryReply, "OP_QUERY is no longer supported");
+
+ const int64_t cursorId = getValidCursorIdFromFindCmd(conn.get(), "DeprecatedReadOpsCounters");
+
+ Message opGetMoreRequest =
+ makeDeprecatedGetMoreMessage(ns, cursorId, 2 /*nToReturn*/, 0 /*flags*/);
+ Message opGetMoreReply;
+ ASSERT(conn->call(opGetMoreRequest, opGetMoreReply));
+ assertFailure(opGetMoreReply, "OP_GET_MORE is no longer supported");
+
+ Message opKillCursorsRequest = makeDeprecatedKillCursorsMessage(cursorId);
+ Message opKillCursorsReply;
+ ASSERT_THROWS(conn->call(opKillCursorsRequest, opKillCursorsReply),
+ ExceptionForCat<ErrorCategory::NetworkError>);
// Check the opcounters after running the deprecated operations.
BSONObj serverStatusReply;
@@ -140,17 +173,9 @@ TEST(OpLegacy, DeprecatedReadOpsCounters) {
ASSERT_EQ(getDeprecatedOpCount(serverStatusReplyPrior, "query") + 1,
getDeprecatedOpCount(serverStatusReply, "query"));
- if (isStandaloneMongod(conn.get())) {
- ASSERT_EQ(getOpCount(serverStatusReplyPrior, "query") + 1,
- getOpCount(serverStatusReply, "query"));
- }
ASSERT_EQ(getDeprecatedOpCount(serverStatusReplyPrior, "getmore") + 1,
getDeprecatedOpCount(serverStatusReply, "getmore"));
- if (isStandaloneMongod(conn.get())) {
- ASSERT_EQ(getOpCount(serverStatusReplyPrior, "getmore") + 1,
- getOpCount(serverStatusReply, "getmore"));
- }
ASSERT_EQ(getDeprecatedOpCount(serverStatusReplyPrior, "killcursors") + 1,
getDeprecatedOpCount(serverStatusReply, "killcursors"));
@@ -205,7 +230,7 @@ std::string getLastError(DBClientBase* conn) {
void exerciseDeprecatedOps(DBClientBase* conn, const std::string& expectedSeverity) {
// Build the deprecated requests and the getLog command.
- const std::string ns = "test.exerciseDeprecatedOps";
+ const std::string ns = "testOpLegacy.exerciseDeprecatedOps";
// Insert some docs into the collection so even though the legacy write ops are failing we can
// still test getMore, killCursors and query.
@@ -214,17 +239,17 @@ void exerciseDeprecatedOps(DBClientBase* conn, const std::string& expectedSeveri
documents: [ {a: 1},{a: 2},{a: 3},{a: 4},{a: 5},{a: 6},{a: 7} ]
})");
BSONObj ignoreResponse;
- ASSERT(conn->runCommand("test", data, ignoreResponse));
+ ASSERT(conn->runCommand("testOpLegacy", data, ignoreResponse));
const BSONObj doc1 = fromjson("{a: 1}");
const BSONObj doc2 = fromjson("{a: 2}");
const BSONObj insert[2] = {doc1, doc2};
const BSONObj query = fromjson("{a: {$lt: 42}}");
const BSONObj update = fromjson("{$set: {b: 2}}");
- auto opInsert = makeInsertMessage(ns, insert, 2, 0 /*continue on error*/);
- auto opUpdate = makeUpdateMessage(ns, query, update, 0 /*no upsert, no multi*/);
- auto opDelete = makeRemoveMessage(ns, query, 0 /*limit*/);
- auto opQuery = makeQueryMessage(
+ auto opInsert = makeDeprecatedInsertMessage(ns, insert, 2, 0 /*continue on error*/);
+ auto opUpdate = makeDeprecatedUpdateMessage(ns, query, update, 0 /*no upsert, no multi*/);
+ auto opDelete = makeDeprecatedRemoveMessage(ns, query, 0 /*limit*/);
+ auto opQuery = makeDeprecatedQueryMessage(
ns, query, 2 /*nToReturn*/, 0 /*nToSkip*/, nullptr /*fieldsToReturn*/, 0 /*queryOptions*/);
Message ignore;
@@ -246,15 +271,14 @@ void exerciseDeprecatedOps(DBClientBase* conn, const std::string& expectedSeveri
ASSERT(conn->call(opQuery, replyQuery));
ASSERT(wasLogged(conn, "query", expectedSeverity));
- QueryResult::ConstView qr = replyQuery.singleData().view2ptr();
- int64_t cursorId = qr.getCursorId();
- ASSERT_NE(0, cursorId);
- auto opGetMore = makeGetMoreMessage(ns, cursorId, 2 /*nToReturn*/, 0 /*flags*/);
+ int64_t cursorId = getValidCursorIdFromFindCmd(conn, "exerciseDeprecatedOps");
+
+ auto opGetMore = makeDeprecatedGetMoreMessage(ns, cursorId, 2 /*nToReturn*/, 0 /*flags*/);
Message replyGetMore;
ASSERT(conn->call(opGetMore, replyGetMore));
ASSERT(wasLogged(conn, "getmore", expectedSeverity));
- auto opKillCursors = makeKillCursorsMessage(cursorId);
+ auto opKillCursors = makeDeprecatedKillCursorsMessage(cursorId);
ASSERT_THROWS(conn->call(opKillCursors, ignore), ExceptionForCat<ErrorCategory::NetworkError>);
ASSERT(wasLogged(conn, "killcursors", expectedSeverity));
@@ -320,12 +344,12 @@ TEST(OpLegacy, GenericCommandViaOpQuery) {
ASSERT(wasLogged(conn.get(), "getLastError", ""));
// The actual command doesn't matter, as long as it's not 'hello' or 'isMaster'.
- auto opQuery = makeQueryMessage("test.$cmd",
- serverStatusCmd,
- 1 /*nToReturn*/,
- 0 /*nToSkip*/,
- nullptr /*fieldsToReturn*/,
- 0 /*queryOptions*/);
+ auto opQuery = makeDeprecatedQueryMessage("testOpLegacy.$cmd",
+ serverStatusCmd,
+ 1 /*nToReturn*/,
+ 0 /*nToSkip*/,
+ nullptr /*fieldsToReturn*/,
+ 0 /*queryOptions*/);
Message replyQuery;
ASSERT(conn->call(opQuery, replyQuery));
QueryResult::ConstView qr = replyQuery.singleData().view2ptr();
@@ -356,12 +380,12 @@ void testAllowedCommand(const char* command) {
ASSERT_EQ("", getLastError(conn.get())); // will start failing soon but will still log
ASSERT(wasLogged(conn.get(), "getLastError", ""));
- auto opQuery = makeQueryMessage("test.$cmd",
- fromjson(command),
- 1 /*nToReturn*/,
- 0 /*nToSkip*/,
- nullptr /*fieldsToReturn*/,
- 0 /*queryOptions*/);
+ auto opQuery = makeDeprecatedQueryMessage("testOpLegacy.$cmd",
+ fromjson(command),
+ 1 /*nToReturn*/,
+ 0 /*nToSkip*/,
+ nullptr /*fieldsToReturn*/,
+ 0 /*queryOptions*/);
Message replyQuery;
ASSERT(conn->call(opQuery, replyQuery));
QueryResult::ConstView qr = replyQuery.singleData().view2ptr();
diff --git a/src/mongo/s/commands/strategy.cpp b/src/mongo/s/commands/strategy.cpp
index 042aa15b7fc..5a80b17e933 100644
--- a/src/mongo/s/commands/strategy.cpp
+++ b/src/mongo/s/commands/strategy.cpp
@@ -1125,125 +1125,6 @@ Future<void> ParseAndRunCommand::run() {
} // namespace
-DbResponse Strategy::queryOp(OperationContext* opCtx, const NamespaceString& nss, DbMessage* dbm) {
- globalOpCounters.gotQuery();
- globalOpCounters.gotQueryDeprecated();
-
- ON_BLOCK_EXIT([opCtx] {
- Grid::get(opCtx)->catalogCache()->checkAndRecordOperationBlockedByRefresh(
- opCtx, mongo::LogicalOp::opQuery);
- });
-
- const QueryMessage q(*dbm);
-
- const auto upconvertedQuery = upconvertQueryEntry(q.query, nss, q.ntoreturn, q.ntoskip);
-
- // Set the upconverted query as the CurOp command object.
- CurOp::get(opCtx)->setGenericOpRequestDetails(
- opCtx, nss, nullptr, upconvertedQuery, dbm->msg().operation());
-
- Client* const client = opCtx->getClient();
- AuthorizationSession* const authSession = AuthorizationSession::get(client);
-
- // The legacy '$comment' operator gets converted to 'comment' by upconvertQueryEntry(). We
- // set the comment in 'opCtx' so that it can be passed on to the respective shards.
- if (auto commentField = upconvertedQuery["comment"]) {
- opCtx->setComment(commentField.wrap());
- }
-
- Status status = auth::checkAuthForFind(authSession, nss, false);
- audit::logQueryAuthzCheck(client, nss, q.query, status.code());
- uassertStatusOK(status);
-
- LOGV2_DEBUG(22768,
- 3,
- "Query: {namespace} {query} ntoreturn: {ntoreturn} options: {queryOptions}",
- "Query",
- "namespace"_attr = q.ns,
- "query"_attr = redact(q.query),
- "ntoreturn"_attr = q.ntoreturn,
- "queryOptions"_attr = q.queryOptions);
-
- if (q.queryOptions & QueryOption_Exhaust) {
- uasserted(18526,
- str::stream() << "The 'exhaust' query option is invalid for mongos queries: "
- << nss.ns() << " " << q.query.toString());
- }
-
- // Determine the default read preference mode based on the value of the slaveOk flag.
- const auto defaultReadPref = q.queryOptions & QueryOption_SecondaryOk
- ? ReadPreference::SecondaryPreferred
- : ReadPreference::PrimaryOnly;
- ReadPreferenceSetting::get(opCtx) =
- uassertStatusOK(ReadPreferenceSetting::fromContainingBSON(q.query, defaultReadPref));
-
- const boost::intrusive_ptr<ExpressionContext> expCtx;
- auto canonicalQuery = uassertStatusOK(
- CanonicalQuery::canonicalize(opCtx,
- q,
- expCtx,
- ExtensionsCallbackNoop(),
- MatchExpressionParser::kAllowAllSpecialFeatures));
-
- const FindCommandRequest& findCommand = canonicalQuery->getFindCommandRequest();
- // Handle query option $maxTimeMS (not used with commands).
- if (findCommand.getMaxTimeMS().value_or(0) > 0) {
- uassert(50749,
- "Illegal attempt to set operation deadline within DBDirectClient",
- !opCtx->getClient()->isInDirectClient());
- 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 (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,
- verbosity,
- ReadPreferenceSetting::get(opCtx),
- &explainBuilder);
-
- BSONObj explainObj = explainBuilder.done();
- return replyToQuery(explainObj);
- }
-
- // Do the work to generate the first batch of results. This blocks waiting to get responses from
- // the shard(s).
- std::vector<BSONObj> batch;
-
- // 0 means the cursor is exhausted. Otherwise we assume that a cursor with the returned id can
- // be retrieved via the ClusterCursorManager.
- CursorId cursorId;
- try {
- cursorId = ClusterFind::runQuery(
- opCtx, *canonicalQuery, ReadPreferenceSetting::get(opCtx), &batch);
- } catch (const ExceptionFor<ErrorCodes::CommandOnShardedViewNotSupportedOnMongod>&) {
- uasserted(40247, "OP_QUERY not supported on views");
- }
-
- // Fill out the response buffer.
- int numResults = 0;
- OpQueryReplyBuilder reply;
- for (auto&& obj : batch) {
- obj.appendSelfToBufBuilder(reply.bufBuilderForResults());
- numResults++;
- }
-
- return DbResponse{reply.toQueryReply(0, // query result flags
- numResults,
- 0, // startingFrom
- cursorId)};
-}
-
// Maintains the state required to execute client commands, and provides the interface to construct
// a future-chain that runs the command against the database.
class ClientCommand final {
@@ -1392,144 +1273,4 @@ Future<DbResponse> Strategy::clientCommand(std::shared_ptr<RequestExecutionConte
return runner->run();
});
}
-
-DbResponse Strategy::getMore(OperationContext* opCtx, const NamespaceString& nss, DbMessage* dbm) {
- const int ntoreturn = dbm->pullInt();
- uassert(
- 34424, str::stream() << "Invalid ntoreturn for OP_GET_MORE: " << ntoreturn, ntoreturn >= 0);
- const long long cursorId = dbm->pullInt64();
-
- globalOpCounters.gotGetMore();
- globalOpCounters.gotGetMoreDeprecated();
-
- // TODO: Handle stale config exceptions here from coll being dropped or sharded during op for
- // now has same semantics as legacy request.
-
- auto statusGetDb = Grid::get(opCtx)->catalogCache()->getDatabase(opCtx, nss.db());
- if (statusGetDb == ErrorCodes::NamespaceNotFound) {
- return replyToQuery(ResultFlag_CursorNotFound, nullptr, 0, 0);
- }
- uassertStatusOK(statusGetDb);
-
- GetMoreCommandRequest getMoreCmd(cursorId, nss.coll().toString());
- getMoreCmd.setDbName(nss.db());
- if (ntoreturn) {
- getMoreCmd.setBatchSize(ntoreturn);
- }
-
- // Set the upconverted getMore as the CurOp command object.
- CurOp::get(opCtx)->setGenericOpRequestDetails(
- opCtx, nss, nullptr, getMoreCmd.toBSON({}), dbm->msg().operation());
-
- auto cursorResponse = ClusterFind::runGetMore(opCtx, getMoreCmd);
- if (cursorResponse == ErrorCodes::CursorNotFound) {
- return replyToQuery(ResultFlag_CursorNotFound, nullptr, 0, 0);
- }
- uassertStatusOK(cursorResponse.getStatus());
-
- // Build the response document.
- BufBuilder buffer(FindCommon::kInitReplyBufferSize);
-
- int numResults = 0;
- for (const auto& obj : cursorResponse.getValue().getBatch()) {
- buffer.appendBuf((void*)obj.objdata(), obj.objsize());
- ++numResults;
- }
-
- return replyToQuery(0,
- buffer.buf(),
- buffer.len(),
- numResults,
- cursorResponse.getValue().getNumReturnedSoFar().value_or(0),
- cursorResponse.getValue().getCursorId());
-}
-
-void Strategy::explainFind(OperationContext* opCtx,
- const BSONObj& findCommandObj,
- const FindCommandRequest& findCommand,
- ExplainOptions::Verbosity verbosity,
- const ReadPreferenceSetting& readPref,
- BSONObjBuilder* out) {
- const auto explainCmd = ClusterExplain::wrapAsExplain(findCommandObj, verbosity);
-
- long long millisElapsed;
- std::vector<AsyncRequestsSender::Response> shardResponses;
-
- for (int tries = 0;; ++tries) {
- bool canRetry = tries < 4; // Fifth try (i.e. try #4) is the last one.
-
- // We will time how long it takes to run the commands on the shards.
- Timer timer;
- try {
- 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>&) {
- Grid::get(opCtx)->catalogCache()->setOperationShouldBlockBehindCatalogCacheRefresh(
- opCtx, true);
-
- if (canRetry) {
- continue;
- }
- throw;
- } catch (const ExceptionForCat<ErrorCategory::NeedRetargettingError>& ex) {
- const auto staleNs = [&] {
- if (auto staleInfo = ex.extraInfo<StaleConfigInfo>()) {
- return staleInfo->getNss();
- }
- throw;
- }();
-
- if (auto staleInfo = ex.extraInfo<StaleConfigInfo>()) {
- Grid::get(opCtx)
- ->catalogCache()
- ->invalidateShardOrEntireCollectionEntryForShardedCollection(
- staleNs, staleInfo->getVersionWanted(), staleInfo->getShardId());
- } else {
- // If we don't have the stale config info and therefore don't know the shard's id,
- // we have to force all further targetting requests for the namespace to block on
- // a refresh.
- Grid::get(opCtx)->catalogCache()->invalidateCollectionEntry_LINEARIZABLE(staleNs);
- }
-
- if (canRetry) {
- continue;
- }
- throw;
- } catch (const ExceptionFor<ErrorCodes::StaleDbVersion>& ex) {
- // Mark database entry in cache as stale.
- Grid::get(opCtx)->catalogCache()->onStaleDatabaseVersion(ex->getDb(),
- ex->getVersionWanted());
- if (canRetry) {
- continue;
- }
- throw;
- } catch (const ExceptionForCat<ErrorCategory::SnapshotError>&) {
- // Simple retry on any type of snapshot error.
- if (canRetry) {
- continue;
- }
- throw;
- }
- }
-
- const char* mongosStageName =
- ClusterExplain::getStageNameForReadOp(shardResponses.size(), findCommandObj);
-
- uassertStatusOK(ClusterExplain::buildExplainResult(
- 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 284b562da52..03b9088f9d4 100644
--- a/src/mongo/s/commands/strategy.h
+++ b/src/mongo/s/commands/strategy.h
@@ -51,53 +51,12 @@ class FindCommandRequest;
class Strategy {
public:
/**
- * Handles a legacy-style opQuery request and sends the response back on success or throws on
- * error.
- *
- * Must not be called with legacy '.$cmd' commands.
- */
- static DbResponse queryOp(OperationContext* opCtx, const NamespaceString& nss, DbMessage* dbm);
-
- /**
- * Handles a legacy-style getMore request and sends the response back on success (or cursor not
- * found) or throws on error.
- */
- static DbResponse getMore(OperationContext* opCtx, const NamespaceString& nss, DbMessage* dbm);
-
- /**
- * Handles a legacy-style killCursors request. Doesn't send any response on success or throws on
- * error.
- */
- static void killCursors(OperationContext* opCtx, DbMessage* dbm);
-
- /**
- * Handles a legacy-style write operation request and updates the last error state on the client
- * with the result from the operation. Doesn't send any response back and does not throw on
- * errors.
- */
- static void writeOp(std::shared_ptr<RequestExecutionContext> rec);
-
- /**
* Executes a command from either OP_QUERY or OP_MSG wire protocols.
*
* Catches StaleConfigException errors and retries the command automatically after refreshing
* the metadata for the failing namespace.
*/
static Future<DbResponse> clientCommand(std::shared_ptr<RequestExecutionContext> rec);
-
- /**
- * Helper to run an explain of a find operation on the shards. Fills 'out' with the result of
- * the of the explain command on success. On failure, throws and does not modify 'out'.
- *
- * Used both if mongos receives an explain command and if it receives an OP_QUERY find with the
- * $explain modifier.
- */
- static void explainFind(OperationContext* opCtx,
- const BSONObj& findCommandObj,
- const FindCommandRequest& findCommand,
- ExplainOptions::Verbosity verbosity,
- const ReadPreferenceSetting& readPref,
- BSONObjBuilder* out);
};
} // namespace mongo
diff --git a/src/mongo/s/service_entry_point_mongos.cpp b/src/mongo/s/service_entry_point_mongos.cpp
index 6dd81be74e9..1e9b1664ae8 100644
--- a/src/mongo/s/service_entry_point_mongos.cpp
+++ b/src/mongo/s/service_entry_point_mongos.cpp
@@ -139,99 +139,24 @@ struct CommandOpRunner final : public HandleRequest::OpRunnerBase {
}
};
-// The base for operations that may throw exceptions, but should not cause the connection to close.
-struct OpRunner : public HandleRequest::OpRunnerBase {
- using HandleRequest::OpRunnerBase::OpRunnerBase;
- virtual DbResponse runOperation() = 0;
- Future<DbResponse> run() override;
-};
-
-Future<DbResponse> OpRunner::run() try {
- using namespace fmt::literals;
- const NamespaceString& nss = hr->nsString;
- const DbMessage& dbm = hr->rec->getDbMessage();
-
- if (dbm.messageShouldHaveNs()) {
- uassert(ErrorCodes::InvalidNamespace, "Invalid ns [{}]"_format(nss.ns()), nss.isValid());
-
- uassert(ErrorCodes::IllegalOperation,
- "Can't use 'local' database through mongos",
- nss.db() != NamespaceString::kLocalDb);
- }
-
- LOGV2_DEBUG(22867,
- 3,
- "Request::process begin ns: {namespace} msg id: {msgId} op: {operation}",
- "Starting operation",
- "namespace"_attr = nss,
- "msgId"_attr = hr->msgId,
- "operation"_attr = networkOpToString(hr->op));
-
- auto dbResponse = runOperation();
-
- LOGV2_DEBUG(22868,
- 3,
- "Request::process end ns: {namespace} msg id: {msgId} op: {operation}",
- "Done processing operation",
- "namespace"_attr = nss,
- "msgId"_attr = hr->msgId,
- "operation"_attr = networkOpToString(hr->op));
-
- return Future<DbResponse>::makeReady(std::move(dbResponse));
-} catch (const DBException& ex) {
- LOGV2_DEBUG(22869,
- 1,
- "Exception thrown while processing {operation} op for {namespace}: {error}",
- "Got an error while processing operation",
- "operation"_attr = networkOpToString(hr->op),
- "namespace"_attr = hr->nsString.ns(),
- "error"_attr = ex);
-
- DbResponse dbResponse;
- if (hr->op == dbQuery || hr->op == dbGetMore) {
- dbResponse = replyToQuery(buildErrReply(ex), ResultFlag_ErrSet);
- } else {
- // No Response.
- }
-
- // We *always* populate the last error for now
- auto opCtx = hr->rec->getOpCtx();
- LastError::get(opCtx->getClient()).setLastError(ex.code(), ex.what());
-
- CurOp::get(opCtx)->debug().errInfo = ex.toStatus();
-
- return Future<DbResponse>::makeReady(std::move(dbResponse));
-}
-
-struct QueryOpRunner final : public OpRunner {
- using OpRunner::OpRunner;
- DbResponse runOperation() override {
- // Commands are handled through CommandOpRunner and Strategy::clientCommand().
- invariant(!hr->nsString.isCommand());
- warnDeprecation(*hr->rec->getOpCtx()->getClient(), networkOpToString(hr->op));
- hr->rec->getOpCtx()->markKillOnClientDisconnect();
- return Strategy::queryOp(hr->rec->getOpCtx(), hr->nsString, &hr->rec->getDbMessage());
- }
-};
-
-struct GetMoreOpRunner final : public OpRunner {
- using OpRunner::OpRunner;
- DbResponse runOperation() override {
- warnDeprecation(*hr->rec->getOpCtx()->getClient(), networkOpToString(hr->op));
- return Strategy::getMore(hr->rec->getOpCtx(), hr->nsString, &hr->rec->getDbMessage());
- }
-};
-
Future<DbResponse> HandleRequest::handleRequest() {
switch (op) {
case dbQuery:
- if (!nsString.isCommand())
- return std::make_unique<QueryOpRunner>(shared_from_this())->run();
+ if (!nsString.isCommand()) {
+ globalOpCounters.gotQueryDeprecated();
+ warnDeprecation(*(rec->getOpCtx()->getClient()), networkOpToString(dbQuery));
+ return Future<DbResponse>::makeReady(
+ makeErrorResponseToDeprecatedOpQuery("OP_QUERY is no longer supported"));
+ }
// FALLTHROUGH: it's a query containing a command
case dbMsg:
return std::make_unique<CommandOpRunner>(shared_from_this())->run();
- case dbGetMore:
- return std::make_unique<GetMoreOpRunner>(shared_from_this())->run();
+ case dbGetMore: {
+ globalOpCounters.gotGetMoreDeprecated();
+ warnDeprecation(*(rec->getOpCtx()->getClient()), networkOpToString(dbGetMore));
+ return Future<DbResponse>::makeReady(
+ makeErrorResponseToDeprecatedOpQuery("OP_GET_MORE is no longer supported"));
+ }
case dbKillCursors:
globalOpCounters.gotKillCursorsDeprecated();
warnDeprecation(*(rec->getOpCtx()->getClient()), networkOpToString(op));