diff options
Diffstat (limited to 'src/mongo/db/commands/find_and_modify.cpp')
-rw-r--r-- | src/mongo/db/commands/find_and_modify.cpp | 671 |
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 |