282 lines
8.2 KiB
C++
282 lines
8.2 KiB
C++
// dbmessage.h
|
|
|
|
/**
|
|
* Copyright (C) 2008 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 <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#pragma once
|
|
|
|
#include "diskloc.h"
|
|
#include "jsobj.h"
|
|
#include "namespace-inl.h"
|
|
#include "../util/net/message.h"
|
|
#include "../client/constants.h"
|
|
#include "instance.h"
|
|
|
|
namespace mongo {
|
|
|
|
/* db response format
|
|
|
|
Query or GetMore: // see struct QueryResult
|
|
int resultFlags;
|
|
int64 cursorID;
|
|
int startingFrom;
|
|
int nReturned;
|
|
list of marshalled JSObjects;
|
|
*/
|
|
|
|
/* db request message format
|
|
|
|
unsigned opid; // arbitary; will be echoed back
|
|
byte operation;
|
|
int options;
|
|
|
|
then for:
|
|
|
|
dbInsert:
|
|
string collection;
|
|
a series of JSObjects
|
|
dbDelete:
|
|
string collection;
|
|
int flags=0; // 1=DeleteSingle
|
|
JSObject query;
|
|
dbUpdate:
|
|
string collection;
|
|
int flags; // 1=upsert
|
|
JSObject query;
|
|
JSObject objectToUpdate;
|
|
objectToUpdate may include { $inc: <field> } or { $set: ... }, see struct Mod.
|
|
dbQuery:
|
|
string collection;
|
|
int nToSkip;
|
|
int nToReturn; // how many you want back as the beginning of the cursor data (0=no limit)
|
|
// greater than zero is simply a hint on how many objects to send back per "cursor batch".
|
|
// a negative number indicates a hard limit.
|
|
JSObject query;
|
|
[JSObject fieldsToReturn]
|
|
dbGetMore:
|
|
string collection; // redundant, might use for security.
|
|
int nToReturn;
|
|
int64 cursorID;
|
|
dbKillCursors=2007:
|
|
int n;
|
|
int64 cursorIDs[n];
|
|
|
|
Note that on Update, there is only one object, which is different
|
|
from insert where you can pass a list of objects to insert in the db.
|
|
Note that the update field layout is very similar layout to Query.
|
|
*/
|
|
|
|
|
|
#pragma pack(1)
|
|
struct QueryResult : public MsgData {
|
|
long long cursorId;
|
|
int startingFrom;
|
|
int nReturned;
|
|
const char *data() {
|
|
return (char *) (((int *)&nReturned)+1);
|
|
}
|
|
int resultFlags() {
|
|
return dataAsInt();
|
|
}
|
|
int& _resultFlags() {
|
|
return dataAsInt();
|
|
}
|
|
void setResultFlagsToOk() {
|
|
_resultFlags() = ResultFlag_AwaitCapable;
|
|
}
|
|
void initializeResultFlags() {
|
|
_resultFlags() = 0;
|
|
}
|
|
};
|
|
|
|
#pragma pack()
|
|
|
|
/* For the database/server protocol, these objects and functions encapsulate
|
|
the various messages transmitted over the connection.
|
|
|
|
See http://www.mongodb.org/display/DOCS/Mongo+Wire+Protocol
|
|
*/
|
|
class DbMessage {
|
|
public:
|
|
DbMessage(const Message& _m) : m(_m) , mark(0) {
|
|
// for received messages, Message has only one buffer
|
|
theEnd = _m.singleData()->_data + _m.header()->dataLen();
|
|
char *r = _m.singleData()->_data;
|
|
reserved = (int *) r;
|
|
data = r + 4;
|
|
nextjsobj = data;
|
|
}
|
|
|
|
/** the 32 bit field before the ns
|
|
* track all bit usage here as its cross op
|
|
* 0: InsertOption_ContinueOnError
|
|
* 1: fromWriteback
|
|
*/
|
|
int& reservedField() { return *reserved; }
|
|
|
|
const char * getns() const {
|
|
return data;
|
|
}
|
|
void getns(Namespace& ns) const {
|
|
ns = data;
|
|
}
|
|
|
|
const char * afterNS() const {
|
|
return data + strlen( data ) + 1;
|
|
}
|
|
|
|
int getInt( int num ) const {
|
|
const int * foo = (const int*)afterNS();
|
|
return foo[num];
|
|
}
|
|
|
|
int getQueryNToReturn() const {
|
|
return getInt( 1 );
|
|
}
|
|
|
|
/**
|
|
* get an int64 at specified offsetBytes after ns
|
|
*/
|
|
long long getInt64( int offsetBytes ) const {
|
|
const char * x = afterNS();
|
|
x += offsetBytes;
|
|
const long long * ll = (const long long*)x;
|
|
return ll[0];
|
|
}
|
|
|
|
void resetPull() { nextjsobj = data; }
|
|
int pullInt() const { return pullInt(); }
|
|
int& pullInt() {
|
|
if ( nextjsobj == data )
|
|
nextjsobj += strlen(data) + 1; // skip namespace
|
|
int& i = *((int *)nextjsobj);
|
|
nextjsobj += 4;
|
|
return i;
|
|
}
|
|
long long pullInt64() const {
|
|
return pullInt64();
|
|
}
|
|
long long &pullInt64() {
|
|
if ( nextjsobj == data )
|
|
nextjsobj += strlen(data) + 1; // skip namespace
|
|
long long &i = *((long long *)nextjsobj);
|
|
nextjsobj += 8;
|
|
return i;
|
|
}
|
|
|
|
OID* getOID() const {
|
|
return (OID *) (data + strlen(data) + 1); // skip namespace
|
|
}
|
|
|
|
void getQueryStuff(const char *&query, int& ntoreturn) {
|
|
int *i = (int *) (data + strlen(data) + 1);
|
|
ntoreturn = *i;
|
|
i++;
|
|
query = (const char *) i;
|
|
}
|
|
|
|
/* for insert and update msgs */
|
|
bool moreJSObjs() const {
|
|
return nextjsobj != 0;
|
|
}
|
|
BSONObj nextJsObj() {
|
|
if ( nextjsobj == data ) {
|
|
nextjsobj += strlen(data) + 1; // skip namespace
|
|
massert( 13066 , "Message contains no documents", theEnd > nextjsobj );
|
|
}
|
|
massert( 10304 , "Client Error: Remaining data too small for BSON object", theEnd - nextjsobj > 3 );
|
|
BSONObj js(nextjsobj);
|
|
massert( 10305 , "Client Error: Invalid object size", js.objsize() > 3 );
|
|
massert( 10306 , "Client Error: Next object larger than space left in message",
|
|
js.objsize() < ( theEnd - data ) );
|
|
if ( cmdLine.objcheck && !js.valid() ) {
|
|
massert( 10307 , "Client Error: bad object in message", false);
|
|
}
|
|
nextjsobj += js.objsize();
|
|
if ( nextjsobj >= theEnd )
|
|
nextjsobj = 0;
|
|
return js;
|
|
}
|
|
|
|
const Message& msg() const { return m; }
|
|
|
|
void markSet() {
|
|
mark = nextjsobj;
|
|
}
|
|
|
|
void markReset() {
|
|
assert( mark );
|
|
nextjsobj = mark;
|
|
}
|
|
|
|
private:
|
|
const Message& m;
|
|
int* reserved;
|
|
const char *data;
|
|
const char *nextjsobj;
|
|
const char *theEnd;
|
|
|
|
const char * mark;
|
|
|
|
public:
|
|
enum ReservedOptions {
|
|
Reserved_InsertOption_ContinueOnError = 1 << 0 ,
|
|
Reserved_FromWriteback = 1 << 1
|
|
};
|
|
};
|
|
|
|
|
|
/* a request to run a query, received from the database */
|
|
class QueryMessage {
|
|
public:
|
|
const char *ns;
|
|
int ntoskip;
|
|
int ntoreturn;
|
|
int queryOptions;
|
|
BSONObj query;
|
|
BSONObj fields;
|
|
|
|
/* parses the message into the above fields */
|
|
QueryMessage(DbMessage& d) {
|
|
ns = d.getns();
|
|
ntoskip = d.pullInt();
|
|
ntoreturn = d.pullInt();
|
|
query = d.nextJsObj();
|
|
if ( d.moreJSObjs() ) {
|
|
fields = d.nextJsObj();
|
|
}
|
|
queryOptions = d.msg().header()->dataAsInt();
|
|
}
|
|
};
|
|
|
|
void replyToQuery(int queryResultFlags,
|
|
AbstractMessagingPort* p, Message& requestMsg,
|
|
void *data, int size,
|
|
int nReturned, int startingFrom = 0,
|
|
long long cursorId = 0
|
|
);
|
|
|
|
|
|
/* object reply helper. */
|
|
void replyToQuery(int queryResultFlags,
|
|
AbstractMessagingPort* p, Message& requestMsg,
|
|
BSONObj& responseObj);
|
|
|
|
/* helper to do a reply using a DbResponse object */
|
|
void replyToQuery(int queryResultFlags, Message &m, DbResponse &dbresponse, BSONObj obj);
|
|
|
|
|
|
} // namespace mongo
|