aboutsummaryrefslogtreecommitdiff
path: root/src/datacache/plugin_datacache_sqlite.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/datacache/plugin_datacache_sqlite.c')
-rw-r--r--src/datacache/plugin_datacache_sqlite.c807
1 files changed, 0 insertions, 807 deletions
diff --git a/src/datacache/plugin_datacache_sqlite.c b/src/datacache/plugin_datacache_sqlite.c
deleted file mode 100644
index 66ff9e82c..000000000
--- a/src/datacache/plugin_datacache_sqlite.c
+++ /dev/null
@@ -1,807 +0,0 @@
1/*
2 This file is part of GNUnet
3 Copyright (C) 2006, 2009, 2015 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 datacache/plugin_datacache_sqlite.c
23 * @brief sqlite for an implementation of a database backend for the datacache
24 * @author Christian Grothoff
25 */
26#include "platform.h"
27#include "gnunet_util_lib.h"
28#include "gnunet_datacache_plugin.h"
29#include "gnunet_sq_lib.h"
30#include <sqlite3.h>
31
32#define LOG(kind, ...) GNUNET_log_from (kind, "datacache-sqlite", __VA_ARGS__)
33
34#define LOG_STRERROR_FILE(kind, op, fn) \
35 GNUNET_log_from_strerror_file (kind, "datacache-sqlite", op, fn)
36
37
38/**
39 * How much overhead do we assume per entry in the
40 * datacache?
41 */
42#define OVERHEAD (sizeof(struct GNUNET_HashCode) + 36)
43
44/**
45 * Context for all functions in this plugin.
46 */
47struct Plugin
48{
49 /**
50 * Our execution environment.
51 */
52 struct GNUNET_DATACACHE_PluginEnvironment *env;
53
54 /**
55 * Handle to the sqlite database.
56 */
57 sqlite3 *dbh;
58
59 /**
60 * Filename used for the DB.
61 */
62 char *fn;
63
64 /**
65 * Prepared statement for #sqlite_plugin_put.
66 */
67 sqlite3_stmt *insert_stmt;
68
69 /**
70 * Prepared statement for #sqlite_plugin_get.
71 */
72 sqlite3_stmt *get_count_stmt;
73
74 /**
75 * Prepared statement for #sqlite_plugin_get.
76 */
77 sqlite3_stmt *get_stmt;
78
79 /**
80 * Prepared statement for #sqlite_plugin_del.
81 */
82 sqlite3_stmt *del_select_stmt;
83
84 /**
85 * Prepared statement for #sqlite_plugin_del.
86 */
87 sqlite3_stmt *del_expired_stmt;
88
89 /**
90 * Prepared statement for #sqlite_plugin_del.
91 */
92 sqlite3_stmt *del_stmt;
93
94 /**
95 * Prepared statement for #sqlite_plugin_get_random.
96 */
97 sqlite3_stmt *get_random_stmt;
98
99 /**
100 * Prepared statement for #sqlite_plugin_get_closest.
101 */
102 sqlite3_stmt *get_closest_stmt;
103
104 /**
105 * Number of key-value pairs in the database.
106 */
107 unsigned int num_items;
108};
109
110
111/**
112 * Log an error message at log-level @a level that indicates
113 * a failure of the command @a cmd with the error from the database @a db
114 *
115 * @param db database handle
116 * @param level log level
117 * @param cmd failed command
118 */
119#define LOG_SQLITE(db, level, cmd) \
120 do \
121 { \
122 LOG (level, \
123 _ ("`%s' failed at %s:%d with error: %s\n"), \
124 cmd, \
125 __FILE__, \
126 __LINE__, \
127 sqlite3_errmsg (db)); \
128 } while (0)
129
130
131/**
132 * Execute SQL statement.
133 *
134 * @param db database handle
135 * @param cmd SQL command to execute
136 */
137#define SQLITE3_EXEC(db, cmd) \
138 do \
139 { \
140 emsg = NULL; \
141 if (SQLITE_OK != sqlite3_exec (db, cmd, NULL, NULL, &emsg)) \
142 { \
143 LOG (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK, \
144 _ ("`%s' failed at %s:%d with error: %s\n"), \
145 "sqlite3_exec", \
146 __FILE__, \
147 __LINE__, \
148 emsg); \
149 sqlite3_free (emsg); \
150 } \
151 } while (0)
152
153
154/**
155 * @brief Prepare a SQL statement
156 *
157 * @param dbh database handle
158 * @param zsql SQL statement text
159 * @param[out] ppStmt set to the prepared statement
160 * @return 0 on success
161 */
162static int
163sq_prepare (sqlite3 *dbh,
164 const char *zSql, /* SQL statement, UTF-8 encoded */
165 sqlite3_stmt **ppStmt)
166{ /* OUT: Statement handle */
167 char *dummy;
168
169 return sqlite3_prepare (dbh,
170 zSql,
171 strlen (zSql),
172 ppStmt,
173 (const char **) &dummy);
174}
175
176
177/**
178 * Store an item in the datastore.
179 *
180 * @param cls closure (our `struct Plugin`)
181 * @param key key to store @a data under
182 * @param xor_distance how close is @a key to our PID?
183 * @param size number of bytes in @a data
184 * @param data data to store
185 * @param type type of the value
186 * @param discard_time when to discard the value in any case
187 * @param path_info_len number of entries in @a path_info
188 * @param path_info array of peers that have processed the request
189 * @return 0 if duplicate, -1 on error, number of bytes used otherwise
190 */
191static ssize_t
192sqlite_plugin_put (void *cls,
193 const struct GNUNET_HashCode *key,
194 uint32_t xor_distance,
195 size_t size,
196 const char *data,
197 enum GNUNET_BLOCK_Type type,
198 struct GNUNET_TIME_Absolute discard_time,
199 unsigned int path_info_len,
200 const struct GNUNET_PeerIdentity *path_info)
201{
202 struct Plugin *plugin = cls;
203 uint32_t type32 = type;
204 struct GNUNET_SQ_QueryParam params[] =
205 { GNUNET_SQ_query_param_uint32 (&type32),
206 GNUNET_SQ_query_param_absolute_time (&discard_time),
207 GNUNET_SQ_query_param_auto_from_type (key),
208 GNUNET_SQ_query_param_uint32 (&xor_distance),
209 GNUNET_SQ_query_param_fixed_size (data, size),
210 GNUNET_SQ_query_param_fixed_size (path_info,
211 path_info_len
212 * sizeof(struct GNUNET_PeerIdentity)),
213 GNUNET_SQ_query_param_end };
214
215 LOG (GNUNET_ERROR_TYPE_DEBUG,
216 "Processing PUT of %u bytes with key `%s' and expiration %s\n",
217 (unsigned int) size,
218 GNUNET_h2s (key),
219 GNUNET_STRINGS_relative_time_to_string (
220 GNUNET_TIME_absolute_get_remaining (
221 discard_time),
222 GNUNET_YES));
223 if (GNUNET_OK != GNUNET_SQ_bind (plugin->insert_stmt, params))
224 {
225 LOG_SQLITE (plugin->dbh,
226 GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
227 "sqlite3_bind_xxx");
228 GNUNET_SQ_reset (plugin->dbh, plugin->insert_stmt);
229 return -1;
230 }
231 if (SQLITE_DONE != sqlite3_step (plugin->insert_stmt))
232 {
233 LOG_SQLITE (plugin->dbh,
234 GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
235 "sqlite3_step");
236 GNUNET_SQ_reset (plugin->dbh, plugin->insert_stmt);
237 return -1;
238 }
239 plugin->num_items++;
240 GNUNET_SQ_reset (plugin->dbh, plugin->insert_stmt);
241 return size + OVERHEAD;
242}
243
244
245/**
246 * Iterate over the results for a particular key
247 * in the datastore.
248 *
249 * @param cls closure (our `struct Plugin`)
250 * @param key
251 * @param type entries of which type are relevant?
252 * @param iter maybe NULL (to just count)
253 * @param iter_cls closure for @a iter
254 * @return the number of results found
255 */
256static unsigned int
257sqlite_plugin_get (void *cls,
258 const struct GNUNET_HashCode *key,
259 enum GNUNET_BLOCK_Type type,
260 GNUNET_DATACACHE_Iterator iter,
261 void *iter_cls)
262{
263 struct Plugin *plugin = cls;
264 uint32_t type32 = type;
265 struct GNUNET_TIME_Absolute now;
266 struct GNUNET_TIME_Absolute exp;
267 size_t size;
268 void *dat;
269 unsigned int cnt;
270 uint32_t off;
271 unsigned int total;
272 size_t psize;
273 struct GNUNET_PeerIdentity *path;
274 struct GNUNET_SQ_QueryParam params_count[] =
275 { GNUNET_SQ_query_param_auto_from_type (key),
276 GNUNET_SQ_query_param_uint32 (&type32),
277 GNUNET_SQ_query_param_absolute_time (&now),
278 GNUNET_SQ_query_param_end };
279 struct GNUNET_SQ_QueryParam params_select[] =
280 { GNUNET_SQ_query_param_auto_from_type (key),
281 GNUNET_SQ_query_param_uint32 (&type32),
282 GNUNET_SQ_query_param_absolute_time (&now),
283 GNUNET_SQ_query_param_uint32 (&off),
284 GNUNET_SQ_query_param_end };
285 struct GNUNET_SQ_ResultSpec rs[] =
286 { GNUNET_SQ_result_spec_variable_size (&dat, &size),
287 GNUNET_SQ_result_spec_absolute_time (&exp),
288 GNUNET_SQ_result_spec_variable_size ((void **) &path, &psize),
289 GNUNET_SQ_result_spec_end };
290
291 now = GNUNET_TIME_absolute_get ();
292 LOG (GNUNET_ERROR_TYPE_DEBUG,
293 "Processing GET for key `%s'\n",
294 GNUNET_h2s (key));
295
296 if (GNUNET_OK != GNUNET_SQ_bind (plugin->get_count_stmt, params_count))
297 {
298 LOG_SQLITE (plugin->dbh,
299 GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
300 "sqlite3_bind_xxx");
301 GNUNET_SQ_reset (plugin->dbh, plugin->get_count_stmt);
302 return 0;
303 }
304 if (SQLITE_ROW != sqlite3_step (plugin->get_count_stmt))
305 {
306 LOG_SQLITE (plugin->dbh,
307 GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
308 "sqlite_step");
309 GNUNET_SQ_reset (plugin->dbh, plugin->get_count_stmt);
310 LOG (GNUNET_ERROR_TYPE_DEBUG,
311 "No content found when processing GET for key `%s'\n",
312 GNUNET_h2s (key));
313 return 0;
314 }
315 total = sqlite3_column_int (plugin->get_count_stmt, 0);
316 GNUNET_SQ_reset (plugin->dbh, plugin->get_count_stmt);
317 if ((0 == total) || (NULL == iter))
318 {
319 if (0 == total)
320 LOG (GNUNET_ERROR_TYPE_DEBUG,
321 "No content found when processing GET for key `%s'\n",
322 GNUNET_h2s (key));
323 return total;
324 }
325
326 cnt = 0;
327 off = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, total);
328 while (cnt < total)
329 {
330 off = (off + 1) % total;
331 if (GNUNET_OK != GNUNET_SQ_bind (plugin->get_stmt, params_select))
332 {
333 LOG_SQLITE (plugin->dbh,
334 GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
335 "sqlite3_bind_xxx");
336 GNUNET_SQ_reset (plugin->dbh, plugin->get_stmt);
337 return cnt;
338 }
339 if (SQLITE_ROW != sqlite3_step (plugin->get_stmt))
340 break;
341 if (GNUNET_OK != GNUNET_SQ_extract_result (plugin->get_stmt, rs))
342 {
343 GNUNET_break (0);
344 GNUNET_SQ_reset (plugin->dbh, plugin->get_stmt);
345 break;
346 }
347 if (0 != psize % sizeof(struct GNUNET_PeerIdentity))
348 {
349 GNUNET_break (0);
350 psize = 0;
351 path = NULL;
352 }
353 psize /= sizeof(struct GNUNET_PeerIdentity);
354 cnt++;
355 LOG (GNUNET_ERROR_TYPE_DEBUG,
356 "Found %u-byte result when processing GET for key `%s'\n",
357 (unsigned int) size,
358 GNUNET_h2s (key));
359 if (GNUNET_OK != iter (iter_cls, key, size, dat, type, exp, psize, path))
360 {
361 GNUNET_SQ_cleanup_result (rs);
362 GNUNET_SQ_reset (plugin->dbh, plugin->get_stmt);
363 break;
364 }
365 GNUNET_SQ_cleanup_result (rs);
366 GNUNET_SQ_reset (plugin->dbh, plugin->get_stmt);
367 }
368 GNUNET_SQ_reset (plugin->dbh, plugin->get_stmt);
369 return cnt;
370}
371
372
373/**
374 * Delete the entry with the lowest expiration value
375 * from the datacache right now.
376 *
377 * @param cls closure (our `struct Plugin`)
378 * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
379 */
380static int
381sqlite_plugin_del (void *cls)
382{
383 struct Plugin *plugin = cls;
384 uint64_t rowid;
385 void *data;
386 size_t dsize;
387 struct GNUNET_HashCode hc;
388 struct GNUNET_TIME_Absolute now;
389 struct GNUNET_SQ_ResultSpec rs[] =
390 { GNUNET_SQ_result_spec_uint64 (&rowid),
391 GNUNET_SQ_result_spec_auto_from_type (&hc),
392 GNUNET_SQ_result_spec_variable_size ((void **) &data, &dsize),
393 GNUNET_SQ_result_spec_end };
394 struct GNUNET_SQ_QueryParam params[] = { GNUNET_SQ_query_param_uint64 (
395 &rowid),
396 GNUNET_SQ_query_param_end };
397 struct GNUNET_SQ_QueryParam time_params[] =
398 { GNUNET_SQ_query_param_absolute_time (&now), GNUNET_SQ_query_param_end };
399
400 LOG (GNUNET_ERROR_TYPE_DEBUG, "Processing DEL\n");
401 now = GNUNET_TIME_absolute_get ();
402 if (GNUNET_OK != GNUNET_SQ_bind (plugin->del_expired_stmt, time_params))
403 {
404 LOG_SQLITE (plugin->dbh,
405 GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
406 "sqlite3_bind");
407 GNUNET_SQ_reset (plugin->dbh, plugin->del_expired_stmt);
408 return GNUNET_SYSERR;
409 }
410 if ((SQLITE_ROW != sqlite3_step (plugin->del_expired_stmt)) ||
411 (GNUNET_OK != GNUNET_SQ_extract_result (plugin->del_expired_stmt, rs)))
412 {
413 GNUNET_SQ_reset (plugin->dbh, plugin->del_expired_stmt);
414 if (SQLITE_ROW != sqlite3_step (plugin->del_select_stmt))
415 {
416 LOG_SQLITE (plugin->dbh,
417 GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
418 "sqlite3_step");
419 GNUNET_SQ_reset (plugin->dbh, plugin->del_select_stmt);
420 return GNUNET_SYSERR;
421 }
422 if (GNUNET_OK != GNUNET_SQ_extract_result (plugin->del_select_stmt, rs))
423 {
424 GNUNET_SQ_reset (plugin->dbh, plugin->del_select_stmt);
425 GNUNET_break (0);
426 return GNUNET_SYSERR;
427 }
428 }
429 GNUNET_SQ_cleanup_result (rs);
430 GNUNET_SQ_reset (plugin->dbh, plugin->del_select_stmt);
431 if (GNUNET_OK != GNUNET_SQ_bind (plugin->del_stmt, params))
432 {
433 LOG_SQLITE (plugin->dbh,
434 GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
435 "sqlite3_bind");
436 GNUNET_SQ_reset (plugin->dbh, plugin->del_stmt);
437 return GNUNET_SYSERR;
438 }
439 if (SQLITE_DONE != sqlite3_step (plugin->del_stmt))
440 {
441 LOG_SQLITE (plugin->dbh,
442 GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
443 "sqlite3_step");
444 GNUNET_SQ_reset (plugin->dbh, plugin->del_stmt);
445 return GNUNET_SYSERR;
446 }
447 plugin->num_items--;
448 plugin->env->delete_notify (plugin->env->cls, &hc, dsize + OVERHEAD);
449 GNUNET_SQ_reset (plugin->dbh, plugin->del_stmt);
450 return GNUNET_OK;
451}
452
453
454/**
455 * Obtain a random key-value pair from the datacache.
456 *
457 * @param cls closure (our `struct Plugin`)
458 * @param iter maybe NULL (to just count)
459 * @param iter_cls closure for @a iter
460 * @return the number of results found, zero (datacache empty) or one
461 */
462static unsigned int
463sqlite_plugin_get_random (void *cls,
464 GNUNET_DATACACHE_Iterator iter,
465 void *iter_cls)
466{
467 struct Plugin *plugin = cls;
468 struct GNUNET_TIME_Absolute exp;
469 size_t size;
470 void *dat;
471 uint32_t off = 0;
472 size_t psize;
473 uint32_t type;
474 struct GNUNET_PeerIdentity *path;
475 struct GNUNET_HashCode key;
476 struct GNUNET_SQ_QueryParam params[] = { GNUNET_SQ_query_param_uint32 (&off),
477 GNUNET_SQ_query_param_end };
478 struct GNUNET_SQ_ResultSpec rs[] =
479 { GNUNET_SQ_result_spec_variable_size (&dat, &size),
480 GNUNET_SQ_result_spec_absolute_time (&exp),
481 GNUNET_SQ_result_spec_variable_size ((void **) &path, &psize),
482 GNUNET_SQ_result_spec_auto_from_type (&key),
483 GNUNET_SQ_result_spec_uint32 (&type),
484 GNUNET_SQ_result_spec_end };
485
486 if (0 == plugin->num_items)
487 return 0;
488 if (NULL == iter)
489 return 1;
490 off =
491 GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_NONCE, plugin->num_items);
492 if (GNUNET_OK != GNUNET_SQ_bind (plugin->get_random_stmt, params))
493 {
494 return 0;
495 }
496 if (SQLITE_ROW != sqlite3_step (plugin->get_random_stmt))
497 {
498 GNUNET_break (0);
499 GNUNET_SQ_reset (plugin->dbh, plugin->get_random_stmt);
500 return 0;
501 }
502 if (GNUNET_OK != GNUNET_SQ_extract_result (plugin->get_random_stmt, rs))
503 {
504 GNUNET_break (0);
505 GNUNET_SQ_reset (plugin->dbh, plugin->get_random_stmt);
506 return 0;
507 }
508 if (0 != psize % sizeof(struct GNUNET_PeerIdentity))
509 {
510 GNUNET_break (0);
511 psize = 0;
512 path = NULL;
513 }
514 psize /= sizeof(struct GNUNET_PeerIdentity);
515 LOG (GNUNET_ERROR_TYPE_DEBUG,
516 "Found %u-byte result with key %s when processing GET-RANDOM\n",
517 (unsigned int) size,
518 GNUNET_h2s (&key));
519 (void) iter (iter_cls,
520 &key,
521 size,
522 dat,
523 (enum GNUNET_BLOCK_Type) type,
524 exp,
525 psize,
526 path);
527 GNUNET_SQ_cleanup_result (rs);
528 GNUNET_SQ_reset (plugin->dbh, plugin->get_random_stmt);
529 return 1;
530}
531
532
533/**
534 * Iterate over the results that are "close" to a particular key in
535 * the datacache. "close" is defined as numerically larger than @a
536 * key (when interpreted as a circular address space), with small
537 * distance.
538 *
539 * @param cls closure (internal context for the plugin)
540 * @param key area of the keyspace to look into
541 * @param num_results number of results that should be returned to @a iter
542 * @param iter maybe NULL (to just count)
543 * @param iter_cls closure for @a iter
544 * @return the number of results found
545 */
546static unsigned int
547sqlite_plugin_get_closest (void *cls,
548 const struct GNUNET_HashCode *key,
549 unsigned int num_results,
550 GNUNET_DATACACHE_Iterator iter,
551 void *iter_cls)
552{
553 struct Plugin *plugin = cls;
554 uint32_t num_results32 = num_results;
555 struct GNUNET_TIME_Absolute now;
556 struct GNUNET_TIME_Absolute exp;
557 size_t size;
558 void *dat;
559 unsigned int cnt;
560 size_t psize;
561 uint32_t type;
562 struct GNUNET_HashCode hc;
563 struct GNUNET_PeerIdentity *path;
564 struct GNUNET_SQ_QueryParam params[] =
565 { GNUNET_SQ_query_param_auto_from_type (key),
566 GNUNET_SQ_query_param_absolute_time (&now),
567 GNUNET_SQ_query_param_uint32 (&num_results32),
568 GNUNET_SQ_query_param_end };
569 struct GNUNET_SQ_ResultSpec rs[] =
570 { GNUNET_SQ_result_spec_variable_size (&dat, &size),
571 GNUNET_SQ_result_spec_absolute_time (&exp),
572 GNUNET_SQ_result_spec_variable_size ((void **) &path, &psize),
573 GNUNET_SQ_result_spec_uint32 (&type),
574 GNUNET_SQ_result_spec_auto_from_type (&hc),
575 GNUNET_SQ_result_spec_end };
576
577 now = GNUNET_TIME_absolute_get ();
578 LOG (GNUNET_ERROR_TYPE_DEBUG,
579 "Processing GET_CLOSEST for key `%s'\n",
580 GNUNET_h2s (key));
581 if (GNUNET_OK != GNUNET_SQ_bind (plugin->get_closest_stmt, params))
582 {
583 LOG_SQLITE (plugin->dbh,
584 GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
585 "sqlite3_bind_xxx");
586 GNUNET_SQ_reset (plugin->dbh, plugin->get_closest_stmt);
587 return 0;
588 }
589 cnt = 0;
590 while (SQLITE_ROW == sqlite3_step (plugin->get_closest_stmt))
591 {
592 if (GNUNET_OK != GNUNET_SQ_extract_result (plugin->get_closest_stmt, rs))
593 {
594 GNUNET_break (0);
595 break;
596 }
597 if (0 != psize % sizeof(struct GNUNET_PeerIdentity))
598 {
599 GNUNET_break (0);
600 psize = 0;
601 path = NULL;
602 }
603 psize /= sizeof(struct GNUNET_PeerIdentity);
604 cnt++;
605 LOG (GNUNET_ERROR_TYPE_DEBUG,
606 "Found %u-byte result at %s when processing GET_CLOSE\n",
607 (unsigned int) size,
608 GNUNET_h2s (&hc));
609 if (GNUNET_OK != iter (iter_cls, &hc, size, dat, type, exp, psize, path))
610 {
611 GNUNET_SQ_cleanup_result (rs);
612 break;
613 }
614 GNUNET_SQ_cleanup_result (rs);
615 }
616 GNUNET_SQ_reset (plugin->dbh, plugin->get_closest_stmt);
617 return cnt;
618}
619
620
621/**
622 * Entry point for the plugin.
623 *
624 * @param cls closure (the `struct GNUNET_DATACACHE_PluginEnvironment`)
625 * @return the plugin's closure (our `struct Plugin`)
626 */
627void *
628libgnunet_plugin_datacache_sqlite_init (void *cls)
629{
630 struct GNUNET_DATACACHE_PluginEnvironment *env = cls;
631 struct GNUNET_DATACACHE_PluginFunctions *api;
632 struct Plugin *plugin;
633 char *fn;
634 char *fn_utf8;
635 sqlite3 *dbh;
636 char *emsg;
637
638 if (GNUNET_YES == GNUNET_CONFIGURATION_get_value_yesno (env->cfg,
639 "datacache-sqlite",
640 "IN_MEMORY"))
641 {
642 if (SQLITE_OK != sqlite3_open (":memory:", &dbh))
643 return NULL;
644 fn_utf8 = NULL;
645 }
646 else
647 {
648 fn = GNUNET_DISK_mktemp ("gnunet-datacache");
649 if (fn == NULL)
650 {
651 GNUNET_break (0);
652 return NULL;
653 }
654 /* fn should be UTF-8-encoded. If it isn't, it's a bug. */
655 fn_utf8 = GNUNET_strdup (fn);
656 if (SQLITE_OK != sqlite3_open (fn_utf8, &dbh))
657 {
658 GNUNET_free (fn);
659 GNUNET_free (fn_utf8);
660 return NULL;
661 }
662 GNUNET_free (fn);
663 }
664
665 SQLITE3_EXEC (dbh, "PRAGMA temp_store=MEMORY");
666 SQLITE3_EXEC (dbh, "PRAGMA locking_mode=EXCLUSIVE");
667 SQLITE3_EXEC (dbh, "PRAGMA journal_mode=OFF");
668 SQLITE3_EXEC (dbh, "PRAGMA synchronous=OFF");
669 SQLITE3_EXEC (dbh, "PRAGMA page_size=4092");
670 if (GNUNET_YES == GNUNET_CONFIGURATION_get_value_yesno (env->cfg,
671 "datacache-sqlite",
672 "IN_MEMORY"))
673 SQLITE3_EXEC (dbh, "PRAGMA sqlite_temp_store=3");
674
675 SQLITE3_EXEC (dbh,
676 "CREATE TABLE ds091 ("
677 " type INTEGER NOT NULL DEFAULT 0,"
678 " expire INTEGER NOT NULL,"
679 " key BLOB NOT NULL DEFAULT '',"
680 " prox INTEGER NOT NULL,"
681 " value BLOB NOT NULL,"
682 " path BLOB DEFAULT '')");
683 SQLITE3_EXEC (dbh, "CREATE INDEX idx_hashidx ON ds091 (key,type,expire)");
684 SQLITE3_EXEC (dbh, "CREATE INDEX idx_prox_expire ON ds091 (prox,expire)");
685 SQLITE3_EXEC (dbh, "CREATE INDEX idx_expire_only ON ds091 (expire)");
686 plugin = GNUNET_new (struct Plugin);
687 plugin->env = env;
688 plugin->dbh = dbh;
689 plugin->fn = fn_utf8;
690
691 if ((SQLITE_OK !=
692 sq_prepare (plugin->dbh,
693 "INSERT INTO ds091 (type, expire, key, prox, value, path) "
694 "VALUES (?, ?, ?, ?, ?, ?)",
695 &plugin->insert_stmt)) ||
696 (SQLITE_OK != sq_prepare (plugin->dbh,
697 "SELECT count(*) FROM ds091 "
698 "WHERE key=? AND type=? AND expire >= ?",
699 &plugin->get_count_stmt)) ||
700 (SQLITE_OK !=
701 sq_prepare (plugin->dbh,
702 "SELECT value,expire,path FROM ds091"
703 " WHERE key=? AND type=? AND expire >= ? LIMIT 1 OFFSET ?",
704 &plugin->get_stmt)) ||
705 (SQLITE_OK != sq_prepare (plugin->dbh,
706 "SELECT _ROWID_,key,value FROM ds091"
707 " WHERE expire < ?"
708 " ORDER BY expire ASC LIMIT 1",
709 &plugin->del_expired_stmt)) ||
710 (SQLITE_OK != sq_prepare (plugin->dbh,
711 "SELECT _ROWID_,key,value FROM ds091"
712 " ORDER BY prox ASC, expire ASC LIMIT 1",
713 &plugin->del_select_stmt)) ||
714 (SQLITE_OK != sq_prepare (plugin->dbh,
715 "DELETE FROM ds091 WHERE _ROWID_=?",
716 &plugin->del_stmt)) ||
717 (SQLITE_OK != sq_prepare (plugin->dbh,
718 "SELECT value,expire,path,key,type FROM ds091 "
719 "ORDER BY key LIMIT 1 OFFSET ?",
720 &plugin->get_random_stmt)) ||
721 (SQLITE_OK !=
722 sq_prepare (plugin->dbh,
723 "SELECT value,expire,path,type,key FROM ds091 "
724 "WHERE key>=? AND expire >= ? ORDER BY KEY ASC LIMIT ?",
725 &plugin->get_closest_stmt)))
726 {
727 LOG_SQLITE (plugin->dbh,
728 GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
729 "sq_prepare");
730 GNUNET_break (SQLITE_OK == sqlite3_close (plugin->dbh));
731 GNUNET_free (plugin);
732 return NULL;
733 }
734
735 api = GNUNET_new (struct GNUNET_DATACACHE_PluginFunctions);
736 api->cls = plugin;
737 api->get = &sqlite_plugin_get;
738 api->put = &sqlite_plugin_put;
739 api->del = &sqlite_plugin_del;
740 api->get_random = &sqlite_plugin_get_random;
741 api->get_closest = &sqlite_plugin_get_closest;
742 LOG (GNUNET_ERROR_TYPE_INFO, "Sqlite datacache running\n");
743 return api;
744}
745
746
747/**
748 * Exit point from the plugin.
749 *
750 * @param cls closure (our `struct Plugin`)
751 * @return NULL
752 */
753void *
754libgnunet_plugin_datacache_sqlite_done (void *cls)
755{
756 struct GNUNET_DATACACHE_PluginFunctions *api = cls;
757 struct Plugin *plugin = api->cls;
758 int result;
759
760#if SQLITE_VERSION_NUMBER >= 3007000
761 sqlite3_stmt *stmt;
762#endif
763
764#if ! WINDOWS || defined(__CYGWIN__)
765 if ((NULL != plugin->fn) && (0 != unlink (plugin->fn)))
766 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "unlink", plugin->fn);
767 GNUNET_free (plugin->fn);
768#endif
769 sqlite3_finalize (plugin->insert_stmt);
770 sqlite3_finalize (plugin->get_count_stmt);
771 sqlite3_finalize (plugin->get_stmt);
772 sqlite3_finalize (plugin->del_select_stmt);
773 sqlite3_finalize (plugin->del_expired_stmt);
774 sqlite3_finalize (plugin->del_stmt);
775 sqlite3_finalize (plugin->get_random_stmt);
776 sqlite3_finalize (plugin->get_closest_stmt);
777 result = sqlite3_close (plugin->dbh);
778#if SQLITE_VERSION_NUMBER >= 3007000
779 if (SQLITE_BUSY == result)
780 {
781 LOG (GNUNET_ERROR_TYPE_WARNING,
782 _ (
783 "Tried to close sqlite without finalizing all prepared statements.\n"));
784 stmt = sqlite3_next_stmt (plugin->dbh, NULL);
785 while (NULL != stmt)
786 {
787 result = sqlite3_finalize (stmt);
788 if (result != SQLITE_OK)
789 LOG (GNUNET_ERROR_TYPE_WARNING,
790 "Failed to close statement %p: %d\n",
791 stmt,
792 result);
793 stmt = sqlite3_next_stmt (plugin->dbh, NULL);
794 }
795 result = sqlite3_close (plugin->dbh);
796 }
797#endif
798 if (SQLITE_OK != result)
799 LOG_SQLITE (plugin->dbh, GNUNET_ERROR_TYPE_ERROR, "sqlite3_close");
800
801 GNUNET_free (plugin);
802 GNUNET_free (api);
803 return NULL;
804}
805
806
807/* end of plugin_datacache_sqlite.c */