use big endian
[oweals/gnunet.git] / src / datastore / plugin_datastore_mysql.c
1 /*
2      This file is part of GNUnet
3      (C) 2009, 2010 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 3, 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 datastore/plugin_datastore_mysql.c
23  * @brief mysql-based datastore backend
24  * @author Igor Wronsky
25  * @author Christian Grothoff
26  *
27  * NOTE: This db module does NOT work with mysql prior to 4.1 since
28  * it uses prepared statements.  MySQL 5.0.46 promises to fix a bug
29  * in MyISAM that is causing us grief.  At the time of this writing,
30  * that version is yet to be released.  In anticipation, the code
31  * will use MyISAM with 5.0.46 (and higher).  If you run such a
32  * version, please run "make check" to verify that the MySQL bug
33  * was actually fixed in your version (and if not, change the
34  * code below to use MyISAM for gn071).
35  *
36  * HIGHLIGHTS
37  *
38  * Pros
39  * + On up-to-date hardware where mysql can be used comfortably, this
40  *   module will have better performance than the other db choices
41  *   (according to our tests).
42  * + Its often possible to recover the mysql database from internal
43  *   inconsistencies. The other db choices do not support repair!
44  * Cons
45  * - Memory usage (Comment: "I have 1G and it never caused me trouble")
46  * - Manual setup
47  *
48  * MANUAL SETUP INSTRUCTIONS
49  *
50  * 1) in /etc/gnunet.conf, set
51  *    <pre>
52  *     [datastore]
53  *     DATABASE = "mysql"
54  *    </pre>
55  * 2) Then access mysql as root,
56  *    <pre>
57  *
58  *    $ mysql -u root -p
59  *
60  *    </pre>
61  *    and do the following. [You should replace $USER with the username
62  *    that will be running the gnunetd process].
63  *    <pre>
64  *
65       CREATE DATABASE gnunet;
66       GRANT select,insert,update,delete,create,alter,drop,create temporary tables
67          ON gnunet.* TO $USER@localhost;
68       SET PASSWORD FOR $USER@localhost=PASSWORD('$the_password_you_like');
69       FLUSH PRIVILEGES;
70  *
71  *    </pre>
72  * 3) In the $HOME directory of $USER, create a ".my.cnf" file
73  *    with the following lines
74  *    <pre>
75
76       [client]
77       user=$USER
78       password=$the_password_you_like
79
80  *    </pre>
81  *
82  * Thats it. Note that .my.cnf file is a security risk unless its on
83  * a safe partition etc. The $HOME/.my.cnf can of course be a symbolic
84  * link. Even greater security risk can be achieved by setting no
85  * password for $USER.  Luckily $USER has only priviledges to mess
86  * up GNUnet's tables, nothing else (unless you give him more,
87  * of course).<p>
88  *
89  * 4) Still, perhaps you should briefly try if the DB connection
90  *    works. First, login as $USER. Then use,
91  *
92  *    <pre>
93  *    $ mysql -u $USER -p $the_password_you_like
94  *    mysql> use gnunet;
95  *    </pre>
96  *
97  *    If you get the message &quot;Database changed&quot; it probably works.
98  *
99  *    [If you get &quot;ERROR 2002: Can't connect to local MySQL server
100  *     through socket '/tmp/mysql.sock' (2)&quot; it may be resolvable by
101  *     &quot;ln -s /var/run/mysqld/mysqld.sock /tmp/mysql.sock&quot;
102  *     so there may be some additional trouble depending on your mysql setup.]
103  *
104  * REPAIRING TABLES
105  *
106  * - Its probably healthy to check your tables for inconsistencies
107  *   every now and then.
108  * - If you get odd SEGVs on gnunetd startup, it might be that the mysql
109  *   databases have been corrupted.
110  * - The tables can be verified/fixed in two ways;
111  *   1) by running mysqlcheck -A, or
112  *   2) by executing (inside of mysql using the GNUnet database):
113  *   mysql> REPAIR TABLE gn090;
114  *   mysql> REPAIR TABLE gn072;
115  *
116  * PROBLEMS?
117  *
118  * If you have problems related to the mysql module, your best
119  * friend is probably the mysql manual. The first thing to check
120  * is that mysql is basically operational, that you can connect
121  * to it, create tables, issue queries etc.
122  *
123  * TODO:
124  * - use FOREIGN KEY for 'uid/vkey'
125  * - consistent naming of uid/vkey
126  */
127
128 #include "platform.h"
129 #include "plugin_datastore.h"
130 #include "gnunet_util_lib.h"
131 #include <mysql/mysql.h>
132
133 #define DEBUG_MYSQL GNUNET_NO
134
135 #define MAX_DATUM_SIZE 65536
136
137 /**
138  * Maximum number of supported parameters for a prepared
139  * statement.  Increase if needed.
140  */
141 #define MAX_PARAM 16
142
143 /**
144  * Die with an error message that indicates
145  * a failure of the command 'cmd' with the message given
146  * by strerror(errno).
147  */
148 #define DIE_MYSQL(cmd, dbh) do { GNUNET_log(GNUNET_ERROR_TYPE_ERROR, _("`%s' failed at %s:%d with error: %s\n"), cmd, __FILE__, __LINE__, mysql_error((dbh)->dbf)); abort(); } while(0);
149
150 /**
151  * Log an error message at log-level 'level' that indicates
152  * a failure of the command 'cmd' on file 'filename'
153  * with the message given by strerror(errno).
154  */
155 #define LOG_MYSQL(level, cmd, dbh) do { GNUNET_log(level, _("`%s' failed at %s:%d with error: %s\n"), cmd, __FILE__, __LINE__, mysql_error((dbh)->dbf)); } while(0);
156
157
158 /* warning, slighly crazy mysql statements ahead.  Essentially, MySQL does not handle
159    "OR" very well, so we need to use UNION instead.  And UNION does not
160    automatically apply a LIMIT on the outermost clause, so we need to
161    repeat ourselves quite a bit.  All hail the performance gods (and thanks
162    to #mysql on freenode) */
163 #define SELECT_IT_LOW_PRIORITY "(SELECT type,prio,anonLevel,expire,hash,vkey FROM gn090 FORCE INDEX(prio) WHERE (prio = ? AND vkey > ?) "\
164                                "ORDER BY prio ASC,vkey ASC LIMIT 1) "                           \
165                                "UNION "\
166                                "(SELECT type,prio,anonLevel,expire,hash,vkey FROM gn090 FORCE INDEX(prio) WHERE (prio > ? AND vkey != ?)"\
167                                "ORDER BY prio ASC,vkey ASC LIMIT 1)"\
168                                "ORDER BY prio ASC,vkey ASC LIMIT 1"
169
170 #define SELECT_IT_NON_ANONYMOUS "(SELECT type,prio,anonLevel,expire,hash,vkey FROM gn090 FORCE INDEX(prio) WHERE (prio = ? AND vkey < ?)"\
171                                 " AND anonLevel=0 ORDER BY prio DESC,vkey DESC LIMIT 1) "\
172                                 "UNION "\
173                                 "(SELECT type,prio,anonLevel,expire,hash,vkey FROM gn090 FORCE INDEX(prio) WHERE (prio < ? AND vkey != ?)"\
174                                 " AND anonLevel=0 ORDER BY prio DESC,vkey DESC LIMIT 1) "\
175                                 "ORDER BY prio DESC,vkey DESC LIMIT 1"
176
177 #define SELECT_IT_EXPIRATION_TIME "(SELECT type,prio,anonLevel,expire,hash,vkey FROM gn090 FORCE INDEX(expire) WHERE (expire = ? AND vkey > ?) "\
178                                   "ORDER BY expire ASC,vkey ASC LIMIT 1) "\
179                                   "UNION "\
180                                   "(SELECT type,prio,anonLevel,expire,hash,vkey FROM gn090 FORCE INDEX(expire) WHERE (expire > ? AND vkey != ?) "\
181                                   "ORDER BY expire ASC,vkey ASC LIMIT 1)"\
182                                   "ORDER BY expire ASC,vkey ASC LIMIT 1"
183
184
185 #define SELECT_IT_MIGRATION_ORDER "(SELECT type,prio,anonLevel,expire,hash,vkey FROM gn090 FORCE INDEX(expire) WHERE (expire = ? AND vkey < ?)"\
186                                   " AND expire > ? AND type!=3"\
187                                   " ORDER BY expire DESC,vkey DESC LIMIT 1) "\
188                                   "UNION "\
189                                   "(SELECT type,prio,anonLevel,expire,hash,vkey FROM gn090 FORCE INDEX(expire) WHERE (expire < ? AND vkey != ?)"\
190                                   " AND expire > ? AND type!=3"\
191                                   " ORDER BY expire DESC,vkey DESC LIMIT 1)"\
192                                   "ORDER BY expire DESC,vkey DESC LIMIT 1"
193
194 // #define SELECT_SIZE "SELECT SUM(BIT_LENGTH(value) DIV 8) FROM gn072"
195
196
197 struct GNUNET_MysqlStatementHandle
198 {
199   struct GNUNET_MysqlStatementHandle *next;
200
201   struct GNUNET_MysqlStatementHandle *prev;
202
203   char *query;
204
205   MYSQL_STMT *statement;
206
207   int valid;
208
209 };
210
211 /**
212  * Context for the universal iterator.
213  */
214 struct NextRequestClosure;
215
216 /**
217  * Type of a function that will prepare
218  * the next iteration.
219  *
220  * @param cls closure
221  * @param nc the next context; NULL for the last
222  *         call which gives the callback a chance to
223  *         clean up the closure
224  * @return GNUNET_OK on success, GNUNET_NO if there are
225  *         no more values, GNUNET_SYSERR on error
226  */
227 typedef int (*PrepareFunction)(void *cls,
228                                struct NextRequestClosure *nc);
229
230
231 struct NextRequestClosure
232 {
233   struct Plugin *plugin;
234
235   struct GNUNET_TIME_Absolute now;
236
237   /**
238    * Function to call to prepare the next
239    * iteration.
240    */
241   PrepareFunction prep;
242
243   /**
244    * Closure for prep.
245    */
246   void *prep_cls;
247
248   MYSQL_BIND rbind[6];
249
250   unsigned int type;
251   
252   unsigned int iter_select;
253
254   PluginIterator dviter;
255
256   void *dviter_cls;
257
258   unsigned int last_prio;
259
260   unsigned long long last_expire;
261
262   unsigned long long last_vkey;
263
264   int end_it;
265 };
266
267
268 /**
269  * Context for all functions in this plugin.
270  */
271 struct Plugin 
272 {
273   /**
274    * Our execution environment.
275    */
276   struct GNUNET_DATASTORE_PluginEnvironment *env;
277
278   MYSQL *dbf;
279   
280   struct GNUNET_MysqlStatementHandle *shead;
281
282   struct GNUNET_MysqlStatementHandle *stail;
283
284   /**
285    * Filename of "my.cnf" (msyql configuration).
286    */
287   char *cnffile;
288
289   /**
290    * Closure of the 'next_task' (must be freed if 'next_task' is cancelled).
291    */
292   struct NextRequestClosure *next_task_nc;
293
294   /**
295    * Pending task with scheduler for running the next request.
296    */
297   GNUNET_SCHEDULER_TaskIdentifier next_task;
298
299   /**
300    * Statements dealing with gn072 table 
301    */
302 #define SELECT_VALUE "SELECT value FROM gn072 WHERE vkey=?"
303   struct GNUNET_MysqlStatementHandle *select_value;
304
305 #define DELETE_VALUE "DELETE FROM gn072 WHERE vkey=?"
306   struct GNUNET_MysqlStatementHandle *delete_value;
307
308 #define INSERT_VALUE "INSERT INTO gn072 (value) VALUES (?)"
309   struct GNUNET_MysqlStatementHandle *insert_value;
310
311   /**
312    * Statements dealing with gn090 table 
313    */
314 #define INSERT_ENTRY "INSERT INTO gn090 (type,prio,anonLevel,expire,hash,vhash,vkey) VALUES (?,?,?,?,?,?,?)"
315   struct GNUNET_MysqlStatementHandle *insert_entry;
316   
317 #define DELETE_ENTRY_BY_VKEY "DELETE FROM gn090 WHERE vkey=?"
318   struct GNUNET_MysqlStatementHandle *delete_entry_by_vkey;
319   
320 #define SELECT_ENTRY_BY_HASH "SELECT type,prio,anonLevel,expire,hash,vkey FROM gn090 FORCE INDEX (hash_vkey) WHERE hash=? AND vkey > ? ORDER BY vkey ASC LIMIT 1 OFFSET ?"
321   struct GNUNET_MysqlStatementHandle *select_entry_by_hash;
322   
323 #define SELECT_ENTRY_BY_HASH_AND_VHASH "SELECT type,prio,anonLevel,expire,hash,vkey FROM gn090 FORCE INDEX (hash_vhash_vkey) WHERE hash=? AND vhash=? AND vkey > ? ORDER BY vkey ASC LIMIT 1 OFFSET ?"
324   struct GNUNET_MysqlStatementHandle *select_entry_by_hash_and_vhash;
325
326 #define SELECT_ENTRY_BY_HASH_AND_TYPE "SELECT type,prio,anonLevel,expire,hash,vkey FROM gn090 FORCE INDEX (hash_vkey) WHERE hash=? AND vkey > ? AND type=? ORDER BY vkey ASC LIMIT 1 OFFSET ?"
327   struct GNUNET_MysqlStatementHandle *select_entry_by_hash_and_type;
328   
329 #define SELECT_ENTRY_BY_HASH_VHASH_AND_TYPE "SELECT type,prio,anonLevel,expire,hash,vkey FROM gn090 FORCE INDEX (hash_vhash_vkey) WHERE hash=? AND vhash=? AND vkey > ? AND type=? ORDER BY vkey ASC LIMIT 1 OFFSET ?"
330   struct GNUNET_MysqlStatementHandle *select_entry_by_hash_vhash_and_type;
331   
332 #define COUNT_ENTRY_BY_HASH "SELECT count(*) FROM gn090 FORCE INDEX (hash) WHERE hash=?"
333   struct GNUNET_MysqlStatementHandle *count_entry_by_hash;
334
335 #define COUNT_ENTRY_BY_HASH_AND_VHASH "SELECT count(*) FROM gn090 FORCE INDEX (hash_vhash_vkey) WHERE hash=? AND vhash=?"
336   struct GNUNET_MysqlStatementHandle *count_entry_by_hash_and_vhash;
337
338 #define COUNT_ENTRY_BY_HASH_AND_TYPE "SELECT count(*) FROM gn090 FORCE INDEX (hash) WHERE hash=? AND type=?"
339   struct GNUNET_MysqlStatementHandle *count_entry_by_hash_and_type;
340
341 #define COUNT_ENTRY_BY_HASH_VHASH_AND_TYPE "SELECT count(*) FROM gn090 FORCE INDEX (hash_vhash) WHERE hash=? AND vhash=? AND type=?"
342   struct GNUNET_MysqlStatementHandle *count_entry_by_hash_vhash_and_type;
343
344 #define UPDATE_ENTRY "UPDATE gn090 SET prio=prio+?,expire=IF(expire>=?,expire,?) WHERE vkey=?"
345   struct GNUNET_MysqlStatementHandle *update_entry;
346
347   struct GNUNET_MysqlStatementHandle *iter[4];
348
349   /**
350    * Size of the mysql database on disk.
351    */
352   unsigned long long content_size;
353
354 };
355
356
357 /**
358  * Obtain the location of ".my.cnf".
359  * @return NULL on error
360  */
361 static char *
362 get_my_cnf_path (const struct GNUNET_CONFIGURATION_Handle *cfg)
363 {
364   char *cnffile;
365   char *home_dir;
366   struct stat st;
367 #ifndef WINDOWS
368   struct passwd *pw;
369 #endif
370   int configured;
371
372 #ifndef WINDOWS
373   pw = getpwuid (getuid ());
374   if (!pw)
375     {
376       GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, 
377                            "getpwuid");
378       return NULL;
379     }
380   if (GNUNET_YES ==
381       GNUNET_CONFIGURATION_have_value (cfg,
382                                        "datastore-mysql", "CONFIG"))
383     {
384       GNUNET_assert (GNUNET_OK == 
385                      GNUNET_CONFIGURATION_get_value_filename (cfg,
386                                                               "datastore-mysql", "CONFIG", &cnffile));
387       configured = GNUNET_YES;
388     }
389   else
390     {
391       home_dir = GNUNET_strdup (pw->pw_dir);
392 #else
393       home_dir = (char *) GNUNET_malloc (_MAX_PATH + 1);
394       plibc_conv_to_win_path ("~/", home_dir);
395 #endif
396       GNUNET_asprintf (&cnffile, "%s/.my.cnf", home_dir);
397       GNUNET_free (home_dir);
398       configured = GNUNET_NO;
399     }
400   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
401               _("Trying to use file `%s' for MySQL configuration.\n"),
402               cnffile);
403   if ((0 != STAT (cnffile, &st)) ||
404       (0 != ACCESS (cnffile, R_OK)) || (!S_ISREG (st.st_mode)))
405     {
406       if (configured == GNUNET_YES)
407         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
408                     _("Could not access file `%s': %s\n"), cnffile,
409                     STRERROR (errno));
410       GNUNET_free (cnffile);
411       return NULL;
412     }
413   return cnffile;
414 }
415
416
417
418 /**
419  * Free a prepared statement.
420  */
421 static void
422 prepared_statement_destroy (struct Plugin *plugin, 
423                             struct GNUNET_MysqlStatementHandle
424                             *s)
425 {
426   GNUNET_CONTAINER_DLL_remove (plugin->shead,
427                                plugin->stail,
428                                s);
429   if (s->valid)
430     mysql_stmt_close (s->statement);
431   GNUNET_free (s->query);
432   GNUNET_free (s);
433 }
434
435
436 /**
437  * Close database connection and all prepared statements (we got a DB
438  * disconnect error).
439  */
440 static int
441 iclose (struct Plugin *plugin)
442 {
443   struct GNUNET_MysqlStatementHandle *spos;
444
445   spos = plugin->shead;
446   while (NULL != plugin->shead)
447     prepared_statement_destroy (plugin,
448                                 plugin->shead);
449   if (plugin->dbf != NULL)
450     {
451       mysql_close (plugin->dbf);
452       plugin->dbf = NULL;
453     }
454   return GNUNET_OK;
455 }
456
457
458 /**
459  * Open the connection with the database (and initialize
460  * our default options).
461  *
462  * @return GNUNET_OK on success
463  */
464 static int
465 iopen (struct Plugin *ret)
466 {
467   char *mysql_dbname;
468   char *mysql_server;
469   char *mysql_user;
470   char *mysql_password;
471   unsigned long long mysql_port;
472   my_bool reconnect;
473   unsigned int timeout;
474
475   ret->dbf = mysql_init (NULL);
476   if (ret->dbf == NULL)
477     return GNUNET_SYSERR;
478   if (ret->cnffile != NULL)
479     mysql_options (ret->dbf, MYSQL_READ_DEFAULT_FILE, ret->cnffile);
480   mysql_options (ret->dbf, MYSQL_READ_DEFAULT_GROUP, "client");
481   reconnect = 0;
482   mysql_options (ret->dbf, MYSQL_OPT_RECONNECT, &reconnect);
483   mysql_options (ret->dbf,
484                  MYSQL_OPT_CONNECT_TIMEOUT, (const void *) &timeout);
485   mysql_options(ret->dbf, MYSQL_SET_CHARSET_NAME, "UTF8");
486   timeout = 60; /* in seconds */
487   mysql_options (ret->dbf, MYSQL_OPT_READ_TIMEOUT, (const void *) &timeout);
488   mysql_options (ret->dbf, MYSQL_OPT_WRITE_TIMEOUT, (const void *) &timeout);
489   mysql_dbname = NULL;
490   if (GNUNET_YES == GNUNET_CONFIGURATION_have_value (ret->env->cfg,
491                                                      "datastore-mysql", "DATABASE"))
492     GNUNET_assert (GNUNET_OK == 
493                    GNUNET_CONFIGURATION_get_value_string (ret->env->cfg,
494                                                           "datastore-mysql", "DATABASE", 
495                                                           &mysql_dbname));
496   else
497     mysql_dbname = GNUNET_strdup ("gnunet");
498   mysql_user = NULL;
499   if (GNUNET_YES == GNUNET_CONFIGURATION_have_value (ret->env->cfg,
500                                                      "datastore-mysql", "USER"))
501     {
502       GNUNET_assert (GNUNET_OK == 
503                     GNUNET_CONFIGURATION_get_value_string (ret->env->cfg,
504                                                            "datastore-mysql", "USER", 
505                                                            &mysql_user));
506     }
507   mysql_password = NULL;
508   if (GNUNET_YES == GNUNET_CONFIGURATION_have_value (ret->env->cfg,
509                                                      "datastore-mysql", "PASSWORD"))
510     {
511       GNUNET_assert (GNUNET_OK ==
512                     GNUNET_CONFIGURATION_get_value_string (ret->env->cfg,
513                                                            "datastore-mysql", "PASSWORD",
514                                                            &mysql_password));
515     }
516   mysql_server = NULL;
517   if (GNUNET_YES == GNUNET_CONFIGURATION_have_value (ret->env->cfg,
518                                                      "datastore-mysql", "HOST"))
519     {
520       GNUNET_assert (GNUNET_OK == 
521                     GNUNET_CONFIGURATION_get_value_string (ret->env->cfg,
522                                                            "datastore-mysql", "HOST", 
523                                                            &mysql_server));
524     }
525   mysql_port = 0;
526   if (GNUNET_YES == GNUNET_CONFIGURATION_have_value (ret->env->cfg,
527                                                      "datastore-mysql", "PORT"))
528     {
529       GNUNET_assert (GNUNET_OK ==
530                     GNUNET_CONFIGURATION_get_value_number (ret->env->cfg, "datastore-mysql",
531                                                            "PORT", &mysql_port));
532     }
533
534   GNUNET_assert (mysql_dbname != NULL);
535   mysql_real_connect (ret->dbf, mysql_server, mysql_user, mysql_password,
536                       mysql_dbname, (unsigned int) mysql_port, NULL,
537                       CLIENT_IGNORE_SIGPIPE);
538   GNUNET_free_non_null (mysql_server);
539   GNUNET_free_non_null (mysql_user);
540   GNUNET_free_non_null (mysql_password);
541   GNUNET_free (mysql_dbname);
542   if (mysql_error (ret->dbf)[0])
543     {
544       LOG_MYSQL (GNUNET_ERROR_TYPE_ERROR,
545                  "mysql_real_connect", ret);
546       return GNUNET_SYSERR;
547     }
548   return GNUNET_OK;
549 }
550
551
552 /**
553  * Run the given MySQL statement.
554  *
555  * @return GNUNET_OK on success, GNUNET_SYSERR on error
556  */
557 static int
558 run_statement (struct Plugin *plugin,
559                const char *statement)
560 {
561   if ((NULL == plugin->dbf) && (GNUNET_OK != iopen (plugin)))
562     return GNUNET_SYSERR;
563   mysql_query (plugin->dbf, statement);
564   if (mysql_error (plugin->dbf)[0])
565     {
566       LOG_MYSQL (GNUNET_ERROR_TYPE_ERROR,
567                  "mysql_query", plugin);
568       iclose (plugin);
569       return GNUNET_SYSERR;
570     }
571   return GNUNET_OK;
572 }
573
574
575 /**
576  * Create a prepared statement.
577  *
578  * @return NULL on error
579  */
580 static struct GNUNET_MysqlStatementHandle *
581 prepared_statement_create (struct Plugin *plugin, 
582                            const char *statement)
583 {
584   struct GNUNET_MysqlStatementHandle *ret;
585
586   ret = GNUNET_malloc (sizeof (struct GNUNET_MysqlStatementHandle));
587   ret->query = GNUNET_strdup (statement);
588   GNUNET_CONTAINER_DLL_insert (plugin->shead,
589                                plugin->stail,
590                                ret);
591   return ret;
592 }
593
594
595 /**
596  * Prepare a statement for running.
597  *
598  * @return GNUNET_OK on success
599  */
600 static int
601 prepare_statement (struct Plugin *plugin, 
602                    struct GNUNET_MysqlStatementHandle *ret)
603 {
604   if (GNUNET_YES == ret->valid)
605     return GNUNET_OK;
606   if ((NULL == plugin->dbf) && 
607       (GNUNET_OK != iopen (plugin)))
608     return GNUNET_SYSERR;
609   ret->statement = mysql_stmt_init (plugin->dbf);
610   if (ret->statement == NULL)
611     {
612       iclose (plugin);
613       return GNUNET_SYSERR;
614     }
615   if (mysql_stmt_prepare (ret->statement, 
616                           ret->query,
617                           strlen (ret->query)))
618     {
619       LOG_MYSQL (GNUNET_ERROR_TYPE_ERROR,
620                  "mysql_stmt_prepare", 
621                  plugin);
622       mysql_stmt_close (ret->statement);
623       ret->statement = NULL;
624       iclose (plugin);
625       return GNUNET_SYSERR;
626     }
627   ret->valid = GNUNET_YES;
628   return GNUNET_OK;
629
630 }
631
632
633 /**
634  * Bind the parameters for the given MySQL statement
635  * and run it.
636  *
637  * @param s statement to bind and run
638  * @param ap arguments for the binding
639  * @return GNUNET_SYSERR on error, GNUNET_OK on success
640  */
641 static int
642 init_params (struct Plugin *plugin,
643              struct GNUNET_MysqlStatementHandle *s,
644              va_list ap)
645 {
646   MYSQL_BIND qbind[MAX_PARAM];
647   unsigned int pc;
648   unsigned int off;
649   enum enum_field_types ft;
650
651   pc = mysql_stmt_param_count (s->statement);
652   if (pc > MAX_PARAM)
653     {
654       /* increase internal constant! */
655       GNUNET_break (0);
656       return GNUNET_SYSERR;
657     }
658   memset (qbind, 0, sizeof (qbind));
659   off = 0;
660   ft = 0;
661   while ((pc > 0) && (-1 != (ft = va_arg (ap, enum enum_field_types))))
662     {
663       qbind[off].buffer_type = ft;
664       switch (ft)
665         {
666         case MYSQL_TYPE_FLOAT:
667           qbind[off].buffer = va_arg (ap, float *);
668           break;
669         case MYSQL_TYPE_LONGLONG:
670           qbind[off].buffer = va_arg (ap, unsigned long long *);
671           qbind[off].is_unsigned = va_arg (ap, int);
672           break;
673         case MYSQL_TYPE_LONG:
674           qbind[off].buffer = va_arg (ap, unsigned int *);
675           qbind[off].is_unsigned = va_arg (ap, int);
676           break;
677         case MYSQL_TYPE_VAR_STRING:
678         case MYSQL_TYPE_STRING:
679         case MYSQL_TYPE_BLOB:
680           qbind[off].buffer = va_arg (ap, void *);
681           qbind[off].buffer_length = va_arg (ap, unsigned long);
682           qbind[off].length = va_arg (ap, unsigned long *);
683           break;
684         default:
685           /* unsupported type */
686           GNUNET_break (0);
687           return GNUNET_SYSERR;
688         }
689       pc--;
690       off++;
691     }
692   if (!((pc == 0) && (ft != -1) && (va_arg (ap, int) == -1)))
693     {
694       GNUNET_break (0);
695       return GNUNET_SYSERR;
696     }
697   if (mysql_stmt_bind_param (s->statement, qbind))
698     {
699       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
700                   _("`%s' failed at %s:%d with error: %s\n"),
701                   "mysql_stmt_bind_param",
702                   __FILE__, __LINE__, mysql_stmt_error (s->statement));
703       iclose (plugin);
704       return GNUNET_SYSERR;
705     }
706   if (mysql_stmt_execute (s->statement))
707     {
708       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
709                   _("`%s' failed at %s:%d with error: %s\n"),
710                   "mysql_stmt_execute",
711                   __FILE__, __LINE__, mysql_stmt_error (s->statement));
712       iclose (plugin);
713       return GNUNET_SYSERR;
714     }
715   return GNUNET_OK;
716 }
717
718 /**
719  * Type of a callback that will be called for each
720  * data set returned from MySQL.
721  *
722  * @param cls user-defined argument
723  * @param num_values number of elements in values
724  * @param values values returned by MySQL
725  * @return GNUNET_OK to continue iterating, GNUNET_SYSERR to abort
726  */
727 typedef int (*GNUNET_MysqlDataProcessor) (void *cls,
728                                           unsigned int num_values,
729                                           MYSQL_BIND * values);
730
731
732 /**
733  * Run a prepared SELECT statement.
734  *
735  * @param result_size number of elements in results array
736  * @param results pointer to already initialized MYSQL_BIND
737  *        array (of sufficient size) for passing results
738  * @param processor function to call on each result
739  * @param processor_cls extra argument to processor
740  * @param ... pairs and triplets of "MYSQL_TYPE_XXX" keys and their respective
741  *        values (size + buffer-reference for pointers); terminated
742  *        with "-1"
743  * @return GNUNET_SYSERR on error, otherwise
744  *         the number of successfully affected (or queried) rows
745  */
746 static int
747 prepared_statement_run_select (struct Plugin *plugin,
748                                struct GNUNET_MysqlStatementHandle
749                                *s,
750                                unsigned int result_size,
751                                MYSQL_BIND * results,
752                                GNUNET_MysqlDataProcessor
753                                processor, void *processor_cls,
754                                ...)
755 {
756   va_list ap;
757   int ret;
758   unsigned int rsize;
759   int total;
760
761   if (GNUNET_OK != prepare_statement (plugin, s))
762     {
763       GNUNET_break (0);
764       return GNUNET_SYSERR;
765     }
766   va_start (ap, processor_cls);
767   if (GNUNET_OK != init_params (plugin, s, ap))
768     {
769       GNUNET_break (0);
770       va_end (ap);
771       return GNUNET_SYSERR;
772     }
773   va_end (ap);
774   rsize = mysql_stmt_field_count (s->statement);
775   if (rsize > result_size)
776     {
777       GNUNET_break (0);
778       return GNUNET_SYSERR;
779     }
780   if (mysql_stmt_bind_result (s->statement, results))
781     {
782       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
783                   _("`%s' failed at %s:%d with error: %s\n"),
784                   "mysql_stmt_bind_result",
785                   __FILE__, __LINE__, mysql_stmt_error (s->statement));
786       iclose (plugin);
787       return GNUNET_SYSERR;
788     }
789
790   total = 0;
791   while (1)
792     {
793       ret = mysql_stmt_fetch (s->statement);
794       if (ret == MYSQL_NO_DATA)
795         break;
796       if (ret != 0)
797         {
798           GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
799                       _("`%s' failed at %s:%d with error: %s\n"),
800                       "mysql_stmt_fetch",
801                       __FILE__, __LINE__, mysql_stmt_error (s->statement));
802           iclose (plugin);
803           return GNUNET_SYSERR;
804         }
805       if (processor != NULL)
806         if (GNUNET_OK != processor (processor_cls, rsize, results))
807           break;
808       total++;
809     }
810   mysql_stmt_reset (s->statement);
811   return total;
812 }
813
814
815 /**
816  * Run a prepared statement that does NOT produce results.
817  *
818  * @param ... pairs and triplets of "MYSQL_TYPE_XXX" keys and their respective
819  *        values (size + buffer-reference for pointers); terminated
820  *        with "-1"
821  * @param insert_id NULL or address where to store the row ID of whatever
822  *        was inserted (only for INSERT statements!)
823  * @return GNUNET_SYSERR on error, otherwise
824  *         the number of successfully affected rows
825  */
826 static int
827 prepared_statement_run (struct Plugin *plugin,
828                         struct GNUNET_MysqlStatementHandle *s,
829                         unsigned long long *insert_id, ...)
830 {
831   va_list ap;
832   int affected;
833
834   if (GNUNET_OK != prepare_statement (plugin, s))
835     return GNUNET_SYSERR;
836   va_start (ap, insert_id);
837   if (GNUNET_OK != init_params (plugin, s, ap))
838     {
839       va_end (ap);
840       return GNUNET_SYSERR;
841     }
842   va_end (ap);
843   affected = mysql_stmt_affected_rows (s->statement);
844   if (NULL != insert_id)
845     *insert_id = (unsigned long long) mysql_stmt_insert_id (s->statement);
846   mysql_stmt_reset (s->statement);
847   return affected;
848 }
849
850
851 /**
852  * Delete an value from the gn072 table.
853  *
854  * @param vkey vkey identifying the value to delete
855  * @return GNUNET_OK on success, GNUNET_NO if no such value exists, GNUNET_SYSERR on error
856  */
857 static int
858 do_delete_value (struct Plugin *plugin,
859                  unsigned long long vkey)
860 {
861   int ret;
862
863 #if DEBUG_MYSQL
864   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
865               "Deleting value %llu from gn072 table\n",
866               vkey);
867 #endif
868   ret = prepared_statement_run (plugin,
869                                 plugin->delete_value,
870                                 NULL,
871                                 MYSQL_TYPE_LONGLONG,
872                                 &vkey, GNUNET_YES, -1);
873   if (ret > 0)
874     {
875       ret = GNUNET_OK;
876     }
877   else
878     {
879       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
880                   "Deleting value %llu from gn072 table failed\n",
881                   vkey);
882     }
883   return ret;
884 }
885
886 /**
887  * Insert a value into the gn072 table.
888  *
889  * @param value the value to insert
890  * @param size size of the value
891  * @param vkey vkey identifying the value henceforth (set)
892  * @return GNUNET_OK on success, GNUNET_SYSERR on error
893  */
894 static int
895 do_insert_value (struct Plugin *plugin,
896                  const void *value, unsigned int size,
897                  unsigned long long *vkey)
898 {
899   unsigned long length = size;
900   int ret;
901
902   ret = prepared_statement_run (plugin,
903                                 plugin->insert_value,
904                                 vkey,
905                                 MYSQL_TYPE_BLOB,
906                                 value, length, &length, -1);
907   if (ret == GNUNET_OK)
908     {
909 #if DEBUG_MYSQL
910       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
911                   "Inserted value number %llu with length %u into gn072 table\n",
912                   *vkey,
913                   size);
914 #endif
915     }
916   else
917     {
918       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
919                   "Failed to insert %u byte value into gn072 table\n",
920                   size);
921     }
922   return ret;
923 }
924
925 /**
926  * Delete an entry from the gn090 table.
927  *
928  * @param vkey vkey identifying the entry to delete
929  * @return GNUNET_OK on success, GNUNET_NO if no such value exists, GNUNET_SYSERR on error
930  */
931 static int
932 do_delete_entry_by_vkey (struct Plugin *plugin,
933                          unsigned long long vkey)
934 {
935   int ret;
936
937 #if DEBUG_MYSQL
938   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
939               "Deleting value %llu from gn090 table\n",
940               vkey);
941 #endif
942   ret = prepared_statement_run (plugin,
943                                 plugin->delete_entry_by_vkey,
944                                 NULL,
945                                 MYSQL_TYPE_LONGLONG,
946                                 &vkey, GNUNET_YES, -1);
947   if (ret > 0)
948     {
949       ret = GNUNET_OK;
950     }
951   else
952     {
953       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
954                   "Deleting value %llu from gn090 table failed\n",
955                   vkey);
956     }
957   return ret;
958 }
959
960 static int
961 return_ok (void *cls, unsigned int num_values, MYSQL_BIND * values)
962 {
963   return GNUNET_OK;
964 }
965
966
967 static int
968 iterator_helper_prepare (void *cls,
969                          struct NextRequestClosure *nrc)
970 {
971   struct Plugin *plugin;
972   int ret;
973
974   if (nrc == NULL)
975     return GNUNET_NO;
976   plugin = nrc->plugin;
977   ret = GNUNET_SYSERR;
978   switch (nrc->iter_select)
979     {
980     case 0:
981     case 1:
982       ret = prepared_statement_run_select (plugin,
983                                            plugin->iter[nrc->iter_select],
984                                            6,
985                                            nrc->rbind,
986                                            &return_ok,
987                                            NULL,
988                                            MYSQL_TYPE_LONG,
989                                            &nrc->last_prio,
990                                            GNUNET_YES,
991                                            MYSQL_TYPE_LONGLONG,
992                                            &nrc->last_vkey,
993                                            GNUNET_YES,
994                                            MYSQL_TYPE_LONG,
995                                            &nrc->last_prio,
996                                            GNUNET_YES,
997                                            MYSQL_TYPE_LONGLONG,
998                                            &nrc->last_vkey,
999                                            GNUNET_YES, -1);
1000       break;
1001     case 2:
1002       ret = prepared_statement_run_select (plugin,
1003                                            plugin->iter[nrc->iter_select],
1004                                            6,
1005                                            nrc->rbind,
1006                                            &return_ok,
1007                                            NULL,
1008                                            MYSQL_TYPE_LONGLONG,
1009                                            &nrc->last_expire,
1010                                            GNUNET_YES,
1011                                            MYSQL_TYPE_LONGLONG,
1012                                            &nrc->last_vkey,
1013                                            GNUNET_YES,
1014                                            MYSQL_TYPE_LONGLONG,
1015                                            &nrc->last_expire,
1016                                            GNUNET_YES,
1017                                            MYSQL_TYPE_LONGLONG,
1018                                            &nrc->last_vkey,
1019                                            GNUNET_YES, -1);
1020       break;
1021     case 3:
1022       ret = prepared_statement_run_select (plugin,
1023                                            plugin->iter[nrc->iter_select],
1024                                            6,
1025                                            nrc->rbind,
1026                                            &return_ok,
1027                                            NULL,
1028                                            MYSQL_TYPE_LONGLONG,
1029                                            &nrc->last_expire,
1030                                            GNUNET_YES,
1031                                            MYSQL_TYPE_LONGLONG,
1032                                            &nrc->last_vkey,
1033                                            GNUNET_YES,
1034                                            MYSQL_TYPE_LONGLONG,
1035                                            &nrc->now.value,
1036                                            GNUNET_YES,
1037                                            MYSQL_TYPE_LONGLONG,
1038                                            &nrc->last_expire,
1039                                            GNUNET_YES,
1040                                            MYSQL_TYPE_LONGLONG,
1041                                            &nrc->last_vkey,
1042                                            GNUNET_YES,
1043                                            MYSQL_TYPE_LONGLONG,
1044                                            &nrc->now.value,
1045                                            GNUNET_YES, -1);
1046       break;
1047     default:
1048       GNUNET_assert (0);
1049     }
1050   return ret;
1051 }
1052
1053
1054 /**
1055  * Continuation of "mysql_next_request".
1056  *
1057  * @param next_cls the next context
1058  * @param tc the task context (unused)
1059  */
1060 static void 
1061 mysql_next_request_cont (void *next_cls,
1062                           const struct GNUNET_SCHEDULER_TaskContext *tc)
1063 {
1064   struct NextRequestClosure *nrc = next_cls;
1065   struct Plugin *plugin;
1066   int ret;
1067   unsigned int type;
1068   unsigned int priority;
1069   unsigned int anonymity;
1070   unsigned long long exp;
1071   unsigned long long vkey;
1072   unsigned long hashSize;
1073   GNUNET_HashCode key;
1074   struct GNUNET_TIME_Absolute expiration;
1075   unsigned long length;
1076   MYSQL_BIND *rbind; /* size 7 */
1077   MYSQL_BIND dbind[1];
1078   char datum[GNUNET_SERVER_MAX_MESSAGE_SIZE];
1079
1080   plugin = nrc->plugin;
1081   plugin->next_task = GNUNET_SCHEDULER_NO_TASK;
1082   plugin->next_task_nc = NULL;
1083
1084  AGAIN: 
1085   GNUNET_assert (nrc->plugin->next_task == GNUNET_SCHEDULER_NO_TASK);
1086   nrc->now = GNUNET_TIME_absolute_get ();
1087   hashSize = sizeof (GNUNET_HashCode);
1088   memset (nrc->rbind, 0, sizeof (nrc->rbind));
1089   rbind = nrc->rbind;
1090   rbind[0].buffer_type = MYSQL_TYPE_LONG;
1091   rbind[0].buffer = &type;
1092   rbind[0].is_unsigned = 1;
1093   rbind[1].buffer_type = MYSQL_TYPE_LONG;
1094   rbind[1].buffer = &priority;
1095   rbind[1].is_unsigned = 1;
1096   rbind[2].buffer_type = MYSQL_TYPE_LONG;
1097   rbind[2].buffer = &anonymity;
1098   rbind[2].is_unsigned = 1;
1099   rbind[3].buffer_type = MYSQL_TYPE_LONGLONG;
1100   rbind[3].buffer = &exp;
1101   rbind[3].is_unsigned = 1;
1102   rbind[4].buffer_type = MYSQL_TYPE_BLOB;
1103   rbind[4].buffer = &key;
1104   rbind[4].buffer_length = hashSize;
1105   rbind[4].length = &hashSize;
1106   rbind[5].buffer_type = MYSQL_TYPE_LONGLONG;
1107   rbind[5].buffer = &vkey;
1108   rbind[5].is_unsigned = GNUNET_YES;
1109
1110   if ( (GNUNET_YES == nrc->end_it) ||
1111        (GNUNET_OK != nrc->prep (nrc->prep_cls,
1112                                 nrc)))
1113     goto END_SET;
1114   GNUNET_assert (nrc->plugin->next_task == GNUNET_SCHEDULER_NO_TASK);
1115   nrc->last_vkey = vkey;
1116   nrc->last_prio = priority;
1117   nrc->last_expire = exp;
1118   if ( (rbind[4].buffer_length != sizeof (GNUNET_HashCode)) ||
1119        (hashSize != sizeof (GNUNET_HashCode)) )
1120     {
1121       GNUNET_break (0);
1122       goto END_SET;
1123     }     
1124 #if DEBUG_MYSQL
1125   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1126               "Found value %llu with prio %u, anon %u, expire %llu selecting from gn090 table\n",
1127               vkey,           
1128               priority,
1129               anonymity,
1130               exp);
1131 #endif
1132   /* now do query on gn072 */
1133   length = sizeof (datum);
1134   memset (dbind, 0, sizeof (dbind));
1135   dbind[0].buffer_type = MYSQL_TYPE_BLOB;
1136   dbind[0].buffer_length = length;
1137   dbind[0].length = &length;
1138   dbind[0].buffer = datum;
1139   ret = prepared_statement_run_select (plugin,
1140                                        plugin->select_value,
1141                                        1,
1142                                        dbind,
1143                                        &return_ok,
1144                                        NULL,
1145                                        MYSQL_TYPE_LONGLONG,
1146                                        &vkey, GNUNET_YES, -1);
1147   GNUNET_break (ret <= 1);     /* should only have one rbind! */
1148   if (ret > 0)
1149     ret = GNUNET_OK;
1150   if (ret != GNUNET_OK) 
1151     {
1152       GNUNET_break (0);
1153       GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 
1154                   _("Failed to obtain value %llu from table `%s'\n"),
1155                   vkey,
1156                   "gn072");
1157       goto AGAIN;
1158     }
1159   GNUNET_break (length <= sizeof(datum));
1160 #if DEBUG_MYSQL
1161   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1162               "Calling iterator with value `%s' number %llu of size %u with type %u, priority %u, anonymity %u and expiration %llu\n",
1163               GNUNET_h2s (&key),
1164               vkey,           
1165               length,
1166               type,
1167               priority,
1168               anonymity,
1169               exp);
1170 #endif
1171   GNUNET_assert (nrc->plugin->next_task == GNUNET_SCHEDULER_NO_TASK);
1172   expiration.value = exp;
1173   ret = nrc->dviter (nrc->dviter_cls,
1174                      nrc,
1175                      &key,
1176                      length,
1177                      datum,
1178                      type,
1179                      priority,
1180                      anonymity,
1181                      expiration,
1182                      vkey);
1183   if (ret == GNUNET_SYSERR)
1184     {
1185       nrc->end_it = GNUNET_YES;
1186       return;
1187     }
1188   if (ret == GNUNET_NO)
1189     {
1190       do_delete_value (plugin, vkey);
1191       do_delete_entry_by_vkey (plugin, vkey);
1192       plugin->content_size -= length;
1193     }
1194   return;
1195  END_SET:
1196   /* call dviter with "end of set" */
1197   GNUNET_assert (nrc->plugin->next_task == GNUNET_SCHEDULER_NO_TASK);
1198   nrc->dviter (nrc->dviter_cls, 
1199                NULL, NULL, 0, NULL, 0, 0, 0, 
1200                GNUNET_TIME_UNIT_ZERO_ABS, 0);
1201   GNUNET_assert (nrc->plugin->next_task == GNUNET_SCHEDULER_NO_TASK);
1202   nrc->prep (nrc->prep_cls, NULL);
1203   GNUNET_assert (nrc->plugin->next_task == GNUNET_SCHEDULER_NO_TASK);
1204   GNUNET_free (nrc);
1205 }
1206
1207
1208 /**
1209  * Function invoked on behalf of a "PluginIterator"
1210  * asking the database plugin to call the iterator
1211  * with the next item.
1212  *
1213  * @param next_cls whatever argument was given
1214  *        to the PluginIterator as "next_cls".
1215  * @param end_it set to GNUNET_YES if we
1216  *        should terminate the iteration early
1217  *        (iterator should be still called once more
1218  *         to signal the end of the iteration).
1219  */
1220 static void 
1221 mysql_plugin_next_request (void *next_cls,
1222                            int end_it)
1223 {
1224   struct NextRequestClosure *nrc = next_cls;
1225
1226   if (GNUNET_YES == end_it)
1227     nrc->end_it = GNUNET_YES;
1228   nrc->plugin->next_task_nc = nrc;
1229   nrc->plugin->next_task = GNUNET_SCHEDULER_add_now (nrc->plugin->env->sched,
1230                                                      &mysql_next_request_cont,
1231                                                      nrc);
1232 }  
1233
1234
1235 /**
1236  * Iterate over the items in the datastore
1237  * using the given query to select and order
1238  * the items.
1239  *
1240  * @param type entries of which type should be considered?
1241  *        Use 0 for any type.
1242  * @param iter never NULL
1243  * @param is_asc are we using ascending order?
1244  */
1245 static void
1246 iterateHelper (struct Plugin *plugin,
1247                unsigned int type,
1248                int is_asc,
1249                unsigned int iter_select, 
1250                PluginIterator dviter,
1251                void *dviter_cls)
1252 {
1253   struct NextRequestClosure *nrc;
1254
1255   nrc = GNUNET_malloc (sizeof (struct NextRequestClosure));
1256   nrc->plugin = plugin;
1257   nrc->type = type;  
1258   nrc->iter_select = iter_select;
1259   nrc->dviter = dviter;
1260   nrc->dviter_cls = dviter_cls;
1261   nrc->prep = &iterator_helper_prepare;
1262   if (is_asc)
1263     {
1264       nrc->last_prio = 0;
1265       nrc->last_vkey = 0;
1266       nrc->last_expire = 0;
1267     }
1268   else
1269     {
1270       nrc->last_prio = 0x7FFFFFFFL;
1271       nrc->last_vkey = 0x7FFFFFFFFFFFFFFFLL; /* MySQL only supports 63 bits */
1272       nrc->last_expire = 0x7FFFFFFFFFFFFFFFLL;       /* MySQL only supports 63 bits */
1273     }
1274   mysql_plugin_next_request (nrc, GNUNET_NO);
1275 }
1276
1277
1278 /**
1279  * Get an estimate of how much space the database is
1280  * currently using.
1281  *
1282  * @param cls our "struct Plugin*"
1283  * @return number of bytes used on disk
1284  */
1285 static unsigned long long
1286 mysql_plugin_get_size (void *cls)
1287 {
1288   struct Plugin *plugin = cls;
1289   return plugin->content_size;
1290 }
1291
1292
1293 /**
1294  * Store an item in the datastore.
1295  *
1296  * @param cls closure
1297  * @param key key for the item
1298  * @param size number of bytes in data
1299  * @param data content stored
1300  * @param type type of the content
1301  * @param priority priority of the content
1302  * @param anonymity anonymity-level for the content
1303  * @param expiration expiration time for the content
1304  * @param msg set to error message
1305  * @return GNUNET_OK on success
1306  */
1307 static int
1308 mysql_plugin_put (void *cls,
1309                   const GNUNET_HashCode * key,
1310                   uint32_t size,
1311                   const void *data,
1312                   enum GNUNET_BLOCK_Type type,
1313                   uint32_t priority,
1314                   uint32_t anonymity,
1315                   struct GNUNET_TIME_Absolute expiration,
1316                   char **msg)
1317 {
1318   struct Plugin *plugin = cls;
1319   unsigned int itype = type;
1320   unsigned int ipriority = priority;
1321   unsigned int ianonymity = anonymity;
1322   unsigned long long lexpiration = expiration.value;
1323   unsigned long hashSize;
1324   unsigned long hashSize2;
1325   unsigned long long vkey;
1326   GNUNET_HashCode vhash;
1327
1328   if (size > MAX_DATUM_SIZE)
1329     {
1330       GNUNET_break (0);
1331       return GNUNET_SYSERR;
1332     }
1333   hashSize = sizeof (GNUNET_HashCode);
1334   hashSize2 = sizeof (GNUNET_HashCode);
1335   GNUNET_CRYPTO_hash (data, size, &vhash);
1336   if (GNUNET_OK != do_insert_value (plugin,
1337                                     data, size, &vkey))
1338     return GNUNET_SYSERR;
1339   if (GNUNET_OK !=
1340       prepared_statement_run (plugin,
1341                               plugin->insert_entry,
1342                               NULL,
1343                               MYSQL_TYPE_LONG,
1344                               &itype,
1345                               GNUNET_YES,
1346                               MYSQL_TYPE_LONG,
1347                               &ipriority,
1348                               GNUNET_YES,
1349                               MYSQL_TYPE_LONG,
1350                               &ianonymity,
1351                               GNUNET_YES,
1352                               MYSQL_TYPE_LONGLONG,
1353                               &lexpiration,
1354                               GNUNET_YES,
1355                               MYSQL_TYPE_BLOB,
1356                               key,
1357                               hashSize,
1358                               &hashSize,
1359                               MYSQL_TYPE_BLOB,
1360                               &vhash,
1361                               hashSize2,
1362                               &hashSize2,
1363                               MYSQL_TYPE_LONGLONG,
1364                               &vkey, GNUNET_YES, -1))
1365     {
1366       do_delete_value (plugin, vkey);
1367       return GNUNET_SYSERR;
1368     }
1369 #if DEBUG_MYSQL
1370   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1371               "Inserted value `%s' number %llu with size %u into gn090 table\n",
1372               GNUNET_h2s (key),
1373               vkey,
1374               (unsigned int) size);
1375 #endif
1376   plugin->content_size += size;
1377   return GNUNET_OK;
1378 }
1379
1380
1381 /**
1382  * Select a subset of the items in the datastore and call
1383  * the given iterator for each of them.
1384  *
1385  * @param cls our "struct Plugin*"
1386  * @param type entries of which type should be considered?
1387  *        Use 0 for any type.
1388  * @param iter function to call on each matching value;
1389  *        will be called once with a NULL value at the end
1390  * @param iter_cls closure for iter
1391  */
1392 static void
1393 mysql_plugin_iter_low_priority (void *cls,
1394                                 enum GNUNET_BLOCK_Type type,
1395                                 PluginIterator iter,
1396                                 void *iter_cls)
1397 {
1398   struct Plugin *plugin = cls;
1399   iterateHelper (plugin, type, GNUNET_YES, 
1400                  0, iter, iter_cls); 
1401 }
1402
1403
1404 struct GetContext
1405 {
1406   GNUNET_HashCode key;
1407   GNUNET_HashCode vhash;
1408
1409   unsigned int prio;
1410   unsigned int anonymity;
1411   unsigned long long expiration;
1412   unsigned long long vkey;
1413   unsigned long long total;
1414   int off;
1415   int count;
1416   int have_vhash;
1417 };
1418
1419
1420 static int
1421 get_statement_prepare (void *cls,
1422                        struct NextRequestClosure *nrc)
1423 {
1424   struct GetContext *gc = cls;
1425   struct Plugin *plugin;
1426   int ret;
1427   unsigned int limit_off;
1428   unsigned long hashSize;
1429
1430   if (NULL == nrc)
1431     {
1432       GNUNET_free (gc);
1433       return GNUNET_NO;
1434     }
1435   if (gc->count == gc->total)
1436     return GNUNET_NO;
1437   plugin = nrc->plugin;
1438   hashSize = sizeof (GNUNET_HashCode);
1439   if (gc->count + gc->off == gc->total)
1440     nrc->last_vkey = 0;          /* back to start */
1441   if (gc->count == 0)
1442     limit_off = gc->off;
1443   else
1444     limit_off = 0;
1445 #if DEBUG_MYSQL
1446   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1447               "Obtaining result number %d/%lld at offset %d with lvc %llu for GET `%s'\n",
1448               gc->count+1,
1449               gc->total,
1450               limit_off,
1451               nrc->last_vkey,
1452               GNUNET_h2s (&gc->key));  
1453 #endif
1454   if (nrc->type != 0)
1455     {
1456       if (gc->have_vhash)
1457         {
1458           ret =
1459             prepared_statement_run_select
1460             (plugin,
1461              plugin->select_entry_by_hash_vhash_and_type, 6, nrc->rbind, &return_ok,
1462              NULL, MYSQL_TYPE_BLOB, &gc->key, hashSize, &hashSize,
1463              MYSQL_TYPE_BLOB, &gc->vhash, hashSize, &hashSize,
1464              MYSQL_TYPE_LONGLONG, &nrc->last_vkey, GNUNET_YES, MYSQL_TYPE_LONG,
1465              &nrc->type, GNUNET_YES, MYSQL_TYPE_LONG, &limit_off, GNUNET_YES,
1466              -1);
1467         }
1468       else
1469         {
1470           ret =
1471             prepared_statement_run_select
1472             (plugin,
1473              plugin->select_entry_by_hash_and_type, 6, nrc->rbind, &return_ok, NULL,
1474              MYSQL_TYPE_BLOB, &gc->key, hashSize, &hashSize,
1475              MYSQL_TYPE_LONGLONG, &nrc->last_vkey, GNUNET_YES, MYSQL_TYPE_LONG,
1476              &nrc->type, GNUNET_YES, MYSQL_TYPE_LONG, &limit_off, GNUNET_YES,
1477              -1);
1478         }
1479     }
1480   else
1481     {
1482       if (gc->have_vhash)
1483         {
1484           ret =
1485             prepared_statement_run_select
1486             (plugin,
1487              plugin->select_entry_by_hash_and_vhash, 6, nrc->rbind, &return_ok, NULL,
1488              MYSQL_TYPE_BLOB, &gc->key, hashSize, &hashSize, MYSQL_TYPE_BLOB,
1489              &gc->vhash, hashSize, &hashSize, MYSQL_TYPE_LONGLONG,
1490              &nrc->last_vkey, GNUNET_YES, MYSQL_TYPE_LONG, &limit_off,
1491              GNUNET_YES, -1);
1492         }
1493       else
1494         {
1495           ret =
1496             prepared_statement_run_select
1497             (plugin,
1498              plugin->select_entry_by_hash, 6, nrc->rbind, &return_ok, NULL,
1499              MYSQL_TYPE_BLOB, &gc->key, hashSize, &hashSize,
1500              MYSQL_TYPE_LONGLONG, &nrc->last_vkey, GNUNET_YES, MYSQL_TYPE_LONG,
1501              &limit_off, GNUNET_YES, -1);
1502         }
1503     }
1504   gc->count++;
1505   return ret;
1506 }
1507
1508
1509 /**
1510  * Iterate over the results for a particular key
1511  * in the datastore.
1512  *
1513  * @param cls closure
1514  * @param key maybe NULL (to match all entries)
1515  * @param vhash hash of the value, maybe NULL (to
1516  *        match all values that have the right key).
1517  *        Note that for DBlocks there is no difference
1518  *        betwen key and vhash, but for other blocks
1519  *        there may be!
1520  * @param type entries of which type are relevant?
1521  *     Use 0 for any type.
1522  * @param iter function to call on each matching value;
1523  *        will be called once with a NULL value at the end
1524  * @param iter_cls closure for iter
1525  */
1526 static void
1527 mysql_plugin_get (void *cls,
1528                   const GNUNET_HashCode * key,
1529                   const GNUNET_HashCode * vhash,
1530                   enum GNUNET_BLOCK_Type type,
1531                   PluginIterator iter, void *iter_cls)
1532 {
1533   struct Plugin *plugin = cls;
1534   unsigned int itype = type;
1535   int ret;
1536   MYSQL_BIND cbind[1];
1537   struct GetContext *gc;
1538   struct NextRequestClosure *nrc;
1539   long long total;
1540   unsigned long hashSize;
1541
1542   if (iter == NULL) 
1543     return;
1544   if (key == NULL)
1545     {
1546       mysql_plugin_iter_low_priority (plugin,
1547                                       type, 
1548                                       iter, iter_cls);
1549       return;
1550     }
1551   hashSize = sizeof (GNUNET_HashCode);
1552   memset (cbind, 0, sizeof (cbind));
1553   total = -1;
1554   cbind[0].buffer_type = MYSQL_TYPE_LONGLONG;
1555   cbind[0].buffer = &total;
1556   cbind[0].is_unsigned = GNUNET_NO;
1557   if (type != 0)
1558     {
1559       if (vhash != NULL)
1560         {
1561           ret =
1562             prepared_statement_run_select
1563             (plugin,
1564              plugin->count_entry_by_hash_vhash_and_type, 1, cbind, &return_ok, NULL,
1565              MYSQL_TYPE_BLOB, key, hashSize, &hashSize, MYSQL_TYPE_BLOB,
1566              vhash, hashSize, &hashSize, MYSQL_TYPE_LONG, &itype, GNUNET_YES,
1567              -1);
1568         }
1569       else
1570         {
1571           ret =
1572             prepared_statement_run_select
1573             (plugin,
1574              plugin->count_entry_by_hash_and_type, 1, cbind, &return_ok, NULL,
1575              MYSQL_TYPE_BLOB, key, hashSize, &hashSize, MYSQL_TYPE_LONG,
1576              &itype, GNUNET_YES, -1);
1577
1578         }
1579     }
1580   else
1581     {
1582       if (vhash != NULL)
1583         {
1584           ret =
1585             prepared_statement_run_select
1586             (plugin,
1587              plugin->count_entry_by_hash_and_vhash, 1, cbind, &return_ok, NULL,
1588              MYSQL_TYPE_BLOB, key, hashSize, &hashSize, MYSQL_TYPE_BLOB,
1589              vhash, hashSize, &hashSize, -1);
1590
1591         }
1592       else
1593         {
1594           ret =
1595             prepared_statement_run_select (plugin,
1596                                            plugin->count_entry_by_hash,
1597                                            1, cbind, &return_ok,
1598                                            NULL, MYSQL_TYPE_BLOB,
1599                                            key, hashSize,
1600                                            &hashSize, -1);
1601         }
1602     }
1603   if ((ret != GNUNET_OK) || (0 >= total))
1604     {
1605       iter (iter_cls, 
1606             NULL, NULL, 0, NULL, 0, 0, 0, 
1607             GNUNET_TIME_UNIT_ZERO_ABS, 0);
1608       return;
1609     }
1610 #if DEBUG_MYSQL
1611   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1612               "Iterating over %lld results for GET `%s'\n",
1613               total,
1614               GNUNET_h2s (key));
1615 #endif
1616   gc = GNUNET_malloc (sizeof (struct GetContext));
1617   gc->key = *key;
1618   if (vhash != NULL)
1619     {
1620       gc->have_vhash = GNUNET_YES;
1621       gc->vhash = *vhash;
1622     }
1623   gc->total = total;
1624   gc->off = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, total);
1625   
1626
1627   nrc = GNUNET_malloc (sizeof (struct NextRequestClosure));
1628   nrc->plugin = plugin;
1629   nrc->type = type;  
1630   nrc->iter_select = -1;
1631   nrc->dviter = iter;
1632   nrc->dviter_cls = iter_cls;
1633   nrc->prep = &get_statement_prepare;
1634   nrc->prep_cls = gc;
1635   nrc->last_vkey = 0;
1636   mysql_plugin_next_request (nrc, GNUNET_NO);
1637 }
1638
1639
1640 /**
1641  * Update the priority for a particular key in the datastore.  If
1642  * the expiration time in value is different than the time found in
1643  * the datastore, the higher value should be kept.  For the
1644  * anonymity level, the lower value is to be used.  The specified
1645  * priority should be added to the existing priority, ignoring the
1646  * priority in value.
1647  *
1648  * Note that it is possible for multiple values to match this put.
1649  * In that case, all of the respective values are updated.
1650  *
1651  * @param cls our "struct Plugin*"
1652  * @param uid unique identifier of the datum
1653  * @param delta by how much should the priority
1654  *     change?  If priority + delta < 0 the
1655  *     priority should be set to 0 (never go
1656  *     negative).
1657  * @param expire new expiration time should be the
1658  *     MAX of any existing expiration time and
1659  *     this value
1660  * @param msg set to error message
1661  * @return GNUNET_OK on success
1662  */
1663 static int
1664 mysql_plugin_update (void *cls,
1665                      uint64_t uid,
1666                      int delta, 
1667                      struct GNUNET_TIME_Absolute expire,
1668                      char **msg)
1669 {
1670   struct Plugin *plugin = cls;
1671   unsigned long long vkey = uid;
1672   unsigned long long lexpire = expire.value;
1673   int ret;
1674
1675 #if DEBUG_MYSQL
1676   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1677               "Updating value %llu adding %d to priority and maxing exp at %llu\n",
1678               vkey,
1679               delta,
1680               lexpire);
1681 #endif
1682   ret = prepared_statement_run (plugin,
1683                                 plugin->update_entry,
1684                                 NULL,
1685                                 MYSQL_TYPE_LONG,
1686                                 &delta,
1687                                 GNUNET_NO,
1688                                 MYSQL_TYPE_LONGLONG,
1689                                 &lexpire,
1690                                 GNUNET_YES,
1691                                 MYSQL_TYPE_LONGLONG,
1692                                 &lexpire,
1693                                 GNUNET_YES,
1694                                 MYSQL_TYPE_LONGLONG,
1695                                 &vkey,
1696                                 GNUNET_YES, -1);
1697   if (ret != GNUNET_OK)
1698     {
1699       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1700                   "Failed to update value %llu\n",
1701                   vkey);
1702     }
1703   return ret;
1704 }
1705
1706
1707 /**
1708  * Select a subset of the items in the datastore and call
1709  * the given iterator for each of them.
1710  *
1711  * @param cls our "struct Plugin*"
1712  * @param type entries of which type should be considered?
1713  *        Use 0 for any type.
1714  * @param iter function to call on each matching value;
1715  *        will be called once with a NULL value at the end
1716  * @param iter_cls closure for iter
1717  */
1718 static void
1719 mysql_plugin_iter_zero_anonymity (void *cls,
1720                                      enum GNUNET_BLOCK_Type type,
1721                                      PluginIterator iter,
1722                                      void *iter_cls)
1723 {
1724   struct Plugin *plugin = cls;
1725   iterateHelper (plugin, type, GNUNET_NO, 1, iter, iter_cls);
1726 }
1727
1728
1729 /**
1730  * Select a subset of the items in the datastore and call
1731  * the given iterator for each of them.
1732  *
1733  * @param cls our "struct Plugin*"
1734  * @param type entries of which type should be considered?
1735  *        Use 0 for any type.
1736  * @param iter function to call on each matching value;
1737  *        will be called once with a NULL value at the end
1738  * @param iter_cls closure for iter
1739  */
1740 static void
1741 mysql_plugin_iter_ascending_expiration (void *cls,
1742                                         enum GNUNET_BLOCK_Type type,
1743                                         PluginIterator iter,
1744                                         void *iter_cls)
1745 {
1746   struct Plugin *plugin = cls;
1747   iterateHelper (plugin, type, GNUNET_YES, 2, iter, iter_cls);
1748 }
1749
1750
1751 /**
1752  * Select a subset of the items in the datastore and call
1753  * the given iterator for each of them.
1754  *
1755  * @param cls our "struct Plugin*"
1756  * @param type entries of which type should be considered?
1757  *        Use 0 for any type.
1758  * @param iter function to call on each matching value;
1759  *        will be called once with a NULL value at the end
1760  * @param iter_cls closure for iter
1761  */
1762 static void
1763 mysql_plugin_iter_migration_order (void *cls,
1764                                       enum GNUNET_BLOCK_Type type,
1765                                       PluginIterator iter,
1766                                       void *iter_cls)
1767 {
1768   struct Plugin *plugin = cls;
1769   iterateHelper (plugin, 0, GNUNET_NO, 3, iter, iter_cls);
1770 }
1771
1772
1773 /**
1774  * Select a subset of the items in the datastore and call
1775  * the given iterator for each of them.
1776  *
1777  * @param cls our "struct Plugin*"
1778  * @param type entries of which type should be considered?
1779  *        Use 0 for any type.
1780  * @param iter function to call on each matching value;
1781  *        will be called once with a NULL value at the end
1782  * @param iter_cls closure for iter
1783  */
1784 static void
1785 mysql_plugin_iter_all_now (void *cls,
1786                            enum GNUNET_BLOCK_Type type,
1787                            PluginIterator iter,
1788                            void *iter_cls)
1789 {
1790   struct Plugin *plugin = cls;
1791   iterateHelper (plugin, 0, GNUNET_YES, 0, iter, iter_cls);
1792 }
1793
1794
1795 /**
1796  * Drop database.
1797  */
1798 static void 
1799 mysql_plugin_drop (void *cls)
1800 {
1801   struct Plugin *plugin = cls;
1802
1803   if ((GNUNET_OK != run_statement (plugin,
1804                                    "DROP TABLE gn090")) ||
1805       (GNUNET_OK != run_statement (plugin,
1806                                    "DROP TABLE gn072")))
1807     return;                     /* error */
1808   plugin->content_size = 0;
1809 }
1810
1811
1812 /**
1813  * Entry point for the plugin.
1814  *
1815  * @param cls the "struct GNUNET_DATASTORE_PluginEnvironment*"
1816  * @return our "struct Plugin*"
1817  */
1818 void *
1819 libgnunet_plugin_datastore_mysql_init (void *cls)
1820 {
1821   struct GNUNET_DATASTORE_PluginEnvironment *env = cls;
1822   struct GNUNET_DATASTORE_PluginFunctions *api;
1823   struct Plugin *plugin;
1824
1825   plugin = GNUNET_malloc (sizeof (struct Plugin));
1826   plugin->env = env;
1827   plugin->cnffile = get_my_cnf_path (env->cfg);
1828   if (GNUNET_OK != iopen (plugin))
1829     {
1830       iclose (plugin);
1831       GNUNET_free_non_null (plugin->cnffile);
1832       GNUNET_free (plugin);
1833       return NULL;
1834     }
1835 #define MRUNS(a) (GNUNET_OK != run_statement (plugin, a) )
1836 #define PINIT(a,b) (NULL == (a = prepared_statement_create(plugin, b)))
1837   if (MRUNS ("CREATE TABLE IF NOT EXISTS gn090 ("
1838              " type INT(11) UNSIGNED NOT NULL DEFAULT 0,"
1839              " prio INT(11) UNSIGNED NOT NULL DEFAULT 0,"
1840              " anonLevel INT(11) UNSIGNED NOT NULL DEFAULT 0,"
1841              " expire BIGINT UNSIGNED NOT NULL DEFAULT 0,"
1842              " hash BINARY(64) NOT NULL DEFAULT '',"
1843              " vhash BINARY(64) NOT NULL DEFAULT '',"
1844              " vkey BIGINT UNSIGNED NOT NULL DEFAULT 0,"
1845              " INDEX hash (hash(64)),"
1846              " INDEX hash_vhash_vkey (hash(64),vhash(64),vkey),"
1847              " INDEX hash_vkey (hash(64),vkey),"
1848              " INDEX vkey (vkey),"
1849              " INDEX prio (prio,vkey),"
1850              " INDEX expire (expire,vkey,type),"
1851              " INDEX anonLevel (anonLevel,prio,vkey,type)"
1852              ") ENGINE=InnoDB") ||
1853       MRUNS ("CREATE TABLE IF NOT EXISTS gn072 ("
1854              " vkey BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,"
1855              " value BLOB NOT NULL DEFAULT '') ENGINE=MyISAM") ||
1856       MRUNS ("SET AUTOCOMMIT = 1") ||
1857       PINIT (plugin->select_value, SELECT_VALUE) ||
1858       PINIT (plugin->delete_value, DELETE_VALUE) ||
1859       PINIT (plugin->insert_value, INSERT_VALUE) ||
1860       PINIT (plugin->insert_entry, INSERT_ENTRY) ||
1861       PINIT (plugin->delete_entry_by_vkey, DELETE_ENTRY_BY_VKEY) ||
1862       PINIT (plugin->select_entry_by_hash, SELECT_ENTRY_BY_HASH) ||
1863       PINIT (plugin->select_entry_by_hash_and_vhash, SELECT_ENTRY_BY_HASH_AND_VHASH)
1864       || PINIT (plugin->select_entry_by_hash_and_type, SELECT_ENTRY_BY_HASH_AND_TYPE)
1865       || PINIT (plugin->select_entry_by_hash_vhash_and_type,
1866                 SELECT_ENTRY_BY_HASH_VHASH_AND_TYPE)
1867       || PINIT (plugin->count_entry_by_hash, COUNT_ENTRY_BY_HASH)
1868       || PINIT (plugin->count_entry_by_hash_and_vhash, COUNT_ENTRY_BY_HASH_AND_VHASH)
1869       || PINIT (plugin->count_entry_by_hash_and_type, COUNT_ENTRY_BY_HASH_AND_TYPE)
1870       || PINIT (plugin->count_entry_by_hash_vhash_and_type,
1871                 COUNT_ENTRY_BY_HASH_VHASH_AND_TYPE)
1872       || PINIT (plugin->update_entry, UPDATE_ENTRY)
1873       || PINIT (plugin->iter[0], SELECT_IT_LOW_PRIORITY)
1874       || PINIT (plugin->iter[1], SELECT_IT_NON_ANONYMOUS)
1875       || PINIT (plugin->iter[2], SELECT_IT_EXPIRATION_TIME)
1876       || PINIT (plugin->iter[3], SELECT_IT_MIGRATION_ORDER))
1877     {
1878       iclose (plugin);
1879       GNUNET_free_non_null (plugin->cnffile);
1880       GNUNET_free (plugin);
1881       return NULL;
1882     }
1883 #undef PINIT
1884 #undef MRUNS
1885
1886   api = GNUNET_malloc (sizeof (struct GNUNET_DATASTORE_PluginFunctions));
1887   api->cls = plugin;
1888   api->get_size = &mysql_plugin_get_size;
1889   api->put = &mysql_plugin_put;
1890   api->next_request = &mysql_plugin_next_request;
1891   api->get = &mysql_plugin_get;
1892   api->update = &mysql_plugin_update;
1893   api->iter_low_priority = &mysql_plugin_iter_low_priority;
1894   api->iter_zero_anonymity = &mysql_plugin_iter_zero_anonymity;
1895   api->iter_ascending_expiration = &mysql_plugin_iter_ascending_expiration;
1896   api->iter_migration_order = &mysql_plugin_iter_migration_order;
1897   api->iter_all_now = &mysql_plugin_iter_all_now;
1898   api->drop = &mysql_plugin_drop;
1899   GNUNET_log_from (GNUNET_ERROR_TYPE_INFO,
1900                    "mysql", _("Mysql database running\n"));
1901   return api;
1902 }
1903
1904
1905 /**
1906  * Exit point from the plugin.
1907  * @param cls our "struct Plugin*"
1908  * @return always NULL
1909  */
1910 void *
1911 libgnunet_plugin_datastore_mysql_done (void *cls)
1912 {
1913   struct GNUNET_DATASTORE_PluginFunctions *api = cls;
1914   struct Plugin *plugin = api->cls;
1915
1916   iclose (plugin);
1917   if (plugin->next_task != GNUNET_SCHEDULER_NO_TASK)
1918     {
1919       GNUNET_SCHEDULER_cancel (plugin->env->sched,
1920                                plugin->next_task);
1921       plugin->next_task = GNUNET_SCHEDULER_NO_TASK;
1922       plugin->next_task_nc->prep (plugin->next_task_nc->prep_cls, NULL);
1923       GNUNET_free (plugin->next_task_nc);
1924       plugin->next_task_nc = NULL;
1925     }
1926   GNUNET_free_non_null (plugin->cnffile);
1927   GNUNET_free (plugin);
1928   GNUNET_free (api);
1929   mysql_library_end ();
1930   return NULL;
1931 }
1932
1933 /* end of plugin_datastore_mysql.c */