summaryrefslogtreecommitdiff
path: root/src/mongo/db/query/new_find.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/mongo/db/query/new_find.cpp')
-rw-r--r--src/mongo/db/query/new_find.cpp221
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() : "";
}