DaZeus  2.0
 All Classes Namespaces Files Functions Variables Enumerations Enumerator Friends Macros
database.cpp
Go to the documentation of this file.
1 
6 #include "database.h"
7 #include "config.h"
8 #include "utils.h"
9 #include <mongo.h>
10 #include <cerrno>
11 #include <cassert>
12 #include <sstream>
13 #include <stdio.h>
14 
15 // #define DEBUG
16 
17 #define M (mongo_sync_connection*)m_
18 #define PROPERTIES std::string(databaseName_ + ".properties").c_str()
19 
23 dazeus::Database::Database( const std::string &hostname, uint16_t port, const std::string &database, const std::string &username, const std::string &password )
24 : m_(0)
25 , lastError_()
26 , hostName_(hostname)
27 , databaseName_(database)
28 , port_(port)
29 , username_(username)
30 , password_(password)
31 {
32 }
33 
34 
39 {
40  if(m_)
41  mongo_sync_disconnect(M);
42 }
43 
47 std::string dazeus::Database::lastError() const
48 {
49  return lastError_;
50 }
51 
53 {
54 #ifdef DEBUG
55  fprintf(stderr, "Initiating connection to Mongo daemon at %s:%d\n",
56  hostName_.c_str(), port_);
57 #endif
58  m_ = (void*)mongo_sync_connect(hostName_.c_str(), port_, TRUE);
59  if(!m_) {
60  lastError_ = strerror(errno);
61 #ifdef DEBUG
62  fprintf(stderr, "Connection error: %s\n", lastError_.c_str());
63 #endif
64  return false;
65  }
66  if(!mongo_sync_conn_set_auto_reconnect(M, TRUE)) {
67  lastError_ = strerror(errno);
68  mongo_sync_disconnect(M);
69 #ifdef DEBUG
70  fprintf(stderr, "Cannot set auto-reconnect: %s\n", lastError_.c_str());
71 #endif
72  return false;
73  }
74 
75 #ifdef DEBUG
76  fprintf(stderr, "Building index on table %s...\n", PROPERTIES);
77 #endif
78  bson *index = bson_build(
79  BSON_TYPE_INT32, "network", 1,
80  BSON_TYPE_INT32, "receiver", 1,
81  BSON_TYPE_INT32, "sender", 1,
82  BSON_TYPE_NONE
83  );
84  bson_finish(index);
85 
86  if(!mongo_sync_cmd_index_create(M, PROPERTIES, index, 0))
87  {
88 #ifdef DEBUG
89  fprintf(stderr, "Index create error: %s\n", strerror(errno));
90 #endif
91  lastError_ = strerror(errno);
92  bson_free(index);
93  return false;
94  }
95 
96  bson_free(index);
97  return true;
98 }
99 
111 std::vector<std::string> dazeus::Database::propertyKeys( const std::string &ns, const std::string &networkScope,
112  const std::string &receiverScope, const std::string &senderScope )
113 {
114  std::stringstream regexStr;
115  regexStr << "^\\Q" << ns << "\\E\\.";
116  std::string regex = regexStr.str();
117 
118  bson *selector = bson_new();
119  bson_append_regex(selector, "variable", regex.c_str(), "");
120  if(networkScope.length() > 0) {
121  bson_append_string(selector, "network", networkScope.c_str(), -1);
122  if(receiverScope.length() > 0) {
123  bson_append_string(selector, "receiver", receiverScope.c_str(), -1);
124  if(senderScope.length() > 0) {
125  bson_append_string(selector, "sender", senderScope.c_str(), -1);
126  } else {
127  bson_append_null(selector, "sender");
128  }
129  } else {
130  bson_append_null(selector, "receiver");
131  bson_append_null(selector, "sender");
132  }
133  } else {
134  bson_append_null(selector, "network");
135  bson_append_null(selector, "receiver");
136  bson_append_null(selector, "sender");
137  }
138  bson_finish(selector);
139 
140  mongo_packet *p = mongo_sync_cmd_query(M, PROPERTIES, 0, 0, 0, selector, NULL /* TODO: variable only? */);
141 
142  bson_free(selector);
143 
144  if(!p) {
145  lastError_ = strerror(errno);
146 #ifdef DEBUG
147  fprintf(stderr, "Database error: %s\n", lastError_.c_str());
148 #endif
149  return std::vector<std::string>();
150  }
151 
152  mongo_sync_cursor *cursor = mongo_sync_cursor_new(M, PROPERTIES, p);
153  if(!cursor) {
154  lastError_ = strerror(errno);
155 #ifdef DEBUG
156  fprintf(stderr, "Database error: %s\n", lastError_.c_str());
157 #endif
158  return std::vector<std::string>();
159  }
160 
161  std::vector<std::string> res;
162  while(mongo_sync_cursor_next(cursor)) {
163  bson *result = mongo_sync_cursor_get_data(cursor);
164  if(!result) {
165  lastError_ = strerror(errno);
166 #ifdef DEBUG
167  fprintf(stderr, "Database error: %s\n", lastError_.c_str());
168 #endif
169  mongo_sync_cursor_free(cursor);
170  return res;
171  }
172 
173  bson_cursor *c = bson_find(result, "variable");
174  const char *value;
175  if(!bson_cursor_get_string(c, &value)) {
176  lastError_ = strerror(errno);
177 #ifdef DEBUG
178  fprintf(stderr, "Database error: %s\n", lastError_.c_str());
179 #endif
180  mongo_sync_cursor_free(cursor);
181  bson_cursor_free(c);
182  return res;
183  }
184 
185  value += ns.length() + 1;
186  res.push_back(value);
187  }
188 
189  return res;
190 }
191 
204 std::string dazeus::Database::property( const std::string &variable,
205  const std::string &networkScope, const std::string &receiverScope,
206  const std::string &senderScope )
207 {
208  // variable, [networkScope, [receiverScope, [senderScope]]]
209  // (empty if not given)
210 
211  // db.c.
212  // find() // for everything
213  // find({network:{$in: ['NETWORKNAME',null]}}) // for network scope
214  // find({network:{$in: ['NETWORKNAME',null]}, receiver:{$in: ['RECEIVER',null]}}) // for receiver scope
215  // find({network:..., receiver:..., sender:{$in: ['SENDER',null]}}) // for sender scope
216  // .sort({'a':-1,'b':-1,'c':-1}).limit(1)
217  // Reverse sort and limit(1) will make sure we only receive the most
218  // specific result. This works, as long as the assumption is true that
219  // no specific fields are set without their less specific fields also
220  // being set (i.e. receiver can't be empty if sender is non-empty).
221 
222  bson *query = bson_build_full(
223  BSON_TYPE_DOCUMENT, "$orderby", TRUE,
224  bson_build(BSON_TYPE_INT32, "network", -1, BSON_TYPE_INT32, "receiver", -1, BSON_TYPE_INT32, "sender", -1, BSON_TYPE_NONE),
225  BSON_TYPE_NONE
226  );
227 
228  /*
229  '$query':{
230  'variable':'foo',
231  'network':{'$in':['bla',null]},
232  'receiver':{'$in':['moo', null]},
233  }
234  */
235 
236  bson *selector = bson_new();
237  bson *network = 0, *receiver = 0, *sender = 0;
238  bson_append_string(selector, "variable", variable.c_str(), -1);
239  if(networkScope.length() > 0) {
240  network = bson_build_full(
241  BSON_TYPE_ARRAY, "$in", TRUE,
242  bson_build(BSON_TYPE_STRING, "1", networkScope.c_str(), -1,
243  BSON_TYPE_NULL, "2", BSON_TYPE_NONE),
244  BSON_TYPE_NONE );
245  bson_finish(network);
246  bson_append_document(selector, "network", network);
247  if(receiverScope.length() > 0) {
248  receiver = bson_build_full(
249  BSON_TYPE_ARRAY, "$in", TRUE,
250  bson_build(BSON_TYPE_STRING, "1", receiverScope.c_str(), -1,
251  BSON_TYPE_NULL, "2", BSON_TYPE_NONE),
252  BSON_TYPE_NONE );
253  bson_finish(receiver);
254  bson_append_document(selector, "receiver", receiver);
255  if(senderScope.length() > 0) {
256  sender = bson_build_full(
257  BSON_TYPE_ARRAY, "$in", TRUE,
258  bson_build(BSON_TYPE_STRING, "1", senderScope.c_str(), -1,
259  BSON_TYPE_NULL, "2", BSON_TYPE_NONE),
260  BSON_TYPE_NONE );
261  bson_finish(sender);
262  bson_append_document(selector, "sender", sender);
263  }
264  }
265  }
266  bson_finish(selector);
267  bson_append_document(query, "$query", selector);
268  bson_finish(query);
269 
270  mongo_packet *p = mongo_sync_cmd_query(M, PROPERTIES, 0, 0, 1, query, NULL /* TODO: value only? */);
271 
272  bson_free(query);
273  bson_free(selector);
274  if(network) bson_free(network);
275  if(receiver) bson_free(receiver);
276  if(sender) bson_free(sender);
277 
278  if(!p) {
279  lastError_ = strerror(errno);
280  return std::string();
281  }
282 
283  mongo_sync_cursor *cursor = mongo_sync_cursor_new(M, PROPERTIES, p);
284  if(!cursor) {
285  lastError_ = strerror(errno);
286  return std::string();
287  }
288 
289  if(!mongo_sync_cursor_next(cursor)) {
290 #ifdef DEBUG
291  fprintf(stderr, "Variable %s not found within given scope.\n", variable.c_str());
292 #endif
293  }
294 
295  bson *result = mongo_sync_cursor_get_data(cursor);
296  if(!result) {
297  lastError_ = strerror(errno);
298  mongo_sync_cursor_free(cursor);
299  return std::string();
300  }
301 
302  bson_cursor *c = bson_find(result, "value");
303  const char *value;
304  if(!bson_cursor_get_string(c, &value)) {
305  lastError_ = strerror(errno);
306 #ifdef DEBUG
307  fprintf(stderr, "Database error: %s\n", lastError_.c_str());
308 #endif
309  mongo_sync_cursor_free(cursor);
310  bson_cursor_free(c);
311  return std::string();
312  }
313 
314  std::string res(value);
315 
316  bson_free(result);
317  bson_cursor_free(c);
318 
319  // only one result
320  assert(!mongo_sync_cursor_next(cursor));
321 
322  mongo_sync_cursor_free(cursor);
323  return res;
324 }
325 
326 void dazeus::Database::setProperty( const std::string &variable,
327  const std::string &value, const std::string &networkScope,
328  const std::string &receiverScope, const std::string &senderScope )
329 {
334  bson *object = bson_build_full(
335  BSON_TYPE_DOCUMENT, "$set", TRUE,
336  bson_build(
337  BSON_TYPE_STRING, "value", value.c_str(), -1,
338  BSON_TYPE_NONE),
339  BSON_TYPE_NONE);
340  bson_finish(object);
341  bson *selector = bson_build(
342  BSON_TYPE_STRING, "variable", variable.c_str(), -1,
343  BSON_TYPE_NONE);
344  if(networkScope.length() > 0) {
345  bson_append_string(selector, "network", networkScope.c_str(), -1);
346 
347  if(receiverScope.length() > 0) {
348  bson_append_string(selector, "receiver", receiverScope.c_str(), -1);
349 
350  if(senderScope.length() > 0) {
351  bson_append_string(selector, "sender", senderScope.c_str(), -1);
352  } else {
353  bson_append_null(selector, "sender");
354  }
355  } else {
356  bson_append_null(selector, "receiver");
357  bson_append_null(selector, "sender");
358  }
359  } else {
360  bson_append_null(selector, "network");
361  bson_append_null(selector, "receiver");
362  bson_append_null(selector, "sender");
363  }
364  bson_finish(selector);
365 
366  // if the value length is zero, run a delete instead
367  if(value.length() == 0) {
368  if(!mongo_sync_cmd_delete(M, PROPERTIES,
369  0, selector))
370  {
371  lastError_ = strerror(errno);
372 #ifdef DEBUG
373  fprintf(stderr, "Error: %s\n", lastError_.c_str());
374 #endif
375  }
376  } else if(!mongo_sync_cmd_update(M, PROPERTIES,
377  MONGO_WIRE_FLAG_UPDATE_UPSERT, selector, object))
378  {
379  lastError_ = strerror(errno);
380 #ifdef DEBUG
381  fprintf(stderr, "Error: %s\n", lastError_.c_str());
382 #endif
383  }
384  bson_free(object);
385  bson_free(selector);
386 }