Merge branch 'credentials' of git+ssh://gnunet.org/gnunet into credentials
[oweals/gnunet.git] / src / datacache / plugin_datacache_sqlite.c
index e386b55de7491861d420d24dbbfe5323c98a0a4b..5567077d3fd8738ca4ff39f20e477d62b885d481 100644 (file)
@@ -1,10 +1,10 @@
 /*
 /*
-     This file is part of GNUnet.
-     (C) 2006, 2007, 2008 Christian Grothoff (and other contributing authors)
+     This file is part of GNUnet
+     Copyright (C) 2006, 2009, 2015 GNUnet e.V.
 
      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
 
      You should have received a copy of the GNU General Public License
      along with GNUnet; see the file COPYING.  If not, write to the
 
      You should have received a copy of the GNU General Public License
      along with GNUnet; see the file COPYING.  If not, write to the
-     Free Software Foundation, Inc., 59 Temple Place - Suite 330,
-     Boston, MA 02111-1307, USA.
+     Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+     Boston, MA 02110-1301, USA.
 */
 
 /**
 */
 
 /**
- * @file applications/dstore_sqlite/dstore.c
- * @brief SQLite based implementation of the dstore service
+ * @file datacache/plugin_datacache_sqlite.c
+ * @brief sqlite for an implementation of a database backend for the datacache
  * @author Christian Grothoff
  * @author Christian Grothoff
- * @todo Indexes, statistics
- *
- * Database: SQLite
  */
  */
-
 #include "platform.h"
 #include "platform.h"
-#include "gnunet_util.h"
-#include "gnunet_dstore_service.h"
-#include "gnunet_stats_service.h"
+#include "gnunet_util_lib.h"
+#include "gnunet_datacache_plugin.h"
 #include <sqlite3.h>
 
 #include <sqlite3.h>
 
-#define DEBUG_DSTORE GNUNET_NO
+#define LOG(kind,...) GNUNET_log_from (kind, "datacache-sqlite", __VA_ARGS__)
 
 
-/**
- * Maximum size for an individual item.
- */
-#define MAX_CONTENT_SIZE 65536
+#define LOG_STRERROR_FILE(kind,op,fn) GNUNET_log_from_strerror_file (kind, "datacache-sqlite", op, fn)
 
 
-/**
- * Bytes used
- */
-static unsigned long long payload;
 
 /**
 
 /**
- * Maximum bytes available
+ * How much overhead do we assume per entry in the
+ * datacache?
  */
  */
-static unsigned long long quota;
+#define OVERHEAD (sizeof(struct GNUNET_HashCode) + 32)
 
 /**
 
 /**
- * Filename of this database
+ * Context for all functions in this plugin.
  */
  */
-static char *fn;
-static char *fn_utf8;
+struct Plugin
+{
+  /**
+   * Our execution environment.
+   */
+  struct GNUNET_DATACACHE_PluginEnvironment *env;
+
+  /**
+   * Handle to the sqlite database.
+   */
+  sqlite3 *dbh;
+
+  /**
+   * Filename used for the DB.
+   */
+  char *fn;
 
 
-static GNUNET_CoreAPIForPlugins *coreAPI;
+  /**
+   * Number of key-value pairs in the database.
+   */
+  unsigned int num_items;
+};
 
 
-static struct GNUNET_Mutex *lock;
 
 /**
 
 /**
- * Statistics service.
+ * Log an error message at log-level @a level that indicates
+ * a failure of the command @a cmd with the error from the database @a db
+ *
+ * @param db database handle
+ * @param level log level
+ * @param cmd failed command
  */
  */
-static GNUNET_Stats_ServiceAPI *stats;
+#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)
 
 
-static unsigned int stat_dstore_size;
-
-static unsigned int stat_dstore_quota;
 
 /**
 
 /**
- * Estimate of the per-entry overhead (including indices).
+ * Execute SQL statement.
+ *
+ * @param db database handle
+ * @param cmd SQL command to execute
  */
  */
-#define OVERHEAD ((4*2+4*2+8*2+8*2+sizeof(GNUNET_HashCode)*5+32))
-
-struct GNUNET_BloomFilter *bloom;
+#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)
 
 
-static char *bloom_name;
 
 /**
  * @brief Prepare a SQL statement
 
 /**
  * @brief Prepare a SQL statement
+ *
+ * @param dbh database handle
+ * @param zsql SQL statement text
+ * @param[out] ppStmt set to the prepared statement
+ * @return 0 on success
  */
 static int
  */
 static int
-sq_prepare (sqlite3 * dbh, const char *zSql,    /* SQL statement, UTF-8 encoded */
-            sqlite3_stmt ** ppStmt)
+sq_prepare (sqlite3 *dbh,
+            const char *zSql,    /* SQL statement, UTF-8 encoded */
+            sqlite3_stmt **ppStmt)
 {                               /* OUT: Statement handle */
   char *dummy;
 {                               /* OUT: Statement handle */
   char *dummy;
+
   return sqlite3_prepare (dbh,
   return sqlite3_prepare (dbh,
-                          zSql,
-                          strlen (zSql), ppStmt, (const char **) &dummy);
+                          zSql, strlen (zSql),
+                          ppStmt,
+                          (const char **) &dummy);
 }
 
 }
 
-#define SQLITE3_EXEC(db, cmd) do { emsg = NULL; if (SQLITE_OK != sqlite3_exec(db, cmd, NULL, NULL, &emsg)) { GNUNET_GE_LOG(coreAPI->ectx, GNUNET_GE_ERROR | GNUNET_GE_ADMIN | GNUNET_GE_BULK, _("`%s' failed at %s:%d with error: %s\n"), "sqlite3_exec", __FILE__, __LINE__, emsg); sqlite3_free(emsg); } } while(0)
 
 /**
 
 /**
- * Log an error message at log-level 'level' that indicates
- * a failure of the command 'cmd' on file 'filename'
- * with the message given by strerror(errno).
+ * Store an item in the datastore.
+ *
+ * @param cls closure (our `struct Plugin`)
+ * @param key key to store @a data under
+ * @param size number of bytes in @a data
+ * @param data data to store
+ * @param type type of the value
+ * @param discard_time when to discard the value in any case
+ * @param path_info_len number of entries in @a path_info
+ * @param path_info array of peers that have processed the request
+ * @return 0 if duplicate, -1 on error, number of bytes used otherwise
  */
  */
