fix
[oweals/gnunet.git] / src / datacache / plugin_datacache_sqlite.c
index 98bcae7889904b78c7de8b8a6f64c7227e252d38..4c429c1b4576893e8b5a60f84386f49ef77a8447 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
-     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
@@ -25,7 +25,7 @@
  */
 #include "platform.h"
 #include "gnunet_util_lib.h"
-#include "plugin_datacache.h"
+#include "gnunet_datacache_plugin.h"
 #include <sqlite3.h>
 
 #define DEBUG_DATACACHE_SQLITE GNUNET_NO
@@ -94,16 +94,17 @@ 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
  */
-static uint32_t 
+static size_t 
 sqlite_plugin_put (void *cls,
                   const GNUNET_HashCode * key,
-                  uint32_t size,
+                  size_t size,
                   const char *data,
                   enum GNUNET_BLOCK_Type type,
                   struct GNUNET_TIME_Absolute discard_time)
 {
   struct Plugin *plugin = cls;
   sqlite3_stmt *stmt;
+  int64_t dval;
 
 #if DEBUG_DATACACHE_SQLITE
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
@@ -111,8 +112,11 @@ sqlite_plugin_put (void *cls,
              "PUT",
              (unsigned int) size,
              GNUNET_h2s (key),
-             (unsigned long long) GNUNET_TIME_absolute_get_remaining (discard_time).value);
+             (unsigned long long) GNUNET_TIME_absolute_get_remaining (discard_time).rel_value);
 #endif
+  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) "
@@ -125,7 +129,7 @@ sqlite_plugin_put (void *cls,
       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_int64 (stmt, 2, dval)) ||
        (SQLITE_OK != sqlite3_bind_blob (stmt, 3, key, sizeof (GNUNET_HashCode),
                                        SQLITE_TRANSIENT)) ||
        (SQLITE_OK != sqlite3_bind_blob (stmt, 4, data, size, SQLITE_TRANSIENT)))
@@ -180,6 +184,7 @@ sqlite_plugin_get (void *cls,
   unsigned int off;
   unsigned int total;
   char scratch[256];
+  int64_t ntime;
 
   now = GNUNET_TIME_absolute_get ();
 #if DEBUG_DATACACHE_SQLITE
@@ -201,7 +206,9 @@ sqlite_plugin_get (void *cls,
   sqlite3_bind_blob (stmt, 1, key, sizeof (GNUNET_HashCode),
                      SQLITE_TRANSIENT);
   sqlite3_bind_int (stmt, 2, type);
-  sqlite3_bind_int64 (stmt, 3, now.value);
+  ntime = (int64_t) now.abs_value;
+  GNUNET_assert (ntime >= 0);
+  sqlite3_bind_int64 (stmt, 3, now.abs_value);
   if (SQLITE_ROW != sqlite3_step (stmt))
     {
       LOG_SQLITE (plugin->dbh,
@@ -235,12 +242,15 @@ sqlite_plugin_get (void *cls,
       sqlite3_bind_blob (stmt, 1, key, sizeof (GNUNET_HashCode),
                          SQLITE_TRANSIENT);
       sqlite3_bind_int (stmt, 2, type);
-      sqlite3_bind_int64 (stmt, 3, now.value);
+      sqlite3_bind_int64 (stmt, 3, now.abs_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);
+      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++;
       if (GNUNET_OK != iter (iter_cls,
                             exp,
@@ -273,6 +283,8 @@ sqlite_plugin_del (void *cls)
   unsigned int dtype;
   sqlite3_stmt *stmt;
   sqlite3_stmt *dstmt;
+  char blob[65536];
+  GNUNET_HashCode hc;
 
 #if DEBUG_DATACACHE_SQLITE
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
@@ -281,21 +293,15 @@ sqlite_plugin_del (void *cls)
 #endif
   stmt = NULL;
   dstmt = NULL;
-  if ((sq_prepare (plugin->dbh,
+  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))
+                   &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));
-      if (dstmt != NULL)
-        sqlite3_finalize (dstmt);
       if (stmt != NULL)
-        sqlite3_finalize (stmt);
+        (void) sqlite3_finalize (stmt);
       return GNUNET_SYSERR;
     }
   if (SQLITE_ROW != sqlite3_step (stmt))
@@ -304,37 +310,69 @@ sqlite_plugin_del (void *cls)
                  _("`%s' failed at %s:%d with error: %s\n"),
                  "sqlite3_step", __FILE__, __LINE__,
                  sqlite3_errmsg (plugin->dbh));
-      sqlite3_finalize (dstmt);
-      sqlite3_finalize (stmt);
+      (void) sqlite3_finalize (stmt);
       return GNUNET_SYSERR;
     }
   dtype = sqlite3_column_int (stmt, 0);
   GNUNET_break (sqlite3_column_bytes (stmt, 1) == sizeof (GNUNET_HashCode));
   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);
+  GNUNET_assert (dsize <= sizeof (blob));
+  memcpy (blob, sqlite3_column_blob (stmt, 2), dsize);
+  memcpy (&hc, sqlite3_column_blob (stmt, 1), sizeof (GNUNET_HashCode));
+  if (SQLITE_OK != sqlite3_finalize (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));    
+  if (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 (stmt != NULL)
+        (void) sqlite3_finalize (stmt);
+      return GNUNET_SYSERR;
+    }
+  if ( (SQLITE_OK !=
+       sqlite3_bind_blob (dstmt,
+                          1, &hc,
+                          sizeof (GNUNET_HashCode),
+                          SQLITE_TRANSIENT)) ||
+       (SQLITE_OK !=
+       sqlite3_bind_blob (dstmt,
+                          2, blob,
+                          dsize,
+                          SQLITE_TRANSIENT)) ||
+       (SQLITE_OK != 
+       sqlite3_bind_int (dstmt, 3, dtype)) )
+    {
+      GNUNET_log (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
+                 _("`%s' failed at %s:%d with error: %s\n"),
+                 "sqlite3_bind", __FILE__, __LINE__,
+                 sqlite3_errmsg (plugin->dbh));    
+      (void) sqlite3_finalize (dstmt);
+      return GNUNET_SYSERR;
+    }
   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);
+      (void) sqlite3_finalize (dstmt);
       return GNUNET_SYSERR;
     }
   plugin->env->delete_notify (plugin->env->cls,
-                             sqlite3_column_blob (stmt, 1),
+                             &hc,
                              dsize + OVERHEAD);
