/**
* 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 .
*
* As a special exception, the copyright holders give permission to link the
* code of portions of this program with the OpenSSL library under certain
* conditions as described in each individual source file and distribute
* linked combinations including the program with the OpenSSL library. You
* must comply with the GNU Affero General Public License in all respects for
* all of the code used other than as permitted herein. If you modify file(s)
* with this exception, you may extend this exception to your version of the
* file(s), but you are not obligated to do so. If you do not wish to do so,
* delete this exception statement from your version. If you delete this
* exception statement from all source files in the program, then also delete
* it in the license file.
*/
#pragma once
#include
#include
#include "mongo/base/status.h"
#include "mongo/db/invalidation_type.h"
#include "mongo/db/query/query_solution.h"
#include "mongo/db/storage/snapshot.h"
namespace mongo {
class BSONObj;
class Collection;
class RecordId;
class PlanStage;
class PlanExecutor;
struct PlanStageStats;
class PlanYieldPolicy;
class WorkingSet;
/**
* A PlanExecutor is the abstraction that knows how to crank a tree of stages into execution.
* The executor is usually part of a larger abstraction that is interacting with the cache
* and/or the query optimizer.
*
* Executes a plan. Calls work() on a plan until a result is produced. Stops when the plan is
* EOF or if the plan errors.
*/
class PlanExecutor {
public:
enum ExecState {
// We successfully populated the out parameter.
ADVANCED,
// We're EOF. We won't return any more results (edge case exception: capped+tailable).
IS_EOF,
// We were killed. This is a special failure case in which we cannot rely on the
// collection or database to still be valid.
// If the underlying PlanStage has any information on the error, it will be available in
// the objOut parameter. Call WorkingSetCommon::toStatusString() to retrieve the error
// details from the output BSON object.
DEAD,
// getNext was asked for data it cannot provide, or the underlying PlanStage had an
// unrecoverable error.
// If the underlying PlanStage has any information on the error, it will be available in
// the objOut parameter. Call WorkingSetCommon::toStatusString() to retrieve the error
// details from the output BSON object.
FAILURE,
};
/**
* The yielding policy of the plan executor. By default, an executor does not yield itself
* (YIELD_MANUAL).
*/
enum YieldPolicy {
// Any call to getNext() may yield. In particular, the executor may be killed during any
// call to getNext(). If this occurs, getNext() will return DEAD. Additionally, this
// will handle all WriteConflictExceptions that occur while processing the query.
YIELD_AUTO,
// This will handle WriteConflictExceptions that occur while processing the query, but
// will not yield locks. abandonSnapshot() will be called if a WriteConflictException
// occurs so callers must be prepared to get a new snapshot.
WRITE_CONFLICT_RETRY_ONLY,
// Owner must yield manually if yields are requested. How to yield yourself:
//
// 0. Let's say you have PlanExecutor* exec.
//
// 1. Register your PlanExecutor with ClientCursor. Registered executors are informed
// about RecordId deletions and namespace invalidation, as well as other important
// events. Do this by calling registerExec() on the executor. Alternatively, this can
// be done per-yield (as described below).
//
// 2. Construct a PlanYieldPolicy 'policy', passing 'exec' to the constructor.
//
// 3. Call PlanYieldPolicy::yield() on 'policy'. If your PlanExecutor is not yet
// registered (because you want to register on a per-yield basis), then pass
// 'true' to yield().
//
// 4. The call to yield() returns a boolean indicating whether or not 'exec' is
// still alove. If it is false, then 'exec' was killed during the yield and is
// no longer valid.
//
// It is not possible to handle WriteConflictExceptions in this mode without restarting
// the query.
YIELD_MANUAL,
};
//
// Factory methods.
//
// On success, return a new PlanExecutor, owned by the caller, through 'out'.
//
// Passing YIELD_AUTO to any of these factories will construct a yielding executor which
// may yield in the following circumstances:
// 1) During plan selection inside the call to make().
// 2) On any call to getNext().
// 3) While executing the plan inside executePlan().
//
// The executor will also be automatically registered to receive notifications in the
// case of YIELD_AUTO, so no further calls to registerExec() or setYieldPolicy() are
// necessary.
//
/**
* Used when there is no canonical query and no query solution.
*
* Right now this is only for idhack updates which neither canonicalize
* nor go through normal planning.
*/
static StatusWith> make(OperationContext* opCtx,
std::unique_ptr ws,
std::unique_ptr rt,
const Collection* collection,
YieldPolicy yieldPolicy);
/**
* Used when we have a NULL collection and no canonical query. In this case,
* we need to explicitly pass a namespace to the plan executor.
*/
static StatusWith> make(OperationContext* opCtx,
std::unique_ptr ws,
std::unique_ptr rt,
const std::string& ns,
YieldPolicy yieldPolicy);
/**
* Used when there is a canonical query but no query solution (e.g. idhack
* queries, queries against a NULL collection, queries using the subplan stage).
*/
static StatusWith> make(OperationContext* opCtx,
std::unique_ptr ws,
std::unique_ptr rt,
std::unique_ptr cq,
const Collection* collection,
YieldPolicy yieldPolicy);
/**
* The constructor for the normal case, when you have a collection, a canonical query,
* and a query solution.
*/
static StatusWith> make(OperationContext* opCtx,
std::unique_ptr ws,
std::unique_ptr rt,
std::unique_ptr qs,
std::unique_ptr cq,
const Collection* collection,
YieldPolicy yieldPolicy);
~PlanExecutor();
//
// Accessors
//
/**
* Get the working set used by this executor, without transferring ownership.
*/
WorkingSet* getWorkingSet() const;
/**
* Get the stage tree wrapped by this executor, without transferring ownership.
*/
PlanStage* getRootStage() const;
/**
* Get the query that this executor is executing, without transferring ownership.
*/
CanonicalQuery* getCanonicalQuery() const;
/**
* Return the NS that the query is running over.
*/
const std::string& ns();
/**
* Return the OperationContext that the plan is currently executing within.
*/
OperationContext* getOpCtx() const;
/**
* Generates a tree of stats objects with a separate lifetime from the execution
* stage tree wrapped by this PlanExecutor.
*
* This is OK even if we were killed.
*/
std::unique_ptr getStats() const;
//
// Methods that just pass down to the PlanStage tree.
//
/**
* Save any state required to recover from changes to the underlying collection's data.
*
* While in the "saved" state, it is only legal to call restoreState,
* detachFromOperationContext, or the destructor.
*/
void saveState();
/**
* Restores the state saved by a saveState() call.
*
* Returns true if the state was successfully restored and the execution tree can be
* work()'d.
*
* Returns false if the PlanExecutor was killed while saved. A killed execution tree cannot be
* worked and should be deleted.
*
* If allowed, will yield and retry if a WriteConflictException is encountered.
*/
bool restoreState();
/**
* Detaches from the OperationContext and releases any storage-engine state.
*
* It is only legal to call this when in a "saved" state. While in the "detached" state, it is
* only legal to call reattachToOperationContext or the destructor. It is not legal to call
* detachFromOperationContext() while already in the detached state.
*/
void detachFromOperationContext();
/**
* Reattaches to the OperationContext and reacquires any storage-engine state.
*
* It is only legal to call this in the "detached" state. On return, the cursor is left in a
* "saved" state, so callers must still call restoreState to use this object.
*/
void reattachToOperationContext(OperationContext* opCtx);
/**
* Same as restoreState but without the logic to retry if a WriteConflictException is
* thrown.
*
* This is only public for PlanYieldPolicy. DO NOT CALL ANYWHERE ELSE.
*/
bool restoreStateWithoutRetrying();
//
// Running Support
//
/**
* Return the next result from the underlying execution tree.
*
* For read operations, objOut or dlOut are populated with another query result.
*
* For write operations, the return depends on the particulars of the write stage.
*
* If a YIELD_AUTO policy is set, then this method may yield.
*/
ExecState getNextSnapshotted(Snapshotted* objOut, RecordId* dlOut);
ExecState getNext(BSONObj* objOut, RecordId* dlOut);
/**
* Returns 'true' if the plan is done producing results (or writing), 'false' otherwise.
*
* Tailable cursors are a possible exception to this: they may have further results even if
* isEOF() returns true.
*/
bool isEOF();
/**
* Execute the plan to completion, throwing out the results. Used when you want to work the
* underlying tree without getting results back.
*
* If a YIELD_AUTO policy is set on this executor, then this will automatically yield.
*
* Returns ErrorCodes::QueryPlanKilled if the plan executor was killed during a yield. If this
* error occurs, it is illegal to subsequently access the collection, since it may have been
* dropped.
*/
Status executePlan();
//
// Concurrency-related methods.
//
/**
* Register this plan executor with the collection cursor manager so that it
* receives notifications for events that happen while yielding any locks.
*
* Deregistration happens automatically when this plan executor is destroyed.
*/
void registerExec(const Collection* collection);
/**
* Unregister this PlanExecutor. Normally you want the PlanExecutor to be registered
* for its lifetime, and you shouldn't have to call this explicitly.
*/
void deregisterExec();
/**
* If we're yielding locks, the database we're operating over or any collection we're
* relying on may be dropped. When this happens all cursors and plan executors on that
* database and collection are killed or deleted in some fashion. Callers must specify
* the 'reason' for why this executor is being killed.
*/
void kill(std::string reason);
/**
* If we're yielding locks, writes may occur to documents that we rely on to keep valid
* state. As such, if the plan yields, it must be notified of relevant writes so that
* we can ensure that it doesn't crash if we try to access invalid state.
*/
void invalidate(OperationContext* txn, const RecordId& dl, InvalidationType type);
/**
* Helper method to aid in displaying an ExecState for debug or other recreational purposes.
*/
static std::string statestr(ExecState s);
/**
* Change the yield policy of the PlanExecutor to 'policy'. If 'registerExecutor' is true,
* and the yield policy is YIELD_AUTO, then the plan executor gets registered to receive
* notifications of events from other threads.
*
* Everybody who sets the policy to YIELD_AUTO really wants to call registerExec()
* immediately after EXCEPT commands that create cursors...so we expose the ability to
* register (or not) here, rather than require all users to have yet another RAII object.
* Only cursor-creating things like find.cpp set registerExecutor to false.
*/
void setYieldPolicy(YieldPolicy policy,
const Collection* collection,
bool registerExecutor = true);
/**
* Stash the BSONObj so that it gets returned from the PlanExecutor on a later call to
* getNext().
*
* Enqueued documents are returned in FIFO order. The queued results are exhausted before
* generating further results from the underlying query plan.
*
* Subsequent calls to getNext() must request the BSONObj and *not* the RecordId.
*
* If used in combination with getNextSnapshotted(), then the SnapshotId associated with
* 'obj' will be null when 'obj' is dequeued.
*/
void enqueue(const BSONObj& obj);
/**
* Helper method which returns a set of BSONObj, where each represents a sort order of our
* output.
*/
BSONObjSet getOutputSorts() const;
private:
ExecState getNextImpl(Snapshotted* objOut, RecordId* dlOut);
/**
* RAII approach to ensuring that plan executors are deregistered.
*
* While retrieving the first batch of results, runQuery manually registers the executor
* with ClientCursor. Certain query execution paths, namely $where, can throw an exception.
* If we fail to deregister the executor, we will call invalidate/kill on the
* still-registered-yet-deleted executor.
*
* For any subsequent calls to getMore, the executor is already registered with ClientCursor
* by virtue of being cached, so this exception-proofing is not required.
*/
struct ScopedExecutorRegistration {
ScopedExecutorRegistration(PlanExecutor* exec, const Collection* collection);
~ScopedExecutorRegistration();
PlanExecutor* const _exec;
const Collection* const _collection;
};
/**
* New PlanExecutor instances are created with the static make() methods above.
*/
PlanExecutor(OperationContext* opCtx,
std::unique_ptr ws,
std::unique_ptr rt,
std::unique_ptr qs,
std::unique_ptr cq,
const Collection* collection,
const std::string& ns);
/**
* Public factory methods delegate to this private factory to do their work.
*/
static StatusWith> make(OperationContext* txn,
std::unique_ptr ws,
std::unique_ptr rt,
std::unique_ptr qs,
std::unique_ptr cq,
const Collection* collection,
const std::string& ns,
YieldPolicy yieldPolicy);
/**
* Clients of PlanExecutor expect that on receiving a new instance from one of the make()
* factory methods, plan selection has already been completed. In order to enforce this
* property, this function is called to do plan selection prior to returning the new
* PlanExecutor.
*
* If the tree contains plan selection stages, such as MultiPlanStage or SubplanStage,
* this calls into their underlying plan selection facilities. Otherwise, does nothing.
*
* If a YIELD_AUTO policy is set then locks are yielded during plan selection.
*
* Returns a non-OK status if query planning fails. In particular, this function returns
* ErrorCodes::QueryPlanKilled if plan execution cannot proceed due to a concurrent write or
* catalog operation.
*/
Status pickBestPlan(YieldPolicy policy, const Collection* collection);
bool killed() {
return static_cast(_killReason);
};
// The OperationContext that we're executing within. We need this in order to release
// locks.
OperationContext* _opCtx;
std::unique_ptr _cq;
std::unique_ptr _workingSet;
std::unique_ptr _qs;
std::unique_ptr _root;
// If _killReason has a value, then we have been killed and the value represents the reason
// for the kill.
// The ScopedExecutorRegistration skips dereigstering the plan executor when the plan executor
// has been killed, so _killReason must outlive _safety.
boost::optional _killReason;
// Deregisters this executor when it is destroyed.
std::unique_ptr _safety;
// What namespace are we operating over?
std::string _ns;
// This is used to handle automatic yielding when allowed by the YieldPolicy. Never NULL.
// TODO make this a non-pointer member. This requires some header shuffling so that this
// file includes plan_yield_policy.h rather than the other way around.
const std::unique_ptr _yieldPolicy;
// A stash of results generated by this plan that the user of the PlanExecutor didn't want
// to consume yet. We empty the queue before retrieving further results from the plan
// stages.
std::queue _stash;
enum { kUsable, kSaved, kDetached } _currentState = kUsable;
bool _everDetachedFromOperationContext = false;
};
} // namespace mongo