-#define LOG_SQLITE(db, level, cmd) do { GNUNET_GE_LOG(coreAPI->ectx, level, _("`%s' failed at %s:%d with error: %s\n"), cmd, __FILE__, __LINE__, sqlite3_errmsg(db)); } while(0)
-
-static void
-db_init (sqlite3 * dbh)
+static ssize_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,
+                  unsigned int path_info_len,
+                  const struct GNUNET_PeerIdentity *path_info)
 {
 {
-  char *emsg;
-
-  SQLITE3_EXEC (dbh, "PRAGMA temp_store=MEMORY");
-  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 ds080 ("
-                "  size INTEGER NOT NULL DEFAULT 0,"
-                "  type INTEGER NOT NULL DEFAULT 0,"
-                "  puttime INTEGER NOT NULL DEFAULT 0,"
-                "  expire INTEGER NOT NULL DEFAULT 0,"
-                "  key BLOB NOT NULL DEFAULT '',"
-                "  vhash BLOB NOT NULL DEFAULT '',"
-                "  value BLOB NOT NULL DEFAULT '')");
-  SQLITE3_EXEC (dbh, "CREATE INDEX idx_hashidx ON ds080 (key,type,expire)");
-  SQLITE3_EXEC (dbh,
-                "CREATE INDEX idx_allidx ON ds080 (key,vhash,type,size)");
-  SQLITE3_EXEC (dbh, "CREATE INDEX idx_puttime ON ds080 (puttime)");
+  struct Plugin *plugin = cls;
+  sqlite3_stmt *stmt;
+  int64_t dval;
+
+  LOG (GNUNET_ERROR_TYPE_DEBUG,
+       "Processing PUT of %u bytes with key `%4s' and expiration %s\n",
+       (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_us;
+  if (dval < 0)
+    dval = INT64_MAX;
+  if (sq_prepare
+      (plugin->dbh,
+       "INSERT INTO ds090 (type, expire, key, value, path) VALUES (?, ?, ?, ?, ?)",
+       &stmt) != SQLITE_OK)
+  {
+    LOG_SQLITE (plugin->dbh,
+                GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
+                "sq_prepare");
+    return -1;
+  }
+  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)) ||
+      (SQLITE_OK != sqlite3_bind_blob (stmt, 5,
+                                      path_info,
+                                      path_info_len * sizeof (struct GNUNET_PeerIdentity),
+                                      SQLITE_TRANSIENT)))
+  {
+    LOG_SQLITE (plugin->dbh,
+                GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
+                "sqlite3_bind_xxx");
+    sqlite3_finalize (stmt);
+    return -1;
+  }
+  if (SQLITE_DONE != sqlite3_step (stmt))
+  {
+    LOG_SQLITE (plugin->dbh,
+                GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
+                "sqlite3_step");
+    sqlite3_finalize (stmt);
+    return -1;
+  }
+  plugin->num_items++;
+  if (SQLITE_OK != sqlite3_finalize (stmt))
+    LOG_SQLITE (plugin->dbh,
+                GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
+                "sqlite3_finalize");
+  return size + OVERHEAD;
 }
 
 }
 
-static int
-db_reset ()
+
+/**
+ * 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 iter_cls closure for @a iter
+ * @return the number of results found
+ */
+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)
 {
 {
-  int fd;
-  sqlite3 *dbh;
-  char *tmpl;
-  const char *tmpdir;
+  struct Plugin *plugin = cls;
+  sqlite3_stmt *stmt;
+  struct GNUNET_TIME_Absolute now;
+  struct GNUNET_TIME_Absolute exp;
+  unsigned int size;
+  const char *dat;
+  unsigned int cnt;
+  unsigned int off;
+  unsigned int total;
+  unsigned int psize;
+  char scratch[256];
+  int64_t ntime;
+  const struct GNUNET_PeerIdentity *path;
+
+  now = GNUNET_TIME_absolute_get ();
+  LOG (GNUNET_ERROR_TYPE_DEBUG,
+       "Processing GET for key `%4s'\n",
+       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_us;
+  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_us)))
+  {
+    LOG_SQLITE (plugin->dbh,
+                GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
+                "sqlite3_bind_xxx");
+    sqlite3_finalize (stmt);
+    return 0;
+  }
 
 
-  if (fn != NULL)
+  if (SQLITE_ROW != sqlite3_step (stmt))
+  {
+    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 GET for key `%4s'\n",
+         GNUNET_h2s (key));
+    return 0;
+  }
+  total = sqlite3_column_int (stmt, 0);
+  sqlite3_finalize (stmt);
+  if ((0 == total) || (NULL == iter))
+  {
+    if (0 == total)
+      LOG (GNUNET_ERROR_TYPE_DEBUG,
+           "No content found when processing GET for key `%4s'\n",
+           GNUNET_h2s (key));
+    return 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,path FROM ds090 WHERE key=? AND type=? AND expire >= ? LIMIT 1 OFFSET %u",
+                     off);
+    if (sq_prepare (plugin->dbh, scratch, &stmt) != SQLITE_OK)
     {
     {
-      UNLINK (fn);
-      GNUNET_free (fn);
-      GNUNET_free (fn_utf8);
+      LOG_SQLITE (plugin->dbh,
+                  GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
+                  "sq_prepare");
+      return cnt;
     }
     }
-  payload = 0;
-
-  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)
+    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_us)))
     {
     {
-      GNUNET_GE_BREAK (NULL, 0);
-      GNUNET_free (fn);
-      fn = NULL;
-      return GNUNET_SYSERR;
+      LOG_SQLITE (plugin->dbh,
+                  GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
+                  "sqlite3_bind_xxx");
+      sqlite3_finalize (stmt);
+      return cnt;
     }
     }
