summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHugh Tong <hugh.tong@mongodb.com>2022-12-20 22:23:27 +0000
committerEvergreen Agent <no-reply@evergreen.mongodb.com>2022-12-20 23:08:56 +0000
commit20770047f9ac49ff8d344bcb10258a9010bcf74b (patch)
tree4a1957aac0fd5ff4c9163975b2f6edc56beaf4a1
parent76bf2602159dece2a833073924638aa3b0796205 (diff)
downloadmongo-20770047f9ac49ff8d344bcb10258a9010bcf74b.tar.gz
SERVER-71030 Modify CursorResponse to serialize/deserialize NamespaceString with tenantId
-rw-r--r--buildscripts/resmokeconfig/suites/native_tenant_data_isolation_with_security_token_jscore_passthrough.yml2
-rw-r--r--jstests/core/count.js2
-rw-r--r--jstests/core/expressions_matching_whole_array.js8
-rw-r--r--jstests/core/js3.js2
-rw-r--r--jstests/core/js_global_scope.js2
-rw-r--r--src/mongo/client/dbclient_cursor.cpp10
-rw-r--r--src/mongo/client/dbclient_cursor_test.cpp54
-rw-r--r--src/mongo/db/commands/count_cmd.cpp3
-rw-r--r--src/mongo/db/commands/current_op.cpp3
-rw-r--r--src/mongo/db/commands/distinct.cpp3
-rw-r--r--src/mongo/db/commands/user_management_commands.cpp3
-rw-r--r--src/mongo/db/pipeline/document_source_merge_cursors_test.cpp11
-rw-r--r--src/mongo/db/query/cursor_response.cpp7
-rw-r--r--src/mongo/db/query/cursor_response.h11
-rw-r--r--src/mongo/db/query/cursor_response_test.cpp91
-rw-r--r--src/mongo/db/query/view_response_formatter.cpp10
-rw-r--r--src/mongo/db/query/view_response_formatter.h6
-rw-r--r--src/mongo/db/query/view_response_formatter_test.cpp57
-rw-r--r--src/mongo/db/transaction/transaction_api.cpp12
-rw-r--r--src/mongo/s/commands/cluster_count_cmd.h2
-rw-r--r--src/mongo/s/commands/cluster_distinct_cmd.cpp2
-rw-r--r--src/mongo/s/query/async_results_merger_params.idl1
22 files changed, 233 insertions, 69 deletions
diff --git a/buildscripts/resmokeconfig/suites/native_tenant_data_isolation_with_security_token_jscore_passthrough.yml b/buildscripts/resmokeconfig/suites/native_tenant_data_isolation_with_security_token_jscore_passthrough.yml
index 17d1deaad5a..b8f9616c951 100644
--- a/buildscripts/resmokeconfig/suites/native_tenant_data_isolation_with_security_token_jscore_passthrough.yml
+++ b/buildscripts/resmokeconfig/suites/native_tenant_data_isolation_with_security_token_jscore_passthrough.yml
@@ -16,6 +16,8 @@ selector:
# columnstore indexes are under development and cannot be used without enabling the feature flag
- featureFlagColumnstoreIndexes
- featureFlagSbeFull
+ # Server side javascript not allowed in Serverless.
+ - requires_scripting
exclude_files:
# server-side javascript is not supported in serverless mode.
- jstests/core/system_js_drop.js
diff --git a/jstests/core/count.js b/jstests/core/count.js
index f0ea858eca0..76b858c17b9 100644
--- a/jstests/core/count.js
+++ b/jstests/core/count.js
@@ -4,6 +4,8 @@
* @tags: [
* requires_fastcount,
* assumes_against_mongod_not_mongos,
+ * # Reliance on the $where operator
+ * requires_scripting,
* ]
*/
diff --git a/jstests/core/expressions_matching_whole_array.js b/jstests/core/expressions_matching_whole_array.js
index 23faf87fe31..a2727e7b9d6 100644
--- a/jstests/core/expressions_matching_whole_array.js
+++ b/jstests/core/expressions_matching_whole_array.js
@@ -1,4 +1,10 @@
// Test MQL expressions which can match against the whole array stored in the field.
+//
+// @tags: [
+// # Uses $where operator
+// requires_scripting
+// ]
+
(function() {
"use strict";
@@ -53,4 +59,4 @@ assertMatches({a: []}, {a: {$gt: MinKey}});
assertMatches({a: [1, 2, 3]}, {a: {$lt: MaxKey}});
assertMatches({a: []}, {a: {$lt: MaxKey}});
-}()); \ No newline at end of file
+}());
diff --git a/jstests/core/js3.js b/jstests/core/js3.js
index f13ce41f2ac..c9239bb4176 100644
--- a/jstests/core/js3.js
+++ b/jstests/core/js3.js
@@ -3,6 +3,8 @@
// requires_fastcount,
// requires_javascript,
// requires_non_retryable_commands,
+// # Uses $where operator
+// requires_scripting,
// ]
t = db.jstests_js3;
diff --git a/jstests/core/js_global_scope.js b/jstests/core/js_global_scope.js
index c5fa98963f4..d78f435acd9 100644
--- a/jstests/core/js_global_scope.js
+++ b/jstests/core/js_global_scope.js
@@ -8,6 +8,8 @@
// # variables in multiversion setup, so we just disable the test from multiversion suites
// # instead.
// multiversion_incompatible,
+// # Uses $where operator
+// requires_scripting,
// ]
(function() {
"use strict";
diff --git a/src/mongo/client/dbclient_cursor.cpp b/src/mongo/client/dbclient_cursor.cpp
index a19b8973db4..9ecc2ce069e 100644
--- a/src/mongo/client/dbclient_cursor.cpp
+++ b/src/mongo/client/dbclient_cursor.cpp
@@ -215,18 +215,14 @@ void DBClientCursor::dataReceived(const Message& reply, bool& retry, string& hos
const auto replyObj = commandDataReceived(reply);
_cursorId = 0; // Don't try to kill cursor if we get back an error.
- // TODO SERVER-70067: pass in the tenant id to parseFromBSON.
- auto cr = uassertStatusOK(CursorResponse::parseFromBSON(replyObj));
+
+ auto cr = uassertStatusOK(CursorResponse::parseFromBSON(replyObj, nullptr, _ns.tenantId()));
_cursorId = cr.getCursorId();
uassert(50935,
"Received a getMore response with a cursor id of 0 and the moreToCome flag set.",
!(_connectionHasPendingReplies && _cursorId == 0));
- // TODO SERVER-70067: Get nss from the parsed cursor directly as it already has the tenant
- // information.
- _ns = NamespaceString(
- _ns.tenantId(), // always reuse the request's tenant in case no tenant in the response.
- cr.getNSS().toString()); // find command can change the ns to use for getMores.
+ _ns = cr.getNSS(); // find command can change the ns to use for getMores.
// Store the resume token, if we got one.
_postBatchResumeToken = cr.getPostBatchResumeToken();
_batch.objs = cr.releaseBatch();
diff --git a/src/mongo/client/dbclient_cursor_test.cpp b/src/mongo/client/dbclient_cursor_test.cpp
index a7c1f9c5666..266307d6b70 100644
--- a/src/mongo/client/dbclient_cursor_test.cpp
+++ b/src/mongo/client/dbclient_cursor_test.cpp
@@ -31,6 +31,7 @@
#include "mongo/client/dbclient_connection.h"
#include "mongo/client/dbclient_cursor.h"
#include "mongo/db/query/cursor_response.h"
+#include "mongo/idl/server_parameter_test_util.h"
#include "mongo/logv2/log.h"
#include "mongo/unittest/unittest.h"
#include "mongo/util/assert_util.h"
@@ -193,34 +194,43 @@ TEST_F(DBClientCursorTest, DBClientCursorGetMoreWithTenant) {
const TenantId tenantId(OID::gen());
const NamespaceString nss(tenantId, "test", "coll");
FindCommandRequest findCmd{nss};
- DBClientCursor cursor(&conn, findCmd, ReadPreferenceSetting{}, false);
- cursor.setBatchSize(2);
- // Set up mock 'find' response.
- const long long cursorId = 42;
- Message findResponseMsg = mockFindResponse(nss, cursorId, {docObj(1), docObj(2)});
- conn.setCallResponse(findResponseMsg);
+ RAIIServerParameterControllerForTest multitenancyController("multitenancySupport", true);
- // Trigger a find command.
- ASSERT(cursor.init());
+ for (bool flagStatus : {false, true}) {
+ RAIIServerParameterControllerForTest featureFlagController("featureFlagRequireTenantID",
+ flagStatus);
- // First batch from the initial find command.
- ASSERT_BSONOBJ_EQ(docObj(1), cursor.next());
- ASSERT_BSONOBJ_EQ(docObj(2), cursor.next());
- ASSERT_FALSE(cursor.moreInCurrentBatch());
+ DBClientCursor cursor(&conn, findCmd, ReadPreferenceSetting{}, false);
+ cursor.setBatchSize(2);
+ ASSERT_EQ(cursor.getNamespaceString(), nss);
- // Set a terminal getMore response with cursorId 0.
- auto getMoreResponseMsg = mockGetMoreResponse(nss, 0, {docObj(3), docObj(4)});
- conn.setCallResponse(getMoreResponseMsg);
+ // Set up mock 'find' response.
+ const long long cursorId = 42;
+ Message findResponseMsg = mockFindResponse(nss, cursorId, {docObj(1), docObj(2)});
+ conn.setCallResponse(findResponseMsg);
- // Trigger a subsequent getMore command.
- ASSERT_TRUE(cursor.more());
+ // Trigger a find command.
+ ASSERT(cursor.init());
- // Second batch from the getMore command.
- ASSERT_BSONOBJ_EQ(docObj(3), cursor.next());
- ASSERT_BSONOBJ_EQ(docObj(4), cursor.next());
- ASSERT_FALSE(cursor.moreInCurrentBatch());
- ASSERT_TRUE(cursor.isDead());
+ // First batch from the initial find command.
+ ASSERT_BSONOBJ_EQ(docObj(1), cursor.next());
+ ASSERT_BSONOBJ_EQ(docObj(2), cursor.next());
+ ASSERT_FALSE(cursor.moreInCurrentBatch());
+
+ // Set a terminal getMore response with cursorId 0.
+ auto getMoreResponseMsg = mockGetMoreResponse(nss, 0, {docObj(3), docObj(4)});
+ conn.setCallResponse(getMoreResponseMsg);
+
+ // Trigger a subsequent getMore command.
+ ASSERT_TRUE(cursor.more());
+
+ // Second batch from the getMore command.
+ ASSERT_BSONOBJ_EQ(docObj(3), cursor.next());
+ ASSERT_BSONOBJ_EQ(docObj(4), cursor.next());
+ ASSERT_FALSE(cursor.moreInCurrentBatch());
+ ASSERT_TRUE(cursor.isDead());
+ }
}
TEST_F(DBClientCursorTest, DBClientCursorHandlesOpMsgExhaustCorrectly) {
diff --git a/src/mongo/db/commands/count_cmd.cpp b/src/mongo/db/commands/count_cmd.cpp
index 9b33d9b0a1a..86ceb40b506 100644
--- a/src/mongo/db/commands/count_cmd.cpp
+++ b/src/mongo/db/commands/count_cmd.cpp
@@ -279,7 +279,8 @@ public:
BSONObj aggResult = CommandHelpers::runCommandDirectly(opCtx, aggRequest);
- uassertStatusOK(ViewResponseFormatter(aggResult).appendAsCountResponse(&result));
+ uassertStatusOK(
+ ViewResponseFormatter(aggResult).appendAsCountResponse(&result, dbName.tenantId()));
return true;
}
diff --git a/src/mongo/db/commands/current_op.cpp b/src/mongo/db/commands/current_op.cpp
index 8c8c57d885e..8ba08e6c37b 100644
--- a/src/mongo/db/commands/current_op.cpp
+++ b/src/mongo/db/commands/current_op.cpp
@@ -96,7 +96,8 @@ public:
CommandHelpers::appendSimpleCommandStatus(bodyBuilder, true);
bodyBuilder.doneFast();
- return CursorResponse::parseFromBSON(replyBuilder.releaseBody());
+ return CursorResponse::parseFromBSON(
+ replyBuilder.releaseBody(), nullptr, request.getNamespace().tenantId());
}
virtual void appendToResponse(BSONObjBuilder* result) const final {
diff --git a/src/mongo/db/commands/distinct.cpp b/src/mongo/db/commands/distinct.cpp
index f61ab426299..59b2305203c 100644
--- a/src/mongo/db/commands/distinct.cpp
+++ b/src/mongo/db/commands/distinct.cpp
@@ -273,7 +273,8 @@ public:
dbName, vts, std::move(viewAggregation.getValue()));
BSONObj aggResult = CommandHelpers::runCommandDirectly(opCtx, aggRequest);
- uassertStatusOK(ViewResponseFormatter(aggResult).appendAsDistinctResponse(&result));
+ uassertStatusOK(ViewResponseFormatter(aggResult).appendAsDistinctResponse(
+ &result, dbName.tenantId()));
return true;
}
diff --git a/src/mongo/db/commands/user_management_commands.cpp b/src/mongo/db/commands/user_management_commands.cpp
index 328b214ae5e..3cbff434678 100644
--- a/src/mongo/db/commands/user_management_commands.cpp
+++ b/src/mongo/db/commands/user_management_commands.cpp
@@ -1467,7 +1467,8 @@ UsersInfoReply CmdUMCTyped<UsersInfoCommand, UMCInfoParams>::Invocation::typedRu
auto bodyBuilder = replyBuilder.getBodyBuilder();
CommandHelpers::appendSimpleCommandStatus(bodyBuilder, true);
bodyBuilder.doneFast();
- auto response = CursorResponse::parseFromBSONThrowing(replyBuilder.releaseBody());
+ auto response =
+ CursorResponse::parseFromBSONThrowing(dbname.tenantId(), replyBuilder.releaseBody());
DBClientCursor cursor(&client,
response.getNSS(),
response.getCursorId(),
diff --git a/src/mongo/db/pipeline/document_source_merge_cursors_test.cpp b/src/mongo/db/pipeline/document_source_merge_cursors_test.cpp
index ac839a02a96..04bf762d340 100644
--- a/src/mongo/db/pipeline/document_source_merge_cursors_test.cpp
+++ b/src/mongo/db/pipeline/document_source_merge_cursors_test.cpp
@@ -47,6 +47,7 @@
#include "mongo/db/query/getmore_command_gen.h"
#include "mongo/db/query/query_knobs_gen.h"
#include "mongo/db/query/query_request_helper.h"
+#include "mongo/db/server_feature_flags_gen.h"
#include "mongo/executor/network_interface_mock.h"
#include "mongo/executor/task_executor.h"
#include "mongo/executor/thread_pool_task_executor_test_fixture.h"
@@ -177,8 +178,8 @@ RemoteCursor makeRemoteCursor(ShardId shardId, HostAndPort host, CursorResponse
}
void checkSerializedAsyncResultsMergerParams(const AsyncResultsMergerParams& params,
- const AsyncResultsMergerParams& serializedParam) {
- static const NamespaceString testNss = NamespaceString(boost::none, kMergeCursorNsStr);
+ const AsyncResultsMergerParams& serializedParam,
+ const NamespaceString& testNss) {
ASSERT_TRUE(params.getSort());
ASSERT_BSONOBJ_EQ(*params.getSort(), *serializedParam.getSort());
ASSERT_EQ(params.getCompareWholeSortKey(), serializedParam.getCompareWholeSortKey());
@@ -221,7 +222,7 @@ TEST_F(DocumentSourceMergeCursorsTest, ShouldBeAbleToParseSerializedARMParams) {
ASSERT(newSpec["$mergeCursors"].type() == BSONType::Object);
auto newParams = AsyncResultsMergerParams::parse(IDLParserContext("$mergeCursors test"),
newSpec["$mergeCursors"].Obj());
- checkSerializedAsyncResultsMergerParams(params, newParams);
+ checkSerializedAsyncResultsMergerParams(params, newParams, getTenantIdNss());
// Test that the $mergeCursors stage will accept the serialized format of
// AsyncResultsMergerParams.
@@ -468,7 +469,7 @@ TEST_F(DocumentSourceMergeCursorsMultiTenancyTest, ShouldBeAbleToParseSerialized
// Check that the namespace contains the tenantid prefix.
ASSERT_EQ(newParams.toBSON()["nss"].str(), expectedTenantNsStr);
ASSERT_EQ(newParams.getNss(), getTenantIdNss());
- checkSerializedAsyncResultsMergerParams(params, newParams);
+ checkSerializedAsyncResultsMergerParams(params, newParams, getTenantIdNss());
// Test that the $mergeCursors stage will accept the serialized format of
// AsyncResultsMergerParams.
@@ -522,7 +523,7 @@ TEST_F(DocumentSourceMergeCursorsMultiTenancyAndFeatureFlagTest,
// Check that the namespace doesn't contain the tenantid prefix.
ASSERT_EQ(newParams.toBSON()["nss"].str(), kMergeCursorNsStr);
ASSERT_EQ(newParams.getNss(), getTenantIdNss());
- checkSerializedAsyncResultsMergerParams(params, newParams);
+ checkSerializedAsyncResultsMergerParams(params, newParams, getTenantIdNss());
// Test that the $mergeCursors stage will accept the serialized format of
// AsyncResultsMergerParams.
diff --git a/src/mongo/db/query/cursor_response.cpp b/src/mongo/db/query/cursor_response.cpp
index 08e51ecba46..d53a24c7c5f 100644
--- a/src/mongo/db/query/cursor_response.cpp
+++ b/src/mongo/db/query/cursor_response.cpp
@@ -178,7 +178,8 @@ std::vector<StatusWith<CursorResponse>> CursorResponse::parseFromBSONMany(
}
StatusWith<CursorResponse> CursorResponse::parseFromBSON(const BSONObj& cmdResponse,
- const BSONObj* ownedObj) {
+ const BSONObj* ownedObj,
+ boost::optional<TenantId> tenantId) {
Status cmdStatus = getStatusFromCommandResult(cmdResponse);
if (!cmdStatus.isOK()) {
return cmdStatus;
@@ -306,7 +307,7 @@ StatusWith<CursorResponse> CursorResponse::parseFromBSON(const BSONObj& cmdRespo
<< writeConcernError.type()};
}
- return {{NamespaceString(fullns),
+ return {{NamespaceStringUtil::deserialize(tenantId, fullns),
cursorId,
std::move(batch),
atClusterTimeElem ? atClusterTimeElem.timestamp() : boost::optional<Timestamp>{},
@@ -324,7 +325,7 @@ void CursorResponse::addToBSON(CursorResponse::ResponseType responseType,
BSONObjBuilder cursorBuilder(builder->subobjStart(kCursorField));
cursorBuilder.append(kIdField, _cursorId);
- cursorBuilder.append(kNsField, _nss.ns());
+ cursorBuilder.append(kNsField, NamespaceStringUtil::serialize(_nss));
const char* batchFieldName =
(responseType == ResponseType::InitialResponse) ? kBatchFieldInitial : kBatchField;
diff --git a/src/mongo/db/query/cursor_response.h b/src/mongo/db/query/cursor_response.h
index f8d69b72dc6..7b33b71e1d5 100644
--- a/src/mongo/db/query/cursor_response.h
+++ b/src/mongo/db/query/cursor_response.h
@@ -186,14 +186,17 @@ public:
* Constructs a CursorResponse from the command BSON response. If 'cmdResponse' is not owned,
* the second argument should be the object that owns the response.
*/
- static StatusWith<CursorResponse> parseFromBSON(const BSONObj& cmdResponse,
- const BSONObj* ownedObj = nullptr);
+ static StatusWith<CursorResponse> parseFromBSON(
+ const BSONObj& cmdResponse,
+ const BSONObj* ownedObj = nullptr,
+ boost::optional<TenantId> tenantId = boost::none);
/**
* A throwing version of 'parseFromBSON'.
*/
- static CursorResponse parseFromBSONThrowing(const BSONObj& cmdResponse) {
- return uassertStatusOK(parseFromBSON(cmdResponse));
+ static CursorResponse parseFromBSONThrowing(boost::optional<TenantId> tenantId,
+ const BSONObj& cmdResponse) {
+ return uassertStatusOK(parseFromBSON(cmdResponse, nullptr, tenantId));
}
/**
diff --git a/src/mongo/db/query/cursor_response_test.cpp b/src/mongo/db/query/cursor_response_test.cpp
index abd6d38bbd9..23a513a177a 100644
--- a/src/mongo/db/query/cursor_response_test.cpp
+++ b/src/mongo/db/query/cursor_response_test.cpp
@@ -34,6 +34,7 @@
#include "mongo/rpc/op_msg_rpc_impls.h"
#include "mongo/db/pipeline/resume_token.h"
+#include "mongo/idl/server_parameter_test_util.h"
#include "mongo/unittest/unittest.h"
namespace mongo {
@@ -306,6 +307,70 @@ TEST(CursorResponseTest, roundTripThroughCursorResponseBuilderWithPartialResults
ASSERT(!cursorBuilderIt.more());
}
+TEST(CursorResponseTest,
+ roundTripThroughCursorResponseBuilderWithPartialResultsReturnedWithTenantId) {
+ CursorResponseBuilder::Options options;
+ options.isInitialResponse = true;
+ TenantId tid(OID::gen());
+ NamespaceString nss(tid, "db.coll");
+
+ RAIIServerParameterControllerForTest multitenancyController("multitenancySupport", true);
+
+ for (bool flagStatus : {false, true}) {
+ RAIIServerParameterControllerForTest featureFlagController("featureFlagRequireTenantID",
+ flagStatus);
+
+ rpc::OpMsgReplyBuilder builder;
+ BSONObj okStatus = BSON("ok" << 1);
+ BSONObj testDoc = BSON("_id" << 1);
+ BSONObj expectedBody =
+ BSON("cursor" << BSON("firstBatch" << BSON_ARRAY(testDoc) << "partialResultsReturned"
+ << true << "id" << CursorId(123) << "ns"
+ << NamespaceStringUtil::serialize(nss)));
+
+ // Use CursorResponseBuilder to serialize the cursor response to OpMsgReplyBuilder.
+ CursorResponseBuilder crb(&builder, options);
+ crb.append(testDoc);
+ crb.setPartialResultsReturned(true);
+ crb.done(CursorId(123), nss);
+
+ // Confirm that the resulting BSONObj response matches the expected body.
+ auto msg = builder.done();
+ auto opMsg = OpMsg::parse(msg);
+ ASSERT_BSONOBJ_EQ(expectedBody, opMsg.body);
+
+ // Append {"ok": 1} to the opMsg body so that it can be parsed by CursorResponse.
+ auto swCursorResponse =
+ CursorResponse::parseFromBSON(opMsg.body.addField(okStatus["ok"]), nullptr, tid);
+ ASSERT_OK(swCursorResponse.getStatus());
+
+ // Confirm the CursorReponse parsed from CursorResponseBuilder output has the correct
+ // content.
+ CursorResponse response = std::move(swCursorResponse.getValue());
+ ASSERT_EQ(response.getCursorId(), CursorId(123));
+
+ ASSERT_EQ(response.getNSS(), nss);
+ ASSERT_EQ(response.getBatch().size(), 1U);
+ ASSERT_BSONOBJ_EQ(response.getBatch()[0], testDoc);
+ ASSERT_EQ(response.getPartialResultsReturned(), true);
+
+ // Re-serialize a BSONObj response from the CursorResponse.
+ auto cursorResBSON = response.toBSONAsInitialResponse();
+
+ // Confirm that the BSON serialized by the CursorResponse is the same as that serialized by
+ // the CursorResponseBuilder. Field ordering differs between the two, so compare
+ // per-element.
+ BSONObjIteratorSorted cursorResIt(cursorResBSON["cursor"].Obj());
+ BSONObjIteratorSorted cursorBuilderIt(opMsg.body["cursor"].Obj());
+ while (cursorResIt.more()) {
+ ASSERT(cursorBuilderIt.more());
+ ASSERT_EQ(cursorResIt.next().woCompare(cursorBuilderIt.next()), 0);
+ }
+ ASSERT(!cursorBuilderIt.more());
+ }
+}
+
+
TEST(CursorResponseTest, parseFromBSONHandleErrorResponse) {
StatusWith<CursorResponse> result =
CursorResponse::parseFromBSON(BSON("ok" << 0 << "code" << 123 << "errmsg"
@@ -392,6 +457,32 @@ TEST(CursorResponseTest, addToBSONSubsequentResponse) {
ASSERT_BSONOBJ_EQ(responseObj, expectedResponse);
}
+TEST(CursorResponseTest, addToBSONInitialResponseWithTenantId) {
+ TenantId tid(OID::gen());
+ NamespaceString nss(tid, "testdb.testcoll");
+
+ RAIIServerParameterControllerForTest multitenancyController("multitenancySupport", true);
+
+ for (bool flagStatus : {false, true}) {
+ RAIIServerParameterControllerForTest featureFlagController("featureFlagRequireTenantID",
+ flagStatus);
+
+ std::vector<BSONObj> batch = {BSON("_id" << 1), BSON("_id" << 2)};
+ CursorResponse response(nss, CursorId(123), batch);
+
+ BSONObjBuilder builder;
+ response.addToBSON(CursorResponse::ResponseType::InitialResponse, &builder);
+ BSONObj responseObj = builder.obj();
+
+ BSONObj expectedResponse =
+ BSON("cursor" << BSON("id" << CursorId(123) << "ns"
+ << NamespaceStringUtil::serialize(nss) << "firstBatch"
+ << BSON_ARRAY(BSON("_id" << 1) << BSON("_id" << 2)))
+ << "ok" << 1.0);
+ ASSERT_BSONOBJ_EQ(responseObj, expectedResponse);
+ }
+}
+
TEST(CursorResponseTest, serializePostBatchResumeToken) {
std::vector<BSONObj> batch = {BSON("_id" << 1), BSON("_id" << 2)};
auto postBatchResumeToken =
diff --git a/src/mongo/db/query/view_response_formatter.cpp b/src/mongo/db/query/view_response_formatter.cpp
index 76c8457c8b5..d169aa61c74 100644
--- a/src/mongo/db/query/view_response_formatter.cpp
+++ b/src/mongo/db/query/view_response_formatter.cpp
@@ -45,8 +45,9 @@ const char ViewResponseFormatter::kOkField[] = "ok";
ViewResponseFormatter::ViewResponseFormatter(BSONObj aggregationResponse)
: _response(std::move(aggregationResponse)) {}
-Status ViewResponseFormatter::appendAsCountResponse(BSONObjBuilder* resultBuilder) {
- auto cursorResponse = CursorResponse::parseFromBSON(_response);
+Status ViewResponseFormatter::appendAsCountResponse(BSONObjBuilder* resultBuilder,
+ boost::optional<TenantId> tenantId) {
+ auto cursorResponse = CursorResponse::parseFromBSON(_response, nullptr, tenantId);
if (!cursorResponse.isOK())
return cursorResponse.getStatus();
@@ -63,8 +64,9 @@ Status ViewResponseFormatter::appendAsCountResponse(BSONObjBuilder* resultBuilde
return Status::OK();
}
-Status ViewResponseFormatter::appendAsDistinctResponse(BSONObjBuilder* resultBuilder) {
- auto cursorResponse = CursorResponse::parseFromBSON(_response);
+Status ViewResponseFormatter::appendAsDistinctResponse(BSONObjBuilder* resultBuilder,
+ boost::optional<TenantId> tenantId) {
+ auto cursorResponse = CursorResponse::parseFromBSON(_response, nullptr, tenantId);
if (!cursorResponse.isOK())
return cursorResponse.getStatus();
diff --git a/src/mongo/db/query/view_response_formatter.h b/src/mongo/db/query/view_response_formatter.h
index 20473e3bbc9..5146efd5229 100644
--- a/src/mongo/db/query/view_response_formatter.h
+++ b/src/mongo/db/query/view_response_formatter.h
@@ -30,6 +30,7 @@
#pragma once
#include "mongo/bson/bsonobj.h"
+#include "mongo/db/tenant_id.h"
namespace mongo {
class BSONObjBuilder;
@@ -53,7 +54,7 @@ public:
* If '_response' is not a valid cursor-based response from the aggregation command, a non-OK
* status is returned and 'resultBuilder' will not be modified.
*/
- Status appendAsCountResponse(BSONObjBuilder* resultBuilder);
+ Status appendAsCountResponse(BSONObjBuilder* resultBuilder, boost::optional<TenantId> tenantId);
/**
* Appends fields to 'resultBuilder' as if '_response' were a response from the distinct
@@ -62,7 +63,8 @@ public:
* If '_response' is not a valid cursor-based response from the aggregation command, a non-OK
* status is returned and 'resultBuilder' will not be modified.
*/
- Status appendAsDistinctResponse(BSONObjBuilder* resultBuilder);
+ Status appendAsDistinctResponse(BSONObjBuilder* resultBuilder,
+ boost::optional<TenantId> tenantId);
private:
BSONObj _response;
diff --git a/src/mongo/db/query/view_response_formatter_test.cpp b/src/mongo/db/query/view_response_formatter_test.cpp
index dc86c5c9fc7..274bcda50f1 100644
--- a/src/mongo/db/query/view_response_formatter_test.cpp
+++ b/src/mongo/db/query/view_response_formatter_test.cpp
@@ -35,6 +35,7 @@
#include "mongo/db/namespace_string.h"
#include "mongo/db/query/cursor_response.h"
#include "mongo/db/query/view_response_formatter.h"
+#include "mongo/idl/server_parameter_test_util.h"
#include "mongo/unittest/unittest.h"
namespace mongo {
@@ -47,7 +48,7 @@ TEST(ViewResponseFormatter, FormatInitialCountResponseSuccessfully) {
CursorResponse cr(testNss, testCursor, {BSON("count" << 7)});
ViewResponseFormatter formatter(cr.toBSON(CursorResponse::ResponseType::InitialResponse));
BSONObjBuilder builder;
- ASSERT_OK(formatter.appendAsCountResponse(&builder));
+ ASSERT_OK(formatter.appendAsCountResponse(&builder, boost::none));
ASSERT_BSONOBJ_EQ(fromjson("{'n': 7, ok: 1}"), builder.obj());
}
@@ -55,15 +56,33 @@ TEST(ViewResponseFormatter, FormatSubsequentCountResponseSuccessfully) {
CursorResponse cr(testNss, testCursor, {BSON("count" << 7)});
ViewResponseFormatter formatter(cr.toBSON(CursorResponse::ResponseType::SubsequentResponse));
BSONObjBuilder builder;
- ASSERT_OK(formatter.appendAsCountResponse(&builder));
+ ASSERT_OK(formatter.appendAsCountResponse(&builder, boost::none));
ASSERT_BSONOBJ_EQ(fromjson("{'n': 7, ok: 1}"), builder.obj());
}
+TEST(ViewResponseFormatter, FormatInitialCountResponseWithTenantIdSuccessfully) {
+ const TenantId tenantId(OID::gen());
+ const NamespaceString nss(tenantId, testNss.toString());
+
+ RAIIServerParameterControllerForTest multitenancyController("multitenancySupport", true);
+
+ for (bool flagStatus : {false, true}) {
+ RAIIServerParameterControllerForTest featureFlagController("featureFlagRequireTenantID",
+ flagStatus);
+
+ CursorResponse cr(nss, testCursor, {BSON("count" << 7)});
+ ViewResponseFormatter formatter(cr.toBSON(CursorResponse::ResponseType::InitialResponse));
+ BSONObjBuilder builder;
+ ASSERT_OK(formatter.appendAsCountResponse(&builder, tenantId));
+ ASSERT_BSONOBJ_EQ(fromjson("{'n': 7, ok: 1}"), builder.obj());
+ }
+}
+
TEST(ViewResponseFormatter, FormatEmptyInitialCountResponseSuccessfully) {
CursorResponse cr(testNss, testCursor, {});
ViewResponseFormatter formatter(cr.toBSON(CursorResponse::ResponseType::InitialResponse));
BSONObjBuilder builder;
- ASSERT_OK(formatter.appendAsCountResponse(&builder));
+ ASSERT_OK(formatter.appendAsCountResponse(&builder, boost::none));
ASSERT_BSONOBJ_EQ(fromjson("{'n': 0, ok: 1}"), builder.obj());
}
@@ -71,14 +90,14 @@ TEST(ViewResponseFormatter, FormatEmptySubsequentCountResponseSuccessfully) {
CursorResponse cr(testNss, testCursor, {});
ViewResponseFormatter formatter(cr.toBSON(CursorResponse::ResponseType::SubsequentResponse));
BSONObjBuilder builder;
- ASSERT_OK(formatter.appendAsCountResponse(&builder));
+ ASSERT_OK(formatter.appendAsCountResponse(&builder, boost::none));
ASSERT_BSONOBJ_EQ(fromjson("{'n': 0, ok: 1}"), builder.obj());
}
TEST(ViewResponseFormatter, FormatFailedCountResponseFails) {
ViewResponseFormatter formatter(fromjson("{ok: 0, errmsg: 'bad value', code: 2}"));
BSONObjBuilder builder;
- ASSERT_NOT_OK(formatter.appendAsCountResponse(&builder));
+ ASSERT_NOT_OK(formatter.appendAsCountResponse(&builder, boost::none));
ASSERT_BSONOBJ_EQ(builder.obj(), BSONObj());
}
@@ -86,7 +105,7 @@ TEST(ViewResponseFormatter, FormatInitialDistinctResponseSuccessfully) {
CursorResponse cr(testNss, testCursor, {fromjson("{_id: null, distinct: [5, 9]}")});
ViewResponseFormatter formatter(cr.toBSON(CursorResponse::ResponseType::InitialResponse));
BSONObjBuilder builder;
- ASSERT_OK(formatter.appendAsDistinctResponse(&builder));
+ ASSERT_OK(formatter.appendAsDistinctResponse(&builder, boost::none));
ASSERT_BSONOBJ_EQ(fromjson("{values: [5, 9], ok: 1}"), builder.obj());
}
@@ -94,15 +113,33 @@ TEST(ViewResponseFormatter, FormatSubsequentDistinctResponseSuccessfully) {
CursorResponse cr(testNss, testCursor, {fromjson("{_id: null, distinct: [5, 9]}")});
ViewResponseFormatter formatter(cr.toBSON(CursorResponse::ResponseType::SubsequentResponse));
BSONObjBuilder builder;
- ASSERT_OK(formatter.appendAsDistinctResponse(&builder));
+ ASSERT_OK(formatter.appendAsDistinctResponse(&builder, boost::none));
ASSERT_BSONOBJ_EQ(fromjson("{values: [5, 9], ok: 1}"), builder.obj());
}
+TEST(ViewResponseFormatter, FormatInitialDistinctResponseWithTenantIdSuccessfully) {
+ const TenantId tenantId(OID::gen());
+ const NamespaceString nss(tenantId, testNss.toString());
+
+ RAIIServerParameterControllerForTest multitenancyController("multitenancySupport", true);
+
+ for (bool flagStatus : {false, true}) {
+ RAIIServerParameterControllerForTest featureFlagController("featureFlagRequireTenantID",
+ flagStatus);
+
+ CursorResponse cr(nss, testCursor, {fromjson("{_id: null, distinct: [5, 9]}")});
+ ViewResponseFormatter formatter(cr.toBSON(CursorResponse::ResponseType::InitialResponse));
+ BSONObjBuilder builder;
+ ASSERT_OK(formatter.appendAsDistinctResponse(&builder, tenantId));
+ ASSERT_BSONOBJ_EQ(fromjson("{values: [5, 9], ok: 1}"), builder.obj());
+ }
+}
+
TEST(ViewResponseFormatter, FormatEmptyDistinctValuesSuccessfully) {
CursorResponse cr(testNss, testCursor, {fromjson("{_id: null, distinct: []}")});
ViewResponseFormatter formatter(cr.toBSON(CursorResponse::ResponseType::InitialResponse));
BSONObjBuilder builder;
- ASSERT_OK(formatter.appendAsDistinctResponse(&builder));
+ ASSERT_OK(formatter.appendAsDistinctResponse(&builder, boost::none));
ASSERT_BSONOBJ_EQ(fromjson("{values: [], ok: 1}"), builder.obj());
}
@@ -110,14 +147,14 @@ TEST(ViewResponseFormatter, FormatEmptyDistinctBatchSuccessfully) {
CursorResponse cr(testNss, testCursor, {});
ViewResponseFormatter formatter(cr.toBSON(CursorResponse::ResponseType::InitialResponse));
BSONObjBuilder builder;
- ASSERT_OK(formatter.appendAsDistinctResponse(&builder));
+ ASSERT_OK(formatter.appendAsDistinctResponse(&builder, boost::none));
ASSERT_BSONOBJ_EQ(fromjson("{values: [], ok: 1}"), builder.obj());
}
TEST(ViewResponseFormatter, FormatFailedDistinctResponseFails) {
ViewResponseFormatter formatter(fromjson("{ok: 0, errmsg: 'bad value', code: 2}"));
BSONObjBuilder builder;
- ASSERT_NOT_OK(formatter.appendAsDistinctResponse(&builder));
+ ASSERT_NOT_OK(formatter.appendAsDistinctResponse(&builder, boost::none));
ASSERT_BSONOBJ_EQ(builder.obj(), BSONObj());
}
diff --git a/src/mongo/db/transaction/transaction_api.cpp b/src/mongo/db/transaction/transaction_api.cpp
index c778d81f97c..9dd92deb5d3 100644
--- a/src/mongo/db/transaction/transaction_api.cpp
+++ b/src/mongo/db/transaction/transaction_api.cpp
@@ -425,11 +425,13 @@ SemiFuture<std::vector<BSONObj>> SEPTransactionClient::exhaustiveFind(
const FindCommandRequest& cmd) const {
return runCommand(cmd.getDbName().db(), cmd.toBSON({}))
.thenRunOn(_executor)
- .then([this, batchSize = cmd.getBatchSize()](BSONObj reply) {
+ .then([this, batchSize = cmd.getBatchSize(), tenantId = cmd.getDbName().tenantId()](
+ BSONObj reply) {
auto cursorResponse = std::make_shared<CursorResponse>(
- uassertStatusOK(CursorResponse::parseFromBSON(reply)));
+ uassertStatusOK(CursorResponse::parseFromBSON(reply, nullptr, tenantId)));
auto response = std::make_shared<std::vector<BSONObj>>();
return AsyncTry([this,
+ tenantId,
batchSize = batchSize,
cursorResponse = std::move(cursorResponse),
response]() mutable {
@@ -450,11 +452,11 @@ SemiFuture<std::vector<BSONObj>> SEPTransactionClient::exhaustiveFind(
return runCommand(cursorResponse->getNSS().db(), getMoreRequest.toBSON({}))
.thenRunOn(_executor)
- .then([response, cursorResponse](BSONObj reply) {
+ .then([response, cursorResponse, tenantId](BSONObj reply) {
// We keep the state of cursorResponse to be able to check the
// cursorId in the next iteration.
- *cursorResponse =
- uassertStatusOK(CursorResponse::parseFromBSON(reply));
+ *cursorResponse = uassertStatusOK(
+ CursorResponse::parseFromBSON(reply, nullptr, tenantId));
uasserted(ErrorCodes::InternalTransactionsExhaustiveFindHasMore,
"More documents to fetch");
})
diff --git a/src/mongo/s/commands/cluster_count_cmd.h b/src/mongo/s/commands/cluster_count_cmd.h
index 565740d14aa..263fbe2dfc1 100644
--- a/src/mongo/s/commands/cluster_count_cmd.h
+++ b/src/mongo/s/commands/cluster_count_cmd.h
@@ -169,7 +169,7 @@ public:
result.resetToEmpty();
ViewResponseFormatter formatter(aggResult);
- auto formatStatus = formatter.appendAsCountResponse(&result);
+ auto formatStatus = formatter.appendAsCountResponse(&result, boost::none);
uassertStatusOK(formatStatus);
return true;
diff --git a/src/mongo/s/commands/cluster_distinct_cmd.cpp b/src/mongo/s/commands/cluster_distinct_cmd.cpp
index 5df1cd7989f..c917ca00421 100644
--- a/src/mongo/s/commands/cluster_distinct_cmd.cpp
+++ b/src/mongo/s/commands/cluster_distinct_cmd.cpp
@@ -263,7 +263,7 @@ public:
opCtx, OpMsgRequest::fromDBAndBody(dbName.db(), std::move(resolvedAggCmd)));
ViewResponseFormatter formatter(aggResult);
- auto formatStatus = formatter.appendAsDistinctResponse(&result);
+ auto formatStatus = formatter.appendAsDistinctResponse(&result, boost::none);
uassertStatusOK(formatStatus);
return true;
diff --git a/src/mongo/s/query/async_results_merger_params.idl b/src/mongo/s/query/async_results_merger_params.idl
index 1a1371fe0d4..fcd27fd8528 100644
--- a/src/mongo/s/query/async_results_merger_params.idl
+++ b/src/mongo/s/query/async_results_merger_params.idl
@@ -46,6 +46,7 @@ types:
cpp_type: CursorResponse
serializer: CursorResponse::toBSONAsInitialResponse
deserializer: CursorResponse::parseFromBSONThrowing
+ deserialize_with_tenant: true
structs:
RemoteCursor: