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