bool batchErrorToLastError(const BatchedCommandRequest& request, const BatchedCommandResponse& response, LastError* error) { unique_ptr<WriteErrorDetail> commandError; WriteErrorDetail* lastBatchError = NULL; if (!response.getOk()) { // Command-level error, all writes failed commandError.reset(new WriteErrorDetail); buildErrorFromResponse(response, commandError.get()); lastBatchError = commandError.get(); } else if (response.isErrDetailsSet()) { // The last error in the batch is always reported - this matches expected COE // semantics for insert batches. For updates and deletes, error is only reported // if the error was on the last item. const bool lastOpErrored = response.getErrDetails().back()->getIndex() == static_cast<int>(request.sizeWriteOps() - 1); if (request.getBatchType() == BatchedCommandRequest::BatchType_Insert || lastOpErrored) { lastBatchError = response.getErrDetails().back(); } } else { // We don't care about write concern errors, these happen in legacy mode in GLE. } // Record an error if one exists if (lastBatchError) { string errMsg = lastBatchError->getErrMessage(); error->setLastError(lastBatchError->getErrCode(), errMsg.empty() ? "see code for details" : errMsg.c_str()); return true; } // Record write stats otherwise // NOTE: For multi-write batches, our semantics change a little because we don't have // un-aggregated "n" stats. if (request.getBatchType() == BatchedCommandRequest::BatchType_Update) { BSONObj upsertedId; if (response.isUpsertDetailsSet()) { // Only report the very last item's upserted id if applicable if (response.getUpsertDetails().back()->getIndex() + 1 == static_cast<int>(request.sizeWriteOps())) { upsertedId = response.getUpsertDetails().back()->getUpsertedID(); } } int numUpserted = 0; if (response.isUpsertDetailsSet()) numUpserted = response.sizeUpsertDetails(); int numMatched = response.getN() - numUpserted; dassert(numMatched >= 0); // Wrap upserted id in "upserted" field BSONObj leUpsertedId; if (!upsertedId.isEmpty()) leUpsertedId = upsertedId.firstElement().wrap(kUpsertedFieldName); error->recordUpdate(numMatched > 0, response.getN(), leUpsertedId); } else if (request.getBatchType() == BatchedCommandRequest::BatchType_Delete) { error->recordDelete(response.getN()); } return false; }