summaryrefslogtreecommitdiff
path: root/src/mongo/db/exec/delete.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/mongo/db/exec/delete.cpp')
-rw-r--r--src/mongo/db/exec/delete.cpp461
1 files changed, 229 insertions, 232 deletions
diff --git a/src/mongo/db/exec/delete.cpp b/src/mongo/db/exec/delete.cpp
index 787f3f244bf..5831b44e86a 100644
--- a/src/mongo/db/exec/delete.cpp
+++ b/src/mongo/db/exec/delete.cpp
@@ -45,274 +45,271 @@
namespace mongo {
- using std::unique_ptr;
- using std::vector;
-
- // static
- const char* DeleteStage::kStageType = "DELETE";
-
- DeleteStage::DeleteStage(OperationContext* txn,
- const DeleteStageParams& params,
- WorkingSet* ws,
- Collection* collection,
- PlanStage* child)
- : _txn(txn),
- _params(params),
- _ws(ws),
- _collection(collection),
- _child(child),
- _idRetrying(WorkingSet::INVALID_ID),
- _idReturning(WorkingSet::INVALID_ID),
- _commonStats(kStageType) { }
-
- DeleteStage::~DeleteStage() {}
-
- bool DeleteStage::isEOF() {
- if (!_collection) {
- return true;
- }
- if (!_params.isMulti && _specificStats.docsDeleted > 0) {
- return true;
- }
- return _idRetrying == WorkingSet::INVALID_ID
- && _idReturning == WorkingSet::INVALID_ID
- && _child->isEOF();
+using std::unique_ptr;
+using std::vector;
+
+// static
+const char* DeleteStage::kStageType = "DELETE";
+
+DeleteStage::DeleteStage(OperationContext* txn,
+ const DeleteStageParams& params,
+ WorkingSet* ws,
+ Collection* collection,
+ PlanStage* child)
+ : _txn(txn),
+ _params(params),
+ _ws(ws),
+ _collection(collection),
+ _child(child),
+ _idRetrying(WorkingSet::INVALID_ID),
+ _idReturning(WorkingSet::INVALID_ID),
+ _commonStats(kStageType) {}
+
+DeleteStage::~DeleteStage() {}
+
+bool DeleteStage::isEOF() {
+ if (!_collection) {
+ return true;
}
+ if (!_params.isMulti && _specificStats.docsDeleted > 0) {
+ return true;
+ }
+ return _idRetrying == WorkingSet::INVALID_ID && _idReturning == WorkingSet::INVALID_ID &&
+ _child->isEOF();
+}
- PlanStage::StageState DeleteStage::work(WorkingSetID* out) {
- ++_commonStats.works;
-
- // Adds the amount of time taken by work() to executionTimeMillis.
- ScopedTimer timer(&_commonStats.executionTimeMillis);
+PlanStage::StageState DeleteStage::work(WorkingSetID* out) {
+ ++_commonStats.works;
- if (isEOF()) { return PlanStage::IS_EOF; }
- invariant(_collection); // If isEOF() returns false, we must have a collection.
+ // Adds the amount of time taken by work() to executionTimeMillis.
+ ScopedTimer timer(&_commonStats.executionTimeMillis);
- // It is possible that after a delete was executed, a WriteConflictException occurred
- // and prevented us from returning ADVANCED with the old version of the document.
- if (_idReturning != WorkingSet::INVALID_ID) {
- // We should only get here if we were trying to return something before.
- invariant(_params.returnDeleted);
+ if (isEOF()) {
+ return PlanStage::IS_EOF;
+ }
+ invariant(_collection); // If isEOF() returns false, we must have a collection.
- WorkingSetMember* member = _ws->get(_idReturning);
- invariant(member->state == WorkingSetMember::OWNED_OBJ);
+ // It is possible that after a delete was executed, a WriteConflictException occurred
+ // and prevented us from returning ADVANCED with the old version of the document.
+ if (_idReturning != WorkingSet::INVALID_ID) {
+ // We should only get here if we were trying to return something before.
+ invariant(_params.returnDeleted);
- *out = _idReturning;
- _idReturning = WorkingSet::INVALID_ID;
- ++_commonStats.advanced;
- return PlanStage::ADVANCED;
- }
+ WorkingSetMember* member = _ws->get(_idReturning);
+ invariant(member->state == WorkingSetMember::OWNED_OBJ);
- // Either retry the last WSM we worked on or get a new one from our child.
- WorkingSetID id;
- StageState status;
- if (_idRetrying == WorkingSet::INVALID_ID) {
- status = _child->work(&id);
- }
- else {
- status = ADVANCED;
- id = _idRetrying;
- _idRetrying = WorkingSet::INVALID_ID;
- }
+ *out = _idReturning;
+ _idReturning = WorkingSet::INVALID_ID;
+ ++_commonStats.advanced;
+ return PlanStage::ADVANCED;
+ }
- if (PlanStage::ADVANCED == status) {
- WorkingSetMember* member = _ws->get(id);
+ // Either retry the last WSM we worked on or get a new one from our child.
+ WorkingSetID id;
+ StageState status;
+ if (_idRetrying == WorkingSet::INVALID_ID) {
+ status = _child->work(&id);
+ } else {
+ status = ADVANCED;
+ id = _idRetrying;
+ _idRetrying = WorkingSet::INVALID_ID;
+ }
- // We want to free this member when we return, unless we need to retry it.
- ScopeGuard memberFreer = MakeGuard(&WorkingSet::free, _ws, id);
+ if (PlanStage::ADVANCED == status) {
+ WorkingSetMember* member = _ws->get(id);
- if (!member->hasLoc()) {
- // We expect to be here because of an invalidation causing a force-fetch, and
- // doc-locking storage engines do not issue invalidations.
- ++_specificStats.nInvalidateSkips;
- ++_commonStats.needTime;
- return PlanStage::NEED_TIME;
- }
- RecordId rloc = member->loc;
- // Deletes can't have projections. This means that covering analysis will always add
- // a fetch. We should always get fetched data, and never just key data.
- invariant(member->hasObj());
+ // We want to free this member when we return, unless we need to retry it.
+ ScopeGuard memberFreer = MakeGuard(&WorkingSet::free, _ws, id);
- try {
- // If the snapshot changed, then we have to make sure we have the latest copy of the
- // doc and that it still matches.
- std::unique_ptr<RecordCursor> cursor;
- if (_txn->recoveryUnit()->getSnapshotId() != member->obj.snapshotId()) {
- cursor = _collection->getCursor(_txn);
- if (!WorkingSetCommon::fetch(_txn, member, cursor)) {
- // Doc is already deleted. Nothing more to do.
- ++_commonStats.needTime;
- return PlanStage::NEED_TIME;
- }
-
- // Make sure the re-fetched doc still matches the predicate.
- if (_params.canonicalQuery &&
- !_params.canonicalQuery->root()->matchesBSON(member->obj.value(), NULL)) {
- // Doesn't match.
- ++_commonStats.needTime;
- return PlanStage::NEED_TIME;
- }
+ if (!member->hasLoc()) {
+ // We expect to be here because of an invalidation causing a force-fetch, and
+ // doc-locking storage engines do not issue invalidations.
+ ++_specificStats.nInvalidateSkips;
+ ++_commonStats.needTime;
+ return PlanStage::NEED_TIME;
+ }
+ RecordId rloc = member->loc;
+ // Deletes can't have projections. This means that covering analysis will always add
+ // a fetch. We should always get fetched data, and never just key data.
+ invariant(member->hasObj());
+
+ try {
+ // If the snapshot changed, then we have to make sure we have the latest copy of the
+ // doc and that it still matches.
+ std::unique_ptr<RecordCursor> cursor;
+ if (_txn->recoveryUnit()->getSnapshotId() != member->obj.snapshotId()) {
+ cursor = _collection->getCursor(_txn);
+ if (!WorkingSetCommon::fetch(_txn, member, cursor)) {
+ // Doc is already deleted. Nothing more to do.
+ ++_commonStats.needTime;
+ return PlanStage::NEED_TIME;
}
- // TODO: Do we want to buffer docs and delete them in a group rather than
- // saving/restoring state repeatedly?
-
- try {
- _child->saveState();
- if (supportsDocLocking()) {
- // Doc-locking engines require this after saveState() since they don't use
- // invalidations.
- WorkingSetCommon::prepareForSnapshotChange(_ws);
- }
- }
- catch ( const WriteConflictException& wce ) {
- std::terminate();
+ // Make sure the re-fetched doc still matches the predicate.
+ if (_params.canonicalQuery &&
+ !_params.canonicalQuery->root()->matchesBSON(member->obj.value(), NULL)) {
+ // Doesn't match.
+ ++_commonStats.needTime;
+ return PlanStage::NEED_TIME;
}
+ }
- if (_params.returnDeleted) {
- // Save a copy of the document that is about to get deleted.
- BSONObj deletedDoc = member->obj.value();
- member->obj.setValue(deletedDoc.getOwned());
- member->loc = RecordId();
- member->state = WorkingSetMember::OWNED_OBJ;
+ // TODO: Do we want to buffer docs and delete them in a group rather than
+ // saving/restoring state repeatedly?
+
+ try {
+ _child->saveState();
+ if (supportsDocLocking()) {
+ // Doc-locking engines require this after saveState() since they don't use
+ // invalidations.
+ WorkingSetCommon::prepareForSnapshotChange(_ws);
}
+ } catch (const WriteConflictException& wce) {
+ std::terminate();
+ }
- // Do the write, unless this is an explain.
- if (!_params.isExplain) {
- WriteUnitOfWork wunit(_txn);
+ if (_params.returnDeleted) {
+ // Save a copy of the document that is about to get deleted.
+ BSONObj deletedDoc = member->obj.value();
+ member->obj.setValue(deletedDoc.getOwned());
+ member->loc = RecordId();
+ member->state = WorkingSetMember::OWNED_OBJ;
+ }
- const bool deleteCappedOK = false;
- const bool deleteNoWarn = false;
- BSONObj deletedId;
+ // Do the write, unless this is an explain.
+ if (!_params.isExplain) {
+ WriteUnitOfWork wunit(_txn);
- _collection->deleteDocument(_txn, rloc, deleteCappedOK, deleteNoWarn,
- _params.shouldCallLogOp ? &deletedId : NULL);
+ const bool deleteCappedOK = false;
+ const bool deleteNoWarn = false;
+ BSONObj deletedId;
- wunit.commit();
- }
+ _collection->deleteDocument(_txn,
+ rloc,
+ deleteCappedOK,
+ deleteNoWarn,
+ _params.shouldCallLogOp ? &deletedId : NULL);
- ++_specificStats.docsDeleted;
- }
- catch ( const WriteConflictException& wce ) {
- _idRetrying = id;
- memberFreer.Dismiss(); // Keep this member around so we can retry deleting it.
- *out = WorkingSet::INVALID_ID;
- _commonStats.needYield++;
- return NEED_YIELD;
+ wunit.commit();
}
- // As restoreState may restore (recreate) cursors, cursors are tied to the
- // transaction in which they are created, and a WriteUnitOfWork is a
- // transaction, make sure to restore the state outside of the WritUnitOfWork.
- try {
- _child->restoreState(_txn);
- }
- catch ( const WriteConflictException& wce ) {
- // Note we don't need to retry anything in this case since the delete already
- // was committed. However, we still need to return the deleted document
- // (if it was requested).
- if (_params.returnDeleted) {
- // member->obj should refer to the deleted document.
- invariant(member->state == WorkingSetMember::OWNED_OBJ);
-
- _idReturning = id;
- // Keep this member around so that we can return it on the next work() call.
- memberFreer.Dismiss();
- }
- *out = WorkingSet::INVALID_ID;
- _commonStats.needYield++;
- return NEED_YIELD;
- }
+ ++_specificStats.docsDeleted;
+ } catch (const WriteConflictException& wce) {
+ _idRetrying = id;
+ memberFreer.Dismiss(); // Keep this member around so we can retry deleting it.
+ *out = WorkingSet::INVALID_ID;
+ _commonStats.needYield++;
+ return NEED_YIELD;
+ }
+ // As restoreState may restore (recreate) cursors, cursors are tied to the
+ // transaction in which they are created, and a WriteUnitOfWork is a
+ // transaction, make sure to restore the state outside of the WritUnitOfWork.
+ try {
+ _child->restoreState(_txn);
+ } catch (const WriteConflictException& wce) {
+ // Note we don't need to retry anything in this case since the delete already
+ // was committed. However, we still need to return the deleted document
+ // (if it was requested).
if (_params.returnDeleted) {
// member->obj should refer to the deleted document.
invariant(member->state == WorkingSetMember::OWNED_OBJ);
- memberFreer.Dismiss(); // Keep this member around so we can return it.
- *out = id;
- ++_commonStats.advanced;
- return PlanStage::ADVANCED;
- }
-
- ++_commonStats.needTime;
- return PlanStage::NEED_TIME;
- }
- else if (PlanStage::FAILURE == status || PlanStage::DEAD == status) {
- *out = id;
- // If a stage fails, it may create a status WSM to indicate why it failed, in which case
- // 'id' is valid. If ID is invalid, we create our own error message.
- if (WorkingSet::INVALID_ID == id) {
- const std::string errmsg = "delete stage failed to read in results from child";
- *out = WorkingSetCommon::allocateStatusMember(_ws, Status(ErrorCodes::InternalError,
- errmsg));
+ _idReturning = id;
+ // Keep this member around so that we can return it on the next work() call.
+ memberFreer.Dismiss();
}
- return status;
- }
- else if (PlanStage::NEED_TIME == status) {
- ++_commonStats.needTime;
+ *out = WorkingSet::INVALID_ID;
+ _commonStats.needYield++;
+ return NEED_YIELD;
}
- else if (PlanStage::NEED_YIELD == status) {
+
+ if (_params.returnDeleted) {
+ // member->obj should refer to the deleted document.
+ invariant(member->state == WorkingSetMember::OWNED_OBJ);
+
+ memberFreer.Dismiss(); // Keep this member around so we can return it.
*out = id;
- ++_commonStats.needYield;
+ ++_commonStats.advanced;
+ return PlanStage::ADVANCED;
}
+ ++_commonStats.needTime;
+ return PlanStage::NEED_TIME;
+ } else if (PlanStage::FAILURE == status || PlanStage::DEAD == status) {
+ *out = id;
+ // If a stage fails, it may create a status WSM to indicate why it failed, in which case
+ // 'id' is valid. If ID is invalid, we create our own error message.
+ if (WorkingSet::INVALID_ID == id) {
+ const std::string errmsg = "delete stage failed to read in results from child";
+ *out = WorkingSetCommon::allocateStatusMember(
+ _ws, Status(ErrorCodes::InternalError, errmsg));
+ }
return status;
+ } else if (PlanStage::NEED_TIME == status) {
+ ++_commonStats.needTime;
+ } else if (PlanStage::NEED_YIELD == status) {
+ *out = id;
+ ++_commonStats.needYield;
}
- void DeleteStage::saveState() {
- _txn = NULL;
- ++_commonStats.yields;
- _child->saveState();
- }
-
- void DeleteStage::restoreState(OperationContext* opCtx) {
- invariant(_txn == NULL);
- _txn = opCtx;
- ++_commonStats.unyields;
- _child->restoreState(opCtx);
-
- const NamespaceString& ns(_collection->ns());
- massert(28537,
- str::stream() << "Demoted from primary while removing from " << ns.ns(),
- !_params.shouldCallLogOp ||
+ return status;
+}
+
+void DeleteStage::saveState() {
+ _txn = NULL;
+ ++_commonStats.yields;
+ _child->saveState();
+}
+
+void DeleteStage::restoreState(OperationContext* opCtx) {
+ invariant(_txn == NULL);
+ _txn = opCtx;
+ ++_commonStats.unyields;
+ _child->restoreState(opCtx);
+
+ const NamespaceString& ns(_collection->ns());
+ massert(28537,
+ str::stream() << "Demoted from primary while removing from " << ns.ns(),
+ !_params.shouldCallLogOp ||
repl::getGlobalReplicationCoordinator()->canAcceptWritesFor(ns));
- }
-
- void DeleteStage::invalidate(OperationContext* txn, const RecordId& dl, InvalidationType type) {
- ++_commonStats.invalidates;
- _child->invalidate(txn, dl, type);
- }
-
- vector<PlanStage*> DeleteStage::getChildren() const {
- vector<PlanStage*> children;
- children.push_back(_child.get());
- return children;
- }
-
- PlanStageStats* DeleteStage::getStats() {
- _commonStats.isEOF = isEOF();
- unique_ptr<PlanStageStats> ret(new PlanStageStats(_commonStats, STAGE_DELETE));
- ret->specific.reset(new DeleteStats(_specificStats));
- ret->children.push_back(_child->getStats());
- return ret.release();
- }
-
- const CommonStats* DeleteStage::getCommonStats() const {
- return &_commonStats;
- }
-
- const SpecificStats* DeleteStage::getSpecificStats() const {
- return &_specificStats;
- }
-
- // static
- long long DeleteStage::getNumDeleted(PlanExecutor* exec) {
- invariant(exec->getRootStage()->isEOF());
- invariant(exec->getRootStage()->stageType() == STAGE_DELETE);
- DeleteStage* deleteStage = static_cast<DeleteStage*>(exec->getRootStage());
- const DeleteStats* deleteStats =
- static_cast<const DeleteStats*>(deleteStage->getSpecificStats());
- return deleteStats->docsDeleted;
- }
+}
+
+void DeleteStage::invalidate(OperationContext* txn, const RecordId& dl, InvalidationType type) {
+ ++_commonStats.invalidates;
+ _child->invalidate(txn, dl, type);
+}
+
+vector<PlanStage*> DeleteStage::getChildren() const {
+ vector<PlanStage*> children;
+ children.push_back(_child.get());
+ return children;
+}
+
+PlanStageStats* DeleteStage::getStats() {
+ _commonStats.isEOF = isEOF();
+ unique_ptr<PlanStageStats> ret(new PlanStageStats(_commonStats, STAGE_DELETE));
+ ret->specific.reset(new DeleteStats(_specificStats));
+ ret->children.push_back(_child->getStats());
+ return ret.release();
+}
+
+const CommonStats* DeleteStage::getCommonStats() const {
+ return &_commonStats;
+}
+
+const SpecificStats* DeleteStage::getSpecificStats() const {
+ return &_specificStats;
+}
+
+// static
+long long DeleteStage::getNumDeleted(PlanExecutor* exec) {
+ invariant(exec->getRootStage()->isEOF());
+ invariant(exec->getRootStage()->stageType() == STAGE_DELETE);
+ DeleteStage* deleteStage = static_cast<DeleteStage*>(exec->getRootStage());
+ const DeleteStats* deleteStats =
+ static_cast<const DeleteStats*>(deleteStage->getSpecificStats());
+ return deleteStats->docsDeleted;
+}
} // namespace mongo