PlanStage::StageState SortKeyGeneratorStage::doWork(WorkingSetID* out) { if (!_sortKeyGen) { _sortKeyGen = stdx::make_unique<SortKeyGenerator>(getOpCtx(), _sortSpec, _query); return PlanStage::NEED_TIME; } auto stageState = child()->work(out); if (stageState == PlanStage::ADVANCED) { WorkingSetMember* member = _ws->get(*out); BSONObj sortKey; Status sortKeyStatus = _sortKeyGen->getSortKey(*member, &sortKey); if (!sortKeyStatus.isOK()) { *out = WorkingSetCommon::allocateStatusMember(_ws, sortKeyStatus); return PlanStage::FAILURE; } // Add the sort key to the WSM as computed data. member->addComputed(new SortKeyComputedData(sortKey)); return PlanStage::ADVANCED; } if (stageState == PlanStage::IS_EOF) { _commonStats.isEOF = true; } return stageState; }
StatusWith<ClusterQueryResult> RouterStageAggregationMerge::next() { // Pipeline::getNext will return a boost::optional<Document> or boost::none if EOF. if (auto result = _mergePipeline->getNext()) { return {result->toBson()}; } // If we reach this point, we have hit EOF. _mergePipeline.get_deleter().dismissDisposal(); _mergePipeline->dispose(getOpCtx()); return {ClusterQueryResult()}; }
boost::optional<IndexKeyEntry> IndexScan::initIndexScan() { if (_params.doNotDedup) { _shouldDedup = false; } else { // TODO it is incorrect to rely on this not changing. SERVER-17678 _shouldDedup = _params.descriptor->isMultikey(getOpCtx()); } // Perform the possibly heavy-duty initialization of the underlying index cursor. _indexCursor = _iam->newCursor(getOpCtx(), _forward); // We always seek once to establish the cursor position. ++_specificStats.seeks; if (_params.bounds.isSimpleRange) { // Start at one key, end at another. _endKey = _params.bounds.endKey; _endKeyInclusive = _params.bounds.endKeyInclusive; _indexCursor->setEndPosition(_endKey, _endKeyInclusive); return _indexCursor->seek(_params.bounds.startKey, /*inclusive*/ true); } else { // For single intervals, we can use an optimized scan which checks against the position // of an end cursor. For all other index scans, we fall back on using // IndexBoundsChecker to determine when we've finished the scan. BSONObj startKey; bool startKeyInclusive; if (IndexBoundsBuilder::isSingleInterval( _params.bounds, &startKey, &startKeyInclusive, &_endKey, &_endKeyInclusive)) { _indexCursor->setEndPosition(_endKey, _endKeyInclusive); return _indexCursor->seek(startKey, startKeyInclusive); } else { _checker.reset(new IndexBoundsChecker(&_params.bounds, _keyPattern, _params.direction)); if (!_checker->getStartSeekPoint(&_seekPoint)) return boost::none; return _indexCursor->seek(_seekPoint); } } }
IndexScan::IndexScan(OperationContext* txn, const IndexScanParams& params, WorkingSet* workingSet, const MatchExpression* filter) : PlanStage(kStageType, txn), _workingSet(workingSet), _iam(params.descriptor->getIndexCatalog()->getIndex(params.descriptor)), _keyPattern(params.descriptor->keyPattern().getOwned()), _scanState(INITIALIZING), _filter(filter), _shouldDedup(true), _forward(params.direction == 1), _params(params), _endKeyInclusive(false) { // We can't always access the descriptor in the call to getStats() so we pull // any info we need for stats reporting out here. _specificStats.keyPattern = _keyPattern; _specificStats.indexName = _params.descriptor->indexName(); _specificStats.isMultiKey = _params.descriptor->isMultikey(getOpCtx()); _specificStats.isUnique = _params.descriptor->unique(); _specificStats.isSparse = _params.descriptor->isSparse(); _specificStats.isPartial = _params.descriptor->isPartial(); _specificStats.indexVersion = _params.descriptor->version(); }
Status CachedPlanStage::replan(PlanYieldPolicy* yieldPolicy, bool shouldCache) { // We're going to start over with a new plan. Clear out info from our old plan. _results.clear(); _ws->clear(); _children.clear(); // Use the query planning module to plan the whole query. std::vector<QuerySolution*> rawSolutions; Status status = QueryPlanner::plan(*_canonicalQuery, _plannerParams, &rawSolutions); if (!status.isOK()) { return Status(ErrorCodes::BadValue, str::stream() << "error processing query: " << _canonicalQuery->toString() << " planner returned error: " << status.reason()); } OwnedPointerVector<QuerySolution> solutions(rawSolutions); // We cannot figure out how to answer the query. Perhaps it requires an index // we do not have? if (0 == solutions.size()) { return Status(ErrorCodes::BadValue, str::stream() << "error processing query: " << _canonicalQuery->toString() << " No query solutions"); } if (1 == solutions.size()) { // If there's only one solution, it won't get cached. Make sure to evict the existing // cache entry if requested by the caller. if (shouldCache) { PlanCache* cache = _collection->infoCache()->getPlanCache(); cache->remove(*_canonicalQuery); } PlanStage* newRoot; // Only one possible plan. Build the stages from the solution. verify(StageBuilder::build( getOpCtx(), _collection, *_canonicalQuery, *solutions[0], _ws, &newRoot)); _children.emplace_back(newRoot); _replannedQs.reset(solutions.popAndReleaseBack()); LOG(1) << "Replanning of query resulted in single query solution, which will not be cached. " << _canonicalQuery->toStringShort() << " plan summary after replan: " << Explain::getPlanSummary(child().get()) << " previous cache entry evicted: " << (shouldCache ? "yes" : "no"); return Status::OK(); } // Many solutions. Create a MultiPlanStage to pick the best, update the cache, // and so on. The working set will be shared by all candidate plans. auto cachingMode = shouldCache ? MultiPlanStage::CachingMode::AlwaysCache : MultiPlanStage::CachingMode::NeverCache; _children.emplace_back( new MultiPlanStage(getOpCtx(), _collection, _canonicalQuery, cachingMode)); MultiPlanStage* multiPlanStage = static_cast<MultiPlanStage*>(child().get()); for (size_t ix = 0; ix < solutions.size(); ++ix) { if (solutions[ix]->cacheData.get()) { solutions[ix]->cacheData->indexFilterApplied = _plannerParams.indexFiltersApplied; } PlanStage* nextPlanRoot; verify(StageBuilder::build( getOpCtx(), _collection, *_canonicalQuery, *solutions[ix], _ws, &nextPlanRoot)); // Takes ownership of 'solutions[ix]' and 'nextPlanRoot'. multiPlanStage->addPlan(solutions.releaseAt(ix), nextPlanRoot, _ws); } // Delegate to the MultiPlanStage's plan selection facility. Status pickBestPlanStatus = multiPlanStage->pickBestPlan(yieldPolicy); if (!pickBestPlanStatus.isOK()) { return pickBestPlanStatus; } LOG(1) << "Replanning " << _canonicalQuery->toStringShort() << " resulted in plan with summary: " << Explain::getPlanSummary(child().get()) << ", which " << (shouldCache ? "has" : "has not") << " been written to the cache"; return Status::OK(); }
void RouterStageAggregationMerge::doReattachToOperationContext() { _mergePipeline->reattachToOperationContext(getOpCtx()); }
void IndexScan::doReattachToOperationContext() { if (_indexCursor) _indexCursor->reattachToOperationContext(getOpCtx()); }