-  CLOSE (fd);
-  fn_utf8 = GNUNET_convert_string_to_utf8 (coreAPI->ectx, fn, strlen (fn),
-#ifdef ENABLE_NLS
-                                           nl_langinfo (CODESET)
-#else
-                                           "UTF-8"      /* good luck */
-#endif
-    );
-  if (SQLITE_OK != sqlite3_open (fn_utf8, &dbh))
+    if (sqlite3_step (stmt) != SQLITE_ROW)
+      break;
+    size = sqlite3_column_bytes (stmt, 0);
+    dat = sqlite3_column_blob (stmt, 0);
+    exp.abs_value_us = sqlite3_column_int64 (stmt, 1);
+    psize = sqlite3_column_bytes (stmt, 2);
+    if (0 != psize % sizeof (struct GNUNET_PeerIdentity))
     {
     {
-      GNUNET_free (fn);
-      GNUNET_free (fn_utf8);
-      fn = NULL;
-      return GNUNET_SYSERR;
+      GNUNET_break (0);
+      psize = 0;
     }
     }
-  db_init (dbh);
-  sqlite3_close (dbh);
-  return GNUNET_OK;
+    psize /= sizeof (struct GNUNET_PeerIdentity);
+    if (0 != psize)
+      path = sqlite3_column_blob (stmt, 2);
+    else
+      path = NULL;
+    ntime = (int64_t) exp.abs_value_us;
+    if (ntime == INT64_MAX)
+      exp = GNUNET_TIME_UNIT_FOREVER_ABS;
+    cnt++;
+    LOG (GNUNET_ERROR_TYPE_DEBUG,
+         "Found %u-byte result when processing GET for key `%4s'\n",
+         (unsigned int) size,
+         GNUNET_h2s (key));
+    if (GNUNET_OK != iter (iter_cls,
+                           key,
+                           size,
+                           dat,
+                           type,
+                           exp,
+                           psize,
+                           path))
+    {
+      sqlite3_finalize (stmt);
+      break;
+    }
+    sqlite3_finalize (stmt);
+  }
+  return cnt;
 }
 
 }
 
+
 /**
 /**
- * Check that we are within quota.
- * @return GNUNET_OK if we are.
+ * 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
  */
 static int
  */
 static int
-checkQuota (sqlite3 * dbh)
+sqlite_plugin_del (void *cls)
 {
 {
-  GNUNET_HashCode dkey;
-  GNUNET_HashCode vhash;
+  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;
-  int err;
-
-  if (payload * 10 <= quota * 9)
-    return GNUNET_OK;           /* we seem to be about 10% off */
-#if DEBUG_DSTORE
-  GNUNET_GE_LOG (coreAPI->ectx,
-                 GNUNET_GE_DEBUG | GNUNET_GE_REQUEST | GNUNET_GE_DEVELOPER,
-                 "DStore above qutoa (have %llu, allowed %llu), will delete some data.\n",
-                 payload, quota);
-#endif
+  struct GNUNET_HashCode hc;
+
+  LOG (GNUNET_ERROR_TYPE_DEBUG,
+       "Processing DEL\n");
   stmt = NULL;
   dstmt = NULL;
   stmt = NULL;
   dstmt = NULL;
-  if ((sq_prepare (dbh,
-                   "SELECT size, type, key, vhash FROM ds080 ORDER BY puttime ASC LIMIT 1",
-                   &stmt) != SQLITE_OK) ||
-      (sq_prepare (dbh,
-                   "DELETE FROM ds080 "
-                   "WHERE key=? AND vhash=? AND type=? AND size=?",
-                   &dstmt) != SQLITE_OK))
-    {
-      GNUNET_GE_LOG (coreAPI->ectx,
-                     GNUNET_GE_ERROR | GNUNET_GE_ADMIN | GNUNET_GE_BULK,
-                     _("`%s' failed at %s:%d with error: %s\n"),
-                     "sq_prepare", __FILE__, __LINE__, sqlite3_errmsg (dbh));
-      GNUNET_GE_BREAK (NULL, 0);
-      if (dstmt != NULL)
-        sqlite3_finalize (dstmt);
-      if (stmt != NULL)
-        sqlite3_finalize (stmt);
-      return GNUNET_SYSERR;
-    }
-  err = SQLITE_DONE;
-  while ((payload * 10 > quota * 9) &&  /* we seem to be about 10% off */
-         ((err = sqlite3_step (stmt)) == SQLITE_ROW))
-    {
-      dsize = sqlite3_column_int (stmt, 0);
-      dtype = sqlite3_column_int (stmt, 1);
-      GNUNET_GE_BREAK (NULL,
-                       sqlite3_column_bytes (stmt,
-                                             2) == sizeof (GNUNET_HashCode));
-      GNUNET_GE_BREAK (NULL,
-                       sqlite3_column_bytes (stmt,
-                                             3) == sizeof (GNUNET_HashCode));
-      memcpy (&dkey, sqlite3_column_blob (stmt, 2), sizeof (GNUNET_HashCode));
-      memcpy (&vhash, sqlite3_column_blob (stmt, 3),
-              sizeof (GNUNET_HashCode));
-      sqlite3_reset (stmt);
-      sqlite3_bind_blob (dstmt,
-                         1, &dkey, sizeof (GNUNET_HashCode),
-                         SQLITE_TRANSIENT);
-      sqlite3_bind_blob (dstmt,
-                         2, &vhash, sizeof (GNUNET_HashCode),
-                         SQLITE_TRANSIENT);
-      sqlite3_bind_int (dstmt, 3, dtype);
-      sqlite3_bind_int (dstmt, 4, dsize);
-      if ((err = sqlite3_step (dstmt)) != SQLITE_DONE)
-        {
-          GNUNET_GE_LOG (coreAPI->ectx,
-                         GNUNET_GE_ERROR | GNUNET_GE_ADMIN | GNUNET_GE_BULK,
-                         _("`%s' failed at %s:%d with error: %s\n"),
-                         "sqlite3_step", __FILE__, __LINE__,
-                         sqlite3_errmsg (dbh));
-          sqlite3_reset (dstmt);
-          GNUNET_GE_BREAK (NULL, 0);    /* should delete but cannot!? */
-          break;
-        }
-      if (sqlite3_total_changes (dbh) > 0)
-        {
-          if (bloom != NULL)
-            GNUNET_bloomfilter_remove (bloom, &dkey);
-          payload -= (dsize + OVERHEAD);
-        }
-#if DEBUG_DSTORE
-      GNUNET_GE_LOG (coreAPI->ectx,
-                     GNUNET_GE_DEBUG | GNUNET_GE_REQUEST |
-                     GNUNET_GE_DEVELOPER,
-                     "Deleting %u bytes decreases DStore payload to %llu out of %llu\n",
-                     dsize, payload, quota);
-#endif
-      sqlite3_reset (dstmt);
-    }
-  if (err != SQLITE_DONE)
-    {
-      GNUNET_GE_LOG (coreAPI->ectx,
-                     GNUNET_GE_ERROR | GNUNET_GE_ADMIN | GNUNET_GE_BULK,
-                     _("`%s' failed at %s:%d with error: %s\n"),
-                     "sqlite3_step", __FILE__, __LINE__,
-                     sqlite3_errmsg (dbh));
-    }
-  sqlite3_finalize (dstmt);
-  sqlite3_finalize (stmt);
-  if (payload * 10 > quota * 9)
-    {
-      /* we seem to be about 10% off */
-      GNUNET_GE_LOG (coreAPI->ectx,
-                     GNUNET_GE_ERROR | GNUNET_GE_BULK | GNUNET_GE_DEVELOPER,
-                     "Failed to delete content to drop below quota (bug?).\n",
-                     payload, quota);
-      return GNUNET_SYSERR;
-    }
+  if (SQLITE_OK !=
+      sq_prepare (plugin->dbh,
+                  "SELECT _ROWID_,key,value FROM ds090 ORDER BY expire ASC LIMIT 1",
+                  &stmt))
+  {
+    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))
+  {
+    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));
+  GNUNET_memcpy (&hc, sqlite3_column_blob (stmt, 1), sizeof (struct GNUNET_HashCode));
+  dsize = sqlite3_column_bytes (stmt, 2);
+  if (SQLITE_OK != sqlite3_finalize (stmt))
+    LOG_SQLITE (plugin->dbh,
+                GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
+                "sqlite3_step");
+  if (SQLITE_OK !=
+      sq_prepare (plugin->dbh,
+                  "DELETE FROM ds090 WHERE _ROWID_=?", &dstmt))
+  {
+    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 (SQLITE_DONE != sqlite3_step (dstmt))
+  {
+    LOG_SQLITE (plugin->dbh,
+                GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
+                "sqlite3_step");
+    (void) sqlite3_finalize (dstmt);
+    return GNUNET_SYSERR;
+  }
+  plugin->num_items--;
+  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;
 }
 
   return GNUNET_OK;
 }
 
