2 This file is part of GNUnet
3 (C) 2009, 2010 Christian Grothoff (and other contributing authors)
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.
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.
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.
22 * @file datastore/plugin_datastore_mysql.c
23 * @brief mysql-based datastore backend
24 * @author Igor Wronsky
25 * @author Christian Grothoff
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).
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!
45 * - Memory usage (Comment: "I have 1G and it never caused me trouble")
48 * MANUAL SETUP INSTRUCTIONS
50 * 1) in /etc/gnunet.conf, set
55 * 2) Then access mysql as root,
61 * and do the following. [You should replace $USER with the username
62 * that will be running the gnunetd process].
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');
72 * 3) In the $HOME directory of $USER, create a ".my.cnf" file
73 * with the following lines
78 password=$the_password_you_like
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,
89 * 4) Still, perhaps you should briefly try if the DB connection
90 * works. First, login as $USER. Then use,
93 * $ mysql -u $USER -p $the_password_you_like
97 * If you get the message "Database changed" it probably works.
99 * [If you get "ERROR 2002: Can't connect to local MySQL server
100 * through socket '/tmp/mysql.sock' (2)" it may be resolvable by
101 * "ln -s /var/run/mysqld/mysqld.sock /tmp/mysql.sock"
102 * so there may be some additional trouble depending on your mysql setup.]
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;
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.
124 * - use FOREIGN KEY for 'uid/vkey'
125 * - consistent naming of uid/vkey
128 #include "platform.h"
129 #include "plugin_datastore.h"
130 #include "gnunet_util_lib.h"
131 #include <mysql/mysql.h>
133 #define DEBUG_MYSQL GNUNET_NO
135 #define MAX_DATUM_SIZE 65536
138 * Maximum number of supported parameters for a prepared
139 * statement. Increase if needed.
144 * Die with an error message that indicates
145 * a failure of the command 'cmd' with the message given
146 * by strerror(errno).
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);
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).
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);
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) " \
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"
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) "\
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"
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) "\
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"
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) "\
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"
194 // #define SELECT_SIZE "SELECT SUM(BIT_LENGTH(value) DIV 8) FROM gn072"
197 struct GNUNET_MysqlStatementHandle
199 struct GNUNET_MysqlStatementHandle *next;
201 struct GNUNET_MysqlStatementHandle *prev;
205 MYSQL_STMT *statement;
212 * Context for the universal iterator.
214 struct NextRequestClosure;
217 * Type of a function that will prepare
218 * the next iteration.
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
227 typedef int (*PrepareFunction)(void *cls,
228 struct NextRequestClosure *nc);
231 struct NextRequestClosure
233 struct Plugin *plugin;
235 struct GNUNET_TIME_Absolute now;
238 * Function to call to prepare the next
241 PrepareFunction prep;
252 unsigned int iter_select;
254 PluginIterator dviter;
258 unsigned int last_prio;
260 unsigned long long last_expire;
262 unsigned long long last_vkey;
269 * Context for all functions in this plugin.
274 * Our execution environment.
276 struct GNUNET_DATASTORE_PluginEnvironment *env;
280 struct GNUNET_MysqlStatementHandle *shead;
282 struct GNUNET_MysqlStatementHandle *stail;
285 * Filename of "my.cnf" (msyql configuration).
290 * Closure of the 'next_task' (must be freed if 'next_task' is cancelled).
292 struct NextRequestClosure *next_task_nc;
295 * Pending task with scheduler for running the next request.
297 GNUNET_SCHEDULER_TaskIdentifier next_task;
300 * Statements dealing with gn072 table
302 #define SELECT_VALUE "SELECT value FROM gn072 WHERE vkey=?"
303 struct GNUNET_MysqlStatementHandle *select_value;
305 #define DELETE_VALUE "DELETE FROM gn072 WHERE vkey=?"
306 struct GNUNET_MysqlStatementHandle *delete_value;
308 #define INSERT_VALUE "INSERT INTO gn072 (value) VALUES (?)"
309 struct GNUNET_MysqlStatementHandle *insert_value;
312 * Statements dealing with gn090 table
314 #define INSERT_ENTRY "INSERT INTO gn090 (type,prio,anonLevel,expire,hash,vhash,vkey) VALUES (?,?,?,?,?,?,?)"
315 struct GNUNET_MysqlStatementHandle *insert_entry;
317 #define DELETE_ENTRY_BY_VKEY "DELETE FROM gn090 WHERE vkey=?"
318 struct GNUNET_MysqlStatementHandle *delete_entry_by_vkey;
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;
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;
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;
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;
332 #define COUNT_ENTRY_BY_HASH "SELECT count(*) FROM gn090 FORCE INDEX (hash) WHERE hash=?"
333 struct GNUNET_MysqlStatementHandle *count_entry_by_hash;
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;
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;
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;
344 #define UPDATE_ENTRY "UPDATE gn090 SET prio=prio+?,expire=IF(expire>=?,expire,?) WHERE vkey=?"
345 struct GNUNET_MysqlStatementHandle *update_entry;
347 struct GNUNET_MysqlStatementHandle *iter[4];
350 * Size of the mysql database on disk.
352 unsigned long long content_size;
358 * Obtain the location of ".my.cnf".
359 * @return NULL on error
362 get_my_cnf_path (const struct GNUNET_CONFIGURATION_Handle *cfg)
373 pw = getpwuid (getuid ());
376 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
381 GNUNET_CONFIGURATION_have_value (cfg,
382 "datastore-mysql", "CONFIG"))
384 GNUNET_assert (GNUNET_OK ==
385 GNUNET_CONFIGURATION_get_value_filename (cfg,
386 "datastore-mysql", "CONFIG", &cnffile));
387 configured = GNUNET_YES;
391 home_dir = GNUNET_strdup (pw->pw_dir);
393 home_dir = (char *) GNUNET_malloc (_MAX_PATH + 1);
394 plibc_conv_to_win_path ("~/", home_dir);
396 GNUNET_asprintf (&cnffile, "%s/.my.cnf", home_dir);
397 GNUNET_free (home_dir);
398 configured = GNUNET_NO;
400 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
401 _("Trying to use file `%s' for MySQL configuration.\n"),
403 if ((0 != STAT (cnffile, &st)) ||
404 (0 != ACCESS (cnffile, R_OK)) || (!S_ISREG (st.st_mode)))
406 if (configured == GNUNET_YES)
407 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
408 _("Could not access file `%s': %s\n"), cnffile,
410 GNUNET_free (cnffile);
419 * Free a prepared statement.
422 prepared_statement_destroy (struct Plugin *plugin,
423 struct GNUNET_MysqlStatementHandle
426 GNUNET_CONTAINER_DLL_remove (plugin->shead,
430 mysql_stmt_close (s->statement);
431 GNUNET_free (s->query);
437 * Close database connection and all prepared statements (we got a DB
441 iclose (struct Plugin *plugin)
443 struct GNUNET_MysqlStatementHandle *spos;
445 spos = plugin->shead;
446 while (NULL != plugin->shead)
447 prepared_statement_destroy (plugin,
449 if (plugin->dbf != NULL)
451 mysql_close (plugin->dbf);
459 * Open the connection with the database (and initialize
460 * our default options).
462 * @return GNUNET_OK on success
465 iopen (struct Plugin *ret)
470 char *mysql_password;
471 unsigned long long mysql_port;
473 unsigned int timeout;
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");
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);
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",
497 mysql_dbname = GNUNET_strdup ("gnunet");
499 if (GNUNET_YES == GNUNET_CONFIGURATION_have_value (ret->env->cfg,
500 "datastore-mysql", "USER"))
502 GNUNET_assert (GNUNET_OK ==
503 GNUNET_CONFIGURATION_get_value_string (ret->env->cfg,
504 "datastore-mysql", "USER",
507 mysql_password = NULL;
508 if (GNUNET_YES == GNUNET_CONFIGURATION_have_value (ret->env->cfg,
509 "datastore-mysql", "PASSWORD"))
511 GNUNET_assert (GNUNET_OK ==
512 GNUNET_CONFIGURATION_get_value_string (ret->env->cfg,
513 "datastore-mysql", "PASSWORD",
517 if (GNUNET_YES == GNUNET_CONFIGURATION_have_value (ret->env->cfg,
518 "datastore-mysql", "HOST"))
520 GNUNET_assert (GNUNET_OK ==
521 GNUNET_CONFIGURATION_get_value_string (ret->env->cfg,
522 "datastore-mysql", "HOST",
526 if (GNUNET_YES == GNUNET_CONFIGURATION_have_value (ret->env->cfg,
527 "datastore-mysql", "PORT"))
529 GNUNET_assert (GNUNET_OK ==
530 GNUNET_CONFIGURATION_get_value_number (ret->env->cfg, "datastore-mysql",
531 "PORT", &mysql_port));
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])
544 LOG_MYSQL (GNUNET_ERROR_TYPE_ERROR,
545 "mysql_real_connect", ret);
546 return GNUNET_SYSERR;
553 * Run the given MySQL statement.
555 * @return GNUNET_OK on success, GNUNET_SYSERR on error
558 run_statement (struct Plugin *plugin,
559 const char *statement)
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])
566 LOG_MYSQL (GNUNET_ERROR_TYPE_ERROR,
567 "mysql_query", plugin);
569 return GNUNET_SYSERR;
576 * Create a prepared statement.
578 * @return NULL on error
580 static struct GNUNET_MysqlStatementHandle *
581 prepared_statement_create (struct Plugin *plugin,
582 const char *statement)
584 struct GNUNET_MysqlStatementHandle *ret;
586 ret = GNUNET_malloc (sizeof (struct GNUNET_MysqlStatementHandle));
587 ret->query = GNUNET_strdup (statement);
588 GNUNET_CONTAINER_DLL_insert (plugin->shead,
596 * Prepare a statement for running.
598 * @return GNUNET_OK on success
601 prepare_statement (struct Plugin *plugin,
602 struct GNUNET_MysqlStatementHandle *ret)
604 if (GNUNET_YES == ret->valid)
606 if ((NULL == plugin->dbf) &&
607 (GNUNET_OK != iopen (plugin)))
608 return GNUNET_SYSERR;
609 ret->statement = mysql_stmt_init (plugin->dbf);
610 if (ret->statement == NULL)
613 return GNUNET_SYSERR;
615 if (mysql_stmt_prepare (ret->statement,
617 strlen (ret->query)))
619 LOG_MYSQL (GNUNET_ERROR_TYPE_ERROR,
620 "mysql_stmt_prepare",
622 mysql_stmt_close (ret->statement);
623 ret->statement = NULL;
625 return GNUNET_SYSERR;
627 ret->valid = GNUNET_YES;
634 * Bind the parameters for the given MySQL statement
637 * @param s statement to bind and run
638 * @param ap arguments for the binding
639 * @return GNUNET_SYSERR on error, GNUNET_OK on success
642 init_params (struct Plugin *plugin,
643 struct GNUNET_MysqlStatementHandle *s,
646 MYSQL_BIND qbind[MAX_PARAM];
649 enum enum_field_types ft;
651 pc = mysql_stmt_param_count (s->statement);
654 /* increase internal constant! */
656 return GNUNET_SYSERR;
658 memset (qbind, 0, sizeof (qbind));
661 while ((pc > 0) && (-1 != (ft = va_arg (ap, enum enum_field_types))))
663 qbind[off].buffer_type = ft;
666 case MYSQL_TYPE_FLOAT:
667 qbind[off].buffer = va_arg (ap, float *);
669 case MYSQL_TYPE_LONGLONG:
670 qbind[off].buffer = va_arg (ap, unsigned long long *);
671 qbind[off].is_unsigned = va_arg (ap, int);
673 case MYSQL_TYPE_LONG:
674 qbind[off].buffer = va_arg (ap, unsigned int *);
675 qbind[off].is_unsigned = va_arg (ap, int);
677 case MYSQL_TYPE_VAR_STRING:
678 case MYSQL_TYPE_STRING:
679 case MYSQL_TYPE_BLOB:
680 qbind[off].buffer = va_arg (ap, void *);
681 qbind[off].buffer_length = va_arg (ap, unsigned long);
682 qbind[off].length = va_arg (ap, unsigned long *);
685 /* unsupported type */
687 return GNUNET_SYSERR;
692 if (!((pc == 0) && (ft != -1) && (va_arg (ap, int) == -1)))
695 return GNUNET_SYSERR;
697 if (mysql_stmt_bind_param (s->statement, qbind))
699 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
700 _("`%s' failed at %s:%d with error: %s\n"),
701 "mysql_stmt_bind_param",
702 __FILE__, __LINE__, mysql_stmt_error (s->statement));
704 return GNUNET_SYSERR;
706 if (mysql_stmt_execute (s->statement))
708 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
709 _("`%s' failed at %s:%d with error: %s\n"),
710 "mysql_stmt_execute",
711 __FILE__, __LINE__, mysql_stmt_error (s->statement));
713 return GNUNET_SYSERR;
719 * Type of a callback that will be called for each
720 * data set returned from MySQL.
722 * @param cls user-defined argument
723 * @param num_values number of elements in values
724 * @param values values returned by MySQL
725 * @return GNUNET_OK to continue iterating, GNUNET_SYSERR to abort
727 typedef int (*GNUNET_MysqlDataProcessor) (void *cls,
728 unsigned int num_values,
729 MYSQL_BIND * values);
733 * Run a prepared SELECT statement.
735 * @param result_size number of elements in results array
736 * @param results pointer to already initialized MYSQL_BIND
737 * array (of sufficient size) for passing results
738 * @param processor function to call on each result
739 * @param processor_cls extra argument to processor
740 * @param ... pairs and triplets of "MYSQL_TYPE_XXX" keys and their respective
741 * values (size + buffer-reference for pointers); terminated
743 * @return GNUNET_SYSERR on error, otherwise
744 * the number of successfully affected (or queried) rows
747 prepared_statement_run_select (struct Plugin *plugin,
748 struct GNUNET_MysqlStatementHandle
750 unsigned int result_size,
751 MYSQL_BIND * results,
752 GNUNET_MysqlDataProcessor
753 processor, void *processor_cls,
761 if (GNUNET_OK != prepare_statement (plugin, s))
764 return GNUNET_SYSERR;
766 va_start (ap, processor_cls);
767 if (GNUNET_OK != init_params (plugin, s, ap))
771 return GNUNET_SYSERR;
774 rsize = mysql_stmt_field_count (s->statement);
775 if (rsize > result_size)
778 return GNUNET_SYSERR;
780 if (mysql_stmt_bind_result (s->statement, results))
782 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
783 _("`%s' failed at %s:%d with error: %s\n"),
784 "mysql_stmt_bind_result",
785 __FILE__, __LINE__, mysql_stmt_error (s->statement));
787 return GNUNET_SYSERR;
793 ret = mysql_stmt_fetch (s->statement);
794 if (ret == MYSQL_NO_DATA)
798 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
799 _("`%s' failed at %s:%d with error: %s\n"),
801 __FILE__, __LINE__, mysql_stmt_error (s->statement));
803 return GNUNET_SYSERR;
805 if (processor != NULL)
806 if (GNUNET_OK != processor (processor_cls, rsize, results))
810 mysql_stmt_reset (s->statement);
816 * Run a prepared statement that does NOT produce results.
818 * @param ... pairs and triplets of "MYSQL_TYPE_XXX" keys and their respective
819 * values (size + buffer-reference for pointers); terminated
821 * @param insert_id NULL or address where to store the row ID of whatever
822 * was inserted (only for INSERT statements!)
823 * @return GNUNET_SYSERR on error, otherwise
824 * the number of successfully affected rows
827 prepared_statement_run (struct Plugin *plugin,
828 struct GNUNET_MysqlStatementHandle *s,
829 unsigned long long *insert_id, ...)
834 if (GNUNET_OK != prepare_statement (plugin, s))
835 return GNUNET_SYSERR;
836 va_start (ap, insert_id);
837 if (GNUNET_OK != init_params (plugin, s, ap))
840 return GNUNET_SYSERR;
843 affected = mysql_stmt_affected_rows (s->statement);
844 if (NULL != insert_id)
845 *insert_id = (unsigned long long) mysql_stmt_insert_id (s->statement);
846 mysql_stmt_reset (s->statement);
852 * Delete an value from the gn072 table.
854 * @param vkey vkey identifying the value to delete
855 * @return GNUNET_OK on success, GNUNET_NO if no such value exists, GNUNET_SYSERR on error
858 do_delete_value (struct Plugin *plugin,
859 unsigned long long vkey)
864 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
865 "Deleting value %llu from gn072 table\n",
868 ret = prepared_statement_run (plugin,
869 plugin->delete_value,
872 &vkey, GNUNET_YES, -1);
879 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
880 "Deleting value %llu from gn072 table failed\n",
887 * Insert a value into the gn072 table.
889 * @param value the value to insert
890 * @param size size of the value
891 * @param vkey vkey identifying the value henceforth (set)
892 * @return GNUNET_OK on success, GNUNET_SYSERR on error
895 do_insert_value (struct Plugin *plugin,
896 const void *value, unsigned int size,
897 unsigned long long *vkey)
899 unsigned long length = size;
902 ret = prepared_statement_run (plugin,
903 plugin->insert_value,
906 value, length, &length, -1);
907 if (ret == GNUNET_OK)
910 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
911 "Inserted value number %llu with length %u into gn072 table\n",
918 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
919 "Failed to insert %u byte value into gn072 table\n",
926 * Delete an entry from the gn090 table.
928 * @param vkey vkey identifying the entry to delete
929 * @return GNUNET_OK on success, GNUNET_NO if no such value exists, GNUNET_SYSERR on error
932 do_delete_entry_by_vkey (struct Plugin *plugin,
933 unsigned long long vkey)
938 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
939 "Deleting value %llu from gn090 table\n",
942 ret = prepared_statement_run (plugin,
943 plugin->delete_entry_by_vkey,
946 &vkey, GNUNET_YES, -1);
953 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
954 "Deleting value %llu from gn090 table failed\n",
961 return_ok (void *cls, unsigned int num_values, MYSQL_BIND * values)
968 iterator_helper_prepare (void *cls,
969 struct NextRequestClosure *nrc)
971 struct Plugin *plugin;
976 plugin = nrc->plugin;
978 switch (nrc->iter_select)
982 ret = prepared_statement_run_select (plugin,
983 plugin->iter[nrc->iter_select],
1002 ret = prepared_statement_run_select (plugin,
1003 plugin->iter[nrc->iter_select],
1008 MYSQL_TYPE_LONGLONG,
1011 MYSQL_TYPE_LONGLONG,
1014 MYSQL_TYPE_LONGLONG,
1017 MYSQL_TYPE_LONGLONG,
1022 ret = prepared_statement_run_select (plugin,
1023 plugin->iter[nrc->iter_select],
1028 MYSQL_TYPE_LONGLONG,
1031 MYSQL_TYPE_LONGLONG,
1034 MYSQL_TYPE_LONGLONG,
1037 MYSQL_TYPE_LONGLONG,
1040 MYSQL_TYPE_LONGLONG,
1043 MYSQL_TYPE_LONGLONG,
1055 * Continuation of "mysql_next_request".
1057 * @param next_cls the next context
1058 * @param tc the task context (unused)
1061 mysql_next_request_cont (void *next_cls,
1062 const struct GNUNET_SCHEDULER_TaskContext *tc)
1064 struct NextRequestClosure *nrc = next_cls;
1065 struct Plugin *plugin;
1068 unsigned int priority;
1069 unsigned int anonymity;
1070 unsigned long long exp;
1071 unsigned long long vkey;
1072 unsigned long hashSize;
1073 GNUNET_HashCode key;
1074 struct GNUNET_TIME_Absolute expiration;
1075 unsigned long length;
1076 MYSQL_BIND *rbind; /* size 7 */
1077 MYSQL_BIND dbind[1];
1078 char datum[GNUNET_SERVER_MAX_MESSAGE_SIZE];
1080 plugin = nrc->plugin;
1081 plugin->next_task = GNUNET_SCHEDULER_NO_TASK;
1082 plugin->next_task_nc = NULL;
1085 GNUNET_assert (nrc->plugin->next_task == GNUNET_SCHEDULER_NO_TASK);
1086 nrc->now = GNUNET_TIME_absolute_get ();
1087 hashSize = sizeof (GNUNET_HashCode);
1088 memset (nrc->rbind, 0, sizeof (nrc->rbind));
1090 rbind[0].buffer_type = MYSQL_TYPE_LONG;
1091 rbind[0].buffer = &type;
1092 rbind[0].is_unsigned = 1;
1093 rbind[1].buffer_type = MYSQL_TYPE_LONG;
1094 rbind[1].buffer = &priority;
1095 rbind[1].is_unsigned = 1;
1096 rbind[2].buffer_type = MYSQL_TYPE_LONG;
1097 rbind[2].buffer = &anonymity;
1098 rbind[2].is_unsigned = 1;
1099 rbind[3].buffer_type = MYSQL_TYPE_LONGLONG;
1100 rbind[3].buffer = &exp;
1101 rbind[3].is_unsigned = 1;
1102 rbind[4].buffer_type = MYSQL_TYPE_BLOB;
1103 rbind[4].buffer = &key;
1104 rbind[4].buffer_length = hashSize;
1105 rbind[4].length = &hashSize;
1106 rbind[5].buffer_type = MYSQL_TYPE_LONGLONG;
1107 rbind[5].buffer = &vkey;
1108 rbind[5].is_unsigned = GNUNET_YES;
1110 if ( (GNUNET_YES == nrc->end_it) ||
1111 (GNUNET_OK != nrc->prep (nrc->prep_cls,
1114 GNUNET_assert (nrc->plugin->next_task == GNUNET_SCHEDULER_NO_TASK);
1115 nrc->last_vkey = vkey;
1116 nrc->last_prio = priority;
1117 nrc->last_expire = exp;
1118 if ( (rbind[4].buffer_length != sizeof (GNUNET_HashCode)) ||
1119 (hashSize != sizeof (GNUNET_HashCode)) )
1125 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1126 "Found value %llu with prio %u, anon %u, expire %llu selecting from gn090 table\n",
1132 /* now do query on gn072 */
1133 length = sizeof (datum);
1134 memset (dbind, 0, sizeof (dbind));
1135 dbind[0].buffer_type = MYSQL_TYPE_BLOB;
1136 dbind[0].buffer_length = length;
1137 dbind[0].length = &length;
1138 dbind[0].buffer = datum;
1139 ret = prepared_statement_run_select (plugin,
1140 plugin->select_value,
1145 MYSQL_TYPE_LONGLONG,
1146 &vkey, GNUNET_YES, -1);
1147 GNUNET_break (ret <= 1); /* should only have one rbind! */
1150 if (ret != GNUNET_OK)
1153 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1154 _("Failed to obtain value %llu from table `%s'\n"),
1159 GNUNET_break (length <= sizeof(datum));
1161 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1162 "Calling iterator with value `%s' number %llu of size %u with type %u, priority %u, anonymity %u and expiration %llu\n",
1171 GNUNET_assert (nrc->plugin->next_task == GNUNET_SCHEDULER_NO_TASK);
1172 expiration.value = exp;
1173 ret = nrc->dviter (nrc->dviter_cls,
1183 if (ret == GNUNET_SYSERR)
1185 nrc->end_it = GNUNET_YES;
1188 if (ret == GNUNET_NO)
1190 do_delete_value (plugin, vkey);
1191 do_delete_entry_by_vkey (plugin, vkey);
1192 plugin->content_size -= length;
1196 /* call dviter with "end of set" */
1197 GNUNET_assert (nrc->plugin->next_task == GNUNET_SCHEDULER_NO_TASK);
1198 nrc->dviter (nrc->dviter_cls,
1199 NULL, NULL, 0, NULL, 0, 0, 0,
1200 GNUNET_TIME_UNIT_ZERO_ABS, 0);
1201 GNUNET_assert (nrc->plugin->next_task == GNUNET_SCHEDULER_NO_TASK);
1202 nrc->prep (nrc->prep_cls, NULL);
1203 GNUNET_assert (nrc->plugin->next_task == GNUNET_SCHEDULER_NO_TASK);
1209 * Function invoked on behalf of a "PluginIterator"
1210 * asking the database plugin to call the iterator
1211 * with the next item.
1213 * @param next_cls whatever argument was given
1214 * to the PluginIterator as "next_cls".
1215 * @param end_it set to GNUNET_YES if we
1216 * should terminate the iteration early
1217 * (iterator should be still called once more
1218 * to signal the end of the iteration).
1221 mysql_plugin_next_request (void *next_cls,
1224 struct NextRequestClosure *nrc = next_cls;
1226 if (GNUNET_YES == end_it)
1227 nrc->end_it = GNUNET_YES;
1228 nrc->plugin->next_task_nc = nrc;
1229 nrc->plugin->next_task = GNUNET_SCHEDULER_add_now (nrc->plugin->env->sched,
1230 &mysql_next_request_cont,
1236 * Iterate over the items in the datastore
1237 * using the given query to select and order
1240 * @param type entries of which type should be considered?
1241 * Use 0 for any type.
1242 * @param iter never NULL
1243 * @param is_asc are we using ascending order?
1246 iterateHelper (struct Plugin *plugin,
1249 unsigned int iter_select,
1250 PluginIterator dviter,
1253 struct NextRequestClosure *nrc;
1255 nrc = GNUNET_malloc (sizeof (struct NextRequestClosure));
1256 nrc->plugin = plugin;
1258 nrc->iter_select = iter_select;
1259 nrc->dviter = dviter;
1260 nrc->dviter_cls = dviter_cls;
1261 nrc->prep = &iterator_helper_prepare;
1266 nrc->last_expire = 0;
1270 nrc->last_prio = 0x7FFFFFFFL;
1271 nrc->last_vkey = 0x7FFFFFFFFFFFFFFFLL; /* MySQL only supports 63 bits */
1272 nrc->last_expire = 0x7FFFFFFFFFFFFFFFLL; /* MySQL only supports 63 bits */
1274 mysql_plugin_next_request (nrc, GNUNET_NO);
1279 * Get an estimate of how much space the database is
1282 * @param cls our "struct Plugin*"
1283 * @return number of bytes used on disk
1285 static unsigned long long
1286 mysql_plugin_get_size (void *cls)
1288 struct Plugin *plugin = cls;
1289 return plugin->content_size;
1294 * Store an item in the datastore.
1296 * @param cls closure
1297 * @param key key for the item
1298 * @param size number of bytes in data
1299 * @param data content stored
1300 * @param type type of the content
1301 * @param priority priority of the content
1302 * @param anonymity anonymity-level for the content
1303 * @param expiration expiration time for the content
1304 * @param msg set to error message
1305 * @return GNUNET_OK on success
1308 mysql_plugin_put (void *cls,
1309 const GNUNET_HashCode * key,
1312 enum GNUNET_BLOCK_Type type,
1315 struct GNUNET_TIME_Absolute expiration,
1318 struct Plugin *plugin = cls;
1319 unsigned int itype = type;
1320 unsigned int ipriority = priority;
1321 unsigned int ianonymity = anonymity;
1322 unsigned long long lexpiration = expiration.value;
1323 unsigned long hashSize;
1324 unsigned long hashSize2;
1325 unsigned long long vkey;
1326 GNUNET_HashCode vhash;
1328 if (size > MAX_DATUM_SIZE)
1331 return GNUNET_SYSERR;
1333 hashSize = sizeof (GNUNET_HashCode);
1334 hashSize2 = sizeof (GNUNET_HashCode);
1335 GNUNET_CRYPTO_hash (data, size, &vhash);
1336 if (GNUNET_OK != do_insert_value (plugin,
1338 return GNUNET_SYSERR;
1340 prepared_statement_run (plugin,
1341 plugin->insert_entry,
1352 MYSQL_TYPE_LONGLONG,
1363 MYSQL_TYPE_LONGLONG,
1364 &vkey, GNUNET_YES, -1))
1366 do_delete_value (plugin, vkey);
1367 return GNUNET_SYSERR;
1370 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1371 "Inserted value `%s' number %llu with size %u into gn090 table\n",
1374 (unsigned int) size);
1376 plugin->content_size += size;
1382 * Select a subset of the items in the datastore and call
1383 * the given iterator for each of them.
1385 * @param cls our "struct Plugin*"
1386 * @param type entries of which type should be considered?
1387 * Use 0 for any type.
1388 * @param iter function to call on each matching value;
1389 * will be called once with a NULL value at the end
1390 * @param iter_cls closure for iter
1393 mysql_plugin_iter_low_priority (void *cls,
1394 enum GNUNET_BLOCK_Type type,
1395 PluginIterator iter,
1398 struct Plugin *plugin = cls;
1399 iterateHelper (plugin, type, GNUNET_YES,
1406 GNUNET_HashCode key;
1407 GNUNET_HashCode vhash;
1410 unsigned int anonymity;
1411 unsigned long long expiration;
1412 unsigned long long vkey;
1413 unsigned long long total;
1421 get_statement_prepare (void *cls,
1422 struct NextRequestClosure *nrc)
1424 struct GetContext *gc = cls;
1425 struct Plugin *plugin;
1427 unsigned int limit_off;
1428 unsigned long hashSize;
1435 if (gc->count == gc->total)
1437 plugin = nrc->plugin;
1438 hashSize = sizeof (GNUNET_HashCode);
1439 if (gc->count + gc->off == gc->total)
1440 nrc->last_vkey = 0; /* back to start */
1442 limit_off = gc->off;
1446 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1447 "Obtaining result number %d/%lld at offset %d with lvc %llu for GET `%s'\n",
1452 GNUNET_h2s (&gc->key));
1459 prepared_statement_run_select
1461 plugin->select_entry_by_hash_vhash_and_type, 6, nrc->rbind, &return_ok,
1462 NULL, MYSQL_TYPE_BLOB, &gc->key, hashSize, &hashSize,
1463 MYSQL_TYPE_BLOB, &gc->vhash, hashSize, &hashSize,
1464 MYSQL_TYPE_LONGLONG, &nrc->last_vkey, GNUNET_YES, MYSQL_TYPE_LONG,
1465 &nrc->type, GNUNET_YES, MYSQL_TYPE_LONG, &limit_off, GNUNET_YES,
1471 prepared_statement_run_select
1473 plugin->select_entry_by_hash_and_type, 6, nrc->rbind, &return_ok, NULL,
1474 MYSQL_TYPE_BLOB, &gc->key, hashSize, &hashSize,
1475 MYSQL_TYPE_LONGLONG, &nrc->last_vkey, GNUNET_YES, MYSQL_TYPE_LONG,
1476 &nrc->type, GNUNET_YES, MYSQL_TYPE_LONG, &limit_off, GNUNET_YES,
1485 prepared_statement_run_select
1487 plugin->select_entry_by_hash_and_vhash, 6, nrc->rbind, &return_ok, NULL,
1488 MYSQL_TYPE_BLOB, &gc->key, hashSize, &hashSize, MYSQL_TYPE_BLOB,
1489 &gc->vhash, hashSize, &hashSize, MYSQL_TYPE_LONGLONG,
1490 &nrc->last_vkey, GNUNET_YES, MYSQL_TYPE_LONG, &limit_off,
1496 prepared_statement_run_select
1498 plugin->select_entry_by_hash, 6, nrc->rbind, &return_ok, NULL,
1499 MYSQL_TYPE_BLOB, &gc->key, hashSize, &hashSize,
1500 MYSQL_TYPE_LONGLONG, &nrc->last_vkey, GNUNET_YES, MYSQL_TYPE_LONG,
1501 &limit_off, GNUNET_YES, -1);
1510 * Iterate over the results for a particular key
1513 * @param cls closure
1514 * @param key maybe NULL (to match all entries)
1515 * @param vhash hash of the value, maybe NULL (to
1516 * match all values that have the right key).
1517 * Note that for DBlocks there is no difference
1518 * betwen key and vhash, but for other blocks
1520 * @param type entries of which type are relevant?
1521 * Use 0 for any type.
1522 * @param iter function to call on each matching value;
1523 * will be called once with a NULL value at the end
1524 * @param iter_cls closure for iter
1527 mysql_plugin_get (void *cls,
1528 const GNUNET_HashCode * key,
1529 const GNUNET_HashCode * vhash,
1530 enum GNUNET_BLOCK_Type type,
1531 PluginIterator iter, void *iter_cls)
1533 struct Plugin *plugin = cls;
1534 unsigned int itype = type;
1536 MYSQL_BIND cbind[1];
1537 struct GetContext *gc;
1538 struct NextRequestClosure *nrc;
1540 unsigned long hashSize;
1546 mysql_plugin_iter_low_priority (plugin,
1551 hashSize = sizeof (GNUNET_HashCode);
1552 memset (cbind, 0, sizeof (cbind));
1554 cbind[0].buffer_type = MYSQL_TYPE_LONGLONG;
1555 cbind[0].buffer = &total;
1556 cbind[0].is_unsigned = GNUNET_NO;
1562 prepared_statement_run_select
1564 plugin->count_entry_by_hash_vhash_and_type, 1, cbind, &return_ok, NULL,
1565 MYSQL_TYPE_BLOB, key, hashSize, &hashSize, MYSQL_TYPE_BLOB,
1566 vhash, hashSize, &hashSize, MYSQL_TYPE_LONG, &itype, GNUNET_YES,
1572 prepared_statement_run_select
1574 plugin->count_entry_by_hash_and_type, 1, cbind, &return_ok, NULL,
1575 MYSQL_TYPE_BLOB, key, hashSize, &hashSize, MYSQL_TYPE_LONG,
1576 &itype, GNUNET_YES, -1);
1585 prepared_statement_run_select
1587 plugin->count_entry_by_hash_and_vhash, 1, cbind, &return_ok, NULL,
1588 MYSQL_TYPE_BLOB, key, hashSize, &hashSize, MYSQL_TYPE_BLOB,
1589 vhash, hashSize, &hashSize, -1);
1595 prepared_statement_run_select (plugin,
1596 plugin->count_entry_by_hash,
1597 1, cbind, &return_ok,
1598 NULL, MYSQL_TYPE_BLOB,
1603 if ((ret != GNUNET_OK) || (0 >= total))
1606 NULL, NULL, 0, NULL, 0, 0, 0,
1607 GNUNET_TIME_UNIT_ZERO_ABS, 0);
1611 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1612 "Iterating over %lld results for GET `%s'\n",
1616 gc = GNUNET_malloc (sizeof (struct GetContext));
1620 gc->have_vhash = GNUNET_YES;
1624 gc->off = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, total);
1627 nrc = GNUNET_malloc (sizeof (struct NextRequestClosure));
1628 nrc->plugin = plugin;
1630 nrc->iter_select = -1;
1632 nrc->dviter_cls = iter_cls;
1633 nrc->prep = &get_statement_prepare;
1636 mysql_plugin_next_request (nrc, GNUNET_NO);
1641 * Update the priority for a particular key in the datastore. If
1642 * the expiration time in value is different than the time found in
1643 * the datastore, the higher value should be kept. For the
1644 * anonymity level, the lower value is to be used. The specified
1645 * priority should be added to the existing priority, ignoring the
1646 * priority in value.
1648 * Note that it is possible for multiple values to match this put.
1649 * In that case, all of the respective values are updated.
1651 * @param cls our "struct Plugin*"
1652 * @param uid unique identifier of the datum
1653 * @param delta by how much should the priority
1654 * change? If priority + delta < 0 the
1655 * priority should be set to 0 (never go
1657 * @param expire new expiration time should be the
1658 * MAX of any existing expiration time and
1660 * @param msg set to error message
1661 * @return GNUNET_OK on success
1664 mysql_plugin_update (void *cls,
1667 struct GNUNET_TIME_Absolute expire,
1670 struct Plugin *plugin = cls;
1671 unsigned long long vkey = uid;
1672 unsigned long long lexpire = expire.value;
1676 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1677 "Updating value %llu adding %d to priority and maxing exp at %llu\n",
1682 ret = prepared_statement_run (plugin,
1683 plugin->update_entry,
1688 MYSQL_TYPE_LONGLONG,
1691 MYSQL_TYPE_LONGLONG,
1694 MYSQL_TYPE_LONGLONG,
1697 if (ret != GNUNET_OK)
1699 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1700 "Failed to update value %llu\n",
1708 * Select a subset of the items in the datastore and call
1709 * the given iterator for each of them.
1711 * @param cls our "struct Plugin*"
1712 * @param type entries of which type should be considered?
1713 * Use 0 for any type.
1714 * @param iter function to call on each matching value;
1715 * will be called once with a NULL value at the end
1716 * @param iter_cls closure for iter
1719 mysql_plugin_iter_zero_anonymity (void *cls,
1720 enum GNUNET_BLOCK_Type type,
1721 PluginIterator iter,
1724 struct Plugin *plugin = cls;
1725 iterateHelper (plugin, type, GNUNET_NO, 1, iter, iter_cls);
1730 * Select a subset of the items in the datastore and call
1731 * the given iterator for each of them.
1733 * @param cls our "struct Plugin*"
1734 * @param type entries of which type should be considered?
1735 * Use 0 for any type.
1736 * @param iter function to call on each matching value;
1737 * will be called once with a NULL value at the end
1738 * @param iter_cls closure for iter
1741 mysql_plugin_iter_ascending_expiration (void *cls,
1742 enum GNUNET_BLOCK_Type type,
1743 PluginIterator iter,
1746 struct Plugin *plugin = cls;
1747 iterateHelper (plugin, type, GNUNET_YES, 2, iter, iter_cls);
1752 * Select a subset of the items in the datastore and call
1753 * the given iterator for each of them.
1755 * @param cls our "struct Plugin*"
1756 * @param type entries of which type should be considered?
1757 * Use 0 for any type.
1758 * @param iter function to call on each matching value;
1759 * will be called once with a NULL value at the end
1760 * @param iter_cls closure for iter
1763 mysql_plugin_iter_migration_order (void *cls,
1764 enum GNUNET_BLOCK_Type type,
1765 PluginIterator iter,
1768 struct Plugin *plugin = cls;
1769 iterateHelper (plugin, 0, GNUNET_NO, 3, iter, iter_cls);
1774 * Select a subset of the items in the datastore and call
1775 * the given iterator for each of them.
1777 * @param cls our "struct Plugin*"
1778 * @param type entries of which type should be considered?
1779 * Use 0 for any type.
1780 * @param iter function to call on each matching value;
1781 * will be called once with a NULL value at the end
1782 * @param iter_cls closure for iter
1785 mysql_plugin_iter_all_now (void *cls,
1786 enum GNUNET_BLOCK_Type type,
1787 PluginIterator iter,
1790 struct Plugin *plugin = cls;
1791 iterateHelper (plugin, 0, GNUNET_YES, 0, iter, iter_cls);
1799 mysql_plugin_drop (void *cls)
1801 struct Plugin *plugin = cls;
1803 if ((GNUNET_OK != run_statement (plugin,
1804 "DROP TABLE gn090")) ||
1805 (GNUNET_OK != run_statement (plugin,
1806 "DROP TABLE gn072")))
1808 plugin->content_size = 0;
1813 * Entry point for the plugin.
1815 * @param cls the "struct GNUNET_DATASTORE_PluginEnvironment*"
1816 * @return our "struct Plugin*"
1819 libgnunet_plugin_datastore_mysql_init (void *cls)
1821 struct GNUNET_DATASTORE_PluginEnvironment *env = cls;
1822 struct GNUNET_DATASTORE_PluginFunctions *api;
1823 struct Plugin *plugin;
1825 plugin = GNUNET_malloc (sizeof (struct Plugin));
1827 plugin->cnffile = get_my_cnf_path (env->cfg);
1828 if (GNUNET_OK != iopen (plugin))
1831 GNUNET_free_non_null (plugin->cnffile);
1832 GNUNET_free (plugin);
1835 #define MRUNS(a) (GNUNET_OK != run_statement (plugin, a) )
1836 #define PINIT(a,b) (NULL == (a = prepared_statement_create(plugin, b)))
1837 if (MRUNS ("CREATE TABLE IF NOT EXISTS gn090 ("
1838 " type INT(11) UNSIGNED NOT NULL DEFAULT 0,"
1839 " prio INT(11) UNSIGNED NOT NULL DEFAULT 0,"
1840 " anonLevel INT(11) UNSIGNED NOT NULL DEFAULT 0,"
1841 " expire BIGINT UNSIGNED NOT NULL DEFAULT 0,"
1842 " hash BINARY(64) NOT NULL DEFAULT '',"
1843 " vhash BINARY(64) NOT NULL DEFAULT '',"
1844 " vkey BIGINT UNSIGNED NOT NULL DEFAULT 0,"
1845 " INDEX hash (hash(64)),"
1846 " INDEX hash_vhash_vkey (hash(64),vhash(64),vkey),"
1847 " INDEX hash_vkey (hash(64),vkey),"
1848 " INDEX vkey (vkey),"
1849 " INDEX prio (prio,vkey),"
1850 " INDEX expire (expire,vkey,type),"
1851 " INDEX anonLevel (anonLevel,prio,vkey,type)"
1852 ") ENGINE=InnoDB") ||
1853 MRUNS ("CREATE TABLE IF NOT EXISTS gn072 ("
1854 " vkey BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,"
1855 " value BLOB NOT NULL DEFAULT '') ENGINE=MyISAM") ||
1856 MRUNS ("SET AUTOCOMMIT = 1") ||
1857 PINIT (plugin->select_value, SELECT_VALUE) ||
1858 PINIT (plugin->delete_value, DELETE_VALUE) ||
1859 PINIT (plugin->insert_value, INSERT_VALUE) ||
1860 PINIT (plugin->insert_entry, INSERT_ENTRY) ||
1861 PINIT (plugin->delete_entry_by_vkey, DELETE_ENTRY_BY_VKEY) ||
1862 PINIT (plugin->select_entry_by_hash, SELECT_ENTRY_BY_HASH) ||
1863 PINIT (plugin->select_entry_by_hash_and_vhash, SELECT_ENTRY_BY_HASH_AND_VHASH)
1864 || PINIT (plugin->select_entry_by_hash_and_type, SELECT_ENTRY_BY_HASH_AND_TYPE)
1865 || PINIT (plugin->select_entry_by_hash_vhash_and_type,
1866 SELECT_ENTRY_BY_HASH_VHASH_AND_TYPE)
1867 || PINIT (plugin->count_entry_by_hash, COUNT_ENTRY_BY_HASH)
1868 || PINIT (plugin->count_entry_by_hash_and_vhash, COUNT_ENTRY_BY_HASH_AND_VHASH)
1869 || PINIT (plugin->count_entry_by_hash_and_type, COUNT_ENTRY_BY_HASH_AND_TYPE)
1870 || PINIT (plugin->count_entry_by_hash_vhash_and_type,
1871 COUNT_ENTRY_BY_HASH_VHASH_AND_TYPE)
1872 || PINIT (plugin->update_entry, UPDATE_ENTRY)
1873 || PINIT (plugin->iter[0], SELECT_IT_LOW_PRIORITY)
1874 || PINIT (plugin->iter[1], SELECT_IT_NON_ANONYMOUS)
1875 || PINIT (plugin->iter[2], SELECT_IT_EXPIRATION_TIME)
1876 || PINIT (plugin->iter[3], SELECT_IT_MIGRATION_ORDER))
1879 GNUNET_free_non_null (plugin->cnffile);
1880 GNUNET_free (plugin);
1886 api = GNUNET_malloc (sizeof (struct GNUNET_DATASTORE_PluginFunctions));
1888 api->get_size = &mysql_plugin_get_size;
1889 api->put = &mysql_plugin_put;
1890 api->next_request = &mysql_plugin_next_request;
1891 api->get = &mysql_plugin_get;
1892 api->update = &mysql_plugin_update;
1893 api->iter_low_priority = &mysql_plugin_iter_low_priority;
1894 api->iter_zero_anonymity = &mysql_plugin_iter_zero_anonymity;
1895 api->iter_ascending_expiration = &mysql_plugin_iter_ascending_expiration;
1896 api->iter_migration_order = &mysql_plugin_iter_migration_order;
1897 api->iter_all_now = &mysql_plugin_iter_all_now;
1898 api->drop = &mysql_plugin_drop;
1899 GNUNET_log_from (GNUNET_ERROR_TYPE_INFO,
1900 "mysql", _("Mysql database running\n"));
1906 * Exit point from the plugin.
1907 * @param cls our "struct Plugin*"
1908 * @return always NULL
1911 libgnunet_plugin_datastore_mysql_done (void *cls)
1913 struct GNUNET_DATASTORE_PluginFunctions *api = cls;
1914 struct Plugin *plugin = api->cls;
1917 if (plugin->next_task != GNUNET_SCHEDULER_NO_TASK)
1919 GNUNET_SCHEDULER_cancel (plugin->env->sched,
1921 plugin->next_task = GNUNET_SCHEDULER_NO_TASK;
1922 plugin->next_task_nc->prep (plugin->next_task_nc->prep_cls, NULL);
1923 GNUNET_free (plugin->next_task_nc);
1924 plugin->next_task_nc = NULL;
1926 GNUNET_free_non_null (plugin->cnffile);
1927 GNUNET_free (plugin);
1929 mysql_library_end ();
1933 /* end of plugin_datastore_mysql.c */