Пример #1
0
/**
 * Get the block for a particular zone and label in the
 * datastore.  Will return at most one result to the iterator.
 *
 * @param cls closure (internal context for the plugin)
 * @param query hash of public key derived from the zone and the label
 * @param iter function to call with the result
 * @param iter_cls closure for @a iter
 * @return #GNUNET_OK on success, #GNUNET_NO if there were no results, #GNUNET_SYSERR on error
 */
static int
namecache_postgres_lookup_block (void *cls,
                                 const struct GNUNET_HashCode *query,
                                 GNUNET_NAMECACHE_BlockCallback iter, void *iter_cls)
{
  struct Plugin *plugin = cls;
  struct GNUNET_PQ_QueryParam params[] = { 
    GNUNET_PQ_query_param_auto_from_type (query),
    GNUNET_PQ_query_param_end
  };
  PGresult *res;
  unsigned int cnt;
  size_t bsize;
  const struct GNUNET_GNSRECORD_Block *block;

  res = GNUNET_PQ_exec_prepared (plugin->dbh,
				 "lookup_block",
				 params);
  if (GNUNET_OK !=
      GNUNET_POSTGRES_check_result (plugin->dbh, res, PGRES_TUPLES_OK,
                                    "PQexecPrepared",
				    "lookup_block"))
  {
    LOG (GNUNET_ERROR_TYPE_DEBUG,
	 "Failing lookup (postgres error)\n");
    return GNUNET_SYSERR;
  }
  if (0 == (cnt = PQntuples (res)))
  {
    /* no result */
    LOG (GNUNET_ERROR_TYPE_DEBUG,
	 "Ending iteration (no more results)\n");
    PQclear (res);
    return GNUNET_NO;
  }
  GNUNET_assert (1 == cnt);
  GNUNET_assert (1 != PQnfields (res));
  bsize = PQgetlength (res, 0, 0);
  block = (const struct GNUNET_GNSRECORD_Block *) PQgetvalue (res, 0, 0);
  if ( (bsize < sizeof (*block)) ||
       (bsize != ntohl (block->purpose.size) +
        sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey) +
        sizeof (struct GNUNET_CRYPTO_EcdsaSignature)) )
  {
    GNUNET_break (0);
    LOG (GNUNET_ERROR_TYPE_DEBUG,
	 "Failing lookup (corrupt block)\n");
    PQclear (res);
    return GNUNET_SYSERR;
  }
  iter (iter_cls, block);
  PQclear (res);
  return GNUNET_OK;
}
Пример #2
0
/**
 * 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
postgres_plugin_del (void *cls)
{
  struct Plugin *plugin = cls;
  uint32_t size;
  uint32_t oid;
  struct GNUNET_HashCode key;
  PGresult *res;

  res = PQexecPrepared (plugin->dbh,
                        "getm",
                        0, NULL, NULL, NULL, 1);
  if (GNUNET_OK !=
      GNUNET_POSTGRES_check_result (plugin->dbh,
                                    res,
                                    PGRES_TUPLES_OK,
                                    "PQexecPrepared",
                                    "getm"))
  {
    LOG (GNUNET_ERROR_TYPE_DEBUG,
	 "Ending iteration (postgres error)\n");
    return 0;
  }
  if (0 == PQntuples (res))
  {
    /* no result */
    LOG (GNUNET_ERROR_TYPE_DEBUG,
	 "Ending iteration (no more results)\n");
    PQclear (res);
    return GNUNET_SYSERR;
  }
  if ((3 != PQnfields (res)) || (sizeof (size) != PQfsize (res, 0)) ||
      (sizeof (oid) != PQfsize (res, 1)) ||
      (sizeof (struct GNUNET_HashCode) != PQgetlength (res, 0, 2)))
  {
    GNUNET_break (0);
    PQclear (res);
    return 0;
  }
  size = ntohl (*(uint32_t *) PQgetvalue (res, 0, 0));
  oid = ntohl (*(uint32_t *) PQgetvalue (res, 0, 1));
  memcpy (&key, PQgetvalue (res, 0, 2), sizeof (struct GNUNET_HashCode));
  PQclear (res);
  if (GNUNET_OK !=
      GNUNET_POSTGRES_delete_by_rowid (plugin->dbh,
                                       "delrow",
                                       oid))
    return GNUNET_SYSERR;
  plugin->num_items--;
  plugin->env->delete_notify (plugin->env->cls,
                              &key,
                              size + OVERHEAD);
  return GNUNET_OK;
}
Пример #3
0
/**
 * Store an item in the datastore.
 *
 * @param cls closure with the `struct Plugin`
 * @param key key for the item
 * @param size number of bytes in data
 * @param data content stored
 * @param type type of the content
 * @param priority priority of the content
 * @param anonymity anonymity-level for the content
 * @param replication replication-level for the content
 * @param expiration expiration time for the content
 * @param cont continuation called with success or failure status
 * @param cont_cls continuation closure
 */
