-fixing
[oweals/gnunet.git] / src / datacache / plugin_datacache_sqlite.c
index 5493dbb905916df3cc90b5ba0e37edd1a832c642..62d5445b410b66d28216ae21ba3b2ce4034a68db 100644 (file)
@@ -4,7 +4,7 @@
 
      GNUnet is free software; you can redistribute it and/or modify
      it under the terms of the GNU General Public License as published
 
      GNUnet is free software; you can redistribute it and/or modify
      it under the terms of the GNU General Public License as published
-     by the Free Software Foundation; either version 2, or (at your
+     by the Free Software Foundation; either version 3, or (at your
      option) any later version.
 
      GNUnet is distributed in the hope that it will be useful, but
      option) any later version.
 
      GNUnet is distributed in the hope that it will be useful, but
  */
 #include "platform.h"
 #include "gnunet_util_lib.h"
  */
 #include "platform.h"
 #include "gnunet_util_lib.h"
-#include "plugin_datacache.h"
+#include "gnunet_datacache_plugin.h"
 #include <sqlite3.h>
 
 #include <sqlite3.h>
 
-#define DEBUG_DATACACHE_SQLITE GNUNET_NO
+#define LOG(kind,...) GNUNET_log_from (kind, "datacache-sqlite", __VA_ARGS__)
+
+#define LOG_STRERROR_FILE(kind,op,fn) GNUNET_log_from_strerror_file (kind, "datacache-sqlite", op, fn)
+
 
 /**
  * How much overhead do we assume per entry in the
  * datacache?
  */
 
 /**
  * How much overhead do we assume per entry in the
  * datacache?
  */
-#define OVERHEAD (sizeof(GNUNET_HashCode) + 32)
+#define OVERHEAD (sizeof(struct GNUNET_HashCode) + 32)
 
 /**
  * Context for all functions in this plugin.
  */
 
 /**
  * Context for all functions in this plugin.
  */
-struct Plugin 
+struct Plugin
 {
   /**
    * Our execution environment.
 {
   /**
    * Our execution environment.
@@ -53,7 +56,7 @@ struct Plugin
 
   /**
    * Filename used for the DB.
 
   /**
    * Filename used for the DB.
-   */ 
+   */
   char *fn;
 };
 
   char *fn;
 };
 
@@ -63,10 +66,10 @@ struct Plugin
  * a failure of the command 'cmd' on file 'filename'
  * with the message given by strerror(errno).
  */
  * a failure of the command 'cmd' on file 'filename'
  * with the message given by strerror(errno).
  */
-#define LOG_SQLITE(db, level, cmd) do { GNUNET_log(level, _("`%s' failed at %s:%d with error: %s\n"), cmd, __FILE__, __LINE__, sqlite3_errmsg(db)); } while(0)
+#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)
 
 
 
 
-#define SQLITE3_EXEC(db, cmd) do { emsg = NULL; if (SQLITE_OK != sqlite3_exec(db, cmd, NULL, NULL, &emsg)) { GNUNET_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)
+#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)
 
 
 /**
 
 
 /**
@@ -77,15 +80,16 @@ sq_prepare (sqlite3 * dbh, const char *zSql,    /* SQL statement, UTF-8 encoded
             sqlite3_stmt ** ppStmt)
 {                               /* OUT: Statement handle */
   char *dummy;
             sqlite3_stmt ** ppStmt)
 {                               /* OUT: Statement handle */
   char *dummy;
-  return sqlite3_prepare (dbh,
-                          zSql,
-                          strlen (zSql), ppStmt, (const char **) &dummy);
+
+  return sqlite3_prepare (dbh, zSql, strlen (zSql), ppStmt,
+                          (const char **) &dummy);
 }
 
 
 /**
  * Store an item in the datastore.
  *
 }
 
 
 /**
  * Store an item in the datastore.
  *
+ * @param cls closure (our "struct Plugin")
  * @param key key to store data under
  * @param size number of bytes in data
  * @param data data to store
  * @param key key to store data under
  * @param size number of bytes in data
  * @param data data to store
@@ -93,60 +97,53 @@ sq_prepare (sqlite3 * dbh, const char *zSql,    /* SQL statement, UTF-8 encoded
  * @param discard_time when to discard the value in any case
  * @return 0 on error, number of bytes used otherwise
  */
  * @param discard_time when to discard the value in any case
  * @return 0 on error, number of bytes used otherwise
  */
