{
PGresult *ret;
- plugin->dbh = GNUNET_POSTGRES_connect (plugin->env->cfg,
- "datacache-postgres");
+ plugin->dbh = GNUNET_PQ_connect_with_cfg (plugin->env->cfg,
+ "datacache-postgres");
if (NULL == plugin->dbh)
return GNUNET_SYSERR;
ret =
{
PGresult *ret;
- plugin->dbh = GNUNET_POSTGRES_connect (plugin->env->cfg, "datastore-postgres");
+ plugin->dbh = GNUNET_PQ_connect_with_cfg (plugin->env->cfg,
+ "datastore-postgres");
if (NULL == plugin->dbh)
return GNUNET_SYSERR;
#include "gnunet_util_lib.h"
+/* ************************* pq_query_helper.c functions ************************ */
+
+
/**
* Function called to convert input argument into SQL parameters.
*
GNUNET_PQ_query_param_uint64 (const uint64_t *x);
+/* ************************* pq_result_helper.c functions ************************ */
+
+
/**
* Extract data from a Postgres database @a result at row @a row.
*
uint64_t *u64);
+/* ************************* pq.c functions ************************ */
+
/**
* Execute a prepared statement.
*
* @param name name of the prepared statement
* @param params parameters to the statement
* @return postgres result
+ * @deprecated (should become an internal API)
*/
PGresult *
GNUNET_PQ_exec_prepared (PGconn *db_conn,
* @return
* #GNUNET_YES if all results could be extracted
* #GNUNET_SYSERR if a result was invalid (non-existing field)
+ * @deprecated (should become an internal API)
*/
int
GNUNET_PQ_extract_result (PGresult *result,
GNUNET_PQ_cleanup_result (struct GNUNET_PQ_ResultSpec *rs);
+/* ******************** pq_eval.c functions ************** */
+
+
+/**
+ * Status code returned from functions running PQ commands.
+ * Can be combined with a function that returns the number
+ * of results, so non-negative values indicate success.
+ */
+enum GNUNET_PQ_QueryStatus
+{
+ /**
+ * A hard error occurred, retrying will not help.
+ */
+ GNUNET_PQ_STATUS_HARD_ERROR = -2,
+
+ /**
+ * A soft error occurred, retrying the transaction may succeed.
+ */
+ GNUNET_PQ_STATUS_SOFT_ERROR = -1,
+
+ /**
+ * The transaction succeeded, but yielded zero results.
+ */
+ GNUNET_PQ_STATUS_SUCCESS_NO_RESULTS = 0,
+
+ /**
+ * The transaction succeeded, and yielded one result.
+ */
+ GNUNET_PQ_STATUS_SUCCESS_ONE_RESULT = 1
+
+};
+
+
+/**
+ * Check the @a result's error code to see what happened.
+ * Also logs errors.
+ *
+ * @param connection connection to execute the statement in
+ * @param statement_name name of the statement that created @a result
+ * @param result result to check
+ * @return status code from the result, mapping PQ status
+ * codes to `enum GNUNET_PQ_QueryStatus`. Never
+ * returns positive values as this function does
+ * not look at the result set.
+ * @deprecated (low level, let's see if we can do with just the high-level functions)
+ */
+enum GNUNET_PQ_QueryStatus
+GNUNET_PQ_eval_result (PGconn *connection,
+ const char *statement_name,
+ PGresult *result);
+
+
+/**
+ * Execute a named prepared @a statement that is NOT a SELECT
+ * statement in @a connnection using the given @a params. Returns the
+ * resulting session state.
+ *
+ * @param connection connection to execute the statement in
+ * @param statement_name name of the statement
+ * @param params parameters to give to the statement (#GNUNET_PQ_query_param_end-terminated)
+ * @return status code from the result, mapping PQ status
+ * codes to `enum GNUNET_PQ_QueryStatus`. Never
+ * returns positive values as this function does
+ * not look at the result set.
+ */
+enum GNUNET_PQ_QueryStatus
+GNUNET_PQ_eval_prepared_non_select (PGconn *connection,
+ const char *statement_name,
+ const struct GNUNET_PQ_QueryParam *params);
+
+
+/**
+ * Function to be called with the results of a SELECT statement
+ * that has returned @a num_results results.
+ *
+ * @param cls closure
+ * @param result the postgres result
+ * @param num_result the number of results in @a result
+ */
+typedef void
+(*GNUNET_PQ_PostgresResultHandler)(void *cls,
+ PGresult *result,
+ unsigned int num_results);
+
+
+/**
+ * Execute a named prepared @a statement that is a SELECT statement
+ * which may return multiple results in @a connection using the given
+ * @a params. Call @a rh with the results. Returns the query
+ * status including the number of results given to @a rh (possibly zero).
+ * @a rh will not have been called if the return value is negative.
+ *
+ * @param connection connection to execute the statement in
+ * @param statement_name name of the statement
+ * @param params parameters to give to the statement (#GNUNET_PQ_query_param_end-terminated)
+ * @param rh function to call with the result set, NULL to ignore
+ * @param rh_cls closure to pass to @a rh
+ * @return status code from the result, mapping PQ status
+ * codes to `enum GNUNET_PQ_QueryStatus`.
+ */
+enum GNUNET_PQ_QueryStatus
+GNUNET_PQ_eval_prepared_multi_select (PGconn *connection,
+ const char *statement_name,
+ const struct GNUNET_PQ_QueryParam *params,
+ GNUNET_PQ_PostgresResultHandler rh,
+ void *rh_cls);
+
+
+/**
+ * Execute a named prepared @a statement that is a SELECT statement
+ * which must return a single result in @a connection using the given
+ * @a params. Stores the result (if any) in @a rs, which the caller
+ * must then clean up using #GNUNET_PQ_cleanup_result() if the return
+ * value was #GNUNET_PQ_STATUS_SUCCESS_ONE_RESULT. Returns the
+ * resulting session status.
+ *
+ * @param connection connection to execute the statement in
+ * @param statement_name name of the statement
+ * @param params parameters to give to the statement (#GNUNET_PQ_query_param_end-terminated)
+ * @param[in,out] rs result specification to use for storing the result of the query
+ * @return status code from the result, mapping PQ status
+ * codes to `enum GNUNET_PQ_QueryStatus`.
+ */
+enum GNUNET_PQ_QueryStatus
+GNUNET_PQ_eval_prepared_singleton_select (PGconn *connection,
+ const char *statement_name,
+ const struct GNUNET_PQ_QueryParam *params,
+ struct GNUNET_PQ_ResultSpec *rs);
+
+
+/* ******************** pq_prepare.c functions ************** */
+
+
+/**
+ * Information needed to prepare a list of SQL statements using
+ * #GNUNET_PQ_prepare_statements().
+ */
+struct GNUNET_PQ_PreparedStatement {
+
+ /**
+ * Name of the statement.
+ */
+ const char *name;
+
+ /**
+ * Actual SQL statement.
+ */
+ const char *sql;
+
+ /**
+ * Number of arguments included in @e sql.
+ */
+ unsigned int num_arguments;
+
+};
+
+
+/**
+ * Terminator for prepared statement list.
+ */
+#define GNUNET_PQ_PREPARED_STATEMENT_END { NULL, NULL, 0 }
+
+
+/**
+ * Create a `struct GNUNET_PQ_PreparedStatement`.
+ *
+ * @param name name of the statement
+ * @param sql actual SQL statement
+ * @param num_args number of arguments in the statement
+ * @return initialized struct
+ */
+struct GNUNET_PQ_PreparedStatement
+GNUNET_PQ_make_prepare (const char *name,
+ const char *sql,
+ unsigned int num_args);
+
+
+/**
+ * Request creation of prepared statements @a ps from Postgres.
+ *
+ * @param connection connection to prepare the statements for
+ * @param ps #GNUNET_PQ_PREPARED_STATEMENT_END-terminated array of prepared
+ * statements.
+ * @return #GNUNET_OK on success,
+ * #GNUNET_SYSERR on error
+ */
+int
+GNUNET_PQ_prepare_statements (PGconn *connection,
+ const struct GNUNET_PQ_PreparedStatement *ps);
+
+
+/* ******************** pq_exec.c functions ************** */
+
+
+/**
+ * Information needed to run a list of SQL statements using
+ * #GNUNET_PQ_exec_statements().
+ */
+struct GNUNET_PQ_ExecuteStatement {
+
+ /**
+ * Actual SQL statement.
+ */
+ const char *sql;
+
+ /**
+ * Should we ignore errors?
+ */
+ int ignore_errors;
+
+};
+
+
+/**
+ * Terminator for executable statement list.
+ */
+#define GNUNET_PQ_EXECUTE_STATEMENT_END { NULL, GNUNET_SYSERR }
+
+
+/**
+ * Create a `struct GNUNET_PQ_ExecuteStatement` where errors are fatal.
+ *
+ * @param sql actual SQL statement
+ * @return initialized struct
+ */
+struct GNUNET_PQ_ExecuteStatement
+GNUNET_PQ_make_execute (const char *sql);
+
+
+/**
+ * Create a `struct GNUNET_PQ_ExecuteStatement` where errors should
+ * be tolerated.
+ *
+ * @param sql actual SQL statement
+ * @return initialized struct
+ */
+struct GNUNET_PQ_ExecuteStatement
+GNUNET_PQ_make_try_execute (const char *sql);
+
+
+/**
+ * Request execution of an array of statements @a es from Postgres.
+ *
+ * @param connection connection to execute the statements over
+ * @param es #GNUNET_PQ_PREPARED_STATEMENT_END-terminated array of prepared
+ * statements.
+ * @return #GNUNET_OK on success (modulo statements where errors can be ignored)
+ * #GNUNET_SYSERR on error
+ */
+int
+GNUNET_PQ_exec_statements (PGconn *connection,
+ const struct GNUNET_PQ_ExecuteStatement *es);
+
+
+/* ******************** pq_connect.c functions ************** */
+
+
+/**
+ * Create a connection to the Postgres database using @a config_str
+ * for the configuration. Initialize logging via GNUnet's log
+ * routines and disable Postgres's logger.
+ *
+ * @param config_str configuration to use
+ * @return NULL on error
+ */
+PGconn *
+GNUNET_PQ_connect (const char *config_str);
+
+
+/**
+ * Connect to a postgres database using the configuration
+ * option "CONFIG" in @a section.
+ *
+ * @param cfg configuration
+ * @param section configuration section to use to get Postgres configuration options
+ * @return the postgres handle, NULL on error
+ */
+PGconn *
+GNUNET_PQ_connect_with_cfg (const struct GNUNET_CONFIGURATION_Handle *cfg,
+ const char *section);
+
+
+
#endif /* GNUNET_PQ_LIB_H_ */
/* end of include/gnunet_pq_lib.h */
/*
* This file is part of GNUnet
- * Copyright (C) 2009-2013, 2016 GNUnet e.V.
+ * Copyright (C) 2009-2013, 2016, 2017 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
};
-/**
- * Create our database indices.
- *
- * @param dbh handle to the database
- */
-static void
-create_indices (PGconn * dbh)
-{
- /* create indices */
- if ( (GNUNET_OK !=
- GNUNET_POSTGRES_exec (dbh,
- "CREATE INDEX ir_query_hash ON ns096blocks (query,expiration_time)")) ||
- (GNUNET_OK !=
- GNUNET_POSTGRES_exec (dbh,
- "CREATE INDEX ir_block_expiration ON ns096blocks (expiration_time)")) )
- LOG (GNUNET_ERROR_TYPE_ERROR,
- _("Failed to create indices\n"));
-}
-
-
/**
* 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
+ * @return #GNUNET_OK on success
*/
static int
database_setup (struct Plugin *plugin)
{
- PGresult *res;
-
- plugin->dbh = GNUNET_POSTGRES_connect (plugin->cfg,
- "namecache-postgres");
+ struct GNUNET_PQ_ExecuteStatement es_temporary =
+ GNUNET_PQ_make_execute ("CREATE TEMPORARY TABLE IF NOT EXISTS ns096blocks ("
+ " query BYTEA NOT NULL DEFAULT '',"
+ " block BYTEA NOT NULL DEFAULT '',"
+ " expiration_time BIGINT NOT NULL DEFAULT 0"
+ ")"
+ "WITH OIDS");
+ struct GNUNET_PQ_ExecuteStatement es_default =
+ GNUNET_PQ_make_execute ("CREATE TABLE IF NOT EXISTS ns096blocks ("
+ " query BYTEA NOT NULL DEFAULT '',"
+ " block BYTEA NOT NULL DEFAULT '',"
+ " expiration_time BIGINT NOT NULL DEFAULT 0"
+ ")"
+ "WITH OIDS");
+ const struct GNUNET_PQ_ExecuteStatement *cr;
+
+ plugin->dbh = GNUNET_PQ_connect_with_cfg (plugin->cfg,
+ "namecache-postgres");
if (NULL == plugin->dbh)
return GNUNET_SYSERR;
if (GNUNET_YES ==
"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");
+ cr = &es_temporary;
}
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");
+ cr = &es_default;
}
- 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;
+ struct GNUNET_PQ_ExecuteStatement es[] = {
+ *cr,
+ GNUNET_PQ_make_try_execute ("CREATE INDEX ir_query_hash ON ns096blocks (query,expiration_time)"),
+ GNUNET_PQ_make_try_execute ("CREATE INDEX ir_block_expiration ON ns096blocks (expiration_time)"),
+ GNUNET_PQ_EXECUTE_STATEMENT_END
+ };
+
+ if (GNUNET_OK !=
+ GNUNET_PQ_exec_statements (plugin->dbh,
+ es))
+ {
+ 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;
+ struct GNUNET_PQ_PreparedStatement ps[] = {
+ GNUNET_PQ_make_prepare ("cache_block",
+ "INSERT INTO ns096blocks (query, block, expiration_time) VALUES "
+ "($1, $2, $3)", 3),
+ GNUNET_PQ_make_prepare ("expire_blocks",
+ "DELETE FROM ns096blocks WHERE expiration_time<$1", 1),
+ GNUNET_PQ_make_prepare ("delete_block",
+ "DELETE FROM ns096blocks WHERE query=$1 AND expiration_time<=$2", 2),
+ GNUNET_PQ_make_prepare ("lookup_block",
+ "SELECT block FROM ns096blocks WHERE query=$1"
+ " ORDER BY expiration_time DESC LIMIT 1", 1),
+ GNUNET_PQ_PREPARED_STATEMENT_END
+ };
+
+ if (GNUNET_OK !=
+ GNUNET_PQ_prepare_statements (plugin->dbh,
+ ps))
+ {
+ PQfinish (plugin->dbh);
+ plugin->dbh = NULL;
+ return GNUNET_SYSERR;
+ }
}
+
return GNUNET_OK;
}
namecache_postgres_expire_blocks (struct Plugin *plugin)
{
struct GNUNET_TIME_Absolute now = GNUNET_TIME_absolute_get ();
- struct GNUNET_PQ_QueryParam params[] = {
+ struct GNUNET_PQ_QueryParam params[] = {
GNUNET_PQ_query_param_absolute_time (&now),
GNUNET_PQ_query_param_end
};
const struct GNUNET_HashCode *query,
struct GNUNET_TIME_AbsoluteNBO expiration_time)
{
- struct GNUNET_PQ_QueryParam params[] = {
+ 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
size_t block_size = ntohl (block->purpose.size) +
sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey) +
sizeof (struct GNUNET_CRYPTO_EcdsaSignature);
- struct GNUNET_PQ_QueryParam params[] = {
+ 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_break (0);
return GNUNET_SYSERR;
}
- delete_old_block (plugin, &query, block->expiration_time);
+ delete_old_block (plugin,
+ &query,
+ block->expiration_time);
res = GNUNET_PQ_exec_prepared (plugin->dbh,
"cache_block",
static int
namecache_postgres_lookup_block (void *cls,
const struct GNUNET_HashCode *query,
- GNUNET_NAMECACHE_BlockCallback iter, void *iter_cls)
+ GNUNET_NAMECACHE_BlockCallback iter,
+ void *iter_cls)
{
struct Plugin *plugin = cls;
- struct GNUNET_PQ_QueryParam params[] = {
+ struct GNUNET_PQ_QueryParam params[] = {
GNUNET_PQ_query_param_auto_from_type (query),
GNUNET_PQ_query_param_end
};
/*
* This file is part of GNUnet
- * Copyright (C) 2009-2013, 2016 GNUnet e.V.
+ * Copyright (C) 2009-2013, 2016, 2017 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
};
-/**
- * Create our database indices.
- *
- * @param dbh handle to the database
- */
-static void
-create_indices (PGconn * dbh)
-{
- /* create indices */
- if ( (GNUNET_OK !=
- GNUNET_POSTGRES_exec (dbh,
- "CREATE INDEX IF NOT EXISTS ir_pkey_reverse ON ns097records (zone_private_key,pkey)")) ||
- (GNUNET_OK !=
- GNUNET_POSTGRES_exec (dbh,
- "CREATE INDEX IF NOT EXISTS ir_pkey_iter ON ns097records (zone_private_key,rvalue)")) ||
- (GNUNET_OK !=
- GNUNET_POSTGRES_exec (dbh, "CREATE INDEX IF NOT EXISTS it_iter ON ns097records (rvalue)")) ||
- (GNUNET_OK !=
- GNUNET_POSTGRES_exec (dbh, "CREATE INDEX IF NOT EXISTS ir_label ON ns097records (label)")) )
- LOG (GNUNET_ERROR_TYPE_ERROR,
- _("Failed to create indices\n"));
-}
-
-
/**
* Initialize the database connections and associated
* data structures (create tables and indices
static int
database_setup (struct Plugin *plugin)
{
- PGresult *res;
-
- plugin->dbh = GNUNET_POSTGRES_connect (plugin->cfg,
- "namestore-postgres");
+ struct GNUNET_PQ_ExecuteStatement es_temporary =
+ GNUNET_PQ_make_execute ("CREATE TEMPORARY TABLE IF NOT EXISTS ns097records ("
+ " zone_private_key BYTEA NOT NULL DEFAULT '',"
+ " pkey BYTEA DEFAULT '',"
+ " rvalue BYTEA NOT NULL DEFAULT '',"
+ " record_count INTEGER NOT NULL DEFAULT 0,"
+ " record_data BYTEA NOT NULL DEFAULT '',"
+ " label TEXT NOT NULL DEFAULT ''"
+ ")"
+ "WITH OIDS");
+ struct GNUNET_PQ_ExecuteStatement es_default =
+ GNUNET_PQ_make_execute ("CREATE TABLE IF NOT EXISTS ns097records ("
+ " zone_private_key BYTEA NOT NULL DEFAULT '',"
+ " pkey BYTEA DEFAULT '',"
+ " rvalue BYTEA NOT NULL DEFAULT '',"
+ " record_count INTEGER NOT NULL DEFAULT 0,"
+ " record_data BYTEA NOT NULL DEFAULT '',"
+ " label TEXT NOT NULL DEFAULT ''"
+ ")"
+ "WITH OIDS");
+ const struct GNUNET_PQ_ExecuteStatement *cr;
+
+ plugin->dbh = GNUNET_PQ_connect_with_cfg (plugin->cfg,
+ "namestore-postgres");
if (NULL == plugin->dbh)
return GNUNET_SYSERR;
if (GNUNET_YES ==
"namestore-postgres",
"TEMPORARY_TABLE"))
{
- res =
- PQexec (plugin->dbh,
- "CREATE TEMPORARY TABLE IF NOT EXISTS ns097records ("
- " zone_private_key BYTEA NOT NULL DEFAULT '',"
- " pkey BYTEA DEFAULT '',"
- " rvalue BYTEA NOT NULL DEFAULT '',"
- " record_count INTEGER NOT NULL DEFAULT 0,"
- " record_data BYTEA NOT NULL DEFAULT '',"
- " label TEXT NOT NULL DEFAULT ''"
- ")" "WITH OIDS");
+ cr = &es_temporary;
}
else
{
- res =
- PQexec (plugin->dbh,
- "CREATE TABLE IF NOT EXISTS ns097records ("
- " zone_private_key BYTEA NOT NULL DEFAULT '',"
- " pkey BYTEA DEFAULT '',"
- " rvalue BYTEA NOT NULL DEFAULT '',"
- " record_count INTEGER NOT NULL DEFAULT 0,"
- " record_data BYTEA NOT NULL DEFAULT '',"
- " label TEXT NOT NULL DEFAULT ''"
- ")" "WITH OIDS");
+ cr = &es_default;
}
- 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",
- "ns097records");
- PQfinish (plugin->dbh);
- plugin->dbh = NULL;
- return GNUNET_SYSERR;
+ struct GNUNET_PQ_ExecuteStatement es[] = {
+ *cr,
+ GNUNET_PQ_make_try_execute ("CREATE INDEX IF NOT EXISTS ir_pkey_reverse "
+ "ON ns097records (zone_private_key,pkey)"),
+ GNUNET_PQ_make_try_execute ("CREATE INDEX IF NOT EXISTS ir_pkey_iter "
+ "ON ns097records (zone_private_key,rvalue)"),
+ GNUNET_PQ_make_try_execute ("CREATE INDEX IF NOT EXISTS it_iter "
+ "ON ns097records (rvalue)"),
+ GNUNET_PQ_make_try_execute ("CREATE INDEX IF NOT EXISTS ir_label "
+ "ON ns097records (label)"),
+ GNUNET_PQ_EXECUTE_STATEMENT_END
+ };
+
+ if (GNUNET_OK !=
+ GNUNET_PQ_exec_statements (plugin->dbh,
+ es))
+ {
+ PQfinish (plugin->dbh);
+ plugin->dbh = NULL;
+ return GNUNET_SYSERR;
+ }
}
- create_indices (plugin->dbh);
-
- if ((GNUNET_OK !=
- GNUNET_POSTGRES_prepare (plugin->dbh,
- "store_records",
- "INSERT INTO ns097records (zone_private_key, pkey, rvalue, record_count, record_data, label) VALUES "
- "($1, $2, $3, $4, $5, $6)", 6)) ||
- (GNUNET_OK !=
- GNUNET_POSTGRES_prepare (plugin->dbh,
- "delete_records",
- "DELETE FROM ns097records WHERE zone_private_key=$1 AND label=$2", 2)) ||
- (GNUNET_OK !=
- GNUNET_POSTGRES_prepare (plugin->dbh,
- "zone_to_name",
- "SELECT record_count,record_data,label FROM ns097records"
- " WHERE zone_private_key=$1 AND pkey=$2", 2)) ||
- (GNUNET_OK !=
- GNUNET_POSTGRES_prepare (plugin->dbh,
- "iterate_zone",
- "SELECT record_count,record_data,label FROM ns097records"
- " WHERE zone_private_key=$1 ORDER BY rvalue LIMIT 1 OFFSET $2", 2)) ||
- (GNUNET_OK !=
- GNUNET_POSTGRES_prepare (plugin->dbh,
- "iterate_all_zones",
- "SELECT record_count,record_data,label,zone_private_key"
- " FROM ns097records ORDER BY rvalue LIMIT 1 OFFSET $1", 1)) ||
- (GNUNET_OK !=
- GNUNET_POSTGRES_prepare (plugin->dbh,
- "lookup_label",
- "SELECT record_count,record_data,label"
- " FROM ns097records WHERE zone_private_key=$1 AND label=$2", 2)))
+
{
- PQfinish (plugin->dbh);
- plugin->dbh = NULL;
- return GNUNET_SYSERR;
+ struct GNUNET_PQ_PreparedStatement ps[] = {
+ GNUNET_PQ_make_prepare ("store_records",
+ "INSERT INTO ns097records (zone_private_key, pkey, rvalue, record_count, record_data, label) VALUES "
+ "($1, $2, $3, $4, $5, $6)", 6),
+ GNUNET_PQ_make_prepare ("delete_records",
+ "DELETE FROM ns097records "
+ "WHERE zone_private_key=$1 AND label=$2", 2),
+ GNUNET_PQ_make_prepare ("zone_to_name",
+ "SELECT record_count,record_data,label FROM ns097records"
+ " WHERE zone_private_key=$1 AND pkey=$2", 2),
+ GNUNET_PQ_make_prepare ("iterate_zone",
+ "SELECT record_count,record_data,label FROM ns097records "
+ "WHERE zone_private_key=$1 ORDER BY rvalue LIMIT 1 OFFSET $2", 2),
+ GNUNET_PQ_make_prepare ("iterate_all_zones",
+ "SELECT record_count,record_data,label,zone_private_key"
+ " FROM ns097records ORDER BY rvalue LIMIT 1 OFFSET $1", 1),
+ GNUNET_PQ_make_prepare ("lookup_label",
+ "SELECT record_count,record_data,label "
+ "FROM ns097records WHERE zone_private_key=$1 AND label=$2", 2),
+ GNUNET_PQ_PREPARED_STATEMENT_END
+ };
+
+ if (GNUNET_OK !=
+ GNUNET_PQ_prepare_statements (plugin->dbh,
+ ps))
+ {
+ PQfinish (plugin->dbh);
+ plugin->dbh = NULL;
+ return GNUNET_SYSERR;
+ }
}
+
return GNUNET_OK;
}
uint64_t rvalue;
uint32_t rd_count_nbo = htonl ((uint32_t) rd_count);
size_t data_size;
- unsigned int i;
memset (&pkey, 0, sizeof (pkey));
- for (i=0;i<rd_count;i++)
+ for (unsigned int i=0;i<rd_count;i++)
if (GNUNET_GNSRECORD_TYPE_PKEY == rd[i].record_type)
{
GNUNET_break (sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey) == rd[i].data_size);
GNUNET_memcpy (&pkey,
- rd[i].data,
- rd[i].data_size);
+ rd[i].data,
+ rd[i].data_size);
break;
}
- rvalue = GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK, UINT64_MAX);
+ rvalue = GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK,
+ UINT64_MAX);
data_size = GNUNET_GNSRECORD_records_get_size (rd_count, rd);
if (data_size > 64 * 65536)
{
const int paramFormats[] = { 1, 1, 1, 1, 1, 1 };
PGresult *res;
- if (data_size != GNUNET_GNSRECORD_records_serialize (rd_count, rd,
- data_size, data))
- {
+ if (data_size !=
+ GNUNET_GNSRECORD_records_serialize (rd_count, rd,
+ data_size, data))
+ {
GNUNET_break (0);
return GNUNET_SYSERR;
}
get_record_and_call_iterator (struct Plugin *plugin,
PGresult *res,
const struct GNUNET_CRYPTO_EcdsaPrivateKey *zone_key,
- GNUNET_NAMESTORE_RecordIterator iter, void *iter_cls)
+ GNUNET_NAMESTORE_RecordIterator iter,
+ void *iter_cls)
{
const char *data;
size_t data_size;
unsigned int cnt;
if (GNUNET_OK !=
- GNUNET_POSTGRES_check_result (plugin->dbh, res, PGRES_TUPLES_OK,
+ GNUNET_POSTGRES_check_result (plugin->dbh,
+ res,
+ PGRES_TUPLES_OK,
"PQexecPrepared",
"iteration"))
{
}
-/**
- * Connect to a postgres database
- *
- * @param cfg configuration
- * @param section configuration section to use to get Postgres configuration options
- * @return the postgres handle
- */
-PGconn *
-GNUNET_POSTGRES_connect (const struct GNUNET_CONFIGURATION_Handle * cfg,
- const char *section)
-{
- PGconn *dbh;
- char *conninfo;
-
- /* Open database and precompile statements */
- if (GNUNET_OK !=
- GNUNET_CONFIGURATION_get_value_string (cfg,
- section,
- "CONFIG",
- &conninfo))
- conninfo = NULL;
- dbh = PQconnectdb (conninfo == NULL ? "" : conninfo);
-
- if (NULL != dbh)
- {
- if (PQstatus (dbh) != CONNECTION_OK)
- {
- GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR,
- "postgres",
- _("Unable to connect to Postgres database '%s': %s\n"),
- conninfo,
- PQerrorMessage (dbh));
- PQfinish (dbh);
- dbh = NULL;
- }
- }
- // FIXME: warn about out-of-memory when dbh is NULL?
- GNUNET_free_non_null (conninfo);
- return dbh;
-}
-
-
/**
* Delete the row identified by the given rowid (qid
* in postgres).
libgnunetpq_la_SOURCES = \
pq.c \
+ pq_connect.c \
+ pq_eval.c \
+ pq_exec.c \
+ pq_prepare.c \
pq_query_helper.c \
pq_result_helper.c
libgnunetpq_la_LIBADD = -lpq \
--- /dev/null
+/*
+ This file is part of GNUnet
+ Copyright (C) 2017 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 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 WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ GNUnet; see the file COPYING. If not, If not, see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file pq/pq_connect.c
+ * @brief functions to connect to libpq (PostGres)
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include "gnunet_util_lib.h"
+#include "gnunet_pq_lib.h"
+
+
+/**
+ * Function called by libpq whenever it wants to log something.
+ * We already log whenever we care, so this function does nothing
+ * and merely exists to silence the libpq logging.
+ *
+ * @param arg the SQL connection that was used
+ * @param res information about some libpq event
+ */
+static void
+pq_notice_receiver_cb (void *arg,
+ const PGresult *res)
+{
+ /* do nothing, intentionally */
+}
+
+
+/**
+ * Function called by libpq whenever it wants to log something.
+ * We log those using the Taler logger.
+ *
+ * @param arg the SQL connection that was used
+ * @param message information about some libpq event
+ */
+static void
+pq_notice_processor_cb (void *arg,
+ const char *message)
+{
+ GNUNET_log_from (GNUNET_ERROR_TYPE_INFO,
+ "pq",
+ "%s",
+ message);
+}
+
+
+/**
+ * Create a connection to the Postgres database using @a config_str
+ * for the configuration. Initialize logging via GNUnet's log
+ * routines and disable Postgres's logger.
+ *
+ * @param config_str configuration to use
+ * @return NULL on error
+ */
+PGconn *
+GNUNET_PQ_connect (const char *config_str)
+{
+ PGconn *conn;
+
+ conn = PQconnectdb (config_str);
+ if ( (NULL == conn) ||
+ (CONNECTION_OK !=
+ PQstatus (conn)) )
+ {
+ GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR,
+ "pq",
+ "Database connection to '%s' failed: %s\n",
+ config_str,
+ (NULL != conn) ?
+ PQerrorMessage (conn)
+ : "PQconnectdb returned NULL");
+ if (NULL != conn)
+ PQfinish (conn);
+ return NULL;
+ }
+ PQsetNoticeReceiver (conn,
+ &pq_notice_receiver_cb,
+ conn);
+ PQsetNoticeProcessor (conn,
+ &pq_notice_processor_cb,
+ conn);
+ return conn;
+}
+
+
+/**
+ * Connect to a postgres database using the configuration
+ * option "CONFIG" in @a section.
+ *
+ * @param cfg configuration
+ * @param section configuration section to use to get Postgres configuration options
+ * @return the postgres handle, NULL on error
+ */
+PGconn *
+GNUNET_PQ_connect_with_cfg (const struct GNUNET_CONFIGURATION_Handle * cfg,
+ const char *section)
+{
+ PGconn *dbh;
+ char *conninfo;
+
+ /* Open database and precompile statements */
+ if (GNUNET_OK !=
+ GNUNET_CONFIGURATION_get_value_string (cfg,
+ section,
+ "CONFIG",
+ &conninfo))
+ conninfo = NULL;
+ dbh = GNUNET_PQ_connect (conninfo == NULL ? "" : conninfo);
+ GNUNET_free_non_null (conninfo);
+ return dbh;
+}
+
+
+/* end of pq/pq_connect.c */
--- /dev/null
+/*
+ This file is part of GNUnet
+ Copyright (C) 2017 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 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 WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ GNUnet; see the file COPYING. If not, If not, see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file pq/pq_eval.c
+ * @brief functions to execute SQL statements with arguments and/or results (PostGres)
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include "gnunet_util_lib.h"
+#include "gnunet_pq_lib.h"
+
+
+/**
+ * Error code returned by Postgres for deadlock.
+ */
+#define PQ_DIAG_SQLSTATE_DEADLOCK "40P01"
+
+/**
+ * Error code returned by Postgres for uniqueness violation.
+ */
+#define PQ_DIAG_SQLSTATE_UNIQUE_VIOLATION "23505"
+
+/**
+ * Error code returned by Postgres on serialization failure.
+ */
+#define PQ_DIAG_SQLSTATE_SERIALIZATION_FAILURE "40001"
+
+
+/**
+ * Check the @a result's error code to see what happened.
+ * Also logs errors.
+ *
+ * @param connection connection to execute the statement in
+ * @param statement_name name of the statement that created @a result
+ * @param result result to check
+ * @return status code from the result, mapping PQ status
+ * codes to `enum GNUNET_PQ_QueryStatus`. Never
+ * returns positive values as this function does
+ * not look at the result set.
+ * @deprecated (low level, let's see if we can do with just the high-level functions)
+ */
+enum GNUNET_PQ_QueryStatus
+GNUNET_PQ_eval_result (PGconn *connection,
+ const char *statement_name,
+ PGresult *result)
+{
+ if (PGRES_COMMAND_OK !=
+ PQresultStatus (result))
+ {
+ const char *sqlstate;
+
+ sqlstate = PQresultErrorField (result,
+ PG_DIAG_SQLSTATE);
+ if (NULL == sqlstate)
+ {
+ /* very unexpected... */
+ GNUNET_break (0);
+ return GNUNET_PQ_STATUS_HARD_ERROR;
+ }
+ if ( (0 == strcmp (sqlstate,
+ PQ_DIAG_SQLSTATE_DEADLOCK)) ||
+ (0 == strcmp (sqlstate,
+ PQ_DIAG_SQLSTATE_SERIALIZATION_FAILURE)) )
+ {
+ /* These two can be retried and have a fair chance of working
+ the next time */
+ GNUNET_log_from (GNUNET_ERROR_TYPE_INFO,
+ "pq",
+ "Query `%s' failed with result: %s/%s/%s/%s/%s\n",
+ statement_name,
+ PQresultErrorField (result,
+ PG_DIAG_MESSAGE_PRIMARY),
+ PQresultErrorField (result,
+ PG_DIAG_MESSAGE_DETAIL),
+ PQresultErrorMessage (result),
+ PQresStatus (PQresultStatus (result)),
+ PQerrorMessage (connection));
+ return GNUNET_PQ_STATUS_SOFT_ERROR;
+ }
+ GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR,
+ "pq",
+ "Query `%s' failed with result: %s/%s/%s/%s/%s\n",
+ statement_name,
+ PQresultErrorField (result,
+ PG_DIAG_MESSAGE_PRIMARY),
+ PQresultErrorField (result,
+ PG_DIAG_MESSAGE_DETAIL),
+ PQresultErrorMessage (result),
+ PQresStatus (PQresultStatus (result)),
+ PQerrorMessage (connection));
+ return GNUNET_PQ_STATUS_HARD_ERROR;
+ }
+ return GNUNET_PQ_STATUS_SUCCESS_NO_RESULTS;
+}
+
+
+/**
+ * Execute a named prepared @a statement that is NOT a SELECT
+ * statement in @a connnection using the given @a params. Returns the
+ * resulting session state.
+ *
+ * @param connection connection to execute the statement in
+ * @param statement_name name of the statement
+ * @param params parameters to give to the statement (#GNUNET_PQ_query_param_end-terminated)
+ * @return status code from the result, mapping PQ status
+ * codes to `enum GNUNET_PQ_QueryStatus`. Never
+ * returns positive values as this function does
+ * not look at the result set.
+ */
+enum GNUNET_PQ_QueryStatus
+GNUNET_PQ_eval_prepared_non_select (PGconn *connection,
+ const char *statement_name,
+ const struct GNUNET_PQ_QueryParam *params)
+{
+ PGresult *result;
+ enum GNUNET_PQ_QueryStatus qs;
+
+ result = GNUNET_PQ_exec_prepared (connection,
+ statement_name,
+ params);
+ qs = GNUNET_PQ_eval_result (connection,
+ statement_name,
+ result);
+ PQclear (result);
+ return qs;
+}
+
+
+/**
+ * Execute a named prepared @a statement that is a SELECT statement
+ * which may return multiple results in @a connection using the given
+ * @a params. Call @a rh with the results. Returns the query
+ * status including the number of results given to @a rh (possibly zero).
+ * @a rh will not have been called if the return value is negative.
+ *
+ * @param connection connection to execute the statement in
+ * @param statement_name name of the statement
+ * @param params parameters to give to the statement (#GNUNET_PQ_query_param_end-terminated)
+ * @param rh function to call with the result set, NULL to ignore
+ * @param rh_cls closure to pass to @a rh
+ * @return status code from the result, mapping PQ status
+ * codes to `enum GNUNET_PQ_QueryStatus`.
+ */
+enum GNUNET_PQ_QueryStatus
+GNUNET_PQ_eval_prepared_multi_select (PGconn *connection,
+ const char *statement_name,
+ const struct GNUNET_PQ_QueryParam *params,
+ GNUNET_PQ_PostgresResultHandler rh,
+ void *rh_cls)
+{
+ PGresult *result;
+ enum GNUNET_PQ_QueryStatus qs;
+ unsigned int ret;
+
+ result = GNUNET_PQ_exec_prepared (connection,
+ statement_name,
+ params);
+ qs = GNUNET_PQ_eval_result (connection,
+ statement_name,
+ result);
+ if (qs < 0)
+ {
+ PQclear (result);
+ return qs;
+ }
+ ret = PQntuples (result);
+ if (NULL != rh)
+ rh (rh_cls,
+ result,
+ ret);
+ PQclear (result);
+ return ret;
+}
+
+
+/**
+ * Execute a named prepared @a statement that is a SELECT statement
+ * which must return a single result in @a connection using the given
+ * @a params. Stores the result (if any) in @a rs, which the caller
+ * must then clean up using #GNUNET_PQ_cleanup_result() if the return
+ * value was #GNUNET_PQ_STATUS_SUCCESS_ONE_RESULT. Returns the
+ * resulting session status.
+ *
+ * @param connection connection to execute the statement in
+ * @param statement_name name of the statement
+ * @param params parameters to give to the statement (#GNUNET_PQ_query_param_end-terminated)
+ * @param[in,out] rs result specification to use for storing the result of the query
+ * @return status code from the result, mapping PQ status
+ * codes to `enum GNUNET_PQ_QueryStatus`.
+ */
+enum GNUNET_PQ_QueryStatus
+GNUNET_PQ_eval_prepared_singleton_select (PGconn *connection,
+ const char *statement_name,
+ const struct GNUNET_PQ_QueryParam *params,
+ struct GNUNET_PQ_ResultSpec *rs)
+{
+ PGresult *result;
+ enum GNUNET_PQ_QueryStatus qs;
+
+ result = GNUNET_PQ_exec_prepared (connection,
+ statement_name,
+ params);
+ qs = GNUNET_PQ_eval_result (connection,
+ statement_name,
+ result);
+ if (qs < 0)
+ {
+ PQclear (result);
+ return qs;
+ }
+ if (0 == PQntuples (result))
+ {
+ PQclear (result);
+ return GNUNET_PQ_STATUS_SUCCESS_NO_RESULTS;
+ }
+ if (1 != PQntuples (result))
+ {
+ /* more than one result, but there must be at most one */
+ GNUNET_break (0);
+ PQclear (result);
+ return GNUNET_PQ_STATUS_HARD_ERROR;
+ }
+ if (GNUNET_OK !=
+ GNUNET_PQ_extract_result (result,
+ rs,
+ 0))
+ {
+ PQclear (result);
+ return GNUNET_PQ_STATUS_HARD_ERROR;
+ }
+ PQclear (result);
+ return GNUNET_PQ_STATUS_SUCCESS_ONE_RESULT;
+}
+
+
+/* end of pq/pq_eval.c */
--- /dev/null
+/*
+ This file is part of GNUnet
+ Copyright (C) 2017 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 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 WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ GNUnet; see the file COPYING. If not, If not, see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file pq/pq_exec.c
+ * @brief functions to execute plain SQL statements (PostGres)
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include "gnunet_util_lib.h"
+#include "gnunet_pq_lib.h"
+
+
+/**
+ * Create a `struct GNUNET_PQ_ExecuteStatement` where errors are fatal.
+ *
+ * @param sql actual SQL statement
+ * @return initialized struct
+ */
+struct GNUNET_PQ_ExecuteStatement
+GNUNET_PQ_make_execute (const char *sql)
+{
+ struct GNUNET_PQ_ExecuteStatement es = {
+ .sql = sql,
+ .ignore_errors = GNUNET_NO
+ };
+
+ return es;
+}
+
+
+/**
+ * Create a `struct GNUNET_PQ_ExecuteStatement` where errors should
+ * be tolerated.
+ *
+ * @param sql actual SQL statement
+ * @return initialized struct
+ */
+struct GNUNET_PQ_ExecuteStatement
+GNUNET_PQ_make_try_execute (const char *sql)
+{
+ struct GNUNET_PQ_ExecuteStatement es = {
+ .sql = sql,
+ .ignore_errors = GNUNET_YES
+ };
+
+ return es;
+}
+
+
+/**
+ * Request execution of an array of statements @a es from Postgres.
+ *
+ * @param connection connection to execute the statements over
+ * @param es #GNUNET_PQ_PREPARED_STATEMENT_END-terminated array of prepared
+ * statements.
+ * @return #GNUNET_OK on success (modulo statements where errors can be ignored)
+ * #GNUNET_SYSERR on error
+ */
+int
+GNUNET_PQ_exec_statements (PGconn *connection,
+ const struct GNUNET_PQ_ExecuteStatement *es)
+{
+ for (unsigned int i=0; NULL != es[i].sql; i++)
+ {
+ PGresult *result;
+
+ result = PQexec (connection,
+ es[i].sql);
+
+ if ( (GNUNET_NO == es[i].ignore_errors) &&
+ (PGRES_COMMAND_OK != PQresultStatus (result)) )
+ {
+ GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR,
+ "pq",
+ "Failed to execute `%s': %s/%s/%s/%s/%s",
+ es[i].sql,
+ PQresultErrorField (result,
+ PG_DIAG_MESSAGE_PRIMARY),
+ PQresultErrorField (result,
+ PG_DIAG_MESSAGE_DETAIL),
+ PQresultErrorMessage (result),
+ PQresStatus (PQresultStatus (result)),
+ PQerrorMessage (connection));
+ PQclear (result);
+ return GNUNET_SYSERR;
+ }
+ PQclear (result);
+ }
+ return GNUNET_OK;
+}
+
+
+/* end of pq/pq_exec.c */
--- /dev/null
+/*
+ This file is part of GNUnet
+ Copyright (C) 2017 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 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 WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ GNUnet; see the file COPYING. If not, If not, see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file pq/pq_prepare.c
+ * @brief functions to connect to libpq (PostGres)
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include "gnunet_util_lib.h"
+#include "gnunet_pq_lib.h"
+
+
+/**
+ * Create a `struct GNUNET_PQ_PreparedStatement`.
+ *
+ * @param name name of the statement
+ * @param sql actual SQL statement
+ * @param num_args number of arguments in the statement
+ * @return initialized struct
+ */
+struct GNUNET_PQ_PreparedStatement
+GNUNET_PQ_make_prepare (const char *name,
+ const char *sql,
+ unsigned int num_args)
+{
+ struct GNUNET_PQ_PreparedStatement ps = {
+ .name = name,
+ .sql = sql,
+ .num_arguments = num_args
+ };
+
+ return ps;
+}
+
+
+/**
+ * Request creation of prepared statements @a ps from Postgres.
+ *
+ * @param connection connection to prepare the statements for
+ * @param ps #GNUNET_PQ_PREPARED_STATEMENT_END-terminated array of prepared
+ * statements.
+ * @return #GNUNET_OK on success,
+ * #GNUNET_SYSERR on error
+ */
+int
+GNUNET_PQ_prepare_statements (PGconn *connection,
+ const struct GNUNET_PQ_PreparedStatement *ps)
+{
+ for (unsigned int i=0;NULL != ps[i].name;i++)
+ {
+ PGresult *ret;
+
+ GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
+ "pq",
+ "Preparing SQL statement `%s' as `%s'\n",
+ ps[i].sql,
+ ps[i].name);
+ ret = PQprepare (connection,
+ ps[i].name,
+ ps[i].sql,
+ ps[i].num_arguments,
+ NULL);
+ if (PGRES_COMMAND_OK != PQresultStatus (ret))
+ {
+ GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
+ "pq",
+ _("PQprepare (`%s' as `%s') failed with error: %s\n"),
+ ps[i].sql,
+ ps[i].name,
+ PQerrorMessage (connection));
+ PQclear (ret);
+ return GNUNET_SYSERR;
+ }
+ }
+ return GNUNET_OK;
+}
+
+
+/* end of pq/pq_prepare.c */
* as needed as well).
*
* @param plugin the plugin context (state for this module)
- * @return GNUNET_OK on success
+ * @return #GNUNET_OK on success
*/
static int
database_setup (struct Plugin *plugin)
{
+ struct GNUNET_PQ_ExecuteStatement es[] = {
+ GNUNET_PQ_make_execute ("CREATE TABLE IF NOT EXISTS channels (\n"
+ " id SERIAL,\n"
+ " pub_key BYTEA NOT NULL CHECK (LENGTH(pub_key)=32),\n"
+ " max_state_message_id BIGINT,\n"
+ " state_hash_message_id BIGINT,\n"
+ " PRIMARY KEY(id)\n"
+ ")"
+ "WITH OIDS"),
+ GNUNET_PQ_make_execute ("CREATE UNIQUE INDEX IF NOT EXISTS channel_pub_key_idx \n"
+ " ON channels (pub_key)"),
+ GNUNET_PQ_make_execute ("CREATE OR REPLACE FUNCTION get_chan_id(BYTEA) RETURNS INTEGER AS \n"
+ " 'SELECT id FROM channels WHERE pub_key=$1;' LANGUAGE SQL STABLE \n"
+ "RETURNS NULL ON NULL INPUT"),
+ GNUNET_PQ_make_execute ("CREATE TABLE IF NOT EXISTS slaves (\n"
+ " id SERIAL,\n"
+ " pub_key BYTEA NOT NULL CHECK (LENGTH(pub_key)=32),\n"
+ " PRIMARY KEY(id)\n"
+ ")"
+ "WITH OIDS"),
+ GNUNET_PQ_make_execute ("CREATE UNIQUE INDEX IF NOT EXISTS slaves_pub_key_idx \n"
+ " ON slaves (pub_key)"),
+ GNUNET_PQ_make_execute ("CREATE OR REPLACE FUNCTION get_slave_id(BYTEA) RETURNS INTEGER AS \n"
+ " 'SELECT id FROM slaves WHERE pub_key=$1;' LANGUAGE SQL STABLE \n"
+ "RETURNS NULL ON NULL INPUT"),
+ GNUNET_PQ_make_execute ("CREATE TABLE IF NOT EXISTS membership (\n"
+ " channel_id BIGINT NOT NULL REFERENCES channels(id),\n"
+ " slave_id BIGINT NOT NULL REFERENCES slaves(id),\n"
+ " did_join INT NOT NULL,\n"
+ " announced_at BIGINT NOT NULL,\n"
+ " effective_since BIGINT NOT NULL,\n"
+ " group_generation BIGINT NOT NULL\n"
+ ")"
+ "WITH OIDS"),
+ GNUNET_PQ_make_execute ("CREATE INDEX IF NOT EXISTS idx_membership_channel_id_slave_id "
+ "ON membership (channel_id, slave_id)"),
+ /** @todo messages table: add method_name column */
+ GNUNET_PQ_make_execute ("CREATE TABLE IF NOT EXISTS messages (\n"
+ " channel_id BIGINT NOT NULL REFERENCES channels(id),\n"
+ " hop_counter INT NOT NULL,\n"
+ " signature BYTEA CHECK (LENGTH(signature)=64),\n"
+ " purpose BYTEA CHECK (LENGTH(purpose)=8),\n"
+ " fragment_id BIGINT NOT NULL,\n"
+ " fragment_offset BIGINT NOT NULL,\n"
+ " message_id BIGINT NOT NULL,\n"
+ " group_generation BIGINT NOT NULL,\n"
+ " multicast_flags INT NOT NULL,\n"
+ " psycstore_flags INT NOT NULL,\n"
+ " data BYTEA,\n"
+ " PRIMARY KEY (channel_id, fragment_id),\n"
+ " UNIQUE (channel_id, message_id, fragment_offset)\n"
+ ")"
+ "WITH OIDS"),
+ GNUNET_PQ_make_execute ("CREATE TABLE IF NOT EXISTS state (\n"
+ " channel_id BIGINT NOT NULL REFERENCES channels(id),\n"
+ " name TEXT NOT NULL,\n"
+ " value_current BYTEA,\n"
+ " value_signed BYTEA,\n"
+ " PRIMARY KEY (channel_id, name)\n"
+ ")"
+ "WITH OIDS"),
+ GNUNET_PQ_make_execute ("CREATE TABLE IF NOT EXISTS state_sync (\n"
+ " channel_id BIGINT NOT NULL REFERENCES channels(id),\n"
+ " name TEXT NOT NULL,\n"
+ " value BYTEA,\n"
+ " PRIMARY KEY (channel_id, name)\n"
+ ")"
+ "WITH OIDS"),
+ GNUNET_PQ_EXECUTE_STATEMENT_END
+ };
+
/* Open database and precompile statements */
- plugin->dbh = GNUNET_POSTGRES_connect (plugin->cfg,
- "psycstore-postgres");
+ plugin->dbh = GNUNET_PQ_connect_with_cfg (plugin->cfg,
+ "psycstore-postgres");
if (NULL == plugin->dbh)
return GNUNET_SYSERR;
-
- /* Create tables */
- if ((GNUNET_OK !=
- GNUNET_POSTGRES_exec(plugin->dbh,
- "CREATE TABLE IF NOT EXISTS channels (\n"
- " id SERIAL,\n"
- " pub_key BYTEA NOT NULL CHECK (LENGTH(pub_key)=32),\n"
- " max_state_message_id BIGINT,\n"
- " state_hash_message_id BIGINT,\n"
- " PRIMARY KEY(id)\n"
- ")" "WITH OIDS")) ||
-
- (GNUNET_OK !=
- GNUNET_POSTGRES_exec(plugin->dbh,
- "CREATE UNIQUE INDEX IF NOT EXISTS channel_pub_key_idx \n"
- " ON channels (pub_key)")) ||
-
- (GNUNET_OK !=
- GNUNET_POSTGRES_exec(plugin->dbh,
- "CREATE OR REPLACE FUNCTION get_chan_id(BYTEA) RETURNS INTEGER AS \n"
- " 'SELECT id FROM channels WHERE pub_key=$1;' LANGUAGE SQL STABLE \n"
- "RETURNS NULL ON NULL INPUT")) ||
-
- (GNUNET_OK !=
- GNUNET_POSTGRES_exec(plugin->dbh,
- "CREATE TABLE IF NOT EXISTS slaves (\n"
- " id SERIAL,\n"
- " pub_key BYTEA NOT NULL CHECK (LENGTH(pub_key)=32),\n"
- " PRIMARY KEY(id)\n"
- ")" "WITH OIDS")) ||
-
- (GNUNET_OK !=
- GNUNET_POSTGRES_exec(plugin->dbh,
- "CREATE UNIQUE INDEX IF NOT EXISTS slaves_pub_key_idx \n"
- " ON slaves (pub_key)")) ||
-
- (GNUNET_OK !=
- GNUNET_POSTGRES_exec(plugin->dbh,
- "CREATE OR REPLACE FUNCTION get_slave_id(BYTEA) RETURNS INTEGER AS \n"
- " 'SELECT id FROM slaves WHERE pub_key=$1;' LANGUAGE SQL STABLE \n"
- "RETURNS NULL ON NULL INPUT")) ||
-
- (GNUNET_OK !=
- GNUNET_POSTGRES_exec(plugin->dbh,
- "CREATE TABLE IF NOT EXISTS membership (\n"
- " channel_id BIGINT NOT NULL REFERENCES channels(id),\n"
- " slave_id BIGINT NOT NULL REFERENCES slaves(id),\n"
- " did_join INT NOT NULL,\n"
- " announced_at BIGINT NOT NULL,\n"
- " effective_since BIGINT NOT NULL,\n"
- " group_generation BIGINT NOT NULL\n"
- ")" "WITH OIDS")) ||
-
- (GNUNET_OK !=
- GNUNET_POSTGRES_exec(plugin->dbh,
- "CREATE INDEX IF NOT EXISTS idx_membership_channel_id_slave_id "
- "ON membership (channel_id, slave_id)")) ||
-
- /** @todo messages table: add method_name column */
- (GNUNET_OK !=
- GNUNET_POSTGRES_exec(plugin->dbh,
- "CREATE TABLE IF NOT EXISTS messages (\n"
- " channel_id BIGINT NOT NULL REFERENCES channels(id),\n"
- " hop_counter INT NOT NULL,\n"
- " signature BYTEA CHECK (LENGTH(signature)=64),\n"
- " purpose BYTEA CHECK (LENGTH(purpose)=8),\n"
- " fragment_id BIGINT NOT NULL,\n"
- " fragment_offset BIGINT NOT NULL,\n"
- " message_id BIGINT NOT NULL,\n"
- " group_generation BIGINT NOT NULL,\n"
- " multicast_flags INT NOT NULL,\n"
- " psycstore_flags INT NOT NULL,\n"
- " data BYTEA,\n"
- " PRIMARY KEY (channel_id, fragment_id),\n"
- " UNIQUE (channel_id, message_id, fragment_offset)\n"
- ")" "WITH OIDS")) ||
-
- (GNUNET_OK !=
- GNUNET_POSTGRES_exec(plugin->dbh,
- "CREATE TABLE IF NOT EXISTS state (\n"
- " channel_id BIGINT NOT NULL REFERENCES channels(id),\n"
- " name TEXT NOT NULL,\n"
- " value_current BYTEA,\n"
- " value_signed BYTEA,\n"
- " PRIMARY KEY (channel_id, name)\n"
- ")" "WITH OIDS")) ||
- (GNUNET_OK !=
- GNUNET_POSTGRES_exec(plugin->dbh,
- "CREATE TABLE IF NOT EXISTS state_sync (\n"
- " channel_id BIGINT NOT NULL REFERENCES channels(id),\n"
- " name TEXT NOT NULL,\n"
- " value BYTEA,\n"
- " PRIMARY KEY (channel_id, name)\n"
- ")" "WITH OIDS")))
+ if (GNUNET_OK !=
+ GNUNET_PQ_exec_statements (plugin->dbh,
+ es))
{
PQfinish (plugin->dbh);
plugin->dbh = NULL;
return GNUNET_SYSERR;
}
-
/* Prepare statements */
if ((GNUNET_OK != GNUNET_POSTGRES_prepare (plugin->dbh,
"transaction_begin",
void *purpose = NULL;
size_t signature_size;
size_t purpose_size;
-
uint64_t fragment_id;
uint64_t fragment_offset;
uint64_t message_id;
size_t buf_size;
int ret = GNUNET_SYSERR;
struct GNUNET_MULTICAST_MessageHeader *mp;
-
uint32_t msg_flags;
-
struct GNUNET_PQ_ResultSpec results[] = {
GNUNET_PQ_result_spec_uint32 ("hop_counter", &hop_counter),
GNUNET_PQ_result_spec_variable_size ("signature", &signature, &signature_size),
void *cb_cls)
{
struct Plugin *plugin = cls;
- *returned_fragments = 0;
-
struct GNUNET_PQ_QueryParam params_select[] = {
GNUNET_PQ_query_param_auto_from_type (channel_key),
GNUNET_PQ_query_param_uint64 (&first_fragment_id),
GNUNET_PQ_query_param_end
};
- return fragment_select (plugin, "select_fragments", params_select, returned_fragments, cb, cb_cls);
+ *returned_fragments = 0;
+ return fragment_select (plugin,
+ "select_fragments",
+ params_select,
+ returned_fragments,
+ cb, cb_cls);
}
GNUNET_PQ_query_param_end
};
- return fragment_select (plugin, "select_latest_fragments", params_select, returned_fragments, cb, cb_cls);
+ return fragment_select (plugin,
+ "select_latest_fragments",
+ params_select,
+ returned_fragments,
+ cb, cb_cls);
}
void *cb_cls)
{
struct Plugin *plugin = cls;
- *returned_fragments = 0;
-
- if (0 == fragment_limit)
- fragment_limit = INT64_MAX;
-
struct GNUNET_PQ_QueryParam params_select[] = {
GNUNET_PQ_query_param_auto_from_type (channel_key),
GNUNET_PQ_query_param_uint64 (&first_message_id),
GNUNET_PQ_query_param_end
};
- return fragment_select (plugin, "select_messages", params_select, returned_fragments, cb, cb_cls);
+ if (0 == fragment_limit)
+ fragment_limit = INT64_MAX;
+ *returned_fragments = 0;
+ return fragment_select (plugin,
+ "select_messages",
+ params_select,
+ returned_fragments,
+ cb, cb_cls);
}
void *cb_cls)
{
struct Plugin *plugin = cls;
- *returned_fragments = 0;
-
struct GNUNET_PQ_QueryParam params_select[] = {
GNUNET_PQ_query_param_auto_from_type (channel_key),
GNUNET_PQ_query_param_auto_from_type (channel_key),
GNUNET_PQ_query_param_end
};
- return fragment_select (plugin, "select_latest_messages", params_select, returned_fragments, cb, cb_cls);
+ *returned_fragments = 0;
+ return fragment_select (plugin,
+ "select_latest_messages",
+ params_select,
+ returned_fragments,
+ cb, cb_cls);
}
static int
-update_message_id (struct Plugin *plugin, const char *stmt,
+update_message_id (struct Plugin *plugin,
+ const char *stmt,
const struct GNUNET_CRYPTO_EddsaPublicKey *channel_key,
uint64_t message_id)
{