static void
postgres_plugin_put (void *cls, 
		     const struct GNUNET_HashCode *key,
		     uint32_t size,
                     const void *data, 
		     enum GNUNET_BLOCK_Type type,
                     uint32_t priority, 
		     uint32_t anonymity,
                     uint32_t replication,
                     struct GNUNET_TIME_Absolute expiration, 
		     PluginPutCont cont,
                     void *cont_cls)
{
  struct Plugin *plugin = cls;
  uint32_t utype = type;
  struct GNUNET_HashCode vhash;
  PGresult *ret;
  struct GNUNET_PQ_QueryParam params[] = {
    GNUNET_PQ_query_param_uint32 (&replication),
    GNUNET_PQ_query_param_uint32 (&utype),
    GNUNET_PQ_query_param_uint32 (&priority),
    GNUNET_PQ_query_param_uint32 (&anonymity),
    GNUNET_PQ_query_param_absolute_time (&expiration),
    GNUNET_PQ_query_param_auto_from_type (key),
    GNUNET_PQ_query_param_auto_from_type (&vhash),
    GNUNET_PQ_query_param_fixed_size (data, size),
    GNUNET_PQ_query_param_end
  };

  GNUNET_CRYPTO_hash (data, size, &vhash);
  ret = GNUNET_PQ_exec_prepared (plugin->dbh,
				 "put",
				 params);
  if (GNUNET_OK !=
      GNUNET_POSTGRES_check_result (plugin->dbh, 
				    ret, 
				    PGRES_COMMAND_OK, 
				    "PQexecPrepared", "put"))
  {
    cont (cont_cls, key, size, 
	  GNUNET_SYSERR, 
	  _("Postgress exec failure"));
    return;
  }
  PQclear (ret);
  plugin->env->duc (plugin->env->cls, 
		    size + GNUNET_DATASTORE_ENTRY_OVERHEAD);
  GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, 
		   "datastore-postgres",
                   "Stored %u bytes in database\n", 
		   (unsigned int) size);
  cont (cont_cls, key, size, GNUNET_OK, NULL);
}
Пример #4
0
/**
 * Wrapper for the iterator for 'sqlite_plugin_replication_get'.
 * Decrements the replication counter and calls the original
 * iterator.
 *
 * @param cls closure with the `struct ReplCtx *`
 * @param key key for the content
 * @param size number of bytes in @a data
 * @param data content stored
 * @param type type of the content
 * @param priority priority of the content
 * @param anonymity anonymity-level for the content
 * @param expiration expiration time for the content
 * @param uid unique identifier for the datum;
 *        maybe 0 if no unique identifier is available
 * @return #GNUNET_SYSERR to abort the iteration, 
 *         #GNUNET_OK to continue
 *         (continue on call to "next", of course),
 *         #GNUNET_NO to delete the item and continue (if supported)
 */
static int
repl_proc (void *cls,
	   const struct GNUNET_HashCode *key,
	   uint32_t size,
           const void *data,
	   enum GNUNET_BLOCK_Type type, 
	   uint32_t priority,
           uint32_t anonymity, 
	   struct GNUNET_TIME_Absolute expiration,
           uint64_t uid)
{
  struct ReplCtx *rc = cls;
  struct Plugin *plugin = rc->plugin;
  int ret;
  uint32_t oid = (uint32_t) uid;
  struct GNUNET_PQ_QueryParam params[] = { 
    GNUNET_PQ_query_param_uint32 (&oid),
    GNUNET_PQ_query_param_end
  };
  PGresult *qret;

  ret = rc->proc (rc->proc_cls, 
		  key, 
		  size, data, 
		  type,
		  priority, 
		  anonymity,
		  expiration, uid);
  if (NULL == key)
    return ret;
  qret = GNUNET_PQ_exec_prepared (plugin->dbh,
				  "decrepl",
				  params);
  if (GNUNET_OK !=
      GNUNET_POSTGRES_check_result (plugin->dbh, 
				    qret, 
				    PGRES_COMMAND_OK, 
				    "PQexecPrepared",
				    "decrepl"))
    return GNUNET_SYSERR;
  PQclear (qret);
  return ret;
}
Пример #5
0
/**
 * 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 a path through the network
 * @return 0 if duplicate, -1 on error, number of bytes used otherwise
 */
static ssize_t
postgres_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)
{
  struct Plugin *plugin = cls;
  PGresult *ret;
  uint32_t btype = htonl (type);
  uint64_t bexpi = GNUNET_TIME_absolute_hton (discard_time).abs_value_us__;

  const char *paramValues[] = {
    (const char *) &btype,
    (const char *) &bexpi,
    (const char *) key,
    (const char *) data,
    (const char *) path_info
  };
  int paramLengths[] = {
    sizeof (btype),
    sizeof (bexpi),
    sizeof (struct GNUNET_HashCode),
    size,
    path_info_len * sizeof (struct GNUNET_PeerIdentity)
  };
  const int paramFormats[] = { 1, 1, 1, 1, 1 };

  ret =
      PQexecPrepared (plugin->dbh, "put", 5, paramValues, paramLengths,
                      paramFormats, 1);
  if (GNUNET_OK !=
      GNUNET_POSTGRES_check_result (plugin->dbh, ret,
				    PGRES_COMMAND_OK, "PQexecPrepared", "put"))
    return -1;
  plugin->num_items++;
  PQclear (ret);
  return size + OVERHEAD;
}
Пример #6
0
/**
 * Get an estimate of how much space the database is
 * currently using.
 *
 * @param cls our `struct Plugin *`
 * @return number of bytes used on disk
 */
static void
postgres_plugin_estimate_size (void *cls, unsigned long long *estimate)
{
  struct Plugin *plugin = cls;
  unsigned long long total;
  PGresult *ret;

  if (NULL == estimate)
    return;
  ret =
      PQexecParams (plugin->dbh,
                    "SELECT SUM(LENGTH(value))+256*COUNT(*) FROM gn090", 0,
                    NULL, NULL, NULL, NULL, 1);
  if (GNUNET_OK !=
      GNUNET_POSTGRES_check_result (plugin->dbh, 
				    ret, 
				    PGRES_TUPLES_OK, 
				    "PQexecParams", 
				    "get_size"))
  {
    *estimate = 0;
    return;
  }
  if ((PQntuples (ret) != 1) || (PQnfields (ret) != 1) )
  {
    GNUNET_break (0);
    PQclear (ret);
    *estimate = 0;
    return;
  }
  if (PQgetlength (ret, 0, 0) != sizeof (unsigned long long))
  {
    GNUNET_break (0 == PQgetlength (ret, 0, 0));
    PQclear (ret);
    *estimate = 0;
    return;
  }
  total = GNUNET_ntohll (*(const unsigned long long *) PQgetvalue (ret, 0, 0));
  PQclear (ret);
  *estimate = total;
}
Пример #7
0
/**
 * Cache a block in the datastore.
 *
 * @param cls closure (internal context for the plugin)
 * @param block block to cache
 * @return #GNUNET_OK on success, else #GNUNET_SYSERR
 */
