diff options
author | David Storch <david.storch@mongodb.com> | 2023-04-13 21:47:43 +0000 |
---|---|---|
committer | Evergreen Agent <no-reply@evergreen.mongodb.com> | 2023-04-17 18:36:58 +0000 |
commit | 7ed14a4b2c043c639d1c6aa4d22f80f30ca44d97 (patch) | |
tree | ef4afef9c6bf01061e17a701b7cc2f24d7863eeb | |
parent | 2774750311fde8495049d5ea609f70be241dad75 (diff) | |
download | mongo-7ed14a4b2c043c639d1c6aa4d22f80f30ca44d97.tar.gz |
SERVER-75356 Fix sharded explain path's handling of 'let' parameters
(cherry picked from commit 33e6eec5978a20a4b227c36f26b0ae909e8e6728)
21 files changed, 376 insertions, 167 deletions
diff --git a/etc/backports_required_for_multiversion_tests.yml b/etc/backports_required_for_multiversion_tests.yml index e3ba6578f08..eeecdb16915 100644 --- a/etc/backports_required_for_multiversion_tests.yml +++ b/etc/backports_required_for_multiversion_tests.yml @@ -364,6 +364,8 @@ last-continuous: ticket: SERVER-67180 - test_file: jstests/core/clustered/clustered_collection_hint.js ticket: SERVER-73482 + - test_file: jstests/core/command_let_variables.js + ticket: SERVER-75356 suites: null last-lts: all: @@ -807,4 +809,6 @@ last-lts: ticket: SERVER-75886 - test_file: jstests/core/clustered/clustered_collection_hint.js ticket: SERVER-73482 + - test_file: jstests/core/command_let_variables.js + ticket: SERVER-75356 suites: null diff --git a/jstests/core/command_let_variables.js b/jstests/core/command_let_variables.js index 1e4286dbc19..e608bda6f5d 100644 --- a/jstests/core/command_let_variables.js +++ b/jstests/core/command_let_variables.js @@ -6,13 +6,15 @@ (function() { "use strict"; +load("jstests/libs/analyze_plan.js"); load("jstests/libs/fixture_helpers.js"); // For 'isMongos' and 'isSharded'. const testDB = db.getSiblingDB("command_let_variables"); const coll = testDB.command_let_variables; -const targetColl = testDB.command_let_variables_target; +coll.drop(); -assert.commandWorked(testDB.dropDatabase()); +const isMongos = FixtureHelpers.isMongos(testDB); +const isCollSharded = FixtureHelpers.isSharded(coll); const testDocs = [ { @@ -82,7 +84,20 @@ expectedResults = [ assert.eq(coll.aggregate(pipeline, {let : {target_trend: "weak decline"}}).toArray(), expectedResults); -if (!FixtureHelpers.isMongos(testDB)) { +// Test that running explain on the agg command works as expected. +let explain = assert.commandWorked(testDB.runCommand({ + explain: + {aggregate: coll.getName(), pipeline, let : {target_trend: "weak decline"}, cursor: {}}, + verbosity: "executionStats" +})); +if (!isMongos) { + assert(explain.hasOwnProperty("stages"), explain); + assert.neq(explain.stages.length, 0, explain); + let lastStage = explain.stages[explain.stages.length - 1]; + assert.eq(lastStage.nReturned, 2, explain); +} + +if (!isMongos) { // Test that if runtimeConstants and let are both specified, both will coexist. // Runtime constants are not allowed on mongos passthroughs. let constants = { @@ -262,33 +277,77 @@ expectedResults = { assert.eq(result.length, 1); assert.eq(expectedResults, result[0]); -// Delete tests with let params will delete a record, assert that a point-wise find yields an empty -// result, and then restore the collection state for further tests down the line. We can't exercise -// a multi-delete here (limit: 0) because of failures in sharded txn passthrough tests. -assert.commandWorked(testDB.runCommand({ - delete: coll.getName(), - let : {target_species: "Song Thrush (Turdus philomelos)"}, - deletes: [{q: {$and: [{_id: 4}, {$expr: {$eq: ["$Species", "$$target_species"]}}]}, limit: 1}] +// Test that let parameters work as expected when the find is run as an explain. +explain = assert.commandWorked(testDB.runCommand({ + explain: { + find: coll.getName(), + let : {target_species: "Song Thrush (Turdus philomelos)"}, + filter: {$expr: {$eq: ["$Species", "$$target_species"]}}, + projection: {_id: 0} + }, + verbosity: "executionStats" })); +if (!isMongos) { + assert.eq(explain.executionStats.nReturned, 1, explain); +} -result = assert - .commandWorked( - testDB.runCommand({find: coll.getName(), filter: {$expr: {$eq: ["$_id", "4"]}}})) - .cursor.firstBatch; -assert.eq(result.length, 0); +// TODO SERVER-76037: Write commands commands do not currently work with 'let' parameters when +// 'featureFlagUpdateOneWithoutShardKey' is enabled. +const skipWriteCmdTests = (function() { + const featureFlagName = "featureFlagUpdateOneWithoutShardKey"; + // Don't assert that the command succeeded, since we expect it to fail if the parameter does not + // exist + const featureFlagParam = db.adminCommand({getParameter: 1, [featureFlagName]: 1}); + return isCollSharded && featureFlagParam.hasOwnProperty(featureFlagName) && + featureFlagParam[featureFlagName].value; +}()); +if (!skipWriteCmdTests) { + // Delete tests with let params will delete a record, assert that a point-wise find yields an + // empty result, and then restore the collection state for further tests down the line. We can't + // exercise a multi-delete here (limit: 0) because of failures in sharded txn passthrough tests. + assert.commandWorked(testDB.runCommand({ + delete: coll.getName(), + let : {target_species: "Song Thrush (Turdus philomelos)"}, + deletes: + [{q: {$and: [{_id: 4}, {$expr: {$eq: ["$Species", "$$target_species"]}}]}, limit: 1}] + })); -// Test that the .remove() shell helper supports let parameters. -assert.commandWorked(coll.insert({_id: 4, Species: "bird_to_remove"})); -result = assert.commandWorked( - coll.remove({$and: [{_id: 4}, {$expr: {$eq: ["$Species", "$$target_species"]}}]}, - {justOne: true, let : {target_species: "bird_to_remove"}})); -assert.eq(result.nRemoved, 1); + result = assert + .commandWorked(testDB.runCommand( + {find: coll.getName(), filter: {$expr: {$eq: ["$_id", "4"]}}})) + .cursor.firstBatch; + assert.eq(result.length, 0); + + assert.commandWorked(coll.insert({_id: 4, Species: "bird_to_remove"})); + + // Test that explain of a delete command works as expected with 'let' parameters. + explain = assert.commandWorked(testDB.runCommand({ + explain: { + delete: coll.getName(), + let : {target_species: "bird_to_remove"}, + deletes: [ + {q: {$and: [{_id: 4}, {$expr: {$eq: ["$Species", "$$target_species"]}}]}, limit: 1} + ] + }, + verbosity: "executionStats" + })); + if (!isMongos) { + let deleteStage = getPlanStage(explain.executionStats.executionStages, "DELETE"); + assert.eq(deleteStage.nWouldDelete, 1, explain); + } -result = assert - .commandWorked( - testDB.runCommand({find: coll.getName(), filter: {$expr: {$eq: ["$_id", "4"]}}})) - .cursor.firstBatch; -assert.eq(result.length, 0); + // Test that the .remove() shell helper supports let parameters. + result = assert.commandWorked( + coll.remove({$and: [{_id: 4}, {$expr: {$eq: ["$Species", "$$target_species"]}}]}, + {justOne: true, let : {target_species: "bird_to_remove"}})); + assert.eq(result.nRemoved, 1); + + result = assert + .commandWorked(testDB.runCommand( + {find: coll.getName(), filter: {$expr: {$eq: ["$_id", "4"]}}})) + .cursor.firstBatch; + assert.eq(result.length, 0); +} // Test that reserved names are not allowed as let variable names. assert.commandFailedWithCode( @@ -333,113 +392,155 @@ assert.commandWorked(testDB.runCommand({ cursor: {} })); -// Test that findAndModify works correctly with let parameter arguments. assert.commandWorked(coll.insert({_id: 5, Species: "spy_bird"})); -result = testDB.runCommand({ - findAndModify: coll.getName(), - let : {target_species: "spy_bird"}, - // Querying on _id field for sharded collection passthroughs. - query: {$and: [{_id: 5}, {$expr: {$eq: ["$Species", "$$target_species"]}}]}, - update: {Species: "questionable_bird"}, - new: true -}); -expectedResults = { - _id: 5, - Species: "questionable_bird" -}; -assert.eq(expectedResults, result.value, result); - -result = testDB.runCommand({ - findAndModify: coll.getName(), - let : {species_name: "not_a_bird", realSpecies: "dino"}, - // Querying on _id field for sharded collection passthroughs. - query: {$and: [{_id: 5}, {$expr: {$eq: ["$Species", "questionable_bird"]}}]}, - update: [{$project: {Species: "$$species_name"}}, {$addFields: {suspect: "$$realSpecies"}}], - new: true -}); -expectedResults = { - _id: 5, - Species: "not_a_bird", - suspect: "dino" -}; -assert.eq(expectedResults, result.value, result); - -// Test that update respects different parameters in both the query and update part. -result = assert.commandWorked(testDB.runCommand({ - update: coll.getName(), - updates: [ - {q: {$expr: {$eq: ["$Species", "$$target_species"]}}, u: [{$set: {Species: "$$new_name"}}]} - ], - let : {target_species: "Chaffinch (Fringilla coelebs)", new_name: "Chaffinch"} -})); -assert.eq(result.n, 1); -assert.eq(result.nModified, 1); - -result = assert.commandWorked(testDB.runCommand( - {find: coll.getName(), filter: {$expr: {$eq: ["$Species", "Chaffinch (Fringilla coelebs)"]}}})); -assert.eq(result.cursor.firstBatch.length, 0); - -result = assert.commandWorked( - testDB.runCommand({find: coll.getName(), filter: {$expr: {$eq: ["$Species", "Chaffinch"]}}})); -assert.eq(result.cursor.firstBatch.length, 1); - -// Test that update respects runtime constants and parameters. -result = assert.commandWorked(testDB.runCommand({ - update: coll.getName(), - updates: [{ - q: {$expr: {$eq: ["$Species", "$$target_species"]}}, - u: [{$set: {Timestamp: "$$NOW"}}, {$set: {Species: "$$new_name"}}] - }], - let : {target_species: "Chaffinch", new_name: "Pied Piper"} -})); -assert.eq(result.n, 1); -assert.eq(result.nModified, 1); -result = assert.commandWorked( - testDB.runCommand({find: coll.getName(), filter: {$expr: {$eq: ["$Species", "Chaffinch"]}}})); -assert.eq(result.cursor.firstBatch.length, 0, result); +// TODO SERVER-76037: Re-enable these test cases in all contexts. +if (!skipWriteCmdTests) { + // Test that explain of findAndModify works correctly with let parameters. + explain = assert.commandWorked(testDB.runCommand({ + explain: { + findAndModify: coll.getName(), + let : {target_species: "spy_bird"}, + // Querying on _id field for sharded collection passthroughs. + query: {$and: [{_id: 5}, {$expr: {$eq: ["$Species", "$$target_species"]}}]}, + update: {Species: "questionable_bird"}, + new: true + }, + verbosity: "executionStats" + })); + if (!isMongos) { + let updateStage = getPlanStage(explain.executionStats.executionStages, "UPDATE"); + assert.eq(updateStage.nMatched, 1, explain); + assert.eq(updateStage.nWouldModify, 1, explain); + } + + // Test that findAndModify works correctly with let parameter arguments. + result = assert.commandWorked(testDB.runCommand({ + findAndModify: coll.getName(), + let : {target_species: "spy_bird"}, + // Querying on _id field for sharded collection passthroughs. + query: {$and: [{_id: 5}, {$expr: {$eq: ["$Species", "$$target_species"]}}]}, + update: {Species: "questionable_bird"}, + new: true + })); + expectedResults = {_id: 5, Species: "questionable_bird"}; + assert.eq(expectedResults, result.value, result); + + result = assert.commandWorked(testDB.runCommand({ + findAndModify: coll.getName(), + let : {species_name: "not_a_bird", realSpecies: "dino"}, + // Querying on _id field for sharded collection passthroughs. + query: {$and: [{_id: 5}, {$expr: {$eq: ["$Species", "questionable_bird"]}}]}, + update: [{$project: {Species: "$$species_name"}}, {$addFields: {suspect: "$$realSpecies"}}], + new: true + })); + expectedResults = {_id: 5, Species: "not_a_bird", suspect: "dino"}; + assert.eq(expectedResults, result.value, result); + + // Test that explain of update works correctly with let parameters. + explain = assert.commandWorked(testDB.runCommand({ + explain: { + update: coll.getName(), + updates: [{ + q: {_id: 3, $expr: {$eq: ["$Species", "$$target_species"]}}, + u: [{$set: {Species: "$$new_name"}}], + }], + let : {target_species: "Chaffinch (Fringilla coelebs)", new_name: "Chaffinch"} + }, + verbosity: "executionStats" + })); + if (!isMongos) { + let updateStage = getPlanStage(explain.executionStats.executionStages, "UPDATE"); + assert.eq(updateStage.nMatched, 1, explain); + assert.eq(updateStage.nWouldModify, 1, explain); + } -result = assert.commandWorked( - testDB.runCommand({find: coll.getName(), filter: {$expr: {$eq: ["$Species", "Pied Piper"]}}})); -assert.eq(result.cursor.firstBatch.length, 1, result); + // Test that update respects different parameters in both the query and update part. + result = assert.commandWorked(testDB.runCommand({ + update: coll.getName(), + updates: [{ + q: {_id: 3, $expr: {$eq: ["$Species", "$$target_species"]}}, + u: [{$set: {Species: "$$new_name"}}], + }], + let : {target_species: "Chaffinch (Fringilla coelebs)", new_name: "Chaffinch"} + })); + assert.eq(result.n, 1); + assert.eq(result.nModified, 1); -// Test that undefined let params in the update's query part fail gracefully. -assert.commandFailedWithCode(testDB.runCommand({ - update: coll.getName(), - updates: [{ - q: {$expr: {$eq: ["$Species", "$$target_species"]}}, - u: [{$set: {Species: "Homo Erectus"}}] - }], - let : {cat: "not_a_bird"} -}), - 17276); + result = assert.commandWorked(testDB.runCommand({ + find: coll.getName(), + filter: {$expr: {$eq: ["$Species", "Chaffinch (Fringilla coelebs)"]}} + })); + assert.eq(result.cursor.firstBatch.length, 0); + + result = assert.commandWorked(testDB.runCommand( + {find: coll.getName(), filter: {$expr: {$eq: ["$Species", "Chaffinch"]}}})); + assert.eq(result.cursor.firstBatch.length, 1); + + // Test that update respects runtime constants and parameters. + result = assert.commandWorked(testDB.runCommand({ + update: coll.getName(), + updates: [{ + q: {_id: 3, $expr: {$eq: ["$Species", "$$target_species"]}}, + u: [{$set: {Timestamp: "$$NOW"}}, {$set: {Species: "$$new_name"}}], + }], + let : {target_species: "Chaffinch", new_name: "Pied Piper"} + })); + assert.eq(result.n, 1); + assert.eq(result.nModified, 1); -// Test that undefined let params in the update's update part fail gracefully. -assert.commandFailedWithCode(testDB.runCommand({ - update: coll.getName(), - updates: [{ - q: {$expr: {$eq: ["$Species", "Chaffinch (Fringilla coelebs)"]}}, - u: [{$set: {Species: "$$new_name"}}] - }], - let : {cat: "not_a_bird"} -}), - 17276); + result = assert.commandWorked(testDB.runCommand( + {find: coll.getName(), filter: {$expr: {$eq: ["$Species", "Chaffinch"]}}})); + assert.eq(result.cursor.firstBatch.length, 0, result); -// Test that the .update() shell helper supports let parameters. -result = assert.commandWorked( - coll.update({$expr: {$eq: ["$Species", "$$target_species"]}}, - [{$set: {Species: "$$new_name"}}], - {let : {target_species: "Pied Piper", new_name: "Chaffinch"}})); -assert.eq(result.nMatched, 1); -assert.eq(result.nModified, 1); + result = assert.commandWorked(testDB.runCommand( + {find: coll.getName(), filter: {$expr: {$eq: ["$Species", "Pied Piper"]}}})); + assert.eq(result.cursor.firstBatch.length, 1, result); -result = assert.commandWorked( - testDB.runCommand({find: coll.getName(), filter: {$expr: {$eq: ["$Species", "Pied Piper"]}}})); -assert.eq(result.cursor.firstBatch.length, 0, result); + // This forces a multi-statement transaction to commit if this test is running in one of the + // multi-statement transaction passthrough suites. We need to do this to ensure the updates + // above commit before running an update that will fail, as the failed update aborts the entire + // transaction and rolls back the updates above. + assert.commandWorked(testDB.runCommand({ping: 1})); -result = assert.commandWorked( - testDB.runCommand({find: coll.getName(), filter: {$expr: {$eq: ["$Species", "Chaffinch"]}}})); -assert.eq(result.cursor.firstBatch.length, 1, result); + // Test that undefined let params in the update's query part fail gracefully. + assert.commandFailedWithCode(testDB.runCommand({ + update: coll.getName(), + updates: [{ + q: {$expr: {$eq: ["$Species", "$$target_species"]}}, + u: [{$set: {Species: "Homo Erectus"}}] + }], + let : {cat: "not_a_bird"} + }), + 17276); + + // Test that undefined let params in the update's update part fail gracefully. + assert.commandFailedWithCode(testDB.runCommand({ + update: coll.getName(), + updates: [{ + q: {_id: 3, $expr: {$eq: ["$Species", "Chaffinch (Fringilla coelebs)"]}}, + u: [{$set: {Species: "$$new_name"}}], + }], + let : {cat: "not_a_bird"} + }), + 17276); + + // Test that the .update() shell helper supports let parameters. + result = assert.commandWorked( + coll.update({_id: 3, $expr: {$eq: ["$Species", "$$target_species"]}}, + [{$set: {Species: "$$new_name"}}], + {let : {target_species: "Pied Piper", new_name: "Chaffinch"}})); + assert.eq(result.nMatched, 1); + assert.eq(result.nModified, 1); + + result = assert.commandWorked(testDB.runCommand( + {find: coll.getName(), filter: {$expr: {$eq: ["$Species", "Pied Piper"]}}})); + assert.eq(result.cursor.firstBatch.length, 0, result); + + result = assert.commandWorked(testDB.runCommand( + {find: coll.getName(), filter: {$expr: {$eq: ["$Species", "Chaffinch"]}}})); + assert.eq(result.cursor.firstBatch.length, 1, result); +} // Test that let variables can be initialized with an expression. result = assert @@ -519,7 +620,10 @@ assert.between(0, result, 1); } // Test that the expressions are evaluated once up front. -{ +// +// TODO SERVER-75927: This does not work as expected when the collection is sharded. Once the bug +// is fixed, we should re-enable this test case when the collection is sharded. +if (!isCollSharded) { const values = assert .commandWorked(testDB.runCommand({ find: coll.getName(), diff --git a/src/mongo/db/pipeline/process_interface/mongos_process_interface.cpp b/src/mongo/db/pipeline/process_interface/mongos_process_interface.cpp index 18e2f11ee1b..be0bb849391 100644 --- a/src/mongo/db/pipeline/process_interface/mongos_process_interface.cpp +++ b/src/mongo/db/pipeline/process_interface/mongos_process_interface.cpp @@ -201,8 +201,15 @@ boost::optional<Document> MongosProcessInterface::lookupSingleDocument( // single shard will be targeted here; however, in certain cases where only the _id // is present, we may need to scatter-gather the query to all shards in order to // find the document. - auto requests = getVersionedRequestsForTargetedShards( - expCtx->opCtx, nss, cri, findCmd, filterObj, CollationSpec::kSimpleSpec); + auto requests = + getVersionedRequestsForTargetedShards(expCtx->opCtx, + nss, + cri, + findCmd, + filterObj, + CollationSpec::kSimpleSpec, + boost::none /*letParameters*/, + boost::none /*runtimeConstants*/); // Dispatch the requests. The 'establishCursors' method conveniently prepares the // result into a vector of cursor responses for us. diff --git a/src/mongo/db/pipeline/sharded_agg_helpers.cpp b/src/mongo/db/pipeline/sharded_agg_helpers.cpp index 8e5fb369e79..c51142298ed 100644 --- a/src/mongo/db/pipeline/sharded_agg_helpers.cpp +++ b/src/mongo/db/pipeline/sharded_agg_helpers.cpp @@ -1192,7 +1192,7 @@ DispatchShardPipelineResults dispatchShardPipeline( // shards, and should participate in the shard version protocol. invariant(executionNsRoutingInfo); shardResults = - scatterGatherVersionedTargetByRoutingTable(opCtx, + scatterGatherVersionedTargetByRoutingTable(expCtx, expCtx->ns.db(), expCtx->ns, *executionNsRoutingInfo, diff --git a/src/mongo/db/s/sessions_collection_config_server.cpp b/src/mongo/db/s/sessions_collection_config_server.cpp index 4e9ebb06578..8aa967d33cb 100644 --- a/src/mongo/db/s/sessions_collection_config_server.cpp +++ b/src/mongo/db/s/sessions_collection_config_server.cpp @@ -99,8 +99,10 @@ void SessionsCollectionConfigServer::_generateIndexesIfNeeded(OperationContext* SessionsCollection::generateCreateIndexesCmd(), ReadPreferenceSetting(ReadPreference::PrimaryOnly), Shard::RetryPolicy::kNoRetry, - BSONObj() /* query */, - BSONObj() /* collation */); + BSONObj() /*query*/, + BSONObj() /*collation*/, + boost::none /*letParameters*/, + boost::none /*runtimeConstants*/); }); for (auto& shardResult : shardResults) { diff --git a/src/mongo/db/s/shardsvr_drop_indexes_command.cpp b/src/mongo/db/s/shardsvr_drop_indexes_command.cpp index a50ace9a5ae..0a552d680a0 100644 --- a/src/mongo/db/s/shardsvr_drop_indexes_command.cpp +++ b/src/mongo/db/s/shardsvr_drop_indexes_command.cpp @@ -201,8 +201,10 @@ ShardsvrDropIndexesCommand::Invocation::Response ShardsvrDropIndexesCommand::Inv CommandHelpers::filterCommandRequestForPassthrough(cmdToBeSent)), ReadPreferenceSetting::get(opCtx), Shard::RetryPolicy::kNotIdempotent, - BSONObj() /* query */, - BSONObj() /* collation */); + BSONObj() /*query*/, + BSONObj() /*collation*/, + boost::none /*letParameters*/, + boost::none /*runtimeConstants*/); // Append responses we've received from previous retries of this operation due to a // stale config error. diff --git a/src/mongo/s/cluster_commands_helpers.cpp b/src/mongo/s/cluster_commands_helpers.cpp index 345c4eae3f7..1619df5129f 100644 --- a/src/mongo/s/cluster_commands_helpers.cpp +++ b/src/mongo/s/cluster_commands_helpers.cpp @@ -132,7 +132,7 @@ namespace { * sample id for it. */ std::vector<AsyncRequestsSender::Request> buildVersionedRequestsForTargetedShards( - OperationContext* opCtx, + boost::intrusive_ptr<ExpressionContext> expCtx, const NamespaceString& nss, const CollectionRoutingInfo& cri, const std::set<ShardId>& shardsToSkip, @@ -140,6 +140,7 @@ std::vector<AsyncRequestsSender::Request> buildVersionedRequestsForTargetedShard const BSONObj& query, const BSONObj& collation, bool eligibleForSampling = false) { + auto opCtx = expCtx->opCtx; const auto& cm = cri.cm; @@ -180,7 +181,6 @@ std::vector<AsyncRequestsSender::Request> buildVersionedRequestsForTargetedShard CollatorFactoryInterface::get(opCtx->getServiceContext())->makeFromBSON(collation)); } - auto expCtx = make_intrusive<ExpressionContext>(opCtx, std::move(collator), nss); getShardIdsForQuery(expCtx, query, collation, cm, &shardIds, nullptr /* info */); const auto targetedSampleId = eligibleForSampling @@ -422,11 +422,37 @@ std::vector<AsyncRequestsSender::Response> scatterGatherVersionedTargetByRouting Shard::RetryPolicy retryPolicy, const BSONObj& query, const BSONObj& collation, + const boost::optional<BSONObj>& letParameters, + const boost::optional<LegacyRuntimeConstants>& runtimeConstants, bool eligibleForSampling) { - const auto requests = buildVersionedRequestsForTargetedShards( - opCtx, nss, cri, {} /* shardsToSkip */, cmdObj, query, collation, eligibleForSampling); + auto expCtx = makeExpressionContextWithDefaultsForTargeter( + opCtx, nss, collation, boost::none /*explainVerbosity*/, letParameters, runtimeConstants); + return scatterGatherVersionedTargetByRoutingTable(expCtx, + dbName, + nss, + cri, + cmdObj, + readPref, + retryPolicy, + query, + collation, + eligibleForSampling); +} - return gatherResponses(opCtx, dbName, readPref, retryPolicy, requests); +[[nodiscard]] std::vector<AsyncRequestsSender::Response> scatterGatherVersionedTargetByRoutingTable( + boost::intrusive_ptr<ExpressionContext> expCtx, + StringData dbName, + const NamespaceString& nss, + const CollectionRoutingInfo& cri, + const BSONObj& cmdObj, + const ReadPreferenceSetting& readPref, + Shard::RetryPolicy retryPolicy, + const BSONObj& query, + const BSONObj& collation, + bool eligibleForSampling) { + const auto requests = buildVersionedRequestsForTargetedShards( + expCtx, nss, cri, {} /* shardsToSkip */, cmdObj, query, collation, eligibleForSampling); + return gatherResponses(expCtx->opCtx, dbName, readPref, retryPolicy, requests); } std::vector<AsyncRequestsSender::Response> @@ -440,9 +466,13 @@ scatterGatherVersionedTargetByRoutingTableNoThrowOnStaleShardVersionErrors( const ReadPreferenceSetting& readPref, Shard::RetryPolicy retryPolicy, const BSONObj& query, - const BSONObj& collation) { + const BSONObj& collation, + const boost::optional<BSONObj>& letParameters, + const boost::optional<LegacyRuntimeConstants>& runtimeConstants) { + auto expCtx = makeExpressionContextWithDefaultsForTargeter( + opCtx, nss, collation, boost::none /*explainVerbosity*/, letParameters, runtimeConstants); const auto requests = buildVersionedRequestsForTargetedShards( - opCtx, nss, cri, shardsToSkip, cmdObj, query, collation); + expCtx, nss, cri, shardsToSkip, cmdObj, query, collation); return gatherResponsesNoThrowOnStaleShardVersionErrors( opCtx, dbName, readPref, retryPolicy, requests); @@ -477,6 +507,12 @@ AsyncRequestsSender::Response executeCommandAgainstShardWithMinKeyChunk( const BSONObj& cmdObj, const ReadPreferenceSetting& readPref, Shard::RetryPolicy retryPolicy) { + auto expCtx = makeExpressionContextWithDefaultsForTargeter(opCtx, + nss, + BSONObj() /*collation*/, + boost::none /*explainVerbosity*/, + boost::none /*letParameters*/, + boost::none /*runtimeConstants*/); const auto query = cri.cm.isSharded() ? cri.cm.getShardKeyPattern().getKeyPattern().globalMin() : BSONObj(); @@ -487,7 +523,7 @@ AsyncRequestsSender::Response executeCommandAgainstShardWithMinKeyChunk( readPref, retryPolicy, buildVersionedRequestsForTargetedShards( - opCtx, nss, cri, {} /* shardsToSkip */, cmdObj, query, BSONObj() /* collation */)); + expCtx, nss, cri, {} /* shardsToSkip */, cmdObj, query, BSONObj() /* collation */)); return std::move(responses.front()); } @@ -670,10 +706,14 @@ std::vector<std::pair<ShardId, BSONObj>> getVersionedRequestsForTargetedShards( const CollectionRoutingInfo& cri, const BSONObj& cmdObj, const BSONObj& query, - const BSONObj& collation) { + const BSONObj& collation, + const boost::optional<BSONObj>& letParameters, + const boost::optional<LegacyRuntimeConstants>& runtimeConstants) { + auto expCtx = makeExpressionContextWithDefaultsForTargeter( + opCtx, nss, collation, boost::none /*explainVerbosity*/, letParameters, runtimeConstants); std::vector<std::pair<ShardId, BSONObj>> requests; auto ars_requests = buildVersionedRequestsForTargetedShards( - opCtx, nss, cri, {} /* shardsToSkip */, cmdObj, query, collation); + expCtx, nss, cri, {} /* shardsToSkip */, cmdObj, query, collation); std::transform(std::make_move_iterator(ars_requests.begin()), std::make_move_iterator(ars_requests.end()), std::back_inserter(requests), diff --git a/src/mongo/s/cluster_commands_helpers.h b/src/mongo/s/cluster_commands_helpers.h index 549e91b529c..b69b114025b 100644 --- a/src/mongo/s/cluster_commands_helpers.h +++ b/src/mongo/s/cluster_commands_helpers.h @@ -183,8 +183,24 @@ std::vector<AsyncRequestsSender::Response> scatterGatherUnversionedTargetAllShar Shard::RetryPolicy retryPolicy, const BSONObj& query, const BSONObj& collation, + const boost::optional<BSONObj>& letParameters, + const boost::optional<LegacyRuntimeConstants>& runtimeConstants, + bool eligibleForSampling = false); +/** + * This overload is for callers which already have a fully initialized 'ExpressionContext' (e.g. + * callers from the aggregation framework). Most callers should prefer the overload above. + */ +[[nodiscard]] std::vector<AsyncRequestsSender::Response> scatterGatherVersionedTargetByRoutingTable( + boost::intrusive_ptr<ExpressionContext> expCtx, + StringData dbName, + const NamespaceString& nss, + const CollectionRoutingInfo& cri, + const BSONObj& cmdObj, + const ReadPreferenceSetting& readPref, + Shard::RetryPolicy retryPolicy, + const BSONObj& query, + const BSONObj& collation, bool eligibleForSampling = false); - /** * Utility for dispatching versioned commands on a namespace, deciding which shards to @@ -205,7 +221,9 @@ scatterGatherVersionedTargetByRoutingTableNoThrowOnStaleShardVersionErrors( const ReadPreferenceSetting& readPref, Shard::RetryPolicy retryPolicy, const BSONObj& query, - const BSONObj& collation); + const BSONObj& collation, + const boost::optional<BSONObj>& letParameters, + const boost::optional<LegacyRuntimeConstants>& runtimeConstants); /** * Utility for dispatching commands against the primary of a database and attaching the appropriate @@ -297,7 +315,9 @@ std::vector<std::pair<ShardId, BSONObj>> getVersionedRequestsForTargetedShards( const CollectionRoutingInfo& cri, const BSONObj& cmdObj, const BSONObj& query, - const BSONObj& collation); + const BSONObj& collation, + const boost::optional<BSONObj>& letParameters, + const boost::optional<LegacyRuntimeConstants>& runtimeConstants); /** * If the command is running in a transaction, returns the proper routing table to use for targeting diff --git a/src/mongo/s/commands/cluster_analyze_cmd.cpp b/src/mongo/s/commands/cluster_analyze_cmd.cpp index ea0ccdb84a7..208d7df1621 100644 --- a/src/mongo/s/commands/cluster_analyze_cmd.cpp +++ b/src/mongo/s/commands/cluster_analyze_cmd.cpp @@ -91,8 +91,10 @@ public: CommandHelpers::filterCommandRequestForPassthrough(unparsedRequest().body)), ReadPreferenceSetting::get(opCtx), Shard::RetryPolicy::kIdempotent, - BSONObj() /* query */, - BSONObj() /* collation */); + BSONObj() /*query*/, + BSONObj() /*collation*/, + boost::none /*letParameters*/, + boost::none /*runtimeConstants*/); for (const auto& shardResult : shardResponses) { const auto& shardResponse = uassertStatusOK(std::move(shardResult.swResponse)); diff --git a/src/mongo/s/commands/cluster_coll_stats_cmd.cpp b/src/mongo/s/commands/cluster_coll_stats_cmd.cpp index d830e04415c..2f0d84ed2ad 100644 --- a/src/mongo/s/commands/cluster_coll_stats_cmd.cpp +++ b/src/mongo/s/commands/cluster_coll_stats_cmd.cpp @@ -247,8 +247,10 @@ public: opCtx, this, CommandHelpers::filterCommandRequestForPassthrough(cmdObjToSend)), ReadPreferenceSetting::get(opCtx), Shard::RetryPolicy::kIdempotent, - {}, - {}); + {} /*query*/, + {} /*collation*/, + boost::none /*letParameters*/, + boost::none /*runtimeConstants*/); BSONObjBuilder shardStats; std::map<std::string, long long> counts; diff --git a/src/mongo/s/commands/cluster_convert_to_capped_cmd.cpp b/src/mongo/s/commands/cluster_convert_to_capped_cmd.cpp index 6fa1d62d5d7..30e8e904bbd 100644 --- a/src/mongo/s/commands/cluster_convert_to_capped_cmd.cpp +++ b/src/mongo/s/commands/cluster_convert_to_capped_cmd.cpp @@ -60,8 +60,10 @@ bool nonShardedCollectionCommandPassthrough(OperationContext* opCtx, cmdObj, ReadPreferenceSetting::get(opCtx), retryPolicy, - {}, - {}); + {} /*query*/, + {} /*collation*/, + boost::none /*letParameters*/, + boost::none /*runtimeConstants*/); invariant(responses.size() == 1); const auto cmdResponse = uassertStatusOK(std::move(responses.front().swResponse)); diff --git a/src/mongo/s/commands/cluster_count_cmd.h b/src/mongo/s/commands/cluster_count_cmd.h index 1325c89a21e..045b1895728 100644 --- a/src/mongo/s/commands/cluster_count_cmd.h +++ b/src/mongo/s/commands/cluster_count_cmd.h @@ -152,7 +152,9 @@ public: Shard::RetryPolicy::kIdempotent, countRequest.getQuery(), collation, - true /* eligibleForSampling */); + boost::none /*letParameters*/, + boost::none /*runtimeConstants*/, + true /*eligibleForSampling*/); } catch (const ExceptionFor<ErrorCodes::CommandOnShardedViewNotSupportedOnMongod>& ex) { // Rewrite the count command as an aggregation. auto countRequest = CountCommandRequest::parse(IDLParserContext("count"), cmdObj); @@ -260,7 +262,9 @@ public: ReadPreferenceSetting::get(opCtx), Shard::RetryPolicy::kIdempotent, targetingQuery, - targetingCollation); + targetingCollation, + boost::none /*letParameters*/, + boost::none /*runtimeConstants*/); } catch (const ExceptionFor<ErrorCodes::CommandOnShardedViewNotSupportedOnMongod>& ex) { CountCommandRequest countRequest(NamespaceStringOrUUID(NamespaceString{})); try { diff --git a/src/mongo/s/commands/cluster_create_indexes_cmd.cpp b/src/mongo/s/commands/cluster_create_indexes_cmd.cpp index e07aa1700e0..095ccd6bbe9 100644 --- a/src/mongo/s/commands/cluster_create_indexes_cmd.cpp +++ b/src/mongo/s/commands/cluster_create_indexes_cmd.cpp @@ -121,8 +121,10 @@ public: applyReadWriteConcern(opCtx, this, cmdToBeSent)), ReadPreferenceSetting(ReadPreference::PrimaryOnly), Shard::RetryPolicy::kNoRetry, - BSONObj() /* query */, - BSONObj() /* collation */); + BSONObj() /*query*/, + BSONObj() /*collation*/, + boost::none /*letParameters*/, + boost::none /*runtimeConstants*/); std::string errmsg; const bool ok = diff --git a/src/mongo/s/commands/cluster_data_size_cmd.cpp b/src/mongo/s/commands/cluster_data_size_cmd.cpp index dda5ed02fcf..057f9e8ad73 100644 --- a/src/mongo/s/commands/cluster_data_size_cmd.cpp +++ b/src/mongo/s/commands/cluster_data_size_cmd.cpp @@ -88,8 +88,10 @@ public: CommandHelpers::filterCommandRequestForPassthrough(cmd.toBSON({}))), ReadPreferenceSetting::get(opCtx), Shard::RetryPolicy::kIdempotent, - {}, - {}); + {} /*query*/, + {} /*collation*/, + boost::none /*letParameters*/, + boost::none /*runtimeConstants*/); std::int64_t size = 0; std::int64_t numObjects = 0; diff --git a/src/mongo/s/commands/cluster_distinct_cmd.cpp b/src/mongo/s/commands/cluster_distinct_cmd.cpp index 3c9ff302f7a..5fb9a701419 100644 --- a/src/mongo/s/commands/cluster_distinct_cmd.cpp +++ b/src/mongo/s/commands/cluster_distinct_cmd.cpp @@ -146,7 +146,9 @@ public: ReadPreferenceSetting::get(opCtx), Shard::RetryPolicy::kIdempotent, targetingQuery, - targetingCollation); + targetingCollation, + boost::none /*letParameters*/, + boost::none /*runtimeConstants*/); } catch (const ExceptionFor<ErrorCodes::CommandOnShardedViewNotSupportedOnMongod>& ex) { auto parsedDistinct = ParsedDistinct::parse( opCtx, ex->getNamespace(), cmdObj, ExtensionsCallbackNoop(), true); @@ -234,6 +236,8 @@ public: Shard::RetryPolicy::kIdempotent, query, collation, + boost::none /*letParameters*/, + boost::none /*runtimeConstants*/, true /* eligibleForSampling */); } catch (const ExceptionFor<ErrorCodes::CommandOnShardedViewNotSupportedOnMongod>& ex) { auto parsedDistinct = ParsedDistinct::parse( diff --git a/src/mongo/s/commands/cluster_filemd5_cmd.cpp b/src/mongo/s/commands/cluster_filemd5_cmd.cpp index 63b5a2a5033..12ab9f07730 100644 --- a/src/mongo/s/commands/cluster_filemd5_cmd.cpp +++ b/src/mongo/s/commands/cluster_filemd5_cmd.cpp @@ -107,7 +107,9 @@ public: ReadPreferenceSetting::get(opCtx), Shard::RetryPolicy::kIdempotent, routingQuery, - CollationSpec::kSimpleSpec); + CollationSpec::kSimpleSpec, + boost::none /*letParameters*/, + boost::none /*runtimeConstants*/); invariant(shardResults.size() == 1); const auto shardResponse = uassertStatusOK(std::move(shardResults[0].swResponse)); uassertStatusOK(shardResponse.status); diff --git a/src/mongo/s/commands/cluster_find_cmd.h b/src/mongo/s/commands/cluster_find_cmd.h index e8ed843575a..cd46d911895 100644 --- a/src/mongo/s/commands/cluster_find_cmd.h +++ b/src/mongo/s/commands/cluster_find_cmd.h @@ -161,7 +161,9 @@ public: ReadPreferenceSetting::get(opCtx), Shard::RetryPolicy::kIdempotent, findCommand->getFilter(), - findCommand->getCollation()); + findCommand->getCollation(), + findCommand->getLet(), + findCommand->getLegacyRuntimeConstants()); millisElapsed = timer.millis(); const char* mongosStageName = diff --git a/src/mongo/s/commands/cluster_index_filter_cmd.cpp b/src/mongo/s/commands/cluster_index_filter_cmd.cpp index f2a1bf18dba..cd81e568d77 100644 --- a/src/mongo/s/commands/cluster_index_filter_cmd.cpp +++ b/src/mongo/s/commands/cluster_index_filter_cmd.cpp @@ -101,7 +101,9 @@ public: ReadPreferenceSetting::get(opCtx), Shard::RetryPolicy::kIdempotent, query, - CollationSpec::kSimpleSpec); + CollationSpec::kSimpleSpec, + boost::none /*letParameters*/, + boost::none /*runtimeConstants*/); // Sort shard responses by shard id. std::sort(shardResponses.begin(), diff --git a/src/mongo/s/commands/cluster_plan_cache_clear_cmd.cpp b/src/mongo/s/commands/cluster_plan_cache_clear_cmd.cpp index b1e9ff01062..5e75487858c 100644 --- a/src/mongo/s/commands/cluster_plan_cache_clear_cmd.cpp +++ b/src/mongo/s/commands/cluster_plan_cache_clear_cmd.cpp @@ -105,7 +105,9 @@ bool ClusterPlanCacheClearCmd::run(OperationContext* opCtx, ReadPreferenceSetting::get(opCtx), Shard::RetryPolicy::kIdempotent, query, - CollationSpec::kSimpleSpec); + CollationSpec::kSimpleSpec, + boost::none /*letParameters*/, + boost::none /*runtimeConstants*/); // Sort shard responses by shard id. std::sort(shardResponses.begin(), diff --git a/src/mongo/s/commands/cluster_set_index_commit_quorum_cmd.cpp b/src/mongo/s/commands/cluster_set_index_commit_quorum_cmd.cpp index 3cd1e398325..e60c92d29d8 100644 --- a/src/mongo/s/commands/cluster_set_index_commit_quorum_cmd.cpp +++ b/src/mongo/s/commands/cluster_set_index_commit_quorum_cmd.cpp @@ -116,8 +116,10 @@ public: opCtx, this, CommandHelpers::filterCommandRequestForPassthrough(cmdObj)), ReadPreferenceSetting::get(opCtx), Shard::RetryPolicy::kNotIdempotent, - BSONObj() /* query */, - BSONObj() /* collation */); + BSONObj() /*query*/, + BSONObj() /*collation*/, + boost::none /*letParameters*/, + boost::none /*runtimeConstants*/); std::string errmsg; const bool ok = diff --git a/src/mongo/s/commands/cluster_validate_cmd.cpp b/src/mongo/s/commands/cluster_validate_cmd.cpp index 3d9e62e8e45..36512d0c920 100644 --- a/src/mongo/s/commands/cluster_validate_cmd.cpp +++ b/src/mongo/s/commands/cluster_validate_cmd.cpp @@ -91,8 +91,10 @@ public: opCtx, this, CommandHelpers::filterCommandRequestForPassthrough(cmdObj)), ReadPreferenceSetting::get(opCtx), Shard::RetryPolicy::kIdempotent, - {}, - {}); + {} /*query*/, + {} /*collation*/, + boost::none /*letParameters*/, + boost::none /*runtimeConstants*/); Status firstFailedShardStatus = Status::OK(); bool isValid = true; |