+
 /**
 /**
- * Store an item in the datastore.
+ * Obtain a random key-value pair from the datacache.
  *
  *
- * @return GNUNET_OK on success, GNUNET_SYSERR on error
+ * @param cls closure (our `struct Plugin`)
+ * @param iter maybe NULL (to just count)
+ * @param iter_cls closure for @a iter
+ * @return the number of results found, zero (datacache empty) or one
  */
  */
-static int
-d_put (const GNUNET_HashCode * key,
-       unsigned int type,
-       GNUNET_CronTime discard_time, unsigned int size, const char *data)
+static unsigned int
+sqlite_plugin_get_random (void *cls,
+                          GNUNET_DATACACHE_Iterator iter,
+                          void *iter_cls)
 {
 {
-  GNUNET_HashCode vhash;
-  sqlite3 *dbh;
+  struct Plugin *plugin = cls;
   sqlite3_stmt *stmt;
   sqlite3_stmt *stmt;
-  int ret;
-  GNUNET_CronTime now;
-
-  if (size > MAX_CONTENT_SIZE)
-    return GNUNET_SYSERR;
-  GNUNET_hash (data, size, &vhash);
-  GNUNET_mutex_lock (lock);
-  if ((fn == NULL) || (SQLITE_OK != sqlite3_open (fn_utf8, &dbh)))
-    {
-      db_reset (dbh);
-      GNUNET_mutex_unlock (lock);
-      return GNUNET_SYSERR;
-    }
-  now = GNUNET_get_time ();
-#if DEBUG_DSTORE
-  GNUNET_GE_LOG (coreAPI->ectx,
-                 GNUNET_GE_DEBUG | GNUNET_GE_REQUEST | GNUNET_GE_DEVELOPER,
-                 "dstore processes put `%.*s' with expiration %llu\n",
-                 size, data, discard_time);
-#endif
-
-  /* first try UPDATE */
-  if (sq_prepare (dbh,
-                  "UPDATE ds080 SET puttime=?, expire=? "
-                  "WHERE key=? AND vhash=? AND type=? AND size=?",
-                  &stmt) != SQLITE_OK)
-    {
-      GNUNET_GE_LOG (coreAPI->ectx,
-                     GNUNET_GE_ERROR | GNUNET_GE_ADMIN | GNUNET_GE_BULK,
-                     _("`%s' failed at %s:%d with error: %s\n"),
-                     "sq_prepare", __FILE__, __LINE__, sqlite3_errmsg (dbh));
-      sqlite3_close (dbh);
-      GNUNET_mutex_unlock (lock);
-      return GNUNET_SYSERR;
-    }
-  if ((SQLITE_OK !=
-       sqlite3_bind_int64 (stmt, 1, now)) ||
-      (SQLITE_OK !=
-       sqlite3_bind_int64 (stmt, 2, discard_time)) ||
-      (SQLITE_OK !=
-       sqlite3_bind_blob (stmt, 3, key, sizeof (GNUNET_HashCode),
-                          SQLITE_TRANSIENT)) ||
-      (SQLITE_OK !=
-       sqlite3_bind_blob (stmt, 4, &vhash, sizeof (GNUNET_HashCode),
-                          SQLITE_TRANSIENT)) ||
-      (SQLITE_OK != sqlite3_bind_int (stmt, 5, type)) ||
-      (SQLITE_OK != sqlite3_bind_int (stmt, 6, size)))
-    {
-      GNUNET_GE_LOG (coreAPI->ectx,
-                     GNUNET_GE_ERROR | GNUNET_GE_ADMIN | GNUNET_GE_BULK,
-                     _("`%s' failed at %s:%d with error: %s\n"),
-                     "sqlite3_bind_xxx", __FILE__, __LINE__,
-                     sqlite3_errmsg (dbh));
-      sqlite3_finalize (stmt);
-      sqlite3_close (dbh);
-      GNUNET_mutex_unlock (lock);
-      return GNUNET_SYSERR;
-    }
-  if (SQLITE_DONE != sqlite3_step (stmt))
-    {
-      GNUNET_GE_LOG (coreAPI->ectx,
-                     GNUNET_GE_ERROR | GNUNET_GE_ADMIN | GNUNET_GE_BULK,
-                     _("`%s' failed at %s:%d with error: %s\n"),
-                     "sqlite3_step", __FILE__, __LINE__,
-                     sqlite3_errmsg (dbh));
-      sqlite3_finalize (stmt);
-      sqlite3_close (dbh);
-      GNUNET_mutex_unlock (lock);
-      return GNUNET_SYSERR;
-    }
-  ret = sqlite3_changes (dbh);
-  sqlite3_finalize (stmt);
-  if (ret > 0)
-    {
-      sqlite3_close (dbh);
-      GNUNET_mutex_unlock (lock);
-      return GNUNET_OK;
-    }
-  if (GNUNET_OK != checkQuota (dbh))
-    {
-      sqlite3_close (dbh);
-      GNUNET_mutex_unlock (lock);
-      return GNUNET_SYSERR;
-    }
-  if (sq_prepare (dbh,
-                  "INSERT INTO ds080 "
-                  "(size, type, puttime, expire, key, vhash, value) "
-                  "VALUES (?, ?, ?, ?, ?, ?, ?)", &stmt) != SQLITE_OK)
-    {
-      GNUNET_GE_LOG (coreAPI->ectx,
-                     GNUNET_GE_ERROR | GNUNET_GE_ADMIN | GNUNET_GE_BULK,
-                     _("`%s' failed at %s:%d with error: %s\n"),
-                     "sq_prepare", __FILE__, __LINE__, sqlite3_errmsg (dbh));
-      sqlite3_close (dbh);
-      GNUNET_mutex_unlock (lock);
-      return GNUNET_SYSERR;
-    }
-  if ((SQLITE_OK == sqlite3_bind_int (stmt, 1, size)) &&
-      (SQLITE_OK == sqlite3_bind_int (stmt, 2, type)) &&
-      (SQLITE_OK == sqlite3_bind_int64 (stmt, 3, now)) &&
-      (SQLITE_OK == sqlite3_bind_int64 (stmt, 4, discard_time)) &&
-      (SQLITE_OK ==
-       sqlite3_bind_blob (stmt, 5, key, sizeof (GNUNET_HashCode),
-                          SQLITE_TRANSIENT)) &&
-      (SQLITE_OK ==
-       sqlite3_bind_blob (stmt, 6, &vhash, sizeof (GNUNET_HashCode),
-                          SQLITE_TRANSIENT))
-      && (SQLITE_OK ==
-          sqlite3_bind_blob (stmt, 7, data, size, SQLITE_TRANSIENT)))
-    {
-      if (SQLITE_DONE != sqlite3_step (stmt))
-        {
-          LOG_SQLITE (dbh,
-                      GNUNET_GE_ERROR | GNUNET_GE_DEVELOPER | GNUNET_GE_ADMIN
-                      | GNUNET_GE_BULK, "sqlite3_step");
-        }
-      else
-        {
-          payload += size + OVERHEAD;
-          if (bloom != NULL)
-            GNUNET_bloomfilter_add (bloom, key);
-        }
-      if (SQLITE_OK != sqlite3_finalize (stmt))
-        LOG_SQLITE (dbh,
-                    GNUNET_GE_ERROR | GNUNET_GE_DEVELOPER | GNUNET_GE_ADMIN |
-                    GNUNET_GE_BULK, "sqlite3_finalize");
-    }
+  struct GNUNET_TIME_Absolute exp;
+  unsigned int size;
+  const char *dat;
+  unsigned int off;
+  unsigned int psize;
+  unsigned int type;
+  char scratch[256];
+  int64_t ntime;
+  const struct GNUNET_PeerIdentity *path;
+  const struct GNUNET_HashCode *key;
+
+  if (0 == plugin->num_items)
+    return 0;
+  if (NULL == iter)
+    return 1;
+  off = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_NONCE,
+                                  plugin->num_items);
+  GNUNET_snprintf (scratch,
+                   sizeof (scratch),
+                   "SELECT value,expire,path,key,type FROM ds090 ORDER BY key LIMIT 1 OFFSET %u",
+                   off);
+  if (SQLITE_OK !=
+      sq_prepare (plugin->dbh, scratch, &stmt))
+  {
+    LOG_SQLITE (plugin->dbh,
+                GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
+                "sq_prepare");
+    return 0;
+  }
+  if (SQLITE_ROW != sqlite3_step (stmt))
+  {
+    GNUNET_break (0);
+    sqlite3_finalize (stmt);
+    return 0;
+  }
+  size = sqlite3_column_bytes (stmt, 0);
+  dat = sqlite3_column_blob (stmt, 0);
+  exp.abs_value_us = sqlite3_column_int64 (stmt, 1);
+  psize = sqlite3_column_bytes (stmt, 2);
+  if (0 != psize % sizeof (struct GNUNET_PeerIdentity))
+  {
+    GNUNET_break (0);
+    psize = 0;
+  }
+  psize /= sizeof (struct GNUNET_PeerIdentity);
+  if (0 != psize)
+    path = sqlite3_column_blob (stmt, 2);
   else
   else
-    {
-      LOG_SQLITE (dbh,
-                  GNUNET_GE_ERROR | GNUNET_GE_DEVELOPER | GNUNET_GE_ADMIN |
-                  GNUNET_GE_BULK, "sqlite3_bind_xxx");
-    }
-#if DEBUG_DSTORE
-  GNUNET_GE_LOG (coreAPI->ectx,
-                 GNUNET_GE_DEBUG | GNUNET_GE_REQUEST | GNUNET_GE_DEVELOPER,
-                 "Storing %u bytes increases DStore payload to %llu out of %llu\n",
-                 size, payload, quota);
-#endif
-  checkQuota (dbh);
-  sqlite3_close (dbh);
-  GNUNET_mutex_unlock (lock);
-  if (stats != NULL)
-    stats->set (stat_dstore_size, payload);
-  return GNUNET_OK;
+    path = NULL;
+
+  GNUNET_assert (sizeof (struct GNUNET_HashCode) ==
+                 sqlite3_column_bytes (stmt, 3));
+  key = sqlite3_column_blob (stmt, 3);
+  type = sqlite3_column_int (stmt, 4);
+
+  ntime = (int64_t) exp.abs_value_us;
+  if (ntime == INT64_MAX)
+    exp = GNUNET_TIME_UNIT_FOREVER_ABS;
+  LOG (GNUNET_ERROR_TYPE_DEBUG,
+       "Found %u-byte result with key %s when processing GET-RANDOM\n",
+       (unsigned int) size,
+       GNUNET_h2s (key));
+  (void) iter (iter_cls,
+               key,
+               size,
+               dat,
+               type,
+               exp,
+               psize,
+               path);
+  sqlite3_finalize (stmt);
+  return 1;
 }
 
 }
 
