big scheduler refactoring, expect some issues
[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 != (int) (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) && (-1 != (int) ft) && (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.abs_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.abs_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.abs_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 (&mysql_next_request_cont,
1262                                                      nrc);
1263 }  
1264
1265
1266 /**
1267  * Iterate over the items in the datastore
1268  * using the given query to select and order
1269  * the items.
1270  *
1271  * @param plugin plugin context
1272  * @param type entries of which type should be considered?
1273  * @param iter_select which iterator statement are we using
1274  * @param is_asc are we using ascending order?
1275  * @param dviter function to call on each matching item
1276  * @param dviter_cls closure for dviter
1277  */
1278 static void
1279 iterateHelper (struct Plugin *plugin,
1280                enum GNUNET_BLOCK_Type type,
1281                int is_asc,
1282                unsigned int iter_select, 
1283                PluginIterator dviter,
1284                void *dviter_cls)
1285 {
1286   struct NextRequestClosure *nrc;
1287
1288   nrc = GNUNET_malloc (sizeof (struct NextRequestClosure));
1289   nrc->plugin = plugin;
1290   nrc->type = type;  
1291   nrc->iter_select = iter_select;
1292   nrc->dviter = dviter;
1293   nrc->dviter_cls = dviter_cls;
1294   nrc->prep = &iterator_helper_prepare;
1295   if (is_asc)
1296     {
1297       nrc->last_prio = 0;
1298       nrc->last_vkey = 0;
1299       nrc->last_expire = 0;
1300     }
1301   else
1302     {
1303       nrc->last_prio = 0x7FFFFFFFL;
1304       nrc->last_vkey = 0x7FFFFFFFFFFFFFFFLL; /* MySQL only supports 63 bits */
1305       nrc->last_expire = 0x7FFFFFFFFFFFFFFFLL;       /* MySQL only supports 63 bits */
1306     }
1307   mysql_plugin_next_request (nrc, GNUNET_NO);
1308 }
1309
1310
1311 /**
1312  * Get an estimate of how much space the database is
1313  * currently using.
1314  *
1315  * @param cls our "struct Plugin *"
1316  * @return number of bytes used on disk
1317  */
1318 static unsigned long long
1319 mysql_plugin_get_size (void *cls)
1320 {
1321   struct Plugin *plugin = cls;
1322   MYSQL_BIND cbind[1];
1323   long long total;
1324
1325   memset (cbind, 0, sizeof (cbind));
1326   total = 0;
1327   cbind[0].buffer_type = MYSQL_TYPE_LONGLONG;
1328   cbind[0].buffer = &total;
1329   cbind[0].is_unsigned = GNUNET_NO;
1330   if (GNUNET_OK != 
1331       prepared_statement_run_select (plugin,
1332                                      plugin->get_size,
1333                                      1, cbind, 
1334                                      &return_ok, NULL,
1335                                      -1))
1336     return 0;
1337   return total;
1338 }
1339
1340
1341 /**
1342  * Store an item in the datastore.
1343  *
1344  * @param cls closure
1345  * @param key key for the item
1346  * @param size number of bytes in data
1347  * @param data content stored
1348  * @param type type of the content
1349  * @param priority priority of the content
1350  * @param anonymity anonymity-level for the content
1351  * @param expiration expiration time for the content
1352  * @param msg set to error message
1353  * @return GNUNET_OK on success
1354  */
1355 static int
1356 mysql_plugin_put (void *cls,
1357                   const GNUNET_HashCode * key,
1358                   uint32_t size,
1359                   const void *data,
1360                   enum GNUNET_BLOCK_Type type,
1361                   uint32_t priority,
1362                   uint32_t anonymity,
1363                   struct GNUNET_TIME_Absolute expiration,
1364                   char **msg)
1365 {
1366   struct Plugin *plugin = cls;
1367   unsigned int itype = type;
1368   unsigned int ipriority = priority;
1369   unsigned int ianonymity = anonymity;
1370   unsigned long long lexpiration = expiration.abs_value;
1371   unsigned long hashSize;
1372   unsigned long hashSize2;
1373   unsigned long long vkey;
1374   GNUNET_HashCode vhash;
1375
1376   if (size > MAX_DATUM_SIZE)
1377     {
1378       GNUNET_break (0);
1379       return GNUNET_SYSERR;
1380     }
1381   hashSize = sizeof (GNUNET_HashCode);
1382   hashSize2 = sizeof (GNUNET_HashCode);
1383   GNUNET_CRYPTO_hash (data, size, &vhash);
1384   if (GNUNET_OK != do_insert_value (plugin,
1385                                     data, size, &vkey))
1386     return GNUNET_SYSERR;
1387   if (GNUNET_OK !=
1388       prepared_statement_run (plugin,
1389                               plugin->insert_entry,
1390                               NULL,
1391                               MYSQL_TYPE_LONG,
1392                               &itype,
1393                               GNUNET_YES,
1394                               MYSQL_TYPE_LONG,
1395                               &ipriority,
1396                               GNUNET_YES,
1397                               MYSQL_TYPE_LONG,
1398                               &ianonymity,
1399                               GNUNET_YES,
1400                               MYSQL_TYPE_LONGLONG,
1401                               &lexpiration,
1402                               GNUNET_YES,
1403                               MYSQL_TYPE_BLOB,
1404                               key,
1405                               hashSize,
1406                               &hashSize,
1407                               MYSQL_TYPE_BLOB,
1408                               &vhash,
1409                               hashSize2,
1410                               &hashSize2,
1411                               MYSQL_TYPE_LONGLONG,
1412                               &vkey, GNUNET_YES, -1))
1413     {
1414       do_delete_value (plugin, vkey);
1415       return GNUNET_SYSERR;
1416     }
1417 #if DEBUG_MYSQL
1418   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1419               "Inserted value `%s' number %llu with size %u into gn090 table\n",
1420               GNUNET_h2s (key),
1421               vkey,
1422               (unsigned int) size);
1423 #endif
1424   if (size > 0)
1425     plugin->env->duc (plugin->env->cls,
1426                       size);
1427   return GNUNET_OK;
1428 }
1429
1430
1431 /**
1432  * Select a subset of the items in the datastore and call
1433  * the given iterator for each of them.
1434  *
1435  * @param cls our "struct Plugin*"
1436  * @param type entries of which type should be considered?
1437  *        Use 0 for any type.
1438  * @param iter function to call on each matching value;
1439  *        will be called once with a NULL value at the end
1440  * @param iter_cls closure for iter
1441  */
1442 static void
1443 mysql_plugin_iter_low_priority (void *cls,
1444                                 enum GNUNET_BLOCK_Type type,
1445                                 PluginIterator iter,
1446                                 void *iter_cls)
1447 {
1448   struct Plugin *plugin = cls;
1449   iterateHelper (plugin, type, GNUNET_YES, 
1450                  0, iter, iter_cls); 
1451 }
1452
1453
1454 struct GetContext
1455 {
1456   GNUNET_HashCode key;
1457   GNUNET_HashCode vhash;
1458
1459   unsigned int prio;
1460   unsigned int anonymity;
1461   unsigned long long expiration;
1462   unsigned long long vkey;
1463   unsigned long long total;
1464   int off;
1465   int count;
1466   int have_vhash;
1467 };
1468
1469
1470 static int
1471 get_statement_prepare (void *cls,
1472                        struct NextRequestClosure *nrc)
1473 {
1474   struct GetContext *gc = cls;
1475   struct Plugin *plugin;
1476   int ret;
1477   unsigned int limit_off;
1478   unsigned long hashSize;
1479
1480   if (NULL == nrc)
1481     {
1482       GNUNET_free (gc);
1483       return GNUNET_NO;
1484     }
1485   if (gc->count == gc->total)
1486     return GNUNET_NO;
1487   plugin = nrc->plugin;
1488   hashSize = sizeof (GNUNET_HashCode);
1489   if (gc->count + gc->off == gc->total)
1490     nrc->last_vkey = 0;          /* back to start */
1491   if (gc->count == 0)
1492     limit_off = gc->off;
1493   else
1494     limit_off = 0;
1495 #if DEBUG_MYSQL
1496   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1497               "Obtaining result number %d/%lld at offset %d with lvc %llu for GET `%s'\n",
1498               gc->count+1,
1499               gc->total,
1500               limit_off,
1501               nrc->last_vkey,
1502               GNUNET_h2s (&gc->key));  
1503 #endif
1504   if (nrc->type != 0)
1505     {
1506       if (gc->have_vhash)
1507         {
1508           ret =
1509             prepared_statement_run_select
1510             (plugin,
1511              plugin->select_entry_by_hash_vhash_and_type, 6, nrc->rbind, &return_ok,
1512              NULL, MYSQL_TYPE_BLOB, &gc->key, hashSize, &hashSize,
1513              MYSQL_TYPE_BLOB, &gc->vhash, hashSize, &hashSize,
1514              MYSQL_TYPE_LONGLONG, &nrc->last_vkey, GNUNET_YES, MYSQL_TYPE_LONG,
1515              &nrc->type, GNUNET_YES, MYSQL_TYPE_LONG, &limit_off, GNUNET_YES,
1516              -1);
1517         }
1518       else
1519         {
1520           ret =
1521             prepared_statement_run_select
1522             (plugin,
1523              plugin->select_entry_by_hash_and_type, 6, nrc->rbind, &return_ok, NULL,
1524              MYSQL_TYPE_BLOB, &gc->key, hashSize, &hashSize,
1525              MYSQL_TYPE_LONGLONG, &nrc->last_vkey, GNUNET_YES, MYSQL_TYPE_LONG,
1526              &nrc->type, GNUNET_YES, MYSQL_TYPE_LONG, &limit_off, GNUNET_YES,
1527              -1);
1528         }
1529     }
1530   else
1531     {
1532       if (gc->have_vhash)
1533         {
1534           ret =
1535             prepared_statement_run_select
1536             (plugin,
1537              plugin->select_entry_by_hash_and_vhash, 6, nrc->rbind, &return_ok, NULL,
1538              MYSQL_TYPE_BLOB, &gc->key, hashSize, &hashSize, MYSQL_TYPE_BLOB,
1539              &gc->vhash, hashSize, &hashSize, MYSQL_TYPE_LONGLONG,
1540              &nrc->last_vkey, GNUNET_YES, MYSQL_TYPE_LONG, &limit_off,
1541              GNUNET_YES, -1);
1542         }
1543       else
1544         {
1545           ret =
1546             prepared_statement_run_select
1547             (plugin,
1548              plugin->select_entry_by_hash, 6, nrc->rbind, &return_ok, NULL,
1549              MYSQL_TYPE_BLOB, &gc->key, hashSize, &hashSize,
1550              MYSQL_TYPE_LONGLONG, &nrc->last_vkey, GNUNET_YES, MYSQL_TYPE_LONG,
1551              &limit_off, GNUNET_YES, -1);
1552         }
1553     }
1554   gc->count++;
1555   return ret;
1556 }
1557
1558
1559 /**
1560  * Iterate over the results for a particular key
1561  * in the datastore.
1562  *
1563  * @param cls closure
1564  * @param key maybe NULL (to match all entries)
1565  * @param vhash hash of the value, maybe NULL (to
1566  *        match all values that have the right key).
1567  *        Note that for DBlocks there is no difference
1568  *        betwen key and vhash, but for other blocks
1569  *        there may be!
1570  * @param type entries of which type are relevant?
1571  *     Use 0 for any type.
1572  * @param iter function to call on each matching value;
1573  *        will be called once with a NULL value at the end
1574  * @param iter_cls closure for iter
1575  */
1576 static void
1577 mysql_plugin_get (void *cls,
1578                   const GNUNET_HashCode * key,
1579                   const GNUNET_HashCode * vhash,
1580                   enum GNUNET_BLOCK_Type type,
1581                   PluginIterator iter, void *iter_cls)
1582 {
1583   struct Plugin *plugin = cls;
1584   unsigned int itype = type;
1585   int ret;
1586   MYSQL_BIND cbind[1];
1587   struct GetContext *gc;
1588   struct NextRequestClosure *nrc;
1589   long long total;
1590   unsigned long hashSize;
1591
1592   if (iter == NULL) 
1593     return;
1594   if (key == NULL)
1595     {
1596       mysql_plugin_iter_low_priority (plugin,
1597                                       type, 
1598                                       iter, iter_cls);
1599       return;
1600     }
1601   hashSize = sizeof (GNUNET_HashCode);
1602   memset (cbind, 0, sizeof (cbind));
1603   total = -1;
1604   cbind[0].buffer_type = MYSQL_TYPE_LONGLONG;
1605   cbind[0].buffer = &total;
1606   cbind[0].is_unsigned = GNUNET_NO;
1607   if (type != 0)
1608     {
1609       if (vhash != NULL)
1610         {
1611           ret =
1612             prepared_statement_run_select
1613             (plugin,
1614              plugin->count_entry_by_hash_vhash_and_type, 1, cbind, &return_ok, NULL,
1615              MYSQL_TYPE_BLOB, key, hashSize, &hashSize, MYSQL_TYPE_BLOB,
1616              vhash, hashSize, &hashSize, MYSQL_TYPE_LONG, &itype, GNUNET_YES,
1617              -1);
1618         }
1619       else
1620         {
1621           ret =
1622             prepared_statement_run_select
1623             (plugin,
1624              plugin->count_entry_by_hash_and_type, 1, cbind, &return_ok, NULL,
1625              MYSQL_TYPE_BLOB, key, hashSize, &hashSize, MYSQL_TYPE_LONG,
1626              &itype, GNUNET_YES, -1);
1627
1628         }
1629     }
1630   else
1631     {
1632       if (vhash != NULL)
1633         {
1634           ret =
1635             prepared_statement_run_select
1636             (plugin,
1637              plugin->count_entry_by_hash_and_vhash, 1, cbind, &return_ok, NULL,
1638              MYSQL_TYPE_BLOB, key, hashSize, &hashSize, MYSQL_TYPE_BLOB,
1639              vhash, hashSize, &hashSize, -1);
1640
1641         }
1642       else
1643         {
1644           ret =
1645             prepared_statement_run_select (plugin,
1646                                            plugin->count_entry_by_hash,
1647                                            1, cbind, &return_ok,
1648                                            NULL, MYSQL_TYPE_BLOB,
1649                                            key, hashSize,
1650                                            &hashSize, -1);
1651         }
1652     }
1653   if ((ret != GNUNET_OK) || (0 >= total))
1654     {
1655       iter (iter_cls, 
1656             NULL, NULL, 0, NULL, 0, 0, 0, 
1657             GNUNET_TIME_UNIT_ZERO_ABS, 0);
1658       return;
1659     }
1660 #if DEBUG_MYSQL
1661   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1662               "Iterating over %lld results for GET `%s'\n",
1663               total,
1664               GNUNET_h2s (key));
1665 #endif
1666   gc = GNUNET_malloc (sizeof (struct GetContext));
1667   gc->key = *key;
1668   if (vhash != NULL)
1669     {
1670       gc->have_vhash = GNUNET_YES;
1671       gc->vhash = *vhash;
1672     }
1673   gc->total = total;
1674   gc->off = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, total);
1675   
1676
1677   nrc = GNUNET_malloc (sizeof (struct NextRequestClosure));
1678   nrc->plugin = plugin;
1679   nrc->type = type;  
1680   nrc->iter_select = -1;
1681   nrc->dviter = iter;
1682   nrc->dviter_cls = iter_cls;
1683   nrc->prep = &get_statement_prepare;
1684   nrc->prep_cls = gc;
1685   nrc->last_vkey = 0;
1686   mysql_plugin_next_request (nrc, GNUNET_NO);
1687 }
1688
1689
1690 /**
1691  * Update the priority for a particular key in the datastore.  If
1692  * the expiration time in value is different than the time found in
1693  * the datastore, the higher value should be kept.  For the
1694  * anonymity level, the lower value is to be used.  The specified
1695  * priority should be added to the existing priority, ignoring the
1696  * priority in value.
1697  *
1698  * Note that it is possible for multiple values to match this put.
1699  * In that case, all of the respective values are updated.
1700  *
1701  * @param cls our "struct Plugin*"
1702  * @param uid unique identifier of the datum
1703  * @param delta by how much should the priority
1704  *     change?  If priority + delta < 0 the
1705  *     priority should be set to 0 (never go
1706  *     negative).
1707  * @param expire new expiration time should be the
1708  *     MAX of any existing expiration time and
1709  *     this value
1710  * @param msg set to error message
1711  * @return GNUNET_OK on success
1712  */
1713 static int
1714 mysql_plugin_update (void *cls,
1715                      uint64_t uid,
1716                      int delta, 
1717                      struct GNUNET_TIME_Absolute expire,
1718                      char **msg)
1719 {
1720   struct Plugin *plugin = cls;
1721   unsigned long long vkey = uid;
1722   unsigned long long lexpire = expire.abs_value;
1723   int ret;
1724
1725 #if DEBUG_MYSQL
1726   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1727               "Updating value %llu adding %d to priority and maxing exp at %llu\n",
1728               vkey,
1729               delta,
1730               lexpire);
1731 #endif
1732   ret = prepared_statement_run (plugin,
1733                                 plugin->update_entry,
1734                                 NULL,
1735                                 MYSQL_TYPE_LONG,
1736                                 &delta,
1737                                 GNUNET_NO,
1738                                 MYSQL_TYPE_LONGLONG,
1739                                 &lexpire,
1740                                 GNUNET_YES,
1741                                 MYSQL_TYPE_LONGLONG,
1742                                 &lexpire,
1743                                 GNUNET_YES,
1744                                 MYSQL_TYPE_LONGLONG,
1745                                 &vkey,
1746                                 GNUNET_YES, -1);
1747   if (ret != GNUNET_OK)
1748     {
1749       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1750                   "Failed to update value %llu\n",
1751                   vkey);
1752     }
1753   return ret;
1754 }
1755
1756
1757 /**
1758  * Select a subset of the items in the datastore and call
1759  * the given iterator for each of them.
1760  *
1761  * @param cls our "struct Plugin*"
1762  * @param type entries of which type should be considered?
1763  *        Use 0 for any type.
1764  * @param iter function to call on each matching value;
1765  *        will be called once with a NULL value at the end
1766  * @param iter_cls closure for iter
1767  */
1768 static void
1769 mysql_plugin_iter_zero_anonymity (void *cls,
1770                                      enum GNUNET_BLOCK_Type type,
1771                                      PluginIterator iter,
1772                                      void *iter_cls)
1773 {
1774   struct Plugin *plugin = cls;
1775   iterateHelper (plugin, type, GNUNET_NO, 1, iter, iter_cls);
1776 }
1777
1778
1779 /**
1780  * Select a subset of the items in the datastore and call
1781  * the given iterator for each of them.
1782  *
1783  * @param cls our "struct Plugin*"
1784  * @param type entries of which type should be considered?
1785  *        Use 0 for any type.
1786  * @param iter function to call on each matching value;
1787  *        will be called once with a NULL value at the end
1788  * @param iter_cls closure for iter
1789  */
1790 static void
1791 mysql_plugin_iter_ascending_expiration (void *cls,
1792                                         enum GNUNET_BLOCK_Type type,
1793                                         PluginIterator iter,
1794                                         void *iter_cls)
1795 {
1796   struct Plugin *plugin = cls;
1797   iterateHelper (plugin, type, GNUNET_YES, 2, iter, iter_cls);
1798 }
1799
1800
1801 /**
1802  * Select a subset of the items in the datastore and call
1803  * the given iterator for each of them.
1804  *
1805  * @param cls our "struct Plugin*"
1806  * @param type entries of which type should be considered?
1807  *        Use 0 for any type.
1808  * @param iter function to call on each matching value;
1809  *        will be called once with a NULL value at the end
1810  * @param iter_cls closure for iter
1811  */
1812 static void
1813 mysql_plugin_iter_migration_order (void *cls,
1814                                       enum GNUNET_BLOCK_Type type,
1815                                       PluginIterator iter,
1816                                       void *iter_cls)
1817 {
1818   struct Plugin *plugin = cls;
1819   iterateHelper (plugin, 0, GNUNET_NO, 3, iter, iter_cls);
1820 }
1821
1822
1823 /**
1824  * Select a subset of the items in the datastore and call
1825  * the given iterator for each of them.
1826  *
1827  * @param cls our "struct Plugin*"
1828  * @param type entries of which type should be considered?
1829  *        Use 0 for any type.
1830  * @param iter function to call on each matching value;
1831  *        will be called once with a NULL value at the end
1832  * @param iter_cls closure for iter
1833  */
1834 static void
1835 mysql_plugin_iter_all_now (void *cls,
1836                            enum GNUNET_BLOCK_Type type,
1837                            PluginIterator iter,
1838                            void *iter_cls)
1839 {
1840   struct Plugin *plugin = cls;
1841   iterateHelper (plugin, 0, GNUNET_YES, 0, iter, iter_cls);
1842 }
1843
1844
1845 /**
1846  * Drop database.
1847  */
1848 static void 
1849 mysql_plugin_drop (void *cls)
1850 {
1851   struct Plugin *plugin = cls;
1852
1853   if ((GNUNET_OK != run_statement (plugin,
1854                                    "DROP TABLE gn090")) ||
1855       (GNUNET_OK != run_statement (plugin,
1856                                    "DROP TABLE gn072")))
1857     return;           /* error */
1858   plugin->env->duc (plugin->env->cls, 0);
1859 }
1860
1861
1862 /**
1863  * Entry point for the plugin.
1864  *
1865  * @param cls the "struct GNUNET_DATASTORE_PluginEnvironment*"
1866  * @return our "struct Plugin*"
1867  */
1868 void *
1869 libgnunet_plugin_datastore_mysql_init (void *cls)
1870 {
1871   struct GNUNET_DATASTORE_PluginEnvironment *env = cls;
1872   struct GNUNET_DATASTORE_PluginFunctions *api;
1873   struct Plugin *plugin;
1874
1875   plugin = GNUNET_malloc (sizeof (struct Plugin));
1876   plugin->env = env;
1877   plugin->cnffile = get_my_cnf_path (env->cfg);
1878   if (GNUNET_OK != iopen (plugin))
1879     {
1880       iclose (plugin);
1881       GNUNET_free_non_null (plugin->cnffile);
1882       GNUNET_free (plugin);
1883       return NULL;
1884     }
1885 #define MRUNS(a) (GNUNET_OK != run_statement (plugin, a) )
1886 #define PINIT(a,b) (NULL == (a = prepared_statement_create(plugin, b)))
1887   if (MRUNS ("CREATE TABLE IF NOT EXISTS gn090 ("
1888              " type INT(11) UNSIGNED NOT NULL DEFAULT 0,"
1889              " prio INT(11) UNSIGNED NOT NULL DEFAULT 0,"
1890              " anonLevel INT(11) UNSIGNED NOT NULL DEFAULT 0,"
1891              " expire BIGINT UNSIGNED NOT NULL DEFAULT 0,"
1892              " hash BINARY(64) NOT NULL DEFAULT '',"
1893              " vhash BINARY(64) NOT NULL DEFAULT '',"
1894              " vkey BIGINT UNSIGNED NOT NULL DEFAULT 0,"
1895              " INDEX hash (hash(64)),"
1896              " INDEX hash_vhash_vkey (hash(64),vhash(64),vkey),"
1897              " INDEX hash_vkey (hash(64),vkey),"
1898              " INDEX vkey (vkey),"
1899              " INDEX prio (prio,vkey),"
1900              " INDEX expire (expire,vkey,type),"
1901              " INDEX anonLevel (anonLevel,prio,vkey,type)"
1902              ") ENGINE=InnoDB") ||
1903       MRUNS ("CREATE TABLE IF NOT EXISTS gn072 ("
1904              " vkey BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,"
1905              " value BLOB NOT NULL DEFAULT '') ENGINE=MyISAM") ||
1906       MRUNS ("SET AUTOCOMMIT = 1") ||
1907       PINIT (plugin->select_value, SELECT_VALUE) ||
1908       PINIT (plugin->delete_value, DELETE_VALUE) ||
1909       PINIT (plugin->insert_value, INSERT_VALUE) ||
1910       PINIT (plugin->insert_entry, INSERT_ENTRY) ||
1911       PINIT (plugin->delete_entry_by_vkey, DELETE_ENTRY_BY_VKEY) ||
1912       PINIT (plugin->select_entry_by_hash, SELECT_ENTRY_BY_HASH) ||
1913       PINIT (plugin->select_entry_by_hash_and_vhash, SELECT_ENTRY_BY_HASH_AND_VHASH)
1914       || PINIT (plugin->select_entry_by_hash_and_type, SELECT_ENTRY_BY_HASH_AND_TYPE)
1915       || PINIT (plugin->select_entry_by_hash_vhash_and_type,
1916                 SELECT_ENTRY_BY_HASH_VHASH_AND_TYPE)
1917       || PINIT (plugin->count_entry_by_hash, COUNT_ENTRY_BY_HASH)
1918       || PINIT (plugin->get_size, SELECT_SIZE)
1919       || PINIT (plugin->count_entry_by_hash_and_vhash, COUNT_ENTRY_BY_HASH_AND_VHASH)
1920       || PINIT (plugin->count_entry_by_hash_and_type, COUNT_ENTRY_BY_HASH_AND_TYPE)
1921       || PINIT (plugin->count_entry_by_hash_vhash_and_type,
1922                 COUNT_ENTRY_BY_HASH_VHASH_AND_TYPE)
1923       || PINIT (plugin->update_entry, UPDATE_ENTRY)
1924       || PINIT (plugin->iter[0], SELECT_IT_LOW_PRIORITY)
1925       || PINIT (plugin->iter[1], SELECT_IT_NON_ANONYMOUS)
1926       || PINIT (plugin->iter[2], SELECT_IT_EXPIRATION_TIME)
1927       || PINIT (plugin->iter[3], SELECT_IT_MIGRATION_ORDER))
1928     {
1929       iclose (plugin);
1930       GNUNET_free_non_null (plugin->cnffile);
1931       GNUNET_free (plugin);
1932       return NULL;
1933     }
1934 #undef PINIT
1935 #undef MRUNS
1936
1937   api = GNUNET_malloc (sizeof (struct GNUNET_DATASTORE_PluginFunctions));
1938   api->cls = plugin;
1939   api->get_size = &mysql_plugin_get_size;
1940   api->put = &mysql_plugin_put;
1941   api->next_request = &mysql_plugin_next_request;
1942   api->get = &mysql_plugin_get;
1943   api->update = &mysql_plugin_update;
1944   api->iter_low_priority = &mysql_plugin_iter_low_priority;
1945   api->iter_zero_anonymity = &mysql_plugin_iter_zero_anonymity;
1946   api->iter_ascending_expiration = &mysql_plugin_iter_ascending_expiration;
1947   api->iter_migration_order = &mysql_plugin_iter_migration_order;
1948   api->iter_all_now = &mysql_plugin_iter_all_now;
1949   api->drop = &mysql_plugin_drop;
1950   GNUNET_log_from (GNUNET_ERROR_TYPE_INFO,
1951                    "mysql", _("Mysql database running\n"));
1952   return api;
1953 }
1954
1955
1956 /**
1957  * Exit point from the plugin.
1958  * @param cls our "struct Plugin*"
1959  * @return always NULL
1960  */
1961 void *
1962 libgnunet_plugin_datastore_mysql_done (void *cls)
1963 {
1964   struct GNUNET_DATASTORE_PluginFunctions *api = cls;
1965   struct Plugin *plugin = api->cls;
1966
1967   iclose (plugin);
1968   if (plugin->next_task != GNUNET_SCHEDULER_NO_TASK)
1969     {
1970       GNUNET_SCHEDULER_cancel (plugin->next_task);
1971       plugin->next_task = GNUNET_SCHEDULER_NO_TASK;
1972       plugin->next_task_nc->prep (plugin->next_task_nc->prep_cls, NULL);
1973       GNUNET_free (plugin->next_task_nc);
1974       plugin->next_task_nc = NULL;
1975     }
1976   GNUNET_free_non_null (plugin->cnffile);
1977   GNUNET_free (plugin);
1978   GNUNET_free (api);
1979   mysql_library_end ();
1980   return NULL;
1981 }
1982
1983 /* end of plugin_datastore_mysql.c */