-  sqlite3_finalize (dstmt);
-  sqlite3_finalize (stmt);
+  if (SQLITE_OK != sqlite3_finalize (dstmt))
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
+               _("`%s' failed at %s:%d with error: %s\n"),
+               "sqlite3_finalize", __FILE__, __LINE__,
+               sqlite3_errmsg (plugin->dbh));    
   return GNUNET_OK;
 }
 
@@ -362,13 +400,12 @@ libgnunet_plugin_datacache_sqlite_init (void *cls)
       GNUNET_break (0);
       return NULL;
     }
-  fn_utf8 = GNUNET_STRINGS_to_utf8 (fn, strlen (fn),
 #ifdef ENABLE_NLS
-                                   nl_langinfo (CODESET)
+  fn_utf8 = GNUNET_STRINGS_to_utf8 (fn, strlen (fn), nl_langinfo (CODESET));
 #else
-                                   "UTF-8"      /* good luck */
+  /* good luck */
+  fn_utf8 = GNUNET_STRINGS_to_utf8 (fn, strlen (fn), "UTF-8");
 #endif
-    );
   if (SQLITE_OK != sqlite3_open (fn_utf8, &dbh))
     {
       GNUNET_free (fn);
@@ -378,6 +415,8 @@ libgnunet_plugin_datacache_sqlite_init (void *cls)
   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");
@@ -401,6 +440,7 @@ libgnunet_plugin_datacache_sqlite_init (void *cls)
                    "sqlite", _("Sqlite datacache running\n"));
   return api;
 }
+// explain SELECT type FROM gn090 WHERE NOT EXISTS (SELECT 1 from gn090 WHERE expire < 42 LIMIT 1) OR expire < 42 ORDER BY repl DESC, Random() LIMIT 1;
 
 
 /**
@@ -414,13 +454,56 @@ 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))
+    GNUNET_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)
+    {
+      GNUNET_log_from (GNUNET_ERROR_TYPE_WARNING, 
+                      "sqlite",
+                      _("Tried to close sqlite without finalizing all prepared statements.\n"));
+      stmt = sqlite3_next_stmt(plugin->dbh, NULL); 
+      while (stmt != NULL)
+        {
+#if DEBUG_SQLITE
+          GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
+                          "sqlite", "Closing statement %p\n", stmt);
+#endif
+          result = sqlite3_finalize(stmt);
+#if DEBUG_SQLITE
+          if (result != SQLITE_OK)
+           GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
+                            "sqlite",
+                            "Failed to close statement %p: %d\n", stmt, result);
+#endif
+         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");
 
+#if WINDOWS && !defined(__CYGWIN__)
   if (0 != UNLINK (plugin->fn))
     GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
                              "unlink", 
                              plugin->fn);
   GNUNET_free (plugin->fn);
-  sqlite3_close (plugin->dbh);
+#endif
   GNUNET_free (plugin);
   GNUNET_free (api);
   return NULL;