+
 /**
 /**
- * Iterate over the results for a particular key
- * in the datastore.
+ * Iterate over the results that are "close" to a particular key in
+ * the datacache.  "close" is defined as numerically larger than @a
+ * key (when interpreted as a circular address space), with small
+ * distance.
  *
  *
- * @param key
- * @param type entries of which type are relevant?
+ * @param cls closure (internal context for the plugin)
+ * @param key area of the keyspace to look into
+ * @param num_results number of results that should be returned to @a iter
  * @param iter maybe NULL (to just count)
  * @param iter maybe NULL (to just count)
- * @return the number of results, GNUNET_SYSERR if the
- *   iter is non-NULL and aborted the iteration
+ * @param iter_cls closure for @a iter
+ * @return the number of results found
  */
  */
-static int
-d_get (const GNUNET_HashCode * key,
-       unsigned int type, GNUNET_ResultProcessor handler, void *closure)
+static unsigned int
+sqlite_plugin_get_closest (void *cls,
+                           const struct GNUNET_HashCode *key,
+                           unsigned int num_results,
+                           GNUNET_DATACACHE_Iterator iter,
+                           void *iter_cls)
 {
 {
-  sqlite3 *dbh;
+  struct Plugin *plugin = cls;
   sqlite3_stmt *stmt;
   sqlite3_stmt *stmt;
-  GNUNET_CronTime now;
+  struct GNUNET_TIME_Absolute now;
+  struct GNUNET_TIME_Absolute exp;
   unsigned int size;
   const char *dat;
   unsigned int cnt;
   unsigned int size;
   const char *dat;
   unsigned int cnt;
-  unsigned int off;
-  unsigned int total;
-  char scratch[256];
-
-  GNUNET_mutex_lock (lock);
-  if ((bloom != NULL) && (GNUNET_NO == GNUNET_bloomfilter_test (bloom, key)))
-    {
-      GNUNET_mutex_unlock (lock);
-      return 0;
-    }
-  if ((fn == NULL) || (SQLITE_OK != sqlite3_open (fn_utf8, &dbh)))
+  unsigned int psize;
+  unsigned int type;
+  int64_t ntime;
+  const struct GNUNET_PeerIdentity *path;
+
+  now = GNUNET_TIME_absolute_get ();
+  LOG (GNUNET_ERROR_TYPE_DEBUG,
+       "Processing GET_CLOSEST for key `%4s'\n",
+       GNUNET_h2s (key));
+  if (SQLITE_OK !=
+      sq_prepare (plugin->dbh,
+                  "SELECT value,expire,path,type,key FROM ds090 WHERE key>=? AND expire >= ? ORDER BY KEY ASC LIMIT ?",
+                  &stmt))
+  {
+    LOG_SQLITE (plugin->dbh,
+                GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
+                "sq_prepare");
+    return 0;
+  }
+  ntime = (int64_t) now.abs_value_us;
+  GNUNET_assert (ntime >= 0);
+  if ((SQLITE_OK !=
+       sqlite3_bind_blob (stmt,
+                          1,
+                          key,
+                          sizeof (struct GNUNET_HashCode),
+                          SQLITE_TRANSIENT)) ||
+      (SQLITE_OK != sqlite3_bind_int64 (stmt, 2, now.abs_value_us)) ||
+      (SQLITE_OK != sqlite3_bind_int (stmt, 3, num_results)) )
+  {
+    LOG_SQLITE (plugin->dbh,
+                GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
+                "sqlite3_bind_xxx");
+    sqlite3_finalize (stmt);
+    return 0;
+  }
+  cnt = 0;
+  while (SQLITE_ROW == sqlite3_step (stmt))
+  {
+    if (sizeof (struct GNUNET_HashCode) !=
+        sqlite3_column_bytes (stmt, 4))
     {
     {
-      db_reset (dbh);
-      GNUNET_mutex_unlock (lock);
-      return GNUNET_SYSERR;
+      GNUNET_break (0);
+      break;
     }
     }
-  now = GNUNET_get_time ();
-#if DEBUG_DSTORE
-  GNUNET_GE_LOG (coreAPI->ectx,
-                 GNUNET_GE_DEBUG | GNUNET_GE_REQUEST | GNUNET_GE_DEVELOPER,
-                 "dstore processes get at `%llu'\n", now);
-#endif
-  if (sq_prepare (dbh,
-                  "SELECT count(*) FROM ds080 WHERE key=? AND type=? AND expire >= ?",
-                  &stmt) != SQLITE_OK)
+    size = sqlite3_column_bytes (stmt, 0);
+    dat = sqlite3_column_blob (stmt, 0);
+    exp.abs_value_us = sqlite3_column_int64 (stmt, 1);
+    psize = sqlite3_column_bytes (stmt, 2);
+    type = sqlite3_column_int (stmt, 3);
+    key = sqlite3_column_blob (stmt, 4);
+    if (0 != psize % sizeof (struct GNUNET_PeerIdentity))
     {
     {
-      GNUNET_GE_LOG (coreAPI->ectx,
-                     GNUNET_GE_ERROR | GNUNET_GE_ADMIN | GNUNET_GE_BULK,
-                     _("`%s' failed at %s:%d with error: %s\n"),
-                     "sq_prepare", __FILE__, __LINE__, sqlite3_errmsg (dbh));
-      sqlite3_close (dbh);
-      db_reset (dbh);
-      GNUNET_mutex_unlock (lock);
-      return GNUNET_SYSERR;
+      GNUNET_break (0);
+      psize = 0;
     }
     }
-  sqlite3_bind_blob (stmt, 1, key, sizeof (GNUNET_HashCode),
-                     SQLITE_TRANSIENT);
-  sqlite3_bind_int (stmt, 2, type);
-  sqlite3_bind_int64 (stmt, 3, now);
-  if (SQLITE_ROW != sqlite3_step (stmt))
+    psize /= sizeof (struct GNUNET_PeerIdentity);
+    if (0 != psize)
+      path = sqlite3_column_blob (stmt, 2);
+    else
+      path = NULL;
+    ntime = (int64_t) exp.abs_value_us;
+    if (ntime == INT64_MAX)
+      exp = GNUNET_TIME_UNIT_FOREVER_ABS;
+    cnt++;
+    LOG (GNUNET_ERROR_TYPE_DEBUG,
+         "Found %u-byte result at %s when processing GET_CLOSE\n",
+         (unsigned int) size,
+         GNUNET_h2s (key));
+    if (GNUNET_OK != iter (iter_cls,
+                           key,
+                           size,
+                           dat,
+                           type,
+                           exp,
+                           psize,
+                           path))
     {
     {
-      LOG_SQLITE (dbh,
-                  GNUNET_GE_ERROR | GNUNET_GE_ADMIN | GNUNET_GE_USER |
-                  GNUNET_GE_BULK, "sqlite_step");
-      sqlite3_reset (stmt);
       sqlite3_finalize (stmt);
       sqlite3_finalize (stmt);
-      db_reset (dbh);
-      GNUNET_mutex_unlock (lock);
-      return GNUNET_SYSERR;
+      break;
     }
     }
-  total = sqlite3_column_int (stmt, 0);
-  sqlite3_reset (stmt);
+  }
   sqlite3_finalize (stmt);
   sqlite3_finalize (stmt);
-  if ((total == 0) || (handler == NULL))
-    {
-      sqlite3_close (dbh);
-      GNUNET_mutex_unlock (lock);
-      return total;
-    }
-
-  cnt = 0;
-  off = GNUNET_random_u32 (GNUNET_RANDOM_QUALITY_WEAK, total);
-  while (cnt < total)
-    {
-      off = (off + 1) % total;
-      GNUNET_snprintf (scratch, 256,
-                       "SELECT size, value FROM ds080 WHERE key=? AND type=? AND expire >= ? LIMIT 1 OFFSET %u",
-                       off);
-      if (sq_prepare (dbh, scratch, &stmt) != SQLITE_OK)
-        {
-          GNUNET_GE_LOG (coreAPI->ectx,
-                         GNUNET_GE_ERROR | GNUNET_GE_ADMIN | GNUNET_GE_BULK,
-                         _("`%s' failed at %s:%d with error: %s\n"),
-                         "sq_prepare", __FILE__, __LINE__,
-                         sqlite3_errmsg (dbh));
-          sqlite3_close (dbh);
-          GNUNET_mutex_unlock (lock);
-          return GNUNET_SYSERR;
-        }
-      sqlite3_bind_blob (stmt, 1, key, sizeof (GNUNET_HashCode),
-                         SQLITE_TRANSIENT);
-      sqlite3_bind_int (stmt, 2, type);
-      sqlite3_bind_int64 (stmt, 3, now);
-      if (sqlite3_step (stmt) != SQLITE_ROW)
-        break;
-      size = sqlite3_column_int (stmt, 0);
-      if (size != sqlite3_column_bytes (stmt, 1))
-        {
-          GNUNET_GE_BREAK (NULL, 0);
-          sqlite3_finalize (stmt);
-          continue;
-        }
-      dat = sqlite3_column_blob (stmt, 1);
-      cnt++;
-#if DEBUG_DSTORE
-      GNUNET_GE_LOG (coreAPI->ectx,
-                     GNUNET_GE_DEBUG | GNUNET_GE_REQUEST |
-                     GNUNET_GE_DEVELOPER,
-                     "dstore found result for get: `%.*s'\n", size, dat);
-#endif
-      if ((handler != NULL) &&
-          (GNUNET_OK != handler (key, type, size, dat, closure)))
-        {
-          sqlite3_finalize (stmt);
-          break;
-        }
-      sqlite3_finalize (stmt);
-    }
-  sqlite3_close (dbh);
-  GNUNET_mutex_unlock (lock);
   return cnt;
 }
 
   return cnt;
 }
 
