// file dbclientcursor.h
/* Copyright 2009 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.
*/
#pragma once
#include
#include "mongo/base/disallow_copying.h"
#include "mongo/client/dbclientinterface.h"
#include "mongo/db/jsobj.h"
#include "mongo/db/json.h"
#include "mongo/util/net/message.h"
namespace mongo {
class AScopedConnection;
/** for mock purposes only -- do not create variants of DBClientCursor, nor hang code here
@see DBClientMockCursor
*/
class DBClientCursorInterface {
MONGO_DISALLOW_COPYING(DBClientCursorInterface);
public:
virtual ~DBClientCursorInterface() {}
virtual bool more() = 0;
virtual BSONObj next() = 0;
// TODO bring more of the DBClientCursor interface to here
protected:
DBClientCursorInterface() {}
};
/** Queries return a cursor object */
class DBClientCursor : public DBClientCursorInterface {
MONGO_DISALLOW_COPYING(DBClientCursor);
public:
/** If true, safe to call next(). Requests more from server if necessary. */
bool more();
/** If true, there is more in our local buffers to be fetched via next(). Returns
false when a getMore request back to server would be required. You can use this
if you want to exhaust whatever data has been fetched to the client already but
then perhaps stop.
*/
int objsLeftInBatch() const {
_assertIfNull();
return _putBack.size() + batch.nReturned - batch.pos;
}
bool moreInCurrentBatch() {
return objsLeftInBatch() > 0;
}
/** next
@return next object in the result cursor.
on an error at the remote server, you will get back:
{ $err: }
if you do not want to handle that yourself, call nextSafe().
Warning: The returned BSONObj will become invalid after the next batch
is fetched or when this cursor is destroyed.
*/
BSONObj next();
/**
restore an object previously returned by next() to the cursor
*/
void putBack(const BSONObj& o) {
_putBack.push(o.getOwned());
}
/** throws AssertionException if get back { $err : ... } */
BSONObj nextSafe();
/** peek ahead at items buffered for future next() calls.
never requests new data from the server. so peek only effective
with what is already buffered.
WARNING: no support for _putBack yet!
*/
void peek(std::vector&, int atMost);
// Peeks at first element, if exists
BSONObj peekFirst();
/**
* peek ahead and see if an error occurred, and get the error if so.
*/
bool peekError(BSONObj* error = NULL);
/**
iterate the rest of the cursor and return the number if items
*/
int itcount() {
int c = 0;
while (more()) {
next();
c++;
}
return c;
}
/** cursor no longer valid -- use with tailable cursors.
note you should only rely on this once more() returns false;
'dead' may be preset yet some data still queued and locally
available from the dbclientcursor.
*/
bool isDead() const {
return cursorId == 0;
}
bool tailable() const {
return (opts & QueryOption_CursorTailable) != 0;
}
/** see ResultFlagType (constants.h) for flag values
mostly these flags are for internal purposes -
ResultFlag_ErrSet is the possible exception to that
*/
bool hasResultFlag(int flag) {
_assertIfNull();
return (resultFlags & flag) != 0;
}
/// Change batchSize after construction. Can change after requesting first batch.
void setBatchSize(int newBatchSize) {
batchSize = newBatchSize;
}
DBClientCursor(DBClientBase* client,
const std::string& ns,
const BSONObj& query,
int nToReturn,
int nToSkip,
const BSONObj* fieldsToReturn,
int queryOptions,
int bs);
DBClientCursor(DBClientBase* client,
const std::string& ns,
long long cursorId,
int nToReturn,
int options);
virtual ~DBClientCursor();
long long getCursorId() const {
return cursorId;
}
/** by default we "own" the cursor and will send the server a KillCursor
message when ~DBClientCursor() is called. This function overrides that.
*/
void decouple() {
_ownCursor = false;
}
void attach(AScopedConnection* conn);
std::string originalHost() const {
return _originalHost;
}
std::string getns() const {
return ns;
}
Message* getMessage() {
return &batch.m;
}
/**
* actually does the query
*/
bool init();
void initLazy(bool isRetry = false);
bool initLazyFinish(bool& retry);
class Batch {
MONGO_DISALLOW_COPYING(Batch);
friend class DBClientCursor;
Message m;
int nReturned{0};
int pos{0};
const char* data{nullptr};
public:
Batch() = default;
};
/**
* For exhaust. Used in DBClientConnection.
*/
void exhaustReceiveMore();
/**
* Marks this object as dead and sends the KillCursors message to the server.
*
* Any errors that result from this are swallowed since this is typically performed as part of
* cleanup and a failure to kill the cursor should not result in a failure of the operation
* using the cursor.
*
* Killing an already killed or exhausted cursor does nothing, so it is safe to always call this
* if you want to ensure that a cursor is killed.
*/
void kill();
private:
DBClientCursor(DBClientBase* client,
const std::string& ns,
const BSONObj& query,
long long cursorId,
int nToReturn,
int nToSkip,
const BSONObj* fieldsToReturn,
int queryOptions,
int bs);
int nextBatchSize();
Batch batch;
DBClientBase* _client;
std::string _originalHost;
const std::string ns;
const bool _isCommand;
BSONObj query;
int nToReturn;
bool haveLimit;
int nToSkip;
const BSONObj* fieldsToReturn;
int opts;
int batchSize;
std::stack _putBack;
int resultFlags;
long long cursorId;
bool _ownCursor; // see decouple()
std::string _scopedHost;
std::string _lazyHost;
bool wasError;
void dataReceived() {
bool retry;
std::string lazyHost;
dataReceived(retry, lazyHost);
}
void dataReceived(bool& retry, std::string& lazyHost);
/**
* Called by dataReceived when the query was actually a command. Parses the command reply
* according to the RPC protocol used to send it, and then fills in the internal field
* of this cursor with the received data.
*/
void commandDataReceived();
void requestMore();
// Don't call from a virtual function
void _assertIfNull() const {
uassert(13348, "connection died", this);
}
// init pieces
void _assembleInit(Message& toSend);
};
/** iterate over objects in current batch only - will not cause a network call
*/
class DBClientCursorBatchIterator {
public:
DBClientCursorBatchIterator(DBClientCursor& c) : _c(c), _n() {}
bool moreInCurrentBatch() {
return _c.moreInCurrentBatch();
}
BSONObj nextSafe() {
massert(13383, "BatchIterator empty", moreInCurrentBatch());
++_n;
return _c.nextSafe();
}
int n() const {
return _n;
}
private:
DBClientCursor& _c;
int _n;
};
} // namespace mongo