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