/** * Copyright (C) 2013 10gen Inc. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . * * As a special exception, the copyright holders give permission to link the * code of portions of this program with the OpenSSL library under certain * conditions as described in each individual source file and distribute * linked combinations including the program with the OpenSSL library. You * must comply with the GNU Affero General Public License in all respects * for all of the code used other than as permitted herein. If you modify * file(s) with this exception, you may extend this exception to your * version of the file(s), but you are not obligated to do so. If you do not * wish to do so, delete this exception statement from your version. If you * delete this exception statement from all source files in the program, * then also delete it in the license file. */ #include "mongo/s/write_ops/batched_command_response.h" #include "mongo/db/field_parser.h" #include "mongo/util/mongoutils/str.h" namespace mongo { using mongoutils::str::stream; const BSONField BatchedCommandResponse::ok("ok"); const BSONField BatchedCommandResponse::errCode("code", ErrorCodes::UnknownError); const BSONField BatchedCommandResponse::errMessage("errmsg"); const BSONField BatchedCommandResponse::n("n", 0); const BSONField BatchedCommandResponse::nModified("nModified", 0); const BSONField > BatchedCommandResponse::upsertDetails("upserted"); const BSONField BatchedCommandResponse::lastOp("lastOp"); const BSONField > BatchedCommandResponse::writeErrors("writeErrors"); const BSONField BatchedCommandResponse::writeConcernError("writeConcernError"); BatchedCommandResponse::BatchedCommandResponse() { clear(); } BatchedCommandResponse::~BatchedCommandResponse() { unsetErrDetails(); } bool BatchedCommandResponse::isValid(std::string* errMsg) const { std::string dummy; if (errMsg == NULL) { errMsg = &dummy; } // All the mandatory fields must be present. if (!_isOkSet) { *errMsg = stream() << "missing " << ok.name() << " field"; return false; } return true; } BSONObj BatchedCommandResponse::toBSON() const { BSONObjBuilder builder; if (_isOkSet) builder.append(ok(), _ok); if (_isErrCodeSet) builder.append(errCode(), _errCode); if (_isErrMessageSet) builder.append(errMessage(), _errMessage); if (_isNModifiedSet) builder.appendNumber(nModified(), _nModified); if (_isNSet) builder.appendNumber(n(), _n); if (_upsertDetails.get()) { BSONArrayBuilder upsertedBuilder(builder.subarrayStart(upsertDetails())); for (std::vector::const_iterator it = _upsertDetails->begin(); it != _upsertDetails->end(); ++it) { BSONObj upsertedDetailsDocument = (*it)->toBSON(); upsertedBuilder.append(upsertedDetailsDocument); } upsertedBuilder.done(); } if (_isLastOpSet) builder.append(lastOp(), _lastOp); if (_writeErrorDetails.get()) { BSONArrayBuilder errDetailsBuilder(builder.subarrayStart(writeErrors())); for (std::vector::const_iterator it = _writeErrorDetails->begin(); it != _writeErrorDetails->end(); ++it) { BSONObj errDetailsDocument = (*it)->toBSON(); errDetailsBuilder.append(errDetailsDocument); } errDetailsBuilder.done(); } if (_wcErrDetails.get()) { builder.append(writeConcernError(), _wcErrDetails->toBSON()); } return builder.obj(); } bool BatchedCommandResponse::parseBSON(const BSONObj& source, string* errMsg) { clear(); std::string dummy; if (!errMsg) errMsg = &dummy; FieldParser::FieldState fieldState; fieldState = FieldParser::extractNumber(source, ok, &_ok, errMsg); if (fieldState == FieldParser::FIELD_INVALID) return false; _isOkSet = fieldState == FieldParser::FIELD_SET; fieldState = FieldParser::extract(source, errCode, &_errCode, errMsg); if (fieldState == FieldParser::FIELD_INVALID) return false; _isErrCodeSet = fieldState == FieldParser::FIELD_SET; fieldState = FieldParser::extract(source, errMessage, &_errMessage, errMsg); if (fieldState == FieldParser::FIELD_INVALID) return false; _isErrMessageSet = fieldState == FieldParser::FIELD_SET; // We're using appendNumber on generation so we'll try a smaller type // (int) first and then fall back to the original type (long long). BSONField fieldN(n()); int tempN; fieldState = FieldParser::extract(source, fieldN, &tempN, errMsg); if (fieldState == FieldParser::FIELD_INVALID) { // try falling back to a larger type fieldState = FieldParser::extract(source, n, &_n, errMsg); if (fieldState == FieldParser::FIELD_INVALID) return false; _isNSet = fieldState == FieldParser::FIELD_SET; } else if (fieldState == FieldParser::FIELD_SET) { _isNSet = true; _n = tempN; } // We're using appendNumber on generation so we'll try a smaller type // (int) first and then fall back to the original type (long long). BSONField fieldNUpdated(nModified()); int tempNUpdated; fieldState = FieldParser::extract(source, fieldNUpdated, &tempNUpdated, errMsg); if (fieldState == FieldParser::FIELD_INVALID) { // try falling back to a larger type fieldState = FieldParser::extract(source, nModified, &_nModified, errMsg); if (fieldState == FieldParser::FIELD_INVALID) return false; _isNModifiedSet = fieldState == FieldParser::FIELD_SET; } else if (fieldState == FieldParser::FIELD_SET) { _isNModifiedSet = true; _nModified = tempNUpdated; } std::vector* tempUpsertDetails = NULL; fieldState = FieldParser::extract( source, upsertDetails, &tempUpsertDetails, errMsg ); if ( fieldState == FieldParser::FIELD_INVALID ) return false; if ( fieldState == FieldParser::FIELD_SET ) _upsertDetails.reset( tempUpsertDetails ); fieldState = FieldParser::extract(source, lastOp, &_lastOp, errMsg); if (fieldState == FieldParser::FIELD_INVALID) return false; _isLastOpSet = fieldState == FieldParser::FIELD_SET; std::vector* tempErrDetails = NULL; fieldState = FieldParser::extract(source, writeErrors, &tempErrDetails, errMsg); if (fieldState == FieldParser::FIELD_INVALID) return false; if (fieldState == FieldParser::FIELD_SET) _writeErrorDetails.reset(tempErrDetails); BSONObj wcErrorObj; fieldState = FieldParser::extract(source, writeConcernError, &wcErrorObj, errMsg); if (fieldState == FieldParser::FIELD_INVALID) return false; if (!wcErrorObj.isEmpty()) { auto_ptr tempWCerror; tempWCerror.reset(new WCErrorDetail()); if (!tempWCerror->parseBSON(wcErrorObj, errMsg)) { return false; } _wcErrDetails.reset(tempWCerror.release()); } return true; } void BatchedCommandResponse::clear() { _ok = false; _isOkSet = false; _errCode = 0; _isErrCodeSet = false; _errMessage.clear(); _isErrMessageSet = false; _nModified = 0; _isNModifiedSet = false; _n = 0; _isNSet = false; _singleUpserted = BSONObj(); _isSingleUpsertedSet = false; if ( _upsertDetails.get() ) { for ( std::vector::const_iterator it = _upsertDetails->begin(); it != _upsertDetails->end(); ++it ) { delete *it; }; _upsertDetails.reset(); } _lastOp = OpTime(); _isLastOpSet = false; if (_writeErrorDetails.get()) { for(std::vector::const_iterator it = _writeErrorDetails->begin(); it != _writeErrorDetails->end(); ++it) { delete *it; }; _writeErrorDetails.reset(); } _wcErrDetails.reset(); } void BatchedCommandResponse::cloneTo(BatchedCommandResponse* other) const { other->clear(); other->_ok = _ok; other->_isOkSet = _isOkSet; other->_errCode = _errCode; other->_isErrCodeSet = _isErrCodeSet; other->_errMessage = _errMessage; other->_isErrMessageSet = _isErrMessageSet; other->_nModified = _nModified; other->_isNModifiedSet = _isNModifiedSet; other->_n = _n; other->_isNSet = _isNSet; other->_singleUpserted = _singleUpserted; other->_isSingleUpsertedSet = _isSingleUpsertedSet; other->unsetUpsertDetails(); if (_upsertDetails.get()) { for (std::vector::const_iterator it = _upsertDetails->begin(); it != _upsertDetails->end(); ++it) { BatchedUpsertDetail* upsertDetailsItem = new BatchedUpsertDetail; (*it)->cloneTo(upsertDetailsItem); other->addToUpsertDetails(upsertDetailsItem); } } other->_lastOp = _lastOp; other->_isLastOpSet = _isLastOpSet; other->unsetErrDetails(); if (_writeErrorDetails.get()) { for(std::vector::const_iterator it = _writeErrorDetails->begin(); it != _writeErrorDetails->end(); ++it) { WriteErrorDetail* errDetailsItem = new WriteErrorDetail; (*it)->cloneTo(errDetailsItem); other->addToErrDetails(errDetailsItem); } } if (_wcErrDetails.get()) { other->_wcErrDetails.reset(new WCErrorDetail()); _wcErrDetails->cloneTo(other->_wcErrDetails.get()); } } std::string BatchedCommandResponse::toString() const { return toBSON().toString(); } void BatchedCommandResponse::setOk(int ok) { _ok = ok; _isOkSet = true; } void BatchedCommandResponse::unsetOk() { _isOkSet = false; } bool BatchedCommandResponse::isOkSet() const { return _isOkSet; } int BatchedCommandResponse::getOk() const { dassert(_isOkSet); return _ok; } void BatchedCommandResponse::setErrCode(int errCode) { _errCode = errCode; _isErrCodeSet = true; } void BatchedCommandResponse::unsetErrCode() { _isErrCodeSet = false; } bool BatchedCommandResponse::isErrCodeSet() const { return _isErrCodeSet; } int BatchedCommandResponse::getErrCode() const { if ( _isErrCodeSet ) { return _errCode; } else { return errCode.getDefault(); } } void BatchedCommandResponse::setErrMessage(const StringData& errMessage) { _errMessage = errMessage.toString(); _isErrMessageSet = true; } void BatchedCommandResponse::unsetErrMessage() { _isErrMessageSet = false; } bool BatchedCommandResponse::isErrMessageSet() const { return _isErrMessageSet; } const std::string& BatchedCommandResponse::getErrMessage() const { dassert(_isErrMessageSet); return _errMessage; } void BatchedCommandResponse::setNModified(long long n) { _nModified = n; _isNModifiedSet = true; } void BatchedCommandResponse::unsetNModified() { _isNModifiedSet = false; } bool BatchedCommandResponse::isNModified() const { return _isNModifiedSet; } long long BatchedCommandResponse::getNModified() const { if ( _isNModifiedSet ) { return _nModified; } else { return nModified.getDefault(); } } void BatchedCommandResponse::setN(long long n) { _n = n; _isNSet = true; } void BatchedCommandResponse::unsetN() { _isNSet = false; } bool BatchedCommandResponse::isNSet() const { return _isNSet; } long long BatchedCommandResponse::getN() const { if ( _isNSet ) { return _n; } else { return n.getDefault(); } } void BatchedCommandResponse::setUpsertDetails( const std::vector& upsertDetails) { unsetUpsertDetails(); for (std::vector::const_iterator it = upsertDetails.begin(); it != upsertDetails.end(); ++it) { auto_ptr tempBatchedUpsertDetail(new BatchedUpsertDetail); (*it)->cloneTo(tempBatchedUpsertDetail.get()); addToUpsertDetails(tempBatchedUpsertDetail.release()); } } void BatchedCommandResponse::addToUpsertDetails(BatchedUpsertDetail* upsertDetails) { if (_upsertDetails.get() == NULL) { _upsertDetails.reset(new std::vector); } _upsertDetails->push_back(upsertDetails); } void BatchedCommandResponse::unsetUpsertDetails() { if (_upsertDetails.get() != NULL) { for (std::vector::iterator it = _upsertDetails->begin(); it != _upsertDetails->end(); ++it) { delete *it; } _upsertDetails.reset(); } } bool BatchedCommandResponse::isUpsertDetailsSet() const { return _upsertDetails.get() != NULL; } size_t BatchedCommandResponse::sizeUpsertDetails() const { dassert(_upsertDetails.get()); return _upsertDetails->size(); } const std::vector& BatchedCommandResponse::getUpsertDetails() const { dassert(_upsertDetails.get()); return *_upsertDetails; } const BatchedUpsertDetail* BatchedCommandResponse::getUpsertDetailsAt(size_t pos) const { dassert(_upsertDetails.get()); dassert(_upsertDetails->size() > pos); return _upsertDetails->at(pos); } void BatchedCommandResponse::setLastOp(OpTime lastOp) { _lastOp = lastOp; _isLastOpSet = true; } void BatchedCommandResponse::unsetLastOp() { _isLastOpSet = false; } bool BatchedCommandResponse::isLastOpSet() const { return _isLastOpSet; } OpTime BatchedCommandResponse::getLastOp() const { dassert(_isLastOpSet); return _lastOp; } void BatchedCommandResponse::setErrDetails(const std::vector& errDetails) { unsetErrDetails(); for (std::vector::const_iterator it = errDetails.begin(); it != errDetails.end(); ++it) { auto_ptr tempBatchErrorDetail(new WriteErrorDetail); (*it)->cloneTo(tempBatchErrorDetail.get()); addToErrDetails(tempBatchErrorDetail.release()); } } void BatchedCommandResponse::addToErrDetails(WriteErrorDetail* errDetails) { if (_writeErrorDetails.get() == NULL) { _writeErrorDetails.reset(new std::vector); } _writeErrorDetails->push_back(errDetails); } void BatchedCommandResponse::unsetErrDetails() { if (_writeErrorDetails.get() != NULL) { for(std::vector::iterator it = _writeErrorDetails->begin(); it != _writeErrorDetails->end(); ++it) { delete *it; } _writeErrorDetails.reset(); } } bool BatchedCommandResponse::isErrDetailsSet() const { return _writeErrorDetails.get() != NULL; } size_t BatchedCommandResponse::sizeErrDetails() const { dassert(_writeErrorDetails.get()); return _writeErrorDetails->size(); } const std::vector& BatchedCommandResponse::getErrDetails() const { dassert(_writeErrorDetails.get()); return *_writeErrorDetails; } const WriteErrorDetail* BatchedCommandResponse::getErrDetailsAt(size_t pos) const { dassert(_writeErrorDetails.get()); dassert(_writeErrorDetails->size() > pos); return _writeErrorDetails->at(pos); } void BatchedCommandResponse::setWriteConcernError(const WCErrorDetail& error) { _wcErrDetails.reset(new WCErrorDetail()); error.cloneTo(_wcErrDetails.get()); } void BatchedCommandResponse::unsetWriteConcernError() { _wcErrDetails.reset(); } bool BatchedCommandResponse::isWriteConcernErrorSet() const { return _wcErrDetails.get(); } const WCErrorDetail* BatchedCommandResponse::getWriteConcernError() const { return _wcErrDetails.get(); } } // namespace mongo