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