diff options
author | Christian Grothoff <christian@grothoff.org> | 2010-07-24 15:23:45 +0000 |
---|---|---|
committer | Christian Grothoff <christian@grothoff.org> | 2010-07-24 15:23:45 +0000 |
commit | 12d1bb08b090ea81140656e3b10a0bc095c4b8e1 (patch) | |
tree | 66e6aebe8cff3ac311f261df15ff68b6d2f926e4 /src/datastore | |
parent | 5b767735eab63f6dc171cbb5161c3ad600a09631 (diff) | |
download | gnunet-12d1bb08b090ea81140656e3b10a0bc095c4b8e1.tar.gz gnunet-12d1bb08b090ea81140656e3b10a0bc095c4b8e1.zip |
init
Diffstat (limited to 'src/datastore')
-rw-r--r-- | src/datastore/plugin_datastore_mysql.c | 1786 |
1 files changed, 1786 insertions, 0 deletions
diff --git a/src/datastore/plugin_datastore_mysql.c b/src/datastore/plugin_datastore_mysql.c new file mode 100644 index 000000000..03fe06b93 --- /dev/null +++ b/src/datastore/plugin_datastore_mysql.c | |||
@@ -0,0 +1,1786 @@ | |||
1 | /* | ||
2 | This file is part of GNUnet | ||
3 | (C) 2009, 2010 Christian Grothoff (and other contributing authors) | ||
4 | |||
5 | GNUnet is free software; you can redistribute it and/or modify | ||
6 | it under the terms of the GNU General Public License as published | ||
7 | by the Free Software Foundation; either version 3, or (at your | ||
8 | option) any later version. | ||
9 | |||
10 | GNUnet is distributed in the hope that it will be useful, but | ||
11 | WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
13 | General Public License for more details. | ||
14 | |||
15 | You should have received a copy of the GNU General Public License | ||
16 | along with GNUnet; see the file COPYING. If not, write to the | ||
17 | Free Software Foundation, Inc., 59 Temple Place - Suite 330, | ||
18 | Boston, MA 02111-1307, USA. | ||
19 | */ | ||
20 | |||
21 | /** | ||
22 | * @file datastore/plugin_datastore_mysql.c | ||
23 | * @brief mysql-based datastore backend | ||
24 | * @author Igor Wronsky | ||
25 | * @author Christian Grothoff | ||
26 | * | ||
27 | * NOTE: This db module does NOT work with mysql prior to 4.1 since | ||
28 | * it uses prepared statements. MySQL 5.0.46 promises to fix a bug | ||
29 | * in MyISAM that is causing us grief. At the time of this writing, | ||
30 | * that version is yet to be released. In anticipation, the code | ||
31 | * will use MyISAM with 5.0.46 (and higher). If you run such a | ||
32 | * version, please run "make check" to verify that the MySQL bug | ||
33 | * was actually fixed in your version (and if not, change the | ||
34 | * code below to use MyISAM for gn071). | ||
35 | * | ||
36 | * HIGHLIGHTS | ||
37 | * | ||
38 | * Pros | ||
39 | * + On up-to-date hardware where mysql can be used comfortably, this | ||
40 | * module will have better performance than the other db choices | ||
41 | * (according to our tests). | ||
42 | * + Its often possible to recover the mysql database from internal | ||
43 | * inconsistencies. The other db choices do not support repair! | ||
44 | * Cons | ||
45 | * - Memory usage (Comment: "I have 1G and it never caused me trouble") | ||
46 | * - Manual setup | ||
47 | * | ||
48 | * MANUAL SETUP INSTRUCTIONS | ||
49 | * | ||
50 | * 1) in /etc/gnunet.conf, set | ||
51 | * <pre> | ||
52 | * | ||
53 | * sqstore = "sqstore_mysql" | ||
54 | * | ||
55 | * </pre> | ||
56 | * 2) Then access mysql as root, | ||
57 | * <pre> | ||
58 | * | ||
59 | * $ mysql -u root -p | ||
60 | * | ||
61 | * </pre> | ||
62 | * and do the following. [You should replace $USER with the username | ||
63 | * that will be running the gnunetd process]. | ||
64 | * <pre> | ||
65 | * | ||
66 | CREATE DATABASE gnunet; | ||
67 | GRANT select,insert,update,delete,create,alter,drop,create temporary tables | ||
68 | ON gnunet.* TO $USER@localhost; | ||
69 | SET PASSWORD FOR $USER@localhost=PASSWORD('$the_password_you_like'); | ||
70 | FLUSH PRIVILEGES; | ||
71 | * | ||
72 | * </pre> | ||
73 | * 3) In the $HOME directory of $USER, create a ".my.cnf" file | ||
74 | * with the following lines | ||
75 | * <pre> | ||
76 | |||
77 | [client] | ||
78 | user=$USER | ||
79 | password=$the_password_you_like | ||
80 | |||
81 | * </pre> | ||
82 | * | ||
83 | * Thats it. Note that .my.cnf file is a security risk unless its on | ||
84 | * a safe partition etc. The $HOME/.my.cnf can of course be a symbolic | ||
85 | * link. Even greater security risk can be achieved by setting no | ||
86 | * password for $USER. Luckily $USER has only priviledges to mess | ||
87 | * up GNUnet's tables, nothing else (unless you give him more, | ||
88 | * of course).<p> | ||
89 | * | ||
90 | * 4) Still, perhaps you should briefly try if the DB connection | ||
91 | * works. First, login as $USER. Then use, | ||
92 | * | ||
93 | * <pre> | ||
94 | * $ mysql -u $USER -p $the_password_you_like | ||
95 | * mysql> use gnunet; | ||
96 | * </pre> | ||
97 | * | ||
98 | * If you get the message "Database changed" it probably works. | ||
99 | * | ||
100 | * [If you get "ERROR 2002: Can't connect to local MySQL server | ||
101 | * through socket '/tmp/mysql.sock' (2)" it may be resolvable by | ||
102 | * "ln -s /var/run/mysqld/mysqld.sock /tmp/mysql.sock" | ||
103 | * so there may be some additional trouble depending on your mysql setup.] | ||
104 | * | ||
105 | * REPAIRING TABLES | ||
106 | * | ||
107 | * - Its probably healthy to check your tables for inconsistencies | ||
108 | * every now and then. | ||
109 | * - If you get odd SEGVs on gnunetd startup, it might be that the mysql | ||
110 | * databases have been corrupted. | ||
111 | * - The tables can be verified/fixed in two ways; | ||
112 | * 1) by running mysqlcheck -A, or | ||
113 | * 2) by executing (inside of mysql using the GNUnet database): | ||
114 | * mysql> REPAIR TABLE gn080; | ||
115 | * mysql> REPAIR TABLE gn072; | ||
116 | * | ||
117 | * PROBLEMS? | ||
118 | * | ||
119 | * If you have problems related to the mysql module, your best | ||
120 | * friend is probably the mysql manual. The first thing to check | ||
121 | * is that mysql is basically operational, that you can connect | ||
122 | * to it, create tables, issue queries etc. | ||
123 | * | ||
124 | */ | ||
125 | |||
126 | #include "platform.h" | ||
127 | #include "plugin_datastore.h" | ||
128 | |||
129 | #define DEBUG_MYSQL GNUNET_NO | ||
130 | |||
131 | #define MAX_DATUM_SIZE 65536 | ||
132 | |||
133 | /** | ||
134 | * Maximum number of supported parameters for a prepared | ||
135 | * statement. Increase if needed. | ||
136 | */ | ||
137 | #define MAX_PARAM 16 | ||
138 | |||
139 | /** | ||
140 | * Die with an error message that indicates | ||
141 | * a failure of the command 'cmd' with the message given | ||
142 | * by strerror(errno). | ||
143 | */ | ||
144 | #define DIE_MYSQL(cmd, dbh) do { GNUNET_log(GNUNET_ERROR_TYPE_ERROR, _("`%s' failed at %s:%d with error: %s\n"), cmd, __FILE__, __LINE__, mysql_error((dbh)->dbf)); abort(); } while(0); | ||
145 | |||
146 | /** | ||
147 | * Log an error message at log-level 'level' that indicates | ||
148 | * a failure of the command 'cmd' on file 'filename' | ||
149 | * with the message given by strerror(errno). | ||
150 | */ | ||
151 | #define LOG_MYSQL(level, cmd, dbh) do { GNUNET_log(level, _("`%s' failed at %s:%d with error: %s\n"), cmd, __FILE__, __LINE__, mysql_error((dbh)->dbf)); } while(0); | ||
152 | |||
153 | |||
154 | /* warning, slighly crazy mysql statements ahead. Essentially, MySQL does not handle | ||
155 | "OR" very well, so we need to use UNION instead. And UNION does not | ||
156 | automatically apply a LIMIT on the outermost clause, so we need to | ||
157 | repeat ourselves quite a bit. All hail the performance gods (and thanks | ||
158 | to #mysql on freenode) */ | ||
159 | #define SELECT_IT_LOW_PRIORITY "(SELECT size,type,prio,anonLevel,expire,hash,vkey FROM gn080 FORCE INDEX(prio) WHERE (prio = ? AND vkey > ?) "\ | ||
160 | "ORDER BY prio ASC,vkey ASC LIMIT 1) "\ | ||
161 | "UNION "\ | ||
162 | "(SELECT size,type,prio,anonLevel,expire,hash,vkey FROM gn080 FORCE INDEX(prio) WHERE (prio > ? AND vkey != ?)"\ | ||
163 | "ORDER BY prio ASC,vkey ASC LIMIT 1)"\ | ||
164 | "ORDER BY prio ASC,vkey ASC LIMIT 1" | ||
165 | |||
166 | #define SELECT_IT_NON_ANONYMOUS "(SELECT size,type,prio,anonLevel,expire,hash,vkey FROM gn080 FORCE INDEX(prio) WHERE (prio = ? AND vkey < ?)"\ | ||
167 | " AND anonLevel=0 ORDER BY prio DESC,vkey DESC LIMIT 1) "\ | ||
168 | "UNION "\ | ||
169 | "(SELECT size,type,prio,anonLevel,expire,hash,vkey FROM gn080 FORCE INDEX(prio) WHERE (prio < ? AND vkey != ?)"\ | ||
170 | " AND anonLevel=0 ORDER BY prio DESC,vkey DESC LIMIT 1) "\ | ||
171 | "ORDER BY prio DESC,vkey DESC LIMIT 1" | ||
172 | |||
173 | #define SELECT_IT_EXPIRATION_TIME "(SELECT size,type,prio,anonLevel,expire,hash,vkey FROM gn080 FORCE INDEX(expire) WHERE (expire = ? AND vkey > ?) "\ | ||
174 | "ORDER BY expire ASC,vkey ASC LIMIT 1) "\ | ||
175 | "UNION "\ | ||
176 | "(SELECT size,type,prio,anonLevel,expire,hash,vkey FROM gn080 FORCE INDEX(expire) WHERE (expire > ? AND vkey != ?) "\ | ||
177 | "ORDER BY expire ASC,vkey ASC LIMIT 1)"\ | ||
178 | "ORDER BY expire ASC,vkey ASC LIMIT 1" | ||
179 | |||
180 | |||
181 | #define SELECT_IT_MIGRATION_ORDER "(SELECT size,type,prio,anonLevel,expire,hash,vkey FROM gn080 FORCE INDEX(expire) WHERE (expire = ? AND vkey < ?)"\ | ||
182 | " AND expire > ? AND type!=3"\ | ||
183 | " ORDER BY expire DESC,vkey DESC LIMIT 1) "\ | ||
184 | "UNION "\ | ||
185 | "(SELECT size,type,prio,anonLevel,expire,hash,vkey FROM gn080 FORCE INDEX(expire) WHERE (expire < ? AND vkey != ?)"\ | ||
186 | " AND expire > ? AND type!=3"\ | ||
187 | " ORDER BY expire DESC,vkey DESC LIMIT 1)"\ | ||
188 | "ORDER BY expire DESC,vkey DESC LIMIT 1" | ||
189 | |||
190 | #define SELECT_SIZE "SELECT sum(size) FROM gn080" | ||
191 | |||
192 | |||
193 | struct GNUNET_MysqlStatementHandle | ||
194 | { | ||
195 | struct GNUNET_MysqlStatementHandle *next; | ||
196 | |||
197 | struct GNUNET_MysqlStatementHandle *prev; | ||
198 | |||
199 | char *query; | ||
200 | |||
201 | MYSQL_STMT *statement; | ||
202 | |||
203 | int valid; | ||
204 | |||
205 | }; | ||
206 | |||
207 | |||
208 | /** | ||
209 | * Context for all functions in this plugin. | ||
210 | */ | ||
211 | struct Plugin | ||
212 | { | ||
213 | /** | ||
214 | * Our execution environment. | ||
215 | */ | ||
216 | struct GNUNET_DATASTORE_PluginEnvironment *env; | ||
217 | |||
218 | MYSQL *dbf; | ||
219 | |||
220 | struct GNUNET_MysqlStatementHandle *shead; | ||
221 | |||
222 | struct GNUNET_MysqlStatementHandle *stail; | ||
223 | |||
224 | /** | ||
225 | * Filename of "my.cnf" (msyql configuration). | ||
226 | */ | ||
227 | char *cnffile; | ||
228 | |||
229 | /** | ||
230 | * Statements dealing with gn072 table | ||
231 | */ | ||
232 | #define SELECT_VALUE "SELECT value FROM gn072 WHERE vkey=?" | ||
233 | struct GNUNET_MysqlStatementHandle *select_value; | ||
234 | |||
235 | #define DELETE_VALUE "DELETE FROM gn072 WHERE vkey=?"o | ||
236 | struct GNUNET_MysqlStatementHandle *delete_value; | ||
237 | |||
238 | #define INSERT_VALUE "INSERT INTO gn072 (value) VALUES (?)" | ||
239 | struct GNUNET_MysqlStatementHandle *insert_value; | ||
240 | |||
241 | /** | ||
242 | * Statements dealing with gn080 table | ||
243 | */ | ||
244 | #define INSERT_ENTRY "INSERT INTO gn080 (size,type,prio,anonLevel,expire,hash,vhash,vkey) VALUES (?,?,?,?,?,?,?,?)" | ||
245 | struct GNUNET_MysqlStatementHandle *insert_entry; | ||
246 | |||
247 | #define DELETE_ENTRY_BY_VKEY "DELETE FROM gn080 WHERE vkey=?" | ||
248 | struct GNUNET_MysqlStatementHandle *delete_entry_by_vkey; | ||
249 | |||
250 | #define SELECT_ENTRY_BY_HASH "SELECT size,type,prio,anonLevel,expire,hash,vkey FROM gn080 FORCE INDEX (hash_vkey) WHERE hash=? AND vkey > ? ORDER BY vkey ASC LIMIT 1 OFFSET ?" | ||
251 | struct GNUNET_MysqlStatementHandle *select_entry_by_hash; | ||
252 | |||
253 | #define SELECT_ENTRY_BY_HASH_AND_VHASH "SELECT size,type,prio,anonLevel,expire,hash,vkey FROM gn080 FORCE INDEX (hash_vhash_vkey) WHERE hash=? AND vhash=? AND vkey > ? ORDER BY vkey ASC LIMIT 1 OFFSET ?" | ||
254 | struct GNUNET_MysqlStatementHandle *select_entry_by_hash_and_vhash; | ||
255 | |||
256 | #define SELECT_ENTRY_BY_HASH_AND_TYPE "SELECT size,type,prio,anonLevel,expire,hash,vkey FROM gn080 FORCE INDEX (hash_vkey) WHERE hash=? AND vkey > ? AND type=? ORDER BY vkey ASC LIMIT 1 OFFSET ?" | ||
257 | struct GNUNET_MysqlStatementHandle *select_entry_by_hash_and_type; | ||
258 | |||
259 | #define SELECT_ENTRY_BY_HASH_VHASH_AND_TYPE "SELECT size,type,prio,anonLevel,expire,hash,vkey FROM gn080 FORCE INDEX (hash_vhash_vkey) WHERE hash=? AND vhash=? AND vkey > ? AND type=? ORDER BY vkey ASC LIMIT 1 OFFSET ?" | ||
260 | struct GNUNET_MysqlStatementHandle *select_entry_by_hash_vhash_and_type; | ||
261 | |||
262 | #define COUNT_ENTRY_BY_HASH "SELECT count(*) FROM gn080 FORCE INDEX (hash) WHERE hash=?" | ||
263 | struct GNUNET_MysqlStatementHandle *count_entry_by_hash; | ||
264 | |||
265 | #define COUNT_ENTRY_BY_HASH_AND_VHASH "SELECT count(*) FROM gn080 FORCE INDEX (hash_vhash_vkey) WHERE hash=? AND vhash=?" | ||
266 | struct GNUNET_MysqlStatementHandle *count_entry_by_hash_and_vhash; | ||
267 | |||
268 | #define COUNT_ENTRY_BY_HASH_AND_TYPE "SELECT count(*) FROM gn080 FORCE INDEX (hash) WHERE hash=? AND type=?" | ||
269 | struct GNUNET_MysqlStatementHandle *count_entry_by_hash_and_type; | ||
270 | |||
271 | #define COUNT_ENTRY_BY_HASH_VHASH_AND_TYPE "SELECT count(*) FROM gn080 FORCE INDEX (hash_vhash) WHERE hash=? AND vhash=? AND type=?" | ||
272 | struct GNUNET_MysqlStatementHandle *count_entry_by_hash_vhash_and_type; | ||
273 | |||
274 | #define UPDATE_ENTRY "UPDATE gn080 SET prio=prio+?,expire=IF(expire>=?,expire,?) WHERE vkey=?" | ||
275 | struct GNUNET_MysqlStatementHandle *update_entry; | ||
276 | |||
277 | struct GNUNET_MysqlStatementHandle *iter[4]; | ||
278 | |||
279 | //static unsigned int stat_size; | ||
280 | |||
281 | /** | ||
282 | * Size of the mysql database on disk. | ||
283 | */ | ||
284 | unsigned long long content_size; | ||
285 | |||
286 | }; | ||
287 | |||
288 | |||
289 | /** | ||
290 | * Obtain the location of ".my.cnf". | ||
291 | * @return NULL on error | ||
292 | */ | ||
293 | static char * | ||
294 | get_my_cnf_path (struct GNUNET_ConfigurationHandle *cfg) | ||
295 | { | ||
296 | char *cnffile; | ||
297 | char *home_dir; | ||
298 | struct stat st; | ||
299 | #ifndef WINDOWS | ||
300 | struct passwd *pw; | ||
301 | #endif | ||
302 | |||
303 | #ifndef WINDOWS | ||
304 | pw = getpwuid (getuid ()); | ||
305 | if (!pw) | ||
306 | { | ||
307 | GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, | ||
308 | "getpwuid"); | ||
309 | return NULL; | ||
310 | } | ||
311 | home_dir = GNUNET_strdup (pw->pw_dir); | ||
312 | #else | ||
313 | home_dir = (char *) GNUNET_malloc (_MAX_PATH + 1); | ||
314 | plibc_conv_to_win_path ("~/", home_dir); | ||
315 | #endif | ||
316 | GNUNET_asprintf (&cnffile, "%s/.my.cnf", home_dir); | ||
317 | GNUNET_free (home_dir); | ||
318 | GNUNET_CONFIUGRATION_get_value_filename (cfg, | ||
319 | "MYSQL", "CONFIG", cnffile, | ||
320 | &home_dir); | ||
321 | GNUNET_free (cnffile); | ||
322 | cnffile = home_dir; | ||
323 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | ||
324 | _("Trying to use file `%s' for MySQL configuration.\n"), | ||
325 | cnffile); | ||
326 | if ((0 != STAT (cnffile, &st)) || | ||
327 | (0 != ACCESS (cnffile, R_OK)) || (!S_ISREG (st.st_mode))) | ||
328 | { | ||
329 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
330 | _("Could not access file `%s': %s\n"), cnffile, | ||
331 | STRERROR (errno)); | ||
332 | GNUNET_free (cnffile); | ||
333 | return NULL; | ||
334 | } | ||
335 | return cnffile; | ||
336 | } | ||
337 | |||
338 | |||
339 | /** | ||
340 | * Close database connection and all prepared statements (we got a DB | ||
341 | * disconnect error). | ||
342 | */ | ||
343 | static int | ||
344 | iclose (struct Plugin *plugin) | ||
345 | { | ||
346 | struct GNUNET_MysqlStatementHandle *spos; | ||
347 | |||
348 | spos = plugin->shead; | ||
349 | while (spos != NULL) | ||
350 | { | ||
351 | if (spos->statement != NULL) | ||
352 | { | ||
353 | mysql_stmt_close (spos->statement); | ||
354 | spos->statement = NULL; | ||
355 | } | ||
356 | spos->valid = GNUNET_NO; | ||
357 | spos = spos->next; | ||
358 | } | ||
359 | if (plugin->dbf != NULL) | ||
360 | { | ||
361 | mysql_close (plugin->dbf); | ||
362 | plugin->dbf = NULL; | ||
363 | } | ||
364 | return GNUNET_OK; | ||
365 | } | ||
366 | |||
367 | |||
368 | /** | ||
369 | * Open the connection with the database (and initialize | ||
370 | * our default options). | ||
371 | * | ||
372 | * @return GNUNET_OK on success | ||
373 | */ | ||
374 | static int | ||
375 | iopen (struct Plugin *ret) | ||
376 | { | ||
377 | char *mysql_dbname; | ||
378 | char *mysql_server; | ||
379 | char *mysql_user; | ||
380 | char *mysql_password; | ||
381 | unsigned long long mysql_port; | ||
382 | my_bool reconnect; | ||
383 | unsigned int timeout; | ||
384 | |||
385 | ret->dbf = mysql_init (NULL); | ||
386 | if (ret->dbf == NULL) | ||
387 | return GNUNET_SYSERR; | ||
388 | if (ret->cnffile != NULL) | ||
389 | mysql_options (ret->dbf, MYSQL_READ_DEFAULT_FILE, ret->cnffile); | ||
390 | mysql_options (ret->dbf, MYSQL_READ_DEFAULT_GROUP, "client"); | ||
391 | reconnect = 0; | ||
392 | mysql_options (ret->dbf, MYSQL_OPT_RECONNECT, &reconnect); | ||
393 | mysql_options (ret->dbf, | ||
394 | MYSQL_OPT_CONNECT_TIMEOUT, (const void *) &timeout); | ||
395 | timeout = 60; /* in seconds */ | ||
396 | mysql_options (ret->dbf, MYSQL_OPT_READ_TIMEOUT, (const void *) &timeout); | ||
397 | mysql_options (ret->dbf, MYSQL_OPT_WRITE_TIMEOUT, (const void *) &timeout); | ||
398 | mysql_dbname = NULL; | ||
399 | if (GNUNET_YES == GNUNET_CONFIGURATION_have_value (ret->env->cfg, | ||
400 | "MYSQL", "DATABASE")) | ||
401 | GNUNET_assert (GNUNET_OK == | ||
402 | GNUNET_CONFIGURATION_get_value_string (ret->env->cfg, | ||
403 | "MYSQL", "DATABASE", | ||
404 | &mysql_dbname)); | ||
405 | else | ||
406 | mysql_dbname = GNUNET_strdup ("gnunet"); | ||
407 | mysql_user = NULL; | ||
408 | if (GNUNET_YES == GNUNET_CONFIGURATION_have_value (ret->env->cfg, | ||
409 | "MYSQL", "USER")) | ||
410 | { | ||
411 | GNUNET_break (GNUNET_OK == | ||
412 | GNUNET_CONFIGURATION_get_value_string (ret->env->cfg, | ||
413 | "MYSQL", "USER", | ||
414 | &mysql_user)); | ||
415 | } | ||
416 | mysql_password = NULL; | ||
417 | if (GNUNET_YES == GNUNET_CONFIGURATION_have_value (ret->env->cfg, | ||
418 | "MYSQL", "PASSWORD")) | ||
419 | { | ||
420 | GNUNET_break (GNUNET_OK == | ||
421 | GNUNET_CONFIGURATION_get_value_string (ret->cfg, | ||
422 | "MYSQL", "PASSWORD", | ||
423 | &mysql_password)); | ||
424 | } | ||
425 | mysql_server = NULL; | ||
426 | if (GNUNET_YES == GNUNET_CONFIGURATION_have_value (ret->cfg, | ||
427 | "MYSQL", "HOST")) | ||
428 | { | ||
429 | GNUNET_break (GNUNET_OK == | ||
430 | GNUNET_CONFIGURATION_get_value_string (ret->cfg, | ||
431 | "MYSQL", "HOST", "", | ||
432 | &mysql_server)); | ||
433 | } | ||
434 | mysql_port = 0; | ||
435 | if (GNUNET_YES == GNUNET_CONFIGURATION_have_value (ret->cfg, | ||
436 | "MYSQL", "PORT")) | ||
437 | { | ||
438 | GNUNET_break (GNUNET_OK == | ||
439 | GNUNET_CONFIGURATION_get_value_number (ret->cfg, "MYSQL", | ||
440 | "PORT", &mysql_port)); | ||
441 | } | ||
442 | |||
443 | GNUNET_assert (mysql_dbname != NULL); | ||
444 | mysql_real_connect (ret->dbf, mysql_server, mysql_user, mysql_password, | ||
445 | mysql_dbname, (unsigned int) mysql_port, NULL, 0); | ||
446 | GNUNET_free (mysql_dbname); | ||
447 | if (mysql_error (ret->dbf)[0]) | ||
448 | { | ||
449 | LOG_MYSQL (GNUNET_ERROR_TYPE_ERROR, | ||
450 | "mysql_real_connect", ret); | ||
451 | return GNUNET_SYSERR; | ||
452 | } | ||
453 | ret->valid = GNUNET_YES; | ||
454 | return GNUNET_OK; | ||
455 | } | ||
456 | |||
457 | |||
458 | /** | ||
459 | * Run the given MySQL statement. | ||
460 | * | ||
461 | * @return GNUNET_OK on success, GNUNET_SYSERR on error | ||
462 | */ | ||
463 | static int | ||
464 | run_statement (struct Plugin *plugin, | ||
465 | const char *statement) | ||
466 | { | ||
467 | if ((NULL == plugin->dbh) && (GNUNET_OK != iopen (plugin))) | ||
468 | return GNUNET_SYSERR; | ||
469 | mysql_query (plugin->dbf, statement); | ||
470 | if (mysql_error (plugin->dbf)[0]) | ||
471 | { | ||
472 | LOG_MYSQL (GNUNET_ERROR_TYPE_ERROR, | ||
473 | "mysql_query", plugin); | ||
474 | iclose (plugin); | ||
475 | return GNUNET_SYSERR; | ||
476 | } | ||
477 | return GNUNET_OK; | ||
478 | } | ||
479 | |||
480 | |||
481 | /** | ||
482 | * Run the given MySQL SELECT statement. The statement | ||
483 | * must have only a single result (one column, one row). | ||
484 | * | ||
485 | * @return result on success, NULL on error | ||
486 | */ | ||
487 | static char * | ||
488 | run_statement_select (struct Plugin *plugin, | ||
489 | const char *statement) | ||
490 | { | ||
491 | MYSQL_RES *sql_res; | ||
492 | MYSQL_ROW sql_row; | ||
493 | char *ret; | ||
494 | |||
495 | if ((NULL == plugin->dbh) && (GNUNET_OK != iopen (plugin))) | ||
496 | return NULL; | ||
497 | mysql_query (plugin->dbf, statement); | ||
498 | if ((mysql_error (plugin->dbf)[0]) || | ||
499 | (!(sql_res = mysql_use_result (plugin->dbf))) || | ||
500 | (!(sql_row = mysql_fetch_row (sql_res)))) | ||
501 | { | ||
502 | LOG_MYSQL (GNUNET_ERROR_TYPE_ERROR, | ||
503 | "mysql_query", plugin); | ||
504 | return NULL; | ||
505 | } | ||
506 | if ((mysql_num_fields (sql_res) != 1) || (sql_row[0] == NULL)) | ||
507 | { | ||
508 | GNUNET_break (mysql_num_fields (sql_res) == 1); | ||
509 | if (sql_res != NULL) | ||
510 | mysql_free_result (sql_res); | ||
511 | return NULL; | ||
512 | } | ||
513 | ret = GNUNET_strdup (sql_row[0]); | ||
514 | mysql_free_result (sql_res); | ||
515 | return ret; | ||
516 | } | ||
517 | |||
518 | |||
519 | /** | ||
520 | * Create a prepared statement. | ||
521 | * | ||
522 | * @return NULL on error | ||
523 | */ | ||
524 | static struct GNUNET_MysqlStatementHandle * | ||
525 | prepared_statement_create (struct Plugin *plugin, | ||
526 | const char *statement) | ||
527 | { | ||
528 | struct GNUNET_MysqlStatementHandle *ret; | ||
529 | |||
530 | ret = GNUNET_malloc (sizeof (struct GNUNET_MysqlStatementHandle)); | ||
531 | ret->query = GNUNET_strdup (statement); | ||
532 | GNUNET_CONTAINER_DLL_insert (plugin->shead, | ||
533 | plugin->stail, | ||
534 | ret); | ||
535 | return ret; | ||
536 | } | ||
537 | |||
538 | |||
539 | /** | ||
540 | * Prepare a statement for running. | ||
541 | * | ||
542 | * @return GNUNET_OK on success | ||
543 | */ | ||
544 | static int | ||
545 | prepare_statement (struct Plugin *plugin, | ||
546 | struct GNUNET_MysqlStatementHandle *ret) | ||
547 | { | ||
548 | if (GNUNET_YES == ret->valid) | ||
549 | return GNUNET_OK; | ||
550 | if ((NULL == plugin->dbh) && | ||
551 | (GNUNET_OK != iopen (plugin))) | ||
552 | return GNUNET_SYSERR; | ||
553 | ret->statement = mysql_stmt_init (plugin->dbf); | ||
554 | if (ret->statement == NULL) | ||
555 | { | ||
556 | iclose (plugin); | ||
557 | return GNUNET_SYSERR; | ||
558 | } | ||
559 | if (mysql_stmt_prepare (ret->statement, | ||
560 | ret->query, | ||
561 | strlen (ret->query))) | ||
562 | { | ||
563 | LOG_MYSQL (GNUNET_ERROR_TYPE_ERROR, | ||
564 | "mysql_stmt_prepare", | ||
565 | plugin); | ||
566 | mysql_stmt_close (ret->statement); | ||
567 | ret->statement = NULL; | ||
568 | iclose (plugin); | ||
569 | return GNUNET_SYSERR; | ||
570 | } | ||
571 | ret->valid = GNUNET_YES; | ||
572 | return GNUNET_OK; | ||
573 | } | ||
574 | |||
575 | |||
576 | /** | ||
577 | * Free a prepared statement. | ||
578 | */ | ||
579 | static void | ||
580 | prepared_statement_destroy (struct Plugin *plugin, | ||
581 | struct GNUNET_MysqlStatementHandle | ||
582 | *s) | ||
583 | { | ||
584 | GNUNET_CONTAINER_DLL_remove (plugin->shead, | ||
585 | plugin->stail, | ||
586 | s); | ||
587 | if (s->valid) | ||
588 | mysql_stmt_close (s->statement); | ||
589 | GNUNET_free (s->query); | ||
590 | GNUNET_free (s); | ||
591 | } | ||
592 | |||
593 | |||
594 | /** | ||
595 | * Bind the parameters for the given MySQL statement | ||
596 | * and run it. | ||
597 | * | ||
598 | * @param s statement to bind and run | ||
599 | * @param ap arguments for the binding | ||
600 | * @return GNUNET_SYSERR on error, GNUNET_OK on success | ||
601 | */ | ||
602 | static int | ||
603 | init_params (struct Plugin *plugin, | ||
604 | struct GNUNET_MysqlStatementHandle *s, | ||
605 | va_list ap) | ||
606 | { | ||
607 | MYSQL_BIND qbind[MAX_PARAM]; | ||
608 | unsigned int pc; | ||
609 | unsigned int off; | ||
610 | enum enum_field_types ft; | ||
611 | |||
612 | pc = mysql_stmt_param_count (s->statement); | ||
613 | if (pc > MAX_PARAM) | ||
614 | { | ||
615 | /* increase internal constant! */ | ||
616 | GNUNET_break (0); | ||
617 | return GNUNET_SYSERR; | ||
618 | } | ||
619 | memset (qbind, 0, sizeof (qbind)); | ||
620 | off = 0; | ||
621 | ft = 0; | ||
622 | while ((pc > 0) && (-1 != (ft = va_arg (ap, enum enum_field_types)))) | ||
623 | { | ||
624 | qbind[off].buffer_type = ft; | ||
625 | switch (ft) | ||
626 | { | ||
627 | case MYSQL_TYPE_FLOAT: | ||
628 | qbind[off].buffer = va_arg (ap, float *); | ||
629 | break; | ||
630 | case MYSQL_TYPE_LONGLONG: | ||
631 | qbind[off].buffer = va_arg (ap, unsigned long long *); | ||
632 | qbind[off].is_unsigned = va_arg (ap, int); | ||
633 | break; | ||
634 | case MYSQL_TYPE_LONG: | ||
635 | qbind[off].buffer = va_arg (ap, unsigned int *); | ||
636 | qbind[off].is_unsigned = va_arg (ap, int); | ||
637 | break; | ||
638 | case MYSQL_TYPE_VAR_STRING: | ||
639 | case MYSQL_TYPE_STRING: | ||
640 | case MYSQL_TYPE_BLOB: | ||
641 | qbind[off].buffer = va_arg (ap, void *); | ||
642 | qbind[off].buffer_length = va_arg (ap, unsigned long); | ||
643 | qbind[off].length = va_arg (ap, unsigned long *); | ||
644 | break; | ||
645 | default: | ||
646 | /* unsupported type */ | ||
647 | GNUNET_break (0); | ||
648 | return GNUNET_SYSERR; | ||
649 | } | ||
650 | pc--; | ||
651 | off++; | ||
652 | } | ||
653 | if (!((pc == 0) && (ft != -1) && (va_arg (ap, int) == -1))) | ||
654 | { | ||
655 | GNUNET_break (0); | ||
656 | return GNUNET_SYSERR; | ||
657 | } | ||
658 | if (mysql_stmt_bind_param (s->statement, qbind)) | ||
659 | { | ||
660 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
661 | _("`%s' failed at %s:%d with error: %s\n"), | ||
662 | "mysql_stmt_bind_param", | ||
663 | __FILE__, __LINE__, mysql_stmt_error (s->statement)); | ||
664 | iclose (plugin); | ||
665 | return GNUNET_SYSERR; | ||
666 | } | ||
667 | if (mysql_stmt_execute (s->statement)) | ||
668 | { | ||
669 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
670 | _("`%s' failed at %s:%d with error: %s\n"), | ||
671 | "mysql_stmt_execute", | ||
672 | __FILE__, __LINE__, mysql_stmt_error (s->statement)); | ||
673 | iclose (plugin); | ||
674 | return GNUNET_SYSERR; | ||
675 | } | ||
676 | return GNUNET_OK; | ||
677 | } | ||
678 | |||
679 | /** | ||
680 | * Type of a callback that will be called for each | ||
681 | * data set returned from MySQL. | ||
682 | * | ||
683 | * @param cls user-defined argument | ||
684 | * @param num_values number of elements in values | ||
685 | * @param values values returned by MySQL | ||
686 | * @return GNUNET_OK to continue iterating, GNUNET_SYSERR to abort | ||
687 | */ | ||
688 | typedef int (*GNUNET_MysqlDataProcessor) (void *cls, | ||
689 | unsigned int num_values, | ||
690 | MYSQL_BIND * values); | ||
691 | |||
692 | |||
693 | /** | ||
694 | * Run a prepared SELECT statement. | ||
695 | * | ||
696 | * @param result_size number of elements in results array | ||
697 | * @param results pointer to already initialized MYSQL_BIND | ||
698 | * array (of sufficient size) for passing results | ||
699 | * @param processor function to call on each result | ||
700 | * @param processor_cls extra argument to processor | ||
701 | * @param ... pairs and triplets of "MYSQL_TYPE_XXX" keys and their respective | ||
702 | * values (size + buffer-reference for pointers); terminated | ||
703 | * with "-1" | ||
704 | * @return GNUNET_SYSERR on error, otherwise | ||
705 | * the number of successfully affected (or queried) rows | ||
706 | */ | ||
707 | static int | ||
708 | prepared_statement_run_select (struct Plugin *plugin, | ||
709 | struct GNUNET_MysqlStatementHandle | ||
710 | *s, | ||
711 | unsigned int result_size, | ||
712 | MYSQL_BIND * results, | ||
713 | GNUNET_MysqlDataProcessor | ||
714 | processor, void *processor_cls, | ||
715 | ...) | ||
716 | { | ||
717 | va_list ap; | ||
718 | int ret; | ||
719 | unsigned int rsize; | ||
720 | int total; | ||
721 | |||
722 | if (GNUNET_OK != prepare_statement (plugin, s)) | ||
723 | { | ||
724 | GNUNET_break (0); | ||
725 | return GNUNET_SYSERR; | ||
726 | } | ||
727 | va_start (ap, processor_cls); | ||
728 | if (GNUNET_OK != init_params (plugin, s, ap)) | ||
729 | { | ||
730 | GNUNET_break (0); | ||
731 | va_end (ap); | ||
732 | return GNUNET_SYSERR; | ||
733 | } | ||
734 | va_end (ap); | ||
735 | rsize = mysql_stmt_field_count (s->statement); | ||
736 | if (rsize > result_size) | ||
737 | { | ||
738 | GNUNET_break (0); | ||
739 | return GNUNET_SYSERR; | ||
740 | } | ||
741 | if (mysql_stmt_bind_result (s->statement, results)) | ||
742 | { | ||
743 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
744 | _("`%s' failed at %s:%d with error: %s\n"), | ||
745 | "mysql_stmt_bind_result", | ||
746 | __FILE__, __LINE__, mysql_stmt_error (s->statement)); | ||
747 | iclose (plugin); | ||
748 | return GNUNET_SYSERR; | ||
749 | } | ||
750 | |||
751 | total = 0; | ||
752 | while (1) | ||
753 | { | ||
754 | ret = mysql_stmt_fetch (s->statement); | ||
755 | if (ret == MYSQL_NO_DATA) | ||
756 | break; | ||
757 | if (ret != 0) | ||
758 | { | ||
759 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
760 | _("`%s' failed at %s:%d with error: %s\n"), | ||
761 | "mysql_stmt_fetch", | ||
762 | __FILE__, __LINE__, mysql_stmt_error (s->statement)); | ||
763 | iclose (plugin); | ||
764 | return GNUNET_SYSERR; | ||
765 | } | ||
766 | if (processor != NULL) | ||
767 | if (GNUNET_OK != processor (processor_cls, rsize, results)) | ||
768 | break; | ||
769 | total++; | ||
770 | } | ||
771 | mysql_stmt_reset (s->statement); | ||
772 | return total; | ||
773 | } | ||
774 | |||
775 | |||
776 | /** | ||
777 | * Run a prepared statement that does NOT produce results. | ||
778 | * | ||
779 | * @param ... pairs and triplets of "MYSQL_TYPE_XXX" keys and their respective | ||
780 | * values (size + buffer-reference for pointers); terminated | ||
781 | * with "-1" | ||
782 | * @param insert_id NULL or address where to store the row ID of whatever | ||
783 | * was inserted (only for INSERT statements!) | ||
784 | * @return GNUNET_SYSERR on error, otherwise | ||
785 | * the number of successfully affected rows | ||
786 | */ | ||
787 | static int | ||
788 | prepared_statement_run (struct Plugin *plugin, | ||
789 | struct GNUNET_MysqlStatementHandle *s, | ||
790 | unsigned long long *insert_id, ...) | ||
791 | { | ||
792 | va_list ap; | ||
793 | int affected; | ||
794 | |||
795 | if (GNUNET_OK != prepare_statement (plugin, s)) | ||
796 | return GNUNET_SYSERR; | ||
797 | va_start (ap, insert_id); | ||
798 | if (GNUNET_OK != init_params (plugin, s, ap)) | ||
799 | { | ||
800 | va_end (ap); | ||
801 | return GNUNET_SYSERR; | ||
802 | } | ||
803 | va_end (ap); | ||
804 | affected = mysql_stmt_affected_rows (s->statement); | ||
805 | if (NULL != insert_id) | ||
806 | *insert_id = (unsigned long long) mysql_stmt_insert_id (s->statement); | ||
807 | mysql_stmt_reset (s->statement); | ||
808 | return affected; | ||
809 | } | ||
810 | |||
811 | |||
812 | |||
813 | |||
814 | /** | ||
815 | * Delete an value from the gn072 table. | ||
816 | * | ||
817 | * @param vkey vkey identifying the value to delete | ||
818 | * @return GNUNET_OK on success, GNUNET_NO if no such value exists, GNUNET_SYSERR on error | ||
819 | */ | ||
820 | static int | ||
821 | do_delete_value (unsigned long long vkey) | ||
822 | { | ||
823 | int ret; | ||
824 | |||
825 | ret = GNUNET_MYSQL_prepared_statement_run (delete_value, | ||
826 | NULL, | ||
827 | MYSQL_TYPE_LONGLONG, | ||
828 | &vkey, GNUNET_YES, -1); | ||
829 | if (ret > 0) | ||
830 | ret = GNUNET_OK; | ||
831 | return ret; | ||
832 | } | ||
833 | |||
834 | /** | ||
835 | * Insert a value into the gn072 table. | ||
836 | * | ||
837 | * @param value the value to insert | ||
838 | * @param size size of the value | ||
839 | * @param vkey vkey identifying the value henceforth (set) | ||
840 | * @return GNUNET_OK on success, GNUNET_SYSERR on error | ||
841 | */ | ||
842 | static int | ||
843 | do_insert_value (const void *value, unsigned int size, | ||
844 | unsigned long long *vkey) | ||
845 | { | ||
846 | unsigned long length = size; | ||
847 | |||
848 | return GNUNET_MYSQL_prepared_statement_run (insert_value, | ||
849 | vkey, | ||
850 | MYSQL_TYPE_BLOB, | ||
851 | value, length, &length, -1); | ||
852 | } | ||
853 | |||
854 | /** | ||
855 | * Delete an entry from the gn080 table. | ||
856 | * | ||
857 | * @param vkey vkey identifying the entry to delete | ||
858 | * @return GNUNET_OK on success, GNUNET_NO if no such value exists, GNUNET_SYSERR on error | ||
859 | */ | ||
860 | static int | ||
861 | do_delete_entry_by_vkey (unsigned long long vkey) | ||
862 | { | ||
863 | int ret; | ||
864 | |||
865 | ret = GNUNET_MYSQL_prepared_statement_run (delete_entry_by_vkey, | ||
866 | NULL, | ||
867 | MYSQL_TYPE_LONGLONG, | ||
868 | &vkey, GNUNET_YES, -1); | ||
869 | if (ret > 0) | ||
870 | ret = GNUNET_OK; | ||
871 | return ret; | ||
872 | } | ||
873 | |||
874 | static int | ||
875 | return_ok (void *cls, unsigned int num_values, MYSQL_BIND * values) | ||
876 | { | ||
877 | return GNUNET_OK; | ||
878 | } | ||
879 | |||
880 | /** | ||
881 | * Given a full (SELECT *) result set from gn080 table, | ||
882 | * assemble it into a GNUNET_DatastoreValue representation. | ||
883 | * | ||
884 | * Call *without* holding the lock, but while within | ||
885 | * mysql_thread_start/end. | ||
886 | * | ||
887 | * @param result location where mysql_stmt_fetch stored the results | ||
888 | * @return NULL on error | ||
889 | */ | ||
890 | static GNUNET_DatastoreValue * | ||
891 | assembleDatum (MYSQL_BIND * result) | ||
892 | { | ||
893 | GNUNET_DatastoreValue *datum; | ||
894 | unsigned int contentSize; | ||
895 | unsigned int type; | ||
896 | unsigned int prio; | ||
897 | unsigned int level; | ||
898 | unsigned long long exp; | ||
899 | unsigned long long vkey; | ||
900 | unsigned long length; | ||
901 | MYSQL_BIND rbind[1]; | ||
902 | int ret; | ||
903 | |||
904 | if ((result[0].buffer_type != MYSQL_TYPE_LONG) || | ||
905 | (!result[0].is_unsigned) || | ||
906 | (result[1].buffer_type != MYSQL_TYPE_LONG) || | ||
907 | (!result[1].is_unsigned) || | ||
908 | (result[2].buffer_type != MYSQL_TYPE_LONG) || | ||
909 | (!result[2].is_unsigned) || | ||
910 | (result[3].buffer_type != MYSQL_TYPE_LONG) || | ||
911 | (!result[3].is_unsigned) || | ||
912 | (result[4].buffer_type != MYSQL_TYPE_LONGLONG) || | ||
913 | (!result[4].is_unsigned) || | ||
914 | (result[5].buffer_type != MYSQL_TYPE_BLOB) || | ||
915 | (result[5].buffer_length != sizeof (GNUNET_HashCode)) || | ||
916 | (*result[5].length != sizeof (GNUNET_HashCode)) || | ||
917 | (result[6].buffer_type != MYSQL_TYPE_LONGLONG) || | ||
918 | (!result[6].is_unsigned)) | ||
919 | { | ||
920 | GNUNET_break (0); | ||
921 | return NULL; /* error */ | ||
922 | } | ||
923 | |||
924 | contentSize = *(unsigned int *) result[0].buffer; | ||
925 | if (contentSize < sizeof (GNUNET_DatastoreValue)) | ||
926 | return NULL; /* error */ | ||
927 | if (contentSize > GNUNET_MAX_BUFFER_SIZE) | ||
928 | { | ||
929 | GNUNET_break (0); /* far too big */ | ||
930 | return NULL; | ||
931 | } | ||
932 | contentSize -= sizeof (GNUNET_DatastoreValue); | ||
933 | type = *(unsigned int *) result[1].buffer; | ||
934 | prio = *(unsigned int *) result[2].buffer; | ||
935 | level = *(unsigned int *) result[3].buffer; | ||
936 | exp = *(unsigned long long *) result[4].buffer; | ||
937 | vkey = *(unsigned long long *) result[6].buffer; | ||
938 | datum = GNUNET_malloc (sizeof (GNUNET_DatastoreValue) + contentSize); | ||
939 | datum->size = htonl (contentSize + sizeof (GNUNET_DatastoreValue)); | ||
940 | datum->type = htonl (type); | ||
941 | datum->priority = htonl (prio); | ||
942 | datum->anonymity_level = htonl (level); | ||
943 | datum->expiration_time = GNUNET_htonll (exp); | ||
944 | |||
945 | /* now do query on gn072 */ | ||
946 | length = contentSize; | ||
947 | memset (rbind, 0, sizeof (rbind)); | ||
948 | rbind[0].buffer_type = MYSQL_TYPE_BLOB; | ||
949 | rbind[0].buffer_length = contentSize; | ||
950 | rbind[0].length = &length; | ||
951 | rbind[0].buffer = &datum[1]; | ||
952 | ret = GNUNET_MYSQL_prepared_statement_run_select (select_value, | ||
953 | 1, | ||
954 | rbind, | ||
955 | &return_ok, | ||
956 | NULL, | ||
957 | MYSQL_TYPE_LONGLONG, | ||
958 | &vkey, GNUNET_YES, -1); | ||
959 | GNUNET_break (ret <= 1); /* should only have one result! */ | ||
960 | if (ret > 0) | ||
961 | ret = GNUNET_OK; | ||
962 | if ((ret != GNUNET_OK) || | ||
963 | (rbind[0].buffer_length != contentSize) || (length != contentSize)) | ||
964 | { | ||
965 | GNUNET_break (ret != 0); /* should have one result! */ | ||
966 | GNUNET_break (length == contentSize); /* length should match! */ | ||
967 | GNUNET_break (rbind[0].buffer_length == contentSize); /* length should be internally consistent! */ | ||
968 | do_delete_value (vkey); | ||
969 | if (ret != 0) | ||
970 | do_delete_entry_by_vkey (vkey); | ||
971 | content_size -= ntohl (datum->size); | ||
972 | GNUNET_free (datum); | ||
973 | return NULL; | ||
974 | } | ||
975 | return datum; | ||
976 | } | ||
977 | |||
978 | |||
979 | /** | ||
980 | * Iterate over the items in the datastore | ||
981 | * using the given query to select and order | ||
982 | * the items. | ||
983 | * | ||
984 | * @param type entries of which type should be considered? | ||
985 | * Use 0 for any type. | ||
986 | * @param iter never NULL | ||
987 | * @param is_asc are we using ascending order? | ||
988 | * @return the number of results, GNUNET_SYSERR if the | ||
989 | * iter is non-NULL and aborted the iteration | ||
990 | */ | ||
991 | static int | ||
992 | iterateHelper (struct Plugin *plugin, | ||
993 | unsigned int type, | ||
994 | int is_asc, | ||
995 | unsigned int iter_select, GNUNET_DatastoreValueIterator dviter, | ||
996 | void *closure) | ||
997 | { | ||
998 | GNUNET_DatastoreValue *datum; | ||
999 | int count; | ||
1000 | int ret; | ||
1001 | unsigned int last_prio; | ||
1002 | unsigned long long last_expire; | ||
1003 | unsigned long long last_vkey; | ||
1004 | unsigned int size; | ||
1005 | unsigned int rtype; | ||
1006 | unsigned int prio; | ||
1007 | unsigned int level; | ||
1008 | unsigned long long expiration; | ||
1009 | unsigned long long vkey; | ||
1010 | unsigned long hashSize; | ||
1011 | GNUNET_HashCode key; | ||
1012 | GNUNET_CronTime now; | ||
1013 | MYSQL_BIND rbind[7]; | ||
1014 | |||
1015 | if (is_asc) | ||
1016 | { | ||
1017 | last_prio = 0; | ||
1018 | last_vkey = 0; | ||
1019 | last_expire = 0; | ||
1020 | } | ||
1021 | else | ||
1022 | { | ||
1023 | last_prio = 0x7FFFFFFFL; | ||
1024 | last_vkey = 0x7FFFFFFFFFFFFFFFLL; /* MySQL only supports 63 bits */ | ||
1025 | last_expire = 0x7FFFFFFFFFFFFFFFLL; /* MySQL only supports 63 bits */ | ||
1026 | } | ||
1027 | hashSize = sizeof (GNUNET_HashCode); | ||
1028 | memset (rbind, 0, sizeof (rbind)); | ||
1029 | rbind[0].buffer_type = MYSQL_TYPE_LONG; | ||
1030 | rbind[0].buffer = &size; | ||
1031 | rbind[0].is_unsigned = 1; | ||
1032 | rbind[1].buffer_type = MYSQL_TYPE_LONG; | ||
1033 | rbind[1].buffer = &rtype; | ||
1034 | rbind[1].is_unsigned = 1; | ||
1035 | rbind[2].buffer_type = MYSQL_TYPE_LONG; | ||
1036 | rbind[2].buffer = &prio; | ||
1037 | rbind[2].is_unsigned = 1; | ||
1038 | rbind[3].buffer_type = MYSQL_TYPE_LONG; | ||
1039 | rbind[3].buffer = &level; | ||
1040 | rbind[3].is_unsigned = 1; | ||
1041 | rbind[4].buffer_type = MYSQL_TYPE_LONGLONG; | ||
1042 | rbind[4].buffer = &expiration; | ||
1043 | rbind[4].is_unsigned = 1; | ||
1044 | rbind[5].buffer_type = MYSQL_TYPE_BLOB; | ||
1045 | rbind[5].buffer = &key; | ||
1046 | rbind[5].buffer_length = hashSize; | ||
1047 | rbind[5].length = &hashSize; | ||
1048 | rbind[6].buffer_type = MYSQL_TYPE_LONGLONG; | ||
1049 | rbind[6].buffer = &vkey; | ||
1050 | rbind[6].is_unsigned = GNUNET_YES; | ||
1051 | |||
1052 | now = GNUNET_get_time (); | ||
1053 | count = 0; | ||
1054 | while (1) | ||
1055 | { | ||
1056 | switch (iter_select) | ||
1057 | { | ||
1058 | case 0: | ||
1059 | case 1: | ||
1060 | ret = prepared_statement_run_select (iter[iter_select], | ||
1061 | 7, | ||
1062 | rbind, | ||
1063 | &return_ok, | ||
1064 | NULL, | ||
1065 | MYSQL_TYPE_LONG, | ||
1066 | &last_prio, | ||
1067 | GNUNET_YES, | ||
1068 | MYSQL_TYPE_LONGLONG, | ||
1069 | &last_vkey, | ||
1070 | GNUNET_YES, | ||
1071 | MYSQL_TYPE_LONG, | ||
1072 | &last_prio, | ||
1073 | GNUNET_YES, | ||
1074 | MYSQL_TYPE_LONGLONG, | ||
1075 | &last_vkey, | ||
1076 | GNUNET_YES, -1); | ||
1077 | break; | ||
1078 | case 2: | ||
1079 | ret = prepared_statement_run_select (iter[iter_select], | ||
1080 | 7, | ||
1081 | rbind, | ||
1082 | &return_ok, | ||
1083 | NULL, | ||
1084 | MYSQL_TYPE_LONGLONG, | ||
1085 | &last_expire, | ||
1086 | GNUNET_YES, | ||
1087 | MYSQL_TYPE_LONGLONG, | ||
1088 | &last_vkey, | ||
1089 | GNUNET_YES, | ||
1090 | MYSQL_TYPE_LONGLONG, | ||
1091 | &last_expire, | ||
1092 | GNUNET_YES, | ||
1093 | MYSQL_TYPE_LONGLONG, | ||
1094 | &last_vkey, | ||
1095 | GNUNET_YES, -1); | ||
1096 | break; | ||
1097 | case 3: | ||
1098 | ret = prepared_statement_run_select (iter[iter_select], | ||
1099 | 7, | ||
1100 | rbind, | ||
1101 | &return_ok, | ||
1102 | NULL, | ||
1103 | MYSQL_TYPE_LONGLONG, | ||
1104 | &last_expire, | ||
1105 | GNUNET_YES, | ||
1106 | MYSQL_TYPE_LONGLONG, | ||
1107 | &last_vkey, | ||
1108 | GNUNET_YES, | ||
1109 | MYSQL_TYPE_LONGLONG, | ||
1110 | &now, | ||
1111 | GNUNET_YES, | ||
1112 | MYSQL_TYPE_LONGLONG, | ||
1113 | &last_expire, | ||
1114 | GNUNET_YES, | ||
1115 | MYSQL_TYPE_LONGLONG, | ||
1116 | &last_vkey, | ||
1117 | GNUNET_YES, | ||
1118 | MYSQL_TYPE_LONGLONG, | ||
1119 | &now, | ||
1120 | GNUNET_YES, -1); | ||
1121 | break; | ||
1122 | default: | ||
1123 | GNUNET_break (0); | ||
1124 | return GNUNET_SYSERR; | ||
1125 | } | ||
1126 | if (ret != GNUNET_OK) | ||
1127 | break; | ||
1128 | last_vkey = vkey; | ||
1129 | last_prio = prio; | ||
1130 | last_expire = expiration; | ||
1131 | count++; | ||
1132 | if (dviter != NULL) | ||
1133 | { | ||
1134 | datum = assembleDatum (rbind); | ||
1135 | if (datum == NULL) | ||
1136 | continue; | ||
1137 | ret = dviter (&key, datum, closure, vkey); | ||
1138 | if (ret == GNUNET_SYSERR) | ||
1139 | { | ||
1140 | GNUNET_free (datum); | ||
1141 | break; | ||
1142 | } | ||
1143 | if (ret == GNUNET_NO) | ||
1144 | { | ||
1145 | do_delete_value (vkey); | ||
1146 | do_delete_entry_by_vkey (vkey); | ||
1147 | content_size -= ntohl (datum->size); | ||
1148 | } | ||
1149 | GNUNET_free (datum); | ||
1150 | } | ||
1151 | } | ||
1152 | return count; | ||
1153 | } | ||
1154 | |||
1155 | |||
1156 | |||
1157 | /** | ||
1158 | * Get an estimate of how much space the database is | ||
1159 | * currently using. | ||
1160 | * | ||
1161 | * @param cls our "struct Plugin*" | ||
1162 | * @return number of bytes used on disk | ||
1163 | */ | ||
1164 | static unsigned long long | ||
1165 | mysql_plugin_get_size (void *cls) | ||
1166 | { | ||
1167 | struct Plugin *plugin = cls; | ||
1168 | return plugin->content_size; | ||
1169 | } | ||
1170 | |||
1171 | |||
1172 | /** | ||
1173 | * Store an item in the datastore. | ||
1174 | * | ||
1175 | * @param cls closure | ||
1176 | * @param key key for the item | ||
1177 | * @param size number of bytes in data | ||
1178 | * @param data content stored | ||
1179 | * @param type type of the content | ||
1180 | * @param priority priority of the content | ||
1181 | * @param anonymity anonymity-level for the content | ||
1182 | * @param expiration expiration time for the content | ||
1183 | * @param msg set to error message | ||
1184 | * @return GNUNET_OK on success | ||
1185 | */ | ||
1186 | static int | ||
1187 | mysql_plugin_put (void *cls, | ||
1188 | const GNUNET_HashCode * key, | ||
1189 | uint32_t size, | ||
1190 | const void *data, | ||
1191 | enum GNUNET_BLOCK_Type type, | ||
1192 | uint32_t priority, | ||
1193 | uint32_t anonymity, | ||
1194 | struct GNUNET_TIME_Absolute expiration, | ||
1195 | char **msg) | ||
1196 | { | ||
1197 | struct Plugin *plugin = cls; | ||
1198 | |||
1199 | unsigned long contentSize; | ||
1200 | unsigned long hashSize; | ||
1201 | unsigned long hashSize2; | ||
1202 | unsigned int size; | ||
1203 | unsigned int type; | ||
1204 | unsigned int prio; | ||
1205 | unsigned int level; | ||
1206 | unsigned long long expiration; | ||
1207 | unsigned long long vkey; | ||
1208 | GNUNET_HashCode vhash; | ||
1209 | |||
1210 | if (((ntohl (value->size) < sizeof (GNUNET_DatastoreValue))) || | ||
1211 | ((ntohl (value->size) - sizeof (GNUNET_DatastoreValue)) > | ||
1212 | MAX_DATUM_SIZE)) | ||
1213 | { | ||
1214 | GNUNET_break (0); | ||
1215 | return GNUNET_SYSERR; | ||
1216 | } | ||
1217 | hashSize = sizeof (GNUNET_HashCode); | ||
1218 | hashSize2 = sizeof (GNUNET_HashCode); | ||
1219 | size = ntohl (value->size); | ||
1220 | type = ntohl (value->type); | ||
1221 | prio = ntohl (value->priority); | ||
1222 | level = ntohl (value->anonymity_level); | ||
1223 | expiration = GNUNET_ntohll (value->expiration_time); | ||
1224 | contentSize = ntohl (value->size) - sizeof (GNUNET_DatastoreValue); | ||
1225 | GNUNET_hash (&value[1], contentSize, &vhash); | ||
1226 | |||
1227 | if (GNUNET_OK != do_insert_value (&value[1], contentSize, &vkey)) | ||
1228 | return GNUNET_SYSERR; | ||
1229 | if (GNUNET_OK != | ||
1230 | prepared_statement_run (insert_entry, | ||
1231 | NULL, | ||
1232 | MYSQL_TYPE_LONG, | ||
1233 | &size, | ||
1234 | GNUNET_YES, | ||
1235 | MYSQL_TYPE_LONG, | ||
1236 | &type, | ||
1237 | GNUNET_YES, | ||
1238 | MYSQL_TYPE_LONG, | ||
1239 | &prio, | ||
1240 | GNUNET_YES, | ||
1241 | MYSQL_TYPE_LONG, | ||
1242 | &level, | ||
1243 | GNUNET_YES, | ||
1244 | MYSQL_TYPE_LONGLONG, | ||
1245 | &expiration, | ||
1246 | GNUNET_YES, | ||
1247 | MYSQL_TYPE_BLOB, | ||
1248 | key, | ||
1249 | hashSize, | ||
1250 | &hashSize, | ||
1251 | MYSQL_TYPE_BLOB, | ||
1252 | &vhash, | ||
1253 | hashSize2, | ||
1254 | &hashSize2, | ||
1255 | MYSQL_TYPE_LONGLONG, | ||
1256 | &vkey, GNUNET_YES, -1)) | ||
1257 | { | ||
1258 | do_delete_value (vkey); | ||
1259 | return GNUNET_SYSERR; | ||
1260 | } | ||
1261 | plugin->content_size += ntohl (value->size); | ||
1262 | return GNUNET_OK; | ||
1263 | } | ||
1264 | |||
1265 | |||
1266 | /** | ||
1267 | * Function invoked on behalf of a "PluginIterator" | ||
1268 | * asking the database plugin to call the iterator | ||
1269 | * with the next item. | ||
1270 | * | ||
1271 | * @param next_cls whatever argument was given | ||
1272 | * to the PluginIterator as "next_cls". | ||
1273 | * @param end_it set to GNUNET_YES if we | ||
1274 | * should terminate the iteration early | ||
1275 | * (iterator should be still called once more | ||
1276 | * to signal the end of the iteration). | ||
1277 | */ | ||
1278 | static void | ||
1279 | mysql_plugin_next_request (void *next_cls, | ||
1280 | int end_it) | ||
1281 | { | ||
1282 | struct Plugin *plugin = cls; | ||
1283 | GNUNET_break (0); | ||
1284 | } | ||
1285 | |||
1286 | |||
1287 | /** | ||
1288 | * Iterate over the results for a particular key | ||
1289 | * in the datastore. | ||
1290 | * | ||
1291 | * @param cls closure | ||
1292 | * @param key maybe NULL (to match all entries) | ||
1293 | * @param vhash hash of the value, maybe NULL (to | ||
1294 | * match all values that have the right key). | ||
1295 | * Note that for DBlocks there is no difference | ||
1296 | * betwen key and vhash, but for other blocks | ||
1297 | * there may be! | ||
1298 | * @param type entries of which type are relevant? | ||
1299 | * Use 0 for any type. | ||
1300 | * @param iter function to call on each matching value; | ||
1301 | * will be called once with a NULL value at the end | ||
1302 | * @param iter_cls closure for iter | ||
1303 | */ | ||
1304 | static void | ||
1305 | mysql_plugin_get (void *cls, | ||
1306 | const GNUNET_HashCode * key, | ||
1307 | const GNUNET_HashCode * vhash, | ||
1308 | enum GNUNET_BLOCK_Type type, | ||
1309 | PluginIterator iter, void *iter_cls) | ||
1310 | { | ||
1311 | struct Plugin *plugin = cls; | ||
1312 | int count; | ||
1313 | unsigned long long total; | ||
1314 | int off; | ||
1315 | int ret; | ||
1316 | unsigned int size; | ||
1317 | unsigned int rtype; | ||
1318 | unsigned int prio; | ||
1319 | unsigned int level; | ||
1320 | unsigned int limit_off; | ||
1321 | unsigned long long expiration; | ||
1322 | unsigned long long vkey; | ||
1323 | unsigned long long last_vkey; | ||
1324 | GNUNET_DatastoreValue *datum; | ||
1325 | GNUNET_HashCode key; | ||
1326 | unsigned long hashSize; | ||
1327 | unsigned long hashSize2; | ||
1328 | MYSQL_BIND rbind[7]; | ||
1329 | |||
1330 | if (key == NULL) | ||
1331 | return iterateLowPriority (type, iter, closure); | ||
1332 | hashSize = sizeof (GNUNET_HashCode); | ||
1333 | hashSize2 = sizeof (GNUNET_HashCode); | ||
1334 | memset (rbind, 0, sizeof (rbind)); | ||
1335 | total = -1; | ||
1336 | rbind[0].buffer_type = MYSQL_TYPE_LONGLONG; | ||
1337 | rbind[0].buffer = &total; | ||
1338 | rbind[0].is_unsigned = GNUNET_YES; | ||
1339 | if (type != 0) | ||
1340 | { | ||
1341 | if (vhash != NULL) | ||
1342 | { | ||
1343 | ret = | ||
1344 | prepared_statement_run_select | ||
1345 | (count_entry_by_hash_vhash_and_type, 1, rbind, &return_ok, NULL, | ||
1346 | MYSQL_TYPE_BLOB, key, hashSize2, &hashSize2, MYSQL_TYPE_BLOB, | ||
1347 | vhash, hashSize2, &hashSize2, MYSQL_TYPE_LONG, &type, GNUNET_YES, | ||
1348 | -1); | ||
1349 | } | ||
1350 | else | ||
1351 | { | ||
1352 | ret = | ||
1353 | prepared_statement_run_select | ||
1354 | (count_entry_by_hash_and_type, 1, rbind, &return_ok, NULL, | ||
1355 | MYSQL_TYPE_BLOB, key, hashSize2, &hashSize2, MYSQL_TYPE_LONG, | ||
1356 | &type, GNUNET_YES, -1); | ||
1357 | |||
1358 | } | ||
1359 | } | ||
1360 | else | ||
1361 | { | ||
1362 | if (vhash != NULL) | ||
1363 | { | ||
1364 | ret = | ||
1365 | prepared_statement_run_select | ||
1366 | (count_entry_by_hash_and_vhash, 1, rbind, &return_ok, NULL, | ||
1367 | MYSQL_TYPE_BLOB, key, hashSize2, &hashSize2, MYSQL_TYPE_BLOB, | ||
1368 | vhash, hashSize2, &hashSize2, -1); | ||
1369 | |||
1370 | } | ||
1371 | else | ||
1372 | { | ||
1373 | ret = | ||
1374 | GNUNET_MYSQL_prepared_statement_run_select (count_entry_by_hash, | ||
1375 | 1, rbind, &return_ok, | ||
1376 | NULL, MYSQL_TYPE_BLOB, | ||
1377 | key, hashSize2, | ||
1378 | &hashSize2, -1); | ||
1379 | } | ||
1380 | } | ||
1381 | if ((ret != GNUNET_OK) || (-1 == total)) | ||
1382 | return GNUNET_SYSERR; | ||
1383 | if ((iter == NULL) || (total == 0)) | ||
1384 | return (int) total; | ||
1385 | |||
1386 | last_vkey = 0; | ||
1387 | count = 0; | ||
1388 | off = GNUNET_random_u32 (GNUNET_RANDOM_QUALITY_WEAK, total); | ||
1389 | |||
1390 | memset (rbind, 0, sizeof (rbind)); | ||
1391 | rbind[0].buffer_type = MYSQL_TYPE_LONG; | ||
1392 | rbind[0].buffer = &size; | ||
1393 | rbind[0].is_unsigned = GNUNET_YES; | ||
1394 | rbind[1].buffer_type = MYSQL_TYPE_LONG; | ||
1395 | rbind[1].buffer = &rtype; | ||
1396 | rbind[1].is_unsigned = GNUNET_YES; | ||
1397 | rbind[2].buffer_type = MYSQL_TYPE_LONG; | ||
1398 | rbind[2].buffer = &prio; | ||
1399 | rbind[2].is_unsigned = GNUNET_YES; | ||
1400 | rbind[3].buffer_type = MYSQL_TYPE_LONG; | ||
1401 | rbind[3].buffer = &level; | ||
1402 | rbind[3].is_unsigned = GNUNET_YES; | ||
1403 | rbind[4].buffer_type = MYSQL_TYPE_LONGLONG; | ||
1404 | rbind[4].buffer = &expiration; | ||
1405 | rbind[4].is_unsigned = GNUNET_YES; | ||
1406 | rbind[5].buffer_type = MYSQL_TYPE_BLOB; | ||
1407 | rbind[5].buffer = &key; | ||
1408 | rbind[5].buffer_length = hashSize; | ||
1409 | rbind[5].length = &hashSize; | ||
1410 | rbind[6].buffer_type = MYSQL_TYPE_LONGLONG; | ||
1411 | rbind[6].buffer = &vkey; | ||
1412 | rbind[6].is_unsigned = GNUNET_YES; | ||
1413 | while (1) | ||
1414 | { | ||
1415 | if (count == 0) | ||
1416 | limit_off = off; | ||
1417 | else | ||
1418 | limit_off = 0; | ||
1419 | if (type != 0) | ||
1420 | { | ||
1421 | if (vhash != NULL) | ||
1422 | { | ||
1423 | ret = | ||
1424 | prepared_statement_run_select | ||
1425 | (select_entry_by_hash_vhash_and_type, 7, rbind, &return_ok, | ||
1426 | NULL, MYSQL_TYPE_BLOB, key, hashSize, &hashSize, | ||
1427 | MYSQL_TYPE_BLOB, vhash, hashSize2, &hashSize2, | ||
1428 | MYSQL_TYPE_LONGLONG, &last_vkey, GNUNET_YES, MYSQL_TYPE_LONG, | ||
1429 | &type, GNUNET_YES, MYSQL_TYPE_LONG, &limit_off, GNUNET_YES, | ||
1430 | -1); | ||
1431 | } | ||
1432 | else | ||
1433 | { | ||
1434 | ret = | ||
1435 | prepared_statement_run_select | ||
1436 | (select_entry_by_hash_and_type, 7, rbind, &return_ok, NULL, | ||
1437 | MYSQL_TYPE_BLOB, key, hashSize, &hashSize, | ||
1438 | MYSQL_TYPE_LONGLONG, &last_vkey, GNUNET_YES, MYSQL_TYPE_LONG, | ||
1439 | &type, GNUNET_YES, MYSQL_TYPE_LONG, &limit_off, GNUNET_YES, | ||
1440 | -1); | ||
1441 | } | ||
1442 | } | ||
1443 | else | ||
1444 | { | ||
1445 | if (vhash != NULL) | ||
1446 | { | ||
1447 | ret = | ||
1448 | prepared_statement_run_select | ||
1449 | (select_entry_by_hash_and_vhash, 7, rbind, &return_ok, NULL, | ||
1450 | MYSQL_TYPE_BLOB, key, hashSize, &hashSize, MYSQL_TYPE_BLOB, | ||
1451 | vhash, hashSize2, &hashSize2, MYSQL_TYPE_LONGLONG, | ||
1452 | &last_vkey, GNUNET_YES, MYSQL_TYPE_LONG, &limit_off, | ||
1453 | GNUNET_YES, -1); | ||
1454 | } | ||
1455 | else | ||
1456 | { | ||
1457 | ret = | ||
1458 | prepared_statement_run_select | ||
1459 | (select_entry_by_hash, 7, rbind, &return_ok, NULL, | ||
1460 | MYSQL_TYPE_BLOB, key, hashSize, &hashSize, | ||
1461 | MYSQL_TYPE_LONGLONG, &last_vkey, GNUNET_YES, MYSQL_TYPE_LONG, | ||
1462 | &limit_off, GNUNET_YES, -1); | ||
1463 | } | ||
1464 | } | ||
1465 | if (ret != GNUNET_OK) | ||
1466 | break; | ||
1467 | last_vkey = vkey; | ||
1468 | datum = assembleDatum (rbind); | ||
1469 | if (datum == NULL) | ||
1470 | continue; | ||
1471 | count++; | ||
1472 | ret = iter (&key, datum, closure, vkey); | ||
1473 | if (ret == GNUNET_SYSERR) | ||
1474 | { | ||
1475 | GNUNET_free (datum); | ||
1476 | break; | ||
1477 | } | ||
1478 | if (ret == GNUNET_NO) | ||
1479 | { | ||
1480 | do_delete_value (vkey); | ||
1481 | do_delete_entry_by_vkey (vkey); | ||
1482 | content_size -= ntohl (datum->size); | ||
1483 | } | ||
1484 | GNUNET_free (datum); | ||
1485 | if (count + off == total) | ||
1486 | last_vkey = 0; /* back to start */ | ||
1487 | if (count == total) | ||
1488 | break; | ||
1489 | } | ||
1490 | } | ||
1491 | |||
1492 | |||
1493 | /** | ||
1494 | * Update the priority for a particular key in the datastore. If | ||
1495 | * the expiration time in value is different than the time found in | ||
1496 | * the datastore, the higher value should be kept. For the | ||
1497 | * anonymity level, the lower value is to be used. The specified | ||
1498 | * priority should be added to the existing priority, ignoring the | ||
1499 | * priority in value. | ||
1500 | * | ||
1501 | * Note that it is possible for multiple values to match this put. | ||
1502 | * In that case, all of the respective values are updated. | ||
1503 | * | ||
1504 | * @param cls our "struct Plugin*" | ||
1505 | * @param uid unique identifier of the datum | ||
1506 | * @param delta by how much should the priority | ||
1507 | * change? If priority + delta < 0 the | ||
1508 | * priority should be set to 0 (never go | ||
1509 | * negative). | ||
1510 | * @param expire new expiration time should be the | ||
1511 | * MAX of any existing expiration time and | ||
1512 | * this value | ||
1513 | * @param msg set to error message | ||
1514 | * @return GNUNET_OK on success | ||
1515 | */ | ||
1516 | static int | ||
1517 | mysql_plugin_update (void *cls, | ||
1518 | uint64_t uid, | ||
1519 | int delta, | ||
1520 | struct GNUNET_TIME_Absolute expire, | ||
1521 | char **msg) | ||
1522 | { | ||
1523 | struct Plugin *plugin = cls; | ||
1524 | unsigned long long vkey = uid; | ||
1525 | |||
1526 | return prepared_statement_run (plugin, | ||
1527 | plugin->update_entry, | ||
1528 | NULL, | ||
1529 | MYSQL_TYPE_LONG, | ||
1530 | &delta, | ||
1531 | GNUNET_NO, | ||
1532 | MYSQL_TYPE_LONGLONG, | ||
1533 | &expire.value, | ||
1534 | GNUNET_YES, | ||
1535 | MYSQL_TYPE_LONGLONG, | ||
1536 | &expire.value, | ||
1537 | GNUNET_YES, | ||
1538 | MYSQL_TYPE_LONGLONG, | ||
1539 | &vkey, | ||
1540 | GNUNET_YES, -1); | ||
1541 | } | ||
1542 | |||
1543 | |||
1544 | /** | ||
1545 | * Select a subset of the items in the datastore and call | ||
1546 | * the given iterator for each of them. | ||
1547 | * | ||
1548 | * @param cls our "struct Plugin*" | ||
1549 | * @param type entries of which type should be considered? | ||
1550 | * Use 0 for any type. | ||
1551 | * @param iter function to call on each matching value; | ||
1552 | * will be called once with a NULL value at the end | ||
1553 | * @param iter_cls closure for iter | ||
1554 | */ | ||
1555 | static void | ||
1556 | mysql_plugin_iter_low_priority (void *cls, | ||
1557 | enum GNUNET_BLOCK_Type type, | ||
1558 | PluginIterator iter, | ||
1559 | void *iter_cls) | ||
1560 | { | ||
1561 | struct Plugin *plugin = cls; | ||
1562 | iterateHelper (plugin, type, GNUNET_YES, | ||
1563 | 0, iter, iter_cls); | ||
1564 | } | ||
1565 | |||
1566 | |||
1567 | /** | ||
1568 | * Select a subset of the items in the datastore and call | ||
1569 | * the given iterator for each of them. | ||
1570 | * | ||
1571 | * @param cls our "struct Plugin*" | ||
1572 | * @param type entries of which type should be considered? | ||
1573 | * Use 0 for any type. | ||
1574 | * @param iter function to call on each matching value; | ||
1575 | * will be called once with a NULL value at the end | ||
1576 | * @param iter_cls closure for iter | ||
1577 | */ | ||
1578 | static void | ||
1579 | mysql_plugin_iter_zero_anonymity (void *cls, | ||
1580 | enum GNUNET_BLOCK_Type type, | ||
1581 | PluginIterator iter, | ||
1582 | void *iter_cls) | ||
1583 | { | ||
1584 | struct Plugin *plugin = cls; | ||
1585 | iterateHelper (plugin, type, GNUNET_NO, 1, iter, closure); | ||
1586 | } | ||
1587 | |||
1588 | |||
1589 | /** | ||
1590 | * Select a subset of the items in the datastore and call | ||
1591 | * the given iterator for each of them. | ||
1592 | * | ||
1593 | * @param cls our "struct Plugin*" | ||
1594 | * @param type entries of which type should be considered? | ||
1595 | * Use 0 for any type. | ||
1596 | * @param iter function to call on each matching value; | ||
1597 | * will be called once with a NULL value at the end | ||
1598 | * @param iter_cls closure for iter | ||
1599 | */ | ||
1600 | static void | ||
1601 | mysql_plugin_iter_ascending_expiration (void *cls, | ||
1602 | enum GNUNET_BLOCK_Type type, | ||
1603 | PluginIterator iter, | ||
1604 | void *iter_cls) | ||
1605 | { | ||
1606 | struct Plugin *plugin = cls; | ||
1607 | iterateHelper (plugin, type, GNUNET_YES, 2, iter, closure); | ||
1608 | } | ||
1609 | |||
1610 | |||
1611 | /** | ||
1612 | * Select a subset of the items in the datastore and call | ||
1613 | * the given iterator for each of them. | ||
1614 | * | ||
1615 | * @param cls our "struct Plugin*" | ||
1616 | * @param type entries of which type should be considered? | ||
1617 | * Use 0 for any type. | ||
1618 | * @param iter function to call on each matching value; | ||
1619 | * will be called once with a NULL value at the end | ||
1620 | * @param iter_cls closure for iter | ||
1621 | */ | ||
1622 | static void | ||
1623 | mysql_plugin_iter_migration_order (void *cls, | ||
1624 | enum GNUNET_BLOCK_Type type, | ||
1625 | PluginIterator iter, | ||
1626 | void *iter_cls) | ||
1627 | { | ||
1628 | struct Plugin *plugin = cls; | ||
1629 | iterateHelper (plugin, 0, GNUNET_NO, 3, iter, closure); | ||
1630 | } | ||
1631 | |||
1632 | |||
1633 | /** | ||
1634 | * Select a subset of the items in the datastore and call | ||
1635 | * the given iterator for each of them. | ||
1636 | * | ||
1637 | * @param cls our "struct Plugin*" | ||
1638 | * @param type entries of which type should be considered? | ||
1639 | * Use 0 for any type. | ||
1640 | * @param iter function to call on each matching value; | ||
1641 | * will be called once with a NULL value at the end | ||
1642 | * @param iter_cls closure for iter | ||
1643 | */ | ||
1644 | static void | ||
1645 | mysql_plugin_iter_all_now (void *cls, | ||
1646 | enum GNUNET_BLOCK_Type type, | ||
1647 | PluginIterator iter, | ||
1648 | void *iter_cls) | ||
1649 | { | ||
1650 | struct Plugin *plugin = cls; | ||
1651 | iterateHelper (plugin, 0, GNUNET_YES, 0, iter, closure); | ||
1652 | } | ||
1653 | |||
1654 | |||
1655 | /** | ||
1656 | * Drop database. | ||
1657 | */ | ||
1658 | static void | ||
1659 | mysql_plugin_drop (void *cls) | ||
1660 | { | ||
1661 | struct Plugin *plugin = cls; | ||
1662 | |||
1663 | if ((GNUNET_OK != run_statement (plugin, | ||
1664 | "DROP TABLE gn080")) || | ||
1665 | (GNUNET_OK != run_statement (plugin, | ||
1666 | "DROP TABLE gn072"))) | ||
1667 | return; /* error */ | ||
1668 | plugin->content_size = 0; | ||
1669 | } | ||
1670 | |||
1671 | |||
1672 | /** | ||
1673 | * Entry point for the plugin. | ||
1674 | * | ||
1675 | * @param cls the "struct GNUNET_DATASTORE_PluginEnvironment*" | ||
1676 | * @return our "struct Plugin*" | ||
1677 | */ | ||
1678 | void * | ||
1679 | libgnunet_plugin_datastore_mysql_init (void *cls) | ||
1680 | { | ||
1681 | struct GNUNET_DATASTORE_PluginEnvironment *env = cls; | ||
1682 | struct GNUNET_DATASTORE_PluginFunctions *api; | ||
1683 | struct Plugin *plugin; | ||
1684 | |||
1685 | plugin = GNUNET_malloc (sizeof (struct Plugin)); | ||
1686 | plugin->env = env; | ||
1687 | plugin->cnffile = get_my_cnf_path (env->cfg); | ||
1688 | if (GNUNET_OK != iopen (plugin)) | ||
1689 | { | ||
1690 | iclose (plugin); | ||
1691 | GNUNET_free_non_null (plugin->cnffile); | ||
1692 | GNUNET_free (plugin); | ||
1693 | return NULL; | ||
1694 | } | ||
1695 | #define MRUNS(a) (GNUNET_OK != run_statement (plugin, a) ) | ||
1696 | #define PINIT(a,b) (NULL == (a = prepared_statement_create(plugin, b))) | ||
1697 | if (MRUNS ("CREATE TABLE IF NOT EXISTS gn080 (" | ||
1698 | " size INT(11) UNSIGNED NOT NULL DEFAULT 0," | ||
1699 | " type INT(11) UNSIGNED NOT NULL DEFAULT 0," | ||
1700 | " prio INT(11) UNSIGNED NOT NULL DEFAULT 0," | ||
1701 | " anonLevel INT(11) UNSIGNED NOT NULL DEFAULT 0," | ||
1702 | " expire BIGINT UNSIGNED NOT NULL DEFAULT 0," | ||
1703 | " hash BINARY(64) NOT NULL DEFAULT ''," | ||
1704 | " vhash BINARY(64) NOT NULL DEFAULT ''," | ||
1705 | " vkey BIGINT UNSIGNED NOT NULL DEFAULT 0," | ||
1706 | " INDEX hash (hash(64))," | ||
1707 | " INDEX hash_vhash_vkey (hash(64),vhash(64),vkey)," | ||
1708 | " INDEX hash_vkey (hash(64),vkey)," | ||
1709 | " INDEX vkey (vkey)," | ||
1710 | " INDEX prio (prio,vkey)," | ||
1711 | " INDEX expire (expire,vkey,type)," | ||
1712 | " INDEX anonLevel (anonLevel,prio,vkey,type)" | ||
1713 | ") ENGINE=InnoDB") || | ||
1714 | MRUNS ("CREATE TABLE IF NOT EXISTS gn072 (" | ||
1715 | " vkey BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY," | ||
1716 | " value BLOB NOT NULL DEFAULT '') ENGINE=MyISAM") || | ||
1717 | MRUNS ("SET AUTOCOMMIT = 1") || | ||
1718 | PINIT (plugin->select_value, SELECT_VALUE) || | ||
1719 | PINIT (plugin->delete_value, DELETE_VALUE) || | ||
1720 | PINIT (plugin->insert_value, INSERT_VALUE) || | ||
1721 | PINIT (plugin->insert_entry, INSERT_ENTRY) || | ||
1722 | PINIT (plugin->delete_entry_by_vkey, DELETE_ENTRY_BY_VKEY) || | ||
1723 | PINIT (plugin->select_entry_by_hash, SELECT_ENTRY_BY_HASH) || | ||
1724 | PINIT (plugin->select_entry_by_hash_and_vhash, SELECT_ENTRY_BY_HASH_AND_VHASH) | ||
1725 | || PINIT (plugin->select_entry_by_hash_and_type, SELECT_ENTRY_BY_HASH_AND_TYPE) | ||
1726 | || PINIT (plugin->select_entry_by_hash_vhash_and_type, | ||
1727 | SELECT_ENTRY_BY_HASH_VHASH_AND_TYPE) | ||
1728 | || PINIT (plugin->count_entry_by_hash, COUNT_ENTRY_BY_HASH) | ||
1729 | || PINIT (plugin->count_entry_by_hash_and_vhash, COUNT_ENTRY_BY_HASH_AND_VHASH) | ||
1730 | || PINIT (plugin->count_entry_by_hash_and_type, COUNT_ENTRY_BY_HASH_AND_TYPE) | ||
1731 | || PINIT (plugin->count_entry_by_hash_vhash_and_type, | ||
1732 | COUNT_ENTRY_BY_HASH_VHASH_AND_TYPE) | ||
1733 | || PINIT (plugin->update_entry, UPDATE_ENTRY) | ||
1734 | || PINIT (plugin->iter[0], SELECT_IT_LOW_PRIORITY) | ||
1735 | || PINIT (plugin->iter[1], SELECT_IT_NON_ANONYMOUS) | ||
1736 | || PINIT (plugin->iter[2], SELECT_IT_EXPIRATION_TIME) | ||
1737 | || PINIT (plugin->iter[3], SELECT_IT_MIGRATION_ORDER)) | ||
1738 | { | ||
1739 | GNUNET_MYSQL_database_close (db); | ||
1740 | db = NULL; | ||
1741 | return GNUNET_SYSERR; | ||
1742 | } | ||
1743 | #undef PINIT | ||
1744 | #undef MRUNS | ||
1745 | |||
1746 | |||
1747 | api = GNUNET_malloc (sizeof (struct GNUNET_DATASTORE_PluginFunctions)); | ||
1748 | api->cls = plugin; | ||
1749 | api->get_size = &mysql_plugin_get_size; | ||
1750 | api->put = &mysql_plugin_put; | ||
1751 | api->next_request = &mysql_plugin_next_request; | ||
1752 | api->get = &mysql_plugin_get; | ||
1753 | api->update = &mysql_plugin_update; | ||
1754 | api->iter_low_priority = &mysql_plugin_iter_low_priority; | ||
1755 | api->iter_zero_anonymity = &mysql_plugin_iter_zero_anonymity; | ||
1756 | api->iter_ascending_expiration = &mysql_plugin_iter_ascending_expiration; | ||
1757 | api->iter_migration_order = &mysql_plugin_iter_migration_order; | ||
1758 | api->iter_all_now = &mysql_plugin_iter_all_now; | ||
1759 | api->drop = &mysql_plugin_drop; | ||
1760 | GNUNET_log_from (GNUNET_ERROR_TYPE_INFO, | ||
1761 | "mysql", _("Mysql database running\n")); | ||
1762 | return api; | ||
1763 | } | ||
1764 | |||
1765 | |||
1766 | /** | ||
1767 | * Exit point from the plugin. | ||
1768 | * @param cls our "struct Plugin*" | ||
1769 | * @return always NULL | ||
1770 | */ | ||
1771 | void * | ||
1772 | libgnunet_plugin_datastore_mysql_done (void *cls) | ||
1773 | { | ||
1774 | struct GNUNET_DATASTORE_PluginFunctions *api = cls; | ||
1775 | struct Plugin *plugin = api->cls; | ||
1776 | |||
1777 | iclose (plugin); | ||
1778 | GNUNET_free_non_null (plugin->cnffile); | ||
1779 | GNUNET_free (plugin); | ||
1780 | GNUNET_free (plugin); | ||
1781 | GNUNET_free (api); | ||
1782 | mysql_library_end (); | ||
1783 | return NULL; | ||
1784 | } | ||
1785 | |||
1786 | /* end of plugin_datastore_mysql.c */ | ||