Runner::RunnerState SubplanRunner::getNext(BSONObj* objOut, DiskLoc* dlOut) { if (_killed) { return Runner::RUNNER_DEAD; } if (isEOF()) { return Runner::RUNNER_EOF; } if (SubplanRunner::PLANNING == _state) { // Try to run as sub-plans. if (runSubplans()) { // If runSubplans returns true we expect something here. invariant(_underlyingRunner.get()); } else if (!_killed) { // Couldn't run as subplans so we'll just call normal getRunner. Runner* runner; Status status = getRunnerAlwaysPlan( _txn, _collection, _query.release(), _plannerParams, &runner ); if (!status.isOK()) { // We utterly failed. _killed = true; // Propagate the error to the user wrapped in a BSONObj if (NULL != objOut) { BSONObjBuilder bob; bob.append("ok", status.isOK() ? 1.0 : 0.0); bob.append("code", status.code()); bob.append("errmsg", status.reason()); *objOut = bob.obj(); } return Runner::RUNNER_ERROR; } else { _underlyingRunner.reset(runner); } } // We can change state when we're either killed or we have an underlying runner. invariant(_killed || NULL != _underlyingRunner.get()); _state = SubplanRunner::RUNNING; } if (_killed) { return Runner::RUNNER_DEAD; } if (isEOF()) { return Runner::RUNNER_EOF; } // If we're here we should have planned already. invariant(SubplanRunner::RUNNING == _state); invariant(_underlyingRunner.get()); return _underlyingRunner->getNext(objOut, dlOut); }
/** * For a given query, get a runner. The runner could be a SingleSolutionRunner, a * CachedQueryRunner, or a MultiPlanRunner, depending on the cache/query solver/etc. */ Status getRunner(Collection* collection, CanonicalQuery* rawCanonicalQuery, Runner** out, size_t plannerOptions) { verify(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(); *out = new EOFRunner(canonicalQuery.release(), ns); return Status::OK(); } // If we have an _id index we can use the idhack runner. if (IDHackRunner::supportsQuery(*canonicalQuery) && collection->getIndexCatalog()->findIdIndex()) { LOG(2) << "Using idhack: " << canonicalQuery->toStringShort(); *out = new IDHackRunner(collection, canonicalQuery.release()); return Status::OK(); } // 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()); } } // Fill out the planning params. We use these for both cached solutions and non-cached. QueryPlannerParams plannerParams; plannerParams.options = plannerOptions; fillOutPlannerParams(collection, rawCanonicalQuery, &plannerParams); // See if the cache has what we're looking for. Status cacheStatus = getRunnerFromCache(canonicalQuery.get(), collection, plannerParams, out); // This can be not-OK and we can carry on. It just means the query wasn't cached. if (cacheStatus.isOK()) { // We got a cached runner. canonicalQuery.release(); return cacheStatus; } if (internalQueryPlanOrChildrenIndependently && SubplanRunner::canUseSubplanRunner(*canonicalQuery)) { QLOG() << "Running query as sub-queries: " << canonicalQuery->toStringShort(); LOG(2) << "Running query as sub-queries: " << canonicalQuery->toStringShort(); SubplanRunner* runner; Status runnerStatus = SubplanRunner::make(collection, plannerParams, canonicalQuery.release(), &runner); if (!runnerStatus.isOK()) { return runnerStatus; } *out = runner; return Status::OK(); } return getRunnerAlwaysPlan(collection, canonicalQuery.release(), plannerParams, out); }
/** * For a given query, get a runner. */ Status getRunner(Collection* collection, CanonicalQuery* rawCanonicalQuery, Runner** out, size_t plannerOptions) { verify(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(); *out = new EOFRunner(canonicalQuery.release(), ns); return Status::OK(); } // If we have an _id index we can use the idhack runner. if (IDHackRunner::supportsQuery(*canonicalQuery) && collection->getIndexCatalog()->findIdIndex()) { LOG(2) << "Using idhack: " << canonicalQuery->toStringShort(); *out = new IDHackRunner(collection, canonicalQuery.release()); return Status::OK(); } // 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()); } } // Fill out the planning params. We use these for both cached solutions and non-cached. QueryPlannerParams plannerParams; plannerParams.options = plannerOptions; fillOutPlannerParams(collection, rawCanonicalQuery, &plannerParams); // Try to look up a cached solution for the query. CachedSolution* rawCS; if (PlanCache::shouldCacheQuery(*canonicalQuery) && collection->infoCache()->getPlanCache()->get(*canonicalQuery, &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, 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(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(collection, *backupQs, sharedWs, &backupRoot)); } // add a CachedPlanStage on top of the previous root root = new CachedPlanStage(collection, rawCanonicalQuery, root, backupRoot); *out = new SingleSolutionRunner(collection, canonicalQuery.release(), chosenSolution, root, sharedWs); return Status::OK(); } } if (internalQueryPlanOrChildrenIndependently && SubplanRunner::canUseSubplanRunner(*canonicalQuery)) { QLOG() << "Running query as sub-queries: " << canonicalQuery->toStringShort(); LOG(2) << "Running query as sub-queries: " << canonicalQuery->toStringShort(); SubplanRunner* runner; Status runnerStatus = SubplanRunner::make(collection, plannerParams, canonicalQuery.release(), &runner); if (!runnerStatus.isOK()) { return runnerStatus; } *out = runner; return Status::OK(); } return getRunnerAlwaysPlan(collection, canonicalQuery.release(), plannerParams, out); }