static int
namecache_postgres_cache_block (void *cls,
                                const struct GNUNET_GNSRECORD_Block *block)
{
  struct Plugin *plugin = cls;
  struct GNUNET_HashCode query;
  size_t block_size = ntohl (block->purpose.size) +
    sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey) +
    sizeof (struct GNUNET_CRYPTO_EcdsaSignature);
  struct GNUNET_PQ_QueryParam params[] = { 
    GNUNET_PQ_query_param_auto_from_type (&query),
    GNUNET_PQ_query_param_fixed_size (block, block_size),
    GNUNET_PQ_query_param_absolute_time_nbo (&block->expiration_time),
    GNUNET_PQ_query_param_end
  };
  PGresult *res;

  namecache_postgres_expire_blocks (plugin);
  GNUNET_CRYPTO_hash (&block->derived_key,
		      sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey),
		      &query);
  if (block_size > 64 * 65536)
  {
    GNUNET_break (0);
    return GNUNET_SYSERR;
  }
  delete_old_block (plugin, &query, block->expiration_time);

  res = GNUNET_PQ_exec_prepared (plugin->dbh,
				 "cache_block",
				 params);
  if (GNUNET_OK !=
      GNUNET_POSTGRES_check_result (plugin->dbh,
                                    res,
                                    PGRES_COMMAND_OK,
                                    "PQexecPrepared",
                                    "cache_block"))
    return GNUNET_SYSERR;
  PQclear (res);
  return GNUNET_OK;
}
Пример #8
0
/**
 * Update the priority for a particular key in the datastore.  If
 * the expiration time in value is different than the time found in
 * the datastore, the higher value should be kept.  For the
 * anonymity level, the lower value is to be used.  The specified
 * priority should be added to the existing priority, ignoring the
 * priority in value.
 *
 * Note that it is possible for multiple values to match this put.
 * In that case, all of the respective values are updated.
 *
 * @param cls our `struct Plugin *`
 * @param uid unique identifier of the datum
 * @param delta by how much should the priority
 *     change?  If priority + delta < 0 the
 *     priority should be set to 0 (never go
 *     negative).
 * @param expire new expiration time should be the
 *     MAX of any existing expiration time and
 *     this value
 * @param cont continuation called with success or failure status
 * @param cons_cls continuation closure
 */
static void
postgres_plugin_update (void *cls, 
			uint64_t uid, 
			int delta,
                        struct GNUNET_TIME_Absolute expire,
                        PluginUpdateCont cont,
			void *cont_cls)
{
  struct Plugin *plugin = cls;
  uint32_t idelta = delta;
  uint32_t oid = (uint32_t) uid;
  struct GNUNET_PQ_QueryParam params[] = { 
    GNUNET_PQ_query_param_uint32 (&idelta),
    GNUNET_PQ_query_param_absolute_time (&expire),
    GNUNET_PQ_query_param_uint32 (&oid),
    GNUNET_PQ_query_param_end
  };
  PGresult *ret;

  ret = GNUNET_PQ_exec_prepared (plugin->dbh,
				 "update",
				 params);
  if (GNUNET_OK !=
      GNUNET_POSTGRES_check_result (plugin->dbh,
				    ret, 
				    PGRES_COMMAND_OK, 
				    "PQexecPrepared", 
				    "update"))
  {
    cont (cont_cls, 
	  GNUNET_SYSERR, 
	  NULL);
    return;
  }
  PQclear (ret);
  cont (cont_cls, 
	GNUNET_OK, 
	NULL);
}
Пример #9
0
/**
 * Removes any expired block.
 *
 * @param plugin the plugin
 */
static void
namecache_postgres_expire_blocks (struct Plugin *plugin)
{
  struct GNUNET_TIME_Absolute now = GNUNET_TIME_absolute_get ();
  struct GNUNET_PQ_QueryParam params[] = { 
    GNUNET_PQ_query_param_absolute_time (&now),
    GNUNET_PQ_query_param_end
  };
  PGresult *res;

  res = GNUNET_PQ_exec_prepared (plugin->dbh,
				 "expire_blocks",
				 params);
  if (GNUNET_OK !=
      GNUNET_POSTGRES_check_result (plugin->dbh,
                                    res,
                                    PGRES_COMMAND_OK,
                                    "PQexecPrepared",
                                    "expire_blocks"))
    return;
  PQclear (res);
}
Пример #10
0
/**
 * Delete older block in the datastore.
 *
 * @param plugin the plugin
 * @param query query for the block
 * @param expiration_time how old does the block have to be for deletion
 */
static void
delete_old_block (struct Plugin *plugin,
                  const struct GNUNET_HashCode *query,
                  struct GNUNET_TIME_AbsoluteNBO expiration_time)
{
  struct GNUNET_PQ_QueryParam params[] = { 
    GNUNET_PQ_query_param_auto_from_type (query),
    GNUNET_PQ_query_param_absolute_time_nbo (&expiration_time),
    GNUNET_PQ_query_param_end
  };
  PGresult *res;

  res = GNUNET_PQ_exec_prepared (plugin->dbh,
				 "delete_block",
				 params);
  if (GNUNET_OK !=
      GNUNET_POSTGRES_check_result (plugin->dbh,
                                    res,
                                    PGRES_COMMAND_OK,
                                    "PQexecPrepared",
                                    "delete_block"))
    return;
  PQclear (res);
}
Пример #11
0
/**
 * @brief Get a database handle
 *
 * @param plugin global context
 * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
 */
