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