plugin_datastore_mysql.c 48 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531
  1. /*
  2. This file is part of GNUnet
  3. (C) 2009, 2010, 2011 Christian Grothoff (and other contributing authors)
  4. GNUnet is free software; you can redistribute it and/or modify
  5. it under the terms of the GNU General Public License as published
  6. by the Free Software Foundation; either version 3, or (at your
  7. option) any later version.
  8. GNUnet is distributed in the hope that it will be useful, but
  9. WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  11. General Public License for more details.
  12. You should have received a copy of the GNU General Public License
  13. along with GNUnet; see the file COPYING. If not, write to the
  14. Free Software Foundation, Inc., 59 Temple Place - Suite 330,
  15. Boston, MA 02111-1307, USA.
  16. */
  17. /**
  18. * @file datastore/plugin_datastore_mysql.c
  19. * @brief mysql-based datastore backend
  20. * @author Igor Wronsky
  21. * @author Christian Grothoff
  22. *
  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).
  31. *
  32. * HIGHLIGHTS
  33. *
  34. * Pros
  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!
  40. * Cons
  41. * - Memory usage (Comment: "I have 1G and it never caused me trouble")
  42. * - Manual setup
  43. *
  44. * MANUAL SETUP INSTRUCTIONS
  45. *
  46. * 1) in /etc/gnunet.conf, set
  47. * @verbatim
  48. [datastore]
  49. DATABASE = "mysql"
  50. @endverbatim
  51. * 2) Then access mysql as root,
  52. * @verbatim
  53. $ mysql -u root -p
  54. @endverbatim
  55. * and do the following. [You should replace $USER with the username
  56. * that will be running the gnunetd process].
  57. * @verbatim
  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');
  62. FLUSH PRIVILEGES;
  63. @endverbatim
  64. * 3) In the $HOME directory of $USER, create a ".my.cnf" file
  65. * with the following lines
  66. * @verbatim
  67. [client]
  68. user=$USER
  69. password=$the_password_you_like
  70. @endverbatim
  71. *
  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,
  77. * of course).<p>
  78. *
  79. * 4) Still, perhaps you should briefly try if the DB connection
  80. * works. First, login as $USER. Then use,
  81. *
  82. * @verbatim
  83. $ mysql -u $USER -p $the_password_you_like
  84. mysql> use gnunet;
  85. @endverbatim
  86. *
  87. * If you get the message &quot;Database changed&quot; it probably works.
  88. *
  89. * [If you get &quot;ERROR 2002: Can't connect to local MySQL server
  90. * through socket '/tmp/mysql.sock' (2)&quot; it may be resolvable by
  91. * &quot;ln -s /var/run/mysqld/mysqld.sock /tmp/mysql.sock&quot;
  92. * so there may be some additional trouble depending on your mysql setup.]
  93. *
  94. * REPAIRING TABLES
  95. *
  96. * - Its probably healthy to check your tables for inconsistencies
  97. * every now and then.
  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):
  103. * @verbatim
  104. mysql> REPAIR TABLE gn090;
  105. @endverbatim
  106. *
  107. * PROBLEMS?
  108. *
  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.
  113. */
  114. #include "platform.h"
  115. #include "gnunet_datastore_plugin.h"
  116. #include "gnunet_util_lib.h"
  117. #include <mysql/mysql.h>
  118. #define DEBUG_MYSQL GNUNET_NO
  119. #define MAX_DATUM_SIZE 65536
  120. /**
  121. * Maximum number of supported parameters for a prepared
  122. * statement. Increase if needed.
  123. */
  124. #define MAX_PARAM 16
  125. /**
  126. * Die with an error message that indicates
  127. * a failure of the command 'cmd' with the message given
  128. * by strerror(errno).
  129. */
  130. #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);
  131. /**
  132. * Log an error message at log-level 'level' that indicates
  133. * a failure of the command 'cmd' on file 'filename'
  134. * with the message given by strerror(errno).
  135. */
  136. #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);
  137. struct GNUNET_MysqlStatementHandle
  138. {
  139. struct GNUNET_MysqlStatementHandle *next;
  140. struct GNUNET_MysqlStatementHandle *prev;
  141. char *query;
  142. MYSQL_STMT *statement;
  143. int valid;
  144. };
  145. /**
  146. * Context for all functions in this plugin.
  147. */
  148. struct Plugin
  149. {
  150. /**
  151. * Our execution environment.
  152. */
  153. struct GNUNET_DATASTORE_PluginEnvironment *env;
  154. /**
  155. * Handle to talk to MySQL.
  156. */
  157. MYSQL *dbf;
  158. /**
  159. * We keep all prepared statements in a DLL. This is the head.
  160. */
  161. struct GNUNET_MysqlStatementHandle *shead;
  162. /**
  163. * We keep all prepared statements in a DLL. This is the tail.
  164. */
  165. struct GNUNET_MysqlStatementHandle *stail;
  166. /**
  167. * Filename of "my.cnf" (msyql configuration).
  168. */
  169. char *cnffile;
  170. /**
  171. * Prepared statements.
  172. */
  173. #define INSERT_ENTRY "INSERT INTO gn090 (repl,type,prio,anonLevel,expire,rvalue,hash,vhash,value) VALUES (?,?,?,?,?,?,?,?,?)"
  174. struct GNUNET_MysqlStatementHandle *insert_entry;
  175. #define DELETE_ENTRY_BY_UID "DELETE FROM gn090 WHERE uid=?"
  176. struct GNUNET_MysqlStatementHandle *delete_entry_by_uid;
  177. #define COUNT_ENTRY_BY_HASH "SELECT count(*) FROM gn090 FORCE INDEX (idx_hash) WHERE hash=?"
  178. struct GNUNET_MysqlStatementHandle *count_entry_by_hash;
  179. #define SELECT_ENTRY_BY_HASH "SELECT type,prio,anonLevel,expire,hash,value,uid FROM gn090 FORCE INDEX (idx_hash) WHERE hash=? ORDER BY uid LIMIT 1 OFFSET ?"
  180. struct GNUNET_MysqlStatementHandle *select_entry_by_hash;
  181. #define COUNT_ENTRY_BY_HASH_AND_VHASH "SELECT count(*) FROM gn090 FORCE INDEX (idx_hash_vhash) WHERE hash=? AND vhash=?"
  182. struct GNUNET_MysqlStatementHandle *count_entry_by_hash_and_vhash;
  183. #define SELECT_ENTRY_BY_HASH_AND_VHASH "SELECT type,prio,anonLevel,expire,hash,value,uid FROM gn090 FORCE INDEX (idx_hash_vhash) WHERE hash=? AND vhash=? ORDER BY uid LIMIT 1 OFFSET ?"
  184. struct GNUNET_MysqlStatementHandle *select_entry_by_hash_and_vhash;
  185. #define COUNT_ENTRY_BY_HASH_AND_TYPE "SELECT count(*) FROM gn090 FORCE INDEX (idx_hash_type_uid) WHERE hash=? AND type=?"
  186. struct GNUNET_MysqlStatementHandle *count_entry_by_hash_and_type;
  187. #define SELECT_ENTRY_BY_HASH_AND_TYPE "SELECT type,prio,anonLevel,expire,hash,value,uid FROM gn090 FORCE INDEX (idx_hash_type_uid) WHERE hash=? AND type=? ORDER BY uid LIMIT 1 OFFSET ?"
  188. struct GNUNET_MysqlStatementHandle *select_entry_by_hash_and_type;
  189. #define COUNT_ENTRY_BY_HASH_VHASH_AND_TYPE "SELECT count(*) FROM gn090 FORCE INDEX (idx_hash_vhash) WHERE hash=? AND vhash=? AND type=?"
  190. struct GNUNET_MysqlStatementHandle *count_entry_by_hash_vhash_and_type;
  191. #define SELECT_ENTRY_BY_HASH_VHASH_AND_TYPE "SELECT type,prio,anonLevel,expire,hash,value,uid FROM gn090 FORCE INDEX (idx_hash_vhash) WHERE hash=? AND vhash=? AND type=? ORDER BY uid ASC LIMIT 1 OFFSET ?"
  192. struct GNUNET_MysqlStatementHandle *select_entry_by_hash_vhash_and_type;
  193. #define UPDATE_ENTRY "UPDATE gn090 SET prio=prio+?,expire=IF(expire>=?,expire,?) WHERE uid=?"
  194. struct GNUNET_MysqlStatementHandle *update_entry;
  195. #define DEC_REPL "UPDATE gn090 SET repl=GREATEST (0, repl - 1) WHERE uid=?"
  196. struct GNUNET_MysqlStatementHandle *dec_repl;
  197. #define SELECT_SIZE "SELECT SUM(BIT_LENGTH(value) DIV 8) FROM gn090"
  198. struct GNUNET_MysqlStatementHandle *get_size;
  199. #define SELECT_IT_NON_ANONYMOUS "SELECT type,prio,anonLevel,expire,hash,value,uid "\
  200. "FROM gn090 FORCE INDEX (idx_anonLevel_type_rvalue) "\
  201. "WHERE anonLevel=0 AND type=? AND "\
  202. "(rvalue >= ? OR"\
  203. " NOT EXISTS (SELECT 1 FROM gn090 FORCE INDEX (idx_anonLevel_type_rvalue) WHERE anonLevel=0 AND type=? AND rvalue>=?)) "\
  204. "ORDER BY rvalue ASC LIMIT 1"
  205. struct GNUNET_MysqlStatementHandle *zero_iter;
  206. #define SELECT_IT_EXPIRATION "SELECT type,prio,anonLevel,expire,hash,value,uid FROM gn090 FORCE INDEX (idx_expire) WHERE expire < ? ORDER BY expire ASC LIMIT 1"
  207. struct GNUNET_MysqlStatementHandle *select_expiration;
  208. #define SELECT_IT_PRIORITY "SELECT type,prio,anonLevel,expire,hash,value,uid FROM gn090 FORCE INDEX (idx_prio) ORDER BY prio ASC LIMIT 1"
  209. struct GNUNET_MysqlStatementHandle *select_priority;
  210. #define SELECT_IT_REPLICATION "SELECT type,prio,anonLevel,expire,hash,value,uid "\
  211. "FROM gn090 FORCE INDEX (idx_repl_rvalue) "\
  212. "WHERE repl=? AND "\
  213. " (rvalue>=? OR"\
  214. " NOT EXISTS (SELECT 1 FROM gn090 FORCE INDEX (idx_repl_rvalue) WHERE repl=? AND rvalue>=?)) "\
  215. "ORDER BY rvalue ASC "\
  216. "LIMIT 1"
  217. struct GNUNET_MysqlStatementHandle *select_replication;
  218. #define SELECT_MAX_REPL "SELECT MAX(repl) FROM gn090"
  219. struct GNUNET_MysqlStatementHandle *max_repl;
  220. };
  221. /**
  222. * Obtain the location of ".my.cnf".
  223. *
  224. * @param cfg our configuration
  225. * @return NULL on error
  226. */
  227. static char *
  228. get_my_cnf_path (const struct GNUNET_CONFIGURATION_Handle *cfg)
  229. {
  230. char *cnffile;
  231. char *home_dir;
  232. struct stat st;
  233. #ifndef WINDOWS
  234. struct passwd *pw;
  235. #endif
  236. int configured;
  237. #ifndef WINDOWS
  238. pw = getpwuid (getuid ());
  239. if (!pw)
  240. {
  241. GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "getpwuid");
  242. return NULL;
  243. }
  244. if (GNUNET_YES ==
  245. GNUNET_CONFIGURATION_have_value (cfg, "datastore-mysql", "CONFIG"))
  246. {
  247. GNUNET_assert (GNUNET_OK ==
  248. GNUNET_CONFIGURATION_get_value_filename (cfg,
  249. "datastore-mysql",
  250. "CONFIG",
  251. &cnffile));
  252. configured = GNUNET_YES;
  253. }
  254. else
  255. {
  256. home_dir = GNUNET_strdup (pw->pw_dir);
  257. GNUNET_asprintf (&cnffile, "%s/.my.cnf", home_dir);
  258. GNUNET_free (home_dir);
  259. configured = GNUNET_NO;
  260. }
  261. #else
  262. home_dir = (char *) GNUNET_malloc (_MAX_PATH + 1);
  263. plibc_conv_to_win_path ("~/", home_dir);
  264. GNUNET_asprintf (&cnffile, "%s/.my.cnf", home_dir);
  265. GNUNET_free (home_dir);
  266. configured = GNUNET_NO;
  267. #endif
  268. GNUNET_log (GNUNET_ERROR_TYPE_INFO,
  269. _("Trying to use file `%s' for MySQL configuration.\n"), cnffile);
  270. if ((0 != STAT (cnffile, &st)) || (0 != ACCESS (cnffile, R_OK)) ||
  271. (!S_ISREG (st.st_mode)))
  272. {
  273. if (configured == GNUNET_YES)
  274. GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
  275. _("Could not access file `%s': %s\n"), cnffile,
  276. STRERROR (errno));
  277. GNUNET_free (cnffile);
  278. return NULL;
  279. }
  280. return cnffile;
  281. }
  282. /**
  283. * Close database connection and all prepared statements (we got a DB
  284. * disconnect error).
  285. *
  286. * @param plugin plugin context
  287. */
  288. static int
  289. iclose (struct Plugin *plugin)
  290. {
  291. struct GNUNET_MysqlStatementHandle *s;
  292. for (s = plugin->shead; s != NULL; s = s->next)
  293. {
  294. if (s->valid)
  295. {
  296. mysql_stmt_close (s->statement);
  297. s->valid = GNUNET_NO;
  298. }
  299. }
  300. if (plugin->dbf != NULL)
  301. {
  302. mysql_close (plugin->dbf);
  303. plugin->dbf = NULL;
  304. }
  305. return GNUNET_OK;
  306. }
  307. /**
  308. * Open the connection with the database (and initialize
  309. * our default options).
  310. *
  311. * @param plugin plugin context
  312. * @return GNUNET_OK on success
  313. */
  314. static int
  315. iopen (struct Plugin *plugin)
  316. {
  317. char *mysql_dbname;
  318. char *mysql_server;
  319. char *mysql_user;
  320. char *mysql_password;
  321. unsigned long long mysql_port;
  322. my_bool reconnect;
  323. unsigned int timeout;
  324. plugin->dbf = mysql_init (NULL);
  325. if (plugin->dbf == NULL)
  326. return GNUNET_SYSERR;
  327. if (plugin->cnffile != NULL)
  328. mysql_options (plugin->dbf, MYSQL_READ_DEFAULT_FILE, plugin->cnffile);
  329. mysql_options (plugin->dbf, MYSQL_READ_DEFAULT_GROUP, "client");
  330. reconnect = 0;
  331. mysql_options (plugin->dbf, MYSQL_OPT_RECONNECT, &reconnect);
  332. timeout = 120; /* in seconds */
  333. mysql_options (plugin->dbf, MYSQL_OPT_CONNECT_TIMEOUT,
  334. (const void *) &timeout);
  335. mysql_options (plugin->dbf, MYSQL_SET_CHARSET_NAME, "UTF8");
  336. timeout = 60; /* in seconds */
  337. mysql_options (plugin->dbf, MYSQL_OPT_READ_TIMEOUT, (const void *) &timeout);
  338. mysql_options (plugin->dbf, MYSQL_OPT_WRITE_TIMEOUT, (const void *) &timeout);
  339. mysql_dbname = NULL;
  340. if (GNUNET_YES ==
  341. GNUNET_CONFIGURATION_have_value (plugin->env->cfg, "datastore-mysql",
  342. "DATABASE"))
  343. GNUNET_assert (GNUNET_OK ==
  344. GNUNET_CONFIGURATION_get_value_string (plugin->env->cfg,
  345. "datastore-mysql",
  346. "DATABASE",
  347. &mysql_dbname));
  348. else
  349. mysql_dbname = GNUNET_strdup ("gnunet");
  350. mysql_user = NULL;
  351. if (GNUNET_YES ==
  352. GNUNET_CONFIGURATION_have_value (plugin->env->cfg, "datastore-mysql",
  353. "USER"))
  354. {
  355. GNUNET_assert (GNUNET_OK ==
  356. GNUNET_CONFIGURATION_get_value_string (plugin->env->cfg,
  357. "datastore-mysql",
  358. "USER", &mysql_user));
  359. }
  360. mysql_password = NULL;
  361. if (GNUNET_YES ==
  362. GNUNET_CONFIGURATION_have_value (plugin->env->cfg, "datastore-mysql",
  363. "PASSWORD"))
  364. {
  365. GNUNET_assert (GNUNET_OK ==
  366. GNUNET_CONFIGURATION_get_value_string (plugin->env->cfg,
  367. "datastore-mysql",
  368. "PASSWORD",
  369. &mysql_password));
  370. }
  371. mysql_server = NULL;
  372. if (GNUNET_YES ==
  373. GNUNET_CONFIGURATION_have_value (plugin->env->cfg, "datastore-mysql",
  374. "HOST"))
  375. {
  376. GNUNET_assert (GNUNET_OK ==
  377. GNUNET_CONFIGURATION_get_value_string (plugin->env->cfg,
  378. "datastore-mysql",
  379. "HOST",
  380. &mysql_server));
  381. }
  382. mysql_port = 0;
  383. if (GNUNET_YES ==
  384. GNUNET_CONFIGURATION_have_value (plugin->env->cfg, "datastore-mysql",
  385. "PORT"))
  386. {
  387. GNUNET_assert (GNUNET_OK ==
  388. GNUNET_CONFIGURATION_get_value_number (plugin->env->cfg,
  389. "datastore-mysql",
  390. "PORT", &mysql_port));
  391. }
  392. GNUNET_assert (mysql_dbname != NULL);
  393. mysql_real_connect (plugin->dbf, mysql_server, mysql_user, mysql_password,
  394. mysql_dbname, (unsigned int) mysql_port, NULL,
  395. CLIENT_IGNORE_SIGPIPE);
  396. GNUNET_free_non_null (mysql_server);
  397. GNUNET_free_non_null (mysql_user);
  398. GNUNET_free_non_null (mysql_password);
  399. GNUNET_free (mysql_dbname);
  400. if (mysql_error (plugin->dbf)[0])
  401. {
  402. LOG_MYSQL (GNUNET_ERROR_TYPE_ERROR, "mysql_real_connect", plugin);
  403. return GNUNET_SYSERR;
  404. }
  405. return GNUNET_OK;
  406. }
  407. /**
  408. * Run the given MySQL statement.
  409. *
  410. * @param plugin plugin context
  411. * @param statement SQL statement to run
  412. * @return GNUNET_OK on success, GNUNET_SYSERR on error
  413. */
  414. static int
  415. run_statement (struct Plugin *plugin, const char *statement)
  416. {
  417. if ((NULL == plugin->dbf) && (GNUNET_OK != iopen (plugin)))
  418. return GNUNET_SYSERR;
  419. mysql_query (plugin->dbf, statement);
  420. if (mysql_error (plugin->dbf)[0])
  421. {
  422. LOG_MYSQL (GNUNET_ERROR_TYPE_ERROR, "mysql_query", plugin);
  423. iclose (plugin);
  424. return GNUNET_SYSERR;
  425. }
  426. return GNUNET_OK;
  427. }
  428. /**
  429. * Create a prepared statement.
  430. *
  431. * @param plugin plugin context
  432. * @param statement SQL statement text to prepare
  433. * @return NULL on error
  434. */
  435. static struct GNUNET_MysqlStatementHandle *
  436. prepared_statement_create (struct Plugin *plugin, const char *statement)
  437. {
  438. struct GNUNET_MysqlStatementHandle *ret;
  439. ret = GNUNET_malloc (sizeof (struct GNUNET_MysqlStatementHandle));
  440. ret->query = GNUNET_strdup (statement);
  441. GNUNET_CONTAINER_DLL_insert (plugin->shead, plugin->stail, ret);
  442. return ret;
  443. }
  444. /**
  445. * Prepare a statement for running.
  446. *
  447. * @param plugin plugin context
  448. * @param ret handle to prepared statement
  449. * @return GNUNET_OK on success
  450. */
  451. static int
  452. prepare_statement (struct Plugin *plugin,
  453. struct GNUNET_MysqlStatementHandle *ret)
  454. {
  455. if (GNUNET_YES == ret->valid)
  456. return GNUNET_OK;
  457. if ((NULL == plugin->dbf) && (GNUNET_OK != iopen (plugin)))
  458. return GNUNET_SYSERR;
  459. ret->statement = mysql_stmt_init (plugin->dbf);
  460. if (ret->statement == NULL)
  461. {
  462. iclose (plugin);
  463. return GNUNET_SYSERR;
  464. }
  465. if (mysql_stmt_prepare (ret->statement, ret->query, strlen (ret->query)))
  466. {
  467. GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, "mysql",
  468. _("Failed to prepare statement `%s'\n"), ret->query);
  469. LOG_MYSQL (GNUNET_ERROR_TYPE_ERROR, "mysql_stmt_prepare", plugin);
  470. mysql_stmt_close (ret->statement);
  471. ret->statement = NULL;
  472. iclose (plugin);
  473. return GNUNET_SYSERR;
  474. }
  475. ret->valid = GNUNET_YES;
  476. return GNUNET_OK;
  477. }
  478. /**
  479. * Bind the parameters for the given MySQL statement
  480. * and run it.
  481. *
  482. * @param plugin plugin context
  483. * @param s statement to bind and run
  484. * @param ap arguments for the binding
  485. * @return GNUNET_SYSERR on error, GNUNET_OK on success
  486. */
  487. static int
  488. init_params (struct Plugin *plugin, struct GNUNET_MysqlStatementHandle *s,
  489. va_list ap)
  490. {
  491. MYSQL_BIND qbind[MAX_PARAM];
  492. unsigned int pc;
  493. unsigned int off;
  494. enum enum_field_types ft;
  495. pc = mysql_stmt_param_count (s->statement);
  496. if (pc > MAX_PARAM)
  497. {
  498. /* increase internal constant! */
  499. GNUNET_break (0);
  500. return GNUNET_SYSERR;
  501. }
  502. memset (qbind, 0, sizeof (qbind));
  503. off = 0;
  504. ft = 0;
  505. while ((pc > 0) && (-1 != (int) (ft = va_arg (ap, enum enum_field_types))))
  506. {
  507. qbind[off].buffer_type = ft;
  508. switch (ft)
  509. {
  510. case MYSQL_TYPE_FLOAT:
  511. qbind[off].buffer = va_arg (ap, float *);
  512. break;
  513. case MYSQL_TYPE_LONGLONG:
  514. qbind[off].buffer = va_arg (ap, unsigned long long *);
  515. qbind[off].is_unsigned = va_arg (ap, int);
  516. break;
  517. case MYSQL_TYPE_LONG:
  518. qbind[off].buffer = va_arg (ap, unsigned int *);
  519. qbind[off].is_unsigned = va_arg (ap, int);
  520. break;
  521. case MYSQL_TYPE_VAR_STRING:
  522. case MYSQL_TYPE_STRING:
  523. case MYSQL_TYPE_BLOB:
  524. qbind[off].buffer = va_arg (ap, void *);
  525. qbind[off].buffer_length = va_arg (ap, unsigned long);
  526. qbind[off].length = va_arg (ap, unsigned long *);
  527. break;
  528. default:
  529. /* unsupported type */
  530. GNUNET_break (0);
  531. return GNUNET_SYSERR;
  532. }
  533. pc--;
  534. off++;
  535. }
  536. if (!((pc == 0) && (-1 != (int) ft) && (va_arg (ap, int) == -1)))
  537. {
  538. GNUNET_assert (0);
  539. return GNUNET_SYSERR;
  540. }
  541. if (mysql_stmt_bind_param (s->statement, qbind))
  542. {
  543. GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
  544. _("`%s' failed at %s:%d with error: %s\n"),
  545. "mysql_stmt_bind_param", __FILE__, __LINE__,
  546. mysql_stmt_error (s->statement));
  547. iclose (plugin);
  548. return GNUNET_SYSERR;
  549. }
  550. if (mysql_stmt_execute (s->statement))
  551. {
  552. GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
  553. _("`%s' for `%s' failed at %s:%d with error: %s\n"),
  554. "mysql_stmt_execute", s->query, __FILE__, __LINE__,
  555. mysql_stmt_error (s->statement));
  556. iclose (plugin);
  557. return GNUNET_SYSERR;
  558. }
  559. return GNUNET_OK;
  560. }
  561. /**
  562. * Run a prepared SELECT statement.
  563. *
  564. * @param plugin plugin context
  565. * @param s statement to run
  566. * @param result_size number of elements in results array
  567. * @param results pointer to already initialized MYSQL_BIND
  568. * array (of sufficient size) for passing results
  569. * @param ap pairs and triplets of "MYSQL_TYPE_XXX" keys and their respective
  570. * values (size + buffer-reference for pointers); terminated
  571. * with "-1"
  572. * @return GNUNET_SYSERR on error, otherwise GNUNET_OK or GNUNET_NO (no result)
  573. */
  574. static int
  575. prepared_statement_run_select_va (struct Plugin *plugin,
  576. struct GNUNET_MysqlStatementHandle *s,
  577. unsigned int result_size,
  578. MYSQL_BIND * results, va_list ap)
  579. {
  580. int ret;
  581. unsigned int rsize;
  582. if (GNUNET_OK != prepare_statement (plugin, s))
  583. {
  584. GNUNET_break (0);
  585. return GNUNET_SYSERR;
  586. }
  587. if (GNUNET_OK != init_params (plugin, s, ap))
  588. {
  589. GNUNET_break (0);
  590. return GNUNET_SYSERR;
  591. }
  592. rsize = mysql_stmt_field_count (s->statement);
  593. if (rsize > result_size)
  594. {
  595. GNUNET_break (0);
  596. return GNUNET_SYSERR;
  597. }
  598. if (mysql_stmt_bind_result (s->statement, results))
  599. {
  600. GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
  601. _("`%s' failed at %s:%d with error: %s\n"),
  602. "mysql_stmt_bind_result", __FILE__, __LINE__,
  603. mysql_stmt_error (s->statement));
  604. iclose (plugin);
  605. return GNUNET_SYSERR;
  606. }
  607. ret = mysql_stmt_fetch (s->statement);
  608. if (ret == MYSQL_NO_DATA)
  609. return GNUNET_NO;
  610. if (ret != 0)
  611. {
  612. GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
  613. _("`%s' failed at %s:%d with error: %s\n"), "mysql_stmt_fetch",
  614. __FILE__, __LINE__, mysql_stmt_error (s->statement));
  615. iclose (plugin);
  616. return GNUNET_SYSERR;
  617. }
  618. mysql_stmt_reset (s->statement);
  619. return GNUNET_OK;
  620. }
  621. /**
  622. * Run a prepared SELECT statement.
  623. *
  624. * @param plugin plugin context
  625. * @param s statement to run
  626. * @param result_size number of elements in results array
  627. * @param results pointer to already initialized MYSQL_BIND
  628. * array (of sufficient size) for passing results
  629. * @param ... pairs and triplets of "MYSQL_TYPE_XXX" keys and their respective
  630. * values (size + buffer-reference for pointers); terminated
  631. * with "-1"
  632. * @return GNUNET_SYSERR on error, otherwise
  633. * the number of successfully affected (or queried) rows
  634. */
  635. static int
  636. prepared_statement_run_select (struct Plugin *plugin,
  637. struct GNUNET_MysqlStatementHandle *s,
  638. unsigned int result_size, MYSQL_BIND * results,
  639. ...)
  640. {
  641. va_list ap;
  642. int ret;
  643. va_start (ap, results);
  644. ret = prepared_statement_run_select_va (plugin, s, result_size, results, ap);
  645. va_end (ap);
  646. return ret;
  647. }
  648. /**
  649. * Run a prepared statement that does NOT produce results.
  650. *
  651. * @param plugin plugin context
  652. * @param s statement to run
  653. * @param insert_id NULL or address where to store the row ID of whatever
  654. * was inserted (only for INSERT statements!)
  655. * @param ... pairs and triplets of "MYSQL_TYPE_XXX" keys and their respective
  656. * values (size + buffer-reference for pointers); terminated
  657. * with "-1"
  658. * @return GNUNET_SYSERR on error, otherwise
  659. * the number of successfully affected rows
  660. */
  661. static int
  662. prepared_statement_run (struct Plugin *plugin,
  663. struct GNUNET_MysqlStatementHandle *s,
  664. unsigned long long *insert_id, ...)
  665. {
  666. va_list ap;
  667. int affected;
  668. if (GNUNET_OK != prepare_statement (plugin, s))
  669. return GNUNET_SYSERR;
  670. va_start (ap, insert_id);
  671. if (GNUNET_OK != init_params (plugin, s, ap))
  672. {
  673. va_end (ap);
  674. return GNUNET_SYSERR;
  675. }
  676. va_end (ap);
  677. affected = mysql_stmt_affected_rows (s->statement);
  678. if (NULL != insert_id)
  679. *insert_id = (unsigned long long) mysql_stmt_insert_id (s->statement);
  680. mysql_stmt_reset (s->statement);
  681. return affected;
  682. }
  683. /**
  684. * Delete an entry from the gn090 table.
  685. *
  686. * @param plugin plugin context
  687. * @param uid unique ID of the entry to delete
  688. * @return GNUNET_OK on success, GNUNET_NO if no such value exists, GNUNET_SYSERR on error
  689. */
  690. static int
  691. do_delete_entry (struct Plugin *plugin, unsigned long long uid)
  692. {
  693. int ret;
  694. #if DEBUG_MYSQL
  695. GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Deleting value %llu from gn090 table\n",
  696. uid);
  697. #endif
  698. ret =
  699. prepared_statement_run (plugin, plugin->delete_entry_by_uid, NULL,
  700. MYSQL_TYPE_LONGLONG, &uid, GNUNET_YES, -1);
  701. if (ret >= 0)
  702. return GNUNET_OK;
  703. GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
  704. "Deleting value %llu from gn090 table failed\n", uid);
  705. return ret;
  706. }
  707. /**
  708. * Get an estimate of how much space the database is
  709. * currently using.
  710. *
  711. * @param cls our "struct Plugin *"
  712. * @return number of bytes used on disk
  713. */
  714. static unsigned long long
  715. mysql_plugin_estimate_size (void *cls)
  716. {
  717. struct Plugin *plugin = cls;
  718. MYSQL_BIND cbind[1];
  719. long long total;
  720. memset (cbind, 0, sizeof (cbind));
  721. total = 0;
  722. cbind[0].buffer_type = MYSQL_TYPE_LONGLONG;
  723. cbind[0].buffer = &total;
  724. cbind[0].is_unsigned = GNUNET_NO;
  725. if (GNUNET_OK !=
  726. prepared_statement_run_select (plugin, plugin->get_size, 1, cbind, -1))
  727. return 0;
  728. return total;
  729. }
  730. /**
  731. * Store an item in the datastore.
  732. *
  733. * @param cls closure
  734. * @param key key for the item
  735. * @param size number of bytes in data
  736. * @param data content stored
  737. * @param type type of the content
  738. * @param priority priority of the content
  739. * @param anonymity anonymity-level for the content
  740. * @param replication replication-level for the content
  741. * @param expiration expiration time for the content
  742. * @param msg set to error message
  743. * @return GNUNET_OK on success
  744. */
  745. static int
  746. mysql_plugin_put (void *cls, const GNUNET_HashCode * key, uint32_t size,
  747. const void *data, enum GNUNET_BLOCK_Type type,
  748. uint32_t priority, uint32_t anonymity, uint32_t replication,
  749. struct GNUNET_TIME_Absolute expiration, char **msg)
  750. {
  751. struct Plugin *plugin = cls;
  752. unsigned int irepl = replication;
  753. unsigned int ipriority = priority;
  754. unsigned int ianonymity = anonymity;
  755. unsigned long long lexpiration = expiration.abs_value;
  756. unsigned long long lrvalue =
  757. (unsigned long long) GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK,
  758. UINT64_MAX);
  759. unsigned long hashSize;
  760. unsigned long hashSize2;
  761. unsigned long lsize;
  762. GNUNET_HashCode vhash;
  763. if (size > MAX_DATUM_SIZE)
  764. {
  765. GNUNET_break (0);
  766. return GNUNET_SYSERR;
  767. }
  768. hashSize = sizeof (GNUNET_HashCode);
  769. hashSize2 = sizeof (GNUNET_HashCode);
  770. lsize = size;
  771. GNUNET_CRYPTO_hash (data, size, &vhash);
  772. if (GNUNET_OK !=
  773. prepared_statement_run (plugin, plugin->insert_entry, NULL,
  774. MYSQL_TYPE_LONG, &irepl, GNUNET_YES,
  775. MYSQL_TYPE_LONG, &type, GNUNET_YES,
  776. MYSQL_TYPE_LONG, &ipriority, GNUNET_YES,
  777. MYSQL_TYPE_LONG, &ianonymity, GNUNET_YES,
  778. MYSQL_TYPE_LONGLONG, &lexpiration, GNUNET_YES,
  779. MYSQL_TYPE_LONGLONG, &lrvalue, GNUNET_YES,
  780. MYSQL_TYPE_BLOB, key, hashSize, &hashSize,
  781. MYSQL_TYPE_BLOB, &vhash, hashSize2, &hashSize2,
  782. MYSQL_TYPE_BLOB, data, lsize, &lsize, -1))
  783. return GNUNET_SYSERR;
  784. #if DEBUG_MYSQL
  785. GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
  786. "Inserted value `%s' with size %u into gn090 table\n",
  787. GNUNET_h2s (key), (unsigned int) size);
  788. #endif
  789. if (size > 0)
  790. plugin->env->duc (plugin->env->cls, size);
  791. return GNUNET_OK;
  792. }
  793. /**
  794. * Update the priority for a particular key in the datastore. If
  795. * the expiration time in value is different than the time found in
  796. * the datastore, the higher value should be kept. For the
  797. * anonymity level, the lower value is to be used. The specified
  798. * priority should be added to the existing priority, ignoring the
  799. * priority in value.
  800. *
  801. * Note that it is possible for multiple values to match this put.
  802. * In that case, all of the respective values are updated.
  803. *
  804. * @param cls our "struct Plugin*"
  805. * @param uid unique identifier of the datum
  806. * @param delta by how much should the priority
  807. * change? If priority + delta < 0 the
  808. * priority should be set to 0 (never go
  809. * negative).
  810. * @param expire new expiration time should be the
  811. * MAX of any existing expiration time and
  812. * this value
  813. * @param msg set to error message
  814. * @return GNUNET_OK on success
  815. */
  816. static int
  817. mysql_plugin_update (void *cls, uint64_t uid, int delta,
  818. struct GNUNET_TIME_Absolute expire, char **msg)
  819. {
  820. struct Plugin *plugin = cls;
  821. unsigned long long vkey = uid;
  822. unsigned long long lexpire = expire.abs_value;
  823. int ret;
  824. #if DEBUG_MYSQL
  825. GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
  826. "Updating value %llu adding %d to priority and maxing exp at %llu\n",
  827. vkey, delta, lexpire);
  828. #endif
  829. ret =
  830. prepared_statement_run (plugin, plugin->update_entry, NULL,
  831. MYSQL_TYPE_LONG, &delta, GNUNET_NO,
  832. MYSQL_TYPE_LONGLONG, &lexpire, GNUNET_YES,
  833. MYSQL_TYPE_LONGLONG, &lexpire, GNUNET_YES,
  834. MYSQL_TYPE_LONGLONG, &vkey, GNUNET_YES, -1);
  835. if (ret != GNUNET_OK)
  836. {
  837. GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Failed to update value %llu\n",
  838. vkey);
  839. }
  840. return ret;
  841. }
  842. /**
  843. * Run the given select statement and call 'proc' on the resulting
  844. * values (which must be in particular positions).
  845. *
  846. * @param plugin the plugin handle
  847. * @param stmt select statement to run
  848. * @param proc function to call on result
  849. * @param proc_cls closure for proc
  850. * @param ... arguments to initialize stmt
  851. */
  852. static void
  853. execute_select (struct Plugin *plugin, struct GNUNET_MysqlStatementHandle *stmt,
  854. PluginDatumProcessor proc, void *proc_cls, ...)
  855. {
  856. va_list ap;
  857. int ret;
  858. unsigned int type;
  859. unsigned int priority;
  860. unsigned int anonymity;
  861. unsigned long long exp;
  862. unsigned long hashSize;
  863. unsigned long size;
  864. unsigned long long uid;
  865. char value[GNUNET_DATASTORE_MAX_VALUE_SIZE];
  866. GNUNET_HashCode key;
  867. struct GNUNET_TIME_Absolute expiration;
  868. MYSQL_BIND rbind[7];
  869. hashSize = sizeof (GNUNET_HashCode);
  870. memset (rbind, 0, sizeof (rbind));
  871. rbind[0].buffer_type = MYSQL_TYPE_LONG;
  872. rbind[0].buffer = &type;
  873. rbind[0].is_unsigned = 1;
  874. rbind[1].buffer_type = MYSQL_TYPE_LONG;
  875. rbind[1].buffer = &priority;
  876. rbind[1].is_unsigned = 1;
  877. rbind[2].buffer_type = MYSQL_TYPE_LONG;
  878. rbind[2].buffer = &anonymity;
  879. rbind[2].is_unsigned = 1;
  880. rbind[3].buffer_type = MYSQL_TYPE_LONGLONG;
  881. rbind[3].buffer = &exp;
  882. rbind[3].is_unsigned = 1;
  883. rbind[4].buffer_type = MYSQL_TYPE_BLOB;
  884. rbind[4].buffer = &key;
  885. rbind[4].buffer_length = hashSize;
  886. rbind[4].length = &hashSize;
  887. rbind[5].buffer_type = MYSQL_TYPE_BLOB;
  888. rbind[5].buffer = value;
  889. rbind[5].buffer_length = size = sizeof (value);
  890. rbind[5].length = &size;
  891. rbind[6].buffer_type = MYSQL_TYPE_LONGLONG;
  892. rbind[6].buffer = &uid;
  893. rbind[6].is_unsigned = 1;
  894. va_start (ap, proc_cls);
  895. ret = prepared_statement_run_select_va (plugin, stmt, 7, rbind, ap);
  896. va_end (ap);
  897. if (ret <= 0)
  898. {
  899. proc (proc_cls, NULL, 0, NULL, 0, 0, 0, GNUNET_TIME_UNIT_ZERO_ABS, 0);
  900. return;
  901. }
  902. GNUNET_assert (size <= sizeof (value));
  903. if ((rbind[4].buffer_length != sizeof (GNUNET_HashCode)) ||
  904. (hashSize != sizeof (GNUNET_HashCode)))
  905. {
  906. GNUNET_break (0);
  907. proc (proc_cls, NULL, 0, NULL, 0, 0, 0, GNUNET_TIME_UNIT_ZERO_ABS, 0);
  908. return;
  909. }
  910. #if DEBUG_MYSQL
  911. GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
  912. "Found %u-byte value under key `%s' with prio %u, anon %u, expire %llu selecting from gn090 table\n",
  913. (unsigned int) size, GNUNET_h2s (&key), priority, anonymity, exp);
  914. #endif
  915. GNUNET_assert (size < MAX_DATUM_SIZE);
  916. expiration.abs_value = exp;
  917. ret =
  918. proc (proc_cls, &key, size, value, type, priority, anonymity, expiration,
  919. uid);
  920. if (ret == GNUNET_NO)
  921. {
  922. do_delete_entry (plugin, uid);
  923. if (size != 0)
  924. plugin->env->duc (plugin->env->cls, -size);
  925. }
  926. }
  927. /**
  928. * Get one of the results for a particular key in the datastore.
  929. *
  930. * @param cls closure
  931. * @param offset offset of the result (modulo num-results);
  932. * specific ordering does not matter for the offset
  933. * @param key key to match, never NULL
  934. * @param vhash hash of the value, maybe NULL (to
  935. * match all values that have the right key).
  936. * Note that for DBlocks there is no difference
  937. * betwen key and vhash, but for other blocks
  938. * there may be!
  939. * @param type entries of which type are relevant?
  940. * Use 0 for any type.
  941. * @param proc function to call on the matching value,
  942. * with NULL for if no value matches
  943. * @param proc_cls closure for proc
  944. */
  945. static void
  946. mysql_plugin_get_key (void *cls, uint64_t offset, const GNUNET_HashCode * key,
  947. const GNUNET_HashCode * vhash,
  948. enum GNUNET_BLOCK_Type type, PluginDatumProcessor proc,
  949. void *proc_cls)
  950. {
  951. struct Plugin *plugin = cls;
  952. int ret;
  953. MYSQL_BIND cbind[1];
  954. long long total;
  955. unsigned long hashSize;
  956. unsigned long hashSize2;
  957. unsigned long long off;
  958. GNUNET_assert (key != NULL);
  959. GNUNET_assert (NULL != proc);
  960. hashSize = sizeof (GNUNET_HashCode);
  961. hashSize2 = sizeof (GNUNET_HashCode);
  962. memset (cbind, 0, sizeof (cbind));
  963. total = -1;
  964. cbind[0].buffer_type = MYSQL_TYPE_LONGLONG;
  965. cbind[0].buffer = &total;
  966. cbind[0].is_unsigned = GNUNET_NO;
  967. if (type != 0)
  968. {
  969. if (vhash != NULL)
  970. {
  971. ret =
  972. prepared_statement_run_select (plugin,
  973. plugin->
  974. count_entry_by_hash_vhash_and_type, 1,
  975. cbind, MYSQL_TYPE_BLOB, key, hashSize,
  976. &hashSize, MYSQL_TYPE_BLOB, vhash,
  977. hashSize2, &hashSize2, MYSQL_TYPE_LONG,
  978. &type, GNUNET_YES, -1);
  979. }
  980. else
  981. {
  982. ret =
  983. prepared_statement_run_select (plugin,
  984. plugin->count_entry_by_hash_and_type,
  985. 1, cbind, MYSQL_TYPE_BLOB, key,
  986. hashSize, &hashSize, MYSQL_TYPE_LONG,
  987. &type, GNUNET_YES, -1);
  988. }
  989. }
  990. else
  991. {
  992. if (vhash != NULL)
  993. {
  994. ret =
  995. prepared_statement_run_select (plugin,
  996. plugin->count_entry_by_hash_and_vhash,
  997. 1, cbind, MYSQL_TYPE_BLOB, key,
  998. hashSize, &hashSize, MYSQL_TYPE_BLOB,
  999. vhash, hashSize2, &hashSize2, -1);
  1000. }
  1001. else
  1002. {
  1003. ret =
  1004. prepared_statement_run_select (plugin, plugin->count_entry_by_hash, 1,
  1005. cbind, MYSQL_TYPE_BLOB, key, hashSize,
  1006. &hashSize, -1);
  1007. }
  1008. }
  1009. if ((ret != GNUNET_OK) || (0 >= total))
  1010. {
  1011. proc (proc_cls, NULL, 0, NULL, 0, 0, 0, GNUNET_TIME_UNIT_ZERO_ABS, 0);
  1012. return;
  1013. }
  1014. offset = offset % total;
  1015. off = (unsigned long long) offset;
  1016. #if DEBUG_MYSQL
  1017. GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
  1018. "Obtaining %llu/%lld result for GET `%s'\n", off, total,
  1019. GNUNET_h2s (key));
  1020. #endif
  1021. if (type != GNUNET_BLOCK_TYPE_ANY)
  1022. {
  1023. if (NULL != vhash)
  1024. {
  1025. execute_select (plugin, plugin->select_entry_by_hash_vhash_and_type, proc,
  1026. proc_cls, MYSQL_TYPE_BLOB, key, hashSize, &hashSize,
  1027. MYSQL_TYPE_BLOB, vhash, hashSize, &hashSize,
  1028. MYSQL_TYPE_LONG, &type, GNUNET_YES, MYSQL_TYPE_LONGLONG,
  1029. &off, GNUNET_YES, -1);
  1030. }
  1031. else
  1032. {
  1033. execute_select (plugin, plugin->select_entry_by_hash_and_type, proc,
  1034. proc_cls, MYSQL_TYPE_BLOB, key, hashSize, &hashSize,
  1035. MYSQL_TYPE_LONG, &type, GNUNET_YES, MYSQL_TYPE_LONGLONG,
  1036. &off, GNUNET_YES, -1);
  1037. }
  1038. }
  1039. else
  1040. {
  1041. if (NULL != vhash)
  1042. {
  1043. execute_select (plugin, plugin->select_entry_by_hash_and_vhash, proc,
  1044. proc_cls, MYSQL_TYPE_BLOB, key, hashSize, &hashSize,
  1045. MYSQL_TYPE_BLOB, vhash, hashSize, &hashSize,
  1046. MYSQL_TYPE_LONGLONG, &off, GNUNET_YES, -1);
  1047. }
  1048. else
  1049. {
  1050. execute_select (plugin, plugin->select_entry_by_hash, proc, proc_cls,
  1051. MYSQL_TYPE_BLOB, key, hashSize, &hashSize,
  1052. MYSQL_TYPE_LONGLONG, &off, GNUNET_YES, -1);
  1053. }
  1054. }
  1055. }
  1056. /**
  1057. * Get a zero-anonymity datum from the datastore.
  1058. *
  1059. * @param cls our "struct Plugin*"
  1060. * @param offset offset of the result
  1061. * @param type entries of which type should be considered?
  1062. * Use 0 for any type.
  1063. * @param proc function to call on a matching value or NULL
  1064. * @param proc_cls closure for iter
  1065. */
  1066. static void
  1067. mysql_plugin_get_zero_anonymity (void *cls, uint64_t offset,
  1068. enum GNUNET_BLOCK_Type type,
  1069. PluginDatumProcessor proc, void *proc_cls)
  1070. {
  1071. struct Plugin *plugin = cls;
  1072. unsigned long long rvalue =
  1073. (unsigned long long) GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK,
  1074. UINT64_MAX);
  1075. execute_select (plugin, plugin->zero_iter, proc, proc_cls, MYSQL_TYPE_LONG,
  1076. &type, GNUNET_YES, MYSQL_TYPE_LONGLONG, &rvalue, GNUNET_YES,
  1077. MYSQL_TYPE_LONG, &type, GNUNET_YES, MYSQL_TYPE_LONGLONG,
  1078. &rvalue, GNUNET_YES, -1);
  1079. }
  1080. /**
  1081. * Context for 'repl_proc' function.
  1082. */
  1083. struct ReplCtx
  1084. {
  1085. /**
  1086. * Plugin handle.
  1087. */
  1088. struct Plugin *plugin;
  1089. /**
  1090. * Function to call for the result (or the NULL).
  1091. */
  1092. PluginDatumProcessor proc;
  1093. /**
  1094. * Closure for proc.
  1095. */
  1096. void *proc_cls;
  1097. };
  1098. /**
  1099. * Wrapper for the processor for 'mysql_plugin_get_replication'.
  1100. * Decrements the replication counter and calls the original
  1101. * iterator.
  1102. *
  1103. * @param cls closure
  1104. * @param key key for the content
  1105. * @param size number of bytes in data
  1106. * @param data content stored
  1107. * @param type type of the content
  1108. * @param priority priority of the content
  1109. * @param anonymity anonymity-level for the content
  1110. * @param expiration expiration time for the content
  1111. * @param uid unique identifier for the datum;
  1112. * maybe 0 if no unique identifier is available
  1113. *
  1114. * @return GNUNET_SYSERR to abort the iteration, GNUNET_OK to continue
  1115. * (continue on call to "next", of course),
  1116. * GNUNET_NO to delete the item and continue (if supported)
  1117. */
  1118. static int
  1119. repl_proc (void *cls, const GNUNET_HashCode * key, uint32_t size,
  1120. const void *data, enum GNUNET_BLOCK_Type type, uint32_t priority,
  1121. uint32_t anonymity, struct GNUNET_TIME_Absolute expiration,
  1122. uint64_t uid)
  1123. {
  1124. struct ReplCtx *rc = cls;
  1125. struct Plugin *plugin = rc->plugin;
  1126. unsigned long long oid;
  1127. int ret;
  1128. int iret;
  1129. ret =
  1130. rc->proc (rc->proc_cls, key, size, data, type, priority, anonymity,
  1131. expiration, uid);
  1132. if (NULL != key)
  1133. {
  1134. oid = (unsigned long long) uid;
  1135. iret =
  1136. prepared_statement_run (plugin, plugin->dec_repl, NULL,
  1137. MYSQL_TYPE_LONGLONG, &oid, GNUNET_YES, -1);
  1138. if (iret == GNUNET_SYSERR)
  1139. {
  1140. GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
  1141. "Failed to reduce replication counter\n");
  1142. return GNUNET_SYSERR;
  1143. }
  1144. }
  1145. return ret;
  1146. }
  1147. /**
  1148. * Get a random item for replication. Returns a single, not expired,
  1149. * random item from those with the highest replication counters. The
  1150. * item's replication counter is decremented by one IF it was positive
  1151. * before. Call 'proc' with all values ZERO or NULL if the datastore
  1152. * is empty.
  1153. *
  1154. * @param cls closure
  1155. * @param proc function to call the value (once only).
  1156. * @param proc_cls closure for proc
  1157. */
  1158. static void
  1159. mysql_plugin_get_replication (void *cls, PluginDatumProcessor proc,
  1160. void *proc_cls)
  1161. {
  1162. struct Plugin *plugin = cls;
  1163. struct ReplCtx rc;
  1164. unsigned long long rvalue;
  1165. unsigned long repl;
  1166. MYSQL_BIND results;
  1167. rc.plugin = plugin;
  1168. rc.proc = proc;
  1169. rc.proc_cls = proc_cls;
  1170. memset (&results, 0, sizeof (results));
  1171. results.buffer_type = MYSQL_TYPE_LONG;
  1172. results.buffer = &repl;
  1173. results.is_unsigned = GNUNET_YES;
  1174. if (1 !=
  1175. prepared_statement_run_select (plugin, plugin->max_repl, 1, &results, -1))
  1176. {
  1177. proc (proc_cls, NULL, 0, NULL, 0, 0, 0, GNUNET_TIME_UNIT_ZERO_ABS, 0);
  1178. return;
  1179. }
  1180. rvalue =
  1181. (unsigned long long) GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK,
  1182. UINT64_MAX);
  1183. execute_select (plugin, plugin->select_replication, &repl_proc, &rc,
  1184. MYSQL_TYPE_LONG, &repl, GNUNET_YES, MYSQL_TYPE_LONGLONG,
  1185. &rvalue, GNUNET_YES, MYSQL_TYPE_LONG, &repl, GNUNET_YES,
  1186. MYSQL_TYPE_LONGLONG, &rvalue, GNUNET_YES, -1);
  1187. }
  1188. /**
  1189. * Context for 'expi_proc' function.
  1190. */
  1191. struct ExpiCtx
  1192. {
  1193. /**
  1194. * Plugin handle.
  1195. */
  1196. struct Plugin *plugin;
  1197. /**
  1198. * Function to call for the result (or the NULL).
  1199. */
  1200. PluginDatumProcessor proc;
  1201. /**
  1202. * Closure for proc.
  1203. */
  1204. void *proc_cls;
  1205. };
  1206. /**
  1207. * Wrapper for the processor for 'mysql_plugin_get_expiration'.
  1208. * If no expired value was found, we do a second query for
  1209. * low-priority content.
  1210. *
  1211. * @param cls closure
  1212. * @param key key for the content
  1213. * @param size number of bytes in data
  1214. * @param data content stored
  1215. * @param type type of the content
  1216. * @param priority priority of the content
  1217. * @param anonymity anonymity-level for the content
  1218. * @param expiration expiration time for the content
  1219. * @param uid unique identifier for the datum;
  1220. * maybe 0 if no unique identifier is available
  1221. *
  1222. * @return GNUNET_SYSERR to abort the iteration, GNUNET_OK to continue
  1223. * (continue on call to "next", of course),
  1224. * GNUNET_NO to delete the item and continue (if supported)
  1225. */
  1226. static int
  1227. expi_proc (void *cls, const GNUNET_HashCode * key, uint32_t size,
  1228. const void *data, enum GNUNET_BLOCK_Type type, uint32_t priority,
  1229. uint32_t anonymity, struct GNUNET_TIME_Absolute expiration,
  1230. uint64_t uid)
  1231. {
  1232. struct ExpiCtx *rc = cls;
  1233. struct Plugin *plugin = rc->plugin;
  1234. if (NULL == key)
  1235. {
  1236. execute_select (plugin, plugin->select_priority, rc->proc, rc->proc_cls,
  1237. -1);
  1238. return GNUNET_SYSERR;
  1239. }
  1240. return rc->proc (rc->proc_cls, key, size, data, type, priority, anonymity,
  1241. expiration, uid);
  1242. }
  1243. /**
  1244. * Get a random item for expiration.
  1245. * Call 'proc' with all values ZERO or NULL if the datastore is empty.
  1246. *
  1247. * @param cls closure
  1248. * @param proc function to call the value (once only).
  1249. * @param proc_cls closure for proc
  1250. */
  1251. static void
  1252. mysql_plugin_get_expiration (void *cls, PluginDatumProcessor proc,
  1253. void *proc_cls)
  1254. {
  1255. struct Plugin *plugin = cls;
  1256. long long nt;
  1257. struct ExpiCtx rc;
  1258. rc.plugin = plugin;
  1259. rc.proc = proc;
  1260. rc.proc_cls = proc_cls;
  1261. nt = (long long) GNUNET_TIME_absolute_get ().abs_value;
  1262. execute_select (plugin, plugin->select_expiration, expi_proc, &rc,
  1263. MYSQL_TYPE_LONGLONG, &nt, GNUNET_YES, -1);
  1264. }
  1265. /**
  1266. * Drop database.
  1267. *
  1268. * @param cls the "struct Plugin*"
  1269. */
  1270. static void
  1271. mysql_plugin_drop (void *cls)
  1272. {
  1273. struct Plugin *plugin = cls;
  1274. if (GNUNET_OK != run_statement (plugin, "DROP TABLE gn090"))
  1275. return; /* error */
  1276. plugin->env->duc (plugin->env->cls, 0);
  1277. }
  1278. /**
  1279. * Entry point for the plugin.
  1280. *
  1281. * @param cls the "struct GNUNET_DATASTORE_PluginEnvironment*"
  1282. * @return our "struct Plugin*"
  1283. */
  1284. void *
  1285. libgnunet_plugin_datastore_mysql_init (void *cls)
  1286. {
  1287. struct GNUNET_DATASTORE_PluginEnvironment *env = cls;
  1288. struct GNUNET_DATASTORE_PluginFunctions *api;
  1289. struct Plugin *plugin;
  1290. plugin = GNUNET_malloc (sizeof (struct Plugin));
  1291. plugin->env = env;
  1292. plugin->cnffile = get_my_cnf_path (env->cfg);
  1293. if (GNUNET_OK != iopen (plugin))
  1294. {
  1295. iclose (plugin);
  1296. GNUNET_free_non_null (plugin->cnffile);
  1297. GNUNET_free (plugin);
  1298. return NULL;
  1299. }
  1300. #define MRUNS(a) (GNUNET_OK != run_statement (plugin, a) )
  1301. #define PINIT(a,b) (NULL == (a = prepared_statement_create(plugin, b)))
  1302. if (MRUNS
  1303. ("CREATE TABLE IF NOT EXISTS gn090 ("
  1304. " repl INT(11) UNSIGNED NOT NULL DEFAULT 0,"
  1305. " type INT(11) UNSIGNED NOT NULL DEFAULT 0,"
  1306. " prio INT(11) UNSIGNED NOT NULL DEFAULT 0,"
  1307. " anonLevel INT(11) UNSIGNED NOT NULL DEFAULT 0,"
  1308. " expire BIGINT UNSIGNED NOT NULL DEFAULT 0,"
  1309. " rvalue BIGINT UNSIGNED NOT NULL,"
  1310. " hash BINARY(64) NOT NULL DEFAULT '',"
  1311. " vhash BINARY(64) NOT NULL DEFAULT '',"
  1312. " value BLOB NOT NULL DEFAULT ''," " uid BIGINT NOT NULL AUTO_INCREMENT,"
  1313. " PRIMARY KEY (uid)," " INDEX idx_hash (hash(64)),"
  1314. " INDEX idx_hash_uid (hash(64),uid),"
  1315. " INDEX idx_hash_vhash (hash(64),vhash(64)),"
  1316. " INDEX idx_hash_type_uid (hash(64),type,rvalue),"
  1317. " INDEX idx_prio (prio)," " INDEX idx_repl_rvalue (repl,rvalue),"
  1318. " INDEX idx_expire (expire),"
  1319. " INDEX idx_anonLevel_type_rvalue (anonLevel,type,rvalue)"
  1320. ") ENGINE=InnoDB") || MRUNS ("SET AUTOCOMMIT = 1") ||
  1321. PINIT (plugin->insert_entry, INSERT_ENTRY) ||
  1322. PINIT (plugin->delete_entry_by_uid, DELETE_ENTRY_BY_UID) ||
  1323. PINIT (plugin->select_entry_by_hash, SELECT_ENTRY_BY_HASH) ||
  1324. PINIT (plugin->select_entry_by_hash_and_vhash,
  1325. SELECT_ENTRY_BY_HASH_AND_VHASH) ||
  1326. PINIT (plugin->select_entry_by_hash_and_type,
  1327. SELECT_ENTRY_BY_HASH_AND_TYPE) ||
  1328. PINIT (plugin->select_entry_by_hash_vhash_and_type,
  1329. SELECT_ENTRY_BY_HASH_VHASH_AND_TYPE) ||
  1330. PINIT (plugin->count_entry_by_hash, COUNT_ENTRY_BY_HASH) ||
  1331. PINIT (plugin->get_size, SELECT_SIZE) ||
  1332. PINIT (plugin->count_entry_by_hash_and_vhash,
  1333. COUNT_ENTRY_BY_HASH_AND_VHASH) ||
  1334. PINIT (plugin->count_entry_by_hash_and_type, COUNT_ENTRY_BY_HASH_AND_TYPE)
  1335. || PINIT (plugin->count_entry_by_hash_vhash_and_type,
  1336. COUNT_ENTRY_BY_HASH_VHASH_AND_TYPE) ||
  1337. PINIT (plugin->update_entry, UPDATE_ENTRY) ||
  1338. PINIT (plugin->dec_repl, DEC_REPL) ||
  1339. PINIT (plugin->zero_iter, SELECT_IT_NON_ANONYMOUS) ||
  1340. PINIT (plugin->select_expiration, SELECT_IT_EXPIRATION) ||
  1341. PINIT (plugin->select_priority, SELECT_IT_PRIORITY) ||
  1342. PINIT (plugin->max_repl, SELECT_MAX_REPL) ||
  1343. PINIT (plugin->select_replication, SELECT_IT_REPLICATION))
  1344. {
  1345. iclose (plugin);
  1346. GNUNET_free_non_null (plugin->cnffile);
  1347. GNUNET_free (plugin);
  1348. return NULL;
  1349. }
  1350. #undef PINIT
  1351. #undef MRUNS
  1352. api = GNUNET_malloc (sizeof (struct GNUNET_DATASTORE_PluginFunctions));
  1353. api->cls = plugin;
  1354. api->estimate_size = &mysql_plugin_estimate_size;
  1355. api->put = &mysql_plugin_put;
  1356. api->update = &mysql_plugin_update;
  1357. api->get_key = &mysql_plugin_get_key;
  1358. api->get_replication = &mysql_plugin_get_replication;
  1359. api->get_expiration = &mysql_plugin_get_expiration;
  1360. api->get_zero_anonymity = &mysql_plugin_get_zero_anonymity;
  1361. api->drop = &mysql_plugin_drop;
  1362. GNUNET_log_from (GNUNET_ERROR_TYPE_INFO, "mysql",
  1363. _("Mysql database running\n"));
  1364. return api;
  1365. }
  1366. /**
  1367. * Exit point from the plugin.
  1368. * @param cls our "struct Plugin*"
  1369. * @return always NULL
  1370. */
  1371. void *
  1372. libgnunet_plugin_datastore_mysql_done (void *cls)
  1373. {
  1374. struct GNUNET_DATASTORE_PluginFunctions *api = cls;
  1375. struct Plugin *plugin = api->cls;
  1376. struct GNUNET_MysqlStatementHandle *s;
  1377. iclose (plugin);
  1378. while (NULL != (s = plugin->shead))
  1379. {
  1380. GNUNET_CONTAINER_DLL_remove (plugin->shead, plugin->stail, s);
  1381. GNUNET_free (s->query);
  1382. GNUNET_free (s);
  1383. }
  1384. GNUNET_free_non_null (plugin->cnffile);
  1385. GNUNET_free (plugin);
  1386. GNUNET_free (api);
  1387. mysql_library_end ();
  1388. return NULL;
  1389. }
  1390. /* end of plugin_datastore_mysql.c */