/**
 * Get all of the keys in the datastore.
 *
 * @param cls closure
 * @param proc function to call on each key
 * @param proc_cls closure for proc
 */
static void
sqlite_plugin_get_keys (void *cls,
			PluginKeyProcessor proc,
			void *proc_cls)
{
  struct Plugin *plugin = cls;
  const GNUNET_HashCode *key;
  sqlite3_stmt *stmt;
  int ret;

  GNUNET_assert (proc != NULL);
  if (sq_prepare (plugin->dbh, "SELECT hash FROM gn090", &stmt) != SQLITE_OK)
  {
    LOG_SQLITE (plugin, NULL, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
		"sqlite_prepare");
    return;
  }
  while (SQLITE_ROW == (ret = sqlite3_step (stmt)))
  {
    key = sqlite3_column_blob (stmt, 1);
    if (sizeof (GNUNET_HashCode) == sqlite3_column_bytes (stmt, 1))
      proc (proc_cls, key, 1);
  }
  if (SQLITE_DONE != ret)
    LOG_SQLITE (plugin, NULL, GNUNET_ERROR_TYPE_ERROR, "sqlite_step");
  sqlite3_finalize (stmt);
}
/**
 * Get an estimate of how much space the database is
 * currently using.
 *
 * @param cls the 'struct Plugin'
 * @return the size of the database on disk (estimate)
 */
static unsigned long long
sqlite_plugin_estimate_size (void *cls)
{
  struct Plugin *plugin = cls;
  sqlite3_stmt *stmt;
  uint64_t pages;
  uint64_t page_size;

#if ENULL_DEFINED
  char *e;
#endif

  if (SQLITE_VERSION_NUMBER < 3006000)
  {
    GNUNET_log_from (GNUNET_ERROR_TYPE_WARNING, "datastore-sqlite",
                     _
                     ("sqlite version to old to determine size, assuming zero\n"));
    return 0;
  }
  CHECK (SQLITE_OK == sqlite3_exec (plugin->dbh, "VACUUM", NULL, NULL, ENULL));
  CHECK (SQLITE_OK ==
         sqlite3_exec (plugin->dbh, "PRAGMA auto_vacuum=INCREMENTAL", NULL,
                       NULL, ENULL));
  CHECK (SQLITE_OK == sq_prepare (plugin->dbh, "PRAGMA page_count", &stmt));
  if (SQLITE_ROW == sqlite3_step (stmt))
    pages = sqlite3_column_int64 (stmt, 0);
  else
    pages = 0;
  sqlite3_finalize (stmt);
  CHECK (SQLITE_OK == sq_prepare (plugin->dbh, "PRAGMA page_size", &stmt));
  CHECK (SQLITE_ROW == sqlite3_step (stmt));
  page_size = sqlite3_column_int64 (stmt, 0);
  sqlite3_finalize (stmt);
  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
              _
              ("Using sqlite page utilization to estimate payload (%llu pages of size %llu bytes)\n"),
              (unsigned long long) pages, (unsigned long long) page_size);
  return pages * page_size;
}
示例#3
0
/**
 * Initialize the database connections and associated
 * data structures (create tables and indices
 * as needed as well).
 *
 * @param plugin the plugin context (state for this module)
 * @return #GNUNET_OK on success
 */
