e386b55de7491861d420d24dbbfe5323c98a0a4b
[oweals/gnunet.git] / src / datacache / plugin_datacache_sqlite.c
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  */
46 static unsigned long long payload;
47
48 /**
49  * Maximum bytes available
50  */
51 static unsigned long long quota;
52
53 /**
54  * Filename of this database
55  */
56 static char *fn;
57 static char *fn_utf8;
58
59 static GNUNET_CoreAPIForPlugins *coreAPI;
60
61 static struct GNUNET_Mutex *lock;
62
63 /**
64  * Statistics service.
65  */
66 static GNUNET_Stats_ServiceAPI *stats;
67
68 static unsigned int stat_dstore_size;
69
70 static 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
77 struct GNUNET_BloomFilter *bloom;
78
79 static char *bloom_name;
80
81 /**
82  * @brief Prepare a SQL statement
83  */
84 static int
85 sq_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
103 static void
104 db_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
127 static int
128 db_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  */
191 static int
192 checkQuota (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  */
308 static int
309 d_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  */
473 static int
474 d_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
596 GNUNET_Dstore_ServiceAPI *
597 provide_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  */
647 void
648 release_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 */