static int
init_connection (struct Plugin *plugin)
{
  PGresult *ret;

  plugin->dbh = GNUNET_POSTGRES_connect (plugin->env->cfg, "datastore-postgres");
  if (NULL == plugin->dbh)
    return GNUNET_SYSERR;

  ret =
      PQexec (plugin->dbh,
              "CREATE TABLE gn090 (" 
	      "  repl INTEGER NOT NULL DEFAULT 0,"
              "  type INTEGER NOT NULL DEFAULT 0,"
              "  prio INTEGER NOT NULL DEFAULT 0,"
              "  anonLevel INTEGER NOT NULL DEFAULT 0,"
              "  expire BIGINT NOT NULL DEFAULT 0,"
              "  rvalue BIGINT NOT NULL DEFAULT 0,"
              "  hash BYTEA NOT NULL DEFAULT '',"
              "  vhash BYTEA NOT NULL DEFAULT '',"
              "  value BYTEA NOT NULL DEFAULT '')" 
	      "WITH OIDS");
  if ( (NULL == ret) ||
       ((PQresultStatus (ret) != PGRES_COMMAND_OK) &&
        (0 != strcmp ("42P07",    /* duplicate table */
                      PQresultErrorField
                      (ret,
                       PG_DIAG_SQLSTATE)))))
  {
    (void) GNUNET_POSTGRES_check_result (plugin->dbh,
                                         ret,
                                         PGRES_COMMAND_OK,
                                         "CREATE TABLE",
                                         "gn090");
    PQfinish (plugin->dbh);
    plugin->dbh = NULL;
    return GNUNET_SYSERR;
  }

  if (PQresultStatus (ret) == PGRES_COMMAND_OK)
  {
    if ((GNUNET_OK !=
         GNUNET_POSTGRES_exec (plugin->dbh, "CREATE INDEX idx_hash ON gn090 (hash)")) ||
        (GNUNET_OK !=
         GNUNET_POSTGRES_exec (plugin->dbh, "CREATE INDEX idx_hash_vhash ON gn090 (hash,vhash)")) ||
        (GNUNET_OK !=
         GNUNET_POSTGRES_exec (plugin->dbh, "CREATE INDEX idx_prio ON gn090 (prio)")) ||
        (GNUNET_OK !=
         GNUNET_POSTGRES_exec (plugin->dbh, "CREATE INDEX idx_expire ON gn090 (expire)")) ||
        (GNUNET_OK !=
         GNUNET_POSTGRES_exec (plugin->dbh,
			       "CREATE INDEX idx_prio_anon ON gn090 (prio,anonLevel)")) ||
        (GNUNET_OK !=
         GNUNET_POSTGRES_exec (plugin->dbh,
			       "CREATE INDEX idx_prio_hash_anon ON gn090 (prio,hash,anonLevel)")) ||
        (GNUNET_OK !=
         GNUNET_POSTGRES_exec (plugin->dbh, "CREATE INDEX idx_repl_rvalue ON gn090 (repl,rvalue)")) ||
        (GNUNET_OK !=
         GNUNET_POSTGRES_exec (plugin->dbh, "CREATE INDEX idx_expire_hash ON gn090 (expire,hash)")))
    {
      PQclear (ret);
      PQfinish (plugin->dbh);
      plugin->dbh = NULL;
      return GNUNET_SYSERR;
    }
  }
  PQclear (ret);

  ret =
      PQexec (plugin->dbh,
              "ALTER TABLE gn090 ALTER value SET STORAGE EXTERNAL");
  if (GNUNET_OK !=
      GNUNET_POSTGRES_check_result (plugin->dbh, ret, PGRES_COMMAND_OK, "ALTER TABLE", "gn090"))
  {
    PQfinish (plugin->dbh);
    plugin->dbh = NULL;
    return GNUNET_SYSERR;
  }
  PQclear (ret);
  ret = PQexec (plugin->dbh, "ALTER TABLE gn090 ALTER hash SET STORAGE PLAIN");
  if (GNUNET_OK !=
      GNUNET_POSTGRES_check_result (plugin->dbh, ret, PGRES_COMMAND_OK, "ALTER TABLE", "gn090"))
  {
    PQfinish (plugin->dbh);
    plugin->dbh = NULL;
    return GNUNET_SYSERR;
  }
  PQclear (ret);
  ret = PQexec (plugin->dbh, "ALTER TABLE gn090 ALTER vhash SET STORAGE PLAIN");
  if (GNUNET_OK !=
      GNUNET_POSTGRES_check_result (plugin->dbh, ret, PGRES_COMMAND_OK, "ALTER TABLE", "gn090"))
  {
    PQfinish (plugin->dbh);
    plugin->dbh = NULL;
    return GNUNET_SYSERR;
  }
  PQclear (ret);
  if ((GNUNET_OK !=
       GNUNET_POSTGRES_prepare (plugin->dbh, "getvt",
                   "SELECT type, prio, anonLevel, expire, hash, value, oid FROM gn090 "
                   "WHERE hash=$1 AND vhash=$2 AND type=$3 "
                   "ORDER BY oid ASC LIMIT 1 OFFSET $4", 4)) ||
      (GNUNET_OK !=
       GNUNET_POSTGRES_prepare (plugin->dbh, "gett",
                   "SELECT type, prio, anonLevel, expire, hash, value, oid FROM gn090 "
                   "WHERE hash=$1 AND type=$2 "
                   "ORDER BY oid ASC LIMIT 1 OFFSET $3", 3)) ||
      (GNUNET_OK !=
       GNUNET_POSTGRES_prepare (plugin->dbh, "getv",
                   "SELECT type, prio, anonLevel, expire, hash, value, oid FROM gn090 "
                   "WHERE hash=$1 AND vhash=$2 "
                   "ORDER BY oid ASC LIMIT 1 OFFSET $3", 3)) ||
      (GNUNET_OK !=
       GNUNET_POSTGRES_prepare (plugin->dbh, "get",
                   "SELECT type, prio, anonLevel, expire, hash, value, oid FROM gn090 "
                   "WHERE hash=$1 " "ORDER BY oid ASC LIMIT 1 OFFSET $2", 2)) ||
      (GNUNET_OK !=
       GNUNET_POSTGRES_prepare (plugin->dbh, "count_getvt",
				"SELECT count(*) FROM gn090 WHERE hash=$1 AND vhash=$2 AND type=$3", 3)) ||
      (GNUNET_OK !=
       GNUNET_POSTGRES_prepare (plugin->dbh, "count_gett",
				"SELECT count(*) FROM gn090 WHERE hash=$1 AND type=$2", 2)) ||
      (GNUNET_OK !=
       GNUNET_POSTGRES_prepare (plugin->dbh, "count_getv",
				"SELECT count(*) FROM gn090 WHERE hash=$1 AND vhash=$2", 2)) ||
      (GNUNET_OK !=
       GNUNET_POSTGRES_prepare (plugin->dbh, "count_get",
				"SELECT count(*) FROM gn090 WHERE hash=$1", 1)) ||
      (GNUNET_OK !=
       GNUNET_POSTGRES_prepare (plugin->dbh, "put",
                   "INSERT INTO gn090 (repl, type, prio, anonLevel, expire, rvalue, hash, vhash, value) "
                   "VALUES ($1, $2, $3, $4, $5, RANDOM(), $6, $7, $8)", 9)) ||
      (GNUNET_OK !=
       GNUNET_POSTGRES_prepare (plugin->dbh, "update",
                   "UPDATE gn090 SET prio = prio + $1, expire = CASE WHEN expire < $2 THEN $2 ELSE expire END "
                   "WHERE oid = $3", 3)) ||
      (GNUNET_OK !=
       GNUNET_POSTGRES_prepare (plugin->dbh, "decrepl",
                   "UPDATE gn090 SET repl = GREATEST (repl - 1, 0) "
                   "WHERE oid = $1", 1)) ||
      (GNUNET_OK !=
       GNUNET_POSTGRES_prepare (plugin->dbh, "select_non_anonymous",
                   "SELECT type, prio, anonLevel, expire, hash, value, oid FROM gn090 "
                   "WHERE anonLevel = 0 AND type = $1 ORDER BY oid DESC LIMIT 1 OFFSET $2",
                   1)) ||
      (GNUNET_OK !=
       GNUNET_POSTGRES_prepare (plugin->dbh, "select_expiration_order",
                   "(SELECT type, prio, anonLevel, expire, hash, value, oid FROM gn090 "
                   "WHERE expire < $1 ORDER BY prio ASC LIMIT 1) " "UNION "
                   "(SELECT type, prio, anonLevel, expire, hash, value, oid FROM gn090 "
                   "ORDER BY prio ASC LIMIT 1) " "ORDER BY expire ASC LIMIT 1",
                   1)) ||
      (GNUNET_OK !=
       GNUNET_POSTGRES_prepare (plugin->dbh, "select_replication_order",
                   "SELECT type, prio, anonLevel, expire, hash, value, oid FROM gn090 "
                   "ORDER BY repl DESC,RANDOM() LIMIT 1", 0)) ||
      (GNUNET_OK !=
       GNUNET_POSTGRES_prepare (plugin->dbh, "delrow", "DELETE FROM gn090 " "WHERE oid=$1", 1)) ||
      (GNUNET_OK !=
       GNUNET_POSTGRES_prepare (plugin->dbh, "get_keys", "SELECT hash FROM gn090", 0)))
  {
    PQfinish (plugin->dbh);
    plugin->dbh = NULL;
    return GNUNET_SYSERR;
  }
  return GNUNET_OK;
}
Пример #12
0
/**
 * Iterate over the results for a particular key
 * in the datastore.
 *
 * @param cls closure with the 'struct Plugin'
 * @param offset offset of the result (modulo num-results);
 *        specific ordering does not matter for the offset
 * @param key maybe NULL (to match all entries)
 * @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 the matching value;
 *        will be called once with a NULL if no value matches
 * @param proc_cls closure for iter
 */
