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