continue to fix execute select
[oweals/gnunet.git] / src / datastore / plugin_datastore_mysql.c
1 /*
2      This file is part of GNUnet
3      Copyright (C) 2009, 2010, 2011 GNUnet e.V.
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., 51 Franklin Street, Fifth Floor,
18      Boston, MA 02110-1301, 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  * @author Christophe Genevey
27  *
28  * NOTE: This db module does NOT work with mysql prior to 4.1 since
29  * it uses prepared statements.  MySQL 5.0.46 promises to fix a bug
30  * in MyISAM that is causing us grief.  At the time of this writing,
31  * that version is yet to be released.  In anticipation, the code
32  * will use MyISAM with 5.0.46 (and higher).  If you run such a
33  * version, please run "make check" to verify that the MySQL bug
34  * was actually fixed in your version (and if not, change the
35  * code below to use MyISAM for gn071).
36  *
37  * HIGHLIGHTS
38  *
39  * Pros
40  * + On up-to-date hardware where mysql can be used comfortably, this
41  *   module will have better performance than the other db choices
42  *   (according to our tests).
43  * + Its often possible to recover the mysql database from internal
44  *   inconsistencies. The other db choices do not support repair!
45  * Cons
46  * - Memory usage (Comment: "I have 1G and it never caused me trouble")
47  * - Manual setup
48  *
49  * MANUAL SETUP INSTRUCTIONS
50  *
51  * 1) in gnunet.conf, set
52  * @verbatim
53        [datastore]
54        DATABASE = "mysql"
55    @endverbatim
56  * 2) Then access mysql as root,
57  * @verbatim
58      $ mysql -u root -p
59    @endverbatim
60  *    and do the following. [You should replace $USER with the username
61  *    that will be running the gnunetd process].
62  * @verbatim
63       CREATE DATABASE gnunet;
64       GRANT select,insert,update,delete,create,alter,drop,create temporary tables
65          ON gnunet.* TO $USER@localhost;
66       SET PASSWORD FOR $USER@localhost=PASSWORD('$the_password_you_like');
67       FLUSH PRIVILEGES;
68    @endverbatim
69  * 3) In the $HOME directory of $USER, create a ".my.cnf" file
70  *    with the following lines
71  * @verbatim
72       [client]
73       user=$USER
74       password=$the_password_you_like
75    @endverbatim
76  *
77  * Thats it. Note that .my.cnf file is a security risk unless its on
78  * a safe partition etc. The $HOME/.my.cnf can of course be a symbolic
79  * link. Even greater security risk can be achieved by setting no
80  * password for $USER.  Luckily $USER has only priviledges to mess
81  * up GNUnet's tables, nothing else (unless you give him more,
82  * of course).<p>
83  *
84  * 4) Still, perhaps you should briefly try if the DB connection
85  *    works. First, login as $USER. Then use,
86  *
87  * @verbatim
88      $ mysql -u $USER -p $the_password_you_like
89      mysql> use gnunet;
90    @endverbatim
91  *
92  *    If you get the message &quot;Database changed&quot; it probably works.
93  *
94  *    [If you get &quot;ERROR 2002: Can't connect to local MySQL server
95  *     through socket '/tmp/mysql.sock' (2)&quot; it may be resolvable by
96  *     &quot;ln -s /var/run/mysqld/mysqld.sock /tmp/mysql.sock&quot;
97  *     so there may be some additional trouble depending on your mysql setup.]
98  *
99  * REPAIRING TABLES
100  *
101  * - Its probably healthy to check your tables for inconsistencies
102  *   every now and then.
103  * - If you get odd SEGVs on gnunetd startup, it might be that the mysql
104  *   databases have been corrupted.
105  * - The tables can be verified/fixed in two ways;
106  *   1) by running mysqlcheck -A, or
107  *   2) by executing (inside of mysql using the GNUnet database):
108  * @verbatim
109      mysql> REPAIR TABLE gn090;
110    @endverbatim
111  *
112  * PROBLEMS?
113  *
114  * If you have problems related to the mysql module, your best
115  * friend is probably the mysql manual. The first thing to check
116  * is that mysql is basically operational, that you can connect
117  * to it, create tables, issue queries etc.
118  */
119
120 #include "platform.h"
121 #include "gnunet_datastore_plugin.h"
122 #include "gnunet_util_lib.h"
123 #include "gnunet_mysql_lib.h"
124 #include "gnunet_my_lib.h"
125
126 #define MAX_DATUM_SIZE 65536
127
128
129 /**
130  * Context for all functions in this plugin.
131  */
132 struct Plugin
133 {
134   /**
135    * Our execution environment.
136    */
137   struct GNUNET_DATASTORE_PluginEnvironment *env;
138
139   /**
140    * Handle to talk to MySQL.
141    */
142   struct GNUNET_MYSQL_Context *mc;
143
144   /**
145    * Prepared statements.
146    */
147 #define INSERT_ENTRY "INSERT INTO gn090 (repl,type,prio,anonLevel,expire,rvalue,hash,vhash,value) VALUES (?,?,?,?,?,?,?,?,?)"
148   struct GNUNET_MYSQL_StatementHandle *insert_entry;
149
150 #define DELETE_ENTRY_BY_UID "DELETE FROM gn090 WHERE uid=?"
151   struct GNUNET_MYSQL_StatementHandle *delete_entry_by_uid;
152
153 #define COUNT_ENTRY_BY_HASH "SELECT count(*) FROM gn090 FORCE INDEX (idx_hash) WHERE hash=?"
154   struct GNUNET_MYSQL_StatementHandle *count_entry_by_hash;
155
156 #define SELECT_ENTRY_BY_HASH "SELECT type,prio,anonLevel,expire,hash,value,uid FROM gn090 FORCE INDEX (idx_hash) WHERE hash=? ORDER BY uid LIMIT 1 OFFSET ?"
157   struct GNUNET_MYSQL_StatementHandle *select_entry_by_hash;
158
159 #define COUNT_ENTRY_BY_HASH_AND_VHASH "SELECT count(*) FROM gn090 FORCE INDEX (idx_hash_vhash) WHERE hash=? AND vhash=?"
160   struct GNUNET_MYSQL_StatementHandle *count_entry_by_hash_and_vhash;
161
162 #define SELECT_ENTRY_BY_HASH_AND_VHASH "SELECT type,prio,anonLevel,expire,hash,value,uid FROM gn090 FORCE INDEX (idx_hash_vhash) WHERE hash=? AND vhash=? ORDER BY uid LIMIT 1 OFFSET ?"
163   struct GNUNET_MYSQL_StatementHandle *select_entry_by_hash_and_vhash;
164
165 #define COUNT_ENTRY_BY_HASH_AND_TYPE "SELECT count(*) FROM gn090 FORCE INDEX (idx_hash_type_uid) WHERE hash=? AND type=?"
166   struct GNUNET_MYSQL_StatementHandle *count_entry_by_hash_and_type;
167
168 #define SELECT_ENTRY_BY_HASH_AND_TYPE "SELECT type,prio,anonLevel,expire,hash,value,uid FROM gn090 FORCE INDEX (idx_hash_type_uid) WHERE hash=? AND type=? ORDER BY uid LIMIT 1 OFFSET ?"
169   struct GNUNET_MYSQL_StatementHandle *select_entry_by_hash_and_type;
170
171 #define COUNT_ENTRY_BY_HASH_VHASH_AND_TYPE "SELECT count(*) FROM gn090 FORCE INDEX (idx_hash_vhash) WHERE hash=? AND vhash=? AND type=?"
172   struct GNUNET_MYSQL_StatementHandle *count_entry_by_hash_vhash_and_type;
173
174 #define SELECT_ENTRY_BY_HASH_VHASH_AND_TYPE "SELECT type,prio,anonLevel,expire,hash,value,uid FROM gn090 FORCE INDEX (idx_hash_vhash) WHERE hash=? AND vhash=? AND type=? ORDER BY uid ASC LIMIT 1 OFFSET ?"
175   struct GNUNET_MYSQL_StatementHandle *select_entry_by_hash_vhash_and_type;
176
177 #define UPDATE_ENTRY "UPDATE gn090 SET prio=prio+?,expire=IF(expire>=?,expire,?) WHERE uid=?"
178   struct GNUNET_MYSQL_StatementHandle *update_entry;
179
180 #define DEC_REPL "UPDATE gn090 SET repl=GREATEST (1, repl) - 1 WHERE uid=?"
181   struct GNUNET_MYSQL_StatementHandle *dec_repl;
182
183 #define SELECT_SIZE "SELECT SUM(BIT_LENGTH(value) DIV 8) FROM gn090"
184   struct GNUNET_MYSQL_StatementHandle *get_size;
185
186 #define SELECT_IT_NON_ANONYMOUS "SELECT type,prio,anonLevel,expire,hash,value,uid "\
187    "FROM gn090 FORCE INDEX (idx_anonLevel_type_rvalue) "\
188    "WHERE anonLevel=0 AND type=? AND "\
189    "(rvalue >= ? OR"\
190    "  NOT EXISTS (SELECT 1 FROM gn090 FORCE INDEX (idx_anonLevel_type_rvalue) WHERE anonLevel=0 AND type=? AND rvalue>=?)) "\
191    "ORDER BY rvalue ASC LIMIT 1"
192   struct GNUNET_MYSQL_StatementHandle *zero_iter;
193
194 #define SELECT_IT_EXPIRATION "SELECT type,prio,anonLevel,expire,hash,value,uid FROM gn090 FORCE INDEX (idx_expire) WHERE expire < ? ORDER BY expire ASC LIMIT 1"
195   struct GNUNET_MYSQL_StatementHandle *select_expiration;
196
197 #define SELECT_IT_PRIORITY "SELECT type,prio,anonLevel,expire,hash,value,uid FROM gn090 FORCE INDEX (idx_prio) ORDER BY prio ASC LIMIT 1"
198   struct GNUNET_MYSQL_StatementHandle *select_priority;
199
200 #define SELECT_IT_REPLICATION "SELECT type,prio,anonLevel,expire,hash,value,uid "\
201   "FROM gn090 FORCE INDEX (idx_repl_rvalue) "\
202   "WHERE repl=? AND "\
203   " (rvalue>=? OR"\
204   "  NOT EXISTS (SELECT 1 FROM gn090 FORCE INDEX (idx_repl_rvalue) WHERE repl=? AND rvalue>=?)) "\
205   "ORDER BY rvalue ASC "\
206   "LIMIT 1"
207   struct GNUNET_MYSQL_StatementHandle *select_replication;
208
209 #define SELECT_MAX_REPL "SELECT MAX(repl) FROM gn090"
210   struct GNUNET_MYSQL_StatementHandle *max_repl;
211
212 #define GET_ALL_KEYS "SELECT hash from gn090"
213   struct GNUNET_MYSQL_StatementHandle *get_all_keys;
214
215 };
216
217 #define MAX_PARAM 16
218
219 /**
220  * Delete an entry from the gn090 table.
221  *
222  * @param plugin plugin context
223  * @param uid unique ID of the entry to delete
224  * @return GNUNET_OK on success, GNUNET_NO if no such value exists, GNUNET_SYSERR on error
225  */
226 static int
227 do_delete_entry (struct Plugin *plugin, unsigned long long uid)
228 {
229   int ret;
230
231   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Deleting value %llu from gn090 table\n",
232               uid);
233 /*  ret = GNUNET_MYSQL_statement_run_prepared (plugin->mc,
234                                                                            plugin->delete_entry_by_uid,
235                                              NULL,
236                                                                            MYSQL_TYPE_LONGLONG,
237                                              &uid,
238                                              GNUNET_YES,
239                                               -1);
240
241 */
242   uint64_t uid64 = (uint64_t) uid;
243
244   struct GNUNET_MY_QueryParam params_delete[] = {
245     GNUNET_MY_query_param_uint64 (&uid64),
246     GNUNET_MY_query_param_end
247   };
248
249   ret = GNUNET_MY_exec_prepared (plugin->mc,
250                                  plugin->delete_entry_by_uid,
251                                  params_delete);
252   if (ret >= 0)
253   {
254     return GNUNET_OK;
255   }
256   GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
257               "Deleting value %llu from gn090 table failed\n",
258               uid);
259   return ret;
260 }
261
262
263 /**
264  * Get an estimate of how much space the database is
265  * currently using.
266  *
267  * @param cls our "struct Plugin *"
268  * @return number of bytes used on disk
269  */
270 static void
271 mysql_plugin_estimate_size (void *cls, unsigned long long *estimate)
272 {
273   struct Plugin *plugin = cls;
274 //  MYSQL_BIND cbind[1];
275   uint64_t total;
276 //  long long total;
277 /*
278   if (NULL == estimate)
279     return;
280   memset (cbind, 0, sizeof (cbind));
281   total = 0;
282   cbind[0].buffer_type = MYSQL_TYPE_LONGLONG;
283   cbind[0].buffer = &total;
284   cbind[0].is_unsigned = GNUNET_NO;
285 */
286   struct GNUNET_MY_QueryParam params_get[] = {
287     GNUNET_MY_query_param_end
288   };
289
290   struct GNUNET_MY_ResultSpec results_get[] = {
291     GNUNET_MY_result_spec_uint64 (&total),
292     GNUNET_MY_result_spec_end
293   };
294   int ret;
295
296 //  if (GNUNET_OK ==
297 //      GNUNET_MYSQL_statement_run_prepared_select (plugin->mc, plugin->get_size, 1, cbind, NULL, NULL, -1))
298   ret = GNUNET_MY_exec_prepared (plugin->mc, plugin->get_size, params_get);
299   if (GNUNET_OK == ret)
300     {
301       if (GNUNET_OK == GNUNET_MY_extract_result (plugin->get_size, results_get))
302       {
303         *estimate = (unsigned long long)total;
304       }
305     }
306     //*estimate = total;
307   else
308     *estimate = 0;
309 }
310
311
312 /**
313  * Store an item in the datastore.
314  *
315  * @param cls closure
316  * @param key key for the item
317  * @param size number of bytes in data
318  * @param data content stored
319  * @param type type of the content
320  * @param priority priority of the content
321  * @param anonymity anonymity-level for the content
322  * @param replication replication-level for the content
323  * @param expiration expiration time for the content
324  * @param cont continuation called with success or failure status
325  * @param cont_cls continuation closure
326  */
327 static void
328 mysql_plugin_put (void *cls, const struct GNUNET_HashCode * key, uint32_t size,
329                   const void *data, enum GNUNET_BLOCK_Type type,
330                   uint32_t priority, uint32_t anonymity, uint32_t replication,
331                   struct GNUNET_TIME_Absolute expiration, PluginPutCont cont,
332                   void *cont_cls)
333 {
334   struct Plugin *plugin = cls;
335   unsigned int irepl = replication;
336   unsigned int ipriority = priority;
337   unsigned int ianonymity = anonymity;
338 //  unsigned long long lexpiration = expiration.abs_value_us;
339   uint64_t lexpiration = expiration.abs_value_us;
340
341 /*  unsigned long long lrvalue =
342       (unsigned long long) GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK,
343                                                      UINT64_MAX);
344 */
345   uint64_t lrvalue =
346       (uint64_t) GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK,
347                                                      UINT64_MAX);
348   unsigned long hashSize;
349   unsigned long hashSize2;
350   unsigned long lsize;
351   struct GNUNET_HashCode vhash;
352
353   if (size > MAX_DATUM_SIZE)
354   {
355     GNUNET_break (0);
356     cont (cont_cls, key, size, GNUNET_SYSERR, _("Data too large"));
357     return;
358   }
359   hashSize = sizeof (struct GNUNET_HashCode);
360   hashSize2 = sizeof (struct GNUNET_HashCode);
361   lsize = size;
362   GNUNET_CRYPTO_hash (data, size, &vhash);
363
364   struct GNUNET_MY_QueryParam params_insert[] = {
365     GNUNET_MY_query_param_uint32 (&irepl),
366     GNUNET_MY_query_param_uint32 (&type),
367     GNUNET_MY_query_param_uint32 (&ipriority),
368     GNUNET_MY_query_param_uint32 (&ianonymity),
369     GNUNET_MY_query_param_uint64 (&lexpiration),
370     GNUNET_MY_query_param_uint64 (&lrvalue),
371     GNUNET_MY_query_param_fixed_size (key, hashSize),
372     GNUNET_MY_query_param_fixed_size (&vhash, hashSize2),
373     GNUNET_MY_query_param_fixed_size (data, lsize),
374     GNUNET_MY_query_param_end
375   };
376 /*
377   if (GNUNET_OK !=
378       GNUNET_MYSQL_statement_run_prepared (plugin->mc, plugin->insert_entry, NULL,
379                               MYSQL_TYPE_LONG, &irepl, GNUNET_YES,
380                               MYSQL_TYPE_LONG, &type, GNUNET_YES,
381                               MYSQL_TYPE_LONG, &ipriority, GNUNET_YES,
382                               MYSQL_TYPE_LONG, &ianonymity, GNUNET_YES,
383                               MYSQL_TYPE_LONGLONG, &lexpiration, GNUNET_YES,
384                               MYSQL_TYPE_LONGLONG, &lrvalue, GNUNET_YES,
385                               MYSQL_TYPE_BLOB, key, hashSize, &hashSize,
386                               MYSQL_TYPE_BLOB, &vhash, hashSize2, &hashSize2,
387                               MYSQL_TYPE_BLOB, data, lsize, &lsize, -1))
388   {
389     cont (cont_cls, key, size, GNUNET_SYSERR, _("MySQL statement run failure"));
390     return;
391   }
392 */
393   if (GNUNET_OK !=
394       GNUNET_MY_exec_prepared (plugin->mc,
395                                plugin->insert_entry,
396                                params_insert))
397   {
398     cont (cont_cls, key, size, GNUNET_SYSERR, _("MySQL statement run failure"));
399     return;
400   }
401   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
402               "Inserted value `%s' with size %u into gn090 table\n",
403               GNUNET_h2s (key), (unsigned int) size);
404   if (size > 0)
405     plugin->env->duc (plugin->env->cls, size);
406   cont (cont_cls, key, size, GNUNET_OK, NULL);
407 }
408
409
410 /**
411  * Update the priority for a particular key in the datastore.  If
412  * the expiration time in value is different than the time found in
413  * the datastore, the higher value should be kept.  For the
414  * anonymity level, the lower value is to be used.  The specified
415  * priority should be added to the existing priority, ignoring the
416  * priority in value.
417  *
418  * Note that it is possible for multiple values to match this put.
419  * In that case, all of the respective values are updated.
420  *
421  * @param cls our "struct Plugin*"
422  * @param uid unique identifier of the datum
423  * @param delta by how much should the priority
424  *     change?  If priority + delta < 0 the
425  *     priority should be set to 0 (never go
426  *     negative).
427  * @param expire new expiration time should be the
428  *     MAX of any existing expiration time and
429  *     this value
430  * @param cont continuation called with success or failure status
431  * @param cons_cls continuation closure
432  */
433 static void
434 mysql_plugin_update (void *cls, uint64_t uid, int delta,
435                      struct GNUNET_TIME_Absolute expire,
436                      PluginUpdateCont cont, void *cont_cls)
437 {
438   struct Plugin *plugin = cls;
439   uint32_t delta1 = (uint32_t)delta;
440   unsigned long long vkey = uid;
441 //  unsigned long long lexpire = expire.abs_value_us;
442   uint64_t lexpire = expire.abs_value_us;
443   int ret;
444
445   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
446               "Updating value %llu adding %d to priority and maxing exp at %s\n",
447               vkey, delta,
448               GNUNET_STRINGS_absolute_time_to_string (expire));
449 /*  ret =
450     GNUNET_MYSQL_statement_run_prepared (plugin->mc, plugin->update_entry, NULL,
451                                          MYSQL_TYPE_LONG, &delta, GNUNET_NO,
452                               MYSQL_TYPE_LONGLONG, &lexpire, GNUNET_YES,
453                               MYSQL_TYPE_LONGLONG, &lexpire, GNUNET_YES,
454                               MYSQL_TYPE_LONGLONG, &vkey, GNUNET_YES, -1);
455 */
456   struct GNUNET_MY_QueryParam params_update[] = {
457     GNUNET_MY_query_param_uint32 (&delta1),
458     GNUNET_MY_query_param_uint64 (&lexpire),
459     GNUNET_MY_query_param_uint64 (&lexpire),
460     GNUNET_MY_query_param_uint64 (&uid),
461     GNUNET_MY_query_param_end
462   };
463
464   ret = GNUNET_MY_exec_prepared (plugin->mc,
465                                  plugin->update_entry,
466                                  params_update);
467
468   if (ret != GNUNET_OK)
469   {
470     GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Failed to update value %llu\n",
471                 vkey);
472   }
473   cont (cont_cls, ret, NULL);
474 }
475
476
477 /**
478  * Run the given select statement and call 'proc' on the resulting
479  * values (which must be in particular positions).
480  *
481  * @param plugin the plugin handle
482  * @param stmt select statement to run
483  * @param proc function to call on result
484  * @param proc_cls closure for proc
485  * @param ... arguments to initialize stmt
486  */
487 static void
488 execute_select (struct Plugin *plugin, struct GNUNET_MYSQL_StatementHandle *stmt,
489                 PluginDatumProcessor proc, void *proc_cls, ...)
490 {
491   va_list ap;
492   int ret;
493   unsigned int type;
494 //  unsigned int priority;
495   uint32_t priority;
496   unsigned int anonymity;
497 //  unsigned long long exp;
498   uint64_t exp;
499   size_t hashSize;
500 //  unsigned long size;
501   size_t size;
502   uint64_t uid;
503   void *value;
504   struct GNUNET_HashCode key;
505   struct GNUNET_TIME_Absolute expiration;
506   //MYSQL_BIND rbind[7];
507
508   hashSize = sizeof (struct GNUNET_HashCode);
509 /*  memset (rbind, 0, sizeof (rbind));
510   rbind[0].buffer_type = MYSQL_TYPE_LONG;
511   rbind[0].buffer = &type;
512   rbind[0].is_unsigned = 1;
513   rbind[1].buffer_type = MYSQL_TYPE_LONG;
514   rbind[1].buffer = &priority;
515   rbind[1].is_unsigned = 1;
516   rbind[2].buffer_type = MYSQL_TYPE_LONG;
517   rbind[2].buffer = &anonymity;
518   rbind[2].is_unsigned = 1;
519   rbind[3].buffer_type = MYSQL_TYPE_LONGLONG;
520   rbind[3].buffer = &exp;
521   rbind[3].is_unsigned = 1;
522   rbind[4].buffer_type = MYSQL_TYPE_BLOB;
523   rbind[4].buffer = &key;
524   rbind[4].buffer_length = hashSize;
525   rbind[4].length = &hashSize;
526   rbind[5].buffer_type = MYSQL_TYPE_BLOB;
527   rbind[5].buffer = value;
528   rbind[5].buffer_length = size = sizeof (value);
529   rbind[5].length = &size;
530   rbind[6].buffer_type = MYSQL_TYPE_LONGLONG;
531   rbind[6].buffer = &uid;
532   rbind[6].is_unsigned = 1;
533 */
534   //  ret = GNUNET_MYSQL_statement_run_prepared_select_va (stmt, 7, rbind, NULL, NULL, ap);
535   va_start (ap, proc_cls);
536
537   int pc = 0;
538
539   struct GNUNET_MY_QueryParam *params_select = NULL;
540
541   unsigned int *param_long = NULL;
542   int param_is_unsigned;
543   unsigned long param_length;
544   unsigned long *length = NULL;
545
546   unsigned long long *param_longlong = NULL;
547   void *param_blob = NULL;
548
549   pc = mysql_stmt_param_count (stmt);
550   if (pc  > MAX_PARAM)
551   {
552     GNUNET_break (0);
553   }
554   
555 //  enum enum_field_type ft;
556   int ft;
557   int j = 0;
558
559   ft = 0;
560
561   while ((pc > 0) && (-1 != (ft = va_arg(ap, int))))
562   {
563    switch (ft)
564    {
565     case MYSQL_TYPE_LONGLONG:
566       param_longlong = va_arg (ap, unsigned long long *);
567       param_is_unsigned = va_arg (ap, int);
568       params_select[j] = GNUNET_MY_query_param_uint64 (param_longlong);
569       break;
570
571     case MYSQL_TYPE_LONG:
572       param_long = va_arg (ap, unsigned int*);
573       param_is_unsigned = va_arg (ap, int);
574       params_select[j] = GNUNET_MY_query_param_uint32 (param_long);
575       break;
576
577     case MYSQL_TYPE_VAR_STRING:
578     case MYSQL_TYPE_STRING:
579     case MYSQL_TYPE_BLOB:
580       param_blob = va_arg (ap, void *);
581       param_length = va_arg (ap, unsigned long);
582       length = va_arg (ap, unsigned long *);
583       //params_select[j] = GNUNET_MY_query_param_fixed_size (param_blob, param_length);
584       params_select[j] = GNUNET_MY_query_param_auto_from_type(&key);
585
586       break;
587     default:
588       GNUNET_break(0);
589    }
590    pc--;
591    j++;
592   }
593
594 /*
595   struct GNUNET_MY_ResultSpec results_select[] = {
596     GNUNET_MY_result_spec_uint32 (&type),
597     GNUNET_MY_result_spec_uint32 (&priority),
598     GNUNET_MY_result_spec_uint32 (&anonymity),
599     GNUNET_MY_result_spec_uint64 (&exp),
600     GNUNET_MY_result_spec_auto_from_type (&key),
601     GNUNET_MY_result_spec_variable_size (&value, &size),
602     GNUNET_MY_result_spec_uint64 (&uid),
603     GNUNET_MY_query_param_end
604   };
605 */
606 /* 
607
608   !!!! FAIL HERE FOR SELECT QUERY
609 */  ret = GNUNET_MY_exec_prepared (plugin->mc, stmt, params_select);
610
611   if(GNUNET_OK != ret)
612   {
613     proc (proc_cls, NULL, 0, NULL, 0, 0, 0, GNUNET_TIME_UNIT_ZERO_ABS, 0);
614     return;
615   }
616   
617 /*  if (ret <= 0)
618   {
619     proc (proc_cls, NULL, 0, NULL, 0, 0, 0, GNUNET_TIME_UNIT_ZERO_ABS, 0);
620     return;
621   }
622
623   va_end (ap);
624
625 /*
626   ret = GNUNET_MY_extract_result (stmt, results_select);
627   if (ret <= 0)
628   {
629     proc (proc_cls, NULL, 0, NULL, 0, 0, 0, GNUNET_TIME_UNIT_ZERO_ABS, 0);
630     return;
631   }
632
633   GNUNET_assert (size <= sizeof (value));
634 /*
635   if ((rbind[4].buffer_length != sizeof (struct GNUNET_HashCode)) ||
636       (hashSize != sizeof (struct GNUNET_HashCode)))
637   {
638
639   expiration.abs_value_us = exp;
640   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
641               "Found %u-byte value under key `%s' with prio %u, anon %u, expire %s selecting from gn090 table\n",
642               (unsigned int) size, GNUNET_h2s (&key),
643               priority, anonymity,
644               GNUNET_STRINGS_absolute_time_to_string (expiration));
645   GNUNET_assert (size < MAX_DATUM_SIZE);
646   ret =
647       proc (proc_cls, &key, size, value, type, priority, anonymity, expiration,
648             uid);
649 */
650
651 /*  if (ret == GNUNET_NO)
652   {
653     do_delete_entry (plugin, uid);
654     if (size != 0)
655     plugin->env->duc (plugin->env->cls, -size);
656   }
657 */  
658 }
659
660
661
662 /**
663  * Get one of the results for a particular key in the datastore.
664  *
665  * @param cls closure
666  * @param offset offset of the result (modulo num-results);
667  *               specific ordering does not matter for the offset
668  * @param key key to match, never NULL
669  * @param vhash hash of the value, maybe NULL (to
670  *        match all values that have the right key).
671  *        Note that for DBlocks there is no difference
672  *        betwen key and vhash, but for other blocks
673  *        there may be!
674  * @param type entries of which type are relevant?
675  *     Use 0 for any type.
676  * @param proc function to call on the matching value,
677  *        with NULL for if no value matches
678  * @param proc_cls closure for proc
679  */
680 static void
681 mysql_plugin_get_key (void *cls, uint64_t offset, const struct GNUNET_HashCode * key,
682                       const struct GNUNET_HashCode * vhash,
683                       enum GNUNET_BLOCK_Type type, PluginDatumProcessor proc,
684                       void *proc_cls)
685 {
686   struct Plugin *plugin = cls;
687   int ret;
688   //MYSQL_BIND cbind[1];
689   //long long total;
690   uint64_t total;
691   unsigned long hashSize;
692   unsigned long hashSize2;
693   unsigned long long off;
694
695   GNUNET_assert (key != NULL);
696   GNUNET_assert (NULL != proc);
697   hashSize = sizeof (struct GNUNET_HashCode);
698   hashSize2 = sizeof (struct GNUNET_HashCode);
699 //  memset (cbind, 0, sizeof (cbind));
700   total = -1;
701 /*  cbind[0].buffer_type = MYSQL_TYPE_LONGLONG;
702   cbind[0].buffer = &total;
703   cbind[0].is_unsigned = GNUNET_NO;
704 */
705
706   struct GNUNET_MY_ResultSpec results_get[] = {
707     GNUNET_MY_result_spec_uint64 (&total),
708     GNUNET_MY_result_spec_end
709   };
710
711   if (type != 0)
712   {
713     if (vhash != NULL)
714     {
715 /*      ret =
716         GNUNET_MYSQL_statement_run_prepared_select (plugin->mc,
717                                          plugin->
718                                          count_entry_by_hash_vhash_and_type, 1,
719                                                     cbind, NULL, NULL, MYSQL_TYPE_BLOB, key, hashSize,
720                                          &hashSize, MYSQL_TYPE_BLOB, vhash,
721                                          hashSize2, &hashSize2, MYSQL_TYPE_LONG,
722                                          &type, GNUNET_YES, -1);
723 */
724       struct GNUNET_MY_QueryParam params_get[] = {
725         GNUNET_MY_query_param_fixed_size (key, hashSize),
726         GNUNET_MY_query_param_fixed_size (vhash, hashSize2),
727         GNUNET_MY_query_param_uint32 (&type),
728         GNUNET_MY_query_param_end
729       };
730
731       ret =
732         GNUNET_MY_exec_prepared (plugin->mc,
733                                  plugin->count_entry_by_hash_vhash_and_type,
734                                  params_get);
735       ret =
736         GNUNET_MY_extract_result (plugin->count_entry_by_hash_vhash_and_type,
737                                   results_get);
738     }
739     else
740     {
741 /*      ret =
742         GNUNET_MYSQL_statement_run_prepared_select (plugin->mc,
743                                          plugin->count_entry_by_hash_and_type,
744                                                     1, cbind, NULL, NULL, MYSQL_TYPE_BLOB, key,
745                                          hashSize, &hashSize, MYSQL_TYPE_LONG,
746                                          &type, GNUNET_YES, -1);
747  */
748         struct GNUNET_MY_QueryParam params_get[] = {
749           GNUNET_MY_query_param_fixed_size (key, hashSize),
750           GNUNET_MY_query_param_uint32 (&type),
751           GNUNET_MY_query_param_end
752         };
753
754
755         ret =
756           GNUNET_MY_exec_prepared (plugin->mc,
757                                    plugin->count_entry_by_hash_and_type,
758                                    params_get);
759         ret =
760           GNUNET_MY_extract_result (plugin->count_entry_by_hash_and_type,
761                                     results_get);
762     }
763   }
764   else
765   {
766     if (vhash != NULL)
767     {
768 /*      ret =
769         GNUNET_MYSQL_statement_run_prepared_select (plugin->mc,
770                                          plugin->count_entry_by_hash_and_vhash,
771                                                     1, cbind, NULL, NULL, MYSQL_TYPE_BLOB, key,
772                                          hashSize, &hashSize, MYSQL_TYPE_BLOB,
773                                          vhash, hashSize2, &hashSize2, -1);
774 */
775          struct GNUNET_MY_QueryParam params_get[] = {
776           GNUNET_MY_query_param_fixed_size (key, hashSize),
777           GNUNET_MY_query_param_fixed_size (vhash, hashSize2),
778           GNUNET_MY_query_param_end
779         };
780
781
782         ret =
783   GNUNET_MY_exec_prepared (plugin->mc,
784                            plugin->count_entry_by_hash_and_vhash,
785                            params_get);
786         ret =
787   GNUNET_MY_extract_result (plugin->count_entry_by_hash_and_vhash,
788                             results_get);
789     }
790     else
791     {
792 /*      ret =
793         GNUNET_MYSQL_statement_run_prepared_select (plugin->mc, plugin->count_entry_by_hash, 1,
794                                                     cbind, NULL, NULL, MYSQL_TYPE_BLOB, key, hashSize,
795                                          &hashSize, -1);
796 */
797         struct GNUNET_MY_QueryParam params_get[] = {
798           GNUNET_MY_query_param_fixed_size (key, hashSize),
799           GNUNET_MY_query_param_end
800         };
801
802
803         ret =
804   GNUNET_MY_exec_prepared (plugin->mc,
805                            plugin->count_entry_by_hash,
806                            params_get);
807         ret =
808   GNUNET_MY_extract_result (plugin->count_entry_by_hash,
809                             results_get);
810     }
811
812   }
813   if ((ret != GNUNET_OK) || (0 >= total))
814   {
815     proc (proc_cls, NULL, 0, NULL, 0, 0, 0, GNUNET_TIME_UNIT_ZERO_ABS, 0);
816     return;
817   }
818   offset = offset % total;
819   off = (unsigned long long) offset;
820   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
821               "Obtaining %llu/%lld result for GET `%s'\n",
822               (unsigned long long) off,
823               (unsigned long long) total,
824               GNUNET_h2s (key));
825   if (type != GNUNET_BLOCK_TYPE_ANY)
826   {
827     if (NULL != vhash)
828     {
829       execute_select (plugin, plugin->select_entry_by_hash_vhash_and_type, proc,
830                       proc_cls, MYSQL_TYPE_BLOB, key, hashSize, &hashSize,
831                       MYSQL_TYPE_BLOB, vhash, hashSize, &hashSize,
832                       MYSQL_TYPE_LONG, &type, GNUNET_YES, MYSQL_TYPE_LONGLONG,
833                       &off, GNUNET_YES, -1);
834     }
835     else
836     {
837       execute_select (plugin, plugin->select_entry_by_hash_and_type, proc,
838                       proc_cls, MYSQL_TYPE_BLOB, key, hashSize, &hashSize,
839                       MYSQL_TYPE_LONG, &type, GNUNET_YES, MYSQL_TYPE_LONGLONG,
840                       &off, GNUNET_YES, -1);
841     }
842   }
843   else
844   {
845     if (NULL != vhash)
846     {
847       execute_select (plugin, plugin->select_entry_by_hash_and_vhash, proc,
848                       proc_cls, MYSQL_TYPE_BLOB, key, hashSize, &hashSize,
849                       MYSQL_TYPE_BLOB, vhash, hashSize, &hashSize,
850                       MYSQL_TYPE_LONGLONG, &off, GNUNET_YES, -1);
851     }
852     else
853     {
854       execute_select (plugin, plugin->select_entry_by_hash, proc, proc_cls,
855                       MYSQL_TYPE_BLOB, key, hashSize, &hashSize,
856                       MYSQL_TYPE_LONGLONG, &off, GNUNET_YES, -1);
857     }
858   }
859 }
860
861
862 /**
863  * Get a zero-anonymity datum from the datastore.
864  *
865  * @param cls our "struct Plugin*"
866  * @param offset offset of the result
867  * @param type entries of which type should be considered?
868  *        Use 0 for any type.
869  * @param proc function to call on a matching value or NULL
870  * @param proc_cls closure for iter
871  */
872 static void
873 mysql_plugin_get_zero_anonymity (void *cls, uint64_t offset,
874                                  enum GNUNET_BLOCK_Type type,
875                                  PluginDatumProcessor proc, void *proc_cls)
876 {
877   struct Plugin *plugin = cls;
878   unsigned long long rvalue =
879       (unsigned long long) GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK,
880                                                      UINT64_MAX);
881
882   execute_select (plugin, plugin->zero_iter, proc, proc_cls, MYSQL_TYPE_LONG,
883                   &type, GNUNET_YES, MYSQL_TYPE_LONGLONG, &rvalue, GNUNET_YES,
884                   MYSQL_TYPE_LONG, &type, GNUNET_YES, MYSQL_TYPE_LONGLONG,
885                   &rvalue, GNUNET_YES, -1);
886 }
887
888
889 /**
890  * Context for 'repl_proc' function.
891  */
892 struct ReplCtx
893 {
894
895   /**
896    * Plugin handle.
897    */
898   struct Plugin *plugin;
899
900   /**
901    * Function to call for the result (or the NULL).
902    */
903   PluginDatumProcessor proc;
904
905   /**
906    * Closure for proc.
907    */
908   void *proc_cls;
909 };
910
911
912 /**
913  * Wrapper for the processor for 'mysql_plugin_get_replication'.
914  * Decrements the replication counter and calls the original
915  * iterator.
916  *
917  * @param cls closure
918  * @param key key for the content
919  * @param size number of bytes in data
920  * @param data content stored
921  * @param type type of the content
922  * @param priority priority of the content
923  * @param anonymity anonymity-level for the content
924  * @param expiration expiration time for the content
925  * @param uid unique identifier for the datum;
926  *        maybe 0 if no unique identifier is available
927  *
928  * @return GNUNET_SYSERR to abort the iteration, GNUNET_OK to continue
929  *         (continue on call to "next", of course),
930  *         GNUNET_NO to delete the item and continue (if supported)
931  */
932 static int
933 repl_proc (void *cls, const struct GNUNET_HashCode * key, uint32_t size,
934            const void *data, enum GNUNET_BLOCK_Type type, uint32_t priority,
935            uint32_t anonymity, struct GNUNET_TIME_Absolute expiration,
936            uint64_t uid)
937 {
938   struct ReplCtx *rc = cls;
939   struct Plugin *plugin = rc->plugin;
940   int ret;
941   int iret;
942
943   ret =
944       rc->proc (rc->proc_cls, key, size, data, type, priority, anonymity,
945                 expiration, uid);
946   if (NULL != key)
947   {
948       struct GNUNET_MY_QueryParam params_proc[] = {
949         GNUNET_MY_query_param_uint64 (&uid),
950         GNUNET_MY_query_param_end
951       };
952
953       iret =
954       GNUNET_MY_exec_prepared (plugin->mc,
955                                plugin->dec_repl,
956                                params_proc);
957     if (iret == GNUNET_SYSERR)
958     {
959       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
960                   "Failed to reduce replication counter\n");
961       return GNUNET_SYSERR;
962     }
963   }
964   return ret;
965 }
966
967
968 /**
969  * Get a random item for replication.  Returns a single, not expired,
970  * random item from those with the highest replication counters.  The
971  * item's replication counter is decremented by one IF it was positive
972  * before.  Call 'proc' with all values ZERO or NULL if the datastore
973  * is empty.
974  *
975  * @param cls closure
976  * @param proc function to call the value (once only).
977  * @param proc_cls closure for proc
978  */
979 static void
980 mysql_plugin_get_replication (void *cls, PluginDatumProcessor proc,
981                               void *proc_cls)
982 {
983   struct Plugin *plugin = cls;
984   struct ReplCtx rc;
985   unsigned long long rvalue;
986   //unsigned long repl;
987   uint32_t repl;
988   MYSQL_BIND results;
989
990   rc.plugin = plugin;
991   rc.proc = proc;
992   rc.proc_cls = proc_cls;
993   memset (&results, 0, sizeof (results));
994   results.buffer_type = MYSQL_TYPE_LONG;
995   results.buffer = &repl;
996   results.is_unsigned = GNUNET_YES;
997
998   struct GNUNET_MY_QueryParam params_get[] = {
999     GNUNET_MY_query_param_end
1000   };
1001
1002   struct GNUNET_MY_ResultSpec results_get[] = {
1003     GNUNET_MY_result_spec_uint32 (&repl),
1004     GNUNET_MY_result_spec_end
1005   };
1006 /*
1007   if (1 !=
1008       GNUNET_MYSQL_statement_run_prepared_select (plugin->mc, plugin->max_repl, 1, &results, NULL, NULL, -1))
1009   {
1010     proc (proc_cls, NULL, 0, NULL, 0, 0, 0, GNUNET_TIME_UNIT_ZERO_ABS, 0);
1011     return;
1012   }
1013 */
1014   if (1 !=
1015       GNUNET_MY_exec_prepared (plugin->mc, plugin->max_repl, params_get))
1016   {
1017     proc (proc_cls, NULL, 0, NULL, 0, 0, 0, GNUNET_TIME_UNIT_ZERO_ABS, 0);
1018     return;
1019   }
1020
1021   if (1 !=
1022       GNUNET_MY_extract_result (plugin->max_repl, results_get))
1023   {
1024       proc (proc_cls, NULL, 0, NULL, 0, 0, 0, GNUNET_TIME_UNIT_ZERO_ABS, 0);
1025       return;
1026   }
1027
1028   rvalue =
1029       (unsigned long long) GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK,
1030                                                      UINT64_MAX);
1031   execute_select (plugin, plugin->select_replication, &repl_proc, &rc,
1032                   MYSQL_TYPE_LONG, &repl, GNUNET_YES, MYSQL_TYPE_LONGLONG,
1033                   &rvalue, GNUNET_YES, MYSQL_TYPE_LONG, &repl, GNUNET_YES,
1034                   MYSQL_TYPE_LONGLONG, &rvalue, GNUNET_YES, -1);
1035
1036 }
1037
1038
1039 /**
1040  * Get all of the keys in the datastore.
1041  *
1042  * @param cls closure
1043  * @param proc function to call on each key
1044  * @param proc_cls closure for proc
1045  */
1046 static void
1047 mysql_plugin_get_keys (void *cls,
1048                         PluginKeyProcessor proc,
1049                         void *proc_cls)
1050 {
1051   struct Plugin *plugin = cls;
1052 //  const char *query = "SELECT hash FROM gn090";
1053   char *query = "SELECT hash FROM gn090";
1054   int ret;
1055   MYSQL_STMT *statement;
1056   struct GNUNET_MYSQL_StatementHandle *statements_handle_select = NULL;
1057
1058
1059   struct GNUNET_HashCode key;
1060 //  MYSQL_BIND cbind[1];
1061 //  unsigned long length;
1062
1063   statement = GNUNET_MYSQL_statement_get_stmt (plugin->get_all_keys);
1064
1065
1066   statements_handle_select = GNUNET_MYSQL_statement_prepare (plugin->mc,
1067                                                              query);
1068 /*
1069   if (statement == NULL)
1070   {
1071     GNUNET_MYSQL_statements_invalidate (plugin->mc);
1072     proc (proc_cls, NULL, 0);
1073     return;
1074   }
1075
1076   if (mysql_stmt_prepare (statement, query, strlen (query)))
1077   {
1078     GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, "mysql",
1079                      _("Failed to prepare statement `%s'\n"), query);
1080     GNUNET_MYSQL_statements_invalidate (plugin->mc);
1081     proc (proc_cls, NULL, 0);
1082     return;
1083   }
1084 */
1085   GNUNET_assert (proc != NULL);
1086
1087   struct GNUNET_MY_QueryParam params_select[] = {
1088     GNUNET_MY_query_param_end
1089   };
1090
1091   struct GNUNET_MY_ResultSpec results_select[] = {
1092     GNUNET_MY_result_spec_auto_from_type (&key),
1093     GNUNET_MY_result_spec_end
1094   };
1095
1096   if (GNUNET_OK != GNUNET_MY_exec_prepared (plugin->mc,
1097                               statements_handle_select,
1098                               params_select))
1099   {
1100     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1101                 _("`%s' for `%s' failed at %s:%d with error: %s\n"),
1102                 "mysql_stmt_execute", query, __FILE__, __LINE__,
1103                 mysql_stmt_error (statement));
1104     GNUNET_MYSQL_statements_invalidate (plugin->mc);
1105     proc (proc_cls, NULL, 0);
1106     return;
1107   }
1108
1109   ret = GNUNET_MY_extract_result (statements_handle_select,
1110                                   results_select);
1111 /*  if (mysql_stmt_execute (statement))
1112   {
1113     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1114                 _("`%s' for `%s' failed at %s:%d with error: %s\n"),
1115                 "mysql_stmt_execute", query, __FILE__, __LINE__,
1116                 mysql_stmt_error (statement));
1117     GNUNET_MYSQL_statements_invalidate (plugin->mc);
1118     proc (proc_cls, NULL, 0);
1119     return;
1120   }
1121   memset (cbind, 0, sizeof (cbind));
1122   cbind[0].buffer_type = MYSQL_TYPE_BLOB;
1123   cbind[0].buffer = &key;
1124   cbind[0].buffer_length = sizeof (key);
1125   cbind[0].length = &length;
1126   cbind[0].is_unsigned = GNUNET_NO;
1127   if (mysql_stmt_bind_result (statement, cbind))
1128   {
1129     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1130                 _("`%s' failed at %s:%d with error: %s\n"),
1131                 "mysql_stmt_bind_result", __FILE__, __LINE__,
1132                 mysql_stmt_error (statement));
1133     GNUNET_MYSQL_statements_invalidate (plugin->mc);
1134     proc (proc_cls, NULL, 0);
1135     return;
1136   }
1137   while (0 == (ret = mysql_stmt_fetch (statement)))
1138   {
1139     if (sizeof (struct GNUNET_HashCode) == length)
1140       proc (proc_cls, &key, 1);
1141   }
1142   proc (proc_cls, NULL, 0);
1143 */
1144   if (ret != MYSQL_NO_DATA)
1145   {
1146     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1147                 _("`%s' failed at %s:%d with error: %s\n"),
1148                      "mysql_stmt_fetch", __FILE__, __LINE__,
1149                      mysql_stmt_error (statement));
1150     GNUNET_MYSQL_statements_invalidate (plugin->mc);
1151     return;
1152   }
1153
1154   mysql_stmt_reset (statement);
1155 }
1156
1157
1158 /**
1159  * Context for 'expi_proc' function.
1160  */
1161 struct ExpiCtx
1162 {
1163
1164   /**
1165    * Plugin handle.
1166    */
1167   struct Plugin *plugin;
1168
1169   /**
1170    * Function to call for the result (or the NULL).
1171    */
1172   PluginDatumProcessor proc;
1173
1174   /**
1175    * Closure for proc.
1176    */
1177   void *proc_cls;
1178 };
1179
1180
1181
1182 /**
1183  * Wrapper for the processor for 'mysql_plugin_get_expiration'.
1184  * If no expired value was found, we do a second query for
1185  * low-priority content.
1186  *
1187  * @param cls closure
1188  * @param key key for the content
1189  * @param size number of bytes in data
1190  * @param data content stored
1191  * @param type type of the content
1192  * @param priority priority of the content
1193  * @param anonymity anonymity-level for the content
1194  * @param expiration expiration time for the content
1195  * @param uid unique identifier for the datum;
1196  *        maybe 0 if no unique identifier is available
1197  *
1198  * @return GNUNET_SYSERR to abort the iteration, GNUNET_OK to continue
1199  *         (continue on call to "next", of course),
1200  *         GNUNET_NO to delete the item and continue (if supported)
1201  */
1202 static int
1203 expi_proc (void *cls, const struct GNUNET_HashCode * key, uint32_t size,
1204            const void *data, enum GNUNET_BLOCK_Type type, uint32_t priority,
1205            uint32_t anonymity, struct GNUNET_TIME_Absolute expiration,
1206            uint64_t uid)
1207 {
1208   struct ExpiCtx *rc = cls;
1209   struct Plugin *plugin = rc->plugin;
1210
1211   if (NULL == key)
1212   {
1213     execute_select (plugin, plugin->select_priority, rc->proc, rc->proc_cls,
1214                     -1);
1215     return GNUNET_SYSERR;
1216   }
1217   return rc->proc (rc->proc_cls, key, size, data, type, priority, anonymity,
1218                    expiration, uid);
1219 }
1220
1221
1222 /**
1223  * Get a random item for expiration.
1224  * Call 'proc' with all values ZERO or NULL if the datastore is empty.
1225  *
1226  * @param cls closure
1227  * @param proc function to call the value (once only).
1228  * @param proc_cls closure for proc
1229  */
1230 static void
1231 mysql_plugin_get_expiration (void *cls, PluginDatumProcessor proc,
1232                              void *proc_cls)
1233 {
1234   struct Plugin *plugin = cls;
1235   long long nt;
1236   struct ExpiCtx rc;
1237
1238   rc.plugin = plugin;
1239   rc.proc = proc;
1240   rc.proc_cls = proc_cls;
1241   nt = (long long) GNUNET_TIME_absolute_get ().abs_value_us;
1242   execute_select (plugin, plugin->select_expiration, expi_proc, &rc,
1243                   MYSQL_TYPE_LONGLONG, &nt, GNUNET_YES, -1);
1244
1245 }
1246
1247
1248 /**
1249  * Drop database.
1250  *
1251  * @param cls the "struct Plugin*"
1252  */
1253 static void
1254 mysql_plugin_drop (void *cls)
1255 {
1256   struct Plugin *plugin = cls;
1257
1258   if (GNUNET_OK != GNUNET_MYSQL_statement_run (plugin->mc, "DROP TABLE gn090"))
1259     return;                     /* error */
1260   plugin->env->duc (plugin->env->cls, 0);
1261 }
1262
1263
1264 /**
1265  * Entry point for the plugin.
1266  *
1267  * @param cls the "struct GNUNET_DATASTORE_PluginEnvironment*"
1268  * @return our "struct Plugin*"
1269  */
1270 void *
1271 libgnunet_plugin_datastore_mysql_init (void *cls)
1272 {
1273   struct GNUNET_DATASTORE_PluginEnvironment *env = cls;
1274   struct GNUNET_DATASTORE_PluginFunctions *api;
1275   struct Plugin *plugin;
1276
1277   plugin = GNUNET_new (struct Plugin);
1278   plugin->env = env;
1279   plugin->mc = GNUNET_MYSQL_context_create (env->cfg, "datastore-mysql");
1280   if (NULL == plugin->mc)
1281   {
1282     GNUNET_free (plugin);
1283     return NULL;
1284   }
1285 #define MRUNS(a) (GNUNET_OK != GNUNET_MYSQL_statement_run (plugin->mc, a) )
1286 #define PINIT(a,b) (NULL == (a = GNUNET_MYSQL_statement_prepare (plugin->mc, b)))
1287   if (MRUNS
1288       ("CREATE TABLE IF NOT EXISTS gn090 ("
1289        " repl INT(11) UNSIGNED NOT NULL DEFAULT 0,"
1290        " type INT(11) UNSIGNED NOT NULL DEFAULT 0,"
1291        " prio INT(11) UNSIGNED NOT NULL DEFAULT 0,"
1292        " anonLevel INT(11) UNSIGNED NOT NULL DEFAULT 0,"
1293        " expire BIGINT UNSIGNED NOT NULL DEFAULT 0,"
1294        " rvalue BIGINT UNSIGNED NOT NULL,"
1295        " hash BINARY(64) NOT NULL DEFAULT '',"
1296        " vhash BINARY(64) NOT NULL DEFAULT '',"
1297        " value BLOB NOT NULL DEFAULT ''," " uid BIGINT NOT NULL AUTO_INCREMENT,"
1298        " PRIMARY KEY (uid)," " INDEX idx_hash (hash(64)),"
1299        " INDEX idx_hash_uid (hash(64),uid),"
1300        " INDEX idx_hash_vhash (hash(64),vhash(64)),"
1301        " INDEX idx_hash_type_uid (hash(64),type,rvalue),"
1302        " INDEX idx_prio (prio)," " INDEX idx_repl_rvalue (repl,rvalue),"
1303        " INDEX idx_expire (expire),"
1304        " INDEX idx_anonLevel_type_rvalue (anonLevel,type,rvalue)"
1305        ") ENGINE=InnoDB") || MRUNS ("SET AUTOCOMMIT = 1") ||
1306       PINIT (plugin->insert_entry, INSERT_ENTRY) ||
1307       PINIT (plugin->delete_entry_by_uid, DELETE_ENTRY_BY_UID) ||
1308       PINIT (plugin->select_entry_by_hash, SELECT_ENTRY_BY_HASH) ||
1309       PINIT (plugin->select_entry_by_hash_and_vhash,
1310              SELECT_ENTRY_BY_HASH_AND_VHASH) ||
1311       PINIT (plugin->select_entry_by_hash_and_type,
1312              SELECT_ENTRY_BY_HASH_AND_TYPE) ||
1313       PINIT (plugin->select_entry_by_hash_vhash_and_type,
1314              SELECT_ENTRY_BY_HASH_VHASH_AND_TYPE) ||
1315       PINIT (plugin->count_entry_by_hash, COUNT_ENTRY_BY_HASH) ||
1316       PINIT (plugin->get_size, SELECT_SIZE) ||
1317       PINIT (plugin->count_entry_by_hash_and_vhash,
1318              COUNT_ENTRY_BY_HASH_AND_VHASH) ||
1319       PINIT (plugin->count_entry_by_hash_and_type, COUNT_ENTRY_BY_HASH_AND_TYPE)
1320       || PINIT (plugin->count_entry_by_hash_vhash_and_type,
1321                 COUNT_ENTRY_BY_HASH_VHASH_AND_TYPE) ||
1322       PINIT (plugin->update_entry, UPDATE_ENTRY) ||
1323       PINIT (plugin->dec_repl, DEC_REPL) ||
1324       PINIT (plugin->zero_iter, SELECT_IT_NON_ANONYMOUS) ||
1325       PINIT (plugin->select_expiration, SELECT_IT_EXPIRATION) ||
1326       PINIT (plugin->select_priority, SELECT_IT_PRIORITY) ||
1327       PINIT (plugin->max_repl, SELECT_MAX_REPL) ||
1328       PINIT (plugin->get_all_keys, GET_ALL_KEYS) ||
1329       PINIT (plugin->select_replication, SELECT_IT_REPLICATION))
1330   {
1331     GNUNET_MYSQL_context_destroy (plugin->mc);
1332     GNUNET_free (plugin);
1333     return NULL;
1334   }
1335 #undef PINIT
1336 #undef MRUNS
1337
1338   api = GNUNET_new (struct GNUNET_DATASTORE_PluginFunctions);
1339   api->cls = plugin;
1340   api->estimate_size = &mysql_plugin_estimate_size;
1341   api->put = &mysql_plugin_put;
1342   api->update = &mysql_plugin_update;
1343   api->get_key = &mysql_plugin_get_key;
1344   api->get_replication = &mysql_plugin_get_replication;
1345   api->get_expiration = &mysql_plugin_get_expiration;
1346   api->get_zero_anonymity = &mysql_plugin_get_zero_anonymity;
1347   api->get_keys = &mysql_plugin_get_keys;
1348   api->drop = &mysql_plugin_drop;
1349   GNUNET_log_from (GNUNET_ERROR_TYPE_INFO, "mysql",
1350                    _("Mysql database running\n"));
1351   return api;
1352 }
1353
1354
1355 /**
1356  * Exit point from the plugin.
1357  * @param cls our "struct Plugin*"
1358  * @return always NULL
1359  */
1360 void *
1361 libgnunet_plugin_datastore_mysql_done (void *cls)
1362 {
1363   struct GNUNET_DATASTORE_PluginFunctions *api = cls;
1364   struct Plugin *plugin = api->cls;
1365
1366   GNUNET_MYSQL_context_destroy (plugin->mc);
1367   GNUNET_free (plugin);
1368   GNUNET_free (api);
1369   return NULL;
1370 }
1371
1372 /* end of plugin_datastore_mysql.c */