44799be637e965afeaa056e34a7226cca1a17c78
[oweals/gnunet.git] / src / datacache / plugin_datacache_sqlite.c
1 /*
2      This file is part of GNUnet
3      (C) 2006, 2009 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 DEBUG_DATACACHE_SQLITE GNUNET_EXTRA_LOGGING
32
33 #define LOG(kind,...) GNUNET_log_from (kind, "datacache-sqlite", __VA_ARGS__)
34
35 #define LOG_STRERROR_FILE(kind,op,fn) 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(GNUNET_HashCode) + 32)
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
66 /**
67  * Log an error message at log-level 'level' that indicates
68  * a failure of the command 'cmd' on file 'filename'
69  * with the message given by strerror(errno).
70  */
71 #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)
72
73
74 #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)
75
76
77 /**
78  * @brief Prepare a SQL statement
79  */
80 static int
81 sq_prepare (sqlite3 * dbh, const char *zSql,    /* SQL statement, UTF-8 encoded */
82             sqlite3_stmt ** ppStmt)
83 {                               /* OUT: Statement handle */
84   char *dummy;
85
86   return sqlite3_prepare (dbh, zSql, strlen (zSql), ppStmt,
87                           (const char **) &dummy);
88 }
89
90
91 /**
92  * Store an item in the datastore.
93  *
94  * @param cls closure (our "struct Plugin")
95  * @param key key to store data under
96  * @param size number of bytes in data
97  * @param data data to store
98  * @param type type of the value
99  * @param discard_time when to discard the value in any case
100  * @return 0 on error, number of bytes used otherwise
101  */
102 static size_t
103 sqlite_plugin_put (void *cls, const GNUNET_HashCode * key, size_t size,
104                    const char *data, enum GNUNET_BLOCK_Type type,
105                    struct GNUNET_TIME_Absolute discard_time)
106 {
107   struct Plugin *plugin = cls;
108   sqlite3_stmt *stmt;
109   int64_t dval;
110
111 #if DEBUG_DATACACHE_SQLITE
112   LOG (GNUNET_ERROR_TYPE_DEBUG,
113        "Processing `%s' of %u bytes with key `%4s' and expiration %llums\n",
114        "PUT", (unsigned int) size, GNUNET_h2s (key),
115        (unsigned long long)
116        GNUNET_TIME_absolute_get_remaining (discard_time).rel_value);
117 #endif
118   dval = (int64_t) discard_time.abs_value;
119   if (dval < 0)
120     dval = INT64_MAX;
121   if (sq_prepare
122       (plugin->dbh,
123        "INSERT INTO ds090 (type, expire, key, value) VALUES (?, ?, ?, ?)",
124        &stmt) != SQLITE_OK)
125   {
126     LOG_SQLITE (plugin->dbh,
127                 GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
128                 "sq_prepare");
129     return 0;
130   }
131   if ((SQLITE_OK != sqlite3_bind_int (stmt, 1, type)) ||
132       (SQLITE_OK != sqlite3_bind_int64 (stmt, 2, dval)) ||
133       (SQLITE_OK !=
134        sqlite3_bind_blob (stmt, 3, key, sizeof (GNUNET_HashCode),
135                           SQLITE_TRANSIENT)) ||
136       (SQLITE_OK != sqlite3_bind_blob (stmt, 4, data, size, SQLITE_TRANSIENT)))
137   {
138     LOG_SQLITE (plugin->dbh, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
139                 "sqlite3_bind_xxx");
140     sqlite3_finalize (stmt);
141     return 0;
142   }
143   if (SQLITE_DONE != sqlite3_step (stmt))
144   {
145     LOG_SQLITE (plugin->dbh, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
146                 "sqlite3_step");
147     sqlite3_finalize (stmt);
148     return 0;
149   }
150   if (SQLITE_OK != sqlite3_finalize (stmt))
151     LOG_SQLITE (plugin->dbh, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
152                 "sqlite3_finalize");
153   return size + OVERHEAD;
154 }
155
156
157 /**
158  * Iterate over the results for a particular key
159  * in the datastore.
160  *
161  * @param cls closure (our "struct Plugin")
162  * @param key
163  * @param type entries of which type are relevant?
164  * @param iter maybe NULL (to just count)
165  * @param iter_cls closure for iter
166  * @return the number of results found
167  */
168 static unsigned int
169 sqlite_plugin_get (void *cls, const GNUNET_HashCode * key,
170                    enum GNUNET_BLOCK_Type type, GNUNET_DATACACHE_Iterator iter,
171                    void *iter_cls)
172 {
173   struct Plugin *plugin = cls;
174   sqlite3_stmt *stmt;
175   struct GNUNET_TIME_Absolute now;
176   struct GNUNET_TIME_Absolute exp;
177   unsigned int size;
178   const char *dat;
179   unsigned int cnt;
180   unsigned int off;
181   unsigned int total;
182   char scratch[256];
183   int64_t ntime;
184
185   now = GNUNET_TIME_absolute_get ();
186 #if DEBUG_DATACACHE_SQLITE
187   LOG (GNUNET_ERROR_TYPE_DEBUG, "Processing `%s' for key `%4s'\n", "GET",
188        GNUNET_h2s (key));
189 #endif
190   if (sq_prepare
191       (plugin->dbh,
192        "SELECT count(*) FROM ds090 WHERE key=? AND type=? AND expire >= ?",
193        &stmt) != SQLITE_OK)
194   {
195     LOG_SQLITE (plugin->dbh,
196                 GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
197                 "sq_prepare");
198     return 0;
199   }
200   ntime = (int64_t) now.abs_value;
201   GNUNET_assert (ntime >= 0);
202   if ((SQLITE_OK !=
203        sqlite3_bind_blob (stmt, 1, key, sizeof (GNUNET_HashCode),
204                           SQLITE_TRANSIENT)) ||
205       (SQLITE_OK != sqlite3_bind_int (stmt, 2, type)) ||
206       (SQLITE_OK != sqlite3_bind_int64 (stmt, 3, now.abs_value)))
207   {
208     LOG_SQLITE (plugin->dbh, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
209                 "sqlite3_bind_xxx");
210     sqlite3_finalize (stmt);
211     return 0;
212   }
213
214   if (SQLITE_ROW != sqlite3_step (stmt))
215   {
216     LOG_SQLITE (plugin->dbh, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
217                 "sqlite_step");
218     sqlite3_finalize (stmt);
219 #if DEBUG_DATACACHE_SQLITE
220     LOG (GNUNET_ERROR_TYPE_DEBUG,
221          "No content found when processing `%s' for key `%4s'\n",
222          "GET", GNUNET_h2s (key));
223 #endif
224     return 0;
225   }
226   total = sqlite3_column_int (stmt, 0);
227   sqlite3_finalize (stmt);
228   if ((total == 0) || (iter == NULL))
229   {
230 #if DEBUG_DATACACHE_SQLITE
231     if (0 == total)
232       LOG (GNUNET_ERROR_TYPE_DEBUG,
233            "No content found when processing `%s' for key `%4s'\n",
234            "GET", GNUNET_h2s (key));
235 #endif
236     return total;
237   }
238
239   cnt = 0;
240   off = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, total);
241   while (cnt < total)
242   {
243     off = (off + 1) % total;
244     GNUNET_snprintf (scratch, sizeof (scratch),
245                      "SELECT value,expire FROM ds090 WHERE key=? AND type=? AND expire >= ? LIMIT 1 OFFSET %u",
246                      off);
247     if (sq_prepare (plugin->dbh, scratch, &stmt) != SQLITE_OK)
248     {
249       LOG_SQLITE (plugin->dbh,
250                   GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
251                   "sq_prepare");
252       return cnt;
253     }
254     if ((SQLITE_OK !=
255          sqlite3_bind_blob (stmt, 1, key, sizeof (GNUNET_HashCode),
256                             SQLITE_TRANSIENT)) ||
257         (SQLITE_OK != sqlite3_bind_int (stmt, 2, type)) ||
258         (SQLITE_OK != sqlite3_bind_int64 (stmt, 3, now.abs_value)))
259     {
260       LOG_SQLITE (plugin->dbh, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
261                   "sqlite3_bind_xxx");
262       sqlite3_finalize (stmt);
263       return cnt;
264     }
265     if (sqlite3_step (stmt) != SQLITE_ROW)
266       break;
267     size = sqlite3_column_bytes (stmt, 0);
268     dat = sqlite3_column_blob (stmt, 0);
269     exp.abs_value = sqlite3_column_int64 (stmt, 1);
270     ntime = (int64_t) exp.abs_value;
271     if (ntime == INT64_MAX)
272       exp = GNUNET_TIME_UNIT_FOREVER_ABS;
273     cnt++;
274 #if DEBUG_DATACACHE_SQLITE
275     LOG (GNUNET_ERROR_TYPE_DEBUG,
276          "Found %u-byte result when processing `%s' for key `%4s'\n",
277          (unsigned int) size,
278          "GET", 
279          GNUNET_h2s (key));
280 #endif
281     if (GNUNET_OK != iter (iter_cls, exp, key, size, dat, type))
282     {
283       sqlite3_finalize (stmt);
284       break;
285     }
286     sqlite3_finalize (stmt);
287   }
288   return cnt;
289 }
290
291
292 /**
293  * Delete the entry with the lowest expiration value
294  * from the datacache right now.
295  *
296  * @param cls closure (our "struct Plugin")
297  * @return GNUNET_OK on success, GNUNET_SYSERR on error
298  */
299 static int
300 sqlite_plugin_del (void *cls)
301 {
302   struct Plugin *plugin = cls;
303   unsigned long long rowid;
304   unsigned int dsize;
305   sqlite3_stmt *stmt;
306   sqlite3_stmt *dstmt;
307   GNUNET_HashCode hc;
308
309 #if DEBUG_DATACACHE_SQLITE
310   LOG (GNUNET_ERROR_TYPE_DEBUG, "Processing `%s'\n", "DEL");
311 #endif
312   stmt = NULL;
313   dstmt = NULL;
314   if (sq_prepare
315       (plugin->dbh,
316        "SELECT _ROWID_,key,value FROM ds090 ORDER BY expire ASC LIMIT 1",
317        &stmt) != SQLITE_OK)
318   {
319     LOG_SQLITE (plugin->dbh,
320                 GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
321                 "sq_prepare");
322     if (stmt != NULL)
323       (void) sqlite3_finalize (stmt);
324     return GNUNET_SYSERR;
325   }
326   if (SQLITE_ROW != sqlite3_step (stmt))
327   {
328     LOG_SQLITE (plugin->dbh,
329                 GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
330                 "sqlite3_step");
331     (void) sqlite3_finalize (stmt);
332     return GNUNET_SYSERR;
333   }
334   rowid = sqlite3_column_int64 (stmt, 0);
335   GNUNET_assert (sqlite3_column_bytes (stmt, 1) == sizeof (GNUNET_HashCode));
336   memcpy (&hc, sqlite3_column_blob (stmt, 1), sizeof (GNUNET_HashCode));
337   dsize = sqlite3_column_bytes (stmt, 2);
338   if (SQLITE_OK != sqlite3_finalize (stmt))
339     LOG_SQLITE (plugin->dbh,
340                 GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
341                 "sqlite3_step");
342   if (sq_prepare
343       (plugin->dbh, "DELETE FROM ds090 WHERE _ROWID_=?",
344        &dstmt) != SQLITE_OK)
345   {
346     LOG_SQLITE (plugin->dbh,
347                 GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
348                 "sq_prepare");
349     if (stmt != NULL)
350       (void) sqlite3_finalize (stmt);
351     return GNUNET_SYSERR;
352   }
353   if (SQLITE_OK !=
354       sqlite3_bind_int64 (dstmt, 1, rowid))
355   {
356     LOG_SQLITE (plugin->dbh,
357                 GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
358                 "sqlite3_bind");
359     (void) sqlite3_finalize (dstmt);
360     return GNUNET_SYSERR;
361   }
362   if (sqlite3_step (dstmt) != SQLITE_DONE)
363   {
364     LOG_SQLITE (plugin->dbh,
365                 GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
366                 "sqlite3_step");
367     (void) sqlite3_finalize (dstmt);
368     return GNUNET_SYSERR;
369   }
370   plugin->env->delete_notify (plugin->env->cls, &hc, dsize + OVERHEAD);
371   if (SQLITE_OK != sqlite3_finalize (dstmt))
372     LOG_SQLITE (plugin->dbh,
373                 GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
374                 "sqlite3_finalize");
375   return GNUNET_OK;
376 }
377
378
379 /**
380  * Entry point for the plugin.
381  *
382  * @param cls closure (the "struct GNUNET_DATACACHE_PluginEnvironmnet")
383  * @return the plugin's closure (our "struct Plugin")
384  */
385 void *
386 libgnunet_plugin_datacache_sqlite_init (void *cls)
387 {
388   struct GNUNET_DATACACHE_PluginEnvironment *env = cls;
389   struct GNUNET_DATACACHE_PluginFunctions *api;
390   struct Plugin *plugin;
391   char *fn;
392   char *fn_utf8;
393   sqlite3 *dbh;
394   char *emsg;
395
396   fn = GNUNET_DISK_mktemp ("gnunet-datacache");
397   if (fn == NULL)
398   {
399     GNUNET_break (0);
400     return NULL;
401   }
402 #ifdef ENABLE_NLS
403   fn_utf8 = GNUNET_STRINGS_to_utf8 (fn, strlen (fn), nl_langinfo (CODESET));
404 #else
405   /* good luck */
406   fn_utf8 = GNUNET_STRINGS_to_utf8 (fn, strlen (fn), "UTF-8");
407 #endif
408   if (SQLITE_OK != sqlite3_open (fn_utf8, &dbh))
409   {
410     GNUNET_free (fn);
411     GNUNET_free (fn_utf8);
412     return NULL;
413   }
414   GNUNET_free (fn);
415
416   SQLITE3_EXEC (dbh, "PRAGMA temp_store=MEMORY");
417   SQLITE3_EXEC (dbh, "PRAGMA locking_mode=EXCLUSIVE");
418   SQLITE3_EXEC (dbh, "PRAGMA journal_mode=OFF");
419   SQLITE3_EXEC (dbh, "PRAGMA synchronous=OFF");
420   SQLITE3_EXEC (dbh, "PRAGMA count_changes=OFF");
421   SQLITE3_EXEC (dbh, "PRAGMA page_size=4092");
422   SQLITE3_EXEC (dbh,
423                 "CREATE TABLE ds090 (" "  type INTEGER NOT NULL DEFAULT 0,"
424                 "  expire INTEGER NOT NULL DEFAULT 0,"
425                 "  key BLOB NOT NULL DEFAULT '',"
426                 "  value BLOB NOT NULL DEFAULT '')");
427   SQLITE3_EXEC (dbh, "CREATE INDEX idx_hashidx ON ds090 (key,type,expire)");
428   SQLITE3_EXEC (dbh, "CREATE INDEX idx_expire ON ds090 (expire)");
429   plugin = GNUNET_malloc (sizeof (struct Plugin));
430   plugin->env = env;
431   plugin->dbh = dbh;
432   plugin->fn = fn_utf8;
433   api = GNUNET_malloc (sizeof (struct GNUNET_DATACACHE_PluginFunctions));
434   api->cls = plugin;
435   api->get = &sqlite_plugin_get;
436   api->put = &sqlite_plugin_put;
437   api->del = &sqlite_plugin_del;
438   LOG (GNUNET_ERROR_TYPE_INFO, 
439        _("Sqlite datacache running\n"));
440   return api;
441 }
442
443
444 /**
445  * Exit point from the plugin.
446  *
447  * @param cls closure (our "struct Plugin")
448  * @return NULL
449  */
450 void *
451 libgnunet_plugin_datacache_sqlite_done (void *cls)
452 {
453   struct GNUNET_DATACACHE_PluginFunctions *api = cls;
454   struct Plugin *plugin = api->cls;
455   int result;
456
457 #if SQLITE_VERSION_NUMBER >= 3007000
458   sqlite3_stmt *stmt;
459 #endif
460
461 #if !WINDOWS || defined(__CYGWIN__)
462   if (0 != UNLINK (plugin->fn))
463     LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "unlink", plugin->fn);
464   GNUNET_free (plugin->fn);
465 #endif
466   result = sqlite3_close (plugin->dbh);
467 #if SQLITE_VERSION_NUMBER >= 3007000
468   if (result == SQLITE_BUSY)
469   {
470     LOG (GNUNET_ERROR_TYPE_WARNING,
471          _("Tried to close sqlite without finalizing all prepared statements.\n"));
472     stmt = sqlite3_next_stmt (plugin->dbh, NULL);
473     while (stmt != NULL)
474     {
475 #if DEBUG_SQLITE
476       LOG (GNUNET_ERROR_TYPE_DEBUG,
477            "Closing statement %p\n", stmt);
478 #endif
479       result = sqlite3_finalize (stmt);
480 #if DEBUG_SQLITE
481       if (result != SQLITE_OK)
482         LOG (GNUNET_ERROR_TYPE_DEBUG, 
483              "Failed to close statement %p: %d\n", stmt, result);
484 #endif
485       stmt = sqlite3_next_stmt (plugin->dbh, NULL);
486     }
487     result = sqlite3_close (plugin->dbh);
488   }
489 #endif
490   if (SQLITE_OK != result)
491     LOG_SQLITE (plugin->dbh, GNUNET_ERROR_TYPE_ERROR, "sqlite3_close");
492
493 #if WINDOWS && !defined(__CYGWIN__)
494   if (0 != UNLINK (plugin->fn))
495     LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "unlink", plugin->fn);
496   GNUNET_free (plugin->fn);
497 #endif
498   GNUNET_free (plugin);
499   GNUNET_free (api);
500   return NULL;
501 }
502
503
504
505 /* end of plugin_datacache_sqlite.c */