GNUnet 0.28.1-dev.6-3-gbc381df64
 
Loading...
Searching...
No Matches
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"
30#include <sqlite3.h>
31
42#define BUSY_TIMEOUT_MS 1000
43
44
50#define LOG_SQLITE(db, level, cmd) do { GNUNET_log_from (level, \
51 "namecache-sqlite", _ ( \
52 "`%s' failed at %s:%d with error: %s\n"), \
53 cmd, \
54 __FILE__, __LINE__, \
55 sqlite3_errmsg ( \
56 db->dbh)); \
57} while (0)
58
59#define LOG(kind, ...) GNUNET_log_from (kind, "namecache-sqlite", __VA_ARGS__)
60
61
65struct Plugin
66{
67 const struct GNUNET_CONFIGURATION_Handle *cfg;
68
72 char *fn;
73
77 sqlite3 *dbh;
78
82 sqlite3_stmt *cache_block;
83
87 sqlite3_stmt *delete_block;
88
92 sqlite3_stmt *purge_block;
93
97 sqlite3_stmt *lookup_block;
98
102 sqlite3_stmt *expire_blocks;
103};
104
105
114static int
116{
117 struct GNUNET_SQ_ExecuteStatement es[] = {
118 GNUNET_SQ_make_try_execute ("PRAGMA temp_store=MEMORY"),
119 GNUNET_SQ_make_try_execute ("PRAGMA synchronous=NORMAL"),
120 GNUNET_SQ_make_try_execute ("PRAGMA legacy_file_format=OFF"),
121 GNUNET_SQ_make_try_execute ("PRAGMA auto_vacuum=INCREMENTAL"),
122 GNUNET_SQ_make_try_execute ("PRAGMA encoding=\"UTF-8\""),
123 GNUNET_SQ_make_try_execute ("PRAGMA locking_mode=EXCLUSIVE"),
124 GNUNET_SQ_make_try_execute ("PRAGMA page_size=4092"),
125 GNUNET_SQ_make_try_execute ("PRAGMA journal_mode=WAL"),
126 GNUNET_SQ_make_execute ("CREATE TABLE IF NOT EXISTS ns096blocks ("
127 " query BLOB NOT NULL,"
128 " block BLOB NOT NULL,"
129 " expiration_time INT8 NOT NULL"
130 ")"),
131 GNUNET_SQ_make_execute ("CREATE INDEX IF NOT EXISTS ir_query_hash "
132 "ON ns096blocks (query,expiration_time)"),
133 GNUNET_SQ_make_execute ("CREATE INDEX IF NOT EXISTS ir_block_expiration "
134 "ON ns096blocks (expiration_time)"),
136 };
137 struct GNUNET_SQ_PrepareStatement ps[] = {
139 "INSERT INTO ns096blocks (query,block,expiration_time) VALUES (?, ?, ?)",
140 &plugin->cache_block),
141 GNUNET_SQ_make_prepare ("DELETE FROM ns096blocks WHERE expiration_time<?",
142 &plugin->expire_blocks),
144 "DELETE FROM ns096blocks WHERE query=? AND expiration_time<=?",
145 &plugin->delete_block),
147 "DELETE FROM ns096blocks WHERE query=?",
148 &plugin->purge_block),
149 GNUNET_SQ_make_prepare ("SELECT block FROM ns096blocks WHERE query=? "
150 "ORDER BY expiration_time DESC LIMIT 1",
151 &plugin->lookup_block),
153 };
154 char *afsdir;
155
156 if (GNUNET_OK !=
158 "namecache-sqlite",
159 "FILENAME",
160 &afsdir))
161 {
163 "namecache-sqlite",
164 "FILENAME");
165 return GNUNET_SYSERR;
166 }
167 if (GNUNET_OK !=
168 GNUNET_DISK_file_test (afsdir))
169 {
170 if (GNUNET_OK !=
172 {
173 GNUNET_break (0);
174 GNUNET_free (afsdir);
175 return GNUNET_SYSERR;
176 }
177 }
178 /* afsdir should be UTF-8-encoded. If it isn't, it's a bug */
179 plugin->fn = afsdir;
180
181 /* Open database and precompile statements */
182 if (SQLITE_OK !=
183 sqlite3_open (plugin->fn, &plugin->dbh))
184 {
186 _ ("Unable to initialize SQLite: %s.\n"),
187 sqlite3_errmsg (plugin->dbh));
188 return GNUNET_SYSERR;
189 }
190 if (GNUNET_OK !=
192 es))
193 {
194 GNUNET_break (0);
196 _ ("Failed to setup database at `%s'\n"),
197 plugin->fn);
198 return GNUNET_SYSERR;
199 }
200 GNUNET_break (SQLITE_OK ==
201 sqlite3_busy_timeout (plugin->dbh,
203
204 if (GNUNET_OK !=
206 ps))
207 {
208 GNUNET_break (0);
210 _ ("Failed to setup database at `%s'\n"),
211 plugin->fn);
212 return GNUNET_SYSERR;
213 }
214
215 return GNUNET_OK;
216}
217
218
224static void
226{
227 int result;
228 sqlite3_stmt *stmt;
229
230 if (NULL != plugin->cache_block)
231 sqlite3_finalize (plugin->cache_block);
232 if (NULL != plugin->lookup_block)
233 sqlite3_finalize (plugin->lookup_block);
234 if (NULL != plugin->expire_blocks)
235 sqlite3_finalize (plugin->expire_blocks);
236 if (NULL != plugin->delete_block)
237 sqlite3_finalize (plugin->delete_block);
238 if (NULL != plugin->purge_block)
239 sqlite3_finalize (plugin->purge_block);
240 result = sqlite3_close (plugin->dbh);
241 if (result == SQLITE_BUSY)
242 {
244 _ (
245 "Tried to close sqlite without finalizing all prepared statements.\n"));
246 stmt = sqlite3_next_stmt (plugin->dbh,
247 NULL);
248 while (stmt != NULL)
249 {
251 "sqlite",
252 "Closing statement %p\n",
253 stmt);
254 result = sqlite3_finalize (stmt);
255 if (result != SQLITE_OK)
257 "sqlite",
258 "Failed to close statement %p: %d\n",
259 stmt,
260 result);
261 stmt = sqlite3_next_stmt (plugin->dbh,
262 NULL);
263 }
264 result = sqlite3_close (plugin->dbh);
265 }
266 if (SQLITE_OK != result)
269 "sqlite3_close");
270
271 GNUNET_free (plugin->fn);
272}
273
274
280static void
282{
284 struct GNUNET_SQ_QueryParam params[] = {
287 };
288 int n;
289
290 if (GNUNET_OK !=
291 GNUNET_SQ_bind (plugin->expire_blocks,
292 params))
293 {
296 "sqlite3_bind_XXXX");
298 plugin->expire_blocks);
299 return;
300 }
301 n = sqlite3_step (plugin->expire_blocks);
303 plugin->expire_blocks);
304 switch (n)
305 {
306 case SQLITE_DONE:
308 "sqlite",
309 "Records expired\n");
310 return;
311
312 case SQLITE_BUSY:
315 "sqlite3_step");
316 return;
317
318 default:
321 "sqlite3_step");
322 return;
323 }
324}
325
326
334static int
336 const struct GNUNET_GNSRECORD_Block *block)
337{
338 static struct GNUNET_TIME_Absolute last_expire;
339 struct Plugin *plugin = cls;
340 struct GNUNET_HashCode query;
342 size_t block_size = GNUNET_GNSRECORD_block_get_size (block);
343 int n;
344
345 /* run expiration of old cache entries once per hour */
346 if (GNUNET_TIME_absolute_get_duration (last_expire).rel_value_us >
348 {
349 last_expire = GNUNET_TIME_absolute_get ();
351 }
353 GNUNET_GNSRECORD_query_from_block (block, &query));
356 "Caching new version of block %s (expires %s)\n",
357 GNUNET_h2s (&query),
359 if (block_size > 64 * 65536)
360 {
361 GNUNET_break (0);
362 return GNUNET_SYSERR;
363 }
364
365 {
366 struct GNUNET_SQ_QueryParam del_params[] = {
370 };
371 /* delete old version of the block */
372 if (GNUNET_OK !=
373 GNUNET_SQ_bind (plugin->delete_block,
374 del_params))
375 {
377 "sqlite3_bind_XXXX");
379 plugin->delete_block);
380 return GNUNET_SYSERR;
381 }
382 }
383 n = sqlite3_step (plugin->delete_block);
384 switch (n)
385 {
386 case SQLITE_DONE:
388 "sqlite",
389 "Old block deleted\n");
390 break;
391
392 case SQLITE_BUSY:
395 "sqlite3_step");
396 break;
397
398 default:
401 "sqlite3_step");
402 break;
403 }
405 plugin->delete_block);
406 {
407 struct GNUNET_SQ_QueryParam ins_params[] = {
410 block_size),
413 };
414
415 /* insert new version of the block */
416 if (GNUNET_OK !=
417 GNUNET_SQ_bind (plugin->cache_block,
418 ins_params))
419 {
422 "sqlite3_bind_XXXX");
424 plugin->cache_block);
425 return GNUNET_SYSERR;
426 }
427 }
429 "Caching block under derived key `%s'\n",
430 GNUNET_h2s_full (&query));
431 n = sqlite3_step (plugin->cache_block);
433 plugin->cache_block);
434 switch (n)
435 {
436 case SQLITE_DONE:
438 "Record stored\n");
439 return GNUNET_OK;
440
441 case SQLITE_BUSY:
444 "sqlite3_step");
445 return GNUNET_NO;
446
447 default:
450 "sqlite3_step");
451 return GNUNET_SYSERR;
452 }
453}
454
455
466static int
468 const struct GNUNET_HashCode *query,
470 void *iter_cls)
471{
472 struct Plugin *plugin = cls;
473 int ret;
474 int sret;
475 size_t block_size;
476 const struct GNUNET_GNSRECORD_Block *block;
477 struct GNUNET_SQ_QueryParam params[] = {
480 };
481 struct GNUNET_SQ_ResultSpec rs[] = {
482 GNUNET_SQ_result_spec_variable_size ((void **) &block,
483 &block_size),
485 };
486
487 if (GNUNET_OK !=
488 GNUNET_SQ_bind (plugin->lookup_block,
489 params))
490 {
493 "sqlite3_bind_XXXX");
495 plugin->lookup_block);
496 return GNUNET_SYSERR;
497 }
498 ret = GNUNET_NO;
499 if (SQLITE_ROW ==
500 (sret = sqlite3_step (plugin->lookup_block)))
501 {
502 if (GNUNET_OK !=
503 GNUNET_SQ_extract_result (plugin->lookup_block,
504 rs))
505 {
506 GNUNET_break (0);
508 }
509 else if ((block_size < sizeof(struct GNUNET_GNSRECORD_Block)))
510 {
511 GNUNET_break (0);
514 }
515 else
516 {
518 "Found block under derived key `%s'\n",
519 GNUNET_h2s_full (query));
520 iter (iter_cls,
521 block);
523 ret = GNUNET_YES;
524 }
525 }
526 else
527 {
528 if (SQLITE_DONE != sret)
529 {
532 "sqlite_step");
534 }
535 else
536 {
538 "No block found under derived key `%s'\n",
539 GNUNET_h2s_full (query));
540 }
541 }
543 plugin->lookup_block);
544 return ret;
545}
546
547
557static int
559 const struct GNUNET_HashCode *query)
560{
561 struct Plugin *plugin = cls;
562 struct GNUNET_SQ_QueryParam params[] = {
565 };
566 int n;
567 int ret;
568
569 if (GNUNET_OK !=
570 GNUNET_SQ_bind (plugin->purge_block,
571 params))
572 {
575 "sqlite3_bind_XXXX");
577 plugin->purge_block);
578 return GNUNET_SYSERR;
579 }
580 n = sqlite3_step (plugin->purge_block);
581 switch (n)
582 {
583 case SQLITE_DONE:
584 ret = (0 == sqlite3_changes (plugin->dbh)) ? GNUNET_NO : GNUNET_OK;
586 "Removed %d block(s) under derived key `%s'\n",
587 sqlite3_changes (plugin->dbh),
588 GNUNET_h2s_full (query));
589 break;
590
591 default:
594 "sqlite3_step");
596 break;
597 }
599 plugin->purge_block);
600 return ret;
601}
602
603
604void *
606
613void *
615{
616 static struct Plugin plugin;
617 const struct GNUNET_CONFIGURATION_Handle *cfg = cls;
619
620 if (NULL != plugin.cfg)
621 return NULL; /* can only initialize once! */
622 memset (&plugin, 0, sizeof(struct Plugin));
623 plugin.cfg = cfg;
625 {
627 return NULL;
628 }
630 api->cls = &plugin;
635 _ ("Sqlite database running\n"));
636 return api;
637}
638
639
640void *
642
649void *
651{
653 struct Plugin *plugin = api->cls;
654
656 plugin->cfg = NULL;
659 "sqlite plugin is finished\n");
660 return NULL;
661}
662
663
664/* end of plugin_namecache_sqlite.c */
static int ret
Final status code.
Definition gnunet-arm.c:93
static struct GNUNET_CONFIGURATION_Handle * cfg
Our configuration.
Definition gnunet-arm.c:108
static struct GNUNET_TESTING_PluginFunctions * plugin
Plugin to dynamically load a test case.
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.
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.
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:557
enum GNUNET_GenericReturnValue GNUNET_DISK_directory_create_for_file(const char *filename)
Create the directory structure for storing a file.
Definition disk.c:664
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:438
#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:671
#define _(String)
GNU gettext support macro.
Definition platform.h:179
#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.
static int namecache_sqlite_del_block(void *cls, const struct GNUNET_HashCode *query)
Remove the block for a particular zone and label from the datastore.
#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(* del_block)(void *cls, const struct GNUNET_HashCode *query)
Remove the block for a particular zone and label from 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.
Description of a DB result cell.
void * cls
Closure for conv and cleaner.
Time for absolute times used by GNUnet, in microseconds.
uint64_t rel_value_us
The actual value.
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.
sqlite3_stmt * purge_block
Removes any block under a query, regardless of expiration.