aboutsummaryrefslogtreecommitdiff
path: root/src/namecache/plugin_namecache_sqlite.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/namecache/plugin_namecache_sqlite.c')
-rw-r--r--src/namecache/plugin_namecache_sqlite.c590
1 files changed, 0 insertions, 590 deletions
diff --git a/src/namecache/plugin_namecache_sqlite.c b/src/namecache/plugin_namecache_sqlite.c
deleted file mode 100644
index 82008c837..000000000
--- a/src/namecache/plugin_namecache_sqlite.c
+++ /dev/null
@@ -1,590 +0,0 @@
1/*
2 * This file is part of GNUnet
3 * Copyright (C) 2009-2013 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 namecache/plugin_namecache_sqlite.c
23 * @brief sqlite-based namecache backend
24 * @author Christian Grothoff
25 */
26#include "platform.h"
27#include "gnunet_sq_lib.h"
28#include "gnunet_namecache_plugin.h"
29#include "gnunet_namecache_service.h"
30#include "gnunet_gnsrecord_lib.h"
31#include "namecache.h"
32#include <sqlite3.h>
33
34/**
35 * After how many ms "busy" should a DB operation fail for good? A
36 * low value makes sure that we are more responsive to requests
37 * (especially PUTs). A high value guarantees a higher success rate
38 * (SELECTs in iterate can take several seconds despite LIMIT=1).
39 *
40 * The default value of 1s should ensure that users do not experience
41 * huge latencies while at the same time allowing operations to
42 * succeed with reasonable probability.
43 */
44#define BUSY_TIMEOUT_MS 1000
45
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 "namecache-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, "namecache-sqlite", __VA_ARGS__)
62
63
64/**
65 * Context for all functions in this plugin.
66 */
67struct Plugin
68{
69 const struct GNUNET_CONFIGURATION_Handle *cfg;
70
71 /**
72 * Database filename.
73 */
74 char *fn;
75
76 /**
77 * Native SQLite database handle.
78 */
79 sqlite3 *dbh;
80
81 /**
82 * Precompiled SQL for caching a block
83 */
84 sqlite3_stmt *cache_block;
85
86 /**
87 * Precompiled SQL for deleting an older block
88 */
89 sqlite3_stmt *delete_block;
90
91 /**
92 * Precompiled SQL for looking up a block
93 */
94 sqlite3_stmt *lookup_block;
95
96 /**
97 * Precompiled SQL for removing expired blocks
98 */
99 sqlite3_stmt *expire_blocks;
100};
101
102
103/**
104 * Initialize the database connections and associated
105 * data structures (create tables and indices
106 * as needed as well).
107 *
108 * @param plugin the plugin context (state for this module)
109 * @return #GNUNET_OK on success
110 */
111static int
112database_setup (struct Plugin *plugin)
113{
114 struct GNUNET_SQ_ExecuteStatement es[] = {
115 GNUNET_SQ_make_try_execute ("PRAGMA temp_store=MEMORY"),
116 GNUNET_SQ_make_try_execute ("PRAGMA synchronous=NORMAL"),
117 GNUNET_SQ_make_try_execute ("PRAGMA legacy_file_format=OFF"),
118 GNUNET_SQ_make_try_execute ("PRAGMA auto_vacuum=INCREMENTAL"),
119 GNUNET_SQ_make_try_execute ("PRAGMA encoding=\"UTF-8\""),
120 GNUNET_SQ_make_try_execute ("PRAGMA locking_mode=EXCLUSIVE"),
121 GNUNET_SQ_make_try_execute ("PRAGMA page_size=4092"),
122 GNUNET_SQ_make_try_execute ("PRAGMA journal_mode=WAL"),
123 GNUNET_SQ_make_execute ("CREATE TABLE IF NOT EXISTS ns096blocks ("
124 " query BLOB NOT NULL,"
125 " block BLOB NOT NULL,"
126 " expiration_time INT8 NOT NULL"
127 ")"),
128 GNUNET_SQ_make_execute ("CREATE INDEX IF NOT EXISTS ir_query_hash "
129 "ON ns096blocks (query,expiration_time)"),
130 GNUNET_SQ_make_execute ("CREATE INDEX IF NOT EXISTS ir_block_expiration "
131 "ON ns096blocks (expiration_time)"),
132 GNUNET_SQ_EXECUTE_STATEMENT_END
133 };
134 struct GNUNET_SQ_PrepareStatement ps[] = {
135 GNUNET_SQ_make_prepare (
136 "INSERT INTO ns096blocks (query,block,expiration_time) VALUES (?, ?, ?)",
137 &plugin->cache_block),
138 GNUNET_SQ_make_prepare ("DELETE FROM ns096blocks WHERE expiration_time<?",
139 &plugin->expire_blocks),
140 GNUNET_SQ_make_prepare (
141 "DELETE FROM ns096blocks WHERE query=? AND expiration_time<=?",
142 &plugin->delete_block),
143 GNUNET_SQ_make_prepare ("SELECT block FROM ns096blocks WHERE query=? "
144 "ORDER BY expiration_time DESC LIMIT 1",
145 &plugin->lookup_block),
146 GNUNET_SQ_PREPARE_END
147 };
148 char *afsdir;
149
150 if (GNUNET_OK !=
151 GNUNET_CONFIGURATION_get_value_filename (plugin->cfg,
152 "namecache-sqlite",
153 "FILENAME",
154 &afsdir))
155 {
156 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
157 "namecache-sqlite",
158 "FILENAME");
159 return GNUNET_SYSERR;
160 }
161 if (GNUNET_OK !=
162 GNUNET_DISK_file_test (afsdir))
163 {
164 if (GNUNET_OK !=
165 GNUNET_DISK_directory_create_for_file (afsdir))
166 {
167 GNUNET_break (0);
168 GNUNET_free (afsdir);
169 return GNUNET_SYSERR;
170 }
171 }
172 /* afsdir should be UTF-8-encoded. If it isn't, it's a bug */
173 plugin->fn = afsdir;
174
175 /* Open database and precompile statements */
176 if (SQLITE_OK !=
177 sqlite3_open (plugin->fn, &plugin->dbh))
178 {
179 LOG (GNUNET_ERROR_TYPE_ERROR,
180 _ ("Unable to initialize SQLite: %s.\n"),
181 sqlite3_errmsg (plugin->dbh));
182 return GNUNET_SYSERR;
183 }
184 if (GNUNET_OK !=
185 GNUNET_SQ_exec_statements (plugin->dbh,
186 es))
187 {
188 GNUNET_break (0);
189 LOG (GNUNET_ERROR_TYPE_ERROR,
190 _ ("Failed to setup database at `%s'\n"),
191 plugin->fn);
192 return GNUNET_SYSERR;
193 }
194 GNUNET_break (SQLITE_OK ==
195 sqlite3_busy_timeout (plugin->dbh,
196 BUSY_TIMEOUT_MS));
197
198 if (GNUNET_OK !=
199 GNUNET_SQ_prepare (plugin->dbh,
200 ps))
201 {
202 GNUNET_break (0);
203 LOG (GNUNET_ERROR_TYPE_ERROR,
204 _ ("Failed to setup database at `%s'\n"),
205 plugin->fn);
206 return GNUNET_SYSERR;
207 }
208
209 return GNUNET_OK;
210}
211
212
213/**
214 * Shutdown database connection and associate data
215 * structures.
216 * @param plugin the plugin context (state for this module)
217 */
218static void
219database_shutdown (struct Plugin *plugin)
220{
221 int result;
222 sqlite3_stmt *stmt;
223
224 if (NULL != plugin->cache_block)
225 sqlite3_finalize (plugin->cache_block);
226 if (NULL != plugin->lookup_block)
227 sqlite3_finalize (plugin->lookup_block);
228 if (NULL != plugin->expire_blocks)
229 sqlite3_finalize (plugin->expire_blocks);
230 if (NULL != plugin->delete_block)
231 sqlite3_finalize (plugin->delete_block);
232 result = sqlite3_close (plugin->dbh);
233 if (result == SQLITE_BUSY)
234 {
235 LOG (GNUNET_ERROR_TYPE_WARNING,
236 _ (
237 "Tried to close sqlite without finalizing all prepared statements.\n"));
238 stmt = sqlite3_next_stmt (plugin->dbh,
239 NULL);
240 while (stmt != NULL)
241 {
242 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
243 "sqlite",
244 "Closing statement %p\n",
245 stmt);
246 result = sqlite3_finalize (stmt);
247 if (result != SQLITE_OK)
248 GNUNET_log_from (GNUNET_ERROR_TYPE_WARNING,
249 "sqlite",
250 "Failed to close statement %p: %d\n",
251 stmt,
252 result);
253 stmt = sqlite3_next_stmt (plugin->dbh,
254 NULL);
255 }
256 result = sqlite3_close (plugin->dbh);
257 }
258 if (SQLITE_OK != result)
259 LOG_SQLITE (plugin,
260 GNUNET_ERROR_TYPE_ERROR,
261 "sqlite3_close");
262
263 GNUNET_free (plugin->fn);
264}
265
266
267/**
268 * Removes any expired block.
269 *
270 * @param plugin the plugin
271 */
272static void
273namecache_sqlite_expire_blocks (struct Plugin *plugin)
274{
275 struct GNUNET_TIME_Absolute now;
276 struct GNUNET_SQ_QueryParam params[] = {
277 GNUNET_SQ_query_param_absolute_time (&now),
278 GNUNET_SQ_query_param_end
279 };
280 int n;
281
282 now = GNUNET_TIME_absolute_get ();
283 if (GNUNET_OK !=
284 GNUNET_SQ_bind (plugin->expire_blocks,
285 params))
286 {
287 LOG_SQLITE (plugin,
288 GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
289 "sqlite3_bind_XXXX");
290 GNUNET_SQ_reset (plugin->dbh,
291 plugin->expire_blocks);
292 return;
293 }
294 n = sqlite3_step (plugin->expire_blocks);
295 GNUNET_SQ_reset (plugin->dbh,
296 plugin->expire_blocks);
297 switch (n)
298 {
299 case SQLITE_DONE:
300 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
301 "sqlite",
302 "Records expired\n");
303 return;
304
305 case SQLITE_BUSY:
306 LOG_SQLITE (plugin,
307 GNUNET_ERROR_TYPE_WARNING | GNUNET_ERROR_TYPE_BULK,
308 "sqlite3_step");
309 return;
310
311 default:
312 LOG_SQLITE (plugin,
313 GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
314 "sqlite3_step");
315 return;
316 }
317}
318
319
320/**
321 * Cache a block in the datastore.
322 *
323 * @param cls closure (internal context for the plugin)
324 * @param block block to cache
325 * @return #GNUNET_OK on success, else #GNUNET_SYSERR
326 */
327static int
328namecache_sqlite_cache_block (void *cls,
329 const struct GNUNET_GNSRECORD_Block *block)
330{
331 static struct GNUNET_TIME_Absolute last_expire;
332 struct Plugin *plugin = cls;
333 struct GNUNET_HashCode query;
334 struct GNUNET_TIME_Absolute expiration;
335 size_t block_size = GNUNET_GNSRECORD_block_get_size (block);
336 struct GNUNET_SQ_QueryParam del_params[] = {
337 GNUNET_SQ_query_param_auto_from_type (&query),
338 GNUNET_SQ_query_param_absolute_time (&expiration),
339 GNUNET_SQ_query_param_end
340 };
341 struct GNUNET_SQ_QueryParam ins_params[] = {
342 GNUNET_SQ_query_param_auto_from_type (&query),
343 GNUNET_SQ_query_param_fixed_size (block,
344 block_size),
345 GNUNET_SQ_query_param_absolute_time (&expiration),
346 GNUNET_SQ_query_param_end
347 };
348 int n;
349
350 /* run expiration of old cache entries once per hour */
351 if (GNUNET_TIME_absolute_get_duration (last_expire).rel_value_us >
352 GNUNET_TIME_UNIT_HOURS.rel_value_us)
353 {
354 last_expire = GNUNET_TIME_absolute_get ();
355 namecache_sqlite_expire_blocks (plugin);
356 }
357 GNUNET_assert (GNUNET_OK ==
358 GNUNET_GNSRECORD_query_from_block (block, &query));
359 expiration = GNUNET_GNSRECORD_block_get_expiration (block);
360 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
361 "Caching new version of block %s (expires %s)\n",
362 GNUNET_h2s (&query),
363 GNUNET_STRINGS_absolute_time_to_string (expiration));
364 if (block_size > 64 * 65536)
365 {
366 GNUNET_break (0);
367 return GNUNET_SYSERR;
368 }
369
370 /* delete old version of the block */
371 if (GNUNET_OK !=
372 GNUNET_SQ_bind (plugin->delete_block,
373 del_params))
374 {
375 LOG_SQLITE (plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
376 "sqlite3_bind_XXXX");
377 GNUNET_SQ_reset (plugin->dbh,
378 plugin->delete_block);
379 return GNUNET_SYSERR;
380 }
381 n = sqlite3_step (plugin->delete_block);
382 switch (n)
383 {
384 case SQLITE_DONE:
385 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
386 "sqlite",
387 "Old block deleted\n");
388 break;
389
390 case SQLITE_BUSY:
391 LOG_SQLITE (plugin,
392 GNUNET_ERROR_TYPE_WARNING | GNUNET_ERROR_TYPE_BULK,
393 "sqlite3_step");
394 break;
395
396 default:
397 LOG_SQLITE (plugin,
398 GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
399 "sqlite3_step");
400 break;
401 }
402 GNUNET_SQ_reset (plugin->dbh,
403 plugin->delete_block);
404
405 /* insert new version of the block */
406 if (GNUNET_OK !=
407 GNUNET_SQ_bind (plugin->cache_block,
408 ins_params))
409 {
410 LOG_SQLITE (plugin,
411 GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
412 "sqlite3_bind_XXXX");
413 GNUNET_SQ_reset (plugin->dbh,
414 plugin->cache_block);
415 return GNUNET_SYSERR;
416 }
417 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
418 "Caching block under derived key `%s'\n",
419 GNUNET_h2s_full (&query));
420 n = sqlite3_step (plugin->cache_block);
421 GNUNET_SQ_reset (plugin->dbh,
422 plugin->cache_block);
423 switch (n)
424 {
425 case SQLITE_DONE:
426 LOG (GNUNET_ERROR_TYPE_DEBUG,
427 "Record stored\n");
428 return GNUNET_OK;
429
430 case SQLITE_BUSY:
431 LOG_SQLITE (plugin,
432 GNUNET_ERROR_TYPE_WARNING | GNUNET_ERROR_TYPE_BULK,
433 "sqlite3_step");
434 return GNUNET_NO;
435
436 default:
437 LOG_SQLITE (plugin,
438 GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
439 "sqlite3_step");
440 return GNUNET_SYSERR;
441 }
442}
443
444
445/**
446 * Get the block for a particular zone and label in the
447 * datastore. Will return at most one result to the iterator.
448 *
449 * @param cls closure (internal context for the plugin)
450 * @param query hash of public key derived from the zone and the label
451 * @param iter function to call with the result
452 * @param iter_cls closure for @a iter
453 * @return #GNUNET_OK on success, #GNUNET_NO if there were no results, #GNUNET_SYSERR on error
454 */
455static int
456namecache_sqlite_lookup_block (void *cls,
457 const struct GNUNET_HashCode *query,
458 GNUNET_NAMECACHE_BlockCallback iter,
459 void *iter_cls)
460{
461 struct Plugin *plugin = cls;
462 int ret;
463 int sret;
464 size_t block_size;
465 const struct GNUNET_GNSRECORD_Block *block;
466 struct GNUNET_SQ_QueryParam params[] = {
467 GNUNET_SQ_query_param_auto_from_type (query),
468 GNUNET_SQ_query_param_end
469 };
470 struct GNUNET_SQ_ResultSpec rs[] = {
471 GNUNET_SQ_result_spec_variable_size ((void **) &block,
472 &block_size),
473 GNUNET_SQ_result_spec_end
474 };
475
476 if (GNUNET_OK !=
477 GNUNET_SQ_bind (plugin->lookup_block,
478 params))
479 {
480 LOG_SQLITE (plugin,
481 GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
482 "sqlite3_bind_XXXX");
483 GNUNET_SQ_reset (plugin->dbh,
484 plugin->lookup_block);
485 return GNUNET_SYSERR;
486 }
487 ret = GNUNET_NO;
488 if (SQLITE_ROW ==
489 (sret = sqlite3_step (plugin->lookup_block)))
490 {
491 if (GNUNET_OK !=
492 GNUNET_SQ_extract_result (plugin->lookup_block,
493 rs))
494 {
495 GNUNET_break (0);
496 ret = GNUNET_SYSERR;
497 }
498 else if ((block_size < sizeof(struct GNUNET_GNSRECORD_Block)))
499 {
500 GNUNET_break (0);
501 GNUNET_SQ_cleanup_result (rs);
502 ret = GNUNET_SYSERR;
503 }
504 else
505 {
506 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
507 "Found block under derived key `%s'\n",
508 GNUNET_h2s_full (query));
509 iter (iter_cls,
510 block);
511 GNUNET_SQ_cleanup_result (rs);
512 ret = GNUNET_YES;
513 }
514 }
515 else
516 {
517 if (SQLITE_DONE != sret)
518 {
519 LOG_SQLITE (plugin,
520 GNUNET_ERROR_TYPE_ERROR,
521 "sqlite_step");
522 ret = GNUNET_SYSERR;
523 }
524 else
525 {
526 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
527 "No block found under derived key `%s'\n",
528 GNUNET_h2s_full (query));
529 }
530 }
531 GNUNET_SQ_reset (plugin->dbh,
532 plugin->lookup_block);
533 return ret;
534}
535
536
537/**
538 * Entry point for the plugin.
539 *
540 * @param cls the "struct GNUNET_NAMECACHE_PluginEnvironment*"
541 * @return NULL on error, otherwise the plugin context
542 */
543void *
544libgnunet_plugin_namecache_sqlite_init (void *cls)
545{
546 static struct Plugin plugin;
547 const struct GNUNET_CONFIGURATION_Handle *cfg = cls;
548 struct GNUNET_NAMECACHE_PluginFunctions *api;
549
550 if (NULL != plugin.cfg)
551 return NULL; /* can only initialize once! */
552 memset (&plugin, 0, sizeof(struct Plugin));
553 plugin.cfg = cfg;
554 if (GNUNET_OK != database_setup (&plugin))
555 {
556 database_shutdown (&plugin);
557 return NULL;
558 }
559 api = GNUNET_new (struct GNUNET_NAMECACHE_PluginFunctions);
560 api->cls = &plugin;
561 api->cache_block = &namecache_sqlite_cache_block;
562 api->lookup_block = &namecache_sqlite_lookup_block;
563 LOG (GNUNET_ERROR_TYPE_INFO,
564 _ ("Sqlite database running\n"));
565 return api;
566}
567
568
569/**
570 * Exit point from the plugin.
571 *
572 * @param cls the plugin context (as returned by "init")
573 * @return always NULL
574 */
575void *
576libgnunet_plugin_namecache_sqlite_done (void *cls)
577{
578 struct GNUNET_NAMECACHE_PluginFunctions *api = cls;
579 struct Plugin *plugin = api->cls;
580
581 database_shutdown (plugin);
582 plugin->cfg = NULL;
583 GNUNET_free (api);
584 LOG (GNUNET_ERROR_TYPE_DEBUG,
585 "sqlite plugin is finished\n");
586 return NULL;
587}
588
589
590/* end of plugin_namecache_sqlite.c */