/**
* 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