diff options
Diffstat (limited to 'src/datastore/plugin_datastore_sqlite.c')
-rw-r--r-- | src/datastore/plugin_datastore_sqlite.c | 1374 |
1 files changed, 0 insertions, 1374 deletions
diff --git a/src/datastore/plugin_datastore_sqlite.c b/src/datastore/plugin_datastore_sqlite.c deleted file mode 100644 index 3c2d7f2d4..000000000 --- a/src/datastore/plugin_datastore_sqlite.c +++ /dev/null | |||
@@ -1,1374 +0,0 @@ | |||
1 | /* | ||
2 | * This file is part of GNUnet | ||
3 | * Copyright (C) 2009, 2011, 2017 GNUnet e.V. | ||
4 | * | ||
5 | * GNUnet is free software: you can redistribute it and/or modify it | ||
6 | * under the terms of the GNU Affero General Public License as published | ||
7 | * by the Free Software Foundation, either version 3 of the License, | ||
8 | * or (at your option) any later version. | ||
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 | * Affero General Public License for more details. | ||
14 | * | ||
15 | * You should have received a copy of the GNU Affero General Public License | ||
16 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
17 | |||
18 | SPDX-License-Identifier: AGPL3.0-or-later | ||
19 | */ | ||
20 | |||
21 | /** | ||
22 | * @file datastore/plugin_datastore_sqlite.c | ||
23 | * @brief sqlite-based datastore backend | ||
24 | * @author Christian Grothoff | ||
25 | */ | ||
26 | |||
27 | #include "platform.h" | ||
28 | #include "gnunet_datastore_plugin.h" | ||
29 | #include "gnunet_sq_lib.h" | ||
30 | #include <sqlite3.h> | ||
31 | |||
32 | |||
33 | /** | ||
34 | * We allocate items on the stack at times. To prevent a stack | ||
35 | * overflow, we impose a limit on the maximum size for the data per | ||
36 | * item. 64k should be enough. | ||
37 | */ | ||
38 | #define MAX_ITEM_SIZE 65536 | ||
39 | |||
40 | /** | ||
41 | * After how many ms "busy" should a DB operation fail for good? | ||
42 | * A low value makes sure that we are more responsive to requests | ||
43 | * (especially PUTs). A high value guarantees a higher success | ||
44 | * rate (SELECTs in iterate can take several seconds despite LIMIT=1). | ||
45 | * | ||
46 | * The default value of 250ms should ensure that users do not experience | ||
47 | * huge latencies while at the same time allowing operations to succeed | ||
48 | * with reasonable probability. | ||
49 | */ | ||
50 | #define BUSY_TIMEOUT_MS 250 | ||
51 | |||
52 | |||
53 | /** | ||
54 | * Log an error message at log-level 'level' that indicates | ||
55 | * a failure of the command 'cmd' on file 'filename' | ||
56 | * with the message given by strerror(errno). | ||
57 | */ | ||
58 | #define LOG_SQLITE(db, level, cmd) \ | ||
59 | do \ | ||
60 | { \ | ||
61 | GNUNET_log_from (level, \ | ||
62 | "sqlite", \ | ||
63 | _ ("`%s' failed at %s:%d with error: %s\n"), \ | ||
64 | cmd, \ | ||
65 | __FILE__, \ | ||
66 | __LINE__, \ | ||
67 | sqlite3_errmsg (db->dbh)); \ | ||
68 | } while (0) | ||
69 | |||
70 | |||
71 | /** | ||
72 | * Log an error message at log-level 'level' that indicates | ||
73 | * a failure of the command 'cmd' on file 'filename' | ||
74 | * with the message given by strerror(errno). | ||
75 | */ | ||
76 | #define LOG_SQLITE_MSG(db, msg, level, cmd) \ | ||
77 | do \ | ||
78 | { \ | ||
79 | GNUNET_log_from (level, \ | ||
80 | "sqlite", \ | ||
81 | _ ("`%s' failed at %s:%d with error: %s\n"), \ | ||
82 | cmd, \ | ||
83 | __FILE__, \ | ||
84 | __LINE__, \ | ||
85 | sqlite3_errmsg (db->dbh)); \ | ||
86 | GNUNET_asprintf (msg, \ | ||
87 | _ ("`%s' failed at %s:%u with error: %s"), \ | ||
88 | cmd, \ | ||
89 | __FILE__, \ | ||
90 | __LINE__, \ | ||
91 | sqlite3_errmsg (db->dbh)); \ | ||
92 | } while (0) | ||
93 | |||
94 | |||
95 | /** | ||
96 | * Context for all functions in this plugin. | ||
97 | */ | ||
98 | struct Plugin | ||
99 | { | ||
100 | /** | ||
101 | * Our execution environment. | ||
102 | */ | ||
103 | struct GNUNET_DATASTORE_PluginEnvironment *env; | ||
104 | |||
105 | /** | ||
106 | * Database filename. | ||
107 | */ | ||
108 | char *fn; | ||
109 | |||
110 | /** | ||
111 | * Native SQLite database handle. | ||
112 | */ | ||
113 | sqlite3 *dbh; | ||
114 | |||
115 | /** | ||
116 | * Precompiled SQL for remove_key. | ||
117 | */ | ||
118 | sqlite3_stmt *remove; | ||
119 | |||
120 | /** | ||
121 | * Precompiled SQL for deletion. | ||
122 | */ | ||
123 | sqlite3_stmt *delRow; | ||
124 | |||
125 | /** | ||
126 | * Precompiled SQL for update. | ||
127 | */ | ||
128 | sqlite3_stmt *update; | ||
129 | |||
130 | /** | ||
131 | * Get maximum repl value in database. | ||
132 | */ | ||
133 | sqlite3_stmt *maxRepl; | ||
134 | |||
135 | /** | ||
136 | * Precompiled SQL for replication decrement. | ||
137 | */ | ||
138 | sqlite3_stmt *updRepl; | ||
139 | |||
140 | /** | ||
141 | * Precompiled SQL for replication selection. | ||
142 | */ | ||
143 | sqlite3_stmt *selRepl; | ||
144 | |||
145 | /** | ||
146 | * Precompiled SQL for expiration selection. | ||
147 | */ | ||
148 | sqlite3_stmt *selExpi; | ||
149 | |||
150 | /** | ||
151 | * Precompiled SQL for expiration selection. | ||
152 | */ | ||
153 | sqlite3_stmt *selZeroAnon; | ||
154 | |||
155 | /** | ||
156 | * Precompiled SQL for insertion. | ||
157 | */ | ||
158 | sqlite3_stmt *insertContent; | ||
159 | |||
160 | /** | ||
161 | * Precompiled SQL for selection | ||
162 | */ | ||
163 | sqlite3_stmt *get[8]; | ||
164 | |||
165 | /** | ||
166 | * Should the database be dropped on shutdown? | ||
167 | */ | ||
168 | int drop_on_shutdown; | ||
169 | }; | ||
170 | |||
171 | |||
172 | /** | ||
173 | * @brief Prepare a SQL statement | ||
174 | * | ||
175 | * @param dbh handle to the database | ||
176 | * @param zSql SQL statement, UTF-8 encoded | ||
177 | * @param ppStmt set to the prepared statement | ||
178 | * @return 0 on success | ||
179 | */ | ||
180 | static int | ||
181 | sq_prepare (sqlite3 *dbh, const char *zSql, sqlite3_stmt **ppStmt) | ||
182 | { | ||
183 | char *dummy; | ||
184 | int result; | ||
185 | |||
186 | result = sqlite3_prepare_v2 (dbh, | ||
187 | zSql, | ||
188 | strlen (zSql), | ||
189 | ppStmt, | ||
190 | (const char **) &dummy); | ||
191 | GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, | ||
192 | "sqlite", | ||
193 | "Prepared `%s' / %p: %d\n", | ||
194 | zSql, | ||
195 | *ppStmt, | ||
196 | result); | ||
197 | return result; | ||
198 | } | ||
199 | |||
200 | |||
201 | /** | ||
202 | * Create our database indices. | ||
203 | * | ||
204 | * @param dbh handle to the database | ||
205 | */ | ||
206 | static void | ||
207 | create_indices (sqlite3 *dbh) | ||
208 | { | ||
209 | /* create indices */ | ||
210 | if ( | ||
211 | 0 != | ||
212 | (SQLITE_OK != | ||
213 | sqlite3_exec (dbh, | ||
214 | "CREATE INDEX IF NOT EXISTS idx_hash ON gn091 (hash)", | ||
215 | NULL, | ||
216 | NULL, | ||
217 | NULL)) | ||
218 | + (SQLITE_OK != | ||
219 | sqlite3_exec ( | ||
220 | dbh, | ||
221 | "CREATE INDEX IF NOT EXISTS idx_anon_type ON gn091 (anonLevel ASC,type)", | ||
222 | NULL, | ||
223 | NULL, | ||
224 | NULL)) | ||
225 | + (SQLITE_OK != | ||
226 | sqlite3_exec (dbh, | ||
227 | "CREATE INDEX IF NOT EXISTS idx_expire ON gn091 (expire ASC)", | ||
228 | NULL, | ||
229 | NULL, | ||
230 | NULL)) | ||
231 | + (SQLITE_OK != | ||
232 | sqlite3_exec ( | ||
233 | dbh, | ||
234 | "CREATE INDEX IF NOT EXISTS idx_repl_rvalue ON gn091 (repl,rvalue)", | ||
235 | NULL, | ||
236 | NULL, | ||
237 | NULL))) | ||
238 | GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, | ||
239 | "sqlite", | ||
240 | "Failed to create indices: %s\n", | ||
241 | sqlite3_errmsg (dbh)); | ||
242 | } | ||
243 | |||
244 | |||
245 | #if 0 | ||
246 | #define CHECK(a) GNUNET_break (a) | ||
247 | #define ENULL NULL | ||
248 | #else | ||
249 | #define ENULL &e | ||
250 | #define ENULL_DEFINED 1 | ||
251 | #define CHECK(a) \ | ||
252 | if (! (a)) \ | ||
253 | { \ | ||
254 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "%s\n", e); \ | ||
255 | sqlite3_free (e); \ | ||
256 | } | ||
257 | #endif | ||
258 | |||
259 | |||
260 | /** | ||
261 | * Initialize the database connections and associated | ||
262 | * data structures (create tables and indices | ||
263 | * as needed as well). | ||
264 | * | ||
265 | * @param cfg our configuration | ||
266 | * @param plugin the plugin context (state for this module) | ||
267 | * @return #GNUNET_OK on success | ||
268 | */ | ||
269 | static int | ||
270 | database_setup (const struct GNUNET_CONFIGURATION_Handle *cfg, | ||
271 | struct Plugin *plugin) | ||
272 | { | ||
273 | sqlite3_stmt *stmt; | ||
274 | char *afsdir; | ||
275 | |||
276 | #if ENULL_DEFINED | ||
277 | char *e; | ||
278 | #endif | ||
279 | |||
280 | if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_filename (cfg, | ||
281 | "datastore-sqlite", | ||
282 | "FILENAME", | ||
283 | &afsdir)) | ||
284 | { | ||
285 | GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, | ||
286 | "datastore-sqlite", | ||
287 | "FILENAME"); | ||
288 | return GNUNET_SYSERR; | ||
289 | } | ||
290 | if (GNUNET_OK != GNUNET_DISK_file_test (afsdir)) | ||
291 | { | ||
292 | if (GNUNET_OK != GNUNET_DISK_directory_create_for_file (afsdir)) | ||
293 | { | ||
294 | GNUNET_break (0); | ||
295 | GNUNET_free (afsdir); | ||
296 | return GNUNET_SYSERR; | ||
297 | } | ||
298 | /* database is new or got deleted, reset payload to zero! */ | ||
299 | if (NULL != plugin->env->duc) | ||
300 | plugin->env->duc (plugin->env->cls, 0); | ||
301 | } | ||
302 | /* afsdir should be UTF-8-encoded. If it isn't, it's a bug */ | ||
303 | plugin->fn = afsdir; | ||
304 | |||
305 | /* Open database and precompile statements */ | ||
306 | if (SQLITE_OK != sqlite3_open (plugin->fn, &plugin->dbh)) | ||
307 | { | ||
308 | GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, | ||
309 | "sqlite", | ||
310 | _ ("Unable to initialize SQLite: %s.\n"), | ||
311 | sqlite3_errmsg (plugin->dbh)); | ||
312 | return GNUNET_SYSERR; | ||
313 | } | ||
314 | CHECK ( | ||
315 | SQLITE_OK == | ||
316 | sqlite3_exec (plugin->dbh, "PRAGMA temp_store=MEMORY", NULL, NULL, ENULL)); | ||
317 | CHECK ( | ||
318 | SQLITE_OK == | ||
319 | sqlite3_exec (plugin->dbh, "PRAGMA synchronous=OFF", NULL, NULL, ENULL)); | ||
320 | CHECK (SQLITE_OK == sqlite3_exec (plugin->dbh, | ||
321 | "PRAGMA legacy_file_format=OFF", | ||
322 | NULL, | ||
323 | NULL, | ||
324 | ENULL)); | ||
325 | CHECK (SQLITE_OK == sqlite3_exec (plugin->dbh, | ||
326 | "PRAGMA auto_vacuum=INCREMENTAL", | ||
327 | NULL, | ||
328 | NULL, | ||
329 | ENULL)); | ||
330 | CHECK (SQLITE_OK == sqlite3_exec (plugin->dbh, | ||
331 | "PRAGMA locking_mode=EXCLUSIVE", | ||
332 | NULL, | ||
333 | NULL, | ||
334 | ENULL)); | ||
335 | CHECK ( | ||
336 | SQLITE_OK == | ||
337 | sqlite3_exec (plugin->dbh, "PRAGMA page_size=4096", NULL, NULL, ENULL)); | ||
338 | |||
339 | CHECK (SQLITE_OK == sqlite3_busy_timeout (plugin->dbh, BUSY_TIMEOUT_MS)); | ||
340 | |||
341 | |||
342 | /* We have to do it here, because otherwise precompiling SQL might fail */ | ||
343 | CHECK (SQLITE_OK == | ||
344 | sq_prepare (plugin->dbh, | ||
345 | "SELECT 1 FROM sqlite_master WHERE tbl_name = 'gn091'", | ||
346 | &stmt)); | ||
347 | |||
348 | /* FIXME: SQLite does not have unsigned integers! This is ok for the type column because | ||
349 | * we only test equality on it and can cast it to/from uint32_t. For repl, prio, and anonLevel | ||
350 | * we do math or inequality tests, so we can't handle the entire range of uint32_t. | ||
351 | * This will also cause problems for expiration times after 294247-01-10-04:00:54 UTC. | ||
352 | */if ((SQLITE_DONE == sqlite3_step (stmt)) && | ||
353 | (SQLITE_OK != sqlite3_exec (plugin->dbh, | ||
354 | "CREATE TABLE gn091 (" | ||
355 | " repl INT4 NOT NULL DEFAULT 0," | ||
356 | " type INT4 NOT NULL DEFAULT 0," | ||
357 | " prio INT4 NOT NULL DEFAULT 0," | ||
358 | " anonLevel INT4 NOT NULL DEFAULT 0," | ||
359 | " expire INT8 NOT NULL DEFAULT 0," | ||
360 | " rvalue INT8 NOT NULL," | ||
361 | " hash TEXT NOT NULL DEFAULT ''," | ||
362 | " vhash TEXT NOT NULL DEFAULT ''," | ||
363 | " value BLOB NOT NULL DEFAULT '')", | ||
364 | NULL, | ||
365 | NULL, | ||
366 | NULL))) | ||
367 | { | ||
368 | LOG_SQLITE (plugin, GNUNET_ERROR_TYPE_ERROR, "sqlite3_exec"); | ||
369 | sqlite3_finalize (stmt); | ||
370 | return GNUNET_SYSERR; | ||
371 | } | ||
372 | sqlite3_finalize (stmt); | ||
373 | create_indices (plugin->dbh); | ||
374 | |||
375 | #define RESULT_COLUMNS \ | ||
376 | "repl, type, prio, anonLevel, expire, hash, value, _ROWID_" | ||
377 | if ( | ||
378 | (SQLITE_OK != sq_prepare (plugin->dbh, | ||
379 | "UPDATE gn091 " | ||
380 | "SET prio = prio + ?, " | ||
381 | "repl = repl + ?, " | ||
382 | "expire = MAX(expire, ?) " | ||
383 | "WHERE hash = ? AND vhash = ?", | ||
384 | &plugin->update)) || | ||
385 | (SQLITE_OK != sq_prepare (plugin->dbh, | ||
386 | "UPDATE gn091 " | ||
387 | "SET repl = MAX (0, repl - 1) WHERE _ROWID_ = ?", | ||
388 | &plugin->updRepl)) || | ||
389 | (SQLITE_OK != sq_prepare (plugin->dbh, | ||
390 | "SELECT " RESULT_COLUMNS " FROM gn091 " | ||
391 | "WHERE repl=?2 AND " | ||
392 | " (rvalue>=?1 OR " | ||
393 | " NOT EXISTS (SELECT 1 FROM gn091 " | ||
394 | "WHERE repl=?2 AND rvalue>=?1 LIMIT 1) ) " | ||
395 | "ORDER BY rvalue ASC LIMIT 1", | ||
396 | &plugin->selRepl)) || | ||
397 | (SQLITE_OK != sq_prepare (plugin->dbh, | ||
398 | "SELECT MAX(repl) FROM gn091", | ||
399 | &plugin->maxRepl)) || | ||
400 | (SQLITE_OK != | ||
401 | sq_prepare (plugin->dbh, | ||
402 | "SELECT " RESULT_COLUMNS " FROM gn091 " | ||
403 | "WHERE NOT EXISTS (SELECT 1 FROM gn091 WHERE expire < ?1 LIMIT 1) OR (expire < ?1) " | ||
404 | "ORDER BY expire ASC LIMIT 1", | ||
405 | &plugin->selExpi)) || | ||
406 | (SQLITE_OK != sq_prepare (plugin->dbh, | ||
407 | "SELECT " RESULT_COLUMNS " FROM gn091 " | ||
408 | "WHERE _ROWID_ >= ? AND " | ||
409 | "anonLevel = 0 AND " | ||
410 | "type = ? " | ||
411 | "ORDER BY _ROWID_ ASC LIMIT 1", | ||
412 | &plugin->selZeroAnon)) || | ||
413 | (SQLITE_OK != | ||
414 | sq_prepare (plugin->dbh, | ||
415 | "INSERT INTO gn091 (repl, type, prio, anonLevel, expire, rvalue, hash, vhash, value) " | ||
416 | "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)", | ||
417 | &plugin->insertContent)) || | ||
418 | (SQLITE_OK != sq_prepare (plugin->dbh, | ||
419 | "SELECT " RESULT_COLUMNS " FROM gn091 " | ||
420 | "WHERE _ROWID_ >= ?1 " | ||
421 | "ORDER BY _ROWID_ ASC LIMIT 1", | ||
422 | &plugin->get[0])) || | ||
423 | (SQLITE_OK != sq_prepare (plugin->dbh, | ||
424 | "SELECT " RESULT_COLUMNS " FROM gn091 " | ||
425 | "WHERE _ROWID_ >= ?1 AND " | ||
426 | "type = ?4 " | ||
427 | "ORDER BY _ROWID_ ASC LIMIT 1", | ||
428 | &plugin->get[1])) || | ||
429 | (SQLITE_OK != sq_prepare (plugin->dbh, | ||
430 | "SELECT " RESULT_COLUMNS " FROM gn091 " | ||
431 | "WHERE _ROWID_ >= ?1 AND " | ||
432 | "hash = ?3 " | ||
433 | "ORDER BY _ROWID_ ASC LIMIT 1", | ||
434 | &plugin->get[2])) || | ||
435 | (SQLITE_OK != sq_prepare (plugin->dbh, | ||
436 | "SELECT " RESULT_COLUMNS " FROM gn091 " | ||
437 | "WHERE _ROWID_ >= ?1 AND " | ||
438 | "hash = ?3 AND " | ||
439 | "type = ?4 " | ||
440 | "ORDER BY _ROWID_ ASC LIMIT 1", | ||
441 | &plugin->get[3])) || | ||
442 | (SQLITE_OK != sq_prepare (plugin->dbh, | ||
443 | "SELECT " RESULT_COLUMNS " FROM gn091 " | ||
444 | "WHERE _ROWID_ >= ?1 AND " | ||
445 | "rvalue >= ?2 " | ||
446 | "ORDER BY _ROWID_ ASC LIMIT 1", | ||
447 | &plugin->get[4])) || | ||
448 | (SQLITE_OK != sq_prepare (plugin->dbh, | ||
449 | "SELECT " RESULT_COLUMNS " FROM gn091 " | ||
450 | "WHERE _ROWID_ >= ?1 AND " | ||
451 | "rvalue >= ?2 AND " | ||
452 | "type = ?4 " | ||
453 | "ORDER BY _ROWID_ ASC LIMIT 1", | ||
454 | &plugin->get[5])) || | ||
455 | (SQLITE_OK != sq_prepare (plugin->dbh, | ||
456 | "SELECT " RESULT_COLUMNS " FROM gn091 " | ||
457 | "WHERE _ROWID_ >= ?1 AND " | ||
458 | "rvalue >= ?2 AND " | ||
459 | "hash = ?3 " | ||
460 | "ORDER BY _ROWID_ ASC LIMIT 1", | ||
461 | &plugin->get[6])) || | ||
462 | (SQLITE_OK != sq_prepare (plugin->dbh, | ||
463 | "SELECT " RESULT_COLUMNS " FROM gn091 " | ||
464 | "WHERE _ROWID_ >= ?1 AND " | ||
465 | "rvalue >= ?2 AND " | ||
466 | "hash = ?3 AND " | ||
467 | "type = ?4 " | ||
468 | "ORDER BY _ROWID_ ASC LIMIT 1", | ||
469 | &plugin->get[7])) || | ||
470 | (SQLITE_OK != sq_prepare (plugin->dbh, | ||
471 | "DELETE FROM gn091 WHERE _ROWID_ = ?", | ||
472 | &plugin->delRow)) || | ||
473 | (SQLITE_OK != sq_prepare (plugin->dbh, | ||
474 | "DELETE FROM gn091 " | ||
475 | "WHERE hash = ? AND " | ||
476 | "value = ? ", | ||
477 | &plugin->remove)) || | ||
478 | false) | ||
479 | { | ||
480 | LOG_SQLITE (plugin, GNUNET_ERROR_TYPE_ERROR, "precompiling"); | ||
481 | return GNUNET_SYSERR; | ||
482 | } | ||
483 | return GNUNET_OK; | ||
484 | } | ||
485 | |||
486 | |||
487 | /** | ||
488 | * Shutdown database connection and associate data | ||
489 | * structures. | ||
490 | * | ||
491 | * @param plugin the plugin context (state for this module) | ||
492 | */ | ||
493 | static void | ||
494 | database_shutdown (struct Plugin *plugin) | ||
495 | { | ||
496 | int result; | ||
497 | |||
498 | #if SQLITE_VERSION_NUMBER >= 3007000 | ||
499 | sqlite3_stmt *stmt; | ||
500 | #endif | ||
501 | |||
502 | if (NULL != plugin->remove) | ||
503 | sqlite3_finalize (plugin->remove); | ||
504 | if (NULL != plugin->delRow) | ||
505 | sqlite3_finalize (plugin->delRow); | ||
506 | if (NULL != plugin->update) | ||
507 | sqlite3_finalize (plugin->update); | ||
508 | if (NULL != plugin->updRepl) | ||
509 | sqlite3_finalize (plugin->updRepl); | ||
510 | if (NULL != plugin->selRepl) | ||
511 | sqlite3_finalize (plugin->selRepl); | ||
512 | if (NULL != plugin->maxRepl) | ||
513 | sqlite3_finalize (plugin->maxRepl); | ||
514 | if (NULL != plugin->selExpi) | ||
515 | sqlite3_finalize (plugin->selExpi); | ||
516 | if (NULL != plugin->selZeroAnon) | ||
517 | sqlite3_finalize (plugin->selZeroAnon); | ||
518 | if (NULL != plugin->insertContent) | ||
519 | sqlite3_finalize (plugin->insertContent); | ||
520 | for (int i = 0; i < 8; ++i) | ||
521 | if (NULL != plugin->get[i]) | ||
522 | sqlite3_finalize (plugin->get[i]); | ||
523 | result = sqlite3_close (plugin->dbh); | ||
524 | #if SQLITE_VERSION_NUMBER >= 3007000 | ||
525 | if (result == SQLITE_BUSY) | ||
526 | { | ||
527 | GNUNET_log_from ( | ||
528 | GNUNET_ERROR_TYPE_WARNING, | ||
529 | "sqlite", | ||
530 | _ ( | ||
531 | "Tried to close sqlite without finalizing all prepared statements.\n")); | ||
532 | stmt = sqlite3_next_stmt (plugin->dbh, NULL); | ||
533 | while (NULL != stmt) | ||
534 | { | ||
535 | GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, | ||
536 | "sqlite", | ||
537 | "Closing statement %p\n", | ||
538 | stmt); | ||
539 | result = sqlite3_finalize (stmt); | ||
540 | if (result != SQLITE_OK) | ||
541 | GNUNET_log_from (GNUNET_ERROR_TYPE_WARNING, | ||
542 | "sqlite", | ||
543 | "Failed to close statement %p: %d\n", | ||
544 | stmt, | ||
545 | result); | ||
546 | stmt = sqlite3_next_stmt (plugin->dbh, NULL); | ||
547 | } | ||
548 | result = sqlite3_close (plugin->dbh); | ||
549 | } | ||
550 | #endif | ||
551 | if (SQLITE_OK != result) | ||
552 | LOG_SQLITE (plugin, GNUNET_ERROR_TYPE_ERROR, "sqlite3_close"); | ||
553 | GNUNET_free (plugin->fn); | ||
554 | } | ||
555 | |||
556 | |||
557 | /** | ||
558 | * Delete the database entry with the given | ||
559 | * row identifier. | ||
560 | * | ||
561 | * @param plugin the plugin context (state for this module) | ||
562 | * @param rid the ID of the row to delete | ||
563 | */ | ||
564 | static int | ||
565 | delete_by_rowid (struct Plugin *plugin, uint64_t rid) | ||
566 | { | ||
567 | struct GNUNET_SQ_QueryParam params[] = { GNUNET_SQ_query_param_uint64 (&rid), | ||
568 | GNUNET_SQ_query_param_end }; | ||
569 | |||
570 | if (GNUNET_OK != GNUNET_SQ_bind (plugin->delRow, params)) | ||
571 | return GNUNET_SYSERR; | ||
572 | if (SQLITE_DONE != sqlite3_step (plugin->delRow)) | ||
573 | { | ||
574 | LOG_SQLITE (plugin, | ||
575 | GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK, | ||
576 | "sqlite3_step"); | ||
577 | GNUNET_SQ_reset (plugin->dbh, plugin->delRow); | ||
578 | return GNUNET_SYSERR; | ||
579 | } | ||
580 | GNUNET_SQ_reset (plugin->dbh, plugin->delRow); | ||
581 | return GNUNET_OK; | ||
582 | } | ||
583 | |||
584 | |||
585 | /** | ||
586 | * Store an item in the datastore. | ||
587 | * | ||
588 | * @param cls closure | ||
589 | * @param key key for the item | ||
590 | * @param absent true if the key was not found in the bloom filter | ||
591 | * @param size number of bytes in @a data | ||
592 | * @param data content stored | ||
593 | * @param type type of the content | ||
594 | * @param priority priority of the content | ||
595 | * @param anonymity anonymity-level for the content | ||
596 | * @param replication replication-level for the content | ||
597 | * @param expiration expiration time for the content | ||
598 | * @param cont continuation called with success or failure status | ||
599 | * @param cont_cls continuation closure | ||
600 | */ | ||
601 | static void | ||
602 | sqlite_plugin_put (void *cls, | ||
603 | const struct GNUNET_HashCode *key, | ||
604 | bool absent, | ||
605 | uint32_t size, | ||
606 | const void *data, | ||
607 | enum GNUNET_BLOCK_Type type, | ||
608 | uint32_t priority, | ||
609 | uint32_t anonymity, | ||
610 | uint32_t replication, | ||
611 | struct GNUNET_TIME_Absolute expiration, | ||
612 | PluginPutCont cont, | ||
613 | void *cont_cls) | ||
614 | { | ||
615 | struct Plugin *plugin = cls; | ||
616 | struct GNUNET_HashCode vhash; | ||
617 | char *msg = NULL; | ||
618 | |||
619 | GNUNET_CRYPTO_hash (data, size, &vhash); | ||
620 | |||
621 | if (! absent) | ||
622 | { | ||
623 | struct GNUNET_SQ_QueryParam params[] = | ||
624 | { GNUNET_SQ_query_param_uint32 (&priority), | ||
625 | GNUNET_SQ_query_param_uint32 (&replication), | ||
626 | GNUNET_SQ_query_param_absolute_time (&expiration), | ||
627 | GNUNET_SQ_query_param_auto_from_type (key), | ||
628 | GNUNET_SQ_query_param_auto_from_type (&vhash), | ||
629 | GNUNET_SQ_query_param_end }; | ||
630 | |||
631 | if (GNUNET_OK != GNUNET_SQ_bind (plugin->update, params)) | ||
632 | { | ||
633 | cont (cont_cls, key, size, GNUNET_SYSERR, _ ("sqlite bind failure")); | ||
634 | return; | ||
635 | } | ||
636 | if (SQLITE_DONE != sqlite3_step (plugin->update)) | ||
637 | { | ||
638 | LOG_SQLITE_MSG (plugin, | ||
639 | &msg, | ||
640 | GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK, | ||
641 | "sqlite3_step"); | ||
642 | cont (cont_cls, key, size, GNUNET_SYSERR, msg); | ||
643 | GNUNET_free (msg); | ||
644 | return; | ||
645 | } | ||
646 | int changes = sqlite3_changes (plugin->dbh); | ||
647 | GNUNET_SQ_reset (plugin->dbh, plugin->update); | ||
648 | if (0 != changes) | ||
649 | { | ||
650 | cont (cont_cls, key, size, GNUNET_NO, NULL); | ||
651 | return; | ||
652 | } | ||
653 | } | ||
654 | |||
655 | uint64_t rvalue; | ||
656 | uint32_t type32 = (uint32_t) type; | ||
657 | struct GNUNET_SQ_QueryParam params[] = | ||
658 | { GNUNET_SQ_query_param_uint32 (&replication), | ||
659 | GNUNET_SQ_query_param_uint32 (&type32), | ||
660 | GNUNET_SQ_query_param_uint32 (&priority), | ||
661 | GNUNET_SQ_query_param_uint32 (&anonymity), | ||
662 | GNUNET_SQ_query_param_absolute_time (&expiration), | ||
663 | GNUNET_SQ_query_param_uint64 (&rvalue), | ||
664 | GNUNET_SQ_query_param_auto_from_type (key), | ||
665 | GNUNET_SQ_query_param_auto_from_type (&vhash), | ||
666 | GNUNET_SQ_query_param_fixed_size (data, size), | ||
667 | GNUNET_SQ_query_param_end }; | ||
668 | int n; | ||
669 | int ret; | ||
670 | sqlite3_stmt *stmt; | ||
671 | |||
672 | if (size > MAX_ITEM_SIZE) | ||
673 | { | ||
674 | cont (cont_cls, key, size, GNUNET_SYSERR, _ ("Data too large")); | ||
675 | return; | ||
676 | } | ||
677 | GNUNET_log_from ( | ||
678 | GNUNET_ERROR_TYPE_DEBUG, | ||
679 | "sqlite", | ||
680 | "Storing in database block with type %u/key `%s'/priority %u/expiration in %s (%s).\n", | ||
681 | type, | ||
682 | GNUNET_h2s (key), | ||
683 | priority, | ||
684 | GNUNET_STRINGS_relative_time_to_string (GNUNET_TIME_absolute_get_remaining ( | ||
685 | expiration), | ||
686 | GNUNET_YES), | ||
687 | GNUNET_STRINGS_absolute_time_to_string (expiration)); | ||
688 | stmt = plugin->insertContent; | ||
689 | rvalue = GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK, UINT64_MAX); | ||
690 | if (GNUNET_OK != GNUNET_SQ_bind (stmt, params)) | ||
691 | { | ||
692 | cont (cont_cls, key, size, GNUNET_SYSERR, NULL); | ||
693 | return; | ||
694 | } | ||
695 | n = sqlite3_step (stmt); | ||
696 | switch (n) | ||
697 | { | ||
698 | case SQLITE_DONE: | ||
699 | if (NULL != plugin->env->duc) | ||
700 | plugin->env->duc (plugin->env->cls, | ||
701 | size + GNUNET_DATASTORE_ENTRY_OVERHEAD); | ||
702 | GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, | ||
703 | "sqlite", | ||
704 | "Stored new entry (%u bytes)\n", | ||
705 | size + GNUNET_DATASTORE_ENTRY_OVERHEAD); | ||
706 | ret = GNUNET_OK; | ||
707 | break; | ||
708 | |||
709 | case SQLITE_BUSY: | ||
710 | GNUNET_break (0); | ||
711 | LOG_SQLITE_MSG (plugin, | ||
712 | &msg, | ||
713 | GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK, | ||
714 | "sqlite3_step"); | ||
715 | ret = GNUNET_SYSERR; | ||
716 | break; | ||
717 | |||
718 | default: | ||
719 | LOG_SQLITE_MSG (plugin, | ||
720 | &msg, | ||
721 | GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK, | ||
722 | "sqlite3_step"); | ||
723 | GNUNET_SQ_reset (plugin->dbh, stmt); | ||
724 | database_shutdown (plugin); | ||
725 | database_setup (plugin->env->cfg, plugin); | ||
726 | cont (cont_cls, key, size, GNUNET_SYSERR, msg); | ||
727 | GNUNET_free (msg); | ||
728 | return; | ||
729 | } | ||
730 | GNUNET_SQ_reset (plugin->dbh, stmt); | ||
731 | cont (cont_cls, key, size, ret, msg); | ||
732 | GNUNET_free (msg); | ||
733 | } | ||
734 | |||
735 | |||
736 | /** | ||
737 | * Execute statement that gets a row and call the callback | ||
738 | * with the result. Resets the statement afterwards. | ||
739 | * | ||
740 | * @param plugin the plugin | ||
741 | * @param stmt the statement | ||
742 | * @param proc processor to call | ||
743 | * @param proc_cls closure for @a proc | ||
744 | */ | ||
745 | static void | ||
746 | execute_get (struct Plugin *plugin, | ||
747 | sqlite3_stmt *stmt, | ||
748 | PluginDatumProcessor proc, | ||
749 | void *proc_cls) | ||
750 | { | ||
751 | int n; | ||
752 | struct GNUNET_TIME_Absolute expiration; | ||
753 | uint32_t replication; | ||
754 | uint32_t type; | ||
755 | uint32_t priority; | ||
756 | uint32_t anonymity; | ||
757 | uint64_t rowid; | ||
758 | void *value; | ||
759 | size_t value_size; | ||
760 | struct GNUNET_HashCode key; | ||
761 | int ret; | ||
762 | struct GNUNET_SQ_ResultSpec rs[] = | ||
763 | { GNUNET_SQ_result_spec_uint32 (&replication), | ||
764 | GNUNET_SQ_result_spec_uint32 (&type), | ||
765 | GNUNET_SQ_result_spec_uint32 (&priority), | ||
766 | GNUNET_SQ_result_spec_uint32 (&anonymity), | ||
767 | GNUNET_SQ_result_spec_absolute_time (&expiration), | ||
768 | GNUNET_SQ_result_spec_auto_from_type (&key), | ||
769 | GNUNET_SQ_result_spec_variable_size (&value, &value_size), | ||
770 | GNUNET_SQ_result_spec_uint64 (&rowid), | ||
771 | GNUNET_SQ_result_spec_end }; | ||
772 | |||
773 | n = sqlite3_step (stmt); | ||
774 | switch (n) | ||
775 | { | ||
776 | case SQLITE_ROW: | ||
777 | if (GNUNET_OK != GNUNET_SQ_extract_result (stmt, rs)) | ||
778 | { | ||
779 | GNUNET_break (0); | ||
780 | break; | ||
781 | } | ||
782 | GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, | ||
783 | "sqlite", | ||
784 | "Found reply in database with expiration %s\n", | ||
785 | GNUNET_STRINGS_absolute_time_to_string (expiration)); | ||
786 | ret = proc (proc_cls, | ||
787 | &key, | ||
788 | value_size, | ||
789 | value, | ||
790 | type, | ||
791 | priority, | ||
792 | anonymity, | ||
793 | replication, | ||
794 | expiration, | ||
795 | rowid); | ||
796 | GNUNET_SQ_cleanup_result (rs); | ||
797 | GNUNET_SQ_reset (plugin->dbh, stmt); | ||
798 | if ((GNUNET_NO == ret) && (GNUNET_OK == delete_by_rowid (plugin, rowid)) && | ||
799 | (NULL != plugin->env->duc)) | ||
800 | plugin->env->duc (plugin->env->cls, | ||
801 | -(value_size + GNUNET_DATASTORE_ENTRY_OVERHEAD)); | ||
802 | return; | ||
803 | |||
804 | case SQLITE_DONE: | ||
805 | /* database must be empty */ | ||
806 | break; | ||
807 | |||
808 | case SQLITE_BUSY: | ||
809 | case SQLITE_ERROR: | ||
810 | case SQLITE_MISUSE: | ||
811 | default: | ||
812 | LOG_SQLITE (plugin, | ||
813 | GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK, | ||
814 | "sqlite3_step"); | ||
815 | if (SQLITE_OK != sqlite3_reset (stmt)) | ||
816 | LOG_SQLITE (plugin, | ||
817 | GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK, | ||
818 | "sqlite3_reset"); | ||
819 | GNUNET_break (0); | ||
820 | proc (proc_cls, NULL, 0, NULL, 0, 0, 0, 0, GNUNET_TIME_UNIT_ZERO_ABS, 0); | ||
821 | database_shutdown (plugin); | ||
822 | database_setup (plugin->env->cfg, plugin); | ||
823 | return; | ||
824 | } | ||
825 | GNUNET_SQ_reset (plugin->dbh, stmt); | ||
826 | proc (proc_cls, NULL, 0, NULL, 0, 0, 0, 0, GNUNET_TIME_UNIT_ZERO_ABS, 0); | ||
827 | } | ||
828 | |||
829 | |||
830 | /** | ||
831 | * Select a subset of the items in the datastore and call | ||
832 | * the given processor for the item. | ||
833 | * | ||
834 | * @param cls our plugin context | ||
835 | * @param next_uid return the result with lowest uid >= next_uid | ||
836 | * @param type entries of which type should be considered? | ||
837 | * Must not be zero (ANY). | ||
838 | * @param proc function to call on the matching value; | ||
839 | * will be called with NULL if no value matches | ||
840 | * @param proc_cls closure for @a proc | ||
841 | */ | ||
842 | static void | ||
843 | sqlite_plugin_get_zero_anonymity (void *cls, | ||
844 | uint64_t next_uid, | ||
845 | enum GNUNET_BLOCK_Type type, | ||
846 | PluginDatumProcessor proc, | ||
847 | void *proc_cls) | ||
848 | { | ||
849 | struct Plugin *plugin = cls; | ||
850 | uint32_t type32 = type; | ||
851 | struct GNUNET_SQ_QueryParam params[] = { GNUNET_SQ_query_param_uint64 ( | ||
852 | &next_uid), | ||
853 | GNUNET_SQ_query_param_uint32 ( | ||
854 | &type32), | ||
855 | GNUNET_SQ_query_param_end }; | ||
856 | |||
857 | GNUNET_assert (type != GNUNET_BLOCK_TYPE_ANY); | ||
858 | if (GNUNET_OK != GNUNET_SQ_bind (plugin->selZeroAnon, params)) | ||
859 | { | ||
860 | proc (proc_cls, NULL, 0, NULL, 0, 0, 0, 0, GNUNET_TIME_UNIT_ZERO_ABS, 0); | ||
861 | return; | ||
862 | } | ||
863 | execute_get (plugin, plugin->selZeroAnon, proc, proc_cls); | ||
864 | } | ||
865 | |||
866 | |||
867 | /** | ||
868 | * Get results for a particular key in the datastore. | ||
869 | * | ||
870 | * @param cls closure | ||
871 | * @param next_uid return the result with lowest uid >= next_uid | ||
872 | * @param random if true, return a random result instead of using next_uid | ||
873 | * @param key maybe NULL (to match all entries) | ||
874 | * @param type entries of which type are relevant? | ||
875 | * Use 0 for any type. | ||
876 | * @param proc function to call on the matching value; | ||
877 | * will be called with NULL if nothing matches | ||
878 | * @param proc_cls closure for @a proc | ||
879 | */ | ||
880 | static void | ||
881 | sqlite_plugin_get_key (void *cls, | ||
882 | uint64_t next_uid, | ||
883 | bool random, | ||
884 | const struct GNUNET_HashCode *key, | ||
885 | enum GNUNET_BLOCK_Type type, | ||
886 | PluginDatumProcessor proc, | ||
887 | void *proc_cls) | ||
888 | { | ||
889 | struct Plugin *plugin = cls; | ||
890 | uint64_t rvalue; | ||
891 | int use_rvalue = random; | ||
892 | uint32_t type32 = (uint32_t) type; | ||
893 | int use_type = GNUNET_BLOCK_TYPE_ANY != type; | ||
894 | int use_key = NULL != key; | ||
895 | sqlite3_stmt *stmt = plugin->get[use_rvalue * 4 + use_key * 2 + use_type]; | ||
896 | struct GNUNET_SQ_QueryParam params[] = | ||
897 | { GNUNET_SQ_query_param_uint64 (&next_uid), | ||
898 | GNUNET_SQ_query_param_uint64 (&rvalue), | ||
899 | GNUNET_SQ_query_param_auto_from_type (key), | ||
900 | GNUNET_SQ_query_param_uint32 (&type32), | ||
901 | GNUNET_SQ_query_param_end }; | ||
902 | |||
903 | /* SQLite doesn't like it when you try to bind a parameter greater than the | ||
904 | * last numbered parameter, but unused parameters in the middle are OK. | ||
905 | */ | ||
906 | if (! use_type) | ||
907 | { | ||
908 | params[3] = (struct GNUNET_SQ_QueryParam) GNUNET_SQ_query_param_end; | ||
909 | if (! use_key) | ||
910 | { | ||
911 | params[2] = (struct GNUNET_SQ_QueryParam) GNUNET_SQ_query_param_end; | ||
912 | if (! use_rvalue) | ||
913 | params[1] = (struct GNUNET_SQ_QueryParam) GNUNET_SQ_query_param_end; | ||
914 | } | ||
915 | } | ||
916 | if (random) | ||
917 | { | ||
918 | rvalue = GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK, UINT64_MAX); | ||
919 | next_uid = 0; | ||
920 | } | ||
921 | else | ||
922 | rvalue = 0; | ||
923 | |||
924 | if (GNUNET_OK != GNUNET_SQ_bind (stmt, params)) | ||
925 | { | ||
926 | proc (proc_cls, NULL, 0, NULL, 0, 0, 0, 0, GNUNET_TIME_UNIT_ZERO_ABS, 0); | ||
927 | return; | ||
928 | } | ||
929 | execute_get (plugin, stmt, proc, proc_cls); | ||
930 | } | ||
931 | |||
932 | |||
933 | /** | ||
934 | * Context for #repl_proc() function. | ||
935 | */ | ||
936 | struct ReplCtx | ||
937 | { | ||
938 | /** | ||
939 | * Function to call for the result (or the NULL). | ||
940 | */ | ||
941 | PluginDatumProcessor proc; | ||
942 | |||
943 | /** | ||
944 | * Closure for @e proc. | ||
945 | */ | ||
946 | void *proc_cls; | ||
947 | |||
948 | /** | ||
949 | * UID to use. | ||
950 | */ | ||
951 | uint64_t uid; | ||
952 | |||
953 | /** | ||
954 | * Yes if UID was set. | ||
955 | */ | ||
956 | int have_uid; | ||
957 | }; | ||
958 | |||
959 | |||
960 | /** | ||
961 | * Wrapper for the processor for #sqlite_plugin_get_replication(). | ||
962 | * Decrements the replication counter and calls the original | ||
963 | * processor. | ||
964 | * | ||
965 | * @param cls closure | ||
966 | * @param key key for the content | ||
967 | * @param size number of bytes in @a data | ||
968 | * @param data content stored | ||
969 | * @param type type of the content | ||
970 | * @param priority priority of the content | ||
971 | * @param anonymity anonymity-level for the content | ||
972 | * @param replication replication-level for the content | ||
973 | * @param expiration expiration time for the content | ||
974 | * @param uid unique identifier for the datum; | ||
975 | * maybe 0 if no unique identifier is available | ||
976 | * @return #GNUNET_OK for normal return, | ||
977 | * #GNUNET_NO to delete the item | ||
978 | */ | ||
979 | static int | ||
980 | repl_proc (void *cls, | ||
981 | const struct GNUNET_HashCode *key, | ||
982 | uint32_t size, | ||
983 | const void *data, | ||
984 | enum GNUNET_BLOCK_Type type, | ||
985 | uint32_t priority, | ||
986 | uint32_t anonymity, | ||
987 | uint32_t replication, | ||
988 | struct GNUNET_TIME_Absolute expiration, | ||
989 | uint64_t uid) | ||
990 | { | ||
991 | struct ReplCtx *rc = cls; | ||
992 | int ret; | ||
993 | |||
994 | if (GNUNET_SYSERR == rc->have_uid) | ||
995 | rc->have_uid = GNUNET_NO; | ||
996 | ret = rc->proc (rc->proc_cls, | ||
997 | key, | ||
998 | size, | ||
999 | data, | ||
1000 | type, | ||
1001 | priority, | ||
1002 | anonymity, | ||
1003 | replication, | ||
1004 | expiration, | ||
1005 | uid); | ||
1006 | if (NULL != key) | ||
1007 | { | ||
1008 | rc->uid = uid; | ||
1009 | rc->have_uid = GNUNET_YES; | ||
1010 | } | ||
1011 | return ret; | ||
1012 | } | ||
1013 | |||
1014 | |||
1015 | /** | ||
1016 | * Get a random item for replication. Returns a single random item | ||
1017 | * from those with the highest replication counters. The item's | ||
1018 | * replication counter is decremented by one IF it was positive before. | ||
1019 | * Call @a proc with all values ZERO or NULL if the datastore is empty. | ||
1020 | * | ||
1021 | * @param cls closure | ||
1022 | * @param proc function to call the value (once only). | ||
1023 | * @param proc_cls closure for @a proc | ||
1024 | */ | ||
1025 | static void | ||
1026 | sqlite_plugin_get_replication (void *cls, | ||
1027 | PluginDatumProcessor proc, | ||
1028 | void *proc_cls) | ||
1029 | { | ||
1030 | struct Plugin *plugin = cls; | ||
1031 | struct ReplCtx rc; | ||
1032 | uint64_t rvalue = 0; | ||
1033 | uint32_t repl; | ||
1034 | struct GNUNET_SQ_QueryParam params_sel_repl[] = | ||
1035 | { GNUNET_SQ_query_param_uint64 (&rvalue), | ||
1036 | GNUNET_SQ_query_param_uint32 (&repl), | ||
1037 | GNUNET_SQ_query_param_end }; | ||
1038 | struct GNUNET_SQ_QueryParam params_upd_repl[] = | ||
1039 | { GNUNET_SQ_query_param_uint64 (&rc.uid), GNUNET_SQ_query_param_end }; | ||
1040 | |||
1041 | GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, | ||
1042 | "datastore-sqlite", | ||
1043 | "Getting random block based on replication order.\n"); | ||
1044 | if (SQLITE_ROW != sqlite3_step (plugin->maxRepl)) | ||
1045 | { | ||
1046 | GNUNET_SQ_reset (plugin->dbh, plugin->maxRepl); | ||
1047 | /* DB empty */ | ||
1048 | proc (proc_cls, NULL, 0, NULL, 0, 0, 0, 0, GNUNET_TIME_UNIT_ZERO_ABS, 0); | ||
1049 | return; | ||
1050 | } | ||
1051 | repl = sqlite3_column_int (plugin->maxRepl, 0); | ||
1052 | GNUNET_SQ_reset (plugin->dbh, plugin->maxRepl); | ||
1053 | rvalue = GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK, UINT64_MAX); | ||
1054 | if (GNUNET_OK != GNUNET_SQ_bind (plugin->selRepl, params_sel_repl)) | ||
1055 | { | ||
1056 | proc (proc_cls, NULL, 0, NULL, 0, 0, 0, 0, GNUNET_TIME_UNIT_ZERO_ABS, 0); | ||
1057 | return; | ||
1058 | } | ||
1059 | rc.have_uid = GNUNET_SYSERR; | ||
1060 | rc.proc = proc; | ||
1061 | rc.proc_cls = proc_cls; | ||
1062 | execute_get (plugin, plugin->selRepl, &repl_proc, &rc); | ||
1063 | if (GNUNET_YES == rc.have_uid) | ||
1064 | { | ||
1065 | if (GNUNET_OK != GNUNET_SQ_bind (plugin->updRepl, params_upd_repl)) | ||
1066 | { | ||
1067 | proc (proc_cls, NULL, 0, NULL, 0, 0, 0, 0, GNUNET_TIME_UNIT_ZERO_ABS, 0); | ||
1068 | return; | ||
1069 | } | ||
1070 | if (SQLITE_DONE != sqlite3_step (plugin->updRepl)) | ||
1071 | LOG_SQLITE (plugin, | ||
1072 | GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK, | ||
1073 | "sqlite3_step"); | ||
1074 | GNUNET_SQ_reset (plugin->dbh, plugin->updRepl); | ||
1075 | } | ||
1076 | if (GNUNET_SYSERR == rc.have_uid) | ||
1077 | { | ||
1078 | /* proc was not called at all so far, do it now. */ | ||
1079 | proc (proc_cls, NULL, 0, NULL, 0, 0, 0, 0, GNUNET_TIME_UNIT_ZERO_ABS, 0); | ||
1080 | } | ||
1081 | } | ||
1082 | |||
1083 | |||
1084 | /** | ||
1085 | * Get a random item that has expired or has low priority. | ||
1086 | * Call @a proc with all values ZERO or NULL if the datastore is empty. | ||
1087 | * | ||
1088 | * @param cls closure | ||
1089 | * @param proc function to call the value (once only). | ||
1090 | * @param proc_cls closure for @a proc | ||
1091 | */ | ||
1092 | static void | ||
1093 | sqlite_plugin_get_expiration (void *cls, | ||
1094 | PluginDatumProcessor proc, | ||
1095 | void *proc_cls) | ||
1096 | { | ||
1097 | struct Plugin *plugin = cls; | ||
1098 | sqlite3_stmt *stmt; | ||
1099 | struct GNUNET_TIME_Absolute now = { 0 }; | ||
1100 | struct GNUNET_SQ_QueryParam params[] = { GNUNET_SQ_query_param_absolute_time ( | ||
1101 | &now), | ||
1102 | GNUNET_SQ_query_param_end }; | ||
1103 | |||
1104 | GNUNET_log_from ( | ||
1105 | GNUNET_ERROR_TYPE_DEBUG, | ||
1106 | "sqlite", | ||
1107 | "Getting random block based on expiration and priority order.\n"); | ||
1108 | now = GNUNET_TIME_absolute_get (); | ||
1109 | stmt = plugin->selExpi; | ||
1110 | if (GNUNET_OK != GNUNET_SQ_bind (stmt, params)) | ||
1111 | { | ||
1112 | proc (proc_cls, NULL, 0, NULL, 0, 0, 0, 0, GNUNET_TIME_UNIT_ZERO_ABS, 0); | ||
1113 | return; | ||
1114 | } | ||
1115 | execute_get (plugin, stmt, proc, proc_cls); | ||
1116 | } | ||
1117 | |||
1118 | |||
1119 | /** | ||
1120 | * Get all of the keys in the datastore. | ||
1121 | * | ||
1122 | * @param cls closure | ||
1123 | * @param proc function to call on each key | ||
1124 | * @param proc_cls closure for @a proc | ||
1125 | */ | ||
1126 | static void | ||
1127 | sqlite_plugin_get_keys (void *cls, PluginKeyProcessor proc, void *proc_cls) | ||
1128 | { | ||
1129 | struct Plugin *plugin = cls; | ||
1130 | struct GNUNET_HashCode key; | ||
1131 | struct GNUNET_SQ_ResultSpec results[] = | ||
1132 | { GNUNET_SQ_result_spec_auto_from_type (&key), GNUNET_SQ_result_spec_end }; | ||
1133 | sqlite3_stmt *stmt; | ||
1134 | int ret; | ||
1135 | |||
1136 | GNUNET_assert (NULL != proc); | ||
1137 | if (SQLITE_OK != sq_prepare (plugin->dbh, "SELECT hash FROM gn091", &stmt)) | ||
1138 | { | ||
1139 | LOG_SQLITE (plugin, | ||
1140 | GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK, | ||
1141 | "sqlite_prepare"); | ||
1142 | proc (proc_cls, NULL, 0); | ||
1143 | return; | ||
1144 | } | ||
1145 | while (SQLITE_ROW == (ret = sqlite3_step (stmt))) | ||
1146 | { | ||
1147 | if (GNUNET_OK == GNUNET_SQ_extract_result (stmt, results)) | ||
1148 | proc (proc_cls, &key, 1); | ||
1149 | else | ||
1150 | GNUNET_break (0); | ||
1151 | } | ||
1152 | if (SQLITE_DONE != ret) | ||
1153 | LOG_SQLITE (plugin, GNUNET_ERROR_TYPE_ERROR, "sqlite_step"); | ||
1154 | sqlite3_finalize (stmt); | ||
1155 | proc (proc_cls, NULL, 0); | ||
1156 | } | ||
1157 | |||
1158 | |||
1159 | /** | ||
1160 | * Drop database. | ||
1161 | * | ||
1162 | * @param cls our plugin context | ||
1163 | */ | ||
1164 | static void | ||
1165 | sqlite_plugin_drop (void *cls) | ||
1166 | { | ||
1167 | struct Plugin *plugin = cls; | ||
1168 | |||
1169 | plugin->drop_on_shutdown = GNUNET_YES; | ||
1170 | } | ||
1171 | |||
1172 | |||
1173 | /** | ||
1174 | * Remove a particular key in the datastore. | ||
1175 | * | ||
1176 | * @param cls closure | ||
1177 | * @param key key for the content | ||
1178 | * @param size number of bytes in data | ||
1179 | * @param data content stored | ||
1180 | * @param cont continuation called with success or failure status | ||
1181 | * @param cont_cls continuation closure for @a cont | ||
1182 | */ | ||
1183 | static void | ||
1184 | sqlite_plugin_remove_key (void *cls, | ||
1185 | const struct GNUNET_HashCode *key, | ||
1186 | uint32_t size, | ||
1187 | const void *data, | ||
1188 | PluginRemoveCont cont, | ||
1189 | void *cont_cls) | ||
1190 | { | ||
1191 | struct Plugin *plugin = cls; | ||
1192 | struct GNUNET_SQ_QueryParam params[] = | ||
1193 | { GNUNET_SQ_query_param_auto_from_type (key), | ||
1194 | GNUNET_SQ_query_param_fixed_size (data, size), | ||
1195 | GNUNET_SQ_query_param_end }; | ||
1196 | |||
1197 | if (GNUNET_OK != GNUNET_SQ_bind (plugin->remove, params)) | ||
1198 | { | ||
1199 | cont (cont_cls, key, size, GNUNET_SYSERR, "bind failed"); | ||
1200 | return; | ||
1201 | } | ||
1202 | if (SQLITE_DONE != sqlite3_step (plugin->remove)) | ||
1203 | { | ||
1204 | LOG_SQLITE (plugin, | ||
1205 | GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK, | ||
1206 | "sqlite3_step"); | ||
1207 | GNUNET_SQ_reset (plugin->dbh, plugin->remove); | ||
1208 | cont (cont_cls, key, size, GNUNET_SYSERR, "sqlite3_step failed"); | ||
1209 | return; | ||
1210 | } | ||
1211 | int changes = sqlite3_changes (plugin->dbh); | ||
1212 | GNUNET_SQ_reset (plugin->dbh, plugin->remove); | ||
1213 | if (0 == changes) | ||
1214 | { | ||
1215 | cont (cont_cls, key, size, GNUNET_NO, NULL); | ||
1216 | return; | ||
1217 | } | ||
1218 | if (NULL != plugin->env->duc) | ||
1219 | plugin->env->duc (plugin->env->cls, | ||
1220 | -(size + GNUNET_DATASTORE_ENTRY_OVERHEAD)); | ||
1221 | cont (cont_cls, key, size, GNUNET_OK, NULL); | ||
1222 | } | ||
1223 | |||
1224 | |||
1225 | /** | ||
1226 | * Get an estimate of how much space the database is | ||
1227 | * currently using. | ||
1228 | * | ||
1229 | * @param cls the `struct Plugin` | ||
1230 | * @return the size of the database on disk (estimate) | ||
1231 | */ | ||
1232 | static void | ||
1233 | sqlite_plugin_estimate_size (void *cls, unsigned long long *estimate) | ||
1234 | { | ||
1235 | struct Plugin *plugin = cls; | ||
1236 | sqlite3_stmt *stmt; | ||
1237 | uint64_t pages; | ||
1238 | uint64_t page_size; | ||
1239 | |||
1240 | #if ENULL_DEFINED | ||
1241 | char *e; | ||
1242 | #endif | ||
1243 | |||
1244 | if (NULL == estimate) | ||
1245 | return; | ||
1246 | if (SQLITE_VERSION_NUMBER < 3006000) | ||
1247 | { | ||
1248 | GNUNET_log_from ( | ||
1249 | GNUNET_ERROR_TYPE_WARNING, | ||
1250 | "datastore-sqlite", | ||
1251 | _ ("sqlite version to old to determine size, assuming zero\n")); | ||
1252 | *estimate = 0; | ||
1253 | return; | ||
1254 | } | ||
1255 | CHECK (SQLITE_OK == sqlite3_exec (plugin->dbh, "VACUUM", NULL, NULL, ENULL)); | ||
1256 | CHECK (SQLITE_OK == sqlite3_exec (plugin->dbh, | ||
1257 | "PRAGMA auto_vacuum=INCREMENTAL", | ||
1258 | NULL, | ||
1259 | NULL, | ||
1260 | ENULL)); | ||
1261 | if (SQLITE_OK != sq_prepare (plugin->dbh, "PRAGMA page_count", &stmt)) | ||
1262 | { | ||
1263 | GNUNET_log_from ( | ||
1264 | GNUNET_ERROR_TYPE_WARNING, | ||
1265 | "datastore-sqlite", | ||
1266 | _("error preparing statement\n")); | ||
1267 | return; | ||
1268 | } | ||
1269 | if (SQLITE_ROW == sqlite3_step (stmt)) | ||
1270 | pages = sqlite3_column_int64 (stmt, 0); | ||
1271 | else | ||
1272 | pages = 0; | ||
1273 | sqlite3_finalize (stmt); | ||
1274 | if (SQLITE_OK != sq_prepare (plugin->dbh, "PRAGMA page_size", &stmt)) | ||
1275 | { | ||
1276 | GNUNET_log_from ( | ||
1277 | GNUNET_ERROR_TYPE_WARNING, | ||
1278 | "datastore-sqlite", | ||
1279 | _("error preparing statement\n")); | ||
1280 | return; | ||
1281 | } | ||
1282 | if (SQLITE_ROW != sqlite3_step (stmt)) | ||
1283 | { | ||
1284 | GNUNET_log_from ( | ||
1285 | GNUNET_ERROR_TYPE_WARNING, | ||
1286 | "datastore-sqlite", | ||
1287 | _("error stepping\n")); | ||
1288 | return; | ||
1289 | } | ||
1290 | page_size = sqlite3_column_int64 (stmt, 0); | ||
1291 | sqlite3_finalize (stmt); | ||
1292 | GNUNET_log ( | ||
1293 | GNUNET_ERROR_TYPE_INFO, | ||
1294 | _ ( | ||
1295 | "Using sqlite page utilization to estimate payload (%llu pages of size %llu bytes)\n"), | ||
1296 | (unsigned long long) pages, | ||
1297 | (unsigned long long) page_size); | ||
1298 | *estimate = pages * page_size; | ||
1299 | } | ||
1300 | |||
1301 | |||
1302 | /** | ||
1303 | * Entry point for the plugin. | ||
1304 | * | ||
1305 | * @param cls the `struct GNUNET_DATASTORE_PluginEnvironment *` | ||
1306 | * @return NULL on error, othrewise the plugin context | ||
1307 | */ | ||
1308 | void * | ||
1309 | libgnunet_plugin_datastore_sqlite_init (void *cls) | ||
1310 | { | ||
1311 | static struct Plugin plugin; | ||
1312 | struct GNUNET_DATASTORE_PluginEnvironment *env = cls; | ||
1313 | struct GNUNET_DATASTORE_PluginFunctions *api; | ||
1314 | |||
1315 | if (NULL != plugin.env) | ||
1316 | return NULL; /* can only initialize once! */ | ||
1317 | memset (&plugin, 0, sizeof(struct Plugin)); | ||
1318 | plugin.env = env; | ||
1319 | if (GNUNET_OK != database_setup (env->cfg, &plugin)) | ||
1320 | { | ||
1321 | database_shutdown (&plugin); | ||
1322 | return NULL; | ||
1323 | } | ||
1324 | api = GNUNET_new (struct GNUNET_DATASTORE_PluginFunctions); | ||
1325 | api->cls = &plugin; | ||
1326 | api->estimate_size = &sqlite_plugin_estimate_size; | ||
1327 | api->put = &sqlite_plugin_put; | ||
1328 | api->get_key = &sqlite_plugin_get_key; | ||
1329 | api->get_replication = &sqlite_plugin_get_replication; | ||
1330 | api->get_expiration = &sqlite_plugin_get_expiration; | ||
1331 | api->get_zero_anonymity = &sqlite_plugin_get_zero_anonymity; | ||
1332 | api->get_keys = &sqlite_plugin_get_keys; | ||
1333 | api->drop = &sqlite_plugin_drop; | ||
1334 | api->remove_key = &sqlite_plugin_remove_key; | ||
1335 | GNUNET_log_from (GNUNET_ERROR_TYPE_INFO, | ||
1336 | "sqlite", | ||
1337 | _ ("Sqlite database running\n")); | ||
1338 | return api; | ||
1339 | } | ||
1340 | |||
1341 | |||
1342 | /** | ||
1343 | * Exit point from the plugin. | ||
1344 | * | ||
1345 | * @param cls the plugin context (as returned by "init") | ||
1346 | * @return always NULL | ||
1347 | */ | ||
1348 | void * | ||
1349 | libgnunet_plugin_datastore_sqlite_done (void *cls) | ||
1350 | { | ||
1351 | char *fn; | ||
1352 | struct GNUNET_DATASTORE_PluginFunctions *api = cls; | ||
1353 | struct Plugin *plugin = api->cls; | ||
1354 | |||
1355 | GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, | ||
1356 | "sqlite", | ||
1357 | "sqlite plugin is done\n"); | ||
1358 | fn = NULL; | ||
1359 | if (plugin->drop_on_shutdown) | ||
1360 | fn = GNUNET_strdup (plugin->fn); | ||
1361 | database_shutdown (plugin); | ||
1362 | plugin->env = NULL; | ||
1363 | GNUNET_free (api); | ||
1364 | if (NULL != fn) | ||
1365 | { | ||
1366 | if (0 != unlink (fn)) | ||
1367 | GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "unlink", fn); | ||
1368 | GNUNET_free (fn); | ||
1369 | } | ||
1370 | return NULL; | ||
1371 | } | ||
1372 | |||
1373 | |||
1374 | /* end of plugin_datastore_sqlite.c */ | ||