summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/mongo/db/clientcursor.cpp22
-rw-r--r--src/mongo/db/clientcursor.h21
-rw-r--r--src/mongo/db/ops/query.cpp17
-rw-r--r--src/mongo/db/query/cached_plan_runner.h11
-rw-r--r--src/mongo/db/query/canonical_query.cpp7
-rw-r--r--src/mongo/db/query/canonical_query.h1
-rw-r--r--src/mongo/db/query/multi_plan_runner.cpp8
-rw-r--r--src/mongo/db/query/multi_plan_runner.h8
-rw-r--r--src/mongo/db/query/new_find.cpp279
-rw-r--r--src/mongo/db/query/new_find.h3
-rw-r--r--src/mongo/db/query/query_planner.cpp3
-rw-r--r--src/mongo/db/query/runner.h10
-rw-r--r--src/mongo/db/query/simple_plan_runner.h15
-rw-r--r--src/mongo/db/query/single_solution_runner.h76
14 files changed, 417 insertions, 64 deletions
diff --git a/src/mongo/db/clientcursor.cpp b/src/mongo/db/clientcursor.cpp
index 54bbcf97e84..86e54eff3eb 100644
--- a/src/mongo/db/clientcursor.cpp
+++ b/src/mongo/db/clientcursor.cpp
@@ -57,21 +57,24 @@ namespace mongo {
ClientCursor::ClientCursor(int qopts, const shared_ptr<Cursor>& c, const string& ns,
BSONObj query) : _ns(ns), _query(query), _c(c),
_yieldSometimesTracker(128, 10) {
- init(qopts);
+ _queryOptions = qopts;
_doingDeletes = false;
+ init();
}
- ClientCursor::ClientCursor(int qopts, Runner* runner, const string& ns, BSONObj query)
- : _ns(ns), _query(query), _runner(runner), _yieldSometimesTracker(128, 10) {
- init(qopts);
+ ClientCursor::ClientCursor(Runner* runner) : _yieldSometimesTracker(128, 10) {
+ _runner.reset(runner);
+ _ns = runner->getQuery().getParsed().ns();
+ _query = runner->getQuery().getParsed().getFilter();
+ _queryOptions = runner->getQuery().getParsed().getOptions();
+ init();
}
- void ClientCursor::init(int qopts) {
+ void ClientCursor::init() {
_db = cc().database();
verify( _db );
verify( _db->ownsNS( _ns ) );
- _queryOptions = qopts;
_idleAgeMillis = 0;
_leftoverMaxTimeMicros = 0;
_pinValue = 0;
@@ -79,7 +82,7 @@ namespace mongo {
Lock::assertAtLeastReadLocked(_ns);
- if (qopts & QueryOption_NoCursorTimeout) {
+ if (_queryOptions & QueryOption_NoCursorTimeout) {
// cursors normally timeout after an inactivity period to prevent excess memory use
// setting this prevents timeout of the cursor in question.
++_pinValue;
@@ -150,10 +153,13 @@ namespace mongo {
bool shouldDelete = false;
if (NULL != cc->_runner.get()) {
+ verify(NULL == cc->c());
shouldDelete = true;
}
// Begin cursor-only
else if (cc->c()->shouldDestroyOnNSDeletion()) {
+ verify(NULL == cc->_runner.get());
+
if (isDB) {
// already checked that db matched above
dassert(str::startsWith(cc->_ns.c_str(), ns));
@@ -419,7 +425,9 @@ namespace mongo {
}
}
+ // DEPRECATED only used by Cursor.
void ClientCursor::storeOpForSlave( DiskLoc last ) {
+ verify(NULL == _runner.get());
if ( ! ( _queryOptions & QueryOption_OplogReplay ))
return;
diff --git a/src/mongo/db/clientcursor.h b/src/mongo/db/clientcursor.h
index 4f739a126b3..98eab57add4 100644
--- a/src/mongo/db/clientcursor.h
+++ b/src/mongo/db/clientcursor.h
@@ -49,7 +49,7 @@ namespace mongo {
ClientCursor(int qopts, const shared_ptr<Cursor>& c, const string& ns,
BSONObj query = BSONObj());
- ClientCursor(int qopts, Runner* runner, const string& ns, BSONObj query = BSONObj());
+ ClientCursor(Runner* runner);
~ClientCursor();
@@ -158,7 +158,6 @@ namespace mongo {
// Replication-related stuff. TODO: Document and clean.
//
- void storeOpForSlave( DiskLoc last );
void updateSlaveLocation( CurOp& curop );
void slaveReadTill( const OpTime& t ) { _slaveReadTill = t; }
/** Just for testing. */
@@ -168,7 +167,9 @@ namespace mongo {
// Query-specific functionality that may be adapted for the Runner.
//
- // Used by ops/query.cpp to stash how many results hvae been returned by a query.
+ Runner* getRunner() const { return _runner.get(); }
+
+ // Used by ops/query.cpp to stash how many results have been returned by a query.
int pos() const { return _pos; }
void incPos(int n) { _pos += n; }
void setPos(int n) { _pos = n; }
@@ -214,6 +215,8 @@ namespace mongo {
// Cursor-only DEPRECATED methods.
//
+ void storeOpForSlave( DiskLoc last );
+
// Only used by ops/query.cpp, which will stop using them when queries are answered only by
// a runner.
const BSONObj& query() const { return _query; }
@@ -260,7 +263,7 @@ namespace mongo {
* Initialization common between Cursor and Runner.
* TODO: Remove when we're all-runner.
*/
- void init(int qopts);
+ void init();
/**
* Allocates a new CursorId.
@@ -294,7 +297,7 @@ namespace mongo {
unsigned _pinValue;
// The namespace we're operating on.
- const string _ns;
+ string _ns;
// The database we're operating on.
Database* _db;
@@ -303,7 +306,7 @@ namespace mongo {
int _pos;
// The query that prompted this ClientCursor. Only used for debugging.
- const BSONObj _query;
+ BSONObj _query;
// See the QueryOptions enum in dbclient.h
int _queryOptions;
@@ -317,7 +320,11 @@ namespace mongo {
// TODO: Document.
uint64_t _leftoverMaxTimeMicros;
- // TODO: document better. Somehow used in sharding.
+ // For chunks that are being migrated, there is a period of time when that chunks data is in
+ // two shards, the donor and the receiver one. That data is picked up by a cursor on the
+ // receiver side, even before the migration was decided. The CollectionMetadata allow one
+ // to inquiry if any given document of the collection belongs indeed to this shard or if it
+ // is coming from (or a vestige of) an ongoing migration.
CollectionMetadataPtr _collMetadata;
//
diff --git a/src/mongo/db/ops/query.cpp b/src/mongo/db/ops/query.cpp
index 41a390d397e..19be9499804 100644
--- a/src/mongo/db/ops/query.cpp
+++ b/src/mongo/db/ops/query.cpp
@@ -115,6 +115,10 @@ namespace mongo {
int pass,
bool& exhaust,
bool* isCursorAuthorized ) {
+ if (useNewQuerySystem) {
+ return newGetMore(ns, ntoreturn, cursorid, curop, pass, exhaust, isCursorAuthorized);
+ }
+
exhaust = false;
int bufSize = 512 + sizeof( QueryResult ) + MaxBytesToReturnToClientAtOnce;
@@ -181,10 +185,10 @@ namespace mongo {
while ( 1 ) {
if ( !c->ok() ) {
if ( c->tailable() ) {
- /* when a tailable cursor hits "EOF", ok() goes false, and current() is null. however
- advance() can still be retries as a reactivation attempt. when there is new data, it will
- return true. that's what we are doing here.
- */
+ // when a tailable cursor hits "EOF", ok() goes false, and current() is
+ // null. however advance() can still be retries as a reactivation attempt.
+ // when there is new data, it will return true. that's what we are doing
+ // here.
if ( c->advance() )
continue;
@@ -835,7 +839,9 @@ namespace mongo {
// we might be missing some data
// and its safe to send this as mongos can resend
// at this point
- throw SendStaleConfigException( ns , "version changed during initial query", shardingVersionAtStart, shardingState.getVersion( ns ) );
+ throw SendStaleConfigException(ns, "version changed during initial query",
+ shardingVersionAtStart,
+ shardingState.getVersion(ns));
}
parentPageFaultSection.reset(0);
@@ -1060,6 +1066,7 @@ namespace mongo {
}
if (useNewQuerySystem) {
+ // TODO: Copy prequel curop debugging into runNewQuery
return newRunQuery(m, q, curop, result);
}
diff --git a/src/mongo/db/query/cached_plan_runner.h b/src/mongo/db/query/cached_plan_runner.h
index c034b3659fa..ce2b87c7980 100644
--- a/src/mongo/db/query/cached_plan_runner.h
+++ b/src/mongo/db/query/cached_plan_runner.h
@@ -72,16 +72,17 @@ namespace mongo {
return false;
}
- /**
- * TODO: Explicit yielding is deprecated pending a ClientCursor rewrite.
- */
- virtual void yield() { _runner->yield(); }
- virtual void unYield() { _runner->unYield(); }
+ virtual void saveState() { _runner->saveState(); }
+ virtual void restoreState() { _runner->restoreState(); }
virtual void invalidate(const DiskLoc& dl) {
_runner->invalidate(dl);
}
+ virtual const CanonicalQuery& getQuery() {
+ return *_canonicalQuery;
+ }
+
private:
scoped_ptr<CanonicalQuery> _canonicalQuery;
scoped_ptr<CachedSolution> _cachedQuery;
diff --git a/src/mongo/db/query/canonical_query.cpp b/src/mongo/db/query/canonical_query.cpp
index 91b0522a408..9caa9bd6c45 100644
--- a/src/mongo/db/query/canonical_query.cpp
+++ b/src/mongo/db/query/canonical_query.cpp
@@ -28,6 +28,13 @@ namespace mongo {
// TODO: ParsedQuery throws. Fix it to return error.
cq->_pq.reset(new ParsedQuery(qm));
+ // TODO: If pq.hasOption(QueryOption_CursorTailable) make sure it's a capped collection and
+ // make sure the order(??) is $natural: 1.
+
+ // TODO: Do we want to do this too?:
+ //if ( pq.getFields() != NULL )
+ // pq.getFields()->validateQuery( query );
+
StatusWithMatchExpression swme = MatchExpressionParser::parse(cq->_pq->getFilter());
if (!swme.isOK()) {
return swme.getStatus();
diff --git a/src/mongo/db/query/canonical_query.h b/src/mongo/db/query/canonical_query.h
index 6647360d461..2560df03437 100644
--- a/src/mongo/db/query/canonical_query.h
+++ b/src/mongo/db/query/canonical_query.h
@@ -40,6 +40,7 @@ namespace mongo {
//
MatchExpression* root() const { return _root.get(); }
BSONObj getQueryObj() const { return _pq->getFilter(); }
+ const ParsedQuery& getParsed() const { return *_pq; }
string toString() const;
diff --git a/src/mongo/db/query/multi_plan_runner.cpp b/src/mongo/db/query/multi_plan_runner.cpp
index fc4eb33adf7..5ee561dfa84 100644
--- a/src/mongo/db/query/multi_plan_runner.cpp
+++ b/src/mongo/db/query/multi_plan_runner.cpp
@@ -38,18 +38,18 @@ namespace mongo {
_candidates.push_back(CandidatePlan(solution, root, ws));
}
- void MultiPlanRunner::yield() {
+ void MultiPlanRunner::saveState() {
if (NULL != _bestPlanRunner) {
- _bestPlanRunner->yield();
+ _bestPlanRunner->saveState();
}
else {
yieldAllPlans();
}
}
- void MultiPlanRunner::unYield() {
+ void MultiPlanRunner::restoreState() {
if (NULL != _bestPlanRunner) {
- _bestPlanRunner->yield();
+ _bestPlanRunner->restoreState();
}
else {
unyieldAllPlans();
diff --git a/src/mongo/db/query/multi_plan_runner.h b/src/mongo/db/query/multi_plan_runner.h
index c96121987e2..14ad8507bf3 100644
--- a/src/mongo/db/query/multi_plan_runner.h
+++ b/src/mongo/db/query/multi_plan_runner.h
@@ -67,10 +67,14 @@ namespace mongo {
*/
bool pickBestPlan(size_t* out);
- virtual void yield();
- virtual void unYield();
+ virtual void saveState();
+ virtual void restoreState();
virtual void invalidate(const DiskLoc& dl);
+ virtual const CanonicalQuery& getQuery() {
+ return *_query;
+ }
+
private:
/**
* Have all our candidate plans do something.
diff --git a/src/mongo/db/query/new_find.cpp b/src/mongo/db/query/new_find.cpp
index 673e5ceb666..e3599cc6b31 100644
--- a/src/mongo/db/query/new_find.cpp
+++ b/src/mongo/db/query/new_find.cpp
@@ -16,24 +16,33 @@
#include "mongo/db/query/new_find.h"
+#include "mongo/client/dbclientinterface.h"
+#include "mongo/db/clientcursor.h"
#include "mongo/db/commands.h"
+#include "mongo/db/keypattern.h"
#include "mongo/db/query/cached_plan_runner.h"
#include "mongo/db/query/canonical_query.h"
#include "mongo/db/query/multi_plan_runner.h"
#include "mongo/db/query/plan_cache.h"
#include "mongo/db/query/query_planner.h"
-#include "mongo/db/query/simple_plan_runner.h"
+#include "mongo/db/query/single_solution_runner.h"
#include "mongo/db/query/stage_builder.h"
+#include "mongo/db/repl/repl_reads_ok.h"
+#include "mongo/s/chunk_version.h"
+#include "mongo/s/d_logic.h"
+#include "mongo/s/stale_exception.h"
namespace mongo {
+ // Copied from db/ops/query.cpp.
+ static const int32_t MaxBytesToReturnToClientAtOnce = 4 * 1024 * 1024;
+
// Used in db/ops/query.cpp.
bool useNewQuerySystem = false;
/**
- * For a given query, get a runner.
- * The runner could be a SimplePlanRunner, a CachedQueryRunner, or a MultiPlanRunner, depending
- * on the cache/query solver/etc.
+ * 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(QueryMessage& q, Runner** out) {
CanonicalQuery* rawCanonicalQuery = NULL;
@@ -70,21 +79,13 @@ namespace mongo {
}
if (1 == solutions.size()) {
- // Only one possible plan. Run it. Cache it as well. If we only found one solution
- // now, we're only going to find one solution later.
- auto_ptr<PlanRankingDecision> why(new PlanRankingDecision());
- why->onlyOneSolution = true;
-
- // Build the stages from the solution.
+ // Only one possible plan. Run it. Build the stages from the solution.
WorkingSet* ws;
PlanStage* root;
verify(StageBuilder::build(*solutions[0], &root, &ws));
- // Cache the solution. Takes ownership of all arguments.
- localCache->add(canonicalQuery.release(), solutions[0], why.release());
-
// And, run the plan.
- *out = new SimplePlanRunner(ws, root);
+ *out = new SingleSolutionRunner(canonicalQuery.release(), solutions[0], root, ws);
return Status::OK();
}
else {
@@ -103,6 +104,123 @@ namespace mongo {
}
/**
+ * Also called by db/ops/query.cpp. This is the new getMore entry point.
+ */
+ QueryResult* newGetMore(const char* ns, int ntoreturn, long long cursorid, CurOp& curop,
+ int pass, bool& exhaust, bool* isCursorAuthorized) {
+ exhaust = false;
+ int bufSize = 512 + sizeof(QueryResult) + MaxBytesToReturnToClientAtOnce;
+
+ BufBuilder bb(bufSize);
+ bb.skip(sizeof(QueryResult));
+
+ // This is a read lock. TODO: There is a cursor flag for not needing this. Do we care?
+ Client::ReadContext ctx(ns);
+
+ // TODO: Document.
+ replVerifyReadsOk();
+
+ ClientCursorPin ccPin(cursorid);
+ ClientCursor* cc = ccPin.c();
+
+ // These are set in the QueryResult msg we return.
+ int resultFlags = ResultFlag_AwaitCapable;
+
+ int numResults = 0;
+ int startingResult = 0;
+
+ if (NULL == cc) {
+ cursorid = 0;
+ resultFlags = ResultFlag_CursorNotFound;
+ }
+ else {
+ // Quote: check for spoofing of the ns such that it does not match the one originally
+ // there for the cursor
+ uassert(17011, "auth error", str::equals(ns, cc->ns().c_str()));
+ *isCursorAuthorized = true;
+
+ // TODO: fail point?
+
+ // If the operation that spawned this cursor had a time limit set, apply leftover
+ // time to this getmore.
+ curop.setMaxTimeMicros(cc->getLeftoverMaxTimeMicros());
+ // TODO:
+ // curop.debug().query = BSONForQuery
+ // curop.setQuery(curop.debug().query);
+
+ // TODO: What is pass?
+ if (0 == pass) { cc->updateSlaveLocation(curop); }
+
+ CollectionMetadataPtr collMetadata = cc->getCollMetadata();
+
+ // If we're replaying the oplog, we save the last time that we read.
+ OpTime slaveReadTill;
+
+ startingResult = cc->pos();
+
+ Runner* runner = cc->getRunner();
+ const ParsedQuery& pq = runner->getQuery().getParsed();
+
+ // Get results out of the runner.
+ // TODO: There may be special handling required for tailable cursors?
+ runner->restoreState();
+ BSONObj obj;
+ // TODO: Differentiate EOF from error.
+ while (runner->getNext(&obj)) {
+ // If we're sharded make sure that we don't return any data that hasn't been
+ // migrated off of our shard yet.
+ if (collMetadata) {
+ KeyPattern kp(collMetadata->getKeyPattern());
+ if (!collMetadata->keyBelongsToMe(kp.extractSingleKey(obj))) { continue; }
+ }
+
+ // Add result to output buffer.
+ bb.appendBuf((void*)obj.objdata(), obj.objsize());
+
+ // Count the result.
+ ++numResults;
+
+ // Possibly note slave's position in the oplog.
+ if (pq.hasOption(QueryOption_OplogReplay)) {
+ BSONElement e = obj["ts"];
+ if (Date == e.type() || Timestamp == e.type()) {
+ slaveReadTill = e._opTime();
+ }
+ }
+
+ if ((numResults && numResults >= ntoreturn)
+ || bb.len() > MaxBytesToReturnToClientAtOnce) {
+ break;
+ }
+ }
+
+ cc->incPos(numResults);
+ runner->saveState();
+
+ // Possibly note slave's position in the oplog.
+ if (pq.hasOption(QueryOption_OplogReplay) && !slaveReadTill.isNull()) {
+ cc->slaveReadTill(slaveReadTill);
+ }
+
+ exhaust = pq.hasOption(QueryOption_Exhaust);
+
+ // If the getmore had a time limit, remaining time is "rolled over" back to the
+ // cursor (for use by future getmore ops).
+ cc->setLeftoverMaxTimeMicros( curop.getRemainingMaxTimeMicros() );
+ }
+
+ QueryResult* qr = reinterpret_cast<QueryResult*>(bb.buf());
+ qr->len = bb.len();
+ qr->setOperation(opReply);
+ qr->_resultFlags() = resultFlags;
+ qr->cursorId = cursorid;
+ qr->startingFrom = startingResult;
+ qr->nReturned = numResults;
+ bb.decouple();
+ return qr;
+ }
+
+ /**
* This is called by db/ops/query.cpp. This is the entry point for answering a query.
*/
string newRunQuery(Message& m, QueryMessage& q, CurOp& curop, Message &result) {
@@ -119,17 +237,133 @@ namespace mongo {
verify(NULL != rawRunner);
auto_ptr<Runner> runner(rawRunner);
+ // We freak out later if this changes before we're done with the query.
+ const ChunkVersion shardingVersionAtStart = shardingState.getVersion(q.ns);
+
+ // We use this a lot below.
+ const ParsedQuery& pq = runner->getQuery().getParsed();
+
+ // TODO: Document why we do this.
+ replVerifyReadsOk(&pq);
+
+ // If this exists, the collection is sharded.
+ // If it doesn't exist, we can assume we're not sharded.
+ // If we're sharded, we might encounter data that is not consistent with our sharding state.
+ // We must ignore this data.
+ CollectionMetadataPtr collMetadata;
+ if (!shardingState.needCollectionMetadata(pq.ns())) {
+ collMetadata = CollectionMetadataPtr();
+ }
+ else {
+ collMetadata = shardingState.getCollectionMetadata(pq.ns());
+ }
+
// Run the query.
BufBuilder bb(32768);
bb.skip(sizeof(QueryResult));
+
+ // How many results have we obtained from the runner?
int numResults = 0;
- // Yielding is handled within the runner, as is page faulting.
- // TODO: pay attention to numWanted.
+ // If we're replaying the oplog, we save the last time that we read.
+ OpTime slaveReadTill;
+
+ // Do we save the Runner in a ClientCursor for getMore calls later?
+ bool saveClientCursor = false;
+
BSONObj obj;
+ // TODO: Differentiate EOF from error.
while (runner->getNext(&obj)) {
+ // If we're sharded make sure that we don't return any data that hasn't been migrated
+ // off of our shared yet.
+ if (collMetadata) {
+ // This information can change if we yield and as such we must make sure to re-fetch
+ // it if we yield.
+ KeyPattern kp(collMetadata->getKeyPattern());
+ // This performs excessive BSONObj creation but that's OK for now.
+ if (!collMetadata->keyBelongsToMe(kp.extractSingleKey(obj))) { continue; }
+ }
+
+ // Add result to output buffer.
bb.appendBuf((void*)obj.objdata(), obj.objsize());
+
+ // Count the result.
++numResults;
+
+ // Possibly note slave's position in the oplog.
+ if (pq.hasOption(QueryOption_OplogReplay)) {
+ BSONElement e = obj["ts"];
+ if (Date == e.type() || Timestamp == e.type()) {
+ slaveReadTill = e._opTime();
+ }
+ }
+
+ // 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;
+ const bool isExplain = pq.isExplain();
+ if (isExplain && pq.enoughForExplain(numResults)) {
+ break;
+ }
+ else if (!supportsGetMore && (pq.enough(numResults)
+ || bb.len() >= MaxBytesToReturnToClientAtOnce)) {
+ break;
+ }
+ else if (pq.enoughForFirstBatch(numResults, bb.len())) {
+ // If only one result requested assume it's a findOne() and don't save the cursor.
+ if (pq.wantMore() && 1 != pq.getNumToReturn()) {
+ saveClientCursor = true;
+ }
+ break;
+ }
+ }
+
+ // TODO: Stage creation can set tailable depending on what's in the parsed query. We have
+ // the full parsed query available during planning...set it there.
+ //
+ // TODO: If we're tailable we want to save the client cursor. Make sure we do this later.
+ //if (pq.hasOption(QueryOption_CursorTailable) && pq.getNumToReturn() != 1) { ... }
+
+ // TODO(greg): This will go away soon.
+ if (!shardingState.getVersion(pq.ns()).isWriteCompatibleWith(shardingVersionAtStart)) {
+ // if the version changed during the query we might be missing some data and its safe to
+ // send this as mongos can resend at this point
+ throw SendStaleConfigException(pq.ns(), "version changed during initial query",
+ shardingVersionAtStart,
+ shardingState.getVersion(pq.ns()));
+ }
+
+ long long ccId = 0;
+ if (saveClientCursor) {
+ // Allocate a new ClientCursor.
+ ClientCursorHolder ccHolder;
+ ccHolder.reset(new ClientCursor(runner.get()));
+ ccId = ccHolder->cursorid();
+
+ // We won't use the runner until it's getMore'd.
+ runner->saveState();
+
+ // ClientCursor takes ownership of runner. Release to make sure it's not deleted.
+ runner.release();
+
+ if (pq.hasOption(QueryOption_OplogReplay) && !slaveReadTill.isNull()) {
+ ccHolder->slaveReadTill(slaveReadTill);
+ }
+
+ if (pq.hasOption(QueryOption_Exhaust)) {
+ curop.debug().exhaust = true;
+ }
+
+ // Set attributes for getMore.
+ ccHolder->setCollMetadata(collMetadata);
+ ccHolder->setPos(numResults);
+
+ // If the query had a time limit, remaining time is "rolled over" to the cursor (for
+ // use by future getmore ops).
+ ccHolder->setLeftoverMaxTimeMicros(curop.getRemainingMaxTimeMicros());
+
+ // Give up our reference to the CC.
+ ccHolder.release();
}
// Add the results from the query into the output buffer.
@@ -138,16 +372,19 @@ namespace mongo {
// Fill out the output buffer's header.
QueryResult* qr = static_cast<QueryResult*>(result.header());
- // TODO: Figure out how to make this work with a clientcursor
- qr->cursorId = 0;
+ qr->cursorId = ccId;
+ curop.debug().cursorid = (0 == ccId ? -1 : ccId);
qr->setResultFlagsToOk();
qr->setOperation(opReply);
qr->startingFrom = 0;
qr->nReturned = numResults;
- // TODO: set curop.debug() fields.
+ // TODO: nscanned is bogus.
+ // curop.debug().nscanned = ( cursor ? cursor->nscanned() : 0LL );
+ curop.debug().ntoskip = pq.getSkip();
+ curop.debug().nreturned = numResults;
- // TODO: what's this? we either return the ns or we return "".
- return "";
+ // curop.debug().exhaust is set above.
+ return curop.debug().exhaust ? pq.ns() : "";
}
} // namespace mongo
diff --git a/src/mongo/db/query/new_find.h b/src/mongo/db/query/new_find.h
index 0a4087a4afe..73e9b34721a 100644
--- a/src/mongo/db/query/new_find.h
+++ b/src/mongo/db/query/new_find.h
@@ -24,6 +24,9 @@
namespace mongo {
+ QueryResult* newGetMore(const char* ns, int ntoreturn, long long cursorid, CurOp& curop,
+ int pass, bool& exhaust, bool* isCursorAuthorized);
+
string newRunQuery(Message& m, QueryMessage& q, CurOp& curop, Message &result);
} // namespace mongo
diff --git a/src/mongo/db/query/query_planner.cpp b/src/mongo/db/query/query_planner.cpp
index ca9af4a1629..e09749e59aa 100644
--- a/src/mongo/db/query/query_planner.cpp
+++ b/src/mongo/db/query/query_planner.cpp
@@ -25,6 +25,9 @@ namespace mongo {
void QueryPlanner::plan(const CanonicalQuery& query, vector<QuerySolution*> *out) {
const MatchExpression* root = query.root();
+ // TODO: If pq.hasOption(QueryOption_OplogReplay) use FindingStartCursor equivalent which
+ // must be translated into stages.
+
// The default plan is always a collection scan with a heavy filter. This is a valid
// solution for any query that does not require an index.
if (!requiresIndex(root)) {
diff --git a/src/mongo/db/query/runner.h b/src/mongo/db/query/runner.h
index 43ad7500ac8..2d07361271a 100644
--- a/src/mongo/db/query/runner.h
+++ b/src/mongo/db/query/runner.h
@@ -16,6 +16,8 @@
#pragma once
+#include "mongo/db/query/canonical_query.h"
+
namespace mongo {
/**
@@ -39,11 +41,13 @@ namespace mongo {
*/
virtual void invalidate(const DiskLoc& dl) = 0;
+ virtual void saveState() = 0;
+ virtual void restoreState() = 0;
+
/**
- * TODO: Kill these once yielding is controlled inside of a runner.
+ * Return the query that the runner is running.
*/
- virtual void yield() = 0;
- virtual void unYield() = 0;
+ virtual const CanonicalQuery& getQuery() = 0;
};
} // namespace mongo
diff --git a/src/mongo/db/query/simple_plan_runner.h b/src/mongo/db/query/simple_plan_runner.h
index d83f286d30b..92d2645777d 100644
--- a/src/mongo/db/query/simple_plan_runner.h
+++ b/src/mongo/db/query/simple_plan_runner.h
@@ -31,8 +31,9 @@ namespace mongo {
* TODO: Yielding policy
* TODO: Graceful error handling
* TODO: Stats, diagnostics, instrumentation, etc.
+ * TODO: Rename. It's not a full runner. It just holds the stage/WS and handles yielding.
*/
- class SimplePlanRunner : public Runner {
+ class SimplePlanRunner {
public:
SimplePlanRunner() : _workingSet(new WorkingSet()) { }
SimplePlanRunner(WorkingSet* ws, PlanStage* rt) : _workingSet(ws), _root(rt) { }
@@ -47,15 +48,9 @@ namespace mongo {
_root.reset(root);
}
- /**
- * TODO: Explicit yielding is deprecated pending a ClientCursor rewrite.
- */
- virtual void yield() { _root->prepareToYield(); }
- virtual void unYield() { _root->recoverFromYield(); }
-
- virtual void invalidate(const DiskLoc& dl) {
- _root->invalidate(dl);
- }
+ void saveState() { _root->prepareToYield(); }
+ void restoreState() { _root->recoverFromYield(); }
+ void invalidate(const DiskLoc& dl) { _root->invalidate(dl); }
PlanStageStats* getStats() { return _root->getStats(); }
diff --git a/src/mongo/db/query/single_solution_runner.h b/src/mongo/db/query/single_solution_runner.h
new file mode 100644
index 00000000000..ed7dacbabfe
--- /dev/null
+++ b/src/mongo/db/query/single_solution_runner.h
@@ -0,0 +1,76 @@
+/**
+ * Copyright (C) 2013 10gen Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include "mongo/db/query/canonical_query.h"
+#include "mongo/db/query/plan_cache.h"
+#include "mongo/db/query/runner.h"
+#include "mongo/db/query/simple_plan_runner.h"
+#include "mongo/db/query/stage_builder.h"
+
+namespace mongo {
+
+ /**
+ * SingleSolutionRunner runs a plan that was the only possible solution to a query. It exists
+ * only to dump stats into the cache after running.
+ */
+ class SingleSolutionRunner : public Runner {
+ public:
+ /**
+ * Takes ownership of both arguments.
+ */
+ SingleSolutionRunner(CanonicalQuery* canonicalQuery, QuerySolution* soln,
+ PlanStage* root, WorkingSet* ws)
+ : _canonicalQuery(canonicalQuery), _solution(soln),
+ _runner(new SimplePlanRunner(ws, root)) { }
+
+ bool getNext(BSONObj* objOut) {
+ // Use the underlying runner until it's exhausted.
+ if (_runner->getNext(objOut)) {
+ return true;
+ }
+
+ // TODO: I'm not convinced we want to cache this. What if it's a collscan solution and
+ // the user adds an index later? We don't want to reach for this.
+
+ // We're done. Update the cache.
+ //PlanCache* cache = PlanCache::get(_canonicalQuery->ns());
+ // TODO: is this a verify?
+ //if (NULL == cache) { return false; }
+ // We're done running. Update cache.
+ //auto_ptr<PlanRankingDecision> why(new PlanRankingDecision());
+ //why->onlyOneSolution = true;
+ //cache->add(canonicalQuery.release(), solutions[0], why.release());
+ return false;
+ }
+
+ virtual void saveState() { _runner->saveState(); }
+ virtual void restoreState() { _runner->restoreState(); }
+ virtual void invalidate(const DiskLoc& dl) { _runner->invalidate(dl); }
+
+ virtual const CanonicalQuery& getQuery() {
+ return *_canonicalQuery;
+ }
+
+ private:
+ scoped_ptr<CanonicalQuery> _canonicalQuery;
+ scoped_ptr<QuerySolution> _solution;
+ scoped_ptr<SimplePlanRunner> _runner;
+ };
+
+} // namespace mongo
+