1e313c686628b530c3a37454741bda118151ea7c
[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  * Obtain a random key-value pair from the datacache.
416  *
417  * @param cls closure (our `struct Plugin`)
418  * @param iter maybe NULL (to just count)
419  * @param iter_cls closure for @a iter
420  * @return the number of results found, zero (datacache empty) or one
421  */
422 static unsigned int
423 sqlite_plugin_get_random (void *cls,
424                           GNUNET_DATACACHE_Iterator iter,
425                           void *iter_cls)
426 {
427   struct Plugin *plugin = cls;
428   sqlite3_stmt *stmt;
429   struct GNUNET_TIME_Absolute exp;
430   unsigned int size;
431   const char *dat;
432   unsigned int off;
433   unsigned int psize;
434   unsigned int type;
435   char scratch[256];
436   int64_t ntime;
437   const struct GNUNET_PeerIdentity *path;
438   const struct GNUNET_HashCode *key;
439
440   if (0 == plugin->num_items)
441     return 0;
442   off = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_NONCE,
443                                   plugin->num_items);
444   GNUNET_snprintf (scratch,
445                    sizeof (scratch),
446                    "SELECT value,expire,path,key,type FROM ds090 ORDER BY key LIMIT 1 OFFSET %u",
447                    off);
448   if (SQLITE_OK !=
449       sq_prepare (plugin->dbh, scratch, &stmt))
450   {
451     LOG_SQLITE (plugin->dbh,
452                 GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
453                 "sq_prepare");
454     return 0;
455   }
456   if (SQLITE_ROW != sqlite3_step (stmt))
457   {
458     GNUNET_break (0);
459     sqlite3_finalize (stmt);
460     return 0;
461   }
462   size = sqlite3_column_bytes (stmt, 0);
463   dat = sqlite3_column_blob (stmt, 0);
464   exp.abs_value_us = sqlite3_column_int64 (stmt, 1);
465   psize = sqlite3_column_bytes (stmt, 2);
466   if (0 != psize % sizeof (struct GNUNET_PeerIdentity))
467   {
468     GNUNET_break (0);
469     psize = 0;
470   }
471   psize /= sizeof (struct GNUNET_PeerIdentity);
472   if (0 != psize)
473     path = sqlite3_column_blob (stmt, 2);
474   else
475     path = NULL;
476
477   GNUNET_assert (sizeof (struct GNUNET_HashCode) ==
478                  sqlite3_column_bytes (stmt, 3));
479   key = sqlite3_column_blob (stmt, 3);
480   type = sqlite3_column_int (stmt, 4);
481
482   ntime = (int64_t) exp.abs_value_us;
483   if (ntime == INT64_MAX)
484     exp = GNUNET_TIME_UNIT_FOREVER_ABS;
485   LOG (GNUNET_ERROR_TYPE_DEBUG,
486        "Found %u-byte result with key %s when processing GET-RANDOM\n",
487        (unsigned int) size,
488        GNUNET_h2s (key));
489   (void) iter (iter_cls,
490                key,
491                size,
492                dat,
493                type,
494                exp,
495                psize,
496                path);
497   sqlite3_finalize (stmt);
498   return 1;
499 }
500
501
502 /**
503  * Entry point for the plugin.
504  *
505  * @param cls closure (the `struct GNUNET_DATACACHE_PluginEnvironment`)
506  * @return the plugin's closure (our `struct Plugin`)
507  */
508 void *
509 libgnunet_plugin_datacache_sqlite_init (void *cls)
510 {
511   struct GNUNET_DATACACHE_PluginEnvironment *env = cls;
512   struct GNUNET_DATACACHE_PluginFunctions *api;
513   struct Plugin *plugin;
514   char *fn;
515   char *fn_utf8;
516   sqlite3 *dbh;
517   char *emsg;
518
519   if (GNUNET_YES ==
520       GNUNET_CONFIGURATION_get_value_yesno (env->cfg,
521                                             "datacache-sqlite",
522                                             "IN_MEMORY"))
523   {
524     if (SQLITE_OK != sqlite3_open (":memory:", &dbh))
525       return NULL;
526     fn_utf8 = NULL;
527   }
528   else
529   {
530     fn = GNUNET_DISK_mktemp ("gnunet-datacache");
531     if (fn == NULL)
532       {
533         GNUNET_break (0);
534         return NULL;
535       }
536     /* fn should be UTF-8-encoded. If it isn't, it's a bug. */
537     fn_utf8 = GNUNET_strdup (fn);
538     if (SQLITE_OK != sqlite3_open (fn_utf8, &dbh))
539     {
540       GNUNET_free (fn);
541       GNUNET_free (fn_utf8);
542       return NULL;
543     }
544     GNUNET_free (fn);
545   }
546
547   SQLITE3_EXEC (dbh, "PRAGMA temp_store=MEMORY");
548   SQLITE3_EXEC (dbh, "PRAGMA locking_mode=EXCLUSIVE");
549   SQLITE3_EXEC (dbh, "PRAGMA journal_mode=OFF");
550   SQLITE3_EXEC (dbh, "PRAGMA synchronous=OFF");
551   SQLITE3_EXEC (dbh, "PRAGMA page_size=4092");
552   if (GNUNET_YES ==
553       GNUNET_CONFIGURATION_get_value_yesno (env->cfg,
554                                             "datacache-sqlite",
555                                             "IN_MEMORY"))
556     SQLITE3_EXEC (dbh, "PRAGMA sqlite_temp_store=3");
557
558   SQLITE3_EXEC (dbh,
559                 "CREATE TABLE ds090 (" "  type INTEGER NOT NULL DEFAULT 0,"
560                 "  expire INTEGER NOT NULL DEFAULT 0,"
561                 "  key BLOB NOT NULL DEFAULT '',"
562                 "  value BLOB NOT NULL DEFAULT '',"
563                 "  path BLOB DEFAULT '')");
564   SQLITE3_EXEC (dbh, "CREATE INDEX idx_hashidx ON ds090 (key,type,expire)");
565   SQLITE3_EXEC (dbh, "CREATE INDEX idx_expire ON ds090 (expire)");
566   plugin = GNUNET_new (struct Plugin);
567   plugin->env = env;
568   plugin->dbh = dbh;
569   plugin->fn = fn_utf8;
570   api = GNUNET_new (struct GNUNET_DATACACHE_PluginFunctions);
571   api->cls = plugin;
572   api->get = &sqlite_plugin_get;
573   api->put = &sqlite_plugin_put;
574   api->del = &sqlite_plugin_del;
575   api->get_random = &sqlite_plugin_get_random;
576   LOG (GNUNET_ERROR_TYPE_INFO,
577        "Sqlite datacache running\n");
578   return api;
579 }
580
581
582 /**
583  * Exit point from the plugin.
584  *
585  * @param cls closure (our `struct Plugin`)
586  * @return NULL
587  */
588 void *
589 libgnunet_plugin_datacache_sqlite_done (void *cls)
590 {
591   struct GNUNET_DATACACHE_PluginFunctions *api = cls;
592   struct Plugin *plugin = api->cls;
593   int result;
594
595 #if SQLITE_VERSION_NUMBER >= 3007000
596   sqlite3_stmt *stmt;
597 #endif
598
599 #if !WINDOWS || defined(__CYGWIN__)
600   if ( (NULL != plugin->fn) &&
601        (0 != UNLINK (plugin->fn)) )
602     LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "unlink", plugin->fn);
603   GNUNET_free_non_null (plugin->fn);
604 #endif
605   result = sqlite3_close (plugin->dbh);
606 #if SQLITE_VERSION_NUMBER >= 3007000
607   if (result == SQLITE_BUSY)
608   {
609     LOG (GNUNET_ERROR_TYPE_WARNING,
610          _
611          ("Tried to close sqlite without finalizing all prepared statements.\n"));
612     stmt = sqlite3_next_stmt (plugin->dbh, NULL);
613     while (stmt != NULL)
614     {
615       LOG (GNUNET_ERROR_TYPE_DEBUG, "Closing statement %p\n", stmt);
616       result = sqlite3_finalize (stmt);
617       if (result != SQLITE_OK)
618         LOG (GNUNET_ERROR_TYPE_WARNING, _("Failed to close statement %p: %d\n"),
619              stmt, result);
620       stmt = sqlite3_next_stmt (plugin->dbh, NULL);
621     }
622     result = sqlite3_close (plugin->dbh);
623   }
624 #endif
625   if (SQLITE_OK != result)
626     LOG_SQLITE (plugin->dbh, GNUNET_ERROR_TYPE_ERROR, "sqlite3_close");
627
628 #if WINDOWS && !defined(__CYGWIN__)
629   if ( (NULL != plugin->fn) &&
630        (0 != UNLINK (plugin->fn)) )
631     LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "unlink", plugin->fn);
632   GNUNET_free_non_null (plugin->fn);
633 #endif
634   GNUNET_free (plugin);
635   GNUNET_free (api);
636   return NULL;
637 }
638
639
640
641 /* end of plugin_datacache_sqlite.c */