GNUnet 0.21.1
plugin_peerstore_sqlite.c
Go to the documentation of this file.
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
28#include "platform.h"
31#include "gnunet_sq_lib.h"
32#include "../../service/peerstore/peerstore.h"
33#include <sqlite3.h>
34
45#define BUSY_TIMEOUT_MS 1000
46
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
66struct Plugin
67{
71 const struct GNUNET_CONFIGURATION_Handle *cfg;
72
76 char *fn;
77
81 sqlite3 *dbh;
82
86 sqlite3_stmt *insert_peerstoredata;
87
91 sqlite3_stmt *select_peerstoredata;
92
97
102
107
112 sqlite3_stmt *expire_peerstoredata;
113
118 sqlite3_stmt *delete_peerstoredata;
119};
120
121
131static int
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),
144 };
145 int ret;
146
147 if (GNUNET_OK !=
148 GNUNET_SQ_bind (stmt,
149 params))
150 {
153 "sqlite3_bind");
155 stmt);
156 return GNUNET_SYSERR;
157 }
158 if (SQLITE_DONE !=
159 sqlite3_step (stmt))
160 {
163 "sqlite3_step");
165 }
166 else
167 {
168 ret = sqlite3_changes (plugin->dbh);
169 }
171 stmt);
172 return ret;
173}
174
175
186static int
189 void *cont_cls)
190{
191 struct Plugin *plugin = cls;
192 sqlite3_stmt *stmt = plugin->expire_peerstoredata;
193 struct GNUNET_SQ_QueryParam params[] = {
196 };
197
198 if (GNUNET_OK !=
199 GNUNET_SQ_bind (stmt,
200 params))
201 {
204 "sqlite3_bind");
206 stmt);
207 return GNUNET_SYSERR;
208 }
209 if (SQLITE_DONE != sqlite3_step (stmt))
210 {
213 "sqlite3_step");
215 stmt);
216 return GNUNET_SYSERR;
217 }
218 if (NULL != cont)
219 cont (cont_cls,
220 sqlite3_changes (plugin->dbh));
222 stmt);
223 return GNUNET_OK;
224}
225
226
241static int
243 const char *sub_system,
244 const struct GNUNET_PeerIdentity *peer,
245 const char *key,
246 uint64_t serial,
247 uint64_t limit,
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
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),
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),
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),
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),
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 {
328 "sqlite3_bind_XXXX");
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 {
342 "Iteration done (no results)\n");
343 ret = GNUNET_NO;
344 break;
345 }
346 if (SQLITE_ROW != sret)
347 {
350 "sqlite_step");
352 break;
353 }
354 {
356 "Returning a matched record.\n");
357 struct GNUNET_SQ_ResultSpec rs[] = {
365 };
366
367 if (GNUNET_OK !=
369 rs))
370 {
371 GNUNET_break (0);
372 break;
373 }
374 if (NULL != iter)
375 iter (iter_cls,
376 seq,
377 &rec,
378 NULL);
380 }
381 }
383 stmt);
384 return ret;
385}
386
387
405static int
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,
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),
426 };
427
429 {
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 {
443 "sqlite3_bind");
444 GNUNET_assert (0);
445 }
446 if (SQLITE_DONE != sqlite3_step (stmt))
447 {
450 "sqlite3_step");
451 }
453 stmt);
454 if (NULL != cont)
455 cont (cont_cls,
456 GNUNET_OK);
457 return GNUNET_OK;
458}
459
460
468static int
469sql_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);
480 "Executed `%s' / %d\n",
481 sql,
482 result);
483 if (SQLITE_OK != result)
485 _ ("Error executing SQL query: %s\n %s\n"),
486 sqlite3_errmsg (dbh),
487 sql);
488 return result;
489}
490
491
500static int
501sql_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);
514 "Prepared `%s' / %p: %d\n",
515 sql,
516 *stmt,
517 result);
518 if (SQLITE_OK != result)
520 _ ("Error preparing SQL query: %s\n %s\n"),
521 sqlite3_errmsg (dbh),
522 sql);
523 return result;
524}
525
526
535static int
537{
538 char *filename;
539
540 if (GNUNET_OK !=
542 "peerstore-sqlite",
543 "FILENAME",
544 &filename))
545 {
547 "peerstore-sqlite",
548 "FILENAME");
549 return GNUNET_SYSERR;
550 }
552 {
554 {
555 GNUNET_break (0);
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 {
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,
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 {
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
663static void
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)
675 "Failed to close statement %p: %d\n",
676 stmt,
677 result);
678 }
679 if (SQLITE_OK != sqlite3_close (plugin->dbh))
682 "sqlite3_close");
683 GNUNET_free (plugin->fn);
684}
685
686
693void *
695{
696 static struct Plugin plugin;
697 const struct GNUNET_CONFIGURATION_Handle *cfg = cls;
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;
707 {
709 return NULL;
710 }
712 api->cls = &plugin;
717 "Sqlite plugin is running\n");
718 return api;
719}
720
721
728void *
730{
732 struct Plugin *plugin = api->cls;
733
735 plugin->cfg = NULL;
738 "Sqlite plugin is finished\n");
739 return NULL;
740}
741
742
743/* end of plugin_peerstore_sqlite.c */
struct GNUNET_GETOPT_CommandLineOption options[]
Definition: 002.c:5
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.
struct GNUNET_HashCode key
The key used in the DHT.
static char * filename
static char * value
Value of the record to add/remove.
static int result
Global testing status.
Plugin API for the peerstore database backend.
API to the peerstore service.
helper functions for Sqlite3 DB interactions
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.
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
struct GNUNET_SQ_ResultSpec GNUNET_SQ_result_spec_string(char **dst)
0-terminated string expected.
struct GNUNET_SQ_QueryParam GNUNET_SQ_query_param_uint64(const uint64_t *x)
Generate query parameter for an uint16_t in host byte order.
struct GNUNET_SQ_ResultSpec GNUNET_SQ_result_spec_absolute_time(struct GNUNET_TIME_Absolute *at)
Absolute time expected.
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
struct GNUNET_SQ_QueryParam GNUNET_SQ_query_param_string(const char *ptr)
Generate query parameter for a string.
#define GNUNET_SQ_result_spec_auto_from_type(dst)
We expect a fixed-size result, with size determined by the type of * dst
struct GNUNET_SQ_ResultSpec GNUNET_SQ_result_spec_uint64(uint64_t *u64)
uint64_t expected.
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
#define GNUNET_log(kind,...)
@ GNUNET_OK
@ GNUNET_NO
@ GNUNET_SYSERR
#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.
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
#define GNUNET_new(type)
Allocate a struct or union of the given type.
#define GNUNET_free(ptr)
Wrapper around free.
void(* GNUNET_PEERSTORE_PluginProcessor)(void *cls, uint64_t seq, const struct GNUNET_PEERSTORE_Record *record, const char *emsg)
Function called by PEERSTORE for each matching record.
GNUNET_PEERSTORE_StoreOption
Options for storing values in PEERSTORE.
void(* GNUNET_PEERSTORE_Continuation)(void *cls, int success)
Continuation called with a status result.
@ GNUNET_PEERSTORE_STOREOPTION_REPLACE
Delete any previous values for the given key before storing the given value.
static unsigned int size
Size of the "table".
Definition: peer.c:68
#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 int sql_prepare(sqlite3 *dbh, const char *sql, sqlite3_stmt **stmt)
Prepare a SQL statement.
static int peerstore_sqlite_store_record(void *cls, const char *sub_system, const struct GNUNET_PeerIdentity *peer, const char *key, const void *value, size_t size, struct GNUNET_TIME_Absolute expiry, enum GNUNET_PEERSTORE_StoreOption options, GNUNET_PEERSTORE_Continuation cont, void *cont_cls)
Store a record in the peerstore.
#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...
static int peerstore_sqlite_expire_records(void *cls, struct GNUNET_TIME_Absolute now, GNUNET_PEERSTORE_Continuation cont, void *cont_cls)
Delete expired records (expiry < now)
static void database_shutdown(struct Plugin *plugin)
Shutdown database connection and associate data structures.
void * libgnunet_plugin_peerstore_sqlite_done(void *cls)
Exit point from the plugin.
static int peerstore_sqlite_iterate_records(void *cls, const char *sub_system, const struct GNUNET_PeerIdentity *peer, const char *key, uint64_t serial, uint64_t limit, GNUNET_PEERSTORE_PluginProcessor iter, void *iter_cls)
Iterate over the records given an optional peer id and/or key.
static int sql_exec(sqlite3 *dbh, const char *sql)
Prepare a SQL statement.
static int peerstore_sqlite_delete_records(void *cls, const char *sub_system, const struct GNUNET_PeerIdentity *peer, const char *key)
Delete records with the given key.
static int database_setup(struct Plugin *plugin)
Initialize the database connections and associated data structures (create tables and indices as need...
void * libgnunet_plugin_peerstore_sqlite_init(void *cls)
Entry point for the plugin.
#define LOG(kind,...)
void * cls
Closure for all of the callbacks.
struct returned by the initialization function of the plugin
void * cls
Closure to pass to all plugin functions.
int(* iterate_records)(void *cls, const char *sub_system, const struct GNUNET_PeerIdentity *peer, const char *key, uint64_t serial, uint64_t limit, GNUNET_PEERSTORE_PluginProcessor iter, void *iter_cls)
Iterate over the records given an optional peer id and/or key.
int(* store_record)(void *cls, const char *sub_system, const struct GNUNET_PeerIdentity *peer, const char *key, const void *value, size_t size, struct GNUNET_TIME_Absolute expiry, enum GNUNET_PEERSTORE_StoreOption options, GNUNET_PEERSTORE_Continuation cont, void *cont_cls)
Store a record in the peerstore.
int(* expire_records)(void *cls, struct GNUNET_TIME_Absolute now, GNUNET_PEERSTORE_Continuation cont, void *cont_cls)
Delete expired records (expiry < now)
Single PEERSTORE record.
size_t value_size
Size of value BLOB.
struct GNUNET_PeerIdentity peer
Peer Identity.
void * value
Record value BLOB.
struct GNUNET_TIME_Absolute expiry
Expiry time of entry.
char * sub_system
Responsible sub system string.
char * key
Record key string.
The identity of the host (wraps the signing key of the peer).
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 * expire_peerstoredata
Precompiled SQL for deleting expired records from peerstoredata.
sqlite3_stmt * insert_peerstoredata
Precompiled SQL for inserting into peerstoredata.
char * fn
Filename used for the DB.
sqlite3_stmt * select_peerstoredata
Precompiled SQL for selecting from peerstoredata.
struct GNUNET_PQ_Context * dbh
Native Postgres database handle.
const struct GNUNET_CONFIGURATION_Handle * cfg
Our configuration.
sqlite3_stmt * select_peerstoredata_by_all
Precompiled SQL for selecting from peerstoredata.
sqlite3_stmt * delete_peerstoredata
Precompiled SQL for deleting records with given key.
sqlite3_stmt * select_peerstoredata_by_key
Precompiled SQL for selecting from peerstoredata.
sqlite3_stmt * select_peerstoredata_by_pid
Precompiled SQL for selecting from peerstoredata.