static int
database_setup (struct Plugin *plugin)
{
  sqlite3_stmt *stmt;
  char *afsdir;
#if ENULL_DEFINED
  char *e;
#endif

  if (GNUNET_OK !=
      GNUNET_CONFIGURATION_get_value_filename (plugin->cfg, "namecache-sqlite",
                                               "FILENAME", &afsdir))
  {
    GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
			       "namecache-sqlite", "FILENAME");
    return GNUNET_SYSERR;
  }
  if (GNUNET_OK != GNUNET_DISK_file_test (afsdir))
  {
    if (GNUNET_OK != GNUNET_DISK_directory_create_for_file (afsdir))
    {
      GNUNET_break (0);
      GNUNET_free (afsdir);
      return GNUNET_SYSERR;
    }
  }
  /* afsdir should be UTF-8-encoded. If it isn't, it's a bug */
  plugin->fn = afsdir;

  /* Open database and precompile statements */
  if (sqlite3_open (plugin->fn, &plugin->dbh) != SQLITE_OK)
  {
    LOG (GNUNET_ERROR_TYPE_ERROR,
	 _("Unable to initialize SQLite: %s.\n"),
	 sqlite3_errmsg (plugin->dbh));
    return GNUNET_SYSERR;
  }
  CHECK (SQLITE_OK ==
         sqlite3_exec (plugin->dbh, "PRAGMA temp_store=MEMORY", NULL, NULL,
                       ENULL));
  CHECK (SQLITE_OK ==
         sqlite3_exec (plugin->dbh, "PRAGMA synchronous=NORMAL", NULL, NULL,
                       ENULL));
  CHECK (SQLITE_OK ==
         sqlite3_exec (plugin->dbh, "PRAGMA legacy_file_format=OFF", NULL, NULL,
                       ENULL));
  CHECK (SQLITE_OK ==
         sqlite3_exec (plugin->dbh, "PRAGMA auto_vacuum=INCREMENTAL", NULL,
                       NULL, ENULL));
  CHECK (SQLITE_OK ==
         sqlite3_exec (plugin->dbh, "PRAGMA encoding=\"UTF-8\"", NULL,
                       NULL, ENULL));
  CHECK (SQLITE_OK ==
         sqlite3_exec (plugin->dbh, "PRAGMA locking_mode=EXCLUSIVE", NULL, NULL,
                       ENULL));
  CHECK (SQLITE_OK ==
         sqlite3_exec (plugin->dbh, "PRAGMA page_size=4092", NULL, NULL,
                       ENULL));

  CHECK (SQLITE_OK == sqlite3_busy_timeout (plugin->dbh, BUSY_TIMEOUT_MS));


  /* Create tables */
  CHECK (SQLITE_OK ==
         sq_prepare (plugin->dbh,
                     "SELECT 1 FROM sqlite_master WHERE tbl_name = 'ns096blocks'",
                     &stmt));
  if ((sqlite3_step (stmt) == SQLITE_DONE) &&
      (sqlite3_exec
       (plugin->dbh,
        "CREATE TABLE ns096blocks ("
        " query BLOB NOT NULL DEFAULT '',"
        " block BLOB NOT NULL DEFAULT '',"
        " expiration_time INT8 NOT NULL DEFAULT 0"
	")",
	NULL, NULL, NULL) != SQLITE_OK))
  {
    LOG_SQLITE (plugin, GNUNET_ERROR_TYPE_ERROR, "sqlite3_exec");
    sqlite3_finalize (stmt);
    return GNUNET_SYSERR;
  }
  sqlite3_finalize (stmt);
  create_indices (plugin->dbh);

  if ((sq_prepare
       (plugin->dbh,
        "INSERT INTO ns096blocks (query,block,expiration_time) VALUES (?, ?, ?)",
        &plugin->cache_block) != SQLITE_OK) ||
      (sq_prepare
       (plugin->dbh,
        "DELETE FROM ns096blocks WHERE expiration_time<?",
        &plugin->expire_blocks) != SQLITE_OK) ||
      (sq_prepare
       (plugin->dbh,
        "DELETE FROM ns096blocks WHERE query=? AND expiration_time<=?",
        &plugin->delete_block) != SQLITE_OK) ||
      (sq_prepare
       (plugin->dbh,
        "SELECT block FROM ns096blocks WHERE query=? ORDER BY expiration_time DESC LIMIT 1",
        &plugin->lookup_block) != SQLITE_OK)
      )
  {
    LOG_SQLITE (plugin,GNUNET_ERROR_TYPE_ERROR, "precompiling");
    return GNUNET_SYSERR;
  }
  return GNUNET_OK;
}
/**
 * Get results for a particular key in the datastore.
 *
 * @param cls closure
 * @param offset offset (mod count).
 * @param key key to match, never NULL
 * @param vhash hash of the value, maybe NULL (to
 *        match all values that have the right key).
 *        Note that for DBlocks there is no difference
 *        betwen key and vhash, but for other blocks
 *        there may be!
 * @param type entries of which type are relevant?
 *     Use 0 for any type.
 * @param proc function to call on each matching value;
 *        will be called once with a NULL value at the end
 * @param proc_cls closure for proc
 */
