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