Status getExecutor(OperationContext* txn, Collection* collection, CanonicalQuery* rawCanonicalQuery, PlanExecutor** out, size_t plannerOptions) { invariant(rawCanonicalQuery); auto_ptr<CanonicalQuery> canonicalQuery(rawCanonicalQuery); // This can happen as we're called by internal clients as well. if (NULL == collection) { const string& ns = canonicalQuery->ns(); LOG(2) << "Collection " << ns << " does not exist." << " Using EOF runner: " << canonicalQuery->toStringShort(); EOFStage* eofStage = new EOFStage(); WorkingSet* ws = new WorkingSet(); *out = new PlanExecutor(ws, eofStage, canonicalQuery.release(), collection); return Status::OK(); } // Fill out the planning params. We use these for both cached solutions and non-cached. QueryPlannerParams plannerParams; plannerParams.options = plannerOptions; fillOutPlannerParams(collection, canonicalQuery.get(), &plannerParams); // If we have an _id index we can use the idhack runner. if (IDHackStage::supportsQuery(*canonicalQuery.get()) && collection->getIndexCatalog()->findIdIndex()) { return getExecutorIDHack(txn, collection, canonicalQuery.release(), plannerParams, out); } // Tailable: If the query requests tailable the collection must be capped. if (canonicalQuery->getParsed().hasOption(QueryOption_CursorTailable)) { if (!collection->isCapped()) { return Status(ErrorCodes::BadValue, "error processing query: " + canonicalQuery->toString() + " tailable cursor requested on non capped collection"); } // If a sort is specified it must be equal to expectedSort. const BSONObj expectedSort = BSON("$natural" << 1); const BSONObj& actualSort = canonicalQuery->getParsed().getSort(); if (!actualSort.isEmpty() && !(actualSort == expectedSort)) { return Status(ErrorCodes::BadValue, "error processing query: " + canonicalQuery->toString() + " invalid sort specified for tailable cursor: " + actualSort.toString()); } } // Try to look up a cached solution for the query. CachedSolution* rawCS; if (PlanCache::shouldCacheQuery(*canonicalQuery) && collection->infoCache()->getPlanCache()->get(*canonicalQuery.get(), &rawCS).isOK()) { // We have a CachedSolution. Have the planner turn it into a QuerySolution. boost::scoped_ptr<CachedSolution> cs(rawCS); QuerySolution *qs, *backupQs; QuerySolution*& chosenSolution=qs; // either qs or backupQs Status status = QueryPlanner::planFromCache(*canonicalQuery.get(), plannerParams, *cs, &qs, &backupQs); if (status.isOK()) { // the working set will be shared by the root and backupRoot plans // and owned by the containing single-solution-runner // WorkingSet* sharedWs = new WorkingSet(); PlanStage *root, *backupRoot=NULL; verify(StageBuilder::build(txn, collection, *qs, sharedWs, &root)); if ((plannerParams.options & QueryPlannerParams::PRIVATE_IS_COUNT) && turnIxscanIntoCount(qs)) { LOG(2) << "Using fast count: " << canonicalQuery->toStringShort() << ", planSummary: " << getPlanSummary(*qs); if (NULL != backupQs) { delete backupQs; } } else if (NULL != backupQs) { verify(StageBuilder::build(txn, collection, *backupQs, sharedWs, &backupRoot)); } // add a CachedPlanStage on top of the previous root root = new CachedPlanStage(collection, canonicalQuery.get(), root, backupRoot); *out = new PlanExecutor(sharedWs, root, chosenSolution, canonicalQuery.release(), collection); return Status::OK(); } } if (internalQueryPlanOrChildrenIndependently && SubplanStage::canUseSubplanning(*canonicalQuery)) { QLOG() << "Running query as sub-queries: " << canonicalQuery->toStringShort(); auto_ptr<WorkingSet> ws(new WorkingSet()); SubplanStage* subplan; Status subplanStatus = SubplanStage::make(txn, collection, ws.get(), plannerParams, canonicalQuery.get(), &subplan); if (subplanStatus.isOK()) { LOG(2) << "Running query as sub-queries: " << canonicalQuery->toStringShort(); *out = new PlanExecutor(ws.release(), subplan, canonicalQuery.release(), collection); return Status::OK(); } else { QLOG() << "Subplanner: " << subplanStatus.reason(); } } return getExecutorAlwaysPlan(txn, collection, canonicalQuery.release(), plannerParams, out); }
PlanStage::StageState SubplanStage::work(WorkingSetID* out) { ++_commonStats.works; // Adds the amount of time taken by work() to executionTimeMillis. ScopedTimer timer(&_commonStats.executionTimeMillis); if (_killed) { return PlanStage::DEAD; } if (isEOF()) { return PlanStage::IS_EOF; } if (SubplanStage::PLANNING == _state) { // Try to run as sub-plans. if (runSubplans()) { // If runSubplans returns true we expect something here. invariant(_child.get()); } else if (!_killed) { // Couldn't run as subplans so we'll just call normal getExecutor. PlanExecutor* exec; Status status = getExecutorAlwaysPlan(_collection, _query, _plannerParams, &exec); if (!status.isOK()) { // We utterly failed. _killed = true; // Propagate the error to the user wrapped in a BSONObj WorkingSetID id = _ws->allocate(); WorkingSetMember* member = _ws->get(id); member->state = WorkingSetMember::OWNED_OBJ; member->keyData.clear(); member->loc = DiskLoc(); BSONObjBuilder bob; bob.append("ok", status.isOK() ? 1.0 : 0.0); bob.append("code", status.code()); bob.append("errmsg", status.reason()); member->obj = bob.obj(); *out = id; return PlanStage::FAILURE; } else { scoped_ptr<PlanExecutor> cleanupExec(exec); _child.reset(exec->releaseStages()); } } // We can change state when we're either killed or we have an underlying runner. invariant(_killed || NULL != _child.get()); _state = SubplanStage::RUNNING; } if (_killed) { return PlanStage::DEAD; } if (isEOF()) { return PlanStage::IS_EOF; } // If we're here we should have planned already. invariant(SubplanStage::RUNNING == _state); invariant(_child.get()); return _child->work(out); }