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.c1063
1 files changed, 0 insertions, 1063 deletions
diff --git a/src/datacache/plugin_datacache_sqlite.c b/src/datacache/plugin_datacache_sqlite.c
deleted file mode 100644
index 0753d87ce..000000000
--- a/src/datacache/plugin_datacache_sqlite.c
+++ /dev/null
@@ -1,1063 +0,0 @@
1/*
2 This file is part of GNUnet
3 Copyright (C) 2006, 2009, 2015, 2022 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_count_any_stmt;
78
79 /**
80 * Prepared statement for #sqlite_plugin_get.
81 */
82 sqlite3_stmt *get_stmt;
83
84 /**
85 * Prepared statement for #sqlite_plugin_get.
86 */
87 sqlite3_stmt *get_any_stmt;
88
89 /**
90 * Prepared statement for #sqlite_plugin_del.
91 */
92 sqlite3_stmt *del_select_stmt;
93
94 /**
95 * Prepared statement for #sqlite_plugin_del.
96 */
97 sqlite3_stmt *del_expired_stmt;
98
99 /**
100 * Prepared statement for #sqlite_plugin_del.
101 */
102 sqlite3_stmt *del_stmt;
103
104 /**
105 * Prepared statement for #sqlite_plugin_get_closest.
106 */
107 sqlite3_stmt *get_closest_stmt;
108
109 /**
110 * Number of key-value pairs in the database.
111 */
112 unsigned int num_items;
113};
114
115
116/**
117 * Log an error message at log-level @a level that indicates
118 * a failure of the command @a cmd with the error from the database @a db
119 *
120 * @param db database handle
121 * @param level log level
122 * @param cmd failed command
123 */
124#define LOG_SQLITE(db, level, cmd) \
125 do \
126 { \
127 LOG (level, \
128 _ ("`%s' failed at %s:%d with error: %s\n"), \
129 cmd, \
130 __FILE__, \
131 __LINE__, \
132 sqlite3_errmsg (db)); \
133 } while (0)
134
135
136/**
137 * Execute SQL statement.
138 *
139 * @param db database handle
140 * @param cmd SQL command to execute
141 */
142#define SQLITE3_EXEC(db, cmd) \
143 do \
144 { \
145 emsg = NULL; \
146 if (SQLITE_OK != \
147 sqlite3_exec (db, cmd, NULL, NULL, &emsg)) \
148 { \
149 LOG (GNUNET_ERROR_TYPE_ERROR, \
150 _ ("`%s' failed at %s:%d with error: %s\n"), \
151 "sqlite3_exec", \
152 __FILE__, \
153 __LINE__, \
154 emsg); \
155 sqlite3_free (emsg); \
156 } \
157 } while (0)
158
159
160/**
161 * @brief Prepare a SQL statement
162 *
163 * @param dbh database handle
164 * @param zsql SQL statement text
165 * @param[out] ppStmt set to the prepared statement
166 * @return 0 on success
167 */
168static int
169sq_prepare (sqlite3 *dbh,
170 const char *zSql, /* SQL statement, UTF-8 encoded */
171 sqlite3_stmt **ppStmt)
172{ /* OUT: Statement handle */
173 char *dummy;
174
175 return sqlite3_prepare (dbh,
176 zSql,
177 strlen (zSql),
178 ppStmt,
179 (const char **) &dummy);
180}
181
182
183/**
184 * Store an item in the datastore.
185 *
186 * @param cls closure (our `struct Plugin`)
187 * @param xor_distance how close is @a key to our PID?
188 * @param block data to store
189 * @return 0 if duplicate, -1 on error, number of bytes used otherwise
190 */
191static ssize_t
192sqlite_plugin_put (void *cls,
193 uint32_t xor_distance,
194 const struct GNUNET_DATACACHE_Block *block)
195{
196 struct Plugin *plugin = cls;
197 uint32_t type32 = (uint32_t) block->type;
198 uint32_t ro32 = (uint32_t) block->ro;
199 struct GNUNET_SQ_QueryParam params[] = {
200 GNUNET_SQ_query_param_uint32 (&type32),
201 GNUNET_SQ_query_param_uint32 (&ro32),
202 GNUNET_SQ_query_param_absolute_time (&block->expiration_time),
203 GNUNET_SQ_query_param_auto_from_type (&block->key),
204 GNUNET_SQ_query_param_uint32 (&xor_distance),
205 GNUNET_SQ_query_param_fixed_size (block->data,
206 block->data_size),
207 GNUNET_SQ_query_param_fixed_size (block->put_path,
208 block->put_path_length
209 * sizeof(struct GNUNET_DHT_PathElement)),
210 GNUNET_SQ_query_param_auto_from_type (&block->trunc_peer),
211 GNUNET_SQ_query_param_end
212 };
213
214 LOG (GNUNET_ERROR_TYPE_DEBUG,
215 "Processing PUT of %u bytes with key `%s' and expiration %s\n",
216 (unsigned int) block->data_size,
217 GNUNET_h2s (&block->key),
218 GNUNET_STRINGS_relative_time_to_string (
219 GNUNET_TIME_absolute_get_remaining (
220 block->expiration_time),
221 GNUNET_YES));
222 if (GNUNET_OK !=
223 GNUNET_SQ_bind (plugin->insert_stmt,
224 params))
225 {
226 LOG_SQLITE (plugin->dbh,
227 GNUNET_ERROR_TYPE_ERROR,
228 "sqlite3_bind_xxx");
229 GNUNET_SQ_reset (plugin->dbh,
230 plugin->insert_stmt);
231 return -1;
232 }
233 if (SQLITE_DONE !=
234 sqlite3_step (plugin->insert_stmt))
235 {
236 LOG_SQLITE (plugin->dbh,
237 GNUNET_ERROR_TYPE_ERROR,
238 "sqlite3_step");
239 GNUNET_SQ_reset (plugin->dbh,
240 plugin->insert_stmt);
241 return -1;
242 }
243 plugin->num_items++;
244 GNUNET_SQ_reset (plugin->dbh,
245 plugin->insert_stmt);
246 return block->data_size + OVERHEAD;
247}
248
249
250/**
251 * Iterate over the results for a particular key
252 * in the datastore.
253 *
254 * @param cls closure (our `struct Plugin`)
255 * @param key
256 * @param iter maybe NULL (to just count)
257 * @param iter_cls closure for @a iter
258 * @return the number of results found
259 */
260static unsigned int
261get_any (void *cls,
262 const struct GNUNET_HashCode *key,
263 GNUNET_DATACACHE_Iterator iter,
264 void *iter_cls)
265{
266 struct Plugin *plugin = cls;
267 struct GNUNET_TIME_Absolute now;
268 unsigned int cnt;
269 uint32_t off;
270 uint32_t btype32;
271 uint32_t bro32;
272 unsigned int total;
273 struct GNUNET_DATACACHE_Block block;
274 void *path;
275 void *data;
276 size_t path_size;
277 struct GNUNET_SQ_QueryParam params_count[] = {
278 GNUNET_SQ_query_param_auto_from_type (key),
279 GNUNET_SQ_query_param_absolute_time (&now),
280 GNUNET_SQ_query_param_end
281 };
282 struct GNUNET_SQ_QueryParam params_select[] = {
283 GNUNET_SQ_query_param_auto_from_type (key),
284 GNUNET_SQ_query_param_absolute_time (&now),
285 GNUNET_SQ_query_param_uint32 (&off),
286 GNUNET_SQ_query_param_end
287 };
288 struct GNUNET_SQ_ResultSpec rs[] = {
289 GNUNET_SQ_result_spec_variable_size (&data,
290 &block.data_size),
291 GNUNET_SQ_result_spec_absolute_time (&block.expiration_time),
292 GNUNET_SQ_result_spec_variable_size (&path,
293 &path_size),
294 GNUNET_SQ_result_spec_auto_from_type (&block.trunc_peer),
295 GNUNET_SQ_result_spec_uint32 (&btype32),
296 GNUNET_SQ_result_spec_uint32 (&bro32),
297 GNUNET_SQ_result_spec_end
298 };
299
300 now = GNUNET_TIME_absolute_get ();
301 LOG (GNUNET_ERROR_TYPE_DEBUG,
302 "Processing GET for key `%s'\n",
303 GNUNET_h2s (key));
304
305 if (GNUNET_OK !=
306 GNUNET_SQ_bind (plugin->get_count_any_stmt,
307 params_count))
308 {
309 LOG_SQLITE (plugin->dbh,
310 GNUNET_ERROR_TYPE_ERROR,
311 "sqlite3_bind_xxx");
312 GNUNET_SQ_reset (plugin->dbh,
313 plugin->get_count_any_stmt);
314 return 0;
315 }
316 if (SQLITE_ROW !=
317 sqlite3_step (plugin->get_count_any_stmt))
318 {
319 LOG_SQLITE (plugin->dbh,
320 GNUNET_ERROR_TYPE_ERROR,
321 "sqlite_step");
322 GNUNET_SQ_reset (plugin->dbh,
323 plugin->get_count_any_stmt);
324 LOG (GNUNET_ERROR_TYPE_DEBUG,
325 "No content found when processing GET for key `%s'\n",
326 GNUNET_h2s (key));
327 return 0;
328 }
329 total = sqlite3_column_int (plugin->get_count_any_stmt,
330 0);
331 GNUNET_SQ_reset (plugin->dbh,
332 plugin->get_count_any_stmt);
333 if ( (0 == total) ||
334 (NULL == iter) )
335 {
336 if (0 == total)
337 LOG (GNUNET_ERROR_TYPE_DEBUG,
338 "No content found when processing GET for key `%s'\n",
339 GNUNET_h2s (key));
340 return total;
341 }
342
343 cnt = 0;
344 block.key = *key;
345 off = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK,
346 total);
347 while (cnt < total)
348 {
349 off = (off + 1) % total;
350 if (GNUNET_OK !=
351 GNUNET_SQ_bind (plugin->get_any_stmt,
352 params_select))
353 {
354 LOG_SQLITE (plugin->dbh,
355 GNUNET_ERROR_TYPE_ERROR,
356 "sqlite3_bind_xxx");
357 GNUNET_SQ_reset (plugin->dbh,
358 plugin->get_any_stmt);
359 return cnt;
360 }
361 if (SQLITE_ROW !=
362 sqlite3_step (plugin->get_any_stmt))
363 break;
364 if (GNUNET_OK !=
365 GNUNET_SQ_extract_result (plugin->get_any_stmt,
366 rs))
367 {
368 GNUNET_break (0);
369 GNUNET_SQ_reset (plugin->dbh,
370 plugin->get_any_stmt);
371 break;
372 }
373 if (0 != path_size % sizeof(struct GNUNET_DHT_PathElement))
374 {
375 GNUNET_break (0);
376 path_size = 0;
377 path = NULL;
378 }
379 block.data = data;
380 block.put_path = path;
381 block.put_path_length = path_size / sizeof(struct GNUNET_DHT_PathElement);
382 block.type = (enum GNUNET_BLOCK_Type) btype32;
383 block.ro = (enum GNUNET_DHT_RouteOption) bro32;
384 cnt++;
385 LOG (GNUNET_ERROR_TYPE_DEBUG,
386 "Found %u-byte result when processing GET for key `%s'\n",
387 (unsigned int) block.data_size,
388 GNUNET_h2s (&block.key));
389 if (GNUNET_OK !=
390 iter (iter_cls,
391 &block))
392 {
393 GNUNET_SQ_cleanup_result (rs);
394 GNUNET_SQ_reset (plugin->dbh,
395 plugin->get_any_stmt);
396 break;
397 }
398 GNUNET_SQ_cleanup_result (rs);
399 GNUNET_SQ_reset (plugin->dbh,
400 plugin->get_any_stmt);
401 }
402 GNUNET_SQ_reset (plugin->dbh,
403 plugin->get_any_stmt);
404 return cnt;
405}
406
407
408/**
409 * Iterate over the results for a particular key
410 * in the datastore.
411 *
412 * @param cls closure (our `struct Plugin`)
413 * @param key
414 * @param type entries of which type are relevant?
415 * @param iter maybe NULL (to just count)
416 * @param iter_cls closure for @a iter
417 * @return the number of results found
418 */
419static unsigned int
420get_typed (void *cls,
421 const struct GNUNET_HashCode *key,
422 enum GNUNET_BLOCK_Type type,
423 GNUNET_DATACACHE_Iterator iter,
424 void *iter_cls)
425{
426 struct Plugin *plugin = cls;
427 uint32_t type32 = type;
428 struct GNUNET_TIME_Absolute now;
429 unsigned int cnt;
430 uint32_t off;
431 uint32_t bro32;
432 unsigned int total;
433 struct GNUNET_DATACACHE_Block block;
434 void *path;
435 void *data;
436 size_t path_size;
437 struct GNUNET_SQ_QueryParam params_count[] = {
438 GNUNET_SQ_query_param_auto_from_type (key),
439 GNUNET_SQ_query_param_uint32 (&type32),
440 GNUNET_SQ_query_param_absolute_time (&now),
441 GNUNET_SQ_query_param_end
442 };
443 struct GNUNET_SQ_QueryParam params_select[] = {
444 GNUNET_SQ_query_param_auto_from_type (key),
445 GNUNET_SQ_query_param_uint32 (&type32),
446 GNUNET_SQ_query_param_absolute_time (&now),
447 GNUNET_SQ_query_param_uint32 (&off),
448 GNUNET_SQ_query_param_end
449 };
450 struct GNUNET_SQ_ResultSpec rs[] = {
451 GNUNET_SQ_result_spec_variable_size (&data,
452 &block.data_size),
453 GNUNET_SQ_result_spec_absolute_time (&block.expiration_time),
454 GNUNET_SQ_result_spec_variable_size (&path,
455 &path_size),
456 GNUNET_SQ_result_spec_auto_from_type (&block.trunc_peer),
457 GNUNET_SQ_result_spec_uint32 (&bro32),
458 GNUNET_SQ_result_spec_end
459 };
460
461 now = GNUNET_TIME_absolute_get ();
462 LOG (GNUNET_ERROR_TYPE_DEBUG,
463 "Processing GET for key `%s'\n",
464 GNUNET_h2s (key));
465
466 if (GNUNET_OK !=
467 GNUNET_SQ_bind (plugin->get_count_stmt,
468 params_count))
469 {
470 LOG_SQLITE (plugin->dbh,
471 GNUNET_ERROR_TYPE_ERROR,
472 "sqlite3_bind_xxx");
473 GNUNET_SQ_reset (plugin->dbh,
474 plugin->get_count_stmt);
475 return 0;
476 }
477 if (SQLITE_ROW !=
478 sqlite3_step (plugin->get_count_stmt))
479 {
480 LOG_SQLITE (plugin->dbh,
481 GNUNET_ERROR_TYPE_ERROR,
482 "sqlite_step");
483 GNUNET_SQ_reset (plugin->dbh,
484 plugin->get_count_stmt);
485 LOG (GNUNET_ERROR_TYPE_DEBUG,
486 "No content found when processing GET for key `%s'\n",
487 GNUNET_h2s (key));
488 return 0;
489 }
490 total = sqlite3_column_int (plugin->get_count_stmt,
491 0);
492 GNUNET_SQ_reset (plugin->dbh,
493 plugin->get_count_stmt);
494 if ( (0 == total) ||
495 (NULL == iter) )
496 {
497 if (0 == total)
498 LOG (GNUNET_ERROR_TYPE_DEBUG,
499 "No content found when processing GET for key `%s'\n",
500 GNUNET_h2s (key));
501 return total;
502 }
503
504 cnt = 0;
505 block.key = *key;
506 off = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK,
507 total);
508 while (cnt < total)
509 {
510 off = (off + 1) % total;
511 if (GNUNET_OK !=
512 GNUNET_SQ_bind (plugin->get_stmt,
513 params_select))
514 {
515 LOG_SQLITE (plugin->dbh,
516 GNUNET_ERROR_TYPE_ERROR,
517 "sqlite3_bind_xxx");
518 GNUNET_SQ_reset (plugin->dbh,
519 plugin->get_stmt);
520 return cnt;
521 }
522 if (SQLITE_ROW !=
523 sqlite3_step (plugin->get_stmt))
524 break;
525 if (GNUNET_OK !=
526 GNUNET_SQ_extract_result (plugin->get_stmt,
527 rs))
528 {
529 GNUNET_break (0);
530 GNUNET_SQ_reset (plugin->dbh,
531 plugin->get_stmt);
532 break;
533 }
534 if (0 != path_size % sizeof(struct GNUNET_DHT_PathElement))
535 {
536 GNUNET_break (0);
537 path_size = 0;
538 path = NULL;
539 }
540 block.data = data;
541 block.put_path = path;
542 block.put_path_length = path_size / sizeof(struct GNUNET_DHT_PathElement);
543 block.type = type;
544 block.ro = (enum GNUNET_DHT_RouteOption) bro32;
545 cnt++;
546 LOG (GNUNET_ERROR_TYPE_DEBUG,
547 "Found %u-byte result when processing GET for key `%s'\n",
548 (unsigned int) block.data_size,
549 GNUNET_h2s (&block.key));
550 if ( (NULL != iter) &&
551 (GNUNET_OK !=
552 iter (iter_cls,
553 &block)) )
554 {
555 GNUNET_SQ_cleanup_result (rs);
556 GNUNET_SQ_reset (plugin->dbh,
557 plugin->get_stmt);
558 break;
559 }
560 GNUNET_SQ_cleanup_result (rs);
561 GNUNET_SQ_reset (plugin->dbh,
562 plugin->get_stmt);
563 }
564 GNUNET_SQ_reset (plugin->dbh,
565 plugin->get_stmt);
566 return cnt;
567}
568
569
570/**
571 * Iterate over the results for a particular key
572 * in the datastore.
573 *
574 * @param cls closure (our `struct Plugin`)
575 * @param key
576 * @param type entries of which type are relevant?
577 * @param iter maybe NULL (to just count)
578 * @param iter_cls closure for @a iter
579 * @return the number of results found
580 */
581static unsigned int
582sqlite_plugin_get (void *cls,
583 const struct GNUNET_HashCode *key,
584 enum GNUNET_BLOCK_Type type,
585 GNUNET_DATACACHE_Iterator iter,
586 void *iter_cls)
587{
588 if (GNUNET_BLOCK_TYPE_ANY == type)
589 return get_any (cls,
590 key,
591 iter,
592 iter_cls);
593 return get_typed (cls,
594 key,
595 type,
596 iter,
597 iter_cls);
598}
599
600
601/**
602 * Delete the entry with the lowest expiration value
603 * from the datacache right now.
604 *
605 * @param cls closure (our `struct Plugin`)
606 * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
607 */
608static enum GNUNET_GenericReturnValue
609sqlite_plugin_del (void *cls)
610{
611 struct Plugin *plugin = cls;
612 uint64_t rowid;
613 void *data;
614 size_t data_size;
615 struct GNUNET_HashCode hc;
616 struct GNUNET_TIME_Absolute now;
617 struct GNUNET_SQ_ResultSpec rs[] = {
618 GNUNET_SQ_result_spec_uint64 (&rowid),
619 GNUNET_SQ_result_spec_auto_from_type (&hc),
620 GNUNET_SQ_result_spec_variable_size (&data,
621 &data_size),
622 GNUNET_SQ_result_spec_end
623 };
624 struct GNUNET_SQ_QueryParam params[] = {
625 GNUNET_SQ_query_param_uint64 (&rowid),
626 GNUNET_SQ_query_param_end
627 };
628 struct GNUNET_SQ_QueryParam time_params[] = {
629 GNUNET_SQ_query_param_absolute_time (&now),
630 GNUNET_SQ_query_param_end
631 };
632
633 LOG (GNUNET_ERROR_TYPE_DEBUG,
634 "Processing DEL\n");
635 now = GNUNET_TIME_absolute_get ();
636 if (GNUNET_OK !=
637 GNUNET_SQ_bind (plugin->del_expired_stmt,
638 time_params))
639 {
640 LOG_SQLITE (plugin->dbh,
641 GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
642 "sqlite3_bind");
643 GNUNET_SQ_reset (plugin->dbh,
644 plugin->del_expired_stmt);
645 return GNUNET_SYSERR;
646 }
647 if ( (SQLITE_ROW !=
648 sqlite3_step (plugin->del_expired_stmt)) ||
649 (GNUNET_OK !=
650 GNUNET_SQ_extract_result (plugin->del_expired_stmt,
651 rs)))
652 {
653 GNUNET_SQ_reset (plugin->dbh,
654 plugin->del_expired_stmt);
655 if (SQLITE_ROW !=
656 sqlite3_step (plugin->del_select_stmt))
657 {
658 LOG_SQLITE (plugin->dbh,
659 GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
660 "sqlite3_step");
661 GNUNET_SQ_reset (plugin->dbh,
662 plugin->del_select_stmt);
663 return GNUNET_SYSERR;
664 }
665 if (GNUNET_OK !=
666 GNUNET_SQ_extract_result (plugin->del_select_stmt,
667 rs))
668 {
669 GNUNET_SQ_reset (plugin->dbh,
670 plugin->del_select_stmt);
671 GNUNET_break (0);
672 return GNUNET_SYSERR;
673 }
674 }
675 GNUNET_SQ_cleanup_result (rs);
676 GNUNET_SQ_reset (plugin->dbh,
677 plugin->del_select_stmt);
678 if (GNUNET_OK !=
679 GNUNET_SQ_bind (plugin->del_stmt,
680 params))
681 {
682 LOG_SQLITE (plugin->dbh,
683 GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
684 "sqlite3_bind");
685 GNUNET_SQ_reset (plugin->dbh,
686 plugin->del_stmt);
687 return GNUNET_SYSERR;
688 }
689 if (SQLITE_DONE !=
690 sqlite3_step (plugin->del_stmt))
691 {
692 LOG_SQLITE (plugin->dbh,
693 GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
694 "sqlite3_step");
695 GNUNET_SQ_reset (plugin->dbh,
696 plugin->del_stmt);
697 return GNUNET_SYSERR;
698 }
699 plugin->num_items--;
700 plugin->env->delete_notify (plugin->env->cls,
701 &hc,
702 data_size + OVERHEAD);
703 GNUNET_SQ_reset (plugin->dbh,
704 plugin->del_stmt);
705 return GNUNET_OK;
706}
707
708
709/**
710 * Iterate over the results that are "close" to a particular key in
711 * the datacache. "close" is defined as numerically larger than @a
712 * key (when interpreted as a circular address space), with small
713 * distance.
714 *
715 * @param cls closure (internal context for the plugin)
716 * @param key area of the keyspace to look into
717 * @param type desired block type for the replies
718 * @param num_results number of results that should be returned to @a iter
719 * @param iter maybe NULL (to just count)
720 * @param iter_cls closure for @a iter
721 * @return the number of results found
722 */
723static unsigned int
724sqlite_plugin_get_closest (void *cls,
725 const struct GNUNET_HashCode *key,
726 enum GNUNET_BLOCK_Type type,
727 unsigned int num_results,
728 GNUNET_DATACACHE_Iterator iter,
729 void *iter_cls)
730{
731 struct Plugin *plugin = cls;
732 uint32_t type32 = type;
733 uint32_t num_results32 = num_results;
734 struct GNUNET_TIME_Absolute now;
735 void *data;
736 void *path;
737 size_t path_size;
738 unsigned int cnt;
739 uint32_t bro32;
740 struct GNUNET_DATACACHE_Block block;
741 uint32_t rtype32;
742 struct GNUNET_SQ_QueryParam params[] = {
743 GNUNET_SQ_query_param_auto_from_type (key),
744 GNUNET_SQ_query_param_absolute_time (&now),
745 GNUNET_SQ_query_param_uint32 (&type32),
746 GNUNET_SQ_query_param_uint32 (&num_results32),
747 GNUNET_SQ_query_param_end
748 };
749 struct GNUNET_SQ_ResultSpec rs[] = {
750 GNUNET_SQ_result_spec_variable_size (&data,
751 &block.data_size),
752 GNUNET_SQ_result_spec_absolute_time (&block.expiration_time),
753 GNUNET_SQ_result_spec_variable_size (&path,
754 &path_size),
755 GNUNET_SQ_result_spec_auto_from_type (&block.trunc_peer),
756 GNUNET_SQ_result_spec_uint32 (&rtype32),
757 GNUNET_SQ_result_spec_uint32 (&bro32),
758 GNUNET_SQ_result_spec_auto_from_type (&block.key),
759 GNUNET_SQ_result_spec_end
760 };
761
762 now = GNUNET_TIME_absolute_get ();
763 LOG (GNUNET_ERROR_TYPE_DEBUG,
764 "Processing GET_CLOSEST for key `%s'\n",
765 GNUNET_h2s (key));
766 if (GNUNET_OK !=
767 GNUNET_SQ_bind (plugin->get_closest_stmt,
768 params))
769 {
770 LOG_SQLITE (plugin->dbh,
771 GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
772 "sqlite3_bind_xxx");
773 GNUNET_SQ_reset (plugin->dbh,
774 plugin->get_closest_stmt);
775 return 0;
776 }
777 cnt = 0;
778 while (SQLITE_ROW ==
779 sqlite3_step (plugin->get_closest_stmt))
780 {
781 if (GNUNET_OK !=
782 GNUNET_SQ_extract_result (plugin->get_closest_stmt,
783 rs))
784 {
785 GNUNET_break (0);
786 break;
787 }
788 if (0 != path_size % sizeof(struct GNUNET_DHT_PathElement))
789 {
790 GNUNET_break (0);
791 path_size = 0;
792 path = NULL;
793 }
794 block.put_path_length
795 = path_size / sizeof(struct GNUNET_DHT_PathElement);
796 block.put_path = path;
797 block.data = data;
798 block.type = (enum GNUNET_BLOCK_Type) rtype32;
799 block.ro = (enum GNUNET_DHT_RouteOption) bro32;
800 cnt++;
801 LOG (GNUNET_ERROR_TYPE_DEBUG,
802 "Found %u-byte result at %s when processing GET_CLOSE\n",
803 (unsigned int) block.data_size,
804 GNUNET_h2s (&block.key));
805
806 if (GNUNET_OK !=
807 iter (iter_cls,
808 &block))
809 {
810 GNUNET_SQ_cleanup_result (rs);
811 break;
812 }
813 GNUNET_SQ_cleanup_result (rs);
814 }
815 GNUNET_SQ_reset (plugin->dbh,
816 plugin->get_closest_stmt);
817 return cnt;
818}
819
820
821/**
822 * Entry point for the plugin.
823 *
824 * @param cls closure (the `struct GNUNET_DATACACHE_PluginEnvironment`)
825 * @return the plugin's closure (our `struct Plugin`)
826 */
827void *
828libgnunet_plugin_datacache_sqlite_init (void *cls)
829{
830 struct GNUNET_DATACACHE_PluginEnvironment *env = cls;
831 struct GNUNET_DATACACHE_PluginFunctions *api;
832 struct Plugin *plugin;
833 char *fn;
834 char *fn_utf8;
835 sqlite3 *dbh;
836 char *emsg;
837
838 if (GNUNET_YES ==
839 GNUNET_CONFIGURATION_get_value_yesno (env->cfg,
840 "datacache-sqlite",
841 "IN_MEMORY"))
842 {
843 if (SQLITE_OK !=
844 sqlite3_open (":memory:",
845 &dbh))
846 return NULL;
847 fn_utf8 = NULL;
848 }
849 else
850 {
851 fn = GNUNET_DISK_mktemp ("gnunet-datacache");
852 if (NULL == fn)
853 {
854 GNUNET_break (0);
855 return NULL;
856 }
857 /* fn should be UTF-8-encoded. If it isn't, it's a bug. */
858 fn_utf8 = GNUNET_strdup (fn);
859 if (SQLITE_OK !=
860 sqlite3_open (fn_utf8,
861 &dbh))
862 {
863 GNUNET_free (fn);
864 GNUNET_free (fn_utf8);
865 return NULL;
866 }
867 GNUNET_free (fn);
868 }
869
870 SQLITE3_EXEC (dbh, "PRAGMA temp_store=MEMORY");
871 SQLITE3_EXEC (dbh, "PRAGMA locking_mode=EXCLUSIVE");
872 SQLITE3_EXEC (dbh, "PRAGMA journal_mode=OFF");
873 SQLITE3_EXEC (dbh, "PRAGMA synchronous=OFF");
874 SQLITE3_EXEC (dbh, "PRAGMA page_size=4092");
875 if (GNUNET_YES ==
876 GNUNET_CONFIGURATION_get_value_yesno (env->cfg,
877 "datacache-sqlite",
878 "IN_MEMORY"))
879 SQLITE3_EXEC (dbh, "PRAGMA sqlite_temp_store=3");
880
881 SQLITE3_EXEC (dbh,
882 "CREATE TABLE ds180 ("
883 " type INTEGER NOT NULL DEFAULT 0,"
884 " ro INTEGER NOT NULL DEFAULT 0,"
885 " expire INTEGER NOT NULL,"
886 " key BLOB NOT NULL DEFAULT '',"
887 " prox INTEGER NOT NULL,"
888 " value BLOB NOT NULL,"
889 " trunc BLOB NOT NULL,"
890 " path BLOB DEFAULT '')");
891 SQLITE3_EXEC (dbh,
892 "CREATE INDEX idx_hashidx"
893 " ON ds180 (key,type,expire)");
894 SQLITE3_EXEC (dbh,
895 "CREATE INDEX idx_prox_expire"
896 " ON ds180 (prox,expire)");
897 SQLITE3_EXEC (dbh,
898 "CREATE INDEX idx_expire_only"
899 " ON ds180 (expire)");
900 plugin = GNUNET_new (struct Plugin);
901 plugin->env = env;
902 plugin->dbh = dbh;
903 plugin->fn = fn_utf8;
904
905 if ((SQLITE_OK !=
906 sq_prepare (plugin->dbh,
907 "INSERT INTO ds180"
908 " (type, ro, expire, key, prox, value, path, trunc)"
909 " VALUES (?, ?, ?, ?, ?, ?, ?, ?)",
910 &plugin->insert_stmt)) ||
911 (SQLITE_OK !=
912 sq_prepare (plugin->dbh,
913 "SELECT COUNT(*) FROM ds180 "
914 "WHERE key=?"
915 " AND type=?"
916 " AND expire >= ?",
917 &plugin->get_count_stmt)) ||
918 (SQLITE_OK !=
919 sq_prepare (plugin->dbh,
920 "SELECT COUNT(*) FROM ds180 "
921 "WHERE key=? AND expire >= ?",
922 &plugin->get_count_any_stmt)) ||
923 (SQLITE_OK !=
924 sq_prepare (plugin->dbh,
925 "SELECT value,expire,path,trunc,ro"
926 " FROM ds180"
927 " WHERE key=?"
928 " AND type=?"
929 " AND expire >= ?"
930 " LIMIT 1 OFFSET ?",
931 &plugin->get_stmt)) ||
932 (SQLITE_OK !=
933 sq_prepare (plugin->dbh,
934 "SELECT value,expire,path,trunc,type,ro"
935 " FROM ds180"
936 " WHERE key=?"
937 " AND expire >= ?"
938 " LIMIT 1 OFFSET ?",
939 &plugin->get_any_stmt)) ||
940 (SQLITE_OK !=
941 sq_prepare (plugin->dbh,
942 "SELECT _ROWID_,key,value FROM ds180"
943 " WHERE expire < ?1"
944 " ORDER BY expire ASC LIMIT 1",
945 &plugin->del_expired_stmt)) ||
946 (SQLITE_OK !=
947 sq_prepare (plugin->dbh,
948 "SELECT _ROWID_,key,value FROM ds180"
949 " ORDER BY prox ASC, expire ASC LIMIT 1",
950 &plugin->del_select_stmt)) ||
951 (SQLITE_OK !=
952 sq_prepare (plugin->dbh,
953 "DELETE FROM ds180 WHERE _ROWID_=?",
954 &plugin->del_stmt)) ||
955 (SQLITE_OK !=
956 sq_prepare (plugin->dbh,
957 "SELECT * FROM ("
958 " SELECT value,expire,path,trunc,type,ro,key"
959 " FROM ds180 "
960 " WHERE key>=?1 "
961 " AND expire >= ?2"
962 " AND ( (type=?3) or (0 == ?3) )"
963 " ORDER BY KEY ASC LIMIT ?4)"
964 "UNION "
965 "SELECT * FROM ("
966 " SELECT value,expire,path,trunc,type,ro,key"
967 " FROM ds180 "
968 " WHERE key<=?1 "
969 " AND expire >= ?2"
970 " AND ( (type=?3) or (0 == ?3) )"
971 " ORDER BY KEY DESC LIMIT ?4)",
972 &plugin->get_closest_stmt)))
973 {
974 LOG_SQLITE (plugin->dbh,
975 GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
976 "sq_prepare");
977 GNUNET_break (SQLITE_OK ==
978 sqlite3_close (plugin->dbh));
979 GNUNET_free (plugin);
980 return NULL;
981 }
982
983 api = GNUNET_new (struct GNUNET_DATACACHE_PluginFunctions);
984 api->cls = plugin;
985 api->get = &sqlite_plugin_get;
986 api->put = &sqlite_plugin_put;
987 api->del = &sqlite_plugin_del;
988 api->get_closest = &sqlite_plugin_get_closest;
989 LOG (GNUNET_ERROR_TYPE_INFO,
990 "Sqlite datacache running\n");
991 return api;
992}
993
994
995/**
996 * Exit point from the plugin.
997 *
998 * @param cls closure (our `struct Plugin`)
999 * @return NULL
1000 */
1001void *
1002libgnunet_plugin_datacache_sqlite_done (void *cls)
1003{
1004 struct GNUNET_DATACACHE_PluginFunctions *api = cls;
1005 struct Plugin *plugin = api->cls;
1006 int result;
1007
1008#if SQLITE_VERSION_NUMBER >= 3007000
1009 sqlite3_stmt *stmt;
1010#endif
1011
1012#if ! WINDOWS || defined(__CYGWIN__)
1013 if ( (NULL != plugin->fn) &&
1014 (0 != unlink (plugin->fn)) )
1015 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING,
1016 "unlink",
1017 plugin->fn);
1018 GNUNET_free (plugin->fn);
1019#endif
1020 sqlite3_finalize (plugin->insert_stmt);
1021 sqlite3_finalize (plugin->get_count_stmt);
1022 sqlite3_finalize (plugin->get_count_any_stmt);
1023 sqlite3_finalize (plugin->get_stmt);
1024 sqlite3_finalize (plugin->get_any_stmt);
1025 sqlite3_finalize (plugin->del_select_stmt);
1026 sqlite3_finalize (plugin->del_expired_stmt);
1027 sqlite3_finalize (plugin->del_stmt);
1028 sqlite3_finalize (plugin->get_closest_stmt);
1029 result = sqlite3_close (plugin->dbh);
1030#if SQLITE_VERSION_NUMBER >= 3007000
1031 if (SQLITE_BUSY == result)
1032 {
1033 LOG (GNUNET_ERROR_TYPE_WARNING,
1034 _ (
1035 "Tried to close sqlite without finalizing all prepared statements.\n"));
1036 stmt = sqlite3_next_stmt (plugin->dbh,
1037 NULL);
1038 while (NULL != stmt)
1039 {
1040 result = sqlite3_finalize (stmt);
1041 if (result != SQLITE_OK)
1042 LOG (GNUNET_ERROR_TYPE_WARNING,
1043 "Failed to close statement %p: %d\n",
1044 stmt,
1045 result);
1046 stmt = sqlite3_next_stmt (plugin->dbh,
1047 NULL);
1048 }
1049 result = sqlite3_close (plugin->dbh);
1050 }
1051#endif
1052 if (SQLITE_OK != result)
1053 LOG_SQLITE (plugin->dbh,
1054 GNUNET_ERROR_TYPE_ERROR,
1055 "sqlite3_close");
1056
1057 GNUNET_free (plugin);
1058 GNUNET_free (api);
1059 return NULL;
1060}
1061
1062
1063/* end of plugin_datacache_sqlite.c */