2 This file is part of GNUnet
3 Copyright (C) 2009, 2010, 2011 GNUnet e.V.
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your 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 Affero General Public License for more details.
17 * @file datastore/plugin_datastore_mysql.c
18 * @brief mysql-based datastore backend
19 * @author Igor Wronsky
20 * @author Christian Grothoff
21 * @author Christophe Genevey
23 * NOTE: This db module does NOT work with mysql prior to 4.1 since
24 * it uses prepared statements. MySQL 5.0.46 promises to fix a bug
25 * in MyISAM that is causing us grief. At the time of this writing,
26 * that version is yet to be released. In anticipation, the code
27 * will use MyISAM with 5.0.46 (and higher). If you run such a
28 * version, please run "make check" to verify that the MySQL bug
29 * was actually fixed in your version (and if not, change the
30 * code below to use MyISAM for gn071).
35 * + On up-to-date hardware where mysql can be used comfortably, this
36 * module will have better performance than the other db choices
37 * (according to our tests).
38 * + Its often possible to recover the mysql database from internal
39 * inconsistencies. The other db choices do not support repair!
41 * - Memory usage (Comment: "I have 1G and it never caused me trouble")
44 * MANUAL SETUP INSTRUCTIONS
46 * 1) in gnunet.conf, set
51 * 2) Then access mysql as root,
55 * and do the following. [You should replace $USER with the username
56 * that will be running the gnunetd process].
58 CREATE DATABASE gnunet;
59 GRANT select,insert,update,delete,create,alter,drop,create temporary tables
60 ON gnunet.* TO $USER@localhost;
61 SET PASSWORD FOR $USER@localhost=PASSWORD('$the_password_you_like');
64 * 3) In the $HOME directory of $USER, create a ".my.cnf" file
65 * with the following lines
69 password=$the_password_you_like
72 * Thats it. Note that .my.cnf file is a security risk unless its on
73 * a safe partition etc. The $HOME/.my.cnf can of course be a symbolic
74 * link. Even greater security risk can be achieved by setting no
75 * password for $USER. Luckily $USER has only priviledges to mess
76 * up GNUnet's tables, nothing else (unless you give him more,
79 * 4) Still, perhaps you should briefly try if the DB connection
80 * works. First, login as $USER. Then use,
83 $ mysql -u $USER -p $the_password_you_like
87 * If you get the message "Database changed" it probably works.
89 * [If you get "ERROR 2002: Can't connect to local MySQL server
90 * through socket '/tmp/mysql.sock' (2)" it may be resolvable by
91 * "ln -s /var/run/mysqld/mysqld.sock /tmp/mysql.sock"
92 * so there may be some additional trouble depending on your mysql setup.]
96 * - Its probably healthy to check your tables for inconsistencies
98 * - If you get odd SEGVs on gnunetd startup, it might be that the mysql
99 * databases have been corrupted.
100 * - The tables can be verified/fixed in two ways;
101 * 1) by running mysqlcheck -A, or
102 * 2) by executing (inside of mysql using the GNUnet database):
104 mysql> REPAIR TABLE gn090;
109 * If you have problems related to the mysql module, your best
110 * friend is probably the mysql manual. The first thing to check
111 * is that mysql is basically operational, that you can connect
112 * to it, create tables, issue queries etc.
115 #include "platform.h"
116 #include "gnunet_datastore_plugin.h"
117 #include "gnunet_util_lib.h"
118 #include "gnunet_mysql_lib.h"
119 #include "gnunet_my_lib.h"
121 #define MAX_DATUM_SIZE 65536
125 * Context for all functions in this plugin.
130 * Our execution environment.
132 struct GNUNET_DATASTORE_PluginEnvironment *env;
135 * Handle to talk to MySQL.
137 struct GNUNET_MYSQL_Context *mc;
140 * Prepared statements.
142 #define INSERT_ENTRY "INSERT INTO gn090 (repl,type,prio,anonLevel,expire,rvalue,hash,vhash,value) VALUES (?,?,?,?,?,?,?,?,?)"
143 struct GNUNET_MYSQL_StatementHandle *insert_entry;
145 #define DELETE_ENTRY_BY_UID "DELETE FROM gn090 WHERE uid=?"
146 struct GNUNET_MYSQL_StatementHandle *delete_entry_by_uid;
148 #define DELETE_ENTRY_BY_HASH_VALUE "DELETE FROM gn090 "\
149 "WHERE hash = ? AND "\
152 struct GNUNET_MYSQL_StatementHandle *delete_entry_by_hash_value;
154 #define RESULT_COLUMNS "repl, type, prio, anonLevel, expire, hash, value, uid"
156 #define SELECT_ENTRY "SELECT " RESULT_COLUMNS " FROM gn090 "\
157 "WHERE uid >= ? AND "\
158 "(rvalue >= ? OR 0 = ?) "\
159 "ORDER BY uid LIMIT 1"
160 struct GNUNET_MYSQL_StatementHandle *select_entry;
162 #define SELECT_ENTRY_BY_HASH "SELECT " RESULT_COLUMNS " FROM gn090 "\
163 "FORCE INDEX (idx_hash_type_uid) "\
166 "(rvalue >= ? OR 0 = ?) "\
167 "ORDER BY uid LIMIT 1"
168 struct GNUNET_MYSQL_StatementHandle *select_entry_by_hash;
170 #define SELECT_ENTRY_BY_HASH_AND_TYPE "SELECT " RESULT_COLUMNS " FROM gn090 "\
171 "FORCE INDEX (idx_hash_type_uid) "\
172 "WHERE hash = ? AND "\
175 "(rvalue >= ? OR 0 = ?) "\
176 "ORDER BY uid LIMIT 1"
177 struct GNUNET_MYSQL_StatementHandle *select_entry_by_hash_and_type;
179 #define UPDATE_ENTRY "UPDATE gn090 SET "\
182 "expire = GREATEST(expire, ?) "\
183 "WHERE hash = ? AND vhash = ?"
184 struct GNUNET_MYSQL_StatementHandle *update_entry;
186 #define DEC_REPL "UPDATE gn090 SET repl=GREATEST (1, repl) - 1 WHERE uid=?"
187 struct GNUNET_MYSQL_StatementHandle *dec_repl;
189 #define SELECT_SIZE "SELECT SUM(LENGTH(value)+256) FROM gn090"
190 struct GNUNET_MYSQL_StatementHandle *get_size;
192 #define SELECT_IT_NON_ANONYMOUS "SELECT " RESULT_COLUMNS " FROM gn090 "\
193 "FORCE INDEX (idx_anonLevel_type_rvalue) "\
194 "WHERE anonLevel=0 AND "\
197 "ORDER BY uid LIMIT 1"
198 struct GNUNET_MYSQL_StatementHandle *zero_iter;
200 #define SELECT_IT_EXPIRATION "SELECT " RESULT_COLUMNS " FROM gn090 "\
201 "FORCE INDEX (idx_expire) "\
203 "ORDER BY expire ASC LIMIT 1"
204 struct GNUNET_MYSQL_StatementHandle *select_expiration;
206 #define SELECT_IT_PRIORITY "SELECT " RESULT_COLUMNS " FROM gn090 "\
207 "FORCE INDEX (idx_prio) "\
208 "ORDER BY prio ASC LIMIT 1"
209 struct GNUNET_MYSQL_StatementHandle *select_priority;
211 #define SELECT_IT_REPLICATION "SELECT " RESULT_COLUMNS " FROM gn090 "\
212 "FORCE INDEX (idx_repl_rvalue) "\
215 " NOT EXISTS (SELECT 1 FROM gn090 FORCE INDEX (idx_repl_rvalue) WHERE repl=? AND rvalue>=?)) "\
216 "ORDER BY rvalue ASC "\
218 struct GNUNET_MYSQL_StatementHandle *select_replication;
220 #define SELECT_MAX_REPL "SELECT MAX(repl) FROM gn090"
221 struct GNUNET_MYSQL_StatementHandle *max_repl;
223 #define GET_ALL_KEYS "SELECT hash from gn090"
224 struct GNUNET_MYSQL_StatementHandle *get_all_keys;
231 * Delete an entry from the gn090 table.
233 * @param plugin plugin context
234 * @param uid unique ID of the entry to delete
235 * @return #GNUNET_OK on success, #GNUNET_NO if no such value exists, #GNUNET_SYSERR on error
238 do_delete_entry (struct Plugin *plugin,
239 unsigned long long uid)
242 uint64_t uid64 = (uint64_t) uid;
243 struct GNUNET_MY_QueryParam params_delete[] = {
244 GNUNET_MY_query_param_uint64 (&uid64),
245 GNUNET_MY_query_param_end
248 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
249 "Deleting value %llu from gn090 table\n",
251 ret = GNUNET_MY_exec_prepared (plugin->mc,
252 plugin->delete_entry_by_uid,
258 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
259 "Deleting value %llu from gn090 table failed\n",
260 (unsigned long long) uid);
266 * Get an estimate of how much space the database is
269 * @param cls our `struct Plugin *`
270 * @return number of bytes used on disk
273 mysql_plugin_estimate_size (void *cls,
274 unsigned long long *estimate)
276 struct Plugin *plugin = cls;
279 struct GNUNET_MY_QueryParam params_get[] = {
280 GNUNET_MY_query_param_end
282 struct GNUNET_MY_ResultSpec results_get[] = {
283 GNUNET_MY_result_spec_uint64 (&total),
284 GNUNET_MY_result_spec_end
287 ret = GNUNET_MY_exec_prepared (plugin->mc,
292 if ( (GNUNET_OK == ret) &&
294 GNUNET_MY_extract_result (plugin->get_size,
297 *estimate = (unsigned long long) total;
298 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
299 "Size estimate for MySQL payload is %lld\n",
301 GNUNET_assert (UINT64_MAX != total);
302 GNUNET_break (GNUNET_NO ==
303 GNUNET_MY_extract_result (plugin->get_size,
310 * Store an item in the datastore.
313 * @param key key for the item
314 * @param absent true if the key was not found in the bloom filter
315 * @param size number of bytes in @a data
316 * @param data content stored
317 * @param type type of the content
318 * @param priority priority of the content
319 * @param anonymity anonymity-level for the content
320 * @param replication replication-level for the content
321 * @param expiration expiration time for the content
322 * @param cont continuation called with success or failure status
323 * @param cont_cls closure for @a cont
326 mysql_plugin_put (void *cls,
327 const struct GNUNET_HashCode *key,
331 enum GNUNET_BLOCK_Type type,
334 uint32_t replication,
335 struct GNUNET_TIME_Absolute expiration,
339 struct Plugin *plugin = cls;
340 uint64_t lexpiration = expiration.abs_value_us;
341 struct GNUNET_HashCode vhash;
343 GNUNET_CRYPTO_hash (data,
348 struct GNUNET_MY_QueryParam params_update[] = {
349 GNUNET_MY_query_param_uint32 (&priority),
350 GNUNET_MY_query_param_uint32 (&replication),
351 GNUNET_MY_query_param_uint64 (&lexpiration),
352 GNUNET_MY_query_param_auto_from_type (key),
353 GNUNET_MY_query_param_auto_from_type (&vhash),
354 GNUNET_MY_query_param_end
358 GNUNET_MY_exec_prepared (plugin->mc,
359 plugin->update_entry,
366 _("MySQL statement run failure"));
370 MYSQL_STMT *stmt = GNUNET_MYSQL_statement_get_stmt (plugin->update_entry);
371 my_ulonglong rows = mysql_stmt_affected_rows (stmt);
373 GNUNET_break (GNUNET_NO ==
374 GNUNET_MY_extract_result (plugin->update_entry,
387 uint64_t lrvalue = GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK,
389 struct GNUNET_MY_QueryParam params_insert[] = {
390 GNUNET_MY_query_param_uint32 (&replication),
391 GNUNET_MY_query_param_uint32 (&type),
392 GNUNET_MY_query_param_uint32 (&priority),
393 GNUNET_MY_query_param_uint32 (&anonymity),
394 GNUNET_MY_query_param_uint64 (&lexpiration),
395 GNUNET_MY_query_param_uint64 (&lrvalue),
396 GNUNET_MY_query_param_auto_from_type (key),
397 GNUNET_MY_query_param_auto_from_type (&vhash),
398 GNUNET_MY_query_param_fixed_size (data, size),
399 GNUNET_MY_query_param_end
402 if (size > MAX_DATUM_SIZE)
405 cont (cont_cls, key, size, GNUNET_SYSERR, _("Data too large"));
410 GNUNET_MY_exec_prepared (plugin->mc,
411 plugin->insert_entry,
418 _("MySQL statement run failure"));
421 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
422 "Inserted value `%s' with size %u into gn090 table\n",
424 (unsigned int) size);
426 plugin->env->duc (plugin->env->cls,
428 GNUNET_break (GNUNET_NO ==
429 GNUNET_MY_extract_result (plugin->insert_entry,
440 * Run the given select statement and call 'proc' on the resulting
441 * values (which must be in particular positions).
443 * @param plugin the plugin handle
444 * @param stmt select statement to run
445 * @param proc function to call on result
446 * @param proc_cls closure for @a proc
447 * @param params_select arguments to initialize stmt
450 execute_select (struct Plugin *plugin,
451 struct GNUNET_MYSQL_StatementHandle *stmt,
452 PluginDatumProcessor proc,
454 struct GNUNET_MY_QueryParam *params_select)
457 uint32_t replication;
464 struct GNUNET_HashCode key;
465 struct GNUNET_TIME_Absolute expiration;
466 struct GNUNET_MY_ResultSpec results_select[] = {
467 GNUNET_MY_result_spec_uint32 (&replication),
468 GNUNET_MY_result_spec_uint32 (&type),
469 GNUNET_MY_result_spec_uint32 (&priority),
470 GNUNET_MY_result_spec_uint32 (&anonymity),
471 GNUNET_MY_result_spec_absolute_time (&expiration),
472 GNUNET_MY_result_spec_auto_from_type (&key),
473 GNUNET_MY_result_spec_variable_size (&value, &value_size),
474 GNUNET_MY_result_spec_uint64 (&uid),
475 GNUNET_MY_result_spec_end
478 ret = GNUNET_MY_exec_prepared (plugin->mc,
481 if (GNUNET_OK != ret)
484 NULL, 0, NULL, 0, 0, 0, 0, GNUNET_TIME_UNIT_ZERO_ABS, 0);
488 ret = GNUNET_MY_extract_result (stmt,
490 if (GNUNET_OK != ret)
493 NULL, 0, NULL, 0, 0, 0, 0, GNUNET_TIME_UNIT_ZERO_ABS, 0);
497 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
498 "Found %u-byte value under key `%s' with prio %u, anon %u, expire %s selecting from gn090 table\n",
499 (unsigned int) value_size,
501 (unsigned int) priority,
502 (unsigned int) anonymity,
503 GNUNET_STRINGS_absolute_time_to_string (expiration));
504 GNUNET_assert (value_size < MAX_DATUM_SIZE);
505 GNUNET_break (GNUNET_NO ==
506 GNUNET_MY_extract_result (stmt,
508 ret = proc (proc_cls,
518 GNUNET_MY_cleanup_result (results_select);
519 if (GNUNET_NO == ret)
521 do_delete_entry (plugin, uid);
523 plugin->env->duc (plugin->env->cls,
530 * Get one of the results for a particular key in the datastore.
533 * @param next_uid return the result with lowest uid >= next_uid
534 * @param random if true, return a random result instead of using next_uid
535 * @param key key to match, never NULL
536 * @param type entries of which type are relevant?
537 * Use 0 for any type.
538 * @param proc function to call on the matching value,
539 * with NULL for if no value matches
540 * @param proc_cls closure for @a proc
543 mysql_plugin_get_key (void *cls,
546 const struct GNUNET_HashCode *key,
547 enum GNUNET_BLOCK_Type type,
548 PluginDatumProcessor proc,
551 struct Plugin *plugin = cls;
556 rvalue = GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK,
565 struct GNUNET_MY_QueryParam params_select[] = {
566 GNUNET_MY_query_param_uint64 (&next_uid),
567 GNUNET_MY_query_param_uint64 (&rvalue),
568 GNUNET_MY_query_param_uint64 (&rvalue),
569 GNUNET_MY_query_param_end
572 execute_select (plugin,
573 plugin->select_entry,
578 else if (type != GNUNET_BLOCK_TYPE_ANY)
580 struct GNUNET_MY_QueryParam params_select[] = {
581 GNUNET_MY_query_param_auto_from_type (key),
582 GNUNET_MY_query_param_uint32 (&type),
583 GNUNET_MY_query_param_uint64 (&next_uid),
584 GNUNET_MY_query_param_uint64 (&rvalue),
585 GNUNET_MY_query_param_uint64 (&rvalue),
586 GNUNET_MY_query_param_end
589 execute_select (plugin,
590 plugin->select_entry_by_hash_and_type,
597 struct GNUNET_MY_QueryParam params_select[] = {
598 GNUNET_MY_query_param_auto_from_type (key),
599 GNUNET_MY_query_param_uint64 (&next_uid),
600 GNUNET_MY_query_param_uint64 (&rvalue),
601 GNUNET_MY_query_param_uint64 (&rvalue),
602 GNUNET_MY_query_param_end
605 execute_select (plugin,
606 plugin->select_entry_by_hash,
615 * Get a zero-anonymity datum from the datastore.
617 * @param cls our `struct Plugin *`
618 * @param next_uid return the result with lowest uid >= next_uid
619 * @param type entries of which type should be considered?
620 * Must not be zero (ANY).
621 * @param proc function to call on a matching value;
622 * will be called with NULL if no value matches
623 * @param proc_cls closure for @a proc
626 mysql_plugin_get_zero_anonymity (void *cls,
628 enum GNUNET_BLOCK_Type type,
629 PluginDatumProcessor proc,
632 struct Plugin *plugin = cls;
633 uint32_t typei = (uint32_t) type;
635 struct GNUNET_MY_QueryParam params_zero_iter[] = {
636 GNUNET_MY_query_param_uint32 (&typei),
637 GNUNET_MY_query_param_uint64 (&next_uid),
638 GNUNET_MY_query_param_end
641 execute_select (plugin,
650 * Context for #repl_proc() function.
658 struct Plugin *plugin;
661 * Function to call for the result (or the NULL).
663 PluginDatumProcessor proc;
666 * Closure for @e proc.
673 * Wrapper for the processor for #mysql_plugin_get_replication().
674 * Decrements the replication counter and calls the original
678 * @param key key for the content
679 * @param size number of bytes in @a data
680 * @param data content stored
681 * @param type type of the content
682 * @param priority priority of the content
683 * @param anonymity anonymity-level for the content
684 * @param replication replication-level for the content
685 * @param expiration expiration time for the content
686 * @param uid unique identifier for the datum;
687 * maybe 0 if no unique identifier is available
688 * @return #GNUNET_SYSERR to abort the iteration, #GNUNET_OK to continue
689 * (continue on call to "next", of course),
690 * #GNUNET_NO to delete the item and continue (if supported)
693 repl_proc (void *cls,
694 const struct GNUNET_HashCode *key,
697 enum GNUNET_BLOCK_Type type,
700 uint32_t replication,
701 struct GNUNET_TIME_Absolute expiration,
704 struct ReplCtx *rc = cls;
705 struct Plugin *plugin = rc->plugin;
709 ret = rc->proc (rc->proc_cls,
721 struct GNUNET_MY_QueryParam params_proc[] = {
722 GNUNET_MY_query_param_uint64 (&uid),
723 GNUNET_MY_query_param_end
726 iret = GNUNET_MY_exec_prepared (plugin->mc,
729 if (GNUNET_SYSERR == iret)
731 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
732 "Failed to reduce replication counter\n");
733 return GNUNET_SYSERR;
741 * Get a random item for replication. Returns a single, not expired,
742 * random item from those with the highest replication counters. The
743 * item's replication counter is decremented by one IF it was positive
744 * before. Call @a proc with all values ZERO or NULL if the datastore
748 * @param proc function to call the value (once only).
749 * @param proc_cls closure for @a proc
752 mysql_plugin_get_replication (void *cls,
753 PluginDatumProcessor proc,
756 struct Plugin *plugin = cls;
760 struct GNUNET_MY_QueryParam params_get[] = {
761 GNUNET_MY_query_param_end
763 struct GNUNET_MY_ResultSpec results_get[] = {
764 GNUNET_MY_result_spec_uint32 (&repl),
765 GNUNET_MY_result_spec_end
767 struct GNUNET_MY_QueryParam params_select[] = {
768 GNUNET_MY_query_param_uint32 (&repl),
769 GNUNET_MY_query_param_uint64 (&rvalue),
770 GNUNET_MY_query_param_uint32 (&repl),
771 GNUNET_MY_query_param_uint64 (&rvalue),
772 GNUNET_MY_query_param_end
777 rc.proc_cls = proc_cls;
780 GNUNET_MY_exec_prepared (plugin->mc,
784 proc (proc_cls, NULL, 0, NULL, 0, 0, 0, 0, GNUNET_TIME_UNIT_ZERO_ABS, 0);
789 GNUNET_MY_extract_result (plugin->max_repl,
792 proc (proc_cls, NULL, 0, NULL, 0, 0, 0, 0, GNUNET_TIME_UNIT_ZERO_ABS, 0);
795 GNUNET_break (GNUNET_NO ==
796 GNUNET_MY_extract_result (plugin->max_repl,
798 rvalue = GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK,
801 execute_select (plugin,
802 plugin->select_replication,
810 * Get all of the keys in the datastore.
813 * @param proc function to call on each key
814 * @param proc_cls closure for @a proc
817 mysql_plugin_get_keys (void *cls,
818 PluginKeyProcessor proc,
821 struct Plugin *plugin = cls;
823 MYSQL_STMT *statement;
825 struct GNUNET_HashCode key;
826 struct GNUNET_HashCode last;
827 struct GNUNET_MY_QueryParam params_select[] = {
828 GNUNET_MY_query_param_end
830 struct GNUNET_MY_ResultSpec results_select[] = {
831 GNUNET_MY_result_spec_auto_from_type (&key),
832 GNUNET_MY_result_spec_end
835 GNUNET_assert (NULL != proc);
836 statement = GNUNET_MYSQL_statement_get_stmt (plugin->get_all_keys);
838 GNUNET_MY_exec_prepared (plugin->mc,
839 plugin->get_all_keys,
842 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
843 _("`%s' for `%s' failed at %s:%d with error: %s\n"),
844 "mysql_stmt_execute",
848 mysql_stmt_error (statement));
849 GNUNET_MYSQL_statements_invalidate (plugin->mc);
850 proc (proc_cls, NULL, 0);
853 memset (&last, 0, sizeof (last)); /* make static analysis happy */
856 while (ret == GNUNET_YES)
858 ret = GNUNET_MY_extract_result (plugin->get_all_keys,
860 if (0 != memcmp (&last,
880 /* finally, let app know we are done */
884 if (GNUNET_SYSERR == ret)
886 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
887 _("`%s' failed at %s:%d with error: %s\n"),
891 mysql_stmt_error (statement));
892 GNUNET_MYSQL_statements_invalidate (plugin->mc);
899 * Context for #expi_proc() function.
907 struct Plugin *plugin;
910 * Function to call for the result (or the NULL).
912 PluginDatumProcessor proc;
915 * Closure for @e proc.
923 * Wrapper for the processor for #mysql_plugin_get_expiration().
924 * If no expired value was found, we do a second query for
925 * low-priority content.
928 * @param key key for the content
929 * @param size number of bytes in data
930 * @param data content stored
931 * @param type type of the content
932 * @param priority priority of the content
933 * @param anonymity anonymity-level for the content
934 * @param replication replication-level for the content
935 * @param expiration expiration time for the content
936 * @param uid unique identifier for the datum;
937 * maybe 0 if no unique identifier is available
938 * @return #GNUNET_SYSERR to abort the iteration, #GNUNET_OK to continue
939 * (continue on call to "next", of course),
940 * #GNUNET_NO to delete the item and continue (if supported)
943 expi_proc (void *cls,
944 const struct GNUNET_HashCode *key,
947 enum GNUNET_BLOCK_Type type,
950 uint32_t replication,
951 struct GNUNET_TIME_Absolute expiration,
954 struct ExpiCtx *rc = cls;
955 struct Plugin *plugin = rc->plugin;
956 struct GNUNET_MY_QueryParam params_select[] = {
957 GNUNET_MY_query_param_end
962 execute_select (plugin,
963 plugin->select_priority,
967 return GNUNET_SYSERR;
969 return rc->proc (rc->proc_cls,
983 * Get a random item for expiration.
984 * Call @a proc with all values ZERO or NULL if the datastore is empty.
987 * @param proc function to call the value (once only).
988 * @param proc_cls closure for @a proc
991 mysql_plugin_get_expiration (void *cls,
992 PluginDatumProcessor proc,
995 struct Plugin *plugin = cls;
996 struct GNUNET_TIME_Absolute now;
997 struct GNUNET_MY_QueryParam params_select[] = {
998 GNUNET_MY_query_param_absolute_time (&now),
999 GNUNET_MY_query_param_end
1005 rc.proc_cls = proc_cls;
1006 now = GNUNET_TIME_absolute_get ();
1007 execute_select (plugin,
1008 plugin->select_expiration,
1018 * @param cls the `struct Plugin *`
1021 mysql_plugin_drop (void *cls)
1023 struct Plugin *plugin = cls;
1026 GNUNET_MYSQL_statement_run (plugin->mc,
1027 "DROP TABLE gn090"))
1029 plugin->env->duc (plugin->env->cls, 0);
1034 * Remove a particular key in the datastore.
1036 * @param cls closure
1037 * @param key key for the content
1038 * @param size number of bytes in data
1039 * @param data content stored
1040 * @param cont continuation called with success or failure status
1041 * @param cont_cls continuation closure for @a cont
1044 mysql_plugin_remove_key (void *cls,
1045 const struct GNUNET_HashCode *key,
1048 PluginRemoveCont cont,
1051 struct Plugin *plugin = cls;
1052 struct GNUNET_MY_QueryParam params_delete[] = {
1053 GNUNET_MY_query_param_auto_from_type (key),
1054 GNUNET_MY_query_param_fixed_size (data, size),
1055 GNUNET_MY_query_param_end
1059 GNUNET_MY_exec_prepared (plugin->mc,
1060 plugin->delete_entry_by_hash_value,
1063 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1064 "Removing key `%s' from gn090 table failed\n",
1070 _("MySQL statement run failure"));
1074 MYSQL_STMT *stmt = GNUNET_MYSQL_statement_get_stmt (plugin->delete_entry_by_hash_value);
1075 my_ulonglong rows = mysql_stmt_affected_rows (stmt);
1086 plugin->env->duc (plugin->env->cls,
1097 * Entry point for the plugin.
1099 * @param cls the `struct GNUNET_DATASTORE_PluginEnvironment *`
1100 * @return our `struct Plugin *`
1103 libgnunet_plugin_datastore_mysql_init (void *cls)
1105 struct GNUNET_DATASTORE_PluginEnvironment *env = cls;
1106 struct GNUNET_DATASTORE_PluginFunctions *api;
1107 struct Plugin *plugin;
1109 plugin = GNUNET_new (struct Plugin);
1111 plugin->mc = GNUNET_MYSQL_context_create (env->cfg,
1113 if (NULL == plugin->mc)
1115 GNUNET_free (plugin);
1118 #define MRUNS(a) (GNUNET_OK != GNUNET_MYSQL_statement_run (plugin->mc, a) )
1119 #define PINIT(a,b) (NULL == (a = GNUNET_MYSQL_statement_prepare (plugin->mc, b)))
1121 ("CREATE TABLE IF NOT EXISTS gn090 ("
1122 " repl INT(11) UNSIGNED NOT NULL DEFAULT 0,"
1123 " type INT(11) UNSIGNED NOT NULL DEFAULT 0,"
1124 " prio INT(11) UNSIGNED NOT NULL DEFAULT 0,"
1125 " anonLevel INT(11) UNSIGNED NOT NULL DEFAULT 0,"
1126 " expire BIGINT UNSIGNED NOT NULL DEFAULT 0,"
1127 " rvalue BIGINT UNSIGNED NOT NULL,"
1128 " hash BINARY(64) NOT NULL DEFAULT '',"
1129 " vhash BINARY(64) NOT NULL DEFAULT '',"
1130 " value BLOB NOT NULL DEFAULT ''," " uid BIGINT NOT NULL AUTO_INCREMENT,"
1131 " PRIMARY KEY (uid),"
1132 " INDEX idx_hash_type_uid (hash(64),type,rvalue),"
1133 " INDEX idx_prio (prio),"
1134 " INDEX idx_repl_rvalue (repl,rvalue),"
1135 " INDEX idx_expire (expire),"
1136 " INDEX idx_anonLevel_type_rvalue (anonLevel,type,rvalue)"
1137 ") ENGINE=InnoDB") || MRUNS ("SET AUTOCOMMIT = 1") ||
1138 PINIT (plugin->insert_entry, INSERT_ENTRY) ||
1139 PINIT (plugin->delete_entry_by_uid, DELETE_ENTRY_BY_UID) ||
1140 PINIT (plugin->delete_entry_by_hash_value, DELETE_ENTRY_BY_HASH_VALUE) ||
1141 PINIT (plugin->select_entry, SELECT_ENTRY) ||
1142 PINIT (plugin->select_entry_by_hash, SELECT_ENTRY_BY_HASH) ||
1143 PINIT (plugin->select_entry_by_hash_and_type,
1144 SELECT_ENTRY_BY_HASH_AND_TYPE) ||
1145 PINIT (plugin->get_size, SELECT_SIZE) ||
1146 PINIT (plugin->update_entry, UPDATE_ENTRY) ||
1147 PINIT (plugin->dec_repl, DEC_REPL) ||
1148 PINIT (plugin->zero_iter, SELECT_IT_NON_ANONYMOUS) ||
1149 PINIT (plugin->select_expiration, SELECT_IT_EXPIRATION) ||
1150 PINIT (plugin->select_priority, SELECT_IT_PRIORITY) ||
1151 PINIT (plugin->max_repl, SELECT_MAX_REPL) ||
1152 PINIT (plugin->get_all_keys, GET_ALL_KEYS) ||
1153 PINIT (plugin->select_replication, SELECT_IT_REPLICATION) ||
1156 GNUNET_MYSQL_context_destroy (plugin->mc);
1157 GNUNET_free (plugin);
1163 api = GNUNET_new (struct GNUNET_DATASTORE_PluginFunctions);
1165 api->estimate_size = &mysql_plugin_estimate_size;
1166 api->put = &mysql_plugin_put;
1167 api->get_key = &mysql_plugin_get_key;
1168 api->get_replication = &mysql_plugin_get_replication;
1169 api->get_expiration = &mysql_plugin_get_expiration;
1170 api->get_zero_anonymity = &mysql_plugin_get_zero_anonymity;
1171 api->get_keys = &mysql_plugin_get_keys;
1172 api->drop = &mysql_plugin_drop;
1173 api->remove_key = &mysql_plugin_remove_key;
1174 GNUNET_log_from (GNUNET_ERROR_TYPE_INFO, "mysql",
1175 _("Mysql database running\n"));
1181 * Exit point from the plugin.
1183 * @param cls our `struct Plugin *`
1184 * @return always NULL
1187 libgnunet_plugin_datastore_mysql_done (void *cls)
1189 struct GNUNET_DATASTORE_PluginFunctions *api = cls;
1190 struct Plugin *plugin = api->cls;
1192 GNUNET_MYSQL_context_destroy (plugin->mc);
1193 GNUNET_free (plugin);
1198 /* end of plugin_datastore_mysql.c */