12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202 |
- /*
- This file is part of GNUnet
- Copyright (C) 2009, 2010, 2011 GNUnet e.V.
- GNUnet is free software: you can redistribute it and/or modify it
- under the terms of the GNU Affero General Public License as published
- by the Free Software Foundation, either version 3 of the License,
- or (at your option) any later version.
- GNUnet is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Affero General Public License for more details.
-
- You should have received a copy of the GNU Affero General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>.
- SPDX-License-Identifier: AGPL3.0-or-later
- */
- /**
- * @file datastore/plugin_datastore_mysql.c
- * @brief mysql-based datastore backend
- * @author Igor Wronsky
- * @author Christian Grothoff
- * @author Christophe Genevey
- *
- * NOTE: This db module does NOT work with mysql prior to 4.1 since
- * it uses prepared statements. MySQL 5.0.46 promises to fix a bug
- * in MyISAM that is causing us grief. At the time of this writing,
- * that version is yet to be released. In anticipation, the code
- * will use MyISAM with 5.0.46 (and higher). If you run such a
- * version, please run "make check" to verify that the MySQL bug
- * was actually fixed in your version (and if not, change the
- * code below to use MyISAM for gn071).
- *
- * HIGHLIGHTS
- *
- * Pros
- * + On up-to-date hardware where mysql can be used comfortably, this
- * module will have better performance than the other db choices
- * (according to our tests).
- * + Its often possible to recover the mysql database from internal
- * inconsistencies. The other db choices do not support repair!
- * Cons
- * - Memory usage (Comment: "I have 1G and it never caused me trouble")
- * - Manual setup
- *
- * MANUAL SETUP INSTRUCTIONS
- *
- * 1) in gnunet.conf, set
- * @verbatim
- [datastore]
- DATABASE = "mysql"
- @endverbatim
- * 2) Then access mysql as root,
- * @verbatim
- $ mysql -u root -p
- @endverbatim
- * and do the following. [You should replace $USER with the username
- * that will be running the gnunetd process].
- * @verbatim
- CREATE DATABASE gnunet;
- GRANT select,insert,update,delete,create,alter,drop,create temporary tables
- ON gnunet.* TO $USER@localhost;
- SET PASSWORD FOR $USER@localhost=PASSWORD('$the_password_you_like');
- FLUSH PRIVILEGES;
- @endverbatim
- * 3) In the $HOME directory of $USER, create a ".my.cnf" file
- * with the following lines
- * @verbatim
- [client]
- user=$USER
- password=$the_password_you_like
- @endverbatim
- *
- * Thats it. Note that .my.cnf file is a security risk unless its on
- * a safe partition etc. The $HOME/.my.cnf can of course be a symbolic
- * link. Even greater security risk can be achieved by setting no
- * password for $USER. Luckily $USER has only priviledges to mess
- * up GNUnet's tables, nothing else (unless you give them more,
- * of course).<p>
- *
- * 4) Still, perhaps you should briefly try if the DB connection
- * works. First, login as $USER. Then use,
- *
- * @verbatim
- $ mysql -u $USER -p $the_password_you_like
- mysql> use gnunet;
- @endverbatim
- *
- * If you get the message "Database changed" it probably works.
- *
- * [If you get "ERROR 2002: Can't connect to local MySQL server
- * through socket '/tmp/mysql.sock' (2)" it may be resolvable by
- * "ln -s /var/run/mysqld/mysqld.sock /tmp/mysql.sock"
- * so there may be some additional trouble depending on your mysql setup.]
- *
- * REPAIRING TABLES
- *
- * - Its probably healthy to check your tables for inconsistencies
- * every now and then.
- * - If you get odd SEGVs on gnunetd startup, it might be that the mysql
- * databases have been corrupted.
- * - The tables can be verified/fixed in two ways;
- * 1) by running mysqlcheck -A, or
- * 2) by executing (inside of mysql using the GNUnet database):
- * @verbatim
- mysql> REPAIR TABLE gn090;
- @endverbatim
- *
- * PROBLEMS?
- *
- * If you have problems related to the mysql module, your best
- * friend is probably the mysql manual. The first thing to check
- * is that mysql is basically operational, that you can connect
- * to it, create tables, issue queries etc.
- */
- #include "platform.h"
- #include "gnunet_datastore_plugin.h"
- #include "gnunet_util_lib.h"
- #include "gnunet_mysql_lib.h"
- #include "gnunet_my_lib.h"
- #define MAX_DATUM_SIZE 65536
- /**
- * Context for all functions in this plugin.
- */
- struct Plugin
- {
- /**
- * Our execution environment.
- */
- struct GNUNET_DATASTORE_PluginEnvironment *env;
- /**
- * Handle to talk to MySQL.
- */
- struct GNUNET_MYSQL_Context *mc;
- /**
- * Prepared statements.
- */
- #define INSERT_ENTRY "INSERT INTO gn090 (repl,type,prio,anonLevel,expire,rvalue,hash,vhash,value) VALUES (?,?,?,?,?,?,?,?,?)"
- struct GNUNET_MYSQL_StatementHandle *insert_entry;
- #define DELETE_ENTRY_BY_UID "DELETE FROM gn090 WHERE uid=?"
- struct GNUNET_MYSQL_StatementHandle *delete_entry_by_uid;
- #define DELETE_ENTRY_BY_HASH_VALUE "DELETE FROM gn090 "\
- "WHERE hash = ? AND "\
- "value = ? "\
- "LIMIT 1"
- struct GNUNET_MYSQL_StatementHandle *delete_entry_by_hash_value;
- #define RESULT_COLUMNS "repl, type, prio, anonLevel, expire, hash, value, uid"
- #define SELECT_ENTRY "SELECT " RESULT_COLUMNS " FROM gn090 "\
- "WHERE uid >= ? AND "\
- "(rvalue >= ? OR 0 = ?) "\
- "ORDER BY uid LIMIT 1"
- struct GNUNET_MYSQL_StatementHandle *select_entry;
- #define SELECT_ENTRY_BY_HASH "SELECT " RESULT_COLUMNS " FROM gn090 "\
- "FORCE INDEX (idx_hash_type_uid) "\
- "WHERE hash=? AND "\
- "uid >= ? AND "\
- "(rvalue >= ? OR 0 = ?) "\
- "ORDER BY uid LIMIT 1"
- struct GNUNET_MYSQL_StatementHandle *select_entry_by_hash;
- #define SELECT_ENTRY_BY_HASH_AND_TYPE "SELECT " RESULT_COLUMNS " FROM gn090 "\
- "FORCE INDEX (idx_hash_type_uid) "\
- "WHERE hash = ? AND "\
- "type = ? AND "\
- "uid >= ? AND "\
- "(rvalue >= ? OR 0 = ?) "\
- "ORDER BY uid LIMIT 1"
- struct GNUNET_MYSQL_StatementHandle *select_entry_by_hash_and_type;
- #define UPDATE_ENTRY "UPDATE gn090 SET "\
- "prio = prio + ?, "\
- "repl = repl + ?, "\
- "expire = GREATEST(expire, ?) "\
- "WHERE hash = ? AND vhash = ?"
- struct GNUNET_MYSQL_StatementHandle *update_entry;
- #define DEC_REPL "UPDATE gn090 SET repl=GREATEST (1, repl) - 1 WHERE uid=?"
- struct GNUNET_MYSQL_StatementHandle *dec_repl;
- #define SELECT_SIZE "SELECT SUM(LENGTH(value)+256) FROM gn090"
- struct GNUNET_MYSQL_StatementHandle *get_size;
- #define SELECT_IT_NON_ANONYMOUS "SELECT " RESULT_COLUMNS " FROM gn090 "\
- "FORCE INDEX (idx_anonLevel_type_rvalue) "\
- "WHERE anonLevel=0 AND "\
- "type=? AND "\
- "uid >= ? "\
- "ORDER BY uid LIMIT 1"
- struct GNUNET_MYSQL_StatementHandle *zero_iter;
- #define SELECT_IT_EXPIRATION "SELECT " RESULT_COLUMNS " FROM gn090 "\
- "FORCE INDEX (idx_expire) "\
- "WHERE expire < ? "\
- "ORDER BY expire ASC LIMIT 1"
- struct GNUNET_MYSQL_StatementHandle *select_expiration;
- #define SELECT_IT_PRIORITY "SELECT " RESULT_COLUMNS " FROM gn090 "\
- "FORCE INDEX (idx_prio) "\
- "ORDER BY prio ASC LIMIT 1"
- struct GNUNET_MYSQL_StatementHandle *select_priority;
- #define SELECT_IT_REPLICATION "SELECT " RESULT_COLUMNS " FROM gn090 "\
- "FORCE INDEX (idx_repl_rvalue) "\
- "WHERE repl=? AND "\
- " (rvalue>=? OR"\
- " NOT EXISTS (SELECT 1 FROM gn090 FORCE INDEX (idx_repl_rvalue) WHERE repl=? AND rvalue>=?)) "\
- "ORDER BY rvalue ASC "\
- "LIMIT 1"
- struct GNUNET_MYSQL_StatementHandle *select_replication;
- #define SELECT_MAX_REPL "SELECT MAX(repl) FROM gn090"
- struct GNUNET_MYSQL_StatementHandle *max_repl;
- #define GET_ALL_KEYS "SELECT hash from gn090"
- struct GNUNET_MYSQL_StatementHandle *get_all_keys;
- };
- #define MAX_PARAM 16
- /**
- * Delete an entry from the gn090 table.
- *
- * @param plugin plugin context
- * @param uid unique ID of the entry to delete
- * @return #GNUNET_OK on success, #GNUNET_NO if no such value exists, #GNUNET_SYSERR on error
- */
- static int
- do_delete_entry (struct Plugin *plugin,
- unsigned long long uid)
- {
- int ret;
- uint64_t uid64 = (uint64_t) uid;
- struct GNUNET_MY_QueryParam params_delete[] = {
- GNUNET_MY_query_param_uint64 (&uid64),
- GNUNET_MY_query_param_end
- };
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Deleting value %llu from gn090 table\n",
- uid);
- ret = GNUNET_MY_exec_prepared (plugin->mc,
- plugin->delete_entry_by_uid,
- params_delete);
- if (ret >= 0)
- {
- return GNUNET_OK;
- }
- GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
- "Deleting value %llu from gn090 table failed\n",
- (unsigned long long) uid);
- return ret;
- }
- /**
- * Get an estimate of how much space the database is
- * currently using.
- *
- * @param cls our `struct Plugin *`
- * @return number of bytes used on disk
- */
- static void
- mysql_plugin_estimate_size (void *cls,
- unsigned long long *estimate)
- {
- struct Plugin *plugin = cls;
- uint64_t total;
- int ret;
- struct GNUNET_MY_QueryParam params_get[] = {
- GNUNET_MY_query_param_end
- };
- struct GNUNET_MY_ResultSpec results_get[] = {
- GNUNET_MY_result_spec_uint64 (&total),
- GNUNET_MY_result_spec_end
- };
- ret = GNUNET_MY_exec_prepared (plugin->mc,
- plugin->get_size,
- params_get);
- *estimate = 0;
- total = UINT64_MAX;
- if ( (GNUNET_OK == ret) &&
- (GNUNET_OK ==
- GNUNET_MY_extract_result (plugin->get_size,
- results_get)) )
- {
- *estimate = (unsigned long long) total;
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "Size estimate for MySQL payload is %lld\n",
- (long long) total);
- GNUNET_assert (UINT64_MAX != total);
- GNUNET_break (GNUNET_NO ==
- GNUNET_MY_extract_result (plugin->get_size,
- NULL));
- }
- }
- /**
- * Store an item in the datastore.
- *
- * @param cls closure
- * @param key key for the item
- * @param absent true if the key was not found in the bloom filter
- * @param size number of bytes in @a data
- * @param data content stored
- * @param type type of the content
- * @param priority priority of the content
- * @param anonymity anonymity-level for the content
- * @param replication replication-level for the content
- * @param expiration expiration time for the content
- * @param cont continuation called with success or failure status
- * @param cont_cls closure for @a cont
- */
- static void
- mysql_plugin_put (void *cls,
- const struct GNUNET_HashCode *key,
- bool absent,
- uint32_t size,
- const void *data,
- enum GNUNET_BLOCK_Type type,
- uint32_t priority,
- uint32_t anonymity,
- uint32_t replication,
- struct GNUNET_TIME_Absolute expiration,
- PluginPutCont cont,
- void *cont_cls)
- {
- struct Plugin *plugin = cls;
- uint64_t lexpiration = expiration.abs_value_us;
- struct GNUNET_HashCode vhash;
- GNUNET_CRYPTO_hash (data,
- size,
- &vhash);
- if (!absent)
- {
- struct GNUNET_MY_QueryParam params_update[] = {
- GNUNET_MY_query_param_uint32 (&priority),
- GNUNET_MY_query_param_uint32 (&replication),
- GNUNET_MY_query_param_uint64 (&lexpiration),
- GNUNET_MY_query_param_auto_from_type (key),
- GNUNET_MY_query_param_auto_from_type (&vhash),
- GNUNET_MY_query_param_end
- };
- if (GNUNET_OK !=
- GNUNET_MY_exec_prepared (plugin->mc,
- plugin->update_entry,
- params_update))
- {
- cont (cont_cls,
- key,
- size,
- GNUNET_SYSERR,
- _("MySQL statement run failure"));
- return;
- }
- MYSQL_STMT *stmt = GNUNET_MYSQL_statement_get_stmt (plugin->update_entry);
- my_ulonglong rows = mysql_stmt_affected_rows (stmt);
- GNUNET_break (GNUNET_NO ==
- GNUNET_MY_extract_result (plugin->update_entry,
- NULL));
- if (0 != rows)
- {
- cont (cont_cls,
- key,
- size,
- GNUNET_NO,
- NULL);
- return;
- }
- }
- uint64_t lrvalue = GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK,
- UINT64_MAX);
- struct GNUNET_MY_QueryParam params_insert[] = {
- GNUNET_MY_query_param_uint32 (&replication),
- GNUNET_MY_query_param_uint32 (&type),
- GNUNET_MY_query_param_uint32 (&priority),
- GNUNET_MY_query_param_uint32 (&anonymity),
- GNUNET_MY_query_param_uint64 (&lexpiration),
- GNUNET_MY_query_param_uint64 (&lrvalue),
- GNUNET_MY_query_param_auto_from_type (key),
- GNUNET_MY_query_param_auto_from_type (&vhash),
- GNUNET_MY_query_param_fixed_size (data, size),
- GNUNET_MY_query_param_end
- };
- if (size > MAX_DATUM_SIZE)
- {
- GNUNET_break (0);
- cont (cont_cls, key, size, GNUNET_SYSERR, _("Data too large"));
- return;
- }
- if (GNUNET_OK !=
- GNUNET_MY_exec_prepared (plugin->mc,
- plugin->insert_entry,
- params_insert))
- {
- cont (cont_cls,
- key,
- size,
- GNUNET_SYSERR,
- _("MySQL statement run failure"));
- return;
- }
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Inserted value `%s' with size %u into gn090 table\n",
- GNUNET_h2s (key),
- (unsigned int) size);
- if (size > 0)
- plugin->env->duc (plugin->env->cls,
- size);
- GNUNET_break (GNUNET_NO ==
- GNUNET_MY_extract_result (plugin->insert_entry,
- NULL));
- cont (cont_cls,
- key,
- size,
- GNUNET_OK,
- NULL);
- }
- /**
- * Run the given select statement and call 'proc' on the resulting
- * values (which must be in particular positions).
- *
- * @param plugin the plugin handle
- * @param stmt select statement to run
- * @param proc function to call on result
- * @param proc_cls closure for @a proc
- * @param params_select arguments to initialize stmt
- */
- static void
- execute_select (struct Plugin *plugin,
- struct GNUNET_MYSQL_StatementHandle *stmt,
- PluginDatumProcessor proc,
- void *proc_cls,
- struct GNUNET_MY_QueryParam *params_select)
- {
- int ret;
- uint32_t replication;
- uint32_t type;
- uint32_t priority;
- uint32_t anonymity;
- uint64_t uid;
- size_t value_size;
- void *value;
- struct GNUNET_HashCode key;
- struct GNUNET_TIME_Absolute expiration;
- struct GNUNET_MY_ResultSpec results_select[] = {
- GNUNET_MY_result_spec_uint32 (&replication),
- GNUNET_MY_result_spec_uint32 (&type),
- GNUNET_MY_result_spec_uint32 (&priority),
- GNUNET_MY_result_spec_uint32 (&anonymity),
- GNUNET_MY_result_spec_absolute_time (&expiration),
- GNUNET_MY_result_spec_auto_from_type (&key),
- GNUNET_MY_result_spec_variable_size (&value, &value_size),
- GNUNET_MY_result_spec_uint64 (&uid),
- GNUNET_MY_result_spec_end
- };
- ret = GNUNET_MY_exec_prepared (plugin->mc,
- stmt,
- params_select);
- if (GNUNET_OK != ret)
- {
- proc (proc_cls,
- NULL, 0, NULL, 0, 0, 0, 0, GNUNET_TIME_UNIT_ZERO_ABS, 0);
- return;
- }
- ret = GNUNET_MY_extract_result (stmt,
- results_select);
- if (GNUNET_OK != ret)
- {
- proc (proc_cls,
- NULL, 0, NULL, 0, 0, 0, 0, GNUNET_TIME_UNIT_ZERO_ABS, 0);
- return;
- }
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Found %u-byte value under key `%s' with prio %u, anon %u, expire %s selecting from gn090 table\n",
- (unsigned int) value_size,
- GNUNET_h2s (&key),
- (unsigned int) priority,
- (unsigned int) anonymity,
- GNUNET_STRINGS_absolute_time_to_string (expiration));
- GNUNET_assert (value_size < MAX_DATUM_SIZE);
- GNUNET_break (GNUNET_NO ==
- GNUNET_MY_extract_result (stmt,
- NULL));
- ret = proc (proc_cls,
- &key,
- value_size,
- value,
- type,
- priority,
- anonymity,
- replication,
- expiration,
- uid);
- GNUNET_MY_cleanup_result (results_select);
- if (GNUNET_NO == ret)
- {
- do_delete_entry (plugin, uid);
- if (0 != value_size)
- plugin->env->duc (plugin->env->cls,
- - value_size);
- }
- }
- /**
- * Get one of the results for a particular key in the datastore.
- *
- * @param cls closure
- * @param next_uid return the result with lowest uid >= next_uid
- * @param random if true, return a random result instead of using next_uid
- * @param key key to match, never NULL
- * @param type entries of which type are relevant?
- * Use 0 for any type.
- * @param proc function to call on the matching value,
- * with NULL for if no value matches
- * @param proc_cls closure for @a proc
- */
- static void
- mysql_plugin_get_key (void *cls,
- uint64_t next_uid,
- bool random,
- const struct GNUNET_HashCode *key,
- enum GNUNET_BLOCK_Type type,
- PluginDatumProcessor proc,
- void *proc_cls)
- {
- struct Plugin *plugin = cls;
- uint64_t rvalue;
- if (random)
- {
- rvalue = GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK,
- UINT64_MAX);
- next_uid = 0;
- }
- else
- rvalue = 0;
- if (NULL == key)
- {
- struct GNUNET_MY_QueryParam params_select[] = {
- GNUNET_MY_query_param_uint64 (&next_uid),
- GNUNET_MY_query_param_uint64 (&rvalue),
- GNUNET_MY_query_param_uint64 (&rvalue),
- GNUNET_MY_query_param_end
- };
- execute_select (plugin,
- plugin->select_entry,
- proc,
- proc_cls,
- params_select);
- }
- else if (type != GNUNET_BLOCK_TYPE_ANY)
- {
- struct GNUNET_MY_QueryParam params_select[] = {
- GNUNET_MY_query_param_auto_from_type (key),
- GNUNET_MY_query_param_uint32 (&type),
- GNUNET_MY_query_param_uint64 (&next_uid),
- GNUNET_MY_query_param_uint64 (&rvalue),
- GNUNET_MY_query_param_uint64 (&rvalue),
- GNUNET_MY_query_param_end
- };
- execute_select (plugin,
- plugin->select_entry_by_hash_and_type,
- proc,
- proc_cls,
- params_select);
- }
- else
- {
- struct GNUNET_MY_QueryParam params_select[] = {
- GNUNET_MY_query_param_auto_from_type (key),
- GNUNET_MY_query_param_uint64 (&next_uid),
- GNUNET_MY_query_param_uint64 (&rvalue),
- GNUNET_MY_query_param_uint64 (&rvalue),
- GNUNET_MY_query_param_end
- };
- execute_select (plugin,
- plugin->select_entry_by_hash,
- proc,
- proc_cls,
- params_select);
- }
- }
- /**
- * Get a zero-anonymity datum from the datastore.
- *
- * @param cls our `struct Plugin *`
- * @param next_uid return the result with lowest uid >= next_uid
- * @param type entries of which type should be considered?
- * Must not be zero (ANY).
- * @param proc function to call on a matching value;
- * will be called with NULL if no value matches
- * @param proc_cls closure for @a proc
- */
- static void
- mysql_plugin_get_zero_anonymity (void *cls,
- uint64_t next_uid,
- enum GNUNET_BLOCK_Type type,
- PluginDatumProcessor proc,
- void *proc_cls)
- {
- struct Plugin *plugin = cls;
- uint32_t typei = (uint32_t) type;
- struct GNUNET_MY_QueryParam params_zero_iter[] = {
- GNUNET_MY_query_param_uint32 (&typei),
- GNUNET_MY_query_param_uint64 (&next_uid),
- GNUNET_MY_query_param_end
- };
- execute_select (plugin,
- plugin->zero_iter,
- proc,
- proc_cls,
- params_zero_iter);
- }
- /**
- * Context for #repl_proc() function.
- */
- struct ReplCtx
- {
- /**
- * Plugin handle.
- */
- struct Plugin *plugin;
- /**
- * Function to call for the result (or the NULL).
- */
- PluginDatumProcessor proc;
- /**
- * Closure for @e proc.
- */
- void *proc_cls;
- };
- /**
- * Wrapper for the processor for #mysql_plugin_get_replication().
- * Decrements the replication counter and calls the original
- * iterator.
- *
- * @param cls closure
- * @param key key for the content
- * @param size number of bytes in @a data
- * @param data content stored
- * @param type type of the content
- * @param priority priority of the content
- * @param anonymity anonymity-level for the content
- * @param replication replication-level for the content
- * @param expiration expiration time for the content
- * @param uid unique identifier for the datum;
- * maybe 0 if no unique identifier is available
- * @return #GNUNET_SYSERR to abort the iteration, #GNUNET_OK to continue
- * (continue on call to "next", of course),
- * #GNUNET_NO to delete the item and continue (if supported)
- */
- static int
- repl_proc (void *cls,
- const struct GNUNET_HashCode *key,
- uint32_t size,
- const void *data,
- enum GNUNET_BLOCK_Type type,
- uint32_t priority,
- uint32_t anonymity,
- uint32_t replication,
- struct GNUNET_TIME_Absolute expiration,
- uint64_t uid)
- {
- struct ReplCtx *rc = cls;
- struct Plugin *plugin = rc->plugin;
- int ret;
- int iret;
- ret = rc->proc (rc->proc_cls,
- key,
- size,
- data,
- type,
- priority,
- anonymity,
- replication,
- expiration,
- uid);
- if (NULL != key)
- {
- struct GNUNET_MY_QueryParam params_proc[] = {
- GNUNET_MY_query_param_uint64 (&uid),
- GNUNET_MY_query_param_end
- };
- iret = GNUNET_MY_exec_prepared (plugin->mc,
- plugin->dec_repl,
- params_proc);
- if (GNUNET_SYSERR == iret)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
- "Failed to reduce replication counter\n");
- return GNUNET_SYSERR;
- }
- }
- return ret;
- }
- /**
- * Get a random item for replication. Returns a single, not expired,
- * random item from those with the highest replication counters. The
- * item's replication counter is decremented by one IF it was positive
- * before. Call @a proc with all values ZERO or NULL if the datastore
- * is empty.
- *
- * @param cls closure
- * @param proc function to call the value (once only).
- * @param proc_cls closure for @a proc
- */
- static void
- mysql_plugin_get_replication (void *cls,
- PluginDatumProcessor proc,
- void *proc_cls)
- {
- struct Plugin *plugin = cls;
- uint64_t rvalue;
- uint32_t repl;
- struct ReplCtx rc;
- struct GNUNET_MY_QueryParam params_get[] = {
- GNUNET_MY_query_param_end
- };
- struct GNUNET_MY_ResultSpec results_get[] = {
- GNUNET_MY_result_spec_uint32 (&repl),
- GNUNET_MY_result_spec_end
- };
- struct GNUNET_MY_QueryParam params_select[] = {
- GNUNET_MY_query_param_uint32 (&repl),
- GNUNET_MY_query_param_uint64 (&rvalue),
- GNUNET_MY_query_param_uint32 (&repl),
- GNUNET_MY_query_param_uint64 (&rvalue),
- GNUNET_MY_query_param_end
- };
- rc.plugin = plugin;
- rc.proc = proc;
- rc.proc_cls = proc_cls;
- if (1 !=
- GNUNET_MY_exec_prepared (plugin->mc,
- plugin->max_repl,
- params_get))
- {
- proc (proc_cls, NULL, 0, NULL, 0, 0, 0, 0, GNUNET_TIME_UNIT_ZERO_ABS, 0);
- return;
- }
- if (GNUNET_OK !=
- GNUNET_MY_extract_result (plugin->max_repl,
- results_get))
- {
- proc (proc_cls, NULL, 0, NULL, 0, 0, 0, 0, GNUNET_TIME_UNIT_ZERO_ABS, 0);
- return;
- }
- GNUNET_break (GNUNET_NO ==
- GNUNET_MY_extract_result (plugin->max_repl,
- NULL));
- rvalue = GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK,
- UINT64_MAX);
- execute_select (plugin,
- plugin->select_replication,
- &repl_proc,
- &rc,
- params_select);
- }
- /**
- * Get all of the keys in the datastore.
- *
- * @param cls closure
- * @param proc function to call on each key
- * @param proc_cls closure for @a proc
- */
- static void
- mysql_plugin_get_keys (void *cls,
- PluginKeyProcessor proc,
- void *proc_cls)
- {
- struct Plugin *plugin = cls;
- int ret;
- MYSQL_STMT *statement;
- unsigned int cnt;
- struct GNUNET_HashCode key;
- struct GNUNET_HashCode last;
- struct GNUNET_MY_QueryParam params_select[] = {
- GNUNET_MY_query_param_end
- };
- struct GNUNET_MY_ResultSpec results_select[] = {
- GNUNET_MY_result_spec_auto_from_type (&key),
- GNUNET_MY_result_spec_end
- };
- GNUNET_assert (NULL != proc);
- statement = GNUNET_MYSQL_statement_get_stmt (plugin->get_all_keys);
- if (GNUNET_OK !=
- GNUNET_MY_exec_prepared (plugin->mc,
- plugin->get_all_keys,
- params_select))
- {
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- _("`%s' for `%s' failed at %s:%d with error: %s\n"),
- "mysql_stmt_execute",
- GET_ALL_KEYS,
- __FILE__,
- __LINE__,
- mysql_stmt_error (statement));
- GNUNET_MYSQL_statements_invalidate (plugin->mc);
- proc (proc_cls, NULL, 0);
- return;
- }
- memset (&last, 0, sizeof (last)); /* make static analysis happy */
- ret = GNUNET_YES;
- cnt = 0;
- while (ret == GNUNET_YES)
- {
- ret = GNUNET_MY_extract_result (plugin->get_all_keys,
- results_select);
- if (0 != GNUNET_memcmp (&last,
- &key))
- {
- if (0 != cnt)
- proc (proc_cls,
- &last,
- cnt);
- cnt = 1;
- last = key;
- }
- else
- {
- cnt++;
- }
- }
- if (0 != cnt)
- proc (proc_cls,
- &last,
- cnt);
- /* finally, let app know we are done */
- proc (proc_cls,
- NULL,
- 0);
- if (GNUNET_SYSERR == ret)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- _("`%s' failed at %s:%d with error: %s\n"),
- "mysql_stmt_fetch",
- __FILE__,
- __LINE__,
- mysql_stmt_error (statement));
- GNUNET_MYSQL_statements_invalidate (plugin->mc);
- return;
- }
- }
- /**
- * Context for #expi_proc() function.
- */
- struct ExpiCtx
- {
- /**
- * Plugin handle.
- */
- struct Plugin *plugin;
- /**
- * Function to call for the result (or the NULL).
- */
- PluginDatumProcessor proc;
- /**
- * Closure for @e proc.
- */
- void *proc_cls;
- };
- /**
- * Wrapper for the processor for #mysql_plugin_get_expiration().
- * If no expired value was found, we do a second query for
- * low-priority content.
- *
- * @param cls closure
- * @param key key for the content
- * @param size number of bytes in data
- * @param data content stored
- * @param type type of the content
- * @param priority priority of the content
- * @param anonymity anonymity-level for the content
- * @param replication replication-level for the content
- * @param expiration expiration time for the content
- * @param uid unique identifier for the datum;
- * maybe 0 if no unique identifier is available
- * @return #GNUNET_SYSERR to abort the iteration, #GNUNET_OK to continue
- * (continue on call to "next", of course),
- * #GNUNET_NO to delete the item and continue (if supported)
- */
- static int
- expi_proc (void *cls,
- const struct GNUNET_HashCode *key,
- uint32_t size,
- const void *data,
- enum GNUNET_BLOCK_Type type,
- uint32_t priority,
- uint32_t anonymity,
- uint32_t replication,
- struct GNUNET_TIME_Absolute expiration,
- uint64_t uid)
- {
- struct ExpiCtx *rc = cls;
- struct Plugin *plugin = rc->plugin;
- struct GNUNET_MY_QueryParam params_select[] = {
- GNUNET_MY_query_param_end
- };
- if (NULL == key)
- {
- execute_select (plugin,
- plugin->select_priority,
- rc->proc,
- rc->proc_cls,
- params_select);
- return GNUNET_SYSERR;
- }
- return rc->proc (rc->proc_cls,
- key,
- size,
- data,
- type,
- priority,
- anonymity,
- replication,
- expiration,
- uid);
- }
- /**
- * Get a random item for expiration.
- * Call @a proc with all values ZERO or NULL if the datastore is empty.
- *
- * @param cls closure
- * @param proc function to call the value (once only).
- * @param proc_cls closure for @a proc
- */
- static void
- mysql_plugin_get_expiration (void *cls,
- PluginDatumProcessor proc,
- void *proc_cls)
- {
- struct Plugin *plugin = cls;
- struct GNUNET_TIME_Absolute now;
- struct GNUNET_MY_QueryParam params_select[] = {
- GNUNET_MY_query_param_absolute_time (&now),
- GNUNET_MY_query_param_end
- };
- struct ExpiCtx rc;
- rc.plugin = plugin;
- rc.proc = proc;
- rc.proc_cls = proc_cls;
- now = GNUNET_TIME_absolute_get ();
- execute_select (plugin,
- plugin->select_expiration,
- expi_proc,
- &rc,
- params_select);
- }
- /**
- * Drop database.
- *
- * @param cls the `struct Plugin *`
- */
- static void
- mysql_plugin_drop (void *cls)
- {
- struct Plugin *plugin = cls;
- if (GNUNET_OK !=
- GNUNET_MYSQL_statement_run (plugin->mc,
- "DROP TABLE gn090"))
- return; /* error */
- plugin->env->duc (plugin->env->cls, 0);
- }
- /**
- * Remove a particular key in the datastore.
- *
- * @param cls closure
- * @param key key for the content
- * @param size number of bytes in data
- * @param data content stored
- * @param cont continuation called with success or failure status
- * @param cont_cls continuation closure for @a cont
- */
- static void
- mysql_plugin_remove_key (void *cls,
- const struct GNUNET_HashCode *key,
- uint32_t size,
- const void *data,
- PluginRemoveCont cont,
- void *cont_cls)
- {
- struct Plugin *plugin = cls;
- struct GNUNET_MY_QueryParam params_delete[] = {
- GNUNET_MY_query_param_auto_from_type (key),
- GNUNET_MY_query_param_fixed_size (data, size),
- GNUNET_MY_query_param_end
- };
- if (GNUNET_OK !=
- GNUNET_MY_exec_prepared (plugin->mc,
- plugin->delete_entry_by_hash_value,
- params_delete))
- {
- GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
- "Removing key `%s' from gn090 table failed\n",
- GNUNET_h2s (key));
- cont (cont_cls,
- key,
- size,
- GNUNET_SYSERR,
- _("MySQL statement run failure"));
- return;
- }
- MYSQL_STMT *stmt = GNUNET_MYSQL_statement_get_stmt (plugin->delete_entry_by_hash_value);
- my_ulonglong rows = mysql_stmt_affected_rows (stmt);
- if (0 == rows)
- {
- cont (cont_cls,
- key,
- size,
- GNUNET_NO,
- NULL);
- return;
- }
- plugin->env->duc (plugin->env->cls,
- -size);
- cont (cont_cls,
- key,
- size,
- GNUNET_OK,
- NULL);
- }
- /**
- * Entry point for the plugin.
- *
- * @param cls the `struct GNUNET_DATASTORE_PluginEnvironment *`
- * @return our `struct Plugin *`
- */
- void *
- libgnunet_plugin_datastore_mysql_init (void *cls)
- {
- struct GNUNET_DATASTORE_PluginEnvironment *env = cls;
- struct GNUNET_DATASTORE_PluginFunctions *api;
- struct Plugin *plugin;
- plugin = GNUNET_new (struct Plugin);
- plugin->env = env;
- plugin->mc = GNUNET_MYSQL_context_create (env->cfg,
- "datastore-mysql");
- if (NULL == plugin->mc)
- {
- GNUNET_free (plugin);
- return NULL;
- }
- #define MRUNS(a) (GNUNET_OK != GNUNET_MYSQL_statement_run (plugin->mc, a) )
- #define PINIT(a,b) (NULL == (a = GNUNET_MYSQL_statement_prepare (plugin->mc, b)))
- if (MRUNS
- ("CREATE TABLE IF NOT EXISTS gn090 ("
- " repl INT(11) UNSIGNED NOT NULL DEFAULT 0,"
- " type INT(11) UNSIGNED NOT NULL DEFAULT 0,"
- " prio INT(11) UNSIGNED NOT NULL DEFAULT 0,"
- " anonLevel INT(11) UNSIGNED NOT NULL DEFAULT 0,"
- " expire BIGINT UNSIGNED NOT NULL DEFAULT 0,"
- " rvalue BIGINT UNSIGNED NOT NULL,"
- " hash BINARY(64) NOT NULL DEFAULT '',"
- " vhash BINARY(64) NOT NULL DEFAULT '',"
- " value BLOB NOT NULL DEFAULT ''," " uid BIGINT NOT NULL AUTO_INCREMENT,"
- " PRIMARY KEY (uid),"
- " INDEX idx_hash_type_uid (hash(64),type,rvalue),"
- " INDEX idx_prio (prio),"
- " INDEX idx_repl_rvalue (repl,rvalue),"
- " INDEX idx_expire (expire),"
- " INDEX idx_anonLevel_type_rvalue (anonLevel,type,rvalue)"
- ") ENGINE=InnoDB") || MRUNS ("SET AUTOCOMMIT = 1") ||
- PINIT (plugin->insert_entry, INSERT_ENTRY) ||
- PINIT (plugin->delete_entry_by_uid, DELETE_ENTRY_BY_UID) ||
- PINIT (plugin->delete_entry_by_hash_value, DELETE_ENTRY_BY_HASH_VALUE) ||
- PINIT (plugin->select_entry, SELECT_ENTRY) ||
- PINIT (plugin->select_entry_by_hash, SELECT_ENTRY_BY_HASH) ||
- PINIT (plugin->select_entry_by_hash_and_type,
- SELECT_ENTRY_BY_HASH_AND_TYPE) ||
- PINIT (plugin->get_size, SELECT_SIZE) ||
- PINIT (plugin->update_entry, UPDATE_ENTRY) ||
- PINIT (plugin->dec_repl, DEC_REPL) ||
- PINIT (plugin->zero_iter, SELECT_IT_NON_ANONYMOUS) ||
- PINIT (plugin->select_expiration, SELECT_IT_EXPIRATION) ||
- PINIT (plugin->select_priority, SELECT_IT_PRIORITY) ||
- PINIT (plugin->max_repl, SELECT_MAX_REPL) ||
- PINIT (plugin->get_all_keys, GET_ALL_KEYS) ||
- PINIT (plugin->select_replication, SELECT_IT_REPLICATION) ||
- false)
- {
- GNUNET_MYSQL_context_destroy (plugin->mc);
- GNUNET_free (plugin);
- return NULL;
- }
- #undef PINIT
- #undef MRUNS
- api = GNUNET_new (struct GNUNET_DATASTORE_PluginFunctions);
- api->cls = plugin;
- api->estimate_size = &mysql_plugin_estimate_size;
- api->put = &mysql_plugin_put;
- api->get_key = &mysql_plugin_get_key;
- api->get_replication = &mysql_plugin_get_replication;
- api->get_expiration = &mysql_plugin_get_expiration;
- api->get_zero_anonymity = &mysql_plugin_get_zero_anonymity;
- api->get_keys = &mysql_plugin_get_keys;
- api->drop = &mysql_plugin_drop;
- api->remove_key = &mysql_plugin_remove_key;
- GNUNET_log_from (GNUNET_ERROR_TYPE_INFO, "mysql",
- _("Mysql database running\n"));
- return api;
- }
- /**
- * Exit point from the plugin.
- *
- * @param cls our `struct Plugin *`
- * @return always NULL
- */
- void *
- libgnunet_plugin_datastore_mysql_done (void *cls)
- {
- struct GNUNET_DATASTORE_PluginFunctions *api = cls;
- struct Plugin *plugin = api->cls;
- GNUNET_MYSQL_context_destroy (plugin->mc);
- GNUNET_free (plugin);
- GNUNET_free (api);
- return NULL;
- }
- /* end of plugin_datastore_mysql.c */
|