-GNUNET_Dstore_ServiceAPI *
-provide_module_dstore_sqlite (GNUNET_CoreAPIForPlugins * capi)
+
+/**
+ * Entry point for the plugin.
+ *
+ * @param cls closure (the `struct GNUNET_DATACACHE_PluginEnvironment`)
+ * @return the plugin's closure (our `struct Plugin`)
+ */
+void *
+libgnunet_plugin_datacache_sqlite_init (void *cls)
 {
 {
-  static GNUNET_Dstore_ServiceAPI api;
-  int fd;
+  struct GNUNET_DATACACHE_PluginEnvironment *env = cls;
+  struct GNUNET_DATACACHE_PluginFunctions *api;
+  struct Plugin *plugin;
+  char *fn;
+  char *fn_utf8;
+  sqlite3 *dbh;
+  char *emsg;
 
 
-#if DEBUG_SQLITE
-  GNUNET_GE_LOG (capi->ectx,
-                 GNUNET_GE_DEBUG | GNUNET_GE_REQUEST | GNUNET_GE_USER,
-                 "SQLite Dstore: initializing database\n");
-#endif
-  coreAPI = capi;
-  if (GNUNET_OK != db_reset ())
+  if (GNUNET_YES ==
+      GNUNET_CONFIGURATION_get_value_yesno (env->cfg,
+                                           "datacache-sqlite",
+                                           "IN_MEMORY"))
+  {
+    if (SQLITE_OK != sqlite3_open (":memory:", &dbh))
+      return NULL;
+    fn_utf8 = NULL;
+  }
+  else
+  {
+    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))
     {
     {
-      GNUNET_GE_BREAK (capi->ectx, 0);
+      GNUNET_free (fn);
+      GNUNET_free (fn_utf8);
       return NULL;
     }
       return NULL;
     }
-  lock = GNUNET_mutex_create (GNUNET_NO);
-
+    GNUNET_free (fn);
+  }
 
 
-  api.get = &d_get;
-  api.put = &d_put;
-  GNUNET_GC_get_configuration_value_number (coreAPI->cfg,
-                                            "DSTORE", "QUOTA", 1, 1024, 1,
-                                            &quota);
-  if (quota == 0)               /* error */
-    quota = 1;
-  quota *= 1024 * 1024;
+  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 page_size=4092");
+  if (GNUNET_YES ==
+      GNUNET_CONFIGURATION_get_value_yesno (env->cfg,
+                                           "datacache-sqlite",
+                                           "IN_MEMORY"))
+    SQLITE3_EXEC (dbh, "PRAGMA sqlite_temp_store=3");
 
 
-  bloom_name = GNUNET_strdup ("/tmp/dbloomXXXXXX");
-  fd = mkstemp (bloom_name);
-  if (fd != -1)
-    {
-      bloom = GNUNET_bloomfilter_load (coreAPI->ectx, bloom_name, quota / (OVERHEAD + 1024),    /* 8 bit per entry in DB, expect 1k entries */
-                                       5);
-      CLOSE (fd);
-    }
-  stats = capi->service_request ("stats");
-  if (stats != NULL)
-    {
-      stat_dstore_size = stats->create (gettext_noop ("# bytes in dstore"));
-      stat_dstore_quota =
-        stats->create (gettext_noop ("# max bytes allowed in dstore"));
-      stats->set (stat_dstore_quota, quota);
-    }
-  return &api;
+  SQLITE3_EXEC (dbh,
+                "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 '',"
+               "  path BLOB 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_new (struct Plugin);
+  plugin->env = env;
+  plugin->dbh = dbh;
+  plugin->fn = fn_utf8;
+  api = GNUNET_new (struct GNUNET_DATACACHE_PluginFunctions);
+  api->cls = plugin;
+  api->get = &sqlite_plugin_get;
+  api->put = &sqlite_plugin_put;
+  api->del = &sqlite_plugin_del;
+  api->get_random = &sqlite_plugin_get_random;
+  api->get_closest = &sqlite_plugin_get_closest;
+  LOG (GNUNET_ERROR_TYPE_INFO,
+       "Sqlite datacache running\n");
+  return api;
 }
 
 }
 
