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