-static uint32_t 
-sqlite_plugin_put (void *cls,
-                  const GNUNET_HashCode * key,
-                  uint32_t size,
-                  const char *data,
-                  uint32_t type,
-                  struct GNUNET_TIME_Absolute discard_time)
+static size_t
+sqlite_plugin_put (void *cls, const struct GNUNET_HashCode * key, size_t size,
+                   const char *data, enum GNUNET_BLOCK_Type type,
+                   struct GNUNET_TIME_Absolute discard_time)
 {
   struct Plugin *plugin = cls;
   sqlite3_stmt *stmt;
 {
   struct Plugin *plugin = cls;
   sqlite3_stmt *stmt;
-
-#if DEBUG_DATACACHE_SQLITE
-  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-             "Processing `%s' of %u bytes with key `%4s' and expiration %llums\n",
-             "PUT",
-             (unsigned int) size,
-             GNUNET_h2s (key),
-             (unsigned long long) GNUNET_TIME_absolute_get_remaining (discard_time).value);
-#endif
-  if (sq_prepare (plugin->dbh,
-                  "INSERT INTO ds090 "
-                  "(type, expire, key, value) "
-                  "VALUES (?, ?, ?, ?)", &stmt) != SQLITE_OK)
-    {
-      GNUNET_log (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
-                 _("`%s' failed at %s:%d with error: %s\n"),
-                 "sq_prepare", __FILE__, __LINE__, 
-                 sqlite3_errmsg (plugin->dbh));
-      return 0;
-    }
-  if ( (SQLITE_OK != sqlite3_bind_int (stmt, 1, type)) ||
-       (SQLITE_OK != sqlite3_bind_int64 (stmt, 2, discard_time.value)) ||
-       (SQLITE_OK != sqlite3_bind_blob (stmt, 3, key, sizeof (GNUNET_HashCode),
-                                       SQLITE_TRANSIENT)) ||
-       (SQLITE_OK != sqlite3_bind_blob (stmt, 4, data, size, SQLITE_TRANSIENT)))
-    {
-      LOG_SQLITE (plugin->dbh,
-                  GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK, 
-                 "sqlite3_bind_xxx");
-      sqlite3_finalize (stmt);
-      return 0;
-    }
+  int64_t dval;
+
+  LOG (GNUNET_ERROR_TYPE_DEBUG,
+       "Processing `%s' of %u bytes with key `%4s' and expiration %s\n",
+       "PUT", (unsigned int) size, GNUNET_h2s (key),
+       GNUNET_STRINGS_relative_time_to_string (GNUNET_TIME_absolute_get_remaining (discard_time), GNUNET_YES));
+  dval = (int64_t) discard_time.abs_value;
+  if (dval < 0)
+    dval = INT64_MAX;
+  if (sq_prepare
+      (plugin->dbh,
+       "INSERT INTO ds090 (type, expire, key, value) VALUES (?, ?, ?, ?)",
+       &stmt) != SQLITE_OK)
+  {
+    LOG_SQLITE (plugin->dbh, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
+                "sq_prepare");
+    return 0;
+  }
+  if ((SQLITE_OK != sqlite3_bind_int (stmt, 1, type)) ||
+      (SQLITE_OK != sqlite3_bind_int64 (stmt, 2, dval)) ||
+      (SQLITE_OK !=
+       sqlite3_bind_blob (stmt, 3, key, sizeof (struct GNUNET_HashCode),
+                          SQLITE_TRANSIENT)) ||
+      (SQLITE_OK != sqlite3_bind_blob (stmt, 4, data, size, SQLITE_TRANSIENT)))
+  {
+    LOG_SQLITE (plugin->dbh, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
+                "sqlite3_bind_xxx");
+    sqlite3_finalize (stmt);
+    return 0;
+  }
   if (SQLITE_DONE != sqlite3_step (stmt))
   if (SQLITE_DONE != sqlite3_step (stmt))
-    {
-      LOG_SQLITE (plugin->dbh,
-                 GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK, 
-                 "sqlite3_step");
-      sqlite3_finalize (stmt);
-      return 0;
-    }
+  {
+    LOG_SQLITE (plugin->dbh, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
+                "sqlite3_step");
+    sqlite3_finalize (stmt);
+    return 0;
+  }
   if (SQLITE_OK != sqlite3_finalize (stmt))
   if (SQLITE_OK != sqlite3_finalize (stmt))
-    LOG_SQLITE (plugin->dbh,
-               GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK, 
-               "sqlite3_finalize");
+    LOG_SQLITE (plugin->dbh, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
+                "sqlite3_finalize");
   return size + OVERHEAD;
 }
 
   return size + OVERHEAD;
 }
 
