GNUnet 0.21.1
plugin_namecache_sqlite.c
Go to the documentation of this file.
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
26#include "platform.h"
27#include "gnunet_sq_lib.h"
31#include <sqlite3.h>
32
43#define BUSY_TIMEOUT_MS 1000
44
45
51#define LOG_SQLITE(db, level, cmd) do { GNUNET_log_from (level, \
52 "namecache-sqlite", _ ( \
53 "`%s' failed at %s:%d with error: %s\n"), \
54 cmd, \
55 __FILE__, __LINE__, \
56 sqlite3_errmsg ( \
57 db->dbh)); \
58} while (0)
59
60#define LOG(kind, ...) GNUNET_log_from (kind, "namecache-sqlite", __VA_ARGS__)
61
62
66struct Plugin
67{
68 const struct GNUNET_CONFIGURATION_Handle *cfg;
69
73 char *fn;
74
78 sqlite3 *dbh;
79
83 sqlite3_stmt *cache_block;
84
88 sqlite3_stmt *delete_block;
89
93 sqlite3_stmt *lookup_block;
94
98 sqlite3_stmt *expire_blocks;
99};
100
101
110static int
112{
113 struct GNUNET_SQ_ExecuteStatement es[] = {
114 GNUNET_SQ_make_try_execute ("PRAGMA temp_store=MEMORY"),
115 GNUNET_SQ_make_try_execute ("PRAGMA synchronous=NORMAL"),
116 GNUNET_SQ_make_try_execute ("PRAGMA legacy_file_format=OFF"),
117 GNUNET_SQ_make_try_execute ("PRAGMA auto_vacuum=INCREMENTAL"),
118 GNUNET_SQ_make_try_execute ("PRAGMA encoding=\"UTF-8\""),
119 GNUNET_SQ_make_try_execute ("PRAGMA locking_mode=EXCLUSIVE"),
120 GNUNET_SQ_make_try_execute ("PRAGMA page_size=4092"),
121 GNUNET_SQ_make_try_execute ("PRAGMA journal_mode=WAL"),
122 GNUNET_SQ_make_execute ("CREATE TABLE IF NOT EXISTS ns096blocks ("
123 " query BLOB NOT NULL,"
124 " block BLOB NOT NULL,"
125 " expiration_time INT8 NOT NULL"
126 ")"),
127 GNUNET_SQ_make_execute ("CREATE INDEX IF NOT EXISTS ir_query_hash "
128 "ON ns096blocks (query,expiration_time)"),
129 GNUNET_SQ_make_execute ("CREATE INDEX IF NOT EXISTS ir_block_expiration "
130 "ON ns096blocks (expiration_time)"),
132 };
133 struct GNUNET_SQ_PrepareStatement ps[] = {
135 "INSERT INTO ns096blocks (query,block,expiration_time) VALUES (?, ?, ?)",
136 &plugin->cache_block),
137 GNUNET_SQ_make_prepare ("DELETE FROM ns096blocks WHERE expiration_time<?",
138 &plugin->expire_blocks),
140 "DELETE FROM ns096blocks WHERE query=? AND expiration_time<=?",
141 &plugin->delete_block),
142 GNUNET_SQ_make_prepare ("SELECT block FROM ns096blocks WHERE query=? "
143 "ORDER BY expiration_time DESC LIMIT 1",
144 &plugin->lookup_block),
146 };
147 char *afsdir;
148
149 if (GNUNET_OK !=
151 "namecache-sqlite",
152 "FILENAME",
153 &afsdir))
154 {
156 "namecache-sqlite",
157 "FILENAME");
158 return GNUNET_SYSERR;
159 }
160 if (GNUNET_OK !=
161 GNUNET_DISK_file_test (afsdir))
162 {
163 if (GNUNET_OK !=
165 {
166 GNUNET_break (0);
167 GNUNET_free (afsdir);
168 return GNUNET_SYSERR;
169 }
170 }
171 /* afsdir should be UTF-8-encoded. If it isn't, it's a bug */
172 plugin->fn = afsdir;
173
174 /* Open database and precompile statements */
175 if (SQLITE_OK !=
176 sqlite3_open (plugin->fn, &plugin->dbh))
177 {
179 _ ("Unable to initialize SQLite: %s.\n"),
180 sqlite3_errmsg (plugin->dbh));
181 return GNUNET_SYSERR;
182 }
183 if (GNUNET_OK !=
185 es))
186 {
187 GNUNET_break (0);
189 _ ("Failed to setup database at `%s'\n"),
190 plugin->fn);
191 return GNUNET_SYSERR;
192 }
193 GNUNET_break (SQLITE_OK ==
194 sqlite3_busy_timeout (plugin->dbh,
196
197 if (GNUNET_OK !=
199 ps))
200 {
201 GNUNET_break (0);
203 _ ("Failed to setup database at `%s'\n"),
204 plugin->fn);
205 return GNUNET_SYSERR;
206 }
207
208 return GNUNET_OK;
209}
210
211
217static void
219{
220 int result;
221 sqlite3_stmt *stmt;
222
223 if (NULL != plugin->cache_block)
224 sqlite3_finalize (plugin->cache_block);
225 if (NULL != plugin->lookup_block)
226 sqlite3_finalize (plugin->lookup_block);
227 if (NULL != plugin->expire_blocks)
228 sqlite3_finalize (plugin->expire_blocks);
229 if (NULL != plugin->delete_block)
230 sqlite3_finalize (plugin->delete_block);
231 result = sqlite3_close (plugin->dbh);
232 if (result == SQLITE_BUSY)
233 {
235 _ (
236 "Tried to close sqlite without finalizing all prepared statements.\n"));
237 stmt = sqlite3_next_stmt (plugin->dbh,
238 NULL);
239 while (stmt != NULL)
240 {
242 "sqlite",
243 "Closing statement %p\n",
244 stmt);
245 result = sqlite3_finalize (stmt);
246 if (result != SQLITE_OK)
248 "sqlite",
249 "Failed to close statement %p: %d\n",
250 stmt,
251 result);
252 stmt = sqlite3_next_stmt (plugin->dbh,
253 NULL);
254 }
255 result = sqlite3_close (plugin->dbh);
256 }
257 if (SQLITE_OK != result)
260 "sqlite3_close");
261
262 GNUNET_free (plugin->fn);
263}
264
265
271static void
273{
274 struct GNUNET_TIME_Absolute now;
276 struct GNUNET_SQ_QueryParam params[] = {
279 };
280 int n;
281
282 if (GNUNET_OK !=
283 GNUNET_SQ_bind (plugin->expire_blocks,
284 params))
285 {
288 "sqlite3_bind_XXXX");
290 plugin->expire_blocks);
291 return;
292 }
293 n = sqlite3_step (plugin->expire_blocks);
295 plugin->expire_blocks);
296 switch (n)
297 {
298 case SQLITE_DONE:
300 "sqlite",
301 "Records expired\n");
302 return;
303
304 case SQLITE_BUSY:
307 "sqlite3_step");
308 return;
309
310 default:
313 "sqlite3_step");
314 return;
315 }
316}
317
318
326static int
328 const struct GNUNET_GNSRECORD_Block *block)
329{
330 static struct GNUNET_TIME_Absolute last_expire;
331 struct Plugin *plugin = cls;
332 struct GNUNET_HashCode query;
334 size_t block_size = GNUNET_GNSRECORD_block_get_size (block);
335 struct GNUNET_SQ_QueryParam del_params[] = {
339 };
340 struct GNUNET_SQ_QueryParam ins_params[] = {
343 block_size),
346 };
347 int n;
348
349 /* run expiration of old cache entries once per hour */
350 if (GNUNET_TIME_absolute_get_duration (last_expire).rel_value_us >
351 GNUNET_TIME_UNIT_HOURS.rel_value_us)
352 {
353 last_expire = GNUNET_TIME_absolute_get ();
355 }
357 GNUNET_GNSRECORD_query_from_block (block, &query));
360 "Caching new version of block %s (expires %s)\n",
361 GNUNET_h2s (&query),
363 if (block_size > 64 * 65536)
364 {
365 GNUNET_break (0);
366 return GNUNET_SYSERR;
367 }
368
369 /* delete old version of the block */
370 if (GNUNET_OK !=
371 GNUNET_SQ_bind (plugin->delete_block,
372 del_params))
373 {
375 "sqlite3_bind_XXXX");
377 plugin->delete_block);
378 return GNUNET_SYSERR;
379 }
380 n = sqlite3_step (plugin->delete_block);
381 switch (n)
382 {
383 case SQLITE_DONE:
385 "sqlite",
386 "Old block deleted\n");
387 break;
388
389 case SQLITE_BUSY:
392 "sqlite3_step");
393 break;
394
395 default:
398 "sqlite3_step");
399 break;
400 }
402 plugin->delete_block);
403
404 /* insert new version of the block */
405 if (GNUNET_OK !=
406 GNUNET_SQ_bind (plugin->cache_block,
407 ins_params))
408 {
411 "sqlite3_bind_XXXX");
413 plugin->cache_block);
414 return GNUNET_SYSERR;
415 }
417 "Caching block under derived key `%s'\n",
418 GNUNET_h2s_full (&query));
419 n = sqlite3_step (plugin->cache_block);
421 plugin->cache_block);
422 switch (n)
423 {
424 case SQLITE_DONE:
426 "Record stored\n");
427 return GNUNET_OK;
428
429 case SQLITE_BUSY:
432 "sqlite3_step");
433 return GNUNET_NO;
434
435 default:
438 "sqlite3_step");
439 return GNUNET_SYSERR;
440 }
441}
442
443
454static int
456 const struct GNUNET_HashCode *query,
458 void *iter_cls)
459{
460 struct Plugin *plugin = cls;
461 int ret;
462 int sret;
463 size_t block_size;
464 const struct GNUNET_GNSRECORD_Block *block;
465 struct GNUNET_SQ_QueryParam params[] = {
468 };
469 struct GNUNET_SQ_ResultSpec rs[] = {
470 GNUNET_SQ_result_spec_variable_size ((void **) &block,
471 &block_size),
473 };
474
475 if (GNUNET_OK !=
476 GNUNET_SQ_bind (plugin->lookup_block,
477 params))
478 {
481 "sqlite3_bind_XXXX");
483 plugin->lookup_block);
484 return GNUNET_SYSERR;
485 }
486 ret = GNUNET_NO;
487 if (SQLITE_ROW ==
488 (sret = sqlite3_step (plugin->lookup_block)))
489 {
490 if (GNUNET_OK !=
491 GNUNET_SQ_extract_result (plugin->lookup_block,
492 rs))
493 {
494 GNUNET_break (0);
496 }
497 else if ((block_size < sizeof(struct GNUNET_GNSRECORD_Block)))
498 {
499 GNUNET_break (0);
502 }
503 else
504 {
506 "Found block under derived key `%s'\n",
507 GNUNET_h2s_full (query));
508 iter (iter_cls,
509 block);
511 ret = GNUNET_YES;
512 }
513 }
514 else
515 {
516 if (SQLITE_DONE != sret)
517 {
520 "sqlite_step");
522 }
523 else
524 {
526 "No block found under derived key `%s'\n",
527 GNUNET_h2s_full (query));
528 }
529 }
531 plugin->lookup_block);
532 return ret;
533}
534
535
542void *
544{
545 static struct Plugin plugin;
546 const struct GNUNET_CONFIGURATION_Handle *cfg = cls;
548
549 if (NULL != plugin.cfg)
550 return NULL; /* can only initialize once! */
551 memset (&plugin, 0, sizeof(struct Plugin));
552 plugin.cfg = cfg;
554 {
556 return NULL;
557 }
559 api->cls = &plugin;
563 _ ("Sqlite database running\n"));
564 return api;
565}
566
567
574void *
576{
578 struct Plugin *plugin = api->cls;
579
581 plugin->cfg = NULL;
584 "sqlite plugin is finished\n");
585 return NULL;
586}
587
588
589/* end of plugin_namecache_sqlite.c */
static int ret
Final status code.
Definition: gnunet-arm.c:94
static struct GNUNET_CONFIGURATION_Handle * cfg
Our configuration.
Definition: gnunet-arm.c:109
struct TestcasePlugin * plugin
The process handle to the testbed service.
static struct GNUNET_PEERSTORE_Handle * ps
Handle to the PEERSTORE service.
static struct GNUNET_TIME_Relative expiration
User supplied expiration value.
static int result
Global testing status.
API that can be used to manipulate GNS record data.
Plugin API for the namecache database backend.
API that can be used to store naming information on a GNUnet node.
helper functions for Sqlite3 DB interactions
struct GNUNET_SQ_PrepareStatement GNUNET_SQ_make_prepare(const char *sql, sqlite3_stmt **pstmt)
Create a struct GNUNET_SQ_PrepareStatement
Definition: sq_prepare.c:37
struct GNUNET_SQ_ExecuteStatement GNUNET_SQ_make_execute(const char *sql)
Create a struct GNUNET_SQ_ExecuteStatement where errors are fatal.
Definition: sq_exec.c:36
struct GNUNET_SQ_ExecuteStatement GNUNET_SQ_make_try_execute(const char *sql)
Create a struct GNUNET_SQ_ExecuteStatement where errors should be tolerated.
Definition: sq_exec.c:55
struct GNUNET_SQ_QueryParam GNUNET_SQ_query_param_fixed_size(const void *ptr, size_t ptr_size)
Generate query parameter for a buffer ptr of ptr_size bytes.
#define GNUNET_SQ_PREPARE_END
Terminator for executable statement list.
enum GNUNET_GenericReturnValue GNUNET_SQ_extract_result(sqlite3_stmt *result, struct GNUNET_SQ_ResultSpec *rs)
Extract results from a query result according to the given specification.
Definition: sq.c:75
void GNUNET_SQ_cleanup_result(struct GNUNET_SQ_ResultSpec *rs)
Free all memory that was allocated in rs during GNUNET_SQ_extract_result().
Definition: sq.c:104
enum GNUNET_GenericReturnValue GNUNET_SQ_exec_statements(sqlite3 *dbh, const struct GNUNET_SQ_ExecuteStatement *es)
Request execution of an array of statements es from Postgres.
Definition: sq_exec.c:76
enum GNUNET_GenericReturnValue GNUNET_SQ_prepare(sqlite3 *dbh, const struct GNUNET_SQ_PrepareStatement *ps)
Prepare all statements given in the (NULL,NULL)-terminated array at ps.
Definition: sq_prepare.c:50
#define GNUNET_SQ_EXECUTE_STATEMENT_END
Terminator for executable statement list.
struct GNUNET_SQ_QueryParam GNUNET_SQ_query_param_absolute_time(const struct GNUNET_TIME_Absolute *x)
Generate query parameter for an absolute time value.
#define GNUNET_SQ_result_spec_end
End of result parameter specification.
struct GNUNET_SQ_ResultSpec GNUNET_SQ_result_spec_variable_size(void **dst, size_t *sptr)
Variable-size result expected.
#define GNUNET_SQ_query_param_auto_from_type(x)
Generate fixed-size query parameter with size determined by variable type.
enum GNUNET_GenericReturnValue GNUNET_SQ_bind(sqlite3_stmt *stmt, const struct GNUNET_SQ_QueryParam *params)
Execute binding operations for a prepared statement.
Definition: sq.c:30
void GNUNET_SQ_reset(sqlite3 *dbh, sqlite3_stmt *stmt)
Reset stmt and log error.
Definition: sq.c:119
#define GNUNET_SQ_query_param_end
End of query parameter specification.
Definition: gnunet_sq_lib.h:87
enum GNUNET_GenericReturnValue GNUNET_CONFIGURATION_get_value_filename(const struct GNUNET_CONFIGURATION_Handle *cfg, const char *section, const char *option, char **value)
Get a configuration value that should be the name of a file or directory.
enum GNUNET_GenericReturnValue GNUNET_DISK_file_test(const char *fil)
Check that fil corresponds to a filename (of a file that exists and that is not a directory).
Definition: disk.c:482
enum GNUNET_GenericReturnValue GNUNET_DISK_directory_create_for_file(const char *filename)
Create the directory structure for storing a file.
Definition: disk.c:582
size_t GNUNET_GNSRECORD_block_get_size(const struct GNUNET_GNSRECORD_Block *block)
Returns the length of this block in bytes.
enum GNUNET_GenericReturnValue GNUNET_GNSRECORD_query_from_block(const struct GNUNET_GNSRECORD_Block *block, struct GNUNET_HashCode *query)
Builds the query hash from a block.
struct GNUNET_TIME_Absolute GNUNET_GNSRECORD_block_get_expiration(const struct GNUNET_GNSRECORD_Block *block)
Returns the expiration of a block.
#define GNUNET_log(kind,...)
#define GNUNET_log_from(kind, comp,...)
@ GNUNET_OK
@ GNUNET_YES
@ GNUNET_NO
@ GNUNET_SYSERR
const char * GNUNET_h2s_full(const struct GNUNET_HashCode *hc)
Convert a hash value to a string (for printing debug messages).
#define GNUNET_assert(cond)
Use this for fatal errors that cannot be handled.
#define GNUNET_break(cond)
Use this for internal assertion violations that are not fatal (can be handled) but should not occur.
const char * GNUNET_h2s(const struct GNUNET_HashCode *hc)
Convert a hash value to a string (for printing debug messages).
void GNUNET_log_config_missing(enum GNUNET_ErrorType kind, const char *section, const char *option)
Log error message about missing configuration option.
@ GNUNET_ERROR_TYPE_WARNING
@ GNUNET_ERROR_TYPE_ERROR
@ GNUNET_ERROR_TYPE_BULK
@ GNUNET_ERROR_TYPE_DEBUG
@ GNUNET_ERROR_TYPE_INFO
#define GNUNET_new(type)
Allocate a struct or union of the given type.
#define GNUNET_free(ptr)
Wrapper around free.
void(* GNUNET_NAMECACHE_BlockCallback)(void *cls, const struct GNUNET_GNSRECORD_Block *block)
Function called for matching blocks.
struct GNUNET_TIME_Relative GNUNET_TIME_absolute_get_duration(struct GNUNET_TIME_Absolute whence)
Get the duration of an operation as the difference of the current time and the given start time "henc...
Definition: time.c:436
#define GNUNET_TIME_UNIT_HOURS
One hour.
struct GNUNET_TIME_Absolute GNUNET_TIME_absolute_get(void)
Get the current time.
Definition: time.c:111
const char * GNUNET_STRINGS_absolute_time_to_string(struct GNUNET_TIME_Absolute t)
Like asctime, except for GNUnet time.
Definition: strings.c:617
#define _(String)
GNU gettext support macro.
Definition: platform.h:178
#define BUSY_TIMEOUT_MS
After how many ms "busy" should a DB operation fail for good? A low value makes sure that we are more...
static void namecache_sqlite_expire_blocks(struct Plugin *plugin)
Removes any expired block.
#define LOG_SQLITE(db, level, cmd)
Log an error message at log-level 'level' that indicates a failure of the command 'cmd' on file 'file...
void * libgnunet_plugin_namecache_sqlite_init(void *cls)
Entry point for the plugin.
void * libgnunet_plugin_namecache_sqlite_done(void *cls)
Exit point from the plugin.
static void database_shutdown(struct Plugin *plugin)
Shutdown database connection and associate data structures.
static int namecache_sqlite_cache_block(void *cls, const struct GNUNET_GNSRECORD_Block *block)
Cache a block in the datastore.
static int database_setup(struct Plugin *plugin)
Initialize the database connections and associated data structures (create tables and indices as need...
#define LOG(kind,...)
static int namecache_sqlite_lookup_block(void *cls, const struct GNUNET_HashCode *query, GNUNET_NAMECACHE_BlockCallback iter, void *iter_cls)
Get the block for a particular zone and label in the datastore.
void * cls
Closure for all of the callbacks.
A 512-bit hashcode.
struct returned by the initialization function of the plugin
int(* lookup_block)(void *cls, const struct GNUNET_HashCode *query, GNUNET_NAMECACHE_BlockCallback iter, void *iter_cls)
Get the block for a particular zone and label in the datastore.
int(* cache_block)(void *cls, const struct GNUNET_GNSRECORD_Block *block)
Cache a block in the datastore.
void * cls
Closure to pass to all plugin functions.
Information needed to run a list of SQL statements using GNUNET_SQ_exec_statements().
Information needed to run a list of SQL statements using GNUNET_SQ_exec_statements().
Description of a DB query parameter.
Definition: gnunet_sq_lib.h:56
Description of a DB result cell.
void * cls
Closure for conv and cleaner.
Time for absolute times used by GNUnet, in microseconds.
Handle for a plugin.
Definition: block.c:38
struct GNUNET_BLOCK_PluginFunctions * api
Plugin API.
Definition: block.c:47
sqlite3_stmt * cache_block
Precompiled SQL for caching a block.
sqlite3_stmt * lookup_block
Precompiled SQL for looking up a block.
sqlite3_stmt * delete_block
Precompiled SQL for deleting an older block.
char * fn
Filename used for the DB.
sqlite3_stmt * expire_blocks
Precompiled SQL for removing expired blocks.
struct GNUNET_PQ_Context * dbh
Native Postgres database handle.
const struct GNUNET_CONFIGURATION_Handle * cfg
Our configuration.