diff options
Diffstat (limited to 'src/plugin/peerstore/plugin_peerstore_sqlite.c')
-rw-r--r-- | src/plugin/peerstore/plugin_peerstore_sqlite.c | 743 |
1 files changed, 743 insertions, 0 deletions
diff --git a/src/plugin/peerstore/plugin_peerstore_sqlite.c b/src/plugin/peerstore/plugin_peerstore_sqlite.c new file mode 100644 index 000000000..c7132bd85 --- /dev/null +++ b/src/plugin/peerstore/plugin_peerstore_sqlite.c | |||
@@ -0,0 +1,743 @@ | |||
1 | /* | ||
2 | * This file is part of GNUnet | ||
3 | * Copyright (C) 2013, 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 peerstore/plugin_peerstore_sqlite.c | ||
23 | * @brief sqlite-based peerstore backend | ||
24 | * @author Omar Tarabai | ||
25 | * @author Christian Grothoff | ||
26 | */ | ||
27 | |||
28 | #include "platform.h" | ||
29 | #include "gnunet_peerstore_plugin.h" | ||
30 | #include "gnunet_peerstore_service.h" | ||
31 | #include "gnunet_sq_lib.h" | ||
32 | #include "../../service/peerstore/peerstore.h" | ||
33 | #include <sqlite3.h> | ||
34 | |||
35 | /** | ||
36 | * After how many ms "busy" should a DB operation fail for good? A | ||
37 | * low value makes sure that we are more responsive to requests | ||
38 | * (especially PUTs). A high value guarantees a higher success rate | ||
39 | * (SELECTs in iterate can take several seconds despite LIMIT=1). | ||
40 | * | ||
41 | * The default value of 1s should ensure that users do not experience | ||
42 | * huge latencies while at the same time allowing operations to | ||
43 | * succeed with reasonable probability. | ||
44 | */ | ||
45 | #define BUSY_TIMEOUT_MS 1000 | ||
46 | |||
47 | /** | ||
48 | * Log an error message at log-level 'level' that indicates | ||
49 | * a failure of the command 'cmd' on file 'filename' | ||
50 | * with the message given by strerror(errno). | ||
51 | */ | ||
52 | #define LOG_SQLITE(db, level, cmd) do { GNUNET_log_from (level, \ | ||
53 | "peerstore-sqlite", _ ( \ | ||
54 | "`%s' failed at %s:%d with error: %s\n"), \ | ||
55 | cmd, \ | ||
56 | __FILE__, __LINE__, \ | ||
57 | sqlite3_errmsg ( \ | ||
58 | db->dbh)); \ | ||
59 | } while (0) | ||
60 | |||
61 | #define LOG(kind, ...) GNUNET_log_from (kind, "peerstore-sqlite", __VA_ARGS__) | ||
62 | |||
63 | /** | ||
64 | * Context for all functions in this plugin. | ||
65 | */ | ||
66 | struct Plugin | ||
67 | { | ||
68 | /** | ||
69 | * Configuration handle | ||
70 | */ | ||
71 | const struct GNUNET_CONFIGURATION_Handle *cfg; | ||
72 | |||
73 | /** | ||
74 | * Database filename. | ||
75 | */ | ||
76 | char *fn; | ||
77 | |||
78 | /** | ||
79 | * Native SQLite database handle. | ||
80 | */ | ||
81 | sqlite3 *dbh; | ||
82 | |||
83 | /** | ||
84 | * Precompiled SQL for inserting into peerstoredata | ||
85 | */ | ||
86 | sqlite3_stmt *insert_peerstoredata; | ||
87 | |||
88 | /** | ||
89 | * Precompiled SQL for selecting from peerstoredata | ||
90 | */ | ||
91 | sqlite3_stmt *select_peerstoredata; | ||
92 | |||
93 | /** | ||
94 | * Precompiled SQL for selecting from peerstoredata | ||
95 | */ | ||
96 | sqlite3_stmt *select_peerstoredata_by_pid; | ||
97 | |||
98 | /** | ||
99 | * Precompiled SQL for selecting from peerstoredata | ||
100 | */ | ||
101 | sqlite3_stmt *select_peerstoredata_by_key; | ||
102 | |||
103 | /** | ||
104 | * Precompiled SQL for selecting from peerstoredata | ||
105 | */ | ||
106 | sqlite3_stmt *select_peerstoredata_by_all; | ||
107 | |||
108 | /** | ||
109 | * Precompiled SQL for deleting expired | ||
110 | * records from peerstoredata | ||
111 | */ | ||
112 | sqlite3_stmt *expire_peerstoredata; | ||
113 | |||
114 | /** | ||
115 | * Precompiled SQL for deleting records | ||
116 | * with given key | ||
117 | */ | ||
118 | sqlite3_stmt *delete_peerstoredata; | ||
119 | }; | ||
120 | |||
121 | |||
122 | /** | ||
123 | * Delete records with the given key | ||
124 | * | ||
125 | * @param cls closure (internal context for the plugin) | ||
126 | * @param sub_system name of sub system | ||
127 | * @param peer Peer identity (can be NULL) | ||
128 | * @param key entry key string (can be NULL) | ||
129 | * @return number of deleted records, #GNUNE_SYSERR on error | ||
130 | */ | ||
131 | static int | ||
132 | peerstore_sqlite_delete_records (void *cls, | ||
133 | const char *sub_system, | ||
134 | const struct GNUNET_PeerIdentity *peer, | ||
135 | const char *key) | ||
136 | { | ||
137 | struct Plugin *plugin = cls; | ||
138 | sqlite3_stmt *stmt = plugin->delete_peerstoredata; | ||
139 | struct GNUNET_SQ_QueryParam params[] = { | ||
140 | GNUNET_SQ_query_param_string (sub_system), | ||
141 | GNUNET_SQ_query_param_auto_from_type (peer), | ||
142 | GNUNET_SQ_query_param_string (key), | ||
143 | GNUNET_SQ_query_param_end | ||
144 | }; | ||
145 | int ret; | ||
146 | |||
147 | if (GNUNET_OK != | ||
148 | GNUNET_SQ_bind (stmt, | ||
149 | params)) | ||
150 | { | ||
151 | LOG_SQLITE (plugin, | ||
152 | GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK, | ||
153 | "sqlite3_bind"); | ||
154 | GNUNET_SQ_reset (plugin->dbh, | ||
155 | stmt); | ||
156 | return GNUNET_SYSERR; | ||
157 | } | ||
158 | if (SQLITE_DONE != | ||
159 | sqlite3_step (stmt)) | ||
160 | { | ||
161 | LOG_SQLITE (plugin, | ||
162 | GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK, | ||
163 | "sqlite3_step"); | ||
164 | ret = GNUNET_SYSERR; | ||
165 | } | ||
166 | else | ||
167 | { | ||
168 | ret = sqlite3_changes (plugin->dbh); | ||
169 | } | ||
170 | GNUNET_SQ_reset (plugin->dbh, | ||
171 | stmt); | ||
172 | return ret; | ||
173 | } | ||
174 | |||
175 | |||
176 | /** | ||
177 | * Delete expired records (expiry < now) | ||
178 | * | ||
179 | * @param cls closure (internal context for the plugin) | ||
180 | * @param now time to use as reference | ||
181 | * @param cont continuation called with the number of records expired | ||
182 | * @param cont_cls continuation closure | ||
183 | * @return #GNUNET_OK on success, #GNUNET_SYSERR on error and cont is not | ||
184 | * called | ||
185 | */ | ||
186 | static int | ||
187 | peerstore_sqlite_expire_records (void *cls, struct GNUNET_TIME_Absolute now, | ||
188 | GNUNET_PEERSTORE_Continuation cont, | ||
189 | void *cont_cls) | ||
190 | { | ||
191 | struct Plugin *plugin = cls; | ||
192 | sqlite3_stmt *stmt = plugin->expire_peerstoredata; | ||
193 | struct GNUNET_SQ_QueryParam params[] = { | ||
194 | GNUNET_SQ_query_param_absolute_time (&now), | ||
195 | GNUNET_SQ_query_param_end | ||
196 | }; | ||
197 | |||
198 | if (GNUNET_OK != | ||
199 | GNUNET_SQ_bind (stmt, | ||
200 | params)) | ||
201 | { | ||
202 | LOG_SQLITE (plugin, | ||
203 | GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK, | ||
204 | "sqlite3_bind"); | ||
205 | GNUNET_SQ_reset (plugin->dbh, | ||
206 | stmt); | ||
207 | return GNUNET_SYSERR; | ||
208 | } | ||
209 | if (SQLITE_DONE != sqlite3_step (stmt)) | ||
210 | { | ||
211 | LOG_SQLITE (plugin, | ||
212 | GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK, | ||
213 | "sqlite3_step"); | ||
214 | GNUNET_SQ_reset (plugin->dbh, | ||
215 | stmt); | ||
216 | return GNUNET_SYSERR; | ||
217 | } | ||
218 | if (NULL != cont) | ||
219 | cont (cont_cls, | ||
220 | sqlite3_changes (plugin->dbh)); | ||
221 | GNUNET_SQ_reset (plugin->dbh, | ||
222 | stmt); | ||
223 | return GNUNET_OK; | ||
224 | } | ||
225 | |||
226 | |||
227 | /** | ||
228 | * Iterate over the records given an optional peer id | ||
229 | * and/or key. | ||
230 | * | ||
231 | * @param cls closure (internal context for the plugin) | ||
232 | * @param sub_system name of sub system | ||
233 | * @param peer Peer identity (can be NULL) | ||
234 | * @param key entry key string (can be NULL) | ||
235 | * @param iter function to call asynchronously with the results, terminated | ||
236 | * by a NULL result | ||
237 | * @param iter_cls closure for @a iter | ||
238 | * @return #GNUNET_OK on success, #GNUNET_SYSERR on error and iter is not | ||
239 | * called | ||
240 | */ | ||
241 | static int | ||
242 | peerstore_sqlite_iterate_records (void *cls, | ||
243 | const char *sub_system, | ||
244 | const struct GNUNET_PeerIdentity *peer, | ||
245 | const char *key, | ||
246 | uint64_t serial, | ||
247 | uint64_t limit, | ||
248 | GNUNET_PEERSTORE_PluginProcessor iter, | ||
249 | void *iter_cls) | ||
250 | { | ||
251 | struct Plugin *plugin = cls; | ||
252 | sqlite3_stmt *stmt; | ||
253 | int err = 0; | ||
254 | int sret; | ||
255 | int ret; | ||
256 | uint64_t seq; | ||
257 | struct GNUNET_PEERSTORE_Record rec; | ||
258 | |||
259 | LOG (GNUNET_ERROR_TYPE_DEBUG, | ||
260 | "Executing iterate request on sqlite db.\n"); | ||
261 | if (NULL == peer) | ||
262 | { | ||
263 | if (NULL == key) | ||
264 | { | ||
265 | struct GNUNET_SQ_QueryParam params[] = { | ||
266 | GNUNET_SQ_query_param_string (sub_system), | ||
267 | GNUNET_SQ_query_param_uint64 (&serial), | ||
268 | GNUNET_SQ_query_param_uint64 (&limit), | ||
269 | GNUNET_SQ_query_param_end | ||
270 | }; | ||
271 | |||
272 | stmt = plugin->select_peerstoredata; | ||
273 | err = GNUNET_SQ_bind (stmt, | ||
274 | params); | ||
275 | } | ||
276 | else | ||
277 | { | ||
278 | struct GNUNET_SQ_QueryParam params[] = { | ||
279 | GNUNET_SQ_query_param_string (sub_system), | ||
280 | GNUNET_SQ_query_param_string (key), | ||
281 | GNUNET_SQ_query_param_uint64 (&serial), | ||
282 | GNUNET_SQ_query_param_uint64 (&limit), | ||
283 | GNUNET_SQ_query_param_end | ||
284 | }; | ||
285 | |||
286 | stmt = plugin->select_peerstoredata_by_key; | ||
287 | err = GNUNET_SQ_bind (stmt, | ||
288 | params); | ||
289 | } | ||
290 | } | ||
291 | else | ||
292 | { | ||
293 | if (NULL == key) | ||
294 | { | ||
295 | struct GNUNET_SQ_QueryParam params[] = { | ||
296 | GNUNET_SQ_query_param_string (sub_system), | ||
297 | GNUNET_SQ_query_param_auto_from_type (peer), | ||
298 | GNUNET_SQ_query_param_uint64 (&serial), | ||
299 | GNUNET_SQ_query_param_uint64 (&limit), | ||
300 | GNUNET_SQ_query_param_end | ||
301 | }; | ||
302 | |||
303 | stmt = plugin->select_peerstoredata_by_pid; | ||
304 | err = GNUNET_SQ_bind (stmt, | ||
305 | params); | ||
306 | } | ||
307 | else | ||
308 | { | ||
309 | struct GNUNET_SQ_QueryParam params[] = { | ||
310 | GNUNET_SQ_query_param_string (sub_system), | ||
311 | GNUNET_SQ_query_param_auto_from_type (peer), | ||
312 | GNUNET_SQ_query_param_string (key), | ||
313 | GNUNET_SQ_query_param_uint64 (&serial), | ||
314 | GNUNET_SQ_query_param_uint64 (&limit), | ||
315 | GNUNET_SQ_query_param_end | ||
316 | }; | ||
317 | |||
318 | stmt = plugin->select_peerstoredata_by_all; | ||
319 | err = GNUNET_SQ_bind (stmt, | ||
320 | params); | ||
321 | } | ||
322 | } | ||
323 | |||
324 | if (GNUNET_OK != err) | ||
325 | { | ||
326 | LOG_SQLITE (plugin, | ||
327 | GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK, | ||
328 | "sqlite3_bind_XXXX"); | ||
329 | GNUNET_SQ_reset (plugin->dbh, | ||
330 | stmt); | ||
331 | return GNUNET_SYSERR; | ||
332 | } | ||
333 | |||
334 | err = 0; | ||
335 | ret = GNUNET_OK; | ||
336 | for (uint64_t i = 0; i < limit; i++) | ||
337 | { | ||
338 | sret = sqlite3_step (stmt); | ||
339 | if (SQLITE_DONE == sret) | ||
340 | { | ||
341 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
342 | "Iteration done (no results)\n"); | ||
343 | ret = GNUNET_NO; | ||
344 | break; | ||
345 | } | ||
346 | if (SQLITE_ROW != sret) | ||
347 | { | ||
348 | LOG_SQLITE (plugin, | ||
349 | GNUNET_ERROR_TYPE_ERROR, | ||
350 | "sqlite_step"); | ||
351 | ret = GNUNET_SYSERR; | ||
352 | break; | ||
353 | } | ||
354 | { | ||
355 | LOG (GNUNET_ERROR_TYPE_DEBUG, | ||
356 | "Returning a matched record.\n"); | ||
357 | struct GNUNET_SQ_ResultSpec rs[] = { | ||
358 | GNUNET_SQ_result_spec_uint64 (&seq), | ||
359 | GNUNET_SQ_result_spec_string (&rec.sub_system), | ||
360 | GNUNET_SQ_result_spec_auto_from_type (&rec.peer), | ||
361 | GNUNET_SQ_result_spec_string (&rec.key), | ||
362 | GNUNET_SQ_result_spec_variable_size (&rec.value, &rec.value_size), | ||
363 | GNUNET_SQ_result_spec_absolute_time (&rec.expiry), | ||
364 | GNUNET_SQ_result_spec_end | ||
365 | }; | ||
366 | |||
367 | if (GNUNET_OK != | ||
368 | GNUNET_SQ_extract_result (stmt, | ||
369 | rs)) | ||
370 | { | ||
371 | GNUNET_break (0); | ||
372 | break; | ||
373 | } | ||
374 | if (NULL != iter) | ||
375 | iter (iter_cls, | ||
376 | seq, | ||
377 | &rec, | ||
378 | NULL); | ||
379 | GNUNET_SQ_cleanup_result (rs); | ||
380 | } | ||
381 | } | ||
382 | GNUNET_SQ_reset (plugin->dbh, | ||
383 | stmt); | ||
384 | return ret; | ||
385 | } | ||
386 | |||
387 | |||
388 | /** | ||
389 | * Store a record in the peerstore. | ||
390 | * Key is the combination of sub system and peer identity. | ||
391 | * One key can store multiple values. | ||
392 | * | ||
393 | * @param cls closure (internal context for the plugin) | ||
394 | * @param sub_system name of the GNUnet sub system responsible | ||
395 | * @param peer peer identity | ||
396 | * @param key record key string | ||
397 | * @param value value to be stored | ||
398 | * @param size size of value to be stored | ||
399 | * @param expiry absolute time after which the record is (possibly) deleted | ||
400 | * @param options options related to the store operation | ||
401 | * @param cont continuation called when record is stored | ||
402 | * @param cont_cls continuation closure | ||
403 | * @return #GNUNET_OK on success, else #GNUNET_SYSERR and cont is not called | ||
404 | */ | ||
405 | static int | ||
406 | peerstore_sqlite_store_record (void *cls, | ||
407 | const char *sub_system, | ||
408 | const struct GNUNET_PeerIdentity *peer, | ||
409 | const char *key, | ||
410 | const void *value, | ||
411 | size_t size, | ||
412 | struct GNUNET_TIME_Absolute expiry, | ||
413 | enum GNUNET_PEERSTORE_StoreOption options, | ||
414 | GNUNET_PEERSTORE_Continuation cont, | ||
415 | void *cont_cls) | ||
416 | { | ||
417 | struct Plugin *plugin = cls; | ||
418 | sqlite3_stmt *stmt; | ||
419 | struct GNUNET_SQ_QueryParam params[] = { | ||
420 | GNUNET_SQ_query_param_string (sub_system), | ||
421 | GNUNET_SQ_query_param_auto_from_type (peer), | ||
422 | GNUNET_SQ_query_param_string (key), | ||
423 | GNUNET_SQ_query_param_fixed_size (value, size), | ||
424 | GNUNET_SQ_query_param_absolute_time (&expiry), | ||
425 | GNUNET_SQ_query_param_end | ||
426 | }; | ||
427 | |||
428 | if (GNUNET_PEERSTORE_STOREOPTION_REPLACE == options) | ||
429 | { | ||
430 | peerstore_sqlite_delete_records (cls, | ||
431 | sub_system, | ||
432 | peer, | ||
433 | key); | ||
434 | } | ||
435 | |||
436 | stmt = plugin->insert_peerstoredata; | ||
437 | if (GNUNET_OK != | ||
438 | GNUNET_SQ_bind (stmt, | ||
439 | params)) | ||
440 | { | ||
441 | LOG_SQLITE (plugin, | ||
442 | GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK, | ||
443 | "sqlite3_bind"); | ||
444 | GNUNET_assert (0); | ||
445 | } | ||
446 | if (SQLITE_DONE != sqlite3_step (stmt)) | ||
447 | { | ||
448 | LOG_SQLITE (plugin, | ||
449 | GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK, | ||
450 | "sqlite3_step"); | ||
451 | } | ||
452 | GNUNET_SQ_reset (plugin->dbh, | ||
453 | stmt); | ||
454 | if (NULL != cont) | ||
455 | cont (cont_cls, | ||
456 | GNUNET_OK); | ||
457 | return GNUNET_OK; | ||
458 | } | ||
459 | |||
460 | |||
461 | /** | ||
462 | * @brief Prepare a SQL statement | ||
463 | * | ||
464 | * @param dbh handle to the database | ||
465 | * @param sql SQL statement, UTF-8 encoded | ||
466 | * @return 0 on success | ||
467 | */ | ||
468 | static int | ||
469 | sql_exec (sqlite3 *dbh, | ||
470 | const char *sql) | ||
471 | { | ||
472 | int result; | ||
473 | |||
474 | result = sqlite3_exec (dbh, | ||
475 | sql, | ||
476 | NULL, | ||
477 | NULL, | ||
478 | NULL); | ||
479 | LOG (GNUNET_ERROR_TYPE_DEBUG, | ||
480 | "Executed `%s' / %d\n", | ||
481 | sql, | ||
482 | result); | ||
483 | if (SQLITE_OK != result) | ||
484 | LOG (GNUNET_ERROR_TYPE_ERROR, | ||
485 | _ ("Error executing SQL query: %s\n %s\n"), | ||
486 | sqlite3_errmsg (dbh), | ||
487 | sql); | ||
488 | return result; | ||
489 | } | ||
490 | |||
491 | |||
492 | /** | ||
493 | * @brief Prepare a SQL statement | ||
494 | * | ||
495 | * @param dbh handle to the database | ||
496 | * @param sql SQL statement, UTF-8 encoded | ||
497 | * @param stmt set to the prepared statement | ||
498 | * @return 0 on success | ||
499 | */ | ||
500 | static int | ||
501 | sql_prepare (sqlite3 *dbh, | ||
502 | const char *sql, | ||
503 | sqlite3_stmt **stmt) | ||
504 | { | ||
505 | char *tail; | ||
506 | int result; | ||
507 | |||
508 | result = sqlite3_prepare_v2 (dbh, | ||
509 | sql, | ||
510 | strlen (sql), | ||
511 | stmt, | ||
512 | (const char **) &tail); | ||
513 | LOG (GNUNET_ERROR_TYPE_DEBUG, | ||
514 | "Prepared `%s' / %p: %d\n", | ||
515 | sql, | ||
516 | *stmt, | ||
517 | result); | ||
518 | if (SQLITE_OK != result) | ||
519 | LOG (GNUNET_ERROR_TYPE_ERROR, | ||
520 | _ ("Error preparing SQL query: %s\n %s\n"), | ||
521 | sqlite3_errmsg (dbh), | ||
522 | sql); | ||
523 | return result; | ||
524 | } | ||
525 | |||
526 | |||
527 | /** | ||
528 | * Initialize the database connections and associated | ||
529 | * data structures (create tables and indices | ||
530 | * as needed as well). | ||
531 | * | ||
532 | * @param plugin the plugin context (state for this module) | ||
533 | * @return #GNUNET_OK on success | ||
534 | */ | ||
535 | static int | ||
536 | database_setup (struct Plugin *plugin) | ||
537 | { | ||
538 | char *filename; | ||
539 | |||
540 | if (GNUNET_OK != | ||
541 | GNUNET_CONFIGURATION_get_value_filename (plugin->cfg, | ||
542 | "peerstore-sqlite", | ||
543 | "FILENAME", | ||
544 | &filename)) | ||
545 | { | ||
546 | GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, | ||
547 | "peerstore-sqlite", | ||
548 | "FILENAME"); | ||
549 | return GNUNET_SYSERR; | ||
550 | } | ||
551 | if (GNUNET_OK != GNUNET_DISK_file_test (filename)) | ||
552 | { | ||
553 | if (GNUNET_OK != GNUNET_DISK_directory_create_for_file (filename)) | ||
554 | { | ||
555 | GNUNET_break (0); | ||
556 | GNUNET_free (filename); | ||
557 | return GNUNET_SYSERR; | ||
558 | } | ||
559 | } | ||
560 | /* filename should be UTF-8-encoded. If it isn't, it's a bug */ | ||
561 | plugin->fn = filename; | ||
562 | /* Open database and precompile statements */ | ||
563 | if (SQLITE_OK != sqlite3_open (plugin->fn, | ||
564 | &plugin->dbh)) | ||
565 | { | ||
566 | LOG (GNUNET_ERROR_TYPE_ERROR, | ||
567 | _ ("Unable to initialize SQLite: %s.\n"), | ||
568 | sqlite3_errmsg (plugin->dbh)); | ||
569 | return GNUNET_SYSERR; | ||
570 | } | ||
571 | sql_exec (plugin->dbh, | ||
572 | "PRAGMA temp_store=MEMORY"); | ||
573 | sql_exec (plugin->dbh, | ||
574 | "PRAGMA synchronous=OFF"); | ||
575 | sql_exec (plugin->dbh, | ||
576 | "PRAGMA legacy_file_format=OFF"); | ||
577 | sql_exec (plugin->dbh, | ||
578 | "PRAGMA auto_vacuum=INCREMENTAL"); | ||
579 | sql_exec (plugin->dbh, | ||
580 | "PRAGMA encoding=\"UTF-8\""); | ||
581 | sql_exec (plugin->dbh, | ||
582 | "PRAGMA page_size=4096"); | ||
583 | sqlite3_busy_timeout (plugin->dbh, | ||
584 | BUSY_TIMEOUT_MS); | ||
585 | /* Create tables */ | ||
586 | sql_exec (plugin->dbh, | ||
587 | "CREATE TABLE IF NOT EXISTS peerstoredata (\n" | ||
588 | " uid INTEGER PRIMARY KEY," | ||
589 | " sub_system TEXT NOT NULL,\n" | ||
590 | " peer_id BLOB NOT NULL,\n" | ||
591 | " key TEXT NOT NULL,\n" | ||
592 | " value BLOB NULL,\n" | ||
593 | " expiry INT8 NOT NULL" ");"); | ||
594 | /* Create Indices */ | ||
595 | if (SQLITE_OK != | ||
596 | sqlite3_exec (plugin->dbh, | ||
597 | "CREATE INDEX IF NOT EXISTS peerstoredata_key_index ON peerstoredata (sub_system, peer_id, key, uid)", | ||
598 | NULL, | ||
599 | NULL, | ||
600 | NULL)) | ||
601 | { | ||
602 | LOG (GNUNET_ERROR_TYPE_ERROR, | ||
603 | _ ("Unable to create indices: %s.\n"), | ||
604 | sqlite3_errmsg (plugin->dbh)); | ||
605 | return GNUNET_SYSERR; | ||
606 | } | ||
607 | /* Prepare statements */ | ||
608 | |||
609 | sql_prepare (plugin->dbh, | ||
610 | "INSERT INTO peerstoredata (sub_system, peer_id, key, value, expiry)" | ||
611 | " VALUES (?,?,?,?,?);", | ||
612 | &plugin->insert_peerstoredata); | ||
613 | sql_prepare (plugin->dbh, | ||
614 | "SELECT uid,sub_system,peer_id,key,value,expiry FROM peerstoredata" | ||
615 | " WHERE sub_system = ?" | ||
616 | " AND uid > ?" | ||
617 | " ORDER BY uid ASC" | ||
618 | " LIMIT ?", | ||
619 | &plugin->select_peerstoredata); | ||
620 | sql_prepare (plugin->dbh, | ||
621 | "SELECT uid,sub_system,peer_id,key,value,expiry FROM peerstoredata" | ||
622 | " WHERE sub_system = ?" | ||
623 | " AND peer_id = ?" | ||
624 | " AND uid > ?" | ||
625 | " ORDER BY uid ASC" | ||
626 | " LIMIT ?", | ||
627 | &plugin->select_peerstoredata_by_pid); | ||
628 | sql_prepare (plugin->dbh, | ||
629 | "SELECT uid,sub_system,peer_id,key,value,expiry FROM peerstoredata" | ||
630 | " WHERE sub_system = ?" | ||
631 | " AND key = ?" | ||
632 | " AND uid > ?" | ||
633 | " ORDER BY uid ASC" | ||
634 | " LIMIT ?", | ||
635 | &plugin->select_peerstoredata_by_key); | ||
636 | sql_prepare (plugin->dbh, | ||
637 | "SELECT uid,sub_system,peer_id,key,value,expiry FROM peerstoredata" | ||
638 | " WHERE sub_system = ?" | ||
639 | " AND peer_id = ?" | ||
640 | " AND key = ?" | ||
641 | " AND uid > ?" | ||
642 | " ORDER BY uid ASC" | ||
643 | " LIMIT ?", | ||
644 | &plugin->select_peerstoredata_by_all); | ||
645 | sql_prepare (plugin->dbh, | ||
646 | "DELETE FROM peerstoredata" | ||
647 | " WHERE expiry < ?", | ||
648 | &plugin->expire_peerstoredata); | ||
649 | sql_prepare (plugin->dbh, | ||
650 | "DELETE FROM peerstoredata" | ||
651 | " WHERE sub_system = ?" | ||
652 | " AND peer_id = ?" " AND key = ?", | ||
653 | &plugin->delete_peerstoredata); | ||
654 | return GNUNET_OK; | ||
655 | } | ||
656 | |||
657 | |||
658 | /** | ||
659 | * Shutdown database connection and associate data | ||
660 | * structures. | ||
661 | * @param plugin the plugin context (state for this module) | ||
662 | */ | ||
663 | static void | ||
664 | database_shutdown (struct Plugin *plugin) | ||
665 | { | ||
666 | int result; | ||
667 | sqlite3_stmt *stmt; | ||
668 | |||
669 | while (NULL != (stmt = sqlite3_next_stmt (plugin->dbh, | ||
670 | NULL))) | ||
671 | { | ||
672 | result = sqlite3_finalize (stmt); | ||
673 | if (SQLITE_OK != result) | ||
674 | LOG (GNUNET_ERROR_TYPE_WARNING, | ||
675 | "Failed to close statement %p: %d\n", | ||
676 | stmt, | ||
677 | result); | ||
678 | } | ||
679 | if (SQLITE_OK != sqlite3_close (plugin->dbh)) | ||
680 | LOG_SQLITE (plugin, | ||
681 | GNUNET_ERROR_TYPE_ERROR, | ||
682 | "sqlite3_close"); | ||
683 | GNUNET_free (plugin->fn); | ||
684 | } | ||
685 | |||
686 | |||
687 | /** | ||
688 | * Entry point for the plugin. | ||
689 | * | ||
690 | * @param cls The struct GNUNET_CONFIGURATION_Handle. | ||
691 | * @return NULL on error, otherwise the plugin context | ||
692 | */ | ||
693 | void * | ||
694 | libgnunet_plugin_peerstore_sqlite_init (void *cls) | ||
695 | { | ||
696 | static struct Plugin plugin; | ||
697 | const struct GNUNET_CONFIGURATION_Handle *cfg = cls; | ||
698 | struct GNUNET_PEERSTORE_PluginFunctions *api; | ||
699 | |||
700 | if (NULL != plugin.cfg) | ||
701 | return NULL; /* can only initialize once! */ | ||
702 | memset (&plugin, | ||
703 | 0, | ||
704 | sizeof(struct Plugin)); | ||
705 | plugin.cfg = cfg; | ||
706 | if (GNUNET_OK != database_setup (&plugin)) | ||
707 | { | ||
708 | database_shutdown (&plugin); | ||
709 | return NULL; | ||
710 | } | ||
711 | api = GNUNET_new (struct GNUNET_PEERSTORE_PluginFunctions); | ||
712 | api->cls = &plugin; | ||
713 | api->store_record = &peerstore_sqlite_store_record; | ||
714 | api->iterate_records = &peerstore_sqlite_iterate_records; | ||
715 | api->expire_records = &peerstore_sqlite_expire_records; | ||
716 | LOG (GNUNET_ERROR_TYPE_DEBUG, | ||
717 | "Sqlite plugin is running\n"); | ||
718 | return api; | ||
719 | } | ||
720 | |||
721 | |||
722 | /** | ||
723 | * Exit point from the plugin. | ||
724 | * | ||
725 | * @param cls The plugin context (as returned by "init") | ||
726 | * @return Always NULL | ||
727 | */ | ||
728 | void * | ||
729 | libgnunet_plugin_peerstore_sqlite_done (void *cls) | ||
730 | { | ||
731 | struct GNUNET_PEERSTORE_PluginFunctions *api = cls; | ||
732 | struct Plugin *plugin = api->cls; | ||
733 | |||
734 | database_shutdown (plugin); | ||
735 | plugin->cfg = NULL; | ||
736 | GNUNET_free (api); | ||
737 | LOG (GNUNET_ERROR_TYPE_DEBUG, | ||
738 | "Sqlite plugin is finished\n"); | ||
739 | return NULL; | ||
740 | } | ||
741 | |||
742 | |||
743 | /* end of plugin_peerstore_sqlite.c */ | ||