summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlberto Massari <alberto.massari@mongodb.com>2022-09-30 10:49:28 +0000
committerEvergreen Agent <no-reply@evergreen.mongodb.com>2022-09-30 11:59:59 +0000
commit459f574b8a4afd9e2e843c625f2ee4b726da12f3 (patch)
treeddf8b9f33a504d61378d5ef771da356fadf5c697
parenteac56c63e577af9746cdb180b88d46c4c09077c4 (diff)
downloadmongo-459f574b8a4afd9e2e843c625f2ee4b726da12f3.tar.gz
SERVER-63604 Optimize detection of the need to produce/propagate RecordId slots in SBE
-rw-r--r--src/mongo/db/dbhelpers.cpp13
-rw-r--r--src/mongo/db/query/canonical_query.h13
-rw-r--r--src/mongo/db/query/canonical_query_encoder.cpp1
-rw-r--r--src/mongo/db/query/cqf_get_executor.cpp18
-rw-r--r--src/mongo/db/query/cqf_get_executor.h7
-rw-r--r--src/mongo/db/query/get_executor.cpp11
-rw-r--r--src/mongo/db/query/planner_analysis.cpp2
-rw-r--r--src/mongo/db/query/query_planner.cpp3
-rw-r--r--src/mongo/db/query/query_planner_options_test.cpp2
-rw-r--r--src/mongo/db/query/query_planner_params.h14
-rw-r--r--src/mongo/db/query/query_planner_test_fixture.cpp4
-rw-r--r--src/mongo/db/query/query_planner_test_fixture.h5
-rw-r--r--src/mongo/db/query/sbe_stage_builder.cpp75
-rw-r--r--src/mongo/db/query/sbe_stage_builder.h1
-rw-r--r--src/mongo/db/query/sbe_stage_builder_lookup.cpp3
-rw-r--r--src/mongo/db/query/sbe_stage_builder_test_fixture.cpp4
-rw-r--r--src/mongo/db/test_output/query/canonical_query_encoder_test/compute_key_s_b_e.txt48
-rw-r--r--src/mongo/db/test_output/query/canonical_query_encoder_test/compute_key_s_b_e_with_pipeline.txt12
-rw-r--r--src/mongo/db/test_output/query/canonical_query_encoder_test/compute_key_s_b_e_with_read_concern.txt6
19 files changed, 129 insertions, 113 deletions
diff --git a/src/mongo/db/dbhelpers.cpp b/src/mongo/db/dbhelpers.cpp
index 89f71139f07..dd00b6366e9 100644
--- a/src/mongo/db/dbhelpers.cpp
+++ b/src/mongo/db/dbhelpers.cpp
@@ -119,14 +119,13 @@ RecordId Helpers::findOne(OperationContext* opCtx,
massertStatusOK(statusWithCQ.getStatus());
unique_ptr<CanonicalQuery> cq = std::move(statusWithCQ.getValue());
+ cq->setForceGenerateRecordId(true);
- auto exec = uassertStatusOK(
- getExecutor(opCtx,
- &collection,
- std::move(cq),
- nullptr /* extractAndAttachPipelineStages */,
- PlanYieldPolicy::YieldPolicy::NO_YIELD,
- QueryPlannerParams::DEFAULT | QueryPlannerParams::PRESERVE_RECORD_ID));
+ auto exec = uassertStatusOK(getExecutor(opCtx,
+ &collection,
+ std::move(cq),
+ nullptr /* extractAndAttachPipelineStages */,
+ PlanYieldPolicy::YieldPolicy::NO_YIELD));
PlanExecutor::ExecState state;
BSONObj obj;
diff --git a/src/mongo/db/query/canonical_query.h b/src/mongo/db/query/canonical_query.h
index 1c0213d0132..e939c248a9d 100644
--- a/src/mongo/db/query/canonical_query.h
+++ b/src/mongo/db/query/canonical_query.h
@@ -245,6 +245,14 @@ public:
_explain = explain;
}
+ bool getForceGenerateRecordId() const {
+ return _forceGenerateRecordId;
+ }
+
+ void setForceGenerateRecordId(bool value) {
+ _forceGenerateRecordId = value;
+ }
+
OperationContext* getOpCtx() const {
tassert(6508300, "'CanonicalQuery' does not have an 'ExpressionContext'", _expCtx);
return _expCtx->opCtx;
@@ -310,6 +318,11 @@ private:
// True if this query can be executed by the SBE.
bool _sbeCompatible = false;
+ // True if this query must produce a RecordId output in addition to the BSON objects that
+ // constitute the result set of the query. Any generated query solution must not discard record
+ // ids, even if the optimizer detects that they are not going to be consumed downstream.
+ bool _forceGenerateRecordId = false;
+
// A map from assigned InputParamId's to parameterised MatchExpression's.
std::vector<const MatchExpression*> _inputParamIdToExpressionMap;
};
diff --git a/src/mongo/db/query/canonical_query_encoder.cpp b/src/mongo/db/query/canonical_query_encoder.cpp
index 348e196d7f9..52344ab35f8 100644
--- a/src/mongo/db/query/canonical_query_encoder.cpp
+++ b/src/mongo/db/query/canonical_query_encoder.cpp
@@ -1119,6 +1119,7 @@ std::string encodeSBE(const CanonicalQuery& cq) {
bufBuilder.appendBuf(proj.objdata(), proj.objsize());
bufBuilder.appendStr(strBuilderEncoded, false /* includeEndingNull */);
+ bufBuilder.appendChar(cq.getForceGenerateRecordId() ? 1 : 0);
encodeFindCommandRequest(cq.getFindCommandRequest(), &bufBuilder);
diff --git a/src/mongo/db/query/cqf_get_executor.cpp b/src/mongo/db/query/cqf_get_executor.cpp
index f2e13faebbd..f1843613b1d 100644
--- a/src/mongo/db/query/cqf_get_executor.cpp
+++ b/src/mongo/db/query/cqf_get_executor.cpp
@@ -484,9 +484,7 @@ std::unique_ptr<PlanExecutor, PlanExecutor::Deleter> getSBEExecutorViaCascadesOp
const CollectionPtr& collection,
const boost::optional<BSONObj>& indexHint,
std::unique_ptr<Pipeline, PipelineDeleter> pipeline,
- std::unique_ptr<CanonicalQuery> canonicalQuery,
- const bool requireRID) {
-
+ std::unique_ptr<CanonicalQuery> canonicalQuery) {
// Ensure that either pipeline or canonicalQuery is set.
tassert(624070,
"getSBEExecutorViaCascadesOptimizer expects exactly one of the following to be set: "
@@ -503,6 +501,7 @@ std::unique_ptr<PlanExecutor, PlanExecutor::Deleter> getSBEExecutorViaCascadesOp
auto curOp = CurOp::get(opCtx);
curOp->debug().cqfUsed = true;
+ const bool requireRID = canonicalQuery ? canonicalQuery->getForceGenerateRecordId() : false;
const bool collectionExists = collection != nullptr;
const std::string uuidStr = collectionExists ? collection->uuid().toString() : "<missing_uuid>";
const std::string collNameStr = nss.coll().toString();
@@ -624,8 +623,7 @@ std::unique_ptr<PlanExecutor, PlanExecutor::Deleter> getSBEExecutorViaCascadesOp
}
std::unique_ptr<PlanExecutor, PlanExecutor::Deleter> getSBEExecutorViaCascadesOptimizer(
- const CollectionPtr& collection, std::unique_ptr<CanonicalQuery> query, const bool requireRID) {
-
+ const CollectionPtr& collection, std::unique_ptr<CanonicalQuery> query) {
boost::optional<BSONObj> indexHint = query->getFindCommandRequest().getHint().isEmpty()
? boost::none
: boost::make_optional(query->getFindCommandRequest().getHint());
@@ -635,14 +633,8 @@ std::unique_ptr<PlanExecutor, PlanExecutor::Deleter> getSBEExecutorViaCascadesOp
auto expCtx = query->getExpCtx();
auto nss = query->nss();
- return getSBEExecutorViaCascadesOptimizer(opCtx,
- expCtx,
- nss,
- collection,
- indexHint,
- nullptr /* pipeline */,
- std::move(query),
- requireRID);
+ return getSBEExecutorViaCascadesOptimizer(
+ opCtx, expCtx, nss, collection, indexHint, nullptr /* pipeline */, std::move(query));
}
} // namespace mongo
diff --git a/src/mongo/db/query/cqf_get_executor.h b/src/mongo/db/query/cqf_get_executor.h
index a2eb34081dc..81454fad196 100644
--- a/src/mongo/db/query/cqf_get_executor.h
+++ b/src/mongo/db/query/cqf_get_executor.h
@@ -49,15 +49,12 @@ std::unique_ptr<PlanExecutor, PlanExecutor::Deleter> getSBEExecutorViaCascadesOp
const CollectionPtr& collection,
const boost::optional<BSONObj>& indexHint,
std::unique_ptr<Pipeline, PipelineDeleter> pipeline,
- std::unique_ptr<CanonicalQuery> = nullptr,
- bool requireRID = false);
+ std::unique_ptr<CanonicalQuery> = nullptr);
/**
* Returns a PlanExecutor for the given CanonicalQuery.
*/
std::unique_ptr<PlanExecutor, PlanExecutor::Deleter> getSBEExecutorViaCascadesOptimizer(
- const CollectionPtr& collection,
- std::unique_ptr<CanonicalQuery> query,
- bool requireRID = false);
+ const CollectionPtr& collection, std::unique_ptr<CanonicalQuery> query);
} // namespace mongo
diff --git a/src/mongo/db/query/get_executor.cpp b/src/mongo/db/query/get_executor.cpp
index e2f0fc91726..3e8653eb229 100644
--- a/src/mongo/db/query/get_executor.cpp
+++ b/src/mongo/db/query/get_executor.cpp
@@ -1437,10 +1437,7 @@ StatusWith<std::unique_ptr<PlanExecutor, PlanExecutor::Deleter>> getExecutor(
sbe::isQuerySbeCompatible(&mainColl, canonicalQuery.get(), plannerParams.options));
if (isEligibleForBonsai(*canonicalQuery, opCtx, mainColl)) {
- return getSBEExecutorViaCascadesOptimizer(mainColl,
- std::move(canonicalQuery),
- plannerParams.options &
- QueryPlannerParams::PRESERVE_RECORD_ID);
+ return getSBEExecutorViaCascadesOptimizer(mainColl, std::move(canonicalQuery));
}
// Use SBE if 'canonicalQuery' is SBE compatible.
@@ -1716,8 +1713,9 @@ StatusWith<std::unique_ptr<PlanExecutor, PlanExecutor::Deleter>> getExecutorDele
// The underlying query plan must preserve the record id, since it will be needed in order to
// identify the record to update.
- const size_t defaultPlannerOptions = QueryPlannerParams::PRESERVE_RECORD_ID;
+ cq->setForceGenerateRecordId(true);
+ const size_t defaultPlannerOptions = QueryPlannerParams::DEFAULT;
ClassicPrepareExecutionHelper helper{
opCtx, collection, ws.get(), cq.get(), nullptr, defaultPlannerOptions};
auto executionResult = helper.prepare();
@@ -1907,8 +1905,9 @@ StatusWith<std::unique_ptr<PlanExecutor, PlanExecutor::Deleter>> getExecutorUpda
// The underlying query plan must preserve the record id, since it will be needed in order to
// identify the record to update.
- const size_t defaultPlannerOptions = QueryPlannerParams::PRESERVE_RECORD_ID;
+ cq->setForceGenerateRecordId(true);
+ const size_t defaultPlannerOptions = QueryPlannerParams::DEFAULT;
ClassicPrepareExecutionHelper helper{
opCtx, collection, ws.get(), cq.get(), nullptr, defaultPlannerOptions};
auto executionResult = helper.prepare();
diff --git a/src/mongo/db/query/planner_analysis.cpp b/src/mongo/db/query/planner_analysis.cpp
index 4b19c1fac59..7cbc7116a92 100644
--- a/src/mongo/db/query/planner_analysis.cpp
+++ b/src/mongo/db/query/planner_analysis.cpp
@@ -585,7 +585,7 @@ bool canUseSimpleSort(const QuerySolutionNode& solnRoot,
// record ids along through the sorting process is wasted work when these ids will never be
// consumed later in the execution of the query. If the record ids are needed, however, then
// we can't use the simple sort stage.
- !(plannerParams.options & QueryPlannerParams::PRESERVE_RECORD_ID);
+ !cq.getForceGenerateRecordId();
}
boost::optional<const projection_ast::Projection*> attemptToGetProjectionFromQuerySolution(
diff --git a/src/mongo/db/query/query_planner.cpp b/src/mongo/db/query/query_planner.cpp
index c0e787c6d99..b330ad4bedf 100644
--- a/src/mongo/db/query/query_planner.cpp
+++ b/src/mongo/db/query/query_planner.cpp
@@ -463,9 +463,6 @@ string optionString(size_t options) {
case QueryPlannerParams::STRICT_DISTINCT_ONLY:
ss << "STRICT_DISTINCT_ONLY ";
break;
- case QueryPlannerParams::PRESERVE_RECORD_ID:
- ss << "PRESERVE_RECORD_ID ";
- break;
case QueryPlannerParams::ASSERT_MIN_TS_HAS_NOT_FALLEN_OFF_OPLOG:
ss << "ASSERT_MIN_TS_HAS_NOT_FALLEN_OFF_OPLOG ";
break;
diff --git a/src/mongo/db/query/query_planner_options_test.cpp b/src/mongo/db/query/query_planner_options_test.cpp
index 7471af9e7a0..7f4cb9e3a1b 100644
--- a/src/mongo/db/query/query_planner_options_test.cpp
+++ b/src/mongo/db/query/query_planner_options_test.cpp
@@ -859,7 +859,7 @@ TEST_F(QueryPlannerTest, DollarResumeAfterFieldPropagatedFromQueryRequestToStage
}
TEST_F(QueryPlannerTest, PreserveRecordIdOptionPrecludesSimpleSort) {
- params.options |= QueryPlannerParams::PRESERVE_RECORD_ID;
+ forceRecordId = true;
runQueryAsCommand(fromjson("{find: 'testns', sort: {a:1}}"));
diff --git a/src/mongo/db/query/query_planner_params.h b/src/mongo/db/query/query_planner_params.h
index c826a92eb12..d055a1ef832 100644
--- a/src/mongo/db/query/query_planner_params.h
+++ b/src/mongo/db/query/query_planner_params.h
@@ -125,15 +125,9 @@ struct QueryPlannerParams {
// declaration of getExecutorDistinct() for more detail.
STRICT_DISTINCT_ONLY = 1 << 8,
- // Instruct the planner that the caller is expecting to consume the record ids associated
- // with documents returned by the plan. Any generated query solution must not discard record
- // ids. In some cases, record ids can be discarded as an optimization when they will not be
- // consumed downstream.
- PRESERVE_RECORD_ID = 1 << 9,
-
// Set this on an oplog scan to uassert that the oplog has not already rolled over the
// minimum 'ts' timestamp specified in the query.
- ASSERT_MIN_TS_HAS_NOT_FALLEN_OFF_OPLOG = 1 << 10,
+ ASSERT_MIN_TS_HAS_NOT_FALLEN_OFF_OPLOG = 1 << 9,
// Instruct the plan enumerator to enumerate contained $ors in a special order. $or
// enumeration can generate an exponential number of plans, and is therefore limited at some
@@ -150,16 +144,16 @@ struct QueryPlannerParams {
// order, we would get assignments [a_b, a_b], [a_c, a_c], [a_c, a_b], then [a_b, a_c]. This
// is thought to be helpful in general, but particularly in cases where all children of the
// $or use the same fields and have the same indexes available, as in this example.
- ENUMERATE_OR_CHILDREN_LOCKSTEP = 1 << 11,
+ ENUMERATE_OR_CHILDREN_LOCKSTEP = 1 << 10,
// Ensure that any plan generated returns data that is "owned." That is, all BSONObjs are
// in an "owned" state and are not pointing to data that belongs to the storage engine.
- RETURN_OWNED_DATA = 1 << 12,
+ RETURN_OWNED_DATA = 1 << 11,
// When generating column scan queries, splits match expressions so that the filters can be
// applied per-column. This is off by default, since the execution side doesn't support it
// yet.
- GENERATE_PER_COLUMN_FILTERS = 1 << 13,
+ GENERATE_PER_COLUMN_FILTERS = 1 << 12,
};
// See Options enum above.
diff --git a/src/mongo/db/query/query_planner_test_fixture.cpp b/src/mongo/db/query/query_planner_test_fixture.cpp
index 6db2512950e..e31cc6ecd98 100644
--- a/src/mongo/db/query/query_planner_test_fixture.cpp
+++ b/src/mongo/db/query/query_planner_test_fixture.cpp
@@ -368,6 +368,7 @@ void QueryPlannerTest::runQueryFull(
ASSERT_OK(statusWithCQ.getStatus());
cq = std::move(statusWithCQ.getValue());
cq->setSbeCompatible(markQueriesSbeCompatible);
+ cq->setForceGenerateRecordId(forceRecordId);
auto statusWithMultiPlanSolns = QueryPlanner::plan(*cq, params);
ASSERT_OK(statusWithMultiPlanSolns.getStatus());
@@ -445,6 +446,7 @@ void QueryPlannerTest::runInvalidQueryFull(const BSONObj& query,
ASSERT_OK(statusWithCQ.getStatus());
cq = std::move(statusWithCQ.getValue());
cq->setSbeCompatible(markQueriesSbeCompatible);
+ cq->setForceGenerateRecordId(forceRecordId);
auto statusWithMultiPlanSolns = QueryPlanner::plan(*cq, params);
plannerStatus = statusWithMultiPlanSolns.getStatus();
@@ -474,6 +476,7 @@ void QueryPlannerTest::runQueryAsCommand(const BSONObj& cmdObj) {
ASSERT_OK(statusWithCQ.getStatus());
cq = std::move(statusWithCQ.getValue());
cq->setSbeCompatible(markQueriesSbeCompatible);
+ cq->setForceGenerateRecordId(forceRecordId);
auto statusWithMultiPlanSolns = QueryPlanner::plan(*cq, params);
ASSERT_OK(statusWithMultiPlanSolns.getStatus());
@@ -502,6 +505,7 @@ void QueryPlannerTest::runInvalidQueryAsCommand(const BSONObj& cmdObj) {
ASSERT_OK(statusWithCQ.getStatus());
cq = std::move(statusWithCQ.getValue());
cq->setSbeCompatible(markQueriesSbeCompatible);
+ cq->setForceGenerateRecordId(forceRecordId);
auto statusWithMultiPlanSolns = QueryPlanner::plan(*cq, params);
plannerStatus = statusWithMultiPlanSolns.getStatus();
diff --git a/src/mongo/db/query/query_planner_test_fixture.h b/src/mongo/db/query/query_planner_test_fixture.h
index 44f51282d55..35cc0d0c294 100644
--- a/src/mongo/db/query/query_planner_test_fixture.h
+++ b/src/mongo/db/query/query_planner_test_fixture.h
@@ -269,7 +269,12 @@ protected:
std::vector<std::unique_ptr<QuerySolution>> solns;
bool relaxBoundsCheck = false;
+ // Value used for the sbeCompatible flag in the CanonicalQuery objects created by the
+ // test.
bool markQueriesSbeCompatible = false;
+ // Value used for the forceGenerateRecordId flag in the CanonicalQuery objects created by the
+ // test.
+ bool forceRecordId = false;
};
} // namespace mongo
diff --git a/src/mongo/db/query/sbe_stage_builder.cpp b/src/mongo/db/query/sbe_stage_builder.cpp
index ab20b24f502..e21b72d0c0c 100644
--- a/src/mongo/db/query/sbe_stage_builder.cpp
+++ b/src/mongo/db/query/sbe_stage_builder.cpp
@@ -473,26 +473,6 @@ SlotBasedStageBuilder::SlotBasedStageBuilder(OperationContext* opCtx,
_data.shouldTrackResumeToken = csn->requestResumeToken;
_data.shouldUseTailableScan = csn->tailable;
}
-
- for (const auto& node : getAllNodesByType(solution.root(), STAGE_VIRTUAL_SCAN)) {
- auto vsn = static_cast<const VirtualScanNode*>(node);
- if (!vsn->hasRecordId) {
- _shouldProduceRecordIdSlot = false;
- break;
- }
- }
-
- const auto [lookupNode, lookupCount] = getFirstNodeByType(solution.root(), STAGE_EQ_LOOKUP);
- if (lookupCount) {
- // TODO: SERVER-63604 optimize _shouldProduceRecordIdSlot maintenance
- _shouldProduceRecordIdSlot = false;
- }
-
- const auto [groupNode, groupCount] = getFirstNodeByType(solution.root(), STAGE_GROUP);
- if (groupCount) {
- // TODO: SERVER-63604 optimize _shouldProduceRecordIdSlot maintenance
- _shouldProduceRecordIdSlot = false;
- }
}
std::unique_ptr<sbe::PlanStage> SlotBasedStageBuilder::build(const QuerySolutionNode* root) {
@@ -500,11 +480,15 @@ std::unique_ptr<sbe::PlanStage> SlotBasedStageBuilder::build(const QuerySolution
invariant(!_buildHasStarted);
_buildHasStarted = true;
- // We always produce a 'resultSlot' and conditionally produce a 'recordIdSlot' based on the
- // 'shouldProduceRecordIdSlot'.
+ // We always produce a 'resultSlot'.
PlanStageReqs reqs;
reqs.set(kResult);
- reqs.setIf(kRecordId, _shouldProduceRecordIdSlot);
+ // We force the root stage to produce a 'recordId' if the iteration can be
+ // resumed (via a resume token or a tailable cursor) or if the caller simply expects to be able
+ // to read it.
+ reqs.setIf(kRecordId,
+ (_data.shouldUseTailableScan || _data.shouldTrackResumeToken ||
+ _cq.getForceGenerateRecordId()));
// Set the target namespace to '_mainNss'. This is necessary as some QuerySolutionNodes that
// require a collection when stage building do not explicitly name which collection they are
@@ -514,11 +498,10 @@ std::unique_ptr<sbe::PlanStage> SlotBasedStageBuilder::build(const QuerySolution
// Build the SBE plan stage tree.
auto [stage, outputs] = build(root, reqs);
- // Assert that we produced a 'resultSlot' and that we prouced a 'recordIdSlot' if the
- // 'shouldProduceRecordIdSlot' flag was set. Also assert that we produced an 'oplogTsSlot' if
- // it's needed.
+ // Assert that we produced a 'resultSlot' and that we produced a 'recordIdSlot' only if it was
+ // needed.
invariant(outputs.has(kResult));
- invariant(!_shouldProduceRecordIdSlot || outputs.has(kRecordId));
+ invariant(reqs.has(kRecordId) == outputs.has(kRecordId));
_data.outputs = std::move(outputs);
@@ -544,6 +527,10 @@ std::pair<std::unique_ptr<sbe::PlanStage>, PlanStageSlots> SlotBasedStageBuilder
outputs.get(kReturnKey),
sbe::makeE<sbe::EFunction>("newObj", sbe::makeEs()));
}
+ // Don't advertize the RecordId output if none of our ancestors are going to use it.
+ if (!reqs.has(kRecordId)) {
+ outputs.clear(kRecordId);
+ }
return {std::move(stage), std::move(outputs)};
}
@@ -659,6 +646,10 @@ std::pair<std::unique_ptr<sbe::PlanStage>, PlanStageSlots> SlotBasedStageBuilder
iamMap,
reqs.has(kIndexKeyPattern));
+ // Remove the RecordId from the output if we were not requested to produce it.
+ if (!reqs.has(PlanStageSlots::kRecordId) && outputs.has(kRecordId)) {
+ outputs.clear(kRecordId);
+ }
if (reqs.has(PlanStageSlots::kReturnKey)) {
sbe::EExpression::Vector mkObjArgs;
@@ -984,10 +975,15 @@ std::pair<std::unique_ptr<sbe::PlanStage>, PlanStageSlots> SlotBasedStageBuilder
_slotIdGenerator);
outputs.set(kResult, fetchResultSlot);
- outputs.set(kRecordId, fetchRecordIdSlot);
+ // Propagate the RecordId output only if requested.
+ if (reqs.has(kRecordId)) {
+ outputs.set(kRecordId, fetchRecordIdSlot);
+ } else {
+ outputs.clear(kRecordId);
+ }
if (fn->filter) {
- forwardingReqs = reqs.copy().set(kResult).set(kRecordId);
+ forwardingReqs = reqs.copy().set(kResult);
relevantSlots = sbe::makeSV();
outputs.forEachSlot(forwardingReqs, [&](auto&& slot) { relevantSlots.push_back(slot); });
@@ -1676,6 +1672,10 @@ std::pair<std::unique_ptr<sbe::PlanStage>, PlanStageSlots> SlotBasedStageBuilder
if (mergeSortNode->dedup) {
stage = sbe::makeS<sbe::UniqueStage>(
std::move(stage), sbe::makeSV(outputs.get(kRecordId)), root->nodeId());
+ // Stop propagating the RecordId output if none of our ancestors are going to use it.
+ if (!reqs.has(kRecordId)) {
+ outputs.clear(kRecordId);
+ }
}
return {std::move(stage), std::move(outputs)};
@@ -1894,6 +1894,10 @@ std::pair<std::unique_ptr<sbe::PlanStage>, PlanStageSlots> SlotBasedStageBuilder
if (orn->dedup) {
stage = sbe::makeS<sbe::UniqueStage>(
std::move(stage), sbe::makeSV(outputs.get(kRecordId)), root->nodeId());
+ // Stop propagating the RecordId output if none of our ancestors are going to use it.
+ if (!reqs.has(kRecordId)) {
+ outputs.clear(kRecordId);
+ }
}
if (orn->filter) {
@@ -2086,6 +2090,10 @@ std::pair<std::unique_ptr<sbe::PlanStage>, PlanStageSlots> SlotBasedStageBuilder
collatorSlot,
root->nodeId());
}
+ // Stop propagating the RecordId output if none of our ancestors are going to use it.
+ if (!reqs.has(kRecordId)) {
+ outputs.clear(kRecordId);
+ }
return {std::move(hashJoinStage), std::move(outputs)};
}
@@ -2195,6 +2203,10 @@ std::pair<std::unique_ptr<sbe::PlanStage>, PlanStageSlots> SlotBasedStageBuilder
sortDirs,
root->nodeId());
}
+ // Stop propagating the RecordId output if none of our ancestors are going to use it.
+ if (!reqs.has(kRecordId)) {
+ outputs.clear(kRecordId);
+ }
return {std::move(mergeJoinStage), std::move(outputs)};
}
@@ -2619,6 +2631,10 @@ std::pair<std::unique_ptr<sbe::PlanStage>, PlanStageSlots> SlotBasedStageBuilder
tassert(
5851600, "should have one and only one child for GROUP", groupNode->children.size() == 1);
tassert(5851601, "GROUP should have had group-by key expression", idExpr);
+ tassert(
+ 6360401,
+ "GROUP cannot propagate a record id slot, but the record id was requested by the parent",
+ !reqs.has(kRecordId));
const auto& childNode = groupNode->children[0].get();
const auto& accStmts = groupNode->accumulators;
@@ -2634,7 +2650,6 @@ std::pair<std::unique_ptr<sbe::PlanStage>, PlanStageSlots> SlotBasedStageBuilder
// Builds the child and gets the child result slot.
auto [childStage, childOutputs] = build(childNode, childReqs);
- _shouldProduceRecordIdSlot = false;
tassert(6075900,
"Expected no optimized expressions but got: {}"_format(_state.preGeneratedExprs.size()),
diff --git a/src/mongo/db/query/sbe_stage_builder.h b/src/mongo/db/query/sbe_stage_builder.h
index ab928f14329..1507069fe90 100644
--- a/src/mongo/db/query/sbe_stage_builder.h
+++ b/src/mongo/db/query/sbe_stage_builder.h
@@ -541,7 +541,6 @@ private:
PlanStageData _data;
bool _buildHasStarted{false};
- bool _shouldProduceRecordIdSlot{true};
// Common parameters to SBE stage builder functions.
StageBuilderState _state;
diff --git a/src/mongo/db/query/sbe_stage_builder_lookup.cpp b/src/mongo/db/query/sbe_stage_builder_lookup.cpp
index 3b35fb71ef6..aa4ae7fbde3 100644
--- a/src/mongo/db/query/sbe_stage_builder_lookup.cpp
+++ b/src/mongo/db/query/sbe_stage_builder_lookup.cpp
@@ -1063,9 +1063,6 @@ std::pair<std::unique_ptr<sbe::PlanStage>, PlanStageSlots> SlotBasedStageBuilder
_state.data->foreignHashJoinCollections.emplace(eqLookupNode->foreignCollection);
}
- // $lookup creates its own output documents.
- _shouldProduceRecordIdSlot = false;
-
auto localReqs = reqs.copy().set(kResult);
auto [localStage, localOutputs] = build(eqLookupNode->children[0].get(), localReqs);
SlotId localDocumentSlot = localOutputs.get(PlanStageSlots::kResult);
diff --git a/src/mongo/db/query/sbe_stage_builder_test_fixture.cpp b/src/mongo/db/query/sbe_stage_builder_test_fixture.cpp
index c6b0525dea9..91042b7b096 100644
--- a/src/mongo/db/query/sbe_stage_builder_test_fixture.cpp
+++ b/src/mongo/db/query/sbe_stage_builder_test_fixture.cpp
@@ -60,6 +60,10 @@ SbeStageBuilderTestFixture::buildPlanStage(
auto statusWithCQ =
CanonicalQuery::canonicalize(operationContext(), std::move(findCommand), false, expCtx);
ASSERT_OK(statusWithCQ.getStatus());
+ if (hasRecordId) {
+ // Force the builder to generate the RecordId output even if it isn't needed by the plan.
+ statusWithCQ.getValue()->setForceGenerateRecordId(true);
+ }
CollectionMock coll(_nss);
CollectionPtr collPtr(&coll);
diff --git a/src/mongo/db/test_output/query/canonical_query_encoder_test/compute_key_s_b_e.txt b/src/mongo/db/test_output/query/canonical_query_encoder_test/compute_key_s_b_e.txt
index f7f248ddb74..8d7a52033c4 100644
--- a/src/mongo/db/test_output/query/canonical_query_encoder_test/compute_key_s_b_e.txt
+++ b/src/mongo/db/test_output/query/canonical_query_encoder_test/compute_key_s_b_e.txt
@@ -1,48 +1,48 @@
==== VARIATION: sbe, query={}, sort={}, proj={}
-YW4ABQAAAAAAAAAAAAAAAG5ubm4FAAAAAGZe
+YW4ABQAAAAAAAAAAAAAAAABubm5uBQAAAABmXg==
==== VARIATION: sbe, query={$or: [{a: 1}, {b: 2}]}, sort={}, proj={}
-b3IAW2VxAGE/AAAAACxlcQBiPwEAAABdBQAAAAAAAAAAAAAAAG5ubm4FAAAAAGZe
+b3IAW2VxAGE/AAAAACxlcQBiPwEAAABdBQAAAAAAAAAAAAAAAABubm5uBQAAAABmXg==
==== VARIATION: sbe, query={a: 1}, sort={}, proj={}
-ZXEAYT8AAAAABQAAAAAAAAAAAAAAAG5ubm4FAAAAAGZe
+ZXEAYT8AAAAABQAAAAAAAAAAAAAAAABubm5uBQAAAABmXg==
==== VARIATION: sbe, query={b: 1}, sort={}, proj={}
-ZXEAYj8AAAAABQAAAAAAAAAAAAAAAG5ubm4FAAAAAGZe
+ZXEAYj8AAAAABQAAAAAAAAAAAAAAAABubm5uBQAAAABmXg==
==== VARIATION: sbe, query={a: 1, b: 1, c: 1}, sort={}, proj={}
-YW4AW2VxAGE/AAAAACxlcQBiPwEAAAAsZXEAYz8CAAAAXQUAAAAAAAAAAAAAAABubm5uBQAAAABmXg==
+YW4AW2VxAGE/AAAAACxlcQBiPwEAAAAsZXEAYz8CAAAAXQUAAAAAAAAAAAAAAAAAbm5ubgUAAAAAZl4=
==== VARIATION: sbe, query={}, sort={a: 1}, proj={}
-YW4ABQAAAAB+YWEAAAAAAAAAAG5ubm4FAAAAAGZe
+YW4ABQAAAAB+YWEAAAAAAAAAAABubm5uBQAAAABmXg==
==== VARIATION: sbe, query={}, sort={a: -1}, proj={}
-YW4ABQAAAAB+ZGEAAAAAAAAAAG5ubm4FAAAAAGZe
+YW4ABQAAAAB+ZGEAAAAAAAAAAABubm5uBQAAAABmXg==
==== VARIATION: sbe, query={a: 1}, sort={a: 1}, proj={}
-ZXEAYT8AAAAABQAAAAB+YWEAAAAAAAAAAG5ubm4FAAAAAGZe
+ZXEAYT8AAAAABQAAAAB+YWEAAAAAAAAAAABubm5uBQAAAABmXg==
==== VARIATION: sbe, query={a: 1}, sort={a: 1}, proj={a: 1}
-ZXEAYT8AAAAADAAAABBhAAEAAAAAfmFhAAAAAAAAAABubm5uBQAAAABmXg==
+ZXEAYT8AAAAADAAAABBhAAEAAAAAfmFhAAAAAAAAAAAAbm5ubgUAAAAAZl4=
==== VARIATION: sbe, query={}, sort={a: 1}, proj={a: 1}
-YW4ADAAAABBhAAEAAAAAfmFhAAAAAAAAAABubm5uBQAAAABmXg==
+YW4ADAAAABBhAAEAAAAAfmFhAAAAAAAAAAAAbm5ubgUAAAAAZl4=
==== VARIATION: sbe, query={}, sort={a: 1}, proj={a: 1}
-YW4ADAAAABBhAAEAAAAAfmFhAAAAAAAAAABubm5uBQAAAABmXg==
+YW4ADAAAABBhAAEAAAAAfmFhAAAAAAAAAAAAbm5ubgUAAAAAZl4=
==== VARIATION: sbe, query={}, sort={}, proj={a: 1}
-YW4ADAAAABBhAAEAAAAAAAAAAAAAAABubm5uBQAAAABmXg==
+YW4ADAAAABBhAAEAAAAAAAAAAAAAAAAAbm5ubgUAAAAAZl4=
==== VARIATION: sbe, query={}, sort={}, proj={a: true}
-YW4ACQAAAAhhAAEAAAAAAAAAAABubm5uBQAAAABmXg==
+YW4ACQAAAAhhAAEAAAAAAAAAAAAAbm5ubgUAAAAAZl4=
==== VARIATION: sbe, query={}, sort={}, proj={a: false}
-YW4ACQAAAAhhAAAAAAAAAAAAAABubm5uBQAAAABmXg==
+YW4ACQAAAAhhAAAAAAAAAAAAAAAAbm5ubgUAAAAAZl4=
==== VARIATION: sbe, query={a: 1}, sort={a: 1}, proj={}, allowDiskUse=0, returnKey=0, requestResumeToken=0
-ZXEAYT8AAAAABQAAAAB+YWEAAAAAAAAAAG5ubm4FAAAAAGZe
+ZXEAYT8AAAAABQAAAAB+YWEAAAAAAAAAAABubm5uBQAAAABmXg==
==== VARIATION: sbe, query={a: 1}, sort={a: 1}, proj={}, allowDiskUse=1, returnKey=0, requestResumeToken=0
-ZXEAYT8AAAAABQAAAAB+YWEAAAAAAAAAAHRubm4FAAAAAGZe
+ZXEAYT8AAAAABQAAAAB+YWEAAAAAAAAAAAB0bm5uBQAAAABmXg==
==== VARIATION: sbe, query={a: 1}, sort={a: 1}, proj={}, allowDiskUse=0, returnKey=0, requestResumeToken=0
-ZXEAYT8AAAAABQAAAAB+YWEAAAAAAAAAAGZubm4FAAAAAGZe
+ZXEAYT8AAAAABQAAAAB+YWEAAAAAAAAAAABmbm5uBQAAAABmXg==
==== VARIATION: sbe, query={a: 1}, sort={a: 1}, proj={}, allowDiskUse=0, returnKey=1, requestResumeToken=0
-ZXEAYT8AAAAABQAAAAB+YWEAAAAAAAAAAG50bm4FAAAAAGZe
+ZXEAYT8AAAAABQAAAAB+YWEAAAAAAAAAAABudG5uBQAAAABmXg==
==== VARIATION: sbe, query={a: 1}, sort={a: 1}, proj={}, allowDiskUse=0, returnKey=0, requestResumeToken=0
-ZXEAYT8AAAAABQAAAAB+YWEAAAAAAAAAAG5uZm4FAAAAAGZe
+ZXEAYT8AAAAABQAAAAB+YWEAAAAAAAAAAABubmZuBQAAAABmXg==
==== VARIATION: sbe, query={a: 1}, sort={a: 1}, proj={}, allowDiskUse=0, returnKey=0, requestResumeToken=0
-ZXEAYT8AAAAABQAAAAB+YWEKAAAAAAAAAAAAAABubm5uBQAAAABmXg==
+ZXEAYT8AAAAABQAAAAB+YWEACgAAAAAAAAAAAAAAbm5ubgUAAAAAZl4=
==== VARIATION: sbe, query={a: 1}, sort={a: 1}, proj={}, allowDiskUse=0, returnKey=0, requestResumeToken=0
-ZXEAYT8AAAAABQAAAAB+YWEAAAAACgAAAAAAAABubm5uBQAAAABmXg==
+ZXEAYT8AAAAABQAAAAB+YWEAAAAAAAoAAAAAAAAAbm5ubgUAAAAAZl4=
==== VARIATION: sbe, query={a: 1}, sort={a: 1}, proj={}, allowDiskUse=0, returnKey=0, requestResumeToken=0
-ZXEAYT8AAAAABQAAAAB+YWEAAAAAAAAAAG5ubm4FAAAAAGZe
+ZXEAYT8AAAAABQAAAAB+YWEAAAAAAAAAAABubm5uBQAAAABmXg==
==== VARIATION: sbe, query={a: 1}, sort={a: 1}, proj={}, allowDiskUse=0, returnKey=0, requestResumeToken=0
-ZXEAYT8AAAAABQAAAAB+YWEAAAAAAAAAAG5ubm4FAAAAAGZe
+ZXEAYT8AAAAABQAAAAB+YWEAAAAAAAAAAABubm5uBQAAAABmXg==
==== VARIATION: sbe, query={a: 1}, sort={}, proj={}, allowDiskUse=0, returnKey=0, requestResumeToken=1
-ZXEAYT8AAAAABQAAAAAAAAAAAAAAAG5udG4YAAAAEiRyZWNvcmRJZAABAAAAAAAAAABmXg==
+ZXEAYT8AAAAABQAAAAAAAAAAAAAAAABubnRuGAAAABIkcmVjb3JkSWQAAQAAAAAAAAAAZl4=
diff --git a/src/mongo/db/test_output/query/canonical_query_encoder_test/compute_key_s_b_e_with_pipeline.txt b/src/mongo/db/test_output/query/canonical_query_encoder_test/compute_key_s_b_e_with_pipeline.txt
index 15084d41d0e..532045bed16 100644
--- a/src/mongo/db/test_output/query/canonical_query_encoder_test/compute_key_s_b_e_with_pipeline.txt
+++ b/src/mongo/db/test_output/query/canonical_query_encoder_test/compute_key_s_b_e_with_pipeline.txt
@@ -1,12 +1,12 @@
==== VARIATION: sbe, query={a: 1}, sort={}, proj={}
-ZXEAYT8AAAAABQAAAAAAAAAAAAAAAG5ubm4FAAAAAGZe
+ZXEAYT8AAAAABQAAAAAAAAAAAAAAAABubm5uBQAAAABmXg==
==== VARIATION: sbe, query={a: 1}, sort={}, proj={}
-ZXEAYT8AAAAABQAAAAAAAAAAAAAAAG5ubm4FAAAAAGZeWgAAAAMkbG9va3VwAEwAAAACZnJvbQAMAAAAZm9yZWlnbmNvbGwAAmFzAAMAAABhcwACbG9jYWxGaWVsZAACAAAAYQACZm9yZWlnbkZpZWxkAAIAAABiAAAA
+ZXEAYT8AAAAABQAAAAAAAAAAAAAAAABubm5uBQAAAABmXloAAAADJGxvb2t1cABMAAAAAmZyb20ADAAAAGZvcmVpZ25jb2xsAAJhcwADAAAAYXMAAmxvY2FsRmllbGQAAgAAAGEAAmZvcmVpZ25GaWVsZAACAAAAYgAAAA==
==== VARIATION: sbe, query={a: 1}, sort={}, proj={}
-ZXEAYT8AAAAABQAAAAAAAAAAAAAAAG5ubm4FAAAAAGZeWwAAAAMkbG9va3VwAE0AAAACZnJvbQAMAAAAZm9yZWlnbmNvbGwAAmFzAAMAAABhcwACbG9jYWxGaWVsZAADAAAAYTEAAmZvcmVpZ25GaWVsZAACAAAAYgAAAA==
+ZXEAYT8AAAAABQAAAAAAAAAAAAAAAABubm5uBQAAAABmXlsAAAADJGxvb2t1cABNAAAAAmZyb20ADAAAAGZvcmVpZ25jb2xsAAJhcwADAAAAYXMAAmxvY2FsRmllbGQAAwAAAGExAAJmb3JlaWduRmllbGQAAgAAAGIAAAA=
==== VARIATION: sbe, query={a: 1}, sort={}, proj={}
-ZXEAYT8AAAAABQAAAAAAAAAAAAAAAG5ubm4FAAAAAGZeWwAAAAMkbG9va3VwAE0AAAACZnJvbQAMAAAAZm9yZWlnbmNvbGwAAmFzAAMAAABhcwACbG9jYWxGaWVsZAACAAAAYQACZm9yZWlnbkZpZWxkAAMAAABiMQAAAA==
+ZXEAYT8AAAAABQAAAAAAAAAAAAAAAABubm5uBQAAAABmXlsAAAADJGxvb2t1cABNAAAAAmZyb20ADAAAAGZvcmVpZ25jb2xsAAJhcwADAAAAYXMAAmxvY2FsRmllbGQAAgAAAGEAAmZvcmVpZ25GaWVsZAADAAAAYjEAAAA=
==== VARIATION: sbe, query={a: 1}, sort={}, proj={}
-ZXEAYT8AAAAABQAAAAAAAAAAAAAAAG5ubm4FAAAAAGZeWwAAAAMkbG9va3VwAE0AAAACZnJvbQAMAAAAZm9yZWlnbmNvbGwAAmFzAAQAAABhczEAAmxvY2FsRmllbGQAAgAAAGEAAmZvcmVpZ25GaWVsZAACAAAAYgAAAA==
+ZXEAYT8AAAAABQAAAAAAAAAAAAAAAABubm5uBQAAAABmXlsAAAADJGxvb2t1cABNAAAAAmZyb20ADAAAAGZvcmVpZ25jb2xsAAJhcwAEAAAAYXMxAAJsb2NhbEZpZWxkAAIAAABhAAJmb3JlaWduRmllbGQAAgAAAGIAAAA=
==== VARIATION: sbe, query={a: 1}, sort={}, proj={}
-ZXEAYT8AAAAABQAAAAAAAAAAAAAAAG5ubm4FAAAAAGZeWgAAAAMkbG9va3VwAEwAAAACZnJvbQAMAAAAZm9yZWlnbmNvbGwAAmFzAAMAAABhcwACbG9jYWxGaWVsZAACAAAAYQACZm9yZWlnbkZpZWxkAAIAAABiAAAAXQAAAAMkbG9va3VwAE8AAAACZnJvbQAMAAAAZm9yZWlnbmNvbGwAAmFzAAQAAABhczEAAmxvY2FsRmllbGQAAwAAAGExAAJmb3JlaWduRmllbGQAAwAAAGIxAAAA
+ZXEAYT8AAAAABQAAAAAAAAAAAAAAAABubm5uBQAAAABmXloAAAADJGxvb2t1cABMAAAAAmZyb20ADAAAAGZvcmVpZ25jb2xsAAJhcwADAAAAYXMAAmxvY2FsRmllbGQAAgAAAGEAAmZvcmVpZ25GaWVsZAACAAAAYgAAAF0AAAADJGxvb2t1cABPAAAAAmZyb20ADAAAAGZvcmVpZ25jb2xsAAJhcwAEAAAAYXMxAAJsb2NhbEZpZWxkAAMAAABhMQACZm9yZWlnbkZpZWxkAAMAAABiMQAAAA==
diff --git a/src/mongo/db/test_output/query/canonical_query_encoder_test/compute_key_s_b_e_with_read_concern.txt b/src/mongo/db/test_output/query/canonical_query_encoder_test/compute_key_s_b_e_with_read_concern.txt
index 5e494c83f72..8177ce2bbc8 100644
--- a/src/mongo/db/test_output/query/canonical_query_encoder_test/compute_key_s_b_e_with_read_concern.txt
+++ b/src/mongo/db/test_output/query/canonical_query_encoder_test/compute_key_s_b_e_with_read_concern.txt
@@ -1,6 +1,6 @@
==== VARIATION: sbe, query={a: 1}, sort={a: 1}, proj={}, allowDiskUse=0, returnKey=0, requestResumeToken=0
-ZXEAYT8AAAAABQAAAAB+YWEAAAAAAAAAAG5ubm4FAAAAAGZe
+ZXEAYT8AAAAABQAAAAB+YWEAAAAAAAAAAABubm5uBQAAAABmXg==
==== VARIATION: sbe, query={a: 1}, sort={a: 1}, proj={}, allowDiskUse=0, returnKey=0, requestResumeToken=0
-ZXEAYT8AAAAABQAAAAB+YWEAAAAAAAAAAG5ubm4FAAAAAGZe
+ZXEAYT8AAAAABQAAAAB+YWEAAAAAAAAAAABubm5uBQAAAABmXg==
==== VARIATION: sbe, query={a: 1}, sort={a: 1}, proj={}, allowDiskUse=0, returnKey=0, requestResumeToken=0
-ZXEAYT8AAAAABQAAAAB+YWEAAAAAAAAAAG5ubm4FAAAAAHRe
+ZXEAYT8AAAAABQAAAAB+YWEAAAAAAAAAAABubm5uBQAAAAB0Xg==