aboutsummaryrefslogtreecommitdiff
path: root/src/datacache/plugin_datacache_sqlite.c
diff options
context:
space:
mode:
authorChristian Grothoff <christian@grothoff.org>2009-07-25 22:24:42 +0000
committerChristian Grothoff <christian@grothoff.org>2009-07-25 22:24:42 +0000
commitc890b76cde9288a8d9f2faa3046fbb06341c8082 (patch)
treeb2c072b5ad2fba45a6bf5856209963fbf37375c3 /src/datacache/plugin_datacache_sqlite.c
parent2ec742c593419e3225fa87dcf44ca96fb62d62b5 (diff)
downloadgnunet-c890b76cde9288a8d9f2faa3046fbb06341c8082.tar.gz
gnunet-c890b76cde9288a8d9f2faa3046fbb06341c8082.zip
towards datacache implementation
Diffstat (limited to 'src/datacache/plugin_datacache_sqlite.c')
-rw-r--r--src/datacache/plugin_datacache_sqlite.c676
1 files changed, 676 insertions, 0 deletions
diff --git a/src/datacache/plugin_datacache_sqlite.c b/src/datacache/plugin_datacache_sqlite.c
new file mode 100644
index 000000000..e386b55de
--- /dev/null
+++ b/src/datacache/plugin_datacache_sqlite.c
@@ -0,0 +1,676 @@
1/*
2 This file is part of GNUnet.
3 (C) 2006, 2007, 2008 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 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 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19*/
20
21/**
22 * @file applications/dstore_sqlite/dstore.c
23 * @brief SQLite based implementation of the dstore service
24 * @author Christian Grothoff
25 * @todo Indexes, statistics
26 *
27 * Database: SQLite
28 */
29
30#include "platform.h"
31#include "gnunet_util.h"
32#include "gnunet_dstore_service.h"
33#include "gnunet_stats_service.h"
34#include <sqlite3.h>
35
36#define DEBUG_DSTORE GNUNET_NO
37
38/**
39 * Maximum size for an individual item.
40 */
41#define MAX_CONTENT_SIZE 65536
42
43/**
44 * Bytes used
45 */
46static unsigned long long payload;
47
48/**
49 * Maximum bytes available
50 */
51static unsigned long long quota;
52
53/**
54 * Filename of this database
55 */
56static char *fn;
57static char *fn_utf8;
58
59static GNUNET_CoreAPIForPlugins *coreAPI;
60
61static struct GNUNET_Mutex *lock;
62
63/**
64 * Statistics service.
65 */
66static GNUNET_Stats_ServiceAPI *stats;
67
68static unsigned int stat_dstore_size;
69
70static unsigned int stat_dstore_quota;
71
72/**
73 * Estimate of the per-entry overhead (including indices).
74 */
75#define OVERHEAD ((4*2+4*2+8*2+8*2+sizeof(GNUNET_HashCode)*5+32))
76
77struct GNUNET_BloomFilter *bloom;
78
79static char *bloom_name;
80
81/**
82 * @brief Prepare a SQL statement
83 */
84static int
85sq_prepare (sqlite3 * dbh, const char *zSql, /* SQL statement, UTF-8 encoded */
86 sqlite3_stmt ** ppStmt)
87{ /* OUT: Statement handle */
88 char *dummy;
89 return sqlite3_prepare (dbh,
90 zSql,
91 strlen (zSql), ppStmt, (const char **) &dummy);
92}
93
94#define SQLITE3_EXEC(db, cmd) do { emsg = NULL; if (SQLITE_OK != sqlite3_exec(db, cmd, NULL, NULL, &emsg)) { GNUNET_GE_LOG(coreAPI->ectx, GNUNET_GE_ERROR | GNUNET_GE_ADMIN | GNUNET_GE_BULK, _("`%s' failed at %s:%d with error: %s\n"), "sqlite3_exec", __FILE__, __LINE__, emsg); sqlite3_free(emsg); } } while(0)
95
96/**
97 * Log an error message at log-level 'level' that indicates
98 * a failure of the command 'cmd' on file 'filename'
99 * with the message given by strerror(errno).
100 */
101#define LOG_SQLITE(db, level, cmd) do { GNUNET_GE_LOG(coreAPI->ectx, level, _("`%s' failed at %s:%d with error: %s\n"), cmd, __FILE__, __LINE__, sqlite3_errmsg(db)); } while(0)
102
103static void
104db_init (sqlite3 * dbh)
105{
106 char *emsg;
107
108 SQLITE3_EXEC (dbh, "PRAGMA temp_store=MEMORY");
109 SQLITE3_EXEC (dbh, "PRAGMA synchronous=OFF");
110 SQLITE3_EXEC (dbh, "PRAGMA count_changes=OFF");
111 SQLITE3_EXEC (dbh, "PRAGMA page_size=4092");
112 SQLITE3_EXEC (dbh,
113 "CREATE TABLE ds080 ("
114 " size INTEGER NOT NULL DEFAULT 0,"
115 " type INTEGER NOT NULL DEFAULT 0,"
116 " puttime INTEGER NOT NULL DEFAULT 0,"
117 " expire INTEGER NOT NULL DEFAULT 0,"
118 " key BLOB NOT NULL DEFAULT '',"
119 " vhash BLOB NOT NULL DEFAULT '',"
120 " value BLOB NOT NULL DEFAULT '')");
121 SQLITE3_EXEC (dbh, "CREATE INDEX idx_hashidx ON ds080 (key,type,expire)");
122 SQLITE3_EXEC (dbh,
123 "CREATE INDEX idx_allidx ON ds080 (key,vhash,type,size)");
124 SQLITE3_EXEC (dbh, "CREATE INDEX idx_puttime ON ds080 (puttime)");
125}
126
127static int
128db_reset ()
129{
130 int fd;
131 sqlite3 *dbh;
132 char *tmpl;
133 const char *tmpdir;
134
135 if (fn != NULL)
136 {
137 UNLINK (fn);
138 GNUNET_free (fn);
139 GNUNET_free (fn_utf8);
140 }
141 payload = 0;
142
143 tmpdir = getenv ("TMPDIR");
144 tmpdir = tmpdir ? tmpdir : "/tmp";
145
146#define TEMPLATE "/gnunet-dstoreXXXXXX"
147 tmpl = GNUNET_malloc (strlen (tmpdir) + sizeof (TEMPLATE) + 1);
148 strcpy (tmpl, tmpdir);
149 strcat (tmpl, TEMPLATE);
150#undef TEMPLATE
151
152#ifdef MINGW
153 fn = (char *) GNUNET_malloc (MAX_PATH + 1);
154 plibc_conv_to_win_path (tmpl, fn);
155 GNUNET_free (tmpl);
156#else
157 fn = tmpl;
158#endif
159 fd = mkstemp (fn);
160 if (fd == -1)
161 {
162 GNUNET_GE_BREAK (NULL, 0);
163 GNUNET_free (fn);
164 fn = NULL;
165 return GNUNET_SYSERR;
166 }
167 CLOSE (fd);
168 fn_utf8 = GNUNET_convert_string_to_utf8 (coreAPI->ectx, fn, strlen (fn),
169#ifdef ENABLE_NLS
170 nl_langinfo (CODESET)
171#else
172 "UTF-8" /* good luck */
173#endif
174 );
175 if (SQLITE_OK != sqlite3_open (fn_utf8, &dbh))
176 {
177 GNUNET_free (fn);
178 GNUNET_free (fn_utf8);
179 fn = NULL;
180 return GNUNET_SYSERR;
181 }
182 db_init (dbh);
183 sqlite3_close (dbh);
184 return GNUNET_OK;
185}
186
187/**
188 * Check that we are within quota.
189 * @return GNUNET_OK if we are.
190 */
191static int
192checkQuota (sqlite3 * dbh)
193{
194 GNUNET_HashCode dkey;
195 GNUNET_HashCode vhash;
196 unsigned int dsize;
197 unsigned int dtype;
198 sqlite3_stmt *stmt;
199 sqlite3_stmt *dstmt;
200 int err;
201
202 if (payload * 10 <= quota * 9)
203 return GNUNET_OK; /* we seem to be about 10% off */
204#if DEBUG_DSTORE
205 GNUNET_GE_LOG (coreAPI->ectx,
206 GNUNET_GE_DEBUG | GNUNET_GE_REQUEST | GNUNET_GE_DEVELOPER,
207 "DStore above qutoa (have %llu, allowed %llu), will delete some data.\n",
208 payload, quota);
209#endif
210 stmt = NULL;
211 dstmt = NULL;
212 if ((sq_prepare (dbh,
213 "SELECT size, type, key, vhash FROM ds080 ORDER BY puttime ASC LIMIT 1",
214 &stmt) != SQLITE_OK) ||
215 (sq_prepare (dbh,
216 "DELETE FROM ds080 "
217 "WHERE key=? AND vhash=? AND type=? AND size=?",
218 &dstmt) != SQLITE_OK))
219 {
220 GNUNET_GE_LOG (coreAPI->ectx,
221 GNUNET_GE_ERROR | GNUNET_GE_ADMIN | GNUNET_GE_BULK,
222 _("`%s' failed at %s:%d with error: %s\n"),
223 "sq_prepare", __FILE__, __LINE__, sqlite3_errmsg (dbh));
224 GNUNET_GE_BREAK (NULL, 0);
225 if (dstmt != NULL)
226 sqlite3_finalize (dstmt);
227 if (stmt != NULL)
228 sqlite3_finalize (stmt);
229 return GNUNET_SYSERR;
230 }
231 err = SQLITE_DONE;
232 while ((payload * 10 > quota * 9) && /* we seem to be about 10% off */
233 ((err = sqlite3_step (stmt)) == SQLITE_ROW))
234 {
235 dsize = sqlite3_column_int (stmt, 0);
236 dtype = sqlite3_column_int (stmt, 1);
237 GNUNET_GE_BREAK (NULL,
238 sqlite3_column_bytes (stmt,
239 2) == sizeof (GNUNET_HashCode));
240 GNUNET_GE_BREAK (NULL,
241 sqlite3_column_bytes (stmt,
242 3) == sizeof (GNUNET_HashCode));
243 memcpy (&dkey, sqlite3_column_blob (stmt, 2), sizeof (GNUNET_HashCode));
244 memcpy (&vhash, sqlite3_column_blob (stmt, 3),
245 sizeof (GNUNET_HashCode));
246 sqlite3_reset (stmt);
247 sqlite3_bind_blob (dstmt,
248 1, &dkey, sizeof (GNUNET_HashCode),
249 SQLITE_TRANSIENT);
250 sqlite3_bind_blob (dstmt,
251 2, &vhash, sizeof (GNUNET_HashCode),
252 SQLITE_TRANSIENT);
253 sqlite3_bind_int (dstmt, 3, dtype);
254 sqlite3_bind_int (dstmt, 4, dsize);
255 if ((err = sqlite3_step (dstmt)) != SQLITE_DONE)
256 {
257 GNUNET_GE_LOG (coreAPI->ectx,
258 GNUNET_GE_ERROR | GNUNET_GE_ADMIN | GNUNET_GE_BULK,
259 _("`%s' failed at %s:%d with error: %s\n"),
260 "sqlite3_step", __FILE__, __LINE__,
261 sqlite3_errmsg (dbh));
262 sqlite3_reset (dstmt);
263 GNUNET_GE_BREAK (NULL, 0); /* should delete but cannot!? */
264 break;
265 }
266 if (sqlite3_total_changes (dbh) > 0)
267 {
268 if (bloom != NULL)
269 GNUNET_bloomfilter_remove (bloom, &dkey);
270 payload -= (dsize + OVERHEAD);
271 }
272#if DEBUG_DSTORE
273 GNUNET_GE_LOG (coreAPI->ectx,
274 GNUNET_GE_DEBUG | GNUNET_GE_REQUEST |
275 GNUNET_GE_DEVELOPER,
276 "Deleting %u bytes decreases DStore payload to %llu out of %llu\n",
277 dsize, payload, quota);
278#endif
279 sqlite3_reset (dstmt);
280 }
281 if (err != SQLITE_DONE)
282 {
283 GNUNET_GE_LOG (coreAPI->ectx,
284 GNUNET_GE_ERROR | GNUNET_GE_ADMIN | GNUNET_GE_BULK,
285 _("`%s' failed at %s:%d with error: %s\n"),
286 "sqlite3_step", __FILE__, __LINE__,
287 sqlite3_errmsg (dbh));
288 }
289 sqlite3_finalize (dstmt);
290 sqlite3_finalize (stmt);
291 if (payload * 10 > quota * 9)
292 {
293 /* we seem to be about 10% off */
294 GNUNET_GE_LOG (coreAPI->ectx,
295 GNUNET_GE_ERROR | GNUNET_GE_BULK | GNUNET_GE_DEVELOPER,
296 "Failed to delete content to drop below quota (bug?).\n",
297 payload, quota);
298 return GNUNET_SYSERR;
299 }
300 return GNUNET_OK;
301}
302
303/**
304 * Store an item in the datastore.
305 *
306 * @return GNUNET_OK on success, GNUNET_SYSERR on error
307 */
308static int
309d_put (const GNUNET_HashCode * key,
310 unsigned int type,
311 GNUNET_CronTime discard_time, unsigned int size, const char *data)
312{
313 GNUNET_HashCode vhash;
314 sqlite3 *dbh;
315 sqlite3_stmt *stmt;
316 int ret;
317 GNUNET_CronTime now;
318
319 if (size > MAX_CONTENT_SIZE)
320 return GNUNET_SYSERR;
321 GNUNET_hash (data, size, &vhash);
322 GNUNET_mutex_lock (lock);
323 if ((fn == NULL) || (SQLITE_OK != sqlite3_open (fn_utf8, &dbh)))
324 {
325 db_reset (dbh);
326 GNUNET_mutex_unlock (lock);
327 return GNUNET_SYSERR;
328 }
329 now = GNUNET_get_time ();
330#if DEBUG_DSTORE
331 GNUNET_GE_LOG (coreAPI->ectx,
332 GNUNET_GE_DEBUG | GNUNET_GE_REQUEST | GNUNET_GE_DEVELOPER,
333 "dstore processes put `%.*s' with expiration %llu\n",
334 size, data, discard_time);
335#endif
336
337 /* first try UPDATE */
338 if (sq_prepare (dbh,
339 "UPDATE ds080 SET puttime=?, expire=? "
340 "WHERE key=? AND vhash=? AND type=? AND size=?",
341 &stmt) != SQLITE_OK)
342 {
343 GNUNET_GE_LOG (coreAPI->ectx,
344 GNUNET_GE_ERROR | GNUNET_GE_ADMIN | GNUNET_GE_BULK,
345 _("`%s' failed at %s:%d with error: %s\n"),
346 "sq_prepare", __FILE__, __LINE__, sqlite3_errmsg (dbh));
347 sqlite3_close (dbh);
348 GNUNET_mutex_unlock (lock);
349 return GNUNET_SYSERR;
350 }
351 if ((SQLITE_OK !=
352 sqlite3_bind_int64 (stmt, 1, now)) ||
353 (SQLITE_OK !=
354 sqlite3_bind_int64 (stmt, 2, discard_time)) ||
355 (SQLITE_OK !=
356 sqlite3_bind_blob (stmt, 3, key, sizeof (GNUNET_HashCode),
357 SQLITE_TRANSIENT)) ||
358 (SQLITE_OK !=
359 sqlite3_bind_blob (stmt, 4, &vhash, sizeof (GNUNET_HashCode),
360 SQLITE_TRANSIENT)) ||
361 (SQLITE_OK != sqlite3_bind_int (stmt, 5, type)) ||
362 (SQLITE_OK != sqlite3_bind_int (stmt, 6, size)))
363 {
364 GNUNET_GE_LOG (coreAPI->ectx,
365 GNUNET_GE_ERROR | GNUNET_GE_ADMIN | GNUNET_GE_BULK,
366 _("`%s' failed at %s:%d with error: %s\n"),
367 "sqlite3_bind_xxx", __FILE__, __LINE__,
368 sqlite3_errmsg (dbh));
369 sqlite3_finalize (stmt);
370 sqlite3_close (dbh);
371 GNUNET_mutex_unlock (lock);
372 return GNUNET_SYSERR;
373 }
374 if (SQLITE_DONE != sqlite3_step (stmt))
375 {
376 GNUNET_GE_LOG (coreAPI->ectx,
377 GNUNET_GE_ERROR | GNUNET_GE_ADMIN | GNUNET_GE_BULK,
378 _("`%s' failed at %s:%d with error: %s\n"),
379 "sqlite3_step", __FILE__, __LINE__,
380 sqlite3_errmsg (dbh));
381 sqlite3_finalize (stmt);
382 sqlite3_close (dbh);
383 GNUNET_mutex_unlock (lock);
384 return GNUNET_SYSERR;
385 }
386 ret = sqlite3_changes (dbh);
387 sqlite3_finalize (stmt);
388 if (ret > 0)
389 {
390 sqlite3_close (dbh);
391 GNUNET_mutex_unlock (lock);
392 return GNUNET_OK;
393 }
394 if (GNUNET_OK != checkQuota (dbh))
395 {
396 sqlite3_close (dbh);
397 GNUNET_mutex_unlock (lock);
398 return GNUNET_SYSERR;
399 }
400 if (sq_prepare (dbh,
401 "INSERT INTO ds080 "
402 "(size, type, puttime, expire, key, vhash, value) "
403 "VALUES (?, ?, ?, ?, ?, ?, ?)", &stmt) != SQLITE_OK)
404 {
405 GNUNET_GE_LOG (coreAPI->ectx,
406 GNUNET_GE_ERROR | GNUNET_GE_ADMIN | GNUNET_GE_BULK,
407 _("`%s' failed at %s:%d with error: %s\n"),
408 "sq_prepare", __FILE__, __LINE__, sqlite3_errmsg (dbh));
409 sqlite3_close (dbh);
410 GNUNET_mutex_unlock (lock);
411 return GNUNET_SYSERR;
412 }
413 if ((SQLITE_OK == sqlite3_bind_int (stmt, 1, size)) &&
414 (SQLITE_OK == sqlite3_bind_int (stmt, 2, type)) &&
415 (SQLITE_OK == sqlite3_bind_int64 (stmt, 3, now)) &&
416 (SQLITE_OK == sqlite3_bind_int64 (stmt, 4, discard_time)) &&
417 (SQLITE_OK ==
418 sqlite3_bind_blob (stmt, 5, key, sizeof (GNUNET_HashCode),
419 SQLITE_TRANSIENT)) &&
420 (SQLITE_OK ==
421 sqlite3_bind_blob (stmt, 6, &vhash, sizeof (GNUNET_HashCode),
422 SQLITE_TRANSIENT))
423 && (SQLITE_OK ==
424 sqlite3_bind_blob (stmt, 7, data, size, SQLITE_TRANSIENT)))
425 {
426 if (SQLITE_DONE != sqlite3_step (stmt))
427 {
428 LOG_SQLITE (dbh,
429 GNUNET_GE_ERROR | GNUNET_GE_DEVELOPER | GNUNET_GE_ADMIN
430 | GNUNET_GE_BULK, "sqlite3_step");
431 }
432 else
433 {
434 payload += size + OVERHEAD;
435 if (bloom != NULL)
436 GNUNET_bloomfilter_add (bloom, key);
437 }
438 if (SQLITE_OK != sqlite3_finalize (stmt))
439 LOG_SQLITE (dbh,
440 GNUNET_GE_ERROR | GNUNET_GE_DEVELOPER | GNUNET_GE_ADMIN |
441 GNUNET_GE_BULK, "sqlite3_finalize");
442 }
443 else
444 {
445 LOG_SQLITE (dbh,
446 GNUNET_GE_ERROR | GNUNET_GE_DEVELOPER | GNUNET_GE_ADMIN |
447 GNUNET_GE_BULK, "sqlite3_bind_xxx");
448 }
449#if DEBUG_DSTORE
450 GNUNET_GE_LOG (coreAPI->ectx,
451 GNUNET_GE_DEBUG | GNUNET_GE_REQUEST | GNUNET_GE_DEVELOPER,
452 "Storing %u bytes increases DStore payload to %llu out of %llu\n",
453 size, payload, quota);
454#endif
455 checkQuota (dbh);
456 sqlite3_close (dbh);
457 GNUNET_mutex_unlock (lock);
458 if (stats != NULL)
459 stats->set (stat_dstore_size, payload);
460 return GNUNET_OK;
461}
462
463/**
464 * Iterate over the results for a particular key
465 * in the datastore.
466 *
467 * @param key
468 * @param type entries of which type are relevant?
469 * @param iter maybe NULL (to just count)
470 * @return the number of results, GNUNET_SYSERR if the
471 * iter is non-NULL and aborted the iteration
472 */
473static int
474d_get (const GNUNET_HashCode * key,
475 unsigned int type, GNUNET_ResultProcessor handler, void *closure)
476{
477 sqlite3 *dbh;
478 sqlite3_stmt *stmt;
479 GNUNET_CronTime now;
480 unsigned int size;
481 const char *dat;
482 unsigned int cnt;
483 unsigned int off;
484 unsigned int total;
485 char scratch[256];
486
487 GNUNET_mutex_lock (lock);
488 if ((bloom != NULL) && (GNUNET_NO == GNUNET_bloomfilter_test (bloom, key)))
489 {
490 GNUNET_mutex_unlock (lock);
491 return 0;
492 }
493 if ((fn == NULL) || (SQLITE_OK != sqlite3_open (fn_utf8, &dbh)))
494 {
495 db_reset (dbh);
496 GNUNET_mutex_unlock (lock);
497 return GNUNET_SYSERR;
498 }
499 now = GNUNET_get_time ();
500#if DEBUG_DSTORE
501 GNUNET_GE_LOG (coreAPI->ectx,
502 GNUNET_GE_DEBUG | GNUNET_GE_REQUEST | GNUNET_GE_DEVELOPER,
503 "dstore processes get at `%llu'\n", now);
504#endif
505 if (sq_prepare (dbh,
506 "SELECT count(*) FROM ds080 WHERE key=? AND type=? AND expire >= ?",
507 &stmt) != SQLITE_OK)
508 {
509 GNUNET_GE_LOG (coreAPI->ectx,
510 GNUNET_GE_ERROR | GNUNET_GE_ADMIN | GNUNET_GE_BULK,
511 _("`%s' failed at %s:%d with error: %s\n"),
512 "sq_prepare", __FILE__, __LINE__, sqlite3_errmsg (dbh));
513 sqlite3_close (dbh);
514 db_reset (dbh);
515 GNUNET_mutex_unlock (lock);
516 return GNUNET_SYSERR;
517 }
518 sqlite3_bind_blob (stmt, 1, key, sizeof (GNUNET_HashCode),
519 SQLITE_TRANSIENT);
520 sqlite3_bind_int (stmt, 2, type);
521 sqlite3_bind_int64 (stmt, 3, now);
522 if (SQLITE_ROW != sqlite3_step (stmt))
523 {
524 LOG_SQLITE (dbh,
525 GNUNET_GE_ERROR | GNUNET_GE_ADMIN | GNUNET_GE_USER |
526 GNUNET_GE_BULK, "sqlite_step");
527 sqlite3_reset (stmt);
528 sqlite3_finalize (stmt);
529 db_reset (dbh);
530 GNUNET_mutex_unlock (lock);
531 return GNUNET_SYSERR;
532 }
533 total = sqlite3_column_int (stmt, 0);
534 sqlite3_reset (stmt);
535 sqlite3_finalize (stmt);
536 if ((total == 0) || (handler == NULL))
537 {
538 sqlite3_close (dbh);
539 GNUNET_mutex_unlock (lock);
540 return total;
541 }
542
543 cnt = 0;
544 off = GNUNET_random_u32 (GNUNET_RANDOM_QUALITY_WEAK, total);
545 while (cnt < total)
546 {
547 off = (off + 1) % total;
548 GNUNET_snprintf (scratch, 256,
549 "SELECT size, value FROM ds080 WHERE key=? AND type=? AND expire >= ? LIMIT 1 OFFSET %u",
550 off);
551 if (sq_prepare (dbh, scratch, &stmt) != SQLITE_OK)
552 {
553 GNUNET_GE_LOG (coreAPI->ectx,
554 GNUNET_GE_ERROR | GNUNET_GE_ADMIN | GNUNET_GE_BULK,
555 _("`%s' failed at %s:%d with error: %s\n"),
556 "sq_prepare", __FILE__, __LINE__,
557 sqlite3_errmsg (dbh));
558 sqlite3_close (dbh);
559 GNUNET_mutex_unlock (lock);
560 return GNUNET_SYSERR;
561 }
562 sqlite3_bind_blob (stmt, 1, key, sizeof (GNUNET_HashCode),
563 SQLITE_TRANSIENT);
564 sqlite3_bind_int (stmt, 2, type);
565 sqlite3_bind_int64 (stmt, 3, now);
566 if (sqlite3_step (stmt) != SQLITE_ROW)
567 break;
568 size = sqlite3_column_int (stmt, 0);
569 if (size != sqlite3_column_bytes (stmt, 1))
570 {
571 GNUNET_GE_BREAK (NULL, 0);
572 sqlite3_finalize (stmt);
573 continue;
574 }
575 dat = sqlite3_column_blob (stmt, 1);
576 cnt++;
577#if DEBUG_DSTORE
578 GNUNET_GE_LOG (coreAPI->ectx,
579 GNUNET_GE_DEBUG | GNUNET_GE_REQUEST |
580 GNUNET_GE_DEVELOPER,
581 "dstore found result for get: `%.*s'\n", size, dat);
582#endif
583 if ((handler != NULL) &&
584 (GNUNET_OK != handler (key, type, size, dat, closure)))
585 {
586 sqlite3_finalize (stmt);
587 break;
588 }
589 sqlite3_finalize (stmt);
590 }
591 sqlite3_close (dbh);
592 GNUNET_mutex_unlock (lock);
593 return cnt;
594}
595
596GNUNET_Dstore_ServiceAPI *
597provide_module_dstore_sqlite (GNUNET_CoreAPIForPlugins * capi)
598{
599 static GNUNET_Dstore_ServiceAPI api;
600 int fd;
601
602#if DEBUG_SQLITE
603 GNUNET_GE_LOG (capi->ectx,
604 GNUNET_GE_DEBUG | GNUNET_GE_REQUEST | GNUNET_GE_USER,
605 "SQLite Dstore: initializing database\n");
606#endif
607 coreAPI = capi;
608 if (GNUNET_OK != db_reset ())
609 {
610 GNUNET_GE_BREAK (capi->ectx, 0);
611 return NULL;
612 }
613 lock = GNUNET_mutex_create (GNUNET_NO);
614
615
616 api.get = &d_get;
617 api.put = &d_put;
618 GNUNET_GC_get_configuration_value_number (coreAPI->cfg,
619 "DSTORE", "QUOTA", 1, 1024, 1,
620 &quota);
621 if (quota == 0) /* error */
622 quota = 1;
623 quota *= 1024 * 1024;
624
625 bloom_name = GNUNET_strdup ("/tmp/dbloomXXXXXX");
626 fd = mkstemp (bloom_name);
627 if (fd != -1)
628 {
629 bloom = GNUNET_bloomfilter_load (coreAPI->ectx, bloom_name, quota / (OVERHEAD + 1024), /* 8 bit per entry in DB, expect 1k entries */
630 5);
631 CLOSE (fd);
632 }
633 stats = capi->service_request ("stats");
634 if (stats != NULL)
635 {
636 stat_dstore_size = stats->create (gettext_noop ("# bytes in dstore"));
637 stat_dstore_quota =
638 stats->create (gettext_noop ("# max bytes allowed in dstore"));
639 stats->set (stat_dstore_quota, quota);
640 }
641 return &api;
642}
643
644/**
645 * Shutdown the module.
646 */
647void
648release_module_dstore_sqlite ()
649{
650 UNLINK (fn);
651 GNUNET_free (fn);
652 GNUNET_free (fn_utf8);
653 fn = NULL;
654 if (bloom != NULL)
655 {
656 GNUNET_bloomfilter_free (bloom);
657 bloom = NULL;
658 }
659 UNLINK (bloom_name);
660 GNUNET_free (bloom_name);
661 bloom_name = NULL;
662 if (stats != NULL)
663 {
664 coreAPI->service_release (stats);
665 stats = NULL;
666 }
667#if DEBUG_SQLITE
668 GNUNET_GE_LOG (coreAPI->ectx,
669 GNUNET_GE_DEBUG | GNUNET_GE_REQUEST | GNUNET_GE_USER,
670 "SQLite Dstore: database shutdown\n");
671#endif
672 GNUNET_mutex_destroy (lock);
673 coreAPI = NULL;
674}
675
676/* end of dstore.c */