@@ -155,17 +152,17 @@ sqlite_plugin_put (void *cls,
  * Iterate over the results for a particular key
  * in the datastore.
  *
  * Iterate over the results for a particular key
  * in the datastore.
  *
+ * @param cls closure (our "struct Plugin")
  * @param key
  * @param type entries of which type are relevant?
  * @param iter maybe NULL (to just count)
  * @param key
  * @param type entries of which type are relevant?
  * @param iter maybe NULL (to just count)
+ * @param iter_cls closure for iter
  * @return the number of results found
  */
  * @return the number of results found
  */
-static unsigned int 
-sqlite_plugin_get (void *cls,
-                  const GNUNET_HashCode * key,
-                  uint32_t type,
-                  GNUNET_DATACACHE_Iterator iter,
-                  void *iter_cls)
+static unsigned int
+sqlite_plugin_get (void *cls, const struct GNUNET_HashCode * key,
+                   enum GNUNET_BLOCK_Type type, GNUNET_DATACACHE_Iterator iter,
+                   void *iter_cls)
 {
   struct Plugin *plugin = cls;
   sqlite3_stmt *stmt;
 {
   struct Plugin *plugin = cls;
   sqlite3_stmt *stmt;
@@ -177,80 +174,99 @@ sqlite_plugin_get (void *cls,
   unsigned int off;
   unsigned int total;
   char scratch[256];
   unsigned int off;
   unsigned int total;
   char scratch[256];
+  int64_t ntime;
 
   now = GNUNET_TIME_absolute_get ();
 
   now = GNUNET_TIME_absolute_get ();
-#if DEBUG_DATACACHE_SQLITE
-  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-             "Processing `%s' for key `%4s'\n",
-             "GET",
-             GNUNET_h2s (key));
-#endif
-  if (sq_prepare (plugin->dbh,
-                  "SELECT count(*) FROM ds090 WHERE key=? AND type=? AND expire >= ?",
-                  &stmt) != SQLITE_OK)
-    {
-      GNUNET_log (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
-                 _("`%s' failed at %s:%d with error: %s\n"),
-                 "sq_prepare", __FILE__, __LINE__, 
-                 sqlite3_errmsg (plugin->dbh));
-      return 0;
-    }
-  sqlite3_bind_blob (stmt, 1, key, sizeof (GNUNET_HashCode),
-                     SQLITE_TRANSIENT);
-  sqlite3_bind_int (stmt, 2, type);
-  sqlite3_bind_int64 (stmt, 3, now.value);
+  LOG (GNUNET_ERROR_TYPE_DEBUG, "Processing `%s' for key `%4s'\n", "GET",
+       GNUNET_h2s (key));
+  if (sq_prepare
+      (plugin->dbh,
+       "SELECT count(*) FROM ds090 WHERE key=? AND type=? AND expire >= ?",
+       &stmt) != SQLITE_OK)
+  {
+    LOG_SQLITE (plugin->dbh, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
+                "sq_prepare");
+    return 0;
+  }
+  ntime = (int64_t) now.abs_value;
+  GNUNET_assert (ntime >= 0);
+  if ((SQLITE_OK !=
+       sqlite3_bind_blob (stmt, 1, key, sizeof (struct GNUNET_HashCode),
+                          SQLITE_TRANSIENT)) ||
+      (SQLITE_OK != sqlite3_bind_int (stmt, 2, type)) ||
+      (SQLITE_OK != sqlite3_bind_int64 (stmt, 3, now.abs_value)))
+  {
+    LOG_SQLITE (plugin->dbh, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
+                "sqlite3_bind_xxx");
+    sqlite3_finalize (stmt);
+    return 0;
+  }
+
   if (SQLITE_ROW != sqlite3_step (stmt))
   if (SQLITE_ROW != sqlite3_step (stmt))
-    {
-      LOG_SQLITE (plugin->dbh,
-                  GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK, 
-                 "sqlite_step");
-      sqlite3_finalize (stmt);
-      return 0;
-    }
+  {
+    LOG_SQLITE (plugin->dbh, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
+                "sqlite_step");
+    sqlite3_finalize (stmt);
+    LOG (GNUNET_ERROR_TYPE_DEBUG,
+         "No content found when processing `%s' for key `%4s'\n", "GET",
+         GNUNET_h2s (key));
+    return 0;
+  }
   total = sqlite3_column_int (stmt, 0);
   sqlite3_finalize (stmt);
   total = sqlite3_column_int (stmt, 0);
   sqlite3_finalize (stmt);
-  if ( (total == 0) || (iter == NULL) )
-    return total;    
+  if ((total == 0) || (iter == NULL))
+  {
+    if (0 == total)
+      LOG (GNUNET_ERROR_TYPE_DEBUG,
+           "No content found when processing `%s' for key `%4s'\n", "GET",
+           GNUNET_h2s (key));
+    return total;
+  }
 
   cnt = 0;
   off = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, total);
   while (cnt < total)
 
   cnt = 0;
   off = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, total);
   while (cnt < total)
+  {
+    off = (off + 1) % total;
+    GNUNET_snprintf (scratch, sizeof (scratch),
+                     "SELECT value,expire FROM ds090 WHERE key=? AND type=? AND expire >= ? LIMIT 1 OFFSET %u",
+                     off);
+    if (sq_prepare (plugin->dbh, scratch, &stmt) != SQLITE_OK)
+    {
+      LOG_SQLITE (plugin->dbh, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
+                  "sq_prepare");
+      return cnt;
+    }
+    if ((SQLITE_OK !=
+         sqlite3_bind_blob (stmt, 1, key, sizeof (struct GNUNET_HashCode),
+                            SQLITE_TRANSIENT)) ||
+        (SQLITE_OK != sqlite3_bind_int (stmt, 2, type)) ||
+        (SQLITE_OK != sqlite3_bind_int64 (stmt, 3, now.abs_value)))
     {
     {
-      off = (off + 1) % total;
-      GNUNET_snprintf (scratch, 
-                      sizeof(scratch),
-                       "SELECT value,expire FROM ds090 WHERE key=? AND type=? AND expire >= ? LIMIT 1 OFFSET %u",
-                       off);
-      if (sq_prepare (plugin->dbh, scratch, &stmt) != SQLITE_OK)
-        {
-          GNUNET_log (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
-                     _("`%s' failed at %s:%d with error: %s\n"),
-                     "sq_prepare", __FILE__, __LINE__,
-                     sqlite3_errmsg (plugin->dbh));
-          return cnt;
-        }
-      sqlite3_bind_blob (stmt, 1, key, sizeof (GNUNET_HashCode),
-                         SQLITE_TRANSIENT);
-      sqlite3_bind_int (stmt, 2, type);
-      sqlite3_bind_int64 (stmt, 3, now.value);
-      if (sqlite3_step (stmt) != SQLITE_ROW)
-        break;
-      size = sqlite3_column_bytes (stmt, 0);
-      dat = sqlite3_column_blob (stmt, 0);
-      exp.value = sqlite3_column_int64 (stmt, 1);
-      cnt++;
-      if (GNUNET_OK != iter (iter_cls,
-                            exp,
-                            key, 
-                            size,
-                            dat,
-                            type))
-        {
-          sqlite3_finalize (stmt);
-          break;
-        }
+      LOG_SQLITE (plugin->dbh, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
+                  "sqlite3_bind_xxx");
       sqlite3_finalize (stmt);
       sqlite3_finalize (stmt);
+      return cnt;
     }
     }
+    if (sqlite3_step (stmt) != SQLITE_ROW)
+      break;
+    size = sqlite3_column_bytes (stmt, 0);
+    dat = sqlite3_column_blob (stmt, 0);
+    exp.abs_value = sqlite3_column_int64 (stmt, 1);
+    ntime = (int64_t) exp.abs_value;
+    if (ntime == INT64_MAX)
+      exp = GNUNET_TIME_UNIT_FOREVER_ABS;
+    cnt++;
+    LOG (GNUNET_ERROR_TYPE_DEBUG,
+         "Found %u-byte result when processing `%s' for key `%4s'\n",
+         (unsigned int) size, "GET", GNUNET_h2s (key));
+    if (GNUNET_OK != iter (iter_cls, exp, key, size, dat, type))
+    {
+      sqlite3_finalize (stmt);
+      break;
+    }
+    sqlite3_finalize (stmt);
+  }
   return cnt;
 }
 
   return cnt;
 }
 