static void
postgres_plugin_get_key (void *cls, 
			 uint64_t offset,
                         const struct GNUNET_HashCode *key,
                         const struct GNUNET_HashCode *vhash,
                         enum GNUNET_BLOCK_Type type,
			 PluginDatumProcessor proc,
                         void *proc_cls)
{
  struct Plugin *plugin = cls;
  uint32_t utype = type;
  PGresult *ret;
  uint64_t total;
  uint64_t limit_off;

  if (0 != type)
  {
    if (NULL != vhash)
    {
      struct GNUNET_PQ_QueryParam params[] = { 
	GNUNET_PQ_query_param_auto_from_type (key),
	GNUNET_PQ_query_param_auto_from_type (vhash),
	GNUNET_PQ_query_param_uint32 (&utype),
	GNUNET_PQ_query_param_end
      };
      ret = GNUNET_PQ_exec_prepared (plugin->dbh,
				     "count_getvt",
				     params);
    }
    else
    {
      struct GNUNET_PQ_QueryParam params[] = { 
	GNUNET_PQ_query_param_auto_from_type (key),
	GNUNET_PQ_query_param_uint32 (&utype),
	GNUNET_PQ_query_param_end
      };
      ret = GNUNET_PQ_exec_prepared (plugin->dbh,
				     "count_gett",
				     params);
    }
  }
  else
  {
    if (NULL != vhash)
    {
      struct GNUNET_PQ_QueryParam params[] = { 
	GNUNET_PQ_query_param_auto_from_type (key),
	GNUNET_PQ_query_param_auto_from_type (vhash),
	GNUNET_PQ_query_param_end
      };
      ret = GNUNET_PQ_exec_prepared (plugin->dbh,
				     "count_getv",
				     params);
    }
    else
    {
      struct GNUNET_PQ_QueryParam params[] = { 
	GNUNET_PQ_query_param_auto_from_type (key),
	GNUNET_PQ_query_param_end
      };
      ret = GNUNET_PQ_exec_prepared (plugin->dbh,
				     "count_get",
				     params);
    }
  }

  if (GNUNET_OK !=
      GNUNET_POSTGRES_check_result (plugin->dbh, 
				    ret,
				    PGRES_TUPLES_OK, 
				    "PQexecParams", 
				    "count"))
  {
    proc (proc_cls, NULL, 0, NULL, 0, 0, 0, 
	  GNUNET_TIME_UNIT_ZERO_ABS, 0);
    return;
  }
  if ( (PQntuples (ret) != 1) || 
       (PQnfields (ret) != 1) ||
       (PQgetlength (ret, 0, 0) != sizeof (uint64_t)))
  {
    GNUNET_break (0);
    PQclear (ret);
    proc (proc_cls, NULL, 0, NULL, 0, 0, 0, 
	  GNUNET_TIME_UNIT_ZERO_ABS, 0);
    return;
  }
  total = GNUNET_ntohll (*(const uint64_t *) PQgetvalue (ret, 0, 0));
  PQclear (ret);
  if (0 == total)
  {
    proc (proc_cls, NULL, 0, NULL, 0, 0, 0, 
	  GNUNET_TIME_UNIT_ZERO_ABS, 0);
    return;
  }
  limit_off = offset % total;

  if (0 != type)
  {
    if (NULL != vhash)
    {
      struct GNUNET_PQ_QueryParam params[] = { 
	GNUNET_PQ_query_param_auto_from_type (key),
	GNUNET_PQ_query_param_auto_from_type (&vhash),
	GNUNET_PQ_query_param_uint32 (&utype),
	GNUNET_PQ_query_param_uint64 (&limit_off),
	GNUNET_PQ_query_param_end
      };
      ret = GNUNET_PQ_exec_prepared (plugin->dbh,
				     "getvt",
				     params);
    }
    else
    {
      struct GNUNET_PQ_QueryParam params[] = { 
	GNUNET_PQ_query_param_auto_from_type (key),
	GNUNET_PQ_query_param_uint32 (&utype),
	GNUNET_PQ_query_param_uint64 (&limit_off),
	GNUNET_PQ_query_param_end
      };
      ret = GNUNET_PQ_exec_prepared (plugin->dbh,
				     "gett",
				     params);
    }
  }
  else
  {
    if (NULL != vhash)
    {
      struct GNUNET_PQ_QueryParam params[] = { 
	GNUNET_PQ_query_param_auto_from_type (key),
	GNUNET_PQ_query_param_auto_from_type (&vhash),
	GNUNET_PQ_query_param_uint64 (&limit_off),
	GNUNET_PQ_query_param_end
      };
      ret = GNUNET_PQ_exec_prepared (plugin->dbh,
				     "getv",
				     params);
    }
    else
    {
      struct GNUNET_PQ_QueryParam params[] = { 
	GNUNET_PQ_query_param_auto_from_type (key),
	GNUNET_PQ_query_param_uint64 (&limit_off),
	GNUNET_PQ_query_param_end
      };
      ret = GNUNET_PQ_exec_prepared (plugin->dbh,
				     "get",
				     params);
    }
  }
  process_result (plugin,
		  proc,
		  proc_cls, 
		  ret, 
		  __FILE__, __LINE__);
}
Пример #13
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)
{
  PGresult *res;

  plugin->dbh = GNUNET_POSTGRES_connect (plugin->cfg,
					 "namecache-postgres");
  if (NULL == plugin->dbh)
    return GNUNET_SYSERR;
  if (GNUNET_YES ==
      GNUNET_CONFIGURATION_get_value_yesno (plugin->cfg,
					    "namecache-postgres",
					    "TEMPORARY_TABLE"))
  {
    res =
      PQexec (plugin->dbh,
              "CREATE TEMPORARY TABLE ns096blocks ("
	      " query BYTEA NOT NULL DEFAULT '',"
	      " block BYTEA NOT NULL DEFAULT '',"
	      " expiration_time BIGINT NOT NULL DEFAULT 0"
	      ")" "WITH OIDS");
  }
  else
  {
    res =
      PQexec (plugin->dbh,
              "CREATE TABLE ns096blocks ("
	      " query BYTEA NOT NULL DEFAULT '',"
	      " block BYTEA NOT NULL DEFAULT '',"
	      " expiration_time BIGINT NOT NULL DEFAULT 0"
	      ")" "WITH OIDS");
  }
  if ( (NULL == res) ||
       ((PQresultStatus (res) != PGRES_COMMAND_OK) &&
        (0 != strcmp ("42P07",    /* duplicate table */
                      PQresultErrorField
                      (res,
                       PG_DIAG_SQLSTATE)))))
  {
    (void) GNUNET_POSTGRES_check_result (plugin->dbh, res,
                                         PGRES_COMMAND_OK, "CREATE TABLE",
					 "ns096blocks");
    PQfinish (plugin->dbh);
    plugin->dbh = NULL;
    return GNUNET_SYSERR;
  }
  if (PQresultStatus (res) == PGRES_COMMAND_OK)
    create_indices (plugin->dbh);
  PQclear (res);

  if ((GNUNET_OK !=
       GNUNET_POSTGRES_prepare (plugin->dbh,
				"cache_block",
				"INSERT INTO ns096blocks (query, block, expiration_time) VALUES "
 				"($1, $2, $3)", 3)) ||
      (GNUNET_OK !=
       GNUNET_POSTGRES_prepare (plugin->dbh,
				"expire_blocks",
				"DELETE FROM ns096blocks WHERE expiration_time<$1", 1)) ||
      (GNUNET_OK !=
       GNUNET_POSTGRES_prepare (plugin->dbh,
				"delete_block",
				"DELETE FROM ns096blocks WHERE query=$1 AND expiration_time<=$2", 2)) ||
      (GNUNET_OK !=
       GNUNET_POSTGRES_prepare (plugin->dbh,
				"lookup_block",
				"SELECT block FROM ns096blocks WHERE query=$1"
				" ORDER BY expiration_time DESC LIMIT 1", 1)))
  {
    PQfinish (plugin->dbh);
    plugin->dbh = NULL;
    return GNUNET_SYSERR;
  }
  return GNUNET_OK;
}
Пример #14
0
/**
 * @brief Get a database handle
 *
 * @param plugin global context
 * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
 */
