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,
59 * and do the following. [You should replace $USER with the username
60 * that will be running the gnunetd process].
62 CREATE DATABASE gnunet;
63 GRANT select,insert,update,delete,create,alter,drop,create temporary tables
64 ON gnunet.* TO $USER@localhost;
65 SET PASSWORD FOR $USER@localhost=PASSWORD('$the_password_you_like');
68 * 3) In the $HOME directory of $USER, create a ".my.cnf" file
69 * with the following lines
73 password=$the_password_you_like
76 * Thats it. Note that .my.cnf file is a security risk unless its on
77 * a safe partition etc. The $HOME/.my.cnf can of course be a symbolic
78 * link. Even greater security risk can be achieved by setting no
79 * password for $USER. Luckily $USER has only priviledges to mess
80 * up GNUnet's tables, nothing else (unless you give him more,
83 * 4) Still, perhaps you should briefly try if the DB connection
84 * works. First, login as $USER. Then use,
87 $ mysql -u $USER -p $the_password_you_like
91 * If you get the message "Database changed" it probably works.
93 * [If you get "ERROR 2002: Can't connect to local MySQL server
94 * through socket '/tmp/mysql.sock' (2)" it may be resolvable by
95 * "ln -s /var/run/mysqld/mysqld.sock /tmp/mysql.sock"
96 * so there may be some additional trouble depending on your mysql setup.]
100 * - Its probably healthy to check your tables for inconsistencies
101 * every now and then.
102 * - If you get odd SEGVs on gnunetd startup, it might be that the mysql
103 * databases have been corrupted.
104 * - The tables can be verified/fixed in two ways;
105 * 1) by running mysqlcheck -A, or
106 * 2) by executing (inside of mysql using the GNUnet database):
108 mysql> REPAIR TABLE gn090;
109 mysql> REPAIR TABLE gn072;
114 * If you have problems related to the mysql module, your best
115 * friend is probably the mysql manual. The first thing to check
116 * is that mysql is basically operational, that you can connect
117 * to it, create tables, issue queries etc.
120 * - use FOREIGN KEY for 'uid/vkey'
121 * - consistent naming of uid/vkey
124 #include "platform.h"
125 #include "gnunet_datastore_plugin.h"
126 #include "gnunet_util_lib.h"
127 #include <mysql/mysql.h>
129 #define DEBUG_MYSQL GNUNET_NO
131 #define MAX_DATUM_SIZE 65536
134 * Maximum number of supported parameters for a prepared
135 * statement. Increase if needed.
140 * Die with an error message that indicates
141 * a failure of the command 'cmd' with the message given
142 * by strerror(errno).
144 #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);
147 * Log an error message at log-level 'level' that indicates
148 * a failure of the command 'cmd' on file 'filename'
149 * with the message given by strerror(errno).
151 #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);
154 /* warning, slighly crazy mysql statements ahead. Essentially, MySQL does not handle
155 "OR" very well, so we need to use UNION instead. And UNION does not
156 automatically apply a LIMIT on the outermost clause, so we need to
157 repeat ourselves quite a bit. All hail the performance gods (and thanks
158 to #mysql on freenode) */
159 #define SELECT_IT_LOW_PRIORITY "(SELECT type,prio,anonLevel,expire,hash,vkey FROM gn090 FORCE INDEX(prio) WHERE (prio = ? AND vkey > ?) "\
160 "ORDER BY prio ASC,vkey ASC LIMIT 1) " \
162 "(SELECT type,prio,anonLevel,expire,hash,vkey FROM gn090 FORCE INDEX(prio) WHERE (prio > ? AND vkey != ?)"\
163 "ORDER BY prio ASC,vkey ASC LIMIT 1)"\
164 "ORDER BY prio ASC,vkey ASC LIMIT 1"
166 #define SELECT_IT_NON_ANONYMOUS "(SELECT type,prio,anonLevel,expire,hash,vkey FROM gn090 FORCE INDEX(prio) WHERE (prio = ? AND vkey < ?)"\
167 " AND anonLevel=0 ORDER BY prio DESC,vkey DESC LIMIT 1) "\
169 "(SELECT type,prio,anonLevel,expire,hash,vkey FROM gn090 FORCE INDEX(prio) WHERE (prio < ? AND vkey != ?)"\
170 " AND anonLevel=0 ORDER BY prio DESC,vkey DESC LIMIT 1) "\
171 "ORDER BY prio DESC,vkey DESC LIMIT 1"
173 #define SELECT_IT_EXPIRATION_TIME "(SELECT type,prio,anonLevel,expire,hash,vkey FROM gn090 FORCE INDEX(expire) WHERE (expire = ? AND vkey > ?) "\
174 "ORDER BY expire ASC,vkey ASC LIMIT 1) "\
176 "(SELECT type,prio,anonLevel,expire,hash,vkey FROM gn090 FORCE INDEX(expire) WHERE (expire > ? AND vkey != ?) "\
177 "ORDER BY expire ASC,vkey ASC LIMIT 1)"\
178 "ORDER BY expire ASC,vkey ASC LIMIT 1"
181 #define SELECT_IT_MIGRATION_ORDER "(SELECT type,prio,anonLevel,expire,hash,vkey FROM gn090 FORCE INDEX(expire) WHERE (expire = ? AND vkey < ?)"\
182 " AND expire > ? AND type!=3"\
183 " ORDER BY expire DESC,vkey DESC LIMIT 1) "\
185 "(SELECT type,prio,anonLevel,expire,hash,vkey FROM gn090 FORCE INDEX(expire) WHERE (expire < ? AND vkey != ?)"\
186 " AND expire > ? AND type!=3"\
187 " ORDER BY expire DESC,vkey DESC LIMIT 1)"\
188 "ORDER BY expire DESC,vkey DESC LIMIT 1"
192 struct GNUNET_MysqlStatementHandle
194 struct GNUNET_MysqlStatementHandle *next;
196 struct GNUNET_MysqlStatementHandle *prev;
200 MYSQL_STMT *statement;
207 * Context for the universal iterator.
209 struct NextRequestClosure;
212 * Type of a function that will prepare
213 * the next iteration.
216 * @param nc the next context; NULL for the last
217 * call which gives the callback a chance to
218 * clean up the closure
219 * @return GNUNET_OK on success, GNUNET_NO if there are
220 * no more values, GNUNET_SYSERR on error
222 typedef int (*PrepareFunction)(void *cls,
223 struct NextRequestClosure *nc);
226 struct NextRequestClosure
228 struct Plugin *plugin;
230 struct GNUNET_TIME_Absolute now;
233 * Function to call to prepare the next
236 PrepareFunction prep;
247 unsigned int iter_select;
249 PluginIterator dviter;
253 unsigned int last_prio;
255 unsigned long long last_expire;
257 unsigned long long last_vkey;
264 * Context for all functions in this plugin.
269 * Our execution environment.
271 struct GNUNET_DATASTORE_PluginEnvironment *env;
275 struct GNUNET_MysqlStatementHandle *shead;
277 struct GNUNET_MysqlStatementHandle *stail;
280 * Filename of "my.cnf" (msyql configuration).
285 * Closure of the 'next_task' (must be freed if 'next_task' is cancelled).
287 struct NextRequestClosure *next_task_nc;
290 * Pending task with scheduler for running the next request.
292 GNUNET_SCHEDULER_TaskIdentifier next_task;
295 * Statements dealing with gn072 table
297 #define SELECT_VALUE "SELECT value FROM gn072 WHERE vkey=?"
298 struct GNUNET_MysqlStatementHandle *select_value;
300 #define DELETE_VALUE "DELETE FROM gn072 WHERE vkey=?"
301 struct GNUNET_MysqlStatementHandle *delete_value;
303 #define INSERT_VALUE "INSERT INTO gn072 (value) VALUES (?)"
304 struct GNUNET_MysqlStatementHandle *insert_value;
307 * Statements dealing with gn090 table
309 #define INSERT_ENTRY "INSERT INTO gn090 (type,prio,anonLevel,expire,hash,vhash,vkey) VALUES (?,?,?,?,?,?,?)"
310 struct GNUNET_MysqlStatementHandle *insert_entry;
312 #define DELETE_ENTRY_BY_VKEY "DELETE FROM gn090 WHERE vkey=?"
313 struct GNUNET_MysqlStatementHandle *delete_entry_by_vkey;
315 #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 ?"
316 struct GNUNET_MysqlStatementHandle *select_entry_by_hash;
318 #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 ?"
319 struct GNUNET_MysqlStatementHandle *select_entry_by_hash_and_vhash;
321 #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 ?"
322 struct GNUNET_MysqlStatementHandle *select_entry_by_hash_and_type;
324 #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 ?"
325 struct GNUNET_MysqlStatementHandle *select_entry_by_hash_vhash_and_type;
327 #define COUNT_ENTRY_BY_HASH "SELECT count(*) FROM gn090 FORCE INDEX (hash) WHERE hash=?"
328 struct GNUNET_MysqlStatementHandle *count_entry_by_hash;
330 #define COUNT_ENTRY_BY_HASH_AND_VHASH "SELECT count(*) FROM gn090 FORCE INDEX (hash_vhash_vkey) WHERE hash=? AND vhash=?"
331 struct GNUNET_MysqlStatementHandle *count_entry_by_hash_and_vhash;
333 #define COUNT_ENTRY_BY_HASH_AND_TYPE "SELECT count(*) FROM gn090 FORCE INDEX (hash) WHERE hash=? AND type=?"
334 struct GNUNET_MysqlStatementHandle *count_entry_by_hash_and_type;
336 #define COUNT_ENTRY_BY_HASH_VHASH_AND_TYPE "SELECT count(*) FROM gn090 FORCE INDEX (hash_vhash) WHERE hash=? AND vhash=? AND type=?"
337 struct GNUNET_MysqlStatementHandle *count_entry_by_hash_vhash_and_type;
339 #define UPDATE_ENTRY "UPDATE gn090 SET prio=prio+?,expire=IF(expire>=?,expire,?) WHERE vkey=?"
340 struct GNUNET_MysqlStatementHandle *update_entry;
342 #define SELECT_SIZE "SELECT SUM(BIT_LENGTH(value) DIV 8) FROM gn072"
343 struct GNUNET_MysqlStatementHandle *get_size;
345 struct GNUNET_MysqlStatementHandle *iter[4];
351 * Obtain the location of ".my.cnf".
353 * @param cfg our configuration
354 * @return NULL on error
357 get_my_cnf_path (const struct GNUNET_CONFIGURATION_Handle *cfg)
368 pw = getpwuid (getuid ());
371 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
376 GNUNET_CONFIGURATION_have_value (cfg,
377 "datastore-mysql", "CONFIG"))
379 GNUNET_assert (GNUNET_OK ==
380 GNUNET_CONFIGURATION_get_value_filename (cfg,
381 "datastore-mysql", "CONFIG", &cnffile));
382 configured = GNUNET_YES;
386 home_dir = GNUNET_strdup (pw->pw_dir);
388 home_dir = (char *) GNUNET_malloc (_MAX_PATH + 1);
389 plibc_conv_to_win_path ("~/", home_dir);
391 GNUNET_asprintf (&cnffile, "%s/.my.cnf", home_dir);
392 GNUNET_free (home_dir);
393 configured = GNUNET_NO;
395 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
396 _("Trying to use file `%s' for MySQL configuration.\n"),
398 if ((0 != STAT (cnffile, &st)) ||
399 (0 != ACCESS (cnffile, R_OK)) || (!S_ISREG (st.st_mode)))
401 if (configured == GNUNET_YES)
402 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
403 _("Could not access file `%s': %s\n"), cnffile,
405 GNUNET_free (cnffile);
414 * Free a prepared statement.
416 * @param plugin plugin context
417 * @param s prepared statement
420 prepared_statement_destroy (struct Plugin *plugin,
421 struct GNUNET_MysqlStatementHandle
424 GNUNET_CONTAINER_DLL_remove (plugin->shead,
428 mysql_stmt_close (s->statement);
429 GNUNET_free (s->query);
435 * Close database connection and all prepared statements (we got a DB
439 iclose (struct Plugin *plugin)
441 struct GNUNET_MysqlStatementHandle *spos;
443 spos = plugin->shead;
444 while (NULL != plugin->shead)
445 prepared_statement_destroy (plugin,
447 if (plugin->dbf != NULL)
449 mysql_close (plugin->dbf);
457 * Open the connection with the database (and initialize
458 * our default options).
460 * @return GNUNET_OK on success
463 iopen (struct Plugin *ret)
468 char *mysql_password;
469 unsigned long long mysql_port;
471 unsigned int timeout;
473 ret->dbf = mysql_init (NULL);
474 if (ret->dbf == NULL)
475 return GNUNET_SYSERR;
476 if (ret->cnffile != NULL)
477 mysql_options (ret->dbf, MYSQL_READ_DEFAULT_FILE, ret->cnffile);
478 mysql_options (ret->dbf, MYSQL_READ_DEFAULT_GROUP, "client");
480 mysql_options (ret->dbf, MYSQL_OPT_RECONNECT, &reconnect);
481 mysql_options (ret->dbf,
482 MYSQL_OPT_CONNECT_TIMEOUT, (const void *) &timeout);
483 mysql_options(ret->dbf, MYSQL_SET_CHARSET_NAME, "UTF8");
484 timeout = 60; /* in seconds */
485 mysql_options (ret->dbf, MYSQL_OPT_READ_TIMEOUT, (const void *) &timeout);
486 mysql_options (ret->dbf, MYSQL_OPT_WRITE_TIMEOUT, (const void *) &timeout);
488 if (GNUNET_YES == GNUNET_CONFIGURATION_have_value (ret->env->cfg,
489 "datastore-mysql", "DATABASE"))
490 GNUNET_assert (GNUNET_OK ==
491 GNUNET_CONFIGURATION_get_value_string (ret->env->cfg,
492 "datastore-mysql", "DATABASE",
495 mysql_dbname = GNUNET_strdup ("gnunet");
497 if (GNUNET_YES == GNUNET_CONFIGURATION_have_value (ret->env->cfg,
498 "datastore-mysql", "USER"))
500 GNUNET_assert (GNUNET_OK ==
501 GNUNET_CONFIGURATION_get_value_string (ret->env->cfg,
502 "datastore-mysql", "USER",
505 mysql_password = NULL;
506 if (GNUNET_YES == GNUNET_CONFIGURATION_have_value (ret->env->cfg,
507 "datastore-mysql", "PASSWORD"))
509 GNUNET_assert (GNUNET_OK ==
510 GNUNET_CONFIGURATION_get_value_string (ret->env->cfg,
511 "datastore-mysql", "PASSWORD",
515 if (GNUNET_YES == GNUNET_CONFIGURATION_have_value (ret->env->cfg,
516 "datastore-mysql", "HOST"))
518 GNUNET_assert (GNUNET_OK ==
519 GNUNET_CONFIGURATION_get_value_string (ret->env->cfg,
520 "datastore-mysql", "HOST",
524 if (GNUNET_YES == GNUNET_CONFIGURATION_have_value (ret->env->cfg,
525 "datastore-mysql", "PORT"))
527 GNUNET_assert (GNUNET_OK ==
528 GNUNET_CONFIGURATION_get_value_number (ret->env->cfg, "datastore-mysql",
529 "PORT", &mysql_port));
532 GNUNET_assert (mysql_dbname != NULL);
533 mysql_real_connect (ret->dbf, mysql_server, mysql_user, mysql_password,
534 mysql_dbname, (unsigned int) mysql_port, NULL,
535 CLIENT_IGNORE_SIGPIPE);
536 GNUNET_free_non_null (mysql_server);
537 GNUNET_free_non_null (mysql_user);
538 GNUNET_free_non_null (mysql_password);
539 GNUNET_free (mysql_dbname);
540 if (mysql_error (ret->dbf)[0])
542 LOG_MYSQL (GNUNET_ERROR_TYPE_ERROR,
543 "mysql_real_connect", ret);
544 return GNUNET_SYSERR;
551 * Run the given MySQL statement.
553 * @param plugin plugin context
554 * @param statement SQL statement to run
555 * @return GNUNET_OK on success, GNUNET_SYSERR on error
558 run_statement (struct Plugin *plugin,
559 const char *statement)
561 if ((NULL == plugin->dbf) && (GNUNET_OK != iopen (plugin)))
562 return GNUNET_SYSERR;
563 mysql_query (plugin->dbf, statement);
564 if (mysql_error (plugin->dbf)[0])
566 LOG_MYSQL (GNUNET_ERROR_TYPE_ERROR,
567 "mysql_query", plugin);
569 return GNUNET_SYSERR;
576 * Create a prepared statement.
578 * @param plugin plugin context
579 * @param statement SQL statement text to prepare
580 * @return NULL on error
582 static struct GNUNET_MysqlStatementHandle *
583 prepared_statement_create (struct Plugin *plugin,
584 const char *statement)
586 struct GNUNET_MysqlStatementHandle *ret;
588 ret = GNUNET_malloc (sizeof (struct GNUNET_MysqlStatementHandle));
589 ret->query = GNUNET_strdup (statement);
590 GNUNET_CONTAINER_DLL_insert (plugin->shead,
598 * Prepare a statement for running.
600 * @param plugin plugin context
601 * @param ret handle to prepared statement
602 * @return GNUNET_OK on success
605 prepare_statement (struct Plugin *plugin,
606 struct GNUNET_MysqlStatementHandle *ret)
608 if (GNUNET_YES == ret->valid)
610 if ((NULL == plugin->dbf) &&
611 (GNUNET_OK != iopen (plugin)))
612 return GNUNET_SYSERR;
613 ret->statement = mysql_stmt_init (plugin->dbf);
614 if (ret->statement == NULL)
617 return GNUNET_SYSERR;
619 if (mysql_stmt_prepare (ret->statement,
621 strlen (ret->query)))
623 LOG_MYSQL (GNUNET_ERROR_TYPE_ERROR,
624 "mysql_stmt_prepare",
626 mysql_stmt_close (ret->statement);
627 ret->statement = NULL;
629 return GNUNET_SYSERR;
631 ret->valid = GNUNET_YES;
638 * Bind the parameters for the given MySQL statement
641 * @param plugin plugin context
642 * @param s statement to bind and run
643 * @param ap arguments for the binding
644 * @return GNUNET_SYSERR on error, GNUNET_OK on success
647 init_params (struct Plugin *plugin,
648 struct GNUNET_MysqlStatementHandle *s,
651 MYSQL_BIND qbind[MAX_PARAM];
654 enum enum_field_types ft;
656 pc = mysql_stmt_param_count (s->statement);
659 /* increase internal constant! */
661 return GNUNET_SYSERR;
663 memset (qbind, 0, sizeof (qbind));
666 while ((pc > 0) && (-1 != (int) (ft = va_arg (ap, enum enum_field_types))))
668 qbind[off].buffer_type = ft;
671 case MYSQL_TYPE_FLOAT:
672 qbind[off].buffer = va_arg (ap, float *);
674 case MYSQL_TYPE_LONGLONG:
675 qbind[off].buffer = va_arg (ap, unsigned long long *);
676 qbind[off].is_unsigned = va_arg (ap, int);
678 case MYSQL_TYPE_LONG:
679 qbind[off].buffer = va_arg (ap, unsigned int *);
680 qbind[off].is_unsigned = va_arg (ap, int);
682 case MYSQL_TYPE_VAR_STRING:
683 case MYSQL_TYPE_STRING:
684 case MYSQL_TYPE_BLOB:
685 qbind[off].buffer = va_arg (ap, void *);
686 qbind[off].buffer_length = va_arg (ap, unsigned long);
687 qbind[off].length = va_arg (ap, unsigned long *);
690 /* unsupported type */
692 return GNUNET_SYSERR;
697 if (! ( (pc == 0) && (-1 != (int) ft) && (va_arg (ap, int) == -1) ) )
700 return GNUNET_SYSERR;
702 if (mysql_stmt_bind_param (s->statement, qbind))
704 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
705 _("`%s' failed at %s:%d with error: %s\n"),
706 "mysql_stmt_bind_param",
707 __FILE__, __LINE__, mysql_stmt_error (s->statement));
709 return GNUNET_SYSERR;
711 if (mysql_stmt_execute (s->statement))
713 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
714 _("`%s' failed at %s:%d with error: %s\n"),
715 "mysql_stmt_execute",
716 __FILE__, __LINE__, mysql_stmt_error (s->statement));
718 return GNUNET_SYSERR;
724 * Type of a callback that will be called for each
725 * data set returned from MySQL.
727 * @param cls user-defined argument
728 * @param num_values number of elements in values
729 * @param values values returned by MySQL
730 * @return GNUNET_OK to continue iterating, GNUNET_SYSERR to abort
732 typedef int (*GNUNET_MysqlDataProcessor) (void *cls,
733 unsigned int num_values,
734 MYSQL_BIND * values);
738 * Run a prepared SELECT statement.
740 * @param plugin plugin context
741 * @param s statement to run
742 * @param result_size number of elements in results array
743 * @param results pointer to already initialized MYSQL_BIND
744 * array (of sufficient size) for passing results
745 * @param processor function to call on each result
746 * @param processor_cls extra argument to processor
747 * @param ... pairs and triplets of "MYSQL_TYPE_XXX" keys and their respective
748 * values (size + buffer-reference for pointers); terminated
750 * @return GNUNET_SYSERR on error, otherwise
751 * the number of successfully affected (or queried) rows
754 prepared_statement_run_select (struct Plugin *plugin,
755 struct GNUNET_MysqlStatementHandle
757 unsigned int result_size,
758 MYSQL_BIND * results,
759 GNUNET_MysqlDataProcessor
760 processor, void *processor_cls,
768 if (GNUNET_OK != prepare_statement (plugin, s))
771 return GNUNET_SYSERR;
773 va_start (ap, processor_cls);
774 if (GNUNET_OK != init_params (plugin, s, ap))
778 return GNUNET_SYSERR;
781 rsize = mysql_stmt_field_count (s->statement);
782 if (rsize > result_size)
785 return GNUNET_SYSERR;
787 if (mysql_stmt_bind_result (s->statement, results))
789 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
790 _("`%s' failed at %s:%d with error: %s\n"),
791 "mysql_stmt_bind_result",
792 __FILE__, __LINE__, mysql_stmt_error (s->statement));
794 return GNUNET_SYSERR;
800 ret = mysql_stmt_fetch (s->statement);
801 if (ret == MYSQL_NO_DATA)
805 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
806 _("`%s' failed at %s:%d with error: %s\n"),
808 __FILE__, __LINE__, mysql_stmt_error (s->statement));
810 return GNUNET_SYSERR;
812 if (processor != NULL)
813 if (GNUNET_OK != processor (processor_cls, rsize, results))
817 mysql_stmt_reset (s->statement);
823 * Run a prepared statement that does NOT produce results.
825 * @param plugin plugin context
826 * @param s statement to run
827 * @param insert_id NULL or address where to store the row ID of whatever
828 * was inserted (only for INSERT statements!)
829 * @param ... pairs and triplets of "MYSQL_TYPE_XXX" keys and their respective
830 * values (size + buffer-reference for pointers); terminated
832 * @return GNUNET_SYSERR on error, otherwise
833 * the number of successfully affected rows
836 prepared_statement_run (struct Plugin *plugin,
837 struct GNUNET_MysqlStatementHandle *s,
838 unsigned long long *insert_id, ...)
843 if (GNUNET_OK != prepare_statement (plugin, s))
844 return GNUNET_SYSERR;
845 va_start (ap, insert_id);
846 if (GNUNET_OK != init_params (plugin, s, ap))
849 return GNUNET_SYSERR;
852 affected = mysql_stmt_affected_rows (s->statement);
853 if (NULL != insert_id)
854 *insert_id = (unsigned long long) mysql_stmt_insert_id (s->statement);
855 mysql_stmt_reset (s->statement);
861 * Delete an value from the gn072 table.
863 * @param plugin plugin context
864 * @param vkey vkey identifying the value to delete
865 * @return GNUNET_OK on success, GNUNET_NO if no such value exists, GNUNET_SYSERR on error
868 do_delete_value (struct Plugin *plugin,
869 unsigned long long vkey)
874 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
875 "Deleting value %llu from gn072 table\n",
878 ret = prepared_statement_run (plugin,
879 plugin->delete_value,
882 &vkey, GNUNET_YES, -1);
889 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
890 "Deleting value %llu from gn072 table failed\n",
897 * Insert a value into the gn072 table.
899 * @param plugin plugin context
900 * @param value the value to insert
901 * @param size size of the value
902 * @param vkey vkey identifying the value henceforth (set)
903 * @return GNUNET_OK on success, GNUNET_SYSERR on error
906 do_insert_value (struct Plugin *plugin,
907 const void *value, unsigned int size,
908 unsigned long long *vkey)
910 unsigned long length = size;
913 ret = prepared_statement_run (plugin,
914 plugin->insert_value,
917 value, length, &length, -1);
918 if (ret == GNUNET_OK)
921 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
922 "Inserted value number %llu with length %u into gn072 table\n",
929 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
930 "Failed to insert %u byte value into gn072 table\n",
937 * Delete an entry from the gn090 table.
939 * @param plugin plugin context
940 * @param vkey vkey identifying the entry to delete
941 * @return GNUNET_OK on success, GNUNET_NO if no such value exists, GNUNET_SYSERR on error
944 do_delete_entry_by_vkey (struct Plugin *plugin,
945 unsigned long long vkey)
950 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
951 "Deleting value %llu from gn090 table\n",
954 ret = prepared_statement_run (plugin,
955 plugin->delete_entry_by_vkey,
958 &vkey, GNUNET_YES, -1);
965 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
966 "Deleting value %llu from gn090 table failed\n",
974 * Function that simply returns GNUNET_OK
976 * @param cls closure, not used
977 * @param num_values not used
978 * @param values not used
982 return_ok (void *cls,
983 unsigned int num_values,
991 * Run the prepared statement to get the next data item ready.
993 * @param cls not used
994 * @param nrc closure for the next request iterator
995 * @return GNUNET_OK on success, GNUNET_NO if there is no additional item
998 iterator_helper_prepare (void *cls,
999 struct NextRequestClosure *nrc)
1001 struct Plugin *plugin;
1006 plugin = nrc->plugin;
1007 ret = GNUNET_SYSERR;
1008 switch (nrc->iter_select)
1012 ret = prepared_statement_run_select (plugin,
1013 plugin->iter[nrc->iter_select],
1021 MYSQL_TYPE_LONGLONG,
1027 MYSQL_TYPE_LONGLONG,
1032 ret = prepared_statement_run_select (plugin,
1033 plugin->iter[nrc->iter_select],
1038 MYSQL_TYPE_LONGLONG,
1041 MYSQL_TYPE_LONGLONG,
1044 MYSQL_TYPE_LONGLONG,
1047 MYSQL_TYPE_LONGLONG,
1052 ret = prepared_statement_run_select (plugin,
1053 plugin->iter[nrc->iter_select],
1058 MYSQL_TYPE_LONGLONG,
1061 MYSQL_TYPE_LONGLONG,
1064 MYSQL_TYPE_LONGLONG,
1065 &nrc->now.abs_value,
1067 MYSQL_TYPE_LONGLONG,
1070 MYSQL_TYPE_LONGLONG,
1073 MYSQL_TYPE_LONGLONG,
1074 &nrc->now.abs_value,
1085 * Continuation of "mysql_next_request".
1087 * @param next_cls the next context
1088 * @param tc the task context (unused)
1091 mysql_next_request_cont (void *next_cls,
1092 const struct GNUNET_SCHEDULER_TaskContext *tc)
1094 struct NextRequestClosure *nrc = next_cls;
1095 struct Plugin *plugin;
1098 unsigned int priority;
1099 unsigned int anonymity;
1100 unsigned long long exp;
1101 unsigned long long vkey;
1102 unsigned long hashSize;
1103 GNUNET_HashCode key;
1104 struct GNUNET_TIME_Absolute expiration;
1105 unsigned long length;
1106 MYSQL_BIND *rbind; /* size 7 */
1107 MYSQL_BIND dbind[1];
1108 char datum[GNUNET_SERVER_MAX_MESSAGE_SIZE];
1110 plugin = nrc->plugin;
1111 plugin->next_task = GNUNET_SCHEDULER_NO_TASK;
1112 plugin->next_task_nc = NULL;
1115 GNUNET_assert (nrc->plugin->next_task == GNUNET_SCHEDULER_NO_TASK);
1116 nrc->now = GNUNET_TIME_absolute_get ();
1117 hashSize = sizeof (GNUNET_HashCode);
1118 memset (nrc->rbind, 0, sizeof (nrc->rbind));
1120 rbind[0].buffer_type = MYSQL_TYPE_LONG;
1121 rbind[0].buffer = &type;
1122 rbind[0].is_unsigned = 1;
1123 rbind[1].buffer_type = MYSQL_TYPE_LONG;
1124 rbind[1].buffer = &priority;
1125 rbind[1].is_unsigned = 1;
1126 rbind[2].buffer_type = MYSQL_TYPE_LONG;
1127 rbind[2].buffer = &anonymity;
1128 rbind[2].is_unsigned = 1;
1129 rbind[3].buffer_type = MYSQL_TYPE_LONGLONG;
1130 rbind[3].buffer = &exp;
1131 rbind[3].is_unsigned = 1;
1132 rbind[4].buffer_type = MYSQL_TYPE_BLOB;
1133 rbind[4].buffer = &key;
1134 rbind[4].buffer_length = hashSize;
1135 rbind[4].length = &hashSize;
1136 rbind[5].buffer_type = MYSQL_TYPE_LONGLONG;
1137 rbind[5].buffer = &vkey;
1138 rbind[5].is_unsigned = GNUNET_YES;
1140 if ( (GNUNET_YES == nrc->end_it) ||
1141 (GNUNET_OK != nrc->prep (nrc->prep_cls,
1144 GNUNET_assert (nrc->plugin->next_task == GNUNET_SCHEDULER_NO_TASK);
1145 nrc->last_vkey = vkey;
1146 nrc->last_prio = priority;
1147 nrc->last_expire = exp;
1148 if ( (rbind[4].buffer_length != sizeof (GNUNET_HashCode)) ||
1149 (hashSize != sizeof (GNUNET_HashCode)) )
1155 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1156 "Found value %llu with prio %u, anon %u, expire %llu selecting from gn090 table\n",
1162 /* now do query on gn072 */
1163 length = sizeof (datum);
1164 memset (dbind, 0, sizeof (dbind));
1165 dbind[0].buffer_type = MYSQL_TYPE_BLOB;
1166 dbind[0].buffer_length = length;
1167 dbind[0].length = &length;
1168 dbind[0].buffer = datum;
1169 ret = prepared_statement_run_select (plugin,
1170 plugin->select_value,
1175 MYSQL_TYPE_LONGLONG,
1176 &vkey, GNUNET_YES, -1);
1177 GNUNET_break (ret <= 1); /* should only have one rbind! */
1180 if (ret != GNUNET_OK)
1183 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1184 _("Failed to obtain value %llu from table `%s'\n"),
1189 GNUNET_break (length <= sizeof(datum));
1191 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1192 "Calling iterator with value `%s' number %llu of size %u with type %u, priority %u, anonymity %u and expiration %llu\n",
1201 GNUNET_assert (nrc->plugin->next_task == GNUNET_SCHEDULER_NO_TASK);
1202 expiration.abs_value = exp;
1203 ret = nrc->dviter (nrc->dviter_cls,
1213 if (ret == GNUNET_SYSERR)
1215 nrc->end_it = GNUNET_YES;
1218 if (ret == GNUNET_NO)
1220 do_delete_value (plugin, vkey);
1221 do_delete_entry_by_vkey (plugin, vkey);
1223 plugin->env->duc (plugin->env->cls,
1228 /* call dviter with "end of set" */
1229 GNUNET_assert (nrc->plugin->next_task == GNUNET_SCHEDULER_NO_TASK);
1230 nrc->dviter (nrc->dviter_cls,
1231 NULL, NULL, 0, NULL, 0, 0, 0,
1232 GNUNET_TIME_UNIT_ZERO_ABS, 0);
1233 GNUNET_assert (nrc->plugin->next_task == GNUNET_SCHEDULER_NO_TASK);
1234 nrc->prep (nrc->prep_cls, NULL);
1235 GNUNET_assert (nrc->plugin->next_task == GNUNET_SCHEDULER_NO_TASK);
1241 * Function invoked on behalf of a "PluginIterator"
1242 * asking the database plugin to call the iterator
1243 * with the next item.
1245 * @param next_cls whatever argument was given
1246 * to the PluginIterator as "next_cls".
1247 * @param end_it set to GNUNET_YES if we
1248 * should terminate the iteration early
1249 * (iterator should be still called once more
1250 * to signal the end of the iteration).
1253 mysql_plugin_next_request (void *next_cls,
1256 struct NextRequestClosure *nrc = next_cls;
1258 if (GNUNET_YES == end_it)
1259 nrc->end_it = GNUNET_YES;
1260 nrc->plugin->next_task_nc = nrc;
1261 nrc->plugin->next_task = GNUNET_SCHEDULER_add_now (&mysql_next_request_cont,
1267 * Iterate over the items in the datastore
1268 * using the given query to select and order
1271 * @param plugin plugin context
1272 * @param type entries of which type should be considered?
1273 * @param iter_select which iterator statement are we using
1274 * @param is_asc are we using ascending order?
1275 * @param dviter function to call on each matching item
1276 * @param dviter_cls closure for dviter
1279 iterateHelper (struct Plugin *plugin,
1280 enum GNUNET_BLOCK_Type type,
1282 unsigned int iter_select,
1283 PluginIterator dviter,
1286 struct NextRequestClosure *nrc;
1288 nrc = GNUNET_malloc (sizeof (struct NextRequestClosure));
1289 nrc->plugin = plugin;
1291 nrc->iter_select = iter_select;
1292 nrc->dviter = dviter;
1293 nrc->dviter_cls = dviter_cls;
1294 nrc->prep = &iterator_helper_prepare;
1299 nrc->last_expire = 0;
1303 nrc->last_prio = 0x7FFFFFFFL;
1304 nrc->last_vkey = 0x7FFFFFFFFFFFFFFFLL; /* MySQL only supports 63 bits */
1305 nrc->last_expire = 0x7FFFFFFFFFFFFFFFLL; /* MySQL only supports 63 bits */
1307 mysql_plugin_next_request (nrc, GNUNET_NO);
1312 * Get an estimate of how much space the database is
1315 * @param cls our "struct Plugin *"
1316 * @return number of bytes used on disk
1318 static unsigned long long
1319 mysql_plugin_get_size (void *cls)
1321 struct Plugin *plugin = cls;
1322 MYSQL_BIND cbind[1];
1325 memset (cbind, 0, sizeof (cbind));
1327 cbind[0].buffer_type = MYSQL_TYPE_LONGLONG;
1328 cbind[0].buffer = &total;
1329 cbind[0].is_unsigned = GNUNET_NO;
1331 prepared_statement_run_select (plugin,
1342 * Store an item in the datastore.
1344 * @param cls closure
1345 * @param key key for the item
1346 * @param size number of bytes in data
1347 * @param data content stored
1348 * @param type type of the content
1349 * @param priority priority of the content
1350 * @param anonymity anonymity-level for the content
1351 * @param expiration expiration time for the content
1352 * @param msg set to error message
1353 * @return GNUNET_OK on success
1356 mysql_plugin_put (void *cls,
1357 const GNUNET_HashCode * key,
1360 enum GNUNET_BLOCK_Type type,
1363 struct GNUNET_TIME_Absolute expiration,
1366 struct Plugin *plugin = cls;
1367 unsigned int itype = type;
1368 unsigned int ipriority = priority;
1369 unsigned int ianonymity = anonymity;
1370 unsigned long long lexpiration = expiration.abs_value;
1371 unsigned long hashSize;
1372 unsigned long hashSize2;
1373 unsigned long long vkey;
1374 GNUNET_HashCode vhash;
1376 if (size > MAX_DATUM_SIZE)
1379 return GNUNET_SYSERR;
1381 hashSize = sizeof (GNUNET_HashCode);
1382 hashSize2 = sizeof (GNUNET_HashCode);
1383 GNUNET_CRYPTO_hash (data, size, &vhash);
1384 if (GNUNET_OK != do_insert_value (plugin,
1386 return GNUNET_SYSERR;
1388 prepared_statement_run (plugin,
1389 plugin->insert_entry,
1400 MYSQL_TYPE_LONGLONG,
1411 MYSQL_TYPE_LONGLONG,
1412 &vkey, GNUNET_YES, -1))
1414 do_delete_value (plugin, vkey);
1415 return GNUNET_SYSERR;
1418 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1419 "Inserted value `%s' number %llu with size %u into gn090 table\n",
1422 (unsigned int) size);
1425 plugin->env->duc (plugin->env->cls,
1432 * Select a subset of the items in the datastore and call
1433 * the given iterator for each of them.
1435 * @param cls our "struct Plugin*"
1436 * @param type entries of which type should be considered?
1437 * Use 0 for any type.
1438 * @param iter function to call on each matching value;
1439 * will be called once with a NULL value at the end
1440 * @param iter_cls closure for iter
1443 mysql_plugin_iter_low_priority (void *cls,
1444 enum GNUNET_BLOCK_Type type,
1445 PluginIterator iter,
1448 struct Plugin *plugin = cls;
1449 iterateHelper (plugin, type, GNUNET_YES,
1456 GNUNET_HashCode key;
1457 GNUNET_HashCode vhash;
1460 unsigned int anonymity;
1461 unsigned long long expiration;
1462 unsigned long long vkey;
1463 unsigned long long total;
1471 get_statement_prepare (void *cls,
1472 struct NextRequestClosure *nrc)
1474 struct GetContext *gc = cls;
1475 struct Plugin *plugin;
1477 unsigned int limit_off;
1478 unsigned long hashSize;
1485 if (gc->count == gc->total)
1487 plugin = nrc->plugin;
1488 hashSize = sizeof (GNUNET_HashCode);
1489 if (gc->count + gc->off == gc->total)
1490 nrc->last_vkey = 0; /* back to start */
1492 limit_off = gc->off;
1496 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1497 "Obtaining result number %d/%lld at offset %d with lvc %llu for GET `%s'\n",
1502 GNUNET_h2s (&gc->key));
1509 prepared_statement_run_select
1511 plugin->select_entry_by_hash_vhash_and_type, 6, nrc->rbind, &return_ok,
1512 NULL, MYSQL_TYPE_BLOB, &gc->key, hashSize, &hashSize,
1513 MYSQL_TYPE_BLOB, &gc->vhash, hashSize, &hashSize,
1514 MYSQL_TYPE_LONGLONG, &nrc->last_vkey, GNUNET_YES, MYSQL_TYPE_LONG,
1515 &nrc->type, GNUNET_YES, MYSQL_TYPE_LONG, &limit_off, GNUNET_YES,
1521 prepared_statement_run_select
1523 plugin->select_entry_by_hash_and_type, 6, nrc->rbind, &return_ok, NULL,
1524 MYSQL_TYPE_BLOB, &gc->key, hashSize, &hashSize,
1525 MYSQL_TYPE_LONGLONG, &nrc->last_vkey, GNUNET_YES, MYSQL_TYPE_LONG,
1526 &nrc->type, GNUNET_YES, MYSQL_TYPE_LONG, &limit_off, GNUNET_YES,
1535 prepared_statement_run_select
1537 plugin->select_entry_by_hash_and_vhash, 6, nrc->rbind, &return_ok, NULL,
1538 MYSQL_TYPE_BLOB, &gc->key, hashSize, &hashSize, MYSQL_TYPE_BLOB,
1539 &gc->vhash, hashSize, &hashSize, MYSQL_TYPE_LONGLONG,
1540 &nrc->last_vkey, GNUNET_YES, MYSQL_TYPE_LONG, &limit_off,
1546 prepared_statement_run_select
1548 plugin->select_entry_by_hash, 6, nrc->rbind, &return_ok, NULL,
1549 MYSQL_TYPE_BLOB, &gc->key, hashSize, &hashSize,
1550 MYSQL_TYPE_LONGLONG, &nrc->last_vkey, GNUNET_YES, MYSQL_TYPE_LONG,
1551 &limit_off, GNUNET_YES, -1);
1560 * Iterate over the results for a particular key
1563 * @param cls closure
1564 * @param key maybe NULL (to match all entries)
1565 * @param vhash hash of the value, maybe NULL (to
1566 * match all values that have the right key).
1567 * Note that for DBlocks there is no difference
1568 * betwen key and vhash, but for other blocks
1570 * @param type entries of which type are relevant?
1571 * Use 0 for any type.
1572 * @param iter function to call on each matching value;
1573 * will be called once with a NULL value at the end
1574 * @param iter_cls closure for iter
1577 mysql_plugin_get (void *cls,
1578 const GNUNET_HashCode * key,
1579 const GNUNET_HashCode * vhash,
1580 enum GNUNET_BLOCK_Type type,
1581 PluginIterator iter, void *iter_cls)
1583 struct Plugin *plugin = cls;
1584 unsigned int itype = type;
1586 MYSQL_BIND cbind[1];
1587 struct GetContext *gc;
1588 struct NextRequestClosure *nrc;
1590 unsigned long hashSize;
1596 mysql_plugin_iter_low_priority (plugin,
1601 hashSize = sizeof (GNUNET_HashCode);
1602 memset (cbind, 0, sizeof (cbind));
1604 cbind[0].buffer_type = MYSQL_TYPE_LONGLONG;
1605 cbind[0].buffer = &total;
1606 cbind[0].is_unsigned = GNUNET_NO;
1612 prepared_statement_run_select
1614 plugin->count_entry_by_hash_vhash_and_type, 1, cbind, &return_ok, NULL,
1615 MYSQL_TYPE_BLOB, key, hashSize, &hashSize, MYSQL_TYPE_BLOB,
1616 vhash, hashSize, &hashSize, MYSQL_TYPE_LONG, &itype, GNUNET_YES,
1622 prepared_statement_run_select
1624 plugin->count_entry_by_hash_and_type, 1, cbind, &return_ok, NULL,
1625 MYSQL_TYPE_BLOB, key, hashSize, &hashSize, MYSQL_TYPE_LONG,
1626 &itype, GNUNET_YES, -1);
1635 prepared_statement_run_select
1637 plugin->count_entry_by_hash_and_vhash, 1, cbind, &return_ok, NULL,
1638 MYSQL_TYPE_BLOB, key, hashSize, &hashSize, MYSQL_TYPE_BLOB,
1639 vhash, hashSize, &hashSize, -1);
1645 prepared_statement_run_select (plugin,
1646 plugin->count_entry_by_hash,
1647 1, cbind, &return_ok,
1648 NULL, MYSQL_TYPE_BLOB,
1653 if ((ret != GNUNET_OK) || (0 >= total))
1656 NULL, NULL, 0, NULL, 0, 0, 0,
1657 GNUNET_TIME_UNIT_ZERO_ABS, 0);
1661 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1662 "Iterating over %lld results for GET `%s'\n",
1666 gc = GNUNET_malloc (sizeof (struct GetContext));
1670 gc->have_vhash = GNUNET_YES;
1674 gc->off = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, total);
1677 nrc = GNUNET_malloc (sizeof (struct NextRequestClosure));
1678 nrc->plugin = plugin;
1680 nrc->iter_select = -1;
1682 nrc->dviter_cls = iter_cls;
1683 nrc->prep = &get_statement_prepare;
1686 mysql_plugin_next_request (nrc, GNUNET_NO);
1691 * Update the priority for a particular key in the datastore. If
1692 * the expiration time in value is different than the time found in
1693 * the datastore, the higher value should be kept. For the
1694 * anonymity level, the lower value is to be used. The specified
1695 * priority should be added to the existing priority, ignoring the
1696 * priority in value.
1698 * Note that it is possible for multiple values to match this put.
1699 * In that case, all of the respective values are updated.
1701 * @param cls our "struct Plugin*"
1702 * @param uid unique identifier of the datum
1703 * @param delta by how much should the priority
1704 * change? If priority + delta < 0 the
1705 * priority should be set to 0 (never go
1707 * @param expire new expiration time should be the
1708 * MAX of any existing expiration time and
1710 * @param msg set to error message
1711 * @return GNUNET_OK on success
1714 mysql_plugin_update (void *cls,
1717 struct GNUNET_TIME_Absolute expire,
1720 struct Plugin *plugin = cls;
1721 unsigned long long vkey = uid;
1722 unsigned long long lexpire = expire.abs_value;
1726 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1727 "Updating value %llu adding %d to priority and maxing exp at %llu\n",
1732 ret = prepared_statement_run (plugin,
1733 plugin->update_entry,
1738 MYSQL_TYPE_LONGLONG,
1741 MYSQL_TYPE_LONGLONG,
1744 MYSQL_TYPE_LONGLONG,
1747 if (ret != GNUNET_OK)
1749 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1750 "Failed to update value %llu\n",
1758 * Select a subset of the items in the datastore and call
1759 * the given iterator for each of them.
1761 * @param cls our "struct Plugin*"
1762 * @param type entries of which type should be considered?
1763 * Use 0 for any type.
1764 * @param iter function to call on each matching value;
1765 * will be called once with a NULL value at the end
1766 * @param iter_cls closure for iter
1769 mysql_plugin_iter_zero_anonymity (void *cls,
1770 enum GNUNET_BLOCK_Type type,
1771 PluginIterator iter,
1774 struct Plugin *plugin = cls;
1775 iterateHelper (plugin, type, GNUNET_NO, 1, iter, iter_cls);
1780 * Select a subset of the items in the datastore and call
1781 * the given iterator for each of them.
1783 * @param cls our "struct Plugin*"
1784 * @param type entries of which type should be considered?
1785 * Use 0 for any type.
1786 * @param iter function to call on each matching value;
1787 * will be called once with a NULL value at the end
1788 * @param iter_cls closure for iter
1791 mysql_plugin_iter_ascending_expiration (void *cls,
1792 enum GNUNET_BLOCK_Type type,
1793 PluginIterator iter,
1796 struct Plugin *plugin = cls;
1797 iterateHelper (plugin, type, GNUNET_YES, 2, iter, iter_cls);
1802 * Select a subset of the items in the datastore and call
1803 * the given iterator for each of them.
1805 * @param cls our "struct Plugin*"
1806 * @param type entries of which type should be considered?
1807 * Use 0 for any type.
1808 * @param iter function to call on each matching value;
1809 * will be called once with a NULL value at the end
1810 * @param iter_cls closure for iter
1813 mysql_plugin_iter_migration_order (void *cls,
1814 enum GNUNET_BLOCK_Type type,
1815 PluginIterator iter,
1818 struct Plugin *plugin = cls;
1819 iterateHelper (plugin, 0, GNUNET_NO, 3, iter, iter_cls);
1824 * Select a subset of the items in the datastore and call
1825 * the given iterator for each of them.
1827 * @param cls our "struct Plugin*"
1828 * @param type entries of which type should be considered?
1829 * Use 0 for any type.
1830 * @param iter function to call on each matching value;
1831 * will be called once with a NULL value at the end
1832 * @param iter_cls closure for iter
1835 mysql_plugin_iter_all_now (void *cls,
1836 enum GNUNET_BLOCK_Type type,
1837 PluginIterator iter,
1840 struct Plugin *plugin = cls;
1841 iterateHelper (plugin, 0, GNUNET_YES, 0, iter, iter_cls);
1849 mysql_plugin_drop (void *cls)
1851 struct Plugin *plugin = cls;
1853 if ((GNUNET_OK != run_statement (plugin,
1854 "DROP TABLE gn090")) ||
1855 (GNUNET_OK != run_statement (plugin,
1856 "DROP TABLE gn072")))
1858 plugin->env->duc (plugin->env->cls, 0);
1863 * Entry point for the plugin.
1865 * @param cls the "struct GNUNET_DATASTORE_PluginEnvironment*"
1866 * @return our "struct Plugin*"
1869 libgnunet_plugin_datastore_mysql_init (void *cls)
1871 struct GNUNET_DATASTORE_PluginEnvironment *env = cls;
1872 struct GNUNET_DATASTORE_PluginFunctions *api;
1873 struct Plugin *plugin;
1875 plugin = GNUNET_malloc (sizeof (struct Plugin));
1877 plugin->cnffile = get_my_cnf_path (env->cfg);
1878 if (GNUNET_OK != iopen (plugin))
1881 GNUNET_free_non_null (plugin->cnffile);
1882 GNUNET_free (plugin);
1885 #define MRUNS(a) (GNUNET_OK != run_statement (plugin, a) )
1886 #define PINIT(a,b) (NULL == (a = prepared_statement_create(plugin, b)))
1887 if (MRUNS ("CREATE TABLE IF NOT EXISTS gn090 ("
1888 " type INT(11) UNSIGNED NOT NULL DEFAULT 0,"
1889 " prio INT(11) UNSIGNED NOT NULL DEFAULT 0,"
1890 " anonLevel INT(11) UNSIGNED NOT NULL DEFAULT 0,"
1891 " expire BIGINT UNSIGNED NOT NULL DEFAULT 0,"
1892 " hash BINARY(64) NOT NULL DEFAULT '',"
1893 " vhash BINARY(64) NOT NULL DEFAULT '',"
1894 " vkey BIGINT UNSIGNED NOT NULL DEFAULT 0,"
1895 " INDEX hash (hash(64)),"
1896 " INDEX hash_vhash_vkey (hash(64),vhash(64),vkey),"
1897 " INDEX hash_vkey (hash(64),vkey),"
1898 " INDEX vkey (vkey),"
1899 " INDEX prio (prio,vkey),"
1900 " INDEX expire (expire,vkey,type),"
1901 " INDEX anonLevel (anonLevel,prio,vkey,type)"
1902 ") ENGINE=InnoDB") ||
1903 MRUNS ("CREATE TABLE IF NOT EXISTS gn072 ("
1904 " vkey BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,"
1905 " value BLOB NOT NULL DEFAULT '') ENGINE=MyISAM") ||
1906 MRUNS ("SET AUTOCOMMIT = 1") ||
1907 PINIT (plugin->select_value, SELECT_VALUE) ||
1908 PINIT (plugin->delete_value, DELETE_VALUE) ||
1909 PINIT (plugin->insert_value, INSERT_VALUE) ||
1910 PINIT (plugin->insert_entry, INSERT_ENTRY) ||
1911 PINIT (plugin->delete_entry_by_vkey, DELETE_ENTRY_BY_VKEY) ||
1912 PINIT (plugin->select_entry_by_hash, SELECT_ENTRY_BY_HASH) ||
1913 PINIT (plugin->select_entry_by_hash_and_vhash, SELECT_ENTRY_BY_HASH_AND_VHASH)
1914 || PINIT (plugin->select_entry_by_hash_and_type, SELECT_ENTRY_BY_HASH_AND_TYPE)
1915 || PINIT (plugin->select_entry_by_hash_vhash_and_type,
1916 SELECT_ENTRY_BY_HASH_VHASH_AND_TYPE)
1917 || PINIT (plugin->count_entry_by_hash, COUNT_ENTRY_BY_HASH)
1918 || PINIT (plugin->get_size, SELECT_SIZE)
1919 || PINIT (plugin->count_entry_by_hash_and_vhash, COUNT_ENTRY_BY_HASH_AND_VHASH)
1920 || PINIT (plugin->count_entry_by_hash_and_type, COUNT_ENTRY_BY_HASH_AND_TYPE)
1921 || PINIT (plugin->count_entry_by_hash_vhash_and_type,
1922 COUNT_ENTRY_BY_HASH_VHASH_AND_TYPE)
1923 || PINIT (plugin->update_entry, UPDATE_ENTRY)
1924 || PINIT (plugin->iter[0], SELECT_IT_LOW_PRIORITY)
1925 || PINIT (plugin->iter[1], SELECT_IT_NON_ANONYMOUS)
1926 || PINIT (plugin->iter[2], SELECT_IT_EXPIRATION_TIME)
1927 || PINIT (plugin->iter[3], SELECT_IT_MIGRATION_ORDER))
1930 GNUNET_free_non_null (plugin->cnffile);
1931 GNUNET_free (plugin);
1937 api = GNUNET_malloc (sizeof (struct GNUNET_DATASTORE_PluginFunctions));
1939 api->get_size = &mysql_plugin_get_size;
1940 api->put = &mysql_plugin_put;
1941 api->next_request = &mysql_plugin_next_request;
1942 api->get = &mysql_plugin_get;
1943 api->update = &mysql_plugin_update;
1944 api->iter_low_priority = &mysql_plugin_iter_low_priority;
1945 api->iter_zero_anonymity = &mysql_plugin_iter_zero_anonymity;
1946 api->iter_ascending_expiration = &mysql_plugin_iter_ascending_expiration;
1947 api->iter_migration_order = &mysql_plugin_iter_migration_order;
1948 api->iter_all_now = &mysql_plugin_iter_all_now;
1949 api->drop = &mysql_plugin_drop;
1950 GNUNET_log_from (GNUNET_ERROR_TYPE_INFO,
1951 "mysql", _("Mysql database running\n"));
1957 * Exit point from the plugin.
1958 * @param cls our "struct Plugin*"
1959 * @return always NULL
1962 libgnunet_plugin_datastore_mysql_done (void *cls)
1964 struct GNUNET_DATASTORE_PluginFunctions *api = cls;
1965 struct Plugin *plugin = api->cls;
1968 if (plugin->next_task != GNUNET_SCHEDULER_NO_TASK)
1970 GNUNET_SCHEDULER_cancel (plugin->next_task);
1971 plugin->next_task = GNUNET_SCHEDULER_NO_TASK;
1972 plugin->next_task_nc->prep (plugin->next_task_nc->prep_cls, NULL);
1973 GNUNET_free (plugin->next_task_nc);
1974 plugin->next_task_nc = NULL;
1976 GNUNET_free_non_null (plugin->cnffile);
1977 GNUNET_free (plugin);
1979 mysql_library_end ();
1983 /* end of plugin_datastore_mysql.c */