static void
sqlite_plugin_get_key (void *cls, uint64_t offset, const GNUNET_HashCode * key,
                       const GNUNET_HashCode * vhash,
                       enum GNUNET_BLOCK_Type type, PluginDatumProcessor proc,
                       void *proc_cls)
{
  struct Plugin *plugin = cls;
  int ret;
  int total;
  int limit_off;
  unsigned int sqoff;
  sqlite3_stmt *stmt;
  char scratch[256];

  GNUNET_assert (proc != NULL);
  GNUNET_assert (key != NULL);
  GNUNET_snprintf (scratch, sizeof (scratch),
                   "SELECT count(*) FROM gn090 WHERE hash=?%s%s",
                   vhash == NULL ? "" : " AND vhash=?",
                   type == 0 ? "" : " AND type=?");
  if (sq_prepare (plugin->dbh, scratch, &stmt) != SQLITE_OK)
  {
    LOG_SQLITE (plugin, NULL, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
                "sqlite_prepare");
    proc (proc_cls, NULL, 0, NULL, 0, 0, 0, GNUNET_TIME_UNIT_ZERO_ABS, 0);
    return;
  }
  sqoff = 1;
  ret =
      sqlite3_bind_blob (stmt, sqoff++, key, sizeof (GNUNET_HashCode),
                         SQLITE_TRANSIENT);
  if ((vhash != NULL) && (ret == SQLITE_OK))
    ret =
        sqlite3_bind_blob (stmt, sqoff++, vhash, sizeof (GNUNET_HashCode),
                           SQLITE_TRANSIENT);
  if ((type != 0) && (ret == SQLITE_OK))
    ret = sqlite3_bind_int (stmt, sqoff++, type);
  if (SQLITE_OK != ret)
  {
    LOG_SQLITE (plugin, NULL, GNUNET_ERROR_TYPE_ERROR, "sqlite_bind");
    sqlite3_finalize (stmt);
    proc (proc_cls, NULL, 0, NULL, 0, 0, 0, GNUNET_TIME_UNIT_ZERO_ABS, 0);
    return;
  }
  ret = sqlite3_step (stmt);
  if (ret != SQLITE_ROW)
  {
    LOG_SQLITE (plugin, NULL, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
                "sqlite_step");
    sqlite3_finalize (stmt);
    proc (proc_cls, NULL, 0, NULL, 0, 0, 0, GNUNET_TIME_UNIT_ZERO_ABS, 0);
    return;
  }
  total = sqlite3_column_int (stmt, 0);
  sqlite3_finalize (stmt);
  if (0 == total)
  {
    proc (proc_cls, NULL, 0, NULL, 0, 0, 0, GNUNET_TIME_UNIT_ZERO_ABS, 0);
    return;
  }
  limit_off = (int) (offset % total);
  if (limit_off < 0)
    limit_off += total;
  GNUNET_snprintf (scratch, sizeof (scratch),
                   "SELECT type, prio, anonLevel, expire, hash, value, _ROWID_ "
                   "FROM gn090 WHERE hash=?%s%s "
                   "ORDER BY _ROWID_ ASC LIMIT 1 OFFSET ?",
                   vhash == NULL ? "" : " AND vhash=?",
                   type == 0 ? "" : " AND type=?");
  if (sq_prepare (plugin->dbh, scratch, &stmt) != SQLITE_OK)
  {
    LOG_SQLITE (plugin, NULL, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
                "sqlite_prepare");
    proc (proc_cls, NULL, 0, NULL, 0, 0, 0, GNUNET_TIME_UNIT_ZERO_ABS, 0);
    return;
  }
  sqoff = 1;
  ret =
      sqlite3_bind_blob (stmt, sqoff++, key, sizeof (GNUNET_HashCode),
                         SQLITE_TRANSIENT);
  if ((vhash != NULL) && (ret == SQLITE_OK))
    ret =
        sqlite3_bind_blob (stmt, sqoff++, vhash, sizeof (GNUNET_HashCode),
                           SQLITE_TRANSIENT);
  if ((type != 0) && (ret == SQLITE_OK))
    ret = sqlite3_bind_int (stmt, sqoff++, type);
  if (ret == SQLITE_OK)
    ret = sqlite3_bind_int64 (stmt, sqoff++, limit_off);
  if (ret != SQLITE_OK)
  {
    LOG_SQLITE (plugin, NULL, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
                "sqlite_bind");
    proc (proc_cls, NULL, 0, NULL, 0, 0, 0, GNUNET_TIME_UNIT_ZERO_ABS, 0);
    return;
  }
  execute_get (plugin, stmt, proc, proc_cls);
  sqlite3_finalize (stmt);
}
/**
 * Initialize the database connections and associated
 * data structures (create tables and indices
 * as needed as well).
 *
 * @param cfg our configuration
 * @param plugin the plugin context (state for this module)
 * @return GNUNET_OK on success
 */
