summaryrefslogtreecommitdiff
path: root/db/query.h
blob: e8b9216b7b02e30a6d1eb537123eb139a7df8cc7 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
// query.h

#pragma once

#include "../stdafx.h"
#include "../grid/message.h"
#include "jsobj.h"
#include "storage.h"

/* requests:

   dbDelete
      int reserved=0;
      string collection;
	  int flags=0; // 1=DeleteSingle
      JSObject query;
   dbUpdate:
      int reserved;
      string collection;
	  int flags; // 1=upsert
      JSObject query;
	  JSObject objectToUpdate;
        objectToUpdate may include { $inc: <field> }.
   dbQuery:
      int reserved;
      string collection;
	  int nToSkip;
	  int nToReturn; // how many you want back as the beginning of the cursor data
      JSObject query;
	  [JSObject fieldsToReturn]
   dbGetMore:
      int reserved;
	  string collection; // redundant, might use for security.
      int nToReturn;
      int64 cursorID;
   dbKillCursors=2007:
      int reserved;
      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.
*/

/* db response format

   Query or GetMore:
      int reserved;
      int64 cursorID;
      int startingFrom;
      int nReturned; // 0=infinity
      list of marshalled JSObjects;
*/

#pragma pack(push)
#pragma pack(1)

struct QueryResult : public MsgData {
	long long cursorId;
	int startingFrom;
	int nReturned;
	const char *data() { return (char *) (((int *)&nReturned)+1); }
};

#pragma pack(pop)

QueryResult* getMore(const char *ns, int ntoreturn, long long cursorid);

// caller must free() returned QueryResult.
QueryResult* runQuery(Message&, const char *ns, int ntoskip, int ntoreturn, 
					  JSObj j, auto_ptr< set<string> > fieldFilter,
					  stringstream&);

void updateObjects(const char *ns, JSObj updateobj, JSObj pattern, bool upsert, stringstream& ss);
void deleteObjects(const char *ns, JSObj pattern, bool justOne);

class ClientCursor;
typedef map<long long, ClientCursor*> CCMap;
extern CCMap clientCursors; /* cursorid -> ClientCursor */

/* Cursor -- and its derived classes -- are our internal cursors.

   ClientCursor is a wrapper that represents a cursorid from our client 
   application's perspective.
*/
class Cursor;
class ClientCursor {
	friend class CursInspector;
public:
	ClientCursor() { 
		cursorid=0; pos=0; nextAtThisLocation=0; 
#if defined(_WIN32)
	cout << "clientcursor() " << cursorid << endl;
#endif
	}
	~ClientCursor();
	long long cursorid;
	string ns;
	auto_ptr<JSMatcher> matcher;
	auto_ptr<Cursor> c;
	int pos;
	DiskLoc lastLoc;
	auto_ptr< set<string> > filter;
	Message originalMessage;

	/* report to us that a new clientcursor exists so we can track it. You still
	   do the initial updateLocation() yourself. 
	   */
	static void add(ClientCursor*);

	static bool erase(long long cursorid);

	static ClientCursor* find(long long id) {
		CCMap::iterator it = clientCursors.find(id);
		if( it == clientCursors.end() ) { 
			cout << "ClientCursor::find(): cursor not found in map " << id << endl;
			return 0;
		}
		return it->second;
	}

	/* call when cursor's location changes so that we can update the 
	   cursorsbylocation map.  if you are locked and internally iterating, only 
	   need to call when you are ready to "unlock".
	   */
	void updateLocation();

private:
	void addToByLocation(DiskLoc cl);
	static void cleanupByLocation(DiskLoc loc, long long cursorid);
public:
	ClientCursor *nextAtThisLocation;
};

long long allocCursorId();