@@ -258,85 +274,84 @@ sqlite_plugin_get (void *cls,
 /**
  * Delete the entry with the lowest expiration value
  * from the datacache right now.
 /**
  * Delete the entry with the lowest expiration value
  * from the datacache right now.
- * 
+ *
+ * @param cls closure (our "struct Plugin")
  * @return GNUNET_OK on success, GNUNET_SYSERR on error
  * @return GNUNET_OK on success, GNUNET_SYSERR on error
- */ 
-static int 
+ */
+static int
 sqlite_plugin_del (void *cls)
 {
   struct Plugin *plugin = cls;
 sqlite_plugin_del (void *cls)
 {
   struct Plugin *plugin = cls;
+  unsigned long long rowid;
   unsigned int dsize;
   unsigned int dsize;
-  unsigned int dtype;
   sqlite3_stmt *stmt;
   sqlite3_stmt *dstmt;
   sqlite3_stmt *stmt;
   sqlite3_stmt *dstmt;
+  struct GNUNET_HashCode hc;
 
 
-#if DEBUG_DATACACHE_SQLITE
-  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-             "Processing `%s'\n",
-             "DEL");
-#endif
+  LOG (GNUNET_ERROR_TYPE_DEBUG, "Processing `%s'\n", "DEL");
   stmt = NULL;
   dstmt = NULL;
   stmt = NULL;
   dstmt = NULL;
-  if ((sq_prepare (plugin->dbh,
-                   "SELECT type, key, value FROM ds090 ORDER BY expire ASC LIMIT 1",
-                   &stmt) != SQLITE_OK) ||
-      (sq_prepare (plugin->dbh,
-                   "DELETE FROM ds090 "
-                   "WHERE key=? AND value=? AND type=?",
-                   &dstmt) != SQLITE_OK))
-    {
-      GNUNET_log (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
-                 _("`%s' failed at %s:%d with error: %s\n"),
-                 "sq_prepare", __FILE__, __LINE__, sqlite3_errmsg (plugin->dbh));
-      if (dstmt != NULL)
-        sqlite3_finalize (dstmt);
-      if (stmt != NULL)
-        sqlite3_finalize (stmt);
-      return GNUNET_SYSERR;
-    }
+  if (sq_prepare
+      (plugin->dbh,
+       "SELECT _ROWID_,key,value FROM ds090 ORDER BY expire ASC LIMIT 1",
+       &stmt) != SQLITE_OK)
+  {
+    LOG_SQLITE (plugin->dbh, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
+                "sq_prepare");
+    if (stmt != NULL)
+      (void) sqlite3_finalize (stmt);
+    return GNUNET_SYSERR;
+  }
   if (SQLITE_ROW != sqlite3_step (stmt))
   if (SQLITE_ROW != sqlite3_step (stmt))
-    {
-      GNUNET_log (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
-                 _("`%s' failed at %s:%d with error: %s\n"),
-                 "sqlite3_step", __FILE__, __LINE__,
-                 sqlite3_errmsg (plugin->dbh));
-      sqlite3_finalize (dstmt);
-      sqlite3_finalize (stmt);
-      return GNUNET_SYSERR;
-    }
-  dtype = sqlite3_column_int (stmt, 0);
-  GNUNET_break (sqlite3_column_bytes (stmt, 1) == sizeof (GNUNET_HashCode));
+  {
+    LOG_SQLITE (plugin->dbh, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
+                "sqlite3_step");
+    (void) sqlite3_finalize (stmt);
+    return GNUNET_SYSERR;
+  }
+  rowid = sqlite3_column_int64 (stmt, 0);
+  GNUNET_assert (sqlite3_column_bytes (stmt, 1) == sizeof (struct GNUNET_HashCode));
+  memcpy (&hc, sqlite3_column_blob (stmt, 1), sizeof (struct GNUNET_HashCode));
   dsize = sqlite3_column_bytes (stmt, 2);
   dsize = sqlite3_column_bytes (stmt, 2);
-  sqlite3_bind_blob (dstmt,
-                    1, sqlite3_column_blob (stmt, 1),
-                    sizeof (GNUNET_HashCode),
-                    SQLITE_TRANSIENT);
-  sqlite3_bind_blob (dstmt,
-                    2, sqlite3_column_blob (stmt, 2),
-                    dsize,
-                    SQLITE_TRANSIENT);
-  sqlite3_bind_int (dstmt, 3, dtype);
+  if (SQLITE_OK != sqlite3_finalize (stmt))
+    LOG_SQLITE (plugin->dbh, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
+                "sqlite3_step");
+  if (sq_prepare (plugin->dbh, "DELETE FROM ds090 WHERE _ROWID_=?", &dstmt) !=
+      SQLITE_OK)
+  {
+    LOG_SQLITE (plugin->dbh, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
+                "sq_prepare");
+    if (stmt != NULL)
+      (void) sqlite3_finalize (stmt);
+    return GNUNET_SYSERR;
+  }
+  if (SQLITE_OK != sqlite3_bind_int64 (dstmt, 1, rowid))
+  {
+    LOG_SQLITE (plugin->dbh, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
+                "sqlite3_bind");
+    (void) sqlite3_finalize (dstmt);
+    return GNUNET_SYSERR;
+  }
   if (sqlite3_step (dstmt) != SQLITE_DONE)
   if (sqlite3_step (dstmt) != SQLITE_DONE)
-    {
-      GNUNET_log (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
-                 _("`%s' failed at %s:%d with error: %s\n"),
-                 "sqlite3_step", __FILE__, __LINE__,
-                 sqlite3_errmsg (plugin->dbh));    
-      sqlite3_finalize (dstmt);
-      sqlite3_finalize (stmt);
-      return GNUNET_SYSERR;
-    }
-  plugin->env->delete_notify (plugin->env->cls,
-                             sqlite3_column_blob (stmt, 1),
-                             dsize + OVERHEAD);
-  sqlite3_finalize (dstmt);
-  sqlite3_finalize (stmt);
+  {
+    LOG_SQLITE (plugin->dbh, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
+                "sqlite3_step");
+    (void) sqlite3_finalize (dstmt);
+    return GNUNET_SYSERR;
+  }
+  plugin->env->delete_notify (plugin->env->cls, &hc, dsize + OVERHEAD);
+  if (SQLITE_OK != sqlite3_finalize (dstmt))
+    LOG_SQLITE (plugin->dbh, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
+                "sqlite3_finalize");
   return GNUNET_OK;
 }
 
 
 /**
  * Entry point for the plugin.
   return GNUNET_OK;
 }
 
 
 /**
  * Entry point for the plugin.
+ *
+ * @param cls closure (the "struct GNUNET_DATACACHE_PluginEnvironmnet")
+ * @return the plugin's closure (our "struct Plugin")
  */
 void *
 libgnunet_plugin_datacache_sqlite_init (void *cls)
  */
 void *
 libgnunet_plugin_datacache_sqlite_init (void *cls)
@@ -346,61 +361,38 @@ libgnunet_plugin_datacache_sqlite_init (void *cls)
   struct Plugin *plugin;
   char *fn;
   char *fn_utf8;
   struct Plugin *plugin;
   char *fn;
   char *fn_utf8;
-  int fd;
   sqlite3 *dbh;
   sqlite3 *dbh;
-  char *tmpl;
-  const char *tmpdir;
   char *emsg;
 
   char *emsg;
 
-  tmpdir = getenv ("TMPDIR");
-  tmpdir = tmpdir ? tmpdir : "/tmp";
-
-#define TEMPLATE "/gnunet-dstoreXXXXXX"
-  tmpl = GNUNET_malloc (strlen (tmpdir) + sizeof (TEMPLATE) + 1);
-  strcpy (tmpl, tmpdir);
-  strcat (tmpl, TEMPLATE);
-#undef TEMPLATE
-#ifdef MINGW
-  fn = (char *) GNUNET_malloc (MAX_PATH + 1);
-  plibc_conv_to_win_path (tmpl, fn);
-  GNUNET_free (tmpl);
-#else
-  fn = tmpl;
-#endif
-  fd = mkstemp (fn);
-  if (fd == -1)
-    {
-      GNUNET_break (0);
-      GNUNET_free (fn);
-      return NULL;
-    }
-  CLOSE (fd);
-  fn_utf8 = GNUNET_STRINGS_to_utf8 (fn, strlen (fn),
-#ifdef ENABLE_NLS
-                                   nl_langinfo (CODESET)
-#else
-                                   "UTF-8"      /* good luck */
-#endif
-    );
+  fn = GNUNET_DISK_mktemp ("gnunet-datacache");
+  if (fn == NULL)
+  {
+    GNUNET_break (0);
+    return NULL;
+  }
+  /* fn should be UTF-8-encoded. If it isn't, it's a bug. */
+  fn_utf8 = GNUNET_strdup (fn);
   if (SQLITE_OK != sqlite3_open (fn_utf8, &dbh))
   if (SQLITE_OK != sqlite3_open (fn_utf8, &dbh))
-    {
-      GNUNET_free (fn);
-      GNUNET_free (fn_utf8);
-      return NULL;
-    }
+  {
+    GNUNET_free (fn);
+    GNUNET_free (fn_utf8);
+    return NULL;
+  }
   GNUNET_free (fn);
 
   SQLITE3_EXEC (dbh, "PRAGMA temp_store=MEMORY");
   GNUNET_free (fn);
 
   SQLITE3_EXEC (dbh, "PRAGMA temp_store=MEMORY");
+  SQLITE3_EXEC (dbh, "PRAGMA locking_mode=EXCLUSIVE");
+  SQLITE3_EXEC (dbh, "PRAGMA journal_mode=OFF");
   SQLITE3_EXEC (dbh, "PRAGMA synchronous=OFF");
   SQLITE3_EXEC (dbh, "PRAGMA count_changes=OFF");
   SQLITE3_EXEC (dbh, "PRAGMA page_size=4092");
   SQLITE3_EXEC (dbh,
   SQLITE3_EXEC (dbh, "PRAGMA synchronous=OFF");
   SQLITE3_EXEC (dbh, "PRAGMA count_changes=OFF");
   SQLITE3_EXEC (dbh, "PRAGMA page_size=4092");
   SQLITE3_EXEC (dbh,
-                "CREATE TABLE ds090 ("
-                "  type INTEGER NOT NULL DEFAULT 0,"
+                "CREATE TABLE ds090 (" "  type INTEGER NOT NULL DEFAULT 0,"
                 "  expire INTEGER NOT NULL DEFAULT 0,"
                 "  key BLOB NOT NULL DEFAULT '',"
                 "  value BLOB NOT NULL DEFAULT '')");
   SQLITE3_EXEC (dbh, "CREATE INDEX idx_hashidx ON ds090 (key,type,expire)");
                 "  expire INTEGER NOT NULL DEFAULT 0,"
                 "  key BLOB NOT NULL DEFAULT '',"
                 "  value BLOB NOT NULL DEFAULT '')");
   SQLITE3_EXEC (dbh, "CREATE INDEX idx_hashidx ON ds090 (key,type,expire)");
+  SQLITE3_EXEC (dbh, "CREATE INDEX idx_expire ON ds090 (expire)");
   plugin = GNUNET_malloc (sizeof (struct Plugin));
   plugin->env = env;
   plugin->dbh = dbh;
   plugin = GNUNET_malloc (sizeof (struct Plugin));
   plugin->env = env;
   plugin->dbh = dbh;
@@ -410,24 +402,61 @@ libgnunet_plugin_datacache_sqlite_init (void *cls)
   api->get = &sqlite_plugin_get;
   api->put = &sqlite_plugin_put;
   api->del = &sqlite_plugin_del;
   api->get = &sqlite_plugin_get;
   api->put = &sqlite_plugin_put;
   api->del = &sqlite_plugin_del;
-  GNUNET_log_from (GNUNET_ERROR_TYPE_INFO,
-                   "sqlite", _("Sqlite datacache running\n"));
+  LOG (GNUNET_ERROR_TYPE_INFO, _("Sqlite datacache running\n"));
   return api;
 }
 
 
 /**
  * Exit point from the plugin.
   return api;
 }
 
 
 /**
  * Exit point from the plugin.
+ *
+ * @param cls closure (our "struct Plugin")
+ * @return NULL
  */
 void *
 libgnunet_plugin_datacache_sqlite_done (void *cls)
 {
   struct GNUNET_DATACACHE_PluginFunctions *api = cls;
   struct Plugin *plugin = api->cls;
  */
 void *
 libgnunet_plugin_datacache_sqlite_done (void *cls)
 {
   struct GNUNET_DATACACHE_PluginFunctions *api = cls;
   struct Plugin *plugin = api->cls;
+  int result;
+
+#if SQLITE_VERSION_NUMBER >= 3007000
+  sqlite3_stmt *stmt;
+#endif
+
+#if !WINDOWS || defined(__CYGWIN__)
+  if (0 != UNLINK (plugin->fn))
+    LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "unlink", plugin->fn);
+  GNUNET_free (plugin->fn);
+#endif
+  result = sqlite3_close (plugin->dbh);
+#if SQLITE_VERSION_NUMBER >= 3007000
+  if (result == SQLITE_BUSY)
+  {
+    LOG (GNUNET_ERROR_TYPE_WARNING,
+         _
+         ("Tried to close sqlite without finalizing all prepared statements.\n"));
+    stmt = sqlite3_next_stmt (plugin->dbh, NULL);
+    while (stmt != NULL)
+    {
+      LOG (GNUNET_ERROR_TYPE_DEBUG, "Closing statement %p\n", stmt);
+      result = sqlite3_finalize (stmt);
+      if (result != SQLITE_OK)
+        LOG (GNUNET_ERROR_TYPE_WARNING, _("Failed to close statement %p: %d\n"),
+             stmt, result);
+      stmt = sqlite3_next_stmt (plugin->dbh, NULL);
+    }
+    result = sqlite3_close (plugin->dbh);
+  }
+#endif
+  if (SQLITE_OK != result)
+    LOG_SQLITE (plugin->dbh, GNUNET_ERROR_TYPE_ERROR, "sqlite3_close");
 
 
-  UNLINK (plugin->fn);
+#if WINDOWS && !defined(__CYGWIN__)
+  if (0 != UNLINK (plugin->fn))
+    LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "unlink", plugin->fn);
   GNUNET_free (plugin->fn);
   GNUNET_free (plugin->fn);
-  sqlite3_close (plugin->dbh);
+#endif
   GNUNET_free (plugin);
   GNUNET_free (api);
   return NULL;
   GNUNET_free (plugin);
   GNUNET_free (api);
   return NULL;
@@ -436,4 +465,3 @@ libgnunet_plugin_datacache_sqlite_done (void *cls)
 
 
 /* end of plugin_datacache_sqlite.c */
 
 
 /* end of plugin_datacache_sqlite.c */
-