static int
database_setup (const struct GNUNET_CONFIGURATION_Handle *cfg,
                struct Plugin *plugin)
{
  sqlite3_stmt *stmt;
  char *afsdir;

#if ENULL_DEFINED
  char *e;
#endif

  if (GNUNET_OK !=
      GNUNET_CONFIGURATION_get_value_filename (cfg, "datastore-sqlite",
                                               "FILENAME", &afsdir))
  {
    GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, "sqlite",
                     _
                     ("Option `%s' in section `%s' missing in configuration!\n"),
                     "FILENAME", "datastore-sqlite");
    return GNUNET_SYSERR;
  }
  if (GNUNET_OK != GNUNET_DISK_file_test (afsdir))
  {
    if (GNUNET_OK != GNUNET_DISK_directory_create_for_file (afsdir))
    {
      GNUNET_break (0);
      GNUNET_free (afsdir);
      return GNUNET_SYSERR;
    }
    /* database is new or got deleted, reset payload to zero! */
    plugin->env->duc (plugin->env->cls, 0);
  }
#ifdef ENABLE_NLS
  plugin->fn =
      GNUNET_STRINGS_to_utf8 (afsdir, strlen (afsdir), nl_langinfo (CODESET));
#else
  plugin->fn = GNUNET_STRINGS_to_utf8 (afsdir, strlen (afsdir), "UTF-8");       /* good luck */
