diff options
Diffstat (limited to 'src/mongo/db/query/sbe_stage_builder.cpp')
-rw-r--r-- | src/mongo/db/query/sbe_stage_builder.cpp | 106 |
1 files changed, 68 insertions, 38 deletions
diff --git a/src/mongo/db/query/sbe_stage_builder.cpp b/src/mongo/db/query/sbe_stage_builder.cpp index 9c9312b2c5f..1fdf6fb72fb 100644 --- a/src/mongo/db/query/sbe_stage_builder.cpp +++ b/src/mongo/db/query/sbe_stage_builder.cpp @@ -318,7 +318,6 @@ SlotBasedStageBuilder::makeLoopJoinForFetch(std::unique_ptr<sbe::PlanStage> inpu std::pair<std::unique_ptr<sbe::PlanStage>, PlanStageSlots> SlotBasedStageBuilder::buildFetch( const QuerySolutionNode* root, const PlanStageReqs& reqs) { auto fn = static_cast<const FetchNode*>(root); - invariant(!reqs.getIndexKeyBitset()); // At present, makeLoopJoinForFetch() doesn't have the necessary logic for producing an // oplogTsSlot, so assert that the caller doesn't need oplogTsSlot. @@ -341,6 +340,11 @@ std::pair<std::unique_ptr<sbe::PlanStage>, PlanStageSlots> SlotBasedStageBuilder auto relevantSlots = sbe::makeSV(); outputs.forEachSlot(forwardingReqs, [&](auto&& slot) { relevantSlots.push_back(slot); }); + // Forward slots for components of the index key if our parent requested them. + if (auto indexKeySlots = outputs.getIndexKeySlots()) { + relevantSlots.insert(relevantSlots.end(), indexKeySlots->begin(), indexKeySlots->end()); + } + sbe::value::SlotId fetchResultSlot, fetchRecordIdSlot; std::tie(fetchResultSlot, fetchRecordIdSlot, stage) = makeLoopJoinForFetch( std::move(stage), outputs.get(kRecordId), root->nodeId(), std::move(relevantSlots)); @@ -354,6 +358,10 @@ std::pair<std::unique_ptr<sbe::PlanStage>, PlanStageSlots> SlotBasedStageBuilder relevantSlots = sbe::makeSV(); outputs.forEachSlot(forwardingReqs, [&](auto&& slot) { relevantSlots.push_back(slot); }); + // Forward slots for components of the index key if our parent requested them. + if (auto indexKeySlots = outputs.getIndexKeySlots()) { + relevantSlots.insert(relevantSlots.end(), indexKeySlots->begin(), indexKeySlots->end()); + } std::tie(std::ignore, stage) = generateFilter(_opCtx, fn->filter.get(), @@ -534,19 +542,11 @@ std::pair<std::unique_ptr<sbe::PlanStage>, PlanStageSlots> SlotBasedStageBuilder auto mergeSortNode = static_cast<const MergeSortNode*>(root); - uassert(5073803, - "SORT_MERGE stage with unfetched children not supported", - mergeSortNode->fetched()); - const auto sortPattern = SortPattern{mergeSortNode->sort, _cq.getExpCtx()}; std::vector<sbe::value::SortDirection> direction; for (const auto& part : sortPattern) { uassert(4822881, "Sorting by expression not supported", !part.expression); - uassert(4822882, - "Sorting by dotted paths not supported", - part.fieldPath && part.fieldPath->getPathLength() == 1); - direction.push_back(part.isAscending ? sbe::value::SortDirection::Ascending : sbe::value::SortDirection::Descending); } @@ -556,46 +556,76 @@ std::pair<std::unique_ptr<sbe::PlanStage>, PlanStageSlots> SlotBasedStageBuilder std::vector<sbe::value::SlotVector> inputVals; // Children must produce all of the slots required by the parent of this SortMergeNode. In - // addition to that, children must always produce a 'recordIdSlot' if the 'dedup' flag is true, - // and children must always produce a 'resultSlot' because it's needed by the sort logic below. - auto childReqs = reqs.copy().set(kResult).setIf(kRecordId, mergeSortNode->dedup); + // addition, children must always produce a 'recordIdSlot' if the 'dedup' flag is true. + auto childReqs = reqs.copy().setIf(kRecordId, mergeSortNode->dedup); for (auto&& child : mergeSortNode->children) { - auto [stage, outputs] = build(child, childReqs); - - sbe::value::SlotMap<std::unique_ptr<sbe::EExpression>> projectMap; sbe::value::SlotVector inputKeysForChild; - for (const auto& part : sortPattern) { - // Slot holding the sort key. - auto sortFieldSlot{_slotIdGenerator.generate()}; - inputKeysForChild.push_back(sortFieldSlot); - - auto fieldName = part.fieldPath->getFieldName(0); - auto fieldNameSV = std::string_view{fieldName.rawData(), fieldName.size()}; - - auto getSortFieldExpr = makeFunction("getField"sv, - sbe::makeE<sbe::EVariable>(outputs.get(kResult)), - sbe::makeE<sbe::EConstant>(fieldNameSV)); - - if (auto collatorSlot = _data.env->getSlotIfExists("collator"_sd); collatorSlot) { - getSortFieldExpr = makeFunction("collComparisonKey"sv, - std::move(getSortFieldExpr), - sbe::makeE<sbe::EVariable>(*collatorSlot)); + // Map of field name to position within the index key. This is used to account for + // mismatches between the sort pattern and the index key pattern. For instance, suppose the + // requested sort is {a: 1, b: 1} and the index key pattern is {c: 1, b: 1, a: 1}. When the + // slots for the relevant components of the index key are generated (i.e. extract keys for + // 'b' and 'a'), we wish to insert them into 'inputKeys' in the order that they appear in + // the sort pattern. + StringMap<size_t> indexKeyPositionMap; + auto ixnNode = getNodeByType(child, STAGE_IXSCAN); + tassert(5184300, + str::stream() << "Can't build exec tree for node: " << child->toString(), + ixnNode); + + auto ixn = static_cast<const IndexScanNode*>(ixnNode); + sbe::IndexKeysInclusionSet indexKeyBitset; + size_t i = 0; + for (auto&& elt : ixn->index.keyPattern) { + for (auto&& sortPart : sortPattern) { + auto path = sortPart.fieldPath->fullPath(); + if (elt.fieldNameStringData() == path) { + indexKeyBitset.set(i); + indexKeyPositionMap.emplace(path, indexKeyPositionMap.size()); + break; + } } - - projectMap.emplace(sortFieldSlot, std::move(getSortFieldExpr)); + ++i; } + childReqs.getIndexKeyBitset() = indexKeyBitset; - stage = - sbe::makeS<sbe::ProjectStage>(std::move(stage), std::move(projectMap), root->nodeId()); + // Children must produce a 'resultSlot' if they produce fetched results. + auto [stage, outputs] = build(child, childReqs); - inputStages.push_back(std::move(stage)); + tassert(5184301, + "SORT_MERGE node must receive a RecordID slot as input from child stage" + " if the 'dedup' flag is set", + !mergeSortNode->dedup || outputs.has(kRecordId)); - invariant(outputs.has(kResult)); - invariant(!mergeSortNode->dedup || outputs.has(kRecordId)); + // Clear the index key bitset after building the child stage. + childReqs.getIndexKeyBitset() = boost::none; + + // Insert the index key slots in the order of the sort pattern. + auto indexKeys = outputs.extractIndexKeySlots(); + tassert(5184302, + "SORT_MERGE must receive index key slots as input from its child stages", + indexKeys.has_value()); + + for (const auto& part : sortPattern) { + auto partPath = part.fieldPath->fullPath(); + auto index = indexKeyPositionMap.find(partPath); + tassert(5184303, + str::stream() << "Could not find index key position for sort key part " + << partPath, + index != indexKeyPositionMap.end()); + auto indexPos = index->second; + tassert(5184304, + str::stream() << "Index position " << indexPos + << " is not less than number of index components " + << indexKeys->size(), + indexPos < indexKeys->size()); + auto indexKeyPart = indexKeys->at(indexPos); + inputKeysForChild.push_back(indexKeyPart); + } inputKeys.push_back(std::move(inputKeysForChild)); + inputStages.push_back(std::move(stage)); auto sv = sbe::makeSV(); outputs.forEachSlot(childReqs, [&](auto&& slot) { sv.push_back(slot); }); |