myriad changes
[oweals/gnunet.git] / src / dht / plugin_dhtlog_mysql.c
1 /*
2      This file is part of GNUnet.
3      (C) 2006 - 2009 Christian Grothoff (and other contributing authors)
4
5      GNUnet is free software; you can redistribute it and/or modify
6      it under the terms of the GNU General Public License as published
7      by the Free Software Foundation; either version 2, or (at your
8      option) any later version.
9
10      GNUnet is distributed in the hope that it will be useful, but
11      WITHOUT ANY WARRANTY; without even the implied warranty of
12      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13      General Public License for more details.
14
15      You should have received a copy of the GNU General Public License
16      along with GNUnet; see the file COPYING.  If not, write to the
17      Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18      Boston, MA 02111-1307, USA.
19 */
20
21 /**
22  * @file src/dht/plugin_dhtlog_mysql.c
23  * @brief MySQL logging plugin to record DHT operations to MySQL server
24  * @author Nathan Evans
25  *
26  * Database: MySQL
27  */
28
29 #include "platform.h"
30 #include "gnunet_util_lib.h"
31 #include "dhtlog.h"
32 #include <mysql/mysql.h>
33
34
35 #define DEBUG_DHTLOG GNUNET_NO
36
37 /**
38  * Maximum number of supported parameters for a prepared
39  * statement.  Increase if needed.
40  */
41 #define MAX_PARAM 32
42
43 /**
44  * A generic statement handle to use
45  * for prepared statements.  This way,
46  * once the statement is initialized
47  * we don't redo work.
48  */
49 struct StatementHandle
50 {
51   /**
52    * Internal statement
53    */
54   MYSQL_STMT *statement;
55
56   /**
57    * Textual query
58    */
59   char *query;
60
61   /**
62    * Whether or not the handle is valid
63    */
64   int valid;
65 };
66
67 /**
68  * Type of a callback that will be called for each
69  * data set returned from MySQL.
70  *
71  * @param cls user-defined argument
72  * @param num_values number of elements in values
73  * @param values values returned by MySQL
74  * @return GNUNET_OK to continue iterating, GNUNET_SYSERR to abort
75  */
76 typedef int (*GNUNET_MysqlDataProcessor) (void *cls,
77                                           unsigned int num_values,
78                                           MYSQL_BIND * values);
79
80 static unsigned long max_varchar_len;
81
82 /**
83  * The configuration the DHT service is running with
84  */
85 static const struct GNUNET_CONFIGURATION_Handle *cfg;
86
87 static unsigned long long current_trial = 0;    /* I like to assign 0, just to remember */
88
89 /**
90  * Connection to the MySQL Server.
91  */
92 static MYSQL *conn;
93
94 #define INSERT_QUERIES_STMT "INSERT INTO queries (trialuid, querytype, hops, dhtkeyuid, dhtqueryid, succeeded, nodeuid) "\
95                           "VALUES (?, ?, ?, ?, ?, ?, ?)"
96 static struct StatementHandle *insert_query;
97
98 #define INSERT_ROUTES_STMT "INSERT INTO routes (trialuid, querytype, hops, dhtkeyuid, dhtqueryid, succeeded, nodeuid, from_node, to_node) "\
99                           "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)"
100 static struct StatementHandle *insert_route;
101
102 #define INSERT_NODES_STMT "INSERT INTO nodes (trialuid, nodeid, nodebits) "\
103                           "VALUES (?, ?, ?)"
104 static struct StatementHandle *insert_node;
105
106 #define INSERT_TRIALS_STMT "INSERT INTO trials"\
107                             "(starttime, numnodes, topology,"\
108                             "topology_percentage, topology_probability,"\
109                             "blacklist_topology, connect_topology, connect_topology_option,"\
110                             "connect_topology_option_modifier, puts, gets, "\
111                             "concurrent, settle_time, num_rounds, malicious_getters,"\
112                             "malicious_putters, malicious_droppers, malicious_get_frequency,"\
113                             "malicious_put_frequency, stop_closest, stop_found, strict_kademlia, "\
114                             "gets_succeeded, message) "\
115                             "VALUES (NOW(), ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"
116
117 static struct StatementHandle *insert_trial;
118
119 #define INSERT_STAT_STMT "INSERT INTO node_statistics"\
120                             "(trialuid, nodeuid, route_requests,"\
121                             "route_forwards, result_requests,"\
122                             "client_results, result_forwards, gets,"\
123                             "puts, data_inserts, find_peer_requests, "\
124                             "find_peers_started, gets_started, puts_started, find_peer_responses_received,"\
125                             "get_responses_received, find_peer_responses_sent, get_responses_sent) "\
126                             "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"
127
128 static struct StatementHandle *insert_stat;
129
130 #define INSERT_DHTKEY_STMT "INSERT INTO dhtkeys (dhtkey, trialuid, keybits) "\
131                           "VALUES (?, ?, ?)"
132 static struct StatementHandle *insert_dhtkey;
133
134 #define UPDATE_TRIALS_STMT "UPDATE trials set endtime=NOW(), gets_succeeded = ? where trialuid = ?"
135 static struct StatementHandle *update_trial;
136
137 #define UPDATE_CONNECTIONS_STMT "UPDATE trials set totalConnections = ? where trialuid = ?"
138 static struct StatementHandle *update_connection;
139
140 #define GET_TRIAL_STMT "SELECT MAX( trialuid ) FROM trials"
141 static struct StatementHandle *get_trial;
142
143 #define GET_TOPOLOGY_STMT "SELECT MAX( topology_uid ) FROM topology"
144 static struct StatementHandle *get_topology;
145
146 #define GET_DHTKEYUID_STMT "SELECT dhtkeyuid FROM dhtkeys where dhtkey = ? and trialuid = ?"
147 static struct StatementHandle *get_dhtkeyuid;
148
149 #define GET_NODEUID_STMT "SELECT nodeuid FROM nodes where trialuid = ? and nodeid = ?"
150 static struct StatementHandle *get_nodeuid;
151
152 #define INSERT_TOPOLOGY_STMT "INSERT INTO topology (trialuid, date, connections) "\
153                              "VALUES (?, NOW(), ?)"
154 static struct StatementHandle *insert_topology;
155
156 #define EXTEND_TOPOLOGY_STMT "INSERT INTO extended_topology (topology_uid, uid_first, uid_second) "\
157                              "VALUES (?, ?, ?)"
158 static struct StatementHandle *extend_topology;
159
160 #define UPDATE_TOPOLOGY_STMT "update topology set connections = ?  where topology_uid = ?"
161 static struct StatementHandle *update_topology;
162
163 /**
164  * Run a query (not a select statement)
165  *
166  * @return GNUNET_OK if executed, GNUNET_SYSERR if an error occurred
167  */
168 int
169 run_statement (const char *statement)
170 {
171   mysql_query (conn, statement);
172   if (mysql_error (conn)[0])
173     {
174       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
175                  "mysql_query");
176       return GNUNET_SYSERR;
177     }
178   return GNUNET_OK;
179 }
180
181 /*
182  * Creates tables if they don't already exist for dht logging
183  */
184 static int
185 itable ()
186 {
187 #define MRUNS(a) (GNUNET_OK != run_statement (a) )
188
189   if (MRUNS ("CREATE TABLE IF NOT EXISTS `dhtkeys` ("
190              "dhtkeyuid int(10) unsigned NOT NULL auto_increment COMMENT 'Unique Key given to each query',"
191              "`dhtkey` varchar(255) NOT NULL COMMENT 'The ASCII value of the key being searched for',"
192              "trialuid int(10) unsigned NOT NULL,"
193              "keybits blob NOT NULL,"
194              "UNIQUE KEY `dhtkeyuid` (`dhtkeyuid`)"
195              ") ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=1"))
196     return GNUNET_SYSERR;
197
198   if (MRUNS ("CREATE TABLE IF NOT EXISTS `nodes` ("
199              "`nodeuid` int(10) unsigned NOT NULL auto_increment,"
200              "`trialuid` int(10) unsigned NOT NULL,"
201              "`nodeid` varchar(255) NOT NULL,"
202              "`nodebits` blob NOT NULL,"
203              "PRIMARY KEY  (`nodeuid`)"
204              ") ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=1"))
205     return GNUNET_SYSERR;
206
207   if (MRUNS ("CREATE TABLE IF NOT EXISTS `queries` ("
208              "`trialuid` int(10) unsigned NOT NULL,"
209              "`queryuid` int(10) unsigned NOT NULL auto_increment,"
210              "`dhtqueryid` bigint(20) NOT NULL,"
211              "`querytype` enum('1','2','3','4','5') NOT NULL,"
212              "`hops` int(10) unsigned NOT NULL,"
213              "`succeeded` tinyint NOT NULL,"
214              "`nodeuid` int(10) unsigned NOT NULL,"
215              "`time` timestamp NOT NULL default CURRENT_TIMESTAMP,"
216              "`dhtkeyuid` int(10) unsigned NOT NULL,"
217              "PRIMARY KEY  (`queryuid`)"
218              ") ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=1"))
219     return GNUNET_SYSERR;
220
221   if (MRUNS ("CREATE TABLE IF NOT EXISTS `routes` ("
222              "`trialuid` int(10) unsigned NOT NULL,"
223              "`queryuid` int(10) unsigned NOT NULL auto_increment,"
224              "`dhtqueryid` bigint(20) NOT NULL,"
225              "`querytype` enum('1','2','3','4','5') NOT NULL,"
226              "`hops` int(10) unsigned NOT NULL,"
227              "`succeeded` tinyint NOT NULL,"
228              "`nodeuid` int(10) unsigned NOT NULL,"
229              "`time` timestamp NOT NULL default CURRENT_TIMESTAMP,"
230              "`dhtkeyuid` int(10) unsigned NOT NULL,"
231              "`from_node` int(10) unsigned NOT NULL,"
232              "`to_node` int(10) unsigned NOT NULL,"
233              "PRIMARY KEY  (`queryuid`)"
234              ") ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=1"))
235     return GNUNET_SYSERR;
236
237   if (MRUNS ("CREATE TABLE IF NOT EXISTS `trials` ("
238              "`trialuid` int(10) unsigned NOT NULL auto_increment,"
239              "`numnodes` int(10) unsigned NOT NULL,"
240              "`topology` int(10) NOT NULL,"
241              "`starttime` datetime NOT NULL,"
242              "`endtime` datetime NOT NULL,"
243              "`puts` int(10) unsigned NOT NULL,"
244              "`gets` int(10) unsigned NOT NULL,"
245              "`concurrent` int(10) unsigned NOT NULL,"
246              "`settle_time` int(10) unsigned NOT NULL,"
247              "`totalConnections` int(10) unsigned NOT NULL,"
248              "`message` text NOT NULL,"
249              "`num_rounds` int(10) unsigned NOT NULL,"
250              "`malicious_getters` int(10) unsigned NOT NULL,"
251              "`malicious_putters` int(10) unsigned NOT NULL,"
252              "`malicious_droppers` int(10) unsigned NOT NULL,"
253              "`totalMessagesDropped` int(10) unsigned NOT NULL,"
254              "`totalBytesDropped` int(10) unsigned NOT NULL,"
255              "`topology_modifier` double NOT NULL,"
256              "`logNMultiplier` double NOT NULL,"
257              "`maxnetbps` bigint(20) unsigned NOT NULL,"
258              "`unknownPeers` int(10) unsigned NOT NULL,"
259              "PRIMARY KEY  (`trialuid`),"
260              "UNIQUE KEY `trialuid` (`trialuid`)"
261              ") ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=1"))
262     return GNUNET_SYSERR;
263
264   if (MRUNS ("CREATE TABLE IF NOT EXISTS `topology` ("
265               "`topology_uid` int(10) unsigned NOT NULL AUTO_INCREMENT,"
266               "`trialuid` int(10) unsigned NOT NULL,"
267               "`date` datetime NOT NULL,"
268               "`connections` int(10) unsigned NOT NULL,"
269               "PRIMARY KEY (`topology_uid`)) ENGINE=MyISAM  DEFAULT CHARSET=utf8 AUTO_INCREMENT=1"))
270     return GNUNET_SYSERR;
271
272   if (MRUNS ("CREATE TABLE IF NOT EXISTS `extended_topology` ("
273              "`extended_uid` int(10) unsigned NOT NULL AUTO_INCREMENT,"
274              "`topology_uid` int(10) unsigned NOT NULL,"
275              "`uid_first` int(10) unsigned NOT NULL,"
276              "`uid_second` int(10) unsigned NOT NULL,"
277              "PRIMARY KEY (`extended_uid`)"
278              ") ENGINE=MyISAM  DEFAULT CHARSET=utf8 AUTO_INCREMENT=1"))
279     return GNUNET_SYSERR;
280
281   if (MRUNS ("CREATE TABLE IF NOT EXISTS `node_statistics` ("
282               "`stat_uid` int(10) unsigned NOT NULL AUTO_INCREMENT,"
283               "`trialuid` int(10) unsigned NOT NULL,"
284               "`nodeuid` int(10) unsigned NOT NULL,"
285               "`route_requests` int(10) unsigned NOT NULL,"
286               "`route_forwards` int(10) unsigned NOT NULL,"
287               "`result_requests` int(10) unsigned NOT NULL,"
288               "`client_results` int(10) unsigned NOT NULL,"
289               "`result_forwards` int(10) unsigned NOT NULL,"
290               "`gets` int(10) unsigned NOT NULL,"
291               "`puts` int(10) unsigned NOT NULL,"
292               "`data_inserts` int(10) unsigned NOT NULL,"
293               "`find_peer_requests` int(10) unsigned NOT NULL,"
294               "`find_peers_started` int(10) unsigned NOT NULL,"
295               "`gets_started` int(10) unsigned NOT NULL,"
296               "`puts_started` int(10) unsigned NOT NULL,"
297               "`find_peer_responses_received` int(10) unsigned NOT NULL,"
298               "`get_responses_received` int(10) unsigned NOT NULL,"
299               "`find_peer_responses_sent` int(10) unsigned NOT NULL,"
300               "`get_responses_sent` int(10) unsigned NOT NULL,"
301              "PRIMARY KEY (`stat_uid`)"
302             ") ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ;"))
303     return GNUNET_SYSERR;
304
305   if (MRUNS ("SET AUTOCOMMIT = 1"))
306     return GNUNET_SYSERR;
307
308   return GNUNET_OK;
309 #undef MRUNS
310 }
311
312 /**
313  * Create a prepared statement.
314  *
315  * @return NULL on error
316  */
317 struct StatementHandle *
318 prepared_statement_create (const char *statement)
319 {
320   struct StatementHandle *ret;
321
322   ret = GNUNET_malloc (sizeof (struct StatementHandle));
323   ret->query = GNUNET_strdup (statement);
324   return ret;
325 }
326
327 /**
328  * Close a prepared statement.
329  *
330  * @return NULL on error
331  */
332 void
333 prepared_statement_close (struct StatementHandle *s)
334 {
335   if (s == NULL)
336     {
337       return;
338     }
339
340   GNUNET_free_non_null(s->query);
341
342   if (s->valid == GNUNET_YES)
343     mysql_stmt_close(s->statement);
344   GNUNET_free(s);
345 }
346
347 /*
348  * Initialize the prepared statements for use with dht test logging
349  */
350 static int
351 iopen (struct GNUNET_DHTLOG_Plugin *plugin)
352 {
353   int ret;
354   my_bool reconnect;
355   unsigned int timeout;
356   char *user;
357   char *password;
358   char *server;
359   char *database;
360   unsigned long long port;
361
362   conn = mysql_init (NULL);
363   if (conn == NULL)
364     return GNUNET_SYSERR;
365
366   if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (plugin->cfg,
367                                                          "MYSQL", "DATABASE",
368                                                          &database))
369     {
370       database = GNUNET_strdup("gnunet");
371     }
372
373   if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (plugin->cfg,
374                                                           "MYSQL", "USER", &user))
375     {
376       user = GNUNET_strdup("dht");
377     }
378
379   if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (plugin->cfg,
380                                                           "MYSQL", "PASSWORD", &password))
381     {
382       password = GNUNET_strdup("dhttest**");
383     }
384
385   if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (plugin->cfg,
386                                                           "MYSQL", "SERVER", &server))
387     {
388       server = GNUNET_strdup("localhost");
389     }
390
391   if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_number (plugin->cfg,
392                                                           "MYSQL", "MYSQL_PORT", &port))
393     {
394       port = 0;
395     }
396
397   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Connecting to mysql with: user %s, pass %s, server %s, database %s, port %d\n",
398               user, password, server, database, port);
399
400   reconnect = 0;
401   timeout = 60; /* in seconds */
402   mysql_options (conn, MYSQL_OPT_RECONNECT, &reconnect);
403   mysql_options (conn,
404                  MYSQL_OPT_CONNECT_TIMEOUT, (const void *) &timeout);
405   mysql_options(conn, MYSQL_SET_CHARSET_NAME, "UTF8");
406   mysql_options (conn, MYSQL_OPT_READ_TIMEOUT, (const void *) &timeout);
407   mysql_options (conn, MYSQL_OPT_WRITE_TIMEOUT, (const void *) &timeout);
408   mysql_real_connect (conn, server, user, password,
409                       database, (unsigned int) port, NULL, CLIENT_IGNORE_SIGPIPE);
410
411   GNUNET_free_non_null(server);
412   GNUNET_free_non_null(password);
413   GNUNET_free_non_null(user);
414   GNUNET_free_non_null(database);
415
416   if (mysql_error (conn)[0])
417     {
418       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
419                  "mysql_real_connect");
420       return GNUNET_SYSERR;
421     }
422
423 #if OLD
424   db = GNUNET_MYSQL_database_open (coreAPI->ectx, coreAPI->cfg);
425   if (db == NULL)
426     return GNUNET_SYSERR;
427 #endif
428
429   ret = itable ();
430
431 #define PINIT(a,b) (NULL == (a = prepared_statement_create(b)))
432   if (PINIT (insert_query, INSERT_QUERIES_STMT) ||
433       PINIT (insert_route, INSERT_ROUTES_STMT) ||
434       PINIT (insert_trial, INSERT_TRIALS_STMT) ||
435       PINIT (insert_stat, INSERT_STAT_STMT) ||
436       PINIT (insert_node, INSERT_NODES_STMT) ||
437       PINIT (insert_dhtkey, INSERT_DHTKEY_STMT) ||
438       PINIT (update_trial, UPDATE_TRIALS_STMT) ||
439       PINIT (get_dhtkeyuid, GET_DHTKEYUID_STMT) ||
440       PINIT (get_nodeuid, GET_NODEUID_STMT) ||
441       PINIT (update_connection, UPDATE_CONNECTIONS_STMT) ||
442       PINIT (get_trial, GET_TRIAL_STMT) ||
443       PINIT (get_topology, GET_TOPOLOGY_STMT) ||
444       PINIT (insert_topology, INSERT_TOPOLOGY_STMT)||
445       PINIT (update_topology, UPDATE_TOPOLOGY_STMT)||
446       PINIT (extend_topology, EXTEND_TOPOLOGY_STMT))
447     {
448       return GNUNET_SYSERR;
449     }
450 #undef PINIT
451
452   return ret;
453 }
454
455 static int
456 return_ok (void *cls, unsigned int num_values, MYSQL_BIND * values)
457 {
458   return GNUNET_OK;
459 }
460
461 /**
462  * Prepare a statement for running.
463  *
464  * @return GNUNET_OK on success
465  */
466 static int
467 prepare_statement (struct StatementHandle *ret)
468 {
469   if (GNUNET_YES == ret->valid)
470     return GNUNET_OK;
471
472   ret->statement = mysql_stmt_init (conn);
473   if (ret->statement == NULL)
474     return GNUNET_SYSERR;
475
476   if (mysql_stmt_prepare (ret->statement, ret->query, strlen (ret->query)))
477     {
478       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
479                  "mysql_stmt_prepare `%s', %s", ret->query, mysql_error(conn));
480       mysql_stmt_close (ret->statement);
481       ret->statement = NULL;
482       return GNUNET_SYSERR;
483     }
484   ret->valid = GNUNET_YES;
485   return GNUNET_OK;
486 }
487
488 /**
489  * Bind the parameters for the given MySQL statement
490  * and run it.
491  *
492  * @param s statement to bind and run
493  * @param ap arguments for the binding
494  * @return GNUNET_SYSERR on error, GNUNET_OK on success
495  */
496 static int
497 init_params (struct StatementHandle *s, va_list ap)
498 {
499   MYSQL_BIND qbind[MAX_PARAM];
500   unsigned int pc;
501   unsigned int off;
502   enum enum_field_types ft;
503
504   pc = mysql_stmt_param_count (s->statement);
505   if (pc > MAX_PARAM)
506     {
507       /* increase internal constant! */
508       GNUNET_break (0);
509       return GNUNET_SYSERR;
510     }
511   memset (qbind, 0, sizeof (qbind));
512   off = 0;
513   ft = 0;
514   while ((pc > 0) && (-1 != (ft = va_arg (ap, enum enum_field_types))))
515     {
516       qbind[off].buffer_type = ft;
517       switch (ft)
518         {
519         case MYSQL_TYPE_FLOAT:
520           qbind[off].buffer = va_arg (ap, float *);
521           break;
522         case MYSQL_TYPE_LONGLONG:
523           qbind[off].buffer = va_arg (ap, unsigned long long *);
524           qbind[off].is_unsigned = va_arg (ap, int);
525           break;
526         case MYSQL_TYPE_LONG:
527           qbind[off].buffer = va_arg (ap, unsigned int *);
528           qbind[off].is_unsigned = va_arg (ap, int);
529           break;
530         case MYSQL_TYPE_VAR_STRING:
531         case MYSQL_TYPE_STRING:
532         case MYSQL_TYPE_BLOB:
533           qbind[off].buffer = va_arg (ap, void *);
534           qbind[off].buffer_length = va_arg (ap, unsigned long);
535           qbind[off].length = va_arg (ap, unsigned long *);
536           break;
537         default:
538           /* unsupported type */
539           GNUNET_break (0);
540           return GNUNET_SYSERR;
541         }
542       pc--;
543       off++;
544     }
545   if (!((pc == 0) && (ft != -1) && (va_arg (ap, int) == -1)))
546     {
547       GNUNET_break (0);
548       return GNUNET_SYSERR;
549     }
550   if (mysql_stmt_bind_param (s->statement, qbind))
551     {
552       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
553                   _("`%s' failed at %s:%d with error: %s\n"),
554                   "mysql_stmt_bind_param",
555                    __FILE__, __LINE__, mysql_stmt_error (s->statement));
556       return GNUNET_SYSERR;
557     }
558
559   if (mysql_stmt_execute (s->statement))
560     {
561     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
562                _("`%s' failed at %s:%d with error: %s\n"),
563                "mysql_stmt_execute",
564                __FILE__, __LINE__, mysql_stmt_error (s->statement));
565       return GNUNET_SYSERR;
566     }
567
568   return GNUNET_OK;
569 }
570
571 /**
572  * Run a prepared SELECT statement.
573  *
574  * @param result_size number of elements in results array
575  * @param results pointer to already initialized MYSQL_BIND
576  *        array (of sufficient size) for passing results
577  * @param processor function to call on each result
578  * @param processor_cls extra argument to processor
579  * @param ... pairs and triplets of "MYSQL_TYPE_XXX" keys and their respective
580  *        values (size + buffer-reference for pointers); terminated
581  *        with "-1"
582  * @return GNUNET_SYSERR on error, otherwise
583  *         the number of successfully affected (or queried) rows
584  */
585 int
586 prepared_statement_run_select (struct StatementHandle
587                                *s, unsigned int result_size,
588                                MYSQL_BIND * results,
589                                GNUNET_MysqlDataProcessor
590                                processor, void *processor_cls,
591                                ...)
592 {
593   va_list ap;
594   int ret;
595   unsigned int rsize;
596   int total;
597
598   if (GNUNET_OK != prepare_statement (s))
599     {
600       GNUNET_break (0);
601       return GNUNET_SYSERR;
602     }
603   va_start (ap, processor_cls);
604   if (GNUNET_OK != init_params (s, ap))
605     {
606       GNUNET_break (0);
607       va_end (ap);
608       return GNUNET_SYSERR;
609     }
610   va_end (ap);
611   rsize = mysql_stmt_field_count (s->statement);
612   if (rsize > result_size)
613     {
614       GNUNET_break (0);
615       return GNUNET_SYSERR;
616     }
617   if (mysql_stmt_bind_result (s->statement, results))
618     {
619       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
620                   _("`%s' failed at %s:%d with error: %s\n"),
621                   "mysql_stmt_bind_result",
622                   __FILE__, __LINE__, mysql_stmt_error (s->statement));
623       return GNUNET_SYSERR;
624     }
625
626   total = 0;
627   while (1)
628     {
629       ret = mysql_stmt_fetch (s->statement);
630       if (ret == MYSQL_NO_DATA)
631         break;
632       if (ret != 0)
633         {
634           GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
635                        _("`%s' failed at %s:%d with error: %s\n"),
636                        "mysql_stmt_fetch",
637                        __FILE__, __LINE__, mysql_stmt_error (s->statement));
638           return GNUNET_SYSERR;
639         }
640       if (processor != NULL)
641         if (GNUNET_OK != processor (processor_cls, rsize, results))
642           break;
643       total++;
644     }
645   mysql_stmt_reset (s->statement);
646   return total;
647 }
648
649
650 static int
651 get_node_uid (unsigned long long *nodeuid, const GNUNET_HashCode * peerHash)
652 {
653   MYSQL_BIND rbind[1];
654   struct GNUNET_CRYPTO_HashAsciiEncoded encPeer;
655   unsigned long long p_len;
656
657   int ret;
658   memset (rbind, 0, sizeof (rbind));
659   rbind[0].buffer_type = MYSQL_TYPE_LONGLONG;
660   rbind[0].buffer = nodeuid;
661   rbind[0].is_unsigned = GNUNET_YES;
662
663   GNUNET_CRYPTO_hash_to_enc (peerHash, &encPeer);
664   p_len = strlen ((char *) &encPeer);
665
666   if (1 != (ret = prepared_statement_run_select (get_nodeuid,
667                                                               1,
668                                                               rbind,
669                                                               return_ok,
670                                                               NULL,
671                                                               MYSQL_TYPE_LONGLONG,
672                                                               &current_trial,
673                                                               GNUNET_YES,
674                                                               MYSQL_TYPE_VAR_STRING,
675                                                               &encPeer,
676                                                               max_varchar_len,
677                                                               &p_len, -1)))
678     {
679 #if DEBUG_DHTLOG
680       fprintf (stderr, "FAILED\n");
681 #endif
682       return GNUNET_SYSERR;
683     }
684   return GNUNET_OK;
685 }
686
687 static int
688 get_current_trial (unsigned long long *trialuid)
689 {
690   MYSQL_BIND rbind[1];
691
692   memset (rbind, 0, sizeof (rbind));
693   rbind[0].buffer_type = MYSQL_TYPE_LONG;
694   rbind[0].is_unsigned = 1;
695   rbind[0].buffer = trialuid;
696
697   if ((GNUNET_OK !=
698        prepared_statement_run_select (get_trial,
699                                       1,
700                                       rbind,
701                                       return_ok, NULL, -1)))
702     {
703       return GNUNET_SYSERR;
704     }
705
706   return GNUNET_OK;
707 }
708
709 static int
710 get_current_topology (unsigned long long *topologyuid)
711 {
712   MYSQL_BIND rbind[1];
713
714   memset (rbind, 0, sizeof (rbind));
715   rbind[0].buffer_type = MYSQL_TYPE_LONGLONG;
716   rbind[0].is_unsigned = 1;
717   rbind[0].buffer = topologyuid;
718
719   if ((GNUNET_OK !=
720        prepared_statement_run_select (get_topology,
721                                       1,
722                                       rbind,
723                                       return_ok, NULL, -1)))
724     {
725       return GNUNET_SYSERR;
726     }
727
728   return GNUNET_OK;
729 }
730
731 static int
732 get_dhtkey_uid (unsigned long long *dhtkeyuid, const GNUNET_HashCode * key)
733 {
734   MYSQL_BIND rbind[1];
735   struct GNUNET_CRYPTO_HashAsciiEncoded encKey;
736   unsigned long long k_len;
737   memset (rbind, 0, sizeof (rbind));
738   rbind[0].buffer_type = MYSQL_TYPE_LONG;
739   rbind[0].is_unsigned = 1;
740   rbind[0].buffer = dhtkeyuid;
741   GNUNET_CRYPTO_hash_to_enc (key, &encKey);
742   k_len = strlen ((char *) &encKey);
743
744   if ((GNUNET_OK !=
745        prepared_statement_run_select (get_dhtkeyuid,
746                                       1,
747                                       rbind,
748                                       return_ok, NULL,
749                                       MYSQL_TYPE_VAR_STRING,
750                                       &encKey,
751                                       max_varchar_len,
752                                       &k_len,
753                                       MYSQL_TYPE_LONGLONG,
754                                       &current_trial,
755                                       GNUNET_YES, -1)))
756     {
757       return GNUNET_SYSERR;
758     }
759
760   return GNUNET_OK;
761 }
762
763 /**
764  * Run a prepared statement that does NOT produce results.
765  *
766  * @param ... pairs and triplets of "MYSQL_TYPE_XXX" keys and their respective
767  *        values (size + buffer-reference for pointers); terminated
768  *        with "-1"
769  * @param insert_id NULL or address where to store the row ID of whatever
770  *        was inserted (only for INSERT statements!)
771  * @return GNUNET_SYSERR on error, otherwise
772  *         the number of successfully affected rows
773  */
774 int
775 prepared_statement_run (struct StatementHandle *s,
776                         unsigned long long *insert_id, ...)
777 {
778   va_list ap;
779   int affected;
780
781   if (GNUNET_OK != prepare_statement(s))
782     {
783       GNUNET_break(0);
784       return GNUNET_SYSERR;
785     }
786   GNUNET_assert(s->valid == GNUNET_YES);
787   if (s->statement == NULL)
788     return GNUNET_SYSERR;
789
790   va_start (ap, insert_id);
791
792   if (GNUNET_OK != init_params (s, ap))
793     {
794       va_end (ap);
795       return GNUNET_SYSERR;
796     }
797
798   va_end (ap);
799   affected = mysql_stmt_affected_rows (s->statement);
800   if (NULL != insert_id)
801     *insert_id = (unsigned long long) mysql_stmt_insert_id (s->statement);
802   mysql_stmt_reset (s->statement);
803
804   return affected;
805 }
806
807 /*
808  * Inserts the specified trial into the dhttests.trials table
809  *
810  * @param trialuid return the trialuid of the newly inserted trial
811  * @param num_nodes how many nodes are in the trial
812  * @param topology integer representing topology for this trial
813  * @param blacklist_topology integer representing blacklist topology for this trial
814  * @param connect_topology integer representing connect topology for this trial
815  * @param connect_topology_option integer representing connect topology option
816  * @param connect_topology_option_modifier float to modify connect option
817  * @param topology_percentage percentage modifier for certain topologies
818  * @param topology_probability probability modifier for certain topologies
819  * @param puts number of puts to perform
820  * @param gets number of gets to perform
821  * @param concurrent number of concurrent requests
822  * @param settle_time time to wait between creating topology and starting testing
823  * @param num_rounds number of times to repeat the trial
824  * @param malicious_getters number of malicious GET peers in the trial
825  * @param malicious_putters number of malicious PUT peers in the trial
826  * @param malicious_droppers number of malicious DROP peers in the trial
827  * @param malicious_get_frequency how often malicious gets are sent
828  * @param malicious_put_frequency how often malicious puts are sent
829  * @param stop_closest stop forwarding PUTs if closest node found
830  * @param stop_found stop forwarding GETs if data found
831  * @param strict_kademlia test used kademlia routing algorithm
832  * @param gets_succeeded how many gets did the test driver report success on
833  * @param message string to put into DB for this trial
834  *
835  * @return GNUNET_OK on success, GNUNET_SYSERR on failure
836  */
837 int add_trial (unsigned long long *trialuid, unsigned int num_nodes, unsigned int topology,
838                unsigned int blacklist_topology, unsigned int connect_topology,
839                unsigned int connect_topology_option, float connect_topology_option_modifier,
840                float topology_percentage, float topology_probability,
841                unsigned int puts, unsigned int gets, unsigned int concurrent, unsigned int settle_time,
842                unsigned int num_rounds, unsigned int malicious_getters, unsigned int malicious_putters,
843                unsigned int malicious_droppers, unsigned int malicious_get_frequency,
844                unsigned int malicious_put_frequency, unsigned int stop_closest, unsigned int stop_found,
845                unsigned int strict_kademlia, unsigned int gets_succeeded,
846                char *message)
847 {
848   MYSQL_STMT *stmt;
849   int ret;
850   unsigned long long m_len;
851   m_len = strlen (message);
852
853   stmt = mysql_stmt_init(conn);
854   if (GNUNET_OK !=
855       (ret = prepared_statement_run (insert_trial, trialuid,
856                                      MYSQL_TYPE_LONG, &num_nodes, GNUNET_YES,
857                                      MYSQL_TYPE_LONG, &topology, GNUNET_YES,
858                                      MYSQL_TYPE_FLOAT, &topology_percentage,
859                                      MYSQL_TYPE_FLOAT, &topology_probability,
860                                      MYSQL_TYPE_LONG, &blacklist_topology, GNUNET_YES,
861                                      MYSQL_TYPE_LONG, &connect_topology, GNUNET_YES,
862                                      MYSQL_TYPE_LONG, &connect_topology_option, GNUNET_YES,
863                                      MYSQL_TYPE_FLOAT, &connect_topology_option_modifier,
864                                      MYSQL_TYPE_LONG, &puts, GNUNET_YES,
865                                      MYSQL_TYPE_LONG, &gets, GNUNET_YES,
866                                      MYSQL_TYPE_LONG, &concurrent, GNUNET_YES,
867                                      MYSQL_TYPE_LONG, &settle_time, GNUNET_YES,
868                                      MYSQL_TYPE_LONG, &num_rounds, GNUNET_YES,
869                                      MYSQL_TYPE_LONG, &malicious_getters, GNUNET_YES,
870                                      MYSQL_TYPE_LONG, &malicious_putters, GNUNET_YES,
871                                      MYSQL_TYPE_LONG, &malicious_droppers, GNUNET_YES,
872                                      MYSQL_TYPE_LONG, &malicious_get_frequency, GNUNET_YES,
873                                      MYSQL_TYPE_LONG, &malicious_put_frequency, GNUNET_YES,
874                                      MYSQL_TYPE_LONG, &stop_closest, GNUNET_YES,
875                                      MYSQL_TYPE_LONG, &stop_found, GNUNET_YES,
876                                      MYSQL_TYPE_LONG, &strict_kademlia, GNUNET_YES,
877                                      MYSQL_TYPE_LONG, &gets_succeeded, GNUNET_YES,
878                                      MYSQL_TYPE_BLOB, message, max_varchar_len +
879                                      max_varchar_len, &m_len,
880                                       -1)))
881     {
882       if (ret == GNUNET_SYSERR)
883         {
884           mysql_stmt_close(stmt);
885           return GNUNET_SYSERR;
886         }
887     }
888
889   get_current_trial (&current_trial);
890
891   mysql_stmt_close(stmt);
892   return GNUNET_OK;
893 }
894
895
896 /*
897  * Inserts the specified stats into the dhttests.node_statistics table
898  *
899  * @param peer the peer inserting the statistic
900  * @param route_requests route requests seen
901  * @param route_forwards route requests forwarded
902  * @param result_requests route result requests seen
903  * @param client_requests client requests initiated
904  * @param result_forwards route results forwarded
905  * @param gets get requests handled
906  * @param puts put requests handle
907  * @param data_inserts data inserted at this node
908  * @param find_peer_requests find peer requests seen
909  * @param find_peers_started find peer requests initiated at this node
910  * @param gets_started get requests initiated at this node
911  * @param puts_started put requests initiated at this node
912  * @param find_peer_responses_received find peer responses received locally
913  * @param get_responses_received get responses received locally
914  * @param find_peer_responses_sent find peer responses sent from this node
915  * @param get_responses_sent get responses sent from this node
916  *
917  * @return GNUNET_OK on success, GNUNET_SYSERR on failure
918  */
919 int
920 add_stat (const struct GNUNET_PeerIdentity *peer, unsigned int route_requests,
921           unsigned int route_forwards, unsigned int result_requests,
922           unsigned int client_requests, unsigned int result_forwards,
923           unsigned int gets, unsigned int puts,
924           unsigned int data_inserts, unsigned int find_peer_requests,
925           unsigned int find_peers_started, unsigned int gets_started,
926           unsigned int puts_started, unsigned int find_peer_responses_received,
927           unsigned int get_responses_received, unsigned int find_peer_responses_sent,
928           unsigned int get_responses_sent)
929 {
930   MYSQL_STMT *stmt;
931   int ret;
932   unsigned long long peer_uid;
933   unsigned long long return_uid;
934   if (peer == NULL)
935     return GNUNET_SYSERR;
936
937   if (GNUNET_OK != get_node_uid (&peer_uid, &peer->hashPubKey))
938     {
939       return GNUNET_SYSERR;
940     }
941
942   stmt = mysql_stmt_init(conn);
943   if (GNUNET_OK !=
944       (ret = prepared_statement_run (insert_stat,
945                                      &return_uid,
946                                      MYSQL_TYPE_LONGLONG, &current_trial, GNUNET_YES,
947                                      MYSQL_TYPE_LONGLONG, &peer_uid, GNUNET_YES,
948                                      MYSQL_TYPE_LONG, &route_requests, GNUNET_YES,
949                                      MYSQL_TYPE_LONG, &route_forwards, GNUNET_YES,
950                                      MYSQL_TYPE_LONG, &result_requests, GNUNET_YES,
951                                      MYSQL_TYPE_LONG, &client_requests, GNUNET_YES,
952                                      MYSQL_TYPE_LONG, &result_forwards, GNUNET_YES,
953                                      MYSQL_TYPE_LONG, &gets, GNUNET_YES,
954                                      MYSQL_TYPE_LONG, &puts, GNUNET_YES,
955                                      MYSQL_TYPE_LONG, &data_inserts, GNUNET_YES,
956                                      MYSQL_TYPE_LONG, &find_peer_requests, GNUNET_YES,
957                                      MYSQL_TYPE_LONG, &find_peers_started, GNUNET_YES,
958                                      MYSQL_TYPE_LONG, &gets_started, GNUNET_YES,
959                                      MYSQL_TYPE_LONG, &puts_started, GNUNET_YES,
960                                      MYSQL_TYPE_LONG, &find_peer_responses_received, GNUNET_YES,
961                                      MYSQL_TYPE_LONG, &get_responses_received, GNUNET_YES,
962                                      MYSQL_TYPE_LONG, &find_peer_responses_sent, GNUNET_YES,
963                                      MYSQL_TYPE_LONG, &get_responses_sent, GNUNET_YES,
964                                      -1)))
965     {
966       if (ret == GNUNET_SYSERR)
967         {
968           mysql_stmt_close(stmt);
969           return GNUNET_SYSERR;
970         }
971     }
972
973   mysql_stmt_close(stmt);
974   return GNUNET_OK;
975 }
976
977 /*
978  * Inserts the specified dhtkey into the dhttests.dhtkeys table,
979  * stores return value of dhttests.dhtkeys.dhtkeyuid into dhtkeyuid
980  *
981  * @param dhtkeyuid return value
982  * @param dhtkey hashcode of key to insert
983  *
984  * @return GNUNET_OK on success, GNUNET_SYSERR on failure
985  */
986 int
987 add_dhtkey (unsigned long long *dhtkeyuid, const GNUNET_HashCode * dhtkey)
988 {
989
990   int ret;
991   struct GNUNET_CRYPTO_HashAsciiEncoded encKey;
992   unsigned long long k_len;
993   unsigned long long h_len;
994   unsigned long long curr_dhtkeyuid;
995   GNUNET_CRYPTO_hash_to_enc (dhtkey, &encKey);
996   k_len = strlen ((char *) &encKey);
997   h_len = sizeof (GNUNET_HashCode);
998   curr_dhtkeyuid = 0;
999   ret = get_dhtkey_uid(&curr_dhtkeyuid, dhtkey);
1000   if (curr_dhtkeyuid != 0) /* dhtkey already exists */
1001     {
1002       if (dhtkeyuid != NULL)
1003         *dhtkeyuid = curr_dhtkeyuid;
1004       return GNUNET_OK;
1005     }
1006
1007   if (GNUNET_OK !=
1008       (ret = prepared_statement_run (insert_dhtkey,
1009                                      dhtkeyuid,
1010                                      MYSQL_TYPE_VAR_STRING,
1011                                      &encKey,
1012                                      max_varchar_len,
1013                                      &k_len,
1014                                      MYSQL_TYPE_LONG,
1015                                      &current_trial,
1016                                      GNUNET_YES,
1017                                      MYSQL_TYPE_BLOB,
1018                                      dhtkey,
1019                                      sizeof (GNUNET_HashCode),
1020                                      &h_len, -1)))
1021     {
1022       if (ret == GNUNET_SYSERR)
1023         {
1024           return GNUNET_SYSERR;
1025         }
1026     }
1027
1028   return GNUNET_OK;
1029 }
1030
1031
1032
1033 /*
1034  * Inserts the specified node into the dhttests.nodes table
1035  *
1036  * @param nodeuid the inserted node uid
1037  * @param node the node to insert
1038  *
1039  * @return GNUNET_OK on success, GNUNET_SYSERR on failure
1040  */
1041 int
1042 add_node (unsigned long long *nodeuid, struct GNUNET_PeerIdentity * node)
1043 {
1044   struct GNUNET_CRYPTO_HashAsciiEncoded encPeer;
1045   unsigned long p_len;
1046   unsigned long h_len;
1047   int ret;
1048
1049   if (node == NULL)
1050     return GNUNET_SYSERR;
1051
1052   GNUNET_CRYPTO_hash_to_enc (&node->hashPubKey, &encPeer);
1053   p_len = (unsigned long) strlen ((char *) &encPeer);
1054   h_len = sizeof (GNUNET_HashCode);
1055   if (GNUNET_OK !=
1056       (ret = prepared_statement_run (insert_node,
1057                                                   nodeuid,
1058                                                   MYSQL_TYPE_LONGLONG,
1059                                                   &current_trial,
1060                                                   GNUNET_YES,
1061                                                   MYSQL_TYPE_VAR_STRING,
1062                                                   &encPeer,
1063                                                   max_varchar_len,
1064                                                   &p_len,
1065                                                   MYSQL_TYPE_BLOB,
1066                                                   &node->hashPubKey,
1067                                                   sizeof (GNUNET_HashCode),
1068                                                   &h_len, -1)))
1069     {
1070       if (ret == GNUNET_SYSERR)
1071         {
1072           return GNUNET_SYSERR;
1073         }
1074     }
1075   return GNUNET_OK;
1076 }
1077
1078 /*
1079  * Update dhttests.trials table with current server time as end time
1080  *
1081  * @param trialuid trial to update
1082  * @param gets_succeeded how many gets did the testcase report as successful
1083  *
1084  * @return GNUNET_OK on success, GNUNET_SYSERR on failure.
1085  */
1086 int
1087 update_trials (unsigned long long trialuid,
1088                unsigned int gets_succeeded)
1089 {
1090   int ret;
1091
1092   if (GNUNET_OK !=
1093       (ret = prepared_statement_run (update_trial,
1094                                     NULL,
1095                                     MYSQL_TYPE_LONG, &gets_succeeded, GNUNET_YES,
1096                                     MYSQL_TYPE_LONGLONG, &trialuid, GNUNET_YES,
1097                                     -1)))
1098     {
1099       if (ret == GNUNET_SYSERR)
1100         {
1101           return GNUNET_SYSERR;
1102         }
1103     }
1104   if (ret > 0)
1105     return GNUNET_OK;
1106   else
1107     return GNUNET_SYSERR;
1108 }
1109
1110
1111 /*
1112  * Update dhttests.trials table with total connections information
1113  *
1114  * @param trialuid the trialuid to update
1115  * @param totalConnections the number of connections
1116  *
1117  * @return GNUNET_OK on success, GNUNET_SYSERR on failure.
1118  */
1119 int
1120 add_connections (unsigned long long trialuid, unsigned int totalConnections)
1121 {
1122   int ret;
1123
1124   if (GNUNET_OK !=
1125       (ret = prepared_statement_run (update_connection,
1126                                                   NULL,
1127                                                   MYSQL_TYPE_LONG,
1128                                                   &totalConnections,
1129                                                   GNUNET_YES,
1130                                                   MYSQL_TYPE_LONGLONG,
1131                                                   &trialuid, GNUNET_YES, -1)))
1132     {
1133       if (ret == GNUNET_SYSERR)
1134         {
1135           return GNUNET_SYSERR;
1136         }
1137     }
1138   if (ret > 0)
1139     return GNUNET_OK;
1140   else
1141     return GNUNET_SYSERR;
1142 }
1143
1144 /*
1145  * Inserts the specified query into the dhttests.queries table
1146  *
1147  * @param sqlqueruid inserted query uid
1148  * @param queryid dht query id
1149  * @param type type of the query
1150  * @param hops number of hops query traveled
1151  * @param succeeded whether or not query was successful
1152  * @param node the node the query hit
1153  * @param key the key of the query
1154  *
1155  * @return GNUNET_OK on success, GNUNET_SYSERR on failure.
1156  */
1157 int
1158 add_query (unsigned long long *sqlqueryuid, unsigned long long queryid,
1159            unsigned int type, unsigned int hops, int succeeded,
1160            const struct GNUNET_PeerIdentity * node, const GNUNET_HashCode * key)
1161 {
1162   int ret;
1163   unsigned long long peer_uid, key_uid;
1164   peer_uid = 0;
1165   key_uid = 0;
1166
1167   if ((node != NULL)
1168       && (GNUNET_OK == get_node_uid (&peer_uid, &node->hashPubKey)))
1169     {
1170
1171     }
1172   else
1173     {
1174       return GNUNET_SYSERR;
1175     }
1176
1177   if ((key != NULL) && (GNUNET_OK == get_dhtkey_uid (&key_uid, key)))
1178     {
1179
1180     }
1181   else if ((key != NULL) && (key->bits[(512 / 8 / sizeof (unsigned int)) - 1] == 42))   /* Malicious marker */
1182     {
1183       key_uid = 0;
1184     }
1185   else
1186     {
1187       return GNUNET_SYSERR;
1188     }
1189
1190   if (GNUNET_OK !=
1191       (ret = prepared_statement_run (insert_query,
1192                                                   sqlqueryuid,
1193                                                   MYSQL_TYPE_LONGLONG,
1194                                                   &current_trial,
1195                                                   GNUNET_YES,
1196                                                   MYSQL_TYPE_LONG,
1197                                                   &type,
1198                                                   GNUNET_NO,
1199                                                   MYSQL_TYPE_LONG,
1200                                                   &hops,
1201                                                   GNUNET_YES,
1202                                                   MYSQL_TYPE_LONGLONG,
1203                                                   &key_uid,
1204                                                   GNUNET_YES,
1205                                                   MYSQL_TYPE_LONGLONG,
1206                                                   &queryid,
1207                                                   GNUNET_YES,
1208                                                   MYSQL_TYPE_LONG,
1209                                                   &succeeded,
1210                                                   GNUNET_NO,
1211                                                   MYSQL_TYPE_LONGLONG,
1212                                                   &peer_uid, GNUNET_YES, -1)))
1213     {
1214       if (ret == GNUNET_SYSERR)
1215         {
1216           return GNUNET_SYSERR;
1217         }
1218     }
1219   if (ret > 0)
1220     return GNUNET_OK;
1221   else
1222     return GNUNET_SYSERR;
1223 }
1224
1225 /*
1226  * Inserts the specified route information into the dhttests.routes table
1227  *
1228  * @param sqlqueruid inserted query uid
1229  * @param queryid dht query id
1230  * @param type type of the query
1231  * @param hops number of hops query traveled
1232  * @param succeeded whether or not query was successful
1233  * @param node the node the query hit
1234  * @param key the key of the query
1235  * @param from_node the node that sent the message to node
1236  * @param to_node next node to forward message to
1237  *
1238  * @return GNUNET_OK on success, GNUNET_SYSERR on failure.
1239  */
1240 int
1241 add_route (unsigned long long *sqlqueryuid, unsigned long long queryid,
1242            unsigned int type, unsigned int hops,
1243            int succeeded, const struct GNUNET_PeerIdentity * node,
1244            const GNUNET_HashCode * key, const struct GNUNET_PeerIdentity * from_node,
1245            const struct GNUNET_PeerIdentity * to_node)
1246 {
1247   unsigned long long peer_uid = 0;
1248   unsigned long long key_uid = 0;
1249   unsigned long long from_uid = 0;
1250   unsigned long long to_uid = 0;
1251   int ret;
1252
1253   if (from_node != NULL)
1254     get_node_uid (&from_uid, &from_node->hashPubKey);
1255   else
1256     from_uid = 0;
1257
1258   if (to_node != NULL)
1259     get_node_uid (&to_uid, &to_node->hashPubKey);
1260   else
1261     to_uid = 0;
1262
1263   if ((node != NULL))
1264     {
1265       if (1 != get_node_uid (&peer_uid, &node->hashPubKey))
1266         {
1267           return GNUNET_SYSERR;
1268         }
1269     }
1270   else
1271     return GNUNET_SYSERR;
1272
1273   if ((key != NULL))
1274     {
1275       if (1 != get_dhtkey_uid (&key_uid, key))
1276         {
1277           return GNUNET_SYSERR;
1278         }
1279     }
1280   else
1281     return GNUNET_SYSERR;
1282
1283   if (GNUNET_OK !=
1284       (ret = prepared_statement_run (insert_route,
1285                                     sqlqueryuid,
1286                                     MYSQL_TYPE_LONGLONG,
1287                                     &current_trial,
1288                                     GNUNET_YES,
1289                                     MYSQL_TYPE_LONG,
1290                                     &type,
1291                                     GNUNET_NO,
1292                                     MYSQL_TYPE_LONG,
1293                                     &hops,
1294                                     GNUNET_YES,
1295                                     MYSQL_TYPE_LONGLONG,
1296                                     &key_uid,
1297                                     GNUNET_YES,
1298                                     MYSQL_TYPE_LONGLONG,
1299                                     &queryid,
1300                                     GNUNET_YES,
1301                                     MYSQL_TYPE_LONG,
1302                                     &succeeded,
1303                                     GNUNET_NO,
1304                                     MYSQL_TYPE_LONGLONG,
1305                                     &peer_uid,
1306                                     GNUNET_YES,
1307                                     MYSQL_TYPE_LONGLONG,
1308                                     &from_uid,
1309                                     GNUNET_YES,
1310                                     MYSQL_TYPE_LONGLONG,
1311                                     &to_uid, GNUNET_YES, -1)))
1312     {
1313       if (ret == GNUNET_SYSERR)
1314         {
1315           return GNUNET_SYSERR;
1316         }
1317     }
1318   if (ret > 0)
1319     return GNUNET_OK;
1320   else
1321     return GNUNET_SYSERR;
1322 }
1323
1324 /*
1325  * Update dhttests.topology table with total connections information
1326  *
1327  * @param totalConnections the number of connections
1328  *
1329  * @return GNUNET_OK on success, GNUNET_SYSERR on failure.
1330  */
1331 int
1332 update_current_topology (unsigned int connections)
1333 {
1334   int ret;
1335   unsigned long long topologyuid;
1336
1337   get_current_topology(&topologyuid);
1338
1339   if (GNUNET_OK !=
1340       (ret = prepared_statement_run (update_topology,
1341                                      NULL,
1342                                      MYSQL_TYPE_LONG,
1343                                      &connections,
1344                                      GNUNET_YES,
1345                                      MYSQL_TYPE_LONGLONG,
1346                                      &topologyuid, GNUNET_YES, -1)))
1347     {
1348       if (ret == GNUNET_SYSERR)
1349         {
1350           return GNUNET_SYSERR;
1351         }
1352     }
1353   if (ret > 0)
1354     return GNUNET_OK;
1355   else
1356     return GNUNET_SYSERR;
1357   return GNUNET_OK;
1358 }
1359
1360 /*
1361  * Records the current topology (number of connections, time, trial)
1362  *
1363  * @param num_connections how many connections are in the topology
1364  *
1365  * @return GNUNET_OK on success, GNUNET_SYSERR on failure
1366  */
1367 int
1368 add_topology (int num_connections)
1369 {
1370   int ret;
1371
1372   if (GNUNET_OK !=
1373       (ret = prepared_statement_run (insert_topology,
1374                                      NULL,
1375                                      MYSQL_TYPE_LONGLONG,
1376                                      &current_trial,
1377                                      GNUNET_YES,
1378                                      MYSQL_TYPE_LONG,
1379                                      &num_connections,
1380                                      GNUNET_YES, -1)))
1381     {
1382       if (ret == GNUNET_SYSERR)
1383         {
1384           return GNUNET_SYSERR;
1385         }
1386     }
1387   if (ret > 0)
1388     return GNUNET_OK;
1389   else
1390     return GNUNET_SYSERR;
1391   return GNUNET_OK;
1392 }
1393
1394 /*
1395  * Records a connection between two peers in the current topology
1396  *
1397  * @param first one side of the connection
1398  * @param second other side of the connection
1399  *
1400  * @return GNUNET_OK on success, GNUNET_SYSERR on failure
1401  */
1402 int
1403 add_extended_topology (const struct GNUNET_PeerIdentity *first, const struct GNUNET_PeerIdentity *second)
1404 {
1405   int ret;
1406   unsigned long long first_uid;
1407   unsigned long long second_uid;
1408   unsigned long long topologyuid;
1409
1410   if (GNUNET_OK != get_current_topology(&topologyuid))
1411     return GNUNET_SYSERR;
1412   if (GNUNET_OK != get_node_uid(&first_uid, &first->hashPubKey))
1413     return GNUNET_SYSERR;
1414   if (GNUNET_OK != get_node_uid(&second_uid, &second->hashPubKey))
1415     return GNUNET_SYSERR;
1416
1417   if (GNUNET_OK !=
1418       (ret = prepared_statement_run (extend_topology,
1419                                      NULL,
1420                                      MYSQL_TYPE_LONGLONG,
1421                                      &topologyuid,
1422                                      GNUNET_YES,
1423                                      MYSQL_TYPE_LONGLONG,
1424                                      &first_uid,
1425                                      GNUNET_YES,
1426                                      MYSQL_TYPE_LONGLONG,
1427                                      &second_uid,
1428                                      GNUNET_YES,-1)))
1429     {
1430       if (ret == GNUNET_SYSERR)
1431         {
1432           return GNUNET_SYSERR;
1433         }
1434     }
1435   if (ret > 0)
1436     return GNUNET_OK;
1437   else
1438     return GNUNET_SYSERR;
1439   return GNUNET_OK;
1440 }
1441
1442
1443 /*
1444  * Provides the dhtlog api
1445  *
1446  * @param c the configuration to use to connect to a server
1447  *
1448  * @return the handle to the server, or NULL on error
1449  */
1450 void *
1451 libgnunet_plugin_dhtlog_mysql_init (void * cls)
1452 {
1453   struct GNUNET_DHTLOG_Plugin *plugin = cls;
1454
1455   cfg = plugin->cfg;
1456   max_varchar_len = 255;
1457 #if DEBUG_DHTLOG
1458   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "MySQL DHT Logger: initializing database\n");
1459 #endif
1460
1461   if (iopen (plugin) != GNUNET_OK)
1462     {
1463       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1464                   _("Failed to initialize MySQL database connection for dhtlog.\n"));
1465       return NULL;
1466     }
1467
1468   GNUNET_assert(plugin->dhtlog_api == NULL);
1469   plugin->dhtlog_api = GNUNET_malloc(sizeof(struct GNUNET_DHTLOG_Handle));
1470   plugin->dhtlog_api->insert_trial = &add_trial;
1471   plugin->dhtlog_api->insert_stat = &add_stat;
1472   plugin->dhtlog_api->insert_query = &add_query;
1473   plugin->dhtlog_api->update_trial = &update_trials;
1474   plugin->dhtlog_api->insert_route = &add_route;
1475   plugin->dhtlog_api->insert_node = &add_node;
1476   plugin->dhtlog_api->insert_dhtkey = &add_dhtkey;
1477   plugin->dhtlog_api->update_connections = &add_connections;
1478   plugin->dhtlog_api->insert_topology = &add_topology;
1479   plugin->dhtlog_api->update_topology = &update_current_topology;
1480   plugin->dhtlog_api->insert_extended_topology = &add_extended_topology;
1481   get_current_trial (&current_trial);
1482
1483   return plugin;
1484 }
1485
1486 /**
1487  * Shutdown the plugin.
1488  */
1489 void *
1490 libgnunet_plugin_dhtlog_mysql_done (void * cls)
1491 {
1492   struct GNUNET_DHTLOG_Handle *dhtlog_api = cls;
1493
1494   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1495               "MySQL DHT Logger: database shutdown\n");
1496   GNUNET_assert(dhtlog_api != NULL);
1497   prepared_statement_close(insert_query);
1498   prepared_statement_close(insert_route);
1499   prepared_statement_close(insert_trial);
1500   prepared_statement_close(insert_node);
1501   prepared_statement_close(insert_dhtkey);
1502   prepared_statement_close(update_trial);
1503   prepared_statement_close(get_dhtkeyuid);
1504   prepared_statement_close(get_nodeuid);
1505   prepared_statement_close(update_connection);
1506   prepared_statement_close(get_trial);
1507   prepared_statement_close(get_topology);
1508   prepared_statement_close(insert_topology);
1509   prepared_statement_close(update_topology);
1510   prepared_statement_close(extend_topology);
1511
1512   if (conn != NULL)
1513     mysql_close (conn);
1514   conn = NULL;
1515   mysql_library_end();
1516   GNUNET_free(dhtlog_api);
1517   return NULL;
1518 }
1519
1520 /* end of plugin_dhtlog_mysql.c */