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
53 * sqstore = "sqstore_mysql"
56 * 2) Then access mysql as root,
62 * and do the following. [You should replace $USER with the username
63 * that will be running the gnunetd process].
66 CREATE DATABASE gnunet;
67 GRANT select,insert,update,delete,create,alter,drop,create temporary tables
68 ON gnunet.* TO $USER@localhost;
69 SET PASSWORD FOR $USER@localhost=PASSWORD('$the_password_you_like');
73 * 3) In the $HOME directory of $USER, create a ".my.cnf" file
74 * with the following lines
79 password=$the_password_you_like
83 * Thats it. Note that .my.cnf file is a security risk unless its on
84 * a safe partition etc. The $HOME/.my.cnf can of course be a symbolic
85 * link. Even greater security risk can be achieved by setting no
86 * password for $USER. Luckily $USER has only priviledges to mess
87 * up GNUnet's tables, nothing else (unless you give him more,
90 * 4) Still, perhaps you should briefly try if the DB connection
91 * works. First, login as $USER. Then use,
94 * $ mysql -u $USER -p $the_password_you_like
98 * If you get the message "Database changed" it probably works.
100 * [If you get "ERROR 2002: Can't connect to local MySQL server
101 * through socket '/tmp/mysql.sock' (2)" it may be resolvable by
102 * "ln -s /var/run/mysqld/mysqld.sock /tmp/mysql.sock"
103 * so there may be some additional trouble depending on your mysql setup.]
107 * - Its probably healthy to check your tables for inconsistencies
108 * every now and then.
109 * - If you get odd SEGVs on gnunetd startup, it might be that the mysql
110 * databases have been corrupted.
111 * - The tables can be verified/fixed in two ways;
112 * 1) by running mysqlcheck -A, or
113 * 2) by executing (inside of mysql using the GNUnet database):
114 * mysql> REPAIR TABLE gn080;
115 * mysql> REPAIR TABLE gn072;
119 * If you have problems related to the mysql module, your best
120 * friend is probably the mysql manual. The first thing to check
121 * is that mysql is basically operational, that you can connect
122 * to it, create tables, issue queries etc.
126 * - remove 'size' field in gn080.
127 * - use FOREIGN KEY for 'uid/vkey'
128 * - consistent naming of uid/vkey
131 #include "platform.h"
132 #include "plugin_datastore.h"
133 #include "gnunet_util_lib.h"
134 #include <mysql/mysql.h>
136 #define DEBUG_MYSQL GNUNET_NO
138 #define MAX_DATUM_SIZE 65536
141 * Maximum number of supported parameters for a prepared
142 * statement. Increase if needed.
147 * Die with an error message that indicates
148 * a failure of the command 'cmd' with the message given
149 * by strerror(errno).
151 #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);
154 * Log an error message at log-level 'level' that indicates
155 * a failure of the command 'cmd' on file 'filename'
156 * with the message given by strerror(errno).
158 #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);
161 /* warning, slighly crazy mysql statements ahead. Essentially, MySQL does not handle
162 "OR" very well, so we need to use UNION instead. And UNION does not
163 automatically apply a LIMIT on the outermost clause, so we need to
164 repeat ourselves quite a bit. All hail the performance gods (and thanks
165 to #mysql on freenode) */
166 #define SELECT_IT_LOW_PRIORITY "(SELECT size,type,prio,anonLevel,expire,hash,vkey FROM gn080 FORCE INDEX(prio) WHERE (prio = ? AND vkey > ?) "\
167 "ORDER BY prio ASC,vkey ASC LIMIT 1) "\
169 "(SELECT size,type,prio,anonLevel,expire,hash,vkey FROM gn080 FORCE INDEX(prio) WHERE (prio > ? AND vkey != ?)"\
170 "ORDER BY prio ASC,vkey ASC LIMIT 1)"\
171 "ORDER BY prio ASC,vkey ASC LIMIT 1"
173 #define SELECT_IT_NON_ANONYMOUS "(SELECT size,type,prio,anonLevel,expire,hash,vkey FROM gn080 FORCE INDEX(prio) WHERE (prio = ? AND vkey < ?)"\
174 " AND anonLevel=0 ORDER BY prio DESC,vkey DESC LIMIT 1) "\
176 "(SELECT size,type,prio,anonLevel,expire,hash,vkey FROM gn080 FORCE INDEX(prio) WHERE (prio < ? AND vkey != ?)"\
177 " AND anonLevel=0 ORDER BY prio DESC,vkey DESC LIMIT 1) "\
178 "ORDER BY prio DESC,vkey DESC LIMIT 1"
180 #define SELECT_IT_EXPIRATION_TIME "(SELECT size,type,prio,anonLevel,expire,hash,vkey FROM gn080 FORCE INDEX(expire) WHERE (expire = ? AND vkey > ?) "\
181 "ORDER BY expire ASC,vkey ASC LIMIT 1) "\
183 "(SELECT size,type,prio,anonLevel,expire,hash,vkey FROM gn080 FORCE INDEX(expire) WHERE (expire > ? AND vkey != ?) "\
184 "ORDER BY expire ASC,vkey ASC LIMIT 1)"\
185 "ORDER BY expire ASC,vkey ASC LIMIT 1"
188 #define SELECT_IT_MIGRATION_ORDER "(SELECT size,type,prio,anonLevel,expire,hash,vkey FROM gn080 FORCE INDEX(expire) WHERE (expire = ? AND vkey < ?)"\
189 " AND expire > ? AND type!=3"\
190 " ORDER BY expire DESC,vkey DESC LIMIT 1) "\
192 "(SELECT size,type,prio,anonLevel,expire,hash,vkey FROM gn080 FORCE INDEX(expire) WHERE (expire < ? AND vkey != ?)"\
193 " AND expire > ? AND type!=3"\
194 " ORDER BY expire DESC,vkey DESC LIMIT 1)"\
195 "ORDER BY expire DESC,vkey DESC LIMIT 1"
197 #define SELECT_SIZE "SELECT sum(size) FROM gn080"
200 struct GNUNET_MysqlStatementHandle
202 struct GNUNET_MysqlStatementHandle *next;
204 struct GNUNET_MysqlStatementHandle *prev;
208 MYSQL_STMT *statement;
215 * Context for the universal iterator.
217 struct NextRequestClosure;
220 * Type of a function that will prepare
221 * the next iteration.
224 * @param nc the next context; NULL for the last
225 * call which gives the callback a chance to
226 * clean up the closure
227 * @return GNUNET_OK on success, GNUNET_NO if there are
228 * no more values, GNUNET_SYSERR on error
230 typedef int (*PrepareFunction)(void *cls,
231 struct NextRequestClosure *nc);
234 struct NextRequestClosure
236 struct Plugin *plugin;
238 struct GNUNET_TIME_Absolute now;
241 * Function to call to prepare the next
244 PrepareFunction prep;
255 unsigned int iter_select;
257 PluginIterator dviter;
261 unsigned int last_prio;
263 unsigned long long last_expire;
265 unsigned long long last_vkey;
272 * Context for all functions in this plugin.
277 * Our execution environment.
279 struct GNUNET_DATASTORE_PluginEnvironment *env;
283 struct GNUNET_MysqlStatementHandle *shead;
285 struct GNUNET_MysqlStatementHandle *stail;
288 * Filename of "my.cnf" (msyql configuration).
293 * Closure of the 'next_task' (must be freed if 'next_task' is cancelled).
295 struct NextRequestClosure *next_task_nc;
298 * Pending task with scheduler for running the next request.
300 GNUNET_SCHEDULER_TaskIdentifier next_task;
303 * Statements dealing with gn072 table
305 #define SELECT_VALUE "SELECT value FROM gn072 WHERE vkey=?"
306 struct GNUNET_MysqlStatementHandle *select_value;
308 #define DELETE_VALUE "DELETE FROM gn072 WHERE vkey=?"
309 struct GNUNET_MysqlStatementHandle *delete_value;
311 #define INSERT_VALUE "INSERT INTO gn072 (value) VALUES (?)"
312 struct GNUNET_MysqlStatementHandle *insert_value;
315 * Statements dealing with gn080 table
317 #define INSERT_ENTRY "INSERT INTO gn080 (size,type,prio,anonLevel,expire,hash,vhash,vkey) VALUES (?,?,?,?,?,?,?,?)"
318 struct GNUNET_MysqlStatementHandle *insert_entry;
320 #define DELETE_ENTRY_BY_VKEY "DELETE FROM gn080 WHERE vkey=?"
321 struct GNUNET_MysqlStatementHandle *delete_entry_by_vkey;
323 #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 ?"
324 struct GNUNET_MysqlStatementHandle *select_entry_by_hash;
326 #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 ?"
327 struct GNUNET_MysqlStatementHandle *select_entry_by_hash_and_vhash;
329 #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 ?"
330 struct GNUNET_MysqlStatementHandle *select_entry_by_hash_and_type;
332 #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 ?"
333 struct GNUNET_MysqlStatementHandle *select_entry_by_hash_vhash_and_type;
335 #define COUNT_ENTRY_BY_HASH "SELECT count(*) FROM gn080 FORCE INDEX (hash) WHERE hash=?"
336 struct GNUNET_MysqlStatementHandle *count_entry_by_hash;
338 #define COUNT_ENTRY_BY_HASH_AND_VHASH "SELECT count(*) FROM gn080 FORCE INDEX (hash_vhash_vkey) WHERE hash=? AND vhash=?"
339 struct GNUNET_MysqlStatementHandle *count_entry_by_hash_and_vhash;
341 #define COUNT_ENTRY_BY_HASH_AND_TYPE "SELECT count(*) FROM gn080 FORCE INDEX (hash) WHERE hash=? AND type=?"
342 struct GNUNET_MysqlStatementHandle *count_entry_by_hash_and_type;
344 #define COUNT_ENTRY_BY_HASH_VHASH_AND_TYPE "SELECT count(*) FROM gn080 FORCE INDEX (hash_vhash) WHERE hash=? AND vhash=? AND type=?"
345 struct GNUNET_MysqlStatementHandle *count_entry_by_hash_vhash_and_type;
347 #define UPDATE_ENTRY "UPDATE gn080 SET prio=prio+?,expire=IF(expire>=?,expire,?) WHERE vkey=?"
348 struct GNUNET_MysqlStatementHandle *update_entry;
350 struct GNUNET_MysqlStatementHandle *iter[4];
352 //static unsigned int stat_size;
355 * Size of the mysql database on disk.
357 unsigned long long content_size;
363 * Obtain the location of ".my.cnf".
364 * @return NULL on error
367 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,
388 GNUNET_CONFIGURATION_get_value_filename (cfg,
389 "MYSQL", "CONFIG", &cnffile);
393 home_dir = GNUNET_strdup (pw->pw_dir);
395 home_dir = (char *) GNUNET_malloc (_MAX_PATH + 1);
396 plibc_conv_to_win_path ("~/", home_dir);
398 GNUNET_asprintf (&cnffile, "%s/.my.cnf", home_dir);
399 GNUNET_free (home_dir);
401 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
402 _("Trying to use file `%s' for MySQL configuration.\n"),
404 if ((0 != STAT (cnffile, &st)) ||
405 (0 != ACCESS (cnffile, R_OK)) || (!S_ISREG (st.st_mode)))
407 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
408 _("Could not access file `%s': %s\n"), cnffile,
410 GNUNET_free (cnffile);
419 * Free a prepared statement.
422 prepared_statement_destroy (struct Plugin *plugin,
423 struct GNUNET_MysqlStatementHandle
426 GNUNET_CONTAINER_DLL_remove (plugin->shead,
430 mysql_stmt_close (s->statement);
431 GNUNET_free (s->query);
437 * Close database connection and all prepared statements (we got a DB
441 iclose (struct Plugin *plugin)
443 struct GNUNET_MysqlStatementHandle *spos;
445 spos = plugin->shead;
446 while (NULL != plugin->shead)
447 prepared_statement_destroy (plugin,
449 if (plugin->dbf != NULL)
451 mysql_close (plugin->dbf);
459 * Open the connection with the database (and initialize
460 * our default options).
462 * @return GNUNET_OK on success
465 iopen (struct Plugin *ret)
470 char *mysql_password;
471 unsigned long long mysql_port;
473 unsigned int timeout;
475 ret->dbf = mysql_init (NULL);
476 if (ret->dbf == NULL)
477 return GNUNET_SYSERR;
478 if (ret->cnffile != NULL)
479 mysql_options (ret->dbf, MYSQL_READ_DEFAULT_FILE, ret->cnffile);
480 mysql_options (ret->dbf, MYSQL_READ_DEFAULT_GROUP, "client");
482 mysql_options (ret->dbf, MYSQL_OPT_RECONNECT, &reconnect);
483 mysql_options (ret->dbf,
484 MYSQL_OPT_CONNECT_TIMEOUT, (const void *) &timeout);
485 timeout = 60; /* in seconds */
486 mysql_options (ret->dbf, MYSQL_OPT_READ_TIMEOUT, (const void *) &timeout);
487 mysql_options (ret->dbf, MYSQL_OPT_WRITE_TIMEOUT, (const void *) &timeout);
489 if (GNUNET_YES == GNUNET_CONFIGURATION_have_value (ret->env->cfg,
490 "MYSQL", "DATABASE"))
491 GNUNET_assert (GNUNET_OK ==
492 GNUNET_CONFIGURATION_get_value_string (ret->env->cfg,
496 mysql_dbname = GNUNET_strdup ("gnunet");
498 if (GNUNET_YES == GNUNET_CONFIGURATION_have_value (ret->env->cfg,
501 GNUNET_break (GNUNET_OK ==
502 GNUNET_CONFIGURATION_get_value_string (ret->env->cfg,
506 mysql_password = NULL;
507 if (GNUNET_YES == GNUNET_CONFIGURATION_have_value (ret->env->cfg,
508 "MYSQL", "PASSWORD"))
510 GNUNET_break (GNUNET_OK ==
511 GNUNET_CONFIGURATION_get_value_string (ret->env->cfg,
516 if (GNUNET_YES == GNUNET_CONFIGURATION_have_value (ret->env->cfg,
519 GNUNET_break (GNUNET_OK ==
520 GNUNET_CONFIGURATION_get_value_string (ret->env->cfg,
525 if (GNUNET_YES == GNUNET_CONFIGURATION_have_value (ret->env->cfg,
528 GNUNET_break (GNUNET_OK ==
529 GNUNET_CONFIGURATION_get_value_number (ret->env->cfg, "MYSQL",
530 "PORT", &mysql_port));
533 GNUNET_assert (mysql_dbname != NULL);
534 mysql_real_connect (ret->dbf, mysql_server, mysql_user, mysql_password,
535 mysql_dbname, (unsigned int) mysql_port, NULL, 0);
536 GNUNET_free (mysql_dbname);
537 if (mysql_error (ret->dbf)[0])
539 LOG_MYSQL (GNUNET_ERROR_TYPE_ERROR,
540 "mysql_real_connect", ret);
541 return GNUNET_SYSERR;
548 * Run the given MySQL statement.
550 * @return GNUNET_OK on success, GNUNET_SYSERR on error
553 run_statement (struct Plugin *plugin,
554 const char *statement)
556 if ((NULL == plugin->dbf) && (GNUNET_OK != iopen (plugin)))
557 return GNUNET_SYSERR;
558 mysql_query (plugin->dbf, statement);
559 if (mysql_error (plugin->dbf)[0])
561 LOG_MYSQL (GNUNET_ERROR_TYPE_ERROR,
562 "mysql_query", plugin);
564 return GNUNET_SYSERR;
572 * Run the given MySQL SELECT statement. The statement
573 * must have only a single result (one column, one row).
575 * @return result on success, NULL on error
578 run_statement_select (struct Plugin *plugin,
579 const char *statement)
585 if ((NULL == plugin->dbf) && (GNUNET_OK != iopen (plugin)))
587 mysql_query (plugin->dbf, statement);
588 if ((mysql_error (plugin->dbf)[0]) ||
589 (!(sql_res = mysql_use_result (plugin->dbf))) ||
590 (!(sql_row = mysql_fetch_row (sql_res))))
592 LOG_MYSQL (GNUNET_ERROR_TYPE_ERROR,
593 "mysql_query", plugin);
596 if ((mysql_num_fields (sql_res) != 1) || (sql_row[0] == NULL))
598 GNUNET_break (mysql_num_fields (sql_res) == 1);
600 mysql_free_result (sql_res);
603 ret = GNUNET_strdup (sql_row[0]);
604 mysql_free_result (sql_res);
611 * Create a prepared statement.
613 * @return NULL on error
615 static struct GNUNET_MysqlStatementHandle *
616 prepared_statement_create (struct Plugin *plugin,
617 const char *statement)
619 struct GNUNET_MysqlStatementHandle *ret;
621 ret = GNUNET_malloc (sizeof (struct GNUNET_MysqlStatementHandle));
622 ret->query = GNUNET_strdup (statement);
623 GNUNET_CONTAINER_DLL_insert (plugin->shead,
631 * Prepare a statement for running.
633 * @return GNUNET_OK on success
636 prepare_statement (struct Plugin *plugin,
637 struct GNUNET_MysqlStatementHandle *ret)
639 if (GNUNET_YES == ret->valid)
641 if ((NULL == plugin->dbf) &&
642 (GNUNET_OK != iopen (plugin)))
643 return GNUNET_SYSERR;
644 ret->statement = mysql_stmt_init (plugin->dbf);
645 if (ret->statement == NULL)
648 return GNUNET_SYSERR;
650 if (mysql_stmt_prepare (ret->statement,
652 strlen (ret->query)))
654 LOG_MYSQL (GNUNET_ERROR_TYPE_ERROR,
655 "mysql_stmt_prepare",
657 mysql_stmt_close (ret->statement);
658 ret->statement = NULL;
660 return GNUNET_SYSERR;
662 ret->valid = GNUNET_YES;
669 * Bind the parameters for the given MySQL statement
672 * @param s statement to bind and run
673 * @param ap arguments for the binding
674 * @return GNUNET_SYSERR on error, GNUNET_OK on success
677 init_params (struct Plugin *plugin,
678 struct GNUNET_MysqlStatementHandle *s,
681 MYSQL_BIND qbind[MAX_PARAM];
684 enum enum_field_types ft;
686 pc = mysql_stmt_param_count (s->statement);
689 /* increase internal constant! */
691 return GNUNET_SYSERR;
693 memset (qbind, 0, sizeof (qbind));
696 while ((pc > 0) && (-1 != (ft = va_arg (ap, enum enum_field_types))))
698 qbind[off].buffer_type = ft;
701 case MYSQL_TYPE_FLOAT:
702 qbind[off].buffer = va_arg (ap, float *);
704 case MYSQL_TYPE_LONGLONG:
705 qbind[off].buffer = va_arg (ap, unsigned long long *);
706 qbind[off].is_unsigned = va_arg (ap, int);
708 case MYSQL_TYPE_LONG:
709 qbind[off].buffer = va_arg (ap, unsigned int *);
710 qbind[off].is_unsigned = va_arg (ap, int);
712 case MYSQL_TYPE_VAR_STRING:
713 case MYSQL_TYPE_STRING:
714 case MYSQL_TYPE_BLOB:
715 qbind[off].buffer = va_arg (ap, void *);
716 qbind[off].buffer_length = va_arg (ap, unsigned long);
717 qbind[off].length = va_arg (ap, unsigned long *);
720 /* unsupported type */
722 return GNUNET_SYSERR;
727 if (!((pc == 0) && (ft != -1) && (va_arg (ap, int) == -1)))
730 return GNUNET_SYSERR;
732 if (mysql_stmt_bind_param (s->statement, qbind))
734 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
735 _("`%s' failed at %s:%d with error: %s\n"),
736 "mysql_stmt_bind_param",
737 __FILE__, __LINE__, mysql_stmt_error (s->statement));
739 return GNUNET_SYSERR;
741 if (mysql_stmt_execute (s->statement))
743 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
744 _("`%s' failed at %s:%d with error: %s\n"),
745 "mysql_stmt_execute",
746 __FILE__, __LINE__, mysql_stmt_error (s->statement));
748 return GNUNET_SYSERR;
754 * Type of a callback that will be called for each
755 * data set returned from MySQL.
757 * @param cls user-defined argument
758 * @param num_values number of elements in values
759 * @param values values returned by MySQL
760 * @return GNUNET_OK to continue iterating, GNUNET_SYSERR to abort
762 typedef int (*GNUNET_MysqlDataProcessor) (void *cls,
763 unsigned int num_values,
764 MYSQL_BIND * values);
768 * Run a prepared SELECT statement.
770 * @param result_size number of elements in results array
771 * @param results pointer to already initialized MYSQL_BIND
772 * array (of sufficient size) for passing results
773 * @param processor function to call on each result
774 * @param processor_cls extra argument to processor
775 * @param ... pairs and triplets of "MYSQL_TYPE_XXX" keys and their respective
776 * values (size + buffer-reference for pointers); terminated
778 * @return GNUNET_SYSERR on error, otherwise
779 * the number of successfully affected (or queried) rows
782 prepared_statement_run_select (struct Plugin *plugin,
783 struct GNUNET_MysqlStatementHandle
785 unsigned int result_size,
786 MYSQL_BIND * results,
787 GNUNET_MysqlDataProcessor
788 processor, void *processor_cls,
796 if (GNUNET_OK != prepare_statement (plugin, s))
799 return GNUNET_SYSERR;
801 va_start (ap, processor_cls);
802 if (GNUNET_OK != init_params (plugin, s, ap))
806 return GNUNET_SYSERR;
809 rsize = mysql_stmt_field_count (s->statement);
810 if (rsize > result_size)
813 return GNUNET_SYSERR;
815 if (mysql_stmt_bind_result (s->statement, results))
817 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
818 _("`%s' failed at %s:%d with error: %s\n"),
819 "mysql_stmt_bind_result",
820 __FILE__, __LINE__, mysql_stmt_error (s->statement));
822 return GNUNET_SYSERR;
828 ret = mysql_stmt_fetch (s->statement);
829 if (ret == MYSQL_NO_DATA)
833 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
834 _("`%s' failed at %s:%d with error: %s\n"),
836 __FILE__, __LINE__, mysql_stmt_error (s->statement));
838 return GNUNET_SYSERR;
840 if (processor != NULL)
841 if (GNUNET_OK != processor (processor_cls, rsize, results))
845 mysql_stmt_reset (s->statement);
851 * Run a prepared statement that does NOT produce results.
853 * @param ... pairs and triplets of "MYSQL_TYPE_XXX" keys and their respective
854 * values (size + buffer-reference for pointers); terminated
856 * @param insert_id NULL or address where to store the row ID of whatever
857 * was inserted (only for INSERT statements!)
858 * @return GNUNET_SYSERR on error, otherwise
859 * the number of successfully affected rows
862 prepared_statement_run (struct Plugin *plugin,
863 struct GNUNET_MysqlStatementHandle *s,
864 unsigned long long *insert_id, ...)
869 if (GNUNET_OK != prepare_statement (plugin, s))
870 return GNUNET_SYSERR;
871 va_start (ap, insert_id);
872 if (GNUNET_OK != init_params (plugin, s, ap))
875 return GNUNET_SYSERR;
878 affected = mysql_stmt_affected_rows (s->statement);
879 if (NULL != insert_id)
880 *insert_id = (unsigned long long) mysql_stmt_insert_id (s->statement);
881 mysql_stmt_reset (s->statement);
887 * Delete an value from the gn072 table.
889 * @param vkey vkey identifying the value to delete
890 * @return GNUNET_OK on success, GNUNET_NO if no such value exists, GNUNET_SYSERR on error
893 do_delete_value (struct Plugin *plugin,
894 unsigned long long vkey)
898 ret = prepared_statement_run (plugin,
899 plugin->delete_value,
902 &vkey, GNUNET_YES, -1);
909 * Insert a value into the gn072 table.
911 * @param value the value to insert
912 * @param size size of the value
913 * @param vkey vkey identifying the value henceforth (set)
914 * @return GNUNET_OK on success, GNUNET_SYSERR on error
917 do_insert_value (struct Plugin *plugin,
918 const void *value, unsigned int size,
919 unsigned long long *vkey)
921 unsigned long length = size;
923 return prepared_statement_run (plugin,
924 plugin->insert_value,
927 value, length, &length, -1);
931 * Delete an entry from the gn080 table.
933 * @param vkey vkey identifying the entry to delete
934 * @return GNUNET_OK on success, GNUNET_NO if no such value exists, GNUNET_SYSERR on error
937 do_delete_entry_by_vkey (struct Plugin *plugin,
938 unsigned long long vkey)
942 ret = prepared_statement_run (plugin,
943 plugin->delete_entry_by_vkey,
946 &vkey, GNUNET_YES, -1);
953 return_ok (void *cls, unsigned int num_values, MYSQL_BIND * values)
960 iterator_helper_prepare (void *cls,
961 struct NextRequestClosure *nrc)
963 struct Plugin *plugin;
968 plugin = nrc->plugin;
970 switch (nrc->iter_select)
974 ret = prepared_statement_run_select (plugin,
975 plugin->iter[nrc->iter_select],
994 ret = prepared_statement_run_select (plugin,
995 plugin->iter[nrc->iter_select],
1000 MYSQL_TYPE_LONGLONG,
1003 MYSQL_TYPE_LONGLONG,
1006 MYSQL_TYPE_LONGLONG,
1009 MYSQL_TYPE_LONGLONG,
1014 ret = prepared_statement_run_select (plugin,
1015 plugin->iter[nrc->iter_select],
1020 MYSQL_TYPE_LONGLONG,
1023 MYSQL_TYPE_LONGLONG,
1026 MYSQL_TYPE_LONGLONG,
1029 MYSQL_TYPE_LONGLONG,
1032 MYSQL_TYPE_LONGLONG,
1035 MYSQL_TYPE_LONGLONG,
1047 * Continuation of "sqlite_next_request".
1049 * @param next_cls the next context
1050 * @param tc the task context (unused)
1053 sqlite_next_request_cont (void *next_cls,
1054 const struct GNUNET_SCHEDULER_TaskContext *tc)
1056 struct NextRequestClosure *nrc = next_cls;
1057 struct Plugin *plugin;
1061 unsigned int priority;
1062 unsigned int anonymity;
1063 unsigned long long exp;
1064 unsigned long long vkey;
1065 unsigned long hashSize;
1066 GNUNET_HashCode key;
1067 struct GNUNET_TIME_Absolute expiration;
1068 unsigned int contentSize;
1069 unsigned long length;
1070 MYSQL_BIND *rbind; /* size 7 */
1071 MYSQL_BIND dbind[1];
1072 char datum[GNUNET_SERVER_MAX_MESSAGE_SIZE];
1074 plugin = nrc->plugin;
1075 plugin->next_task = GNUNET_SCHEDULER_NO_TASK;
1076 plugin->next_task_nc = NULL;
1079 ret = nrc->prep (nrc->prep_cls,
1081 if (ret != GNUNET_OK)
1083 nrc->now = GNUNET_TIME_absolute_get ();
1084 hashSize = sizeof (GNUNET_HashCode);
1085 memset (nrc->rbind, 0, sizeof (nrc->rbind));
1087 rbind[0].buffer_type = MYSQL_TYPE_LONG;
1088 rbind[0].buffer = &size;
1089 rbind[0].is_unsigned = 1;
1090 rbind[1].buffer_type = MYSQL_TYPE_LONG;
1091 rbind[1].buffer = &type;
1092 rbind[1].is_unsigned = 1;
1093 rbind[2].buffer_type = MYSQL_TYPE_LONG;
1094 rbind[2].buffer = &priority;
1095 rbind[2].is_unsigned = 1;
1096 rbind[3].buffer_type = MYSQL_TYPE_LONG;
1097 rbind[3].buffer = &anonymity;
1098 rbind[3].is_unsigned = 1;
1099 rbind[4].buffer_type = MYSQL_TYPE_LONGLONG;
1100 rbind[4].buffer = &exp;
1101 rbind[4].is_unsigned = 1;
1102 rbind[5].buffer_type = MYSQL_TYPE_BLOB;
1103 rbind[5].buffer = &key;
1104 rbind[5].buffer_length = hashSize;
1105 rbind[5].length = &hashSize;
1106 rbind[6].buffer_type = MYSQL_TYPE_LONGLONG;
1107 rbind[6].buffer = &vkey;
1108 rbind[6].is_unsigned = GNUNET_YES;
1110 nrc->last_vkey = vkey;
1111 nrc->last_prio = priority;
1112 nrc->last_expire = exp;
1113 if ((rbind[0].buffer_type != MYSQL_TYPE_LONG) ||
1114 (!rbind[0].is_unsigned) ||
1115 (rbind[1].buffer_type != MYSQL_TYPE_LONG) ||
1116 (!rbind[1].is_unsigned) ||
1117 (rbind[2].buffer_type != MYSQL_TYPE_LONG) ||
1118 (!rbind[2].is_unsigned) ||
1119 (rbind[3].buffer_type != MYSQL_TYPE_LONG) ||
1120 (!rbind[3].is_unsigned) ||
1121 (rbind[4].buffer_type != MYSQL_TYPE_LONGLONG) ||
1122 (!rbind[4].is_unsigned) ||
1123 (rbind[5].buffer_type != MYSQL_TYPE_BLOB) ||
1124 (rbind[5].buffer_length != sizeof (GNUNET_HashCode)) ||
1125 (*rbind[5].length != sizeof (GNUNET_HashCode)) ||
1126 (rbind[6].buffer_type != MYSQL_TYPE_LONGLONG) ||
1127 (!rbind[6].is_unsigned))
1132 contentSize = *(unsigned int *) rbind[0].buffer;
1133 if (contentSize >= GNUNET_SERVER_MAX_MESSAGE_SIZE)
1135 GNUNET_break (0); /* far too big */
1138 /* now do query on gn072 */
1139 length = contentSize;
1140 memset (rbind, 0, sizeof (rbind));
1141 rbind[0].buffer_type = MYSQL_TYPE_BLOB;
1142 rbind[0].buffer_length = contentSize;
1143 rbind[0].length = &length;
1144 rbind[0].buffer = datum;
1145 ret = prepared_statement_run_select (plugin,
1146 plugin->select_value,
1151 MYSQL_TYPE_LONGLONG,
1152 &vkey, GNUNET_YES, -1);
1153 GNUNET_break (ret <= 1); /* should only have one rbind! */
1156 if ((ret != GNUNET_OK) ||
1157 (dbind[0].buffer_length != contentSize) || (length != contentSize))
1159 GNUNET_break (ret != 0); /* should have one rbind! */
1160 GNUNET_break (length == contentSize); /* length should match! */
1161 GNUNET_break (dbind[0].buffer_length == contentSize); /* length should be internally consistent! */
1162 do_delete_value (plugin, vkey);
1164 do_delete_entry_by_vkey (plugin, vkey);
1165 plugin->content_size -= contentSize;
1168 expiration.value = exp;
1169 ret = nrc->dviter (nrc->dviter_cls,
1179 if (ret == GNUNET_SYSERR)
1181 /* is this correct, or should we not call iter again period? */
1184 if (ret == GNUNET_NO)
1186 do_delete_value (plugin, vkey);
1187 do_delete_entry_by_vkey (plugin, vkey);
1188 plugin->content_size -= contentSize;
1192 /* call dviter with "end of set" */
1193 nrc->dviter (nrc->dviter_cls,
1194 NULL, NULL, 0, NULL, 0, 0, 0,
1195 GNUNET_TIME_UNIT_ZERO_ABS, 0);
1196 nrc->prep (nrc->prep_cls, NULL);
1202 * Function invoked on behalf of a "PluginIterator"
1203 * asking the database plugin to call the iterator
1204 * with the next item.
1206 * @param next_cls whatever argument was given
1207 * to the PluginIterator as "next_cls".
1208 * @param end_it set to GNUNET_YES if we
1209 * should terminate the iteration early
1210 * (iterator should be still called once more
1211 * to signal the end of the iteration).
1214 mysql_plugin_next_request (void *next_cls,
1217 struct NextRequestClosure *nrc = next_cls;
1219 if (GNUNET_YES == end_it)
1220 nrc->end_it = GNUNET_YES;
1221 nrc->plugin->next_task_nc = nrc;
1222 nrc->plugin->next_task = GNUNET_SCHEDULER_add_now (nrc->plugin->env->sched,
1223 &sqlite_next_request_cont,
1229 * Iterate over the items in the datastore
1230 * using the given query to select and order
1233 * @param type entries of which type should be considered?
1234 * Use 0 for any type.
1235 * @param iter never NULL
1236 * @param is_asc are we using ascending order?
1239 iterateHelper (struct Plugin *plugin,
1242 unsigned int iter_select,
1243 PluginIterator dviter,
1246 struct NextRequestClosure *nrc;
1248 nrc = GNUNET_malloc (sizeof (struct NextRequestClosure));
1249 nrc->plugin = plugin;
1251 nrc->iter_select = iter_select;
1252 nrc->dviter = dviter;
1253 nrc->dviter_cls = dviter_cls;
1254 nrc->prep = &iterator_helper_prepare;
1259 nrc->last_expire = 0;
1263 nrc->last_prio = 0x7FFFFFFFL;
1264 nrc->last_vkey = 0x7FFFFFFFFFFFFFFFLL; /* MySQL only supports 63 bits */
1265 nrc->last_expire = 0x7FFFFFFFFFFFFFFFLL; /* MySQL only supports 63 bits */
1267 mysql_plugin_next_request (nrc, GNUNET_NO);
1272 * Get an estimate of how much space the database is
1275 * @param cls our "struct Plugin*"
1276 * @return number of bytes used on disk
1278 static unsigned long long
1279 mysql_plugin_get_size (void *cls)
1281 struct Plugin *plugin = cls;
1282 return plugin->content_size;
1287 * Store an item in the datastore.
1289 * @param cls closure
1290 * @param key key for the item
1291 * @param size number of bytes in data
1292 * @param data content stored
1293 * @param type type of the content
1294 * @param priority priority of the content
1295 * @param anonymity anonymity-level for the content
1296 * @param expiration expiration time for the content
1297 * @param msg set to error message
1298 * @return GNUNET_OK on success
1301 mysql_plugin_put (void *cls,
1302 const GNUNET_HashCode * key,
1305 enum GNUNET_BLOCK_Type type,
1308 struct GNUNET_TIME_Absolute expiration,
1311 struct Plugin *plugin = cls;
1312 unsigned long hashSize;
1313 unsigned long hashSize2;
1314 unsigned long long vkey;
1315 GNUNET_HashCode vhash;
1317 if (size > MAX_DATUM_SIZE)
1320 return GNUNET_SYSERR;
1322 hashSize = sizeof (GNUNET_HashCode);
1323 hashSize2 = sizeof (GNUNET_HashCode);
1324 GNUNET_CRYPTO_hash (data, size, &vhash);
1325 if (GNUNET_OK != do_insert_value (plugin,
1327 return GNUNET_SYSERR;
1329 prepared_statement_run (plugin,
1330 plugin->insert_entry,
1344 MYSQL_TYPE_LONGLONG,
1355 MYSQL_TYPE_LONGLONG,
1356 &vkey, GNUNET_YES, -1))
1358 do_delete_value (plugin, vkey);
1359 return GNUNET_SYSERR;
1361 plugin->content_size += size;
1367 * Select a subset of the items in the datastore and call
1368 * the given iterator for each of them.
1370 * @param cls our "struct Plugin*"
1371 * @param type entries of which type should be considered?
1372 * Use 0 for any type.
1373 * @param iter function to call on each matching value;
1374 * will be called once with a NULL value at the end
1375 * @param iter_cls closure for iter
1378 mysql_plugin_iter_low_priority (void *cls,
1379 enum GNUNET_BLOCK_Type type,
1380 PluginIterator iter,
1383 struct Plugin *plugin = cls;
1384 iterateHelper (plugin, type, GNUNET_YES,
1391 GNUNET_HashCode key;
1392 GNUNET_HashCode vhash;
1395 unsigned int anonymity;
1396 unsigned int limit_off;
1397 unsigned long long expiration;
1398 unsigned long long vkey;
1399 unsigned long long total;
1403 unsigned long size; /* OBSOLETE! */
1404 unsigned long hashSize;
1409 get_statement_prepare (void *cls,
1410 struct NextRequestClosure *nrc)
1412 struct GetContext *gc = cls;
1413 struct Plugin *plugin;
1422 if (gc->count == gc->total)
1424 plugin = nrc->plugin;
1425 memset (nrc->rbind, 0, sizeof (nrc->rbind));
1426 gc->hashSize = sizeof (GNUNET_HashCode);
1428 rbind[0].buffer_type = MYSQL_TYPE_LONG;
1429 rbind[0].buffer = &gc->size;
1430 rbind[0].is_unsigned = GNUNET_YES;
1431 rbind[1].buffer_type = MYSQL_TYPE_LONG;
1432 rbind[1].buffer = &nrc->type;
1433 rbind[1].is_unsigned = GNUNET_YES;
1434 rbind[2].buffer_type = MYSQL_TYPE_LONG;
1435 rbind[2].buffer = &nrc->last_prio;
1436 rbind[2].is_unsigned = GNUNET_YES;
1437 rbind[3].buffer_type = MYSQL_TYPE_LONG;
1438 rbind[3].buffer = &gc->anonymity;
1439 rbind[3].is_unsigned = GNUNET_YES;
1440 rbind[4].buffer_type = MYSQL_TYPE_LONGLONG;
1441 rbind[4].buffer = &gc->expiration;
1442 rbind[4].is_unsigned = GNUNET_YES;
1443 rbind[5].buffer_type = MYSQL_TYPE_BLOB;
1444 rbind[5].buffer = &gc->key;
1445 rbind[5].buffer_length = gc->hashSize;
1446 rbind[5].length = &gc->hashSize;
1447 rbind[6].buffer_type = MYSQL_TYPE_LONGLONG;
1448 rbind[6].buffer = &gc->vkey;
1449 rbind[6].is_unsigned = GNUNET_YES;
1451 gc->limit_off = gc->off;
1460 prepared_statement_run_select
1462 plugin->select_entry_by_hash_vhash_and_type, 7, nrc->rbind, &return_ok,
1463 NULL, MYSQL_TYPE_BLOB, &gc->key, gc->hashSize, &gc->hashSize,
1464 MYSQL_TYPE_BLOB, &gc->vhash, gc->hashSize, &gc->hashSize,
1465 MYSQL_TYPE_LONGLONG, &nrc->last_vkey, GNUNET_YES, MYSQL_TYPE_LONG,
1466 &nrc->type, GNUNET_YES, MYSQL_TYPE_LONG, &gc->limit_off, GNUNET_YES,
1472 prepared_statement_run_select
1474 plugin->select_entry_by_hash_and_type, 7, nrc->rbind, &return_ok, NULL,
1475 MYSQL_TYPE_BLOB, &gc->key, gc->hashSize, &gc->hashSize,
1476 MYSQL_TYPE_LONGLONG, &nrc->last_vkey, GNUNET_YES, MYSQL_TYPE_LONG,
1477 &nrc->type, GNUNET_YES, MYSQL_TYPE_LONG, &gc->limit_off, GNUNET_YES,
1486 prepared_statement_run_select
1488 plugin->select_entry_by_hash_and_vhash, 7, nrc->rbind, &return_ok, NULL,
1489 MYSQL_TYPE_BLOB, &gc->key, gc->hashSize, &gc->hashSize, MYSQL_TYPE_BLOB,
1490 &gc->vhash, gc->hashSize, &gc->hashSize, MYSQL_TYPE_LONGLONG,
1491 &nrc->last_vkey, GNUNET_YES, MYSQL_TYPE_LONG, &gc->limit_off,
1497 prepared_statement_run_select
1499 plugin->select_entry_by_hash, 7, nrc->rbind, &return_ok, NULL,
1500 MYSQL_TYPE_BLOB, &gc->key, gc->hashSize, &gc->hashSize,
1501 MYSQL_TYPE_LONGLONG, &nrc->last_vkey, GNUNET_YES, MYSQL_TYPE_LONG,
1502 &gc->limit_off, GNUNET_YES, -1);
1505 if (gc->count + gc->off == gc->total)
1506 nrc->last_vkey = 0; /* back to start */
1512 * Iterate over the results for a particular key
1515 * @param cls closure
1516 * @param key maybe NULL (to match all entries)
1517 * @param vhash hash of the value, maybe NULL (to
1518 * match all values that have the right key).
1519 * Note that for DBlocks there is no difference
1520 * betwen key and vhash, but for other blocks
1522 * @param type entries of which type are relevant?
1523 * Use 0 for any type.
1524 * @param iter function to call on each matching value;
1525 * will be called once with a NULL value at the end
1526 * @param iter_cls closure for iter
1529 mysql_plugin_get (void *cls,
1530 const GNUNET_HashCode * key,
1531 const GNUNET_HashCode * vhash,
1532 enum GNUNET_BLOCK_Type type,
1533 PluginIterator iter, void *iter_cls)
1535 struct Plugin *plugin = cls;
1537 MYSQL_BIND cbind[1];
1538 struct GetContext *gc;
1539 struct NextRequestClosure *nrc;
1540 unsigned long long total;
1541 unsigned long hashSize;
1547 mysql_plugin_iter_low_priority (plugin,
1552 hashSize = sizeof (GNUNET_HashCode);
1553 memset (cbind, 0, sizeof (cbind));
1555 cbind[0].buffer_type = MYSQL_TYPE_LONGLONG;
1556 cbind[0].buffer = &total;
1557 cbind[0].is_unsigned = GNUNET_YES;
1563 prepared_statement_run_select
1565 plugin->count_entry_by_hash_vhash_and_type, 1, cbind, &return_ok, NULL,
1566 MYSQL_TYPE_BLOB, key, hashSize, &hashSize, MYSQL_TYPE_BLOB,
1567 vhash, hashSize, &hashSize, MYSQL_TYPE_LONG, &type, GNUNET_YES,
1573 prepared_statement_run_select
1575 plugin->count_entry_by_hash_and_type, 1, cbind, &return_ok, NULL,
1576 MYSQL_TYPE_BLOB, key, hashSize, &hashSize, MYSQL_TYPE_LONG,
1577 &type, GNUNET_YES, -1);
1586 prepared_statement_run_select
1588 plugin->count_entry_by_hash_and_vhash, 1, cbind, &return_ok, NULL,
1589 MYSQL_TYPE_BLOB, key, hashSize, &hashSize, MYSQL_TYPE_BLOB,
1590 vhash, hashSize, &hashSize, -1);
1596 prepared_statement_run_select (plugin,
1597 plugin->count_entry_by_hash,
1598 1, cbind, &return_ok,
1599 NULL, MYSQL_TYPE_BLOB,
1604 if ((ret != GNUNET_OK) || (-1 == total) || (0 == total))
1607 NULL, NULL, 0, NULL, 0, 0, 0,
1608 GNUNET_TIME_UNIT_ZERO_ABS, 0);
1611 gc = GNUNET_malloc (sizeof (struct GetContext));
1615 gc->have_vhash = GNUNET_YES;
1619 gc->off = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, total);
1622 nrc = GNUNET_malloc (sizeof (struct NextRequestClosure));
1623 nrc->plugin = plugin;
1625 nrc->iter_select = -1;
1627 nrc->dviter_cls = iter_cls;
1628 nrc->prep = &get_statement_prepare;
1630 nrc->last_vkey = 0; // FIXME: used to be 'vkey', why? where did vkey come from?
1631 mysql_plugin_next_request (nrc, GNUNET_NO);
1636 * Update the priority for a particular key in the datastore. If
1637 * the expiration time in value is different than the time found in
1638 * the datastore, the higher value should be kept. For the
1639 * anonymity level, the lower value is to be used. The specified
1640 * priority should be added to the existing priority, ignoring the
1641 * priority in value.
1643 * Note that it is possible for multiple values to match this put.
1644 * In that case, all of the respective values are updated.
1646 * @param cls our "struct Plugin*"
1647 * @param uid unique identifier of the datum
1648 * @param delta by how much should the priority
1649 * change? If priority + delta < 0 the
1650 * priority should be set to 0 (never go
1652 * @param expire new expiration time should be the
1653 * MAX of any existing expiration time and
1655 * @param msg set to error message
1656 * @return GNUNET_OK on success
1659 mysql_plugin_update (void *cls,
1662 struct GNUNET_TIME_Absolute expire,
1665 struct Plugin *plugin = cls;
1666 unsigned long long vkey = uid;
1668 return prepared_statement_run (plugin,
1669 plugin->update_entry,
1674 MYSQL_TYPE_LONGLONG,
1677 MYSQL_TYPE_LONGLONG,
1680 MYSQL_TYPE_LONGLONG,
1687 * Select a subset of the items in the datastore and call
1688 * the given iterator for each of them.
1690 * @param cls our "struct Plugin*"
1691 * @param type entries of which type should be considered?
1692 * Use 0 for any type.
1693 * @param iter function to call on each matching value;
1694 * will be called once with a NULL value at the end
1695 * @param iter_cls closure for iter
1698 mysql_plugin_iter_zero_anonymity (void *cls,
1699 enum GNUNET_BLOCK_Type type,
1700 PluginIterator iter,
1703 struct Plugin *plugin = cls;
1704 iterateHelper (plugin, type, GNUNET_NO, 1, iter, iter_cls);
1709 * Select a subset of the items in the datastore and call
1710 * the given iterator for each of them.
1712 * @param cls our "struct Plugin*"
1713 * @param type entries of which type should be considered?
1714 * Use 0 for any type.
1715 * @param iter function to call on each matching value;
1716 * will be called once with a NULL value at the end
1717 * @param iter_cls closure for iter
1720 mysql_plugin_iter_ascending_expiration (void *cls,
1721 enum GNUNET_BLOCK_Type type,
1722 PluginIterator iter,
1725 struct Plugin *plugin = cls;
1726 iterateHelper (plugin, type, GNUNET_YES, 2, iter, iter_cls);
1731 * Select a subset of the items in the datastore and call
1732 * the given iterator for each of them.
1734 * @param cls our "struct Plugin*"
1735 * @param type entries of which type should be considered?
1736 * Use 0 for any type.
1737 * @param iter function to call on each matching value;
1738 * will be called once with a NULL value at the end
1739 * @param iter_cls closure for iter
1742 mysql_plugin_iter_migration_order (void *cls,
1743 enum GNUNET_BLOCK_Type type,
1744 PluginIterator iter,
1747 struct Plugin *plugin = cls;
1748 iterateHelper (plugin, 0, GNUNET_NO, 3, iter, iter_cls);
1753 * Select a subset of the items in the datastore and call
1754 * the given iterator for each of them.
1756 * @param cls our "struct Plugin*"
1757 * @param type entries of which type should be considered?
1758 * Use 0 for any type.
1759 * @param iter function to call on each matching value;
1760 * will be called once with a NULL value at the end
1761 * @param iter_cls closure for iter
1764 mysql_plugin_iter_all_now (void *cls,
1765 enum GNUNET_BLOCK_Type type,
1766 PluginIterator iter,
1769 struct Plugin *plugin = cls;
1770 iterateHelper (plugin, 0, GNUNET_YES, 0, iter, iter_cls);
1778 mysql_plugin_drop (void *cls)
1780 struct Plugin *plugin = cls;
1782 if ((GNUNET_OK != run_statement (plugin,
1783 "DROP TABLE gn080")) ||
1784 (GNUNET_OK != run_statement (plugin,
1785 "DROP TABLE gn072")))
1787 plugin->content_size = 0;
1792 * Entry point for the plugin.
1794 * @param cls the "struct GNUNET_DATASTORE_PluginEnvironment*"
1795 * @return our "struct Plugin*"
1798 libgnunet_plugin_datastore_mysql_init (void *cls)
1800 struct GNUNET_DATASTORE_PluginEnvironment *env = cls;
1801 struct GNUNET_DATASTORE_PluginFunctions *api;
1802 struct Plugin *plugin;
1804 plugin = GNUNET_malloc (sizeof (struct Plugin));
1806 plugin->cnffile = get_my_cnf_path (env->cfg);
1807 if (GNUNET_OK != iopen (plugin))
1810 GNUNET_free_non_null (plugin->cnffile);
1811 GNUNET_free (plugin);
1814 #define MRUNS(a) (GNUNET_OK != run_statement (plugin, a) )
1815 #define PINIT(a,b) (NULL == (a = prepared_statement_create(plugin, b)))
1816 if (MRUNS ("CREATE TABLE IF NOT EXISTS gn080 ("
1817 " size INT(11) UNSIGNED NOT NULL DEFAULT 0,"
1818 " type INT(11) UNSIGNED NOT NULL DEFAULT 0,"
1819 " prio INT(11) UNSIGNED NOT NULL DEFAULT 0,"
1820 " anonLevel INT(11) UNSIGNED NOT NULL DEFAULT 0,"
1821 " expire BIGINT UNSIGNED NOT NULL DEFAULT 0,"
1822 " hash BINARY(64) NOT NULL DEFAULT '',"
1823 " vhash BINARY(64) NOT NULL DEFAULT '',"
1824 " vkey BIGINT UNSIGNED NOT NULL DEFAULT 0,"
1825 " INDEX hash (hash(64)),"
1826 " INDEX hash_vhash_vkey (hash(64),vhash(64),vkey),"
1827 " INDEX hash_vkey (hash(64),vkey),"
1828 " INDEX vkey (vkey),"
1829 " INDEX prio (prio,vkey),"
1830 " INDEX expire (expire,vkey,type),"
1831 " INDEX anonLevel (anonLevel,prio,vkey,type)"
1832 ") ENGINE=InnoDB") ||
1833 MRUNS ("CREATE TABLE IF NOT EXISTS gn072 ("
1834 " vkey BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,"
1835 " value BLOB NOT NULL DEFAULT '') ENGINE=MyISAM") ||
1836 MRUNS ("SET AUTOCOMMIT = 1") ||
1837 PINIT (plugin->select_value, SELECT_VALUE) ||
1838 PINIT (plugin->delete_value, DELETE_VALUE) ||
1839 PINIT (plugin->insert_value, INSERT_VALUE) ||
1840 PINIT (plugin->insert_entry, INSERT_ENTRY) ||
1841 PINIT (plugin->delete_entry_by_vkey, DELETE_ENTRY_BY_VKEY) ||
1842 PINIT (plugin->select_entry_by_hash, SELECT_ENTRY_BY_HASH) ||
1843 PINIT (plugin->select_entry_by_hash_and_vhash, SELECT_ENTRY_BY_HASH_AND_VHASH)
1844 || PINIT (plugin->select_entry_by_hash_and_type, SELECT_ENTRY_BY_HASH_AND_TYPE)
1845 || PINIT (plugin->select_entry_by_hash_vhash_and_type,
1846 SELECT_ENTRY_BY_HASH_VHASH_AND_TYPE)
1847 || PINIT (plugin->count_entry_by_hash, COUNT_ENTRY_BY_HASH)
1848 || PINIT (plugin->count_entry_by_hash_and_vhash, COUNT_ENTRY_BY_HASH_AND_VHASH)
1849 || PINIT (plugin->count_entry_by_hash_and_type, COUNT_ENTRY_BY_HASH_AND_TYPE)
1850 || PINIT (plugin->count_entry_by_hash_vhash_and_type,
1851 COUNT_ENTRY_BY_HASH_VHASH_AND_TYPE)
1852 || PINIT (plugin->update_entry, UPDATE_ENTRY)
1853 || PINIT (plugin->iter[0], SELECT_IT_LOW_PRIORITY)
1854 || PINIT (plugin->iter[1], SELECT_IT_NON_ANONYMOUS)
1855 || PINIT (plugin->iter[2], SELECT_IT_EXPIRATION_TIME)
1856 || PINIT (plugin->iter[3], SELECT_IT_MIGRATION_ORDER))
1859 GNUNET_free_non_null (plugin->cnffile);
1860 GNUNET_free (plugin);
1866 api = GNUNET_malloc (sizeof (struct GNUNET_DATASTORE_PluginFunctions));
1868 api->get_size = &mysql_plugin_get_size;
1869 api->put = &mysql_plugin_put;
1870 api->next_request = &mysql_plugin_next_request;
1871 api->get = &mysql_plugin_get;
1872 api->update = &mysql_plugin_update;
1873 api->iter_low_priority = &mysql_plugin_iter_low_priority;
1874 api->iter_zero_anonymity = &mysql_plugin_iter_zero_anonymity;
1875 api->iter_ascending_expiration = &mysql_plugin_iter_ascending_expiration;
1876 api->iter_migration_order = &mysql_plugin_iter_migration_order;
1877 api->iter_all_now = &mysql_plugin_iter_all_now;
1878 api->drop = &mysql_plugin_drop;
1879 GNUNET_log_from (GNUNET_ERROR_TYPE_INFO,
1880 "mysql", _("Mysql database running\n"));
1886 * Exit point from the plugin.
1887 * @param cls our "struct Plugin*"
1888 * @return always NULL
1891 libgnunet_plugin_datastore_mysql_done (void *cls)
1893 struct GNUNET_DATASTORE_PluginFunctions *api = cls;
1894 struct Plugin *plugin = api->cls;
1897 if (plugin->next_task != GNUNET_SCHEDULER_NO_TASK)
1899 GNUNET_SCHEDULER_cancel (plugin->env->sched,
1901 plugin->next_task = GNUNET_SCHEDULER_NO_TASK;
1902 plugin->next_task_nc->prep (plugin->next_task_nc->prep_cls, NULL);
1903 GNUNET_free (plugin->next_task_nc);
1904 plugin->next_task_nc = NULL;
1906 GNUNET_free_non_null (plugin->cnffile);
1907 GNUNET_free (plugin);
1909 mysql_library_end ();
1913 /* end of plugin_datastore_mysql.c */