static int
init_connection (struct Plugin *plugin)
{
  PGresult *ret;

  plugin->dbh = GNUNET_POSTGRES_connect (plugin->env->cfg,
					 "datacache-postgres");
  if (NULL == plugin->dbh)
    return GNUNET_SYSERR;
  ret =
      PQexec (plugin->dbh,
              "CREATE TEMPORARY TABLE gn090dc ("
              "  type INTEGER NOT NULL DEFAULT 0,"
              "  discard_time BIGINT NOT NULL DEFAULT 0,"
              "  key BYTEA NOT NULL DEFAULT '',"
              "  value BYTEA NOT NULL DEFAULT '',"
              "  path BYTEA DEFAULT '')"
	      "WITH OIDS");
  if ( (ret == NULL) ||
       ((PQresultStatus (ret) != PGRES_COMMAND_OK) &&
	(0 != strcmp ("42P07",    /* duplicate table */
		      PQresultErrorField
		      (ret,
		       PG_DIAG_SQLSTATE)))))
  {
    (void) GNUNET_POSTGRES_check_result (plugin->dbh, ret,
					 PGRES_COMMAND_OK,
                                         "CREATE TABLE",
					 "gn090dc");
    PQfinish (plugin->dbh);
    plugin->dbh = NULL;
    return GNUNET_SYSERR;
  }
  if (PQresultStatus (ret) == PGRES_COMMAND_OK)
  {
    if ((GNUNET_OK !=
         GNUNET_POSTGRES_exec (plugin->dbh,
                               "CREATE INDEX idx_key ON gn090dc (key)")) ||
        (GNUNET_OK !=
         GNUNET_POSTGRES_exec (plugin->dbh,
                               "CREATE INDEX idx_dt ON gn090dc (discard_time)")))
    {
      PQclear (ret);
      PQfinish (plugin->dbh);
      plugin->dbh = NULL;
      return GNUNET_SYSERR;
    }
  }
  PQclear (ret);
  ret =
      PQexec (plugin->dbh,
              "ALTER TABLE gn090dc ALTER value SET STORAGE EXTERNAL");
  if (GNUNET_OK !=
      GNUNET_POSTGRES_check_result (plugin->dbh,
                                    ret,
                                    PGRES_COMMAND_OK,
                                    "ALTER TABLE",
                                    "gn090dc"))
  {
    PQfinish (plugin->dbh);
    plugin->dbh = NULL;
    return GNUNET_SYSERR;
  }
  PQclear (ret);
  ret = PQexec (plugin->dbh,
                "ALTER TABLE gn090dc ALTER key SET STORAGE PLAIN");
  if (GNUNET_OK !=
      GNUNET_POSTGRES_check_result (plugin->dbh,
                                    ret,
                                    PGRES_COMMAND_OK,
                                    "ALTER TABLE",
                                    "gn090dc"))
  {
    PQfinish (plugin->dbh);
    plugin->dbh = NULL;
    return GNUNET_SYSERR;
  }
  PQclear (ret);
  if ((GNUNET_OK !=
       GNUNET_POSTGRES_prepare (plugin->dbh,
                                "getkt",
                                "SELECT discard_time,type,value,path FROM gn090dc "
                                "WHERE key=$1 AND type=$2 ", 2)) ||
      (GNUNET_OK !=
       GNUNET_POSTGRES_prepare (plugin->dbh,
                                "getk",
                                "SELECT discard_time,type,value,path FROM gn090dc "
                                "WHERE key=$1", 1)) ||
      (GNUNET_OK !=
       GNUNET_POSTGRES_prepare (plugin->dbh,
                                "getm",
                                "SELECT length(value),oid,key FROM gn090dc "
                                "ORDER BY discard_time ASC LIMIT 1", 0)) ||
      (GNUNET_OK !=
       GNUNET_POSTGRES_prepare (plugin->dbh,
                                "get_random",
                                "SELECT discard_time,type,value,path,key FROM gn090dc "
                                "ORDER BY key ASC LIMIT 1 OFFSET $1", 1)) ||
      (GNUNET_OK !=
       GNUNET_POSTGRES_prepare (plugin->dbh,
                                "get_closest",
                                "SELECT discard_time,type,value,path,key FROM gn090dc "
                                "WHERE key>=$1 ORDER BY key ASC LIMIT $2", 1)) ||
      (GNUNET_OK !=
       GNUNET_POSTGRES_prepare (plugin->dbh,
                                "delrow",
                                "DELETE FROM gn090dc WHERE oid=$1", 1)) ||
      (GNUNET_OK !=
       GNUNET_POSTGRES_prepare (plugin->dbh,
                                "put",
                                "INSERT INTO gn090dc (type, discard_time, key, value, path) "
                                "VALUES ($1, $2, $3, $4, $5)", 5)))
  {
    PQfinish (plugin->dbh);
    plugin->dbh = NULL;
    return GNUNET_SYSERR;
  }
  return GNUNET_OK;
}
Пример #15
0
/**
 * 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 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_cls closure for @a iter
 * @return the number of results found
 */
