StatusWith<CursorResponse> CursorResponse::parseFromBSON(const BSONObj& cmdResponse) { Status cmdStatus = getStatusFromCommandResult(cmdResponse); if (!cmdStatus.isOK()) { if (ErrorCodes::isStaleShardingError(cmdStatus.code())) { auto vWanted = ChunkVersion::fromBSON(cmdResponse, "vWanted"); auto vReceived = ChunkVersion::fromBSON(cmdResponse, "vReceived"); if (!vWanted.hasEqualEpoch(vReceived)) { return Status(ErrorCodes::StaleEpoch, cmdStatus.reason()); } } return cmdStatus; } std::string fullns; BSONObj batchObj; CursorId cursorId; BSONElement cursorElt = cmdResponse[kCursorField]; if (cursorElt.type() != BSONType::Object) { return {ErrorCodes::TypeMismatch, str::stream() << "Field '" << kCursorField << "' must be a nested object in: " << cmdResponse}; } BSONObj cursorObj = cursorElt.Obj(); BSONElement idElt = cursorObj[kIdField]; if (idElt.type() != BSONType::NumberLong) { return {ErrorCodes::TypeMismatch, str::stream() << "Field '" << kIdField << "' must be of type long in: " << cmdResponse}; } cursorId = idElt.Long(); BSONElement nsElt = cursorObj[kNsField]; if (nsElt.type() != BSONType::String) { return {ErrorCodes::TypeMismatch, str::stream() << "Field '" << kNsField << "' must be of type string in: " << cmdResponse}; } fullns = nsElt.String(); BSONElement batchElt = cursorObj[kBatchField]; if (batchElt.eoo()) { batchElt = cursorObj[kBatchFieldInitial]; } if (batchElt.type() != BSONType::Array) { return {ErrorCodes::TypeMismatch, str::stream() << "Must have array field '" << kBatchFieldInitial << "' or '" << kBatchField << "' in: " << cmdResponse}; } batchObj = batchElt.Obj(); std::vector<BSONObj> batch; for (BSONElement elt : batchObj) { if (elt.type() != BSONType::Object) { return { ErrorCodes::BadValue, str::stream() << "getMore response batch contains a non-object element: " << elt}; } batch.push_back(elt.Obj().getOwned()); } return {{NamespaceString(fullns), cursorId, batch}}; }
StatusWith<CursorResponse> CursorResponse::parseFromBSON(const BSONObj& cmdResponse) { Status cmdStatus = getStatusFromCommandResult(cmdResponse); if (!cmdStatus.isOK()) { if (ErrorCodes::isStaleShardVersionError(cmdStatus.code())) { auto vWanted = ChunkVersion::fromBSON(cmdResponse, "vWanted"); auto vReceived = ChunkVersion::fromBSON(cmdResponse, "vReceived"); if (!vWanted.hasEqualEpoch(vReceived)) { return Status(ErrorCodes::StaleEpoch, cmdStatus.reason()); } } return cmdStatus; } std::string fullns; BSONObj batchObj; CursorId cursorId; BSONElement cursorElt = cmdResponse[kCursorField]; if (cursorElt.type() != BSONType::Object) { return {ErrorCodes::TypeMismatch, str::stream() << "Field '" << kCursorField << "' must be a nested object in: " << cmdResponse}; } BSONObj cursorObj = cursorElt.Obj(); BSONElement idElt = cursorObj[kIdField]; if (idElt.type() != BSONType::NumberLong) { return { ErrorCodes::TypeMismatch, str::stream() << "Field '" << kIdField << "' must be of type long in: " << cmdResponse}; } cursorId = idElt.Long(); BSONElement nsElt = cursorObj[kNsField]; if (nsElt.type() != BSONType::String) { return {ErrorCodes::TypeMismatch, str::stream() << "Field '" << kNsField << "' must be of type string in: " << cmdResponse}; } fullns = nsElt.String(); BSONElement batchElt = cursorObj[kBatchField]; if (batchElt.eoo()) { batchElt = cursorObj[kBatchFieldInitial]; } if (batchElt.type() != BSONType::Array) { return {ErrorCodes::TypeMismatch, str::stream() << "Must have array field '" << kBatchFieldInitial << "' or '" << kBatchField << "' in: " << cmdResponse}; } batchObj = batchElt.Obj(); std::vector<BSONObj> batch; for (BSONElement elt : batchObj) { if (elt.type() != BSONType::Object) { return {ErrorCodes::BadValue, str::stream() << "getMore response batch contains a non-object element: " << elt}; } batch.push_back(elt.Obj()); } for (auto& doc : batch) { doc.shareOwnershipWith(cmdResponse); } auto latestOplogTimestampElem = cmdResponse[kInternalLatestOplogTimestampField]; if (latestOplogTimestampElem && latestOplogTimestampElem.type() != BSONType::bsonTimestamp) { return { ErrorCodes::BadValue, str::stream() << "invalid _internalLatestOplogTimestamp format; expected timestamp but found: " << latestOplogTimestampElem.type()}; } auto writeConcernError = cmdResponse["writeConcernError"]; if (writeConcernError && writeConcernError.type() != BSONType::Object) { return {ErrorCodes::BadValue, str::stream() << "invalid writeConcernError format; expected object but found: " << writeConcernError.type()}; } return {{NamespaceString(fullns), cursorId, std::move(batch), boost::none, latestOplogTimestampElem ? latestOplogTimestampElem.timestamp() : boost::optional<Timestamp>{}, writeConcernError ? writeConcernError.Obj().getOwned() : boost::optional<BSONObj>{}}}; }