LegacyReply::LegacyReply(const Message* message) { invariant(message->operation() == opReply); QueryResult::View qr = message->singleData().view2ptr(); // should be checked by caller. invariant(qr.msgdata().getNetworkOp() == opReply); uassert(ErrorCodes::BadValue, str::stream() << "Got legacy command reply with a bad cursorId field," << " expected a value of 0 but got " << qr.getCursorId(), qr.getCursorId() == 0); uassert(ErrorCodes::BadValue, str::stream() << "Got legacy command reply with a bad nReturned field," << " expected a value of 1 but got " << qr.getNReturned(), qr.getNReturned() == 1); uassert(ErrorCodes::BadValue, str::stream() << "Got legacy command reply with a bad startingFrom field," << " expected a value of 0 but got " << qr.getStartingFrom(), qr.getStartingFrom() == 0); auto status = Validator<BSONObj>::validateLoad(qr.data(), qr.dataLen()); uassert(ErrorCodes::InvalidBSON, str::stream() << "Got legacy command reply with invalid BSON in the metadata field" << causedBy(status), status.isOK()); _commandReply = BSONObj(qr.data()); _commandReply.shareOwnershipWith(message->sharedBuffer()); if (_commandReply.firstElementFieldName() == "$err"_sd) { // Upconvert legacy errors. BSONObjBuilder bob; bob.appendAs(_commandReply.firstElement(), "errmsg"); bob.append("ok", 0.0); if (auto code = _commandReply["code"]) { bob.append(code); } _commandReply = bob.obj(); } return; }
void DBClientCursor::commandDataReceived() { int op = batch.m.operation(); invariant(op == opReply || op == dbCommandReply); batch.nReturned = 1; batch.pos = 0; auto commandReply = rpc::makeReply(&batch.m); auto commandStatus = getStatusFromCommandResult(commandReply->getCommandReply()); if (ErrorCodes::SendStaleConfig == commandStatus) { throw RecvStaleConfigException("stale config in DBClientCursor::dataReceived()", commandReply->getCommandReply()); } else if (!commandStatus.isOK()) { wasError = true; } if (_client->getReplyMetadataReader()) { uassertStatusOK(_client->getReplyMetadataReader()(commandReply->getMetadata(), _client->getServerAddress())); } // HACK: If we got an OP_COMMANDREPLY, take the reply object // and shove it in to an OP_REPLY message. if (op == dbCommandReply) { // Need to take ownership here as we destroy the underlying message. BSONObj reply = commandReply->getCommandReply().getOwned(); batch.m.reset(); replyToQuery(0, batch.m, reply); } QueryResult::View qr = batch.m.singleData().view2ptr(); batch.data = qr.data(); }
void DBClientCursor::dataReceived(bool& retry, string& host) { // If this is a reply to our initial command request. if (_isCommand && cursorId == 0) { commandDataReceived(); return; } QueryResult::View qr = batch.m.singleData().view2ptr(); resultFlags = qr.getResultFlags(); if (qr.getResultFlags() & ResultFlag_ErrSet) { wasError = true; } if (qr.getResultFlags() & ResultFlag_CursorNotFound) { // cursor id no longer valid at the server. invariant(qr.getCursorId() == 0); if (!(opts & QueryOption_CursorTailable)) { uasserted(13127, str::stream() << "cursor id " << cursorId << " didn't exist on server."); } // 0 indicates no longer valid (dead) cursorId = 0; } if (cursorId == 0 || !(opts & QueryOption_CursorTailable)) { // only set initially: we don't want to kill it on end of data // if it's a tailable cursor cursorId = qr.getCursorId(); } batch.nReturned = qr.getNReturned(); batch.pos = 0; batch.data = qr.data(); batch.remainingBytes = qr.dataLen(); _client->checkResponse(batch.data, batch.nReturned, &retry, &host); // watches for "not master" if (qr.getResultFlags() & ResultFlag_ShardConfigStale) { BSONObj error; verify(peekError(&error)); throw RecvStaleConfigException( (string) "stale config on lazy receive" + causedBy(getErrField(error)), error); } /* this assert would fire the way we currently work: verify( nReturned || cursorId == 0 ); */ }
LegacyReply::LegacyReply(const Message* message) : _message(std::move(message)) { invariant(message->operation() == opReply); QueryResult::View qr = _message->singleData().view2ptr(); // should be checked by caller. invariant(qr.msgdata().getNetworkOp() == opReply); uassert(ErrorCodes::BadValue, str::stream() << "Got legacy command reply with a bad cursorId field," << " expected a value of 0 but got " << qr.getCursorId(), qr.getCursorId() == 0); uassert(ErrorCodes::BadValue, str::stream() << "Got legacy command reply with a bad nReturned field," << " expected a value of 1 but got " << qr.getNReturned(), qr.getNReturned() == 1); uassert(ErrorCodes::BadValue, str::stream() << "Got legacy command reply with a bad startingFrom field," << " expected a value of 0 but got " << qr.getStartingFrom(), qr.getStartingFrom() == 0); std::tie(_commandReply, _metadata) = uassertStatusOK(rpc::upconvertReplyMetadata(BSONObj(qr.data()))); // Copy the bson array of documents from the message into // a contiguous area of memory owned by _docBuffer so // DocumentRange can be used to iterate over documents auto cursorElem = _commandReply[LegacyReplyBuilder::kCursorTag]; if (cursorElem.eoo()) return; BSONObj cursorObj = cursorElem.Obj(); auto firstBatchElem = cursorObj[LegacyReplyBuilder::kFirstBatchTag]; if (firstBatchElem.eoo()) return; for (BSONObjIterator it(firstBatchElem.Obj()); it.more(); it.next()) { invariant((*it).isABSONObj()); BSONObj doc = (*it).Obj(); doc.appendSelfToBufBuilder(_docBuffer); } const char* dataBegin = _docBuffer.buf(); const char* dataEnd = dataBegin + _docBuffer.len(); _outputDocs = DocumentRange(dataBegin, dataEnd); return; }
void NetworkInterfaceASIO::_completedWriteCallback(AsyncOp* op) { // If we were told to send an empty message, toRecv will be empty here. // TODO: handle metadata SERVER-19156 BSONObj commandReply; if (op->toRecv()->empty()) { LOG(3) << "received an empty message"; } else { QueryResult::View qr = op->toRecv()->singleData().view2ptr(); // unavoidable copy commandReply = BSONObj(qr.data()).getOwned(); } _completeOperation( op, RemoteCommandResponse(std::move(commandReply), BSONObj(), now() - op->start())); }
void DBClientCursor::dataReceived( bool& retry, string& host ) { QueryResult::View qr = batch.m->singleData().view2ptr(); resultFlags = qr.getResultFlags(); if ( qr.getResultFlags() & ResultFlag_ErrSet ) { wasError = true; } if ( qr.getResultFlags() & ResultFlag_CursorNotFound ) { // cursor id no longer valid at the server. verify( qr.getCursorId() == 0 ); cursorId = 0; // 0 indicates no longer valid (dead) if ( ! ( opts & QueryOption_CursorTailable ) ) throw UserException( 13127 , "getMore: cursor didn't exist on server, possible restart or timeout?" ); } if ( cursorId == 0 || ! ( opts & QueryOption_CursorTailable ) ) { // only set initially: we don't want to kill it on end of data // if it's a tailable cursor cursorId = qr.getCursorId(); } batch.nReturned = qr.getNReturned(); batch.pos = 0; batch.data = qr.data(); _client->checkResponse( batch.data, batch.nReturned, &retry, &host ); // watches for "not master" if( qr.getResultFlags() & ResultFlag_ShardConfigStale ) { BSONObj error; verify( peekError( &error ) ); throw RecvStaleConfigException( (string)"stale config on lazy receive" + causedBy( getErrField( error ) ), error ); } /* this assert would fire the way we currently work: verify( nReturned || cursorId == 0 ); */ }