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(size) FROM gn090"
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;
577 * Run the given MySQL SELECT statement. The statement
578 * must have only a single result (one column, one row).
580 * @return result on success, NULL on error
583 run_statement_select (struct Plugin *plugin,
584 const char *statement)
590 if ((NULL == plugin->dbf) && (GNUNET_OK != iopen (plugin)))
592 mysql_query (plugin->dbf, statement);
593 if ((mysql_error (plugin->dbf)[0]) ||
594 (!(sql_res = mysql_use_result (plugin->dbf))) ||
595 (!(sql_row = mysql_fetch_row (sql_res))))
597 LOG_MYSQL (GNUNET_ERROR_TYPE_ERROR,
598 "mysql_query", plugin);
601 if ((mysql_num_fields (sql_res) != 1) || (sql_row[0] == NULL))
603 GNUNET_break (mysql_num_fields (sql_res) == 1);
605 mysql_free_result (sql_res);
608 ret = GNUNET_strdup (sql_row[0]);
609 mysql_free_result (sql_res);
616 * Create a prepared statement.
618 * @return NULL on error
620 static struct GNUNET_MysqlStatementHandle *
621 prepared_statement_create (struct Plugin *plugin,
622 const char *statement)
624 struct GNUNET_MysqlStatementHandle *ret;
626 ret = GNUNET_malloc (sizeof (struct GNUNET_MysqlStatementHandle));
627 ret->query = GNUNET_strdup (statement);
628 GNUNET_CONTAINER_DLL_insert (plugin->shead,
636 * Prepare a statement for running.
638 * @return GNUNET_OK on success
641 prepare_statement (struct Plugin *plugin,
642 struct GNUNET_MysqlStatementHandle *ret)
644 if (GNUNET_YES == ret->valid)
646 if ((NULL == plugin->dbf) &&
647 (GNUNET_OK != iopen (plugin)))
648 return GNUNET_SYSERR;
649 ret->statement = mysql_stmt_init (plugin->dbf);
650 if (ret->statement == NULL)
653 return GNUNET_SYSERR;
655 if (mysql_stmt_prepare (ret->statement,
657 strlen (ret->query)))
659 LOG_MYSQL (GNUNET_ERROR_TYPE_ERROR,
660 "mysql_stmt_prepare",
662 mysql_stmt_close (ret->statement);
663 ret->statement = NULL;
665 return GNUNET_SYSERR;
667 ret->valid = GNUNET_YES;
674 * Bind the parameters for the given MySQL statement
677 * @param s statement to bind and run
678 * @param ap arguments for the binding
679 * @return GNUNET_SYSERR on error, GNUNET_OK on success
682 init_params (struct Plugin *plugin,
683 struct GNUNET_MysqlStatementHandle *s,
686 MYSQL_BIND qbind[MAX_PARAM];
689 enum enum_field_types ft;
691 pc = mysql_stmt_param_count (s->statement);
694 /* increase internal constant! */
696 return GNUNET_SYSERR;
698 memset (qbind, 0, sizeof (qbind));
701 while ((pc > 0) && (-1 != (ft = va_arg (ap, enum enum_field_types))))
703 qbind[off].buffer_type = ft;
706 case MYSQL_TYPE_FLOAT:
707 qbind[off].buffer = va_arg (ap, float *);
709 case MYSQL_TYPE_LONGLONG:
710 qbind[off].buffer = va_arg (ap, unsigned long long *);
711 qbind[off].is_unsigned = va_arg (ap, int);
713 case MYSQL_TYPE_LONG:
714 qbind[off].buffer = va_arg (ap, unsigned int *);
715 qbind[off].is_unsigned = va_arg (ap, int);
717 case MYSQL_TYPE_VAR_STRING:
718 case MYSQL_TYPE_STRING:
719 case MYSQL_TYPE_BLOB:
720 qbind[off].buffer = va_arg (ap, void *);
721 qbind[off].buffer_length = va_arg (ap, unsigned long);
722 qbind[off].length = va_arg (ap, unsigned long *);
725 /* unsupported type */
727 return GNUNET_SYSERR;
732 if (!((pc == 0) && (ft != -1) && (va_arg (ap, int) == -1)))
735 return GNUNET_SYSERR;
737 if (mysql_stmt_bind_param (s->statement, qbind))
739 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
740 _("`%s' failed at %s:%d with error: %s\n"),
741 "mysql_stmt_bind_param",
742 __FILE__, __LINE__, mysql_stmt_error (s->statement));
744 return GNUNET_SYSERR;
746 if (mysql_stmt_execute (s->statement))
748 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
749 _("`%s' failed at %s:%d with error: %s\n"),
750 "mysql_stmt_execute",
751 __FILE__, __LINE__, mysql_stmt_error (s->statement));
753 return GNUNET_SYSERR;
759 * Type of a callback that will be called for each
760 * data set returned from MySQL.
762 * @param cls user-defined argument
763 * @param num_values number of elements in values
764 * @param values values returned by MySQL
765 * @return GNUNET_OK to continue iterating, GNUNET_SYSERR to abort
767 typedef int (*GNUNET_MysqlDataProcessor) (void *cls,
768 unsigned int num_values,
769 MYSQL_BIND * values);
773 * Run a prepared SELECT statement.
775 * @param result_size number of elements in results array
776 * @param results pointer to already initialized MYSQL_BIND
777 * array (of sufficient size) for passing results
778 * @param processor function to call on each result
779 * @param processor_cls extra argument to processor
780 * @param ... pairs and triplets of "MYSQL_TYPE_XXX" keys and their respective
781 * values (size + buffer-reference for pointers); terminated
783 * @return GNUNET_SYSERR on error, otherwise
784 * the number of successfully affected (or queried) rows
787 prepared_statement_run_select (struct Plugin *plugin,
788 struct GNUNET_MysqlStatementHandle
790 unsigned int result_size,
791 MYSQL_BIND * results,
792 GNUNET_MysqlDataProcessor
793 processor, void *processor_cls,
801 if (GNUNET_OK != prepare_statement (plugin, s))
804 return GNUNET_SYSERR;
806 va_start (ap, processor_cls);
807 if (GNUNET_OK != init_params (plugin, s, ap))
811 return GNUNET_SYSERR;
814 rsize = mysql_stmt_field_count (s->statement);
815 if (rsize > result_size)
818 return GNUNET_SYSERR;
820 if (mysql_stmt_bind_result (s->statement, results))
822 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
823 _("`%s' failed at %s:%d with error: %s\n"),
824 "mysql_stmt_bind_result",
825 __FILE__, __LINE__, mysql_stmt_error (s->statement));
827 return GNUNET_SYSERR;
833 ret = mysql_stmt_fetch (s->statement);
834 if (ret == MYSQL_NO_DATA)
838 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
839 _("`%s' failed at %s:%d with error: %s\n"),
841 __FILE__, __LINE__, mysql_stmt_error (s->statement));
843 return GNUNET_SYSERR;
845 if (processor != NULL)
846 if (GNUNET_OK != processor (processor_cls, rsize, results))
850 mysql_stmt_reset (s->statement);
856 * Run a prepared statement that does NOT produce results.
858 * @param ... pairs and triplets of "MYSQL_TYPE_XXX" keys and their respective
859 * values (size + buffer-reference for pointers); terminated
861 * @param insert_id NULL or address where to store the row ID of whatever
862 * was inserted (only for INSERT statements!)
863 * @return GNUNET_SYSERR on error, otherwise
864 * the number of successfully affected rows
867 prepared_statement_run (struct Plugin *plugin,
868 struct GNUNET_MysqlStatementHandle *s,
869 unsigned long long *insert_id, ...)
874 if (GNUNET_OK != prepare_statement (plugin, s))
875 return GNUNET_SYSERR;
876 va_start (ap, insert_id);
877 if (GNUNET_OK != init_params (plugin, s, ap))
880 return GNUNET_SYSERR;
883 affected = mysql_stmt_affected_rows (s->statement);
884 if (NULL != insert_id)
885 *insert_id = (unsigned long long) mysql_stmt_insert_id (s->statement);
886 mysql_stmt_reset (s->statement);
892 * Delete an value from the gn072 table.
894 * @param vkey vkey identifying the value to delete
895 * @return GNUNET_OK on success, GNUNET_NO if no such value exists, GNUNET_SYSERR on error
898 do_delete_value (struct Plugin *plugin,
899 unsigned long long vkey)
904 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
905 "Deleting value %llu from gn072 table\n",
908 ret = prepared_statement_run (plugin,
909 plugin->delete_value,
912 &vkey, GNUNET_YES, -1);
919 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
920 "Deleting value %llu from gn072 table failed\n",
927 * Insert a value into the gn072 table.
929 * @param value the value to insert
930 * @param size size of the value
931 * @param vkey vkey identifying the value henceforth (set)
932 * @return GNUNET_OK on success, GNUNET_SYSERR on error
935 do_insert_value (struct Plugin *plugin,
936 const void *value, unsigned int size,
937 unsigned long long *vkey)
939 unsigned long length = size;
942 ret = prepared_statement_run (plugin,
943 plugin->insert_value,
946 value, length, &length, -1);
947 if (ret == GNUNET_OK)
950 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
951 "Inserted value number %llu with length %u into gn072 table\n",
958 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
959 "Failed to insert %u byte value into gn072 table\n",
966 * Delete an entry from the gn090 table.
968 * @param vkey vkey identifying the entry to delete
969 * @return GNUNET_OK on success, GNUNET_NO if no such value exists, GNUNET_SYSERR on error
972 do_delete_entry_by_vkey (struct Plugin *plugin,
973 unsigned long long vkey)
978 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
979 "Deleting value %llu from gn090 table\n",
982 ret = prepared_statement_run (plugin,
983 plugin->delete_entry_by_vkey,
986 &vkey, GNUNET_YES, -1);
993 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
994 "Deleting value %llu from gn090 table failed\n",
1001 return_ok (void *cls, unsigned int num_values, MYSQL_BIND * values)
1008 iterator_helper_prepare (void *cls,
1009 struct NextRequestClosure *nrc)
1011 struct Plugin *plugin;
1016 plugin = nrc->plugin;
1017 ret = GNUNET_SYSERR;
1018 switch (nrc->iter_select)
1022 ret = prepared_statement_run_select (plugin,
1023 plugin->iter[nrc->iter_select],
1031 MYSQL_TYPE_LONGLONG,
1037 MYSQL_TYPE_LONGLONG,
1042 ret = prepared_statement_run_select (plugin,
1043 plugin->iter[nrc->iter_select],
1048 MYSQL_TYPE_LONGLONG,
1051 MYSQL_TYPE_LONGLONG,
1054 MYSQL_TYPE_LONGLONG,
1057 MYSQL_TYPE_LONGLONG,
1062 ret = prepared_statement_run_select (plugin,
1063 plugin->iter[nrc->iter_select],
1068 MYSQL_TYPE_LONGLONG,
1071 MYSQL_TYPE_LONGLONG,
1074 MYSQL_TYPE_LONGLONG,
1077 MYSQL_TYPE_LONGLONG,
1080 MYSQL_TYPE_LONGLONG,
1083 MYSQL_TYPE_LONGLONG,
1095 * Continuation of "mysql_next_request".
1097 * @param next_cls the next context
1098 * @param tc the task context (unused)
1101 mysql_next_request_cont (void *next_cls,
1102 const struct GNUNET_SCHEDULER_TaskContext *tc)
1104 struct NextRequestClosure *nrc = next_cls;
1105 struct Plugin *plugin;
1108 unsigned int priority;
1109 unsigned int anonymity;
1110 unsigned long long exp;
1111 unsigned long long vkey;
1112 unsigned long hashSize;
1113 GNUNET_HashCode key;
1114 struct GNUNET_TIME_Absolute expiration;
1115 unsigned long length;
1116 MYSQL_BIND *rbind; /* size 7 */
1117 MYSQL_BIND dbind[1];
1118 char datum[GNUNET_SERVER_MAX_MESSAGE_SIZE];
1120 plugin = nrc->plugin;
1121 plugin->next_task = GNUNET_SCHEDULER_NO_TASK;
1122 plugin->next_task_nc = NULL;
1125 GNUNET_assert (nrc->plugin->next_task == GNUNET_SCHEDULER_NO_TASK);
1126 nrc->now = GNUNET_TIME_absolute_get ();
1127 hashSize = sizeof (GNUNET_HashCode);
1128 memset (nrc->rbind, 0, sizeof (nrc->rbind));
1130 rbind[0].buffer_type = MYSQL_TYPE_LONG;
1131 rbind[0].buffer = &type;
1132 rbind[0].is_unsigned = 1;
1133 rbind[1].buffer_type = MYSQL_TYPE_LONG;
1134 rbind[1].buffer = &priority;
1135 rbind[1].is_unsigned = 1;
1136 rbind[2].buffer_type = MYSQL_TYPE_LONG;
1137 rbind[2].buffer = &anonymity;
1138 rbind[2].is_unsigned = 1;
1139 rbind[3].buffer_type = MYSQL_TYPE_LONGLONG;
1140 rbind[3].buffer = &exp;
1141 rbind[3].is_unsigned = 1;
1142 rbind[4].buffer_type = MYSQL_TYPE_BLOB;
1143 rbind[4].buffer = &key;
1144 rbind[4].buffer_length = hashSize;
1145 rbind[4].length = &hashSize;
1146 rbind[5].buffer_type = MYSQL_TYPE_LONGLONG;
1147 rbind[5].buffer = &vkey;
1148 rbind[5].is_unsigned = GNUNET_YES;
1150 if ( (GNUNET_YES == nrc->end_it) ||
1151 (GNUNET_OK != nrc->prep (nrc->prep_cls,
1154 GNUNET_assert (nrc->plugin->next_task == GNUNET_SCHEDULER_NO_TASK);
1155 nrc->last_vkey = vkey;
1156 nrc->last_prio = priority;
1157 nrc->last_expire = exp;
1158 if ( (rbind[4].buffer_length != sizeof (GNUNET_HashCode)) ||
1159 (hashSize != sizeof (GNUNET_HashCode)) )
1165 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1166 "Found value %llu with prio %u, anon %u, expire %llu selecting from gn090 table\n",
1172 /* now do query on gn072 */
1173 length = sizeof (datum);
1174 memset (dbind, 0, sizeof (dbind));
1175 dbind[0].buffer_type = MYSQL_TYPE_BLOB;
1176 dbind[0].buffer_length = length;
1177 dbind[0].length = &length;
1178 dbind[0].buffer = datum;
1179 ret = prepared_statement_run_select (plugin,
1180 plugin->select_value,
1185 MYSQL_TYPE_LONGLONG,
1186 &vkey, GNUNET_YES, -1);
1187 GNUNET_break (ret <= 1); /* should only have one rbind! */
1190 if (ret != GNUNET_OK)
1193 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1194 _("Failed to obtain value %llu from table `%s'\n"),
1199 GNUNET_break (length <= sizeof(datum));
1201 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1202 "Calling iterator with value `%s' number %llu of size %u with type %u, priority %u, anonymity %u and expiration %llu\n",
1211 GNUNET_assert (nrc->plugin->next_task == GNUNET_SCHEDULER_NO_TASK);
1212 expiration.value = exp;
1213 ret = nrc->dviter (nrc->dviter_cls,
1223 if (ret == GNUNET_SYSERR)
1225 nrc->end_it = GNUNET_YES;
1228 if (ret == GNUNET_NO)
1230 do_delete_value (plugin, vkey);
1231 do_delete_entry_by_vkey (plugin, vkey);
1232 plugin->content_size -= length;
1236 /* call dviter with "end of set" */
1237 GNUNET_assert (nrc->plugin->next_task == GNUNET_SCHEDULER_NO_TASK);
1238 nrc->dviter (nrc->dviter_cls,
1239 NULL, NULL, 0, NULL, 0, 0, 0,
1240 GNUNET_TIME_UNIT_ZERO_ABS, 0);
1241 GNUNET_assert (nrc->plugin->next_task == GNUNET_SCHEDULER_NO_TASK);
1242 nrc->prep (nrc->prep_cls, NULL);
1243 GNUNET_assert (nrc->plugin->next_task == GNUNET_SCHEDULER_NO_TASK);
1249 * Function invoked on behalf of a "PluginIterator"
1250 * asking the database plugin to call the iterator
1251 * with the next item.
1253 * @param next_cls whatever argument was given
1254 * to the PluginIterator as "next_cls".
1255 * @param end_it set to GNUNET_YES if we
1256 * should terminate the iteration early
1257 * (iterator should be still called once more
1258 * to signal the end of the iteration).
1261 mysql_plugin_next_request (void *next_cls,
1264 struct NextRequestClosure *nrc = next_cls;
1266 if (GNUNET_YES == end_it)
1267 nrc->end_it = GNUNET_YES;
1268 nrc->plugin->next_task_nc = nrc;
1269 nrc->plugin->next_task = GNUNET_SCHEDULER_add_now (nrc->plugin->env->sched,
1270 &mysql_next_request_cont,
1276 * Iterate over the items in the datastore
1277 * using the given query to select and order
1280 * @param type entries of which type should be considered?
1281 * Use 0 for any type.
1282 * @param iter never NULL
1283 * @param is_asc are we using ascending order?
1286 iterateHelper (struct Plugin *plugin,
1289 unsigned int iter_select,
1290 PluginIterator dviter,
1293 struct NextRequestClosure *nrc;
1295 nrc = GNUNET_malloc (sizeof (struct NextRequestClosure));
1296 nrc->plugin = plugin;
1298 nrc->iter_select = iter_select;
1299 nrc->dviter = dviter;
1300 nrc->dviter_cls = dviter_cls;
1301 nrc->prep = &iterator_helper_prepare;
1306 nrc->last_expire = 0;
1310 nrc->last_prio = 0x7FFFFFFFL;
1311 nrc->last_vkey = 0x7FFFFFFFFFFFFFFFLL; /* MySQL only supports 63 bits */
1312 nrc->last_expire = 0x7FFFFFFFFFFFFFFFLL; /* MySQL only supports 63 bits */
1314 mysql_plugin_next_request (nrc, GNUNET_NO);
1319 * Get an estimate of how much space the database is
1322 * @param cls our "struct Plugin*"
1323 * @return number of bytes used on disk
1325 static unsigned long long
1326 mysql_plugin_get_size (void *cls)
1328 struct Plugin *plugin = cls;
1329 return plugin->content_size;
1334 * Store an item in the datastore.
1336 * @param cls closure
1337 * @param key key for the item
1338 * @param size number of bytes in data
1339 * @param data content stored
1340 * @param type type of the content
1341 * @param priority priority of the content
1342 * @param anonymity anonymity-level for the content
1343 * @param expiration expiration time for the content
1344 * @param msg set to error message
1345 * @return GNUNET_OK on success
1348 mysql_plugin_put (void *cls,
1349 const GNUNET_HashCode * key,
1352 enum GNUNET_BLOCK_Type type,
1355 struct GNUNET_TIME_Absolute expiration,
1358 struct Plugin *plugin = cls;
1359 unsigned int itype = type;
1360 unsigned int ipriority = priority;
1361 unsigned int ianonymity = anonymity;
1362 unsigned long long lexpiration = expiration.value;
1363 unsigned long hashSize;
1364 unsigned long hashSize2;
1365 unsigned long long vkey;
1366 GNUNET_HashCode vhash;
1368 if (size > MAX_DATUM_SIZE)
1371 return GNUNET_SYSERR;
1373 hashSize = sizeof (GNUNET_HashCode);
1374 hashSize2 = sizeof (GNUNET_HashCode);
1375 GNUNET_CRYPTO_hash (data, size, &vhash);
1376 if (GNUNET_OK != do_insert_value (plugin,
1378 return GNUNET_SYSERR;
1380 prepared_statement_run (plugin,
1381 plugin->insert_entry,
1392 MYSQL_TYPE_LONGLONG,
1403 MYSQL_TYPE_LONGLONG,
1404 &vkey, GNUNET_YES, -1))
1406 do_delete_value (plugin, vkey);
1407 return GNUNET_SYSERR;
1410 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1411 "Inserted value `%s' number %llu with size %u into gn090 table\n",
1414 (unsigned int) size);
1416 plugin->content_size += size;
1422 * Select a subset of the items in the datastore and call
1423 * the given iterator for each of them.
1425 * @param cls our "struct Plugin*"
1426 * @param type entries of which type should be considered?
1427 * Use 0 for any type.
1428 * @param iter function to call on each matching value;
1429 * will be called once with a NULL value at the end
1430 * @param iter_cls closure for iter
1433 mysql_plugin_iter_low_priority (void *cls,
1434 enum GNUNET_BLOCK_Type type,
1435 PluginIterator iter,
1438 struct Plugin *plugin = cls;
1439 iterateHelper (plugin, type, GNUNET_YES,
1446 GNUNET_HashCode key;
1447 GNUNET_HashCode vhash;
1450 unsigned int anonymity;
1451 unsigned long long expiration;
1452 unsigned long long vkey;
1453 unsigned long long total;
1461 get_statement_prepare (void *cls,
1462 struct NextRequestClosure *nrc)
1464 struct GetContext *gc = cls;
1465 struct Plugin *plugin;
1467 unsigned int limit_off;
1468 unsigned long hashSize;
1475 if (gc->count == gc->total)
1477 plugin = nrc->plugin;
1478 hashSize = sizeof (GNUNET_HashCode);
1479 if (gc->count + gc->off == gc->total)
1480 nrc->last_vkey = 0; /* back to start */
1482 limit_off = gc->off;
1486 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1487 "Obtaining result number %d/%lld at offset %d with lvc %llu for GET `%s'\n",
1492 GNUNET_h2s (&gc->key));
1499 prepared_statement_run_select
1501 plugin->select_entry_by_hash_vhash_and_type, 6, nrc->rbind, &return_ok,
1502 NULL, MYSQL_TYPE_BLOB, &gc->key, hashSize, &hashSize,
1503 MYSQL_TYPE_BLOB, &gc->vhash, hashSize, &hashSize,
1504 MYSQL_TYPE_LONGLONG, &nrc->last_vkey, GNUNET_YES, MYSQL_TYPE_LONG,
1505 &nrc->type, GNUNET_YES, MYSQL_TYPE_LONG, &limit_off, GNUNET_YES,
1511 prepared_statement_run_select
1513 plugin->select_entry_by_hash_and_type, 6, nrc->rbind, &return_ok, NULL,
1514 MYSQL_TYPE_BLOB, &gc->key, hashSize, &hashSize,
1515 MYSQL_TYPE_LONGLONG, &nrc->last_vkey, GNUNET_YES, MYSQL_TYPE_LONG,
1516 &nrc->type, GNUNET_YES, MYSQL_TYPE_LONG, &limit_off, GNUNET_YES,
1525 prepared_statement_run_select
1527 plugin->select_entry_by_hash_and_vhash, 6, nrc->rbind, &return_ok, NULL,
1528 MYSQL_TYPE_BLOB, &gc->key, hashSize, &hashSize, MYSQL_TYPE_BLOB,
1529 &gc->vhash, hashSize, &hashSize, MYSQL_TYPE_LONGLONG,
1530 &nrc->last_vkey, GNUNET_YES, MYSQL_TYPE_LONG, &limit_off,
1536 prepared_statement_run_select
1538 plugin->select_entry_by_hash, 6, nrc->rbind, &return_ok, NULL,
1539 MYSQL_TYPE_BLOB, &gc->key, hashSize, &hashSize,
1540 MYSQL_TYPE_LONGLONG, &nrc->last_vkey, GNUNET_YES, MYSQL_TYPE_LONG,
1541 &limit_off, GNUNET_YES, -1);
1550 * Iterate over the results for a particular key
1553 * @param cls closure
1554 * @param key maybe NULL (to match all entries)
1555 * @param vhash hash of the value, maybe NULL (to
1556 * match all values that have the right key).
1557 * Note that for DBlocks there is no difference
1558 * betwen key and vhash, but for other blocks
1560 * @param type entries of which type are relevant?
1561 * Use 0 for any type.
1562 * @param iter function to call on each matching value;
1563 * will be called once with a NULL value at the end
1564 * @param iter_cls closure for iter
1567 mysql_plugin_get (void *cls,
1568 const GNUNET_HashCode * key,
1569 const GNUNET_HashCode * vhash,
1570 enum GNUNET_BLOCK_Type type,
1571 PluginIterator iter, void *iter_cls)
1573 struct Plugin *plugin = cls;
1574 unsigned int itype = type;
1576 MYSQL_BIND cbind[1];
1577 struct GetContext *gc;
1578 struct NextRequestClosure *nrc;
1580 unsigned long hashSize;
1586 mysql_plugin_iter_low_priority (plugin,
1591 hashSize = sizeof (GNUNET_HashCode);
1592 memset (cbind, 0, sizeof (cbind));
1594 cbind[0].buffer_type = MYSQL_TYPE_LONGLONG;
1595 cbind[0].buffer = &total;
1596 cbind[0].is_unsigned = GNUNET_NO;
1602 prepared_statement_run_select
1604 plugin->count_entry_by_hash_vhash_and_type, 1, cbind, &return_ok, NULL,
1605 MYSQL_TYPE_BLOB, key, hashSize, &hashSize, MYSQL_TYPE_BLOB,
1606 vhash, hashSize, &hashSize, MYSQL_TYPE_LONG, &itype, GNUNET_YES,
1612 prepared_statement_run_select
1614 plugin->count_entry_by_hash_and_type, 1, cbind, &return_ok, NULL,
1615 MYSQL_TYPE_BLOB, key, hashSize, &hashSize, MYSQL_TYPE_LONG,
1616 &itype, GNUNET_YES, -1);
1625 prepared_statement_run_select
1627 plugin->count_entry_by_hash_and_vhash, 1, cbind, &return_ok, NULL,
1628 MYSQL_TYPE_BLOB, key, hashSize, &hashSize, MYSQL_TYPE_BLOB,
1629 vhash, hashSize, &hashSize, -1);
1635 prepared_statement_run_select (plugin,
1636 plugin->count_entry_by_hash,
1637 1, cbind, &return_ok,
1638 NULL, MYSQL_TYPE_BLOB,
1643 if ((ret != GNUNET_OK) || (0 >= total))
1646 NULL, NULL, 0, NULL, 0, 0, 0,
1647 GNUNET_TIME_UNIT_ZERO_ABS, 0);
1651 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1652 "Iterating over %lld results for GET `%s'\n",
1656 gc = GNUNET_malloc (sizeof (struct GetContext));
1660 gc->have_vhash = GNUNET_YES;
1664 gc->off = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, total);
1667 nrc = GNUNET_malloc (sizeof (struct NextRequestClosure));
1668 nrc->plugin = plugin;
1670 nrc->iter_select = -1;
1672 nrc->dviter_cls = iter_cls;
1673 nrc->prep = &get_statement_prepare;
1676 mysql_plugin_next_request (nrc, GNUNET_NO);
1681 * Update the priority for a particular key in the datastore. If
1682 * the expiration time in value is different than the time found in
1683 * the datastore, the higher value should be kept. For the
1684 * anonymity level, the lower value is to be used. The specified
1685 * priority should be added to the existing priority, ignoring the
1686 * priority in value.
1688 * Note that it is possible for multiple values to match this put.
1689 * In that case, all of the respective values are updated.
1691 * @param cls our "struct Plugin*"
1692 * @param uid unique identifier of the datum
1693 * @param delta by how much should the priority
1694 * change? If priority + delta < 0 the
1695 * priority should be set to 0 (never go
1697 * @param expire new expiration time should be the
1698 * MAX of any existing expiration time and
1700 * @param msg set to error message
1701 * @return GNUNET_OK on success
1704 mysql_plugin_update (void *cls,
1707 struct GNUNET_TIME_Absolute expire,
1710 struct Plugin *plugin = cls;
1711 unsigned long long vkey = uid;
1712 unsigned long long lexpire = expire.value;
1716 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1717 "Updating value %llu adding %d to priority and maxing exp at %llu\n",
1722 ret = prepared_statement_run (plugin,
1723 plugin->update_entry,
1728 MYSQL_TYPE_LONGLONG,
1731 MYSQL_TYPE_LONGLONG,
1734 MYSQL_TYPE_LONGLONG,
1737 if (ret != GNUNET_OK)
1739 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1740 "Failed to update value %llu\n",
1748 * Select a subset of the items in the datastore and call
1749 * the given iterator for each of them.
1751 * @param cls our "struct Plugin*"
1752 * @param type entries of which type should be considered?
1753 * Use 0 for any type.
1754 * @param iter function to call on each matching value;
1755 * will be called once with a NULL value at the end
1756 * @param iter_cls closure for iter
1759 mysql_plugin_iter_zero_anonymity (void *cls,
1760 enum GNUNET_BLOCK_Type type,
1761 PluginIterator iter,
1764 struct Plugin *plugin = cls;
1765 iterateHelper (plugin, type, GNUNET_NO, 1, iter, iter_cls);
1770 * Select a subset of the items in the datastore and call
1771 * the given iterator for each of them.
1773 * @param cls our "struct Plugin*"
1774 * @param type entries of which type should be considered?
1775 * Use 0 for any type.
1776 * @param iter function to call on each matching value;
1777 * will be called once with a NULL value at the end
1778 * @param iter_cls closure for iter
1781 mysql_plugin_iter_ascending_expiration (void *cls,
1782 enum GNUNET_BLOCK_Type type,
1783 PluginIterator iter,
1786 struct Plugin *plugin = cls;
1787 iterateHelper (plugin, type, GNUNET_YES, 2, iter, iter_cls);
1792 * Select a subset of the items in the datastore and call
1793 * the given iterator for each of them.
1795 * @param cls our "struct Plugin*"
1796 * @param type entries of which type should be considered?
1797 * Use 0 for any type.
1798 * @param iter function to call on each matching value;
1799 * will be called once with a NULL value at the end
1800 * @param iter_cls closure for iter
1803 mysql_plugin_iter_migration_order (void *cls,
1804 enum GNUNET_BLOCK_Type type,
1805 PluginIterator iter,
1808 struct Plugin *plugin = cls;
1809 iterateHelper (plugin, 0, GNUNET_NO, 3, iter, iter_cls);
1814 * Select a subset of the items in the datastore and call
1815 * the given iterator for each of them.
1817 * @param cls our "struct Plugin*"
1818 * @param type entries of which type should be considered?
1819 * Use 0 for any type.
1820 * @param iter function to call on each matching value;
1821 * will be called once with a NULL value at the end
1822 * @param iter_cls closure for iter
1825 mysql_plugin_iter_all_now (void *cls,
1826 enum GNUNET_BLOCK_Type type,
1827 PluginIterator iter,
1830 struct Plugin *plugin = cls;
1831 iterateHelper (plugin, 0, GNUNET_YES, 0, iter, iter_cls);
1839 mysql_plugin_drop (void *cls)
1841 struct Plugin *plugin = cls;
1843 if ((GNUNET_OK != run_statement (plugin,
1844 "DROP TABLE gn090")) ||
1845 (GNUNET_OK != run_statement (plugin,
1846 "DROP TABLE gn072")))
1848 plugin->content_size = 0;
1853 * Entry point for the plugin.
1855 * @param cls the "struct GNUNET_DATASTORE_PluginEnvironment*"
1856 * @return our "struct Plugin*"
1859 libgnunet_plugin_datastore_mysql_init (void *cls)
1861 struct GNUNET_DATASTORE_PluginEnvironment *env = cls;
1862 struct GNUNET_DATASTORE_PluginFunctions *api;
1863 struct Plugin *plugin;
1865 plugin = GNUNET_malloc (sizeof (struct Plugin));
1867 plugin->cnffile = get_my_cnf_path (env->cfg);
1868 if (GNUNET_OK != iopen (plugin))
1871 GNUNET_free_non_null (plugin->cnffile);
1872 GNUNET_free (plugin);
1875 #define MRUNS(a) (GNUNET_OK != run_statement (plugin, a) )
1876 #define PINIT(a,b) (NULL == (a = prepared_statement_create(plugin, b)))
1877 if (MRUNS ("CREATE TABLE IF NOT EXISTS gn090 ("
1878 " type INT(11) UNSIGNED NOT NULL DEFAULT 0,"
1879 " prio INT(11) UNSIGNED NOT NULL DEFAULT 0,"
1880 " anonLevel INT(11) UNSIGNED NOT NULL DEFAULT 0,"
1881 " expire BIGINT UNSIGNED NOT NULL DEFAULT 0,"
1882 " hash BINARY(64) NOT NULL DEFAULT '',"
1883 " vhash BINARY(64) NOT NULL DEFAULT '',"
1884 " vkey BIGINT UNSIGNED NOT NULL DEFAULT 0,"
1885 " INDEX hash (hash(64)),"
1886 " INDEX hash_vhash_vkey (hash(64),vhash(64),vkey),"
1887 " INDEX hash_vkey (hash(64),vkey),"
1888 " INDEX vkey (vkey),"
1889 " INDEX prio (prio,vkey),"
1890 " INDEX expire (expire,vkey,type),"
1891 " INDEX anonLevel (anonLevel,prio,vkey,type)"
1892 ") ENGINE=InnoDB") ||
1893 MRUNS ("CREATE TABLE IF NOT EXISTS gn072 ("
1894 " vkey BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,"
1895 " value BLOB NOT NULL DEFAULT '') ENGINE=MyISAM") ||
1896 MRUNS ("SET AUTOCOMMIT = 1") ||
1897 PINIT (plugin->select_value, SELECT_VALUE) ||
1898 PINIT (plugin->delete_value, DELETE_VALUE) ||
1899 PINIT (plugin->insert_value, INSERT_VALUE) ||
1900 PINIT (plugin->insert_entry, INSERT_ENTRY) ||
1901 PINIT (plugin->delete_entry_by_vkey, DELETE_ENTRY_BY_VKEY) ||
1902 PINIT (plugin->select_entry_by_hash, SELECT_ENTRY_BY_HASH) ||
1903 PINIT (plugin->select_entry_by_hash_and_vhash, SELECT_ENTRY_BY_HASH_AND_VHASH)
1904 || PINIT (plugin->select_entry_by_hash_and_type, SELECT_ENTRY_BY_HASH_AND_TYPE)
1905 || PINIT (plugin->select_entry_by_hash_vhash_and_type,
1906 SELECT_ENTRY_BY_HASH_VHASH_AND_TYPE)
1907 || PINIT (plugin->count_entry_by_hash, COUNT_ENTRY_BY_HASH)
1908 || PINIT (plugin->count_entry_by_hash_and_vhash, COUNT_ENTRY_BY_HASH_AND_VHASH)
1909 || PINIT (plugin->count_entry_by_hash_and_type, COUNT_ENTRY_BY_HASH_AND_TYPE)
1910 || PINIT (plugin->count_entry_by_hash_vhash_and_type,
1911 COUNT_ENTRY_BY_HASH_VHASH_AND_TYPE)
1912 || PINIT (plugin->update_entry, UPDATE_ENTRY)
1913 || PINIT (plugin->iter[0], SELECT_IT_LOW_PRIORITY)
1914 || PINIT (plugin->iter[1], SELECT_IT_NON_ANONYMOUS)
1915 || PINIT (plugin->iter[2], SELECT_IT_EXPIRATION_TIME)
1916 || PINIT (plugin->iter[3], SELECT_IT_MIGRATION_ORDER))
1919 GNUNET_free_non_null (plugin->cnffile);
1920 GNUNET_free (plugin);
1926 api = GNUNET_malloc (sizeof (struct GNUNET_DATASTORE_PluginFunctions));
1928 api->get_size = &mysql_plugin_get_size;
1929 api->put = &mysql_plugin_put;
1930 api->next_request = &mysql_plugin_next_request;
1931 api->get = &mysql_plugin_get;
1932 api->update = &mysql_plugin_update;
1933 api->iter_low_priority = &mysql_plugin_iter_low_priority;
1934 api->iter_zero_anonymity = &mysql_plugin_iter_zero_anonymity;
1935 api->iter_ascending_expiration = &mysql_plugin_iter_ascending_expiration;
1936 api->iter_migration_order = &mysql_plugin_iter_migration_order;
1937 api->iter_all_now = &mysql_plugin_iter_all_now;
1938 api->drop = &mysql_plugin_drop;
1939 GNUNET_log_from (GNUNET_ERROR_TYPE_INFO,
1940 "mysql", _("Mysql database running\n"));
1946 * Exit point from the plugin.
1947 * @param cls our "struct Plugin*"
1948 * @return always NULL
1951 libgnunet_plugin_datastore_mysql_done (void *cls)
1953 struct GNUNET_DATASTORE_PluginFunctions *api = cls;
1954 struct Plugin *plugin = api->cls;
1957 if (plugin->next_task != GNUNET_SCHEDULER_NO_TASK)
1959 GNUNET_SCHEDULER_cancel (plugin->env->sched,
1961 plugin->next_task = GNUNET_SCHEDULER_NO_TASK;
1962 plugin->next_task_nc->prep (plugin->next_task_nc->prep_cls, NULL);
1963 GNUNET_free (plugin->next_task_nc);
1964 plugin->next_task_nc = NULL;
1966 GNUNET_free_non_null (plugin->cnffile);
1967 GNUNET_free (plugin);
1969 mysql_library_end ();
1973 /* end of plugin_datastore_mysql.c */