+
 /**
 /**
- * Shutdown the module.
+ * Exit point from the plugin.
+ *
+ * @param cls closure (our `struct Plugin`)
+ * @return NULL
  */
  */
-void
-release_module_dstore_sqlite ()
+void *
+libgnunet_plugin_datacache_sqlite_done (void *cls)
 {
 {
-  UNLINK (fn);
-  GNUNET_free (fn);
-  GNUNET_free (fn_utf8);
-  fn = NULL;
-  if (bloom != NULL)
-    {
-      GNUNET_bloomfilter_free (bloom);
-      bloom = NULL;
-    }
-  UNLINK (bloom_name);
-  GNUNET_free (bloom_name);
-  bloom_name = NULL;
-  if (stats != NULL)
+  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 ( (NULL != plugin->fn) &&
+       (0 != UNLINK (plugin->fn)) )
+    LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING,
+                       "unlink",
+                       plugin->fn);
+  GNUNET_free_non_null (plugin->fn);
+#endif
+  result = sqlite3_close (plugin->dbh);
+#if SQLITE_VERSION_NUMBER >= 3007000
+  if (SQLITE_BUSY == result)
+  {
+    LOG (GNUNET_ERROR_TYPE_WARNING,
+         _("Tried to close sqlite without finalizing all prepared statements.\n"));
+    stmt = sqlite3_next_stmt (plugin->dbh, NULL);
+    while (NULL != stmt)
     {
     {
-      coreAPI->service_release (stats);
-      stats = NULL;
+      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);
     }
     }
-#if DEBUG_SQLITE
-  GNUNET_GE_LOG (coreAPI->ectx,
-                 GNUNET_GE_DEBUG | GNUNET_GE_REQUEST | GNUNET_GE_USER,
-                 "SQLite Dstore: database shutdown\n");
+    result = sqlite3_close (plugin->dbh);
+  }
 #endif
 #endif
-  GNUNET_mutex_destroy (lock);
-  coreAPI = NULL;
+  if (SQLITE_OK != result)
+    LOG_SQLITE (plugin->dbh,
+                GNUNET_ERROR_TYPE_ERROR,
+                "sqlite3_close");
+
+#if WINDOWS && !defined(__CYGWIN__)
+  if ( (NULL != plugin->fn) &&
+       (0 != UNLINK (plugin->fn)) )
+    LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING,
+                       "unlink",
+                       plugin->fn);
+  GNUNET_free_non_null (plugin->fn);
+#endif
+  GNUNET_free (plugin);
+  GNUNET_free (api);
+  return NULL;
 }
 
 }
 
-/* end of dstore.c */
+
+
+/* end of plugin_datacache_sqlite.c */