summaryrefslogtreecommitdiff
path: root/src/mongo/db/commands/find_and_modify.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/mongo/db/commands/find_and_modify.cpp')
-rw-r--r--src/mongo/db/commands/find_and_modify.cpp671
1 files changed, 331 insertions, 340 deletions
diff --git a/src/mongo/db/commands/find_and_modify.cpp b/src/mongo/db/commands/find_and_modify.cpp
index 609b29673c2..ee7c2544ac3 100644
--- a/src/mongo/db/commands/find_and_modify.cpp
+++ b/src/mongo/db/commands/find_and_modify.cpp
@@ -67,423 +67,414 @@ namespace mongo {
namespace {
- const UpdateStats* getUpdateStats(const PlanStageStats* stats) {
- // The stats may refer to an update stage, or a projection stage wrapping an update stage.
- if (StageType::STAGE_PROJECTION == stats->stageType) {
- invariant(stats->children.size() == 1);
- stats = stats->children[0];
- }
+const UpdateStats* getUpdateStats(const PlanStageStats* stats) {
+ // The stats may refer to an update stage, or a projection stage wrapping an update stage.
+ if (StageType::STAGE_PROJECTION == stats->stageType) {
+ invariant(stats->children.size() == 1);
+ stats = stats->children[0];
+ }
- invariant(StageType::STAGE_UPDATE == stats->stageType);
- return static_cast<UpdateStats*>(stats->specific.get());
+ invariant(StageType::STAGE_UPDATE == stats->stageType);
+ return static_cast<UpdateStats*>(stats->specific.get());
+}
+
+const DeleteStats* getDeleteStats(const PlanStageStats* stats) {
+ // The stats may refer to a delete stage, or a projection stage wrapping a delete stage.
+ if (StageType::STAGE_PROJECTION == stats->stageType) {
+ invariant(stats->children.size() == 1);
+ stats = stats->children[0];
}
- const DeleteStats* getDeleteStats(const PlanStageStats* stats) {
- // The stats may refer to a delete stage, or a projection stage wrapping a delete stage.
- if (StageType::STAGE_PROJECTION == stats->stageType) {
- invariant(stats->children.size() == 1);
- stats = stats->children[0];
- }
+ invariant(StageType::STAGE_DELETE == stats->stageType);
+ return static_cast<DeleteStats*>(stats->specific.get());
+}
- invariant(StageType::STAGE_DELETE == stats->stageType);
- return static_cast<DeleteStats*>(stats->specific.get());
+/**
+ * If the operation succeeded, then Status::OK() is returned, possibly with a document value
+ * to return to the client. If no matching document to update or remove was found, then none
+ * is returned. Otherwise, the updated or deleted document is returned.
+ *
+ * If the operation failed, then an error Status is returned.
+ */
+StatusWith<boost::optional<BSONObj>> advanceExecutor(PlanExecutor* exec, bool isRemove) {
+ BSONObj value;
+ PlanExecutor::ExecState state = exec->getNext(&value, nullptr);
+ if (PlanExecutor::ADVANCED == state) {
+ return boost::optional<BSONObj>(std::move(value));
}
+ if (PlanExecutor::FAILURE == state || PlanExecutor::DEAD == state) {
+ const std::unique_ptr<PlanStageStats> stats(exec->getStats());
+ error() << "Plan executor error during findAndModify: " << PlanExecutor::statestr(state)
+ << ", stats: " << Explain::statsToBSON(*stats);
- /**
- * If the operation succeeded, then Status::OK() is returned, possibly with a document value
- * to return to the client. If no matching document to update or remove was found, then none
- * is returned. Otherwise, the updated or deleted document is returned.
- *
- * If the operation failed, then an error Status is returned.
- */
- StatusWith<boost::optional<BSONObj>> advanceExecutor(PlanExecutor* exec, bool isRemove) {
- BSONObj value;
- PlanExecutor::ExecState state = exec->getNext(&value, nullptr);
- if (PlanExecutor::ADVANCED == state) {
- return boost::optional<BSONObj>(std::move(value));
+ if (WorkingSetCommon::isValidStatusMemberObject(value)) {
+ const Status errorStatus = WorkingSetCommon::getMemberObjectStatus(value);
+ invariant(!errorStatus.isOK());
+ return {errorStatus.code(), errorStatus.reason()};
}
- if (PlanExecutor::FAILURE == state || PlanExecutor::DEAD == state) {
- const std::unique_ptr<PlanStageStats> stats(exec->getStats());
- error() << "Plan executor error during findAndModify: "
- << PlanExecutor::statestr(state)
- << ", stats: " << Explain::statsToBSON(*stats);
-
- if (WorkingSetCommon::isValidStatusMemberObject(value)) {
- const Status errorStatus =
- WorkingSetCommon::getMemberObjectStatus(value);
- invariant(!errorStatus.isOK());
- return {errorStatus.code(), errorStatus.reason()};
- }
- const std::string opstr = isRemove ? "delete" : "update";
- return {ErrorCodes::OperationFailed, str::stream()
- << "executor returned " << PlanExecutor::statestr(state)
- << " while executing " << opstr};
+ const std::string opstr = isRemove ? "delete" : "update";
+ return {ErrorCodes::OperationFailed,
+ str::stream() << "executor returned " << PlanExecutor::statestr(state)
+ << " while executing " << opstr};
+ }
+ invariant(state == PlanExecutor::IS_EOF);
+ return boost::optional<BSONObj>(boost::none);
+}
+
+void makeUpdateRequest(const FindAndModifyRequest& args,
+ bool explain,
+ UpdateLifecycleImpl* updateLifecycle,
+ UpdateRequest* requestOut) {
+ requestOut->setQuery(args.getQuery());
+ requestOut->setProj(args.getFields());
+ requestOut->setUpdates(args.getUpdateObj());
+ requestOut->setSort(args.getSort());
+ requestOut->setUpsert(args.isUpsert());
+ requestOut->setReturnDocs(args.shouldReturnNew() ? UpdateRequest::RETURN_NEW
+ : UpdateRequest::RETURN_OLD);
+ requestOut->setMulti(false);
+ requestOut->setYieldPolicy(PlanExecutor::YIELD_AUTO);
+ requestOut->setExplain(explain);
+ requestOut->setLifecycle(updateLifecycle);
+}
+
+void makeDeleteRequest(const FindAndModifyRequest& args, bool explain, DeleteRequest* requestOut) {
+ requestOut->setQuery(args.getQuery());
+ requestOut->setProj(args.getFields());
+ requestOut->setSort(args.getSort());
+ requestOut->setMulti(false);
+ requestOut->setYieldPolicy(PlanExecutor::YIELD_AUTO);
+ requestOut->setReturnDeleted(true); // Always return the old value.
+ requestOut->setExplain(explain);
+}
+
+void appendCommandResponse(PlanExecutor* exec,
+ bool isRemove,
+ const boost::optional<BSONObj>& value,
+ BSONObjBuilder& result) {
+ const std::unique_ptr<PlanStageStats> stats(exec->getStats());
+ BSONObjBuilder lastErrorObjBuilder(result.subobjStart("lastErrorObject"));
+
+ if (isRemove) {
+ lastErrorObjBuilder.appendNumber("n", getDeleteStats(stats.get())->docsDeleted);
+ } else {
+ const UpdateStats* updateStats = getUpdateStats(stats.get());
+ lastErrorObjBuilder.appendBool("updatedExisting", updateStats->nMatched > 0);
+ lastErrorObjBuilder.appendNumber("n", updateStats->inserted ? 1 : updateStats->nMatched);
+ // Note we have to use the objInserted from the stats here, rather than 'value'
+ // because the _id field could have been excluded by a projection.
+ if (!updateStats->objInserted.isEmpty()) {
+ lastErrorObjBuilder.appendAs(updateStats->objInserted["_id"], kUpsertedFieldName);
}
- invariant(state == PlanExecutor::IS_EOF);
- return boost::optional<BSONObj>(boost::none);
}
+ lastErrorObjBuilder.done();
- void makeUpdateRequest(const FindAndModifyRequest& args,
- bool explain,
- UpdateLifecycleImpl* updateLifecycle,
- UpdateRequest* requestOut) {
- requestOut->setQuery(args.getQuery());
- requestOut->setProj(args.getFields());
- requestOut->setUpdates(args.getUpdateObj());
- requestOut->setSort(args.getSort());
- requestOut->setUpsert(args.isUpsert());
- requestOut->setReturnDocs(args.shouldReturnNew()
- ? UpdateRequest::RETURN_NEW
- : UpdateRequest::RETURN_OLD);
- requestOut->setMulti(false);
- requestOut->setYieldPolicy(PlanExecutor::YIELD_AUTO);
- requestOut->setExplain(explain);
- requestOut->setLifecycle(updateLifecycle);
+ if (value) {
+ result.append("value", *value);
+ } else {
+ result.appendNull("value");
}
+}
+
+Status checkCanAcceptWritesForDatabase(const NamespaceString& nsString) {
+ if (!repl::getGlobalReplicationCoordinator()->canAcceptWritesFor(nsString)) {
+ return Status(ErrorCodes::NotMaster,
+ str::stream()
+ << "Not primary while running findAndModify command on collection "
+ << nsString.ns());
+ }
+ return Status::OK();
+}
+
+} // namespace
- void makeDeleteRequest(const FindAndModifyRequest& args,
- bool explain,
- DeleteRequest* requestOut) {
- requestOut->setQuery(args.getQuery());
- requestOut->setProj(args.getFields());
- requestOut->setSort(args.getSort());
- requestOut->setMulti(false);
- requestOut->setYieldPolicy(PlanExecutor::YIELD_AUTO);
- requestOut->setReturnDeleted(true); // Always return the old value.
- requestOut->setExplain(explain);
+/* Find and Modify an object returning either the old (default) or new value*/
+class CmdFindAndModify : public Command {
+public:
+ void help(std::stringstream& help) const override {
+ help << "{ findAndModify: \"collection\", query: {processed:false}, update: {$set: "
+ "{processed:true}}, new: true}\n"
+ "{ findAndModify: \"collection\", query: {processed:false}, remove: true, sort: "
+ "{priority:-1}}\n"
+ "Either update or remove is required, all other fields have default values.\n"
+ "Output is in the \"value\" field\n";
}
- void appendCommandResponse(PlanExecutor* exec,
- bool isRemove,
- const boost::optional<BSONObj>& value,
- BSONObjBuilder& result) {
- const std::unique_ptr<PlanStageStats> stats(exec->getStats());
- BSONObjBuilder lastErrorObjBuilder(result.subobjStart("lastErrorObject"));
+ CmdFindAndModify() : Command("findAndModify", false, "findandmodify") {}
+ bool slaveOk() const override {
+ return false;
+ }
+ bool isWriteCommandForConfigServer() const override {
+ return true;
+ }
+ void addRequiredPrivileges(const std::string& dbname,
+ const BSONObj& cmdObj,
+ std::vector<Privilege>* out) override {
+ find_and_modify::addPrivilegesRequiredForFindAndModify(this, dbname, cmdObj, out);
+ }
- if (isRemove) {
- lastErrorObjBuilder.appendNumber("n", getDeleteStats(stats.get())->docsDeleted);
+ Status explain(OperationContext* txn,
+ const std::string& dbName,
+ const BSONObj& cmdObj,
+ ExplainCommon::Verbosity verbosity,
+ BSONObjBuilder* out) const override {
+ const std::string fullNs = parseNsCollectionRequired(dbName, cmdObj);
+ Status allowedWriteStatus = userAllowedWriteNS(fullNs);
+ if (!allowedWriteStatus.isOK()) {
+ return allowedWriteStatus;
}
- else {
- const UpdateStats* updateStats = getUpdateStats(stats.get());
- lastErrorObjBuilder.appendBool("updatedExisting", updateStats->nMatched > 0);
- lastErrorObjBuilder.appendNumber("n", updateStats->inserted ? 1
- : updateStats->nMatched);
- // Note we have to use the objInserted from the stats here, rather than 'value'
- // because the _id field could have been excluded by a projection.
- if (!updateStats->objInserted.isEmpty()) {
- lastErrorObjBuilder.appendAs(updateStats->objInserted["_id"], kUpsertedFieldName);
- }
- }
- lastErrorObjBuilder.done();
- if (value) {
- result.append("value", *value);
- }
- else {
- result.appendNull("value");
+ StatusWith<FindAndModifyRequest> parseStatus =
+ FindAndModifyRequest::parseFromBSON(NamespaceString(fullNs), cmdObj);
+ if (!parseStatus.isOK()) {
+ return parseStatus.getStatus();
}
- }
- Status checkCanAcceptWritesForDatabase(const NamespaceString& nsString) {
- if (!repl::getGlobalReplicationCoordinator()->canAcceptWritesFor(nsString)) {
- return Status(ErrorCodes::NotMaster, str::stream()
- << "Not primary while running findAndModify command on collection "
- << nsString.ns());
- }
- return Status::OK();
- }
+ const FindAndModifyRequest& args = parseStatus.getValue();
+ const NamespaceString& nsString = args.getNamespaceString();
-} // namespace
+ auto client = txn->getClient();
- /* Find and Modify an object returning either the old (default) or new value*/
- class CmdFindAndModify : public Command {
- public:
- void help(std::stringstream& help) const override {
- help <<
- "{ findAndModify: \"collection\", query: {processed:false}, update: {$set: {processed:true}}, new: true}\n"
- "{ findAndModify: \"collection\", query: {processed:false}, remove: true, sort: {priority:-1}}\n"
- "Either update or remove is required, all other fields have default values.\n"
- "Output is in the \"value\" field\n";
- }
+ if (args.isRemove()) {
+ DeleteRequest request(nsString);
+ const bool isExplain = true;
+ makeDeleteRequest(args, isExplain, &request);
- CmdFindAndModify() : Command("findAndModify", false, "findandmodify") { }
- bool slaveOk() const override { return false; }
- bool isWriteCommandForConfigServer() const override { return true; }
- void addRequiredPrivileges(const std::string& dbname,
- const BSONObj& cmdObj,
- std::vector<Privilege>* out) override {
- find_and_modify::addPrivilegesRequiredForFindAndModify(this, dbname, cmdObj, out);
- }
+ ParsedDelete parsedDelete(txn, &request);
+ Status parsedDeleteStatus = parsedDelete.parseRequest();
+ if (!parsedDeleteStatus.isOK()) {
+ return parsedDeleteStatus;
+ }
+
+ // Explain calls of the findAndModify command are read-only, but we take write
+ // locks so that the timing information is more accurate.
+ AutoGetDb autoDb(txn, dbName, MODE_IX);
+ Lock::CollectionLock collLock(txn->lockState(), nsString.ns(), MODE_IX);
+
+ ensureShardVersionOKOrThrow(client, nsString.ns());
- Status explain(OperationContext* txn,
- const std::string& dbName,
- const BSONObj& cmdObj,
- ExplainCommon::Verbosity verbosity,
- BSONObjBuilder* out) const override {
- const std::string fullNs = parseNsCollectionRequired(dbName, cmdObj);
- Status allowedWriteStatus = userAllowedWriteNS(fullNs);
- if (!allowedWriteStatus.isOK()) {
- return allowedWriteStatus;
+ Collection* collection = nullptr;
+ if (autoDb.getDb()) {
+ collection = autoDb.getDb()->getCollection(nsString.ns());
+ } else {
+ return {ErrorCodes::DatabaseNotFound,
+ str::stream() << "database " << dbName << " does not exist."};
}
- StatusWith<FindAndModifyRequest> parseStatus =
- FindAndModifyRequest::parseFromBSON(NamespaceString(fullNs), cmdObj);
- if (!parseStatus.isOK()) {
- return parseStatus.getStatus();
+ PlanExecutor* rawExec;
+ Status execStatus = getExecutorDelete(txn, collection, &parsedDelete, &rawExec);
+ if (!execStatus.isOK()) {
+ return execStatus;
}
+ const std::unique_ptr<PlanExecutor> exec(rawExec);
+ Explain::explainStages(exec.get(), verbosity, out);
+ } else {
+ UpdateRequest request(nsString);
+ const bool ignoreVersion = false;
+ UpdateLifecycleImpl updateLifecycle(ignoreVersion, nsString);
+ const bool isExplain = true;
+ makeUpdateRequest(args, isExplain, &updateLifecycle, &request);
+
+ ParsedUpdate parsedUpdate(txn, &request);
+ Status parsedUpdateStatus = parsedUpdate.parseRequest();
+ if (!parsedUpdateStatus.isOK()) {
+ return parsedUpdateStatus;
+ }
+
+ OpDebug* opDebug = &CurOp::get(txn)->debug();
- const FindAndModifyRequest& args = parseStatus.getValue();
- const NamespaceString& nsString = args.getNamespaceString();
+ // Explain calls of the findAndModify command are read-only, but we take write
+ // locks so that the timing information is more accurate.
+ AutoGetDb autoDb(txn, dbName, MODE_IX);
+ Lock::CollectionLock collLock(txn->lockState(), nsString.ns(), MODE_IX);
- auto client = txn->getClient();
+ ensureShardVersionOKOrThrow(client, nsString.ns());
+ Collection* collection = nullptr;
+ if (autoDb.getDb()) {
+ collection = autoDb.getDb()->getCollection(nsString.ns());
+ } else {
+ return {ErrorCodes::DatabaseNotFound,
+ str::stream() << "database " << dbName << " does not exist."};
+ }
+
+ PlanExecutor* rawExec;
+ Status execStatus =
+ getExecutorUpdate(txn, collection, &parsedUpdate, opDebug, &rawExec);
+ if (!execStatus.isOK()) {
+ return execStatus;
+ }
+ const std::unique_ptr<PlanExecutor> exec(rawExec);
+ Explain::explainStages(exec.get(), verbosity, out);
+ }
+
+ return Status::OK();
+ }
+
+ bool run(OperationContext* txn,
+ const std::string& dbName,
+ BSONObj& cmdObj,
+ int options,
+ std::string& errmsg,
+ BSONObjBuilder& result) override {
+ // findAndModify command is not replicated directly.
+ invariant(txn->writesAreReplicated());
+ const std::string fullNs = parseNsCollectionRequired(dbName, cmdObj);
+ Status allowedWriteStatus = userAllowedWriteNS(fullNs);
+ if (!allowedWriteStatus.isOK()) {
+ return appendCommandStatus(result, allowedWriteStatus);
+ }
+
+ StatusWith<FindAndModifyRequest> parseStatus =
+ FindAndModifyRequest::parseFromBSON(NamespaceString(fullNs), cmdObj);
+ if (!parseStatus.isOK()) {
+ return appendCommandStatus(result, parseStatus.getStatus());
+ }
+
+ const FindAndModifyRequest& args = parseStatus.getValue();
+ const NamespaceString& nsString = args.getNamespaceString();
+
+ StatusWith<WriteConcernOptions> wcResult = extractWriteConcern(cmdObj);
+ if (!wcResult.isOK()) {
+ return appendCommandStatus(result, wcResult.getStatus());
+ }
+ txn->setWriteConcern(wcResult.getValue());
+ setupSynchronousCommit(txn);
+
+ boost::optional<DisableDocumentValidation> maybeDisableValidation;
+ if (shouldBypassDocumentValidationForCommand(cmdObj))
+ maybeDisableValidation.emplace(txn);
+
+ auto client = txn->getClient();
+
+ // We may encounter a WriteConflictException when creating a collection during an
+ // upsert, even when holding the exclusive lock on the database (due to other load on
+ // the system). The query framework should handle all other WriteConflictExceptions,
+ // but we defensively wrap the operation in the retry loop anyway.
+ //
+ // SERVER-17579 getExecutorUpdate() and getExecutorDelete() can throw a
+ // WriteConflictException when checking whether an index is ready or not.
+ // (on debug builds only)
+ MONGO_WRITE_CONFLICT_RETRY_LOOP_BEGIN {
if (args.isRemove()) {
DeleteRequest request(nsString);
- const bool isExplain = true;
+ const bool isExplain = false;
makeDeleteRequest(args, isExplain, &request);
ParsedDelete parsedDelete(txn, &request);
Status parsedDeleteStatus = parsedDelete.parseRequest();
if (!parsedDeleteStatus.isOK()) {
- return parsedDeleteStatus;
+ return appendCommandStatus(result, parsedDeleteStatus);
}
- // Explain calls of the findAndModify command are read-only, but we take write
- // locks so that the timing information is more accurate.
- AutoGetDb autoDb(txn, dbName, MODE_IX);
+ AutoGetOrCreateDb autoDb(txn, dbName, MODE_IX);
Lock::CollectionLock collLock(txn->lockState(), nsString.ns(), MODE_IX);
+ Collection* collection = autoDb.getDb()->getCollection(nsString.ns());
ensureShardVersionOKOrThrow(client, nsString.ns());
- Collection* collection = nullptr;
- if (autoDb.getDb()) {
- collection = autoDb.getDb()->getCollection(nsString.ns());
- }
- else {
- return {ErrorCodes::DatabaseNotFound,
- str::stream() << "database " << dbName << " does not exist."};
+ Status isPrimary = checkCanAcceptWritesForDatabase(nsString);
+ if (!isPrimary.isOK()) {
+ return appendCommandStatus(result, isPrimary);
}
PlanExecutor* rawExec;
Status execStatus = getExecutorDelete(txn, collection, &parsedDelete, &rawExec);
if (!execStatus.isOK()) {
- return execStatus;
+ return appendCommandStatus(result, execStatus);
}
const std::unique_ptr<PlanExecutor> exec(rawExec);
- Explain::explainStages(exec.get(), verbosity, out);
- }
- else {
+
+ StatusWith<boost::optional<BSONObj>> advanceStatus =
+ advanceExecutor(exec.get(), args.isRemove());
+ if (!advanceStatus.isOK()) {
+ return appendCommandStatus(result, advanceStatus.getStatus());
+ }
+
+ boost::optional<BSONObj> value = advanceStatus.getValue();
+ appendCommandResponse(exec.get(), args.isRemove(), value, result);
+ } else {
UpdateRequest request(nsString);
const bool ignoreVersion = false;
UpdateLifecycleImpl updateLifecycle(ignoreVersion, nsString);
- const bool isExplain = true;
+ const bool isExplain = false;
makeUpdateRequest(args, isExplain, &updateLifecycle, &request);
ParsedUpdate parsedUpdate(txn, &request);
Status parsedUpdateStatus = parsedUpdate.parseRequest();
if (!parsedUpdateStatus.isOK()) {
- return parsedUpdateStatus;
+ return appendCommandStatus(result, parsedUpdateStatus);
}
OpDebug* opDebug = &CurOp::get(txn)->debug();
- // Explain calls of the findAndModify command are read-only, but we take write
- // locks so that the timing information is more accurate.
- AutoGetDb autoDb(txn, dbName, MODE_IX);
+ AutoGetOrCreateDb autoDb(txn, dbName, MODE_IX);
Lock::CollectionLock collLock(txn->lockState(), nsString.ns(), MODE_IX);
+ Collection* collection = autoDb.getDb()->getCollection(nsString.ns());
ensureShardVersionOKOrThrow(client, nsString.ns());
- Collection* collection = nullptr;
- if (autoDb.getDb()) {
- collection = autoDb.getDb()->getCollection(nsString.ns());
- }
- else {
- return {ErrorCodes::DatabaseNotFound,
- str::stream() << "database " << dbName << " does not exist."};
- }
-
- PlanExecutor* rawExec;
- Status execStatus = getExecutorUpdate(txn, collection, &parsedUpdate, opDebug,
- &rawExec);
- if (!execStatus.isOK()) {
- return execStatus;
+ Status isPrimary = checkCanAcceptWritesForDatabase(nsString);
+ if (!isPrimary.isOK()) {
+ return appendCommandStatus(result, isPrimary);
}
- const std::unique_ptr<PlanExecutor> exec(rawExec);
- Explain::explainStages(exec.get(), verbosity, out);
- }
-
- return Status::OK();
- }
-
- bool run(OperationContext* txn,
- const std::string& dbName,
- BSONObj& cmdObj,
- int options,
- std::string& errmsg,
- BSONObjBuilder& result) override {
- // findAndModify command is not replicated directly.
- invariant(txn->writesAreReplicated());
- const std::string fullNs = parseNsCollectionRequired(dbName, cmdObj);
- Status allowedWriteStatus = userAllowedWriteNS(fullNs);
- if (!allowedWriteStatus.isOK()) {
- return appendCommandStatus(result, allowedWriteStatus);
- }
- StatusWith<FindAndModifyRequest> parseStatus =
- FindAndModifyRequest::parseFromBSON(NamespaceString(fullNs), cmdObj);
- if (!parseStatus.isOK()) {
- return appendCommandStatus(result, parseStatus.getStatus());
- }
-
- const FindAndModifyRequest& args = parseStatus.getValue();
- const NamespaceString& nsString = args.getNamespaceString();
-
- StatusWith<WriteConcernOptions> wcResult = extractWriteConcern(cmdObj);
- if (!wcResult.isOK()) {
- return appendCommandStatus(result, wcResult.getStatus());
- }
- txn->setWriteConcern(wcResult.getValue());
- setupSynchronousCommit(txn);
-
- boost::optional<DisableDocumentValidation> maybeDisableValidation;
- if (shouldBypassDocumentValidationForCommand(cmdObj))
- maybeDisableValidation.emplace(txn);
-
- auto client = txn->getClient();
-
- // We may encounter a WriteConflictException when creating a collection during an
- // upsert, even when holding the exclusive lock on the database (due to other load on
- // the system). The query framework should handle all other WriteConflictExceptions,
- // but we defensively wrap the operation in the retry loop anyway.
- //
- // SERVER-17579 getExecutorUpdate() and getExecutorDelete() can throw a
- // WriteConflictException when checking whether an index is ready or not.
- // (on debug builds only)
- MONGO_WRITE_CONFLICT_RETRY_LOOP_BEGIN {
- if (args.isRemove()) {
- DeleteRequest request(nsString);
- const bool isExplain = false;
- makeDeleteRequest(args, isExplain, &request);
-
- ParsedDelete parsedDelete(txn, &request);
- Status parsedDeleteStatus = parsedDelete.parseRequest();
- if (!parsedDeleteStatus.isOK()) {
- return appendCommandStatus(result, parsedDeleteStatus);
- }
-
- AutoGetOrCreateDb autoDb(txn, dbName, MODE_IX);
- Lock::CollectionLock collLock(txn->lockState(), nsString.ns(), MODE_IX);
- Collection* collection = autoDb.getDb()->getCollection(nsString.ns());
-
- ensureShardVersionOKOrThrow(client, nsString.ns());
-
- Status isPrimary = checkCanAcceptWritesForDatabase(nsString);
- if (!isPrimary.isOK()) {
- return appendCommandStatus(result, isPrimary);
- }
-
- PlanExecutor* rawExec;
- Status execStatus = getExecutorDelete(txn, collection, &parsedDelete, &rawExec);
- if (!execStatus.isOK()) {
- return appendCommandStatus(result, execStatus);
- }
- const std::unique_ptr<PlanExecutor> exec(rawExec);
-
- StatusWith<boost::optional<BSONObj>> advanceStatus =
- advanceExecutor(exec.get(), args.isRemove());
- if (!advanceStatus.isOK()) {
- return appendCommandStatus(result, advanceStatus.getStatus());
- }
-
- boost::optional<BSONObj> value = advanceStatus.getValue();
- appendCommandResponse(exec.get(), args.isRemove(), value, result);
- }
- else {
- UpdateRequest request(nsString);
- const bool ignoreVersion = false;
- UpdateLifecycleImpl updateLifecycle(ignoreVersion, nsString);
- const bool isExplain = false;
- makeUpdateRequest(args, isExplain, &updateLifecycle, &request);
-
- ParsedUpdate parsedUpdate(txn, &request);
- Status parsedUpdateStatus = parsedUpdate.parseRequest();
- if (!parsedUpdateStatus.isOK()) {
- return appendCommandStatus(result, parsedUpdateStatus);
- }
-
- OpDebug* opDebug = &CurOp::get(txn)->debug();
-
- AutoGetOrCreateDb autoDb(txn, dbName, MODE_IX);
- Lock::CollectionLock collLock(txn->lockState(), nsString.ns(), MODE_IX);
- Collection* collection = autoDb.getDb()->getCollection(nsString.ns());
-
- ensureShardVersionOKOrThrow(client, nsString.ns());
-
- Status isPrimary = checkCanAcceptWritesForDatabase(nsString);
- if (!isPrimary.isOK()) {
- return appendCommandStatus(result, isPrimary);
+ // Create the collection if it does not exist when performing an upsert
+ // because the update stage does not create its own collection.
+ if (!collection && args.isUpsert()) {
+ // Release the collection lock and reacquire a lock on the database
+ // in exclusive mode in order to create the collection.
+ collLock.relockAsDatabaseExclusive(autoDb.lock());
+ collection = autoDb.getDb()->getCollection(nsString.ns());
+ Status isPrimaryAfterRelock = checkCanAcceptWritesForDatabase(nsString);
+ if (!isPrimaryAfterRelock.isOK()) {
+ return appendCommandStatus(result, isPrimaryAfterRelock);
}
- // Create the collection if it does not exist when performing an upsert
- // because the update stage does not create its own collection.
- if (!collection && args.isUpsert()) {
- // Release the collection lock and reacquire a lock on the database
- // in exclusive mode in order to create the collection.
- collLock.relockAsDatabaseExclusive(autoDb.lock());
- collection = autoDb.getDb()->getCollection(nsString.ns());
- Status isPrimaryAfterRelock = checkCanAcceptWritesForDatabase(nsString);
- if (!isPrimaryAfterRelock.isOK()) {
- return appendCommandStatus(result, isPrimaryAfterRelock);
- }
-
- if (collection) {
- // Someone else beat us to creating the collection, do nothing.
- }
- else {
- WriteUnitOfWork wuow(txn);
- Status createCollStatus = userCreateNS(txn, autoDb.getDb(),
- nsString.ns(), BSONObj());
- if (!createCollStatus.isOK()) {
- return appendCommandStatus(result, createCollStatus);
- }
- wuow.commit();
-
- collection = autoDb.getDb()->getCollection(nsString.ns());
- invariant(collection);
+ if (collection) {
+ // Someone else beat us to creating the collection, do nothing.
+ } else {
+ WriteUnitOfWork wuow(txn);
+ Status createCollStatus =
+ userCreateNS(txn, autoDb.getDb(), nsString.ns(), BSONObj());
+ if (!createCollStatus.isOK()) {
+ return appendCommandStatus(result, createCollStatus);
}
- }
+ wuow.commit();
- PlanExecutor* rawExec;
- Status execStatus = getExecutorUpdate(txn, collection, &parsedUpdate, opDebug,
- &rawExec);
- if (!execStatus.isOK()) {
- return appendCommandStatus(result, execStatus);
- }
- const std::unique_ptr<PlanExecutor> exec(rawExec);
-
- StatusWith<boost::optional<BSONObj>> advanceStatus =
- advanceExecutor(exec.get(), args.isRemove());
- if (!advanceStatus.isOK()) {
- return appendCommandStatus(result, advanceStatus.getStatus());
+ collection = autoDb.getDb()->getCollection(nsString.ns());
+ invariant(collection);
}
+ }
- boost::optional<BSONObj> value = advanceStatus.getValue();
- appendCommandResponse(exec.get(), args.isRemove(), value, result);
+ PlanExecutor* rawExec;
+ Status execStatus =
+ getExecutorUpdate(txn, collection, &parsedUpdate, opDebug, &rawExec);
+ if (!execStatus.isOK()) {
+ return appendCommandStatus(result, execStatus);
}
- } MONGO_WRITE_CONFLICT_RETRY_LOOP_END(txn, "findAndModify", nsString.ns());
+ const std::unique_ptr<PlanExecutor> exec(rawExec);
- WriteConcernResult res;
- auto waitForWCStatus = waitForWriteConcern(
- txn,
- repl::ReplClientInfo::forClient(txn->getClient()).getLastOp(),
- &res
- );
- appendCommandWCStatus(result, waitForWCStatus);
+ StatusWith<boost::optional<BSONObj>> advanceStatus =
+ advanceExecutor(exec.get(), args.isRemove());
+ if (!advanceStatus.isOK()) {
+ return appendCommandStatus(result, advanceStatus.getStatus());
+ }
- return true;
+ boost::optional<BSONObj> value = advanceStatus.getValue();
+ appendCommandResponse(exec.get(), args.isRemove(), value, result);
+ }
}
+ MONGO_WRITE_CONFLICT_RETRY_LOOP_END(txn, "findAndModify", nsString.ns());
+
+ WriteConcernResult res;
+ auto waitForWCStatus = waitForWriteConcern(
+ txn, repl::ReplClientInfo::forClient(txn->getClient()).getLastOp(), &res);
+ appendCommandWCStatus(result, waitForWCStatus);
+
+ return true;
+ }
- } cmdFindAndModify;
+} cmdFindAndModify;
} // namespace mongo