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