#endif
  GNUNET_free (afsdir);

  /* Open database and precompile statements */
  if (sqlite3_open (plugin->fn, &plugin->dbh) != SQLITE_OK)
  {
    GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, "sqlite",
                     _("Unable to initialize SQLite: %s.\n"),
                     sqlite3_errmsg (plugin->dbh));
    return GNUNET_SYSERR;
  }
  CHECK (SQLITE_OK ==
         sqlite3_exec (plugin->dbh, "PRAGMA temp_store=MEMORY", NULL, NULL,
                       ENULL));
  CHECK (SQLITE_OK ==
         sqlite3_exec (plugin->dbh, "PRAGMA synchronous=OFF", NULL, NULL,
                       ENULL));
  CHECK (SQLITE_OK ==
         sqlite3_exec (plugin->dbh, "PRAGMA legacy_file_format=OFF", NULL, NULL,
                       ENULL));
  CHECK (SQLITE_OK ==
         sqlite3_exec (plugin->dbh, "PRAGMA auto_vacuum=INCREMENTAL", NULL,
                       NULL, ENULL));
  CHECK (SQLITE_OK ==
         sqlite3_exec (plugin->dbh, "PRAGMA locking_mode=EXCLUSIVE", NULL, NULL,
                       ENULL));
  CHECK (SQLITE_OK ==
         sqlite3_exec (plugin->dbh, "PRAGMA count_changes=OFF", NULL, NULL,
                       ENULL));
  CHECK (SQLITE_OK ==
         sqlite3_exec (plugin->dbh, "PRAGMA page_size=4092", NULL, NULL,
                       ENULL));

  CHECK (SQLITE_OK == sqlite3_busy_timeout (plugin->dbh, BUSY_TIMEOUT_MS));


  /* We have to do it here, because otherwise precompiling SQL might fail */
  CHECK (SQLITE_OK ==
         sq_prepare (plugin->dbh,
                     "SELECT 1 FROM sqlite_master WHERE tbl_name = 'gn090'",
                     &stmt));
  if ((sqlite3_step (stmt) == SQLITE_DONE) &&
      (sqlite3_exec
       (plugin->dbh,
        "CREATE TABLE gn090 (" "  repl INT4 NOT NULL DEFAULT 0,"
        "  type INT4 NOT NULL DEFAULT 0," "  prio INT4 NOT NULL DEFAULT 0,"
        "  anonLevel INT4 NOT NULL DEFAULT 0,"
        "  expire INT8 NOT NULL DEFAULT 0," "  rvalue INT8 NOT NULL,"
        "  hash TEXT NOT NULL DEFAULT ''," "  vhash TEXT NOT NULL DEFAULT '',"
        "  value BLOB NOT NULL DEFAULT '')", NULL, NULL, NULL) != SQLITE_OK))
  {
    LOG_SQLITE (plugin, NULL, GNUNET_ERROR_TYPE_ERROR, "sqlite3_exec");
    sqlite3_finalize (stmt);
    return GNUNET_SYSERR;
  }
  sqlite3_finalize (stmt);
  create_indices (plugin->dbh);

  if ((sq_prepare
       (plugin->dbh,
        "UPDATE gn090 "
        "SET prio = prio + ?, expire = MAX(expire,?) WHERE _ROWID_ = ?",
        &plugin->updPrio) != SQLITE_OK) ||
      (sq_prepare
       (plugin->dbh,
        "UPDATE gn090 " "SET repl = MAX (0, repl - 1) WHERE _ROWID_ = ?",
        &plugin->updRepl) != SQLITE_OK) ||
      (sq_prepare
       (plugin->dbh,
        "SELECT type,prio,anonLevel,expire,hash,value,_ROWID_ " "FROM gn090 "
#if SQLITE_VERSION_NUMBER >= 3007000
        "INDEXED BY idx_repl_rvalue "
#endif
        "WHERE repl=?2 AND " " (rvalue>=?1 OR "
        "  NOT EXISTS (SELECT 1 FROM gn090 "
#if SQLITE_VERSION_NUMBER >= 3007000
        "INDEXED BY idx_repl_rvalue "
#endif
        "WHERE repl=?2 AND rvalue>=?1 LIMIT 1) ) "
        "ORDER BY rvalue ASC LIMIT 1", &plugin->selRepl) != SQLITE_OK) ||
      (sq_prepare (plugin->dbh, "SELECT MAX(repl) FROM gn090"
#if SQLITE_VERSION_NUMBER >= 3007000
                   " INDEXED BY idx_repl_rvalue"
#endif
                   "", &plugin->maxRepl) != SQLITE_OK) ||
      (sq_prepare
       (plugin->dbh,
        "SELECT type,prio,anonLevel,expire,hash,value,_ROWID_ " "FROM gn090 "
#if SQLITE_VERSION_NUMBER >= 3007000
        "INDEXED BY idx_expire "
#endif
        "WHERE NOT EXISTS (SELECT 1 FROM gn090 WHERE expire < ?1 LIMIT 1) OR (expire < ?1) "
        "ORDER BY expire ASC LIMIT 1", &plugin->selExpi) != SQLITE_OK) ||
      (sq_prepare
       (plugin->dbh,
        "SELECT type,prio,anonLevel,expire,hash,value,_ROWID_ " "FROM gn090 "
#if SQLITE_VERSION_NUMBER >= 3007000
        "INDEXED BY idx_anon_type_hash "
#endif
        "WHERE (anonLevel = 0 AND type=?1) "
        "ORDER BY hash DESC LIMIT 1 OFFSET ?2",
        &plugin->selZeroAnon) != SQLITE_OK) ||
      (sq_prepare
       (plugin->dbh,
        "INSERT INTO gn090 (repl, type, prio, anonLevel, expire, rvalue, hash, vhash, value) "
        "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)",
        &plugin->insertContent) != SQLITE_OK) ||
      (sq_prepare
       (plugin->dbh, "DELETE FROM gn090 WHERE _ROWID_ = ?",
        &plugin->delRow) != SQLITE_OK))
  {
    LOG_SQLITE (plugin, NULL, GNUNET_ERROR_TYPE_ERROR, "precompiling");
    return GNUNET_SYSERR;
  }

  return GNUNET_OK;
}