fe6daf4dd281d85559f0f4dff369da958f85f4d1
[oweals/gnunet.git] / src / datacache / plugin_datacache_sqlite.c
1 /*
2      This file is part of GNUnet
3      Copyright (C) 2006, 2009, 2015 Christian Grothoff (and other contributing authors)
4
5      GNUnet is free software; you can redistribute it and/or modify
6      it under the terms of the GNU General Public License as published
7      by the Free Software Foundation; either version 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., 59 Temple Place - Suite 330,
18      Boston, MA 02111-1307, 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, zSql, strlen (zSql), ppStmt,
105                           (const char **) &dummy);
106 }
107
108
109 /**
110  * Store an item in the datastore.
111  *
112  * @param cls closure (our `struct Plugin`)
113  * @param key key to store data under
114  * @param size number of bytes in @a data
115  * @param data data to store
116  * @param type type of the value
117  * @param discard_time when to discard the value in any case
118  * @param path_info_len number of entries in @a path_info
119  * @param path_info array of peers that have processed the request
120  * @return 0 if duplicate, -1 on error, number of bytes used otherwise
121  */
122 static ssize_t
123 sqlite_plugin_put (void *cls,
124                    const struct GNUNET_HashCode *key,
125                    size_t size,
126                    const char *data,
127                    enum GNUNET_BLOCK_Type type,
128                    struct GNUNET_TIME_Absolute discard_time,
129                    unsigned int path_info_len,
130                    const struct GNUNET_PeerIdentity *path_info)
131 {
132   struct Plugin *plugin = cls;
133   sqlite3_stmt *stmt;
134   int64_t dval;
135
136   LOG (GNUNET_ERROR_TYPE_DEBUG,
137        "Processing PUT of %u bytes with key `%4s' and expiration %s\n",
138        (unsigned int) size,
139        GNUNET_h2s (key),
140        GNUNET_STRINGS_relative_time_to_string (GNUNET_TIME_absolute_get_remaining (discard_time), GNUNET_YES));
141   dval = (int64_t) discard_time.abs_value_us;
142   if (dval < 0)
143     dval = INT64_MAX;
144   if (sq_prepare
145       (plugin->dbh,
146        "INSERT INTO ds090 (type, expire, key, value, path) VALUES (?, ?, ?, ?, ?)",
147        &stmt) != SQLITE_OK)
148   {
149     LOG_SQLITE (plugin->dbh,
150                 GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
151                 "sq_prepare");
152     return -1;
153   }
154   if ((SQLITE_OK != sqlite3_bind_int (stmt, 1, type)) ||
155       (SQLITE_OK != sqlite3_bind_int64 (stmt, 2, dval)) ||
156       (SQLITE_OK !=
157        sqlite3_bind_blob (stmt, 3,
158                           key, sizeof (struct GNUNET_HashCode),
159                           SQLITE_TRANSIENT)) ||
160       (SQLITE_OK != sqlite3_bind_blob (stmt, 4,
161                                        data, size,
162                                        SQLITE_TRANSIENT)) ||
163       (SQLITE_OK != sqlite3_bind_blob (stmt, 5,
164                                        path_info,
165                                        path_info_len * sizeof (struct GNUNET_PeerIdentity),
166                                        SQLITE_TRANSIENT)))
167   {
168     LOG_SQLITE (plugin->dbh,
169                 GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
170                 "sqlite3_bind_xxx");
171     sqlite3_finalize (stmt);
172     return -1;
173   }
174   if (SQLITE_DONE != sqlite3_step (stmt))
175   {
176     LOG_SQLITE (plugin->dbh,
177                 GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
178                 "sqlite3_step");
179     sqlite3_finalize (stmt);
180     return -1;
181   }
182   plugin->num_items++;
183   if (SQLITE_OK != sqlite3_finalize (stmt))
184     LOG_SQLITE (plugin->dbh,
185                 GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
186                 "sqlite3_finalize");
187   return size + OVERHEAD;
188 }
189
190
191 /**
192  * Iterate over the results for a particular key
193  * in the datastore.
194  *
195  * @param cls closure (our `struct Plugin`)
196  * @param key
197  * @param type entries of which type are relevant?
198  * @param iter maybe NULL (to just count)
199  * @param iter_cls closure for @a iter
200  * @return the number of results found
201  */
202 static unsigned int
203 sqlite_plugin_get (void *cls,
204                    const struct GNUNET_HashCode *key,
205                    enum GNUNET_BLOCK_Type type,
206                    GNUNET_DATACACHE_Iterator iter,
207                    void *iter_cls)
208 {
209   struct Plugin *plugin = cls;
210   sqlite3_stmt *stmt;
211   struct GNUNET_TIME_Absolute now;
212   struct GNUNET_TIME_Absolute exp;
213   unsigned int size;
214   const char *dat;
215   unsigned int cnt;
216   unsigned int off;
217   unsigned int total;
218   unsigned int psize;
219   char scratch[256];
220   int64_t ntime;
221   const struct GNUNET_PeerIdentity *path;
222
223   now = GNUNET_TIME_absolute_get ();
224   LOG (GNUNET_ERROR_TYPE_DEBUG,
225        "Processing GET for key `%4s'\n",
226        GNUNET_h2s (key));
227   if (sq_prepare
228       (plugin->dbh,
229        "SELECT count(*) FROM ds090 WHERE key=? AND type=? AND expire >= ?",
230        &stmt) != SQLITE_OK)
231   {
232     LOG_SQLITE (plugin->dbh,
233                 GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
234                 "sq_prepare");
235     return 0;
236   }
237   ntime = (int64_t) now.abs_value_us;
238   GNUNET_assert (ntime >= 0);
239   if ((SQLITE_OK !=
240        sqlite3_bind_blob (stmt, 1, key, sizeof (struct GNUNET_HashCode),
241                           SQLITE_TRANSIENT)) ||
242       (SQLITE_OK != sqlite3_bind_int (stmt, 2, type)) ||
243       (SQLITE_OK != sqlite3_bind_int64 (stmt, 3, now.abs_value_us)))
244   {
245     LOG_SQLITE (plugin->dbh,
246                 GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
247                 "sqlite3_bind_xxx");
248     sqlite3_finalize (stmt);
249     return 0;
250   }
251
252   if (SQLITE_ROW != sqlite3_step (stmt))
253   {
254     LOG_SQLITE (plugin->dbh, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
255                 "sqlite_step");
256     sqlite3_finalize (stmt);
257     LOG (GNUNET_ERROR_TYPE_DEBUG,
258          "No content found when processing GET for key `%4s'\n",
259          GNUNET_h2s (key));
260     return 0;
261   }
262   total = sqlite3_column_int (stmt, 0);
263   sqlite3_finalize (stmt);
264   if ((total == 0) || (iter == NULL))
265   {
266     if (0 == total)
267       LOG (GNUNET_ERROR_TYPE_DEBUG,
268            "No content found when processing GET for key `%4s'\n",
269            GNUNET_h2s (key));
270     return total;
271   }
272
273   cnt = 0;
274   off = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, total);
275   while (cnt < total)
276   {
277     off = (off + 1) % total;
278     GNUNET_snprintf (scratch, sizeof (scratch),
279                      "SELECT value,expire,path FROM ds090 WHERE key=? AND type=? AND expire >= ? LIMIT 1 OFFSET %u",
280                      off);
281     if (sq_prepare (plugin->dbh, scratch, &stmt) != SQLITE_OK)
282     {
283       LOG_SQLITE (plugin->dbh,
284                   GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
285                   "sq_prepare");
286       return cnt;
287     }
288     if ((SQLITE_OK !=
289          sqlite3_bind_blob (stmt, 1,
290                             key,
291                             sizeof (struct GNUNET_HashCode),
292                             SQLITE_TRANSIENT)) ||
293         (SQLITE_OK != sqlite3_bind_int (stmt, 2, type)) ||
294         (SQLITE_OK != sqlite3_bind_int64 (stmt, 3, now.abs_value_us)))
295     {
296       LOG_SQLITE (plugin->dbh,
297                   GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
298                   "sqlite3_bind_xxx");
299       sqlite3_finalize (stmt);
300       return cnt;
301     }
302     if (sqlite3_step (stmt) != SQLITE_ROW)
303       break;
304     size = sqlite3_column_bytes (stmt, 0);
305     dat = sqlite3_column_blob (stmt, 0);
306     exp.abs_value_us = sqlite3_column_int64 (stmt, 1);
307     psize = sqlite3_column_bytes (stmt, 2);
308     if (0 != psize % sizeof (struct GNUNET_PeerIdentity))
309     {
310       GNUNET_break (0);
311       psize = 0;
312     }
313     psize /= sizeof (struct GNUNET_PeerIdentity);
314     if (0 != psize)
315       path = sqlite3_column_blob (stmt, 2);
316     else
317       path = NULL;
318     ntime = (int64_t) exp.abs_value_us;
319     if (ntime == INT64_MAX)
320       exp = GNUNET_TIME_UNIT_FOREVER_ABS;
321     cnt++;
322     LOG (GNUNET_ERROR_TYPE_DEBUG,
323          "Found %u-byte result when processing GET for key `%4s'\n",
324          (unsigned int) size,
325          GNUNET_h2s (key));
326     if (GNUNET_OK != iter (iter_cls, key, size, dat, type, exp, psize, path))
327     {
328       sqlite3_finalize (stmt);
329       break;
330     }
331     sqlite3_finalize (stmt);
332   }
333   return cnt;
334 }
335
336
337 /**
338  * Delete the entry with the lowest expiration value
339  * from the datacache right now.
340  *
341  * @param cls closure (our `struct Plugin`)
342  * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
343  */
344 static int
345 sqlite_plugin_del (void *cls)
346 {
347   struct Plugin *plugin = cls;
348   unsigned long long rowid;
349   unsigned int dsize;
350   sqlite3_stmt *stmt;
351   sqlite3_stmt *dstmt;
352   struct GNUNET_HashCode hc;
353
354   LOG (GNUNET_ERROR_TYPE_DEBUG, "Processing `%s'\n", "DEL");
355   stmt = NULL;
356   dstmt = NULL;
357   if (sq_prepare
358       (plugin->dbh,
359        "SELECT _ROWID_,key,value FROM ds090 ORDER BY expire ASC LIMIT 1",
360        &stmt) != SQLITE_OK)
361   {
362     LOG_SQLITE (plugin->dbh, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
363                 "sq_prepare");
364     if (stmt != NULL)
365       (void) sqlite3_finalize (stmt);
366     return GNUNET_SYSERR;
367   }
368   if (SQLITE_ROW != sqlite3_step (stmt))
369   {
370     LOG_SQLITE (plugin->dbh, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
371                 "sqlite3_step");
372     (void) sqlite3_finalize (stmt);
373     return GNUNET_SYSERR;
374   }
375   rowid = sqlite3_column_int64 (stmt, 0);
376   GNUNET_assert (sqlite3_column_bytes (stmt, 1) == sizeof (struct GNUNET_HashCode));
377   memcpy (&hc, sqlite3_column_blob (stmt, 1), sizeof (struct GNUNET_HashCode));
378   dsize = sqlite3_column_bytes (stmt, 2);
379   if (SQLITE_OK != sqlite3_finalize (stmt))
380     LOG_SQLITE (plugin->dbh, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
381                 "sqlite3_step");
382   if (sq_prepare (plugin->dbh, "DELETE FROM ds090 WHERE _ROWID_=?", &dstmt) !=
383       SQLITE_OK)
384   {
385     LOG_SQLITE (plugin->dbh, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
386                 "sq_prepare");
387     if (stmt != NULL)
388       (void) sqlite3_finalize (stmt);
389     return GNUNET_SYSERR;
390   }
391   if (SQLITE_OK != sqlite3_bind_int64 (dstmt, 1, rowid))
392   {
393     LOG_SQLITE (plugin->dbh, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
394                 "sqlite3_bind");
395     (void) sqlite3_finalize (dstmt);
396     return GNUNET_SYSERR;
397   }
398   if (sqlite3_step (dstmt) != SQLITE_DONE)
399   {
400     LOG_SQLITE (plugin->dbh, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
401                 "sqlite3_step");
402     (void) sqlite3_finalize (dstmt);
403     return GNUNET_SYSERR;
404   }
405   plugin->num_items--;
406   plugin->env->delete_notify (plugin->env->cls, &hc, dsize + OVERHEAD);
407   if (SQLITE_OK != sqlite3_finalize (dstmt))
408     LOG_SQLITE (plugin->dbh, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
409                 "sqlite3_finalize");
410   return GNUNET_OK;
411 }
412
413
414 /**
415  * Entry point for the plugin.
416  *
417  * @param cls closure (the `struct GNUNET_DATACACHE_PluginEnvironmnet`)
418  * @return the plugin's closure (our `struct Plugin`)
419  */
420 void *
421 libgnunet_plugin_datacache_sqlite_init (void *cls)
422 {
423   struct GNUNET_DATACACHE_PluginEnvironment *env = cls;
424   struct GNUNET_DATACACHE_PluginFunctions *api;
425   struct Plugin *plugin;
426   char *fn;
427   char *fn_utf8;
428   sqlite3 *dbh;
429   char *emsg;
430
431   if (GNUNET_YES ==
432       GNUNET_CONFIGURATION_get_value_yesno (env->cfg,
433                                             "datacache-sqlite",
434                                             "IN_MEMORY"))
435   {
436     if (SQLITE_OK != sqlite3_open (":memory:", &dbh))
437       return NULL;
438     fn_utf8 = NULL;
439   }
440   else
441   {
442     fn = GNUNET_DISK_mktemp ("gnunet-datacache");
443     if (fn == NULL)
444       {
445         GNUNET_break (0);
446         return NULL;
447       }
448     /* fn should be UTF-8-encoded. If it isn't, it's a bug. */
449     fn_utf8 = GNUNET_strdup (fn);
450     if (SQLITE_OK != sqlite3_open (fn_utf8, &dbh))
451     {
452       GNUNET_free (fn);
453       GNUNET_free (fn_utf8);
454       return NULL;
455     }
456     GNUNET_free (fn);
457   }
458
459   SQLITE3_EXEC (dbh, "PRAGMA temp_store=MEMORY");
460   SQLITE3_EXEC (dbh, "PRAGMA locking_mode=EXCLUSIVE");
461   SQLITE3_EXEC (dbh, "PRAGMA journal_mode=OFF");
462   SQLITE3_EXEC (dbh, "PRAGMA synchronous=OFF");
463   SQLITE3_EXEC (dbh, "PRAGMA page_size=4092");
464   if (GNUNET_YES ==
465       GNUNET_CONFIGURATION_get_value_yesno (env->cfg,
466                                             "datacache-sqlite",
467                                             "IN_MEMORY"))
468     SQLITE3_EXEC (dbh, "PRAGMA sqlite_temp_store=3");
469
470   SQLITE3_EXEC (dbh,
471                 "CREATE TABLE ds090 (" "  type INTEGER NOT NULL DEFAULT 0,"
472                 "  expire INTEGER NOT NULL DEFAULT 0,"
473                 "  key BLOB NOT NULL DEFAULT '',"
474                 "  value BLOB NOT NULL DEFAULT '',"
475                 "  path BLOB DEFAULT '')");
476   SQLITE3_EXEC (dbh, "CREATE INDEX idx_hashidx ON ds090 (key,type,expire)");
477   SQLITE3_EXEC (dbh, "CREATE INDEX idx_expire ON ds090 (expire)");
478   plugin = GNUNET_new (struct Plugin);
479   plugin->env = env;
480   plugin->dbh = dbh;
481   plugin->fn = fn_utf8;
482   api = GNUNET_new (struct GNUNET_DATACACHE_PluginFunctions);
483   api->cls = plugin;
484   api->get = &sqlite_plugin_get;
485   api->put = &sqlite_plugin_put;
486   api->del = &sqlite_plugin_del;
487   LOG (GNUNET_ERROR_TYPE_INFO, _("Sqlite datacache running\n"));
488   return api;
489 }
490
491
492 /**
493  * Exit point from the plugin.
494  *
495  * @param cls closure (our `struct Plugin`)
496  * @return NULL
497  */
498 void *
499 libgnunet_plugin_datacache_sqlite_done (void *cls)
500 {
501   struct GNUNET_DATACACHE_PluginFunctions *api = cls;
502   struct Plugin *plugin = api->cls;
503   int result;
504
505 #if SQLITE_VERSION_NUMBER >= 3007000
506   sqlite3_stmt *stmt;
507 #endif
508
509 #if !WINDOWS || defined(__CYGWIN__)
510   if ( (NULL != plugin->fn) &&
511        (0 != UNLINK (plugin->fn)) )
512     LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "unlink", plugin->fn);
513   GNUNET_free_non_null (plugin->fn);
514 #endif
515   result = sqlite3_close (plugin->dbh);
516 #if SQLITE_VERSION_NUMBER >= 3007000
517   if (result == SQLITE_BUSY)
518   {
519     LOG (GNUNET_ERROR_TYPE_WARNING,
520          _
521          ("Tried to close sqlite without finalizing all prepared statements.\n"));
522     stmt = sqlite3_next_stmt (plugin->dbh, NULL);
523     while (stmt != NULL)
524     {
525       LOG (GNUNET_ERROR_TYPE_DEBUG, "Closing statement %p\n", stmt);
526       result = sqlite3_finalize (stmt);
527       if (result != SQLITE_OK)
528         LOG (GNUNET_ERROR_TYPE_WARNING, _("Failed to close statement %p: %d\n"),
529              stmt, result);
530       stmt = sqlite3_next_stmt (plugin->dbh, NULL);
531     }
532     result = sqlite3_close (plugin->dbh);
533   }
534 #endif
535   if (SQLITE_OK != result)
536     LOG_SQLITE (plugin->dbh, GNUNET_ERROR_TYPE_ERROR, "sqlite3_close");
537
538 #if WINDOWS && !defined(__CYGWIN__)
539   if ( (NULL != plugin->fn) &&
540        (0 != UNLINK (plugin->fn)) )
541     LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "unlink", plugin->fn);
542   GNUNET_free_non_null (plugin->fn);
543 #endif
544   GNUNET_free (plugin);
545   GNUNET_free (api);
546   return NULL;
547 }
548
549
550
551 /* end of plugin_datacache_sqlite.c */