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