static unsigned int
postgres_plugin_get_closest (void *cls,
                             const struct GNUNET_HashCode *key,
                             unsigned int num_results,
                             GNUNET_DATACACHE_Iterator iter,
                             void *iter_cls)
{
  struct Plugin *plugin = cls;
  uint32_t nbo_limit = htonl (num_results);
  const char *paramValues[] = {
    (const char *) key,
    (const char *) &nbo_limit,
  };
  int paramLengths[] = {
    sizeof (struct GNUNET_HashCode),
    sizeof (nbo_limit)

  };
  const int paramFormats[] = { 1, 1 };
  struct GNUNET_TIME_Absolute expiration_time;
  uint32_t size;
  unsigned int type;
  unsigned int cnt;
  unsigned int i;
  unsigned int path_len;
  const struct GNUNET_PeerIdentity *path;
  PGresult *res;

  res =
      PQexecPrepared (plugin->dbh,
                      "get_closest",
                      2,
                      paramValues,
                      paramLengths,
                      paramFormats,
                      1);
  if (GNUNET_OK !=
      GNUNET_POSTGRES_check_result (plugin->dbh,
                                    res,
                                    PGRES_TUPLES_OK,
                                    "PQexecPrepared",
				    "get_closest"))
  {
    LOG (GNUNET_ERROR_TYPE_DEBUG,
	 "Ending iteration (postgres error)\n");
    return 0;
  }

  if (0 == (cnt = PQntuples (res)))
  {
    /* no result */
    LOG (GNUNET_ERROR_TYPE_DEBUG,
	 "Ending iteration (no more results)\n");
    PQclear (res);
    return 0;
  }
  if (NULL == iter)
  {
    PQclear (res);
    return cnt;
  }
  if ( (5 != PQnfields (res)) ||
       (sizeof (uint64_t) != PQfsize (res, 0)) ||
       (sizeof (uint32_t) != PQfsize (res, 1)) ||
       (sizeof (struct GNUNET_HashCode) != PQfsize (res, 4)) )
  {
    GNUNET_break (0);
    PQclear (res);
    return 0;
  }
  for (i = 0; i < cnt; i++)
  {
    expiration_time.abs_value_us =
        GNUNET_ntohll (*(uint64_t *) PQgetvalue (res, i, 0));
    type = ntohl (*(uint32_t *) PQgetvalue (res, i, 1));
    size = PQgetlength (res, i, 2);
    path_len = PQgetlength (res, i, 3);
    if (0 != (path_len % sizeof (struct GNUNET_PeerIdentity)))
    {
      GNUNET_break (0);
      path_len = 0;
    }
    path_len %= sizeof (struct GNUNET_PeerIdentity);
    path = (const struct GNUNET_PeerIdentity *) PQgetvalue (res, i, 3);
    key = (const struct GNUNET_HashCode *) PQgetvalue (res, i, 4);
    LOG (GNUNET_ERROR_TYPE_DEBUG,
	 "Found result of size %u bytes and type %u in database\n",
	 (unsigned int) size,
         (unsigned int) type);
    if (GNUNET_SYSERR ==
        iter (iter_cls,
              key,
              size,
              PQgetvalue (res, i, 2),
              (enum GNUNET_BLOCK_Type) type,
	      expiration_time,
	      path_len,
	      path))
    {
      LOG (GNUNET_ERROR_TYPE_DEBUG,
	   "Ending iteration (client error)\n");
      PQclear (res);
      return cnt;
    }
  }
  PQclear (res);
  return cnt;
}
Пример #16
0
/**
 * Obtain a random key-value pair from the datacache.
 *
 * @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 unsigned int
postgres_plugin_get_random (void *cls,
                            GNUNET_DATACACHE_Iterator iter,
                            void *iter_cls)
{
  struct Plugin *plugin = cls;
  unsigned int off;
  uint32_t off_be;
  struct GNUNET_TIME_Absolute expiration_time;
  uint32_t size;
  unsigned int path_len;
  const struct GNUNET_PeerIdentity *path;
  const struct GNUNET_HashCode *key;
  unsigned int type;
  PGresult *res;
  const char *paramValues[] = {
    (const char *) &off_be
  };
  int paramLengths[] = {
    sizeof (off_be)
  };
  const int paramFormats[] = { 1 };

  if (0 == plugin->num_items)
    return 0;
  if (NULL == iter)
    return 1;
  off = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_NONCE,
                                  plugin->num_items);
  off_be = htonl (off);
  res =
    PQexecPrepared (plugin->dbh, "get_random",
                    1, paramValues, paramLengths, paramFormats,
                    1);
  if (GNUNET_OK !=
      GNUNET_POSTGRES_check_result (plugin->dbh,
                                    res,
                                    PGRES_TUPLES_OK,
                                    "PQexecPrepared",
				    "get_random"))
  {
    GNUNET_break (0);
    return 0;
  }
  if (0 == PQntuples (res))
  {
    GNUNET_break (0);
    return 0;
  }
  if ( (5 != PQnfields (res)) ||
       (sizeof (uint64_t) != PQfsize (res, 0)) ||
       (sizeof (uint32_t) != PQfsize (res, 1)) ||
       (sizeof (struct GNUNET_HashCode) != PQfsize (res, 4)) )
  {
    GNUNET_break (0);
    PQclear (res);
    return 0;
  }
  expiration_time.abs_value_us =
    GNUNET_ntohll (*(uint64_t *) PQgetvalue (res, 0, 0));
  type = ntohl (*(uint32_t *) PQgetvalue (res, 0, 1));
  size = PQgetlength (res, 0, 2);
  path_len = PQgetlength (res, 0, 3);
  if (0 != (path_len % sizeof (struct GNUNET_PeerIdentity)))
  {
    GNUNET_break (0);
    path_len = 0;
  }
  path_len %= sizeof (struct GNUNET_PeerIdentity);
  path = (const struct GNUNET_PeerIdentity *) PQgetvalue (res, 0, 3);
  key = (const struct GNUNET_HashCode *) PQgetvalue (res, 0, 4);
  LOG (GNUNET_ERROR_TYPE_DEBUG,
       "Found random value with key %s of size %u bytes and type %u in database\n",
       GNUNET_h2s (key),
       (unsigned int) size,
       (unsigned int) type);
  (void) iter (iter_cls,
               key,
               size,
               PQgetvalue (res, 0, 2),
               (enum GNUNET_BLOCK_Type) type,
               expiration_time,
               path_len,
               path);
  PQclear (res);
  return 1;
}