diff options
Diffstat (limited to 'src/mongo/db/query/new_find.cpp')
-rw-r--r-- | src/mongo/db/query/new_find.cpp | 221 |
1 files changed, 63 insertions, 158 deletions
diff --git a/src/mongo/db/query/new_find.cpp b/src/mongo/db/query/new_find.cpp index 6b7b8af08e1..b29aca68c25 100644 --- a/src/mongo/db/query/new_find.cpp +++ b/src/mongo/db/query/new_find.cpp @@ -90,11 +90,6 @@ namespace { return n >= pq.getNumToReturn(); } - bool enoughForExplain(const mongo::LiteParsedQuery& pq, long long n) { - if (pq.wantMore() || 0 == pq.getNumToReturn()) { return false; } - return n >= pq.getNumToReturn(); - } - /** * Returns true if 'me' is a GTE or GE predicate over the "ts" field. * Such predicates can be used for the oplog start hack. @@ -511,60 +506,6 @@ namespace mongo { // We use this a lot below. const LiteParsedQuery& pq = cq->getParsed(); - // set this outside loop. we will need to use this both within loop and when deciding - // to fill in explain information - const bool isExplain = pq.isExplain(); - - // New-style explains get diverted through a separate path which calls back into the - // query planner and query execution mechanisms. - // - // TODO temporary until find() becomes a real command. - if (isExplain && enableNewExplain) { - size_t options = QueryPlannerParams::DEFAULT; - if (shardingState.needCollectionMetadata(pq.ns())) { - options |= QueryPlannerParams::INCLUDE_SHARD_FILTER; - } - - BufBuilder bb; - bb.skip(sizeof(QueryResult::Value)); - - PlanExecutor* rawExec; - // Takes ownership of 'cq'. - Status execStatus = getExecutor(txn, collection, cq, &rawExec, options); - if (!execStatus.isOK()) { - uasserted(17510, "Explain error: " + execStatus.reason()); - } - - scoped_ptr<PlanExecutor> exec(rawExec); - BSONObjBuilder explainBob; - Status explainStatus = Explain::explainStages(exec.get(), Explain::EXEC_ALL_PLANS, - &explainBob); - if (!explainStatus.isOK()) { - uasserted(18521, "Explain error: " + explainStatus.reason()); - } - - // Add the resulting object to the return buffer. - BSONObj explainObj = explainBob.obj(); - bb.appendBuf((void*)explainObj.objdata(), explainObj.objsize()); - - curop.debug().iscommand = true; - // TODO: Does this get overwritten/do we really need to set this twice? - curop.debug().query = q.query; - - // Set query result fields. - QueryResult::View qr = bb.buf(); - bb.decouple(); - qr.setResultFlagsToOk(); - qr.msgdata().setLen(bb.len()); - curop.debug().responseLength = bb.len(); - qr.msgdata().setOperation(opReply); - qr.setCursorId(0); - qr.setStartingFrom(0); - qr.setNReturned(1); - result.setData(qr.view2ptr(), true); - return ""; - } - // We'll now try to get the query executor that will execute this query for us. There // are a few cases in which we know upfront which executor we should get and, therefore, // we shortcut the selection process here. @@ -606,6 +547,41 @@ namespace mongo { verify(NULL != rawExec); auto_ptr<PlanExecutor> exec(rawExec); + // If it's actually an explain, do the explain and return rather than falling through + // to the normal query execution loop. + if (pq.isExplain()) { + BufBuilder bb; + bb.skip(sizeof(QueryResult::Value)); + + BSONObjBuilder explainBob; + Status explainStatus = Explain::explainStages(exec.get(), Explain::EXEC_ALL_PLANS, + &explainBob); + if (!explainStatus.isOK()) { + uasserted(18521, "Explain error: " + explainStatus.reason()); + } + + // Add the resulting object to the return buffer. + BSONObj explainObj = explainBob.obj(); + bb.appendBuf((void*)explainObj.objdata(), explainObj.objsize()); + + curop.debug().iscommand = true; + // TODO: Does this get overwritten/do we really need to set this twice? + curop.debug().query = q.query; + + // Set query result fields. + QueryResult::View qr = bb.buf(); + bb.decouple(); + qr.setResultFlagsToOk(); + qr.msgdata().setLen(bb.len()); + curop.debug().responseLength = bb.len(); + qr.msgdata().setOperation(opReply); + qr.setCursorId(0); + qr.setStartingFrom(0); + qr.setNReturned(1); + result.setData(qr.view2ptr(), true); + return ""; + } + // We freak out later if this changes before we're done with the query. const ChunkVersion shardingVersionAtStart = shardingState.getVersion(cq->ns()); @@ -662,10 +638,8 @@ namespace mongo { curop.debug().planSummary = stats.summaryStr.c_str(); while (PlanExecutor::ADVANCED == (state = exec->getNext(&obj, NULL))) { - // Add result to output buffer. This is unnecessary if explain info is requested - if (!isExplain) { - bb.appendBuf((void*)obj.objdata(), obj.objsize()); - } + // Add result to output buffer. + bb.appendBuf((void*)obj.objdata(), obj.objsize()); // Count the result. ++numResults; @@ -681,13 +655,8 @@ namespace mongo { // TODO: only one type of 2d search doesn't support this. We need a way to pull it out // of CanonicalQuery. :( const bool supportsGetMore = true; - if (isExplain) { - if (enoughForExplain(pq, numResults)) { - break; - } - } - else if (!supportsGetMore && (enough(pq, numResults) - || bb.len() >= MaxBytesToReturnToClientAtOnce)) { + if (!supportsGetMore && (enough(pq, numResults) + || bb.len() >= MaxBytesToReturnToClientAtOnce)) { break; } else if (enoughForFirstBatch(pq, numResults, bb.len())) { @@ -743,59 +712,33 @@ namespace mongo { shardingState.getVersion(pq.ns())); } - // Used to fill in explain and to determine if the query is slow enough to be logged. - int elapsedMillis = curop.elapsedMillis(); - - // Get explain information if: - // 1) it is needed by an explain query; - // 2) profiling is enabled; or - // 3) profiling is disabled but we still need explain details to log a "slow" query. - // Producing explain information is expensive and should be done only if we are certain - // the information will be used. - boost::scoped_ptr<TypeExplain> explain(NULL); - if (isExplain || - ctx.ctx().db()->getProfilingLevel() > 0 || - elapsedMillis > serverGlobalParams.slowMS) { - // Ask the executor to produce explain information. - TypeExplain* bareExplain; - Status res = Explain::legacyExplain(exec.get(), &bareExplain); - if (res.isOK()) { - explain.reset(bareExplain); - } - else if (isExplain) { - error() << "could not produce explain of query '" << pq.getFilter() - << "', error: " << res.reason(); - // If numResults and the data in bb don't correspond, we'll crash later when rooting - // through the reply msg. - BSONObj emptyObj; - bb.appendBuf((void*)emptyObj.objdata(), emptyObj.objsize()); - // The explain output is actually a result. - numResults = 1; - // TODO: we can fill out millis etc. here just fine even if the plan screwed up. + // Set debug information for consumption by the profiler. + if (ctx.ctx().db()->getProfilingLevel() > 0 || + curop.elapsedMillis() > serverGlobalParams.slowMS) { + PlanSummaryStats newStats; + Explain::getSummaryStats(exec.get(), &newStats); + + curop.debug().ntoskip = pq.getSkip(); + curop.debug().nreturned = numResults; + curop.debug().scanAndOrder = newStats.hasSortStage; + curop.debug().nscanned = newStats.totalKeysExamined; + curop.debug().nscannedObjects = newStats.totalDocsExamined; + curop.debug().idhack = newStats.isIdhack; + + // Get BSON stats. + scoped_ptr<PlanStageStats> execStats(exec->getStats()); + BSONObjBuilder statsBob; + Explain::statsToBSON(*execStats, &statsBob); + curop.debug().execStats.set(statsBob.obj()); + + // Replace exec stats with plan summary if stats cannot fit into CachedBSONObj. + if (curop.debug().execStats.tooBig() && !curop.debug().planSummary.empty()) { + BSONObjBuilder bob; + bob.append("summary", curop.debug().planSummary.toString()); + curop.debug().execStats.set(bob.done()); } } - // Fill in the missing run-time fields in explain, starting with propeties of - // the process running the query. - if (isExplain && NULL != explain.get()) { - std::string server = mongoutils::str::stream() - << getHostNameCached() << ":" << serverGlobalParams.port; - explain->setServer(server); - - // We might have skipped some results due to chunk migration etc. so our count is - // correct. - explain->setN(numResults); - - // Clock the whole operation. - explain->setMillis(elapsedMillis); - - BSONObj explainObj = explain->toBSON(); - bb.appendBuf((void*)explainObj.objdata(), explainObj.objsize()); - - // The explain output is actually a result. - numResults = 1; - } - long long ccId = 0; if (saveClientCursor) { // We won't use the executor until it's getMore'd. @@ -849,44 +792,6 @@ namespace mongo { qr.setStartingFrom(0); qr.setNReturned(numResults); - // Set debug information for consumption by the profiler. - curop.debug().ntoskip = pq.getSkip(); - curop.debug().nreturned = numResults; - if (NULL != explain.get()) { - if (explain->isScanAndOrderSet()) { - curop.debug().scanAndOrder = explain->getScanAndOrder(); - } - else { - curop.debug().scanAndOrder = false; - } - - if (explain->isNScannedSet()) { - curop.debug().nscanned = explain->getNScanned(); - } - - if (explain->isNScannedObjectsSet()) { - curop.debug().nscannedObjects = explain->getNScannedObjects(); - } - - if (explain->isIDHackSet()) { - curop.debug().idhack = explain->getIDHack(); - } - - if (!explain->stats.isEmpty()) { - // execStats is a CachedBSONObj because it lives in the race-prone - // curop. - curop.debug().execStats.set(explain->stats); - - // Replace exec stats with plan summary if stats cannot fit into CachedBSONObj. - if (curop.debug().execStats.tooBig() && !curop.debug().planSummary.empty()) { - BSONObjBuilder bob; - bob.append("summary", curop.debug().planSummary.toString()); - curop.debug().execStats.set(bob.done()); - } - - } - } - // curop.debug().exhaust is set above. return curop.debug().exhaust ? pq.ns() : ""; } |