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 gn080;
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.
125 * - remove 'size' field in gn080.
126 * - use FOREIGN KEY for 'uid/vkey'
127 * - consistent naming of uid/vkey
130 #include "platform.h"
131 #include "plugin_datastore.h"
132 #include "gnunet_util_lib.h"
133 #include <mysql/mysql.h>
135 #define DEBUG_MYSQL GNUNET_NO
137 #define MAX_DATUM_SIZE 65536
140 * Maximum number of supported parameters for a prepared
141 * statement. Increase if needed.
146 * Die with an error message that indicates
147 * a failure of the command 'cmd' with the message given
148 * by strerror(errno).
150 #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);
153 * Log an error message at log-level 'level' that indicates
154 * a failure of the command 'cmd' on file 'filename'
155 * with the message given by strerror(errno).
157 #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);
160 /* warning, slighly crazy mysql statements ahead. Essentially, MySQL does not handle
161 "OR" very well, so we need to use UNION instead. And UNION does not
162 automatically apply a LIMIT on the outermost clause, so we need to
163 repeat ourselves quite a bit. All hail the performance gods (and thanks
164 to #mysql on freenode) */
165 #define SELECT_IT_LOW_PRIORITY "(SELECT size,type,prio,anonLevel,expire,hash,vkey FROM gn080 FORCE INDEX(prio) WHERE (prio = ? AND vkey > ?) "\
166 "ORDER BY prio ASC,vkey ASC LIMIT 1) "\
168 "(SELECT size,type,prio,anonLevel,expire,hash,vkey FROM gn080 FORCE INDEX(prio) WHERE (prio > ? AND vkey != ?)"\
169 "ORDER BY prio ASC,vkey ASC LIMIT 1)"\
170 "ORDER BY prio ASC,vkey ASC LIMIT 1"
172 #define SELECT_IT_NON_ANONYMOUS "(SELECT size,type,prio,anonLevel,expire,hash,vkey FROM gn080 FORCE INDEX(prio) WHERE (prio = ? AND vkey < ?)"\
173 " AND anonLevel=0 ORDER BY prio DESC,vkey DESC LIMIT 1) "\
175 "(SELECT size,type,prio,anonLevel,expire,hash,vkey FROM gn080 FORCE INDEX(prio) WHERE (prio < ? AND vkey != ?)"\
176 " AND anonLevel=0 ORDER BY prio DESC,vkey DESC LIMIT 1) "\
177 "ORDER BY prio DESC,vkey DESC LIMIT 1"
179 #define SELECT_IT_EXPIRATION_TIME "(SELECT size,type,prio,anonLevel,expire,hash,vkey FROM gn080 FORCE INDEX(expire) WHERE (expire = ? AND vkey > ?) "\
180 "ORDER BY expire ASC,vkey ASC LIMIT 1) "\
182 "(SELECT size,type,prio,anonLevel,expire,hash,vkey FROM gn080 FORCE INDEX(expire) WHERE (expire > ? AND vkey != ?) "\
183 "ORDER BY expire ASC,vkey ASC LIMIT 1)"\
184 "ORDER BY expire ASC,vkey ASC LIMIT 1"
187 #define SELECT_IT_MIGRATION_ORDER "(SELECT size,type,prio,anonLevel,expire,hash,vkey FROM gn080 FORCE INDEX(expire) WHERE (expire = ? AND vkey < ?)"\
188 " AND expire > ? AND type!=3"\
189 " ORDER BY expire DESC,vkey DESC LIMIT 1) "\
191 "(SELECT size,type,prio,anonLevel,expire,hash,vkey FROM gn080 FORCE INDEX(expire) WHERE (expire < ? AND vkey != ?)"\
192 " AND expire > ? AND type!=3"\
193 " ORDER BY expire DESC,vkey DESC LIMIT 1)"\
194 "ORDER BY expire DESC,vkey DESC LIMIT 1"
196 #define SELECT_SIZE "SELECT sum(size) FROM gn080"
199 struct GNUNET_MysqlStatementHandle
201 struct GNUNET_MysqlStatementHandle *next;
203 struct GNUNET_MysqlStatementHandle *prev;
207 MYSQL_STMT *statement;
214 * Context for the universal iterator.
216 struct NextRequestClosure;
219 * Type of a function that will prepare
220 * the next iteration.
223 * @param nc the next context; NULL for the last
224 * call which gives the callback a chance to
225 * clean up the closure
226 * @return GNUNET_OK on success, GNUNET_NO if there are
227 * no more values, GNUNET_SYSERR on error
229 typedef int (*PrepareFunction)(void *cls,
230 struct NextRequestClosure *nc);
233 struct NextRequestClosure
235 struct Plugin *plugin;
237 struct GNUNET_TIME_Absolute now;
240 * Function to call to prepare the next
243 PrepareFunction prep;
254 unsigned int iter_select;
256 PluginIterator dviter;
260 unsigned int last_prio;
262 unsigned long long last_expire;
264 unsigned long long last_vkey;
271 * Context for all functions in this plugin.
276 * Our execution environment.
278 struct GNUNET_DATASTORE_PluginEnvironment *env;
282 struct GNUNET_MysqlStatementHandle *shead;
284 struct GNUNET_MysqlStatementHandle *stail;
287 * Filename of "my.cnf" (msyql configuration).
292 * Closure of the 'next_task' (must be freed if 'next_task' is cancelled).
294 struct NextRequestClosure *next_task_nc;
297 * Pending task with scheduler for running the next request.
299 GNUNET_SCHEDULER_TaskIdentifier next_task;
302 * Statements dealing with gn072 table
304 #define SELECT_VALUE "SELECT value FROM gn072 WHERE vkey=?"
305 struct GNUNET_MysqlStatementHandle *select_value;
307 #define DELETE_VALUE "DELETE FROM gn072 WHERE vkey=?"
308 struct GNUNET_MysqlStatementHandle *delete_value;
310 #define INSERT_VALUE "INSERT INTO gn072 (value) VALUES (?)"
311 struct GNUNET_MysqlStatementHandle *insert_value;
314 * Statements dealing with gn080 table
316 #define INSERT_ENTRY "INSERT INTO gn080 (size,type,prio,anonLevel,expire,hash,vhash,vkey) VALUES (?,?,?,?,?,?,?,?)"
317 struct GNUNET_MysqlStatementHandle *insert_entry;
319 #define DELETE_ENTRY_BY_VKEY "DELETE FROM gn080 WHERE vkey=?"
320 struct GNUNET_MysqlStatementHandle *delete_entry_by_vkey;
322 #define SELECT_ENTRY_BY_HASH "SELECT size,type,prio,anonLevel,expire,hash,vkey FROM gn080 FORCE INDEX (hash_vkey) WHERE hash=? AND vkey > ? ORDER BY vkey ASC LIMIT 1 OFFSET ?"
323 struct GNUNET_MysqlStatementHandle *select_entry_by_hash;
325 #define SELECT_ENTRY_BY_HASH_AND_VHASH "SELECT size,type,prio,anonLevel,expire,hash,vkey FROM gn080 FORCE INDEX (hash_vhash_vkey) WHERE hash=? AND vhash=? AND vkey > ? ORDER BY vkey ASC LIMIT 1 OFFSET ?"
326 struct GNUNET_MysqlStatementHandle *select_entry_by_hash_and_vhash;
328 #define SELECT_ENTRY_BY_HASH_AND_TYPE "SELECT size,type,prio,anonLevel,expire,hash,vkey FROM gn080 FORCE INDEX (hash_vkey) WHERE hash=? AND vkey > ? AND type=? ORDER BY vkey ASC LIMIT 1 OFFSET ?"
329 struct GNUNET_MysqlStatementHandle *select_entry_by_hash_and_type;
331 #define SELECT_ENTRY_BY_HASH_VHASH_AND_TYPE "SELECT size,type,prio,anonLevel,expire,hash,vkey FROM gn080 FORCE INDEX (hash_vhash_vkey) WHERE hash=? AND vhash=? AND vkey > ? AND type=? ORDER BY vkey ASC LIMIT 1 OFFSET ?"
332 struct GNUNET_MysqlStatementHandle *select_entry_by_hash_vhash_and_type;
334 #define COUNT_ENTRY_BY_HASH "SELECT count(*) FROM gn080 FORCE INDEX (hash) WHERE hash=?"
335 struct GNUNET_MysqlStatementHandle *count_entry_by_hash;
337 #define COUNT_ENTRY_BY_HASH_AND_VHASH "SELECT count(*) FROM gn080 FORCE INDEX (hash_vhash_vkey) WHERE hash=? AND vhash=?"
338 struct GNUNET_MysqlStatementHandle *count_entry_by_hash_and_vhash;
340 #define COUNT_ENTRY_BY_HASH_AND_TYPE "SELECT count(*) FROM gn080 FORCE INDEX (hash) WHERE hash=? AND type=?"
341 struct GNUNET_MysqlStatementHandle *count_entry_by_hash_and_type;
343 #define COUNT_ENTRY_BY_HASH_VHASH_AND_TYPE "SELECT count(*) FROM gn080 FORCE INDEX (hash_vhash) WHERE hash=? AND vhash=? AND type=?"
344 struct GNUNET_MysqlStatementHandle *count_entry_by_hash_vhash_and_type;
346 #define UPDATE_ENTRY "UPDATE gn080 SET prio=prio+?,expire=IF(expire>=?,expire,?) WHERE vkey=?"
347 struct GNUNET_MysqlStatementHandle *update_entry;
349 struct GNUNET_MysqlStatementHandle *iter[4];
351 //static unsigned int stat_size;
354 * Size of the mysql database on disk.
356 unsigned long long content_size;
362 * Obtain the location of ".my.cnf".
363 * @return NULL on error
366 get_my_cnf_path (const struct GNUNET_CONFIGURATION_Handle *cfg)
377 pw = getpwuid (getuid ());
380 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
385 GNUNET_CONFIGURATION_have_value (cfg,
386 "datastore-mysql", "CONFIG"))
388 GNUNET_assert (GNUNET_OK ==
389 GNUNET_CONFIGURATION_get_value_filename (cfg,
390 "datastore-mysql", "CONFIG", &cnffile));
391 configured = GNUNET_YES;
395 home_dir = GNUNET_strdup (pw->pw_dir);
397 home_dir = (char *) GNUNET_malloc (_MAX_PATH + 1);
398 plibc_conv_to_win_path ("~/", home_dir);
400 GNUNET_asprintf (&cnffile, "%s/.my.cnf", home_dir);
401 GNUNET_free (home_dir);
402 configured = GNUNET_NO;
404 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
405 _("Trying to use file `%s' for MySQL configuration.\n"),
407 if ((0 != STAT (cnffile, &st)) ||
408 (0 != ACCESS (cnffile, R_OK)) || (!S_ISREG (st.st_mode)))
410 if (configured == GNUNET_YES)
411 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
412 _("Could not access file `%s': %s\n"), cnffile,
414 GNUNET_free (cnffile);
423 * Free a prepared statement.
426 prepared_statement_destroy (struct Plugin *plugin,
427 struct GNUNET_MysqlStatementHandle
430 GNUNET_CONTAINER_DLL_remove (plugin->shead,
434 mysql_stmt_close (s->statement);
435 GNUNET_free (s->query);
441 * Close database connection and all prepared statements (we got a DB
445 iclose (struct Plugin *plugin)
447 struct GNUNET_MysqlStatementHandle *spos;
449 spos = plugin->shead;
450 while (NULL != plugin->shead)
451 prepared_statement_destroy (plugin,
453 if (plugin->dbf != NULL)
455 mysql_close (plugin->dbf);
463 * Open the connection with the database (and initialize
464 * our default options).
466 * @return GNUNET_OK on success
469 iopen (struct Plugin *ret)
474 char *mysql_password;
475 unsigned long long mysql_port;
477 unsigned int timeout;
479 ret->dbf = mysql_init (NULL);
480 if (ret->dbf == NULL)
481 return GNUNET_SYSERR;
482 if (ret->cnffile != NULL)
483 mysql_options (ret->dbf, MYSQL_READ_DEFAULT_FILE, ret->cnffile);
484 mysql_options (ret->dbf, MYSQL_READ_DEFAULT_GROUP, "client");
486 mysql_options (ret->dbf, MYSQL_OPT_RECONNECT, &reconnect);
487 mysql_options (ret->dbf,
488 MYSQL_OPT_CONNECT_TIMEOUT, (const void *) &timeout);
489 mysql_options(ret->dbf, MYSQL_SET_CHARSET_NAME, "UTF8");
490 timeout = 60; /* in seconds */
491 mysql_options (ret->dbf, MYSQL_OPT_READ_TIMEOUT, (const void *) &timeout);
492 mysql_options (ret->dbf, MYSQL_OPT_WRITE_TIMEOUT, (const void *) &timeout);
494 if (GNUNET_YES == GNUNET_CONFIGURATION_have_value (ret->env->cfg,
495 "datastore-mysql", "DATABASE"))
496 GNUNET_assert (GNUNET_OK ==
497 GNUNET_CONFIGURATION_get_value_string (ret->env->cfg,
498 "datastore-mysql", "DATABASE",
501 mysql_dbname = GNUNET_strdup ("gnunet");
503 if (GNUNET_YES == GNUNET_CONFIGURATION_have_value (ret->env->cfg,
504 "datastore-mysql", "USER"))
506 GNUNET_assert (GNUNET_OK ==
507 GNUNET_CONFIGURATION_get_value_string (ret->env->cfg,
508 "datastore-mysql", "USER",
511 mysql_password = NULL;
512 if (GNUNET_YES == GNUNET_CONFIGURATION_have_value (ret->env->cfg,
513 "datastore-mysql", "PASSWORD"))
515 GNUNET_assert (GNUNET_OK ==
516 GNUNET_CONFIGURATION_get_value_string (ret->env->cfg,
517 "datastore-mysql", "PASSWORD",
521 if (GNUNET_YES == GNUNET_CONFIGURATION_have_value (ret->env->cfg,
522 "datastore-mysql", "HOST"))
524 GNUNET_assert (GNUNET_OK ==
525 GNUNET_CONFIGURATION_get_value_string (ret->env->cfg,
526 "datastore-mysql", "HOST",
530 if (GNUNET_YES == GNUNET_CONFIGURATION_have_value (ret->env->cfg,
531 "datastore-mysql", "PORT"))
533 GNUNET_assert (GNUNET_OK ==
534 GNUNET_CONFIGURATION_get_value_number (ret->env->cfg, "datastore-mysql",
535 "PORT", &mysql_port));
538 GNUNET_assert (mysql_dbname != NULL);
539 mysql_real_connect (ret->dbf, mysql_server, mysql_user, mysql_password,
540 mysql_dbname, (unsigned int) mysql_port, NULL,
541 CLIENT_IGNORE_SIGPIPE);
542 GNUNET_free_non_null (mysql_server);
543 GNUNET_free_non_null (mysql_user);
544 GNUNET_free_non_null (mysql_password);
545 GNUNET_free (mysql_dbname);
546 if (mysql_error (ret->dbf)[0])
548 LOG_MYSQL (GNUNET_ERROR_TYPE_ERROR,
549 "mysql_real_connect", ret);
550 return GNUNET_SYSERR;
557 * Run the given MySQL statement.
559 * @return GNUNET_OK on success, GNUNET_SYSERR on error
562 run_statement (struct Plugin *plugin,
563 const char *statement)
565 if ((NULL == plugin->dbf) && (GNUNET_OK != iopen (plugin)))
566 return GNUNET_SYSERR;
567 mysql_query (plugin->dbf, statement);
568 if (mysql_error (plugin->dbf)[0])
570 LOG_MYSQL (GNUNET_ERROR_TYPE_ERROR,
571 "mysql_query", plugin);
573 return GNUNET_SYSERR;
581 * Run the given MySQL SELECT statement. The statement
582 * must have only a single result (one column, one row).
584 * @return result on success, NULL on error
587 run_statement_select (struct Plugin *plugin,
588 const char *statement)
594 if ((NULL == plugin->dbf) && (GNUNET_OK != iopen (plugin)))
596 mysql_query (plugin->dbf, statement);
597 if ((mysql_error (plugin->dbf)[0]) ||
598 (!(sql_res = mysql_use_result (plugin->dbf))) ||
599 (!(sql_row = mysql_fetch_row (sql_res))))
601 LOG_MYSQL (GNUNET_ERROR_TYPE_ERROR,
602 "mysql_query", plugin);
605 if ((mysql_num_fields (sql_res) != 1) || (sql_row[0] == NULL))
607 GNUNET_break (mysql_num_fields (sql_res) == 1);
609 mysql_free_result (sql_res);
612 ret = GNUNET_strdup (sql_row[0]);
613 mysql_free_result (sql_res);
620 * Create a prepared statement.
622 * @return NULL on error
624 static struct GNUNET_MysqlStatementHandle *
625 prepared_statement_create (struct Plugin *plugin,
626 const char *statement)
628 struct GNUNET_MysqlStatementHandle *ret;
630 ret = GNUNET_malloc (sizeof (struct GNUNET_MysqlStatementHandle));
631 ret->query = GNUNET_strdup (statement);
632 GNUNET_CONTAINER_DLL_insert (plugin->shead,
640 * Prepare a statement for running.
642 * @return GNUNET_OK on success
645 prepare_statement (struct Plugin *plugin,
646 struct GNUNET_MysqlStatementHandle *ret)
648 if (GNUNET_YES == ret->valid)
650 if ((NULL == plugin->dbf) &&
651 (GNUNET_OK != iopen (plugin)))
652 return GNUNET_SYSERR;
653 ret->statement = mysql_stmt_init (plugin->dbf);
654 if (ret->statement == NULL)
657 return GNUNET_SYSERR;
659 if (mysql_stmt_prepare (ret->statement,
661 strlen (ret->query)))
663 LOG_MYSQL (GNUNET_ERROR_TYPE_ERROR,
664 "mysql_stmt_prepare",
666 mysql_stmt_close (ret->statement);
667 ret->statement = NULL;
669 return GNUNET_SYSERR;
671 ret->valid = GNUNET_YES;
678 * Bind the parameters for the given MySQL statement
681 * @param s statement to bind and run
682 * @param ap arguments for the binding
683 * @return GNUNET_SYSERR on error, GNUNET_OK on success
686 init_params (struct Plugin *plugin,
687 struct GNUNET_MysqlStatementHandle *s,
690 MYSQL_BIND qbind[MAX_PARAM];
693 enum enum_field_types ft;
695 pc = mysql_stmt_param_count (s->statement);
698 /* increase internal constant! */
700 return GNUNET_SYSERR;
702 memset (qbind, 0, sizeof (qbind));
705 while ((pc > 0) && (-1 != (ft = va_arg (ap, enum enum_field_types))))
707 qbind[off].buffer_type = ft;
710 case MYSQL_TYPE_FLOAT:
711 qbind[off].buffer = va_arg (ap, float *);
713 case MYSQL_TYPE_LONGLONG:
714 qbind[off].buffer = va_arg (ap, unsigned long long *);
715 qbind[off].is_unsigned = va_arg (ap, int);
717 case MYSQL_TYPE_LONG:
718 qbind[off].buffer = va_arg (ap, unsigned int *);
719 qbind[off].is_unsigned = va_arg (ap, int);
721 case MYSQL_TYPE_VAR_STRING:
722 case MYSQL_TYPE_STRING:
723 case MYSQL_TYPE_BLOB:
724 qbind[off].buffer = va_arg (ap, void *);
725 qbind[off].buffer_length = va_arg (ap, unsigned long);
726 qbind[off].length = va_arg (ap, unsigned long *);
729 /* unsupported type */
731 return GNUNET_SYSERR;
736 if (!((pc == 0) && (ft != -1) && (va_arg (ap, int) == -1)))
739 return GNUNET_SYSERR;
741 if (mysql_stmt_bind_param (s->statement, qbind))
743 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
744 _("`%s' failed at %s:%d with error: %s\n"),
745 "mysql_stmt_bind_param",
746 __FILE__, __LINE__, mysql_stmt_error (s->statement));
748 return GNUNET_SYSERR;
750 if (mysql_stmt_execute (s->statement))
752 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
753 _("`%s' failed at %s:%d with error: %s\n"),
754 "mysql_stmt_execute",
755 __FILE__, __LINE__, mysql_stmt_error (s->statement));
757 return GNUNET_SYSERR;
763 * Type of a callback that will be called for each
764 * data set returned from MySQL.
766 * @param cls user-defined argument
767 * @param num_values number of elements in values
768 * @param values values returned by MySQL
769 * @return GNUNET_OK to continue iterating, GNUNET_SYSERR to abort
771 typedef int (*GNUNET_MysqlDataProcessor) (void *cls,
772 unsigned int num_values,
773 MYSQL_BIND * values);
777 * Run a prepared SELECT statement.
779 * @param result_size number of elements in results array
780 * @param results pointer to already initialized MYSQL_BIND
781 * array (of sufficient size) for passing results
782 * @param processor function to call on each result
783 * @param processor_cls extra argument to processor
784 * @param ... pairs and triplets of "MYSQL_TYPE_XXX" keys and their respective
785 * values (size + buffer-reference for pointers); terminated
787 * @return GNUNET_SYSERR on error, otherwise
788 * the number of successfully affected (or queried) rows
791 prepared_statement_run_select (struct Plugin *plugin,
792 struct GNUNET_MysqlStatementHandle
794 unsigned int result_size,
795 MYSQL_BIND * results,
796 GNUNET_MysqlDataProcessor
797 processor, void *processor_cls,
805 if (GNUNET_OK != prepare_statement (plugin, s))
808 return GNUNET_SYSERR;
810 va_start (ap, processor_cls);
811 if (GNUNET_OK != init_params (plugin, s, ap))
815 return GNUNET_SYSERR;
818 rsize = mysql_stmt_field_count (s->statement);
819 if (rsize > result_size)
822 return GNUNET_SYSERR;
824 if (mysql_stmt_bind_result (s->statement, results))
826 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
827 _("`%s' failed at %s:%d with error: %s\n"),
828 "mysql_stmt_bind_result",
829 __FILE__, __LINE__, mysql_stmt_error (s->statement));
831 return GNUNET_SYSERR;
837 ret = mysql_stmt_fetch (s->statement);
838 if (ret == MYSQL_NO_DATA)
842 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
843 _("`%s' failed at %s:%d with error: %s\n"),
845 __FILE__, __LINE__, mysql_stmt_error (s->statement));
847 return GNUNET_SYSERR;
849 if (processor != NULL)
850 if (GNUNET_OK != processor (processor_cls, rsize, results))
854 mysql_stmt_reset (s->statement);
860 * Run a prepared statement that does NOT produce results.
862 * @param ... pairs and triplets of "MYSQL_TYPE_XXX" keys and their respective
863 * values (size + buffer-reference for pointers); terminated
865 * @param insert_id NULL or address where to store the row ID of whatever
866 * was inserted (only for INSERT statements!)
867 * @return GNUNET_SYSERR on error, otherwise
868 * the number of successfully affected rows
871 prepared_statement_run (struct Plugin *plugin,
872 struct GNUNET_MysqlStatementHandle *s,
873 unsigned long long *insert_id, ...)
878 if (GNUNET_OK != prepare_statement (plugin, s))
879 return GNUNET_SYSERR;
880 va_start (ap, insert_id);
881 if (GNUNET_OK != init_params (plugin, s, ap))
884 return GNUNET_SYSERR;
887 affected = mysql_stmt_affected_rows (s->statement);
888 if (NULL != insert_id)
889 *insert_id = (unsigned long long) mysql_stmt_insert_id (s->statement);
890 mysql_stmt_reset (s->statement);
896 * Delete an value from the gn072 table.
898 * @param vkey vkey identifying the value to delete
899 * @return GNUNET_OK on success, GNUNET_NO if no such value exists, GNUNET_SYSERR on error
902 do_delete_value (struct Plugin *plugin,
903 unsigned long long vkey)
908 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
909 "Deleting value %llu from gn072 table\n",
912 ret = prepared_statement_run (plugin,
913 plugin->delete_value,
916 &vkey, GNUNET_YES, -1);
923 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
924 "Deleting value %llu from gn072 table failed\n",
931 * Insert a value into the gn072 table.
933 * @param value the value to insert
934 * @param size size of the value
935 * @param vkey vkey identifying the value henceforth (set)
936 * @return GNUNET_OK on success, GNUNET_SYSERR on error
939 do_insert_value (struct Plugin *plugin,
940 const void *value, unsigned int size,
941 unsigned long long *vkey)
943 unsigned long length = size;
946 ret = prepared_statement_run (plugin,
947 plugin->insert_value,
950 value, length, &length, -1);
951 if (ret == GNUNET_OK)
954 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
955 "Inserted value number %llu with length %u into gn072 table\n",
962 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
963 "Failed to insert %u byte value into gn072 table\n",
970 * Delete an entry from the gn080 table.
972 * @param vkey vkey identifying the entry to delete
973 * @return GNUNET_OK on success, GNUNET_NO if no such value exists, GNUNET_SYSERR on error
976 do_delete_entry_by_vkey (struct Plugin *plugin,
977 unsigned long long vkey)
982 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
983 "Deleting value %llu from gn080 table\n",
986 ret = prepared_statement_run (plugin,
987 plugin->delete_entry_by_vkey,
990 &vkey, GNUNET_YES, -1);
997 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
998 "Deleting value %llu from gn080 table failed\n",
1005 return_ok (void *cls, unsigned int num_values, MYSQL_BIND * values)
1012 iterator_helper_prepare (void *cls,
1013 struct NextRequestClosure *nrc)
1015 struct Plugin *plugin;
1020 plugin = nrc->plugin;
1021 ret = GNUNET_SYSERR;
1022 switch (nrc->iter_select)
1026 ret = prepared_statement_run_select (plugin,
1027 plugin->iter[nrc->iter_select],
1035 MYSQL_TYPE_LONGLONG,
1041 MYSQL_TYPE_LONGLONG,
1046 ret = prepared_statement_run_select (plugin,
1047 plugin->iter[nrc->iter_select],
1052 MYSQL_TYPE_LONGLONG,
1055 MYSQL_TYPE_LONGLONG,
1058 MYSQL_TYPE_LONGLONG,
1061 MYSQL_TYPE_LONGLONG,
1066 ret = prepared_statement_run_select (plugin,
1067 plugin->iter[nrc->iter_select],
1072 MYSQL_TYPE_LONGLONG,
1075 MYSQL_TYPE_LONGLONG,
1078 MYSQL_TYPE_LONGLONG,
1081 MYSQL_TYPE_LONGLONG,
1084 MYSQL_TYPE_LONGLONG,
1087 MYSQL_TYPE_LONGLONG,
1099 * Continuation of "mysql_next_request".
1101 * @param next_cls the next context
1102 * @param tc the task context (unused)
1105 mysql_next_request_cont (void *next_cls,
1106 const struct GNUNET_SCHEDULER_TaskContext *tc)
1108 struct NextRequestClosure *nrc = next_cls;
1109 struct Plugin *plugin;
1113 unsigned int priority;
1114 unsigned int anonymity;
1115 unsigned long long exp;
1116 unsigned long long vkey;
1117 unsigned long hashSize;
1118 GNUNET_HashCode key;
1119 struct GNUNET_TIME_Absolute expiration;
1120 unsigned long length;
1121 MYSQL_BIND *rbind; /* size 7 */
1122 MYSQL_BIND dbind[1];
1123 char datum[GNUNET_SERVER_MAX_MESSAGE_SIZE];
1125 plugin = nrc->plugin;
1126 plugin->next_task = GNUNET_SCHEDULER_NO_TASK;
1127 plugin->next_task_nc = NULL;
1130 GNUNET_assert (nrc->plugin->next_task == GNUNET_SCHEDULER_NO_TASK);
1131 nrc->now = GNUNET_TIME_absolute_get ();
1132 hashSize = sizeof (GNUNET_HashCode);
1133 memset (nrc->rbind, 0, sizeof (nrc->rbind));
1135 rbind[0].buffer_type = MYSQL_TYPE_LONG;
1136 rbind[0].buffer = &size;
1137 rbind[0].is_unsigned = 1;
1138 rbind[1].buffer_type = MYSQL_TYPE_LONG;
1139 rbind[1].buffer = &type;
1140 rbind[1].is_unsigned = 1;
1141 rbind[2].buffer_type = MYSQL_TYPE_LONG;
1142 rbind[2].buffer = &priority;
1143 rbind[2].is_unsigned = 1;
1144 rbind[3].buffer_type = MYSQL_TYPE_LONG;
1145 rbind[3].buffer = &anonymity;
1146 rbind[3].is_unsigned = 1;
1147 rbind[4].buffer_type = MYSQL_TYPE_LONGLONG;
1148 rbind[4].buffer = &exp;
1149 rbind[4].is_unsigned = 1;
1150 rbind[5].buffer_type = MYSQL_TYPE_BLOB;
1151 rbind[5].buffer = &key;
1152 rbind[5].buffer_length = hashSize;
1153 rbind[5].length = &hashSize;
1154 rbind[6].buffer_type = MYSQL_TYPE_LONGLONG;
1155 rbind[6].buffer = &vkey;
1156 rbind[6].is_unsigned = GNUNET_YES;
1158 if ( (GNUNET_YES == nrc->end_it) ||
1159 (GNUNET_OK != nrc->prep (nrc->prep_cls,
1162 GNUNET_assert (nrc->plugin->next_task == GNUNET_SCHEDULER_NO_TASK);
1163 if (size >= GNUNET_SERVER_MAX_MESSAGE_SIZE)
1165 GNUNET_break (0); /* far too big */
1168 nrc->last_vkey = vkey;
1169 nrc->last_prio = priority;
1170 nrc->last_expire = exp;
1171 if ((rbind[0].buffer_type != MYSQL_TYPE_LONG) ||
1172 (!rbind[0].is_unsigned) ||
1173 (rbind[1].buffer_type != MYSQL_TYPE_LONG) ||
1174 (!rbind[1].is_unsigned) ||
1175 (rbind[2].buffer_type != MYSQL_TYPE_LONG) ||
1176 (!rbind[2].is_unsigned) ||
1177 (rbind[3].buffer_type != MYSQL_TYPE_LONG) ||
1178 (!rbind[3].is_unsigned) ||
1179 (rbind[4].buffer_type != MYSQL_TYPE_LONGLONG) ||
1180 (!rbind[4].is_unsigned) ||
1181 (rbind[5].buffer_type != MYSQL_TYPE_BLOB) ||
1182 (rbind[5].buffer_length != sizeof (GNUNET_HashCode)) ||
1183 (*rbind[5].length != sizeof (GNUNET_HashCode)) ||
1184 (rbind[6].buffer_type != MYSQL_TYPE_LONGLONG) ||
1185 (!rbind[6].is_unsigned))
1191 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1192 "Found value %llu with size %u, prio %u, anon %u, expire %llu selecting from gn080 table\n",
1199 /* now do query on gn072 */
1201 memset (dbind, 0, sizeof (dbind));
1202 dbind[0].buffer_type = MYSQL_TYPE_BLOB;
1203 dbind[0].buffer_length = size;
1204 dbind[0].length = &length;
1205 dbind[0].buffer = datum;
1206 ret = prepared_statement_run_select (plugin,
1207 plugin->select_value,
1212 MYSQL_TYPE_LONGLONG,
1213 &vkey, GNUNET_YES, -1);
1214 GNUNET_break (ret <= 1); /* should only have one rbind! */
1217 if ((ret != GNUNET_OK) ||
1218 (dbind[0].buffer_length != size) || (length != size))
1220 GNUNET_break (ret != 0); /* should have one rbind! */
1221 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1222 "Failed to obtain %llu from gn072\n",
1224 GNUNET_break (length == size); /* length should match! */
1225 GNUNET_break (dbind[0].buffer_length == size); /* length should be internally consistent! */
1228 do_delete_value (plugin, vkey);
1229 do_delete_entry_by_vkey (plugin, vkey);
1230 plugin->content_size -= size;
1235 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1236 "Calling iterator with value %llu of size %u with type %u, priority %u, anonymity %u and expiration %llu\n",
1244 GNUNET_assert (nrc->plugin->next_task == GNUNET_SCHEDULER_NO_TASK);
1245 expiration.value = exp;
1246 ret = nrc->dviter (nrc->dviter_cls,
1256 if (ret == GNUNET_SYSERR)
1258 nrc->end_it = GNUNET_YES;
1261 if (ret == GNUNET_NO)
1263 do_delete_value (plugin, vkey);
1264 do_delete_entry_by_vkey (plugin, vkey);
1265 plugin->content_size -= size;
1269 /* call dviter with "end of set" */
1270 GNUNET_assert (nrc->plugin->next_task == GNUNET_SCHEDULER_NO_TASK);
1271 nrc->dviter (nrc->dviter_cls,
1272 NULL, NULL, 0, NULL, 0, 0, 0,
1273 GNUNET_TIME_UNIT_ZERO_ABS, 0);
1274 GNUNET_assert (nrc->plugin->next_task == GNUNET_SCHEDULER_NO_TASK);
1275 nrc->prep (nrc->prep_cls, NULL);
1276 GNUNET_assert (nrc->plugin->next_task == GNUNET_SCHEDULER_NO_TASK);
1282 * Function invoked on behalf of a "PluginIterator"
1283 * asking the database plugin to call the iterator
1284 * with the next item.
1286 * @param next_cls whatever argument was given
1287 * to the PluginIterator as "next_cls".
1288 * @param end_it set to GNUNET_YES if we
1289 * should terminate the iteration early
1290 * (iterator should be still called once more
1291 * to signal the end of the iteration).
1294 mysql_plugin_next_request (void *next_cls,
1297 struct NextRequestClosure *nrc = next_cls;
1299 if (GNUNET_YES == end_it)
1300 nrc->end_it = GNUNET_YES;
1301 nrc->plugin->next_task_nc = nrc;
1302 nrc->plugin->next_task = GNUNET_SCHEDULER_add_now (nrc->plugin->env->sched,
1303 &mysql_next_request_cont,
1309 * Iterate over the items in the datastore
1310 * using the given query to select and order
1313 * @param type entries of which type should be considered?
1314 * Use 0 for any type.
1315 * @param iter never NULL
1316 * @param is_asc are we using ascending order?
1319 iterateHelper (struct Plugin *plugin,
1322 unsigned int iter_select,
1323 PluginIterator dviter,
1326 struct NextRequestClosure *nrc;
1328 nrc = GNUNET_malloc (sizeof (struct NextRequestClosure));
1329 nrc->plugin = plugin;
1331 nrc->iter_select = iter_select;
1332 nrc->dviter = dviter;
1333 nrc->dviter_cls = dviter_cls;
1334 nrc->prep = &iterator_helper_prepare;
1339 nrc->last_expire = 0;
1343 nrc->last_prio = 0x7FFFFFFFL;
1344 nrc->last_vkey = 0x7FFFFFFFFFFFFFFFLL; /* MySQL only supports 63 bits */
1345 nrc->last_expire = 0x7FFFFFFFFFFFFFFFLL; /* MySQL only supports 63 bits */
1347 mysql_plugin_next_request (nrc, GNUNET_NO);
1352 * Get an estimate of how much space the database is
1355 * @param cls our "struct Plugin*"
1356 * @return number of bytes used on disk
1358 static unsigned long long
1359 mysql_plugin_get_size (void *cls)
1361 struct Plugin *plugin = cls;
1362 return plugin->content_size;
1367 * Store an item in the datastore.
1369 * @param cls closure
1370 * @param key key for the item
1371 * @param size number of bytes in data
1372 * @param data content stored
1373 * @param type type of the content
1374 * @param priority priority of the content
1375 * @param anonymity anonymity-level for the content
1376 * @param expiration expiration time for the content
1377 * @param msg set to error message
1378 * @return GNUNET_OK on success
1381 mysql_plugin_put (void *cls,
1382 const GNUNET_HashCode * key,
1385 enum GNUNET_BLOCK_Type type,
1388 struct GNUNET_TIME_Absolute expiration,
1391 struct Plugin *plugin = cls;
1392 unsigned int itype = type;
1393 unsigned int isize = size;
1394 unsigned int ipriority = priority;
1395 unsigned int ianonymity = anonymity;
1396 unsigned long long lexpiration = expiration.value;
1397 unsigned long hashSize;
1398 unsigned long hashSize2;
1399 unsigned long long vkey;
1400 GNUNET_HashCode vhash;
1402 if (size > MAX_DATUM_SIZE)
1405 return GNUNET_SYSERR;
1407 hashSize = sizeof (GNUNET_HashCode);
1408 hashSize2 = sizeof (GNUNET_HashCode);
1409 GNUNET_CRYPTO_hash (data, size, &vhash);
1410 if (GNUNET_OK != do_insert_value (plugin,
1412 return GNUNET_SYSERR;
1414 prepared_statement_run (plugin,
1415 plugin->insert_entry,
1429 MYSQL_TYPE_LONGLONG,
1440 MYSQL_TYPE_LONGLONG,
1441 &vkey, GNUNET_YES, -1))
1443 do_delete_value (plugin, vkey);
1444 return GNUNET_SYSERR;
1447 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1448 "Inserted value number %llu with size %u into gn080 table\n",
1452 plugin->content_size += size;
1458 * Select a subset of the items in the datastore and call
1459 * the given iterator for each of them.
1461 * @param cls our "struct Plugin*"
1462 * @param type entries of which type should be considered?
1463 * Use 0 for any type.
1464 * @param iter function to call on each matching value;
1465 * will be called once with a NULL value at the end
1466 * @param iter_cls closure for iter
1469 mysql_plugin_iter_low_priority (void *cls,
1470 enum GNUNET_BLOCK_Type type,
1471 PluginIterator iter,
1474 struct Plugin *plugin = cls;
1475 iterateHelper (plugin, type, GNUNET_YES,
1482 GNUNET_HashCode key;
1483 GNUNET_HashCode vhash;
1486 unsigned int anonymity;
1487 unsigned int limit_off;
1488 unsigned long long expiration;
1489 unsigned long long vkey;
1490 unsigned long long total;
1494 unsigned long size; /* OBSOLETE! */
1495 unsigned long hashSize;
1500 get_statement_prepare (void *cls,
1501 struct NextRequestClosure *nrc)
1503 struct GetContext *gc = cls;
1504 struct Plugin *plugin;
1512 if (gc->count == gc->total)
1514 plugin = nrc->plugin;
1515 gc->hashSize = sizeof (GNUNET_HashCode);
1517 gc->limit_off = gc->off;
1526 prepared_statement_run_select
1528 plugin->select_entry_by_hash_vhash_and_type, 7, nrc->rbind, &return_ok,
1529 NULL, MYSQL_TYPE_BLOB, &gc->key, gc->hashSize, &gc->hashSize,
1530 MYSQL_TYPE_BLOB, &gc->vhash, gc->hashSize, &gc->hashSize,
1531 MYSQL_TYPE_LONGLONG, &nrc->last_vkey, GNUNET_YES, MYSQL_TYPE_LONG,
1532 &nrc->type, GNUNET_YES, MYSQL_TYPE_LONG, &gc->limit_off, GNUNET_YES,
1538 prepared_statement_run_select
1540 plugin->select_entry_by_hash_and_type, 7, nrc->rbind, &return_ok, NULL,
1541 MYSQL_TYPE_BLOB, &gc->key, gc->hashSize, &gc->hashSize,
1542 MYSQL_TYPE_LONGLONG, &nrc->last_vkey, GNUNET_YES, MYSQL_TYPE_LONG,
1543 &nrc->type, GNUNET_YES, MYSQL_TYPE_LONG, &gc->limit_off, GNUNET_YES,
1552 prepared_statement_run_select
1554 plugin->select_entry_by_hash_and_vhash, 7, nrc->rbind, &return_ok, NULL,
1555 MYSQL_TYPE_BLOB, &gc->key, gc->hashSize, &gc->hashSize, MYSQL_TYPE_BLOB,
1556 &gc->vhash, gc->hashSize, &gc->hashSize, MYSQL_TYPE_LONGLONG,
1557 &nrc->last_vkey, GNUNET_YES, MYSQL_TYPE_LONG, &gc->limit_off,
1563 prepared_statement_run_select
1565 plugin->select_entry_by_hash, 7, nrc->rbind, &return_ok, NULL,
1566 MYSQL_TYPE_BLOB, &gc->key, gc->hashSize, &gc->hashSize,
1567 MYSQL_TYPE_LONGLONG, &nrc->last_vkey, GNUNET_YES, MYSQL_TYPE_LONG,
1568 &gc->limit_off, GNUNET_YES, -1);
1571 if (gc->count + gc->off == gc->total)
1572 nrc->last_vkey = 0; /* back to start */
1578 * Iterate over the results for a particular key
1581 * @param cls closure
1582 * @param key maybe NULL (to match all entries)
1583 * @param vhash hash of the value, maybe NULL (to
1584 * match all values that have the right key).
1585 * Note that for DBlocks there is no difference
1586 * betwen key and vhash, but for other blocks
1588 * @param type entries of which type are relevant?
1589 * Use 0 for any type.
1590 * @param iter function to call on each matching value;
1591 * will be called once with a NULL value at the end
1592 * @param iter_cls closure for iter
1595 mysql_plugin_get (void *cls,
1596 const GNUNET_HashCode * key,
1597 const GNUNET_HashCode * vhash,
1598 enum GNUNET_BLOCK_Type type,
1599 PluginIterator iter, void *iter_cls)
1601 struct Plugin *plugin = cls;
1602 unsigned int itype = type;
1604 MYSQL_BIND cbind[1];
1605 struct GetContext *gc;
1606 struct NextRequestClosure *nrc;
1607 unsigned long long total;
1608 unsigned long hashSize;
1614 mysql_plugin_iter_low_priority (plugin,
1619 hashSize = sizeof (GNUNET_HashCode);
1620 memset (cbind, 0, sizeof (cbind));
1622 cbind[0].buffer_type = MYSQL_TYPE_LONGLONG;
1623 cbind[0].buffer = &total;
1624 cbind[0].is_unsigned = GNUNET_YES;
1630 prepared_statement_run_select
1632 plugin->count_entry_by_hash_vhash_and_type, 1, cbind, &return_ok, NULL,
1633 MYSQL_TYPE_BLOB, key, hashSize, &hashSize, MYSQL_TYPE_BLOB,
1634 vhash, hashSize, &hashSize, MYSQL_TYPE_LONG, &itype, GNUNET_YES,
1640 prepared_statement_run_select
1642 plugin->count_entry_by_hash_and_type, 1, cbind, &return_ok, NULL,
1643 MYSQL_TYPE_BLOB, key, hashSize, &hashSize, MYSQL_TYPE_LONG,
1644 &itype, GNUNET_YES, -1);
1653 prepared_statement_run_select
1655 plugin->count_entry_by_hash_and_vhash, 1, cbind, &return_ok, NULL,
1656 MYSQL_TYPE_BLOB, key, hashSize, &hashSize, MYSQL_TYPE_BLOB,
1657 vhash, hashSize, &hashSize, -1);
1663 prepared_statement_run_select (plugin,
1664 plugin->count_entry_by_hash,
1665 1, cbind, &return_ok,
1666 NULL, MYSQL_TYPE_BLOB,
1671 if ((ret != GNUNET_OK) || (-1 == total) || (0 == total))
1674 NULL, NULL, 0, NULL, 0, 0, 0,
1675 GNUNET_TIME_UNIT_ZERO_ABS, 0);
1678 gc = GNUNET_malloc (sizeof (struct GetContext));
1682 gc->have_vhash = GNUNET_YES;
1686 gc->off = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, total);
1689 nrc = GNUNET_malloc (sizeof (struct NextRequestClosure));
1690 nrc->plugin = plugin;
1692 nrc->iter_select = -1;
1694 nrc->dviter_cls = iter_cls;
1695 nrc->prep = &get_statement_prepare;
1697 nrc->last_vkey = 0; // FIXME: used to be 'vkey', why? where did vkey come from?
1698 mysql_plugin_next_request (nrc, GNUNET_NO);
1703 * Update the priority for a particular key in the datastore. If
1704 * the expiration time in value is different than the time found in
1705 * the datastore, the higher value should be kept. For the
1706 * anonymity level, the lower value is to be used. The specified
1707 * priority should be added to the existing priority, ignoring the
1708 * priority in value.
1710 * Note that it is possible for multiple values to match this put.
1711 * In that case, all of the respective values are updated.
1713 * @param cls our "struct Plugin*"
1714 * @param uid unique identifier of the datum
1715 * @param delta by how much should the priority
1716 * change? If priority + delta < 0 the
1717 * priority should be set to 0 (never go
1719 * @param expire new expiration time should be the
1720 * MAX of any existing expiration time and
1722 * @param msg set to error message
1723 * @return GNUNET_OK on success
1726 mysql_plugin_update (void *cls,
1729 struct GNUNET_TIME_Absolute expire,
1732 struct Plugin *plugin = cls;
1733 unsigned long long vkey = uid;
1734 unsigned long long lexpire = expire.value;
1738 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1739 "Updating value %llu adding %d to priority and maxing exp at %llu\n",
1744 ret = prepared_statement_run (plugin,
1745 plugin->update_entry,
1750 MYSQL_TYPE_LONGLONG,
1753 MYSQL_TYPE_LONGLONG,
1756 MYSQL_TYPE_LONGLONG,
1759 if (ret != GNUNET_OK)
1761 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1762 "Failed to update value %llu\n",
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_zero_anonymity (void *cls,
1782 enum GNUNET_BLOCK_Type type,
1783 PluginIterator iter,
1786 struct Plugin *plugin = cls;
1787 iterateHelper (plugin, type, GNUNET_NO, 1, 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_ascending_expiration (void *cls,
1804 enum GNUNET_BLOCK_Type type,
1805 PluginIterator iter,
1808 struct Plugin *plugin = cls;
1809 iterateHelper (plugin, type, GNUNET_YES, 2, 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_migration_order (void *cls,
1826 enum GNUNET_BLOCK_Type type,
1827 PluginIterator iter,
1830 struct Plugin *plugin = cls;
1831 iterateHelper (plugin, 0, GNUNET_NO, 3, iter, iter_cls);
1836 * Select a subset of the items in the datastore and call
1837 * the given iterator for each of them.
1839 * @param cls our "struct Plugin*"
1840 * @param type entries of which type should be considered?
1841 * Use 0 for any type.
1842 * @param iter function to call on each matching value;
1843 * will be called once with a NULL value at the end
1844 * @param iter_cls closure for iter
1847 mysql_plugin_iter_all_now (void *cls,
1848 enum GNUNET_BLOCK_Type type,
1849 PluginIterator iter,
1852 struct Plugin *plugin = cls;
1853 iterateHelper (plugin, 0, GNUNET_YES, 0, iter, iter_cls);
1861 mysql_plugin_drop (void *cls)
1863 struct Plugin *plugin = cls;
1865 if ((GNUNET_OK != run_statement (plugin,
1866 "DROP TABLE gn080")) ||
1867 (GNUNET_OK != run_statement (plugin,
1868 "DROP TABLE gn072")))
1870 plugin->content_size = 0;
1875 * Entry point for the plugin.
1877 * @param cls the "struct GNUNET_DATASTORE_PluginEnvironment*"
1878 * @return our "struct Plugin*"
1881 libgnunet_plugin_datastore_mysql_init (void *cls)
1883 struct GNUNET_DATASTORE_PluginEnvironment *env = cls;
1884 struct GNUNET_DATASTORE_PluginFunctions *api;
1885 struct Plugin *plugin;
1887 plugin = GNUNET_malloc (sizeof (struct Plugin));
1889 plugin->cnffile = get_my_cnf_path (env->cfg);
1890 if (GNUNET_OK != iopen (plugin))
1893 GNUNET_free_non_null (plugin->cnffile);
1894 GNUNET_free (plugin);
1897 #define MRUNS(a) (GNUNET_OK != run_statement (plugin, a) )
1898 #define PINIT(a,b) (NULL == (a = prepared_statement_create(plugin, b)))
1899 if (MRUNS ("CREATE TABLE IF NOT EXISTS gn080 ("
1900 " size INT(11) UNSIGNED NOT NULL DEFAULT 0,"
1901 " type INT(11) UNSIGNED NOT NULL DEFAULT 0,"
1902 " prio INT(11) UNSIGNED NOT NULL DEFAULT 0,"
1903 " anonLevel INT(11) UNSIGNED NOT NULL DEFAULT 0,"
1904 " expire BIGINT UNSIGNED NOT NULL DEFAULT 0,"
1905 " hash BINARY(64) NOT NULL DEFAULT '',"
1906 " vhash BINARY(64) NOT NULL DEFAULT '',"
1907 " vkey BIGINT UNSIGNED NOT NULL DEFAULT 0,"
1908 " INDEX hash (hash(64)),"
1909 " INDEX hash_vhash_vkey (hash(64),vhash(64),vkey),"
1910 " INDEX hash_vkey (hash(64),vkey),"
1911 " INDEX vkey (vkey),"
1912 " INDEX prio (prio,vkey),"
1913 " INDEX expire (expire,vkey,type),"
1914 " INDEX anonLevel (anonLevel,prio,vkey,type)"
1915 ") ENGINE=InnoDB") ||
1916 MRUNS ("CREATE TABLE IF NOT EXISTS gn072 ("
1917 " vkey BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,"
1918 " value BLOB NOT NULL DEFAULT '') ENGINE=MyISAM") ||
1919 MRUNS ("SET AUTOCOMMIT = 1") ||
1920 PINIT (plugin->select_value, SELECT_VALUE) ||
1921 PINIT (plugin->delete_value, DELETE_VALUE) ||
1922 PINIT (plugin->insert_value, INSERT_VALUE) ||
1923 PINIT (plugin->insert_entry, INSERT_ENTRY) ||
1924 PINIT (plugin->delete_entry_by_vkey, DELETE_ENTRY_BY_VKEY) ||
1925 PINIT (plugin->select_entry_by_hash, SELECT_ENTRY_BY_HASH) ||
1926 PINIT (plugin->select_entry_by_hash_and_vhash, SELECT_ENTRY_BY_HASH_AND_VHASH)
1927 || PINIT (plugin->select_entry_by_hash_and_type, SELECT_ENTRY_BY_HASH_AND_TYPE)
1928 || PINIT (plugin->select_entry_by_hash_vhash_and_type,
1929 SELECT_ENTRY_BY_HASH_VHASH_AND_TYPE)
1930 || PINIT (plugin->count_entry_by_hash, COUNT_ENTRY_BY_HASH)
1931 || PINIT (plugin->count_entry_by_hash_and_vhash, COUNT_ENTRY_BY_HASH_AND_VHASH)
1932 || PINIT (plugin->count_entry_by_hash_and_type, COUNT_ENTRY_BY_HASH_AND_TYPE)
1933 || PINIT (plugin->count_entry_by_hash_vhash_and_type,
1934 COUNT_ENTRY_BY_HASH_VHASH_AND_TYPE)
1935 || PINIT (plugin->update_entry, UPDATE_ENTRY)
1936 || PINIT (plugin->iter[0], SELECT_IT_LOW_PRIORITY)
1937 || PINIT (plugin->iter[1], SELECT_IT_NON_ANONYMOUS)
1938 || PINIT (plugin->iter[2], SELECT_IT_EXPIRATION_TIME)
1939 || PINIT (plugin->iter[3], SELECT_IT_MIGRATION_ORDER))
1942 GNUNET_free_non_null (plugin->cnffile);
1943 GNUNET_free (plugin);
1949 api = GNUNET_malloc (sizeof (struct GNUNET_DATASTORE_PluginFunctions));
1951 api->get_size = &mysql_plugin_get_size;
1952 api->put = &mysql_plugin_put;
1953 api->next_request = &mysql_plugin_next_request;
1954 api->get = &mysql_plugin_get;
1955 api->update = &mysql_plugin_update;
1956 api->iter_low_priority = &mysql_plugin_iter_low_priority;
1957 api->iter_zero_anonymity = &mysql_plugin_iter_zero_anonymity;
1958 api->iter_ascending_expiration = &mysql_plugin_iter_ascending_expiration;
1959 api->iter_migration_order = &mysql_plugin_iter_migration_order;
1960 api->iter_all_now = &mysql_plugin_iter_all_now;
1961 api->drop = &mysql_plugin_drop;
1962 GNUNET_log_from (GNUNET_ERROR_TYPE_INFO,
1963 "mysql", _("Mysql database running\n"));
1969 * Exit point from the plugin.
1970 * @param cls our "struct Plugin*"
1971 * @return always NULL
1974 libgnunet_plugin_datastore_mysql_done (void *cls)
1976 struct GNUNET_DATASTORE_PluginFunctions *api = cls;
1977 struct Plugin *plugin = api->cls;
1980 if (plugin->next_task != GNUNET_SCHEDULER_NO_TASK)
1982 GNUNET_SCHEDULER_cancel (plugin->env->sched,
1984 plugin->next_task = GNUNET_SCHEDULER_NO_TASK;
1985 plugin->next_task_nc->prep (plugin->next_task_nc->prep_cls, NULL);
1986 GNUNET_free (plugin->next_task_nc);
1987 plugin->next_task_nc = NULL;
1989 GNUNET_free_non_null (plugin->cnffile);
1990 GNUNET_free (plugin);
1992 mysql_library_end ();
1996 /* end of plugin_datastore_mysql.c */